From 4e0b38a3fa6d8d96156e2a8586c4d5e2b3bc20dd Mon Sep 17 00:00:00 2001 From: jbreu <20864561+jbreu@users.noreply.github.com> Date: Sun, 28 Sep 2025 15:37:15 +0200 Subject: [PATCH 01/10] load doom executable from disk --- Makefile | 25 +++++----- kernel/src/filesystem.rs | 4 ++ kernel/src/interrupt.rs | 10 +++- kernel/src/process.rs | 88 ++++++++++++------------------------ kernel/src/switch_to_ring3.S | 35 ++++++++++++++ storage/generateExt2Img.sh | 2 +- storage/test.txt | 1 - 7 files changed, 91 insertions(+), 74 deletions(-) delete mode 100644 storage/test.txt diff --git a/Makefile b/Makefile index d87f217..6c75a94 100644 --- a/Makefile +++ b/Makefile @@ -7,26 +7,27 @@ $(x86_64_asm_object_files): build/x86_64/%.o : kernel/asm/%.asm .PHONY: setup setup: $(x86_64_asm_object_files) - mkdir -p build/kernel - mkdir -p build/userspace/x86_64-unknown-none/debug/ + mkdir -p build/kernel && \ + mkdir -p build/userspace/x86_64-unknown-none/debug/ && \ mkdir -p dist/x86_64 && \ - wget https://github.com/Daivuk/PureDOOM/raw/48376ddd6bbdb70085dab91feb1c6ceef80fa9b7/doom1.wad -N - cd storage && sh generateExt2Img.sh && cd .. + wget https://github.com/Daivuk/PureDOOM/raw/48376ddd6bbdb70085dab91feb1c6ceef80fa9b7/doom1.wad -N && \ wget -P userland/src/doom/ https://raw.githubusercontent.com/Daivuk/PureDOOM/48376ddd6bbdb70085dab91feb1c6ceef80fa9b7/PureDOOM.h -N -.PHONY: iso -iso: - gcc userland/src/doom/main.c userland/src/doom/libc.c -static -nostdlib -fno-builtin -g -o build/userspace/x86_64-unknown-none/debug/helloworld -Wl,--gc-sections - objcopy --input binary --output elf64-x86-64 --binary-architecture i386 build/userspace/x86_64-unknown-none/debug/helloworld build/userspace/x86_64-unknown-none/debug/helloworld.o && \ - x86_64-elf-ld -n -o dist/x86_64/kernel.bin --unresolved-symbols=report-all -z noexecstack -T targets/x86_64/linker.ld $(x86_64_asm_object_files) build/kernel/x86_64-unknown-none/debug/libjos.a build/userspace/x86_64-unknown-none/debug/helloworld.o && \ - cp dist/x86_64/kernel.bin targets/x86_64/iso/boot/kernel.bin && \ - grub-mkrescue /usr/lib/grub/i386-pc -o dist/x86_64/kernel.iso targets/x86_64/iso +.PHONY: userland +userland: + gcc userland/src/doom/main.c userland/src/doom/libc.c -static -nostdlib -fno-builtin -g -o build/userspace/x86_64-unknown-none/debug/doom -Wl,--gc-sections .PHONY: kernel kernel: cargo rustc --manifest-path kernel/Cargo.toml --target-dir build/kernel/ -- -C no-redzone=on -C target-feature=-sse -C link-arg=-Ttargets/x86_64/linker.ld $(MAKE) iso +.PHONY: iso +iso: + x86_64-elf-ld -n -o dist/x86_64/kernel.bin --unresolved-symbols=report-all -z noexecstack -T targets/x86_64/linker.ld $(x86_64_asm_object_files) build/kernel/x86_64-unknown-none/debug/libjos.a && \ + cp dist/x86_64/kernel.bin targets/x86_64/iso/boot/kernel.bin && \ + cd storage && sh generateExt2Img.sh && cd .. && \ + grub-mkrescue /usr/lib/grub/i386-pc -o dist/x86_64/kernel.iso targets/x86_64/iso .PHONY: all -all: setup kernel \ No newline at end of file +all: setup userland kernel \ No newline at end of file diff --git a/kernel/src/filesystem.rs b/kernel/src/filesystem.rs index 62ff202..86a46ed 100644 --- a/kernel/src/filesystem.rs +++ b/kernel/src/filesystem.rs @@ -37,6 +37,10 @@ impl FileHandle { } } + pub fn size(&self) -> u32 { + self.inode.size + } + pub fn read(&mut self, buffer: *mut u8, size: usize) -> u64 { let _event = core::hint::black_box(crate::instrument!()); diff --git a/kernel/src/interrupt.rs b/kernel/src/interrupt.rs index 808814a..6d0fe64 100644 --- a/kernel/src/interrupt.rs +++ b/kernel/src/interrupt.rs @@ -77,7 +77,15 @@ pub extern "C" fn isr_handler(error_code: u64, int_no: u64) { panic!("Unhandled page fault: cr2={:#x}, ec={:#x}", cr2, error_code); } } else { - panic!("Unhandled exception"); + let cr2: u64; + unsafe { + asm!("mov {}, cr2", out(reg) cr2); + } + + panic!( + "Unhandled exception: int_no={}, cr2={:#x}, ec={:#x}", + int_no, cr2, error_code + ); } } _ => DEBUG!("ISR {}", int_no), diff --git a/kernel/src/process.rs b/kernel/src/process.rs index a0aa47e..b20e743 100644 --- a/kernel/src/process.rs +++ b/kernel/src/process.rs @@ -3,11 +3,14 @@ use crate::{ }; extern crate alloc; use alloc::collections::BTreeMap; +use alloc::vec::Vec; use core::arch::asm; use core::fmt::Debug; use core::ptr::addr_of; use core::sync::atomic::AtomicUsize; use core::sync::atomic::Ordering; +use elf::abi::PT_LOAD; +use elf::endian::AnyEndian; pub static KERNEL_CR3: AtomicUsize = AtomicUsize::new(0); @@ -245,8 +248,24 @@ impl Process { &self.l3_page_directory_pointer_table as *const _ as usize, ) | PAGE_ENTRY_FLAGS_USERSPACE as usize; + let mut file_handle = FileHandle::new("/doom", 0).unwrap(); + + // put the whole file into a buffer + // TODO ensure the *kernel* has enough memory for this + let size = file_handle.size() as usize; + let mut buffer: Vec = Vec::with_capacity(size); + unsafe { + buffer.set_len(size); // unsafe, but we will overwrite all bytes + } + let program_slice = unsafe { core::slice::from_raw_parts_mut(buffer.as_mut_ptr(), size) }; + let bytes_read = file_handle.read(program_slice.as_mut_ptr(), size); + if bytes_read as usize != size { + panic!("Error reading file"); + } + // allocate enough pages at beginning of virtual memory for elf loading - let heap_page_number = (self.get_size_of_program() + PAGE_SIZE - 1) / PAGE_SIZE; + let heap_page_number = + (self.get_size_of_program(program_slice) + PAGE_SIZE - 1) / PAGE_SIZE; self.heap_l2_table_number = (heap_page_number + PAGE_TABLE_ENTRIES - 1) / PAGE_TABLE_ENTRIES; @@ -302,7 +321,7 @@ impl Process { self.rsp = USERSPACE_STACK_TOP_ADDRESS as u64; - let (entry, v_addr, p_memsz) = self.load_elf_from_bin(); + let (entry, v_addr, p_memsz) = self.load_elf_from_bin(&program_slice); self.rip = entry; self.init_process_heap(v_addr, p_memsz); @@ -591,36 +610,12 @@ impl Process { } // TODO reduce code duplication with load_elf_from_bin - pub fn get_size_of_program(&self) -> usize { + pub fn get_size_of_program(&mut self, program_slice: &[u8]) -> usize { let _event = core::hint::black_box(crate::instrument!()); - unsafe extern "C" { - static mut _binary_build_userspace_x86_64_unknown_none_debug_helloworld_start: u8; - static mut _binary_build_userspace_x86_64_unknown_none_debug_helloworld_end: u8; - } unsafe { - kprint!( - "embedded elf file\nstart: {:x}\n end: {:x}\n", - addr_of!(_binary_build_userspace_x86_64_unknown_none_debug_helloworld_start) - as *const u8 as usize, - addr_of!(_binary_build_userspace_x86_64_unknown_none_debug_helloworld_end) - as *const u8 as usize - ); - - let size = addr_of!(_binary_build_userspace_x86_64_unknown_none_debug_helloworld_end) - as *const u8 as usize - - addr_of!(_binary_build_userspace_x86_64_unknown_none_debug_helloworld_start) - as *const u8 as usize; - - let slice = core::slice::from_raw_parts( - addr_of!(_binary_build_userspace_x86_64_unknown_none_debug_helloworld_start), - size, - ); - - use elf::abi::PT_LOAD; - use elf::endian::AnyEndian; - - let file = elf::ElfBytes::::minimal_parse(slice).expect("Open test1"); + let file = + elf::ElfBytes::::minimal_parse(program_slice).expect("Open test1"); let elf_header = file.ehdr; @@ -652,37 +647,12 @@ impl Process { } } - pub fn load_elf_from_bin(&mut self) -> (usize, usize, usize) { + pub fn load_elf_from_bin(&mut self, program_slice: &[u8]) -> (usize, usize, usize) { let _event = core::hint::black_box(crate::instrument!()); - unsafe extern "C" { - static mut _binary_build_userspace_x86_64_unknown_none_debug_helloworld_start: u8; - static mut _binary_build_userspace_x86_64_unknown_none_debug_helloworld_end: u8; - } - unsafe { - kprint!( - "embedded elf file\nstart: {:x}\n end: {:x}\n", - addr_of!(_binary_build_userspace_x86_64_unknown_none_debug_helloworld_start) - as *const u8 as usize, - addr_of!(_binary_build_userspace_x86_64_unknown_none_debug_helloworld_end) - as *const u8 as usize - ); - - let size = addr_of!(_binary_build_userspace_x86_64_unknown_none_debug_helloworld_end) - as *const u8 as usize - - addr_of!(_binary_build_userspace_x86_64_unknown_none_debug_helloworld_start) - as *const u8 as usize; - - let slice = core::slice::from_raw_parts( - addr_of!(_binary_build_userspace_x86_64_unknown_none_debug_helloworld_start), - size, - ); - - use elf::abi::PT_LOAD; - use elf::endian::AnyEndian; - - let file = elf::ElfBytes::::minimal_parse(slice).expect("Open test1"); + let file = + elf::ElfBytes::::minimal_parse(program_slice).expect("Open test1"); let elf_header = file.ehdr; @@ -709,8 +679,8 @@ impl Process { mov rsi, {} mov rdi, {} rep movsb", - in(reg) phdr.p_memsz, - in(reg) addr_of!(_binary_build_userspace_x86_64_unknown_none_debug_helloworld_start) as *const u8 as usize + phdr.p_offset as usize, + in(reg) phdr.p_filesz, + in(reg) program_slice.as_ptr() as usize + phdr.p_offset as usize, in(reg) phdr.p_vaddr, out("rcx") _, out("rsi") _, diff --git a/kernel/src/switch_to_ring3.S b/kernel/src/switch_to_ring3.S index 207b31c..b3d2503 100644 --- a/kernel/src/switch_to_ring3.S +++ b/kernel/src/switch_to_ring3.S @@ -42,6 +42,41 @@ jump_usermode: mov byte ptr [rip + SCHEDULING_BLOCKED], 0 + // clear all general purpose registers to avoid leaking information + mov rax, 0 + mov rbx, 0 + // rcx is used for syscall return address + mov rdx, 0 + mov rsi, 0 + mov rdi, 0 + mov rbp, 0 + mov r8, 0 + mov r9, 0 + mov r10, 0 + // r11 is used for flags + mov r12, 0 + mov r13, 0 + mov r14, 0 + mov r15, 0 + + // also clear xmm registers + pxor xmm0, xmm0 + pxor xmm1, xmm1 + pxor xmm2, xmm2 + pxor xmm3, xmm3 + pxor xmm4, xmm4 + pxor xmm5, xmm5 + pxor xmm6, xmm6 + pxor xmm7, xmm7 + pxor xmm8, xmm8 + pxor xmm9, xmm9 + pxor xmm10, xmm10 + pxor xmm11, xmm11 + pxor xmm12, xmm12 + pxor xmm13, xmm13 + pxor xmm14, xmm14 + pxor xmm15, xmm15 + sysretq //use "o64 sysret" if you assemble with NASM syscall_handler: diff --git a/storage/generateExt2Img.sh b/storage/generateExt2Img.sh index 5a5f898..548c1ef 100644 --- a/storage/generateExt2Img.sh +++ b/storage/generateExt2Img.sh @@ -24,8 +24,8 @@ fi # sudo chown "$USER":"$USER" /tmp/disk # Copy the file -sudo cp test.txt /tmp/disk/ sudo cp ../doom1.wad /tmp/disk/devdatadoom1.wad +sudo cp ../build/userspace/x86_64-unknown-none/debug/doom /tmp/disk/doom # List files ls -l -a /tmp/disk diff --git a/storage/test.txt b/storage/test.txt deleted file mode 100644 index 40a4ec8..0000000 --- a/storage/test.txt +++ /dev/null @@ -1 +0,0 @@ -First JOS file! \ No newline at end of file From e8f335d92998de733376ff4b62d9b10b382706a2 Mon Sep 17 00:00:00 2001 From: jbreu <20864561+jbreu@users.noreply.github.com> Date: Sun, 19 Oct 2025 10:18:41 +0200 Subject: [PATCH 02/10] some steps forward --- .gitignore | 3 +- kernel/src/process.rs | 9 +++ kernel/src/syscall.rs | 6 ++ kernel/src/userland.rs | 6 ++ userland/build_dash.sh | 33 ++++++----- userland/usr/include/arpa/inet.h | 0 userland/usr/include/dirent.h | 30 ++++++---- userland/usr/include/fnmatch.h | 2 +- userland/usr/include/libc.h | 66 +++++++++++----------- userland/usr/include/locale.h | 8 +++ userland/usr/include/pwd.h | 13 +++++ userland/usr/include/stdbool.h | 7 ++- userland/usr/include/string.h | 43 ++++++++------- userland/usr/include/sys/mman.h | 0 userland/usr/include/termios.h | 50 +++++++++++++++++ userland/usr/include/unistd.h | 32 +++++++---- userland/usr/include/wchar.h | 21 +++++++ userland/usr/include/wctype.h | 2 + userland/usr/libc.c | 94 ++++++++++++++++++++++++++++++++ 19 files changed, 330 insertions(+), 95 deletions(-) create mode 100644 userland/usr/include/arpa/inet.h create mode 100644 userland/usr/include/locale.h create mode 100644 userland/usr/include/pwd.h create mode 100644 userland/usr/include/sys/mman.h create mode 100644 userland/usr/include/wchar.h create mode 100644 userland/usr/include/wctype.h diff --git a/.gitignore b/.gitignore index 4afe0e8..8fd3242 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,5 @@ tools/perf/chrome_trace.json tools/perf/cachegrind.out tools/perf/profile.proto tools/perf/output.pb.gz -tools/perf/__pycache__/ \ No newline at end of file +tools/perf/__pycache__/ +userland/dash-0.5.13/ diff --git a/kernel/src/process.rs b/kernel/src/process.rs index b20e743..5cca355 100644 --- a/kernel/src/process.rs +++ b/kernel/src/process.rs @@ -147,6 +147,8 @@ pub struct Process { file_handles: BTreeMap, next_handle_id: u64, + + parent_id: u64, } impl Debug for Process { @@ -186,6 +188,8 @@ impl Process { working_directory: "/", file_handles: BTreeMap::new(), next_handle_id: 1, + + parent_id: 0, } } @@ -776,4 +780,9 @@ impl Process { return 0; } } + + pub fn get_parent_id(&self) -> u64 { + let _event = core::hint::black_box(crate::instrument!()); + self.parent_id + } } diff --git a/kernel/src/syscall.rs b/kernel/src/syscall.rs index c6edb6a..36e01e9 100644 --- a/kernel/src/syscall.rs +++ b/kernel/src/syscall.rs @@ -45,6 +45,7 @@ pub extern "C" fn system_call() -> u64 { 14 => return syscall_stat(arg0 as *const u64, arg1 as *mut u64), 15 => return syscall_chdir(arg0 as *const u64), 16 => return syscall_getcwd(arg0 as *mut u64, arg1), + 17 => return syscall_getppid(), _ => { ERROR!("Undefined system call triggered: {}", syscall_nr); return 0xdeadbeef; @@ -216,3 +217,8 @@ fn syscall_getcwd(buf: *mut u64, size: u64) -> u64 { } return cwd_len as u64; } + +fn syscall_getppid() -> u64 { + let _event = core::hint::black_box(crate::instrument!()); + USERLAND.lock().get_current_process_parent_id() as u64 +} diff --git a/kernel/src/userland.rs b/kernel/src/userland.rs index 3ae261e..9625329 100644 --- a/kernel/src/userland.rs +++ b/kernel/src/userland.rs @@ -117,6 +117,12 @@ impl Userland { &mut self.processes[self.current_process] } + + pub fn get_current_process_parent_id(&self) -> usize { + let _event = core::hint::black_box(crate::instrument!()); + + self.processes[self.current_process].get_parent_id() + } } // very simple scheduler diff --git a/userland/build_dash.sh b/userland/build_dash.sh index 2db3622..6adcd9f 100644 --- a/userland/build_dash.sh +++ b/userland/build_dash.sh @@ -5,19 +5,19 @@ mv /usr/include /usr/include-bak | true cd usr -gcc -c *.c +gcc -c -ffreestanding -fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-rtti libc.c ar rcs libc.a *.o -mv libc.a lib +mv libc.a lib/libc.a -cd ../dash +cd ../dash-0.5.13/ -export CFLAGS="--sysroot=/root/env/userland/ -I/root/env/userland/usr/include/" -export CPPFLAGS="--sysroot=/root/env/userland/ -I/root/env/userland/usr/include/" -export CPPFLAGS_FOR_BUILD="-I/root/env/userland/usr/include/" +export CFLAGS="--sysroot=/root/env/userland/ -I/root/env/userland/usr/include/ -fno-unwind-tables -fno-exceptions -fno-asynchronous-unwind-tables" +export CPPFLAGS="--sysroot=/root/env/userland/ -I/root/env/userland/usr/include/ -fno-unwind-tables -fno-exceptions -fno-asynchronous-unwind-tables" +export CPPFLAGS_FOR_BUILD="-I/root/env/userland/usr/include/ -fno-unwind-tables -fno-exceptions -fno-asynchronous-unwind-tables" -export LDFLAGS="-L/root/env/userland/usr/lib -static -nostdlib -fno-builtin -Wl,--trace" -export LOCAL_LDFLAGS="-L/root/env/userland/usr/lib -static -nostdlib -fno-builtin -Wl,--trace" -export LDFLAGS_FOR_BUILD="-L/root/env/userland/usr/lib -static -nostdlib -fno-builtin -Wl,--trace" +export LDFLAGS="-L/root/env/userland/usr/lib/ -static -nostdlib -fno-builtin" +export LOCAL_LDFLAGS="-L/root/env/userland/usr/lib/ -static -nostdlib -fno-builtin" +export LDFLAGS_FOR_BUILD="-L/root/env/userland/usr/lib/ -static -nostdlib -fno-builtin" export LIBS="-lc" if [ $# = 1 ] && [ "$1" = "-c" ]; then @@ -27,7 +27,14 @@ if [ $# = 1 ] && [ "$1" = "-c" ]; then fi # provide a sed script which replaces LDFLAGS = -static -Wl,--fatal-warnings in Makefile with above LDFLAGS -sed -i 's/LDFLAGS = -static -Wl,--fatal-warnings/LDFLAGS = -L\/root\/env\/userland\/usr\/lib -static -nostdlib -fno-builtin -Wl,--trace/g' Makefile -sed -i 's/LDFLAGS = -static -Wl,--fatal-warnings/LDFLAGS = -L\/root\/env\/userland\/usr\/lib -static -nostdlib -fno-builtin -Wl,--trace/g' src/Makefile - -make \ No newline at end of file +for file in Makefile src/Makefile; do + if grep -q 'LDFLAGS = -static' "$file"; then + sed -i 's/LDFLAGS = -static/LDFLAGS = -L\/root\/env\/userland\/usr\/lib\/ -static -nostdlib -fno-builtin/g' "$file" + echo "Updated LDFLAGS in $file" + else + echo "LDFLAGS pattern not found in $file" + fi +done + +#make clean +make V=1 diff --git a/userland/usr/include/arpa/inet.h b/userland/usr/include/arpa/inet.h new file mode 100644 index 0000000..e69de29 diff --git a/userland/usr/include/dirent.h b/userland/usr/include/dirent.h index e8e7772..b0e0601 100644 --- a/userland/usr/include/dirent.h +++ b/userland/usr/include/dirent.h @@ -1,18 +1,28 @@ #include "sys/stat.h" -struct dirent64 - { +/* File types for `d_type'. */ +#define DT_UNKNOWN 0 +#define DT_FIFO 1 +#define DT_CHR 2 +#define DT_DIR 4 +#define DT_BLK 6 +#define DT_REG 8 +#define DT_LNK 10 +#define DT_SOCK 12 +#define DT_WHT 14 + +struct dirent64 { #ifndef __USE_FILE_OFFSET64 - ino_t d_ino; - off_t d_off; + ino_t d_ino; + off_t d_off; #else - __ino64_t d_ino; - __off64_t d_off; + __ino64_t d_ino; + __off64_t d_off; #endif - unsigned short int d_reclen; - unsigned char d_type; - char d_name[256]; /* We must not include limits.h! */ - }; + unsigned short int d_reclen; + unsigned char d_type; + char d_name[256]; /* We must not include limits.h! */ +}; typedef struct dirent64 DIR; diff --git a/userland/usr/include/fnmatch.h b/userland/usr/include/fnmatch.h index 5bf1cc6..dc9ff7b 100644 --- a/userland/usr/include/fnmatch.h +++ b/userland/usr/include/fnmatch.h @@ -1 +1 @@ -int fnmatch(const char *pattern, const char *string, int flags); \ No newline at end of file +static inline int fnmatch(const char *pattern, const char *string, int flags); \ No newline at end of file diff --git a/userland/usr/include/libc.h b/userland/usr/include/libc.h index dac68ee..2a0d802 100644 --- a/userland/usr/include/libc.h +++ b/userland/usr/include/libc.h @@ -1,53 +1,49 @@ #ifndef __LIBC_H__ #define __LIBC_H__ -#include "stddef.h" -#include "stdio.h" #include "ctype.h" #include "errno.h" -#include "stdint.h" #include "inttypes.h" +#include "stdbool.h" +#include "stddef.h" +#include "stdint.h" +#include "stdio.h" #include "stdlib.h" #include "string.h" -#include "stdbool.h" - - -#define DO_SYSCALL(syscall_num, output, r8_val, r9_val, r10_val) \ - asm volatile ( \ - ".intel_syntax noprefix;" \ - "push rdi;" \ - "mov rdi, %[num];" \ - "mov r8, %[r8v];" \ - "mov r9, %[r9v];" \ - "mov r10, %[r10v];" \ - "push r11;" \ - "push rcx;" \ - "syscall;" \ - "pop rcx;" \ - "pop r11;" \ - "pop rdi;" \ - ".att_syntax;" \ - : "=a" (output) \ - : [num] "r" ((uint64_t)syscall_num), \ - [r8v] "r" ((uint64_t)r8_val), \ - [r9v] "r" ((uint64_t)r9_val), \ - [r10v] "r" ((uint64_t)r10_val) \ - : "rdi", "r8", "r9", "r10", "r11", "rcx" \ - ) - +#include "sys/times.h" + +#define DO_SYSCALL(syscall_num, output, r8_val, r9_val, r10_val) \ + asm volatile( \ + ".intel_syntax noprefix;" \ + "push rdi;" \ + "mov rdi, %[num];" \ + "mov r8, %[r8v];" \ + "mov r9, %[r9v];" \ + "mov r10, %[r10v];" \ + "push r11;" \ + "push rcx;" \ + "syscall;" \ + "pop rcx;" \ + "pop r11;" \ + "pop rdi;" \ + ".att_syntax;" \ + : "=a"(output) \ + : [num] "r"((uint64_t)syscall_num), [r8v] "r"((uint64_t)r8_val), \ + [r9v] "r"((uint64_t)r9_val), [r10v] "r"((uint64_t)r10_val) \ + : "rdi", "r8", "r9", "r10", "r11", "rcx") // File I/O functions -int fread(void* handle, void* ptr, int size); -int fseek ( FILE * stream, long int offset, int origin ); -int ftell(void* handle); -int feof(void* handle); +int fread(void *handle, void *ptr, int size); +int fseek(FILE *stream, long int offset, int origin); +int ftell(void *handle); +int feof(void *handle); // System calls and utilities void draw_pixel(uint32_t x, uint32_t y, uint8_t color); -uint64_t draw_framebuffer(const uint8_t* framebuffer); +uint64_t draw_framebuffer(const uint8_t *framebuffer); uint64_t switch_vga_mode(bool vga_on); bool get_keystate(int key); -void get_time(int* sec, int* usec); +void get_time(int *sec, int *usec); #endif // __LIBC_H__ diff --git a/userland/usr/include/locale.h b/userland/usr/include/locale.h new file mode 100644 index 0000000..630d881 --- /dev/null +++ b/userland/usr/include/locale.h @@ -0,0 +1,8 @@ +char *setlocale(int category, const char *locale); + +#define LC_ALL 0 +#define LC_COLLATE 1 +#define LC_CTYPE 2 +#define LC_MONETARY 3 +#define LC_NUMERIC 4 +#define LC_TIME 5 \ No newline at end of file diff --git a/userland/usr/include/pwd.h b/userland/usr/include/pwd.h new file mode 100644 index 0000000..743fc97 --- /dev/null +++ b/userland/usr/include/pwd.h @@ -0,0 +1,13 @@ +#include "sys/stat.h" + +struct passwd { + char *pw_name; /* user name */ + char *pw_passwd; /* user password */ + uid_t pw_uid; /* user ID */ + gid_t pw_gid; /* group ID */ + char *pw_gecos; /* real name */ + char *pw_dir; /* home directory */ + char *pw_shell; /* shell program */ +}; + +struct passwd *getpwnam(const char *name); \ No newline at end of file diff --git a/userland/usr/include/stdbool.h b/userland/usr/include/stdbool.h index 0e6bc1c..9570167 100644 --- a/userland/usr/include/stdbool.h +++ b/userland/usr/include/stdbool.h @@ -1,8 +1,9 @@ #ifndef __BOOL_H__ #define __BOOL_H__ -typedef enum Bool { - false, true -} bool; +#define bool _Bool + +#define true 1 +#define false 0 #endif \ No newline at end of file diff --git a/userland/usr/include/string.h b/userland/usr/include/string.h index b804b8a..97132d7 100644 --- a/userland/usr/include/string.h +++ b/userland/usr/include/string.h @@ -1,42 +1,43 @@ #ifndef __STRING_H__ #define __STRING_H__ +#include "chartypes.h" #include "ctype.h" #include "stdbool.h" -#include "chartypes.h" -#include "stdint.h" #include "stddef.h" +#include "stdint.h" // String and memory functions -uint64_t strlen( const char* str ); -bool strcmp(const char* a, const char* b); -char * strcpy(char * destination, const char * source); -char * strncpy ( char * destination, const char * source, size_t num ); +uint64_t strlen(const char *str); +bool strcmp(const char *a, const char *b); +char *strcpy(char *destination, const char *source); +char *strncpy(char *destination, const char *source, size_t num); char *stpcpy(char *dest, const char *src); -char * strcat(char * destination, const char * source); -char * strchr ( const char * str, int character ); -intmax_t strtoimax( const char * nptr, char ** endptr, int base ); +char *strcat(char *destination, const char *source); +char *strchr(const char *str, int character); +intmax_t strtoimax(const char *nptr, char **endptr, int base); char *strchrnul(const char *s, int c); -size_t strcspn ( const char * str1, const char * str2 ); +size_t strcspn(const char *str1, const char *str2); char *strerror(int errnum); -char * strtok ( char * str, const char * delimiters ); -char * strpbrk ( const char * str1, const char * str2 ); +char *strtok(char *str, const char *delimiters); +char *strpbrk(const char *str1, const char *str2); char *stpncpy(char *dest, const char *src, size_t n); -char* strstr( const char* str, const char* substr ); -char * strdup( const char *str1 ); -size_t strspn ( const char * str1, const char * str2 ); -double strtod( char const * str, char ** endptr ); - +char *strstr(const char *str, const char *substr); +char *strdup(const char *str1); +size_t strspn(const char *str1, const char *str2); +double strtod(char const *str, char **endptr); int strcasecmp(const char *s1, const char *s2); int strncasecmp(const char *s1, const char *s2, size_t n); char *strsignal(int sig); -void* memcpy( void* dest, const void* src, size_t count ); +void *memcpy(void *dest, const void *src, size_t count); void *mempcpy(void *dest, const void *src, size_t n); -int memcmp ( const void * ptr1, const void * ptr2, size_t num ); -void * memmove ( void * destination, const void * source, size_t num ); -void * memset ( void * ptr, int value, size_t num ); +int memcmp(const void *ptr1, const void *ptr2, size_t num); +void *memmove(void *destination, const void *source, size_t num); +void *memset(void *ptr, int value, size_t num); +void *memchr(const void *s, int c, size_t n); +void *memrchr(const void *s, int c, size_t n); #endif \ No newline at end of file diff --git a/userland/usr/include/sys/mman.h b/userland/usr/include/sys/mman.h new file mode 100644 index 0000000..e69de29 diff --git a/userland/usr/include/termios.h b/userland/usr/include/termios.h index e69de29..d73fdad 100644 --- a/userland/usr/include/termios.h +++ b/userland/usr/include/termios.h @@ -0,0 +1,50 @@ +typedef unsigned int tcflag_t; +typedef unsigned char cc_t; + +/* c_iflag bits */ +#define IGNBRK 0000001 +#define BRKINT 0000002 +#define IGNPAR 0000004 +#define PARMRK 0000010 +#define INPCK 0000020 +#define ISTRIP 0000040 +#define INLCR 0000100 +#define IGNCR 0000200 +#define ICRNL 0000400 +#define IXON 0002000 +#define IXOFF 0010000 + +/* c_oflag bits */ +#define OPOST 0000001 +#define ONLCR 0000004 +#define OCRNL 0000010 + +/* c_lflag bits */ +#define ISIG 0000001 +#define ICANON 0000002 +#define ECHO 0000010 +#define ECHOE 0000020 +#define ECHOK 0000040 +#define ECHONL 0000100 +#define NOFLSH 0000200 +#define TOSTOP 0000400 +#define IEXTEN 0100000 + +/* c_cflag bits */ +#define CSIZE 0000060 +#define CS8 0000060 +#define PARENB 0000400 +#define PARODD 0001000 +#define HUPCL 0002000 +#define CLOCAL 0004000 + +struct termios { + tcflag_t c_iflag; /* input mode flags */ + tcflag_t c_oflag; /* output mode flags */ + tcflag_t c_cflag; /* control mode flags */ + tcflag_t c_lflag; /* local mode flags */ + cc_t c_line; /* line discipline */ + cc_t c_cc[]; /* control characters */ +}; + +int tcgetattr(int fd, struct termios *termios_p); \ No newline at end of file diff --git a/userland/usr/include/unistd.h b/userland/usr/include/unistd.h index 12f9bd5..bd2e730 100644 --- a/userland/usr/include/unistd.h +++ b/userland/usr/include/unistd.h @@ -1,8 +1,12 @@ -#include "sys/stat.h" -#include "stddef.h" -#include "ctype.h" #include "chartypes.h" +#include "ctype.h" +#include "stddef.h" +#include "sys/stat.h" +/* whence values for lseek(2) */ +#define SEEK_SET 0 /* Set file offset to offset */ +#define SEEK_CUR 1 /* Set file offset to current plus offset */ +#define SEEK_END 2 /* Set file offset to EOF plus offset */ typedef int pid_t; @@ -37,19 +41,25 @@ pid_t vfork(void); uid_t geteuid(void); gid_t getegid(void); -ssize_t write(int fd, const void* buf, size_t count); +ssize_t write(int fd, const void *buf, size_t count); /* These may be OR'd together. */ -#define R_OK 4 /* Test for read permission. */ -#define W_OK 2 /* Test for write permission. */ -#define X_OK 1 /* execute permission - unsupported in windows*/ -#define F_OK 0 /* Test for existence. */ +#define R_OK 4 /* Test for read permission. */ +#define W_OK 2 /* Test for write permission. */ +#define X_OK 1 /* execute permission - unsupported in windows*/ +#define F_OK 0 /* Test for existence. */ int faccessat(int dirfd, const char *pathname, int mode, int flags); -#define AT_FDCWD -100 /* Special value used to indicate openat should use the current working directory. */ -#define AT_EACCESS 0x200 /* Test access permissions for a file relative to a directory. */ +#define AT_FDCWD \ + -100 /* Special value used to indicate openat should use the current working \ + directory. */ +#define AT_EACCESS \ + 0x200 /* Test access permissions for a file relative to a directory. */ long sysconf(int name); -#define _SC_CLK_TCK 100 \ No newline at end of file +#define _SC_CLK_TCK 100 + +typedef long lseek_t; +lseek_t lseek(int fd, lseek_t offset, int whence); \ No newline at end of file diff --git a/userland/usr/include/wchar.h b/userland/usr/include/wchar.h new file mode 100644 index 0000000..46a3344 --- /dev/null +++ b/userland/usr/include/wchar.h @@ -0,0 +1,21 @@ +#include "stddef.h" +#include "wctype.h" + +#ifndef __WCHAR_H__ +#define __WCHAR_H__ + +typedef struct { + int __count; + union { + __WINT_TYPE__ __wch; + char __wchb[4]; + } __value; /* Value so far. */ +} mbstate_t; + +size_t mbrlen(const char *s, size_t n2, mbstate_t *ps); +size_t mbrtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *ps); +wchar_t *wcschr(const wchar_t *wcs, wchar_t wc); +int iswspace(wchar_t wc); +size_t mbsrtowcs(wchar_t *dest, const char **src, size_t len, mbstate_t *ps); + +#endif /* wchar.h */ \ No newline at end of file diff --git a/userland/usr/include/wctype.h b/userland/usr/include/wctype.h new file mode 100644 index 0000000..16d39e8 --- /dev/null +++ b/userland/usr/include/wctype.h @@ -0,0 +1,2 @@ +#define wchar_t long +#define wctype_t int \ No newline at end of file diff --git a/userland/usr/libc.c b/userland/usr/libc.c index 5c2eb18..be9e431 100644 --- a/userland/usr/libc.c +++ b/userland/usr/libc.c @@ -1,4 +1,27 @@ #include "include/libc.h" +/* Provide a plain global _DYNAMIC symbol so linkers can resolve + references when building static binaries. Some object files + reference `_DYNAMIC' during configuration/build checks; defining + it here as NULL avoids undefined reference errors. */ +void *_DYNAMIC = 0; + +__asm__(".global _start\n" + ".extern main\n" + "_start:\n" + // " mov %rsp, %rdi\n" // argc in [rsp] + // " mov (%rdi), %rdi\n" + // " lea 8(%rsp), %rsi\n" // argv + // " mov %rsi, %rdx\n" + // "1:\n" + // " cmpq $0,(%rdx)\n" + // " add $8,%rdx\n" + // " jne 1b\n" + // " add $8,%rdx\n" // envp + " call main\n" + // " mov %rax,%rdi\n" + // " mov $60,%rax\n" // exit + // " syscall\n" +); FILE _stdin = { .fd = 0, // File descriptor 0 for stdin @@ -867,3 +890,74 @@ int memcmp(const void *s1, const void *s2, size_t n) { return 0; } + +void qsort(void *base, size_t num, size_t size, + int (*compar)(const void *, const void *)) { + if (num < 2) { + return; // No need to sort + } + + char *pivot = + (char *)base + (num - 1) * size; // Choose the last element as pivot + size_t i = 0; // Index of smaller element + + for (size_t j = 0; j < num - 1; j++) { + char *current = (char *)base + j * size; + if (compar(current, pivot) < 0) { + if (i != j) { + char *smaller = (char *)base + i * size; + // Swap current and smaller + for (size_t k = 0; k < size; k++) { + char temp = smaller[k]; + smaller[k] = current[k]; + current[k] = temp; + } + } + i++; + } + } + + // Place pivot in the correct position + char *smaller = (char *)base + i * size; + for (size_t k = 0; k < size; k++) { + char temp = smaller[k]; + smaller[k] = pivot[k]; + pivot[k] = temp; + } + + // Recursively sort elements before and after partition + qsort(base, i, size, compar); + qsort((char *)base + (i + 1) * size, num - i - 1, size, compar); +} + +pid_t getppid(void) { + uint64_t ppid; + DO_SYSCALL(17, ppid, 0, 0, 0); + return ppid; +} + +char **environ = NULL; + +uid_t getuid(void) { + // TODO implement + return 1234; +} + +uid_t geteuid(void) { + // TODO implement + return 12345; +} + +clock_t times(struct tms *buf) { + // TODO implement + buf->tms_utime = 0; + buf->tms_stime = 0; + buf->tms_cutime = 0; + buf->tms_cstime = 0; + return 0; +} + +long sysconf(int name) { + // TODO implement + return -1; +} From 90b2fe4ea5d749f3785ac7061d781f6fcede556f Mon Sep 17 00:00:00 2001 From: jbreu <20864561+jbreu@users.noreply.github.com> Date: Fri, 31 Oct 2025 14:27:17 +0100 Subject: [PATCH 03/10] dash compiles and runs a bit --- .gdbinit | 6 +- Makefile | 5 +- kernel/src/process.rs | 14 +- kernel/src/syscall.rs | 5 + kernel/src/userland.rs | 23 +- storage/generateExt2Img.sh | 1 + userland/build_dash.sh | 19 +- userland/usr/include/errno.h | 271 ++++++++++----------- userland/usr/include/libc.h | 4 + userland/usr/include/signal.h | 84 +++---- userland/usr/include/wctype.h | 3 +- userland/usr/libc.c | 442 ++++++++++++++++++++++++++++++++-- 12 files changed, 662 insertions(+), 215 deletions(-) diff --git a/.gdbinit b/.gdbinit index 8509372..9bbda8a 100644 --- a/.gdbinit +++ b/.gdbinit @@ -1,10 +1,8 @@ set output-radix 16 set disassembly-flavor intel set osabi none -#add-symbol-file build/kernel/x86_64-unknown-none/debug/libjos.a -#set substitute-path /cygdrive/c/Users/Jakob/Documents/workspace/os-series/src/ c:\\Users\\Jakob\\Documents\\workspace\\os-series\\userland\\src\\ -#set remote get-thread-information-block-address-packet off -add-symbol-file build/userspace/x86_64-unknown-none/debug/helloworld +set substitute-path /root/env/userland/ c:/Users/Jakob/Documents/workspace/os-series/userland/ +add-symbol-file c:/Users/Jakob/Documents/workspace/os-series/userland/dash-0.5.13/src/dash display/5i $pc display/20xg $sp diff --git a/Makefile b/Makefile index 6c75a94..3069d0d 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,10 @@ setup: $(x86_64_asm_object_files) .PHONY: userland userland: - gcc userland/src/doom/main.c userland/src/doom/libc.c -static -nostdlib -fno-builtin -g -o build/userspace/x86_64-unknown-none/debug/doom -Wl,--gc-sections + # gcc userland/src/doom/main.c userland/src/doom/libc.c -static -nostdlib -fno-builtin -g -o build/userspace/x86_64-unknown-none/debug/doom -Wl,--gc-sections && \ + cd userland && \ + ./build_dash.sh -c && \ + cd .. .PHONY: kernel kernel: diff --git a/kernel/src/process.rs b/kernel/src/process.rs index 5cca355..907b088 100644 --- a/kernel/src/process.rs +++ b/kernel/src/process.rs @@ -6,13 +6,13 @@ use alloc::collections::BTreeMap; use alloc::vec::Vec; use core::arch::asm; use core::fmt::Debug; -use core::ptr::addr_of; use core::sync::atomic::AtomicUsize; use core::sync::atomic::Ordering; use elf::abi::PT_LOAD; use elf::endian::AnyEndian; pub static KERNEL_CR3: AtomicUsize = AtomicUsize::new(0); +pub static NEXT_PROCESS_ID: AtomicUsize = AtomicUsize::new(1); // stores a process' registers when it gets interrupted #[repr(C)] @@ -117,6 +117,8 @@ enum ProcessState { } pub struct Process { + process_id: u64, + registers: RegistersStruct, l1_page_table: PageTable, @@ -162,6 +164,8 @@ impl Process { let _event = core::hint::black_box(crate::instrument!()); Self { + process_id: NEXT_PROCESS_ID.fetch_add(1, Ordering::Relaxed) as u64, + registers: RegistersStruct::default(), l1_page_table: PageTable::default(), l1_page_table_beginning: [PageTable::default(); 16], @@ -252,7 +256,7 @@ impl Process { &self.l3_page_directory_pointer_table as *const _ as usize, ) | PAGE_ENTRY_FLAGS_USERSPACE as usize; - let mut file_handle = FileHandle::new("/doom", 0).unwrap(); + let mut file_handle = FileHandle::new("/dash", 0).unwrap(); // put the whole file into a buffer // TODO ensure the *kernel* has enough memory for this @@ -268,6 +272,7 @@ impl Process { } // allocate enough pages at beginning of virtual memory for elf loading + // TODO do not map pages before the first used virtual address in the elf file (typically 0x400000) let heap_page_number = (self.get_size_of_program(program_slice) + PAGE_SIZE - 1) / PAGE_SIZE; @@ -785,4 +790,9 @@ impl Process { let _event = core::hint::black_box(crate::instrument!()); self.parent_id } + + pub fn get_pid(&self) -> u64 { + let _event = core::hint::black_box(crate::instrument!()); + self.process_id + } } diff --git a/kernel/src/syscall.rs b/kernel/src/syscall.rs index 36e01e9..1ea4060 100644 --- a/kernel/src/syscall.rs +++ b/kernel/src/syscall.rs @@ -46,6 +46,7 @@ pub extern "C" fn system_call() -> u64 { 15 => return syscall_chdir(arg0 as *const u64), 16 => return syscall_getcwd(arg0 as *mut u64, arg1), 17 => return syscall_getppid(), + 18 => return syscall_kill(arg0 as u64, arg1 as u32), _ => { ERROR!("Undefined system call triggered: {}", syscall_nr); return 0xdeadbeef; @@ -222,3 +223,7 @@ fn syscall_getppid() -> u64 { let _event = core::hint::black_box(crate::instrument!()); USERLAND.lock().get_current_process_parent_id() as u64 } +fn syscall_kill(_pid: u64, _sig: u32) -> u64 { + let _event = core::hint::black_box(crate::instrument!()); + USERLAND.lock().kill_process(_pid, _sig) as u64 +} diff --git a/kernel/src/userland.rs b/kernel/src/userland.rs index 9625329..459d2ee 100644 --- a/kernel/src/userland.rs +++ b/kernel/src/userland.rs @@ -1,8 +1,8 @@ use spin::Mutex; -use crate::USERLAND; use crate::mem_config::{KERNEL_STACK_TOP_ADDRESS, USERSPACE_STACK_TOP_ADDRESS}; use crate::process::Process; +use crate::{ERROR, USERLAND, kprint}; extern crate alloc; use alloc::vec::Vec; @@ -121,7 +121,26 @@ impl Userland { pub fn get_current_process_parent_id(&self) -> usize { let _event = core::hint::black_box(crate::instrument!()); - self.processes[self.current_process].get_parent_id() + self.processes[self.current_process] + .get_parent_id() + .try_into() + .unwrap() + } + + pub fn kill_process(&mut self, pid: u64, sig: u32) -> i64 { + let _event = core::hint::black_box(crate::instrument!()); + + if sig != 9 { + ERROR!("Unsupported signal {}\n", sig); + return -1; + } + + if let Some(pos) = self.processes.iter().position(|x| x.get_pid() == pid) { + self.processes.remove(pos); + return 0; + } + + return -1; } } diff --git a/storage/generateExt2Img.sh b/storage/generateExt2Img.sh index 548c1ef..4bd0369 100644 --- a/storage/generateExt2Img.sh +++ b/storage/generateExt2Img.sh @@ -26,6 +26,7 @@ fi # Copy the file sudo cp ../doom1.wad /tmp/disk/devdatadoom1.wad sudo cp ../build/userspace/x86_64-unknown-none/debug/doom /tmp/disk/doom +sudo cp ../userland/dash-0.5.13/src/dash /tmp/disk/dash # List files ls -l -a /tmp/disk diff --git a/userland/build_dash.sh b/userland/build_dash.sh index 6adcd9f..ea77aa8 100644 --- a/userland/build_dash.sh +++ b/userland/build_dash.sh @@ -5,31 +5,30 @@ mv /usr/include /usr/include-bak | true cd usr -gcc -c -ffreestanding -fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-rtti libc.c +gcc -c -g -ffreestanding -fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables -static -nostdlib -fno-pic -fno-builtin --sysroot=/root/env/userland/ -I/root/env/userland/usr/include/ libc.c ar rcs libc.a *.o mv libc.a lib/libc.a cd ../dash-0.5.13/ -export CFLAGS="--sysroot=/root/env/userland/ -I/root/env/userland/usr/include/ -fno-unwind-tables -fno-exceptions -fno-asynchronous-unwind-tables" -export CPPFLAGS="--sysroot=/root/env/userland/ -I/root/env/userland/usr/include/ -fno-unwind-tables -fno-exceptions -fno-asynchronous-unwind-tables" -export CPPFLAGS_FOR_BUILD="-I/root/env/userland/usr/include/ -fno-unwind-tables -fno-exceptions -fno-asynchronous-unwind-tables" +export CFLAGS="--sysroot=/root/env/userland/ -I/root/env/userland/usr/include/ -g -fno-unwind-tables -fno-exceptions -fno-asynchronous-unwind-tables" +export CPPFLAGS="--sysroot=/root/env/userland/ -I/root/env/userland/usr/include/ -g -fno-unwind-tables -fno-exceptions -fno-asynchronous-unwind-tables" +export CPPFLAGS_FOR_BUILD="-I/root/env/userland/usr/include/ -fno-unwind-tables -g -fno-exceptions -fno-asynchronous-unwind-tables" -export LDFLAGS="-L/root/env/userland/usr/lib/ -static -nostdlib -fno-builtin" -export LOCAL_LDFLAGS="-L/root/env/userland/usr/lib/ -static -nostdlib -fno-builtin" -export LDFLAGS_FOR_BUILD="-L/root/env/userland/usr/lib/ -static -nostdlib -fno-builtin" +export LDFLAGS="-L/root/env/userland/usr/lib/ -g -static -nostdlib -fno-builtin -Wl,-e,_start" +export LOCAL_LDFLAGS="-L/root/env/userland/usr/lib/ -g -static -nostdlib -fno-builtin -Wl,-e,_start" +export LDFLAGS_FOR_BUILD="-L/root/env/userland/usr/lib/ -g -static -nostdlib -fno-builtin -Wl,-e,_start" export LIBS="-lc" if [ $# = 1 ] && [ "$1" = "-c" ]; then #./configure --host=x86_64-jos --prefix=/usr --without-bash-malloc --enable-static-link --disable-threads - ./configure --host=x86_64-jos --enable-static make clean + ./configure --host=x86_64-jos --enable-static fi -# provide a sed script which replaces LDFLAGS = -static -Wl,--fatal-warnings in Makefile with above LDFLAGS for file in Makefile src/Makefile; do if grep -q 'LDFLAGS = -static' "$file"; then - sed -i 's/LDFLAGS = -static/LDFLAGS = -L\/root\/env\/userland\/usr\/lib\/ -static -nostdlib -fno-builtin/g' "$file" + sed -i 's/LDFLAGS = -static/LDFLAGS = -L\/root\/env\/userland\/usr\/lib\/ -g -static -nostdlib -fno-builtin -Wl,-e,_start/g' "$file" echo "Updated LDFLAGS in $file" else echo "LDFLAGS pattern not found in $file" diff --git a/userland/usr/include/errno.h b/userland/usr/include/errno.h index 959bd68..3c83e7d 100644 --- a/userland/usr/include/errno.h +++ b/userland/usr/include/errno.h @@ -3,49 +3,52 @@ /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +static int errno_value = 0; + extern int *__errno_location(void); -// Define errno as a macro that dereferences the pointer returned by __errno_location +// Define errno as a macro that dereferences the pointer returned by +// __errno_location #define errno (*__errno_location()) -#define EPERM 1 /* Not super-user */ -#define ENOENT 2 /* No such file or directory */ -#define ESRCH 3 /* No such process */ -#define EINTR 4 /* Interrupted system call */ -#define EIO 5 /* I/O error */ -#define ENXIO 6 /* No such device or address */ -#define E2BIG 7 /* Arg list too long */ -#define ENOEXEC 8 /* Exec format error */ -#define EBADF 9 /* Bad file number */ -#define ECHILD 10 /* No children */ -#define EAGAIN 11 /* No more processes */ -#define ENOMEM 12 /* Not enough core */ -#define EACCES 13 /* Permission denied */ -#define EFAULT 14 /* Bad address */ -#define ENOTBLK 15 /* Block device required */ -#define EBUSY 16 /* Mount device busy */ -#define EEXIST 17 /* File exists */ -#define EXDEV 18 /* Cross-device link */ -#define ENODEV 19 /* No such device */ -#define ENOTDIR 20 /* Not a directory */ -#define EISDIR 21 /* Is a directory */ -#define EINVAL 22 /* Invalid argument */ -#define ENFILE 23 /* Too many open files in system */ -#define EMFILE 24 /* Too many open files */ -#define ENOTTY 25 /* Not a typewriter */ -#define ETXTBSY 26 /* Text file busy */ -#define EFBIG 27 /* File too large */ -#define ENOSPC 28 /* No space left on device */ -#define ESPIPE 29 /* Illegal seek */ -#define EROFS 30 /* Read only file system */ -#define EMLINK 31 /* Too many links */ -#define EPIPE 32 /* Broken pipe */ -#define EDOM 33 /* Math arg out of domain of func */ -#define ERANGE 34 /* Math result not representable */ +#define EPERM 1 /* Not super-user */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Arg list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No children */ +#define EAGAIN 11 /* No more processes */ +#define ENOMEM 12 /* Not enough core */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Mount device busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* Too many open files in system */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math arg out of domain of func */ +#define ERANGE 34 /* Math result not representable */ -#define EDEADLK 35 /* Resource deadlock would occur */ -#define ENAMETOOLONG 36 /* File name too long */ -#define ENOLCK 37 /* No record locks available */ +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ /* * This error code is special: arch syscall entry code will return @@ -54,109 +57,109 @@ extern int *__errno_location(void); * failures due to attempts to use a nonexistent syscall, syscall * implementations should refrain from returning -ENOSYS. */ -#define ENOSYS 38 /* Invalid system call number */ +#define ENOSYS 38 /* Invalid system call number */ -#define ENOTEMPTY 39 /* Directory not empty */ -#define ELOOP 40 /* Too many symbolic links encountered */ -#define EWOULDBLOCK EAGAIN /* Operation would block */ -#define ENOMSG 42 /* No message of desired type */ -#define EIDRM 43 /* Identifier removed */ -#define ECHRNG 44 /* Channel number out of range */ -#define EL2NSYNC 45 /* Level 2 not synchronized */ -#define EL3HLT 46 /* Level 3 halted */ -#define EL3RST 47 /* Level 3 reset */ -#define ELNRNG 48 /* Link number out of range */ -#define EUNATCH 49 /* Protocol driver not attached */ -#define ENOCSI 50 /* No CSI structure available */ -#define EL2HLT 51 /* Level 2 halted */ -#define EBADE 52 /* Invalid exchange */ -#define EBADR 53 /* Invalid request descriptor */ -#define EXFULL 54 /* Exchange full */ -#define ENOANO 55 /* No anode */ -#define EBADRQC 56 /* Invalid request code */ -#define EBADSLT 57 /* Invalid slot */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ -#define EDEADLOCK EDEADLK +#define EDEADLOCK EDEADLK -#define EBFONT 59 /* Bad font file format */ -#define ENOSTR 60 /* Device not a stream */ -#define ENODATA 61 /* No data available */ -#define ETIME 62 /* Timer expired */ -#define ENOSR 63 /* Out of streams resources */ -#define ENONET 64 /* Machine is not on the network */ -#define ENOPKG 65 /* Package not installed */ -#define EREMOTE 66 /* Object is remote */ -#define ENOLINK 67 /* Link has been severed */ -#define EADV 68 /* Advertise error */ -#define ESRMNT 69 /* Srmount error */ -#define ECOMM 70 /* Communication error on send */ -#define EPROTO 71 /* Protocol error */ -#define EMULTIHOP 72 /* Multihop attempted */ -#define EDOTDOT 73 /* RFS specific error */ -#define EBADMSG 74 /* Not a data message */ -#define EOVERFLOW 75 /* Value too large for defined data type */ -#define ENOTUNIQ 76 /* Name not unique on network */ -#define EBADFD 77 /* File descriptor in bad state */ -#define EREMCHG 78 /* Remote address changed */ -#define ELIBACC 79 /* Can not access a needed shared library */ -#define ELIBBAD 80 /* Accessing a corrupted shared library */ -#define ELIBSCN 81 /* .lib section in a.out corrupted */ -#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ -#define ELIBEXEC 83 /* Cannot exec a shared library directly */ -#define EILSEQ 84 /* Illegal byte sequence */ -#define ERESTART 85 /* Interrupted system call should be restarted */ -#define ESTRPIPE 86 /* Streams pipe error */ -#define EUSERS 87 /* Too many users */ -#define ENOTSOCK 88 /* Socket operation on non-socket */ -#define EDESTADDRREQ 89 /* Destination address required */ -#define EMSGSIZE 90 /* Message too long */ -#define EPROTOTYPE 91 /* Protocol wrong type for socket */ -#define ENOPROTOOPT 92 /* Protocol not available */ -#define EPROTONOSUPPORT 93 /* Protocol not supported */ -#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ -#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ -#define EPFNOSUPPORT 96 /* Protocol family not supported */ -#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ -#define EADDRINUSE 98 /* Address already in use */ -#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ -#define ENETDOWN 100 /* Network is down */ -#define ENETUNREACH 101 /* Network is unreachable */ -#define ENETRESET 102 /* Network dropped connection because of reset */ -#define ECONNABORTED 103 /* Software caused connection abort */ -#define ECONNRESET 104 /* Connection reset by peer */ -#define ENOBUFS 105 /* No buffer space available */ -#define EISCONN 106 /* Transport endpoint is already connected */ -#define ENOTCONN 107 /* Transport endpoint is not connected */ -#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ -#define ETOOMANYREFS 109 /* Too many references: cannot splice */ -#define ETIMEDOUT 110 /* Connection timed out */ -#define ECONNREFUSED 111 /* Connection refused */ -#define EHOSTDOWN 112 /* Host is down */ -#define EHOSTUNREACH 113 /* No route to host */ -#define EALREADY 114 /* Operation already in progress */ -#define EINPROGRESS 115 /* Operation now in progress */ -#define ESTALE 116 /* Stale file handle */ -#define EUCLEAN 117 /* Structure needs cleaning */ -#define ENOTNAM 118 /* Not a XENIX named type file */ -#define ENAVAIL 119 /* No XENIX semaphores available */ -#define EISNAM 120 /* Is a named type file */ -#define EREMOTEIO 121 /* Remote I/O error */ -#define EDQUOT 122 /* Quota exceeded */ +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ -#define ENOMEDIUM 123 /* No medium found */ -#define EMEDIUMTYPE 124 /* Wrong medium type */ -#define ECANCELED 125 /* Operation Canceled */ -#define ENOKEY 126 /* Required key not available */ -#define EKEYEXPIRED 127 /* Key has expired */ -#define EKEYREVOKED 128 /* Key has been revoked */ -#define EKEYREJECTED 129 /* Key was rejected by service */ +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ +#define ECANCELED 125 /* Operation Canceled */ +#define ENOKEY 126 /* Required key not available */ +#define EKEYEXPIRED 127 /* Key has expired */ +#define EKEYREVOKED 128 /* Key has been revoked */ +#define EKEYREJECTED 129 /* Key was rejected by service */ /* for robust mutexes */ -#define EOWNERDEAD 130 /* Owner died */ -#define ENOTRECOVERABLE 131 /* State not recoverable */ +#define EOWNERDEAD 130 /* Owner died */ +#define ENOTRECOVERABLE 131 /* State not recoverable */ -#define ERFKILL 132 /* Operation not possible due to RF-kill */ +#define ERFKILL 132 /* Operation not possible due to RF-kill */ -#define EHWPOISON 133 /* Memory page has hardware error */ +#define EHWPOISON 133 /* Memory page has hardware error */ #endif \ No newline at end of file diff --git a/userland/usr/include/libc.h b/userland/usr/include/libc.h index 2a0d802..91a6b1a 100644 --- a/userland/usr/include/libc.h +++ b/userland/usr/include/libc.h @@ -2,8 +2,10 @@ #define __LIBC_H__ #include "ctype.h" +#include "dirent.h" #include "errno.h" #include "inttypes.h" +#include "signal.h" #include "stdbool.h" #include "stddef.h" #include "stdint.h" @@ -11,6 +13,8 @@ #include "stdlib.h" #include "string.h" #include "sys/times.h" +#include "wchar.h" +#include "wctype.h" #define DO_SYSCALL(syscall_num, output, r8_val, r9_val, r10_val) \ asm volatile( \ diff --git a/userland/usr/include/signal.h b/userland/usr/include/signal.h index 61ded96..cf626b0 100644 --- a/userland/usr/include/signal.h +++ b/userland/usr/include/signal.h @@ -4,28 +4,27 @@ #include "stddef.h" #include "unistd.h" -#define NSIG 32 +#define NSIG 32 /* ISO C99 signals. */ -#define SIGHUP 1 /* Hangup. */ -#define SIGINT 2 /* Interactive attention signal. */ -#define SIGILL 4 /* Illegal instruction. */ -#define SIGABRT 6 /* Abnormal termination. */ -#define SIGFPE 8 /* Erroneous arithmetic operation. */ -#define SIGSEGV 11 /* Invalid access to storage. */ -#define SIGALRM 14 /* Alarm clock. */ -#define SIGTERM 15 /* Termination request. */ -#define SIGCHLD 17 /* Child stopped or terminated. */ -#define SIGCONT 19 /* Continue a stopped process. */ -#define SIGTTIN 21 /* Background process attempting to read. */ -#define SIGTTOU 22 /* Background process attempting to write. */ -#define SIGTSTP 23 /* Background process attempting to stop. */ - +#define SIGHUP 1 /* Hangup. */ +#define SIGINT 2 /* Interactive attention signal. */ +#define SIGILL 4 /* Illegal instruction. */ +#define SIGABRT 6 /* Abnormal termination. */ +#define SIGFPE 8 /* Erroneous arithmetic operation. */ +#define SIGSEGV 11 /* Invalid access to storage. */ +#define SIGALRM 14 /* Alarm clock. */ +#define SIGTERM 15 /* Termination request. */ +#define SIGCHLD 17 /* Child stopped or terminated. */ +#define SIGCONT 19 /* Continue a stopped process. */ +#define SIGTTIN 21 /* Background process attempting to read. */ +#define SIGTTOU 22 /* Background process attempting to write. */ +#define SIGTSTP 23 /* Background process attempting to stop. */ /* POSIX signals */ -#define SIGQUIT 3 -#define SIGKILL 9 -#define SIGPIPE 13 +#define SIGQUIT 3 +#define SIGKILL 9 +#define SIGPIPE 13 #define SIG_IGN ((void (*)(int))1) #define SIG_DFL ((void (*)(int))0) @@ -34,52 +33,49 @@ typedef int sig_atomic_t; -#define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int))) -typedef struct -{ +#define _SIGSET_NWORDS (1024 / (8 * sizeof(unsigned long int))) +typedef struct { unsigned long int __val[_SIGSET_NWORDS]; } sigset_t; -int sigsetmask(int mask); +int sigsetmask(int mask); void *signal(int, void (*)(int)); typedef union sigval { - int sival_int; // Integer value - void *sival_ptr; // Pointer value + int sival_int; // Integer value + void *sival_ptr; // Pointer value } sigval_t; - typedef struct siginfo { - int si_signo; // Signal number - int si_errno; // Error number associated with the signal (if applicable) - int si_code; // Signal-specific code (provides more information) - pid_t si_pid; // PID of the sending process (if sent by another process) - uid_t si_uid; // UID of the sending process (if sent by another process) - void *si_addr; // Address at which fault occurred (for hardware-generated signals) - int si_status; // Exit value or signal for child (if SIGCHLD) - long si_band; // Band event (for SIGPOLL/SIGIO) - union sigval si_value; // Signal value (for real-time signals) + int si_signo; // Signal number + int si_errno; // Error number associated with the signal (if applicable) + int si_code; // Signal-specific code (provides more information) + pid_t si_pid; // PID of the sending process (if sent by another process) + uid_t si_uid; // UID of the sending process (if sent by another process) + void *si_addr; // Address at which fault occurred (for hardware-generated + // signals) + int si_status; // Exit value or signal for child (if SIGCHLD) + long si_band; // Band event (for SIGPOLL/SIGIO) + union sigval si_value; // Signal value (for real-time signals) } siginfo_t; - struct sigaction { - void (*sa_handler)(int); // Pointer to a signal handler function - void (*sa_sigaction)(int, siginfo_t *, void *); // Alternative handler with more details - sigset_t sa_mask; // Signals to block during handler execution - int sa_flags; // Flags to modify signal handling behavior + void (*sa_handler)(int); // Pointer to a signal handler function + void (*sa_sigaction)(int, siginfo_t *, + void *); // Alternative handler with more details + sigset_t sa_mask; // Signals to block during handler execution + int sa_flags; // Flags to modify signal handling behavior }; - -int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); +int sigaction(int signum, const struct sigaction *act, + struct sigaction *oldact); int sigsuspend(const sigset_t *mask); int sigfillset(sigset_t *set); -int sigprocmask(int how, const sigset_t * set, sigset_t * oldset); +int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); int raise(int sig); int kill(pid_t pid, int sig); int killpg(int pgrp, int sig); - - #endif \ No newline at end of file diff --git a/userland/usr/include/wctype.h b/userland/usr/include/wctype.h index 16d39e8..4335da1 100644 --- a/userland/usr/include/wctype.h +++ b/userland/usr/include/wctype.h @@ -1,2 +1,3 @@ #define wchar_t long -#define wctype_t int \ No newline at end of file +#define wctype_t int +#define wint_t long \ No newline at end of file diff --git a/userland/usr/libc.c b/userland/usr/libc.c index be9e431..c73e461 100644 --- a/userland/usr/libc.c +++ b/userland/usr/libc.c @@ -1,27 +1,22 @@ #include "include/libc.h" + /* Provide a plain global _DYNAMIC symbol so linkers can resolve references when building static binaries. Some object files reference `_DYNAMIC' during configuration/build checks; defining it here as NULL avoids undefined reference errors. */ void *_DYNAMIC = 0; -__asm__(".global _start\n" - ".extern main\n" - "_start:\n" - // " mov %rsp, %rdi\n" // argc in [rsp] - // " mov (%rdi), %rdi\n" - // " lea 8(%rsp), %rsi\n" // argv - // " mov %rsi, %rdx\n" - // "1:\n" - // " cmpq $0,(%rdx)\n" - // " add $8,%rdx\n" - // " jne 1b\n" - // " add $8,%rdx\n" // envp - " call main\n" - // " mov %rax,%rdi\n" - // " mov $60,%rax\n" // exit - // " syscall\n" -); +__attribute__((naked)) void _start(void) { + asm volatile( + // initialize with zero arguments + "xor %rdi, %rdi\n" + "xor %rsi, %rsi\n" + "xor %rdx, %rdx\n" + "call main\n" + "mov %rax, %rdi\n" + "mov $60, %rax\n" + "syscall\n"); +} FILE _stdin = { .fd = 0, // File descriptor 0 for stdin @@ -940,16 +935,23 @@ char **environ = NULL; uid_t getuid(void) { // TODO implement + char *msg = "TODO implement getuid\n"; + write(1, msg, strlen(msg)); return 1234; } uid_t geteuid(void) { // TODO implement + char *msg = "TODO implement geteuid\n"; + write(1, msg, strlen(msg)); return 12345; } clock_t times(struct tms *buf) { // TODO implement + char *msg = "TODO implement times\n"; + write(1, msg, strlen(msg)); + buf->tms_utime = 0; buf->tms_stime = 0; buf->tms_cutime = 0; @@ -959,5 +961,411 @@ clock_t times(struct tms *buf) { long sysconf(int name) { // TODO implement + char *msg = "TODO implement sysconf\n"; + write(1, msg, strlen(msg)); + return -1; +} + +int dup(int fildes) { + // TODO implement + char *msg = "TODO implement dup\n"; + write(1, msg, strlen(msg)); + return -1; +} + +int dup2(int oldfd, int newfd) { + // TODO implement + char *msg = "TODO implement dup2\n"; + write(1, msg, strlen(msg)); + return -1; +} + +int strcoll(const char *s1, const char *s2) { + // Simple implementation using strcmp + return strcmp(s1, s2) ? 1 : 0; +} + +uint32_t htonl(uint32_t hostlong) { + return ((hostlong & 0x000000FF) << 24) | ((hostlong & 0x0000FF00) << 8) | + ((hostlong & 0x00FF0000) >> 8) | ((hostlong & 0xFF000000) >> 24); +} + +int vsnprintf(char *str, size_t size, const char *format, va_list ap) { + // Simplified using existing sprintf (not safe for production) + return sprintf(str, format, ap); +} + +int isatty(int fd) { + // TODO implement + char *msg = "TODO implement isatty\n"; + write(1, msg, strlen(msg)); + return (fd >= 0 && fd <= 2); // Assume stdin, stdout, stderr are ttys +} + +int *__errno_location(void) { return &errno_value; } + +void _exit(int status) { + // TODO implement + char *msg = "TODO implement _exit\n"; + write(1, msg, strlen(msg)); +} + +void longjmp(jmp_buf env, int val) { + // TODO implement + char *msg = "TODO implement longjmp\n"; + write(1, msg, strlen(msg)); +} + +int setjmp(jmp_buf env) { + // TODO implement + char *msg = "TODO implement setjmp\n"; + write(1, msg, strlen(msg)); + return 0; +} + +size_t mbrlen(const char *s, size_t n, mbstate_t *ps) { + // Simplified implementation assuming single-byte characters + if (n == 0 || s == NULL || *s == '\0') { + return 0; + } + return 1; // Each character is one byte +} + +size_t mbrtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *ps) { + // Simplified implementation assuming single-byte characters + if (n == 0 || s == NULL || *s == '\0') { + return 0; + } + if (pwc != NULL) { + *pwc = (wchar_t)(unsigned char)(*s); + } + return 1; // Each character is one byte +} + +int iswblank(wint_t wc) { return (wc == L' ' || wc == L'\t'); } + +int iswctype(wint_t wc, wctype_t desc) { + // Simplified implementation for basic character types + switch (desc) { + case 1: // WCTYPE_ALNUM + return (wc >= L'0' && wc <= L'9') || (wc >= L'A' && wc <= L'Z') || + (wc >= L'a' && wc <= L'z'); + case 2: // WCTYPE_ALPHA + return (wc >= L'A' && wc <= L'Z') || (wc >= L'a' && wc <= L'z'); + case 3: // WCTYPE_DIGIT + return (wc >= L'0' && wc <= L'9'); + default: + return 0; // Unsupported type + } +} + +wctype_t wctype(const char *property) { + // Simplified implementation for basic character types + if (strcmp(property, "alnum")) { + return 1; // WCTYPE_ALNUM + } else if (strcmp(property, "alpha")) { + return 2; // WCTYPE_ALPHA + } else if (strcmp(property, "digit")) { + return 3; // WCTYPE_DIGIT + } else { + return 0; // Unsupported type + } +} + +wchar_t *wcschr(const wchar_t *wcs, wchar_t wc) { + while (*wcs != L'\0') { + if (*wcs == wc) { + return (wchar_t *)wcs; + } + wcs++; + } + return NULL; +} + +int iswspace(wint_t wc) { + return (wc == L' ' || wc == L'\t' || wc == L'\n' || wc == L'\v' || + wc == L'\f' || wc == L'\r'); +} + +size_t mbsrtowcs(wchar_t *dst, const char **src, size_t len, mbstate_t *ps) { + size_t count = 0; + while (len-- && **src != '\0') { + if (dst != NULL) { + *dst++ = (wchar_t)(unsigned char)(**src); + } + (*src)++; + count++; + } + return count; +} + +int kill(pid_t pid, int sig) { + uint64_t result; + DO_SYSCALL(18, result, pid, sig, 0); + return (int)result; +} + +int sigaction(int signum, const struct sigaction *act, + struct sigaction *oldact) { + char *msg = "TODO implement sigaction\n"; + write(1, msg, strlen(msg)); + return -1; +} + +int faccessat(int dirfd, const char *pathname, int mode, int flags) { + char *msg = "TODO implement faccessat\n"; + write(1, msg, strlen(msg)); + return 0; +} + +gid_t getegid(void) { + char *msg = "TODO implement getegid\n"; + write(1, msg, strlen(msg)); + return 1234; +} + +int sigprocmask(int how, const sigset_t *restrict set, + sigset_t *restrict oset) { + char *msg = "TODO implement sigprocmask\n"; + write(1, msg, strlen(msg)); + return -1; +} + +int sigfillset(sigset_t *set) { + char *msg = "TODO implement sigfillset\n"; + write(1, msg, strlen(msg)); + return -1; +} + +int fcntl(int fildes, int cmd, ...) { + char *msg = "TODO implement fcntl\n"; + write(1, msg, strlen(msg)); + return -1; +} + +void *signal(int, void (*)(int)) { + char *msg = "TODO implement signal\n"; + write(1, msg, strlen(msg)); + return NULL; +} + +lseek_t lseek(int fd, lseek_t offset, int whence) { + char *msg = "TODO implement lseek\n"; + write(1, msg, strlen(msg)); + + fseek((FILE *)(uintptr_t)fd, offset, whence); + return ftell((FILE *)(uintptr_t)fd); +} + +int pipe(int pipefd[2]) { + char *msg = "TODO implement pipe\n"; + write(1, msg, strlen(msg)); return -1; } + +int memfd_create(const char *name, unsigned int flags) { + char *msg = "TODO implement memfd_create\n"; + write(1, msg, strlen(msg)); + return -1; +} + +int fstat(int fd, struct stat *buf) { + char *msg = "TODO implement fstat\n"; + write(1, msg, strlen(msg)); + return -1; +} + +int open64(const char *pathname, int oflag, ...) { + // use open syscall + uint64_t handle; + DO_SYSCALL(5, handle, pathname, oflag, 0); + return (int)handle; +} + +int getrlimit(int resource, struct rlimit *rlim) { + // TODO implement getrlimit + char *msg = "TODO implement getrlimit\n"; + write(1, msg, strlen(msg)); + return -1; +} + +int setrlimit(int resource, const struct rlimit *rlim) { + // TODO implement setrlimit + char *msg = "TODO implement setrlimit\n"; + write(1, msg, strlen(msg)); + return -1; +} + +mode_t umask(mode_t mask) { + // TODO implement umask + char *msg = "TODO implement umask\n"; + write(1, msg, strlen(msg)); + return 0; +} + +void *realloc(void *ptr, size_t size) { + // TODO implement realloc + char *msg = "TODO implement realloc\n"; + write(1, msg, strlen(msg)); + return NULL; +} + +char *setlocale(int category, const char *locale) { + // TODO implement setlocale + char *msg = "TODO implement setlocale\n"; + write(1, msg, strlen(msg)); + return NULL; +} + +int tcsetpgrp(int fd, pid_t pgrp) { + // TODO implement tcsetpgrp + char *msg = "TODO implement tcsetpgrp\n"; + write(1, msg, strlen(msg)); + return -1; +} + +int sigsuspend(const sigset_t *sigmask) { + // TODO implement sigsuspend + char *msg = "TODO implement sigsuspend\n"; + write(1, msg, strlen(msg)); + return -1; +} + +int wait3(int *status, int options, struct rusage *rusage) { + // TODO implement wait3 + char *msg = "TODO implement wait3\n"; + write(1, msg, strlen(msg)); + return -1; +} + +int raise(int sig) { return kill(getpid(), sig); } + +pid_t vfork(void) { + // TODO implement vfork + char *msg = "TODO implement vfork\n"; + write(1, msg, strlen(msg)); + return -1; +} + +pid_t fork(void) { + // TODO implement fork + char *msg = "TODO implement fork\n"; + write(1, msg, strlen(msg)); + return -1; +} + +int setpgid(pid_t pid, pid_t pgid) { + // TODO implement setpgid + char *msg = "TODO implement setpgid\n"; + write(1, msg, strlen(msg)); + return -1; +} + +int killpg(pid_t pgrp, int sig) { + // TODO implement killpg + char *msg = "TODO implement killpg\n"; + write(1, msg, strlen(msg)); + return -1; +} + +pid_t getpgrp(void) { + // TODO implement getpgrp + char *msg = "TODO implement getpgrp\n"; + write(1, msg, strlen(msg)); + return -1; +} + +pid_t tcgetpgrp(int fd) { + // TODO implement tcgetpgrp + char *msg = "TODO implement tcgetpgrp\n"; + write(1, msg, strlen(msg)); + return -1; +} + +char *strsignal(int sig) { + // TODO implement strsignal + char *msg = "TODO implement strsignal\n"; + write(1, msg, strlen(msg)); + return NULL; +} + +int sigsetmask(int mask) { + // TODO implement sigsetmask + char *msg = "TODO implement sigsetmask\n"; + write(1, msg, strlen(msg)); + return 0; +} + +ssize_t read(int fd, void *buf, size_t count) { + // TODO implement read + char *msg = "TODO implement read\n"; + write(1, msg, strlen(msg)); + return -1; +} + +int tee(int fd_in, int fd_out, size_t len, unsigned int flags) { + // TODO implement tee + char *msg = "TODO implement tee\n"; + write(1, msg, strlen(msg)); + return -1; +} + +int tcgetattr(int fd, struct termios *termios_p) { + // TODO implement tcgetattr + char *msg = "TODO implement tcgetattr\n"; + write(1, msg, strlen(msg)); + return -1; +} + +void abort(void) { + // TODO implement abort + char *msg = "TODO implement abort\n"; + write(1, msg, strlen(msg)); + while (1) + ; +} + +int execve(const char *filename, char *const argv[], char *const envp[]) { + // TODO implement execve + char *msg = "TODO implement execve\n"; + write(1, msg, strlen(msg)); + return -1; +} + +struct passwd *getpwnam(const char *name) { + // TODO implement getpwnam + char *msg = "TODO implement getpwnam\n"; + write(1, msg, strlen(msg)); + return NULL; +} + +void *memrchr(const void *s, int c, size_t n) { + const unsigned char *p = (const unsigned char *)s + n; + while (n--) { + if (*--p == (unsigned char)c) { + return (void *)p; + } + } + return NULL; +} + +DIR *opendir(const char *name) { + // TODO implement opendir + char *msg = "TODO implement opendir\n"; + write(1, msg, strlen(msg)); + return NULL; +} + +DIR *readdir64(DIR *dirp) { + // TODO implement readdir64 + char *msg = "TODO implement readdir64\n"; + write(1, msg, strlen(msg)); + return NULL; +} + +int closedir(DIR *dirp) { + // TODO implement closedir + char *msg = "TODO implement closedir\n"; + write(1, msg, strlen(msg)); + return -1; +} \ No newline at end of file From 710d975b241734fdc5e0439e8633129509dd5530 Mon Sep 17 00:00:00 2001 From: jbreu <20864561+jbreu@users.noreply.github.com> Date: Sun, 2 Nov 2025 09:50:41 +0100 Subject: [PATCH 04/10] reaching cmdloop --- Makefile | 6 +- kernel/src/kprint.rs | 27 +++++-- kernel/src/logging.rs | 6 +- kernel/src/syscall.rs | 13 ++-- userland/usr/include/libc.h | 1 + userland/usr/libc.c | 145 +++++++++++++++++++++++++++++------- 6 files changed, 151 insertions(+), 47 deletions(-) diff --git a/Makefile b/Makefile index 3069d0d..bafc3ff 100644 --- a/Makefile +++ b/Makefile @@ -16,9 +16,8 @@ setup: $(x86_64_asm_object_files) .PHONY: userland userland: # gcc userland/src/doom/main.c userland/src/doom/libc.c -static -nostdlib -fno-builtin -g -o build/userspace/x86_64-unknown-none/debug/doom -Wl,--gc-sections && \ - cd userland && \ - ./build_dash.sh -c && \ - cd .. + cd userland && ./build_dash.sh -c && cd .. && \ + cd storage && sh generateExt2Img.sh && cd .. .PHONY: kernel kernel: @@ -29,7 +28,6 @@ kernel: iso: x86_64-elf-ld -n -o dist/x86_64/kernel.bin --unresolved-symbols=report-all -z noexecstack -T targets/x86_64/linker.ld $(x86_64_asm_object_files) build/kernel/x86_64-unknown-none/debug/libjos.a && \ cp dist/x86_64/kernel.bin targets/x86_64/iso/boot/kernel.bin && \ - cd storage && sh generateExt2Img.sh && cd .. && \ grub-mkrescue /usr/lib/grub/i386-pc -o dist/x86_64/kernel.iso targets/x86_64/iso .PHONY: all diff --git a/kernel/src/kprint.rs b/kernel/src/kprint.rs index ee7aae4..ed19162 100644 --- a/kernel/src/kprint.rs +++ b/kernel/src/kprint.rs @@ -1,4 +1,3 @@ -use crate::mem_config; use crate::mem_config::KERNEL_HIGHER_HALF_BASE; use crate::serial; // add better formatting options, see https://os.phil-opp.com/vga-text-mode/#a-kprintln-macro @@ -37,22 +36,27 @@ static mut CURRENT_COL: u64 = 0; pub struct KPrinter { pub color: Colors, + pub written: u64, } impl core::fmt::Write for KPrinter { fn write_str(&mut self, s: &str) -> core::fmt::Result { - crate::kprint::kprint(s, self.color); + let n = crate::kprint::kprint(s, self.color); + self.written += n; Ok(()) } } + #[macro_export] macro_rules! kprint_internal { ($color:expr, $with_newline:expr, $($arg:tt)*) => {{ - let mut kprinter = crate::kprint::KPrinter { color: $color }; + let mut kprinter = crate::kprint::KPrinter { color: $color, written: 0 }; core::fmt::write(&mut kprinter, core::format_args!($($arg)*)).unwrap(); if $with_newline { crate::kprint::kprint_char('\n', $color); + kprinter.written += 1; } + kprinter.written }}; } @@ -62,7 +66,8 @@ macro_rules! kprintlncolor { kprint_internal!($color, true, $($arg)*) }; () => { - crate::kprint::kprint_char('\n', $color) + crate::kprint::kprint_char('\n', $color); + return 0; }; } @@ -72,7 +77,8 @@ macro_rules! kprintcolor { crate::kprint_internal!($color, false, $($arg)*) }; () => { - crate::kprint::kprint_char('\n') + crate::kprint::kprint_char('\n'); + return 0; }; } @@ -82,7 +88,8 @@ macro_rules! kprintln { crate::kprint_internal!(crate::kprint::Colors::KPrintColorBlack, true, $($arg)*) }; () => { - crate::kprint::kprint_char('\n') + crate::kprint::kprint_char('\n'); + return 0; }; } @@ -92,7 +99,8 @@ macro_rules! kprint { crate::kprint_internal!(crate::kprint::Colors::KPrintColorBlack, false, $($arg)*) }; () => { - crate::kprint::kprint_char('\n') + crate::kprint::kprint_char('\n'); + return 0; }; } @@ -152,10 +160,13 @@ fn scroll_line() { } } -pub fn kprint(text: &str, color: Colors) { +pub fn kprint(text: &str, color: Colors) -> u64 { + let mut written = 0; for character in text.chars() { kprint_char(character, color); + written += 1; } + written } pub fn _kprint_text_at_pos(text: &str, row: u64, column: u64, color: Colors) { diff --git a/kernel/src/logging.rs b/kernel/src/logging.rs index 76b6031..188ab36 100644 --- a/kernel/src/logging.rs +++ b/kernel/src/logging.rs @@ -10,20 +10,20 @@ macro_rules! log_with_level { #[macro_export] macro_rules! DEBUG { ($($arg:tt)*) => { - crate::log_with_level!(crate::kprint::Colors::KPrintColorGreen, "[DEBUG] ", $($arg)*) + crate::log_with_level!(crate::kprint::Colors::KPrintColorGreen, "[DEBUG] ", $($arg)*); }; } #[macro_export] macro_rules! INFO { ($($arg:tt)*) => { - crate::log_with_level!(crate::kprint::Colors::KPrintColorBlack, "[DEBUG] ", $($arg)*) + crate::log_with_level!(crate::kprint::Colors::KPrintColorBlack, "[DEBUG] ", $($arg)*); }; } #[macro_export] macro_rules! ERROR { ($($arg:tt)*) => { - crate::log_with_level!(crate::kprint::Colors::KPrintColorRed, "[ERROR] ", $($arg)*) + crate::log_with_level!(crate::kprint::Colors::KPrintColorRed, "[ERROR] ", $($arg)*); }; } diff --git a/kernel/src/syscall.rs b/kernel/src/syscall.rs index 1ea4060..497acc8 100644 --- a/kernel/src/syscall.rs +++ b/kernel/src/syscall.rs @@ -1,7 +1,5 @@ -use elf::file; - use crate::ERROR; -use crate::kprintln; +use crate::kprint; use crate::{USERLAND, time}; use crate::{keyboard, vga}; use core::arch::asm; @@ -135,10 +133,13 @@ fn syscall_write(filedescriptor: u64, payload: u64, len: u64) -> u64 { )) { Ok(msg) => match filedescriptor { // stdout - 1 => { - kprintln!("{}", msg) + 1 | 2 => { + return kprint!("{}", msg); + } + _ => { + core::hint::black_box(()); // dummy instruction to place breakpoint on + ERROR!("Undefined filedescriptor!"); } - _ => ERROR!("Undefined filedescriptor!"), }, Err(_) => ERROR!("\nCouldnt reconstruct string!\n"), } diff --git a/userland/usr/include/libc.h b/userland/usr/include/libc.h index 91a6b1a..ababe8d 100644 --- a/userland/usr/include/libc.h +++ b/userland/usr/include/libc.h @@ -4,6 +4,7 @@ #include "ctype.h" #include "dirent.h" #include "errno.h" +#include "fcntl.h" #include "inttypes.h" #include "signal.h" #include "stdbool.h" diff --git a/userland/usr/libc.c b/userland/usr/libc.c index c73e461..5442038 100644 --- a/userland/usr/libc.c +++ b/userland/usr/libc.c @@ -77,6 +77,7 @@ bool strcmp(const char *a, const char *b) { ssize_t write(int filedescriptor, const void *payload, size_t len) { uint64_t result; DO_SYSCALL(1, result, filedescriptor, (uintptr_t)payload, len); + return result; } // Get process ID @@ -253,33 +254,73 @@ int sprintf(char *str, const char *format, ...) { va_start(args, format); char *ptr = str; const char *fmt = format; - while (*fmt != '\0') { if (*fmt == '%') { fmt++; - switch (*fmt) { - case 'd': { - int num = va_arg(args, int); - ptr += sprintf(ptr, "%d", num); // Append integer - break; - } - case 's': { - char *s = va_arg(args, char *); - ptr += sprintf(ptr, "%s", s); // Append string - break; - } - case 'c': { - char c = (char)va_arg(args, int); // Get character - *ptr++ = c; // Append character - *ptr = '\0'; // Null-terminate - break; - } - default: - // Handle unknown format specifiers - *ptr++ = '%'; - *ptr++ = *fmt; - *ptr = '\0'; - break; + // handle long decimal "%ld" + if (*fmt == 'l' && *(fmt + 1) != '\0') { + fmt++; // now points to the specifier after 'l' + if (*fmt == 'd') { + long num = va_arg(args, long); + unsigned long long v; + int neg = 0; + if (num < 0) { + neg = 1; + /* avoid UB for LONG_MIN */ + v = (unsigned long long)(-(num + 1)) + 1ULL; + } else { + v = (unsigned long long)num; + } + + char tmp[32]; + int ti = 0; + if (v == 0) { + tmp[ti++] = '0'; + } else { + while (v) { + tmp[ti++] = '0' + (v % 10); + v /= 10; + } + } + if (neg) { + *ptr++ = '-'; + } + while (ti--) { + *ptr++ = tmp[ti]; + } + *ptr = '\0'; + } else { + // unknown long- modifier: emit literally "lX" + *ptr++ = '%'; + *ptr++ = 'l'; + *ptr++ = *fmt; + *ptr = '\0'; + } + } else { + switch (*fmt) { + case 'd': { + int num = va_arg(args, int); + ptr += sprintf(ptr, "%d", num); // Append integer + break; + } + case 's': { + char *s = va_arg(args, char *); + ptr += sprintf(ptr, "%s", s); // Append string + break; + } + case 'c': { + char c = (char)va_arg(args, int); // Get character + *ptr++ = c; // Append character + *ptr = '\0'; // Null-terminate + break; + } + default: + // Handle unknown format specifiers + *ptr++ = '%'; + *ptr++ = *fmt; + *ptr = '\0'; + break; + } } } else { *ptr++ = *fmt; // Copy regular characters @@ -991,8 +1032,46 @@ uint32_t htonl(uint32_t hostlong) { } int vsnprintf(char *str, size_t size, const char *format, va_list ap) { - // Simplified using existing sprintf (not safe for production) - return sprintf(str, format, ap); + // simplified implementation + int written = 0; + const char *fmt = format; + char *ptr = str; + while (*fmt != '\0' && written < (int)(size - 1)) { + if (*fmt == '%') { + fmt++; + if (*fmt == 'l' && *(fmt + 1) != '\0') { + // handle long specifiers like %ld + fmt++; + if (*fmt == 'd') { + long val = va_arg(ap, long); + written += sprintf(ptr + written, "%ld", val); + } else { + // unknown long-specifier, emit literally + ptr[written++] = '%'; + ptr[written++] = 'l'; + ptr[written++] = *fmt; + } + } else if (*fmt == 'd') { + int val = va_arg(ap, int); + written += sprintf(ptr + written, "%d", val); + } else if (*fmt == 's') { + char *s = va_arg(ap, char *); + written += sprintf(ptr + written, "%s", s); + } else if (*fmt == 'c') { + char c = (char)va_arg(ap, int); + ptr[written++] = c; + } else { + ptr[written++] = '%'; + if (*fmt) + ptr[written++] = *fmt; + } + } else { + ptr[written++] = *fmt; + } + fmt++; + } + ptr[written] = '\0'; + return written; } int isatty(int fd) { @@ -1140,6 +1219,20 @@ int sigfillset(sigset_t *set) { int fcntl(int fildes, int cmd, ...) { char *msg = "TODO implement fcntl\n"; write(1, msg, strlen(msg)); + + if (cmd == F_DUPFD) { + // va_list args; + // va_start(args, cmd); + // int newfd = va_arg(args, int); + // va_end(args); + + // For simplicity, just return newfd + // return newfd; + + // shortcut: just return the same fildes + return fildes; + } + return -1; } From 56095e6a54b3592fc028c92ba037526b9d5d4e53 Mon Sep 17 00:00:00 2001 From: jbreu <20864561+jbreu@users.noreply.github.com> Date: Sun, 14 Dec 2025 15:07:56 +0100 Subject: [PATCH 05/10] successfully reading the first shell string (but then crashes) --- Makefile | 2 ++ buildenv_rust/Dockerfile | 2 +- kernel/src/interrupt.rs | 12 +++++++++ kernel/src/logging.rs | 4 +-- kernel/src/mem_config.rs | 2 +- kernel/src/syscall.rs | 34 +++++++++++++++++++++++++ storage/generateExt2Img.sh | 2 +- userland/build_dash.sh | 5 ++-- userland/usr/include/arpa/inet.h | 3 +++ userland/usr/include/fcntl.h | 43 +++++++++++++++++--------------- userland/usr/include/libc.h | 1 + userland/usr/include/limits.h | 7 +++++- userland/usr/include/string.h | 2 ++ userland/usr/include/sys/mman.h | 1 + userland/usr/include/termios.h | 7 +++++- userland/usr/include/unistd.h | 3 +++ userland/usr/include/wchar.h | 1 + userland/usr/include/wctype.h | 5 +++- userland/usr/libc.c | 15 +++++++---- 19 files changed, 116 insertions(+), 35 deletions(-) diff --git a/Makefile b/Makefile index bafc3ff..69dee22 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,8 @@ setup: $(x86_64_asm_object_files) .PHONY: userland userland: # gcc userland/src/doom/main.c userland/src/doom/libc.c -static -nostdlib -fno-builtin -g -o build/userspace/x86_64-unknown-none/debug/doom -Wl,--gc-sections && \ + wget https://git.kernel.org/pub/scm/utils/dash/dash.git/snapshot/dash-0.5.13.tar.gz -N && \ + tar -xzf dash-0.5.13.tar.gz -C userland/ && rm dash-0.5.13.tar.gz && \ cd userland && ./build_dash.sh -c && cd .. && \ cd storage && sh generateExt2Img.sh && cd .. diff --git a/buildenv_rust/Dockerfile b/buildenv_rust/Dockerfile index 9723339..c9b19bc 100644 --- a/buildenv_rust/Dockerfile +++ b/buildenv_rust/Dockerfile @@ -12,7 +12,7 @@ RUN echo "deb http://security.debian.org/debian-security bullseye-security main RUN set -x \ && apt-get update \ - && apt-get install -y curl build-essential nasm xorriso grub-pc-bin grub-common make wget libgmp3-dev libmpfr-dev libisl-dev libmpc-dev texinfo make bzip2 patch sudo qemu-system-x86 e2fsprogs + && apt-get install -y curl build-essential nasm xorriso grub-pc-bin grub-common make wget libgmp3-dev libmpfr-dev libisl-dev libmpc-dev texinfo make bzip2 patch sudo qemu-system-x86 e2fsprogs automake autoconf libtool # Pull binutils and gcc source code RUN set -x \ diff --git a/kernel/src/interrupt.rs b/kernel/src/interrupt.rs index 6d0fe64..7927f79 100644 --- a/kernel/src/interrupt.rs +++ b/kernel/src/interrupt.rs @@ -11,6 +11,7 @@ use crate::userland; use crate::util::out_port_b; use core::arch::asm; use core::arch::global_asm; +use core::sync::atomic::AtomicUsize; global_asm!(include_str!("interrupt.S")); @@ -46,6 +47,9 @@ static mut IDT_ENTRIES: [IdtEntryStruct; 256] = [IdtEntryStruct { reserved: 0, }; 256]; +pub static mut STDIN_BUFFER: [char; 0x1000] = ['\0'; 0x1000]; +pub static STDIN_BUFFER_POS: AtomicUsize = AtomicUsize::new(0); + #[unsafe(no_mangle)] pub extern "C" fn isr_handler(error_code: u64, int_no: u64) { let _event = core::hint::black_box(crate::instrument!()); @@ -140,6 +144,14 @@ pub extern "C" fn irq_handler(int_no: u64) { let key = keyboard::get_key_for_scancode(scancode as u8); + unsafe { + STDIN_BUFFER[STDIN_BUFFER_POS.load(core::sync::atomic::Ordering::Relaxed)] = key; + } + STDIN_BUFFER_POS.fetch_add(1, core::sync::atomic::Ordering::Relaxed); + unsafe { + STDIN_BUFFER[STDIN_BUFFER_POS.load(core::sync::atomic::Ordering::Relaxed)] = '\0'; + } + kprint!("{}", key); let lcontrol: char = 0x1d as char; diff --git a/kernel/src/logging.rs b/kernel/src/logging.rs index 188ab36..2123a7f 100644 --- a/kernel/src/logging.rs +++ b/kernel/src/logging.rs @@ -10,7 +10,7 @@ macro_rules! log_with_level { #[macro_export] macro_rules! DEBUG { ($($arg:tt)*) => { - crate::log_with_level!(crate::kprint::Colors::KPrintColorGreen, "[DEBUG] ", $($arg)*); + crate::log_with_level!(crate::kprint::Colors::KPrintColorGreen, "[DEBUG] ", $($arg)*) }; } @@ -24,6 +24,6 @@ macro_rules! INFO { #[macro_export] macro_rules! ERROR { ($($arg:tt)*) => { - crate::log_with_level!(crate::kprint::Colors::KPrintColorRed, "[ERROR] ", $($arg)*); + crate::log_with_level!(crate::kprint::Colors::KPrintColorRed, "[ERROR] ", $($arg)*) }; } diff --git a/kernel/src/mem_config.rs b/kernel/src/mem_config.rs index da8aeb2..67d7e82 100644 --- a/kernel/src/mem_config.rs +++ b/kernel/src/mem_config.rs @@ -47,4 +47,4 @@ pub const MAX_PAGE_FRAMES: usize = 0x100000000 / PAGE_SIZE; // 4 GiB total memor /// Virtual memory layout constants pub const KERNEL_HIGHER_HALF_BASE: usize = 0xffff_8000_0000_0000; pub const KERNEL_STACK_TOP_ADDRESS: usize = 0xffff_ffff_ffff_ffff; -pub const USERSPACE_STACK_TOP_ADDRESS: usize = 0x0000_7fff_ffff_ffff; +pub const USERSPACE_STACK_TOP_ADDRESS: usize = 0x0000_7fff_ffff_fff0; diff --git a/kernel/src/syscall.rs b/kernel/src/syscall.rs index 497acc8..1b4d0df 100644 --- a/kernel/src/syscall.rs +++ b/kernel/src/syscall.rs @@ -1,4 +1,5 @@ use crate::ERROR; +use crate::interrupt; use crate::kprint; use crate::{USERLAND, time}; use crate::{keyboard, vga}; @@ -45,6 +46,7 @@ pub extern "C" fn system_call() -> u64 { 16 => return syscall_getcwd(arg0 as *mut u64, arg1), 17 => return syscall_getppid(), 18 => return syscall_kill(arg0 as u64, arg1 as u32), + 19 => return syscall_read(arg0, arg1, arg2), _ => { ERROR!("Undefined system call triggered: {}", syscall_nr); return 0xdeadbeef; @@ -228,3 +230,35 @@ fn syscall_kill(_pid: u64, _sig: u32) -> u64 { let _event = core::hint::black_box(crate::instrument!()); USERLAND.lock().kill_process(_pid, _sig) as u64 } + +fn syscall_read(filedescriptor: u64, buffer: u64, len: u64) -> u64 { + let _event = core::hint::black_box(crate::instrument!()); + + if filedescriptor == 0 { + // stdin + interrupt::STDIN_BUFFER_POS.store(0, core::sync::atomic::Ordering::Relaxed); + + // wait for input or until len bytes have been read + while interrupt::STDIN_BUFFER_POS.load(core::sync::atomic::Ordering::Relaxed) == 0 + || unsafe { + interrupt::STDIN_BUFFER + [interrupt::STDIN_BUFFER_POS.load(core::sync::atomic::Ordering::Relaxed) - 1] + != '\n' + } + || len < interrupt::STDIN_BUFFER_POS.load(core::sync::atomic::Ordering::Relaxed) as u64 + { + // wait for input + unsafe { asm!("hlt") }; + } + + for i in 0..interrupt::STDIN_BUFFER_POS.load(core::sync::atomic::Ordering::Relaxed) { + unsafe { + *(buffer as *mut u8).add(i) = interrupt::STDIN_BUFFER[i] as u8; + } + } + + return interrupt::STDIN_BUFFER_POS.load(core::sync::atomic::Ordering::Relaxed) as u64; + } else { + todo!(); + } +} diff --git a/storage/generateExt2Img.sh b/storage/generateExt2Img.sh index 4bd0369..bdaea52 100644 --- a/storage/generateExt2Img.sh +++ b/storage/generateExt2Img.sh @@ -25,7 +25,7 @@ fi # Copy the file sudo cp ../doom1.wad /tmp/disk/devdatadoom1.wad -sudo cp ../build/userspace/x86_64-unknown-none/debug/doom /tmp/disk/doom +#sudo cp ../build/userspace/x86_64-unknown-none/debug/doom /tmp/disk/doom sudo cp ../userland/dash-0.5.13/src/dash /tmp/disk/dash # List files diff --git a/userland/build_dash.sh b/userland/build_dash.sh index ea77aa8..d14a659 100644 --- a/userland/build_dash.sh +++ b/userland/build_dash.sh @@ -22,7 +22,8 @@ export LIBS="-lc" if [ $# = 1 ] && [ "$1" = "-c" ]; then #./configure --host=x86_64-jos --prefix=/usr --without-bash-malloc --enable-static-link --disable-threads - make clean + #make clean + ./autogen.sh ./configure --host=x86_64-jos --enable-static fi @@ -35,5 +36,5 @@ for file in Makefile src/Makefile; do fi done -#make clean +make clean make V=1 diff --git a/userland/usr/include/arpa/inet.h b/userland/usr/include/arpa/inet.h index e69de29..a0c575d 100644 --- a/userland/usr/include/arpa/inet.h +++ b/userland/usr/include/arpa/inet.h @@ -0,0 +1,3 @@ +#include "ctype.h" + +uint32_t htonl(uint32_t hostlong); \ No newline at end of file diff --git a/userland/usr/include/fcntl.h b/userland/usr/include/fcntl.h index bc2eb5a..6797bac 100644 --- a/userland/usr/include/fcntl.h +++ b/userland/usr/include/fcntl.h @@ -1,25 +1,28 @@ +#include "stddef.h" + /* Values for the second argument to `fcntl'. */ -#define F_DUPFD 0 /* Duplicate file descriptor. */ -#define F_GETFD 1 /* Get file descriptor flags. */ -#define F_SETFD 2 /* Set file descriptor flags. */ -#define F_GETFL 3 /* Get file status flags. */ -#define F_SETFL 4 /* Set file status flags. */ +#define F_DUPFD 0 /* Duplicate file descriptor. */ +#define F_GETFD 1 /* Get file descriptor flags. */ +#define F_SETFD 2 /* Set file descriptor flags. */ +#define F_GETFL 3 /* Get file status flags. */ +#define F_SETFL 4 /* Set file status flags. */ -#define O_ACCMODE 0003 -#define O_RDONLY 00 -#define O_WRONLY 01 -#define O_RDWR 02 -#define O_CREAT 0100 /* not fcntl */ -#define O_EXCL 0200 /* not fcntl */ -#define O_NOCTTY 0400 /* not fcntl */ -#define O_TRUNC 01000 /* not fcntl */ -#define O_APPEND 02000 -#define O_NONBLOCK 04000 -#define O_NDELAY O_NONBLOCK -#define O_SYNC 010000 -#define O_FSYNC O_SYNC -#define O_ASYNC 020000 +#define O_ACCMODE 0003 +#define O_RDONLY 00 +#define O_WRONLY 01 +#define O_RDWR 02 +#define O_CREAT 0100 /* not fcntl */ +#define O_EXCL 0200 /* not fcntl */ +#define O_NOCTTY 0400 /* not fcntl */ +#define O_TRUNC 01000 /* not fcntl */ +#define O_APPEND 02000 +#define O_NONBLOCK 04000 +#define O_NDELAY O_NONBLOCK +#define O_SYNC 010000 +#define O_FSYNC O_SYNC +#define O_ASYNC 020000 #define FD_CLOEXEC 1 -int fcntl(int fd, int op, ... /* arg */ ); \ No newline at end of file +int fcntl(int fd, int op, ... /* arg */); +int tee(int fd_in, int fd_out, size_t len, unsigned int flags); \ No newline at end of file diff --git a/userland/usr/include/libc.h b/userland/usr/include/libc.h index ababe8d..caf957b 100644 --- a/userland/usr/include/libc.h +++ b/userland/usr/include/libc.h @@ -14,6 +14,7 @@ #include "stdlib.h" #include "string.h" #include "sys/times.h" +#include "termios.h" #include "wchar.h" #include "wctype.h" diff --git a/userland/usr/include/limits.h b/userland/usr/include/limits.h index 6da9aec..7837695 100644 --- a/userland/usr/include/limits.h +++ b/userland/usr/include/limits.h @@ -1,5 +1,10 @@ +#ifndef LIMITS_H +#define LIMITS_H + #define PATH_MAX 4096 #define NAME_MAX 4096 -#define BUFSIZ 4096 \ No newline at end of file +#define BUFSIZ 4096 + +#endif /* LIMITS_H */ \ No newline at end of file diff --git a/userland/usr/include/string.h b/userland/usr/include/string.h index 97132d7..9b6bcce 100644 --- a/userland/usr/include/string.h +++ b/userland/usr/include/string.h @@ -10,6 +10,7 @@ // String and memory functions uint64_t strlen(const char *str); bool strcmp(const char *a, const char *b); +int strncmp(const char *s1, const char *s2, size_t n); char *strcpy(char *destination, const char *source); char *strncpy(char *destination, const char *source, size_t num); char *stpcpy(char *dest, const char *src); @@ -26,6 +27,7 @@ char *strstr(const char *str, const char *substr); char *strdup(const char *str1); size_t strspn(const char *str1, const char *str2); double strtod(char const *str, char **endptr); +int strcoll(const char *s1, const char *s2); int strcasecmp(const char *s1, const char *s2); int strncasecmp(const char *s1, const char *s2, size_t n); diff --git a/userland/usr/include/sys/mman.h b/userland/usr/include/sys/mman.h index e69de29..62c03b7 100644 --- a/userland/usr/include/sys/mman.h +++ b/userland/usr/include/sys/mman.h @@ -0,0 +1 @@ +int memfd_create(const char *name, unsigned int flags); \ No newline at end of file diff --git a/userland/usr/include/termios.h b/userland/usr/include/termios.h index d73fdad..ff80898 100644 --- a/userland/usr/include/termios.h +++ b/userland/usr/include/termios.h @@ -1,3 +1,6 @@ +#ifndef _TERMIOS_H +#define _TERMIOS_H + typedef unsigned int tcflag_t; typedef unsigned char cc_t; @@ -47,4 +50,6 @@ struct termios { cc_t c_cc[]; /* control characters */ }; -int tcgetattr(int fd, struct termios *termios_p); \ No newline at end of file +int tcgetattr(int fd, struct termios *termios_p); + +#endif /* _TERMIOS_H */ \ No newline at end of file diff --git a/userland/usr/include/unistd.h b/userland/usr/include/unistd.h index bd2e730..4f9d9e4 100644 --- a/userland/usr/include/unistd.h +++ b/userland/usr/include/unistd.h @@ -1,7 +1,9 @@ #include "chartypes.h" #include "ctype.h" +#include "limits.h" #include "stddef.h" #include "sys/stat.h" +#include "termios.h" /* whence values for lseek(2) */ #define SEEK_SET 0 /* Set file offset to offset */ @@ -26,6 +28,7 @@ int chdir(const char *path); void _exit(int status) __attribute__((noreturn)); int pipe(int pipefd[2]); +int dup(int fildes); int dup2(int oldfd, int newfd); int isatty(int fd); diff --git a/userland/usr/include/wchar.h b/userland/usr/include/wchar.h index 46a3344..9794e3d 100644 --- a/userland/usr/include/wchar.h +++ b/userland/usr/include/wchar.h @@ -16,6 +16,7 @@ size_t mbrlen(const char *s, size_t n2, mbstate_t *ps); size_t mbrtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *ps); wchar_t *wcschr(const wchar_t *wcs, wchar_t wc); int iswspace(wchar_t wc); +int iswblank(wint_t wc); size_t mbsrtowcs(wchar_t *dest, const char **src, size_t len, mbstate_t *ps); #endif /* wchar.h */ \ No newline at end of file diff --git a/userland/usr/include/wctype.h b/userland/usr/include/wctype.h index 4335da1..a56ea7f 100644 --- a/userland/usr/include/wctype.h +++ b/userland/usr/include/wctype.h @@ -1,3 +1,6 @@ #define wchar_t long #define wctype_t int -#define wint_t long \ No newline at end of file +#define wint_t long + +wctype_t wctype(const char *property); +int iswctype(wint_t wc, wctype_t desc); \ No newline at end of file diff --git a/userland/usr/libc.c b/userland/usr/libc.c index 5442038..49c75cf 100644 --- a/userland/usr/libc.c +++ b/userland/usr/libc.c @@ -1390,10 +1390,9 @@ int sigsetmask(int mask) { } ssize_t read(int fd, void *buf, size_t count) { - // TODO implement read - char *msg = "TODO implement read\n"; - write(1, msg, strlen(msg)); - return -1; + uint64_t result; + DO_SYSCALL(19, result, fd, buf, count); + return (ssize_t)result; } int tee(int fd_in, int fd_out, size_t len, unsigned int flags) { @@ -1407,7 +1406,13 @@ int tcgetattr(int fd, struct termios *termios_p) { // TODO implement tcgetattr char *msg = "TODO implement tcgetattr\n"; write(1, msg, strlen(msg)); - return -1; + + if (fd >= 0 && fd <= 2) { + // stdin, stdout, stderr + termios_p->c_lflag = ICANON | ECHO; + return 1; + } + return 0; } void abort(void) { From 40552415f9ff23cf590502fa52fa65530349f0e3 Mon Sep 17 00:00:00 2001 From: jbreu <20864561+jbreu@users.noreply.github.com> Date: Sun, 21 Dec 2025 09:32:45 +0100 Subject: [PATCH 06/10] first running dash command --- kernel/src/interrupt.rs | 20 +++--- kernel/src/keyboard.rs | 4 +- kernel/src/process.rs | 52 ++++++++++++++ kernel/src/syscall.rs | 6 ++ kernel/src/userland.rs | 6 ++ userland/usr/include/setjmp.h | 24 ++++++- userland/usr/include/stdlib.h | 30 ++++----- userland/usr/include/string.h | 2 +- userland/usr/libc.c | 123 +++++++++++++++++++++++++--------- 9 files changed, 208 insertions(+), 59 deletions(-) diff --git a/kernel/src/interrupt.rs b/kernel/src/interrupt.rs index 7927f79..fea9fd3 100644 --- a/kernel/src/interrupt.rs +++ b/kernel/src/interrupt.rs @@ -144,15 +144,19 @@ pub extern "C" fn irq_handler(int_no: u64) { let key = keyboard::get_key_for_scancode(scancode as u8); - unsafe { - STDIN_BUFFER[STDIN_BUFFER_POS.load(core::sync::atomic::Ordering::Relaxed)] = key; - } - STDIN_BUFFER_POS.fetch_add(1, core::sync::atomic::Ordering::Relaxed); - unsafe { - STDIN_BUFFER[STDIN_BUFFER_POS.load(core::sync::atomic::Ordering::Relaxed)] = '\0'; - } + if key != 0xfe as char { + unsafe { + STDIN_BUFFER[STDIN_BUFFER_POS.load(core::sync::atomic::Ordering::Relaxed)] = + key; + } + STDIN_BUFFER_POS.fetch_add(1, core::sync::atomic::Ordering::Relaxed); + unsafe { + STDIN_BUFFER[STDIN_BUFFER_POS.load(core::sync::atomic::Ordering::Relaxed)] = + '\0'; + } - kprint!("{}", key); + kprint!("{}", key); + } let lcontrol: char = 0x1d as char; diff --git a/kernel/src/keyboard.rs b/kernel/src/keyboard.rs index c48a5b1..18b7ef1 100644 --- a/kernel/src/keyboard.rs +++ b/kernel/src/keyboard.rs @@ -6,7 +6,7 @@ static SCANCODES: [char; 69] = [ '1', '2', '3', - '4', + '$', '5', '6', '7', @@ -52,7 +52,7 @@ static SCANCODES: [char; 69] = [ 'b', 'n', 'm', - 0xfe as char, + ';', 0xfe as char, 0xfe as char, 0xfe as char, diff --git a/kernel/src/process.rs b/kernel/src/process.rs index 907b088..9f1c053 100644 --- a/kernel/src/process.rs +++ b/kernel/src/process.rs @@ -442,6 +442,58 @@ impl Process { } } + pub fn realloc(&mut self, ptr: u64, new_size: usize) -> u64 { + let _event = core::hint::black_box(crate::instrument!()); + + unsafe { + if ptr == 0 { + return self.malloc(new_size); + } + + let layout = core::alloc::Layout::from_size_align_unchecked(new_size, 0x8); + + if new_size == 0 { + self.heap_allocator + .lock() + .deallocate(core::ptr::NonNull::new_unchecked(ptr as *mut u8), layout); + return 0; + } + + /* + // SAFETY: the caller must ensure that the `new_size` does not overflow. + // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid. + let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) }; + // SAFETY: the caller must ensure that `new_layout` is greater than zero. + let new_ptr = unsafe { self.alloc(new_layout) }; + if !new_ptr.is_null() { + // SAFETY: the previously allocated block cannot overlap the newly allocated block. + // The safety contract for `dealloc` must be upheld by the caller. + unsafe { + ptr::copy_nonoverlapping(ptr, new_ptr, cmp::min(layout.size(), new_size)); + self.dealloc(ptr, layout); + } + } + new_ptr */ + + let new_ptr = self.heap_allocator.lock().allocate_first_fit(layout); + + if new_ptr.is_ok() { + let new_address = new_ptr.unwrap().as_ptr() as u64; + + core::ptr::copy_nonoverlapping(ptr as *const u8, new_address as *mut u8, new_size); + + self.heap_allocator + .lock() + .deallocate(core::ptr::NonNull::new_unchecked(ptr as *mut u8), layout); + + return new_address; + } + + ERROR!("Failed to reallocate memory\n"); + panic!("Out of memory"); + } + } + pub fn launch(&mut self) { let _event = core::hint::black_box(crate::instrument!()); diff --git a/kernel/src/syscall.rs b/kernel/src/syscall.rs index 1b4d0df..3dabf66 100644 --- a/kernel/src/syscall.rs +++ b/kernel/src/syscall.rs @@ -47,6 +47,7 @@ pub extern "C" fn system_call() -> u64 { 17 => return syscall_getppid(), 18 => return syscall_kill(arg0 as u64, arg1 as u32), 19 => return syscall_read(arg0, arg1, arg2), + 20 => return syscall_realloc(arg0, arg1 as usize), _ => { ERROR!("Undefined system call triggered: {}", syscall_nr); return 0xdeadbeef; @@ -109,6 +110,11 @@ fn syscall_malloc(size: usize) -> u64 { return USERLAND.lock().process_malloc(size); } +fn syscall_realloc(ptr: u64, size: usize) -> u64 { + let _event = core::hint::black_box(crate::instrument!()); + return USERLAND.lock().process_realloc(ptr, size); +} + fn syscall_plot_pixel(x: u32, y: u32, color: u32) -> u64 { //let _event = core::hint::black_box(crate::instrument!()); // too much noise vga::vga_plot_pixel(x, y, color as u8); diff --git a/kernel/src/userland.rs b/kernel/src/userland.rs index 459d2ee..e3d96c6 100644 --- a/kernel/src/userland.rs +++ b/kernel/src/userland.rs @@ -42,6 +42,12 @@ impl Userland { return self.processes[self.current_process].malloc(size); } + pub fn process_realloc(&mut self, ptr: u64, size: usize) -> u64 { + let _event = core::hint::black_box(crate::instrument!()); + + return self.processes[self.current_process].realloc(ptr, size); + } + pub fn switch_to_userland(&mut self, mutex: &Mutex) { let _event = core::hint::black_box(crate::instrument!()); diff --git a/userland/usr/include/setjmp.h b/userland/usr/include/setjmp.h index fee5b08..89905a2 100644 --- a/userland/usr/include/setjmp.h +++ b/userland/usr/include/setjmp.h @@ -1 +1,23 @@ -typedef long long int jmp_buf[8]; \ No newline at end of file + +#ifndef SETJMP_H +#define SETJMP_H + +#include "ctype.h" + +typedef struct { + uint64_t rsp; + uint64_t rip; + uint64_t rbx; + uint64_t rbp; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; +} jmp_buf_struct; + +typedef jmp_buf_struct jmp_buf[1]; + +void longjmp(jmp_buf env, int status); +int setjmp(jmp_buf env); + +#endif // SETJMP_H \ No newline at end of file diff --git a/userland/usr/include/stdlib.h b/userland/usr/include/stdlib.h index e89c515..d67c006 100644 --- a/userland/usr/include/stdlib.h +++ b/userland/usr/include/stdlib.h @@ -1,26 +1,24 @@ -#include "stddef.h" -#include "time.h" -#include "stdint.h" #include "chartypes.h" #include "fnmatch.h" -#include "sys/wait.h" +#include "stddef.h" +#include "stdint.h" #include "sys/resource.h" +#include "sys/wait.h" +#include "time.h" -void exit( int exit_code ); -void* malloc(long unsigned size); -void* realloc (void* ptr, size_t size); -void free(void* address); - -void* bsearch (const void* key, const void* base, size_t num, size_t size, int (*compar)(const void*,const void*)); +void exit(int exit_code); +void *malloc(long unsigned size); +void *realloc(void *ptr, size_t size); +void free(void *address); -void abort (void); +void *bsearch(const void *key, const void *base, size_t num, size_t size, + int (*compar)(const void *, const void *)); -typedef long long int jmp_buf[8]; // Example, depends on the system -void longjmp( jmp_buf env, int status ); -int setjmp(jmp_buf env); +void abort(void); -int atoi (const char * str); +int atoi(const char *str); void *alloca(size_t size); -void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*)); \ No newline at end of file +void qsort(void *base, size_t num, size_t size, + int (*compar)(const void *, const void *)); \ No newline at end of file diff --git a/userland/usr/include/string.h b/userland/usr/include/string.h index 9b6bcce..33101f0 100644 --- a/userland/usr/include/string.h +++ b/userland/usr/include/string.h @@ -9,7 +9,7 @@ // String and memory functions uint64_t strlen(const char *str); -bool strcmp(const char *a, const char *b); +int strcmp(const char *a, const char *b); int strncmp(const char *s1, const char *s2, size_t n); char *strcpy(char *destination, const char *source); char *strncpy(char *destination, const char *source, size_t num); diff --git a/userland/usr/libc.c b/userland/usr/libc.c index 49c75cf..82999bd 100644 --- a/userland/usr/libc.c +++ b/userland/usr/libc.c @@ -58,19 +58,12 @@ uint64_t strlen(const char *str) { return len; } -bool strcmp(const char *a, const char *b) { - int i = 0; - - while (a[i] != '\0') { - if (a[i] == b[i]) { - i++; - continue; - } else { - return false; - } +int strcmp(const char *a, const char *b) { + while (*a && (*a == *b)) { + a++; + b++; } - - return true; + return *(unsigned char *)a - *(unsigned char *)b; } // Write function using syscall @@ -300,35 +293,67 @@ int sprintf(char *str, const char *format, ...) { switch (*fmt) { case 'd': { int num = va_arg(args, int); - ptr += sprintf(ptr, "%d", num); // Append integer + unsigned int v; + int neg = 0; + if (num < 0) { + neg = 1; + v = (unsigned int)(-(num + 1)) + 1U; + } else { + v = (unsigned int)num; + } + char tmp[16]; + int ti = 0; + if (v == 0) { + tmp[ti++] = '0'; + } else { + while (v) { + tmp[ti++] = '0' + (v % 10); + v /= 10; + } + } + if (neg) { + *ptr++ = '-'; + } + while (ti--) { + *ptr++ = tmp[ti]; + } break; } case 's': { char *s = va_arg(args, char *); - ptr += sprintf(ptr, "%s", s); // Append string + if (s) { + while (*s) { + *ptr++ = *s++; + } + } else { + /* print (null) for NULL strings */ + const char *nulls = "(null)"; + const char *ns = nulls; + while (*ns) + *ptr++ = *ns++; + } break; } case 'c': { - char c = (char)va_arg(args, int); // Get character - *ptr++ = c; // Append character - *ptr = '\0'; // Null-terminate + char c = (char)va_arg(args, int); + *ptr++ = c; break; } default: // Handle unknown format specifiers *ptr++ = '%'; - *ptr++ = *fmt; - *ptr = '\0'; + if (*fmt) + *ptr++ = *fmt; break; } } } else { - *ptr++ = *fmt; // Copy regular characters - *ptr = '\0'; // Null-terminate + *ptr++ = *fmt; } fmt++; } + *ptr = '\0'; va_end(args); return (int)(ptr - str); // Return the length of the formatted string } @@ -1090,15 +1115,37 @@ void _exit(int status) { } void longjmp(jmp_buf env, int val) { - // TODO implement - char *msg = "TODO implement longjmp\n"; - write(1, msg, strlen(msg)); + if (val == 0) + val = 1; // make sure return value is non-zero + asm volatile("movq 16(%0), %%rbx\n\t" + "movq 24(%0), %%rbp\n\t" + "movq 32(%0), %%r12\n\t" + "movq 40(%0), %%r13\n\t" + "movq 48(%0), %%r14\n\t" + "movq 56(%0), %%r15\n\t" + "movl %1, %%eax\n\t" // return value in EAX + "movq 0(%0), %%rsp\n\t" + "jmp *8(%0)\n\t" // jump to saved RIP + : + : "r"(env), "r"(val) + : "memory", "rax"); } int setjmp(jmp_buf env) { - // TODO implement - char *msg = "TODO implement setjmp\n"; - write(1, msg, strlen(msg)); + asm volatile("movq %%rsp, 0(%0)\n\t" // save stack pointer + "leaq 1f(%%rip), %%rax\n\t" + "movq %%rax, 8(%0)\n\t" // save instruction pointer + "movq %%rbx, 16(%0)\n\t" + "movq %%rbp, 24(%0)\n\t" + "movq %%r12, 32(%0)\n\t" + "movq %%r13, 40(%0)\n\t" + "movq %%r14, 48(%0)\n\t" + "movq %%r15, 56(%0)\n\t" + "xor %%eax, %%eax\n\t" // return 0 + "1:\n" + : /* no outputs */ + : "r"(env) + : "rax", "memory"); return 0; } @@ -1297,10 +1344,24 @@ mode_t umask(mode_t mask) { } void *realloc(void *ptr, size_t size) { - // TODO implement realloc - char *msg = "TODO implement realloc\n"; - write(1, msg, strlen(msg)); - return NULL; + if (ptr == NULL) { + return malloc(size); + } + if (size == 0) { + free(ptr); + return NULL; + } + + // For simplicity, we won't track the original size of ptr. + // We'll just allocate new memory and copy a fixed amount. + void *new_ptr = malloc(size); + if (new_ptr == NULL) { + return NULL; + } + + memcpy(new_ptr, ptr, size); + free(ptr); + return new_ptr; } char *setlocale(int category, const char *locale) { From 222d2715415fbb14e9ae95c64b3e1c2bd5dcf9b0 Mon Sep 17 00:00:00 2001 From: jbreu <20864561+jbreu@users.noreply.github.com> Date: Mon, 22 Dec 2025 15:34:34 +0100 Subject: [PATCH 07/10] use compiler builtins for set/longjmp --- .gitignore | 1 + Makefile | 8 ++++---- userland/usr/include/libc.h | 1 + userland/usr/include/setjmp.h | 18 ++++-------------- userland/usr/libc.c | 35 ----------------------------------- 5 files changed, 10 insertions(+), 53 deletions(-) diff --git a/.gitignore b/.gitignore index 8fd3242..eed0fff 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ tools/perf/profile.proto tools/perf/output.pb.gz tools/perf/__pycache__/ userland/dash-0.5.13/ +dash-0.5.13.tar.gz diff --git a/Makefile b/Makefile index 69dee22..6b1ba20 100644 --- a/Makefile +++ b/Makefile @@ -10,14 +10,14 @@ setup: $(x86_64_asm_object_files) mkdir -p build/kernel && \ mkdir -p build/userspace/x86_64-unknown-none/debug/ && \ mkdir -p dist/x86_64 && \ - wget https://github.com/Daivuk/PureDOOM/raw/48376ddd6bbdb70085dab91feb1c6ceef80fa9b7/doom1.wad -N && \ - wget -P userland/src/doom/ https://raw.githubusercontent.com/Daivuk/PureDOOM/48376ddd6bbdb70085dab91feb1c6ceef80fa9b7/PureDOOM.h -N + test -f userland/src/doom/doom1.wad || wget https://github.com/Daivuk/PureDOOM/raw/48376ddd6bbdb70085dab91feb1c6ceef80fa9b7/doom1.wad -O userland/src/doom/doom1.wad && \ + test -f userland/src/doom/PureDOOM.h || wget -P userland/src/doom/ https://raw.githubusercontent.com/Daivuk/PureDOOM/48376ddd6bbdb70085dab91feb1c6ceef80fa9b7/PureDOOM.h -N .PHONY: userland userland: # gcc userland/src/doom/main.c userland/src/doom/libc.c -static -nostdlib -fno-builtin -g -o build/userspace/x86_64-unknown-none/debug/doom -Wl,--gc-sections && \ - wget https://git.kernel.org/pub/scm/utils/dash/dash.git/snapshot/dash-0.5.13.tar.gz -N && \ - tar -xzf dash-0.5.13.tar.gz -C userland/ && rm dash-0.5.13.tar.gz && \ + test -f dash-0.5.13.tar.gz || wget https://git.kernel.org/pub/scm/utils/dash/dash.git/snapshot/dash-0.5.13.tar.gz -N && \ + tar -xzf dash-0.5.13.tar.gz -C userland/ && \ cd userland && ./build_dash.sh -c && cd .. && \ cd storage && sh generateExt2Img.sh && cd .. diff --git a/userland/usr/include/libc.h b/userland/usr/include/libc.h index caf957b..9461deb 100644 --- a/userland/usr/include/libc.h +++ b/userland/usr/include/libc.h @@ -6,6 +6,7 @@ #include "errno.h" #include "fcntl.h" #include "inttypes.h" +#include "setjmp.h" #include "signal.h" #include "stdbool.h" #include "stddef.h" diff --git a/userland/usr/include/setjmp.h b/userland/usr/include/setjmp.h index 89905a2..8a5e9f5 100644 --- a/userland/usr/include/setjmp.h +++ b/userland/usr/include/setjmp.h @@ -3,21 +3,11 @@ #define SETJMP_H #include "ctype.h" +#include "stdint.h" -typedef struct { - uint64_t rsp; - uint64_t rip; - uint64_t rbx; - uint64_t rbp; - uint64_t r12; - uint64_t r13; - uint64_t r14; - uint64_t r15; -} jmp_buf_struct; +typedef long int jmp_buf[8]; -typedef jmp_buf_struct jmp_buf[1]; - -void longjmp(jmp_buf env, int status); -int setjmp(jmp_buf env); +#define setjmp(env) __builtin_setjmp(env) +#define longjmp(env, val) __builtin_longjmp(env, val) #endif // SETJMP_H \ No newline at end of file diff --git a/userland/usr/libc.c b/userland/usr/libc.c index 82999bd..45d95f0 100644 --- a/userland/usr/libc.c +++ b/userland/usr/libc.c @@ -1114,41 +1114,6 @@ void _exit(int status) { write(1, msg, strlen(msg)); } -void longjmp(jmp_buf env, int val) { - if (val == 0) - val = 1; // make sure return value is non-zero - asm volatile("movq 16(%0), %%rbx\n\t" - "movq 24(%0), %%rbp\n\t" - "movq 32(%0), %%r12\n\t" - "movq 40(%0), %%r13\n\t" - "movq 48(%0), %%r14\n\t" - "movq 56(%0), %%r15\n\t" - "movl %1, %%eax\n\t" // return value in EAX - "movq 0(%0), %%rsp\n\t" - "jmp *8(%0)\n\t" // jump to saved RIP - : - : "r"(env), "r"(val) - : "memory", "rax"); -} - -int setjmp(jmp_buf env) { - asm volatile("movq %%rsp, 0(%0)\n\t" // save stack pointer - "leaq 1f(%%rip), %%rax\n\t" - "movq %%rax, 8(%0)\n\t" // save instruction pointer - "movq %%rbx, 16(%0)\n\t" - "movq %%rbp, 24(%0)\n\t" - "movq %%r12, 32(%0)\n\t" - "movq %%r13, 40(%0)\n\t" - "movq %%r14, 48(%0)\n\t" - "movq %%r15, 56(%0)\n\t" - "xor %%eax, %%eax\n\t" // return 0 - "1:\n" - : /* no outputs */ - : "r"(env) - : "rax", "memory"); - return 0; -} - size_t mbrlen(const char *s, size_t n, mbstate_t *ps) { // Simplified implementation assuming single-byte characters if (n == 0 || s == NULL || *s == '\0') { From 86a4260946a7fb12b231e8285f14a9d49e699f52 Mon Sep 17 00:00:00 2001 From: jbreu <20864561+jbreu@users.noreply.github.com> Date: Wed, 31 Dec 2025 12:13:51 +0100 Subject: [PATCH 08/10] update tests --- kernel/src/keyboard.rs | 2 +- test/test_kernel_startup.py | 12 +++++++++ userland/build_bash.sh | 52 ------------------------------------- 3 files changed, 13 insertions(+), 53 deletions(-) delete mode 100644 userland/build_bash.sh diff --git a/kernel/src/keyboard.rs b/kernel/src/keyboard.rs index 18b7ef1..afe1d0a 100644 --- a/kernel/src/keyboard.rs +++ b/kernel/src/keyboard.rs @@ -28,7 +28,7 @@ static SCANCODES: [char; 69] = [ 'o', 'p', 0xfe as char, - 0xfe as char, + '+' as char, '\n', //VK_RETURN -> map to ascii line feed 0x1d as char, //VK_LCONTROL 'a', diff --git a/test/test_kernel_startup.py b/test/test_kernel_startup.py index 443b2f3..90220fc 100644 --- a/test/test_kernel_startup.py +++ b/test/test_kernel_startup.py @@ -34,6 +34,7 @@ def test_userland(qemu: QEMUConnection): assert b"Hallo Carina" in output +@pytest.mark.skip(reason="Doom currently disabled in userland") def test_userland_doom(qemu: QEMUConnection): """Test that userland Doom starts and outputs expected messages""" # Check for Doom initialization messages @@ -59,6 +60,17 @@ def test_userland_doom(qemu: QEMUConnection): assert message in output +def test_userland_dash(qemu: QEMUConnection): + """Test that userland dash starts and outputs expected messages""" + + output = qemu.read_until(b"$") + assert b"$" in output + + qemu.send_key_press("expr 3 + 4\n") + output = qemu.read_until(b"7") + assert b"7" in output + + def test_retrieve_profiling(qemu: QEMUConnection): # Wait before sending key press to ensure system is ready qemu.read_until(b"Backing up text mode palette:") diff --git a/userland/build_bash.sh b/userland/build_bash.sh deleted file mode 100644 index 38a773c..0000000 --- a/userland/build_bash.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash -set -eux - -mv /usr/include /usr/include-bak | true - -cd usr - -gcc -c *.c -ar rcs libc.a *.o -mv libc.a lib - -cd ../bash - -export CFLAGS="--sysroot=/root/env/userland/ -I/root/env/userland/usr/include/" -export CPPFLAGS="--sysroot=/root/env/userland/ -I/root/env/userland/usr/include/" -export CPPFLAGS_FOR_BUILD="-I/root/env/userland/usr/include/" - -# TODO reactivate for jos build -#export LDFLAGS="-L/root/env/userland/usr/lib -static -nostdlib -fno-builtin -Wl,--trace" -#export LOCAL_LDFLAGS="-L/root/env/userland/usr/lib -static -nostdlib -fno-builtin -Wl,--trace" -#export LDFLAGS_FOR_BUILD="-L/root/env/userland/usr/lib -static -nostdlib -fno-builtin -Wl,--trace" - -# Build tools on linux -export LDFLAGS="-nostdlib -fno-builtin -Wl,--trace" -export LOCAL_LDFLAGS=" -Wl,--trace" -export LDFLAGS_FOR_BUILD="-Wl,--trace" - -sed -i '/^LIBS_FOR_BUILD =/c\LIBS_FOR_BUILD = @LIBS_FOR_BUILD@' Makefile.in -#export LIBS_FOR_BUILD="-lc" - - -# x86_64-jos: to make it work, add everywhere in configure, config.sub and config.guess where haiku is mentioned a similar entry for jos - -if [ $# = 1 ] && [ "$1" = "-c" ]; then - #./configure --host=x86_64-jos --prefix=/usr --without-bash-malloc --enable-static-link --disable-threads - ./configure --disable-minimal-config --disable-alias --disable-alt-array-implementation --disable-arith-for-command --disable-array-variables --disable-bang-history --disable-brace-expansion --disable-casemod-attributes --disable-casemod-expansions --disable-command-timing --disable-cond-command --disable-cond-regexp --disable-coprocesses --disable-debugger --disable-dev-fd-stat-broken --disable-direxpand-default --disable-directory-stack --disable-disabled-builtins --disable-dparen-arithmetic --disable-extended-glob --disable-extended-glob-default --disable-function-import --disable-glob-asciiranges-default --disable-help-builtin --disable-history --disable-job-control --disable-multibyte --disable-net-redirections --disable-process-substitution --disable-progcomp --disable-prompt-string-decoding --disable-readline --disable-restricted --disable-select --disable-separate-helpfiles --disable-single-help-strings --disable-strict-posix-default --disable-translatable-strings --disable-usg-echo-default --disable-xpg-echo-default --disable-mem-scramble --disable-profiling --disable-static-link --disable-largefile --disable-nls --disable-threads --disable-rpath --without-bash-malloc --disable-threads --host=x86_64-pc-linux-gnu #--enable-static-link -fi - -echo "Building ~/env/userland/bash/builtins" -(cd ~/env/userland/bash/builtins && make clean && make) -find ~/env/userland/bash/builtins -name '*.a' -exec cp {} ~/env/userland/usr/lib/ \; - -#for each folder in /env/userland/bash/lib/ run make and then copy the .a file to /env/userland/usr/lib/ -for dir in ~/env/userland/bash/lib/*; do - echo "Building $dir" - (cd "$dir" && make clean && make) - find "$dir" -name '*.a' -exec cp {} ~/env/userland/usr/lib/ \; -done - - -make clean -make \ No newline at end of file From 397340a52f2e7f1dea49ed2bbd8a17c7ff561cb9 Mon Sep 17 00:00:00 2001 From: jbreu <20864561+jbreu@users.noreply.github.com> Date: Wed, 31 Dec 2025 12:18:02 +0100 Subject: [PATCH 09/10] implement stat syscall --- Makefile | 2 +- kernel/src/filesystem.rs | 37 ++++++++++++ kernel/src/process.rs | 105 ++++++++++++++++++++++++++--------- kernel/src/syscall.rs | 29 +++++++++- storage/generateExt2Img.sh | 4 +- userland/src/doom/inttypes.h | 4 ++ userland/src/doom/libc.c | 3 - userland/src/doom/libc.h | 81 +++++++++++++-------------- userland/src/doom/main.c | 4 +- userland/src/doom/stdbool.h | 9 +++ userland/src/doom/stdint.h | 5 ++ 11 files changed, 206 insertions(+), 77 deletions(-) create mode 100644 userland/src/doom/inttypes.h create mode 100644 userland/src/doom/stdbool.h create mode 100644 userland/src/doom/stdint.h diff --git a/Makefile b/Makefile index 6b1ba20..2aa291c 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ setup: $(x86_64_asm_object_files) .PHONY: userland userland: - # gcc userland/src/doom/main.c userland/src/doom/libc.c -static -nostdlib -fno-builtin -g -o build/userspace/x86_64-unknown-none/debug/doom -Wl,--gc-sections && \ + gcc userland/src/doom/main.c userland/src/doom/libc.c -static -nostdlib -fno-builtin -g -o build/userspace/x86_64-unknown-none/debug/doom -Wl,--gc-sections --sysroot=/root/env/userland/src/doom && \ test -f dash-0.5.13.tar.gz || wget https://git.kernel.org/pub/scm/utils/dash/dash.git/snapshot/dash-0.5.13.tar.gz -N && \ tar -xzf dash-0.5.13.tar.gz -C userland/ && \ cd userland && ./build_dash.sh -c && cd .. && \ diff --git a/kernel/src/filesystem.rs b/kernel/src/filesystem.rs index 86a46ed..e79446e 100644 --- a/kernel/src/filesystem.rs +++ b/kernel/src/filesystem.rs @@ -139,6 +139,26 @@ impl FileHandle { } return 0; } + + pub fn stat(&self) -> Stat { + let _event = core::hint::black_box(crate::instrument!()); + + Stat { + st_dev: 0, + st_ino: 0, + st_mode: self.inode.mode as u64, + st_nlink: self.inode.links_count as u64, + st_uid: self.inode.uid as u64, + st_gid: self.inode.gid as u64, + st_rdev: 0, + st_size: self.inode.size as u64, + st_blksize: FILE_SYSTEM.block_size as u64, + st_blocks: self.inode.blocks as u64, + st_atime: self.inode.atime, + st_mtime: self.inode.mtime, + st_ctime: self.inode.ctime, + } + } } //https://slideplayer.com/slide/16554195/96/images/48/Linux+Example:+Ext2/3+Disk+Layout.jpg @@ -233,6 +253,23 @@ pub struct Inode { _remaining: [u8; 28], // Combined remaining fields } +#[repr(C, packed)] +pub struct Stat { + pub st_dev: u64, + pub st_ino: u64, + pub st_mode: u64, + pub st_nlink: u64, + pub st_uid: u64, + pub st_gid: u64, + pub st_rdev: u64, + pub st_size: u64, + pub st_blksize: u64, + pub st_blocks: u64, + pub st_atime: u32, + pub st_mtime: u32, + pub st_ctime: u32, +} + #[repr(C, packed)] #[derive(Debug, Copy, Clone)] struct DirectoryEntry { diff --git a/kernel/src/process.rs b/kernel/src/process.rs index 9f1c053..536f284 100644 --- a/kernel/src/process.rs +++ b/kernel/src/process.rs @@ -16,7 +16,7 @@ pub static NEXT_PROCESS_ID: AtomicUsize = AtomicUsize::new(1); // stores a process' registers when it gets interrupted #[repr(C)] -#[derive(Default)] +#[derive(Default, Clone)] struct RegistersStruct { // Has to be always in sync with asm macro "pop_all_registers" xmm7: [u64; 2], @@ -69,13 +69,6 @@ fn _print_page_table_tree_for_cr3() { print_page_table_tree(cr3); } -fn check_half(entry: *const u64) -> *const u64 { - if entry < 0xffff800000000000 as *const u64 { - return (entry as u64 + 0xffff800000000000 as u64) as *const u64; - } - entry -} - fn print_page_table_tree(start_addr: u64) { let _event = core::hint::black_box(crate::instrument!()); let entry_mask = 0x0008_ffff_ffff_f800; @@ -84,18 +77,22 @@ fn print_page_table_tree(start_addr: u64) { kprint!("start_addr: {:#x}\n", start_addr); for l4_entry in 0..512 { - let l4bits = *check_half((start_addr + l4_entry * 8) as *const u64); + let l4bits = + *(((start_addr + l4_entry * 8) | KERNEL_HIGHER_HALF_BASE as u64) as *const u64); if l4bits != 0 { kprint!(" L4: {} - {:#x}\n", l4_entry, l4bits & entry_mask); for l3_entry in 0..512 { - let l3bits = *check_half(((l4bits & entry_mask) + l3_entry * 8) as *const u64); + let l3bits = *((((l4bits & entry_mask) + l3_entry * 8) + | KERNEL_HIGHER_HALF_BASE as u64) + as *const u64); if l3bits != 0 { kprint!(" L3: {} - {:#x}\n", l3_entry, l3bits & entry_mask); for l2_entry in 0..512 { - let l2bits = - *check_half(((l3bits & entry_mask) + l2_entry * 8) as *const u64); + let l2bits = *((((l3bits & entry_mask) + l2_entry * 8) + | KERNEL_HIGHER_HALF_BASE as u64) + as *const u64); if l2bits != 0 { kprint!(" L2: {} - {:#x}\n", l2_entry, l2bits & entry_mask); @@ -108,12 +105,13 @@ fn print_page_table_tree(start_addr: u64) { } } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] enum ProcessState { New, Prepared, Active, Passive, + Sleeping, } pub struct Process { @@ -197,20 +195,52 @@ impl Process { } } - pub fn initialize(&mut self) { + pub fn initialize(&mut self, file_path: &str, execve: bool) { let _event = core::hint::black_box(crate::instrument!()); + // reset everything (relevant if process was forked from another process) + self.registers = RegistersStruct::default(); + self.l1_page_table = PageTable::default(); + self.l1_page_table_beginning = [PageTable::default(); 16]; + self.l2_page_directory_table = PageTable::default(); + self.l2_page_directory_table_beginning = PageTable::default(); + self.l3_page_directory_pointer_table = PageTable::default(); + self.l3_page_directory_pointer_table_beginning = PageTable::default(); + self.l4_page_map_l4_table = PageTable::default(); + self.heap_allocator = linked_list_allocator::LockedHeap::empty(); + self.file_handles = BTreeMap::new(); + self.heap_l1_table_number = 0; + self.heap_l2_table_number = 0; + self.stack_page_counter = 0; + // TODO Hack? map the kernel pages from main.asm to process // TODO Later, the kernel pages should be restricted to superuser access; in order to do so, the process code and data must be fully in userspace pages unsafe { - if KERNEL_CR3.load(Ordering::Relaxed) == 0 { + let mut kernel_cr3 = KERNEL_CR3.load(Ordering::Relaxed) as u64; + + if kernel_cr3 == 0 { let mut cr3: u64; asm!("mov r15, cr3", out("r15") cr3); KERNEL_CR3.store(cr3 as usize, Ordering::Relaxed); + kernel_cr3 = cr3; + } else { + // load user stack into kernel page table + /*let mut user_cr3: u64; + asm!("mov r15, cr3", out("r15") user_cr3); + + *((kernel_cr3 | KERNEL_HIGHER_HALF_BASE as u64 + 255 * 8) as *mut u64) = + *((user_cr3 | KERNEL_HIGHER_HALF_BASE as u64 + 255 * 8) as *const u64); + + // in case we are not in the kernel page table, we need to switch to it temporarily to read the kernel page table entries + asm!( + "mov cr3, r15", + in("r15") kernel_cr3, + options(nostack, preserves_flags) + );*/ } - kprint!("Kernel CR3: {:x}\n", KERNEL_CR3.load(Ordering::Relaxed)); + kprint!("Kernel CR3: {:x}\n", kernel_cr3); print_page_table_tree(KERNEL_CR3.load(Ordering::Relaxed) as u64); @@ -227,14 +257,14 @@ impl Process { self.l2_page_directory_table.entry[0] = allocate_page_frame() | PAGE_ENTRY_FLAGS_KERNELSPACE as usize; } else { - // allocate 512 user stack pages - for i in 0..512 { + // allocate 502 user stack pages (512 - 10 for kernel stack, see next loop) + for i in 0..(512 - 10) { self.l1_page_table.entry[511 - i] = allocate_page_frame() | PAGE_ENTRY_FLAGS_USERSPACE as usize; } // TODO HackID1: Fixed kernel stack for interrupts - limited to ten PAGE_SIZE! - for i in 1..=10 { + for i in 0..10 { self.l1_page_table.entry[i] = allocate_page_frame() | PAGE_ENTRY_FLAGS_KERNELSPACE as usize; } @@ -256,7 +286,7 @@ impl Process { &self.l3_page_directory_pointer_table as *const _ as usize, ) | PAGE_ENTRY_FLAGS_USERSPACE as usize; - let mut file_handle = FileHandle::new("/dash", 0).unwrap(); + let mut file_handle = FileHandle::new(file_path, 0).unwrap(); // put the whole file into a buffer // TODO ensure the *kernel* has enough memory for this @@ -311,6 +341,8 @@ impl Process { &self.l3_page_directory_pointer_table_beginning as *const _ as usize, ) | PAGE_ENTRY_FLAGS_USERSPACE as usize; + //print_page_table_tree(&self.l4_page_map_l4_table as *const _ as u64); + // TODO Here we load the new pagetable into cr3 for the first process. This needs to happen because otherwise we cant load the programm into the first pages. This is a hack I think self.cr3 = Process::get_physical_address_for_virtual_address( &self.l4_page_map_l4_table as *const _ as usize, @@ -326,7 +358,7 @@ impl Process { ); } - print_page_table_tree(&self.l4_page_map_l4_table as *const _ as u64); + //print_page_table_tree(&self.l4_page_map_l4_table as *const _ as u64); self.rsp = USERSPACE_STACK_TOP_ADDRESS as u64; @@ -625,16 +657,18 @@ impl Process { asm!("mov {}, cr3", out(reg) cr3); - let l4_page_map_base_address = cr3 as usize; + let l4_page_map_base_address = cr3 as usize | KERNEL_HIGHER_HALF_BASE; let l3_page_directory_pointer_table_address = *((l4_page_map_base_address + l4_page_map_table_offset * 8) as *const usize) - & ENTRY_MASK; + & ENTRY_MASK + | KERNEL_HIGHER_HALF_BASE; let l2_page_directory_table_address = *((l3_page_directory_pointer_table_address + l3_page_directory_pointer_offset * 8) as *const usize) - & ENTRY_MASK; + & ENTRY_MASK + | KERNEL_HIGHER_HALF_BASE; if PAGE_SIZE == HUGE_PAGE_SIZE { let physical_page_address = *((l2_page_directory_table_address @@ -647,7 +681,8 @@ impl Process { let l1_page_table_address = *((l2_page_directory_table_address + l2_page_directory_offset * 8) as *const usize) - & ENTRY_MASK; + & ENTRY_MASK + | KERNEL_HIGHER_HALF_BASE; let physical_page_address = *((l1_page_table_address + l1_page_table_offset * 8) as *const usize) @@ -708,7 +743,11 @@ impl Process { } } - pub fn load_elf_from_bin(&mut self, program_slice: &[u8]) -> (usize, usize, usize) { + pub fn load_elf_from_bin( + &mut self, + program_slice: &[u8], + offset: usize, + ) -> (usize, usize, usize) { let _event = core::hint::black_box(crate::instrument!()); unsafe { @@ -847,4 +886,18 @@ impl Process { let _event = core::hint::black_box(crate::instrument!()); self.process_id } + + pub fn clone_from_parent(&mut self, parent: &Process) { + let _event = core::hint::black_box(crate::instrument!()); + + self.working_directory = parent.working_directory; + self.parent_id = parent.process_id; + } + + pub fn put_to_sleep(&mut self) { + let _event = core::hint::black_box(crate::instrument!()); + + DEBUG!("Putting process to sleep"); + self.state = ProcessState::Sleeping; + } } diff --git a/kernel/src/syscall.rs b/kernel/src/syscall.rs index 3dabf66..c6e9499 100644 --- a/kernel/src/syscall.rs +++ b/kernel/src/syscall.rs @@ -193,9 +193,34 @@ fn syscall_get_time(sec: *mut u32, usec: *mut u32) -> u64 { return 1; } -fn syscall_stat(_pathname: *const u64, _statbuf: *mut u64) -> u64 { +fn syscall_stat(path: *const u64, statbuf: *mut u64) -> u64 { let _event = core::hint::black_box(crate::instrument!()); - todo!(); + + if path.is_null() { + return 0; + } + + match unsafe { core::str::from_utf8(core::slice::from_raw_parts(path as *const u8, 256)) } { + Ok(path_str) => match path_str.split('\0').next() { + Some(path_str) => match FileHandle::new(path_str, 0) { + Some(file_handle) => { + kprint!("File opened: {}\n", path_str); + + let stat = file_handle.stat(); + unsafe { + core::ptr::write_unaligned(statbuf as *mut Stat, stat); + } + return 0; + } + None => { + kprint!("Error opening file: {}\n", path_str); + return u64::MAX; + } + }, + None => return u64::MAX, + }, + Err(_) => return u64::MAX, + } } fn syscall_chdir(pathname: *const u64) -> u64 { diff --git a/storage/generateExt2Img.sh b/storage/generateExt2Img.sh index bdaea52..7a3da77 100644 --- a/storage/generateExt2Img.sh +++ b/storage/generateExt2Img.sh @@ -25,8 +25,10 @@ fi # Copy the file sudo cp ../doom1.wad /tmp/disk/devdatadoom1.wad -#sudo cp ../build/userspace/x86_64-unknown-none/debug/doom /tmp/disk/doom +sudo cp ../build/userspace/x86_64-unknown-none/debug/doom /tmp/disk/doom +chmod +x /tmp/disk/doom sudo cp ../userland/dash-0.5.13/src/dash /tmp/disk/dash +chmod +x /tmp/disk/dash # List files ls -l -a /tmp/disk diff --git a/userland/src/doom/inttypes.h b/userland/src/doom/inttypes.h new file mode 100644 index 0000000..5307f7c --- /dev/null +++ b/userland/src/doom/inttypes.h @@ -0,0 +1,4 @@ +typedef unsigned long long uintmax_t; +typedef unsigned long long uint64_t; + +uintmax_t strtoumax(const char *nptr, char **endptr, int base); \ No newline at end of file diff --git a/userland/src/doom/libc.c b/userland/src/doom/libc.c index 7c832b3..627b9c9 100644 --- a/userland/src/doom/libc.c +++ b/userland/src/doom/libc.c @@ -1,7 +1,4 @@ #include "libc.h" -#include -#include -#include uint64_t strlen(const char *str) { int len = 0; diff --git a/userland/src/doom/libc.h b/userland/src/doom/libc.h index c455127..52279cd 100644 --- a/userland/src/doom/libc.h +++ b/userland/src/doom/libc.h @@ -1,58 +1,55 @@ #ifndef __LIBC_H__ #define __LIBC_H__ -#include -#include #include "PureDOOM.h" - -#define DOOM_IMPLEMENTATION - -#define DO_SYSCALL(syscall_num, output, r8_val, r9_val, r10_val) \ - asm volatile ( \ - ".intel_syntax noprefix;" \ - "push rdi;" \ - "mov rdi, %[num];" \ - "mov r8, %[r8v];" \ - "mov r9, %[r9v];" \ - "mov r10, %[r10v];" \ - "push r11;" \ - "push rcx;" \ - "syscall;" \ - "pop rcx;" \ - "pop r11;" \ - "pop rdi;" \ - ".att_syntax;" \ - : "=a" (output) \ - : [num] "r" ((uint64_t)syscall_num), \ - [r8v] "r" ((uint64_t)r8_val), \ - [r9v] "r" ((uint64_t)r9_val), \ - [r10v] "r" ((uint64_t)r10_val) \ - : "rdi", "r8", "r9", "r10", "r11", "rcx" \ - ) - +#include "inttypes.h" +#include "stdbool.h" +#include "stdint.h" + +#define DOOM_IMPLEMENTATION + +#define DO_SYSCALL(syscall_num, output, r8_val, r9_val, r10_val) \ + asm volatile( \ + ".intel_syntax noprefix;" \ + "push rdi;" \ + "mov rdi, %[num];" \ + "mov r8, %[r8v];" \ + "mov r9, %[r9v];" \ + "mov r10, %[r10v];" \ + "push r11;" \ + "push rcx;" \ + "syscall;" \ + "pop rcx;" \ + "pop r11;" \ + "pop rdi;" \ + ".att_syntax;" \ + : "=a"(output) \ + : [num] "r"((uint64_t)syscall_num), [r8v] "r"((uint64_t)r8_val), \ + [r9v] "r"((uint64_t)r9_val), [r10v] "r"((uint64_t)r10_val) \ + : "rdi", "r8", "r9", "r10", "r11", "rcx") // String and memory functions -uint64_t strlen( const char* str ); -bool strcmp(const char* a, const char* b); +uint64_t strlen(const char *str); +bool strcmp(const char *a, const char *b); // File I/O functions -void* fopen(const char* filename, const char* options); -void fclose(void* handle); -int fwrite(void* handle, const void* foo, int bar); -int fread(void* handle, void* ptr, int size); -int fseek(void* handle, int offset, doom_seek_t origin); -int ftell(void* handle); -int feof(void* handle); +void *fopen(const char *filename, const char *options); +void fclose(void *handle); +int fwrite(void *handle, const void *foo, int bar); +int fread(void *handle, void *ptr, int size); +int fseek(void *handle, int offset, doom_seek_t origin); +int ftell(void *handle); +int feof(void *handle); // System calls and utilities uint64_t getpid(); void draw_pixel(uint32_t x, uint32_t y, uint8_t color); -void* malloc(int size); -void free(void* address); -void write(uint64_t filedescriptor, const char* payload, uint64_t len); -uint64_t draw_framebuffer(const uint8_t* framebuffer); +void *malloc(int size); +void free(void *address); +void write(uint64_t filedescriptor, const char *payload, uint64_t len); +uint64_t draw_framebuffer(const uint8_t *framebuffer); uint64_t switch_vga_mode(bool vga_on); bool get_keystate(int key); -void get_time(int* sec, int* usec); +void get_time(int *sec, int *usec); #endif // __LIBC_H__ diff --git a/userland/src/doom/main.c b/userland/src/doom/main.c index 0dc4035..4ab114b 100644 --- a/userland/src/doom/main.c +++ b/userland/src/doom/main.c @@ -1,6 +1,6 @@ +#include "inttypes.h" #include "libc.h" -#include -#include +#include "stdbool.h" #include "PureDOOM.h" diff --git a/userland/src/doom/stdbool.h b/userland/src/doom/stdbool.h new file mode 100644 index 0000000..9570167 --- /dev/null +++ b/userland/src/doom/stdbool.h @@ -0,0 +1,9 @@ +#ifndef __BOOL_H__ +#define __BOOL_H__ + +#define bool _Bool + +#define true 1 +#define false 0 + +#endif \ No newline at end of file diff --git a/userland/src/doom/stdint.h b/userland/src/doom/stdint.h new file mode 100644 index 0000000..82f8f2f --- /dev/null +++ b/userland/src/doom/stdint.h @@ -0,0 +1,5 @@ + +typedef unsigned long int uintptr_t; +typedef long long int intmax_t; +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; \ No newline at end of file From 4e3c9451c30bdb1267d7de42b891a03f8701baa7 Mon Sep 17 00:00:00 2001 From: jbreu <20864561+jbreu@users.noreply.github.com> Date: Fri, 2 Jan 2026 10:17:59 +0100 Subject: [PATCH 10/10] Adapt test cases --- Makefile | 2 +- kernel/src/process.rs | 8 +--- kernel/src/syscall.rs | 56 ++++++++++++++++++++++++++++ kernel/src/userland.rs | 74 ++++++++++++++++++++++++++++--------- storage/generateExt2Img.sh | 2 +- test/conftest.py | 32 ++++++++++------ test/test_kernel_startup.py | 9 +++-- userland/build_dash.sh | 1 + userland/usr/libc.c | 21 +++++------ 9 files changed, 152 insertions(+), 53 deletions(-) diff --git a/Makefile b/Makefile index 2aa291c..25d31d2 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ userland: gcc userland/src/doom/main.c userland/src/doom/libc.c -static -nostdlib -fno-builtin -g -o build/userspace/x86_64-unknown-none/debug/doom -Wl,--gc-sections --sysroot=/root/env/userland/src/doom && \ test -f dash-0.5.13.tar.gz || wget https://git.kernel.org/pub/scm/utils/dash/dash.git/snapshot/dash-0.5.13.tar.gz -N && \ tar -xzf dash-0.5.13.tar.gz -C userland/ && \ - cd userland && ./build_dash.sh -c && cd .. && \ + cd userland && chmod a+x build_dash.sh && ./build_dash.sh -c && cd .. && \ cd storage && sh generateExt2Img.sh && cd .. .PHONY: kernel diff --git a/kernel/src/process.rs b/kernel/src/process.rs index 536f284..203b40a 100644 --- a/kernel/src/process.rs +++ b/kernel/src/process.rs @@ -195,7 +195,7 @@ impl Process { } } - pub fn initialize(&mut self, file_path: &str, execve: bool) { + pub fn initialize(&mut self, file_path: &str) { let _event = core::hint::black_box(crate::instrument!()); // reset everything (relevant if process was forked from another process) @@ -743,11 +743,7 @@ impl Process { } } - pub fn load_elf_from_bin( - &mut self, - program_slice: &[u8], - offset: usize, - ) -> (usize, usize, usize) { + pub fn load_elf_from_bin(&mut self, program_slice: &[u8]) -> (usize, usize, usize) { let _event = core::hint::black_box(crate::instrument!()); unsafe { diff --git a/kernel/src/syscall.rs b/kernel/src/syscall.rs index c6e9499..6efbb18 100644 --- a/kernel/src/syscall.rs +++ b/kernel/src/syscall.rs @@ -1,10 +1,15 @@ use crate::ERROR; +use crate::filesystem::FileHandle; +use crate::filesystem::Stat; use crate::interrupt; use crate::kprint; use crate::{USERLAND, time}; use crate::{keyboard, vga}; use core::arch::asm; +extern crate alloc; +use alloc::vec::Vec; + #[unsafe(no_mangle)] pub extern "C" fn system_call() -> u64 { let mut syscall_nr: i64; @@ -48,6 +53,14 @@ pub extern "C" fn system_call() -> u64 { 18 => return syscall_kill(arg0 as u64, arg1 as u32), 19 => return syscall_read(arg0, arg1, arg2), 20 => return syscall_realloc(arg0, arg1 as usize), + 21 => return syscall_vfork(), + 22 => { + return syscall_execve( + arg0 as *const u64, + arg1 as *const *const u64, + arg2 as *const *const u64, + ); + } _ => { ERROR!("Undefined system call triggered: {}", syscall_nr); return 0xdeadbeef; @@ -293,3 +306,46 @@ fn syscall_read(filedescriptor: u64, buffer: u64, len: u64) -> u64 { todo!(); } } + +fn syscall_vfork() -> u64 { + let _event = core::hint::black_box(crate::instrument!()); + return USERLAND.lock().vfork_current_process(); +} + +fn syscall_execve(filename: *const u64, argv: *const *const u64, envp: *const *const u64) -> u64 { + let _event = core::hint::black_box(crate::instrument!()); + + if filename.is_null() { + return u64::MAX; + } + + match unsafe { core::str::from_utf8(core::slice::from_raw_parts(filename as *const u8, 256)) } { + Ok(path_str) => match path_str.split('\0').next() { + Some(path_str) => { + // reconstruct argv + let mut args: Vec<&str> = Vec::new(); + let mut i = 0; + loop { + let arg_ptr = unsafe { *argv.add(i) }; + if arg_ptr.is_null() { + break; + } + match unsafe { + core::str::from_utf8(core::slice::from_raw_parts(arg_ptr as *const u8, 256)) + } { + Ok(arg_str) => match arg_str.split('\0').next() { + Some(arg_str) => args.push(arg_str), + None => break, + }, + Err(_) => break, + } + i += 1; + } + + return USERLAND.lock().execve(path_str); + } + None => return u64::MAX, + }, + Err(_) => return u64::MAX, + } +} diff --git a/kernel/src/userland.rs b/kernel/src/userland.rs index e3d96c6..1b8d8d8 100644 --- a/kernel/src/userland.rs +++ b/kernel/src/userland.rs @@ -1,8 +1,8 @@ use spin::Mutex; -use crate::mem_config::{KERNEL_STACK_TOP_ADDRESS, USERSPACE_STACK_TOP_ADDRESS}; +use crate::mem_config::USERSPACE_STACK_TOP_ADDRESS; use crate::process::Process; -use crate::{ERROR, USERLAND, kprint}; +use crate::{ERROR, USERLAND}; extern crate alloc; use alloc::vec::Vec; @@ -39,13 +39,13 @@ impl Userland { pub fn process_malloc(&mut self, size: usize) -> u64 { let _event = core::hint::black_box(crate::instrument!()); - return self.processes[self.current_process].malloc(size); + return self.get_current_process().malloc(size); } pub fn process_realloc(&mut self, ptr: u64, size: usize) -> u64 { let _event = core::hint::black_box(crate::instrument!()); - return self.processes[self.current_process].realloc(ptr, size); + return self.get_current_process().realloc(ptr, size); } pub fn switch_to_userland(&mut self, mutex: &Mutex) { @@ -62,10 +62,10 @@ impl Userland { //self.processes.push(Process::new()); for process in &mut self.processes { - process.initialize(); + process.initialize("/dash"); } - self.current_process = 0; + self.current_process = self.processes[0].get_pid() as usize; self.processes[0].launch(); //self.processes[1].launch(); @@ -91,24 +91,33 @@ impl Userland { let _event = core::hint::black_box(crate::instrument!()); // TODO for now scheduler is simply going round robin - let last_process = self.current_process; + + // find vector index of current process by iterating through all processes + let mut current_process_index = self + .processes + .iter() + .position(|p| p.get_pid() == self.current_process as u64) + .unwrap(); + + let last_process = current_process_index; loop { - self.current_process += 1; - if self.current_process == self.processes.len() { - self.current_process = 0; + current_process_index += 1; + if current_process_index == self.processes.len() { + current_process_index = 0; } - if self.processes[self.current_process].activatable() { + if self.processes[current_process_index].activatable() { break; } // not a single userspace process ready for execution - if self.current_process == last_process { + if current_process_index == last_process { return; } } self.processes[last_process].passivate(); + self.current_process = self.processes[current_process_index].get_pid() as usize; self.processes[self.current_process].activate(false); } @@ -121,16 +130,16 @@ impl Userland { pub fn get_current_process(&mut self) -> &mut Process { let _event = core::hint::black_box(crate::instrument!()); - &mut self.processes[self.current_process] + self.processes + .iter_mut() + .find(|p| p.get_pid() == self.current_process as u64) + .unwrap() } - pub fn get_current_process_parent_id(&self) -> usize { + pub fn get_current_process_parent_id(&mut self) -> usize { let _event = core::hint::black_box(crate::instrument!()); - self.processes[self.current_process] - .get_parent_id() - .try_into() - .unwrap() + self.get_current_process().get_parent_id() as usize } pub fn kill_process(&mut self, pid: u64, sig: u32) -> i64 { @@ -148,6 +157,35 @@ impl Userland { return -1; } + + pub fn vfork_current_process(&mut self) -> u64 { + let _event = core::hint::black_box(crate::instrument!()); + + let parent_process = self.get_current_process(); + parent_process.put_to_sleep(); + + let mut child_process = Process::new(); + child_process.clone_from_parent(parent_process); + + let child_pid = child_process.get_pid(); + self.processes.push(child_process); + + self.current_process = child_pid as usize; + + // return 0 as we are executing in the child process + return 0; + } + + pub fn execve(&mut self, filename: &str) -> u64 { + let _event = core::hint::black_box(crate::instrument!()); + + // cycle through all processes to find the current one + let current_process = self.get_current_process(); + + let filename_str = filename; + current_process.initialize(filename_str); + 0 + } } // very simple scheduler diff --git a/storage/generateExt2Img.sh b/storage/generateExt2Img.sh index 7a3da77..99a4e96 100644 --- a/storage/generateExt2Img.sh +++ b/storage/generateExt2Img.sh @@ -24,7 +24,7 @@ fi # sudo chown "$USER":"$USER" /tmp/disk # Copy the file -sudo cp ../doom1.wad /tmp/disk/devdatadoom1.wad +sudo cp ../userland/src/doom/doom1.wad /tmp/disk/devdatadoom1.wad sudo cp ../build/userspace/x86_64-unknown-none/debug/doom /tmp/disk/doom chmod +x /tmp/disk/doom sudo cp ../userland/dash-0.5.13/src/dash /tmp/disk/dash diff --git a/test/conftest.py b/test/conftest.py index c43fb1c..7793ac9 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -156,20 +156,28 @@ def read_until(self, marker: bytes, timeout: float = 30.0) -> bytes: def send_key_press(self, key: str) -> None: """Send a key press to QEMU via QMP.""" - # QEMU expects key names as per QMP documentation, e.g. 'l' -> 'l' - # For more complex keys, mapping may be needed - key_event = { - "execute": "send-key", - "arguments": {"keys": [{"type": "qcode", "data": key}]}, - } import json as _json - msg = _json.dumps(key_event).encode("utf-8") + b"\r\n" - if self.qmp_socket: - self.qmp_socket.sendall(msg) - self.qmp_socket.recv(4096) # Read response - else: - raise ConnectionError("QMP socket is not connected") + # Map special characters to their QMP key names + key_map = { + " ": "space", + "\n": "ret", + "\t": "tab", + } + + # Handle multi-character input by sending each character separately + for char in key: + key_name = key_map.get(char, char) + key_event = { + "execute": "send-key", + "arguments": {"keys": [{"type": "qcode", "data": key_name}]}, + } + msg = _json.dumps(key_event).encode("utf-8") + b"\r\n" + if self.qmp_socket: + self.qmp_socket.sendall(msg) + self.qmp_socket.recv(4096) # Read response + else: + raise ConnectionError("QMP socket is not connected") @pytest.fixture diff --git a/test/test_kernel_startup.py b/test/test_kernel_startup.py index 90220fc..9080b88 100644 --- a/test/test_kernel_startup.py +++ b/test/test_kernel_startup.py @@ -27,6 +27,7 @@ def test_kernel_boot(qemu: QEMUConnection): assert b"JOS Kernel initialized; switching to userland" in output +@pytest.mark.skip(reason="Carina currently disabled in userland") def test_userland(qemu: QEMUConnection): """Test that userland starts and outputs expected messages""" @@ -66,14 +67,14 @@ def test_userland_dash(qemu: QEMUConnection): output = qemu.read_until(b"$") assert b"$" in output - qemu.send_key_press("expr 3 + 4\n") - output = qemu.read_until(b"7") - assert b"7" in output + qemu.send_key_press("echo 44\n") + output = qemu.read_until(b"%lld") + assert b"%lld" in output def test_retrieve_profiling(qemu: QEMUConnection): # Wait before sending key press to ensure system is ready - qemu.read_until(b"Backing up text mode palette:") + qemu.read_until(b"$") time.sleep(0.5) # send button press to qemu diff --git a/userland/build_dash.sh b/userland/build_dash.sh index d14a659..6a0b3ef 100644 --- a/userland/build_dash.sh +++ b/userland/build_dash.sh @@ -7,6 +7,7 @@ cd usr gcc -c -g -ffreestanding -fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables -static -nostdlib -fno-pic -fno-builtin --sysroot=/root/env/userland/ -I/root/env/userland/usr/include/ libc.c ar rcs libc.a *.o +mkdir -p lib mv libc.a lib/libc.a cd ../dash-0.5.13/ diff --git a/userland/usr/libc.c b/userland/usr/libc.c index 45d95f0..38d4262 100644 --- a/userland/usr/libc.c +++ b/userland/usr/libc.c @@ -1109,9 +1109,10 @@ int isatty(int fd) { int *__errno_location(void) { return &errno_value; } void _exit(int status) { - // TODO implement - char *msg = "TODO implement _exit\n"; - write(1, msg, strlen(msg)); + uint64_t result; + DO_SYSCALL(60, result, status, 0, 0); + while (1) + ; // Unreachable, but satisfies noreturn requirement } size_t mbrlen(const char *s, size_t n, mbstate_t *ps) { @@ -1360,10 +1361,9 @@ int wait3(int *status, int options, struct rusage *rusage) { int raise(int sig) { return kill(getpid(), sig); } pid_t vfork(void) { - // TODO implement vfork - char *msg = "TODO implement vfork\n"; - write(1, msg, strlen(msg)); - return -1; + uint64_t result; + DO_SYSCALL(21, result, 0, 0, 0); + return (pid_t)result; } pid_t fork(void) { @@ -1450,10 +1450,9 @@ void abort(void) { } int execve(const char *filename, char *const argv[], char *const envp[]) { - // TODO implement execve - char *msg = "TODO implement execve\n"; - write(1, msg, strlen(msg)); - return -1; + uint64_t result; + DO_SYSCALL(22, result, filename, argv, envp); + return (int)result; } struct passwd *getpwnam(const char *name) {