mirror of
https://github.com/cccriscv/mini-riscv-os.git
synced 2025-11-16 04:24:33 +00:00
init
This commit is contained in:
178
doc/Background.md
Normal file
178
doc/Background.md
Normal file
@@ -0,0 +1,178 @@
|
||||
# 背景知識
|
||||
|
||||
|
||||
file:///D:/ccc109/sp/10-riscv/pdf/RISC-V-Reader-Chinese-v2p1.pdf
|
||||
|
||||
101 頁
|
||||
|
||||
有三种标准的中断源:软件、时钟和外部来源。软件中断通过向内存映射寄存器中存数来触发,并通常用于由一个 hart 中断另一个 hart(在其他架构中称为处理器间中断机制)。当 hart 的时间比较器(一个名为 mtimecmp 的内存映射寄存器)大于实时计数器mtime 时,会触发时钟中断。。外部中断由平台级中断控制器(大多数外部设备连接到这个中断控制器)引发。不同的硬件平台具有不同的内存映射并且需要中断控制器的不同特性,因此用于发出和消除这些中断的机制因平台而异。所有 RISC-V 系统的共同问题是如何处理异常和屏蔽中断,这是下一节的主题。
|
||||
|
||||
10.3 机器模式下的异常处理
|
||||
|
||||
八个控制状态寄存器(CSR)是机器模式下异常处理的必要部分:
|
||||
|
||||
* mtvec(Machine Trap Vector)它保存发生异常时处理器需要跳转到的地址。
|
||||
* mepc(Machine Exception PC)它指向发生异常的指令。
|
||||
* mcause(Machine Exception Cause)它指示发生异常的种类。
|
||||
* mie(Machine Interrupt Enable)它指出处理器目前能处理和必须忽略的中断。
|
||||
* mip(Machine Interrupt Pending)它列出目前正准备处理的中断。
|
||||
* mtval(Machine Trap Value)它保存了陷入(trap)的附加信息:地址例外中出错的地址、发生非法指令例外的指令本身,对于其他异常,它的值为 0。
|
||||
* mscratch(Machine Scratch)它暂时存放一个字大小的数据。
|
||||
* mstatus(Machine Status)它保存全局中断使能,以及许多其他的状态,如图10.4 所示。
|
||||
|
||||
处理器在 M 模式下运行时,只有在全局中断使能位 mstatus.MIE 置 1 时才会产生中断.此外,每个中断在控制状态寄存器 mie 中都有自己的使能位。这些位在 mie 中的位置,对应于图 10.3 中的中断代码。例如,mie[7]对应于 M 模式中的时钟中断。控制状态寄存器mip具有相同的布局,并且它指示当前待处理的中断。将所有三个控制状态寄存器合在一起考虑,如果 status.MIE = 1,mie[7] = 1,且 mip[7] = 1,则可以处理机器的时钟中断。
|
||||
|
||||
当一个 hart 发生异常时,硬件自动经历如下的状态转换:
|
||||
|
||||
* 异常指令的 PC 被保存在 mepc 中,PC 被设置为 mtvec。(对于同步异常,mepc 指向导致异常的指令;对于中断,它指向中断处理后应该恢复执行的位置。)
|
||||
* mepc = PC; PC = mtvec
|
||||
* 根据异常来源设置 mcause(如图 10.3 所示),并将 mtval 设置为出错的地址或 者其它适用于特定异常的信息字。
|
||||
* 把控制状态寄存器 mstatus 中的 MIE 位置零以禁用中断,并把先前的 MIE 值保留到 MPIE 中。
|
||||
* 发生异常之前的权限模式保留在 mstatus 的 MPP 域中,再把权限模式更改为M。图 10.5 显示了 MPP 域的编码(如果处理器仅实现 M 模式,则有效地跳过这个步骤)。
|
||||
|
||||
为避免覆盖整数寄存器中的内容,中断处理程序先在最开始用 mscratch 和整数寄存器(例如 a0)中的值交换。通常,软件会让 mscratch 包含指向附加临时内存空间的指针,处理程序用该指针来保存其主体中将会用到的整数寄存器。在主体执行之后,中断程序会恢复它保存到内存中的寄存器,然后再次使用 mscratch 和 a0 交换,将两个寄存器恢复到它们在发生异常之前的值。
|
||||
|
||||
最后,处理程序用 mret 指令(M 模式特有的指令)返回。mret 将 PC 设置为 mepc,通过将 mstatus 的 MPIE 域复制到 MIE 来恢复之前的中断使能设置,并将权限模式设置为 mstatus 的 MPP 域中的值。这基本是前一段中描述的逆操作。
|
||||
|
||||
> mret: PC=mepc; mstatus:MIE=MPIE;
|
||||
|
||||
图 10.6 展示了遵循此模式的基本时钟中断处理程序的 RISC-V 汇编代码。它只对时间比较器执行了递增操作,然后继续执行之前的任务。更实际的时钟中断处理程序可能会调用调度程序,从而在任务之间切换。它是非抢占的,因此在处理程序的过程中中断会被禁用。不考虑这些限制条件的话,它就是一个只有一页的 RISC-V 中断处理程序的完整示例!
|
||||
|
||||

