Saya melihat beberapa potensi masalah dengan bagian-bagian kritis itu. Ada peringatan dan solusi untuk semua ini, tetapi sebagai ringkasan:
- Tidak ada yang mencegah kompiler untuk memindahkan kode di makro ini, untuk optimasi atau alasan acak lainnya.
- Mereka menyimpan dan mengembalikan beberapa bagian dari status prosesor yang diharapkan oleh kompiler inline untuk dibiarkan sendiri (kecuali jika dikatakan sebaliknya).
- Tidak ada yang mencegah gangguan terjadi di tengah-tengah urutan dan mengubah keadaan antara ketika itu dibaca dan ketika itu ditulis.
Pertama, Anda pasti membutuhkan beberapa penghalang memori kompiler . GCC mengimplementasikan ini sebagai penjahat . Pada dasarnya, ini adalah cara untuk memberitahu kompiler "Tidak, Anda tidak dapat memindahkan akses memori di seluruh unit inline ini karena hal itu dapat mempengaruhi hasil dari akses memori." Khususnya, Anda membutuhkan keduanya "memory"
dan "cc"
clobbers, pada makro awal dan akhir. Ini akan mencegah hal-hal lain (seperti pemanggilan fungsi) diatur ulang relatif terhadap rakitan inline juga, karena kompiler tahu mereka mungkin memiliki akses memori. Saya telah melihat GCC untuk ARM terus status dalam register kode kondisi seluruh perakitan inline dengan "memory"
clobbers, jadi Anda pasti perlu "cc"
clobber.
Kedua, bagian-bagian penting ini menyimpan dan memulihkan lebih dari sekadar apakah interupsi diaktifkan. Secara khusus, mereka menyimpan dan memulihkan sebagian besar CPSR (Current Program Status Register) (tautannya adalah untuk Cortex-R4 karena saya tidak dapat menemukan diagram yang bagus untuk A9, tetapi harus identik). Ada batasan - batasan halus di mana bagian-bagian negara sebenarnya dapat dimodifikasi, tetapi ini lebih dari perlu di sini.
Antara lain, ini termasuk kode kondisi (di mana hasil instruksi seperti cmp
disimpan sehingga instruksi bersyarat berikutnya dapat bertindak atas hasilnya). Compiler pasti akan bingung dengan ini. Ini mudah dipecahkan menggunakan "cc"
clobber seperti yang disebutkan di atas. Namun, ini akan membuat kode gagal setiap saat, sehingga tidak terdengar seperti apa yang Anda lihat bermasalah. Agak dari bom waktu yang berdetak, dalam memodifikasi acak kode lain dapat menyebabkan kompiler melakukan sesuatu yang sedikit berbeda yang akan rusak oleh ini.
Ini juga akan mencoba untuk menyimpan / mengembalikan bit IT, yang digunakan untuk mengimplementasikan eksekusi kondisional Thumb . Perhatikan bahwa jika Anda tidak pernah menjalankan kode Thumb, ini tidak masalah. Saya tidak pernah mengetahui bagaimana perakitan inline GCC dengan bit-bit IT, selain menyimpulkannya, berarti kompiler tidak boleh meletakkan perakitan inline di blok TI dan selalu mengharapkan perakitan berakhir di luar blok TI. Saya belum pernah melihat GCC menghasilkan kode yang melanggar asumsi ini, dan saya telah melakukan beberapa perakitan inline yang cukup rumit dengan optimasi berat, jadi saya cukup yakin mereka memegangnya. Ini berarti mungkin tidak akan benar-benar mencoba untuk mengubah bit IT, dalam hal ini semuanya baik-baik saja. Mencoba untuk memodifikasi bit-bit ini diklasifikasikan sebagai "tidak dapat diprediksi secara arsitektur", jadi itu bisa melakukan segala macam hal buruk, tapi mungkin tidak akan melakukan apa pun.
Kategori bit terakhir yang akan disimpan / dikembalikan (selain yang benar-benar menonaktifkan interupsi) adalah bit mode. Ini mungkin tidak akan berubah, jadi mungkin tidak masalah, tetapi jika Anda memiliki kode yang dengan sengaja mengubah mode, bagian interupsi ini dapat menyebabkan masalah. Mengubah antara mode istimewa dan pengguna adalah satu-satunya kasus melakukan ini yang saya harapkan.
Ketiga, tidak ada yang mencegah interupsi dari mengubah bagian lain CPSR antara MRS
dan MSR
di ARM_INT_LOCK
. Setiap perubahan seperti itu dapat ditimpa. Dalam kebanyakan sistem yang masuk akal, interupsi asinkron tidak mengubah status kode yang diinterupsi (termasuk CPSR). Jika mereka melakukannya, menjadi sangat sulit untuk berpikir tentang kode apa yang akan dilakukan. Namun, itu mungkin (mengubah bit menonaktifkan FIQ tampaknya paling mungkin bagi saya), jadi Anda harus mempertimbangkan jika sistem Anda melakukan ini.
Inilah cara saya menerapkannya dengan cara yang mengatasi semua masalah potensial yang saya tunjukkan:
#define ARM_INT_KEY_TYPE unsigned int
#define ARM_INT_LOCK(key_) \
asm volatile(\
"mrs %[key], cpsr\n\t"\
"ands %[key], %[key], #0xC0\n\t"\
"cpsid if\n\t" : [key]"=r"(key_) :: "memory", "cc" );
#define ARM_INT_UNLOCK(key_) asm volatile (\
"tst %[key], #0x40\n\t"\
"beq 0f\n\t"\
"cpsie f\n\t"\
"0: tst %[key], #0x80\n\t"\
"beq 1f\n\t"\
"cpsie i\n\t"
"1:\n\t" :: [key]"r" (key_) : "memory", "cc")
Pastikan untuk mengkompilasi -mcpu=cortex-a9
karena setidaknya beberapa versi GCC (seperti milik saya) default ke CPU ARM yang lebih lama yang tidak mendukung cpsie
dan cpsid
.
Saya menggunakan ands
bukan hanya and
di ARM_INT_LOCK
jadi instruksi 16-bit jika ini digunakan dalam kode Thumb. The "cc"
mengkritik diperlukan anyways, jadi ketat ukuran manfaat kinerja / kode.
0
dan 1
yang label lokal , untuk referensi.
Ini harus dapat digunakan dalam semua cara yang sama seperti versi Anda. Ini ARM_INT_LOCK
sama cepat / kecilnya dengan yang asli. Sayangnya, saya tidak dapat menemukan cara untuk melakukannya ARM_INT_UNLOCK
dengan aman di mana pun dekat sebagai beberapa instruksi.
Jika sistem Anda memiliki batasan kapan IRQ dan FIQ dinonaktifkan, ini bisa disederhanakan. Misalnya, jika mereka selalu dinonaktifkan bersama-sama, Anda dapat bergabung menjadi cbz
+ cpsie if
seperti ini:
#define ARM_INT_UNLOCK(key_) asm volatile (\
"cbz %[key], 0f\n\t"\
"cpsie if\n\t"\
"0:\n\t" :: [key]"r" (key_) : "memory", "cc")
Atau, jika Anda tidak peduli tentang FIQ sama sekali maka itu sama dengan hanya drop mengaktifkan / menonaktifkannya sepenuhnya.
Jika Anda tahu bahwa tidak ada lagi yang pernah mengubah bit negara lainnya di CPSR antara kunci dan membuka kunci, maka Anda juga dapat menggunakan melanjutkan dengan sesuatu yang sangat mirip dengan kode asli Anda, kecuali dengan keduanya "memory"
dan "cc"
clobbers di keduanya ARM_INT_LOCK
danARM_INT_UNLOCK