从0分析RTT内核aarch64启动流程
分析前先看下面这个函数:
1 | .macro get_phy, reg, symbol |
adrp \reg, \symbol
计算symbol相较于pc寄存器的页基地址,可以看成:1
reg = (symbol & ~(0xFFF)) - (pc & ~(0xFFF)) + (pc & ~(0xFFF))
再通过add \reg, \reg, #:lo12:\symbol将symbol的低12位与reg相加存放回reg中
这么使用是为了解除直接使用ldr reg,=symbol
的限制
step1
首先从内核的入口_start进行分析:
1 | /* Variable registers: x21~x28 */ |
- 首先通过
mov dtb_paddr, x0
将设备树的物理地址从x0中取出(由uboot放入x0中)放入dtb_paddr中,由dtb_paddr .req x21
可知dtb_paddr 是寄存器x21的别名,同理,x1-x3中的值会被放入x23-x24中。 - 再通过
get_phy stack_top, .boot_cpu_stack_top
将CPU得栈顶地址保存在X25中 - 再将tpidr_el1&&tpidrro_el0清0
- 执行cpu的一些初始化,如init_cpu_el是对el3的一下初始化,init_kernel_bss将bss段清0,init_cpu_stack_early初始化CPU栈
- 通过
mov x0, dtb_paddr
将设备树的物理地址放入x0寄存器 - 在rt_hw_fdt_install_early将设备树的地址,size信息存放在全局变量中
- 将rtthread_startup的地址存入x8寄存器中后跳转到init_mmu_early
step2
我们进入init_mmu_early中查看:
1 | init_mmu_early: |
首先获得了early_page_array的物理地址,通过源码可知,early_page_array是预留出来的一片24*4096的地址空间:
1
2.early_page_array:
.space 24 * ARCH_PAGE_SIZE再通过
bl set_free_page
将这个地址存入全局变量__init_page_array
中:1
2
3
4void set_free_page(void *page_array)
{
__init_page_array = page_array;
}将
early_tbl0_page
early_tbl1_page
存入x0,x1寄存器中。early_tbl0_page
early_tbl1_page
描述如下:1
2
3
4
5.early_tbl0_page:
.space ARCH_PAGE_SIZE
.early_tbl1_page:
/* Map 4G -> 2M * 512 entries */
.space 4 * ARCH_PAGE_SIZE再通过get_pvoff x2 x3计算物理地址与虚拟地址的差值并存入x3寄存器:
1
2
3
4
5.macro get_pvoff, tmp, out
ldr \tmp, =.boot_cpu_stack_top
get_phy \out, .boot_cpu_stack_top
sub \out, \out, \tmp
.endm该函数先将符号boot_cpu_stack_top的地址(在链接阶段已经确定是虚拟地址)存入tmp寄存器,再将boot_cpu_stack_top的物理地址存入out寄存器,最后相减存回out寄存器
将ARCH_EARLY_MAP_SIZE的值存入x2寄存器
跳转到
rt_hw_mem_setup_early
函数,此时x0-3寄存去的值分别为:- early_tbl0_page
- early_tbl1_page
- ARCH_EARLY_MAP_SIZE
- pvoff
将early_tbl0_page和early_tbl1_page映射为2M的页表,分别用于内核态和用户态,而
early_tbl0_page
和early_tbl0_page
分别为内核态和用户态l0页表的首地址再跳转到
enable_mmu_early
函数中
step3
enable_mmu_early
:
1 | enable_mmu_early: |
- 再获得刚刚分配的页表的首地址给x0与x1并将地址存放在
ttbr0_el1
和ttbr1_el1
中 - 设置内存屏障
- 跳转到mmu_tcr_init函数
- 获取pv_off到x0中并将sp设置为cpu栈顶指针的虚拟地址
- 将x30寄存器设置为
kernel_start
的地址 - 设置sctlr_el1寄存器,启动mmu和缓存,并失效前面的所有缓存
- 返回
返回后由于x30寄存器中存放的是kernel_start
地址所以会跳转到kernel_start
中执行
step4
1 | kernel_start: |
- 将x29寄存器清0,再跳转到x8中执行。x8寄存器在step1中
ldr x8, =rtthread_startup
被存放了rtthread_startup
的地址,所以会跳转到rtthread_startup
中执行,接下来就是c的代码的阅读啦~