|
1 | | -# 第一章 |
2 | | - |
3 | | -第一章旨在展示一个尽量简单的**特权态裸机应用程序**: |
4 | | - |
5 | | -- 只有[一个文件](src/main.rs); |
6 | | -- 链接脚本在 [build.rs](build.rs),以免增加依赖; |
7 | | -- 只依赖 [*sbi-rt*](https://crates.io/crates/sbi-rt) 以获得封装好的 SBI 调用; |
8 | | -- 这个程序被 SEE 引导,工作在 S 态; |
9 | | -- 这个程序不需要环境: |
10 | | - - 从汇编进入并为 Rust 准备栈; |
11 | | - - 依赖 SBI 提供的 `legacy::console_putchar` 打印 `Hello, world!`; |
12 | | - - 依赖 SBI 提供的 `system_reset` 调用关机; |
13 | | - |
14 | | -它不配被称作一个操作系统,因为它没有操作(硬件),也不构造(执行用户程序的)系统; |
15 | | - |
16 | | -## sbi-rt |
17 | | - |
18 | | -这个库就是 kernel 的 libc。 |
19 | | - |
20 | | -它根据 [SBI 标准](https://github.com/riscv-non-isa/riscv-sbi-doc)封装了一系列函数,通过 `ecall` 命令调用 SBI 提供的响应功能。本章需要使用 `legacy::console_putchar` 向控制台打印字符,以及 `system_reset` 在程序运行完后关机。 |
21 | | - |
22 | | -## 定制链接脚本 |
23 | | - |
24 | | -build.rs 的用法见[文档](https://doc.rust-lang.org/cargo/reference/build-scripts.html)。这个定制的链接脚本是特殊的: |
25 | | - |
26 | | -```ld |
27 | | -OUTPUT_ARCH(riscv) |
28 | | -SECTIONS { |
29 | | - .text 0x80200000 : { |
30 | | - *(.text.entry) |
31 | | - *(.text .text.*) |
32 | | - } |
33 | | - .rodata : { |
34 | | - *(.rodata .rodata.*) |
35 | | - *(.srodata .srodata.*) |
36 | | - } |
37 | | - .data : { |
38 | | - *(.data .data.*) |
39 | | - *(.sdata .sdata.*) |
40 | | - } |
41 | | - .bss : { |
42 | | - *(.bss.uninit) |
43 | | - *(.bss .bss.*) |
44 | | - *(.sbss .sbss.*) |
45 | | - } |
| 1 | +# 第一章:应用程序与基本执行环境 |
| 2 | + |
| 3 | +本章实现了一个最简单的 RISC-V S 态裸机程序,展示操作系统的最小执行环境。 |
| 4 | + |
| 5 | +## 功能概述 |
| 6 | + |
| 7 | +- 使用 `_start` 裸函数汇编入口,初始化栈并跳转到 Rust |
| 8 | +- 通过 SBI 调用打印 `Hello, world!` |
| 9 | +- 调用 SBI 关机 |
| 10 | +- 在 `build.rs` 中生成链接脚本,将 `.text.entry` 放置在 `0x8020_0000`,确保被 SBI 正确引导 |
| 11 | + |
| 12 | +## 裸函数入口 |
| 13 | + |
| 14 | +本章使用 `#[naked]` 属性定义裸函数 `_start` 作为程序入口。裸函数不会生成函数序言和尾声,可以在没有栈的情况下执行: |
| 15 | + |
| 16 | +```rust |
| 17 | +#[unsafe(naked)] |
| 18 | +#[link_section = ".text.entry"] |
| 19 | +unsafe extern "C" fn _start() -> ! { |
| 20 | + core::arch::naked_asm!( |
| 21 | + "la sp, {stack} + {stack_size}", |
| 22 | + "j {main}", |
| 23 | + // ... |
| 24 | + ) |
46 | 25 | } |
47 | 26 | ``` |
48 | 27 |
|
49 | | -1. 为了被引导,它的 `.text` 在最前面。一般是 `.rodata` 在最前面。`.text` 的最前面是 `.text.entry`,有且只有一个汇编入口放在这个节,实现引导; |
50 | | -2. 正常情况下,裸机应用程序需要清除自己的 `.bss` 节,所以需要定义全局符号以便动态定位 `.bss`。但这一章的程序并不依赖 清空的 `.bss`,所以没有导出符号。`.bss` 本身仍然需要,因为栈会放在里面。 |
| 28 | +入口函数完成两件事:设置栈指针 `sp`,然后跳转到 Rust 主函数。链接脚本确保 `.text.entry` 位于 `0x8020_0000`,这是 SBI 引导后的跳转地址。 |
| 29 | + |
| 30 | +## Dependencies |
| 31 | + |
| 32 | +| 依赖 | 说明 | |
| 33 | +|------|------| |
| 34 | +| `tg-sbi` | SBI 调用封装库,支持标准 RustSBI 模式和 nobios 模式 | |
| 35 | + |
| 36 | +## Features |
| 37 | + |
| 38 | +| Feature | 说明 | |
| 39 | +|---------|------| |
| 40 | +| `nobios` | 无需外部 SBI 实现,直接从 QEMU `-bios none` 模式启动 | |
51 | 41 |
|
52 | | -## 工作流程解读 |
| 42 | +## License |
53 | 43 |
|
54 | | -1. SBI 初始化完成后,将固定跳转到 0x8020_0000 地址; |
55 | | -2. 根据链接脚本,汇编入口函数被放置在这个地址。它叫做 `_start`,这个名字是特殊的!GNU LD 及兼容其脚本的链接器会将这个名字认为是默认的入口,否则需要指定。这个函数是一个 rust 裸函数([`#[naked]`](https://github.com/rust-lang/rust/issues/90957)),编译器不会为它添加任何序言和尾声,因此可以在没有栈的情况下执行。它将栈指针指向预留的栈空间,然后跳转到 `rust_main` 函数; |
56 | | -3. `rust_main` 函数在一个最简单的循环打印调用 sbi 打印 `Hello, world!` 字符串,然后关机。 |
| 44 | +Licensed under either of MIT license or Apache License, Version 2.0 at your option. |
0 commit comments