diff --git a/GNUmakefile b/GNUmakefile index 5346f36..1cc192f 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -213,7 +213,7 @@ ifeq ($(KARCH),loongarch64) iso_root -o $(IMAGE_NAME).iso endif rm -rf iso_root - qemu-system-$(KARCH) -cdrom $(IMAGE_NAME).iso -m 512M -serial stdio + qemu-system-$(KARCH) -cdrom $(IMAGE_NAME).iso -m 1G -serial stdio $(IMAGE_NAME).hdd: limine/limine kernel rm -f $(IMAGE_NAME).hdd diff --git a/assets/doom1.wad b/assets/doom1.wad new file mode 100644 index 0000000..1a58f66 Binary files /dev/null and b/assets/doom1.wad differ diff --git a/assets/helloworld.elf b/assets/helloworld.elf index 12f4335..7c3acae 100755 Binary files a/assets/helloworld.elf and b/assets/helloworld.elf differ diff --git a/build_doomgeneric.sh b/build_doomgeneric.sh index 869d42d..da37b45 100644 --- a/build_doomgeneric.sh +++ b/build_doomgeneric.sh @@ -1,6 +1,6 @@ bash build_libxunil.sh cd user/apps/doomgeneric/doomgeneric rm -r ./build -make -f Makefile.xunil +make -j16 -f Makefile.xunil cp doomgeneric ../../../../assets/doomgeneric cd ../../../.. diff --git a/build_helloworld.sh b/build_helloworld.sh new file mode 100644 index 0000000..4058150 --- /dev/null +++ b/build_helloworld.sh @@ -0,0 +1,4 @@ +bash build_libxunil.sh +cd user/apps/helloworld +bash compile.sh +cd ../../../ diff --git a/kernel/src/arch/arch.rs b/kernel/src/arch/arch.rs index 2cf19f2..c658424 100644 --- a/kernel/src/arch/arch.rs +++ b/kernel/src/arch/arch.rs @@ -1,8 +1,9 @@ #[cfg(target_arch = "x86_64")] pub use crate::arch::x86_64::paging::FRAME_ALLOCATOR_X86_64 as FRAME_ALLOCATOR; -use crate::driver::timer::TIMER; -use core::{alloc::GlobalAlloc, arch::asm}; +use crate::{driver::timer::TIMER, util::serial_print}; +use alloc::string::ToString; +use core::{alloc::GlobalAlloc, arch::asm, sync::atomic::Ordering}; use limine::response::{HhdmResponse, MemoryMapResponse}; #[cfg(target_arch = "x86_64")] @@ -50,10 +51,11 @@ pub fn idle() { } pub fn sleep(ticks: u64) { - let start = TIMER.now(); - while (TIMER.now() - start).elapsed() <= ticks { - idle(); - } + // let start = TIMER.now(); + // while start.ticks_since() < ticks { + // serial_print(start.ticks_since().to_string().as_str()); + // core::hint::spin_loop(); + // } } pub fn infinite_idle() -> ! { diff --git a/kernel/src/arch/x86_64/elf.rs b/kernel/src/arch/x86_64/elf.rs index ffb8640..b8fee5a 100644 --- a/kernel/src/arch/x86_64/elf.rs +++ b/kernel/src/arch/x86_64/elf.rs @@ -16,7 +16,7 @@ use crate::{ pub fn run_elf_x86_64(entry_point: *const u8, heap_base: u64) { let stack_base: u64 = 0x0000_7fff_0000_0000; - let page_count = 3; + let page_count = 4096; // 16 mib let page_size = 0x1000u64; let stack_top = stack_base + (page_count as u64 * page_size); diff --git a/kernel/src/arch/x86_64/heap.rs b/kernel/src/arch/x86_64/heap.rs index 90eed88..539e8b0 100644 --- a/kernel/src/arch/x86_64/heap.rs +++ b/kernel/src/arch/x86_64/heap.rs @@ -20,7 +20,7 @@ fn align_up(addr: usize, align: usize) -> usize { pub static ALLOCATOR: Locked = Locked::new(LinkedListAllocator::new()); pub const HEAP_START: usize = 0x_4444_4444_0000; -pub const HEAP_SIZE: usize = 256 * 1024 * 1024; // 256 MiB +pub const HEAP_SIZE: usize = 64 * 1024 * 1024; // 64 MiB pub struct LinkedNode { pub size: usize, @@ -54,7 +54,7 @@ impl LinkedListAllocator { fn size_align(layout: Layout) -> (usize, usize) { let layout = layout - .align_to(core::mem::align_of::()) + .align_to(16) .expect("Align to LinkedNode failed") .pad_to_align(); @@ -70,7 +70,7 @@ impl LinkedListAllocator { } unsafe fn add_free_memory_region(&mut self, start: usize, size: usize) { - assert_eq!(align_up(start, core::mem::align_of::()), start); // Check if we are up at least 1 LinkedNode size + assert_eq!(align_up(start, 16), start); // Check if we are up at least 1 LinkedNode size assert!(size >= core::mem::size_of::()); // check if we have enough space for a LinkedNode let mut linked_node = LinkedNode::new(size); diff --git a/kernel/src/arch/x86_64/init.rs b/kernel/src/arch/x86_64/init.rs index 4d22ace..4cef92c 100644 --- a/kernel/src/arch/x86_64/init.rs +++ b/kernel/src/arch/x86_64/init.rs @@ -8,8 +8,14 @@ use crate::{ util::serial_print, }; use limine::response::{HhdmResponse, MemoryMapResponse}; -use x86_64::instructions::interrupts::without_interrupts; -use x86_64::instructions::{interrupts, port::Port}; +use x86_64::{ + instructions::interrupts::without_interrupts, + registers::control::{Cr0, Cr0Flags}, +}; +use x86_64::{ + instructions::{interrupts, port::Port}, + registers::control::{Cr4, Cr4Flags}, +}; const TIMER_PRECISION_HZ: u32 = 1000; const PIT_DIVISOR: u16 = (1_193_182_u32 / TIMER_PRECISION_HZ) as u16; @@ -53,6 +59,19 @@ pub fn init_x86_64<'a>( memory_map_response: &'a MemoryMapResponse, ) -> OffsetPageTable<'static> { load_gdt_x86_64(); + + unsafe { + let mut cr0 = Cr0::read(); + cr0.remove(Cr0Flags::EMULATE_COPROCESSOR); + cr0.insert(Cr0Flags::MONITOR_COPROCESSOR); + Cr0::write(cr0); + + let mut cr4 = Cr4::read(); + cr4.insert(Cr4Flags::OSFXSR); + cr4.insert(Cr4Flags::OSXMMEXCPT_ENABLE); + Cr4::write(cr4); + } + init_idt_x86_64(); unsafe { diff --git a/kernel/src/arch/x86_64/interrupts.rs b/kernel/src/arch/x86_64/interrupts.rs index f684c1e..b1625e9 100644 --- a/kernel/src/arch/x86_64/interrupts.rs +++ b/kernel/src/arch/x86_64/interrupts.rs @@ -6,6 +6,7 @@ use crate::{ timer::TIMER, }, println, + util::serial_print, }; use lazy_static::lazy_static; use pc_keyboard::DecodedKey; @@ -182,11 +183,11 @@ unsafe extern "C" fn syscall_interrupt_handler() { "add rsp, 8", "add rsp, 8", // pop them in reverse orser - "pop rbx", - "pop rcx", - "pop rdx", - "pop rsi", "pop rdi", + "pop rsi", + "pop rdx", + "pop rcx", + "pop rbx", "pop rbp", "pop r8", "pop r9", diff --git a/kernel/src/driver/elf/program.rs b/kernel/src/driver/elf/program.rs index 75c7e05..455817b 100644 --- a/kernel/src/driver/elf/program.rs +++ b/kernel/src/driver/elf/program.rs @@ -291,27 +291,26 @@ pub fn load_segment_to_memory( let p_offset: u64 = unsafe { (*phdr).p_offset }; let file_size: u64 = unsafe { (*phdr).p_filesz }; - let vaddr: *mut u8 = get_vaddr(phdr, load_bias); - if p_offset > elf_bytes.len() as u64 || file_size > elf_bytes.len() as u64 || p_offset + file_size > elf_bytes.len() as u64 + || file_size > mem_size { return; } // invalid, could read past it's memory - if file_size > mem_size { - return; - } + let vaddr: u64 = get_vaddr(phdr, load_bias) as u64; + let mem_page: u64 = align_down(vaddr, PAGE_SIZE); + let file_page: u64 = align_down(p_offset, PAGE_SIZE); + let page_off: u64 = vaddr - mem_page; - let seg_start = align_down(vaddr as u64, PAGE_SIZE); - let seg_end = align_up(vaddr as u64 + mem_size, PAGE_SIZE); + let seg_start = mem_page; + let seg_end = align_up(vaddr + mem_size, PAGE_SIZE); let mut flags = PageTableFlags::PRESENT | PageTableFlags::USER_ACCESSIBLE | PageTableFlags::WRITABLE; - if unsafe { ((*phdr).p_flags & PF_X) != 0 } { - } else { + if unsafe { ((*phdr).p_flags & PF_X) == 0 } { flags |= PageTableFlags::NO_EXECUTE; } @@ -337,19 +336,18 @@ pub fn load_segment_to_memory( drop(frame_allocator); + let dst = (mem_page + page_off) as *mut u8; + let src = unsafe { elf_bytes.as_ptr().add(p_offset as usize) }; + unsafe { - core::ptr::copy_nonoverlapping( - elf_bytes.as_ptr().add(p_offset as usize), - vaddr, - file_size as usize, - ); + core::ptr::copy_nonoverlapping(src, dst, file_size as usize); if mem_size > file_size { memset( - vaddr.add(file_size as usize), + dst.add(file_size as usize), 0, (mem_size - file_size) as usize, ); } - }; + } } diff --git a/kernel/src/driver/graphics/framebuffer.rs b/kernel/src/driver/graphics/framebuffer.rs index a713d10..4add54b 100644 --- a/kernel/src/driver/graphics/framebuffer.rs +++ b/kernel/src/driver/graphics/framebuffer.rs @@ -70,6 +70,22 @@ impl Framebuffer { } } + pub unsafe fn load_from_ptr( + &mut self, + src_ptr: *const u32, + src_width: usize, + src_height: usize, + ) { + let h = core::cmp::min(src_height, self.height); + let w = core::cmp::min(src_width, self.width); + + for y in 0..h { + let src_row = src_ptr.add(y * src_width); + let dst_row = self.back_buffer.as_mut_ptr().add(y * self.pitch); + core::ptr::copy_nonoverlapping(src_row, dst_row, w); + } + } + pub fn clear(&mut self, color: u32) { self.back_buffer.fill(color); } diff --git a/kernel/src/driver/serial.rs b/kernel/src/driver/serial.rs index 236ede0..bca1bf6 100644 --- a/kernel/src/driver/serial.rs +++ b/kernel/src/driver/serial.rs @@ -1,6 +1,6 @@ -use crate::driver::graphics::base::rgb; use crate::driver::graphics::font_render::render_text; use crate::driver::graphics::framebuffer::Framebuffer; +use crate::{driver::graphics::base::rgb, util::serial_print}; use core::fmt::{self, Write}; use spin::Mutex; @@ -15,6 +15,7 @@ pub struct ConsoleWriter<'a> { impl Write for ConsoleWriter<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { + serial_print(s); self.console.render_text(self.fb, s, 2, false); Ok(()) } diff --git a/kernel/src/driver/syscall.rs b/kernel/src/driver/syscall.rs index f3f893f..aa61c3a 100644 --- a/kernel/src/driver/syscall.rs +++ b/kernel/src/driver/syscall.rs @@ -3,15 +3,19 @@ use core::{ ptr::null_mut, }; +use alloc::string::ToString; use x86_64::{ VirtAddr, structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB}, }; use crate::{ - arch::arch::{FRAME_ALLOCATOR, get_allocator, infinite_idle}, - driver::graphics::framebuffer::with_framebuffer, - println, + arch::arch::{FRAME_ALLOCATOR, get_allocator, infinite_idle, sleep}, + driver::{ + graphics::{framebuffer::with_framebuffer, primitives::rectangle_filled}, + timer::TIMER, + }, + print, println, task::scheduler::SCHEDULER, util::{align_up, serial_print}, }; @@ -37,6 +41,10 @@ const UNLINK: usize = 87; const GETDENTS64: usize = 217; const CLOCK_GETTIME: usize = 228; const EXIT_GROUP: usize = 231; +const SLEEP: usize = 909090; // zzz haha +const DRAW_PIXEL: usize = 5555; +const FRAMEBUFFER_SWAP: usize = 6666; +pub const DRAW_BUFFER: usize = 7777; pub unsafe fn malloc(size: usize, align: usize) -> *mut u8 { let align = if align < 1 { @@ -74,7 +82,6 @@ pub unsafe fn memset(ptr: *mut u8, val: u8, count: usize) { } pub unsafe fn sbrk(increment: isize) -> isize { - serial_print("sbrk called"); let mut scheduler = SCHEDULER.lock(); if scheduler.current_process == -1 { return -1; @@ -100,34 +107,40 @@ pub unsafe fn sbrk(increment: isize) -> isize { if new < heap_base { return -1; } - if new > stack_top - 3 * 4096 { + if new > stack_top - 16384 * 4096 { + // 67 mib max return -1; } + if new > old { let map_start = align_up(old, 4096); let map_end = align_up(new, 4096); for addr in (map_start..map_end).step_by(4096) { - let frame = frame_allocator.allocate_frame().unwrap(); + if let Some(frame) = frame_allocator.allocate_frame() { + // TODO: do not use x86_64 only + let virt_addr = VirtAddr::new(addr); + let page = Page::::containing_address(virt_addr); + unsafe { + process + .address_space + .mapper + .map_to( + page, + frame, + PageTableFlags::PRESENT + | PageTableFlags::WRITABLE + | PageTableFlags::USER_ACCESSIBLE + | PageTableFlags::NO_EXECUTE, + &mut *frame_allocator, + ) + .unwrap() + .flush(); - // TODO: do not use x86_64 only - let virt_addr = VirtAddr::new(addr); - let page = Page::::containing_address(virt_addr); - unsafe { - process - .address_space - .mapper - .map_to( - page, - frame, - PageTableFlags::PRESENT - | PageTableFlags::WRITABLE - | PageTableFlags::USER_ACCESSIBLE - | PageTableFlags::NO_EXECUTE, - &mut *frame_allocator, - ) - .unwrap() - .flush(); + core::ptr::write_bytes(virt_addr.as_mut_ptr::(), 0, 4096); + } + } else { + return -1; } } } @@ -135,8 +148,6 @@ pub unsafe fn sbrk(increment: isize) -> isize { process.heap_end = new; - serial_print("sbrk finished"); - return old as isize; }) .unwrap_or(-1); @@ -150,21 +161,58 @@ pub unsafe extern "C" fn syscall_dispatch( arg2: isize, ) -> isize { match num { - SYS_BRK => sbrk(arg0), - SYS_WRITE => { + BRK => sbrk(arg0), + WRITE => { let buf_ptr = arg1 as *const u8; let len = arg2 as usize; let bytes: &[u8] = unsafe { core::slice::from_raw_parts(buf_ptr, len) }; - let s = core::str::from_utf8(bytes).unwrap_or(""); - println!("SYS_WRITE called: {:?} {:?}", s, len); + if let Ok(s) = core::str::from_utf8(bytes) { + print!("{}", s); + } else { + for byte in bytes { + if *byte == b'\0' { + continue; + } + print!("{}", *byte as char); + } + } + with_framebuffer(|fb| fb.swap()); 0 } - SYS_EXIT => { - println!("Program exit."); + EXIT => { + println!("Program exit: {}", arg0); with_framebuffer(|fb| fb.swap()); infinite_idle(); } + 67 => { + println!("{:?}", arg1); + 0 + } + SLEEP => { + sleep(arg0 as u64); + 0 + } + CLOCK_GETTIME => TIMER.now().elapsed() as isize, + DRAW_PIXEL => { + with_framebuffer(|mut fb| { + rectangle_filled(&mut fb, arg0 as usize, arg1 as usize, 4, 4, arg2 as u32); + }); + 0 + } + DRAW_BUFFER => { + with_framebuffer(|mut fb| { + fb.load_from_ptr(arg0 as *const u32, arg1 as usize, arg2 as usize); + fb.swap(); + }); + 0 + } + FRAMEBUFFER_SWAP => { + with_framebuffer(|fb| { + fb.swap(); + }); + 0 + } _ => -38, // syscall not found } } diff --git a/kernel/src/driver/timer.rs b/kernel/src/driver/timer.rs index 55242eb..368374b 100644 --- a/kernel/src/driver/timer.rs +++ b/kernel/src/driver/timer.rs @@ -3,6 +3,8 @@ use core::{ sync::atomic::{AtomicU64, Ordering}, }; +use crate::util::serial_print; + pub static TIMER: Timer = Timer::new(); pub struct Timer { @@ -50,6 +52,11 @@ impl Time { pub fn elapsed(&self) -> u64 { self.interrupt_count } + + pub fn ticks_since(&self) -> u64 { + let now = TIMER.interrupt_count.load(Ordering::Relaxed); + now.saturating_sub(self.interrupt_count) + } } impl Add for Time { diff --git a/kernel/src/task/scheduler.rs b/kernel/src/task/scheduler.rs index f918e0b..bce10f9 100644 --- a/kernel/src/task/scheduler.rs +++ b/kernel/src/task/scheduler.rs @@ -39,6 +39,8 @@ impl Locked { let stack_top = guard.processes[&pid].stack_top; guard.current_process = pid as i64; + drop(guard); + enter_usermode(entry_point as u64, (stack_top & !0xF) - 8); } diff --git a/kernel/src/userspace_stub.rs b/kernel/src/userspace_stub.rs index c97e658..a56a775 100644 --- a/kernel/src/userspace_stub.rs +++ b/kernel/src/userspace_stub.rs @@ -26,7 +26,7 @@ use crate::{ static CURSOR_BYTES: &[u8] = include_bytes!("../../assets/cursors/default.bmp"); -#[repr(C, align(8))] +#[repr(C, align(16))] struct AlignedElf([u8; include_bytes!("../../assets/doomgeneric").len()]); static TEST_ELF: AlignedElf = AlignedElf(*include_bytes!("../../assets/doomgeneric")); static TEST_ELF_BYTES: &[u8] = &TEST_ELF.0; @@ -86,8 +86,6 @@ fn boot_animation() { pub fn userspace_init(mapper: &mut OffsetPageTable) -> ! { // this is just a stub - boot_animation(); - let (entry_point, heap_base) = load_file(mapper, TEST_ELF_BYTES); println!("Entry point: {:?}", entry_point); diff --git a/user/apps/helloworld/compile.sh b/user/apps/helloworld/compile.sh index 9dd4556..c8c7120 100644 --- a/user/apps/helloworld/compile.sh +++ b/user/apps/helloworld/compile.sh @@ -1,4 +1,3 @@ -gcc -nostdlib -nostdinc -static -static-pie \ - -o $1 $1.c \ - -L../../libxunil/target/release -l:libxunil.a -mv $1 ../../../assets/$1.elf +GCC_INCLUDES=$(gcc -print-file-name=include) +SYS_INCLUDES=/usr/include +gcc -w -static -D__NO_INLINE__ -O0 -mno-mmx -mno-avx -fno-stack-protector -fno-inline -fno-inline-small-functions -fno-indirect-inlining -fno-builtin -fcompare-debug-second -nostdlib -nostdinc helloworld.c -Wl,--gc-sections -L../../libxunil/target/release -l:libxunil.a -I../../libxunil/include -I"$GCC_INCLUDES" -I"$SYS_INCLUDES" -o ../../../assets/helloworld.elf diff --git a/user/libxunil/src/include/inttypes.h b/user/libxunil/include/inttypes.h similarity index 50% rename from user/libxunil/src/include/inttypes.h rename to user/libxunil/include/inttypes.h index c2d4f4b..9a6118b 100644 --- a/user/libxunil/src/include/inttypes.h +++ b/user/libxunil/include/inttypes.h @@ -1,2 +1 @@ -#include #include diff --git a/user/libxunil/src/include/stdio.h b/user/libxunil/include/stdio.h similarity index 71% rename from user/libxunil/src/include/stdio.h rename to user/libxunil/include/stdio.h index 13a6468..dbfb286 100644 --- a/user/libxunil/src/include/stdio.h +++ b/user/libxunil/include/stdio.h @@ -1,18 +1,9 @@ #pragma once +#include #include #include -typedef struct _iobuf -{ - char* _ptr; - int _cnt; - char* _base; - int _flag; - int _file; - int _charbuf; - int _bufsiz; - char* _tmpfname; -} FILE; +typedef struct FILE FILE; extern FILE *stdin; extern FILE *stdout; @@ -20,6 +11,10 @@ extern FILE *stderr; #define EOF (-1) +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + FILE *fopen(const char *path, const char *mode); int fclose(FILE *fp); size_t fread(void *ptr, size_t size, size_t nmemb, FILE *fp); @@ -31,12 +26,19 @@ char *fgets(char *s, int size, FILE *fp); int fputs(const char *s, FILE *fp); int feof(FILE *fp); int ferror(FILE *fp); +int remove(const char *path); +int rename(const char *path, const char *new_path); int printf(const char *fmt, ...); +int puts(const char *s); +int putchar(int c); + int fprintf(FILE *fp, const char *fmt, ...); int sprintf(char *buf, const char *fmt, ...); int snprintf(char *buf, size_t size, const char *fmt, ...); int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap); int vfprintf(FILE *fp, const char *fmt, va_list ap); -void write(int fd, const char* buf, unsigned long count); +ssize_t write(int fd, const void *buf, size_t count); void exit(int code); + +int sscanf(const char *str, const char *format, ...); diff --git a/user/libxunil/src/include/stdlib.h b/user/libxunil/include/stdlib.h similarity index 79% rename from user/libxunil/src/include/stdlib.h rename to user/libxunil/include/stdlib.h index c9fbb5f..b32dd28 100644 --- a/user/libxunil/src/include/stdlib.h +++ b/user/libxunil/include/stdlib.h @@ -1,4 +1,5 @@ #pragma once +#include #include void *malloc(size_t size); @@ -18,3 +19,6 @@ double strtod(const char *s, char **endptr); char *getenv(const char *name); void qsort(void *base, size_t nmemb, size_t size, int (*cmp)(const void *, const void *)); int abs(int x); +int system(const char *cmd); +int draw_pixel(uint32_t x, uint32_t y, uint32_t color); +int framebuffer_swap(); diff --git a/user/libxunil/src/include/string.h b/user/libxunil/include/string.h similarity index 100% rename from user/libxunil/src/include/string.h rename to user/libxunil/include/string.h diff --git a/user/libxunil/src/include/strings.h b/user/libxunil/include/strings.h similarity index 100% rename from user/libxunil/src/include/strings.h rename to user/libxunil/include/strings.h diff --git a/user/libxunil/src/file.rs b/user/libxunil/src/file.rs index 64e51f0..2a87d75 100644 --- a/user/libxunil/src/file.rs +++ b/user/libxunil/src/file.rs @@ -1,40 +1,236 @@ +use core::{ + ffi::CStr, + ptr::{null, null_mut}, +}; + +use crate::{mem::free, printf, puts}; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct FILE { + pub data: *const u8, // pointer to the file's data + pub size: usize, // total size + pub cursor: usize, // current position + pub writable: bool, // is this a write buffer? + pub write_buf: *mut u8, // for writable fake files + pub write_cap: usize, + pub fd: i64, +} + +impl FILE { + pub const fn zeroed() -> FILE { + FILE { + data: null(), + size: 0, + cursor: 0, + writable: false, + write_buf: null_mut(), + write_cap: 0, + fd: -1, + } + } +} + +struct FakeFileEntry { + name: &'static str, + data: &'static [u8], +} + +#[repr(C, align(8))] +struct AlignedWAD([u8; include_bytes!("../../../assets/doom1.wad").len()]); +static TEST_WAD: AlignedWAD = AlignedWAD(*include_bytes!("../../../assets/doom1.wad")); +static TEST_WAD_BYTES: &[u8] = &TEST_WAD.0; + +static FILES: &[FakeFileEntry] = &[ + FakeFileEntry { + name: "doom1.wad", + data: TEST_WAD_BYTES, + }, + FakeFileEntry { + name: "default.cfg", + data: b"", + }, + FakeFileEntry { + name: "doom.cfg", + data: b"", + }, +]; + +static mut FILE_POOL: [FILE; 16] = [FILE::zeroed(); 16]; +static mut FILE_POOL_USED: [bool; 16] = [false; 16]; +static mut STDERR_FILE: FILE = FILE::zeroed(); +static mut STDOUT_FILE: FILE = FILE::zeroed(); +static mut STDIN_FILE: FILE = FILE::zeroed(); + #[unsafe(no_mangle)] -extern "C" fn fopen(path: *const u8, mode: *const u8) -> *mut u8 { - 0x10 as *mut u8 +pub static mut stderr: *mut FILE = unsafe { &raw mut STDERR_FILE }; +#[unsafe(no_mangle)] +pub static mut stdin: *mut FILE = unsafe { &raw mut STDIN_FILE }; +#[unsafe(no_mangle)] +pub static mut stdout: *mut FILE = unsafe { &raw mut STDOUT_FILE }; + +pub unsafe fn get_file_pool_slot() -> (*mut FILE, i64) { + unsafe { + for i in 0..16 { + if !FILE_POOL_USED[i] { + FILE_POOL_USED[i] = true; + return (&mut FILE_POOL[i], i as i64); + } + } + + (null_mut(), -1) + } } #[unsafe(no_mangle)] -extern "C" fn fclose(file_ptr: *mut u8) -> i32 { +extern "C" fn fopen(path: *const i8, mode: *const i8) -> *mut FILE { + if path.is_null() || mode.is_null() { + return null_mut(); + } + + unsafe { + let name = CStr::from_ptr(path).to_str().unwrap_or(""); + + for entry in FILES { + if name.contains(entry.name) { + let (slot, fd) = get_file_pool_slot(); + + if slot.is_null() { + return null_mut(); + } + + (*slot).data = entry.data.as_ptr(); + (*slot).size = entry.data.len(); + (*slot).cursor = 0; + (*slot).writable = false; + (*slot).write_buf = null_mut(); + (*slot).write_cap = 0; + (*slot).fd = fd; + return slot; + } + } + } + + null_mut() +} + +#[unsafe(no_mangle)] +extern "C" fn fclose(file_ptr: *mut FILE) -> i32 { + if file_ptr.is_null() || unsafe { (*file_ptr).fd < 0 || (*file_ptr).fd >= 16 } { + return -1; + } + + unsafe { FILE_POOL_USED[(*file_ptr).fd as usize] = false }; + unsafe { *file_ptr = FILE::zeroed() }; + 0 } #[unsafe(no_mangle)] -extern "C" fn fprintf(file_ptr: *mut u8, s: *const u8) -> i32 { +unsafe extern "C" fn fprintf(file_ptr: *mut FILE, fmt: *const u8, args: ...) -> i32 { + if fmt.is_null() || file_ptr.is_null() || unsafe { (*file_ptr).fd < 0 || (*file_ptr).fd >= 16 } + { + return -1; + } + 0 } #[unsafe(no_mangle)] -extern "C" fn fread(ptr: *mut u8, size: i32, nmemb: *const u8, fp: *const u8) -> i32 { +extern "C" fn fread(ptr: *mut u8, size: usize, nmemb: usize, fp: *mut FILE) -> usize { + if size == 0 + || nmemb == 0 + || ptr.is_null() + || fp.is_null() + || unsafe { (*fp).fd < 0 || (*fp).fd >= 16 } + { + return 0; + } + + let total = match size.checked_mul(nmemb) { + Some(t) => t, + None => return 0, + }; + + unsafe { + let f = &mut *fp; + if f.cursor > f.size { + puts(b"failed to read\0".as_ptr()); + return 0; + } + + let available = f.size - f.cursor; + let to_read = total.min(available); + + if to_read > 0 { + core::ptr::copy_nonoverlapping(f.data.add(f.cursor), ptr, to_read); + f.cursor = f.cursor.saturating_add(to_read); + } + + to_read / size + } +} + +#[unsafe(no_mangle)] +extern "C" fn fseek(stream: *mut FILE, offset: i64, whence: i32) -> i32 { + if stream.is_null() || unsafe { (*stream).fd } == -1 { + return -1; + } + + let f = unsafe { &mut *stream }; + + let new_pos = match whence { + 0 => { + if offset < 0 { + return -1; + } + offset as usize + } + 1 => { + let cur = f.cursor as i64; + let pos = cur.saturating_add(offset); + if pos < 0 { + return -1; + } + pos as usize + } + 2 => { + let end = f.size as i64; + let pos = end.saturating_add(offset); + if pos < 0 { + return -1; + } + pos as usize + } + _ => return -1, + }; + + if new_pos > f.size { + return -1; + } + + f.cursor = new_pos; 0 } #[unsafe(no_mangle)] -extern "C" fn fwrite(ptr: *mut u8, size: i32, nmemb: *const u8, fp: *const u8) -> i32 { - 0 +extern "C" fn fwrite(ptr: *mut u8, size: usize, count: usize, fp: *mut FILE) -> usize { + if ptr.is_null() || fp.is_null() || unsafe { (*fp).fd < 0 || (*fp).fd >= 16 } { + return 0; + } + count } #[unsafe(no_mangle)] -extern "C" fn fseek(s: *const u8, size: i32, fp: *const u8) -> i32 { - 0 +extern "C" fn ftell(stream: *mut FILE) -> i64 { + if stream.is_null() || unsafe { (*stream).fd < 0 || (*stream).fd >= 16 } { + return -1; + } + unsafe { (*stream).cursor as i64 } } #[unsafe(no_mangle)] -extern "C" fn ftell(fp: *const u8) -> i64 { - 0 -} - -#[unsafe(no_mangle)] -extern "C" fn fflush(file_ptr: *mut u8) -> i32 { +extern "C" fn fflush(file_ptr: *mut FILE) -> i32 { 0 } @@ -44,7 +240,7 @@ extern "C" fn mkdir(path: *const u8, mode: *const u8) -> i32 { } #[unsafe(no_mangle)] -extern "C" fn remove(path: *const u8) -> i32 { +extern "C" fn remove(path: *const i8) -> i32 { 0 } @@ -57,8 +253,3 @@ extern "C" fn rename(path: *const u8, new_path: *const u8) -> i32 { unsafe extern "C" fn vfprintf(stream: *const u8, format: *const u8, args: ...) -> i32 { 0 } - -#[unsafe(no_mangle)] -unsafe extern "C" fn vsnprintf(s: *mut u8, n: i32, format: *const u8, args: ...) -> i32 { - 0 -} diff --git a/user/libxunil/src/heap.rs b/user/libxunil/src/heap.rs index 2d7d6ca..88355e7 100644 --- a/user/libxunil/src/heap.rs +++ b/user/libxunil/src/heap.rs @@ -2,9 +2,6 @@ use spin::mutex::Mutex; use crate::util::align_up; -pub const HEAP_START: usize = 0x_4444_4444_0000; -pub const HEAP_SIZE: usize = 256 * 1024 * 1024; // 256 MiB - pub struct LinkedNode { pub size: usize, pub next: Option<&'static mut LinkedNode>, @@ -28,6 +25,12 @@ pub struct LinkedListAllocator { head: LinkedNode, } +#[derive(Clone, Copy)] +pub struct Allocation { + pub start: usize, + pub end: usize, +} + impl LinkedListAllocator { pub const fn new() -> LinkedListAllocator { Self { @@ -36,58 +39,70 @@ impl LinkedListAllocator { } pub unsafe fn add_free_memory_region(&mut self, start: usize, size: usize) { - assert_eq!(align_up(start, core::mem::align_of::()), start); // Check if we are up at least 1 LinkedNode size - assert!(size >= core::mem::size_of::()); // check if we have enough space for a LinkedNode - - let mut linked_node = LinkedNode::new(size); - linked_node.next = self.head.next.take(); - - let linked_node_ptr = start as *mut LinkedNode; // Treat the start memory region as a LinkedNode type unsafe { - linked_node_ptr.write(linked_node); // write the data, very risky - self.head.next = Some(&mut *linked_node_ptr); + assert_eq!(align_up(start, 16), start); + assert!(size >= core::mem::size_of::()); + + let mut node = LinkedNode::new(size); + node.next = self.head.next.take(); + + let node_ptr = start as *mut LinkedNode; + node_ptr.write(node); + self.head.next = Some(&mut *node_ptr); } } - pub fn find_region( - &mut self, - size: usize, - align: usize, - ) -> Option<(&'static mut LinkedNode, usize)> { + pub fn find_region(&mut self, size: usize) -> Option { let mut current = &mut self.head; - while let Some(ref mut region) = current.next { - if let Ok(alloc_start) = Self::alloc_from_region(®ion, size, align) { - let next = region.next.take(); - let ret = Some((current.next.take().unwrap(), alloc_start)); - current.next = next; + while let Some(ref region) = current.next { + let mut alloc = match Self::alloc_from_region(region, size) { + Ok(a) => a, + Err(()) => { + current = current.next.as_mut().unwrap(); + continue; + } + }; - return ret; + let taken = current.next.take().unwrap(); + let region_end = taken.end_addr(); + + let old_next = unsafe { + let node_ptr = taken as *mut LinkedNode; + let next_ptr = core::ptr::addr_of_mut!((*node_ptr).next); + core::ptr::read(next_ptr) + }; + + let excess_size = region_end - alloc.end; + + if excess_size >= core::mem::size_of::() { + unsafe { + let remainder_ptr = alloc.end as *mut LinkedNode; + remainder_ptr.write(LinkedNode { + size: excess_size, + next: old_next, + }); + current.next = Some(&mut *remainder_ptr); + } } else { - current = current.next.as_mut().unwrap(); + alloc.end = region_end; + current.next = old_next; } + + return Some(alloc); } None } - fn alloc_from_region(region: &LinkedNode, size: usize, align: usize) -> Result { - let alloc_start = align_up(region.start_addr() + core::mem::size_of::(), align); - let alloc_end = (alloc_start - core::mem::size_of::()) - .checked_add(size) - .ok_or(())?; // check for overflows - - if alloc_end > region.end_addr() { + fn alloc_from_region(region: &LinkedNode, size: usize) -> Result { + let start = region.start_addr(); + let end_unaligned = start.checked_add(size).ok_or(())?; + let end = align_up(end_unaligned, 16); + if end > region.end_addr() { return Err(()); } - - let excess_size = region.end_addr() - alloc_end; - if excess_size > 0 && excess_size < core::mem::size_of::() { - // if the remaining space is not enough for another LinkedNode, skip this region. - return Err(()); - } - - Ok(alloc_start) + Ok(Allocation { start, end }) } } diff --git a/user/libxunil/src/lib.rs b/user/libxunil/src/lib.rs index 08eb9bd..308d150 100644 --- a/user/libxunil/src/lib.rs +++ b/user/libxunil/src/lib.rs @@ -1,33 +1,57 @@ #![no_std] #![feature(c_variadic)] - -use core::ptr::{null, null_mut}; +use core::{ + ffi::VaList, + fmt::{Error, Result, Write}, + ptr::{addr_of_mut, null, null_mut}, + usize, +}; use crate::{ - mem::{malloc, memcpy, memset}, - syscall::{EXIT, WRITE, syscall3}, + mem::{malloc, memcpy}, + syscall::{DRAW_BUFFER, DRAW_PIXEL, EXIT, FRAMEBUFFER_SWAP, WRITE, syscall0, syscall3}, }; pub mod file; pub mod heap; pub mod mem; pub mod syscall; +pub mod time; pub mod util; +static mut ERRNO: core::ffi::c_int = 0; + +static TOUPPER_TABLE: [i32; 384] = { + let mut table = [0i32; 384]; + let mut i = 0usize; + while i < 384 { + let c = i.wrapping_sub(128) as u8; + table[i] = if c.is_ascii_lowercase() { + (c - 0x20) as i32 + } else { + c as i32 + }; + i += 1; + } + table +}; + #[unsafe(no_mangle)] -extern "C" fn write(fd: i64, buf: *const u8, count: usize) -> isize { - unsafe { syscall3(WRITE, fd as usize, buf as usize, count) } +extern "C" fn write(fd: i32, buf: *const u8, count: usize) -> isize { + unsafe { syscall3(WRITE, fd as isize, buf as isize, count as isize) } } #[unsafe(no_mangle)] -extern "C" fn exit(code: i64) -> ! { - unsafe { syscall3(EXIT, code as usize, 0, 0) }; - loop {} +extern "C" fn exit(code: i32) -> ! { + unsafe { syscall3(EXIT, code as isize, 0, 0) }; + loop { + unsafe { core::arch::asm!("nop") }; + } } #[unsafe(no_mangle)] extern "C" fn strlen(s: *const u8) -> usize { - let mut len = 0; + let mut len = 0usize; while unsafe { *s.add(len) } != 0 { len += 1; } @@ -35,33 +59,292 @@ extern "C" fn strlen(s: *const u8) -> usize { } #[unsafe(no_mangle)] -extern "C" fn puts(s: *const u8) -> isize { +extern "C" fn puts(s: *const u8) -> i32 { write(1, s, strlen(s)); + write(1, b"\n\0".as_ptr(), 1); 0 } #[unsafe(no_mangle)] -extern "C" fn putchar(s: i32) -> isize { - write(1, (s as u8 + b'0') as *const u8, 1); +extern "C" fn putchar(c: i32) -> i32 { + let b = c as u8; + write(1, core::ptr::addr_of!(b), 1); 0 } #[unsafe(no_mangle)] -extern "C" fn abs(n: i64) -> i64 { +extern "C" fn abs(n: i32) -> i32 { n.abs() } -#[unsafe(no_mangle)] -unsafe extern "C" fn printf(format: *const u8, args: ...) { - unsafe { syscall3(WRITE, 1, format as usize, strlen(format)) }; +struct BufWriter { + buf: *mut u8, + max: usize, + pos: usize, +} + +impl Write for BufWriter { + fn write_str(&mut self, s: &str) -> Result { + for byte in s.bytes() { + if self.pos >= self.max { + return Err(Error); + } + unsafe { + *self.buf.add(self.pos) = byte; + } + self.pos += 1; + } + Ok(()) + } +} + +struct StdoutWriter { + size: usize, +} + +impl Write for StdoutWriter { + fn write_str(&mut self, s: &str) -> Result { + self.size += s.len(); + write(1, s.as_ptr(), s.len()); + Ok(()) + } +} + +pub unsafe fn write_c_formatted(fmt: *const u8, args: &mut VaList, writer: &mut impl Write) { + let mut fi = 0usize; + + loop { + let ch = unsafe { *fmt.add(fi) }; + fi += 1; + if ch == 0 { + break; + } + + if ch != b'%' { + let _ = writer.write_char(ch as char); + continue; + } + + let mut precision: Option = None; + let mut next_byte = unsafe { *fmt.add(fi) }; + + if next_byte == b'.' { + fi += 1; + let mut p_val = 0usize; + loop { + let digit = unsafe { *fmt.add(fi) }; + if digit >= b'0' && digit <= b'9' { + p_val = p_val * 10 + (digit - b'0') as usize; + fi += 1; + } else { + break; + } + } + precision = Some(p_val); + next_byte = unsafe { *fmt.add(fi) }; + } + + let spec = next_byte; + fi += 1; + + unsafe { + match spec { + b'd' | b'i' => { + let v: i32 = args.arg(); + if let Some(p) = precision { + let _ = write!(writer, "{:01$}", v, p); + } else { + let _ = write!(writer, "{}", v); + } + } + b'u' => { + let v: u32 = args.arg(); + if let Some(p) = precision { + let _ = write!(writer, "{:01$}", v, p); + } else { + let _ = write!(writer, "{}", v); + } + } + b'x' => { + let v: u32 = args.arg(); + if let Some(p) = precision { + let _ = write!(writer, "{:01$x}", v, p); + } else { + let _ = write!(writer, "{:x}", v); + } + } + b'X' => { + let v: u32 = args.arg(); + if let Some(p) = precision { + let _ = write!(writer, "{:01$X}", v, p); + } else { + let _ = write!(writer, "{:X}", v); + } + } + b'o' => { + let v: u32 = args.arg(); + if let Some(p) = precision { + let _ = write!(writer, "{:01$o}", v, p); + } else { + let _ = write!(writer, "{:o}", v); + } + } + b'p' => { + let v: *const u8 = args.arg(); + if v.is_null() { + let _ = writer.write_str("(null)"); + } else { + let _ = write!(writer, "0x{:x}", v as usize); + } + } + b'c' => { + let v: i32 = args.arg(); + let _ = writer.write_char((v as u8) as char); + } + b's' => { + let ptr: *const u8 = args.arg(); + if ptr.is_null() { + let _ = writer.write_str("(null)"); + } else { + let mut si = 0usize; + loop { + let c = *ptr.add(si); + if c == 0 { + break; + } + if let Some(p) = precision { + if si >= p { + break; + } + } + let _ = writer.write_char(c as char); + si += 1; + } + } + } + b'f' | b'F' | b'g' | b'G' => { + let v: f64 = args.arg(); + if let Some(p) = precision { + let _ = write!(writer, "{:.*}", p, v); + } else { + let _ = write!(writer, "{}", v); + } + } + b'l' => { + let next_spec = *fmt.add(fi); + fi += 1; + match next_spec { + b'd' | b'i' => { + let v: i64 = args.arg(); + if let Some(p) = precision { + let _ = write!(writer, "{:01$}", v, p); + } else { + let _ = write!(writer, "{}", v); + } + } + b'u' => { + let v: u64 = args.arg(); + if let Some(p) = precision { + let _ = write!(writer, "{:01$}", v, p); + } else { + let _ = write!(writer, "{}", v); + } + } + _ => { + let _ = writer.write_char('%'); + let _ = writer.write_char('l'); + let _ = writer.write_char(next_spec as char); + } + } + } + b'%' => { + let _ = writer.write_char('%'); + } + _ => { + let _ = writer.write_char('%'); + let _ = writer.write_char(spec as char); + } + } + } + } } #[unsafe(no_mangle)] -extern "C" fn atoi(mut c: *const u8) -> i64 { - let mut value: i64 = 0; - let mut sign: i64 = 1; +pub unsafe extern "C" fn printf(fmt: *const u8, mut args: ...) -> i32 { + let mut writer = StdoutWriter { size: 0 }; + + unsafe { write_c_formatted(fmt, &mut args, &mut writer) }; + + writer.size as i32 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vsnprintf( + buf: *mut u8, + size: usize, + fmt: *const u8, + mut args: VaList, +) -> i32 { + if buf.is_null() || size == 0 || fmt.is_null() { + return -1; + } + + let max = size - 1; + let mut writer = BufWriter { buf, max, pos: 0 }; + + unsafe { write_c_formatted(fmt, &mut args, &mut writer) }; + + unsafe { + *buf.add(writer.pos) = 0; + } + + writer.pos as i32 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn snprintf(buf: *mut u8, size: usize, fmt: *const u8, mut args: ...) -> i32 { + if buf.is_null() || size == 0 || fmt.is_null() { + return -1; + } + + let max = size - 1; + let mut writer = BufWriter { buf, max, pos: 0 }; + + unsafe { write_c_formatted(fmt, &mut args, &mut writer) }; + + unsafe { + *buf.add(writer.pos) = 0; + } + + writer.pos as i32 +} + +unsafe extern "C" { + fn main(argc: i32, argv: *const *const u8) -> i32; +} + +#[unsafe(no_mangle)] +pub extern "C" fn __stack_chk_fail() -> ! { + exit(127) +} + +#[unsafe(no_mangle)] +pub extern "C" fn __stack_chk_fail_local() -> ! { + __stack_chk_fail() +} + +#[unsafe(no_mangle)] +pub extern "C" fn _start() -> ! { + let code = unsafe { main(0, null()) }; + + exit(code as i32); +} +#[unsafe(no_mangle)] +extern "C" fn atoi(mut c: *const u8) -> i32 { + let mut value: i32 = 0; + let mut sign: i32 = 1; unsafe { while (*c).is_ascii_whitespace() { c = c.add(1); @@ -75,7 +358,7 @@ extern "C" fn atoi(mut c: *const u8) -> i64 { } while (*c).is_ascii_digit() { value *= 10; - value += ((*c) - b'0') as i64; + value += ((*c) - b'0') as i32; c = c.add(1); } } @@ -162,69 +445,88 @@ extern "C" fn atof(mut c: *const u8) -> f64 { } } -pub fn compare_str(str_1: *const u8, str_2: *const u8, case: bool, n: usize) -> i32 { +pub fn compare_str(str_1: *const u8, str_2: *const u8, ignore_case: bool, n: usize) -> i32 { let mut len = 0; - while len < n { let mut c_1 = unsafe { *str_1.add(len) }; let mut c_2 = unsafe { *str_2.add(len) }; - - if case { + if ignore_case { c_1 = c_1.to_ascii_lowercase(); c_2 = c_2.to_ascii_lowercase(); } - if c_1 != c_2 { - return (c_1 - c_2) as i32; + return (c_1 as i32) - (c_2 as i32); } - if c_1 == 0 { return 0; } - len += 1; } - 0 } #[unsafe(no_mangle)] unsafe extern "C" fn strcasecmp(str_1: *const u8, str_2: *const u8) -> i32 { - compare_str(str_1, str_2, true, 999999999999999) + compare_str(str_1, str_2, true, usize::MAX) } #[unsafe(no_mangle)] unsafe extern "C" fn strcmp(str_1: *const u8, str_2: *const u8) -> i32 { - compare_str(str_1, str_2, false, 999999999999999) + compare_str(str_1, str_2, false, usize::MAX) } #[unsafe(no_mangle)] -unsafe extern "C" fn strncasecmp(str_1: *const u8, str_2: *const u8, n: i32) -> i32 { - compare_str(str_1, str_2, true, n as usize) +unsafe extern "C" fn strncasecmp(str_1: *const u8, str_2: *const u8, n: usize) -> i32 { + compare_str(str_1, str_2, true, n) } #[unsafe(no_mangle)] -unsafe extern "C" fn strncmp(str_1: *const u8, str_2: *const u8, n: i32) -> i32 { - compare_str(str_1, str_2, false, n as usize) +unsafe extern "C" fn strncmp(str_1: *const u8, str_2: *const u8, n: usize) -> i32 { + compare_str(str_1, str_2, false, n) } #[unsafe(no_mangle)] -unsafe extern "C" fn strncpy(dest: *mut u8, source: *const u8, n: usize) -> *mut u8 { - let mut i = 0usize; +unsafe extern "C" fn draw_pixel(x: u32, y: u32, color: u32) -> i32 { unsafe { - while i < n { - let b = *source.add(i); - *dest.add(i) = b; - i += 1; + return syscall3(DRAW_PIXEL, x as isize, y as isize, color as isize) as i32; + } +} - if b == 0 { - while i < n { - *dest.add(i) = 0; - i += 1; - } - break; +#[unsafe(no_mangle)] +unsafe extern "C" fn draw_buffer(buffer: *const u32, width: u32, height: u32) -> i32 { + unsafe { + return syscall3( + DRAW_BUFFER, + buffer as isize, + width as isize, + height as isize, + ) as i32; + } +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn framebuffer_swap() -> i32 { + unsafe { + return syscall0(FRAMEBUFFER_SWAP) as i32; + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strncpy(dest: *mut u8, source: *const u8, n: usize) -> *mut u8 { + let mut i = 0usize; + while i < n { + let b = unsafe { *source.add(i) }; + unsafe { *dest.add(i) = b }; + + if b == 0 { + let mut j = i + 1; + while j < n { + unsafe { *dest.add(j) = 0 }; + j += 1; } + break; } + i += 1; } dest @@ -277,7 +579,7 @@ unsafe extern "C" fn strstr(haystack: *const u8, needle: *const u8) -> *const u8 } #[unsafe(no_mangle)] -unsafe extern "C" fn strchr(s: *const u8, ch: u8) -> *const u8 { +unsafe extern "C" fn strchr(s: *const u8, ch: i32) -> *const u8 { if s.is_null() { return null(); } @@ -286,7 +588,7 @@ unsafe extern "C" fn strchr(s: *const u8, ch: u8) -> *const u8 { unsafe { while *s.add(i) != 0 { - if *s.add(i) == ch { + if *s.add(i) == ch as u8 { return s.add(i); } @@ -321,21 +623,40 @@ unsafe extern "C" fn strrchr(s: *const u8, ch: u8) -> *const u8 { } #[unsafe(no_mangle)] -unsafe extern "C" fn toupper(char: u8) -> u8 { - char.to_ascii_uppercase() +unsafe extern "C" fn toupper(char: i32) -> i32 { + (char as u8).to_ascii_uppercase() as i32 } #[unsafe(no_mangle)] -unsafe extern "C" fn tolower(char: u8) -> u8 { - char.to_ascii_lowercase() +unsafe extern "C" fn tolower(char: i32) -> i32 { + (char as u8).to_ascii_lowercase() as i32 } #[unsafe(no_mangle)] extern "C" fn system(cmd: *const u8) -> i32 { 0 } +#[unsafe(no_mangle)] +extern "C" fn __ctype_toupper_loc() -> *const u8 { + TOUPPER_TABLE.as_ptr() as *const u8 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn __isoc23_sscanf() -> ! { + panic!("sscanf not implemented"); +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sscanf(str: *mut u8, fmt: *const u8, args: ...) -> i32 { + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn __errno_location() -> *mut core::ffi::c_int { + addr_of_mut!(ERRNO) +} #[panic_handler] -fn panic(_: &core::panic::PanicInfo) -> ! { - loop {} +fn panic(error: &core::panic::PanicInfo) -> ! { + exit(-1) } diff --git a/user/libxunil/src/mem.rs b/user/libxunil/src/mem.rs index 5c89d3a..6c0c113 100644 --- a/user/libxunil/src/mem.rs +++ b/user/libxunil/src/mem.rs @@ -1,17 +1,23 @@ -use core::ptr::null_mut; +use core::{mem, ptr::null_mut, usize}; use crate::{ - heap::ALLOCATOR, + heap::{ALLOCATOR, LinkedNode}, syscall::{BRK, syscall1}, + util::align_up, }; -#[unsafe(no_mangle)] -pub extern "C" fn sbrk(increment: i64) -> i64 { - unsafe { syscall1(BRK, increment as usize) as i64 } +pub fn sbrk(increment: i64) -> isize { + unsafe { syscall1(BRK, increment as isize) as isize } } const MAX_SIZE: u64 = 18446744073709551615; +#[repr(C, align(16))] +struct Header { + size: usize, + _pad: usize, +} + #[unsafe(no_mangle)] pub extern "C" fn calloc(count: u64, size: u64) -> *mut u8 { if count != 0 && size > MAX_SIZE / count { @@ -19,19 +25,16 @@ pub extern "C" fn calloc(count: u64, size: u64) -> *mut u8 { } let mut total = count * size; - if total == 0 { total = 1; } let ptr = malloc(total); - if ptr.is_null() { return null_mut(); } memset(ptr, 0, total as usize); - ptr } @@ -40,78 +43,126 @@ pub extern "C" fn free(ptr: *mut u8) { if ptr.is_null() { return; } - unsafe { - let size = *(((ptr as usize) - core::mem::size_of::()) as *const usize); + unsafe { + let header_ptr = (ptr as usize - mem::size_of::
()) as *mut Header; + let total = (*header_ptr).size; let mut allocator = ALLOCATOR.lock(); - allocator - .add_free_memory_region(ptr as usize - core::mem::size_of::(), size as usize); + allocator.add_free_memory_region(header_ptr as usize, total); } } #[unsafe(no_mangle)] pub extern "C" fn malloc(size: u64) -> *mut u8 { + let req = size as usize; + let req = if req == 0 { 1 } else { req }; + + let hdr = mem::size_of::
(); + let needed_unaligned = match hdr.checked_add(req) { + Some(v) => v, + None => return null_mut(), + }; + let align_req = 16; + let needed = align_up(needed_unaligned, align_req); + let mut allocator = ALLOCATOR.lock(); - if let Some(region) = allocator.find_region(size as usize, 16) { - return region.1 as *mut u8; - } else { - let start_addr: i64 = sbrk(size as i64); - if start_addr == -1 { - return null_mut(); + if let Some(region) = allocator.find_region(needed) { + unsafe { + let header_ptr = region.start as *mut Header; + header_ptr.write(Header { + size: region.end - region.start, + _pad: 0, + }); + return (region.start + hdr) as *mut u8; } - - unsafe { allocator.add_free_memory_region(start_addr as usize, size as usize) }; - drop(allocator); - malloc(size) } + + let min_region = mem::size_of::(); + let req_region = core::cmp::max(needed, min_region); + + let align = align_req; + let over = match req_region.checked_add(align) { + Some(v) => v, + None => return null_mut(), + }; + if over > i64::MAX as usize { + return null_mut(); + } + + let raw_start = sbrk(over as i64); + if raw_start == -1 { + return null_mut(); + } + + let raw_start = raw_start as usize; + let aligned_start = align_up(raw_start, align); + let usable = over - (aligned_start - raw_start); + + if usable < min_region { + return null_mut(); + } + + unsafe { + allocator.add_free_memory_region(aligned_start, usable); + } + + drop(allocator); + malloc(size) } #[unsafe(no_mangle)] pub extern "C" fn memcpy(dest_str: *mut u8, src_str: *const u8, n: usize) -> *mut u8 { unsafe { core::ptr::copy(src_str, dest_str, n) }; - dest_str } #[unsafe(no_mangle)] -pub extern "C" fn memset(str: *mut u8, c: i64, n: usize) -> *mut u8 { - unsafe { - core::ptr::write_bytes(str, c as u8, n); +pub extern "C" fn memset(dst: *mut u8, c: i64, n: usize) -> *mut u8 { + if dst.is_null() || n == 0 { + return dst; } - str + unsafe { + let val = c as u8; + let mut i = 0usize; + while i < n { + *dst.add(i) = val; + i += 1; + } + } + + dst } #[unsafe(no_mangle)] pub unsafe extern "C" fn realloc(ptr: *mut u8, size: usize) -> *mut u8 { + if size == 0 { + free(ptr); + return null_mut(); + } + + if ptr.is_null() { + return malloc(size as u64); + } + unsafe { - if size == 0 { - free(ptr); + let hdr = mem::size_of::
(); + let header_ptr = (ptr as usize - hdr) as *mut Header; + let total = (*header_ptr).size; + let old_payload = total.saturating_sub(hdr); + + if size <= old_payload { + return ptr; + } + + let new_ptr = malloc(size as u64); + if new_ptr.is_null() { return null_mut(); } - let header = ((ptr as usize) - core::mem::size_of::()) as *mut usize; - let old_size = *header; - - if old_size == size { - return ptr; - } else if size < old_size { - let mut allocator = ALLOCATOR.lock(); - let difference = old_size.abs_diff(size); - let start = (ptr as usize) + size; - *header = size; - allocator.add_free_memory_region(start as usize, difference as usize); - return ptr; - } else { - let new_ptr = malloc(size as u64); - if new_ptr.is_null() { - return null_mut(); - } - - core::ptr::copy_nonoverlapping(ptr, new_ptr, old_size); - free(ptr); - return new_ptr; - } + core::ptr::copy_nonoverlapping(ptr, new_ptr, old_payload); + free(ptr); + new_ptr } } diff --git a/user/libxunil/src/syscall.rs b/user/libxunil/src/syscall.rs index 2f20df9..9b5b576 100644 --- a/user/libxunil/src/syscall.rs +++ b/user/libxunil/src/syscall.rs @@ -19,6 +19,10 @@ pub const UNLINK: usize = 87; pub const GETDENTS64: usize = 217; pub const CLOCK_GETTIME: usize = 228; pub const EXIT_GROUP: usize = 231; +pub const SLEEP: usize = 909090; // zzz haha +pub const DRAW_PIXEL: usize = 5555; +pub const DRAW_BUFFER: usize = 7777; +pub const FRAMEBUFFER_SWAP: usize = 6666; #[inline(always)] pub unsafe fn syscall0(num: usize) -> isize { @@ -28,6 +32,8 @@ pub unsafe fn syscall0(num: usize) -> isize { "int 0x80", in("rax") num, lateout("rax") ret, + out("rdi") _, out("rcx") _, out("rdx") _, out("rsi") _, + out("r8") _, out("r9") _, out("r10") _, out("r11") _, options(nostack) ); } @@ -36,7 +42,7 @@ pub unsafe fn syscall0(num: usize) -> isize { } #[inline(always)] -pub unsafe fn syscall1(num: usize, arg0: usize) -> isize { +pub unsafe fn syscall1(num: usize, arg0: isize) -> isize { let ret: isize; unsafe { core::arch::asm!( @@ -44,6 +50,8 @@ pub unsafe fn syscall1(num: usize, arg0: usize) -> isize { in("rax") num, in("rdi") arg0, lateout("rax") ret, + out("rcx") _, out("rdx") _, out("rsi") _, + out("r8") _, out("r9") _, out("r10") _, out("r11") _, options(nostack) ); } @@ -52,7 +60,7 @@ pub unsafe fn syscall1(num: usize, arg0: usize) -> isize { } #[inline(always)] -pub unsafe fn syscall3(num: usize, arg0: usize, arg1: usize, arg2: usize) -> isize { +pub unsafe fn syscall3(num: usize, arg0: isize, arg1: isize, arg2: isize) -> isize { let ret: isize; unsafe { core::arch::asm!( diff --git a/user/libxunil/src/time.rs b/user/libxunil/src/time.rs new file mode 100644 index 0000000..48d36c4 --- /dev/null +++ b/user/libxunil/src/time.rs @@ -0,0 +1,38 @@ +use crate::syscall::{CLOCK_GETTIME, SLEEP, syscall0, syscall1}; + +#[repr(C)] +pub struct Timeval { + pub tv_sec: i64, + pub tv_usec: i64, +} + +#[repr(C)] +pub struct Timezone { + pub tz_minuteswest: i32, + pub tz_dsttime: i32, +} + +pub const TICKS_PER_SECOND: u64 = 1000; + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sleep_ms(ms: i32) -> i32 { + unsafe { syscall1(SLEEP, ms as isize) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn gettimeofday(tv: *mut Timeval, _tz: *mut Timezone) -> i32 { + unsafe { + if !tv.is_null() { + let ticks = syscall0(CLOCK_GETTIME) as u64; + + let seconds = ticks / TICKS_PER_SECOND; + let microseconds = (ticks % TICKS_PER_SECOND) * (1_000_000 / TICKS_PER_SECOND); + + (*tv).tv_sec = seconds as i64; + (*tv).tv_usec = microseconds as i64; + } + } + + 0 +}