|
||||
|
||||
图 10.6;简单的 RISC-V 时钟中断处理程序代码。代码中假定了全局中断已通过置位 mstatus.MIE 启
|
||||
用;时钟中断已通过置位 mie[7]启用;mtvec CSR 已设置为此处理程序的入口地址;而且 mscratch
|
||||
CSR 已经设置为有 16 个字节用于保存寄存器的临时空间的地址。第一部分保存了五个寄存器,把 a0 保存在 mscratch 中,a1 到 a4 保存在内存中。然后它检查 mcause 来读取异常的类别:如果 mcause<0 则是中断,反之则是同步异常。如果是中断,就检查 mcause 的低位是否等于 7,如果是,就是 M 模式的时钟中断。如果确定是时钟中断,就给时间比较器加上 1000 个时钟周期,于是下一个时钟中断会发生在大约 1000 个时钟周期之后。最后一段恢复了 a0 到 a4 和 mscratch,然后用 mret 指令返回。
|
||||
|
||||
默认情况下,发生所有异常(不论在什么权限模式下)的时候,控制权都会被移交到M 模式的异常处理程序。但是 Unix 系统中的大多数例外都应该进行 S 模式下的系统调用。M 模式的异常处理程序可以将异常重新导向 S 模式,但这些额外的操作会减慢大多数异常的处理速度。因此,RISC-V 提供了一种异常委托机制。通过该机制可以选择性地将中断和同步异常交给 S 模式处理,而完全绕过 M 模式。
|
||||
|
||||
mideleg(Machine Interrupt Delegation,机器中断委托)CSR 控制将哪些中断委托给 S模式。与 mip 和 mie 一样,mideleg 中的每个位对应于图 10.3 中相同的异常。例如,mideleg[5]对应于 S 模式的时钟中断,如果把它置位,S 模式的时钟中断将会移交 S 模式的异常处理程序,而不是 M 模式的异常处理程序。
|
||||
|
||||
委托给 S 模式的任何中断都可以被 S 模式的软件屏蔽。sie(Supervisor InterruptEnable,监管者中断使能)和 sip(Supervisor Interrupt Pending,监管者中断待处理)CSR是 S 模式的控制状态寄存器,他们是 mie 和 mip 的子集。它们有着和 M 模式下相同的布局,但在 sie 和 sip 中只有与由 mideleg 委托的中断对应的位才能读写。那些没有被委派的中断对应的位始终为零。
|
||||
|
||||
M 模式还可以通过 medeleg CSR 将同步异常委托给 S 模式。该机制类似于刚才提到的中断委托,但 medeleg 中的位对应的不再是中断,而是图 10.3 中的同步异常编码。例如,置上 medeleg[15]便会把 store page fault(store 过程中出现的缺页)委托给 S 模式。
|
||||
|
||||
请注意,无论委派设置是怎样的,发生异常时控制权都不会移交给权限更低的模式。
|
||||
|
||||
在 M 模式下发生的异常总是在 M 模式下处理。在 S 模式下发生的异常,根据具体的委派设置,可能由 M 模式或 S 模式处理,但永远不会由 U 模式处理。
|
||||
|
||||
S 模式有几个异常处理 CSR:sepc、stvec、scause、sscratch、stval 和 sstatus,它们执行与 10.2 中描述的 M 模式 CSR 相同的功能。图 10.9 显示了 sstatus 寄存器的布局。
|
||||
|
||||
监管者异常返回指令 sret 与 mret 的行为相同,但它作用于 S 模式的异常处理 CSR,而不是 M 模式的 CSR。
|
||||
|
||||
S 模式处理例外的行为已和 M 模式非常相似。如果 hart 接受了异常并且把它委派给了S 模式,则硬件会原子地经历几个类似的状态转换,其中用到了 S 模式而不是 M 模式的CSR:
|
||||
|
||||
* 发生例外的指令的 PC 被存入 sepc,且 PC 被设置为 stvec。
|
||||
* scause 按图 10.3 根据异常类型设置,stval 被设置成出错的地址或者其它特定异常的信息字。
|
||||
* 把 sstatus CSR 中的 SIE 置零,屏蔽中断,且 SIE 之前的值被保存在 SPIE 中。
|
||||
* 发生例外时的权限模式被保存在 sstatus 的 SPP 域,然后设置当前模式为 S 模式。
|
||||
|
||||
##
|
||||
|
||||
* https://github.com/RISCV-on-Microsemi-FPGA/RVBM-BootLoader/blob/master/src/riscv_hal/startup.S
|
||||
* https://github.com/RISCV-on-Microsemi-FPGA/RVBM-BootLoader/blob/master/src/riscv_hal/entry.S
|
||||
* https://github.com/RISCV-on-Microsemi-FPGA/RVBM-BootLoader/blob/master/src/interrupts.c
|
||||
|
||||
```cpp
|
||||
/******************************************************************************
|
||||
* RISC-V interrupt handler for machine timer interrupts.
|
||||
*****************************************************************************/
|
||||
void handle_m_timer_interrupt(){
|
||||
|
||||
clear_csr(mie, MIP_MTIP);
|
||||
|
||||
add_10ms_to_mtimecmp();
|
||||
|
||||
SysTick_Handler();
|
||||
|
||||
// Re-enable the timer interrupt.
|
||||
set_csr(mie, MIP_MTIP);
|
||||
}
|
||||
```
|
||||
|
||||
```cpp
|
||||
/*------------------------------------------------------------------------------
|
||||
* Count the number of elapsed milliseconds (SysTick_Handler is called every
|
||||
* 10mS so the resolution will be 10ms). Rolls over every 49 days or so...
|
||||
*
|
||||
* Should be safe to read g_10ms_count from elsewhere.
|
||||
*/
|
||||
void SysTick_Handler(void)
|
||||
{
|
||||
g_10ms_count += 10;
|
||||
|
||||
/*
|
||||
* For neatness, if we roll over, reset cleanly back to 0 so the count
|
||||
* always goes up in proper 10s.
|
||||
*/
|
||||
if(g_10ms_count < 10)
|
||||
g_10ms_count = 0;
|
||||
}
|
||||
```
|
||||
|
||||
```cpp
|
||||
uintptr_t handle_trap(uintptr_t mcause, uintptr_t epc)
|
||||
{
|
||||
if (0){
|
||||
// External Machine-Level Interrupt from PLIC
|
||||
}else if ((mcause & MCAUSE_INT) && ((mcause & MCAUSE_CAUSE) == IRQ_M_EXT)) {
|
||||
handle_m_ext_interrupt();
|
||||
}else if ((mcause & MCAUSE_INT) && ((mcause & MCAUSE_CAUSE) == IRQ_M_TIMER)) {
|
||||
handle_m_timer_interrupt();
|
||||
}
|
||||
else{
|
||||
write(1, "trap\n", 5);
|
||||
_exit(1 + mcause);
|
||||
}
|
||||
return epc;
|
||||
}
|
||||
```
|
||||
|
||||
start.s
|
||||
|
||||
```s
|
||||
trap_entry:
|
||||
addi sp, sp, -32*REGBYTES
|
||||
...
|
||||
csrr a0, mcause
|
||||
csrr a1, mepc
|
||||
mv a2, sp
|
||||
jal handle_trap
|
||||
csrw mepc, a0
|
||||
# Remain in M-mode after mret
|
||||
li t0, MSTATUS_MPP
|
||||
csrs mstatus, t0
|
||||
...
|
||||
addi sp, sp, 32*REGBYTES
|
||||
mret
|
||||
```
|
||||
|
||||
* https://github.com/d0iasm/rvemu/blob/master/src/cpu.rs
|
||||
|
||||
```rust
|
||||
// mret
|
||||
// "The RISC-V Reader" book says:
|
||||
// "Returns from a machine-mode exception handler. Sets the pc to
|
||||
// CSRs[mepc], the privilege mode to CSRs[mstatus].MPP,
|
||||
// CSRs[mstatus].MIE to CSRs[mstatus].MPIE, and CSRs[mstatus].MPIE
|
||||
// to 1; and, if user mode is supported, sets CSRs[mstatus].MPP to 0".
|
||||
```
|
||||
|
||||
* RISC-V-Reader-Chinese-v2p1.pdf
|
||||
|
||||
* mret ExceptionReturn(Machine)
|
||||
* 机器模式异常返回(Machine-mode Exception Return). R-type, RV32I and RV64I 特权架构从机器模式异常处理程序返回。将 pc 设置为 CSRs[mepc], 将特权级设置成 CSRs[mstatus].MPP, CSRs[mstatus].MIE 置成 CSRs[mstatus].MPIE, 并且将 CSRs[mstatus].MPIE 为 1;并且,如果支持用户模式,则将 CSR [mstatus].MPP 设置为 0。
|
||||
|
||||
```cpp
|
||||
// set the machine-mode trap handler.
|
||||
w_mtvec((reg_t)sys_timer);
|
||||
```
|
||||
|
||||
```cpp
|
||||
// Machine-mode interrupt vector
|
||||
static inline void w_mtvec(reg_t x)
|
||||
{
|
||||
asm volatile("csrw mtvec, %0" : : "r" (x));
|
||||
}
|
||||
```
|
||||
|
||||
83
doc/Threads.md
Normal file
83
doc/Threads.md
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
|
||||
|
||||
像是 07-thread 裏使用了 tcb_t
|
||||
|
||||
```cpp
|
||||
/* Thread Control Block */
|
||||
typedef struct {
|
||||
void *stack;
|
||||
void *orig_stack;
|
||||
uint8_t in_use;
|
||||
} tcb_t;
|
||||
|
||||
static tcb_t tasks[MAX_TASKS];
|
||||
static int lastTask;
|
||||
|
||||
```
|
||||
|
||||
還有用了一大堆組合語言
|
||||
|
||||
```cpp
|
||||
void __attribute__((naked)) thread_start()
|
||||
{
|
||||
lastTask = 0;
|
||||
|
||||
/* Reset APSR before context switch.
|
||||
* Make sure we have a _clean_ PSR for the task.
|
||||
*/
|
||||
asm volatile("mov r0, #0\n"
|
||||
"msr APSR_nzcvq, r0\n");
|
||||
/* To bridge the variable in C and the register in ASM,
|
||||
* move the task's stack pointer address into r0.
|
||||
* http://www.ethernut.de/en/documents/arm-inline-asm.html
|
||||
*/
|
||||
asm volatile("mov r0, %0\n" : : "r" (tasks[lastTask].stack));
|
||||
asm volatile("msr psp, r0\n"
|
||||
"mov r0, #3\n"
|
||||
"msr control, r0\n"
|
||||
"isb\n");
|
||||
/* This is how we simulate stack handling that pendsv_handler
|
||||
* does. Thread_create sets 17 entries in stack, and the 9
|
||||
* entries we pop here will be pushed back in pendsv_handler
|
||||
* in the same order.
|
||||
*
|
||||
*
|
||||
* pop {r4-r11, lr}
|
||||
* ldr r0, [sp]
|
||||
* stack
|
||||
* offset -------
|
||||
* | 16 | <- Reset value of PSR
|
||||
* -------
|
||||
* | 15 | <- Task entry
|
||||
* -------
|
||||
* | 14 | <- LR for task
|
||||
* -------
|
||||
* | ... | register
|
||||
* ------- -------
|
||||
* | 9 | <- Task argument ----> | r0 |
|
||||
* psp after pop--< -------
|
||||
* | 8 | <- EXC_RETURN ----> | lr |
|
||||
* ------- -------
|
||||
* | 7 | | r11 |
|
||||
* ------- -------
|
||||
* | ... | | ... |
|
||||
* ------- -------
|
||||
* | 0 | | r4 |
|
||||
* psp -> ------- -------
|
||||
*
|
||||
* Instead of "pop {r0}", use "ldr r0, [sp]" to ensure consistent
|
||||
* with the way how PendSV saves _old_ context[1].
|
||||
*/
|
||||
asm volatile("pop {r4-r11, lr}\n"
|
||||
"ldr r0, [sp]\n");
|
||||
/* Okay, we are ready to run first task, get address from
|
||||
* stack[15]. We just pop 9 register so #24 comes from
|
||||
* (15 - 9) * sizeof(entry of sp) = 6 * 4.
|
||||
*/
|
||||
asm volatile("ldr pc, [sp, #24]\n");
|
||||
|
||||
/* Never reach here */
|
||||
while(1);
|
||||
}
|
||||
```
|
||||
5
doc/Uart.md
Normal file
5
doc/Uart.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Uart
|
||||
|
||||
* https://www.activexperts.com/serial-port-component/tutorials/uart/
|
||||
* https://www.maxlinear.com/Files/Documents/Intro_To_UARTs.pdf
|
||||
* https://twilco.github.io/riscv-from-scratch/2019/07/08/riscv-from-scratch-3.html
|
||||
101
doc/freeRtosRef.md
Normal file
101
doc/freeRtosRef.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# FreeRTOS
|
||||
|
||||
* https://github.com/illustris/FreeRTOS-RISCV/blob/master/Source/portable/GCC/RISCV/port.c
|
||||
|
||||
```cpp
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vPortSysTickHandler( void )
|
||||
{
|
||||
prvSetNextTimerInterrupt();
|
||||
|
||||
/* Increment the RTOS tick. */
|
||||
if( xTaskIncrementTick() != pdFALSE )
|
||||
{
|
||||
vTaskSwitchContext();
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
```
|
||||
|
||||
* https://github.com/illustris/FreeRTOS-RISCV/blob/master/Source/portable/GCC/RISCV/portasm.S
|
||||
|
||||
```s
|
||||
vPortYield:
|
||||
/*
|
||||
* This routine can be called from outside of interrupt handler. This means
|
||||
* interrupts may be enabled at this point. This is probably okay for registers and
|
||||
* stack. However, "mepc" will be overwritten by the interrupt handler if a timer
|
||||
* interrupt happens during the yield. To avoid this, prevent interrupts before starting.
|
||||
* The write to mstatus in the restore context routine will enable interrupts after the
|
||||
* mret. A more fine-grain lock may be possible.
|
||||
*/
|
||||
csrci mstatus, 8
|
||||
|
||||
portSAVE_CONTEXT
|
||||
portSAVE_RA
|
||||
jal vTaskSwitchContext
|
||||
portRESTORE_CONTEXT
|
||||
```
|
||||
|
||||
* https://github.com/illustris/FreeRTOS-RISCV/blob/master/Source/tasks.c
|
||||
|
||||
```cpp
|
||||
void vTaskSwitchContext( void )
|
||||
{
|
||||
if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )
|
||||
{
|
||||
/* The scheduler is currently suspended - do not allow a context
|
||||
switch. */
|
||||
xYieldPending = pdTRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
xYieldPending = pdFALSE;
|
||||
traceTASK_SWITCHED_OUT();
|
||||
|
||||
#if ( configGENERATE_RUN_TIME_STATS == 1 )
|
||||
{
|
||||
#ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
|
||||
portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime );
|
||||
#else
|
||||
ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE();
|
||||
#endif
|
||||
|
||||
/* Add the amount of time the task has been running to the
|
||||
accumulated time so far. The time the task started running was
|
||||
stored in ulTaskSwitchedInTime. Note that there is no overflow
|
||||
protection here so count values are only valid until the timer
|
||||
overflows. The guard against negative values is to protect
|
||||
against suspect run time stat counter implementations - which
|
||||
are provided by the application, not the kernel. */
|
||||
if( ulTotalRunTime > ulTaskSwitchedInTime )
|
||||
{
|
||||
pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime );
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
ulTaskSwitchedInTime = ulTotalRunTime;
|
||||
}
|
||||
#endif /* configGENERATE_RUN_TIME_STATS */
|
||||
|
||||
/* Check for stack overflow, if configured. */
|
||||
taskCHECK_FOR_STACK_OVERFLOW();
|
||||
|
||||
/* Select a new task to run using either the generic C or port
|
||||
optimised asm code. */
|
||||
taskSELECT_HIGHEST_PRIORITY_TASK();
|
||||
traceTASK_SWITCHED_IN();
|
||||
|
||||
#if ( configUSE_NEWLIB_REENTRANT == 1 )
|
||||
{
|
||||
/* Switch Newlib's _impure_ptr variable to point to the _reent
|
||||
structure specific to this task. */
|
||||
_impure_ptr = &( pxCurrentTCB->xNewLib_reent );
|
||||
}
|
||||
#endif /* configUSE_NEWLIB_REENTRANT */
|
||||
}
|
||||
}
|
||||
```
|
||||
BIN
doc/img/InterruptHandler.png
Normal file
BIN
doc/img/InterruptHandler.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 298 KiB |
101
doc/xv6ref.md
Normal file
101
doc/xv6ref.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# xv6 參考
|
||||
|
||||
当 xv6 内核在 CPU 上执行时,可能会发生两种类型的陷阱:异常和设备中断。上一节概述了 CPU 对此类陷阱的响应。
|
||||
|
||||
当内核执行时,它指向汇编代码 kernelvec (kernel/kernelve.S:10)。由于xv6已经在内核中,因此kernelvec可以依赖于将`satp`设置为内核页表,并依赖于引用有效内核堆栈的堆栈指针。kernelvec保存所有寄存器,这样我们最终可以恢复中断的代码,而不会干扰它。
|
||||
|
||||
|
||||
***
|
||||
kernelvec将寄存器保存在中断的内核线程的堆栈上,这是有意义的,因为寄存器值属于该线程。如果陷阱导致切换到不同的线程,这一点尤其重要 - 在这种情况下,陷阱实际上会返回到新线程的堆栈上,将被中断线程的已保存寄存器安全地留在其堆栈上。
|
||||
***
|
||||
|
||||
kernelvec在保存寄存器后跳转到kerneltrap(kernel/trap.c:134)。kerneltrap为两种类型的陷阱做好准备:设备中断和异常。它调用sdevintr(kernel/trap.c:177)来检查和处理前者。如果陷阱不是设备中断,那么它就是一个异常,如果它发生在内核中,那总是一个致命的错误。
|
||||
|
||||
## supervisor mode & mret
|
||||
|
||||
File: start.c
|
||||
|
||||
```cpp
|
||||
// entry.S jumps here in machine mode on stack0.
|
||||
void
|
||||
start()
|
||||
{
|
||||
// set M Previous Privilege mode to Supervisor, for mret.
|
||||
unsigned long x = r_mstatus();
|
||||
x &= ~MSTATUS_MPP_MASK;
|
||||
x |= MSTATUS_MPP_S;
|
||||
w_mstatus(x);
|
||||
|
||||
// set M Exception Program Counter to main, for mret.
|
||||
// requires gcc -mcmodel=medany
|
||||
w_mepc((uint64)main);
|
||||
|
||||
// disable paging for now.
|
||||
w_satp(0);
|
||||
|
||||
// delegate all interrupts and exceptions to supervisor mode.
|
||||
w_medeleg(0xffff);
|
||||
w_mideleg(0xffff);
|
||||
|
||||
// ask for clock interrupts.
|
||||
timerinit();
|
||||
|
||||
// keep each CPU's hartid in its tp register, for cpuid().
|
||||
int id = r_mhartid();
|
||||
w_tp(id);
|
||||
|
||||
// switch to supervisor mode and jump to main().
|
||||
asm volatile("mret");
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
File: main.c
|
||||
|
||||
```cpp
|
||||
#include "types.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "riscv.h"
|
||||
#include "defs.h"
|
||||
|
||||
volatile static int started = 0;
|
||||
|
||||
// start() jumps here in supervisor mode on all CPUs.
|
||||
void
|
||||
main()
|
||||
{
|
||||
if(cpuid() == 0){
|
||||
consoleinit();
|
||||
printfinit();
|
||||
printf("\n");
|
||||
printf("xv6 kernel is booting\n");
|
||||
printf("\n");
|
||||
kinit(); // physical page allocator
|
||||
kvminit(); // create kernel page table
|
||||
kvminithart(); // turn on paging
|
||||
procinit(); // process table
|
||||
trapinit(); // trap vectors
|
||||
trapinithart(); // install kernel trap vector
|
||||
plicinit(); // set up interrupt controller
|
||||
plicinithart(); // ask PLIC for device interrupts
|
||||
binit(); // buffer cache
|
||||
iinit(); // inode cache
|
||||
fileinit(); // file table
|
||||
virtio_disk_init(); // emulated hard disk
|
||||
userinit(); // first user process
|
||||
__sync_synchronize();
|
||||
started = 1;
|
||||
} else {
|
||||
while(started == 0)
|
||||
;
|
||||
__sync_synchronize();
|
||||
printf("hart %d starting\n", cpuid());
|
||||
kvminithart(); // turn on paging
|
||||
trapinithart(); // install kernel trap vector
|
||||
plicinithart(); // ask PLIC for device interrupts
|
||||
}
|
||||
|
||||
scheduler();
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user