Skip to main content

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