diff --git a/00_before_we_start/README.CN.md b/00_before_we_start/README.CN.md index 6a220dce..f0e76950 100644 --- a/00_before_we_start/README.CN.md +++ b/00_before_we_start/README.CN.md @@ -21,13 +21,13 @@ ## BSP 代码 `BSP` 表示 Board Support Package。`BSP`代码在`src/bsp.rs`中,而且包含目标主板特殊的定义和功能。这些是诸如主板的内存映射或相应主板上的设备驱动程序实例之类的东西。 -就像处理器架构代码一样,`BSP`代码模块架构也是试着镜像`kernel`的子系统模块,但是这次它没有明显的重新导入。这意味着必须从bsp名称空间开始调用提供的所有内容,例如,`bsp::driver::driver_manager()`。 +就像处理器架构代码一样,`BSP`代码模块架构也是试着镜像`kernel`的子系统模块,但是这次它没有明显的重新导出。这意味着必须从bsp名称空间开始调用提供的所有内容,例如,`bsp::driver::driver_manager()`。 ## 内核接口 -`arch`和`bsp`都包含根据实际目标和主板不同而编译的代码。例如,`interrupt controller`对于硬件`Raspberry Pi 3`和`Raspberry Pi 4`是不同的,但是我们想要`kernel`剩下的代码可以轻松的适配它们。 +`arch`和`bsp`都包含根据实际目标和主板不同而编译的代码。例如,`interrupt controller`对于硬件`Raspberry Pi 3`和`Raspberry Pi 4`是不同的,但是我们想让`kernel`剩下的代码可以轻松地适配它们。 -为了在`arch`,`bsp`和`generic kernel code`之间提供一个清晰的抽象,`interface`特征在*在可能的情况下*和*在可能的地方*被提供。它们在各自的子系统模块中定义,并有助于将程序的惯用法强制执行到接口,而不是实现上。例如,有一个通用IRQ处理接口,由两个树莓派不同的中断控制器驱动将实现的,并且仅将接口导出到`kernel`剩下的代码中。 +为了在`arch`,`bsp`和`generic kernel code`之间提供一个清晰的抽象,`interface`特征在*在可能的情况下*和*在可能的地方*被提供。它们在各自的子系统模块中定义,并有助于将程序的调用方法体现到接口,而不是具体实现上。例如,有一个通用IRQ处理接口,由两个树莓派不同的中断控制器驱动将实现的,并且仅将接口导出到`kernel`剩下的代码中。 ``` +-------------------+ diff --git a/02_runtime_init/README.CN.md b/02_runtime_init/README.CN.md new file mode 100644 index 00000000..eb932091 --- /dev/null +++ b/02_runtime_init/README.CN.md @@ -0,0 +1,199 @@ +# 教程 02 - 执行初始化 + +## tl;dr + +我们拓展了`cpu.S`,在第一次启动的时候调用Rust代码。在Rust的代码中先清零了[bss] section,然后通过调用`panic()`挂起CPU。再次运行`make qemu`看看新增加的代码是怎么运行的。 + +## 值得注意的变化 + +- 链接脚本(linker script)中有了更多的section。 + - `.rodata`, `.data` + - `.bss` +- `_start()`: + - 当核心不是`core0`第0号核心的时候,挂起该CPU核心。 + - `core0`会调用Rust的函数`runtime_init()`。 +- `runtime_init.rs`内的`runtime_init()` + - 清零了`.bss` section. + - 它调用了`kernel_init()`, 这个函数又调用了`panic!()`, panic函数最终把`core0`和其他核心一样挂起了。 + +[bss]: https://en.wikipedia.org/wiki/.bss + +## 相比之前的变化(diff) +```diff + +diff -uNr 01_wait_forever/src/_arch/aarch64/cpu.S 02_runtime_init/src/_arch/aarch64/cpu.S +--- 01_wait_forever/src/_arch/aarch64/cpu.S ++++ 02_runtime_init/src/_arch/aarch64/cpu.S +@@ -7,5 +7,15 @@ + .global _start + + _start: +-1: wfe // Wait for event +- b 1b // In case an event happened, jump back to 1 ++ mrs x1, mpidr_el1 // Read Multiprocessor Affinity Register ++ and x1, x1, #3 // Clear all bits except [1:0], which hold core id ++ cbz x1, 2f // Jump to label 2 if we are core 0 ++1: wfe // Wait for event ++ b 1b // In case an event happened, jump back to 1 ++2: // If we are here, we are core0 ++ ldr x1, =_start // Load address of function "_start()" ++ mov sp, x1 // Set start of stack to before our code, aka first ++ // address before "_start()" ++ bl runtime_init // Jump to the "runtime_init()" kernel function ++ b 1b // We should never reach here. But just in case, ++ // park this core aswell + +diff -uNr 01_wait_forever/src/bsp/raspberrypi/link.ld 02_runtime_init/src/bsp/raspberrypi/link.ld +--- 01_wait_forever/src/bsp/raspberrypi/link.ld ++++ 02_runtime_init/src/bsp/raspberrypi/link.ld +@@ -13,5 +13,24 @@ + *(.text._start) *(.text*) + } + ++ .rodata : ++ { ++ *(.rodata*) ++ } ++ ++ .data : ++ { ++ *(.data*) ++ } ++ ++ /* Section is zeroed in u64 chunks, align start and end to 8 bytes */ ++ .bss ALIGN(8): ++ { ++ __bss_start = .; ++ *(.bss*); ++ . = ALIGN(8); ++ __bss_end = .; ++ } ++ + /DISCARD/ : { *(.comment*) } + } + +diff -uNr 01_wait_forever/src/main.rs 02_runtime_init/src/main.rs +--- 01_wait_forever/src/main.rs ++++ 02_runtime_init/src/main.rs +@@ -97,10 +97,20 @@ + #![no_main] + #![no_std] + +-// `mod cpu` provides the `_start()` function, the first function to run. ++// `mod cpu` provides the `_start()` function, the first function to run. `_start()` then calls ++// `runtime_init()`, which jumps to `kernel_init()`. + + mod bsp; + mod cpu; ++mod memory; + mod panic_wait; ++mod runtime_init; + +-// Kernel code coming next tutorial. ++/// Early init code. ++/// ++/// # Safety ++/// ++/// - Only a single core must be active and running this function. ++unsafe fn kernel_init() -> ! { ++ panic!() ++} + +diff -uNr 01_wait_forever/src/memory.rs 02_runtime_init/src/memory.rs +--- 01_wait_forever/src/memory.rs ++++ 02_runtime_init/src/memory.rs +@@ -0,0 +1,29 @@ ++// SPDX-License-Identifier: MIT OR Apache-2.0 ++// ++// Copyright (c) 2018-2020 Andre Richter ++ ++//! Memory Management. ++ ++use core::ops::Range; ++ ++//-------------------------------------------------------------------------------------------------- ++// Public Code ++//-------------------------------------------------------------------------------------------------- ++ ++/// Zero out a memory region. ++/// ++/// # Safety ++/// ++/// - `range.start` and `range.end` must be valid. ++/// - `range.start` and `range.end` must be `T` aligned. ++pub unsafe fn zero_volatile(range: Range<*mut T>) ++where ++ T: From, ++{ ++ let mut ptr = range.start; ++ ++ while ptr < range.end { ++ core::ptr::write_volatile(ptr, T::from(0)); ++ ptr = ptr.offset(1); ++ } ++} + +diff -uNr 01_wait_forever/src/runtime_init.rs 02_runtime_init/src/runtime_init.rs +--- 01_wait_forever/src/runtime_init.rs ++++ 02_runtime_init/src/runtime_init.rs +@@ -0,0 +1,58 @@ ++// SPDX-License-Identifier: MIT OR Apache-2.0 ++// ++// Copyright (c) 2018-2020 Andre Richter ++ ++//! Rust runtime initialization code. ++ ++use crate::memory; ++use core::ops::Range; ++ ++//-------------------------------------------------------------------------------------------------- ++// Private Code ++//-------------------------------------------------------------------------------------------------- ++ ++/// Return the range spanning the .bss section. ++/// ++/// # Safety ++/// ++/// - The symbol-provided addresses must be valid. ++/// - The symbol-provided addresses must be usize aligned. ++unsafe fn bss_range() -> Range<*mut usize> { ++ extern "C" { ++ // Boundaries of the .bss section, provided by linker script symbols. ++ static mut __bss_start: usize; ++ static mut __bss_end: usize; ++ } ++ ++ Range { ++ start: &mut __bss_start, ++ end: &mut __bss_end, ++ } ++} ++ ++/// Zero out the .bss section. ++/// ++/// # Safety ++/// ++/// - Must only be called pre `kernel_init()`. ++#[inline(always)] ++unsafe fn zero_bss() { ++ memory::zero_volatile(bss_range()); ++} ++ ++//-------------------------------------------------------------------------------------------------- ++// Public Code ++//-------------------------------------------------------------------------------------------------- ++ ++/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel ++/// init code. ++/// ++/// # Safety ++/// ++/// - Only a single core must be active and running this function. ++#[no_mangle] ++pub unsafe extern "C" fn runtime_init() -> ! { ++ zero_bss(); ++ ++ crate::kernel_init() ++} + +``` diff --git a/README.CN.md b/README.CN.md index 7617a6b8..c9c661e8 100644 --- a/README.CN.md +++ b/README.CN.md @@ -25,7 +25,7 @@ _带上我最诚挚的问候,
Andre ([@andre-richter])_ - 每篇教程都包含一个独立可引导的二进制内核文件。 - 每篇新的教程都在之前的基础上扩展。 - 每篇教程的指南里面都有一个简短的章节来总结新增的代码和功能,也会展示源代码的区别,方便检查和同步。 -- 部分教程中有除了`tl;dr`章节外还有非常详细、具体的介绍。长期计划是所有的教程都会有详细的文字说明。但是现在我认为教程独特的地方是`tl;dr`和`diff`还不够详细。 +- 部分教程中有除了`tl;dr`章节外还有非常详细、具体的介绍。长期计划是所有的教程都会有详细的文字说明。但是目前只有那些我认为教程的`tl;dr`和`diff`还不够详细的章节会详细说明。 - 教程中所用的代码可以在**树莓派 3 和 4**上运行。 - 教程的第一到五章是基础内容,只能运行在`QEMU`上。 - 到了[第六章]时(06_drivers_gpio_uart),你可以在树莓派上加载和运行内核并通过`UART`来观察输出结果。