Make Doom run by improving and fixing libc stubs, adding proper heap

allocation, adding a timer, a framebuffer swap, rectangle and buffer
draw syscall, moving the header files to the correct directory,
disabling stack protection, AVX and MMX, while enabling SSE, fix
interrupt handler popping in the wrong order, improve
load_segment_to_memory alignment, add load_from_ptr to framebuffer for
very quick drawing, serial_print as well as render font in
consolewriter, zero the memory in sbrk, add a fake file system and
methods to libxunil for DOOM, add correct printf, vsnprintf add _start
which calls the main function in libxunil, mark all registers as used
inside syscalls for no memory corruption, add build_helloworld.sh script
This commit is contained in:
csd4ni3l
2026-04-07 18:06:52 +02:00
parent ae3915147a
commit 9e8090c736
29 changed files with 980 additions and 256 deletions

View File

@@ -213,7 +213,7 @@ ifeq ($(KARCH),loongarch64)
iso_root -o $(IMAGE_NAME).iso iso_root -o $(IMAGE_NAME).iso
endif endif
rm -rf iso_root 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 $(IMAGE_NAME).hdd: limine/limine kernel
rm -f $(IMAGE_NAME).hdd rm -f $(IMAGE_NAME).hdd

BIN
assets/doom1.wad Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,6 @@
bash build_libxunil.sh bash build_libxunil.sh
cd user/apps/doomgeneric/doomgeneric cd user/apps/doomgeneric/doomgeneric
rm -r ./build rm -r ./build
make -f Makefile.xunil make -j16 -f Makefile.xunil
cp doomgeneric ../../../../assets/doomgeneric cp doomgeneric ../../../../assets/doomgeneric
cd ../../../.. cd ../../../..

4
build_helloworld.sh Normal file
View File

@@ -0,0 +1,4 @@
bash build_libxunil.sh
cd user/apps/helloworld
bash compile.sh
cd ../../../

View File

