从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
 4- void 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的代码的阅读啦~
 
		 
                      