Skip to main content

user_lib/
lib.rs

1#![no_std]
2//!
3//! 教程阅读建议:
4//!
5//! - `_start` 展示用户程序最小运行时(初始化控制台、堆、调用 main);
6//! - 其余辅助函数(sleep/pipe_*)展示了常见 syscall 组合用法。
7
8mod heap;
9
10extern crate alloc;
11
12use tg_console::log;
13
14pub use tg_console::{print, println};
15pub use tg_syscall::*;
16
17#[cfg(feature = "tangram")]
18pub mod tangram;
19
20/// Query framebuffer dimensions from kernel.
21/// Returns (width, height).
22pub fn fb_info() -> (u32, u32) {
23    let packed: usize;
24    unsafe {
25        core::arch::asm!(
26            "ecall",
27            in("a7") 2000usize,
28            lateout("a0") packed,
29        );
30    }
31    let width = (packed >> 32) as u32;
32    let height = packed as u32;
33    (width, height)
34}
35
36/// Write a rectangular BGRA pixel region to the kernel framebuffer.
37/// The kernel copies the data and flushes the display.
38pub fn fb_write(x: u32, y: u32, w: u32, h: u32, data: *const u8) -> isize {
39    let ret: isize;
40    unsafe {
41        core::arch::asm!(
42            "ecall",
43            in("a7") 2001usize,
44            inlateout("a0") x as usize => ret,
45            in("a1") y as usize,
46            in("a2") w as usize,
47            in("a3") h as usize,
48            in("a4") data as usize,
49        );
50    }
51    ret
52}
53
54#[unsafe(no_mangle)]
55#[unsafe(link_section = ".text.entry")]
56pub extern "C" fn _start() -> ! {
57    // 用户态运行时初始化顺序与内核类似:先 I/O,再堆,再进入 main。
58    tg_console::init_console(&Console);
59    tg_console::set_log_level(option_env!("LOG"));
60    heap::init();
61
62    unsafe extern "C" {
63        fn main() -> i32;
64    }
65
66    // SAFETY: main 函数由用户程序提供,链接器保证其存在且符合 C ABI
67    exit(unsafe { main() });
68    unreachable!()
69}
70
71#[panic_handler]
72fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
73    let err = panic_info.message();
74    if let Some(location) = panic_info.location() {
75        log::error!("Panicked at {}:{}, {err}", location.file(), location.line());
76    } else {
77        log::error!("Panicked: {err}");
78    }
79    exit(1);
80    unreachable!()
81}
82
83pub fn getchar() -> u8 {
84    let mut c = [0u8; 1];
85    read(STDIN, &mut c);
86    c[0]
87}
88
89struct Console;
90
91impl tg_console::Console for Console {
92    #[inline]
93    fn put_char(&self, c: u8) {
94        tg_syscall::write(STDOUT, &[c]);
95    }
96
97    #[inline]
98    fn put_str(&self, s: &str) {
99        tg_syscall::write(STDOUT, s.as_bytes());
100    }
101}
102
103pub fn sleep(period_ms: usize) {
104    // 轮询时钟 + 主动让出 CPU 的教学实现,便于理解 time/yield 系统调用协作。
105    let mut time: TimeSpec = TimeSpec::ZERO;
106    clock_gettime(ClockId::CLOCK_MONOTONIC, &mut time as *mut _ as _);
107    let time = time + TimeSpec::from_millsecond(period_ms);
108    loop {
109        let mut now: TimeSpec = TimeSpec::ZERO;
110        clock_gettime(ClockId::CLOCK_MONOTONIC, &mut now as *mut _ as _);
111        if now > time {
112            break;
113        }
114        sched_yield();
115    }
116}
117
118pub fn get_time() -> isize {
119    let mut time: TimeSpec = TimeSpec::ZERO;
120    clock_gettime(ClockId::CLOCK_MONOTONIC, &mut time as *mut _ as _);
121    (time.tv_sec * 1000 + time.tv_nsec / 1_000_000) as isize
122}
123
124pub fn trace_read(ptr: *const u8) -> Option<u8> {
125    let ret = trace(0, ptr as usize, 0);
126    if ret >= 0 && ret <= 255 {
127        Some(ret as u8)
128    } else {
129        None
130    }
131}
132
133pub fn trace_write(ptr: *const u8, value: u8) -> isize {
134    trace(1, ptr as usize, value as usize)
135}
136
137pub fn count_syscall(syscall_id: usize) -> isize {
138    trace(2, syscall_id, 0)
139}
140
141/// 从管道读取数据
142/// 返回实际读取的总字节数,负数表示错误
143pub fn pipe_read(pipe_fd: usize, buffer: &mut [u8]) -> isize {
144    let mut total_read = 0usize;
145    let len = buffer.len();
146    loop {
147        if total_read >= len {
148            return total_read as isize;
149        }
150        let ret = read(pipe_fd, &mut buffer[total_read..]);
151        if ret == -2 {
152            // 暂时无数据,让出 CPU 后重试
153            sched_yield();
154            continue;
155        } else if ret == 0 {
156            // EOF,写端关闭
157            return total_read as isize;
158        } else if ret < 0 {
159            // 其他错误
160            return ret;
161        } else {
162            total_read += ret as usize;
163        }
164    }
165}
166
167/// 向管道写入数据
168/// 返回实际写入的总字节数,负数表示错误
169pub fn pipe_write(pipe_fd: usize, buffer: &[u8]) -> isize {
170    let mut total_write = 0usize;
171    let len = buffer.len();
172    loop {
173        if total_write >= len {
174            return total_write as isize;
175        }
176        let ret = write(pipe_fd, &buffer[total_write..]);
177        if ret == -2 {
178            // 缓冲区满,让出 CPU 后重试
179            sched_yield();
180            continue;
181        } else if ret < 0 {
182            // 其他错误
183            return ret;
184        } else {
185            total_write += ret as usize;
186        }
187    }
188}