Skip to main content

user_lib/
lib.rs

1#![no_std]
2//!
3//! 教程阅读建议:
4//!
5//! - `_start` 展示用户程序最小运行时(初始化控制台、堆、调用 main);
6//! - 其余辅助函数(sleep/pipe_*)展示了常见 syscall 组合用法。
7
8mod heap;
9mod tangram;
10
11extern crate alloc;
12
13use alloc::alloc::alloc;
14use core::alloc::Layout;
15use core::sync::atomic::{AtomicUsize, Ordering};
16use tg_console::log;
17
18pub use tg_console::{print, println};
19pub use tg_syscall::*;
20
21const SYSCALL_FRAMEBUFFER: usize = 0x1000_0001;
22const SYSCALL_FRAMEBUFFER_FLUSH: usize = 0x1000_0002;
23const TG_ALLOC_ARENA_SIZE: usize = 16 << 20;
24
25#[repr(align(16))]
26struct TgAllocArena([u8; TG_ALLOC_ARENA_SIZE]);
27
28static mut TG_ALLOC_ARENA: TgAllocArena = TgAllocArena([0; TG_ALLOC_ARENA_SIZE]);
29static TG_ALLOC_OFFSET: AtomicUsize = AtomicUsize::new(0);
30
31#[unsafe(no_mangle)]
32#[unsafe(link_section = ".text.entry")]
33pub extern "C" fn _start() -> ! {
34    // 用户态运行时初始化顺序与内核类似:先 I/O,再堆,再进入 main。
35    tg_console::init_console(&Console);
36    tg_console::set_log_level(option_env!("LOG"));
37    heap::init();
38
39    unsafe extern "C" {
40        fn main() -> i32;
41    }
42
43    // SAFETY: main 函数由用户程序提供,链接器保证其存在且符合 C ABI
44    exit(unsafe { main() });
45    unreachable!()
46}
47
48#[panic_handler]
49fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
50    let err = panic_info.message();
51    if let Some(location) = panic_info.location() {
52        log::error!("Panicked at {}:{}, {err}", location.file(), location.line());
53    } else {
54        log::error!("Panicked: {err}");
55    }
56    exit(1);
57    unreachable!()
58}
59
60pub fn getchar() -> u8 {
61    getchar_blocking()
62}
63
64#[cfg(target_arch = "riscv64")]
65pub fn framebuffer_info() -> Option<(*mut u8, usize, usize, usize)> {
66    let fb_ptr: isize;
67    let fb_len: usize;
68    let width: usize;
69    let height: usize;
70    unsafe {
71        core::arch::asm!(
72            "ecall",
73            inlateout("a0") 0isize => fb_ptr,
74            lateout("a1") fb_len,
75            lateout("a2") width,
76            lateout("a3") height,
77            in("a7") SYSCALL_FRAMEBUFFER,
78        );
79    }
80    if fb_ptr <= 0 || fb_len == 0 || width == 0 || height == 0 {
81        None
82    } else {
83        Some((fb_ptr as *mut u8, fb_len, width, height))
84    }
85}
86
87#[cfg(not(target_arch = "riscv64"))]
88pub fn framebuffer_info() -> Option<(*mut u8, usize, usize, usize)> {
89    None
90}
91
92#[cfg(target_arch = "riscv64")]
93pub fn framebuffer_flush() -> isize {
94    let ret: isize;
95    unsafe {
96        core::arch::asm!(
97            "ecall",
98            inlateout("a0") 0isize => ret,
99            in("a7") SYSCALL_FRAMEBUFFER_FLUSH,
100        );
101    }
102    ret
103}
104
105#[cfg(not(target_arch = "riscv64"))]
106pub fn framebuffer_flush() -> isize {
107    -1
108}
109
110pub fn render_block(block: usize) -> isize {
111    let Some((fb_ptr, fb_len, width, height)) = framebuffer_info() else {
112        return -1;
113    };
114
115    let used_len = width
116        .checked_mul(height)
117        .and_then(|pixels| pixels.checked_mul(4))
118        .unwrap_or(0);
119    if used_len == 0 || used_len > fb_len {
120        return -1;
121    }
122
123    let framebuffer = unsafe { core::slice::from_raw_parts_mut(fb_ptr, used_len) };
124    tangram::render_block_by_index(framebuffer, width, height, block);
125    framebuffer_flush()
126}
127
128pub fn getchar_poll() -> Option<u8> {
129    let mut c = [0u8; 1];
130    match read(STDIN, &mut c) {
131        1 => Some(c[0]),
132        _ => None,
133    }
134}
135
136pub fn getchar_blocking() -> u8 {
137    loop {
138        if let Some(c) = getchar_poll() {
139            return c;
140        }
141        sched_yield();
142    }
143}
144
145struct Console;
146
147impl tg_console::Console for Console {
148    #[inline]
149    fn put_char(&self, c: u8) {
150        tg_syscall::write(STDOUT, &[c]);
151    }
152
153    #[inline]
154    fn put_str(&self, s: &str) {
155        tg_syscall::write(STDOUT, s.as_bytes());
156    }
157}
158
159pub fn sleep(period_ms: usize) {
160    // 轮询时钟 + 主动让出 CPU 的教学实现,便于理解 time/yield 系统调用协作。
161    let mut time: TimeSpec = TimeSpec::ZERO;
162    clock_gettime(ClockId::CLOCK_MONOTONIC, &mut time as *mut _ as _);
163    let time = time + TimeSpec::from_millsecond(period_ms);
164    loop {
165        let mut now: TimeSpec = TimeSpec::ZERO;
166        clock_gettime(ClockId::CLOCK_MONOTONIC, &mut now as *mut _ as _);
167        if now > time {
168            break;
169        }
170        sched_yield();
171    }
172}
173
174pub fn get_time() -> isize {
175    let mut time: TimeSpec = TimeSpec::ZERO;
176    clock_gettime(ClockId::CLOCK_MONOTONIC, &mut time as *mut _ as _);
177    (time.tv_sec * 1000 + time.tv_nsec / 1_000_000) as isize
178}
179
180#[unsafe(no_mangle)]
181pub extern "C" fn tg_sys_open(path: *const u8, flags: i32) -> i32 {
182    if path.is_null() {
183        return -1;
184    }
185    let mut len = 0usize;
186    unsafe {
187        while *path.add(len) != 0 {
188            len += 1;
189        }
190    }
191    let raw = unsafe { core::slice::from_raw_parts(path, len) };
192    let mut start = 0usize;
193
194    while start + 1 < raw.len() && raw[start] == b'.' && raw[start + 1] == b'/' {
195        start += 2;
196    }
197    while start < raw.len() && (raw[start] == b'/' || raw[start] == b'\\') {
198        start += 1;
199    }
200
201    let mut path_bytes = &raw[start..];
202    if let Some(pos) = path_bytes.iter().rposition(|&b| b == b'/' || b == b'\\') {
203        path_bytes = &path_bytes[pos + 1..];
204    }
205    if path_bytes.is_empty() {
206        return -1;
207    }
208
209    let path_str = unsafe { core::str::from_utf8_unchecked(path_bytes) };
210    open(path_str, OpenFlags::from_bits(flags as u32).unwrap_or(OpenFlags::RDONLY)) as i32
211}
212
213#[unsafe(no_mangle)]
214pub extern "C" fn tg_sys_close(fd: i32) -> i32 {
215    if fd < 0 {
216        return -1;
217    }
218    close(fd as usize) as i32
219}
220
221#[unsafe(no_mangle)]
222pub extern "C" fn tg_sys_read(fd: i32, buf: *mut u8, len: usize) -> i32 {
223    if fd < 0 || buf.is_null() {
224        return -1;
225    }
226    let data = unsafe { core::slice::from_raw_parts_mut(buf, len) };
227    read(fd as usize, data) as i32
228}
229
230#[unsafe(no_mangle)]
231pub extern "C" fn tg_sys_write(fd: i32, buf: *const u8, len: usize) -> i32 {
232    
233    if fd < 0 || buf.is_null() {
234        return -1;
235    }
236    let data = unsafe { core::slice::from_raw_parts(buf, len) };
237    write(fd as usize, data) as i32
238}
239
240#[unsafe(no_mangle)]
241pub extern "C" fn tg_sys_unlink(path: *const u8) -> i32 {
242    if path.is_null() {
243        return -1;
244    }
245    let mut len = 0usize;
246    unsafe {
247        while *path.add(len) != 0 {
248            len += 1;
249        }
250    }
251    let raw = unsafe { core::slice::from_raw_parts(path, len) };
252    let mut start = 0usize;
253
254    while start + 1 < raw.len() && raw[start] == b'.' && raw[start + 1] == b'/' {
255        start += 2;
256    }
257    while start < raw.len() && (raw[start] == b'/' || raw[start] == b'\\') {
258        start += 1;
259    }
260
261    let mut path_bytes = &raw[start..];
262    if let Some(pos) = path_bytes.iter().rposition(|&b| b == b'/' || b == b'\\') {
263        path_bytes = &path_bytes[pos + 1..];
264    }
265    if path_bytes.is_empty() {
266        return -1;
267    }
268
269    let path_str = unsafe { core::str::from_utf8_unchecked(path_bytes) };
270    unlink(path_str) as i32
271}
272
273#[unsafe(no_mangle)]
274pub extern "C" fn tg_alloc(size: usize) -> *mut u8 {
275    let req = size.max(1);
276    let align_mask = 8usize - 1;
277
278    loop {
279        let current = TG_ALLOC_OFFSET.load(Ordering::Relaxed);
280        let aligned = (current + align_mask) & !align_mask;
281        let Some(next) = aligned.checked_add(req) else {
282            return core::ptr::null_mut();
283        };
284        if next > TG_ALLOC_ARENA_SIZE {
285            return core::ptr::null_mut();
286        }
287
288        if TG_ALLOC_OFFSET
289            .compare_exchange(current, next, Ordering::SeqCst, Ordering::Relaxed)
290            .is_ok()
291        {
292            let base = core::ptr::addr_of_mut!(TG_ALLOC_ARENA) as *mut u8;
293            unsafe {
294                return base.add(aligned);
295            }
296        }
297    }
298}
299
300pub fn trace_read(ptr: *const u8) -> Option<u8> {
301    let ret = trace(0, ptr as usize, 0);
302    if ret >= 0 && ret <= 255 {
303        Some(ret as u8)
304    } else {
305        None
306    }
307}
308
309pub fn trace_write(ptr: *const u8, value: u8) -> isize {
310    trace(1, ptr as usize, value as usize)
311}
312
313pub fn count_syscall(syscall_id: usize) -> isize {
314    trace(2, syscall_id, 0)
315}
316
317/// 从管道读取数据
318/// 返回实际读取的总字节数,负数表示错误
319pub fn pipe_read(pipe_fd: usize, buffer: &mut [u8]) -> isize {
320    let mut total_read = 0usize;
321    let len = buffer.len();
322    loop {
323        if total_read >= len {
324            return total_read as isize;
325        }
326        let ret = read(pipe_fd, &mut buffer[total_read..]);
327        if ret == -2 {
328            // 暂时无数据,让出 CPU 后重试
329            sched_yield();
330            continue;
331        } else if ret == 0 {
332            // EOF,写端关闭
333            return total_read as isize;
334        } else if ret < 0 {
335            // 其他错误
336            return ret;
337        } else {
338            total_read += ret as usize;
339        }
340    }
341}
342
343/// 向管道写入数据
344/// 返回实际写入的总字节数,负数表示错误
345pub fn pipe_write(pipe_fd: usize, buffer: &[u8]) -> isize {
346    let mut total_write = 0usize;
347    let len = buffer.len();
348    loop {
349        if total_write >= len {
350            return total_write as isize;
351        }
352        let ret = write(pipe_fd, &buffer[total_write..]);
353        if ret == -2 {
354            // 缓冲区满,让出 CPU 后重试
355            sched_yield();
356            continue;
357        } else if ret < 0 {
358            // 其他错误
359            return ret;
360        } else {
361            total_write += ret as usize;
362        }
363    }
364}