当RISC-V计算机上电时,它会初始化自己并运行一个存储在只读内存中的引导加载程序。引导加载程序将xv6内核加载到内存中。然后,在机器模式下,中央处理器从_entry (kernel/entry.S:6)开始运行xv6。
_entry.S 入口
简述:完成多核cpu栈的分配并跳转到kernel/start.c
执行start
函数
1 | # qemu -kernel loads the kernel at 0x80000000 |
qemu -kernel 将内核加载到地址0x80000000处,并让每个CPU跳转到这里来执行。
kernel.ld 会将上述代码加载到0x800000000处,以供CPU跳转执行。(是否可以认为这里是入口)
我们将一行行解释这里的指令
la sp, stack0
,stack0被定义在start.c中:char stack0[4096 * NCPU]
,la
可以理解成load address指令,这里即是将stack0的地址加载到sp寄存器上(问题1:sp寄存器一般不是指向栈顶吗,这里的栈顶怎么成了stack0[0]了)。li a0, 1024*4
,li
可以理解成load imm(立即数)指令,即是将10244加载到a0寄存器,(说实话我没看到li la指令的区别,实际上是有的,还有比较大的区别,我们不细究,知道怎么使用就好,一个是地址,一个是立即数*)。csrr a1, mhartid
,csrr
我们要详细介绍下:有兴趣可以看下这篇文章,下面我们摘取介绍下CSR
CSR 是支撑 RISC-V 特权指令集的一个重要概念。CSR 的全称为 控制与状态寄存器(control and status registers)。
简单来说,CSR 是 CPU 中的一系列特殊的寄存器,这些寄存器能够反映和控制 CPU 当前的状态和执行机制。在 RISC-V 特权指令集手册 中定义的一些典型的 CSR 如下:
- misa,反映 CPU 对于 RISC-V 指令集的支持情况,如 CPU 所支持的最长的位数(32 / 64 / 128)和 CPU 所支持的 RISC-V 扩展。
- mstatus,包含很多与 CPU 执行机制有关的状态位,如 MIE 是否开启 M-mode 中断等。
- mhartid,简单理解为当前cpu的id
操作CSR寄存器的有以下几种指令(其中包括伪指令)
csrr
,读取一个CSR寄存器值到通用寄存器。如:csrr t0, mstatus
,读取mstatus
的值到t0
中。csrw
,将通用寄存器的值写入到CSR寄存器。如:csrw mstatus, t0
, 将t0
写入到mstatus
中。csrs
,把CSR
中指定的bit置1。如:csrsi mstatus, (1 << 2)
,将mstatus
的右起第3位置1。csrc
,把CSR
中指定的bit置0。csrrw
,读取一个CSR
的值到通用寄存器,然后把另一个值写入该CSR
。如:csrrw t0, mstatus, t0
,将mstatus
的值与t0
的值交换csrrs
,读取一个CSR
的值到通用寄存器,然后把该CSR
中指定的bit
置1。csrrc
, 读取一个CSR
的值到通用寄存器,然后把该CSR
中指定的bit
置0。
那么对于本行指令,就是将mhartid寄存器的值读取至a1中。addi a1, a1, 1
, a1 = a1 + 1mul a0, a0, a1
, a0 = a0 * a1
,即 a0 = 1024*4*a1
,这里我理解cpuid是从0开始递增的,所以对于cpu0来说,那么a0就等于1024*4add sp, sp, a0
,sp = sp + a0。
经过上述操作每个cpu分配到了一块栈,栈顶为stack0 + mhartid,大小为4096。
分配完栈后,便调用start(kernel/start.c:21)。
kernel/start的start函数
1 | void |
- 将机器模式切换到管理模式
- 将main函数入口写入到mepc(M Exception Program Counter)寄存器中,前程序计数器?
- w_satp(0); 禁用页表映射
- 将中断与异常代理到管理模式下
- 开启时钟中断
- 记录cpuid到tp寄存器中
- 调用mret,返回,弹出了mepc寄存器里main函数地址,开始执行main函数(具体的mret怎么执行还需要看下)。