RISC-v 启动流程

基于全志哪吒tina-d1-h开发版分析

前言

以全志哪吒tina-d1-h开发D1-H 芯片介绍为例uboot使用bootm启动uImage

uImage制作命令

mkimage -A riscv -O linux -T kernel -C  gzip -a  0x40080000 -e  0x40080000 -n 'RISCV OpenWrt Linux-5.4.61' -d  out/d1-h-nezha/compile_dir/target/linux-d1-h-nezha/Image.gz  out/d1-h-nezha/d1-h-nezha-uImage

bootm启动uImage

boot_normal=sunxi_flash read 45000000 ${boot_partition};bootm 45000000

bootm地址和-a指定的地址不同,从0x4500_0000开始提取64byte的头部,然后把头部去掉,在copy-a指定的load地址(load地址在uboot中根据BASE_ADDRESS和KERNEL_OFFSET重新计算为0x4000_0000)。

uboot启动kernel

images->ep指向0x40000000物理地址boot_hart启动CPU IDcpu不支持超线程)ft_addr表示DTB物理地址uboot会将command line更新到dts根节点下的chosenbootargs属性值中,以及根据ddr的起始地址和大小更新dts根节点下的memory节点内容(起始地址和大小)。

kernel = (void (*)(ulong, void *))images->ep;

kernel(gd->arch.boot_hart, images->ft_addr);

kernel启动

kernel启动地址0x40000000对应_start入口地址,执行一些初始化操作后调用_start_kernel汇编函数,函数的输入参数为启动cpu ID和DTB物理地址。_start_kernel主要工作包括:屏蔽所有中断,清bss段,setup_vm创建页表,切到虚拟地址空间运行,配置C运行环境,解析dtb,start_kernel。

代码路径:arch/riscv/kernel/head.S

屏蔽所有中断

csrw CSR_SIE, zero
csrw CSR_SIP, zero

bss段

	la a3, __bss_start
	la a4, __bss_stop
	ble a4, a3, clear_bss_done
clear_bss:
	REG_S zero, (a3)
	add a3, a3, RISCV_SZPTR
	blt a3, a4, clear_bss

setup_vm创建页表

参考平台使用3级页表,在分析setup_vm创建页表之前需要先熟悉下risc-v的内存布局。

Kernel space

        KERN_VIRT_SIZE: 0x20_0000_0000  //128GB

User space

        TASK_SIZE: 0x40_0000_0000 //256GB

配置参数

        CONFIG_PAGE_OFFSET: 0xFFFF_FFE0_0000_0000

        CONFIG_VA_BITS=39

setup_vm()创建页表,主要创建early_pg_dirtrampoline_pg_dir两张页表。

early_pg_dir页表是一张大小为4KB的数组,每一个页表项占8Bytes,存放在.init.data段,格式为early_pg_dir[512]。early_pg_dir页表的生命周期会持续到setup_vm_final()函数创建swapper_pg_dir页表,每一个页表项的结构体如下:(参考:玄铁C910用户手册_v13.pdf   虚拟内存管理章节)

early_pg_dir页表地址需要写入到MMU地址转换寄存器(SATP)中,SV39 SATP格式如下:

setup_vm()函数执行完成后,early_pg_dir页表全貌如下图:

1. 固定映射区(FIXADDR_START)建立映射,kernel启动过程中将DTB物理地址映射到固定映射区中。

2. kernel加载地址到PAGE_OFFSET建立映射,将虚拟地址[PAGE_OFFSET, PAGE_OFFSET+image size]区间映射到物理地址[0x4000000, 0x40000000 + image size]区间。

trampoline_pg_dir页表是一张大小为4KB的数组,每一个页表项占8Bytes,存放在.bss段,格式为trampoline_pg_dir[512]。setup_vm()函数执行完成后,trampoline_pg_dir页表全貌如下图:

trampoline_pg_dir页表仅建立虚拟地址[PAGE_OFFSET, PAGE_OFFSET + 2MB]区间到物理地址[0x40000000, 0x40000000 + 2MB]区间的映射。

切到虚拟地址空间运行

汇编函数relocate,输入参数为early_pg_dir,该函数执行后,系统就进入到虚拟地址运行。该函数主要工作如下:

1. 将函数返回地址rareturn address)寄存器中的值修改为虚拟地址

2. 将((trampoline_pg_dir >> 12) | SATP_MODE写入SATP寄存器,并刷新TLB sfence.vma

3. 修改部分通用寄存器到虚拟地址

4. 更新页表,将((early_pg_dir >> 12) | SATP_MODE写入SATP寄存器,并刷新TLB sfence.vma,该页表覆盖整个kernel,可支持系统运行到paging_init(),在setup_vm_final()函数中会map所有的ddr memory

配置C环境

la tp, init_task
sw zero, TASK_TI_CPU(tp)  // 初始化thread_info.cpu=0
la sp, init_thread_union + THREAD_SIZE

解析DTB

DTB早期解析实现函数early_init_dt_scan(),主要工作包括command line解析,获取addresssize格式,添加内存到memblock

start_kernel

start_kernel会初始化所有的资源,如启动cpu初始化,建立运行页表,内存初始化,进程调度初始化,启动多核,加载所有的module等,此处指分析运行页表的建立。

swapper_pg_dir页表是一张大小为4KB的数组,每一个页表项占8Bytes,存放在.bss段,格式为swapper_pg_dir[512]。

setup_vm_final()函数建立swapper_pg_dir页表,swapper_pg_dir页表是一张大小为4KB的数组,每一个页表项占8Bytes,存放在.bss段,格式为swapper_pg_dir[512]。该函数执行完成后,会映射memblock中的所有memory空间,然后清除固定映射区,swapper_pg_dir页表全貌如下图:

vmlinux内存布局

根据vmlinux内存布局,可知道页表建立过程中,各个页表的存放位置如下:

early_pg_dir[512]:               0xffffffe000023000         存放.init.data4KB
early_pmd[512]:                  0xffffffe000022000         存放.init.data4KB
fixmap_pmd[512]:               0xffffffe000936000         存放.bss4kB
trampoline_pmd[512]:         0xffffffe000937000         存放.bss段,4kB
fixmap_pte[512]:                 0xffffffe000938000         存放.bss段,4kB
trampoline_pg_dir[512]:     0xffffffe000939000          存放.bss段,4kB
swapper_pg_dir[512]:        0xffffffe00093a000          存放.bss段,4kB

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值