Skip to main content

tg_rcore_tutorial_linker/
lib.rs

1//! 这个板块为内核提供链接脚本的文本,以及依赖于定制链接脚本的功能。
2//!
3//! build.rs 文件可依赖此板块,并将 [`SCRIPT`] 文本常量写入链接脚本文件:
4//!
5//! ```rust
6//! use std::{env, fs, path::PathBuf};
7//!
8//! let ld = &PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("linker.ld");
9//! fs::write(ld, linker::SCRIPT).unwrap();
10//!
11//! println!("cargo:rerun-if-changed=build.rs");
12//! println!("cargo:rustc-link-arg=-T{}", ld.display());
13//! ```
14//!
15//! 内核使用 [`boot0`] 宏定义内核启动栈和高级语言入口:
16//!
17//! ```rust
18//! linker::boot0!(rust_main; stack = 4 * 4096);
19//! ```
20//!
21//! 内核所在内核区域定义成 4 个部分([`KernelRegionTitle`]):
22//!
23//! 1. 代码段
24//! 2. 只读数据段
25//! 3. 数据段
26//! 4. 启动数据段
27//!
28//! 启动数据段放在最后,以便启动完成后换栈。届时可放弃启动数据段,将其加入动态内存区。
29//!
30//! 用 [`KernelLayout`] 结构体定位、保存和访问内核内存布局。
31//!
32//! 教程阅读建议:
33//!
34//! - 初学者先看常量 `SCRIPT` / `NOBIOS_SCRIPT`,理解链接布局;
35//! - 再看 `boot0!` 宏,理解 `_start -> rust_main` 的启动桥接;
36//! - 最后看 `KernelLayout`,理解章节代码如何读取段边界和清零 `.bss`。
37
38#![no_std]
39#![deny(warnings, missing_docs)]
40
41mod app;
42
43pub use app::{AppIterator, AppMeta};
44
45/// 链接脚本。
46pub const SCRIPT: &[u8] = b"\
47OUTPUT_ARCH(riscv)
48SECTIONS {
49    .text 0x80200000 : {
50        __start = .;
51        *(.text.entry)
52        *(.text .text.*)
53    }
54    .rodata : ALIGN(4K) {
55        __rodata = .;
56        *(.rodata .rodata.*)
57        *(.srodata .srodata.*)
58    }
59    .data : ALIGN(4K) {
60        __data = .;
61        *(.data .data.*)
62        *(.sdata .sdata.*)
63    }
64    .bss : ALIGN(8) {
65        __sbss = .;
66        *(.bss .bss.*)
67        *(.sbss .sbss.*)
68        __ebss = .;
69    }
70    .boot : ALIGN(4K) {
71        __boot = .;
72        KEEP(*(.boot.stack))
73    }
74    __end = .;
75}";
76
77/// 链接脚本(nobios 模式)。
78///
79/// M-Mode 入口在 0x80000000,S-Mode 内核在 0x80200000。
80///
81/// 对应关系:
82/// - `tg-sbi` 的 `m_entry.asm` 放在 M 态区域;
83/// - 章节内核 `.text.entry` 在 S 态区域启动。
84pub const NOBIOS_SCRIPT: &[u8] = b"\
85OUTPUT_ARCH(riscv)
86ENTRY(_m_start)
87M_BASE_ADDRESS = 0x80000000;
88S_BASE_ADDRESS = 0x80200000;
89
90SECTIONS {
91    . = M_BASE_ADDRESS;
92
93    .text.m_entry : {
94        *(.text.m_entry)
95    }
96
97    .text.m_trap : {
98        *(.text.m_trap)
99    }
100
101    .bss.m_stack : {
102        *(.bss.m_stack)
103    }
104
105    .bss.m_data : {
106        *(.bss.m_data)
107    }
108
109    . = S_BASE_ADDRESS;
110
111    .text : {
112        __start = .;
113        *(.text.entry)
114        *(.text .text.*)
115    }
116    .rodata : ALIGN(4K) {
117        __rodata = .;
118        *(.rodata .rodata.*)
119        *(.srodata .srodata.*)
120    }
121    .data : ALIGN(4K) {
122        __data = .;
123        *(.data .data.*)
124        *(.sdata .sdata.*)
125    }
126    .bss : ALIGN(8) {
127        __sbss = .;
128        *(.bss .bss.*)
129        *(.sbss .sbss.*)
130        __ebss = .;
131    }
132    .boot : ALIGN(4K) {
133        __boot = .;
134        KEEP(*(.boot.stack))
135    }
136    __end = .;
137}";
138
139/// 定义内核入口。
140///
141/// 将设置一个启动栈,并在启动栈上调用高级语言入口。
142///
143/// # Safety
144///
145/// 此宏生成的 `_start` 函数是一个裸函数,作为内核的入口点。
146/// 它会:
147/// - 设置栈指针到 `__end`(由链接脚本定义)
148/// - 跳转到指定的入口函数
149///
150/// 调用者需要确保链接脚本正确定义了相关符号。
151#[macro_export]
152macro_rules! boot0 {
153    ($entry:ident; stack = $stack:expr) => {
154        /// 内核入口点。
155        ///
156        /// # Safety
157        ///
158        /// 这是一个裸函数,由 bootloader 直接调用。
159        /// 调用时 CPU 处于 M 模式或 S 模式,需要正确设置栈指针后才能执行 Rust 代码。
160        #[cfg(target_arch = "riscv64")]
161        #[unsafe(naked)]
162        #[no_mangle]
163        #[link_section = ".text.entry"]
164        unsafe extern "C" fn _start() -> ! {
165            #[link_section = ".boot.stack"]
166            static mut STACK: [u8; $stack] = [0u8; $stack];
167
168            // SAFETY: 设置栈指针并跳转到高级语言入口。
169            // __end 由链接脚本定义,指向启动栈的末尾。
170            // 注意:这里并不直接“调用”函数,而是跳转,避免无意义的返回路径。
171            core::arch::naked_asm!(
172                "la sp, __end",
173                "j  {main}",
174                main = sym rust_main,
175            )
176        }
177
178        #[cfg(not(target_arch = "riscv64"))]
179        #[no_mangle]
180        unsafe extern "C" fn _start() -> ! {
181            unimplemented!("_start() is only supported on riscv64")
182        }
183    };
184}
185
186/// 内核地址信息。
187#[derive(Debug)]
188pub struct KernelLayout {
189    text: usize,
190    rodata: usize,
191    data: usize,
192    sbss: usize,
193    ebss: usize,
194    boot: usize,
195    end: usize,
196}
197
198impl KernelLayout {
199    /// 非零初始化,避免 bss。
200    pub const INIT: Self = Self {
201        text: usize::MAX,
202        rodata: usize::MAX,
203        data: usize::MAX,
204        sbss: usize::MAX,
205        ebss: usize::MAX,
206        boot: usize::MAX,
207        end: usize::MAX,
208    };
209
210    /// 定位内核布局。
211    #[inline]
212    pub fn locate() -> Self {
213        unsafe extern "C" {
214            fn __start();
215            fn __rodata();
216            fn __data();
217            fn __sbss();
218            fn __ebss();
219            fn __boot();
220            fn __end();
221        }
222
223        Self {
224            text: __start as *const () as _,
225            rodata: __rodata as *const () as _,
226            data: __data as *const () as _,
227            sbss: __sbss as *const () as _,
228            ebss: __ebss as *const () as _,
229            boot: __boot as *const () as _,
230            end: __end as *const () as _,
231        }
232    }
233
234    /// 内核起始地址。
235    #[inline]
236    pub const fn start(&self) -> usize {
237        self.text
238    }
239
240    /// 内核结尾地址。
241    #[inline]
242    pub const fn end(&self) -> usize {
243        self.end
244    }
245
246    /// 内核静态二进制长度。
247    #[inline]
248    pub const fn len(&self) -> usize {
249        self.end - self.text
250    }
251
252    /// 清零 .bss 段。
253    ///
254    /// # Safety
255    ///
256    /// 调用者必须确保:
257    /// - 此函数在访问任何 .bss 段中的静态变量之前调用
258    /// - .bss 段的地址范围(`sbss` 到 `ebss`)是有效的
259    /// - 此函数只被调用一次
260    #[inline]
261    pub unsafe fn zero_bss(&self) {
262        let mut ptr = self.sbss as *mut u8;
263        let end = self.ebss as *mut u8;
264        while ptr < end {
265            // SAFETY: ptr 在 [sbss, ebss) 范围内,这是有效的 .bss 段内存。
266            // 使用 volatile write 确保多核场景下其他核能看到写入。
267            unsafe { ptr.write_volatile(0) };
268            // SAFETY: ptr 仍位于同一有效 .bss 地址区间内,单步向后移动 1 字节。
269            ptr = unsafe { ptr.add(1) };
270        }
271    }
272
273    /// 内核区段迭代器。
274    #[inline]
275    pub fn iter(&self) -> KernelRegionIterator<'_> {
276        KernelRegionIterator {
277            layout: self,
278            next: Some(KernelRegionTitle::Text),
279        }
280    }
281}
282
283use core::{fmt, ops::Range};
284
285/// 内核内存分区迭代器。
286pub struct KernelRegionIterator<'a> {
287    layout: &'a KernelLayout,
288    next: Option<KernelRegionTitle>,
289}
290
291/// 内核内存分区名称。
292#[derive(Clone, Copy)]
293pub enum KernelRegionTitle {
294    /// 代码段。
295    Text,
296    /// 只读数据段。
297    Rodata,
298    /// 数据段。
299    Data,
300    /// 启动数据段。
301    Boot,
302}
303
304/// 内核内存分区。
305pub struct KernelRegion {
306    /// 分区名称。
307    pub title: KernelRegionTitle,
308    /// 分区地址范围。
309    pub range: Range<usize>,
310}
311
312impl fmt::Display for KernelRegion {
313    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314        match self.title {
315            KernelRegionTitle::Text => write!(f, ".text ----> ")?,
316            KernelRegionTitle::Rodata => write!(f, ".rodata --> ")?,
317            KernelRegionTitle::Data => write!(f, ".data ----> ")?,
318            KernelRegionTitle::Boot => write!(f, ".boot ----> ")?,
319        }
320        write!(f, "{:#10x}..{:#10x}", self.range.start, self.range.end)
321    }
322}
323
324impl Iterator for KernelRegionIterator<'_> {
325    type Item = KernelRegion;
326
327    fn next(&mut self) -> Option<Self::Item> {
328        use KernelRegionTitle::*;
329        match self.next? {
330            Text => {
331                self.next = Some(Rodata);
332                Some(KernelRegion {
333                    title: Text,
334                    range: self.layout.text..self.layout.rodata,
335                })
336            }
337            Rodata => {
338                self.next = Some(Data);
339                Some(KernelRegion {
340                    title: Rodata,
341                    range: self.layout.rodata..self.layout.data,
342                })
343            }
344            Data => {
345                self.next = Some(Boot);
346                Some(KernelRegion {
347                    title: Data,
348                    range: self.layout.data..self.layout.ebss,
349                })
350            }
351            Boot => {
352                self.next = None;
353                Some(KernelRegion {
354                    title: Boot,
355                    range: self.layout.boot..self.layout.end,
356                })
357            }
358        }
359    }
360}