2017-01-18

文来自本人的旧博客 http://blog.163.com/awaken_ing/blog/static/12061319720157119242210

1. _createpage_tables

pgtbl r4 @ page table address

其中pgtbl为宏

.macro pgtbl, rd
ldr \rd, =(KERNEL_RAM_PADDR - 0x4000)
.endm

这里KERNELRAMPADDR和zreladdr的值相等.

1.1 identity mapping

/*
* Create identity mapping for first MB of kernel to
* cater for the MMU enable. This identity mapping
* will be removed by paging_init(). We use our current program
* counter to determine corresponding section base address.
*/
mov r6, pc
mov r6, r6, lsr #20 @ start of kernel section
orr r3, r7, r6, lsl #20 @ flags + kernel base
str r3, [r4, r6, lsl #2] @ identity mapping
@ 和arch\arm\boot\compressed\head.S类似
@ r3内容:
@ 31       20       19                 0  
@  PC高12bit       __cpu_mm_mmu_flags
@存放的地址:
@   31          14           13            2                1 0  
@  页表基址(0x4000)         页表index(pc高12bit)              00  

这里建立的和This identity mapping 和arch/arm/boot/compressed/head.S类似.

因为.init段大小小于1M,所以只创建一个section entry, 就可以容纳下.

问题:为何这里要创建一个页表?

答:因为后面要开mmu, 这些没有被映射过的应该就没法访问. 让我们试下不建立这个页表的效果:

[root@localhost qemu_arm]#  arm-none-linux-gnueabi-gdb -q 
(gdb) b *0x8000
Breakpoint 1 at 0x8000
(gdb) target extended-remote localhost:1234
Remote debugging using localhost:1234
0x00000000 in ?? ()

(gdb) add-symbol-file linux-2.6.35.7/vmlinux 0xc0027000  -s .init 0x8000
add symbol table from file "linux-2.6.35.7/vmlinux" at
    .text_addr = 0xc0027000
    .init_addr = 0x8000
(y or n) y
Reading symbols from /opt/qemu_arm/linux-2.6.35.7/vmlinux...done.
(gdb) b __create_page_tables
Breakpoint 2 at 0x8078

(gdb) c
Continuing.

Breakpoint 1, 0x00008000 in stext ()
(gdb) c
Continuing.

Breakpoint 2, 0x00008078 in __create_page_tables ()
(gdb) disass  $pc
Dump of assembler code for function __create_page_tables:
=> 0x00008078 <+0>: mov r4, #16384  ; 0x4000
   0x0000807c <+4>: mov r0, r4
   0x00008080 <+8>: mov r3, #0
   0x00008084 <+12>:    add r6, r0, #16384  ; 0x4000
   0x00008088 <+16>:    str r3, [r0], #4
   0x0000808c <+20>:    str r3, [r0], #4
   0x00008090 <+24>:    str r3, [r0], #4
   0x00008094 <+28>:    str r3, [r0], #4
   0x00008098 <+32>:    teq r0, r6
   0x0000809c <+36>:    bne 0x8088 <__create_page_tables+16>
   0x000080a0 <+40>:    ldr r7, [r10, #8]
   0x000080a4 <+44>:    mov r6, pc
   0x000080a8 <+48>:    lsr r6, r6, #20
   0x000080ac <+52>:    orr r3, r7, r6, lsl #20
   0x000080b0 <+56>:    str r3, [r4, r6, lsl #2]
   0x000080b4 <+60>:    add r0, r4, #12288  ; 0x3000
   0x000080b8 <+64>:    str r3, [r0]!
   0x000080bc <+68>:    ldr r6, [pc, #88]   ; 0x811c
   0x000080c0 <+72>:    add r0, r0, #4
   0x000080c4 <+76>:    add r6, r4, r6, lsr #18
---Type  to continue, or q  to quit---q
Quit

(gdb) c
Continuing.

Breakpoint 3, 0x000080b0 in __create_page_tables ()
(gdb) i r r3 r4 r6 pc
r3             0xc1e    3102
r4             0x4000   16384
r6             0x0  0
pc             0x80b0   0x80b0 <__create_page_tables+56>

所以, 等效于set *0x4000=0xc1e

(gdb) b __turn_mmu_on
Breakpoint 4 at 0x8060
(gdb) c
Continuing.

Breakpoint 4, 0x00008060 in __turn_mmu_on ()
(gdb) p /x *0x4000   前面建立的页表项
$2 = 0xc1e
(gdb) set *0x4000=0x0    这里修改页表项
(gdb) p /x *0x4000   确认修改
$3 = 0x0
(gdb) b __mmap_switched
Breakpoint 5 at 0x8148
(gdb) c
Continuing.
Remote connection closed
(gdb)



[root@localhost qemu_arm]# sh debug_boot.sh 
Uncompressing Linux... done, booting the kernel.
qemu: fatal: Trying to execute code outside RAM or ROM at 0xffff000c

R00=00093177 R01=00000183 R02=00000100 R03=1f0f1c12
R04=00004000 R05=0000001f R06=00003135 R07=00000c12
R08=00021bec R09=41069265 R10=00021bb8 R11=00000000
R12=005c6b7c R13=00000000 R14=0000806c R15=ffff000c
PSR=400001d7 -Z-- A abt32
s00=00000000 s01=00000000 d00=0000000000000000
s02=00000000 s03=00000000 d01=0000000000000000
s04=00000000 s05=00000000 d02=0000000000000000
s06=00000000 s07=00000000 d03=0000000000000000
s08=00000000 s09=00000000 d04=0000000000000000
s10=00000000 s11=00000000 d05=0000000000000000
s12=00000000 s13=00000000 d06=0000000000000000
s14=00000000 s15=00000000 d07=0000000000000000
s16=00000000 s17=00000000 d08=0000000000000000
s18=00000000 s19=00000000 d09=0000000000000000
s20=00000000 s21=00000000 d10=0000000000000000
s22=00000000 s23=00000000 d11=0000000000000000
s24=00000000 s25=00000000 d12=0000000000000000
s26=00000000 s27=00000000 d13=0000000000000000
s28=00000000 s29=00000000 d14=0000000000000000
s30=00000000 s31=00000000 d15=0000000000000000
FPSCR: 00000000
debug_boot.sh: line 6: 27036 Aborted                 qemu-system-arm -M versatilepb -m 128M -kernel /opt/qemu_arm/linux-2.6.35.7/arch/arm/boot/zImage -nographic -S -s

1.2 将内核映射到以0xC0008000为起始地址的虚拟地址

/*
* Now setup the pagetables for our kernel direct
* mapped region.
*/
add r0, r4, #(KERNEL_START & 0xff000000) >> 18
str r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!
ldr r6, =(KERNEL_END - 1)
add r0, r0, #4
add r6, r4, r6, lsr #18
1: cmp r0, r6
add r3, r3, #1 << 20
strls r3, [r0], #4
bls 1b
@KERNEL_START为0xc000 8000
@ #(KERNEL_START & 0xff000000) >> 18 为0x3000
@#(KERNEL_START & 0x00f00000) >> 18 为 0
@ 项存放到哪个物理地址去呢:
@   31..14                 13..2                1 0  
@  页表基址(0x4000)       页表index(pc高12bit)    00

问题:这里创建的不是identity mappings, 如何看出来?

答:因为#(KERNEL_START & 0xff000000)是虚拟地址, 超过了物理内存

问题:为何 #(KERNEL_START & 0x00f00000) >> 18 ?

答:是因为add和str指令的机器码的格式, 能够表示的立即数范围受限.
add的 格式:

ADD <suffix> <dest>, <op 1>, <op 2> 
31 - 28 27 26 25 24 - 21 20 19 - 16 15 - 12 11 - 0
condition 0 0 I 0 1 0 0 S op_1 dest op_2/shift

2. 看下sp

startkernel前设置sp为initthreadunion + THREADSTART_SP

#define THREAD_START_SP (THREAD_SIZE - 8)

linux-2.6.35.7\arch\arm\kernel\init_task.c

union thread_union init_thread_union __init_task_data =
{ INIT_THREAD_INFO(init_task) };

/* Attach to the init_task data structure for proper alignment */
#define __init_task_data __attribute__((__section__(".data..init_task")))

arch/arm/kernel/vmlinux.lds.S中

. = ALIGN(THREAD_SIZE);
__data_loc = .;

.data : AT(__data_loc) {
    _data = .;      /* address in memory */
    _sdata = .;

    /*
     * first, the init task union, aligned
     * to an 8192 byte boundary.
     */
    INIT_TASK_DATA(THREAD_SIZE)

其中

#define INIT_TASK_DATA(align)                       \
    . = ALIGN(align);                       \
    *(.data..init_task)

问题:为何要用联合体union threadunion ? 答:由于栈是向低地址生长, threadinfo是向高地址生长, 这样就相当于共用HREAD_SIZE-8.

再看看为何-8
减去某个数, 是因为 这样就可以使用

static inline struct thread_info *current_thread_info(void)
{
register unsigned long sp asm ("sp");
return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
}

得到thread_info的起始地址, 所以, 要减去某个正数. 减去8, 而不是别的, 是因为sp要8对齐, 且尽量不浪费空间. 参见[2]

3. 相关资料

[1] http://lli_njupt.0fees.net/ar01s08.html

[2] http://stackoverflow.com/questions/25237607/in-arm-linux-what-is-the-purpose-of-the-few-bytes-reserved-at-the-bottom-of-k/25244273#2524427

本文地址: https://awakening-fong.github.io/posts/arm/arm_qemu_02

转载请注明出处: https://awakening-fong.github.io


若无法评论, 请打开JavaScript, 并通过proxy.


blog comments powered by Disqus