内核在nand上的分布
烧写的是zImage, 烧写到了 nand 的 100000 地址处
u-boot 传参,并启动u-boot
setenv bootcmd nand read C0008000 100000 500000 ; bootm
从 nand 地址 100000 处, 读 500000 大小数据,到 C0008000 ,并从 bootm 启动
此时和 我们说的 uImage 不同,按道理 官方u-boot 只能引导 uImage 这一种内核镜像
除非他修改了u-boot,像链接那样https://blog.csdn.net/ce123_zhouwei/article/details/7336810
果然修改了 u-boot
common/cmd_bootm.c do_bootm 函数
#ifdef CONFIG_ZIMAGE_BOOT
#define LINUX_ZIMAGE_MAGIC 0x016f2818
if (*(ulong *)(addr + 9*4) == LINUX_ZIMAGE_MAGIC) {
printf("Boot with zImage\n");
addr = virt_to_phys(addr); // #define virt_to_phys(x) (x)
// addr = C0008000
hdr->ih_os = IH_OS_LINUX;
hdr->ih_ep = ntohl(addr); // hdr->ih_ep = C0008000
goto after_header_check;
}
#endif
然后走向了 do_bootm_linux (cmdtp, flag, argc, argv, addr, len_ptr, verify);
然后 theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
综上, u-boot 将zImage 从nand读取到了 内存的0xC0008000,然后从0xC0008000启动,也就是 start 标号处.接下来就是压缩内核启动流程
//其他启动方式,这里不考虑 setenv bootcmd tftp 20008000 zImage\; bootm 20008000
zImage 的启动流程
- 当前状态
此时zImage 在 0xC0008000 处
且当前状态为
1.内存已经初始化好
2.r0 = 0
3.r1 = mach id // ok6410的 machid , 1626
4.r2 = tags addr
5.pc = 0xC0008000
// 可以看出 此时 start 标号的 运行地址 为 0xC0008000
- zImage的生成
=================
arch/arm/boot/compressed/piggy.gzip: gzip compressed data, from Unix, max compression
cmd_arch/arm/boot/compressed/piggy.gzip := (cat arch/arm/boot/compressed/../Image | \
gzip -n -f -9 > \
arch/arm/boot/compressed/piggy.gzip) ||\
(rm -f arch/arm/boot/compressed/piggy.gzip ; false)
=================
arch/arm/boot/compressed/vmlinux: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, not stripped
cmd_arch/arm/boot/compressed/vmlinux := arm-linux-ld -EL \
--defsym zreladdr=0x50008000 \
-p \
--no-undefined \
-X \
-T arch/arm/boot/compressed/vmlinux.lds \
arch/arm/boot/compressed/head.o arch/arm/boot/compressed/piggy.gzip.o \
arch/arm/boot/compressed/misc.o arch/arm/boot/compressed/decompress.o \
arch/arm/boot/compressed/lib1funcs.o \
-o arch/arm/boot/compressed/vmlinux
=================
arch/arm/boot/zImage: Linux kernel ARM boot executable zImage (little-endian)
cmd_arch/arm/boot/zImage := arm-linux-objcopy \
-O binary \
-R .comment \
-S arch/arm/boot/compressed/vmlinux arch/arm/boot/zImage
=================
可以从 vmlinux.lds 和 nm vmlinux 看出 start 标号的 链接地址 是 0x00000000 .
- 启动流程
//1. 将解压后的 内核的起始位置放入 r4
ldr r4, =zreladdr // zreladdr 大小为 0x50008000
...
//2. 重定位过程
// 这里考虑一个问题,重定位需要重定位什么
// 一般一个二进制文件中的段有 这些 : .code .rodata .data .bss
// .code .rodata .data .bss 都需要重定位
// zImage的里面有这些 .text .got .got.plt .bss .stack
// Linux怎么实现这些段的重定位
// - GOT 技术 解决了 .code .rodata .data .bss 的重定位
teq r0, #0
//如果delta为0,无须对GOT表项和BSS进行重定位,否则下面进行重新定向
beq not_relocated
// 重定位代码,需要处理的事情
// 使链接地址 和 运行地址 一致
// r0 是 运行地址和链接地址的偏移量,大小等于 0xC0008000
// 2.1. 为 GOT 做重定位 做准备
add r11, r11, r0
// 计算出 GOT start 的 运行地址 ,大小等于0xC0008000 + 003ed9bc
// ✗ nm vmlinux |grep _got_start
// 003ed9bc A _got_start
add r12, r12, r0
// 计算出 GOT end 的 运行地址,大小等于 0xC0008000 + 003ed9e8
// ✗ nm vmlinux |grep _got_end
// 003ed9e8 A _got_end
// 2.2. 为 清0 .bss段 做准备
/*
* If we are running fully PIC === CONFIG_ZBOOT_ROM = n,
* we need to fix up pointers into the BSS region.
* Note that the stack pointer has already been fixed up.
*/
add r2, r2, r0
// __bss_start
// 计算出 BSS start 的 运行地址,大小等于 0xC0008000 + 003ed9f8
// ✗ nm vmlinux |grep __bss_start
// 003ed9f8 A __bss_start
add r3, r3, r0
// _end
// 计算出 BSS end 的 运行地址,大小等于 0xC0008000 + 003eda18
// ✗ nm vmlinux |grep _end
// 003eda18 A _end
/*
* Relocate all entries in the GOT table.
*/
// Global Offset Table
// 参考 https://blog.csdn.net/hujiuding/article/details/80142334
// 2.3. 重定位 GOT
1: ldr r1, [r11, #0] @ relocate entries in the GOT
// 加载 r11+0 地址中的数据 到 r1
add r1, r1, r0 @ table. This fixes up the
// r1 = r1 + 0xC0008000
str r1, [r11], #4 @ C references.
// 将r1 的数据保存到 r11+4 的地址中去,r11=r11+4
cmp r11, r12
// 比较r11 与 r12
blo 1b
// 如果小于,则跳转到上面的标号1处
// 2.4. 清0 .bss 段
not_relocated: mov r0, #0
1: str r0, [r2], #4 @ clear bss
str r0, [r2], #4
str r0, [r2], #4
str r0, [r2], #4
cmp r2, r3
blo 1b
// 3. 解压内核,并将解压后的内核放到 zreladdr
// 设置参数
mov r0, r4
mov r1, sp @ malloc space above stack
add r2, sp, #0x10000 @ 64k max
mov r3, r7
// 这里开始使用重定位的数据.
bl decompress_kernel
// 4. 关cache
bl cache_clean_flush
bl cache_off
// 5. 将控制权传给 解压后的内核.
// 设置 启动 未压缩 kernel 所需的 r0 r1 r2
mov r0, #0 @ must be zero
mov r1, r7 @ restore architecture number
mov r2, r8 @ restore atags pointer
mov pc, r4 @ call kernel
内存分布
总述
- 输入文件(五个.o文件)
.text .data .bss .stack 其他
- 输出文件(arch/arm/boot/compressed/vmlinux)
.text .got .got.plt .bss .stack 其他
需要考虑 .code .rodata .data .bss .stack .heap
- 文件中的链接地址
00000000
.text(包括.rodata)
003edad8
.got
003edb04
.got.plt
003edb10
.bss
003edb30
.stack
003eeb30
- 运行时的运行地址与链接地址
zImage 整个 在 C0008000 地址 处.但是链接地址是从 00000000 开始的.
zImage的第一个指令(arch/arm/boot/compressed/head.S) start 标号的第一个指令 (mov r0, r0) 位置在 C0008000 , 其链接地址在 00000000
然后将关键符号的地址(链接地址) 对应的 运行地址 都 计算出来.
.bss 起始地址
.bss 结束地址
.got 起始地址
.got 结束地址 // .got.plt起始地址
.got.plg 结束地址
压缩内核结束地址
栈顶位置
然后对去全局数据进行重定位(.got)
// 这里的代码块叫做动态链接器
// 动态链接器会从定位GOT表红的每个条目,使他包含正确的运行地址.
// 每个引用去全局数据的目标模块都有自己的GOT.
/*
* Relocate all entries in the GOT table.
*/
1: ldr r1, [r11, #0] @ relocate entries in the GOT
add r1, r1, r0 @ table. This fixes up the
str r1, [r11], #4 @ C references.
cmp r11, r12
blo 1b
// 这里将 .got段(不包括.got.plt) 中的 内容 都 加了 C00008000,并放回原位.
// .got 段 中 有 全局数据的链接地址
// 在运行时,索引一个全局数据,是怎么索引的,对应汇编代码是什么???
// 没有具体代码,简单来说,就是将一条指令,转换为多条指令
// mov r0,r1(A)
// 1. 寻找变量A,找到GOT表中对应A的条目所在的位置(运行地址)
// 2. 获取该位置中变量A的运行地址
// 3. 通过A的运行地址找到变量A中的值
// 4. 将A的值放到对应的寄存器(r0)中
为什么不对函数进行重定位呢?(.got.plt)
分述
- .code
// 也就是 输出文件中的 .text
逻辑地址
开始地址 : 00000000
结束地址 : 003edad8
运行地址
开始地址 : C0008000
结束地址 : C0008000 + 003edad8
.text : {
_start = .;
*(.start)
*(.text)
*(.text.*)
*(.fixup)
*(.gnu.warning)
*(.rodata)
*(.rodata.*)
*(.glue_7)
*(.glue_7t)
*(.piggydata)
. = ALIGN(4);
}
- .rodata
输出文件中的 .text 包括 .rodata
- .data
/DISCARD/ : {
*(.ARM.exidx*)
*(.ARM.extab*)
/*
* Discard any r/w data - this produces a link error if we have any,
* which is required for PIC decompression. Local data generates
* GOTOFF relocations, which prevents it being relocated independently
* of the text/got segments.
*/
*(.data)
}
// 为了 PIC 且 合入 GOT, data 段 被 .got 接管了.
- .bss
逻辑地址
003ed9f8 - 003eda18
运行地址
__bss_start 0xC0008000 + 003ed9f8
_end 0xC0008000 + 003eda18
- .stack
链接地址
.stack 的链接开始地址是 003edb30 ,链接结束地址是 3EEB30 ,大小 4096字节.
运行地址
.stack 的运行开始地址是 0xC0008000 + 003edb30 ,运行结束地址是 0xC0008000 + 3EEB30 ,大小 4096字节.
_end = .;
. = ALIGN(8); /* the stack must be 64-bit aligned */
.stack : { *(.stack) }
$ nm vmlinux |grep _end
003edb30 A _end
.word .L_user_stack_end @ sp
.align
.section ".stack", "aw", %nobits
.L_user_stack: .space 4096
.L_user_stack_end:
运行地址
ldr sp, [r0, #28]
// 此时 sp 装的是 r0+28 地址中的值, 也就是 003edb30 +C0008000
mov r1, sp @ malloc space above stack
add r2, sp, #0x10000 @ 64k max
bl decompress_kernel
arm 是满减栈
003edb30 +C0008000 , 里面存第一个数据,栈顶
003edb30 +C0008000 -4 , 里面存第二个数据.
- .heap
逻辑地址
无
运行地址
(003edb30 +C0008000 , 003edb30 +C0008000 + 0x10000 ]
sp 中的值 为 003edb30 +C0008000
/*
* The C runtime environment should now be setup sufficiently.
* Set up some pointers, and start decompressing.
* r4 = kernel execution address
* r7 = architecture ID
* r8 = atags pointer
*/
mov r0, r4
mov r1, sp @ malloc space above stack
add r2, sp, #0x10000 @ 64k max
mov r3, r7
bl decompress_kernel
void
decompress_kernel( \
unsigned long output_start, \ // r0 , kernel execution address
unsigned long free_mem_ptr_p, \ // r1 , 栈顶 , 堆的开始
unsigned long free_mem_ptr_end_p, \ // r2 , 堆的结束
int arch_id); // r3 , arch id , 1626
// r2 中的值将被 decompress_kernel 用于申请堆空间,空间大小为
(003edb30 +C0008000 , 003edb30 +C0008000 + 0x10000 ]
// 这个空间被 include/linux/decompress/mm.h 定义的 malloc 和 free 管理
- .got 和 .got.plt
输入文件中没有,输出文件中有.
PIC技术
逻辑地址 :
003edad8 - 003edb04
物理地址 :
C0008000 + 003edad8 - C0008000 + 003edb04
Image(非压缩内核)启动流程
- 当前状态
此时Image 在 0x50008000 处
且当前状态为
1.内存已经初始化好
2.r0 = 0
3.r1 = mach id // ok6410的 machid , 1626
4.r2 = tags addr
5.pc = 0x50008000
之前的压缩启动流程跟现在一点关系都没有
在pc设置后,(除了当前寄存器及寄存器指向的地址中的内容)非压缩内核启动流程的代码运行完全用不到之前代码运行时建立的环境.
- Image 的生成
vmlinux: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, BuildID[sha1]=3a0278f4e626cd58ef56496dd843b5b17d78aca6, not stripped
cmd_vmlinux := arm-linux-ld \
-EL \
-p \
--no-undefined \
-X \
--build-id \
-o vmlinux \
-T arch/arm/kernel/vmlinux.lds \
arch/arm/kernel/head.o \
arch/arm/kernel/init_task.o \
init/built-in.o \
--start-group \
usr/built-in.o \
arch/arm/kernel/built-in.o \
arch/arm/mm/built-in.o \
arch/arm/common/built-in.o \
arch/arm/mach-s3c64xx/built-in.o \
arch/arm/plat-samsung/built-in.o \
kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o crypto/built-in.o block/built-in.o \
arch/arm/lib/lib.a \
lib/lib.a \
arch/arm/lib/built-in.o \
lib/built-in.o drivers/built-in.o sound/built-in.o firmware/built-in.o net/built-in.o \
--end-group \
.tmp_kallsyms2.o
=================
arch/arm/boot/Image: data
cmd_arch/arm/boot/Image := arm-linux-objcopy -O binary \
-R .comment \
-S vmlinux \
arch/arm/boot/Image
=================
- 启动流程
1. 第一条指令
Image 的运行地址 在 0x50008000
Image 的起始链接地址 是 C0008000,也就是说
1. stext标号 在 C0008000
2. setmode宏展开的 第一句 msr cpsr_c,PSR_F_BIT | PSR_I_BIT | SVC_MODE 在 C0008000
2. 算出页表基址相关地址 (PAGE_OFFSET对应的运行地址)
算出 PAGE_OFFSET(0xC0000000) 逻辑地址 对应的 运行地址(50008000 - C0008000) + C0000000 = 50000000,并存储到r8.
为 页表基址 提供参考值
3.创建临时页表,更明了的信息请参考 表 ,表明为 临时页表内容 // 参考 https://blog.csdn.net/clb1609158506/article/details/41447475 中的图
// 通过 pgtbl r4, r8 算得 ,r4 = 5000 4000 ,r4 中的数据为页表基址的物理地址
// 之后的创建页表的动作都用的是r4中的数据作为页表基址.
// 注意 swapper_pg_dir 中 存储着 页表基址的虚拟理地址 0xC0004000
1. 清0 页表 所在的地址空间 5000 4000-50008000
// 之后 开始填充这个空间
// 除了地址内容还有 ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags
// 即 arch/arm/mm/proc-v6.S 中的 __v6_proc_info 中的
// ALT_UP(.long \
// PMD_TYPE_SECT | \
// PMD_SECT_AP_WRITE | \
// PMD_SECT_AP_READ | \
// PMD_FLAGS_UP)
2. 为 从__enable_mmu到__enable_mmu_end 的代码 创建 直接映射
// 需要映射的地址 __enable_mmu(c0008154) 到 __enable_mmu_end(c0008198)
// 页表占用的物理地址5000 5400-5000 5404 (1个页表项)
// 映射的地址 (50000000 - 50100000) - (C0000000 - C0010000)
// Sections , 映射1M字节.
3. 为 kernel 建立从虚拟地址到物理地址的映射
// 需要映射的地址 (0x50008000-508cd554)
// 页表占用的物理地址 5000 7000 - 5000 7024
// 映射的地址(5000 0000 - 5090 0000) - (C000 0000 - C090 0000)
// Sections , 映射1M字节.
4. 为 r2 中的 bootargs 建立从虚拟地址到物理地址的映射,包括了过程1中创建的16K一级页表
// 需要映射的地址 (r2 所在地址,r2=0x5000 0100) // 从u-boot 中查到的
// 页表占用的物理地址 5000 7000-5000 7004
// 映射的地址 (5000 0000 - 5010 0000) - (C0000000 - C0100000)
// Sections , 映射1M字节.
4. 开MMU
// 在这一步已经做好了重定位
// 下面的指令 中 链接地址就是虚拟地址,大小一样.下面的地址最好不要称呼为链接地址,最好称呼为虚拟地址
// 访问虚拟地址时,MMU会自动做转换.
5.
__mmap_switched
//0. 如何进入 __mmap_switched
// 开启 MMU 之前, ldr r13, =__mmap_switched
// 开启 MMU 之后, mov r3, r13; mov pc, r3
// 根据 (2. 为 从__enable_mmu到__enable_mmu_end 的代码 创建 直接映射) 访问内存
// 进入 __mmap_switched 之前的最后的一条指令
// 做 mov pc, r3 的动作的时候,是需要MMU访存流程的.
// 即 取值 : 把 r13(__mmap_switched) 中的地址的指令 (adr r3, __mmap_switched_data,实际是条伪指令,会编译成多条指令)给CPU
// 这时,就需要 走 MMU开启后的 内存访问流程.
// 1.给出的 __mmap_switched 地址 此时变成了虚拟地址(原来是链接地址) ,可以通过 System.map找到 虚拟地址c00081e0
// 2.寻找对应的物理地址 , tlb中找不到物理地址,访问 临时页表 (3. 为 kernel 建立从虚拟地址到物理地址的映射), 找到 虚拟地址c00081e0 对应的物理地址 500081e0
// 3.寻找物理地址中的值,值为 (adr r3, __mmap_switched_data)的二进制代码.
1. 搬运数据段
// 从 __data_loc 搬移到 _sdata
// 判断数据段存储地址(r4,__data_loc )和数据段起始地址(r5,_sdata)
// 如果不一样的话需要搬移到数据段起始地址上
// 本例中没有搬移
2. 清空bss段
3. 赋值变量
cpu id
arch/arm/kernel/setup.c 中的 unsigned int processor_id
机器 id
arch/arm/boot/compressed/misc.c 中的 unsigned int __machine_arch_type
tags 首地址
arch/arm/kernel/setup.c 中的 unsigned int __atags_pointer __initdata
cp15 中的 寄存器1 的 架构相关寄存器的值
arch/arm/kernel/entry-armv.S 中的 cr_alignment
cp15 中的 寄存器1 的 架构相关寄存器的值 & A = 0
arch/arm/kernel/entry-armv.S 中的 cr_no_alignment
4. 设置sp
sp = init_thread_union(c0802000) + 8184
= 0xC0803FF8
6.
b start_kernel
跳转到 start_kernel
内存分布
总述
做了几件事
// 此时没有虚拟地址,只有运行地址和链接地址
1.创建临时页表
2.开MMU
// 有虚拟地址和链接地址和物理地址
// 在开MMU的情况下,怎么理解 3 4 5 过程
3.搬运数据段
// 其实没做
4.清空bss段
5.设置sp
内存重定位也是存在的,只不过过程和 压缩内核启动流程 不一样
在这里,内存重定位过程如下
1.创建临时页表
2.打开MMU
// 此时内存重定位已经完成.
3.访问指令和数据
在 压缩内核启动流程 中
重定位前, 访问 全局符号 时,因为全局符号是链接地址,所以要将链接地址转换为运行地址(运行的物理地址)
重定位技术: GOT
重定位后,所有的全局符号都已经是运行地址,直接访问,直接给出(指令或数据)
在 非压缩内核启动流程中
重定位前, 访问 全局符号 时,因为全局符号是链接地址,所以要将链接地址转换为运行地址(运行的物理地址)
重定位技术: MMU
重定位后, 访问全局符号 时,因为全局符号时虚拟地址,MMU自动将虚拟地址转换为运行地址,直接给出(指令或数据)
分述
需要考虑 .code .rodata .data .bss .stack .heap ,
其实在 MMU 开启的那一刻,整个 Image 都已经重定位了
所以其中的 .code .rodata .data .bss 在那一刻都完成了重定位.
.stack 是在 MMU 打开之后,通过设置sp建立的,大小为0xC0803FF8
.heap 还没建立
其他
- 临时页表内容
__create_page_tables | 功能 | 页表起始地址 | 页表结束地址 | 页表项个数 | 物理地址起始 | 物理地址结束 | 物理地址范围 | 映射关系 | 虚拟地址起始 | 虚拟地址结束 | 虚拟地址范围 |
---|---|---|---|---|---|---|---|---|---|---|---|
过程1 | 清0地址空间 | 5000 4000 | 5000 8000 | 4096个 | |||||||
过程2 | 为 从__enable_mmu(c0008154) 到 __enable_mmu_end(c0008198) 的代码 创建 映射 | 5000 5400 | 5000 5404 | 1个 | 5000 0000 | 5010 0000 | 1M | 线性映射(直接相等) | 5000 0000 | 5010 0000 | 1M |
过程3 | 为 整个 Image 创建映射 | 5000 7000 | 5000 7024 | 9个 | 5000 0000 | 5090 0000 | 9M | 线性映射 | C000 0000 | C090 0000 | 9M |
过程4 | 为atags所在空间创建映射 | 5000 7000 | 5000 7004 | 1个 | 5000 0000 | 5010 0000 | 1M | 线性映射 | C000 0000 | C010 0000 | 1M |
其他 | 过程4覆盖了 过程3创建的第一个页表项,页表起始地址(5000 7000)不是随便找的,能保证 虚拟地址C000 81e0(__mmap_switched) 与 translation table base 5000 4000 结合能找到 一级描述符 地址 5000 7000 ,且 50007000 地址 中的内容 与 虚拟地址C000 81e0 结合 能找到 物理地址 5000 81e0(__mmap_switched) |
- arch/arm/boot/compressed/vmlinux 的段
$ readelf -S arch/arm/boot/compressed/vmlinux
There are 20 section headers, starting at offset 0x3fbd44:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00000000 008000 3edad8 00 AX 0 0 32
[ 2] .got PROGBITS 003edad8 3f5ad8 00002c 00 WA 0 0 4
[ 3] .got.plt PROGBITS 003edb04 3f5b04 00000c 04 WA 0 0 4
[ 4] .bss NOBITS 003edb10 3f5b10 000020 00 WA 0 0 4
[ 5] .stack NOBITS 003edb30 3f5b10 001000 00 WA 0 0 1
[ 6] .comment PROGBITS 00000000 3f5b10 000054 00 0 0 1
[ 7] .ARM.attributes ARM_ATTRIBUTES 00000000 3f5b64 000029 00 0 0 1
[ 8] .debug_line PROGBITS 00000000 3f5b8d 001049 00 0 0 1
[ 9] .debug_info PROGBITS 00000000 3f6bd6 001906 00 0 0 1
[10] .debug_abbrev PROGBITS 00000000 3f84dc 000618 00 0 0 1
[11] .debug_aranges PROGBITS 00000000 3f8af8 000088 00 0 0 8
[12] .debug_ranges PROGBITS 00000000 3f8b80 0004c0 00 0 0 8
[13] .debug_frame PROGBITS 00000000 3f9040 000288 00 0 0 4
[14] .debug_loc PROGBITS 00000000 3f92c8 00216c 00 0 0 1
[15] .debug_pubnames PROGBITS 00000000 3fb434 0001a4 00 0 0 1
[16] .debug_str PROGBITS 00000000 3fb5d8 0006a1 01 MS 0 0 1
[17] .shstrtab STRTAB 00000000 3fbc79 0000ca 00 0 0 1
[18] .symtab SYMTAB 00000000 3fc064 000c90 10 19 159 4
[19] .strtab STRTAB 00000000 3fccf4 000595 00 0 0 1
- 临时页表推演过程
/*
* Setup the initial page tables. We only setup the barest
* amount which are required to get the kernel running, which
* generally means mapping in the kernel code.
*
* r8 = phys_offset, r9 = cpuid, r10 = procinfo
*
* Returns:
* r0, r3, r5-r7 corrupted
* r4 = physical page table address
*/
__create_page_tables:
pgtbl r4, r8 @ page table address
// r4 = 5000 4000
// 1. 清0 5000 4000 到 5000 8000
/*
* Clear the 16K level 1 swapper page table
*/
mov r0, r4
// r0 = 5000 4000
mov r3, #0
// r3 = 0
add r6, r0, #0x4000
// r6 = 5000 8000
1: str r3, [r0], #4
// 存 0 到 地址 5000 4000
// r0 = r0 + 4 = 5000 4004
str r3, [r0], #4
// 存 0 到 地址 5000 4004
// r0 = r0 + 4 = 5000 4008
str r3, [r0], #4
// 存 0 到 地址 5000 4008
// r0 = r0 + 4 = 5000 400C
str r3, [r0], #4
// 存 0 到 地址 5000 400C
// r0 = r0 + 4 = 5000 4010
teq r0, r6
bne 1b
// 2. 5000 5400 地址处 存储 一个页表项 5000 5400-5000 5404
// phy : 5000 0000
// size : 10 0000
// 功能 : 映射 __enable_mmu(c0008154) 到 __enable_mmu_end(c0008198) 的代码
ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags
// 映射种类为 section , 大小为 10 0000
/*
* Create identity mapping to cater for __enable_mmu.
* This identity mapping will be removed by paging_init().
*/
adr r0, __enable_mmu_loc
// r0 中 装入 __enable_mmu_loc 的 物理地址
ldmia r0, {r3, r5, r6}
// r3 中 装入 __enable_mmu_loc 的 逻辑地址
// r5 中 装入 __enable_mmu 的 逻辑地址 , c0008154
// r6 中 装入 __enable_mmu_end 的 逻辑地址 , c0008198
sub r0, r0, r3 @ virt->phys offset
// r0 = r0 - r3 = (物理地址 - 逻辑地址) // 50008000-C0008000
add r5, r5, r0 @ phys __enable_mmu
// r5 = r5 + r0 = __enable_mmu 的 物理地址 , 50008154
add r6, r6, r0 @ phys __enable_mmu_end
// r6 = r6 + r0 = __enable_mmu_end 的 物理地址 , 50008198
mov r5, r5, lsr #20
// r5 = r5 >> 20 = 0x500
mov r6, r6, lsr #20
// r6 = r6 >> 20 = 0x500
1: orr r3, r7, r5, lsl #20 @ flags + kernel base
// r3 = r7 | (r5 << 20) = 5000 0000 | flags
str r3, [r4, r5, lsl #2] @ identity mapping
// 存 r3 到 r4 + (r5 << 2) 即 存 (5000 0000 | flags) 到 5000 4000 + 1400 = 5000 5400
// 不重新计算r4
teq r5, r6
// r5 == r6
addne r5, r5, #1 @ next section
// 不做
bne 1b
// 不做
// 3. 5000 7000 地址处 存储 9((0x5000 7023- 0x5000 7000)/4 + 1) 页表项 5000 7000 - 5000 7024
// 1.
// phy : 5000 0000
// size : 10 0000
// 2.
// phy : 5010 0000
// size : 10 0000
// 3.
// phy : 5020 0000
// size : 10 0000
// 4.
// phy : 5030 0000
// size : 10 0000
// 5.
// phy : 5040 0000
// size : 10 0000
// 6.
// phy : 5050 0000
// size : 10 0000
// 7.
// phy : 5060 0000
// size : 10 0000
// 8.
// phy : 5070 0000
// size : 10 0000
// 9.
// phy : 5080 0000
// size : 10 0000
/*
* Now setup the pagetables for our kernel direct
* mapped region.
*/
mov r3, pc
// 当前pc 懒得算 ,拿 __create_page_tables(c000804c)来算,对应的运行地址 是 5000804c
// r3 = 5000804c
mov r3, r3, lsr #20
// r3 = 500
orr r3, r7, r3, lsl #20
// r3 = 5000 0000 | r7
add r0, r4, #(KERNEL_START & 0xff000000) >> 18
// r0 = 5000 4000 + (0xC0008000 & 0xff000000 ) >> 0x12
// r0 = 5000 4000 + 3000
// r0 = 5000 7000
str r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!
// 存 (5000 0000 | r7) 到 5000 7000 + (0xC0008000 & 0x00f00000) >> 18
// 存 (5000 0000 | r7) 到 5000 7000 + 0
// 不重新计算r0
ldr r6, =(KERNEL_END - 1)
// r6 = c08cd554-1 = c08cd553
add r0, r0, #4
// r0 = 5000 B400 + 4
add r6, r4, r6, lsr #18
// r6 = 5000 4000 + c08cd553 << 0x12
// r6 = 5000 4000 + 3023
// r6 = 5000 7023
1: cmp r0, r6
// 比较 5000 7000 与 5000 7023
add r3, r3, #1 << 20
// r3 = r3 + 10 0000
strls r3, [r0], #4
// 如果 r0 < r6
// 存 r3 到 r0
// r0 = r0 + 4
bls 1b
#ifdef CONFIG_XIP_KERNEL
/*
* Map some ram to cover our .data and .bss areas.
*/
add r3, r8, #TEXT_OFFSET
orr r3, r3, r7
add r0, r4, #(KERNEL_RAM_VADDR & 0xff000000) >> 18
str r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!
ldr r6, =(_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
#endif
// 3. 5000 7000 地址处 存储 一个页表项 5000 7000-5000 7004
// phy : 5000 0000
// size : 10 0000
/*
* Then map boot params address in r2 or
* the first 1MB of ram if boot params address is not specified.
*/
#board/samsung/smdk6410/smdk6410.c
# gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);
#
#include/configs/smdk6410.h
# #define PHYS_SDRAM_1 MEMORY_BASE_ADDRESS /* SDRAM Bank #1 */
# #define MEMORY_BASE_ADDRESS 0x50000000
// r2 = 0x5000 0100
mov r0, r2, lsr #20
// r0 = r2 >> 20 = 0x500
movs r0, r0, lsl #20
// r0 = r0 << 20 = 0x5000 0000
moveq r0, r8
// Z为没置位,该句不执行
sub r3, r0, r8
// r3 = r0 - r8 = 0x5000 0000 - 0x 5000 0000 = 0
add r3, r3, #PAGE_OFFSET
// r3 = r3 + 0xC000 0000 = 0xC000 0000
add r3, r4, r3, lsr #18
// r3 = r4 + 0xC0000000 >> 18
// r3 = 5000 4000 + 0x3000 = 0x5000 7000
orr r6, r7, r0
// r6 = r7 | r0
// r6 = r7 | 0x5000 0000
str r6, [r3]
// 存 0x5000 0000 到 地址 0x5000 7000
#ifdef CONFIG_DEBUG_LL
#ifndef CONFIG_DEBUG_ICEDCC
/*
* Map in IO space for serial debugging.
* This allows debug messages to be output
* via a serial console before paging_init.
*/
addruart r7, r3
mov r3, r3, lsr #20
mov r3, r3, lsl #2
add r0, r4, r3
rsb r3, r3, #0x4000 @ PTRS_PER_PGD*sizeof(long)
cmp r3, #0x0800 @ limit to 512MB
movhi r3, #0x0800
add r6, r0, r3
mov r3, r7, lsr #20
ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
orr r3, r7, r3, lsl #20
1: str r3, [r0], #4
add r3, r3, #1 << 20
teq r0, r6
bne 1b
#else /* CONFIG_DEBUG_ICEDCC */
/* we don't need any serial debugging mappings for ICEDCC */
ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
#endif /* !CONFIG_DEBUG_ICEDCC */
#if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)
/*
* If we're using the NetWinder or CATS, we also need to map
* in the 16550-type serial port for the debug messages
*/
add r0, r4, #0xff000000 >> 18
orr r3, r7, #0x7c000000
str r3, [r0]
#endif
#ifdef CONFIG_ARCH_RPC
/*
* Map in screen at 0x02000000 & SCREEN2_BASE
* Similar reasons here - for debug. This is
* only for Acorn RiscPC architectures.
*/
add r0, r4, #0x02000000 >> 18
orr r3, r7, #0x02000000
str r3, [r0]
add r0, r4, #0xd8000000 >> 18
str r3, [r0]
#endif
#endif
mov pc, lr
ENDPROC(__create_page_tables)
.ltorg
.align
__enable_mmu_loc:
.long .
.long __enable_mmu
.long __enable_mmu_end
问题,为什么 vmlinux 里面有 .stack 段
arch/arm/boot/compressed/head.S
1115 .align
1116 .section ".stack", "aw", %nobits
1117 .L_user_stack: .space 4096
1118 .L_user_stack_end:
- GOT技术
arch/arm/boot/compressed/vmlinux
_got_start 003edad8
*(.got)
_got_end 003edb04
*(.got.plt)
_edata 003edb10
arch/arm/boot/compressed/vmlinux 生成过程
-T arch/arm/boot/compressed/vmlinux.lds \
arch/arm/boot/compressed/head.o arch/arm/boot/compressed/piggy.gzip.o \
arch/arm/boot/compressed/misc.o arch/arm/boot/compressed/decompress.o \
arch/arm/boot/compressed/lib1funcs.o \
-o arch/arm/boot/compressed/vmlinux
五个.o 里面都没有 .got 和 .got.plt 段,为什么 vmlinux就有这个段了呢???
深入理解计算机系统第三版 7.12 与位置无关的代码(PIC)
程序员自我修养第七章