@@ -1,8 +1,9 @@
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
pub use crate::arch::x86_64::paging::FRAME_ALLOCATOR_X86_64 as FRAME_ALLOCATOR; pub use crate::arch::x86_64::paging::FRAME_ALLOCATOR_X86_64 as FRAME_ALLOCATOR;
use crate::driver::timer::TIMER; use crate::{driver::timer::TIMER, util::serial_print};
use core::{alloc::GlobalAlloc, arch::asm}; use alloc::string::ToString;
use core::{alloc::GlobalAlloc, arch::asm, sync::atomic::Ordering};
use limine::response::{HhdmResponse, MemoryMapResponse}; use limine::response::{HhdmResponse, MemoryMapResponse};
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
@@ -50,10 +51,11 @@ pub fn idle() {
} }
pub fn sleep(ticks: u64) { pub fn sleep(ticks: u64) {
let start = TIMER.now(); // let start = TIMER.now();
while (TIMER.now() - start).elapsed() <= ticks { // while start.ticks_since() < ticks {
idle(); // serial_print(start.ticks_since().to_string().as_str());
} // core::hint::spin_loop();
// }
} }
pub fn infinite_idle() -> ! { pub fn infinite_idle() -> ! {

View File

@@ -16,7 +16,7 @@ use crate::{
pub fn run_elf_x86_64(entry_point: *const u8, heap_base: u64) { pub fn run_elf_x86_64(entry_point: *const u8, heap_base: u64) {
let stack_base: u64 = 0x0000_7fff_0000_0000; let stack_base: u64 = 0x0000_7fff_0000_0000;
let page_count = 3; let page_count = 4096; // 16 mib
let page_size = 0x1000u64; let page_size = 0x1000u64;
let stack_top = stack_base + (page_count as u64 * page_size); let stack_top = stack_base + (page_count as u64 * page_size);

View File

@@ -20,7 +20,7 @@ fn align_up(addr: usize, align: usize) -> usize {
pub static ALLOCATOR: Locked<LinkedListAllocator> = Locked::new(LinkedListAllocator::new()); pub static ALLOCATOR: Locked<LinkedListAllocator> = Locked::new(LinkedListAllocator::new());
pub const HEAP_START: usize = 0x_4444_4444_0000; 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 struct LinkedNode {
pub size: usize, pub size: usize,
@@ -54,7 +54,7 @@ impl LinkedListAllocator {
fn size_align(layout: Layout) -> (usize, usize) { fn size_align(layout: Layout) -> (usize, usize) {
let layout = layout let layout = layout
.align_to(core::mem::align_of::<LinkedNode>()) .align_to(16)
.expect("Align to LinkedNode failed") .expect("Align to LinkedNode failed")
.pad_to_align(); .pad_to_align();
@@ -70,7 +70,7 @@ impl LinkedListAllocator {
} }
unsafe fn add_free_memory_region(&mut self, start: usize, size: usize) { unsafe fn add_free_memory_region(&mut self, start: usize, size: usize) {
assert_eq!(align_up(start, core::mem::align_of::<LinkedNode>()), 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::<LinkedNode>()); // check if we have enough space for a LinkedNode assert!(size >= core::mem::size_of::<LinkedNode>()); // check if we have enough space for a LinkedNode
let mut linked_node = LinkedNode::new(size); let mut linked_node = LinkedNode::new(size);

View File

@@ -8,8 +8,14 @@ use crate::{
util::serial_print, util::serial_print,
}; };
use limine::response::{HhdmResponse, MemoryMapResponse}; use limine::response::{HhdmResponse, MemoryMapResponse};
use x86_64::instructions::interrupts::without_interrupts; use x86_64::{
use x86_64::instructions::{interrupts, port::Port}; 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 TIMER_PRECISION_HZ: u32 = 1000;
const PIT_DIVISOR: u16 = (1_193_182_u32 / TIMER_PRECISION_HZ) as u16; 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, memory_map_response: &'a MemoryMapResponse,
) -> OffsetPageTable<'static> { ) -> OffsetPageTable<'static> {
load_gdt_x86_64(); 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(); init_idt_x86_64();
unsafe { unsafe {

View File

@@ -6,6 +6,7 @@ use crate::{
timer::TIMER, timer::TIMER,
}, },
println, println,
util::serial_print,
}; };
use lazy_static::lazy_static; use lazy_static::lazy_static;
use pc_keyboard::DecodedKey; use pc_keyboard::DecodedKey;
@@ -182,11 +183,11 @@ unsafe extern "C" fn syscall_interrupt_handler() {
"add rsp, 8", "add rsp, 8",
"add rsp, 8", "add rsp, 8",
// pop them in reverse orser // pop them in reverse orser
"pop rbx",
"pop rcx",
"pop rdx",
"pop rsi",
"pop rdi", "pop rdi",
"pop rsi",
"pop rdx",
"pop rcx",
"pop rbx",
"pop rbp", "pop rbp",
"pop r8", "pop r8",
"pop r9", "pop r9",

View File

@@ -291,27 +291,26 @@ pub fn load_segment_to_memory(
let p_offset: u64 = unsafe { (*phdr).p_offset }; let p_offset: u64 = unsafe { (*phdr).p_offset };
let file_size: u64 = unsafe { (*phdr).p_filesz }; 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 if p_offset > elf_bytes.len() as u64
|| file_size > elf_bytes.len() as u64 || file_size > elf_bytes.len() as u64
|| p_offset + file_size > elf_bytes.len() as u64 || p_offset + file_size > elf_bytes.len() as u64
|| file_size > mem_size
{ {
return; return;
} // invalid, could read past it's memory } // invalid, could read past it's memory
if file_size > mem_size { let vaddr: u64 = get_vaddr(phdr, load_bias) as u64;
return; 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_start = mem_page;
let seg_end = align_up(vaddr as u64 + mem_size, PAGE_SIZE); let seg_end = align_up(vaddr + mem_size, PAGE_SIZE);
let mut flags = let mut flags =
PageTableFlags::PRESENT | PageTableFlags::USER_ACCESSIBLE | PageTableFlags::WRITABLE; PageTableFlags::PRESENT | PageTableFlags::USER_ACCESSIBLE | PageTableFlags::WRITABLE;
if unsafe { ((*phdr).p_flags & PF_X) != 0 } { if unsafe { ((*phdr).p_flags & PF_X) == 0 } {
} else {
flags |= PageTableFlags::NO_EXECUTE; flags |= PageTableFlags::NO_EXECUTE;
} }
@@ -337,19 +336,18 @@ pub fn load_segment_to_memory(
drop(frame_allocator); 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 { unsafe {
core::ptr::copy_nonoverlapping( core::ptr::copy_nonoverlapping(src, dst, file_size as usize);
elf_bytes.as_ptr().add(p_offset as usize),
vaddr,
file_size as usize,
);
if mem_size > file_size { if mem_size > file_size {
memset( memset(
vaddr.add(file_size as usize), dst.add(file_size as usize),
0, 0,
(mem_size - file_size) as usize, (mem_size - file_size) as usize,
); );
} }
}; }
} }

View File

@@ -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) { pub fn clear(&mut self, color: u32) {
self.back_buffer.fill(color); self.back_buffer.fill(color);
} }

View File

@@ -1,6 +1,6 @@
use crate::driver::graphics::base::rgb;
use crate::driver::graphics::font_render::render_text; use crate::driver::graphics::font_render::render_text;
use crate::driver::graphics::framebuffer::Framebuffer; use crate::driver::graphics::framebuffer::Framebuffer;
use crate::{driver::graphics::base::rgb, util::serial_print};
use core::fmt::{self, Write}; use core::fmt::{self, Write};
use spin::Mutex; use spin::Mutex;
@@ -15,6 +15,7 @@ pub struct ConsoleWriter<'a> {
impl Write for ConsoleWriter<'_> { impl Write for ConsoleWriter<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result { fn write_str(&mut self, s: &str) -> fmt::Result {
serial_print(s);
self.console.render_text(self.fb, s, 2, false); self.console.render_text(self.fb, s, 2, false);
Ok(()) Ok(())
} }

View File

@@ -3,15 +3,19 @@ use core::{
ptr::null_mut, ptr::null_mut,
}; };
use alloc::string::ToString;
use x86_64::{ use x86_64::{
VirtAddr, VirtAddr,
structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB}, structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB},
}; };
use crate::{ use crate::{
arch::arch::{FRAME_ALLOCATOR, get_allocator, infinite_idle}, arch::arch::{FRAME_ALLOCATOR, get_allocator, infinite_idle, sleep},
driver::graphics::framebuffer::with_framebuffer, driver::{
println, graphics::{framebuffer::with_framebuffer, primitives::rectangle_filled},
timer::TIMER,
},
print, println,
task::scheduler::SCHEDULER, task::scheduler::SCHEDULER,
util::{align_up, serial_print}, util::{align_up, serial_print},
}; };
@@ -37,6 +41,10 @@ const UNLINK: usize = 87;
const GETDENTS64: usize = 217; const GETDENTS64: usize = 217;
const CLOCK_GETTIME: usize = 228; const CLOCK_GETTIME: usize = 228;
const EXIT_GROUP: usize = 231; 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 { pub unsafe fn malloc(size: usize, align: usize) -> *mut u8 {
let align = if align < 1 { 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 { pub unsafe fn sbrk(increment: isize) -> isize {
serial_print("sbrk called");
let mut scheduler = SCHEDULER.lock(); let mut scheduler = SCHEDULER.lock();
if scheduler.current_process == -1 { if scheduler.current_process == -1 {
return -1; return -1;
@@ -100,16 +107,17 @@ pub unsafe fn sbrk(increment: isize) -> isize {
if new < heap_base { if new < heap_base {
return -1; return -1;
} }
if new > stack_top - 3 * 4096 { if new > stack_top - 16384 * 4096 {
// 67 mib max
return -1; return -1;
} }
if new > old { if new > old {
let map_start = align_up(old, 4096); let map_start = align_up(old, 4096);
let map_end = align_up(new, 4096); let map_end = align_up(new, 4096);
for addr in (map_start..map_end).step_by(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 // TODO: do not use x86_64 only
let virt_addr = VirtAddr::new(addr); let virt_addr = VirtAddr::new(addr);
let page = Page::<Size4KiB>::containing_address(virt_addr); let page = Page::<Size4KiB>::containing_address(virt_addr);
@@ -128,6 +136,11 @@ pub unsafe fn sbrk(increment: isize) -> isize {
) )
.unwrap() .unwrap()
.flush(); .flush();
core::ptr::write_bytes(virt_addr.as_mut_ptr::<u8>(), 0, 4096);
}
} else {
return -1;
} }
} }
} }
@@ -135,8 +148,6 @@ pub unsafe fn sbrk(increment: isize) -> isize {
process.heap_end = new; process.heap_end = new;
serial_print("sbrk finished");
return old as isize; return old as isize;
}) })
.unwrap_or(-1); .unwrap_or(-1);
@@ -150,21 +161,58 @@ pub unsafe extern "C" fn syscall_dispatch(
arg2: isize, arg2: isize,
) -> isize { ) -> isize {
match num { match num {
SYS_BRK => sbrk(arg0), BRK => sbrk(arg0),
SYS_WRITE => { WRITE => {
let buf_ptr = arg1 as *const u8; let buf_ptr = arg1 as *const u8;
let len = arg2 as usize; let len = arg2 as usize;
let bytes: &[u8] = unsafe { core::slice::from_raw_parts(buf_ptr, len) }; let bytes: &[u8] = unsafe { core::slice::from_raw_parts(buf_ptr, len) };
let s = core::str::from_utf8(bytes).unwrap_or("<non-utf8>"); if let Ok(s) = core::str::from_utf8(bytes) {
println!("SYS_WRITE called: {:?} {:?}", s, len); print!("{}", s);
} else {
for byte in bytes {
if *byte == b'\0' {
continue;
}
print!("{}", *byte as char);
}
}
with_framebuffer(|fb| fb.swap()); with_framebuffer(|fb| fb.swap());
0 0
} }
SYS_EXIT => { EXIT => {
println!("Program exit."); println!("Program exit: {}", arg0);
with_framebuffer(|fb| fb.swap()); with_framebuffer(|fb| fb.swap());
infinite_idle(); 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 _ => -38, // syscall not found
} }
} }

View File

@@ -3,6 +3,8 @@ use core::{
sync::atomic::{AtomicU64, Ordering}, sync::atomic::{AtomicU64, Ordering},
}; };
use crate::util::serial_print;
pub static TIMER: Timer = Timer::new(); pub static TIMER: Timer = Timer::new();
pub struct Timer { pub struct Timer {
@@ -50,6 +52,11 @@ impl Time {
pub fn elapsed(&self) -> u64 { pub fn elapsed(&self) -> u64 {
self.interrupt_count 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 { impl Add for Time {

View File

@@ -39,6 +39,8 @@ impl Locked<Scheduler> {
let stack_top = guard.processes[&pid].stack_top; let stack_top = guard.processes[&pid].stack_top;
guard.current_process = pid as i64; guard.current_process = pid as i64;
drop(guard);
enter_usermode(entry_point as u64, (stack_top & !0xF) - 8); enter_usermode(entry_point as u64, (stack_top & !0xF) - 8);
} }

View File

@@ -26,7 +26,7 @@ use crate::{
static CURSOR_BYTES: &[u8] = include_bytes!("../../assets/cursors/default.bmp"); 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()]); struct AlignedElf([u8; include_bytes!("../../assets/doomgeneric").len()]);
static TEST_ELF: AlignedElf = AlignedElf(*include_bytes!("../../assets/doomgeneric")); static TEST_ELF: AlignedElf = AlignedElf(*include_bytes!("../../assets/doomgeneric"));
static TEST_ELF_BYTES: &[u8] = &TEST_ELF.0; static TEST_ELF_BYTES: &[u8] = &TEST_ELF.0;
@@ -86,8 +86,6 @@ fn boot_animation() {
pub fn userspace_init(mapper: &mut OffsetPageTable) -> ! { pub fn userspace_init(mapper: &mut OffsetPageTable) -> ! {
// this is just a stub // this is just a stub
boot_animation();
let (entry_point, heap_base) = load_file(mapper, TEST_ELF_BYTES); let (entry_point, heap_base) = load_file(mapper, TEST_ELF_BYTES);
println!("Entry point: {:?}", entry_point); println!("Entry point: {:?}", entry_point);

View File

@@ -1,4 +1,3 @@
gcc -nostdlib -nostdinc -static -static-pie \ GCC_INCLUDES=$(gcc -print-file-name=include)
-o $1 $1.c \ SYS_INCLUDES=/usr/include
-L../../libxunil/target/release -l:libxunil.a 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
mv $1 ../../../assets/$1.elf

View File

@@ -1,2 +1 @@
#include <stddef.h>
#include <stdint.h> #include <stdint.h>

View File

@@ -1,18 +1,9 @@
#pragma once #pragma once
#include <sys/types.h>
#include <stddef.h> #include <stddef.h>
#include <stdarg.h> #include <stdarg.h>
typedef struct _iobuf typedef struct FILE FILE;
{
char* _ptr;
int _cnt;
char* _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
} FILE;
extern FILE *stdin; extern FILE *stdin;
extern FILE *stdout; extern FILE *stdout;
@@ -20,6 +11,10 @@ extern FILE *stderr;
#define EOF (-1) #define EOF (-1)
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
FILE *fopen(const char *path, const char *mode); FILE *fopen(const char *path, const char *mode);
int fclose(FILE *fp); int fclose(FILE *fp);
size_t fread(void *ptr, size_t size, size_t nmemb, 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 fputs(const char *s, FILE *fp);
int feof(FILE *fp); int feof(FILE *fp);
int ferror(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 printf(const char *fmt, ...);
int puts(const char *s);
int putchar(int c);
int fprintf(FILE *fp, const char *fmt, ...); int fprintf(FILE *fp, const char *fmt, ...);
int sprintf(char *buf, const char *fmt, ...); int sprintf(char *buf, const char *fmt, ...);
int snprintf(char *buf, size_t size, 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 vsnprintf(char *buf, size_t size, const char *fmt, va_list ap);
int vfprintf(FILE *fp, 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); void exit(int code);
int sscanf(const char *str, const char *format, ...);

View File

@@ -1,4 +1,5 @@
#pragma once #pragma once
#include <inttypes.h>
#include <stddef.h> #include <stddef.h>
void *malloc(size_t size); void *malloc(size_t size);
@@ -18,3 +19,6 @@ double strtod(const char *s, char **endptr);
char *getenv(const char *name); char *getenv(const char *name);
void qsort(void *base, size_t nmemb, size_t size, int (*cmp)(const void *, const void *)); void qsort(void *base, size_t nmemb, size_t size, int (*cmp)(const void *, const void *));
int abs(int x); int abs(int x);
int system(const char *cmd);
int draw_pixel(uint32_t x, uint32_t y, uint32_t color);
int framebuffer_swap();

View File

@@ -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)] #[unsafe(no_mangle)]
extern "C" fn fopen(path: *const u8, mode: *const u8) -> *mut u8 { pub static mut stderr: *mut FILE = unsafe { &raw mut STDERR_FILE };
0x10 as *mut u8 #[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)] #[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 0
} }
#[unsafe(no_mangle)] #[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 0
} }
#[unsafe(no_mangle)] #[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 0
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
extern "C" fn fwrite(ptr: *mut u8, size: i32, nmemb: *const u8, fp: *const u8) -> i32 { extern "C" fn fwrite(ptr: *mut u8, size: usize, count: usize, fp: *mut FILE) -> usize {
0 if ptr.is_null() || fp.is_null() || unsafe { (*fp).fd < 0 || (*fp).fd >= 16 } {
return 0;
}
count
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
extern "C" fn fseek(s: *const u8, size: i32, fp: *const u8) -> i32 { extern "C" fn ftell(stream: *mut FILE) -> i64 {
0 if stream.is_null() || unsafe { (*stream).fd < 0 || (*stream).fd >= 16 } {
return -1;
}
unsafe { (*stream).cursor as i64 }
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
extern "C" fn ftell(fp: *const u8) -> i64 { extern "C" fn fflush(file_ptr: *mut FILE) -> i32 {
0
}
#[unsafe(no_mangle)]
extern "C" fn fflush(file_ptr: *mut u8) -> i32 {
0 0
} }
@@ -44,7 +240,7 @@ extern "C" fn mkdir(path: *const u8, mode: *const u8) -> i32 {
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
extern "C" fn remove(path: *const u8) -> i32 { extern "C" fn remove(path: *const i8) -> i32 {
0 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 { unsafe extern "C" fn vfprintf(stream: *const u8, format: *const u8, args: ...) -> i32 {
0 0
} }
#[unsafe(no_mangle)]
unsafe extern "C" fn vsnprintf(s: *mut u8, n: i32, format: *const u8, args: ...) -> i32 {
0
}

View File

@@ -2,9 +2,6 @@ use spin::mutex::Mutex;
use crate::util::align_up; 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 struct LinkedNode {
pub size: usize, pub size: usize,
pub next: Option<&'static mut LinkedNode>, pub next: Option<&'static mut LinkedNode>,
@@ -28,6 +25,12 @@ pub struct LinkedListAllocator {
head: LinkedNode, head: LinkedNode,
} }
#[derive(Clone, Copy)]
pub struct Allocation {
pub start: usize,
pub end: usize,
}
impl LinkedListAllocator { impl LinkedListAllocator {
pub const fn new() -> LinkedListAllocator { pub const fn new() -> LinkedListAllocator {
Self { Self {
@@ -36,58 +39,70 @@ impl LinkedListAllocator {
} }
pub unsafe fn add_free_memory_region(&mut self, start: usize, size: usize) { pub unsafe fn add_free_memory_region(&mut self, start: usize, size: usize) {
assert_eq!(align_up(start, core::mem::align_of::<LinkedNode>()), start); // Check if we are up at least 1 LinkedNode size
assert!(size >= core::mem::size_of::<LinkedNode>()); // 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 { unsafe {
linked_node_ptr.write(linked_node); // write the data, very risky assert_eq!(align_up(start, 16), start);
self.head.next = Some(&mut *linked_node_ptr); assert!(size >= core::mem::size_of::<LinkedNode>());
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( pub fn find_region(&mut self, size: usize) -> Option<Allocation> {
&mut self,
size: usize,
align: usize,
) -> Option<(&'static mut LinkedNode, usize)> {
let mut current = &mut self.head; let mut current = &mut self.head;
while let Some(ref mut region) = current.next { while let Some(ref region) = current.next {
if let Ok(alloc_start) = Self::alloc_from_region(&region, size, align) { let mut alloc = match Self::alloc_from_region(region, size) {
let next = region.next.take(); Ok(a) => a,
let ret = Some((current.next.take().unwrap(), alloc_start)); Err(()) => {
current.next = next;
return ret;
} else {
current = current.next.as_mut().unwrap(); current = current.next.as_mut().unwrap();
continue;
} }
};
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::<LinkedNode>() {
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 {
alloc.end = region_end;
current.next = old_next;
}
return Some(alloc);
} }
None None
} }
fn alloc_from_region(region: &LinkedNode, size: usize, align: usize) -> Result<usize, ()> { fn alloc_from_region(region: &LinkedNode, size: usize) -> Result<Allocation, ()> {
let alloc_start = align_up(region.start_addr() + core::mem::size_of::<usize>(), align); let start = region.start_addr();
let alloc_end = (alloc_start - core::mem::size_of::<usize>()) let end_unaligned = start.checked_add(size).ok_or(())?;
.checked_add(size) let end = align_up(end_unaligned, 16);
.ok_or(())?; // check for overflows if end > region.end_addr() {
if alloc_end > region.end_addr() {
return Err(()); return Err(());
} }
Ok(Allocation { start, end })
let excess_size = region.end_addr() - alloc_end;
if excess_size > 0 && excess_size < core::mem::size_of::<LinkedNode>() {
// if the remaining space is not enough for another LinkedNode, skip this region.
return Err(());
}
Ok(alloc_start)
} }
} }

View File

@@ -1,33 +1,57 @@
#![no_std] #![no_std]
#![feature(c_variadic)] #![feature(c_variadic)]
use core::{
use core::ptr::{null, null_mut}; ffi::VaList,
fmt::{Error, Result, Write},
ptr::{addr_of_mut, null, null_mut},
usize,
};
use crate::{ use crate::{
mem::{malloc, memcpy, memset}, mem::{malloc, memcpy},
syscall::{EXIT, WRITE, syscall3}, syscall::{DRAW_BUFFER, DRAW_PIXEL, EXIT, FRAMEBUFFER_SWAP, WRITE, syscall0, syscall3},
}; };
pub mod file; pub mod file;
pub mod heap; pub mod heap;
pub mod mem; pub mod mem;
pub mod syscall; pub mod syscall;
pub mod time;
pub mod util; 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)] #[unsafe(no_mangle)]
extern "C" fn write(fd: i64, buf: *const u8, count: usize) -> isize { extern "C" fn write(fd: i32, buf: *const u8, count: usize) -> isize {
unsafe { syscall3(WRITE, fd as usize, buf as usize, count) } unsafe { syscall3(WRITE, fd as isize, buf as isize, count as isize) }
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
extern "C" fn exit(code: i64) -> ! { extern "C" fn exit(code: i32) -> ! {
unsafe { syscall3(EXIT, code as usize, 0, 0) }; unsafe { syscall3(EXIT, code as isize, 0, 0) };
loop {} loop {
unsafe { core::arch::asm!("nop") };
}
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
extern "C" fn strlen(s: *const u8) -> usize { extern "C" fn strlen(s: *const u8) -> usize {
let mut len = 0; let mut len = 0usize;
while unsafe { *s.add(len) } != 0 { while unsafe { *s.add(len) } != 0 {
len += 1; len += 1;
} }
@@ -35,33 +59,292 @@ extern "C" fn strlen(s: *const u8) -> usize {
} }
#[unsafe(no_mangle)] #[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, s, strlen(s));
write(1, b"\n\0".as_ptr(), 1);
0 0
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
extern "C" fn putchar(s: i32) -> isize { extern "C" fn putchar(c: i32) -> i32 {
write(1, (s as u8 + b'0') as *const u8, 1); let b = c as u8;
write(1, core::ptr::addr_of!(b), 1);
0 0
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
extern "C" fn abs(n: i64) -> i64 { extern "C" fn abs(n: i32) -> i32 {
n.abs() n.abs()
} }
#[unsafe(no_mangle)] struct BufWriter {
unsafe extern "C" fn printf(format: *const u8, args: ...) { buf: *mut u8,
unsafe { syscall3(WRITE, 1, format as usize, strlen(format)) }; 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<usize> = 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)] #[unsafe(no_mangle)]
extern "C" fn atoi(mut c: *const u8) -> i64 { pub unsafe extern "C" fn printf(fmt: *const u8, mut args: ...) -> i32 {
let mut value: i64 = 0; let mut writer = StdoutWriter { size: 0 };
let mut sign: i64 = 1;
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 { unsafe {
while (*c).is_ascii_whitespace() { while (*c).is_ascii_whitespace() {
c = c.add(1); c = c.add(1);
@@ -75,7 +358,7 @@ extern "C" fn atoi(mut c: *const u8) -> i64 {
} }
while (*c).is_ascii_digit() { while (*c).is_ascii_digit() {
value *= 10; value *= 10;
value += ((*c) - b'0') as i64; value += ((*c) - b'0') as i32;
c = c.add(1); 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; let mut len = 0;
while len < n { while len < n {
let mut c_1 = unsafe { *str_1.add(len) }; let mut c_1 = unsafe { *str_1.add(len) };
let mut c_2 = unsafe { *str_2.add(len) }; let mut c_2 = unsafe { *str_2.add(len) };
if ignore_case {
if case {
c_1 = c_1.to_ascii_lowercase(); c_1 = c_1.to_ascii_lowercase();
c_2 = c_2.to_ascii_lowercase(); c_2 = c_2.to_ascii_lowercase();
} }
if c_1 != c_2 { 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 { if c_1 == 0 {
return 0; return 0;
} }
len += 1; len += 1;
} }
0 0
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
unsafe extern "C" fn strcasecmp(str_1: *const u8, str_2: *const u8) -> i32 { 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(no_mangle)]
unsafe extern "C" fn strcmp(str_1: *const u8, str_2: *const u8) -> i32 { 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(no_mangle)]
unsafe extern "C" fn strncasecmp(str_1: *const u8, str_2: *const u8, n: i32) -> i32 { unsafe extern "C" fn strncasecmp(str_1: *const u8, str_2: *const u8, n: usize) -> i32 {
compare_str(str_1, str_2, true, n as usize) compare_str(str_1, str_2, true, n)
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
unsafe extern "C" fn strncmp(str_1: *const u8, str_2: *const u8, n: i32) -> i32 { unsafe extern "C" fn strncmp(str_1: *const u8, str_2: *const u8, n: usize) -> i32 {
compare_str(str_1, str_2, false, n as usize) compare_str(str_1, str_2, false, n)
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
unsafe extern "C" fn strncpy(dest: *mut u8, source: *const u8, n: usize) -> *mut u8 { unsafe extern "C" fn draw_pixel(x: u32, y: u32, color: u32) -> i32 {
let mut i = 0usize;
unsafe { unsafe {
return syscall3(DRAW_PIXEL, x as isize, y as isize, color as isize) as i32;
}
}
#[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 { while i < n {
let b = *source.add(i); let b = unsafe { *source.add(i) };
*dest.add(i) = b; unsafe { *dest.add(i) = b };
i += 1;
if b == 0 { if b == 0 {
while i < n { let mut j = i + 1;
*dest.add(i) = 0; while j < n {
i += 1; unsafe { *dest.add(j) = 0 };
j += 1;
} }
break; break;
} }
} i += 1;
} }
dest dest
@@ -277,7 +579,7 @@ unsafe extern "C" fn strstr(haystack: *const u8, needle: *const u8) -> *const u8
} }
#[unsafe(no_mangle)] #[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() { if s.is_null() {
return null(); return null();
} }
@@ -286,7 +588,7 @@ unsafe extern "C" fn strchr(s: *const u8, ch: u8) -> *const u8 {
unsafe { unsafe {
while *s.add(i) != 0 { while *s.add(i) != 0 {
if *s.add(i) == ch { if *s.add(i) == ch as u8 {
return s.add(i); return s.add(i);
} }
@@ -321,21 +623,40 @@ unsafe extern "C" fn strrchr(s: *const u8, ch: u8) -> *const u8 {
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
unsafe extern "C" fn toupper(char: u8) -> u8 { unsafe extern "C" fn toupper(char: i32) -> i32 {
char.to_ascii_uppercase() (char as u8).to_ascii_uppercase() as i32
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
unsafe extern "C" fn tolower(char: u8) -> u8 { unsafe extern "C" fn tolower(char: i32) -> i32 {
char.to_ascii_lowercase() (char as u8).to_ascii_lowercase() as i32
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
extern "C" fn system(cmd: *const u8) -> i32 { extern "C" fn system(cmd: *const u8) -> i32 {
0 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] #[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! { fn panic(error: &core::panic::PanicInfo) -> ! {
loop {} exit(-1)
} }

View File

@@ -1,17 +1,23 @@
use core::ptr::null_mut; use core::{mem, ptr::null_mut, usize};
use crate::{ use crate::{
heap::ALLOCATOR, heap::{ALLOCATOR, LinkedNode},
syscall::{BRK, syscall1}, syscall::{BRK, syscall1},
util::align_up,
}; };
#[unsafe(no_mangle)] pub fn sbrk(increment: i64) -> isize {
pub extern "C" fn sbrk(increment: i64) -> i64 { unsafe { syscall1(BRK, increment as isize) as isize }
unsafe { syscall1(BRK, increment as usize) as i64 }
} }
const MAX_SIZE: u64 = 18446744073709551615; const MAX_SIZE: u64 = 18446744073709551615;
#[repr(C, align(16))]
struct Header {
size: usize,
_pad: usize,
}
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn calloc(count: u64, size: u64) -> *mut u8 { pub extern "C" fn calloc(count: u64, size: u64) -> *mut u8 {
if count != 0 && size > MAX_SIZE / count { 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; let mut total = count * size;
if total == 0 { if total == 0 {
total = 1; total = 1;
} }
let ptr = malloc(total); let ptr = malloc(total);
if ptr.is_null() { if ptr.is_null() {
return null_mut(); return null_mut();
} }
memset(ptr, 0, total as usize); memset(ptr, 0, total as usize);
ptr ptr
} }
@@ -40,78 +43,126 @@ pub extern "C" fn free(ptr: *mut u8) {
if ptr.is_null() { if ptr.is_null() {
return; return;
} }
unsafe {
let size = *(((ptr as usize) - core::mem::size_of::<usize>()) as *const usize);
unsafe {
let header_ptr = (ptr as usize - mem::size_of::<Header>()) as *mut Header;
let total = (*header_ptr).size;
let mut allocator = ALLOCATOR.lock(); let mut allocator = ALLOCATOR.lock();
allocator allocator.add_free_memory_region(header_ptr as usize, total);
.add_free_memory_region(ptr as usize - core::mem::size_of::<usize>(), size as usize);
} }
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn malloc(size: u64) -> *mut u8 { 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::<Header>();
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(); let mut allocator = ALLOCATOR.lock();
if let Some(region) = allocator.find_region(size as usize, 16) { if let Some(region) = allocator.find_region(needed) {
return region.1 as *mut u8; unsafe {
} else { let header_ptr = region.start as *mut Header;
let start_addr: i64 = sbrk(size as i64); header_ptr.write(Header {
if start_addr == -1 { size: region.end - region.start,
_pad: 0,
});
return (region.start + hdr) as *mut u8;
}
}
let min_region = mem::size_of::<LinkedNode>();
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(); return null_mut();
} }
unsafe { allocator.add_free_memory_region(start_addr as usize, size as usize) }; 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); drop(allocator);
malloc(size) malloc(size)
} }
}
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn memcpy(dest_str: *mut u8, src_str: *const u8, n: usize) -> *mut u8 { 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) }; unsafe { core::ptr::copy(src_str, dest_str, n) };
dest_str dest_str
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn memset(str: *mut u8, c: i64, n: usize) -> *mut u8 { pub extern "C" fn memset(dst: *mut u8, c: i64, n: usize) -> *mut u8 {
unsafe { if dst.is_null() || n == 0 {
core::ptr::write_bytes(str, c as u8, n); 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)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn realloc(ptr: *mut u8, size: usize) -> *mut u8 { pub unsafe extern "C" fn realloc(ptr: *mut u8, size: usize) -> *mut u8 {
unsafe {
if size == 0 { if size == 0 {
free(ptr); free(ptr);
return null_mut(); return null_mut();
} }
let header = ((ptr as usize) - core::mem::size_of::<usize>()) as *mut usize; if ptr.is_null() {
let old_size = *header; return malloc(size as u64);
}
if old_size == size { unsafe {
let hdr = mem::size_of::<Header>();
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; 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); let new_ptr = malloc(size as u64);
if new_ptr.is_null() { if new_ptr.is_null() {
return null_mut(); return null_mut();
} }
core::ptr::copy_nonoverlapping(ptr, new_ptr, old_size); core::ptr::copy_nonoverlapping(ptr, new_ptr, old_payload);
free(ptr); free(ptr);
return new_ptr; new_ptr
}
} }
} }

View File

@@ -19,6 +19,10 @@ pub const UNLINK: usize = 87;
pub const GETDENTS64: usize = 217; pub const GETDENTS64: usize = 217;
pub const CLOCK_GETTIME: usize = 228; pub const CLOCK_GETTIME: usize = 228;
pub const EXIT_GROUP: usize = 231; 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)] #[inline(always)]
pub unsafe fn syscall0(num: usize) -> isize { pub unsafe fn syscall0(num: usize) -> isize {
@@ -28,6 +32,8 @@ pub unsafe fn syscall0(num: usize) -> isize {
"int 0x80", "int 0x80",
in("rax") num, in("rax") num,
lateout("rax") ret, lateout("rax") ret,
out("rdi") _, out("rcx") _, out("rdx") _, out("rsi") _,
out("r8") _, out("r9") _, out("r10") _, out("r11") _,
options(nostack) options(nostack)
); );
} }
@@ -36,7 +42,7 @@ pub unsafe fn syscall0(num: usize) -> isize {
} }
#[inline(always)] #[inline(always)]
pub unsafe fn syscall1(num: usize, arg0: usize) -> isize { pub unsafe fn syscall1(num: usize, arg0: isize) -> isize {
let ret: isize; let ret: isize;
unsafe { unsafe {
core::arch::asm!( core::arch::asm!(
@@ -44,6 +50,8 @@ pub unsafe fn syscall1(num: usize, arg0: usize) -> isize {
in("rax") num, in("rax") num,
in("rdi") arg0, in("rdi") arg0,
lateout("rax") ret, lateout("rax") ret,
out("rcx") _, out("rdx") _, out("rsi") _,
out("r8") _, out("r9") _, out("r10") _, out("r11") _,
options(nostack) options(nostack)
); );
} }
@@ -52,7 +60,7 @@ pub unsafe fn syscall1(num: usize, arg0: usize) -> isize {
} }
#[inline(always)] #[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; let ret: isize;
unsafe { unsafe {
core::arch::asm!( core::arch::asm!(

38
user/libxunil/src/time.rs Normal file
View File

@@ -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
}