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