まず、問題が発生したシーンを見てみましょう。
[ 55.195101] Unable to handle kernel paging request at virtual address ffffdfc7be9c2100
[ 55.195107] Mem abort info:
[ 55.195109] ESR = 0x96000004
[ 55.195112] Exception class = DABT (current EL), IL = 32 bits
[ 55.195114] SET = 0, FnV = 0
[ 55.195117] EA = 0, S1PTW = 0
[ 55.195118] Data abort info:
[ 55.195120] ISV = 0, ISS = 0x00000004
[ 55.195122] CM = 0, WnR = 0
[ 55.195125] [ffffdfc7be9c2100] address between user and kernel address ranges
[ 55.195128] Internal error: Oops: 96000004 [#1] PREEMPT SMP
.............
次のエラープロンプトが表示されます。仮想アドレスffffdfc7be9c2100でカーネルページング要求セットを処理できません。アドレス0xffffdfc7be9c2100が違法なのはなぜですか?そして、「見てユーザーとカーネルアドレス範囲の間アドレス」
このアドレスffffdfc7be9c2100が異常になるのはなぜですか?これは、ARM64仮想アドレススペースレイアウトと言えます。ARM64について説明する前に、32ビット仮想アドレス空間について説明する必要があります。
32ビットマシンでは、4Gアドレス空間全体が2つの部分に分割され、ユーザー空間は0〜3G、カーネル空間は3G〜4Gの1G空間を占めます。64ビットマシンで仮想アドレススペースをどのように分散する必要がありますか?
ARM64では、64ビットの仮想アドレスはまだ完全にはサポートされていません。ARMは複数桁の仮想アドレス幅をサポートします。次の図に示すように、48ビットの仮想アドレス幅です。
ユーザー空間の範囲は0x0000 0000 0000 0000〜0x0000 FFFF FFFF FFFFで、上位16ビットはすべて0です。カーネル空間のアドレス範囲は0xFFFF 0000 0000 0000〜0xFFFF FFFF FFFF FFFFで、上位16ビットはすべて1です。
中央の[0x0000 FFFF FFFF FFFF FFFF 0xFFFF 0000 0000 0000]は不正な領域です。動作中にCPUがこの領域にアクセスすると、mem_abort例外がトリガーされます。
シミュレータプラットフォームで使用するアドレス幅はどのくらいですか?シミュレータプラットフォームで使用されるアドレスビット幅は39ビットです。
/*
* TASK_SIZE - the maximum size of a user space task.
* TASK_UNMAPPED_BASE - the lower boundary of the mmap VM area.
*/
#ifdef CONFIG_COMPAT
#ifdef CONFIG_ARM64_64K_PAGES
/*
* With CONFIG_ARM64_64K_PAGES enabled, the last page is occupied
* by the compat vectors page.
*/
#define TASK_SIZE_32 UL(0x100000000)
#else
#define TASK_SIZE_32 (UL(0x100000000) - PAGE_SIZE)
#endif /* CONFIG_ARM64_64K_PAGES */
#define TASK_SIZE (test_thread_flag(TIF_32BIT) ? \
TASK_SIZE_32 : TASK_SIZE_64)
#define TASK_SIZE_OF(tsk) (test_tsk_thread_flag(tsk, TIF_32BIT) ? \
TASK_SIZE_32 : TASK_SIZE_64)
#else
#define TASK_SIZE TASK_SIZE_64
#endif /* CONFIG_COMPAT */
#define VA_BITS (CONFIG_ARM64_VA_BITS)
#define TASK_SIZE_64 (UL(1) << VA_BITS)
ユーザーの仮想アドレス空間の各プロセスは互いに分離されているため、各プロセスは仮想アドレス空間全体を見ることができると感じ、サイズはTASK_SIZEで表されます。
- 32ビットユーザー空間プログラム:TASK_SIZE = TASK_SIZE_32 = 0x100000000 = 4G(64ビットカーネルが32ビットアプリケーションを実行する場合)
- 64ビットのユーザー空間プログラム:TASK_SIZE = TASK_SIZE_64 = 1 << 39
アドレス幅が39ビットの場合、仮想アドレス空間は次のように分散されます。
ARM64ビット仮想アドレス空間のレイアウトをクリアしたので、前に戻って、ミスしたアドレスを見てみましょう。CPUがアクセスするアドレスはffffdfc7be9c2100であり、このアドレスはたまたま不正な領域にあるため、エラーが発生し、この問題がビットフリップf→dである可能性があります
コードが違法エリアへのアクセスをどのように判断するか見てみましょう
129/*
130 * Dump out the page tables associated with 'addr' in the currently active mm.
131 */
132void show_pte(unsigned long addr)
133{
134 struct mm_struct *mm;
135 pgd_t *pgdp;
136 pgd_t pgd;
137
138 if (addr < TASK_SIZE) {
139 /* TTBR0 */
140 mm = current->active_mm;
141 if (mm == &init_mm) {
142 pr_alert("[%016lx] user address but active_mm is swapper\n",
143 addr);
144 return;
145 }
146 } else if (addr >= VA_START) {
147 /* TTBR1 */
148 mm = &init_mm;
149 } else {
150 pr_alert("[%016lx] address between user and kernel address ranges\n",
151 addr);
152 return;
153 }
アクセスされるアドレスがTASK_SIZEより小さい場合は、ユーザー空間のアドレスであるので、ttbr0を設定する必要があります。
アクセスされたアドレスがVA_STARTより大きい場合、それはカーネル領域のアドレスなので、ttbr1を設定する必要があります。
アクセスしたアドレスが不正な領域にある場合、上記のエラーが出力されます。