根據crash學習ARM64虛擬地址空間佈局

我們先來看一個出錯的現場:

[   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
.............

可以看到出錯提示是:Unable to hanle kernel paging requeset at virtual address ffffdfc7be9c2100。爲什麼0xffffdfc7be9c2100這個地址是非法的? 接着再看“address between user and kernel address ranges” 

爲什麼這個地址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,此地址剛好落在在非法區域,所以導致出錯,此問題可能存在bit位翻轉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。

如果訪問的地址落在非法區域,就會打印上述的錯誤。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章