qemu观察arm linux启动过程02
文来自本人的旧博客 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
本文地址: https://awakening-fong.github.io/posts/arm/arm_qemu_02
转载请注明出处: https://awakening-fong.github.io
若无法评论, 请打开JavaScript, 并通过proxy.
blog comments powered by Disqus