前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >根据crash学习ARM64虚拟地址空间布局

根据crash学习ARM64虚拟地址空间布局

作者头像
DragonKingZhu
发布2020-04-10 14:35:29
2.9K0
发布2020-04-10 14:35:29
举报

我们先来看一个出错的现场:

代码语言:javascript
复制
[   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位。

代码语言:javascript
复制
/*
 * 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

再来看下代码是如何判断非法区域的访问的

代码语言:javascript
复制
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。

如果访问的地址落在非法区域,就会打印上述的错误。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档