Learn ARM64 virtual address space layout according to crash

Let's first look at a scene where something went wrong:

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

You can see the error prompt is: Unable to hanle kernel paging requeset at virtual address ffffdfc7be9c2100. Why is the address 0xffffdfc7be9c2100 illegal? Then look at " address between user and kernel address ranges

Why this address ffffdfc7be9c2100 will be abnormal? This has to say the ARM64 virtual address space layout. Before talking about ARM64, you need to talk about the 32-bit virtual address space.

On 32-bit machines, the entire 4G address space is divided into 2 parts, user space occupies 0-3G, and kernel space occupies 1G space of 3G-4G. How should the virtual address space be distributed on 64-bit machines?

64-bit virtual addresses are not yet fully supported on ARM64. How are they allocated? ARM supports a virtual address width of multiple digits, as shown in the following figure is a 48-bit virtual address width

The range of user space is 0x0000 0000 0000 0000 to 0x0000 FFFF FFFF FFFF, and the upper 16 bits are all 0s. The address range of the kernel space is 0xFFFF 0000 0000 0000 to 0xFFFF FFFF FFFF FFFF, the upper 16 bits are all 1s.

The middle [0x0000 FFFF FFFF FFFF FFFF 0xFFFF 0000 0000 0000] is an illegal area . If the CPU accesses this area during operation, a mem_abort exception will be triggered.

 

How much address width do we use on the simulator platform? The address bit width used by our simulator platform is 39 bits.

/*
 * 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)

Because each process of the user's virtual address space is isolated from each other, each process feels that it can see the entire virtual address space, and the size is represented by TASK_SIZE

  • 32-bit user space program: TASK_SIZE = TASK_SIZE_32 = 0x100000000 = 4G (when a 64-bit kernel runs a 32-bit application)
  • 64-bit user space program: TASK_SIZE = TASK_SIZE_64 = 1 << 39

If the address width is 39 bits, the virtual address space is distributed as follows:

Now that we have cleared the layout of the ARM64-bit virtual address space, let's go back and look at the address where we made a mistake. The address to be accessed by the CPU is ffffdfc7be9c2100, this address happens to fall in an illegal area, so it causes an error, this problem may exist bit flip f → d

 

Let's look at how the code judges the access of illegal areas

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 }

If the address accessed is less than TASK_SIZE is the address of the user space, you need to set ttbr0.

If the address accessed is greater than VA_START, it is the address of the kernel area, you need to set ttbr1.

If the accessed address falls in an illegal area, the above error will be printed.

 

 

Published 187 original articles · won 108 · 370,000 views

Guess you like

Origin blog.csdn.net/longwang155069/article/details/105381709