11 Commits

Author SHA1 Message Date
boreddevnl
a1b6d58b77 Tweak: os_codename = Voyager 2026-04-12 17:59:10 +02:00
boreddevnl
cbc196a4b1 tweak: add -stable after kernel_version 2026-04-12 17:57:54 +02:00
boreddevnl
b4c14af48d TWEAK: kernel_version = 3.2.3 --> 4.0.0 2026-04-12 17:56:35 +02:00
boreddevnl
700839e6be FEAT: VFS overhaul 2026-04-12 17:53:31 +02:00
boreddevnl
921e8a5658 RM: Legacy drive selector in explorer 2026-04-12 00:34:22 +02:00
boreddevnl
437d57312f FIX: remove accidentally copied code 2026-04-12 00:28:03 +02:00
boreddevnl
afc4e16fcf STABILITY: SMP improvements 2026-04-12 00:26:04 +02:00
boreddevnl
38ed0b5ffa CHECKP: semi-stable vfs 2026-04-11 23:08:33 +02:00
boreddevnl
5933483009 CHECKP: shitty VFS 2026-04-11 21:41:11 +02:00
boreddevnl
6b6a22d518 OPT: use ui_draw_image in paint.c 2026-04-11 16:14:39 +02:00
boreddevnl
85427041de FEAT: Caps lock support in ps2 driver 2026-04-11 16:12:37 +02:00
60 changed files with 5224 additions and 1370 deletions

3
.gitignore vendored
View File

@@ -29,4 +29,5 @@ limine
**/.DS_Store
.DS_Store
/build/
*.o
*.o
disk.img

286
build.log Normal file
View File

@@ -0,0 +1,286 @@
mkdir -p build
mkdir -p build
nasm -f elf64 src/arch/boot.asm -o build/boot.o
nasm -f elf64 src/arch/gdt_asm.asm -o build/gdt_asm.o
nasm -f elf64 src/arch/interrupts.asm -o build/interrupts.o
nasm -f elf64 src/arch/process_asm.asm -o build/process_asm.o
nasm -f elf64 src/arch/syscalls.asm -o build/syscalls.o
nasm -f elf64 src/arch/test_syscall.asm -o build/test_syscall.o
nasm -f elf64 src/arch/user_test.asm -o build/user_test.o
Building Limine host utility...
make[1]: Nothing to be done for `all'.
mkdir -p build/
mkdir -p build/
mkdir -p build/
mkdir -p build/
mkdir -p build/
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/core/kutils.c -o build/kutils.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/core/main.c -o build/main.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/core/version.c -o build/version.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/elf.c -o build/elf.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/core/platform.c -o build/platform.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/core/panic.c -o build/panic.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/idt.c -o build/idt.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/gdt.c -o build/gdt.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/kernel_subsystem.c -o build/kernel_subsystem.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/lapic.c -o build/lapic.o
src/sys/idt.c: In function 'pic_remap':
src/sys/idt.c:120:17: warning: variable 'a2' set but not used [-Wunused-but-set-variable]
120 | uint8_t a1, a2;
| ^~
src/sys/idt.c:120:13: warning: variable 'a1' set but not used [-Wunused-but-set-variable]
120 | uint8_t a1, a2;
| ^~
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/module_manager.c -o build/module_manager.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/process.c -o build/process.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/smp.c -o build/smp.o
mkdir -p build/
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/syscall.c -o build/syscall.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/sysfs_init.c -o build/sysfs_init.o
src/sys/smp.c: In function 'smp_init':
src/sys/smp.c:171:14: warning: variable 'bsp_index' set but not used [-Wunused-but-set-variable]
171 | uint32_t bsp_index = 0;
| ^~~~~~~~~
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/sys/work_queue.c -o build/work_queue.o
mkdir -p build/
src/sys/sysfs_init.c:11:13: warning: 'sys_itoa' defined but not used [-Wunused-function]
11 | static void sys_itoa(int n, char *s) {
| ^~~~~~~~
mkdir -p build/
src/sys/syscall.c: In function 'syscall_handler_inner':
src/sys/syscall.c:493:28: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
493 | float scale = *(float*)&scale_bits;
| ^~~~~~~~~~~~~~~~~~~
src/sys/syscall.c:561:28: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
561 | float scale = *(float*)&scale_bits;
| ^~~~~~~~~~~~~~~~~~~
src/sys/syscall.c:569:28: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
569 | float slope = *(float*)&slope_bits;
| ^~~~~~~~~~~~~~~~~~~
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/mem/memory_manager.c -o build/memory_manager.o
src/sys/syscall.c:695:21: warning: unused variable 'win' [-Wunused-variable]
695 | Window *win = (Window *)arg2;
| ^~~
src/sys/syscall.c:725:28: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
725 | float scale = *(float*)&scale_bits;
| ^~~~~~~~~~~~~~~~~~~
src/sys/syscall.c:762:28: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
762 | float scale = *(float*)&scale_bits;
| ^~~~~~~~~~~~~~~~~~~
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/mem/paging.c -o build/paging.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/mem/vm.c -o build/vm.o
mkdir -p build/
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/dev/disk_manager.c -o build/disk_manager.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/dev/ahci.c -o build/ahci.o
mkdir -p build/
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/dev/pci.c -o build/pci.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/dev/ps2.c -o build/ps2.o
src/dev/pci.c: In function 'pci_enumerate_devices':
src/dev/pci.c:52:31: warning: comparison is always true due to limited range of data type [-Wtype-limits]
52 | for (uint8_t bus = 0; bus < 256 && count < max_devices; bus++) {
| ^
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/dev/rtc.c -o build/rtc.o
src/dev/rtc.c: In function 'rtc_get_datetime':
src/dev/rtc.c:28:13: warning: unused variable 'last_century' [-Wunused-variable]
28 | uint8_t last_century;
| ^~~~~~~~~~~~
src/dev/rtc.c:21:13: warning: unused variable 'century' [-Wunused-variable]
21 | uint8_t century;
| ^~~~~~~
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/net/lwip_port.c -o build/lwip_port.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/net/network.c -o build/network.o
mkdir -p build/
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/net/nic/e1000.c -o build/e1000.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/net/nic/nic.c -o build/nic.o
src/net/network.c: In function 'network_dhcp_acquire':
src/net/network.c:186:9: warning: unused variable 'loops' [-Wunused-variable]
186 | int loops = 0;
| ^~~~~
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/net/nic/nic_netif.c -o build/nic_netif.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/net/nic/rtl8111.c -o build/rtl8111.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/net/nic/rtl8139.c -o build/rtl8139.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/net/nic/virtio_net.c -o build/virtio_net.o
mkdir -p build/
src/net/network.c: In function 'network_init':
src/net/network.c:93:9: warning: 'ip.bytes[0]' may be used uninitialized [-Wmaybe-uninitialized]
93 | k_itoa(ip.bytes[0], buf); serial_write(buf); serial_write(".");
| ^~~~~~~~~~~~~~~~~~~~~~~~
src/net/network.c:88:24: note: 'ip.bytes[0]' was declared here
88 | ipv4_address_t ip;
| ^~
src/net/network.c:94:9: warning: 'ip.bytes[1]' may be used uninitialized [-Wmaybe-uninitialized]
94 | k_itoa(ip.bytes[1], buf); serial_write(buf); serial_write(".");
| ^~~~~~~~~~~~~~~~~~~~~~~~
src/net/network.c:88:24: note: 'ip.bytes[1]' was declared here
88 | ipv4_address_t ip;
| ^~
src/net/network.c:95:9: warning: 'ip.bytes[2]' may be used uninitialized [-Wmaybe-uninitialized]
95 | k_itoa(ip.bytes[2], buf); serial_write(buf); serial_write(".");
| ^~~~~~~~~~~~~~~~~~~~~~~~
src/net/network.c:88:24: note: 'ip.bytes[2]' was declared here
88 | ipv4_address_t ip;
| ^~
src/net/network.c:96:9: warning: 'ip.bytes[3]' may be used uninitialized [-Wmaybe-uninitialized]
96 | k_itoa(ip.bytes[3], buf); serial_write(buf); serial_write("\n");
| ^~~~~~~~~~~~~~~~~~~~~~~~
src/net/network.c:88:24: note: 'ip.bytes[3]' was declared here
88 | ipv4_address_t ip;
| ^~
src/net/nic/rtl8111.c: In function 'rtl8111_init':
src/net/nic/rtl8111.c:67:14: warning: unused variable 'bar2' [-Wunused-variable]
67 | uint32_t bar2 = pci_read_config(pci_dev->bus, pci_dev->device, pci_dev->function, 0x18);
| ^~~~
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/fs/fat32.c -o build/fat32.o
mkdir -p build/
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/fs/procfs.c -o build/procfs.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/fs/sysfs.c -o build/sysfs.o
src/fs/fat32.c: In function 'realfs_delete_from_vol':
src/fs/fat32.c:1110:14: warning: variable 'entry_offset' set but not used [-Wunused-but-set-variable]
1110 | uint32_t entry_offset = 0;
| ^~~~~~~~~~~~
src/fs/fat32.c:1109:14: warning: variable 'entry_sector' set but not used [-Wunused-but-set-variable]
1109 | uint32_t entry_sector = 0;
| ^~~~~~~~~~~~
src/fs/sysfs.c: In function 'sysfs_open':
src/fs/sysfs.c:12:31: warning: unused parameter 'fs_private' [-Wunused-parameter]
12 | static void* sysfs_open(void *fs_private, const char *path, const char *mode) {
| ~~~~~~^~~~~~~~~~
src/fs/sysfs.c:12:73: warning: unused parameter 'mode' [-Wunused-parameter]
12 | static void* sysfs_open(void *fs_private, const char *path, const char *mode) {
| ~~~~~~~~~~~~^~~~
src/fs/sysfs.c: In function 'sysfs_close':
src/fs/sysfs.c:43:31: warning: unused parameter 'fs_private' [-Wunused-parameter]
43 | static void sysfs_close(void *fs_private, void *handle) {
| ~~~~~~^~~~~~~~~~
src/fs/sysfs.c: In function 'sysfs_read':
src/fs/sysfs.c:47:29: warning: unused parameter 'fs_private' [-Wunused-parameter]
47 | static int sysfs_read(void *fs_private, void *handle, void *buf, int size) {
| ~~~~~~^~~~~~~~~~
src/fs/sysfs.c: In function 'sysfs_write':
src/fs/sysfs.c:56:30: warning: unused parameter 'fs_private' [-Wunused-parameter]
56 | static int sysfs_write(void *fs_private, void *handle, const void *buf, int size) {
| ~~~~~~^~~~~~~~~~
src/fs/sysfs.c: In function 'sysfs_readdir':
src/fs/sysfs.c:86:49: warning: comparison of integer expressions of different signedness: 'size_t' {aka 'long unsigned int'} and 'int' [-Wsign-compare]
86 | if (path_len == 0 || (k_strlen(s->name) > path_len && k_strncmp(s->name, path, path_len) == 0 && s->name[path_len] == '/')) {
| ^
src/fs/sysfs.c:65:32: warning: unused parameter 'fs_private' [-Wunused-parameter]
65 | static int sysfs_readdir(void *fs_private, const char *path, vfs_dirent_t *entries, int max) {
| ~~~~~~^~~~~~~~~~
src/fs/sysfs.c: In function 'sysfs_exists':
src/fs/sysfs.c:142:31: warning: comparison of integer expressions of different signedness: 'size_t' {aka 'long unsigned int'} and 'int' [-Wsign-compare]
142 | if (k_strlen(s->name) > path_len && k_strncmp(s->name, path, path_len) == 0 && s->name[path_len] == '/') return true;
| ^
src/fs/sysfs.c:116:32: warning: unused parameter 'fs_private' [-Wunused-parameter]
116 | static bool sysfs_exists(void *fs_private, const char *path) {
| ~~~~~~^~~~~~~~~~
src/fs/procfs.c: In function 'procfs_open':
src/fs/procfs.c:15:25: warning: unused parameter 'fs_private' [-Wunused-parameter]
15 | void* procfs_open(void *fs_private, const char *path, const char *mode) {
| ~~~~~~^~~~~~~~~~
src/fs/procfs.c:15:67: warning: unused parameter 'mode' [-Wunused-parameter]
15 | void* procfs_open(void *fs_private, const char *path, const char *mode) {
| ~~~~~~~~~~~~^~~~
src/fs/procfs.c: In function 'procfs_close':
src/fs/procfs.c:50:25: warning: unused parameter 'fs_private' [-Wunused-parameter]
50 | void procfs_close(void *fs_private, void *handle) {
| ~~~~~~^~~~~~~~~~
src/fs/procfs.c: In function 'procfs_read':
src/fs/procfs.c:54:23: warning: unused parameter 'fs_private' [-Wunused-parameter]
54 | int procfs_read(void *fs_private, void *handle, void *buf, int size) {
| ~~~~~~^~~~~~~~~~
src/fs/procfs.c: In function 'procfs_write':
src/fs/procfs.c:178:24: warning: unused parameter 'fs_private' [-Wunused-parameter]
178 | int procfs_write(void *fs_private, void *handle, const void *buf, int size) {
| ~~~~~~^~~~~~~~~~
src/fs/procfs.c: In function 'procfs_readdir':
src/fs/procfs.c:200:26: warning: unused parameter 'fs_private' [-Wunused-parameter]
200 | int procfs_readdir(void *fs_private, const char *path, vfs_dirent_t *entries, int max) {
| ~~~~~~^~~~~~~~~~
src/fs/procfs.c: In function 'procfs_exists':
src/fs/procfs.c:241:26: warning: unused parameter 'fs_private' [-Wunused-parameter]
241 | bool procfs_exists(void *fs_private, const char *path) {
| ~~~~~~^~~~~~~~~~
src/fs/procfs.c: In function 'procfs_is_dir':
src/fs/procfs.c:264:26: warning: unused parameter 'fs_private' [-Wunused-parameter]
264 | bool procfs_is_dir(void *fs_private, const char *path) {
| ~~~~~~^~~~~~~~~~
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/fs/tar.c -o build/tar.o
mkdir -p build/
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/fs/vfs.c -o build/vfs.o
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/wm/cmd.c -o build/cmd.o
mkdir -p build/
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/wm/explorer.c -o build/explorer.o
src/wm/cmd.c: In function 'internal_cmd_cd':
src/wm/cmd.c:1014:13: error: too few arguments to function 'vfs_normalize_path'; expected 3, have 2
1014 | vfs_normalize_path(full_path, normalized_path);
| ^~~~~~~~~~~~~~~~~~
In file included from src/wm/cmd.c:11:
src/fs/vfs.h:111:6: note: declared here
111 | void vfs_normalize_path(const char *cwd, const char *path, char *normalized);
| ^~~~~~~~~~~~~~~~~~
mkdir -p build/
make: *** [build/cmd.o] Error 1
make: *** Waiting for unfinished jobs....
x86_64-elf-gcc -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fPIE -m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone -Isrc -Isrc/net/lwip -Isrc/core -Isrc/sys -Isrc/mem -Isrc/dev -Isrc/net -Isrc/net/nic -Isrc/fs -Isrc/wm -c src/wm/font_manager.c -o build/font_manager.o
src/wm/explorer.c: In function 'explorer_draw_file_icon':
src/wm/explorer.c:885:73: warning: unused parameter 'color' [-Wunused-parameter]
885 | static void explorer_draw_file_icon(int x, int y, bool is_dir, uint32_t color, const char *filename, const char *current_path) {
| ~~~~~~~~~^~~~~
src/wm/explorer.c: In function 'explorer_paint':
src/wm/explorer.c:922:15: warning: unused variable 'dirty' [-Wunused-variable]
922 | DirtyRect dirty = graphics_get_dirty_rect();
| ^~~~~
In file included from src/wm/font_manager.c:4:
src/wm/stb_truetype.h: In function 'stbtt_FreeShape':
src/wm/stb_truetype.h:2672:54: warning: unused parameter 'info' [-Wunused-parameter]
2672 | STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
| ~~~~~~~~~~~~~~~~~~~~~~^~~~
src/wm/stb_truetype.h: In function 'stbtt__hheap_alloc':
src/wm/stb_truetype.h:2770:70: warning: unused parameter 'userdata' [-Wunused-parameter]
2770 | static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata)
| ~~~~~~^~~~~~~~
src/wm/stb_truetype.h: In function 'stbtt__hheap_cleanup':
src/wm/stb_truetype.h:2797:58: warning: unused parameter 'userdata' [-Wunused-parameter]
2797 | static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata)
| ~~~~~~^~~~~~~~
src/wm/stb_truetype.h: In function 'stbtt_FlattenCurves':
src/wm/stb_truetype.h:3618:154: warning: unused parameter 'userdata' [-Wunused-parameter]
3618 | static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)
| ~~~~~~^~~~~~~~
src/wm/stb_truetype.h: In function 'stbtt_FreeBitmap':
src/wm/stb_truetype.h:3708:62: warning: unused parameter 'userdata' [-Wunused-parameter]
3708 | STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)
| ~~~~~~^~~~~~~~
src/wm/stb_truetype.h: In function 'stbtt_FreeSDF':
src/wm/stb_truetype.h:4767:59: warning: unused parameter 'userdata' [-Wunused-parameter]
4767 | STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata)
| ~~~~~~^~~~~~~~
src/wm/font_manager.c: In function 'font_manager_load':
src/wm/font_manager.c:112:9: warning: unused variable 'read' [-Wunused-variable]
112 | int read = fat32_read(fh, buffer, fsize);
| ^~~~

BIN
disk.img

Binary file not shown.

View File

@@ -45,7 +45,11 @@ isr%2_wrapper:
push r14
push r15
; Save SSE/FPU state (fxsave requires 16-byte alignment)
test qword [rsp + 144], 3
jz %%skip_swap
swapgs
%%skip_swap:
sub rsp, 512
fxsave [rsp]
@@ -76,6 +80,12 @@ isr%2_wrapper:
pop rcx
pop rbx
pop rax
test qword [rsp + 24], 3
jz %%skip_swap_back
swapgs
%%skip_swap_back:
add rsp, 16 ; drop dummy vector and error code
iretq
%endmacro
@@ -163,8 +173,12 @@ exception_common:
push r13
push r14
push r15
test qword [rsp + 144], 3
jz .skip_swap_exc
swapgs
.skip_swap_exc:
; Save SSE/FPU state (fxsave requires 16-byte alignment)
sub rsp, 512
fxsave [rsp]
@@ -196,6 +210,12 @@ exception_common:
pop rcx
pop rbx
pop rax
test qword [rsp + 24], 3
jz .skip_swap_back_exc
swapgs
.skip_swap_back_exc:
add rsp, 16 ; drop vector and error code
iretq

View File

@@ -15,15 +15,14 @@ section .text
; R9 = arg5
syscall_entry:
; 1. Switch to Kernel Stack safely
; Note: For true SMP safety, we need per-CPU storage (via swapgs).
; For now, we use a global scratch which is only safe because we mask interrupts on entry.
mov [rel user_rsp_scratch], rsp
mov rsp, [rel kernel_syscall_stack]
swapgs
mov [gs:40], rsp
mov rsp, [gs:48]
; 2. Build iretq frame (compatible with registers_t)
; 2. Build iretq frame
push 0x1B ; SS (User Data)
push qword [rel user_rsp_scratch] ; RSP
push qword [gs:40] ; RSP
push r11 ; RFLAGS (captured by syscall)
push 0x23 ; CS (User Code)
push rcx ; RIP (return address from syscall)
@@ -81,14 +80,7 @@ syscall_entry:
pop rax
add rsp, 16 ; drop int_no/err_code
; Debug: check RIP before iretq
; We can't easily print from here without destroying registers,
; but we can at least check if it's canonical.
swapgs
iretq
section .bss
global kernel_syscall_stack
global user_rsp_scratch
kernel_syscall_stack: resq 1
user_rsp_scratch: resq 1

View File

@@ -30,6 +30,16 @@ int k_strcmp(const char *s1, const char *s2) {
return *(const unsigned char*)s1 - *(const unsigned char*)s2;
}
int k_strncmp(const char *s1, const char *s2, size_t n) {
while (n && *s1 && (*s1 == *s2)) {
s1++;
s2++;
n--;
}
if (n == 0) return 0;
return *(const unsigned char*)s1 - *(const unsigned char*)s2;
}
void k_strcpy(char *dest, const char *src) {
while (*src) *dest++ = *src++;
*dest = 0;

View File

@@ -13,6 +13,7 @@ void k_memset(void *dest, int val, size_t len);
void k_memcpy(void *dest, const void *src, size_t len);
size_t k_strlen(const char *str);
int k_strcmp(const char *s1, const char *s2);
int k_strncmp(const char *s1, const char *s2, size_t n);
void k_strcpy(char *dest, const char *src);
int k_atoi(const char *str);
void k_itoa(int n, char *buf);

View File

@@ -17,12 +17,19 @@
#include "io.h"
#include "fat32.h"
#include "tar.h"
#include "vfs.h"
#include "memory_manager.h"
#include "platform.h"
#include "wallpaper.h"
#include "smp.h"
#include "work_queue.h"
#include "lapic.h"
#include "fs/sysfs.h"
#include "fs/procfs.h"
#include "sys/kernel_subsystem.h"
#include "sys/module_manager.h"
extern void sysfs_init_subsystems(void);
// --- Limine Requests ---
__attribute__((used, section(".requests")))
@@ -133,6 +140,7 @@ static void fat32_mkdir_recursive(const char *path) {
void kmain(void) {
init_serial();
vfs_init();
serial_write("\n[DEBUG] Entering kmain...\n");
platform_init();
@@ -156,6 +164,8 @@ void kmain(void) {
// The memory manager will now scan the memory map and manage all usable regions.
memory_manager_init_from_memmap(memmap_request.response);
serial_write("[DEBUG] memory_manager_init OK\n");
smp_init_bsp();
serial_write("[DEBUG] smp_init_bsp OK\n");
} else {
serial_write("[DEBUG] ERROR: No usable memory for heap! Check Limine memmap.\n");
hcf();
@@ -198,6 +208,11 @@ void kmain(void) {
fat32_mkdir("/Library/DOOM");
fat32_mkdir("/docs");
// Initialize Virtual Filesystems
sysfs_init_subsystems();
vfs_mount("/sys", "sysfs", "sysfs", sysfs_get_ops(), NULL);
vfs_mount("/proc", "procfs", "procfs", procfs_get_ops(), NULL);
if (module_request.response == NULL) {
serial_write("[DEBUG] ERROR: Limine Module Response is NULL!\n");
} else {
@@ -237,6 +252,8 @@ void kmain(void) {
fat32_close(fh);
}
}
// Register all discovered modules in our module manager for /sys/module
module_manager_register(clean_path, (uint64_t)mod->address, mod->size);
}
}
@@ -256,7 +273,7 @@ void kmain(void) {
// Initialize LAPIC for IPI support
lapic_init();
// Initialize SMP — bring up all CPU cores
// Initialize SMP
if (smp_request.response != NULL) {
uint32_t online = smp_init(smp_request.response);
serial_write("[DEBUG] SMP init complete, CPUs online: ");
@@ -264,7 +281,6 @@ void kmain(void) {
serial_write("\n");
} else {
serial_write("[DEBUG] No SMP response from bootloader\n");
// Still init as single-CPU
smp_init(NULL);
}

View File

@@ -14,7 +14,7 @@ static size_t man_strlen(const char *str) {
}
static void write_man_file(const char *name, const char *content) {
char path[128] = "A:/Library/man/";
char path[128] = "/Library/man/";
int i = 15;
while (*name) path[i++] = *name++;
path[i++] = '.';
@@ -31,8 +31,8 @@ static void write_man_file(const char *name, const char *content) {
}
void create_man_entries(void) {
fat32_mkdir("A:/Library");
fat32_mkdir("A:/Library/man");
fat32_mkdir("/Library");
fat32_mkdir("/Library/man");
write_man_file("ping", "PING - Send ICMP echo requests\n\nUsage: ping <ip>\n\nSends ICMP echo requests to the specified IP address and displays the response times.");
write_man_file("net", "NET - Network utilities\n\nUsage: net init\nnet info\nnet ipset >ip<\nnet udpsend >ip< >port< >message< net ping >ip< net help\n\nA collection of network-related commands.");
@@ -55,7 +55,7 @@ void create_man_entries(void) {
write_man_file("touch", "TOUCH - Create empty file\n\nUsage: touch <filename>\n\nCreates a new empty file if it doesn't exist.");
write_man_file("cc", "CC - C Compiler\n\nUsage: cc <file.c>\n\nThe BoredOS C Compiler. Compiles C source files into executables. (execute these with ./>file<)");
write_man_file("crash", "CRASH - Trigger kernel exception\n\nUsage: crash\n\nIntentionally triggers a null pointer dereference to test handlers.");
write_man_file("sysfetch", "SYSFETCH - Show OS information\n\nUsage: sysfetch\n\nDisplays system information in a neofetch-like layout. Configurable via A:/Library/conf/sysfetch.cfg.");
write_man_file("sysfetch", "SYSFETCH - Show OS information\n\nUsage: sysfetch\n\nDisplays system information in a neofetch-like layout. Configurable via /Library/conf/sysfetch.cfg.");
write_man_file("meminfo", "MEMINFO - Memory usage stats\n\nUsage: meminfo\n\nDisplays current physical and virtual memory allocation statistics.");
write_man_file("pci_list", "PCI_LIST - Scan PCI bus\n\nUsage: pci_list\n\nScans the PCI bus and lists all detected hardware devices.");
write_man_file("reboot", "REBOOT - Restart system\n\nUsage: reboot\n\nRestarts the computer immediately.");

View File

@@ -4,6 +4,7 @@
#include <stdint.h>
#include "limine.h"
#include <stddef.h>
#include "platform.h"
static volatile struct limine_hhdm_request hhdm_request __attribute__((used, section(".requests"))) = {
.id = LIMINE_HHDM_REQUEST,
.revision = 0,
@@ -69,3 +70,13 @@ void platform_get_cpu_model(char *model) {
}
model[48] = '\0';
}
void platform_get_cpu_vendor(char *vendor) {
uint32_t eax, ebx, ecx, edx;
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(0));
char *p = (char *)vendor;
*((uint32_t *)&p[0]) = ebx;
*((uint32_t *)&p[4]) = edx;
*((uint32_t *)&p[8]) = ecx;
p[12] = '\0';
}

View File

@@ -10,5 +10,6 @@ void platform_init(void);
uint64_t p2v(uint64_t phys);
uint64_t v2p(uint64_t virt);
void platform_get_cpu_model(char *model);
void platform_get_cpu_vendor(char *vendor);
#endif

View File

@@ -14,9 +14,9 @@ void get_os_info(os_info_t *info) {
const char *os_name = "BoredOS";
const char *os_version = "26.4";
const char *os_codename = "Geometry";
const char *os_codename = "Voyager";
const char *kernel_name = "Boredkernel";
const char *kernel_version = "3.2.3";
const char *kernel_version = "4.0.0-stable";
const char *build_date = __DATE__;
const char *build_time = __TIME__;
const char *build_arch = "x86_64";

509
src/dev/ahci.c Normal file
View File

@@ -0,0 +1,509 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "ahci.h"
#include "pci.h"
#include "disk.h"
#include "memory_manager.h"
#include "paging.h"
#include "io.h"
#include <stddef.h>
extern void serial_write(const char *str);
extern void serial_write_num(uint64_t num);
extern void serial_write_hex(uint32_t val);
// ============================================================================
// AHCI Driver State
// ============================================================================
static HBA_MEM *abar = NULL; // MMIO-mapped AHCI Base Address
static bool ahci_initialized = false;
static int active_port_count = 0;
#define MAX_AHCI_PORTS 32
typedef struct {
bool active;
int port_num;
HBA_PORT *port;
HBA_CMD_HEADER *cmd_list; // 1KB, 1KB aligned
void *fis_base; // 256B, 256B aligned
HBA_CMD_TBL *cmd_tbl; // Command table for slot 0
} ahci_port_state_t;
static ahci_port_state_t ports[MAX_AHCI_PORTS];
// ============================================================================
// String Helpers
// ============================================================================
static void ahci_strcpy(char *d, const char *s) {
while ((*d++ = *s++));
}
// Kernel virtual to physical address conversion
extern uint64_t v2p(uint64_t vaddr);
// ============================================================================
// Port Setup
// ============================================================================
static void ahci_stop_cmd(HBA_PORT *port) {
// Clear ST (Start)
port->cmd &= ~HBA_PORT_CMD_ST;
// Clear FRE (FIS Receive Enable)
port->cmd &= ~HBA_PORT_CMD_FRE;
// Wait until FR and CR clear
int timeout = 500000;
while (timeout-- > 0) {
if (port->cmd & HBA_PORT_CMD_FR) continue;
if (port->cmd & HBA_PORT_CMD_CR) continue;
break;
}
}
static void ahci_start_cmd(HBA_PORT *port) {
// Wait until CR clears
while (port->cmd & HBA_PORT_CMD_CR);
// Set FRE and ST
port->cmd |= HBA_PORT_CMD_FRE;
port->cmd |= HBA_PORT_CMD_ST;
}
static int ahci_check_port_type(HBA_PORT *port) {
uint32_t ssts = port->ssts;
uint8_t ipm = (ssts >> 8) & 0x0F;
uint8_t det = ssts & 0x0F;
if (det != 3) return -1; // No device detected
if (ipm != 1) return -1; // Not in active state
switch (port->sig) {
case SATA_SIG_ATA: return 0; // SATA drive
case SATA_SIG_ATAPI: return 1; // SATAPI drive
case SATA_SIG_SEMB: return 2; // SEMB
case SATA_SIG_PM: return 3; // Port multiplier
default: return -1;
}
}
static void ahci_port_rebase(ahci_port_state_t *ps) {
HBA_PORT *port = ps->port;
ahci_stop_cmd(port);
// Allocate command list (1KB, 1024-byte aligned)
ps->cmd_list = (HBA_CMD_HEADER*)kmalloc_aligned(1024, 1024);
if (!ps->cmd_list) return;
mem_memset(ps->cmd_list, 0, 1024);
uint64_t clb_phys = v2p((uint64_t)ps->cmd_list);
port->clb = (uint32_t)(clb_phys & 0xFFFFFFFF);
port->clbu = (uint32_t)(clb_phys >> 32);
// Allocate FIS receive area (256 bytes, 256-byte aligned)
ps->fis_base = kmalloc_aligned(256, 256);
if (!ps->fis_base) return;
mem_memset(ps->fis_base, 0, 256);
uint64_t fb_phys = v2p((uint64_t)ps->fis_base);
port->fb = (uint32_t)(fb_phys & 0xFFFFFFFF);
port->fbu = (uint32_t)(fb_phys >> 32);
// Allocate command table for slot 0 (256-byte aligned, room for 8 PRDT entries)
int cmd_tbl_size = sizeof(HBA_CMD_TBL) + 8 * sizeof(HBA_PRDT_ENTRY);
ps->cmd_tbl = (HBA_CMD_TBL*)kmalloc_aligned(cmd_tbl_size, 256);
if (!ps->cmd_tbl) return;
mem_memset(ps->cmd_tbl, 0, cmd_tbl_size);
// Set command header 0 to point to our command table
uint64_t ctba_phys = v2p((uint64_t)ps->cmd_tbl);
ps->cmd_list[0].ctba = (uint32_t)(ctba_phys & 0xFFFFFFFF);
ps->cmd_list[0].ctbau = (uint32_t)(ctba_phys >> 32);
ps->cmd_list[0].prdtl = 1; // 1 PRDT entry default
// Clear error and interrupt status
port->serr = 0xFFFFFFFF;
port->is = 0xFFFFFFFF;
ahci_start_cmd(port);
}
// ============================================================================
// Sector I/O
// ============================================================================
static int ahci_find_free_slot(HBA_PORT *port) {
uint32_t slots = (port->sact | port->ci);
for (int i = 0; i < 32; i++) {
if (!(slots & (1 << i))) return i;
}
return -1;
}
int ahci_read_sectors(int port_num, uint64_t lba, uint32_t count, uint8_t *buffer) {
if (!ahci_initialized || port_num < 0 || port_num >= MAX_AHCI_PORTS) return -1;
ahci_port_state_t *ps = &ports[port_num];
if (!ps->active) return -1;
HBA_PORT *port = ps->port;
// Clear any pending interrupts/errors
port->is = 0xFFFFFFFF;
int slot = ahci_find_free_slot(port);
if (slot < 0) return -1;
HBA_CMD_HEADER *cmd_hdr = &ps->cmd_list[slot];
cmd_hdr->cfl = sizeof(FIS_REG_H2D) / sizeof(uint32_t);
cmd_hdr->w = 0; // Read
cmd_hdr->prdtl = 1;
HBA_CMD_TBL *cmd_tbl = ps->cmd_tbl;
mem_memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + sizeof(HBA_PRDT_ENTRY));
// Setup PRDT
uint64_t buf_phys = v2p((uint64_t)buffer);
cmd_tbl->prdt[0].dba = (uint32_t)(buf_phys & 0xFFFFFFFF);
cmd_tbl->prdt[0].dbau = (uint32_t)(buf_phys >> 32);
cmd_tbl->prdt[0].dbc = (count * 512) - 1; // 0-based byte count
cmd_tbl->prdt[0].i = 1;
// Setup Command FIS
FIS_REG_H2D *fis = (FIS_REG_H2D*)&cmd_tbl->cfis;
fis->fis_type = FIS_TYPE_REG_H2D;
fis->c = 1; // Command
fis->command = ATA_CMD_READ_DMA_EX;
fis->lba0 = (uint8_t)(lba);
fis->lba1 = (uint8_t)(lba >> 8);
fis->lba2 = (uint8_t)(lba >> 16);
fis->device = 1 << 6; // LBA mode
fis->lba3 = (uint8_t)(lba >> 24);
fis->lba4 = (uint8_t)(lba >> 32);
fis->lba5 = (uint8_t)(lba >> 40);
fis->countl = (uint8_t)(count);
fis->counth = (uint8_t)(count >> 8);
// Issue command
port->ci = (1 << slot);
// Wait for completion
int timeout = 1000000;
while (timeout-- > 0) {
if (!(port->ci & (1 << slot))) break;
if (port->is & (1 << 30)) { // Task File Error
serial_write("[AHCI] Read error on port ");
serial_write_num(port_num);
serial_write("\n");
return -1;
}
}
if (timeout <= 0) {
serial_write("[AHCI] Read timeout on port ");
serial_write_num(port_num);
serial_write("\n");
return -1;
}
return 0;
}
int ahci_write_sectors(int port_num, uint64_t lba, uint32_t count, const uint8_t *buffer) {
if (!ahci_initialized || port_num < 0 || port_num >= MAX_AHCI_PORTS) return -1;
ahci_port_state_t *ps = &ports[port_num];
if (!ps->active) return -1;
HBA_PORT *port = ps->port;
port->is = 0xFFFFFFFF;
int slot = ahci_find_free_slot(port);
if (slot < 0) return -1;
HBA_CMD_HEADER *cmd_hdr = &ps->cmd_list[slot];
cmd_hdr->cfl = sizeof(FIS_REG_H2D) / sizeof(uint32_t);
cmd_hdr->w = 1; // Write
cmd_hdr->prdtl = 1;
HBA_CMD_TBL *cmd_tbl = ps->cmd_tbl;
mem_memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + sizeof(HBA_PRDT_ENTRY));
uint64_t buf_phys = v2p((uint64_t)buffer);
cmd_tbl->prdt[0].dba = (uint32_t)(buf_phys & 0xFFFFFFFF);
cmd_tbl->prdt[0].dbau = (uint32_t)(buf_phys >> 32);
cmd_tbl->prdt[0].dbc = (count * 512) - 1;
cmd_tbl->prdt[0].i = 1;
FIS_REG_H2D *fis = (FIS_REG_H2D*)&cmd_tbl->cfis;
fis->fis_type = FIS_TYPE_REG_H2D;
fis->c = 1;
fis->command = ATA_CMD_WRITE_DMA_EX;
fis->lba0 = (uint8_t)(lba);
fis->lba1 = (uint8_t)(lba >> 8);
fis->lba2 = (uint8_t)(lba >> 16);
fis->device = 1 << 6;
fis->lba3 = (uint8_t)(lba >> 24);
fis->lba4 = (uint8_t)(lba >> 32);
fis->lba5 = (uint8_t)(lba >> 40);
fis->countl = (uint8_t)(count);
fis->counth = (uint8_t)(count >> 8);
port->ci = (1 << slot);
int timeout = 1000000;
while (timeout-- > 0) {
if (!(port->ci & (1 << slot))) break;
if (port->is & (1 << 30)) {
serial_write("[AHCI] Write error on port ");
serial_write_num(port_num);
serial_write("\n");
return -1;
}
}
if (timeout <= 0) {
serial_write("[AHCI] Write timeout on port ");
serial_write_num(port_num);
serial_write("\n");
return -1;
}
return 0;
}
// ============================================================================
// AHCI Disk Integration — wrap AHCI into Disk read/write_sector
// ============================================================================
typedef struct {
int ahci_port;
} AHCIDriverData;
static int ahci_disk_read_sector(Disk *disk, uint32_t sector, uint8_t *buffer) {
AHCIDriverData *data = (AHCIDriverData*)disk->driver_data;
// For partitions, add offset and use parent's port
if (disk->is_partition && disk->parent) {
AHCIDriverData *pdata = (AHCIDriverData*)disk->parent->driver_data;
return ahci_read_sectors(pdata->ahci_port,
(uint64_t)sector + disk->partition_lba_offset, 1, buffer);
}
return ahci_read_sectors(data->ahci_port, (uint64_t)sector, 1, buffer);
}
static int ahci_disk_write_sector(Disk *disk, uint32_t sector, const uint8_t *buffer) {
AHCIDriverData *data = (AHCIDriverData*)disk->driver_data;
if (disk->is_partition && disk->parent) {
AHCIDriverData *pdata = (AHCIDriverData*)disk->parent->driver_data;
return ahci_write_sectors(pdata->ahci_port,
(uint64_t)sector + disk->partition_lba_offset, 1, buffer);
}
return ahci_write_sectors(data->ahci_port, (uint64_t)sector, 1, buffer);
}
// ============================================================================
// Initialization
// ============================================================================
int ahci_get_port_count(void) {
return active_port_count;
}
bool ahci_port_is_active(int port_num) {
if (port_num < 0 || port_num >= MAX_AHCI_PORTS) return false;
return ports[port_num].active;
}
void ahci_init(void) {
serial_write("[AHCI] Scanning PCI for AHCI controller...\n");
// Find AHCI controller (Class 0x01, Subclass 0x06)
pci_device_t pci_dev;
if (!pci_find_device_by_class(PCI_CLASS_MASS_STORAGE, PCI_SUBCLASS_SATA, &pci_dev)) {
serial_write("[AHCI] No AHCI controller found\n");
return;
}
serial_write("[AHCI] Found AHCI controller (");
serial_write("vendor=0x");
serial_write_hex(pci_dev.vendor_id);
serial_write(", device=0x");
serial_write_hex(pci_dev.device_id);
serial_write(")\n");
// Enable Bus Mastering and MMIO
pci_enable_bus_mastering(&pci_dev);
pci_enable_mmio(&pci_dev);
// Read ABAR (BAR5)
uint32_t abar_raw = pci_get_bar(&pci_dev, 5);
uint64_t abar_phys = abar_raw & 0xFFFFF000; // Mask out lower bits
if (abar_phys == 0) {
serial_write("[AHCI] Invalid ABAR address\n");
return;
}
serial_write("[AHCI] ABAR physical address: 0x");
serial_write_hex((uint32_t)abar_phys);
serial_write("\n");
// Map ABAR region into kernel virtual address space
// Identity-map several pages to cover the HBA memory (at least 0x1100 bytes)
uint64_t abar_virt = abar_phys; // Use identity mapping
for (uint64_t offset = 0; offset < 0x2000; offset += 4096) {
paging_map_page(paging_get_pml4_phys(), abar_virt + offset,
abar_phys + offset,
PT_PRESENT | PT_RW | PT_CACHE_DISABLE);
}
abar = (HBA_MEM*)abar_virt;
// Enable AHCI mode
abar->ghc |= (1 << 31); // AE (AHCI Enable)
serial_write("[AHCI] Version: ");
serial_write_num(abar->vs >> 16);
serial_write(".");
serial_write_num(abar->vs & 0xFFFF);
serial_write("\n");
// Probe ports
uint32_t pi = abar->pi;
active_port_count = 0;
for (int i = 0; i < 32; i++) {
ports[i].active = false;
if (!(pi & (1 << i))) continue;
HBA_PORT *port = &abar->ports[i];
int type = ahci_check_port_type(port);
if (type == 0) { // SATA drive
serial_write("[AHCI] Port ");
serial_write_num(i);
serial_write(": SATA drive detected\n");
ports[i].port_num = i;
ports[i].port = port;
ahci_port_rebase(&ports[i]);
ports[i].active = true;
active_port_count++;
// Register as a block device
Disk *disk = (Disk*)kmalloc(sizeof(Disk));
if (disk) {
AHCIDriverData *drv = (AHCIDriverData*)kmalloc(sizeof(AHCIDriverData));
drv->ahci_port = i;
disk->devname[0] = 0; // Auto-assign
disk->type = DISK_TYPE_SATA;
ahci_strcpy(disk->label, "SATA Drive");
disk->read_sector = ahci_disk_read_sector;
disk->write_sector = ahci_disk_write_sector;
disk->driver_data = drv;
disk->partition_lba_offset = 0;
disk->total_sectors = 0;
disk->parent = NULL;
disk->is_partition = false;
disk->is_fat32 = false;
disk_register(disk);
// Let disk_manager parse partitions — we call a scan function
extern void disk_manager_scan_partitions(Disk *disk);
// Inline MBR parse for this disk
extern void serial_write(const char *str);
serial_write("[AHCI] Probing partitions on /dev/");
serial_write(disk->devname);
serial_write("...\n");
// Read MBR sector 0
uint8_t *mbr_buf = (uint8_t*)kmalloc(512);
if (mbr_buf) {
if (ahci_disk_read_sector(disk, 0, mbr_buf) == 0) {
if (mbr_buf[510] == 0x55 && mbr_buf[511] == 0xAA) {
// Parse MBR partition table
typedef struct {
uint8_t status;
uint8_t chs_first[3];
uint8_t type;
uint8_t chs_last[3];
uint32_t lba_start;
uint32_t sector_count;
} __attribute__((packed)) MBR_Part;
MBR_Part *parts = (MBR_Part*)&mbr_buf[446];
int pn = 1;
for (int p = 0; p < 4; p++) {
if (parts[p].type == 0x00 || parts[p].sector_count == 0)
continue;
bool fat32 = false;
if (parts[p].type == 0x0B || parts[p].type == 0x0C) {
// Verify BPB
uint8_t *pbuf = (uint8_t*)kmalloc(512);
if (pbuf) {
if (ahci_disk_read_sector(disk, parts[p].lba_start, pbuf) == 0) {
if (pbuf[510] == 0x55 && pbuf[511] == 0xAA) {
uint16_t bps = *(uint16_t*)&pbuf[11];
uint16_t spf16 = *(uint16_t*)&pbuf[22];
uint32_t spf32 = *(uint32_t*)&pbuf[36];
if (bps == 512 && spf16 == 0 && spf32 > 0)
fat32 = true;
}
}
kfree(pbuf);
}
}
disk_register_partition(disk, parts[p].lba_start,
parts[p].sector_count, fat32, pn);
pn++;
}
// Fallback: raw FAT32
if (pn == 1) {
uint16_t bps = *(uint16_t*)&mbr_buf[11];
uint16_t spf16 = *(uint16_t*)&mbr_buf[22];
uint32_t spf32 = *(uint32_t*)&mbr_buf[36];
if (bps == 512 && spf16 == 0 && spf32 > 0) {
disk->is_fat32 = true;
disk->partition_lba_offset = 0;
serial_write("[AHCI] Raw FAT32 volume detected\n");
}
}
}
}
kfree(mbr_buf);
}
}
} else if (type == 1) {
serial_write("[AHCI] Port ");
serial_write_num(i);
serial_write(": SATAPI drive (ignored)\n");
}
}
if (active_port_count > 0) {
ahci_initialized = true;
serial_write("[AHCI] Initialization complete: ");
serial_write_num(active_port_count);
serial_write(" SATA port(s) active\n");
} else {
serial_write("[AHCI] No active SATA ports found\n");
}
}

174
src/dev/ahci.h Normal file
View File

@@ -0,0 +1,174 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef AHCI_H
#define AHCI_H
#include <stdint.h>
#include <stdbool.h>
// ============================================================================
// FIS (Frame Information Structure) Types
// ============================================================================
typedef enum {
FIS_TYPE_REG_H2D = 0x27, // Register FIS — Host to Device
FIS_TYPE_REG_D2H = 0x34, // Register FIS — Device to Host
FIS_TYPE_DMA_ACT = 0x39, // DMA Activate FIS
FIS_TYPE_DMA_SETUP = 0x41, // DMA Setup FIS
FIS_TYPE_DATA = 0x46, // Data FIS
FIS_TYPE_BIST = 0x58, // BIST Activate FIS
FIS_TYPE_PIO_SETUP = 0x5F, // PIO Setup FIS
FIS_TYPE_DEV_BITS = 0xA1, // Set Device Bits FIS
} FIS_TYPE;
// ============================================================================
// HBA Register Structures (MMIO-mapped from ABAR)
// ============================================================================
// Port Registers (one set per port, at ABAR + 0x100 + portno*0x80)
typedef volatile struct {
uint32_t clb; // 0x00: Command List Base Address (lower 32 bits)
uint32_t clbu; // 0x04: Command List Base Address (upper 32 bits)
uint32_t fb; // 0x08: FIS Base Address (lower 32 bits)
uint32_t fbu; // 0x0C: FIS Base Address (upper 32 bits)
uint32_t is; // 0x10: Interrupt Status
uint32_t ie; // 0x14: Interrupt Enable
uint32_t cmd; // 0x18: Command and Status
uint32_t rsv0; // 0x1C: Reserved
uint32_t tfd; // 0x20: Task File Data
uint32_t sig; // 0x24: Signature
uint32_t ssts; // 0x28: SATA Status (SStatus)
uint32_t sctl; // 0x2C: SATA Control (SControl)
uint32_t serr; // 0x30: SATA Error (SError)
uint32_t sact; // 0x34: SATA Active (SCR3)
uint32_t ci; // 0x38: Command Issue
uint32_t sntf; // 0x3C: SATA Notification (SCR4)
uint32_t fbs; // 0x40: FIS-based Switch Control
uint32_t rsv1[11]; // 0x44~0x6F
uint32_t vendor[4]; // 0x70~0x7F
} HBA_PORT;
// Global HBA Memory Registers (at ABAR)
typedef volatile struct {
uint32_t cap; // 0x00: Host Capability
uint32_t ghc; // 0x04: Global Host Control
uint32_t is; // 0x08: Interrupt Status
uint32_t pi; // 0x0C: Port Implemented
uint32_t vs; // 0x10: Version
uint32_t ccc_ctl; // 0x14: Command Completion Coalescing Control
uint32_t ccc_pts; // 0x18: Command Completion Coalescing Ports
uint32_t em_loc; // 0x1C: Enclosure Management Location
uint32_t em_ctl; // 0x20: Enclosure Management Control
uint32_t cap2; // 0x24: Host Capabilities Extended
uint32_t bohc; // 0x28: BIOS/OS Handoff Control and Status
uint8_t rsv[0xA0 - 0x2C];
uint8_t vendor[0x100 - 0xA0];
HBA_PORT ports[]; // Port 0 at offset 0x100 (flexible array member)
} HBA_MEM;
// ============================================================================
// Command List / Table Structures (DMA)
// ============================================================================
// Command Header (32 bytes each, 32 entries per port = 1KB)
typedef struct {
uint8_t cfl:5; // Command FIS Length (in DWORDs)
uint8_t a:1; // ATAPI
uint8_t w:1; // Write (1=H2D, 0=D2H)
uint8_t p:1; // Prefetchable
uint8_t r:1; // Reset
uint8_t b:1; // BIST
uint8_t c:1; // Clear Busy upon R_OK
uint8_t rsv0:1;
uint8_t pmp:4; // Port Multiplier Port
uint16_t prdtl; // Physical Region Descriptor Table Length (entries)
volatile uint32_t prdbc; // PRD Byte Count transferred
uint32_t ctba; // Command Table Descriptor Base Address (lower 32)
uint32_t ctbau; // Command Table Descriptor Base Address (upper 32)
uint32_t rsv1[4]; // Reserved
} __attribute__((packed)) HBA_CMD_HEADER;
// Physical Region Descriptor Table Entry
typedef struct {
uint32_t dba; // Data Base Address (lower 32)
uint32_t dbau; // Data Base Address (upper 32)
uint32_t rsv0; // Reserved
uint32_t dbc:22; // Byte Count (0-based, max 4MB)
uint32_t rsv1:9; // Reserved
uint32_t i:1; // Interrupt on Completion
} __attribute__((packed)) HBA_PRDT_ENTRY;
// Host-to-Device Register FIS
typedef struct {
uint8_t fis_type; // FIS_TYPE_REG_H2D
uint8_t pmport:4; // Port Multiplier
uint8_t rsv0:3; // Reserved
uint8_t c:1; // 1=Command, 0=Control
uint8_t command; // Command register
uint8_t featurel; // Feature register (7:0)
uint8_t lba0; // LBA (7:0)
uint8_t lba1; // LBA (15:8)
uint8_t lba2; // LBA (23:16)
uint8_t device; // Device register
uint8_t lba3; // LBA (31:24)
uint8_t lba4; // LBA (39:32)
uint8_t lba5; // LBA (47:40)
uint8_t featureh; // Feature register (15:8)
uint8_t countl; // Count (7:0)
uint8_t counth; // Count (15:8)
uint8_t icc; // Isochronous Command Completion
uint8_t control; // Control register
uint8_t rsv1[4]; // Reserved
} __attribute__((packed)) FIS_REG_H2D;
// Command Table (256-byte aligned)
typedef struct {
uint8_t cfis[64]; // Command FIS
uint8_t acmd[16]; // ATAPI Command
uint8_t rsv[48]; // Reserved
HBA_PRDT_ENTRY prdt[]; // PRDT entries (variable, at least 1)
} __attribute__((packed)) HBA_CMD_TBL;
// ============================================================================
// Port Signature Values
// ============================================================================
#define SATA_SIG_ATA 0x00000101 // SATA drive
#define SATA_SIG_ATAPI 0xEB140101 // SATAPI drive
#define SATA_SIG_SEMB 0xC33C0101 // Enclosure management bridge
#define SATA_SIG_PM 0x96690101 // Port multiplier
// ============================================================================
// Port Command Bits
// ============================================================================
#define HBA_PORT_CMD_ST 0x0001 // Start
#define HBA_PORT_CMD_FRE 0x0010 // FIS Receive Enable
#define HBA_PORT_CMD_FR 0x4000 // FIS Receive Running
#define HBA_PORT_CMD_CR 0x8000 // Command List Running
// ============================================================================
// ATA Commands
// ============================================================================
#define ATA_CMD_READ_DMA_EX 0x25
#define ATA_CMD_WRITE_DMA_EX 0x35
#define ATA_CMD_IDENTIFY 0xEC
// ============================================================================
// Public API
// ============================================================================
void ahci_init(void);
int ahci_read_sectors(int port_num, uint64_t lba, uint32_t count, uint8_t *buffer);
int ahci_write_sectors(int port_num, uint64_t lba, uint32_t count, const uint8_t *buffer);
int ahci_get_port_count(void);
bool ahci_port_is_active(int port_num);
#endif

View File

@@ -8,6 +8,7 @@
#include <stdbool.h>
#define SECTOR_SIZE 512
#define MAX_DISKS 16
typedef enum {
DISK_TYPE_RAM,
@@ -17,26 +18,45 @@ typedef enum {
} DiskType;
typedef struct Disk {
char letter;
char devname[16]; // Device name: "sda", "sdb", "sda1", etc.
DiskType type;
bool is_fat32;
char name[32];
uint32_t partition_lba_offset; // LBA offset of FAT32 partition (0 for raw)
char label[32]; // Human-readable label
uint32_t partition_lba_offset; // LBA offset of partition (0 for whole disk)
uint32_t total_sectors; // Total sectors on this device/partition
// Function pointers for driver operations
int (*read_sector)(struct Disk *disk, uint32_t sector, uint8_t *buffer);
int (*write_sector)(struct Disk *disk, uint32_t sector, const uint8_t *buffer);
// Private driver data
void *driver_data;
void *driver_data;
// Parent disk (for partitions — points to the whole-disk Disk)
struct Disk *parent;
bool is_partition;
bool registered;
} Disk;
// Initialization and scanning
void disk_manager_init(void);
void disk_manager_scan(void); // Scans for new disks
Disk* disk_get_by_letter(char letter);
char disk_get_next_free_letter(void);
void disk_manager_scan(void);
// Device registration
void disk_register(Disk *disk);
void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_count,
bool is_fat32, int part_num);
// Lookup
Disk* disk_get_by_name(const char *devname);
int disk_get_count(void);
Disk* disk_get_by_index(int index);
// Auto-naming helpers
const char* disk_get_next_dev_name(void); // Returns "sda", "sdb", etc.
// Backward compat (deprecated — wraps disk_get_by_name)
Disk* disk_get_by_letter(char letter);
char disk_get_next_free_letter(void);
#endif

View File

@@ -5,15 +5,42 @@
#include "pci.h"
#include "memory_manager.h"
#include "io.h"
#include "wm.h"
#include "wm.h"
#include "ahci.h"
#include "../fs/vfs.h"
#include "../fs/fat32.h"
#include "../sys/spinlock.h"
#include <stddef.h>
#define MAX_DISKS 26
static spinlock_t ide_lock = SPINLOCK_INIT;
static Disk *disks[MAX_DISKS];
static int disk_count = 0;
static int next_drive_letter_idx = 0; // For backward compat
static int next_sd_index = 0; // For sda, sdb, sdc...
// === ATA Definitions ===
extern void serial_write(const char *str);
extern void serial_write_num(uint64_t num);
// === String Helpers ===
static void dm_strcpy(char *dest, const char *src) {
while (*src) *dest++ = *src++;
*dest = 0;
}
static int dm_strcmp(const char *a, const char *b) {
while (*a && *a == *b) { a++; b++; }
return (unsigned char)*a - (unsigned char)*b;
}
static int dm_strlen(const char *s) {
int n = 0;
while (s[n]) n++;
return n;
}
// === ATA Definitions (Legacy IDE PIO — kept as fallback) ===
#define ATA_PRIMARY_IO 0x1F0
#define ATA_PRIMARY_CTRL 0x3F6
@@ -35,101 +62,91 @@ static int disk_count = 0;
#define ATA_CMD_WRITE_PIO 0x30
#define ATA_CMD_IDENTIFY 0xEC
#define ATA_SR_BSY 0x80 // Busy
#define ATA_SR_DRDY 0x40 // Drive ready
#define ATA_SR_DF 0x20 // Drive write fault
#define ATA_SR_DSC 0x10 // Drive seek complete
#define ATA_SR_DRQ 0x08 // Data request ready
#define ATA_SR_CORR 0x04 // Corrected data
#define ATA_SR_IDX 0x02 // Index
#define ATA_SR_ERR 0x01 // Error
#define ATA_SR_BSY 0x80
#define ATA_SR_DRDY 0x40
#define ATA_SR_DF 0x20
#define ATA_SR_DSC 0x10
#define ATA_SR_DRQ 0x08
#define ATA_SR_CORR 0x04
#define ATA_SR_IDX 0x02
#define ATA_SR_ERR 0x01
typedef struct {
uint16_t port_base;
bool slave;
} ATADriverData;
// === Helpers ===
// === ATA PIO Driver ===
static void dm_strcpy(char *dest, const char *src) {
while (*src) *dest++ = *src++;
*dest = 0;
static int ata_wait_bsy(uint16_t port_base) {
int timeout = 10000000;
while ((inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY) && --timeout > 0);
return timeout <= 0 ? -1 : 0;
}
void disk_register(Disk *disk);
static int ramdisk_read(Disk *disk, uint32_t sector, uint8_t *buffer) {
(void)disk; (void)sector; (void)buffer;
return 0;
}
static int ramdisk_write(Disk *disk, uint32_t sector, const uint8_t *buffer) {
(void)disk; (void)sector; (void)buffer;
static int ata_wait_drq(uint16_t port_base) {
int timeout = 10000000;
while (!(inb(port_base + ATA_REG_STATUS) & (ATA_SR_DRQ | ATA_SR_ERR)) && --timeout > 0);
if (timeout <= 0 || (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR)) return -1;
return 0;
}
static void ata_wait_bsy(uint16_t port_base) {
while (inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY);
}
static void ata_wait_drq(uint16_t port_base) {
while (!(inb(port_base + ATA_REG_STATUS) & ATA_SR_DRQ));
}
// Returns 1 if drive exists, 0 otherwise
static int ata_identify(uint16_t port_base, bool slave) {
// Select Drive
outb(port_base + ATA_REG_HDDEVSEL, slave ? 0xB0 : 0xA0);
// Zero out sector count and LBA registers
outb(port_base + ATA_REG_SEC_COUNT0, 0);
outb(port_base + ATA_REG_LBA0, 0);
outb(port_base + ATA_REG_LBA1, 0);
outb(port_base + ATA_REG_LBA2, 0);
// Send Identify command
outb(port_base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
// Check if status is 0 (no drive)
uint8_t status = inb(port_base + ATA_REG_STATUS);
if (status == 0) return 0;
// Wait until BSY clears
int timeout = 10000;
while ((inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY) && --timeout > 0) {
status = inb(port_base + ATA_REG_STATUS);
if (status == 0) return 0; // Check again
if (status == 0) return 0;
}
if (timeout <= 0) return 0; // Hardware didn't respond
// Check for error
if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) {
return 0; // Error, likely not ATA
}
// Wait for DRQ or ERR
while (!(inb(port_base + ATA_REG_STATUS) & (ATA_SR_DRQ | ATA_SR_ERR)));
if (timeout <= 0) return 0;
if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) return 0;
// Read 256 words (512 bytes) of identity data
if (ata_wait_drq(port_base) != 0) return 0;
if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) return 0;
uint32_t sectors = 0;
for (int i = 0; i < 256; i++) {
uint16_t data = inw(port_base + ATA_REG_DATA);
(void)data;
if (i == 60) sectors |= (uint32_t)data;
if (i == 61) sectors |= (uint32_t)data << 16;
}
return 1;
return sectors;
}
static int ata_read_sector(Disk *disk, uint32_t lba, uint8_t *buffer) {
ATADriverData *data = (ATADriverData*)disk->driver_data;
uint16_t port_base = data->port_base;
bool slave = data->slave;
ata_wait_bsy(port_base);
// Select drive and send highest 4 bits of LBA
// For partition reads, add the partition LBA offset
if (disk->is_partition && disk->parent) {
lba += disk->partition_lba_offset;
// Use parent's driver
data = (ATADriverData*)disk->parent->driver_data;
port_base = data->port_base;
slave = data->slave;
}
uint64_t flags = spinlock_acquire_irqsave(&ide_lock);
if (ata_wait_bsy(port_base) != 0) {
spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
outb(port_base + ATA_REG_FEATURES, 0x00);
outb(port_base + ATA_REG_SEC_COUNT0, 1);
@@ -137,25 +154,45 @@ static int ata_read_sector(Disk *disk, uint32_t lba, uint8_t *buffer) {
outb(port_base + ATA_REG_LBA1, (uint8_t)(lba >> 8));
outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16));
outb(port_base + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
ata_wait_bsy(port_base);
ata_wait_drq(port_base);
if (ata_wait_bsy(port_base) != 0) {
spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
if (ata_wait_drq(port_base) != 0) {
spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
uint16_t *ptr = (uint16_t*)buffer;
for (int i = 0; i < 256; i++) {
ptr[i] = inw(port_base + ATA_REG_DATA);
}
return 0; // Success
spinlock_release_irqrestore(&ide_lock, flags);
return 0;
}
static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) {
ATADriverData *data = (ATADriverData*)disk->driver_data;
uint16_t port_base = data->port_base;
bool slave = data->slave;
ata_wait_bsy(port_base);
// For partition writes, add the partition LBA offset
if (disk->is_partition && disk->parent) {
lba += disk->partition_lba_offset;
data = (ATADriverData*)disk->parent->driver_data;
port_base = data->port_base;
slave = data->slave;
}
uint64_t flags = spinlock_acquire_irqsave(&ide_lock);
if (ata_wait_bsy(port_base) != 0) {
spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
outb(port_base + ATA_REG_FEATURES, 0x00);
outb(port_base + ATA_REG_SEC_COUNT0, 1);
@@ -163,75 +200,131 @@ static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) {
outb(port_base + ATA_REG_LBA1, (uint8_t)(lba >> 8));
outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16));
outb(port_base + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
ata_wait_bsy(port_base);
ata_wait_drq(port_base);
if (ata_wait_bsy(port_base) != 0) {
spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
if (ata_wait_drq(port_base) != 0) {
spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
const uint16_t *ptr = (const uint16_t*)buffer;
for (int i = 0; i < 256; i++) {
outw(port_base + ATA_REG_DATA, ptr[i]);
}
// Flush / Sync
outb(port_base + ATA_REG_COMMAND, 0xE7); // Cache Flush
ata_wait_bsy(port_base);
if (ata_wait_bsy(port_base) != 0) {
spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
spinlock_release_irqrestore(&ide_lock, flags);
return 0;
}
// === Device Naming ===
char disk_get_next_free_letter(void) {
for (int i = 0; i < MAX_DISKS; i++) {
char letter = 'A' + i;
bool used = false;
for (int j = 0; j < disk_count; j++) {
if (disks[j]->letter == letter) {
used = true;
break;
}
}
if (!used) return letter;
}
return 0; // No free letters
const char* disk_get_next_dev_name(void) {
static char name[8];
name[0] = 's';
name[1] = 'd';
name[2] = 'a' + next_sd_index;
name[3] = 0;
next_sd_index++;
return name;
}
// === Registration ===
void disk_register(Disk *disk) {
if (disk_count >= MAX_DISKS) return;
// Ensure letter is unique
if (disk->letter == 0) {
disk->letter = disk_get_next_free_letter();
// Auto-assign devname if empty
if (disk->devname[0] == 0) {
const char *n = disk_get_next_dev_name();
dm_strcpy(disk->devname, n);
}
disk->registered = true;
disks[disk_count++] = disk;
serial_write("[DISK] Registered /dev/");
serial_write(disk->devname);
serial_write(" (");
serial_write(disk->label);
serial_write(")\n");
}
void disk_manager_init(void) {
for (int i = 0; i < MAX_DISKS; i++) {
disks[i] = NULL;
void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_count,
bool is_fat32, int part_num) {
if (disk_count >= MAX_DISKS) return;
Disk *part = (Disk*)kmalloc(sizeof(Disk));
if (!part) return;
// Build name: parent_devname + partition number (e.g. "sda1")
int len = dm_strlen(parent->devname);
for (int i = 0; i < len; i++) part->devname[i] = parent->devname[i];
part->devname[len] = '0' + part_num;
part->devname[len + 1] = 0;
part->type = parent->type;
part->is_fat32 = is_fat32;
dm_strcpy(part->label, is_fat32 ? "FAT32 Partition" : "Unknown Partition");
part->partition_lba_offset = lba_offset;
part->total_sectors = sector_count;
part->read_sector = parent->read_sector;
part->write_sector = parent->write_sector;
part->driver_data = parent->driver_data;
part->parent = parent;
part->is_partition = true;
part->registered = true;
disks[disk_count++] = part;
serial_write("[DISK] Registered /dev/");
serial_write(part->devname);
serial_write(" (LBA offset ");
serial_write_num(lba_offset);
serial_write(", ");
serial_write_num(sector_count);
serial_write(" sectors, FAT32=");
serial_write(" sectors, FAT32=");
serial_write(is_fat32 ? "yes" : "no");
serial_write(")\n");
if (is_fat32) {
// Try to initialize and mount FAT32 volume to VFS
void *vol = fat32_mount_volume(part);
if (vol) {
char mount_path[32];
mount_path[0] = '/';
mount_path[1] = 'd'; mount_path[2] = 'e'; mount_path[3] = 'v'; mount_path[4] = '/';
dm_strcpy(mount_path + 5, part->devname);
if (vfs_mount(mount_path, part->devname, "fat32", fat32_get_realfs_ops(), vol)) {
serial_write("[VFS] Mounted ");
serial_write(mount_path);
serial_write(" to VFS\n");
wm_notify_fs_change();
} else {
serial_write("[VFS] Failed to mount ");
serial_write(mount_path);
serial_write("\n");
}
}
}
disk_count = 0;
// Register A: (Ramdisk)
Disk *ramdisk = (Disk*)kmalloc(sizeof(Disk));
ramdisk->letter = 'A';
ramdisk->type = DISK_TYPE_RAM;
ramdisk->is_fat32 = true; // Ramdisk is always formatted
dm_strcpy(ramdisk->name, "RAM");
ramdisk->read_sector = ramdisk_read;
ramdisk->write_sector = ramdisk_write;
ramdisk->driver_data = NULL;
ramdisk->partition_lba_offset = 0;
disk_register(ramdisk);
}
Disk* disk_get_by_letter(char letter) {
// Uppercase
if (letter >= 'a' && letter <= 'z') letter -= 32;
// === Lookup ===
Disk* disk_get_by_name(const char *devname) {
if (!devname) return NULL;
for (int i = 0; i < disk_count; i++) {
if (disks[i]->letter == letter) {
if (dm_strcmp(disks[i]->devname, devname) == 0) {
return disks[i];
}
}
@@ -247,130 +340,181 @@ Disk* disk_get_by_index(int index) {
return disks[index];
}
// === Backward Compat (deprecated) ===
// === MBR Partition Table Structures ===
char disk_get_next_free_letter(void) {
char letter = 'B' + next_drive_letter_idx++;
if (letter > 'Z') return 0;
return letter;
}
Disk* disk_get_by_letter(char letter) {
// Maps old letter scheme: A=ramfs (not a block device), B+=first real disk, etc.
if (letter >= 'a' && letter <= 'z') letter -= 32;
// A: was the ramdisk — return NULL since ramfs is now VFS-managed
if (letter == 'A') return NULL;
// B-Z map to disk indices 0, 1, 2...
// Find real disks (non-RAM, non-partition-parent)
int real_idx = 0;
for (int i = 0; i < disk_count; i++) {
if (disks[i]->is_partition && disks[i]->is_fat32) {
if (real_idx == (letter - 'B')) {
return disks[i];
}
real_idx++;
}
}
return NULL;
}
// === MBR Partition Table ===
typedef struct {
uint8_t status; // 0x80 = bootable, 0x00 = inactive
uint8_t chs_first[3]; // CHS of first sector
uint8_t type; // Partition type
uint8_t chs_last[3]; // CHS of last sector
uint32_t lba_start; // LBA of first sector
uint32_t sector_count; // Number of sectors
uint8_t status;
uint8_t chs_first[3];
uint8_t type;
uint8_t chs_last[3];
uint32_t lba_start;
uint32_t sector_count;
} __attribute__((packed)) MBR_PartitionEntry;
// FAT32 partition type codes
#define PART_TYPE_FAT32 0x0B
#define PART_TYPE_FAT32_LBA 0x0C
// Check if sector contains a valid FAT32 BPB (Volume Boot Record)
static bool is_fat32_bpb(const uint8_t *sector) {
// Must have 0xAA55 boot signature
if (sector[510] != 0x55 || sector[511] != 0xAA) return false;
// Check for FAT32 filesystem string at offset 82
// "FAT32 " in the fs_type field of the BPB
if (sector[82] == 'F' && sector[83] == 'A' && sector[84] == 'T' &&
sector[85] == '3' && sector[86] == '2') {
return true;
}
// Also accept if bytes_per_sector is 512 and sectors_per_fat_16 is 0
// (FAT32 always has sectors_per_fat_16 == 0)
uint16_t bps = *(uint16_t*)&sector[11];
uint16_t spf16 = *(uint16_t*)&sector[22];
uint32_t spf32 = *(uint32_t*)&sector[36];
if (bps == 512 && spf16 == 0 && spf32 > 0) {
return true;
}
return false;
}
// Parse MBR partition table and find a FAT32 partition.
// Sets disk->partition_lba_offset and returns true if found.
static bool detect_fat32_partition(Disk *disk) {
// Parse MBR and register each partition as a child block device
static void parse_mbr_partitions(Disk *disk) {
uint8_t *buffer = (uint8_t*)kmalloc(512);
if (!buffer) return false;
// Read sector 0 (MBR or raw BPB)
if (!buffer) return;
if (disk->read_sector(disk, 0, buffer) != 0) {
kfree(buffer);
return false;
return;
}
// Must have 0xAA55 boot signature
// Check for valid MBR signature
if (buffer[510] != 0x55 || buffer[511] != 0xAA) {
kfree(buffer);
return false;
return;
}
// Check MBR partition table entries (4 entries at offset 446)
MBR_PartitionEntry *partitions = (MBR_PartitionEntry*)&buffer[446];
int part_num = 1;
for (int i = 0; i < 4; i++) {
if (partitions[i].type == PART_TYPE_FAT32 ||
partitions[i].type == PART_TYPE_FAT32_LBA) {
uint32_t part_lba = partitions[i].lba_start;
// Read the partition's first sector to verify it's a valid FAT32 BPB
uint32_t start = partitions[i].lba_start;
uint32_t size = partitions[i].sector_count;
uint8_t type = partitions[i].type;
if (type == 0x00) continue; // Empty entry
if (size == 0) continue;
if (start >= disk->total_sectors) continue; // Invalid start
bool fat32 = false;
if (type == PART_TYPE_FAT32 || type == PART_TYPE_FAT32_LBA) {
// Verify by reading the BPB
uint8_t *pbuf = (uint8_t*)kmalloc(512);
if (!pbuf) { kfree(buffer); return false; }
if (disk->read_sector(disk, part_lba, pbuf) == 0 && is_fat32_bpb(pbuf)) {
disk->partition_lba_offset = part_lba;
if (pbuf) {
if (disk->read_sector(disk, start, pbuf) == 0) {
fat32 = is_fat32_bpb(pbuf);
}
kfree(pbuf);
kfree(buffer);
return true;
}
kfree(pbuf);
}
disk_register_partition(disk, partitions[i].lba_start,
partitions[i].sector_count, fat32, part_num);
part_num++;
}
// Fallback: check if sector 0 itself is a raw FAT32 BPB (no partition table)
if (is_fat32_bpb(buffer)) {
// Fallback: if no partitions found, check if entire disk is a raw FAT32 volume
if (part_num == 1 && is_fat32_bpb(buffer)) {
serial_write("[DISK] No MBR partitions — raw FAT32 volume on /dev/");
serial_write(disk->devname);
serial_write("\n");
disk->is_fat32 = true;
disk->partition_lba_offset = 0;
kfree(buffer);
return true;
}
kfree(buffer);
return false;
}
// === ATA Drive Discovery ===
static void try_add_ata_drive(uint16_t port, bool slave, const char *name) {
if (ata_identify(port, slave)) {
uint32_t sectors = ata_identify(port, slave);
if (sectors > 0) {
Disk *new_disk = (Disk*)kmalloc(sizeof(Disk));
if (!new_disk) return;
ATADriverData *data = (ATADriverData*)kmalloc(sizeof(ATADriverData));
data->port_base = port;
data->slave = slave;
new_disk->letter = 0; // Auto-assign
new_disk->devname[0] = 0; // Auto-assign
new_disk->type = DISK_TYPE_IDE;
dm_strcpy(new_disk->name, name);
dm_strcpy(new_disk->label, name);
new_disk->read_sector = ata_read_sector;
new_disk->write_sector = ata_write_sector;
new_disk->driver_data = data;
new_disk->partition_lba_offset = 0;
// Detect FAT32 (with MBR partition support)
if (detect_fat32_partition(new_disk)) {
new_disk->is_fat32 = true;
disk_register(new_disk);
} else {
kfree(data);
kfree(new_disk);
}
new_disk->total_sectors = sectors;
new_disk->parent = NULL;
new_disk->is_partition = false;
new_disk->is_fat32 = false;
disk_register(new_disk);
// Parse MBR to find partitions
parse_mbr_partitions(new_disk);
}
}
// === Init & Scan ===
void disk_manager_init(void) {
for (int i = 0; i < MAX_DISKS; i++) {
disks[i] = NULL;
}
disk_count = 0;
next_sd_index = 0;
next_drive_letter_idx = 0;
serial_write("[DISK] Disk manager initialized (VFS mode)\n");
// NOTE: Ramdisk (A:) is no longer registered here.
// RAMFS is managed directly by fat32.c and mounted at "/" via VFS.
}
void disk_manager_scan(void) {
// Probe Standard ATA Ports
try_add_ata_drive(ATA_PRIMARY_IO, false, "IDE1");
try_add_ata_drive(ATA_PRIMARY_IO, true, "IDE2");
try_add_ata_drive(ATA_SECONDARY_IO, false, "IDE3");
try_add_ata_drive(ATA_SECONDARY_IO, true, "IDE4");
serial_write("[DISK] Initializing AHCI (SATA DMA)...\n");
ahci_init();
if (ahci_get_port_count() == 0) {
serial_write("[DISK] No AHCI ports found, falling back to legacy IDE PIO...\n");
try_add_ata_drive(ATA_PRIMARY_IO, false, "IDE Primary Master");
try_add_ata_drive(ATA_PRIMARY_IO, true, "IDE Primary Slave");
try_add_ata_drive(ATA_SECONDARY_IO, false, "IDE Secondary Master");
try_add_ata_drive(ATA_SECONDARY_IO, true, "IDE Secondary Slave");
} else {
serial_write("[DISK] AHCI initialized successfully, skipping legacy IDE.\n");
}
}

View File

@@ -97,3 +97,23 @@ int pci_find_device_by_class(uint8_t class_code, uint8_t subclass, pci_device_t*
}
return 0;
}
uint32_t pci_get_bar(pci_device_t *dev, int bar_num) {
if (!dev || bar_num < 0 || bar_num > 5) return 0;
uint8_t offset = 0x10 + (bar_num * 4);
return pci_read_config(dev->bus, dev->device, dev->function, offset);
}
void pci_enable_bus_mastering(pci_device_t *dev) {
if (!dev) return;
uint32_t cmd = pci_read_config(dev->bus, dev->device, dev->function, 0x04);
cmd |= (1 << 2); // Set Bus Master bit
pci_write_config(dev->bus, dev->device, dev->function, 0x04, cmd);
}
void pci_enable_mmio(pci_device_t *dev) {
if (!dev) return;
uint32_t cmd = pci_read_config(dev->bus, dev->device, dev->function, 0x04);
cmd |= (1 << 1); // Set Memory Space bit
pci_write_config(dev->bus, dev->device, dev->function, 0x04, cmd);
}

View File

@@ -22,6 +22,9 @@ typedef struct {
#define PCI_CLASS_NETWORK_CONTROLLER 0x02
#define PCI_CLASS_ETHERNET_CONTROLLER 0x00
#define PCI_CLASS_MASS_STORAGE 0x01
#define PCI_SUBCLASS_SATA 0x06
#define PCI_SUBCLASS_IDE 0x01
uint32_t pci_read_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset);
void pci_write_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value);
@@ -35,4 +38,9 @@ int pci_enumerate_devices(pci_device_t* devices, int max_devices);
int pci_find_device(uint16_t vendor_id, uint16_t device_id, pci_device_t* device);
int pci_find_device_by_class(uint8_t class_code, uint8_t subclass, pci_device_t* device);
// BAR access and bus mastering helpers
uint32_t pci_get_bar(pci_device_t *dev, int bar_num);
void pci_enable_bus_mastering(pci_device_t *dev);
void pci_enable_mmio(pci_device_t *dev);
#endif

View File

@@ -16,18 +16,19 @@ extern void serial_print_hex(uint64_t n);
volatile uint64_t kernel_ticks = 0;
uint64_t timer_handler(registers_t *regs) {
kernel_ticks++;
wm_timer_tick();
network_process_frames();
if (smp_this_cpu_id() == 0) {
kernel_ticks++;
wm_timer_tick();
network_process_frames();
extern void k_beep_process(void);
k_beep_process();
}
extern void k_beep_process(void);
k_beep_process();
outb(0x20, 0x20); // EOI after processing to prevent nested timer interrupts
outb(0x20, 0x20);
extern uint64_t process_schedule(uint64_t current_rsp);
uint64_t new_rsp = process_schedule((uint64_t)regs);
// SMP: Wake AP cores to run their assigned processes
if (smp_cpu_count() > 1) {
lapic_send_ipi_all();
}
@@ -37,9 +38,27 @@ uint64_t timer_handler(registers_t *regs) {
// --- Keyboard ---
static bool shift_pressed = false;
static bool caps_lock_on = false;
bool ps2_ctrl_pressed = false;
static bool extended_scancode = false;
static void ps2_kbd_wait_write(void) {
uint32_t timeout = 100000;
while (timeout--) {
if ((inb(0x64) & 2) == 0) return;
}
}
static void ps2_update_leds(void) {
uint8_t led_status = 0;
if (caps_lock_on) led_status |= 4;
ps2_kbd_wait_write();
outb(0x60, 0xED);
ps2_kbd_wait_write();
outb(0x60, led_status);
}
static char scancode_map[128] = {
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
'\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
@@ -67,7 +86,7 @@ uint64_t keyboard_handler(registers_t *regs) {
if (scancode == 0x1D) {
ps2_ctrl_pressed = true;
extended_scancode = false; // Reset if Ctrl is pressed (prevents E0 1D bug)
extended_scancode = false;
} else if (scancode == 0x9D) {
ps2_ctrl_pressed = false;
extended_scancode = false;
@@ -77,10 +96,9 @@ uint64_t keyboard_handler(registers_t *regs) {
extern process_t* process_get_current(void);
process_t* proc = process_get_current();
if (proc && proc->is_user && proc->is_terminal_proc && proc->ui_window) {
// Only kill if the associated terminal window is focused
if (((Window*)proc->ui_window)->focused) {
extern uint64_t process_terminate_current(void);
outb(0x20, 0x20); // EOI before context switch
outb(0x20, 0x20);
return process_terminate_current();
}
}
@@ -90,7 +108,10 @@ uint64_t keyboard_handler(registers_t *regs) {
shift_pressed = true;
} else if (scancode == 0xAA || scancode == 0xB6) { // Shift Up
shift_pressed = false;
} else if (!(scancode & 0x80)) { // Key Press (not release)
} else if (scancode == 0x3A) { // Caps Lock Down
caps_lock_on = !caps_lock_on;
ps2_update_leds();
} else if (!(scancode & 0x80)) { // Key Press
if (extended_scancode) {
extended_scancode = false;
switch (scancode) {
@@ -102,6 +123,10 @@ uint64_t keyboard_handler(registers_t *regs) {
} else {
char c = shift_pressed ? scancode_map_shift[scancode] : scancode_map[scancode];
if (c) {
if (caps_lock_on) {
if (c >= 'a' && c <= 'z') c -= 32;
else if (c >= 'A' && c <= 'Z') c += 32;
}
wm_handle_key(c, true);
}
}
@@ -115,8 +140,13 @@ uint64_t keyboard_handler(registers_t *regs) {
case 0x4D: wm_handle_key(20, false); break; // Right arrow
}
} else {
char c = shift_pressed ? scancode_map_shift[scancode & 0x7F] : scancode_map[scancode & 0x7F];
uint8_t base_scancode = scancode & 0x7F;
char c = shift_pressed ? scancode_map_shift[base_scancode] : scancode_map[base_scancode];
if (c) {
if (caps_lock_on) {
if (c >= 'a' && c <= 'z') c -= 32;
else if (c >= 'A' && c <= 'Z') c += 32;
}
wm_handle_key(c, false);
}
}
@@ -177,7 +207,7 @@ void mouse_init(void) {
mouse_write(0xF6);
mouse_read();
// Enable Wheel - Magic Sequence
// Enable Wheel
mouse_write(0xF3); mouse_read(); mouse_write(200); mouse_read();
mouse_write(0xF3); mouse_read(); mouse_write(100); mouse_read();
mouse_write(0xF3); mouse_read(); mouse_write(80); mouse_read();

File diff suppressed because it is too large Load Diff

View File

@@ -25,7 +25,7 @@ typedef struct {
uint16_t num_heads; // Number of heads
uint32_t hidden_sectors; // Hidden sectors
uint32_t total_sectors_32; // Total sectors 32-bit
// FAT32 Specific
uint32_t sectors_per_fat_32; // Sectors per FAT 32-bit
uint16_t flags; // Flags
@@ -61,6 +61,18 @@ typedef struct {
uint32_t file_size; // File size
} __attribute__((packed)) FAT32_DirEntry;
// Long File Name Directory Entry (32 bytes)
typedef struct {
uint8_t order; // Sequence number (0x40 = last, | index)
uint16_t name1[5]; // Characters 1-5 (UCS-2)
uint8_t attr; // Always 0x0F
uint8_t type; // Always 0x00
uint8_t checksum; // Checksum of short name
uint16_t name2[6]; // Characters 6-11 (UCS-2)
uint16_t first_cluster; // Always 0x0000
uint16_t name3[2]; // Characters 12-13 (UCS-2)
} __attribute__((packed)) FAT32_LFNEntry;
// File Attributes
#define ATTR_READ_ONLY 0x01
#define ATTR_HIDDEN 0x02
@@ -70,6 +82,7 @@ typedef struct {
#define ATTR_ARCHIVE 0x20
#define ATTR_DEVICE 0x40
#define ATTR_RESERVED 0x80
#define ATTR_LFN 0x0F // LFN marker (all of the above ORed)
// FAT32 Constants
#define FAT32_SECTOR_SIZE 512
@@ -88,7 +101,9 @@ typedef struct {
bool valid; // Is this handle valid?
uint32_t dir_sector; // Sector containing the directory entry
uint32_t dir_offset; // Offset within that sector
char drive; // Drive letter (A, B, ...)
bool is_directory; // Is this a directory?
uint8_t attributes; // File attributes
void *volume; // Pointer to owning FAT32_Volume (or NULL for ramfs)
} FAT32_FileHandle;
// Directory Entry Info (for listing)
@@ -101,12 +116,23 @@ typedef struct {
uint16_t write_time;
} FAT32_FileInfo;
// === VFS Integration ===
// Forward-declared VFS ops type (defined in vfs.h)
struct vfs_fs_ops;
// Get VFS ops structs for registration
struct vfs_fs_ops* fat32_get_ramfs_ops(void);
struct vfs_fs_ops* fat32_get_realfs_ops(void);
// Mount a real FAT32 volume from a block device — returns fs_private for VFS
void* fat32_mount_volume(void *disk_ptr);
// === Function Declarations ===
// Initialization
void fat32_init(void);
// File Operations
// File Operations (backward-compat wrappers — dispatch through VFS)
FAT32_FileHandle* fat32_open(const char *path, const char *mode);
void fat32_close(FAT32_FileHandle *handle);
int fat32_read(FAT32_FileHandle *handle, void *buffer, int size);
@@ -124,7 +150,10 @@ bool fat32_is_directory(const char *path);
// Listing
int fat32_list_directory(const char *path, FAT32_FileInfo *entries, int max_entries);
// Working Directory
// Info
int fat32_get_info(const char *path, FAT32_FileInfo *info);
// Working Directory (backward compat — wraps VFS path tracking)
bool fat32_chdir(const char *path);
void fat32_get_current_dir(char *buffer, int size);
bool fat32_change_drive(char drive);

290
src/fs/procfs.c Normal file
View File

@@ -0,0 +1,290 @@
#include "vfs.h"
#include "../sys/process.h"
#include "../sys/syscall.h"
#include "../dev/disk.h"
#include "memory_manager.h"
#include "core/kutils.h"
typedef struct {
uint32_t pid;
char type[32];
int offset;
bool is_root;
} procfs_handle_t;
void* procfs_open(void *fs_private, const char *path, const char *mode) {
if (path[0] == '/') path++;
procfs_handle_t *h = (procfs_handle_t*)kmalloc(sizeof(procfs_handle_t));
k_memset(h, 0, sizeof(procfs_handle_t));
h->offset = 0;
if (path[0] == '\0') {
h->is_root = true;
return h;
}
if (path[0] >= '0' && path[0] <= '9') {
char pid_str[16];
int i = 0;
while (path[i] && path[i] != '/' && i < 15) {
pid_str[i] = path[i];
i++;
}
pid_str[i] = 0;
h->pid = k_atoi(pid_str);
if (path[i] == '/') {
k_strcpy(h->type, path + i + 1);
} else {
h->type[0] = 0;
}
return h;
}
h->pid = 0xFFFFFFFF;
k_strcpy(h->type, path);
return h;
}
void procfs_close(void *fs_private, void *handle) {
if (handle) kfree(handle);
}
int procfs_read(void *fs_private, void *handle, void *buf, int size) {
procfs_handle_t *h = (procfs_handle_t*)handle;
if (!h) return -1;
char out[1024];
out[0] = 0;
if (h->pid == 0xFFFFFFFF) {
if (k_strcmp(h->type, "version") == 0) {
extern void get_os_info(os_info_t *info);
os_info_t info;
get_os_info(&info);
k_strcpy(out, info.os_name);
k_strcpy(out + k_strlen(out), " [");
k_strcpy(out + k_strlen(out), info.os_codename);
k_strcpy(out + k_strlen(out), "] Version ");
k_strcpy(out + k_strlen(out), info.os_version);
k_strcpy(out + k_strlen(out), "\nKernel: ");
k_strcpy(out + k_strlen(out), info.kernel_name);
k_strcpy(out + k_strlen(out), " ");
k_strcpy(out + k_strlen(out), info.kernel_version);
k_strcpy(out + k_strlen(out), "\nBuild: ");
k_strcpy(out + k_strlen(out), info.build_date);
k_strcpy(out + k_strlen(out), " ");
k_strcpy(out + k_strlen(out), info.build_time);
k_strcpy(out + k_strlen(out), "\n");
} else if (k_strcmp(h->type, "uptime") == 0) {
extern uint32_t wm_get_ticks(void);
uint32_t ticks = wm_get_ticks();
k_itoa(ticks / 60, out);
k_strcpy(out + k_strlen(out), " seconds\nRaw_Ticks:");
char t_s[16]; k_itoa(ticks, t_s);
k_strcpy(out + k_strlen(out), t_s);
k_strcpy(out + k_strlen(out), "\n");
} else if (k_strcmp(h->type, "cpuinfo") == 0) {
extern uint32_t smp_cpu_count(void);
extern void platform_get_cpu_model(char *model);
char model[64];
platform_get_cpu_model(model);
k_strcpy(out, "Processor: ");
k_strcpy(out + k_strlen(out), model);
k_strcpy(out + k_strlen(out), "\nCores: ");
char c_s[16]; k_itoa(smp_cpu_count(), c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\nArchitecture: x86_64\n");
} else if (k_strcmp(h->type, "meminfo") == 0) {
extern MemStats memory_get_stats(void);
MemStats stats = memory_get_stats();
k_strcpy(out, "MemTotal: ");
char m_s[32]; k_itoa(stats.total_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\nMemFree: ");
k_itoa(stats.available_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\nMemUsed: ");
k_itoa(stats.used_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\nPeak: ");
k_itoa(stats.peak_memory_used / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\nBlocks: ");
k_itoa(stats.allocated_blocks, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), "\nFragmentation: ");
k_itoa(stats.fragmentation_percent, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), "%\n");
} else if (k_strcmp(h->type, "devices") == 0) {
extern int disk_get_count(void);
extern Disk* disk_get_by_index(int index);
int dcount = disk_get_count();
k_strcpy(out, "Block Devices:\n");
for (int i = 0; i < dcount; i++) {
Disk *d = disk_get_by_index(i);
if (d) {
k_strcpy(out + k_strlen(out), " - ");
k_strcpy(out + k_strlen(out), d->devname);
k_strcpy(out + k_strlen(out), "\n");
}
}
}
}
else {
process_t *proc = process_get_by_pid(h->pid);
if (!proc) return -1;
if (k_strcmp(h->type, "name") == 0 || k_strcmp(h->type, "cmdline") == 0) {
k_strcpy(out, proc->name);
k_strcpy(out + k_strlen(out), "\n");
} else if (k_strcmp(h->type, "status") == 0) {
k_strcpy(out, "Name: ");
k_strcpy(out + k_strlen(out), proc->name);
k_strcpy(out + k_strlen(out), "\nPID: ");
char pid_s[16]; k_itoa(proc->pid, pid_s);
k_strcpy(out + k_strlen(out), pid_s);
k_strcpy(out + k_strlen(out), "\nState: RUNNING\nMemory: ");
uint64_t mem_val = proc->used_memory;
if (h->pid == 0) {
extern MemStats memory_get_stats(void);
mem_val = memory_get_stats().used_memory;
}
char mem_s[32]; k_itoa(mem_val / 1024, mem_s);
k_strcpy(out + k_strlen(out), mem_s);
k_strcpy(out + k_strlen(out), " KB\nTicks: ");
char tick_s[32]; k_itoa(proc->ticks, tick_s);
k_strcpy(out + k_strlen(out), tick_s);
k_strcpy(out + k_strlen(out), "\nIdle: ");
k_strcpy(out + k_strlen(out), proc->is_idle ? "1" : "0");
k_strcpy(out + k_strlen(out), "\n");
}
}
int len = k_strlen(out);
if (h->offset >= len) return 0;
int to_copy = len - h->offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + h->offset, to_copy);
h->offset += to_copy;
return to_copy;
}
int procfs_write(void *fs_private, void *handle, const void *buf, int size) {
procfs_handle_t *h = (procfs_handle_t*)handle;
if (!h || h->pid == 0xFFFFFFFF) return -1;
if (k_strcmp(h->type, "signal") == 0) {
char cmd[16];
int to_copy = size < 15 ? size : 15;
k_memcpy(cmd, buf, to_copy);
cmd[to_copy] = 0;
if (k_strcmp(cmd, "9") == 0 || k_strcmp(cmd, "kill") == 0) {
process_t *proc = process_get_by_pid(h->pid);
if (proc && proc->pid != 0) {
process_terminate(proc);
return size;
}
}
}
return -1;
}
int procfs_readdir(void *fs_private, const char *path, vfs_dirent_t *entries, int max) {
if (path[0] == '/') path++;
if (path[0] == '\0') {
int out = 0;
k_strcpy(entries[out++].name, "version");
entries[out-1].is_directory = 0;
k_strcpy(entries[out++].name, "uptime");
entries[out-1].is_directory = 0;
k_strcpy(entries[out++].name, "cpuinfo");
entries[out-1].is_directory = 0;
k_strcpy(entries[out++].name, "meminfo");
entries[out-1].is_directory = 0;
k_strcpy(entries[out++].name, "devices");
entries[out-1].is_directory = 0;
extern process_t processes[];
for (int i = 0; i < 16 && out < max; i++) {
if (processes[i].pid != 0xFFFFFFFF) {
k_itoa(processes[i].pid, entries[out].name);
entries[out].is_directory = 1;
entries[out].size = 0;
out++;
}
}
return out;
}
if (path[0] >= '0' && path[0] <= '9') {
int out = 0;
k_strcpy(entries[out++].name, "name");
k_strcpy(entries[out++].name, "status");
k_strcpy(entries[out++].name, "cmdline");
k_strcpy(entries[out++].name, "signal");
for(int i=0; i<out; i++) entries[i].is_directory = 0;
return out;
}
return 0;
}
bool procfs_exists(void *fs_private, const char *path) {
if (path[0] == '/') path++;
if (path[0] == '\0') return true;
if (path[0] >= '0' && path[0] <= '9') {
char pid_str[16];
int i = 0;
while (path[i] && path[i] != '/' && i < 15) {
pid_str[i] = path[i];
i++;
}
pid_str[i] = 0;
uint32_t pid = k_atoi(pid_str);
if (process_get_by_pid(pid)) return true;
}
if (k_strcmp(path, "version") == 0 || k_strcmp(path, "uptime") == 0) return true;
if (k_strcmp(path, "cpuinfo") == 0 || k_strcmp(path, "meminfo") == 0) return true;
if (k_strcmp(path, "devices") == 0) return true;
return false;
}
bool procfs_is_dir(void *fs_private, const char *path) {
if (path[0] == '/') path++;
if (path[0] == '\0') return true;
if (path[0] >= '0' && path[0] <= '9') {
int i = 0;
while (path[i] && path[i] != '/') i++;
if (path[i] == '\0') return true;
return false;
}
return false;
}
vfs_fs_ops_t procfs_ops = {
.open = procfs_open,
.close = procfs_close,
.read = procfs_read,
.write = procfs_write,
.readdir = procfs_readdir,
.exists = procfs_exists,
.is_dir = procfs_is_dir
};
vfs_fs_ops_t* procfs_get_ops(void) {
return &procfs_ops;
}

8
src/fs/procfs.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef PROCFS_H
#define PROCFS_H
#include "vfs.h"
vfs_fs_ops_t* procfs_get_ops(void);
#endif

181
src/fs/sysfs.c Normal file
View File

@@ -0,0 +1,181 @@
#include "vfs.h"
#include "../sys/kernel_subsystem.h"
#include "memory_manager.h"
#include "core/kutils.h"
typedef struct {
kernel_subsystem_t *sub;
subsystem_file_t *file;
int offset;
} sysfs_handle_t;
static void* sysfs_open(void *fs_private, const char *path, const char *mode) {
if (path[0] == '/') path++;
if (path[0] == '\0') return NULL;
kernel_subsystem_t *sub = NULL;
int last_slash = -1;
for (int j = 0; path[j]; j++) if (path[j] == '/') last_slash = j;
if (last_slash != -1) {
char prefix[64];
k_memcpy(prefix, path, last_slash);
prefix[last_slash] = 0;
sub = subsystem_get_by_name(prefix);
if (sub) {
const char *filename = path + last_slash + 1;
for (int j = 0; j < sub->file_count; j++) {
if (k_strcmp(sub->files[j].name, filename) == 0) {
sysfs_handle_t *h = (sysfs_handle_t*)kmalloc(sizeof(sysfs_handle_t));
h->sub = sub;
h->file = &sub->files[j];
h->offset = 0;
return h;
}
}
}
}
return NULL;
}
static void sysfs_close(void *fs_private, void *handle) {
if (handle) kfree(handle);
}
static int sysfs_read(void *fs_private, void *handle, void *buf, int size) {
sysfs_handle_t *h = (sysfs_handle_t*)handle;
if (!h || !h->file || !h->file->read) return -1;
int bytes = h->file->read((char*)buf, size, h->offset);
if (bytes > 0) h->offset += bytes;
return bytes;
}
static int sysfs_write(void *fs_private, void *handle, const void *buf, int size) {
sysfs_handle_t *h = (sysfs_handle_t*)handle;
if (!h || !h->file || !h->file->write) return -1;
int bytes = h->file->write((const char*)buf, size, h->offset);
if (bytes > 0) h->offset += bytes;
return bytes;
}
static int sysfs_readdir(void *fs_private, const char *path, vfs_dirent_t *entries, int max) {
if (path[0] == '/') path++;
kernel_subsystem_t *exact_sub = subsystem_get_by_name(path);
int out = 0;
if (exact_sub) {
for (int i = 0; i < exact_sub->file_count && out < max; i++) {
k_strcpy(entries[out].name, exact_sub->files[i].name);
entries[out].is_directory = 0;
entries[out].size = 0;
out++;
}
}
int count = subsystem_get_count();
int path_len = k_strlen(path);
for (int i = 0; i < count && out < max; i++) {
kernel_subsystem_t *s = subsystem_get_by_index(i);
if (path_len == 0 || (k_strlen(s->name) > path_len && k_strncmp(s->name, path, path_len) == 0 && s->name[path_len] == '/')) {
const char *sub_path = s->name + (path_len ? path_len + 1 : 0);
char comp[64];
int j = 0;
while (sub_path[j] && sub_path[j] != '/' && j < 63) {
comp[j] = sub_path[j];
j++;
}
comp[j] = 0;
if (comp[0] == '\0') continue;
bool found = false;
for (int k = 0; k < out; k++) {
if (k_strcmp(entries[k].name, comp) == 0) {
found = true;
break;
}
}
if (!found) {
k_strcpy(entries[out].name, comp);
entries[out].is_directory = 1;
entries[out].size = 0;
out++;
}
}
}
return out;
}
static bool sysfs_exists(void *fs_private, const char *path) {
if (path[0] == '/') path++;
if (path[0] == '\0') return true;
if (subsystem_get_by_name(path)) return true;
// File check
int last_slash = -1;
for (int j = 0; path[j]; j++) if (path[j] == '/') last_slash = j;
if (last_slash != -1) {
char prefix[64];
k_memcpy(prefix, path, last_slash);
prefix[last_slash] = 0;
kernel_subsystem_t *sub = subsystem_get_by_name(prefix);
if (sub) {
const char *filename = path + last_slash + 1;
for (int j = 0; j < sub->file_count; j++) {
if (k_strcmp(sub->files[j].name, filename) == 0) return true;
}
}
}
int count = subsystem_get_count();
int path_len = k_strlen(path);
for (int i = 0; i < count; i++) {
kernel_subsystem_t *s = subsystem_get_by_index(i);
if (k_strlen(s->name) > path_len && k_strncmp(s->name, path, path_len) == 0 && s->name[path_len] == '/') return true;
}
return false;
}
static bool sysfs_is_dir(void *fs_private, const char *path) {
if (path[0] == '/') path++;
if (path[0] == '\0') return true;
int last_slash = -1;
for (int j = 0; path[j]; j++) if (path[j] == '/') last_slash = j;
if (last_slash != -1) {
char prefix[64];
k_memcpy(prefix, path, last_slash);
prefix[last_slash] = 0;
kernel_subsystem_t *sub = subsystem_get_by_name(prefix);
if (sub) {
const char *filename = path + last_slash + 1;
for (int j = 0; j < sub->file_count; j++) {
if (k_strcmp(sub->files[j].name, filename) == 0) return false;
}
}
}
return sysfs_exists(fs_private, path);
}
vfs_fs_ops_t sysfs_ops = {
.open = sysfs_open,
.close = sysfs_close,
.read = sysfs_read,
.write = sysfs_write,
.readdir = sysfs_readdir,
.exists = sysfs_exists,
.is_dir = sysfs_is_dir
};
vfs_fs_ops_t* sysfs_get_ops(void) {
return &sysfs_ops;
}

8
src/fs/sysfs.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef SYSFS_H
#define SYSFS_H
#include "vfs.h"
vfs_fs_ops_t* sysfs_get_ops(void);
#endif

816
src/fs/vfs.c Normal file
View File

@@ -0,0 +1,816 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "vfs.h"
#include "memory_manager.h"
#include "spinlock.h"
#include <stddef.h>
#include "disk.h"
#include "process.h"
static vfs_mount_t mounts[VFS_MAX_MOUNTS];
static int mount_count = 0;
static vfs_file_t open_files[VFS_MAX_OPEN_FILES];
static spinlock_t vfs_lock = SPINLOCK_INIT;
extern void serial_write(const char *str);
extern void serial_write_num(uint64_t num);
static int vfs_strlen(const char *s) {
int n = 0;
while (s[n]) n++;
return n;
}
static void vfs_strcpy(char *d, const char *s) {
while ((*d++ = *s++));
}
static int vfs_strcmp(const char *a, const char *b) {
while (*a && *a == *b) { a++; b++; }
return (unsigned char)*a - (unsigned char)*b;
}
static int vfs_strncmp(const char *a, const char *b, int n) {
for (int i = 0; i < n; i++) {
if (a[i] != b[i]) return (unsigned char)a[i] - (unsigned char)b[i];
if (!a[i]) return 0;
}
return 0;
}
static bool vfs_starts_with(const char *str, const char *prefix) {
while (*prefix) {
if (*str++ != *prefix++) return false;
}
return true;
}
static bool vfs_path_is_parent(const char *parent, const char *child) {
int plen = vfs_strlen(parent);
if (vfs_strncmp(parent, child, plen) != 0) return false;
if (child[plen] == '\0') return true;
if (child[plen] == '/') return true;
if (plen == 1 && parent[0] == '/') return true;
return false;
}
void vfs_normalize_path(const char *cwd, const char *path, char *normalized) {
char parts[32][64]; // Reduced size to save stack, 64 is enough for most names
int depth = 0;
int i = 0;
// Handle relative path by starting with CWD
if (path[0] != '/' && cwd) {
int ci = 0;
if (cwd[0] == '/') ci = 1;
while (cwd[ci]) {
if (cwd[ci] == '/') { ci++; continue; }
int j = 0;
while (cwd[ci] && cwd[ci] != '/' && j < 63) {
parts[depth][j++] = cwd[ci++];
}
parts[depth][j] = 0;
if (j > 0) depth++;
if (depth >= 32) break;
if (cwd[ci] == '/') ci++;
}
}
if (path[0] == '/') i = 1;
while (path[i]) {
if (path[i] == '/') { i++; continue; }
int j = 0;
while (path[i] && path[i] != '/' && j < 63) {
parts[depth][j++] = path[i++];
}
parts[depth][j] = 0;
if (parts[depth][0] == '.' && parts[depth][1] == 0) {
// "." skip
} else if (parts[depth][0] == '.' && parts[depth][1] == '.' && parts[depth][2] == 0) {
// ".." pop
if (depth > 0) depth--;
} else {
if (j > 0) {
depth++;
if (depth >= 32) break;
}
}
if (path[i] == '/') i++;
}
normalized[0] = '/';
int pos = 1;
for (int k = 0; k < depth; k++) {
int l = 0;
while (parts[k][l] && pos < VFS_MAX_PATH - 2) {
normalized[pos++] = parts[k][l++];
}
if (k < depth - 1) normalized[pos++] = '/';
}
normalized[pos] = 0;
if (pos == 1 && normalized[0] == '/') {
normalized[1] = 0;
}
}
static vfs_mount_t* vfs_resolve_mount(const char *path, const char **rel_path_out) {
vfs_mount_t *best = NULL;
int best_len = -1;
for (int i = 0; i < mount_count; i++) {
if (!mounts[i].active) continue;
int mlen = mounts[i].path_len;
if (mlen == 1 && mounts[i].path[0] == '/') {
if (best_len < 1) {
best = &mounts[i];
best_len = 1;
}
continue;
}
if (vfs_strncmp(path, mounts[i].path, mlen) == 0) {
if (path[mlen] == '/' || path[mlen] == '\0') {
if (mlen > best_len) {
best = &mounts[i];
best_len = mlen;
}
}
}
}
if (best && rel_path_out) {
const char *rel = path + best_len;
while (*rel == '/') rel++;
*rel_path_out = rel;
}
return best;
}
static vfs_file_t* vfs_alloc_file(void) {
for (int i = 0; i < VFS_MAX_OPEN_FILES; i++) {
if (!open_files[i].valid) {
open_files[i].valid = true;
open_files[i].fs_handle = NULL;
open_files[i].mount = NULL;
open_files[i].position = 0;
open_files[i].is_device = false;
return &open_files[i];
}
}
return NULL;
}
static void vfs_free_file(vfs_file_t *f) {
if (f) {
f->valid = false;
f->fs_handle = NULL;
f->mount = NULL;
f->position = 0;
f->is_device = false;
}
}
void vfs_init(void) {
for (int i = 0; i < VFS_MAX_MOUNTS; i++) {
mounts[i].active = false;
}
for (int i = 0; i < VFS_MAX_OPEN_FILES; i++) {
open_files[i].valid = false;
}
mount_count = 0;
serial_write("[VFS] Virtual File System initialized\n");
}
// ===============
// Mount / Unmount
// ===============
bool vfs_mount(const char *mount_path, const char *device, const char *fs_type,
vfs_fs_ops_t *ops, void *fs_private) {
uint64_t flags = spinlock_acquire_irqsave(&vfs_lock);
if (mount_count >= VFS_MAX_MOUNTS) {
spinlock_release_irqrestore(&vfs_lock, flags);
serial_write("[VFS] ERROR: Mount table full\n");
return false;
}
for (int i = 0; i < mount_count; i++) {
if (mounts[i].active && vfs_strcmp(mounts[i].path, mount_path) == 0) {
spinlock_release_irqrestore(&vfs_lock, flags);
serial_write("[VFS] ERROR: Mount point already in use: ");
serial_write(mount_path);
serial_write("\n");
return false;
}
}
vfs_mount_t *m = &mounts[mount_count];
vfs_strcpy(m->path, mount_path);
m->path_len = vfs_strlen(mount_path);
m->ops = ops;
m->fs_private = fs_private;
vfs_strcpy(m->device, device ? device : "none");
vfs_strcpy(m->fs_type, fs_type ? fs_type : "unknown");
m->active = true;
mount_count++;
spinlock_release_irqrestore(&vfs_lock, flags);
serial_write("[VFS] Mounted ");
serial_write(fs_type);
serial_write(" (");
serial_write(device ? device : "none");
serial_write(") at ");
serial_write(mount_path);
serial_write("\n");
return true;
}
bool vfs_umount(const char *mount_path) {
uint64_t flags = spinlock_acquire_irqsave(&vfs_lock);
for (int i = 0; i < mount_count; i++) {
if (mounts[i].active && vfs_strcmp(mounts[i].path, mount_path) == 0) {
for (int j = 0; j < VFS_MAX_OPEN_FILES; j++) {
if (open_files[j].valid && open_files[j].mount == &mounts[i]) {
if (mounts[i].ops->close) {
mounts[i].ops->close(mounts[i].fs_private, open_files[j].fs_handle);
}
vfs_free_file(&open_files[j]);
}
}
serial_write("[VFS] Unmounted ");
serial_write(mounts[i].path);
serial_write("\n");
mounts[i].active = false;
// Compact array
for (int k = i; k < mount_count - 1; k++) {
mounts[k] = mounts[k + 1];
}
mount_count--;
spinlock_release_irqrestore(&vfs_lock, flags);
return true;
}
}
spinlock_release_irqrestore(&vfs_lock, flags);
return false;
}
// ==============
// File Operations
// ==============
vfs_file_t* vfs_open(const char *path, const char *mode) {
if (!path || !mode) return NULL;
char normalized[VFS_MAX_PATH];
process_t *proc = process_get_current();
vfs_normalize_path(proc ? proc->cwd : "/", path, normalized);
uint64_t flags = spinlock_acquire_irqsave(&vfs_lock);
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
// Fallback for block devices (/dev/sda etc)
if (vfs_starts_with(normalized, "/dev/")) {
const char *devname = normalized + 5;
Disk *d = disk_get_by_name(devname);
if (d && (!mount || mount->path_len == 1)) {
vfs_file_t *vf = vfs_alloc_file();
if (vf) {
vf->mount = &mounts[0];
vf->fs_handle = (void*)d;
vf->is_device = true;
vf->position = 0;
spinlock_release_irqrestore(&vfs_lock, flags);
return vf;
}
}
}
if (!mount || !mount->ops->open) {
spinlock_release_irqrestore(&vfs_lock, flags);
return NULL;
}
if (!rel_path || rel_path[0] == '\0') {
rel_path = "/";
}
vfs_file_t *vf = vfs_alloc_file();
if (!vf) {
spinlock_release_irqrestore(&vfs_lock, flags);
serial_write("[VFS] ERROR: No free file handles\n");
return NULL;
}
vf->mount = mount;
spinlock_release_irqrestore(&vfs_lock, flags);
void *fs_handle = mount->ops->open(mount->fs_private, rel_path, mode);
if (!fs_handle) {
flags = spinlock_acquire_irqsave(&vfs_lock);
vfs_free_file(vf);
spinlock_release_irqrestore(&vfs_lock, flags);
return NULL;
}
vf->fs_handle = fs_handle;
return vf;
}
void vfs_close(vfs_file_t *file) {
if (!file || !file->valid) return;
vfs_mount_t *mount = file->mount;
if (mount && mount->ops->close) {
mount->ops->close(mount->fs_private, file->fs_handle);
}
uint64_t flags = spinlock_acquire_irqsave(&vfs_lock);
vfs_free_file(file);
spinlock_release_irqrestore(&vfs_lock, flags);
}
int vfs_read(vfs_file_t *file, void *buf, int size) {
if (!file || !file->valid || !file->mount) return -1;
if (file->is_device) {
Disk *d = (Disk*)file->fs_handle;
if (!d) return -1;
uint32_t total_read = 0;
uint32_t sector = (uint32_t)(file->position / 512);
uint32_t offset = (uint32_t)(file->position % 512);
uint8_t sector_buf[512];
while (total_read < (uint32_t)size) {
if (sector >= d->total_sectors) break;
if (d->read_sector(d, sector, sector_buf) != 0) break;
uint32_t to_copy = 512 - offset;
if (to_copy > (uint32_t)size - total_read) to_copy = (uint32_t)size - total_read;
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy((uint8_t*)buf + total_read, sector_buf + offset, to_copy);
total_read += to_copy;
file->position += to_copy;
sector++;
offset = 0;
}
return (int)total_read;
}
if (!file->mount->ops->read) return -1;
int ret = file->mount->ops->read(file->mount->fs_private, file->fs_handle, buf, size);
if (ret > 0) file->position += ret;
return ret;
}
int vfs_write(vfs_file_t *file, const void *buf, int size) {
if (!file || !file->valid || !file->mount) return -1;
if (!file->mount->ops->write) return -1;
return file->mount->ops->write(file->mount->fs_private, file->fs_handle, buf, size);
}
int vfs_seek(vfs_file_t *file, int offset, int whence) {
if (!file || !file->valid || !file->mount) return -1;
if (file->is_device) {
Disk *d = (Disk*)file->fs_handle;
if (!d) return -1;
uint64_t new_pos = file->position;
if (whence == 0) new_pos = (uint64_t)offset; // SET
else if (whence == 1) new_pos += (uint64_t)offset; // CUR
else if (whence == 2) new_pos = (uint64_t)(d->total_sectors * 512 + offset); // END
if (new_pos > (uint64_t)d->total_sectors * 512) new_pos = (uint64_t)d->total_sectors * 512;
file->position = new_pos;
return 0;
}
if (!file->mount->ops->seek) return -1;
int ret = file->mount->ops->seek(file->mount->fs_private, file->fs_handle, offset, whence);
if (ret == 0) {
// Sync position back from driver if possible
if (file->mount->ops->get_position) {
file->position = file->mount->ops->get_position(file->fs_handle);
} else {
// Manual sync if driver doesn't support get_position but seek succeeded
if (whence == 0) file->position = offset;
else if (whence == 1) file->position += offset;
}
}
return ret;
}
uint32_t vfs_file_position(vfs_file_t *file) {
if (!file || !file->valid || !file->mount) return 0;
if (file->is_device) return (uint32_t)file->position;
if (!file->mount->ops->get_position) return 0;
return file->mount->ops->get_position(file->fs_handle);
}
uint32_t vfs_file_size(vfs_file_t *file) {
if (!file || !file->valid || !file->mount) return 0;
if (file->is_device) {
Disk *d = (Disk*)file->fs_handle;
return d ? d->total_sectors * 512 : 0;
}
if (!file->mount->ops->get_size) return 0;
return file->mount->ops->get_size(file->fs_handle);
}
int vfs_list_directory(const char *path, vfs_dirent_t *entries, int max) {
if (!path || !entries) return -1;
char normalized[VFS_MAX_PATH];
vfs_normalize_path("/", path, normalized);
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
int count = 0;
if (mount && mount->ops->readdir) {
if (!rel_path || rel_path[0] == '\0') rel_path = "/";
count = mount->ops->readdir(mount->fs_private, rel_path, entries, max);
if (count < 0) count = 0;
}
uint64_t v_flags = spinlock_acquire_irqsave(&vfs_lock);
for (int i = 0; i < mount_count; i++) {
if (!mounts[i].active) continue;
if (vfs_strcmp(mounts[i].path, normalized) == 0) continue;
if (vfs_path_is_parent(normalized, mounts[i].path)) {
const char *sub = mounts[i].path + vfs_strlen(normalized);
if (*sub == '/') sub++;
if (*sub != '\0') {
char comp[VFS_MAX_NAME];
int j = 0;
while (sub[j] && sub[j] != '/' && j < VFS_MAX_NAME - 1) {
comp[j] = sub[j];
j++;
}
comp[j] = 0;
bool found = false;
for (int k = 0; k < count; k++) {
if (vfs_strcmp(entries[k].name, comp) == 0) {
found = true;
break;
}
}
if (!found && count < max) {
vfs_strcpy(entries[count].name, comp);
entries[count].is_directory = 1;
entries[count].size = 0;
entries[count].start_cluster = 0;
count++;
}
}
}
}
spinlock_release_irqrestore(&vfs_lock, v_flags);
// Special case: Ensure "dev", "sys", "proc" are visible in "/"
if (vfs_strcmp(normalized, "/") == 0) {
const char *virtual_dirs[] = {"dev", "sys", "proc"};
for (int v = 0; v < 3; v++) {
bool found = false;
for (int i = 0; i < count; i++) {
if (vfs_strcmp(entries[i].name, virtual_dirs[v]) == 0) {
found = true;
break;
}
}
if (!found && count < max) {
vfs_strcpy(entries[count].name, virtual_dirs[v]);
entries[count].is_directory = 1;
entries[count].size = 0;
entries[count].start_cluster = 0;
count++;
}
}
}
// Special case: /dev listing for block devices
if (vfs_strcmp(normalized, "/dev") == 0) {
int dcount = disk_get_count();
for (int i = 0; i < dcount && count < max; i++) {
Disk *d = disk_get_by_index(i);
if (d) {
bool found = false;
for (int k = 0; k < count; k++) {
if (vfs_strcmp(entries[k].name, d->devname) == 0) {
found = true;
break;
}
}
if (!found) {
vfs_strcpy(entries[count].name, d->devname);
entries[count].size = d->total_sectors * 512;
entries[count].is_directory = 0;
entries[count].start_cluster = 0;
entries[count].write_date = 0;
entries[count].write_time = 0;
count++;
}
}
}
}
return count;
}
bool vfs_mkdir(const char *path) {
if (!path) return false;
char normalized[VFS_MAX_PATH];
vfs_normalize_path("/", path, normalized);
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
if (vfs_starts_with(normalized, "/dev/")) {
if (!mount || !rel_path || rel_path[0] == '\0') {
return false;
}
}
if (!mount || !mount->ops->mkdir) return false;
return mount->ops->mkdir(mount->fs_private, rel_path);
}
bool vfs_rmdir(const char *path) {
if (!path) return false;
char normalized[VFS_MAX_PATH];
vfs_normalize_path("/", path, normalized);
if (normalized[0] == '/' && normalized[1] == '\0') return false;
if (vfs_strcmp(normalized, "/dev") == 0) return false;
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
if (vfs_starts_with(normalized, "/dev/")) {
if (!mount || !rel_path || rel_path[0] == '\0') {
return false;
}
}
if (!mount || !mount->ops->rmdir) return false;
return mount->ops->rmdir(mount->fs_private, rel_path);
}
bool vfs_delete(const char *path) {
if (!path) return false;
char normalized[VFS_MAX_PATH];
vfs_normalize_path("/", path, normalized);
if (normalized[0] == '/' && normalized[1] == '\0') return false;
if (vfs_strcmp(normalized, "/dev") == 0) return false;
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
if (vfs_starts_with(normalized, "/dev/")) {
if (!mount || !rel_path || rel_path[0] == '\0') {
return false;
}
}
if (!mount || !mount->ops->unlink) return false;
return mount->ops->unlink(mount->fs_private, rel_path);
}
bool vfs_rename(const char *old_path, const char *new_path) {
if (!old_path || !new_path) return false;
char norm_old[VFS_MAX_PATH], norm_new[VFS_MAX_PATH];
vfs_normalize_path("/", old_path, norm_old);
vfs_normalize_path("/", new_path, norm_new);
const char *rel_old = NULL, *rel_new = NULL;
vfs_mount_t *mount_old = vfs_resolve_mount(norm_old, &rel_old);
vfs_mount_t *mount_new = vfs_resolve_mount(norm_new, &rel_new);
if (!mount_old || mount_old != mount_new) return false;
if (!mount_old->ops->rename) return false;
if (!rel_old || rel_old[0] == '\0') return false;
if (!rel_new || rel_new[0] == '\0') return false;
return mount_old->ops->rename(mount_old->fs_private, rel_old, rel_new);
}
bool vfs_exists(const char *path) {
if (!path) return false;
char normalized[VFS_MAX_PATH];
vfs_normalize_path("/", path, normalized);
if (normalized[0] == '/' && normalized[1] == '\0') return true;
uint64_t flags_vfs = spinlock_acquire_irqsave(&vfs_lock);
for (int i = 0; i < mount_count; i++) {
if (mounts[i].active && vfs_starts_with(mounts[i].path, normalized)) {
spinlock_release_irqrestore(&vfs_lock, flags_vfs);
return true;
}
}
spinlock_release_irqrestore(&vfs_lock, flags_vfs);
if (vfs_strcmp(normalized, "/dev") == 0 ||
vfs_strcmp(normalized, "/sys") == 0 ||
vfs_strcmp(normalized, "/proc") == 0) return true;
if (vfs_starts_with(normalized, "/dev/")) {
const char *dev = normalized + 5;
if (disk_get_by_name(dev)) return true;
}
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
if (!mount || !mount->ops->exists) return false;
if (!rel_path || rel_path[0] == '\0') return true;
return mount->ops->exists(mount->fs_private, rel_path);
}
bool vfs_is_directory(const char *path) {
if (!path) return false;
char normalized[VFS_MAX_PATH];
vfs_normalize_path("/", path, normalized);
if (normalized[0] == '/' && normalized[1] == '\0') return true;
uint64_t flags_vfs = spinlock_acquire_irqsave(&vfs_lock);
for (int i = 0; i < mount_count; i++) {
if (mounts[i].active && vfs_path_is_parent(normalized, mounts[i].path)) {
if (vfs_strcmp(mounts[i].path, normalized) == 0) {
spinlock_release_irqrestore(&vfs_lock, flags_vfs);
return true;
}
// If normalized is a parent of a mount, it's a virtual directory
spinlock_release_irqrestore(&vfs_lock, flags_vfs);
return true;
}
}
spinlock_release_irqrestore(&vfs_lock, flags_vfs);
if (vfs_strcmp(normalized, "/dev") == 0 ||
vfs_strcmp(normalized, "/sys") == 0 ||
vfs_strcmp(normalized, "/proc") == 0) return true;
if (vfs_starts_with(normalized, "/dev/")) {
const char *dev = normalized + 5;
Disk *d = disk_get_by_name(dev);
if (d) return false;
}
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
if (!mount) return false;
if (!rel_path || rel_path[0] == '\0') return true;
if (!mount->ops->is_dir) return false;
return mount->ops->is_dir(mount->fs_private, rel_path);
}
int vfs_get_info(const char *path, vfs_dirent_t *info) {
if (!path || !info) return -1;
char normalized[VFS_MAX_PATH];
vfs_normalize_path("/", path, normalized);
if (normalized[0] == '/' && normalized[1] == '\0') {
vfs_strcpy(info->name, "/");
info->size = 0;
info->is_directory = 1;
info->start_cluster = 0;
info->write_date = 0;
info->write_time = 0;
return 0;
}
if (vfs_strcmp(normalized, "/dev") == 0 ||
vfs_strcmp(normalized, "/sys") == 0 ||
vfs_strcmp(normalized, "/proc") == 0) {
const char *name = normalized + 1;
vfs_strcpy(info->name, name);
info->size = 0;
info->is_directory = 1;
info->start_cluster = 0;
info->write_date = 0;
info->write_time = 0;
return 0;
}
uint64_t flags_vfs = spinlock_acquire_irqsave(&vfs_lock);
for (int i = 0; i < mount_count; i++) {
if (mounts[i].active && vfs_path_is_parent(normalized, mounts[i].path)) {
if (vfs_strcmp(mounts[i].path, normalized) != 0) {
const char *p = normalized + vfs_strlen(normalized);
while (p > normalized && *(p-1) != '/') p--;
vfs_strcpy(info->name, p);
info->size = 0;
info->is_directory = 1;
info->start_cluster = 0;
info->write_date = 0;
info->write_time = 0;
spinlock_release_irqrestore(&vfs_lock, flags_vfs);
return 0;
}
}
}
spinlock_release_irqrestore(&vfs_lock, flags_vfs);
// Device check
if (vfs_starts_with(normalized, "/dev/")) {
const char *dev = normalized + 5;
Disk *d = disk_get_by_name(dev);
if (d) {
vfs_strcpy(info->name, d->devname);
info->size = d->total_sectors * 512;
info->is_directory = 0;
info->start_cluster = 0;
info->write_date = 0;
info->write_time = 0;
return 0;
}
}
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
if (!mount || !mount->ops->get_info) return -1;
if (!rel_path || rel_path[0] == '\0') {
// Info about mount root
vfs_strcpy(info->name, mount->device);
info->size = 0;
info->is_directory = 1;
info->start_cluster = 0;
info->write_date = 0;
info->write_time = 0;
return 0;
}
return mount->ops->get_info(mount->fs_private, rel_path, info);
}
int vfs_get_mount_count(void) {
return mount_count;
}
vfs_mount_t* vfs_get_mount(int index) {
if (index < 0 || index >= mount_count) return NULL;
if (!mounts[index].active) return NULL;
return &mounts[index];
}
void vfs_automount_partition(const char *devname) {
char mount_path[64] = "/mnt/";
int i = 5;
const char *d = devname;
while (*d && i < 62) mount_path[i++] = *d++;
mount_path[i] = 0;
serial_write("[VFS] Auto-mount requested for ");
serial_write(devname);
serial_write(" at ");
serial_write(mount_path);
serial_write("\n");
}

117
src/fs/vfs.h Normal file
View File

@@ -0,0 +1,117 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef VFS_H
#define VFS_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#define VFS_MAX_PATH 1024
#define VFS_MAX_NAME 256
#define VFS_MAX_MOUNTS 16
#define VFS_MAX_OPEN_FILES 64
// Forward declarations
typedef struct vfs_mount vfs_mount_t;
typedef struct vfs_file vfs_file_t;
// Directory entry for readdir
typedef struct vfs_dirent {
char name[VFS_MAX_NAME];
uint32_t size;
uint8_t is_directory;
uint32_t start_cluster;
uint16_t write_date;
uint16_t write_time;
} vfs_dirent_t;
// Filesystem operations — implemented by each filesystem type
typedef struct vfs_fs_ops {
// File operations — return opaque FS handle
void* (*open)(void *fs_private, const char *rel_path, const char *mode);
void (*close)(void *fs_private, void *file_handle);
int (*read)(void *fs_private, void *file_handle, void *buf, int size);
int (*write)(void *fs_private, void *file_handle, const void *buf, int size);
int (*seek)(void *fs_private, void *file_handle, int offset, int whence);
// Directory operations
int (*readdir)(void *fs_private, const char *rel_path, vfs_dirent_t *entries, int max);
bool (*mkdir)(void *fs_private, const char *rel_path);
bool (*rmdir)(void *fs_private, const char *rel_path);
bool (*unlink)(void *fs_private, const char *rel_path);
bool (*rename)(void *fs_private, const char *old_path, const char *new_path);
// Query operations
bool (*exists)(void *fs_private, const char *rel_path);
bool (*is_dir)(void *fs_private, const char *rel_path);
int (*get_info)(void *fs_private, const char *rel_path, vfs_dirent_t *info);
// Handle info (for backward compat with syscall position/size queries)
uint32_t (*get_position)(void *file_handle);
uint32_t (*get_size)(void *file_handle);
} vfs_fs_ops_t;
// VFS file handle
struct vfs_file {
void *fs_handle; // FS-specific handle (e.g. FAT32_FileHandle*)
vfs_mount_t *mount; // Mount this file belongs to
bool valid;
uint64_t position; // Current Seek Position (for raw devices/fallbacks)
bool is_device; // Is this a raw device handle?
};
// Mount entry
struct vfs_mount {
char path[256]; // Mount point (e.g. "/", "/mnt/sda1")
int path_len;
vfs_fs_ops_t *ops;
void *fs_private; // FS-specific data (e.g. FAT32_Volume*)
char device[32]; // Device name (e.g. "ramfs", "sda1")
char fs_type[16]; // "ramfs", "fat32"
bool active;
};
// Initialization
void vfs_init(void);
// Mount/unmount
bool vfs_mount(const char *mount_path, const char *device, const char *fs_type,
vfs_fs_ops_t *ops, void *fs_private);
bool vfs_umount(const char *mount_path);
// File operations
vfs_file_t* vfs_open(const char *path, const char *mode);
void vfs_close(vfs_file_t *file);
int vfs_read(vfs_file_t *file, void *buf, int size);
int vfs_write(vfs_file_t *file, const void *buf, int size);
int vfs_seek(vfs_file_t *file, int offset, int whence);
// Directory operations
int vfs_list_directory(const char *path, vfs_dirent_t *entries, int max);
bool vfs_mkdir(const char *path);
bool vfs_rmdir(const char *path);
bool vfs_delete(const char *path);
bool vfs_rename(const char *old_path, const char *new_path);
// Query operations
bool vfs_exists(const char *path);
bool vfs_is_directory(const char *path);
int vfs_get_info(const char *path, vfs_dirent_t *info);
// Mount enumeration
int vfs_get_mount_count(void);
vfs_mount_t* vfs_get_mount(int index);
// Block device auto-mount
void vfs_automount_partition(const char *devname);
// Path utilities
void vfs_normalize_path(const char *cwd, const char *path, char *normalized);
// Backward compat: get position/size from vfs_file
uint32_t vfs_file_position(vfs_file_t *file);
uint32_t vfs_file_size(vfs_file_t *file);
#endif

View File

@@ -0,0 +1,73 @@
#include "kernel_subsystem.h"
#include "memory_manager.h"
#include "spinlock.h"
static kernel_subsystem_t subsystems[MAX_SUBSYSTEMS];
static int subsystem_count = 0;
static spinlock_t sub_lock = SPINLOCK_INIT;
extern void mem_memset(void *dest, int val, size_t len);
extern void mem_memcpy(void *dest, const void *src, size_t len);
static void sub_strcpy(char *dest, const char *src) {
while (*src) *dest++ = *src++;
*dest = 0;
}
static int sub_strcmp(const char *a, const char *b) {
while (*a && *a == *b) { a++; b++; }
return (unsigned char)*a - (unsigned char)*b;
}
void subsystem_register(const char *name, kernel_subsystem_t **out_sub) {
uint64_t flags = spinlock_acquire_irqsave(&sub_lock);
if (subsystem_count >= MAX_SUBSYSTEMS) {
spinlock_release_irqrestore(&sub_lock, flags);
if (out_sub) *out_sub = NULL;
return;
}
// Check if already exists
for (int i = 0; i < subsystem_count; i++) {
if (sub_strcmp(subsystems[i].name, name) == 0) {
spinlock_release_irqrestore(&sub_lock, flags);
if (out_sub) *out_sub = &subsystems[i];
return;
}
}
kernel_subsystem_t *s = &subsystems[subsystem_count++];
mem_memset(s, 0, sizeof(kernel_subsystem_t));
sub_strcpy(s->name, name);
spinlock_release_irqrestore(&sub_lock, flags);
if (out_sub) *out_sub = s;
}
void subsystem_add_file(kernel_subsystem_t *sub, const char *name,
int (*read)(char*, int, int),
int (*write)(const char*, int, int)) {
if (!sub || sub->file_count >= MAX_SUBSYSTEM_FILES) return;
subsystem_file_t *f = &sub->files[sub->file_count++];
sub_strcpy(f->name, name);
f->read = read;
f->write = write;
}
kernel_subsystem_t* subsystem_get_by_name(const char *name) {
for (int i = 0; i < subsystem_count; i++) {
if (sub_strcmp(subsystems[i].name, name) == 0) return &subsystems[i];
}
return NULL;
}
int subsystem_get_count(void) {
return subsystem_count;
}
kernel_subsystem_t* subsystem_get_by_index(int index) {
if (index < 0 || index >= subsystem_count) return NULL;
return &subsystems[index];
}

View File

@@ -0,0 +1,32 @@
#ifndef KERNEL_SUBSYSTEM_H
#define KERNEL_SUBSYSTEM_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#define MAX_SUBSYSTEMS 16
#define MAX_SUBSYSTEM_FILES 32
typedef struct {
char name[64];
int (*read)(char *buffer, int size, int offset);
int (*write)(const char *buffer, int size, int offset);
} subsystem_file_t;
typedef struct {
char name[64];
subsystem_file_t files[MAX_SUBSYSTEM_FILES];
int file_count;
} kernel_subsystem_t;
void subsystem_register(const char *name, kernel_subsystem_t **out_sub);
void subsystem_add_file(kernel_subsystem_t *sub, const char *name,
int (*read)(char*, int, int),
int (*write)(const char*, int, int));
kernel_subsystem_t* subsystem_get_by_name(const char *name);
int subsystem_get_count(void);
kernel_subsystem_t* subsystem_get_by_index(int index);
#endif

29
src/sys/module_manager.c Normal file
View File

@@ -0,0 +1,29 @@
#include "module_manager.h"
#include "memory_manager.h"
#define MAX_MODULES 32
static kernel_module_t modules[MAX_MODULES];
static int module_count = 0;
static void mod_strcpy(char *dest, const char *src) {
while (*src) *dest++ = *src++;
*dest = 0;
}
void module_manager_register(const char *name, uint64_t addr, uint64_t size) {
if (module_count >= MAX_MODULES) return;
kernel_module_t *m = &modules[module_count++];
mod_strcpy(m->name, name);
m->address = addr;
m->size = size;
}
int module_manager_get_count(void) {
return module_count;
}
kernel_module_t* module_manager_get_index(int index) {
if (index < 0 || index >= module_count) return NULL;
return &modules[index];
}

17
src/sys/module_manager.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef MODULE_MANAGER_H
#define MODULE_MANAGER_H
#include <stdint.h>
#include <stddef.h>
typedef struct {
char name[64];
uint64_t address;
uint64_t size;
} kernel_module_t;
void module_manager_register(const char *name, uint64_t addr, uint64_t size);
int module_manager_get_count(void);
kernel_module_t* module_manager_get_index(int index);
#endif

View File

@@ -20,13 +20,12 @@ extern void serial_write(const char *str);
#define MAX_PROCESSES 16
#define MAX_CPUS_SCHED 32
process_t processes[MAX_PROCESSES] __attribute__((aligned(16)));
int process_count = 0;
static process_t* current_process[MAX_CPUS_SCHED] = {0}; // Per-CPU
static uint32_t next_pid = 0;
static void *free_kernel_stack_later = NULL;
static uint64_t free_pml4_later = 0;
static void *free_kernel_stack_later[MAX_CPUS_SCHED] = {0};
static uint64_t free_pml4_later[MAX_CPUS_SCHED] = {0};
static spinlock_t runqueue_lock = SPINLOCK_INIT;
static uint32_t next_cpu_assign = 1; // Round-robin CPU assignment (start from CPU 1)
static uint32_t next_cpu_assign = 1;
void process_init(void) {
for (int i = 0; i < MAX_PROCESSES; i++) {
@@ -34,9 +33,10 @@ void process_init(void) {
}
// Current kernel execution is PID 0
process_t *kernel_proc = &processes[process_count++];
process_t *kernel_proc = &processes[0];
kernel_proc->pid = next_pid++;
kernel_proc->is_user = false;
kernel_proc->is_idle = true;
// We don't have its RSP or PML4 yet, but it's already running.
// The timer interrupt will naturally capture its context on the first tick!
@@ -55,21 +55,39 @@ void process_init(void) {
kernel_proc->next = kernel_proc; // Circular linked list
kernel_proc->cpu_affinity = 0; // Kernel always on BSP
mem_memset(kernel_proc->cwd, 0, 1024);
kernel_proc->cwd[0] = '/';
current_process[0] = kernel_proc;
}
process_t* process_create(void (*entry_point)(void), bool is_user) {
uint64_t rflags = spinlock_acquire_irqsave(&runqueue_lock);
if (process_count >= MAX_PROCESSES) {
process_t *new_proc = NULL;
for (int i = 0; i < MAX_PROCESSES; i++) {
if (processes[i].pid == 0xFFFFFFFF) {
new_proc = &processes[i];
break;
}
}
if (!new_proc) {
spinlock_release_irqrestore(&runqueue_lock, rflags);
return NULL;
}
process_t *new_proc = &processes[process_count++];
new_proc->pid = next_pid++;
new_proc->is_user = is_user;
process_t *parent = process_get_current();
if (parent) {
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy(new_proc->cwd, parent->cwd, 1024);
} else {
mem_memset(new_proc->cwd, 0, 1024);
new_proc->cwd[0] = '/';
}
// 1. Setup Page Table
if (is_user) {
new_proc->pml4_phys = paging_create_user_pml4_phys();
@@ -163,9 +181,8 @@ process_t* process_create_elf(const char* filepath, const char* args_str) {
// Find an available slot
for (int i = 0; i < MAX_PROCESSES; i++) {
if (processes[i].pid == 0xFFFFFFFF || i >= process_count) {
if (processes[i].pid == 0xFFFFFFFF) {
new_proc = &processes[i];
if (i >= process_count) process_count = i + 1;
break;
}
}
@@ -376,21 +393,35 @@ process_t* process_get_current(void) {
}
uint64_t process_schedule(uint64_t current_rsp) {
if (free_kernel_stack_later) {
kfree(free_kernel_stack_later);
free_kernel_stack_later = NULL;
uint32_t my_cpu = smp_this_cpu_id();
uint64_t rflags = spinlock_acquire_irqsave(&runqueue_lock);
void *cleanup_stack = NULL;
uint64_t cleanup_pml4 = 0;
if (free_kernel_stack_later[my_cpu]) {
cleanup_stack = free_kernel_stack_later[my_cpu];
free_kernel_stack_later[my_cpu] = NULL;
}
if (free_pml4_later) {
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
paging_destroy_user_pml4_phys(free_pml4_later);
free_pml4_later = 0;
if (free_pml4_later[my_cpu]) {
cleanup_pml4 = free_pml4_later[my_cpu];
free_pml4_later[my_cpu] = 0;
}
uint32_t my_cpu = smp_this_cpu_id();
process_t *cur = current_process[my_cpu];
if (!cur || !cur->next || cur == cur->next)
if (!cur || !cur->next || cur == cur->next) {
spinlock_release_irqrestore(&runqueue_lock, rflags);
// Perform cleanup outside the lock
if (cleanup_stack) kfree(cleanup_stack);
if (cleanup_pml4) {
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
paging_destroy_user_pml4_phys(cleanup_pml4);
}
return current_rsp;
}
// Save context
cur->rsp = current_rsp;
@@ -412,11 +443,8 @@ uint64_t process_schedule(uint64_t current_rsp) {
next_proc = next_proc->next;
}
// If we didn't find a ready process for our CPU, stay on current (unless we are terminated)
if (next_proc->cpu_affinity != my_cpu || next_proc->pid == 0xFFFFFFFF) {
// Fallback to idle if current is terminated
if (cur && cur->pid == 0xFFFFFFFF) {
// Find the idle process for this CPU
for (int i = 0; i < MAX_PROCESSES; i++) {
if (processes[i].pid == 0 || (processes[i].cpu_affinity == my_cpu && processes[i].is_user == false)) {
next_proc = &processes[i];
@@ -424,18 +452,25 @@ uint64_t process_schedule(uint64_t current_rsp) {
}
}
} else {
spinlock_release_irqrestore(&runqueue_lock, rflags);
if (cleanup_stack) kfree(cleanup_stack);
if (cleanup_pml4) {
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
paging_destroy_user_pml4_phys(cleanup_pml4);
}
return current_rsp;
}
}
current_process[my_cpu] = next_proc;
// Update Kernel Stack for User Mode interrupts and System Calls
if (current_process[my_cpu]->is_user && current_process[my_cpu]->kernel_stack) {
tss_set_stack_cpu(my_cpu, current_process[my_cpu]->kernel_stack);
if (my_cpu == 0) {
extern uint64_t kernel_syscall_stack;
kernel_syscall_stack = current_process[my_cpu]->kernel_stack;
cpu_state_t *cpu_state = smp_get_cpu(my_cpu);
if (cpu_state) {
cpu_state->kernel_syscall_stack = current_process[my_cpu]->kernel_stack;
}
}
@@ -443,8 +478,16 @@ uint64_t process_schedule(uint64_t current_rsp) {
paging_switch_directory(current_process[my_cpu]->pml4_phys);
current_process[my_cpu]->ticks++;
uint64_t next_rsp = current_process[my_cpu]->rsp;
return current_process[my_cpu]->rsp;
spinlock_release_irqrestore(&runqueue_lock, rflags);
if (cleanup_stack) kfree(cleanup_stack);
if (cleanup_pml4) {
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
paging_destroy_user_pml4_phys(cleanup_pml4);
}
return next_rsp;
}
process_t* process_get_by_pid(uint32_t pid) {
@@ -600,25 +643,17 @@ uint64_t process_terminate_current(void) {
// 4. Load context for the NEXT process
if (current_process[my_cpu]->is_user && current_process[my_cpu]->kernel_stack) {
tss_set_stack_cpu(my_cpu, current_process[my_cpu]->kernel_stack);
if (my_cpu == 0) {
extern uint64_t kernel_syscall_stack;
kernel_syscall_stack = current_process[my_cpu]->kernel_stack;
cpu_state_t *cpu_state = smp_get_cpu(my_cpu);
if (cpu_state) {
cpu_state->kernel_syscall_stack = current_process[my_cpu]->kernel_stack;
}
}
paging_switch_directory(current_process[my_cpu]->pml4_phys);
// 5. Free memory
if (to_delete->user_stack_alloc) kfree(to_delete->user_stack_alloc);
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
if (to_delete->pml4_phys && to_delete->is_user) {
paging_destroy_user_pml4_phys(to_delete->pml4_phys);
}
to_delete->user_stack_alloc = NULL;
free_kernel_stack_later = to_delete->kernel_stack_alloc;
free_kernel_stack_later[my_cpu] = to_delete->kernel_stack_alloc;
to_delete->kernel_stack_alloc = NULL;
free_pml4_later[my_cpu] = to_delete->pml4_phys;
to_delete->pml4_phys = 0;
uint64_t next_rsp = current_process[my_cpu]->rsp;
@@ -666,4 +701,3 @@ process_t* process_get_by_ui_window(void *win) {
}
return NULL;
}

View File

@@ -52,7 +52,9 @@ typedef struct process {
uint64_t ticks;
uint64_t sleep_until;
size_t used_memory;
uint32_t cpu_affinity; // Which CPU this process runs on (0 = BSP)
uint32_t cpu_affinity;
bool is_idle;
char cwd[1024];
} __attribute__((aligned(16))) process_t;
typedef struct {
@@ -60,6 +62,7 @@ typedef struct {
char name[64];
uint64_t ticks;
size_t used_memory;
bool is_idle;
} ProcessInfo;
void process_init(void);
@@ -73,7 +76,7 @@ uint64_t process_terminate_current(void);
void process_terminate(process_t *proc);
process_t* process_get_by_pid(uint32_t pid);
// SMP: IPI handler for AP scheduling (called from ISR)
// SMP: IPI handler for AP scheduling
uint64_t sched_ipi_handler(registers_t *regs);
void process_push_gui_event(process_t *proc, gui_event_t *ev);

View File

@@ -10,6 +10,7 @@
#include "paging.h"
#include "process.h"
#include "work_queue.h"
#include "core/kutils.h"
extern void serial_write(const char *str);
extern void serial_write_num(uint32_t n);
@@ -18,6 +19,16 @@ extern void serial_write_hex(uint64_t n);
static cpu_state_t *cpu_states = NULL;
static uint32_t total_cpus = 0;
static uint32_t bsp_lapic_id = 0;
static cpu_state_t *bsp_cpu_state = NULL;
#define MSR_GS_BASE 0xC0000101
#define MSR_KERNEL_GS_BASE 0xC0000102
static inline void wrmsr(uint32_t msr, uint64_t value) {
uint32_t low = (uint32_t)value;
uint32_t high = (uint32_t)(value >> 32);
asm volatile("wrmsr" : : "c"(msr), "a"(low), "d"(high));
}
static uint32_t read_lapic_id(void) {
uint32_t eax, ebx, ecx, edx;
@@ -27,6 +38,12 @@ static uint32_t read_lapic_id(void) {
uint32_t smp_this_cpu_id(void) {
if (total_cpus <= 1) return 0;
// Use GS-based self-pointer to get the structure first
cpu_state_t *state;
asm volatile("movq %%gs:0, %0" : "=r"(state) : : "memory");
if (state) return state->cpu_id;
uint32_t lapic = read_lapic_id();
for (uint32_t i = 0; i < total_cpus; i++) {
if (cpu_states[i].lapic_id == lapic) return i;
@@ -68,13 +85,21 @@ static void ap_entry(struct limine_smp_info *info) {
extern void idt_load(void);
idt_load();
extern void syscall_init(void);
syscall_init();
uint64_t kernel_cr3 = paging_get_pml4_phys();
asm volatile("mov %0, %%cr3" : : "r"(kernel_cr3));
extern void lapic_enable(void);
lapic_enable();
cpu_states[my_id].self = &cpu_states[my_id];
cpu_states[my_id].online = true;
cpu_states[my_id].kernel_syscall_stack = cpu_states[my_id].kernel_stack;
wrmsr(MSR_GS_BASE, (uint64_t)&cpu_states[my_id]);
wrmsr(MSR_KERNEL_GS_BASE, (uint64_t)&cpu_states[my_id]);
serial_write("[SMP] AP ");
serial_write_num(my_id);
@@ -84,12 +109,30 @@ static void ap_entry(struct limine_smp_info *info) {
process_t *ap_idle = process_create(NULL, false);
ap_idle->cpu_affinity = my_id;
ap_idle->is_idle = true;
k_strcpy(ap_idle->name, "idle:");
char id_s[8]; k_itoa(my_id, id_s);
k_strcpy(ap_idle->name + 5, id_s);
process_set_current_for_cpu(my_id, ap_idle);
asm volatile("sti");
work_queue_drain_loop();
}
void smp_init_bsp(void) {
static cpu_state_t bsp_state_static = {0};
bsp_state_static.cpu_id = 0;
bsp_state_static.lapic_id = read_lapic_id();
bsp_state_static.self = &bsp_state_static;
bsp_state_static.online = true;
wrmsr(MSR_GS_BASE, (uint64_t)&bsp_state_static);
wrmsr(MSR_KERNEL_GS_BASE, (uint64_t)&bsp_state_static);
bsp_cpu_state = &bsp_state_static;
}
// --- SMP Initialization ---
uint32_t smp_init(struct limine_smp_response *smp_resp) {
if (!smp_resp || smp_resp->cpu_count <= 1) {
@@ -132,8 +175,15 @@ uint32_t smp_init(struct limine_smp_response *smp_resp) {
cpu_states[i].lapic_id = cpu->lapic_id;
if (cpu->lapic_id == bsp_lapic_id) {
cpu_states[i].online = true;
cpu_states[i] = *bsp_cpu_state; // Copy early BSP state
cpu_states[i].self = &cpu_states[i];
cpu_states[i].kernel_stack = 0; // Limine stack for now
cpu_states[i].kernel_syscall_stack = 0;
bsp_index = i;
wrmsr(MSR_GS_BASE, (uint64_t)&cpu_states[i]);
wrmsr(MSR_KERNEL_GS_BASE, (uint64_t)&cpu_states[i]);
serial_write("[SMP] BSP CPU ");
serial_write_num(i);
serial_write(" (LAPIC ");

View File

@@ -8,29 +8,27 @@
#include <stdbool.h>
#include "spinlock.h"
// Per-CPU state. Dynamically allocated at boot based on actual CPU count.
typedef struct cpu_state {
uint32_t cpu_id; // Logical CPU index (0 = BSP)
uint32_t lapic_id; // Local APIC ID from Limine
uint64_t kernel_stack; // Top of kernel stack for this CPU
void *kernel_stack_alloc; // Base allocation for kfree
volatile bool online; // True once AP is fully initialized
struct cpu_state *self;
uint32_t cpu_id;
uint32_t lapic_id;
uint64_t kernel_stack;
void *kernel_stack_alloc;
volatile bool online;
uint64_t user_rsp_scratch;
uint64_t kernel_syscall_stack;
} cpu_state_t;
void smp_init_bsp(void);
// Initialize SMP — call after GDT/IDT/memory init but before wm_init.
// Pass the Limine SMP response. APs will be started and will enter their
// idle loops. Returns the number of CPUs brought online.
struct limine_smp_response;
uint32_t smp_init(struct limine_smp_response *smp_resp);
// Get the current CPU index (0 = BSP). Uses CPUID to read LAPIC ID,
// then looks up in the cpu table.
uint32_t smp_this_cpu_id(void);
// Total number of CPUs online.
uint32_t smp_cpu_count(void);
// Get per-CPU state by index.
cpu_state_t *smp_get_cpu(uint32_t cpu_id);
#endif

View File

@@ -8,6 +8,7 @@
#include "process.h"
#include "wm.h"
#include "fat32.h"
#include "vfs.h"
#include "paging.h"
#include "work_queue.h"
#include "smp.h"
@@ -76,6 +77,14 @@ static void smp_user_wrapper(void *arg) {
}
void syscall_init(void) {
uint64_t efer = rdmsr(MSR_EFER);
efer |= 1;
wrmsr(MSR_EFER, efer);
uint64_t star = ((uint64_t)0x001B << 48) | ((uint64_t)0x0008 << 32);
wrmsr(MSR_STAR, star);
extern void syscall_entry(void);
wrmsr(MSR_LSTAR, (uint64_t)syscall_entry);
wrmsr(MSR_FMASK, 0x200);
}
static void user_window_close(Window *win) {
@@ -852,33 +861,35 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
const char *mode = (const char *)arg3;
if (!path || !mode) return -1;
FAT32_FileHandle *fh = fat32_open(path, mode);
if (!fh) return -1;
// vfs_open now handles normalization internally with process_get_current()
// but let's be explicit if we can.
vfs_file_t *vf = vfs_open(path, mode);
if (!vf) return -1;
for (int i = 0; i < MAX_PROCESS_FDS; i++) {
if (proc->fds[i] == NULL) {
proc->fds[i] = fh;
proc->fds[i] = vf;
return (uint64_t)i;
}
}
fat32_close(fh);
vfs_close(vf);
return -1;
} else if (cmd == FS_CMD_READ) {
int fd = (int)arg2;
void *buf = (void *)arg3;
uint32_t len = (uint32_t)arg4;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)fat32_read((FAT32_FileHandle*)proc->fds[fd], buf, (int)len);
return (uint64_t)vfs_read((vfs_file_t*)proc->fds[fd], buf, (int)len);
} else if (cmd == FS_CMD_WRITE) {
int fd = (int)arg2;
const void *buf = (const void *)arg3;
uint32_t len = (uint32_t)arg4;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)fat32_write((FAT32_FileHandle*)proc->fds[fd], buf, (int)len);
return (uint64_t)vfs_write((vfs_file_t*)proc->fds[fd], buf, (int)len);
} else if (cmd == FS_CMD_CLOSE) {
int fd = (int)arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
fat32_close((FAT32_FileHandle*)proc->fds[fd]);
vfs_close((vfs_file_t*)proc->fds[fd]);
proc->fds[fd] = NULL;
return 0;
} else if (cmd == FS_CMD_SEEK) {
@@ -886,49 +897,107 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int offset = (int)arg3;
int whence = (int)arg4; // 0=SET, 1=CUR, 2=END
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)fat32_seek((FAT32_FileHandle*)proc->fds[fd], offset, whence);
return (uint64_t)vfs_seek((vfs_file_t*)proc->fds[fd], offset, whence);
} else if (cmd == FS_CMD_TELL) {
int fd = (int)arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)((FAT32_FileHandle*)proc->fds[fd])->position;
return (uint64_t)vfs_file_position((vfs_file_t*)proc->fds[fd]);
} else if (cmd == FS_CMD_SIZE) {
int fd = (int)arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)((FAT32_FileHandle*)proc->fds[fd])->size;
}
else if (cmd == FS_CMD_LIST) {
return (uint64_t)vfs_file_size((vfs_file_t*)proc->fds[fd]);
} else if (cmd == FS_CMD_GETCWD) {
char *buf = (char *)arg2;
int size = (int)arg3;
if (!buf || size <= 0) return -1;
int len = (int)k_strlen(proc->cwd);
if (len >= size) return -1;
k_strcpy(buf, proc->cwd);
return (uint64_t)len;
} else if (cmd == FS_CMD_CHDIR) {
const char *path = (const char *)arg2;
FAT32_FileInfo *entries = (FAT32_FileInfo *)arg3;
if (!path) return -1;
char normalized[VFS_MAX_PATH];
vfs_normalize_path(proc->cwd, path, normalized);
if (vfs_is_directory(normalized)) {
k_strcpy(proc->cwd, normalized);
return 0;
}
return -1;
} else if (cmd == FS_CMD_LIST) {
const char *path = (const char *)arg2;
FAT32_FileInfo *u_entries = (FAT32_FileInfo *)arg3;
int max_entries = (int)arg4;
if (!path || !entries) return -1;
return (uint64_t)fat32_list_directory(path, entries, max_entries);
if (!path || !u_entries) return -1;
char normalized[VFS_MAX_PATH];
vfs_normalize_path(proc->cwd, path, normalized);
// Safety cap for kernel allocation
if (max_entries > 256) max_entries = 256;
if (max_entries <= 0) return 0;
vfs_dirent_t *v_entries = (vfs_dirent_t *)kmalloc(sizeof(vfs_dirent_t) * max_entries);
if (!v_entries) return -1;
int count = vfs_list_directory(normalized, v_entries, max_entries);
if (count > 0) {
for (int i = 0; i < count; i++) {
// Direct copy as layouts are now aligned
k_strcpy(u_entries[i].name, v_entries[i].name);
u_entries[i].size = v_entries[i].size;
u_entries[i].is_directory = v_entries[i].is_directory;
u_entries[i].start_cluster = v_entries[i].start_cluster;
u_entries[i].write_date = v_entries[i].write_date;
u_entries[i].write_time = v_entries[i].write_time;
}
}
kfree(v_entries);
return (uint64_t)count;
} else if (cmd == FS_CMD_DELETE) {
const char *path = (const char *)arg2;
if (!path) return -1;
return fat32_delete(path) ? 0 : -1;
char normalized[VFS_MAX_PATH];
vfs_normalize_path(proc->cwd, path, normalized);
return vfs_delete(normalized) ? 0 : -1;
} else if (cmd == FS_CMD_GET_INFO) {
const char *path = (const char *)arg2;
FAT32_FileInfo *info = (FAT32_FileInfo *)arg3;
if (!path || !info) return -1;
extern int fat32_get_info(const char *path, FAT32_FileInfo *info);
return (uint64_t)fat32_get_info(path, info);
FAT32_FileInfo *u_info = (FAT32_FileInfo *)arg3;
if (!path || !u_info) return -1;
char normalized[VFS_MAX_PATH];
vfs_normalize_path(proc->cwd, path, normalized);
vfs_dirent_t v_info;
int res = vfs_get_info(normalized, &v_info);
if (res == 0) {
k_strcpy(u_info->name, v_info.name);
u_info->size = v_info.size;
u_info->is_directory = v_info.is_directory;
u_info->start_cluster = v_info.start_cluster;
u_info->write_date = v_info.write_date;
u_info->write_time = v_info.write_time;
}
return (uint64_t)res;
} else if (cmd == FS_CMD_MKDIR) {
const char *path = (const char *)arg2;
if (!path) return -1;
return fat32_mkdir(path) ? 0 : -1;
return vfs_mkdir(path) ? 0 : -1;
} else if (cmd == FS_CMD_EXISTS) {
const char *path = (const char *)arg2;
if (!path) return 0;
return fat32_exists(path) ? 1 : 0;
return vfs_exists(path) ? 1 : 0;
} else if (cmd == FS_CMD_GETCWD) {
char *buf = (char *)arg2;
int size = (int)arg3;
if (!buf) return -1;
extern void fat32_get_current_dir(char *buf, int size);
fat32_get_current_dir(buf, size);
return 0;
} else if (cmd == FS_CMD_CHDIR) {
const char *path = (const char *)arg2;
if (!path) return -1;
extern bool fat32_chdir(const char *path);
return fat32_chdir(path) ? 0 : -1;
}
return 0;
@@ -1055,15 +1124,6 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
extern void k_beep(int freq, int ms);
k_beep(freq, ms);
return 0;
} else if (cmd == 15) { // SYSTEM_CMD_MEMINFO
uint64_t *out = (uint64_t *)arg2;
if (!out) return -1;
MemStats stats = memory_get_stats();
out[0] = stats.total_memory;
out[1] = stats.used_memory;
return 0;
} else if (cmd == 16) { // SYSTEM_CMD_UPTIME
return wm_get_ticks();
} else if (cmd == 17) { // SYSTEM_CMD_PCI_LIST
typedef struct {
uint16_t vendor;
@@ -1224,54 +1284,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
size_t max_len = (size_t)arg3;
extern int network_tcp_recv_nb(void *buf, size_t max_len);
return (uint64_t)network_tcp_recv_nb(buf, max_len);
} else if (cmd == SYSTEM_CMD_PROCESS_LIST) {
ProcessInfo *out = (ProcessInfo *)arg2;
int max_procs = (int)arg3;
if (!out) return 0;
extern process_t processes[];
// Dynamically calculate kernel usage as: Total System Used - User Process Sum
MemStats stats = memory_get_stats();
size_t total_used = stats.used_memory;
size_t user_used = 0;
for (int i = 0; i < 16; i++) {
if (processes[i].pid != 0xFFFFFFFF && processes[i].pid != 0 && processes[i].is_user) {
user_used += processes[i].used_memory;
}
}
if (total_used > user_used) processes[0].used_memory = total_used - user_used;
else processes[0].used_memory = 0;
int count = 0;
for (int i = 0; i < 16; i++) {
if (processes[i].pid != 0xFFFFFFFF && (processes[i].is_user || processes[i].pid == 0)) {
out[count].pid = processes[i].pid;
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy(out[count].name, processes[i].name, 64);
if (processes[i].pid == 0) {
out[count].name[0] = 'k'; out[count].name[1] = 'e'; out[count].name[2] = 'r';
out[count].name[3] = 'n'; out[count].name[4] = 'e'; out[count].name[5] = 'l';
out[count].name[6] = '\0';
}
out[count].ticks = processes[i].ticks;
out[count].used_memory = processes[i].used_memory;
count++;
if (count >= max_procs) break;
}
}
return (uint64_t)count;
} else if (cmd == SYSTEM_CMD_GET_CPU_MODEL) {
char *user_buf = (char *)arg2;
if (!user_buf) return -1;
char model[64];
platform_get_cpu_model(model);
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy(user_buf, model, 49);
return 0;
return -1;
} else if (cmd == 47) { // SYSTEM_CMD_SET_RESOLUTION
uint16_t req_w = (uint16_t)arg2;
uint16_t req_h = (uint16_t)arg3;
@@ -1310,12 +1323,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
return 0;
}
return -1;
} else if (cmd == 49) { // SYSTEM_CMD_GET_OS_INFO
os_info_t *info = (os_info_t *)arg2;
if (!info) return -1;
extern void get_os_info(os_info_t *info);
get_os_info(info);
return 0;
return -1;
} else if (cmd == SYSTEM_CMD_PARALLEL_RUN) {
void (*user_fn)(void*) = (void (*)(void*))arg2;
void **args = (void **)arg3;

View File

@@ -6,7 +6,6 @@
#include <stdint.h>
// Forward declarations
typedef struct Window Window;
typedef struct registers_t registers_t;
@@ -53,11 +52,8 @@ typedef struct {
#define SYSTEM_CMD_SET_RAW_MODE 41
#define SYSTEM_CMD_TCP_RECV_NB 42
#define SYSTEM_CMD_YIELD 43
#define SYSTEM_CMD_PROCESS_LIST 44
#define SYSTEM_CMD_GET_CPU_MODEL 45
#define SYSTEM_CMD_SLEEP 46
#define SYSTEM_CMD_SET_RESOLUTION 47
#define SYSTEM_CMD_GET_OS_INFO 49
#define SYSTEM_CMD_PARALLEL_RUN 50
void syscall_init(void);

201
src/sys/sysfs_init.c Normal file
View File

@@ -0,0 +1,201 @@
#include "kernel_subsystem.h"
#include "smp.h"
#include "pci.h"
#include "memory_manager.h"
#include "module_manager.h"
#include "io.h"
#include "core/kutils.h"
#include "wm/graphics.h"
#include "core/platform.h"
// --- Helper: itoa ---
static void sys_itoa(int n, char *s) {
k_itoa(n, s);
}
// --- Graphics Implementation ---
static int read_gfx_drm(char *buf, int size, int offset) {
char out[512];
k_memset(out, 0, 512);
k_strcpy(out, "Driver: Simple Framebuffer\n");
k_strcpy(out + k_strlen(out), "Resolution: ");
char s[32]; k_itoa(get_screen_width(), s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), "x");
k_itoa(get_screen_height(), s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), "\nDepth: ");
k_itoa(graphics_get_fb_bpp(), s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), " bpp\nAddress: 0x");
k_itoa_hex(graphics_get_fb_addr(), s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), "\n");
int len = (int)k_strlen(out);
if (offset >= len) return 0;
int to_copy = len - offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + offset, to_copy);
return to_copy;
}
// --- Memory Tracking Implementation ---
static int read_mem_tracking(char *buf, int size, int offset) {
MemStats stats = memory_get_stats();
char out[1024];
k_memset(out, 0, 1024);
k_strcpy(out, "--- Kernel Heap Tracking ---\n");
k_strcpy(out + k_strlen(out), "Allocated Blocks: ");
char s[32]; k_itoa(stats.allocated_blocks, s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), "\nFragmentation: ");
k_itoa(stats.fragmentation_percent, s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), "%\n");
int len = (int)k_strlen(out);
if (offset >= len) return 0;
int to_copy = len - offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + offset, to_copy);
return to_copy;
}
// --- Module Implementation ---
static int read_sys_modules(char *buf, int size, int offset) {
int count = module_manager_get_count();
char out[2048] = "Loaded Modules:\n";
for (int i = 0; i < count; i++) {
kernel_module_t *mod = module_manager_get_index(i);
k_strcpy(out + k_strlen(out), " - ");
k_strcpy(out + k_strlen(out), mod->name);
k_strcpy(out + k_strlen(out), " (");
char sz_s[16]; k_itoa(mod->size / 1024, sz_s);
k_strcpy(out + k_strlen(out), sz_s);
k_strcpy(out + k_strlen(out), " KB)\n");
}
int len = k_strlen(out);
if (offset >= len) return 0;
int to_copy = len - offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + offset, to_copy);
return to_copy;
}
// --- PCI Bus Implementation ---
static int read_pci_bus(char *buf, int size, int offset) {
pci_device_t devices[64];
int count = pci_enumerate_devices(devices, 64);
char out[4096];
k_memset(out, 0, 4096);
k_strcpy(out, "PCI Bus Devices:\n");
for (int i = 0; i < count; i++) {
char line[128];
k_strcpy(line, " [");
char b_s[8]; k_itoa(devices[i].bus, b_s);
k_strcpy(line + k_strlen(line), b_s);
k_strcpy(line + k_strlen(line), ":");
k_itoa(devices[i].device, b_s);
k_strcpy(line + k_strlen(line), b_s);
k_strcpy(line + k_strlen(line), ":");
k_itoa(devices[i].function, b_s);
k_strcpy(line + k_strlen(line), b_s);
k_strcpy(line + k_strlen(line), "] Vendor:");
k_itoa_hex(devices[i].vendor_id, b_s);
k_strcpy(line + k_strlen(line), b_s);
k_strcpy(line + k_strlen(line), " Device:");
k_itoa_hex(devices[i].device_id, b_s);
k_strcpy(line + k_strlen(line), b_s);
k_strcpy(line + k_strlen(line), " Class:");
k_itoa_hex(devices[i].class_code, b_s);
k_strcpy(line + k_strlen(line), b_s);
k_strcpy(line + k_strlen(line), "\n");
if (k_strlen(out) + k_strlen(line) < 4095) {
k_strcpy(out + k_strlen(out), line);
}
}
int len = (int)k_strlen(out);
if (offset >= len) return 0;
int to_copy = len - offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + offset, to_copy);
return to_copy;
}
// --- CPU System Implementation ---
static int read_cpu_info(char *buf, int size, int offset) {
char out[1024];
k_memset(out, 0, 1024);
char vendor[16];
platform_get_cpu_vendor(vendor);
k_strcpy(out + k_strlen(out), vendor);
k_strcpy(out + k_strlen(out), "\nCores: ");
char c_s[16]; k_itoa(smp_cpu_count(), c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\nSpeed: ~3.00 GHz\nFeatures: sse sse2 sse3 apic smp\n");
int len = (int)k_strlen(out);
if (offset >= len) return 0;
int to_copy = len - offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + offset, to_copy);
return to_copy;
}
// --- GPIO Implementation ---
static int read_gpio_debug(char *buf, int size, int offset) {
uint8_t p64 = inb(0x64);
char out[64] = "Port 0x64 Status: ";
char s[16]; k_itoa(p64, s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), "\n");
int len = k_strlen(out);
if (offset >= len) return 0;
int to_copy = len - offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + offset, to_copy);
return to_copy;
}
void sysfs_init_subsystems(void) {
kernel_subsystem_t *kernel, *devices, *bus, *class, *debug, *mem_debug;
subsystem_register("kernel", &kernel);
subsystem_register("devices", &devices);
subsystem_register("bus", &bus);
subsystem_register("class", &class);
subsystem_register("kernel/debug", &debug);
// CPU info
subsystem_add_file(kernel, "cpuinfo", read_cpu_info, NULL);
// Bus info
kernel_subsystem_t *pci_bus;
subsystem_register("bus/pci", &pci_bus);
subsystem_add_file(pci_bus, "devices", read_pci_bus, NULL);
// Module info
kernel_subsystem_t *modules_sub;
subsystem_register("module", &modules_sub);
subsystem_add_file(modules_sub, "loaded", read_sys_modules, NULL);
// Memory Tracking
subsystem_register("kernel/debug/memory", &mem_debug);
subsystem_add_file(mem_debug, "tracking", read_mem_tracking, NULL);
// Graphics DRM
kernel_subsystem_t *gfx_debug;
subsystem_register("kernel/debug/graphics", &gfx_debug);
subsystem_add_file(gfx_debug, "drm", read_gfx_drm, NULL);
// GPIO
subsystem_add_file(debug, "gpio", read_gpio_debug, NULL);
}

View File

@@ -14,7 +14,7 @@ int main(int argc, char **argv) {
printf("Manual for: %s\n", argv[1]);
printf("---------------------------\n");
strcpy(path, "A:/Library/man/");
strcpy(path, "/Library/man/");
strcat(path, argv[1]);
strcat(path, ".txt");

View File

@@ -9,15 +9,6 @@
#define MAX_ASCII_WIDTH 80
#define MAX_INFO_LINES 10
static char* strchr(const char *s, int c) {
while (*s != (char)c) {
if (!*s++) {
return 0;
}
}
return (char *)s;
}
static char* strncpy(char *dest, const char *src, size_t n) {
size_t i;
for (i = 0; i < n && src[i] != '\0'; i++) dest[i] = src[i];
@@ -117,7 +108,7 @@ static char* trim(char *str) {
}
static void set_config_defaults() {
strcpy(config.ascii_art_file, "A:/Library/art/boredos.txt");
strcpy(config.ascii_art_file, "/Library/art/boredos.txt");
strcpy(config.user_host_string, "root@boredos");
strcpy(config.separator, "------------");
strcpy(config.os_label, "OS");
@@ -160,7 +151,7 @@ static void parse_config(char* buffer) {
static void load_config() {
set_config_defaults();
int fd = sys_open("A:/Library/conf/sysfetch.cfg", "r");
int fd = sys_open("/Library/conf/sysfetch.cfg", "r");
if (fd < 0) return;
char *buffer = malloc(4096);
@@ -237,52 +228,82 @@ int main(int argc, char **argv) {
if (config.separator[0]) {
strcpy(info_lines[info_line_count++], config.separator);
}
os_info_t os_info;
sys_get_os_info(&os_info);
// Helper for proc parsing
auto int find_v(const char *b, const char *k) {
char *p = (char*)b; int kl = strlen(k);
while (*p) {
if (memcmp(p, k, kl) == 0 && p[kl] == ':') {
p += kl + 1; while (*p == ' ') p++; return atoi(p);
}
while (*p && *p != '\n') p++; if (*p == '\n') p++;
}
return 0;
}
int fd_v = sys_open("/proc/version", "r");
char v_buf[512];
if (fd_v >= 0) {
int b = sys_read(fd_v, v_buf, 511);
v_buf[b] = 0;
sys_close(fd_v);
} else strcpy(v_buf, "Unknown");
if (config.os_label[0]) {
strcpy(info_lines[info_line_count], config.os_label);
strcat(info_lines[info_line_count], ": ");
strcat(info_lines[info_line_count], os_info.os_name);
strcat(info_lines[info_line_count], " V");
strcat(info_lines[info_line_count], os_info.os_version);
strcat(info_lines[info_line_count], " '");
strcat(info_lines[info_line_count], os_info.os_codename);
strcat(info_lines[info_line_count], "'");
// Parse "BoredOS [codename] Version X.Y.Z"
strcat(info_lines[info_line_count], v_buf);
// Truncate at newline
char *nl = strchr(info_lines[info_line_count], '\n');
if (nl) *nl = 0;
info_line_count++;
}
if (config.kernel_label[0]) {
strcpy(info_lines[info_line_count], config.kernel_label);
strcat(info_lines[info_line_count], ": ");
strcat(info_lines[info_line_count], os_info.kernel_name);
strcat(info_lines[info_line_count], " V");
strcat(info_lines[info_line_count], os_info.kernel_version);
strcat(info_lines[info_line_count], " ");
strcat(info_lines[info_line_count], os_info.build_arch);
char *kstart = strchr(v_buf, '\n');
if (kstart) {
strcat(info_lines[info_line_count], kstart + 1);
char *knext = strchr(info_lines[info_line_count], '\n');
if (knext) *knext = 0;
} else strcat(info_lines[info_line_count], "Unknown");
info_line_count++;
}
if (config.uptime_label[0]) {
uint64_t ticks = sys_system(16, 0, 0, 0, 0);
int minutes = ticks / 3600; // 60Hz timer
strcpy(info_lines[info_line_count], config.uptime_label);
strcat(info_lines[info_line_count], ": ");
itoa(minutes, temp_buf);
strcat(info_lines[info_line_count], temp_buf);
strcat(info_lines[info_line_count++], " mins");
int fd_u = sys_open("/proc/uptime", "r");
if (fd_u >= 0) {
char u_buf[64];
int b = sys_read(fd_u, u_buf, 63);
u_buf[b] = 0;
sys_close(fd_u);
int sec = atoi(u_buf);
int mins = sec / 60;
strcpy(info_lines[info_line_count], config.uptime_label);
strcat(info_lines[info_line_count], ": ");
itoa(mins, temp_buf);
strcat(info_lines[info_line_count], temp_buf);
strcat(info_lines[info_line_count++], " mins");
}
}
if (config.shell_label[0]) {
strcpy(info_lines[info_line_count], config.shell_label);
strcat(info_lines[info_line_count++], ": bsh");
}
if (config.memory_label[0]) {
uint64_t mem[2];
if (sys_system(15, (uint64_t)mem, 0, 0, 0) == 0) {
int fd_m = sys_open("/proc/meminfo", "r");
if (fd_m >= 0) {
char m_buf[512];
int b = sys_read(fd_m, m_buf, 511);
m_buf[b] = 0;
sys_close(fd_m);
int total = find_v(m_buf, "MemTotal");
int used = find_v(m_buf, "MemUsed");
strcpy(info_lines[info_line_count], config.memory_label);
strcat(info_lines[info_line_count], ": ");
itoa((int)(mem[1] / 1024 / 1024), temp_buf);
itoa(used / 1024, temp_buf);
strcat(info_lines[info_line_count], temp_buf);
strcat(info_lines[info_line_count], "MiB / ");
itoa((int)(mem[0] / 1024 / 1024), temp_buf);
itoa(total / 1024, temp_buf);
strcat(info_lines[info_line_count], temp_buf);
strcat(info_lines[info_line_count++], "MiB");
}

View File

@@ -149,26 +149,12 @@ char *strrchr(const char *s, int c) {
if (c == 0) last = s;
return (char*)last;
}
char *strchr(const char *s, int c) {
while (*s) { if (*s == c) return (char*)s; s++; }
if (c == 0) return (char*)s;
return NULL;
}
char *strdup(const char *s) {
size_t len = strlen(s) + 1;
char *dup = malloc(len);
if (dup) memcpy(dup, s, len);
return dup;
}
char *strstr(const char *haystack, const char *needle) {
size_t n = strlen(needle);
if (!n) return (char *)haystack;
while (*haystack) {
if (!strncmp(haystack, needle, n)) return (char *)haystack;
haystack++;
}
return NULL;
}
int toupper(int c) { return (c >= 'a' && c <= 'z') ? c - 32 : c; }
int tolower(int c) { return (c >= 'A' && c <= 'Z') ? c + 32 : c; }

View File

@@ -39,8 +39,19 @@ void DG_SleepMs(uint32_t ms) {
}
uint32_t DG_GetTicksMs(void) {
uint64_t ticks = sys_system(16, 0, 0, 0, 0); // SYSTEM_CMD_UPTIME = 16 (100Hz)
return (uint32_t)(ticks * 10);
int fd = sys_open("/proc/uptime", "r");
if (fd < 0) return 0;
char buf[128];
int bytes = sys_read(fd, buf, 127);
sys_close(fd);
if (bytes <= 0) return 0;
buf[bytes] = 0;
char *p = strstr(buf, "Raw_Ticks:");
if (!p) return 0;
uint32_t ticks = atoi(p + 10);
// 60Hz to ms: ticks * 1000 / 60 = ticks * 50 / 3
return (ticks * 50) / 3;
}
void DG_SetWindowTitle(const char * title) {
@@ -108,7 +119,7 @@ int DG_GetKey(int* pressed, unsigned char* key) {
int main(int argc, char** argv) {
(void)argc;
(void)argv;
char* fake_argv[] = {"doom", "-iwad", "A:/Library/DOOM/doom1.wad"};
char* fake_argv[] = {"doom", "-iwad", "/Library/DOOM/doom1.wad"};
doomgeneric_Create(3, fake_argv);
while (1) {

View File

@@ -95,36 +95,39 @@ static void about_paint(ui_window_t win) {
draw_ascii_logo(win, 14, offset_y);
int fh = ui_get_font_height();
os_info_t os_info;
sys_get_os_info(&os_info);
char os_name_str[128];
os_name_str[0] = 0;
strcat(os_name_str, os_info.os_name);
strcat(os_name_str, " '");
strcat(os_name_str, os_info.os_codename);
strcat(os_name_str, "'");
int fd_v = sys_open("/proc/version", "r");
char v_buf[1024]; v_buf[0] = 0;
if (fd_v >= 0) {
int b = sys_read(fd_v, v_buf, 1023);
v_buf[b] = 0;
sys_close(fd_v);
}
char os_version_str[128];
os_version_str[0] = 0;
strcat(os_version_str, os_info.os_name);
strcat(os_version_str, " Version ");
strcat(os_version_str, os_info.os_version);
char kernel_version_str[128];
kernel_version_str[0] = 0;
strcat(kernel_version_str, os_info.kernel_name);
strcat(kernel_version_str, " Version ");
strcat(kernel_version_str, os_info.kernel_version);
strcat(kernel_version_str, " ");
strcat(kernel_version_str, os_info.build_arch);
char os_name_str[128] = "Unknown OS";
char os_version_str[128] = "Unknown Version";
char kernel_version_str[128] = "Unknown Kernel";
char build_date_str[128] = "Unknown Build";
char build_date_str[128];
build_date_str[0] = 0;
strcat(build_date_str, "Build Date: ");
strcat(build_date_str, os_info.build_date);
strcat(build_date_str, " ");
strcat(build_date_str, os_info.build_time);
if (v_buf[0]) {
char *line1 = v_buf;
char *line2 = strchr(line1, '\n'); if (line2) { *line2 = 0; line2++; }
char *line3 = line2 ? strchr(line2, '\n') : NULL; if (line3) { *line3 = 0; line3++; }
strcpy(os_name_str, line1);
if (line2) {
strcpy(os_version_str, line2);
}
if (line3) {
strcpy(kernel_version_str, line3);
char *line4 = strchr(line3, '\n');
if (line4) {
*line4 = 0; line4++;
strcpy(build_date_str, line4);
char *line5 = strchr(build_date_str, '\n');
if (line5) *line5 = 0;
}
}
}
ui_draw_string(win, offset_x, offset_y + 105, os_name_str, 0xFFFFFFFF);
ui_draw_string(win, offset_x, offset_y + 105 + fh, os_version_str, 0xFFFFFFFF);

View File

@@ -32,19 +32,6 @@ static int win_h = 960;
static char history_stack[HISTORY_MAX][512];
static int history_count = 0;
static char* strstr(const char* haystack, const char* needle) {
if (!*needle) return (char*)haystack;
for (; *haystack; haystack++) {
const char *h = haystack;
const char *n = needle;
while (*h && *n && *h == *n) {
h++; n++;
}
if (!*n) return (char*)haystack;
}
return NULL;
}
static char* str_istrstr(const char* haystack, const char* needle) {
if (!*needle) return (char*)haystack;
for (; *haystack; haystack++) {

View File

@@ -37,7 +37,7 @@ static void notepad_ensure_cursor_visible(int h) {
}
static void notepad_load_state() {
int fd = sys_open("A:/tmp/notepad_state.txt", "r");
int fd = sys_open("/tmp/notepad_state.txt", "r");
if (fd >= 0) {
sys_serial_write("Notepad: Loading state...\n");
buf_len = sys_read(fd, buffer, NOTEPAD_BUF_SIZE - 1);
@@ -50,8 +50,8 @@ static void notepad_load_state() {
static void notepad_save_state() {
// Ensure dir exists
sys_mkdir("A:/tmp");
int fd = sys_open("A:/tmp/notepad_state.txt", "w");
sys_mkdir("/tmp");
int fd = sys_open("/tmp/notepad_state.txt", "w");
if (fd >= 0) {
sys_write_fs(fd, buffer, buf_len);
sys_close(fd);

View File

@@ -28,7 +28,7 @@ static uint32_t *canvas_buffer = NULL;
static uint32_t current_color = COLOR_BLACK;
static int last_mx = -1;
static int last_my = -1;
static char current_file_path[256] = "/Desktop/drawing.pnt";
static char current_file_path[256] = "/root/Desktop/drawing.pnt";
static void paint_strcpy(char *dest, const char *src) {
while (*src) *dest++ = *src++;
@@ -79,12 +79,7 @@ static void paint_paint(ui_window_t win) {
// Draw canvas content
if (canvas_buffer) {
for (int y = 0; y < CANVAS_H; y++) {
for (int x = 0; x < CANVAS_W; x++) {
uint32_t color = canvas_buffer[y * CANVAS_W + x];
ui_draw_rect(win, canvas_x + x, canvas_y + y, 1, 1, color);
}
}
ui_draw_image(win, canvas_x, canvas_y, CANVAS_W, CANVAS_H, canvas_buffer);
}
}
@@ -96,7 +91,6 @@ static void paint_put_brush(ui_window_t win, int cx, int cy, int *min_x, int *mi
int py = cy + dy;
if (px >= 0 && px < CANVAS_W && py >= 0 && py < CANVAS_H) {
canvas_buffer[py * CANVAS_W + px] = current_color;
ui_draw_rect(win, 60 + px, 0 + py, 1, 1, current_color);
if (px < *min_x) *min_x = px;
if (py < *min_y) *min_y = py;
@@ -140,6 +134,7 @@ void paint_handle_mouse(ui_window_t win, int x, int y) {
}
if (min_x <= max_x && min_y <= max_y) {
ui_draw_image(win, 60, 0, CANVAS_W, CANVAS_H, canvas_buffer);
ui_mark_dirty(win, 60 + min_x, 0 + min_y, (max_x - min_x) + 1, (max_y - min_y) + 1);
}

View File

@@ -84,7 +84,7 @@ int main(int argc, char **argv) {
uint64_t dt[6] = {0};
syscall2(SYS_GUI, GUI_CMD_GET_DATETIME, (uint64_t)dt);
strcpy(g_filename, "/Desktop/screenshot-");
strcpy(g_filename, "/root/Desktop/screenshot-");
append_num((int)dt[0], 4); // Year
append_num((int)dt[1], 2); // Month
append_num((int)dt[2], 2); // Day
@@ -110,7 +110,7 @@ int main(int argc, char **argv) {
// Show notification
char notif[256] = "Saved ";
strcat(notif, g_filename + 9); // Skip "/Desktop/"
strcat(notif, g_filename + 14); // Skip "/root/Desktop/"
syscall2(SYS_GUI, GUI_CMD_SHOW_NOTIFICATION, (uint64_t)notif);
} else {
res = 0;

View File

@@ -32,38 +32,111 @@ static uint64_t kernel_ticks_prev = 0;
static uint64_t total_mem_system = 0;
static uint64_t used_mem_system = 0;
static char cpu_model_name[64] = "Unknown CPU";
static int cpu_cores = 1;
typedef struct {
size_t total_memory;
size_t used_memory;
size_t available_memory;
size_t allocated_blocks;
size_t free_blocks;
size_t largest_free_block;
size_t smallest_free_block;
size_t fragmentation_percent;
size_t peak_memory_used;
} MemStats;
static int find_value(const char *buf, const char *key) {
char *p = (char*)buf;
int key_len = strlen(key);
while (*p) {
if (memcmp(p, key, key_len) == 0 && p[key_len] == ':') {
p += key_len + 1;
while (*p == ' ') p++;
return atoi(p);
}
while (*p && *p != '\n') p++;
if (*p == '\n') p++;
}
return 0;
}
static void find_string(const char *buf, const char *key, char *out, int max_len) {
char *p = (char*)buf;
int key_len = strlen(key);
while (*p) {
if (memcmp(p, key, key_len) == 0 && p[key_len] == ':') {
p += key_len + 1;
while (*p == ' ') p++;
int i = 0;
while (*p && *p != '\n' && i < max_len - 1) {
out[i++] = *p++;
}
out[i] = 0;
return;
}
while (*p && *p != '\n') p++;
if (*p == '\n') p++;
}
strcpy(out, "Unknown");
}
static void update_proc_list(void) {
proc_count = sys_system(SYSTEM_CMD_PROCESS_LIST, (uint64_t)proc_list, 32, 0, 0);
uint64_t uptime_now = sys_system(SYSTEM_CMD_UPTIME, 0, 0, 0, 0);
FAT32_FileInfo entries[64];
int count = sys_list("/proc", entries, 64);
if (count < 0) return;
proc_count = 0;
uint64_t user_ticks_now = 0;
for (int i = 0; i < proc_count; i++) {
if (proc_list[i].pid != 0) {
user_ticks_now += proc_list[i].ticks;
for (int i = 0; i < count; i++) {
if (entries[i].is_directory) {
// Check if name is numeric (PID)
bool numeric = true;
for (int j = 0; entries[i].name[j]; j++) {
if (entries[i].name[j] < '0' || entries[i].name[j] > '9') {
numeric = false;
break;
}
}
if (!numeric) continue;
int pid = atoi(entries[i].name);
char path[64];
strcpy(path, "/proc/");
strcat(path, entries[i].name);
strcat(path, "/status");
int fd = sys_open(path, "r");
if (fd >= 0) {
char buf[512];
int bytes = sys_read(fd, buf, 511);
sys_close(fd);
if (bytes > 0) {
buf[bytes] = 0;
proc_list[proc_count].pid = pid;
find_string(buf, "Name", proc_list[proc_count].name, 64);
proc_list[proc_count].used_memory = (size_t)find_value(buf, "Memory") * 1024;
uint64_t ticks = (uint64_t)find_value(buf, "Ticks");
proc_list[proc_count].ticks = ticks;
proc_list[proc_count].is_idle = find_value(buf, "Idle") == 1;
if (!proc_list[proc_count].is_idle) user_ticks_now += ticks;
proc_count++;
if (proc_count >= 32) break;
}
}
}
}
// Global stats
int fd_u = sys_open("/proc/uptime", "r");
uint64_t uptime_now = 0;
if (fd_u >= 0) {
char buf[256];
int bytes = sys_read(fd_u, buf, 255);
sys_close(fd_u);
if (bytes > 0) {
buf[bytes] = 0;
uptime_now = (uint64_t)find_value(buf, "Raw_Ticks");
}
}
if (uptime_prev > 0) {
uint64_t total_delta = uptime_now - uptime_prev;
if (total_delta > 0) {
uint64_t used_delta = user_ticks_now - kernel_ticks_prev; // Reusing the global state variable for prev user_ticks
// On a 4 CPU system, theoretically used_delta can be 4x total_delta
int usage = (int)((used_delta * 100) / (total_delta * 4));
uint64_t used_delta = user_ticks_now - kernel_ticks_prev;
int cores = cpu_cores > 0 ? cpu_cores : 1;
int usage = (int)((used_delta * 100) / (total_delta * cores));
if (usage > 100) usage = 100;
cpu_history[history_idx] = usage;
}
@@ -72,11 +145,18 @@ static void update_proc_list(void) {
uptime_prev = uptime_now;
kernel_ticks_prev = user_ticks_now;
MemStats stats;
sys_system(SYSTEM_CMD_MEMINFO, (uint64_t)&stats, 0, 0, 0);
total_mem_system = stats.total_memory;
used_mem_system = stats.used_memory;
mem_history[history_idx] = (int)(stats.used_memory / 1024);
int fd_m = sys_open("/proc/meminfo", "r");
if (fd_m >= 0) {
char buf[1024];
int bytes = sys_read(fd_m, buf, 1023);
sys_close(fd_m);
if (bytes > 0) {
buf[bytes] = 0;
total_mem_system = (uint64_t)find_value(buf, "MemTotal") * 1024;
used_mem_system = (uint64_t)find_value(buf, "MemUsed") * 1024;
mem_history[history_idx] = (int)(used_mem_system / 1024);
}
}
history_idx = (history_idx + 1) % GRAPH_POINTS;
}
@@ -168,9 +248,12 @@ static void draw_taskman(void) {
// Memory Graph Area
ui_draw_string(win_taskman, 205, 10, "MEMORY", COLOR_MEM);
char mem_pct_label[16];
int current_mem_pct = 0;
if (total_mem_system > 0) current_mem_pct = (int)((used_mem_system * 100) / total_mem_system);
itoa(current_mem_pct, mem_pct_label);
int current_mem_pct_x10 = 0;
if (total_mem_system > 0) current_mem_pct_x10 = (int)((used_mem_system * 1000) / total_mem_system);
itoa(current_mem_pct_x10 / 10, mem_pct_label);
strcat(mem_pct_label, ".");
char frac[4]; itoa(current_mem_pct_x10 % 10, frac);
strcat(mem_pct_label, frac);
strcat(mem_pct_label, "%");
ui_draw_string(win_taskman, 340, 10, mem_pct_label, COLOR_MEM);
@@ -255,8 +338,18 @@ static void draw_taskman(void) {
int main(void) {
win_taskman = ui_window_create("Task Manager", 100, 100, 400, 480);
// Fetch CPU model
sys_system(SYSTEM_CMD_GET_CPU_MODEL, (uint64_t)cpu_model_name, 0, 0, 0);
int fd_c = sys_open("/proc/cpuinfo", "r");
if (fd_c >= 0) {
char buf[1024];
int bytes = sys_read(fd_c, buf, 1023);
sys_close(fd_c);
if (bytes > 0) {
buf[bytes] = 0;
find_string(buf, "Processor", cpu_model_name, 64);
int cores = find_value(buf, "Cores");
if (cores > 0) cpu_cores = cores;
}
}
for(int i=0; i<GRAPH_POINTS; i++) { cpu_history[i] = 0; mem_history[i] = 0; }

View File

@@ -202,9 +202,27 @@ char* strcat(char *dest, const char *src) {
return ret;
}
char *strchr(const char *s, int c) {
while (*s != (char)c) {
if (!*s++) return NULL;
}
return (char *)s;
}
char *strstr(const char *haystack, const char *needle) {
size_t needle_len = strlen(needle);
if (!needle_len) return (char *)haystack;
while (*haystack) {
if (memcmp(haystack, needle, needle_len) == 0) return (char *)haystack;
haystack++;
}
return NULL;
}
int atoi(const char *nptr) {
int res = 0;
int sign = 1;
while (*nptr == ' ' || *nptr == '\t' || *nptr == '\n' || *nptr == '\r') nptr++;
if (*nptr == '-') {
sign = -1;
nptr++;

View File

@@ -11,11 +11,9 @@ void* realloc(void* ptr, size_t size);
void *memset(void *s, int c, size_t n);
void *memcpy(void *dest, const void *src, size_t n);
// String functions
size_t strlen(const char *s);
int strcmp(const char *s1, const char *s2);
char* strcpy(char *dest, const char *src);
char* strcat(char *dest, const char *src);
#include "string.h"
// Math/Utility functions
int atoi(const char *nptr);
void itoa(int n, char *buf);

View File

@@ -7,5 +7,11 @@ void *memmove(void *dest, const void *src, size_t n);
int memcmp(const void *s1, const void *s2, size_t n);
void *memcpy(void *dest, const void *src, size_t n);
void *memset(void *s, int c, size_t n);
char *strchr(const char *s, int c);
char *strstr(const char *haystack, const char *needle);
size_t strlen(const char *s);
int strcmp(const char *s1, const char *s2);
char* strcpy(char *dest, const char *src);
char* strcat(char *dest, const char *src);
#endif

View File

@@ -251,10 +251,6 @@ void sys_yield(void) {
syscall1(SYS_SYSTEM, SYSTEM_CMD_YIELD);
}
int sys_get_os_info(os_info_t *info) {
return (int)syscall5(SYS_SYSTEM, SYSTEM_CMD_GET_OS_INFO, (uint64_t)info, 0, 0, 0);
}
void sys_parallel_run(void (*fn)(void*), void **args, int count) {
syscall5(SYS_SYSTEM, SYSTEM_CMD_PARALLEL_RUN, (uint64_t)fn, (uint64_t)args, (uint64_t)count, 0);
}

View File

@@ -44,8 +44,6 @@
#define SYSTEM_CMD_REBOOT 12
#define SYSTEM_CMD_SHUTDOWN 13
#define SYSTEM_CMD_BEEP 14
#define SYSTEM_CMD_MEMINFO 15
#define SYSTEM_CMD_UPTIME 16
#define SYSTEM_CMD_PCI_LIST 17
#define SYSTEM_CMD_NETWORK_DHCP 18
#define SYSTEM_CMD_NETWORK_GET_MAC 19
@@ -69,13 +67,10 @@
#define SYSTEM_CMD_DNS_LOOKUP 37
#define SYSTEM_CMD_SET_DNS 38
#define SYSTEM_CMD_NET_UNLOCK 39
#define SYSTEM_CMD_PROCESS_LIST 44
#define SYSTEM_CMD_GET_CPU_MODEL 45
#define SYSTEM_CMD_SLEEP 46
#define SYSTEM_CMD_SET_RAW_MODE 41
#define SYSTEM_CMD_TCP_RECV_NB 42
#define SYSTEM_CMD_YIELD 43
#define SYSTEM_CMD_GET_OS_INFO 49
#define SYSTEM_CMD_PARALLEL_RUN 50
// Internal assembly entry into Ring 0
@@ -137,6 +132,7 @@ typedef struct {
char name[64];
uint64_t ticks;
size_t used_memory;
uint32_t is_idle;
} ProcessInfo;
// Network API

View File

@@ -6,11 +6,18 @@
int main(int argc, char **argv) {
(void)argc; (void)argv;
uint64_t ticks = sys_system(16, 0, 0, 0, 0); // SYSTEM_CMD_UPTIME
uint64_t seconds = ticks / 100; // 100Hz timer assumed
uint64_t minutes = seconds / 60;
uint64_t hours = minutes / 60;
uint64_t days = hours / 24;
int fd = sys_open("/proc/uptime", "r");
if (fd < 0) return 1;
char buf[128];
int bytes = sys_read(fd, buf, 127);
sys_close(fd);
if (bytes <= 0) return 1;
buf[bytes] = 0;
int seconds = atoi(buf);
int minutes = seconds / 60;
int hours = minutes / 60;
int days = hours / 24;
printf("Uptime: %d days, %d hours, %d minutes, %d seconds\n",
(int)days, (int)(hours % 24), (int)(minutes % 60), (int)(seconds % 60));

View File

@@ -8,6 +8,7 @@
#include "rtc.h"
#include "fat32.h"
#include "vfs.h"
#include "disk.h"
#include "kutils.h"
#include <stddef.h>
@@ -75,7 +76,6 @@ static ShellConfig shell_config;
// CMD Window State (per-window context)
typedef struct {
char current_drive;
char current_dir[256];
} CmdState;
@@ -554,10 +554,7 @@ void cmd_print_prompt(void) {
cursor_col = 0;
if (shell_config.show_drive) {
current_color = shell_config.prompt_drive_color;
cmd_putchar(cmd_state->current_drive);
current_color = shell_config.prompt_colon_color;
cmd_putchar(':');
// No more drive letter in VFS
}
if (shell_config.show_dir) {
@@ -951,11 +948,7 @@ static void internal_cmd_cd(char *args) {
if (!args || !args[0]) {
// No argument - show current directory
if (cmd_state) {
char drive_str[3];
drive_str[0] = cmd_state->current_drive;
drive_str[1] = ':';
drive_str[2] = 0;
cmd_write(drive_str);
cmd_write(cmd_state->current_dir);
cmd_write("\n");
} else {
@@ -985,9 +978,7 @@ static void internal_cmd_cd(char *args) {
cmd_strcpy(full_path, path);
} else if (path[0] == '/') {
// Absolute path
full_path[0] = cmd_state->current_drive;
full_path[1] = ':';
int j = 2;
int j = 0;
int k = 0;
while (path[k] && j < 509) {
full_path[j++] = path[k++];
@@ -995,9 +986,7 @@ static void internal_cmd_cd(char *args) {
full_path[j] = 0;
} else {
// Relative path - resolve from current directory
full_path[0] = cmd_state->current_drive;
full_path[1] = ':';
int j = 2;
int j = 0;
// Copy current directory
const char *dir = cmd_state->current_dir;
@@ -1019,18 +1008,14 @@ static void internal_cmd_cd(char *args) {
}
// Validate directory exists
if (fat32_is_directory(full_path)) {
if (vfs_is_directory(full_path)) {
// Normalize the path to resolve .. and .
char normalized_path[512];
fat32_normalize_path(full_path, normalized_path);
vfs_normalize_path(cmd_state->current_dir, full_path, normalized_path);
cmd_update_dir(normalized_path);
cmd_write("Changed to: ");
char drive_str[3];
drive_str[0] = cmd_state->current_drive;
drive_str[1] = ':';
drive_str[2] = 0;
cmd_write(drive_str);
cmd_write(cmd_state->current_dir);
cmd_write("\n");
} else {
@@ -1086,7 +1071,7 @@ static void internal_cmd_txtedit(char *args) {
cmd_write("\n");
cmd_is_waiting_for_process = true;
process_t *proc = process_create_elf("A:/bin/txtedit.elf", normalized_path);
process_t *proc = process_create_elf("/bin/txtedit.elf", normalized_path);
if (proc) {
proc->is_terminal_proc = true;
proc->ui_window = &win_cmd;
@@ -1105,15 +1090,13 @@ void cmd_exec_elf(char *args) {
char full_exec_path[512] = {0};
int i = 0;
if (args[0] && args[1] != ':') {
if (args[0] && args[0] != '/') {
if (cmd_state) {
full_exec_path[i++] = cmd_state->current_drive;
full_exec_path[i++] = ':';
const char *dir = cmd_state->current_dir;
while (*dir && i < 509) {
full_exec_path[i++] = *dir++;
}
if (i > 2 && full_exec_path[i-1] != '/') {
if (i > 0 && full_exec_path[i-1] != '/') {
full_exec_path[i++] = '/';
}
}
@@ -1215,23 +1198,10 @@ static const CommandEntry commands[] = {
// Helper to sync cmd window directory after cd
static void cmd_update_dir(const char *path) {
if (!cmd_state || !path) return;
// Extract drive if provided
const char *p = path;
char drive = cmd_state->current_drive;
if (p[0] && p[1] == ':') {
drive = p[0];
if (drive >= 'a' && drive <= 'z') drive -= 32;
p += 2;
}
// Update drive
cmd_state->current_drive = drive;
// Update directory
if (*p) {
// Remove trailing slashes and copy
int len = 0;
@@ -1269,20 +1239,7 @@ static void cmd_exec_single(char *cmd) {
// Check for drive switch (e.g. "A:", "B:")
if (cmd[0] && cmd[1] == ':' && cmd[2] == 0) {
char letter = cmd[0];
if (letter >= 'a' && letter <= 'z') letter -= 32;
// Check if drive exists (don't change global, just check)
if (disk_get_by_letter(letter)) {
// Update cmd window's drive, not global
if (cmd_state) {
cmd_state->current_drive = letter;
cmd_state->current_dir[0] = '/';
cmd_state->current_dir[1] = 0;
}
} else {
cmd_write("Invalid drive.\n");
}
cmd_write("Drive letters are no longer supported. Use VFS paths (e.g. /dev/sda1)\n");
return;
}
@@ -1336,19 +1293,15 @@ static void cmd_exec_single(char *cmd) {
char full_exec_path[512];
int i = 0;
// Add drive letter
// Add current directory
if (cmd_state) {
full_exec_path[i++] = cmd_state->current_drive;
full_exec_path[i++] = ':';
// Add current directory
const char *dir = cmd_state->current_dir;
while (*dir && i < 509) {
full_exec_path[i++] = *dir++;
}
// Add separator if current dir doesn't end with /
if (i > 2 && full_exec_path[i-1] != '/') {
if (i > 0 && full_exec_path[i-1] != '/') {
full_exec_path[i++] = '/';
}
}
@@ -1436,15 +1389,11 @@ static void cmd_exec_single(char *cmd) {
i += 2;
while (args[i] == ' ') { temp_args[j++] = ' '; i++; }
// Prepend drive and directory to filename if relative
if (args[i] && args[i+1] != ':') {
temp_args[j++] = cmd_state->current_drive;
temp_args[j++] = ':';
if (args[i] != '/') {
const char *d = cmd_state->current_dir;
while (*d && j < 509) temp_args[j++] = *d++;
if (j > 2 && temp_args[j-1] != '/') temp_args[j++] = '/';
}
// Prepend directory to filename if relative
if (args[i] && args[i] != '/') {
const char *d = cmd_state->current_dir;
while (*d && j < 509) temp_args[j++] = *d++;
if (j > 0 && temp_args[j-1] != '/') temp_args[j++] = '/';
}
in_redirect = true;
} else if (args[i] == '>' && args[i+1] != '>') {
@@ -1453,15 +1402,11 @@ static void cmd_exec_single(char *cmd) {
i++;
while (args[i] == ' ') { temp_args[j++] = ' '; i++; }
// Prepend drive and directory to filename if relative
if (args[i] && args[i+1] != ':') {
temp_args[j++] = cmd_state->current_drive;
temp_args[j++] = ':';
if (args[i] != '/') {
const char *d = cmd_state->current_dir;
while (*d && j < 509) temp_args[j++] = *d++;
if (j > 2 && temp_args[j-1] != '/') temp_args[j++] = '/';
}
// Prepend directory to filename if relative
if (args[i] && args[i] != '/') {
const char *d = cmd_state->current_dir;
while (*d && j < 509) temp_args[j++] = *d++;
if (j > 0 && temp_args[j-1] != '/') temp_args[j++] = '/';
}
in_redirect = true;
} else {
@@ -1488,10 +1433,8 @@ static void cmd_exec_single(char *cmd) {
// Already has drive letter
cmd_strcpy(full_path_arg, args);
} else if (args[0] == '/') {
// Absolute path, just prepend drive
full_path_arg[0] = cmd_state->current_drive;
full_path_arg[1] = ':';
int i = 2;
// Absolute path
int i = 0;
int j = 0;
while (args[j] && i < 509) {
full_path_arg[i++] = args[j++];
@@ -1500,8 +1443,6 @@ static void cmd_exec_single(char *cmd) {
} else {
// Relative path - need to build from current directory
int i = 0;
full_path_arg[i++] = cmd_state->current_drive;
full_path_arg[i++] = ':';
// Add current directory
const char *dir = cmd_state->current_dir;
@@ -1510,7 +1451,7 @@ static void cmd_exec_single(char *cmd) {
}
// Add separator if current dir doesn't end with /
if (i > 2 && full_path_arg[i-1] != '/') {
if (i > 0 && full_path_arg[i-1] != '/') {
full_path_arg[i++] = '/';
}
@@ -1529,10 +1470,8 @@ static void cmd_exec_single(char *cmd) {
// Has drive letter, use as-is
cmd_strcpy(full_path_arg, args);
} else if (args[0] == '/') {
// Absolute path, just prepend drive
full_path_arg[0] = cmd_state->current_drive;
full_path_arg[1] = ':';
int i = 2;
// Absolute path
int i = 0;
int j = 0;
while (args[j] && i < 509) {
full_path_arg[i++] = args[j++];
@@ -1541,8 +1480,6 @@ static void cmd_exec_single(char *cmd) {
} else {
// Relative path - need to build from current directory
int i = 0;
full_path_arg[i++] = cmd_state->current_drive;
full_path_arg[i++] = ':';
// Add current directory
const char *dir = cmd_state->current_dir;
@@ -1551,7 +1488,7 @@ static void cmd_exec_single(char *cmd) {
}
// Add separator if current dir doesn't end with /
if (i > 2 && full_path_arg[i-1] != '/') {
if (i > 0 && full_path_arg[i-1] != '/') {
full_path_arg[i++] = '/';
}
@@ -1568,10 +1505,10 @@ static void cmd_exec_single(char *cmd) {
cmd_strcpy(full_path_arg, args);
args = full_path_arg;
} else {
// Add drive letter
full_path_arg[0] = cmd_state->current_drive;
full_path_arg[1] = ':';
int i = 2;
// Try to use arg as is (it's either absolute or relative)
// If it's relative, we should actually build it...
// Let's just use it as is for now since later path resolving will catch it
int i = 0;
int j = 0;
while (args[j] && i < 509) {
full_path_arg[i++] = args[j++];
@@ -1580,10 +1517,8 @@ static void cmd_exec_single(char *cmd) {
args = full_path_arg;
}
} else if (is_ls_command || is_cd_command) {
// For ls and cd with no args, pass current directory with drive
full_path_arg[0] = cmd_state->current_drive;
full_path_arg[1] = ':';
int i = 2;
// For ls and cd with no args, pass current directory
int i = 0;
const char *dir = cmd_state->current_dir;
while (*dir && i < 509) {
full_path_arg[i++] = *dir++;
@@ -1602,7 +1537,7 @@ static void cmd_exec_single(char *cmd) {
}
}
// Check for executable in Current Directory or A:/bin/
// Check for executable in Current Directory or /bin/
char search_path[512];
// Check if the command already ends in .elf (case insensitive)
@@ -1619,11 +1554,9 @@ static void cmd_exec_single(char *cmd) {
// 1. Try Current Directory + .elf
if (cmd_state) {
int idx = 0;
search_path[idx++] = cmd_state->current_drive;
search_path[idx++] = ':';
const char *dir = cmd_state->current_dir;
while (*dir && idx < 500) search_path[idx++] = *dir++;
if (idx > 2 && search_path[idx-1] != '/') search_path[idx++] = '/';
if (idx > 0 && search_path[idx-1] != '/') search_path[idx++] = '/';
const char *c = cmd;
while (*c && idx < 500) search_path[idx++] = *c++;
if (!has_elf_ext) {
@@ -1644,10 +1577,10 @@ static void cmd_exec_single(char *cmd) {
}
}
// 2. Try A:/bin/ + .elf
// 2. Try /bin/ + .elf
{
int idx = 0;
const char *bin_prefix = "A:/bin/";
const char *bin_prefix = "/bin/";
while (*bin_prefix) search_path[idx++] = *bin_prefix++;
const char *c = cmd;
while (*c && idx < 500) search_path[idx++] = *c++;
@@ -2171,11 +2104,12 @@ void cmd_reset(void) {
}
static void create_ramfs_files(void) {
if (!fat32_exists("Documents")) fat32_mkdir("Documents");
if (!fat32_exists("Projects")) fat32_mkdir("Projects");
if (!fat32_exists("Documents/Important")) fat32_mkdir("Documents/Important");
if (!fat32_exists("Apps")) fat32_mkdir("Apps");
if (!fat32_exists("Desktop")) fat32_mkdir("Desktop");
if (!fat32_exists("root")) fat32_mkdir("root");
if (!fat32_exists("root/Documents")) fat32_mkdir("root/Documents");
if (!fat32_exists("root/Documents/Important")) fat32_mkdir("root/Documents/Important");
if (!fat32_exists("root/Apps")) fat32_mkdir("root/Apps");
if (!fat32_exists("root/Desktop")) fat32_mkdir("root/Desktop");
if (!fat32_exists("root/projects")) fat32_mkdir("root/projects");
if (!fat32_exists("RecycleBin")) fat32_mkdir("RecycleBin");
if (!fat32_exists("Library/conf")) fat32_mkdir("Library/conf");
@@ -2223,7 +2157,7 @@ static void create_ramfs_files(void) {
"// BoredOS System Fetch Configuration\n"
"// ----------------------------------\n"
"// To use custom ascii art, uncomment the line below and point it to your file.\n"
"ascii_art_file=A:/Library/art/boredos.txt\n"
"ascii_art_file=/Library/art/boredos.txt\n"
"user_host_string=root@boredos\n"
"separator=------------\n"
"\n"
@@ -2259,7 +2193,7 @@ static void create_ramfs_files(void) {
FAT32_FileHandle *fh = fat32_open("Apps/README.md", "w");
FAT32_FileHandle *fh = fat32_open("root/Apps/README.md", "w");
if (fh) {
const char *content =
"# All compiled C files in this directory are openable from any other directory by typing in the name of the compiled file by typing in the name of the compiled file.\n\n"
@@ -2269,24 +2203,24 @@ static void create_ramfs_files(void) {
fat32_close(fh);
}
fh = fat32_open("Documents/notes.txt", "w");
fh = fat32_open("root/Documents/notes.txt", "w");
if (fh) {
const char *content = "My Notes\n\n- First note\n- Second note\n";
fat32_write(fh, (void *)content, 39);
fat32_close(fh);
}
fh = fat32_open("Projects/project1.txt", "w");
fh = fat32_open("root/projects/project1.txt", "w");
if (fh) {
const char *content = "Project 1\n\nStatus: In Progress\n";
fat32_write(fh, (void *)content, 32);
fat32_close(fh);
}
fat32_open("Desktop/Recycle Bin.shortcut", "w");
fat32_open("root/Desktop/Recycle Bin.shortcut", "w");
fh = fat32_open("Apps/wordofgod.c", "w");
fh = fat32_open("root/Apps/wordofgod.c", "w");
if (fh) {
// Buffer the entire file content to write in one go
// This prevents issues with multiple small writes causing truncation
@@ -2340,7 +2274,7 @@ static void create_ramfs_files(void) {
fat32_close(fh);
}
fh = fat32_open("Apps/DOOM.c", "w");
fh = fat32_open("root/Apps/DOOM.c", "w");
if (fh) {
const char *content =
"int main(){\n"
@@ -2389,9 +2323,13 @@ void cmd_init(void) {
// Initialize cmd state (per-window context)
CmdState *state = (CmdState*)kmalloc(sizeof(CmdState));
if (state) {
state->current_drive = 'A';
state->current_dir[0] = '/';
state->current_dir[1] = 0;
state->current_dir[1] = 'r';
state->current_dir[2] = 'o';
state->current_dir[3] = 'o';
state->current_dir[4] = 't';
state->current_dir[5] = 0;
win_cmd.data = state;
cmd_state = state; // Set static pointer
}

View File

@@ -5,6 +5,7 @@
#include "graphics.h"
#include "font_manager.h"
#include "fat32.h"
#include "vfs.h"
#include "disk.h"
#include "wm.h"
#include "memory_manager.h"
@@ -239,8 +240,8 @@ static void explorer_draw_icon_label(int x, int y, const char *label, uint32_t c
static bool check_desktop_limit_explorer(Window *win) {
ExplorerState *state = (ExplorerState*)win->data;
if (explorer_str_starts_with(state->current_path, "/Desktop")) {
if (explorer_strcmp(state->current_path, "/Desktop") == 0 || explorer_strcmp(state->current_path, "/Desktop/") == 0) { // Check if root desktop
if (explorer_str_starts_with(state->current_path, "/root/Desktop")) {
if (explorer_strcmp(state->current_path, "/root/Desktop") == 0 || explorer_strcmp(state->current_path, "/root/Desktop/") == 0) { // Check if root desktop
if (state->item_count >= desktop_max_cols * (desktop_max_rows_per_col > 1 ? desktop_max_rows_per_col - 1 : 0)) {
state->dialog_state = DIALOG_ERROR;
explorer_strcpy(state->dialog_input, "Desktop is full!");
@@ -302,14 +303,14 @@ static void dialog_confirm_create_file(Window *win) {
}
explorer_strcat(full_path, state->dialog_input);
if (fat32_exists(full_path)) {
if (vfs_exists(full_path)) {
state->dialog_state = DIALOG_CREATE_REPLACE_CONFIRM;
return;
}
FAT32_FileHandle *file = fat32_open(full_path, "w");
vfs_file_t *file = vfs_open(full_path, "w");
if (file) {
fat32_close(file);
vfs_close(file);
explorer_refresh_all();
}
@@ -325,9 +326,9 @@ static void dialog_force_create_file(Window *win) {
}
explorer_strcat(full_path, state->dialog_input);
FAT32_FileHandle *file = fat32_open(full_path, "w");
vfs_file_t *file = vfs_open(full_path, "w");
if (file) {
fat32_close(file);
vfs_close(file);
explorer_refresh_all();
}
dialog_close(win);
@@ -346,7 +347,7 @@ static void dialog_confirm_create_folder(Window *win) {
}
explorer_strcat(full_path, state->dialog_input);
if (fat32_mkdir(full_path)) {
if (vfs_mkdir(full_path)) {
explorer_refresh_all();
}
@@ -354,18 +355,18 @@ static void dialog_confirm_create_folder(Window *win) {
}
bool explorer_delete_permanently(const char *path) {
if (fat32_is_directory(path)) {
if (vfs_is_directory(path)) {
int capacity = 64;
FAT32_FileInfo *entries = (FAT32_FileInfo*)kmalloc(capacity * sizeof(FAT32_FileInfo));
vfs_dirent_t *entries = (vfs_dirent_t*)kmalloc(capacity * sizeof(vfs_dirent_t));
if (!entries) return false;
int count = fat32_list_directory(path, entries, capacity);
int count = vfs_list_directory(path, entries, capacity);
while (count == capacity) {
capacity *= 2;
FAT32_FileInfo *new_entries = (FAT32_FileInfo*)krealloc(entries, capacity * sizeof(FAT32_FileInfo));
vfs_dirent_t *new_entries = (vfs_dirent_t*)krealloc(entries, capacity * sizeof(vfs_dirent_t));
if (!new_entries) { kfree(entries); return false; }
entries = new_entries;
count = fat32_list_directory(path, entries, capacity);
count = vfs_list_directory(path, entries, capacity);
}
for (int i = 0; i < count; i++) {
@@ -381,13 +382,13 @@ bool explorer_delete_permanently(const char *path) {
if (entries[i].is_directory) {
explorer_delete_permanently(child_path);
} else {
fat32_delete(child_path);
vfs_delete(child_path);
}
}
kfree(entries);
return fat32_rmdir(path);
return vfs_rmdir(path);
} else {
return fat32_delete(path);
return vfs_delete(path);
}
}
@@ -421,10 +422,10 @@ bool explorer_delete_recursive(const char *path) {
char origin_path[FAT32_MAX_PATH];
explorer_strcpy(origin_path, dest_path);
explorer_strcat(origin_path, ".origin");
FAT32_FileHandle *fh = fat32_open(origin_path, "w");
vfs_file_t *fh = vfs_open(origin_path, "w");
if (fh) {
fat32_write(fh, path, explorer_strlen(path));
fat32_close(fh);
vfs_write(fh, path, explorer_strlen(path));
vfs_close(fh);
}
explorer_copy_recursive(path, dest_path);
@@ -467,19 +468,19 @@ bool explorer_clipboard_has_content(void) {
}
static bool explorer_copy_recursive(const char *src_path, const char *dest_path) {
if (fat32_is_directory(src_path)) {
if (!fat32_mkdir(dest_path)) return false;
if (vfs_is_directory(src_path)) {
if (!vfs_mkdir(dest_path)) return false;
int capacity = 64;
FAT32_FileInfo *files = (FAT32_FileInfo*)kmalloc(capacity * sizeof(FAT32_FileInfo));
vfs_dirent_t *files = (vfs_dirent_t*)kmalloc(capacity * sizeof(vfs_dirent_t));
if (!files) return false;
int count = fat32_list_directory(src_path, files, capacity);
int count = vfs_list_directory(src_path, files, capacity);
while (count == capacity) {
capacity *= 2;
FAT32_FileInfo *new_files = (FAT32_FileInfo*)krealloc(files, capacity * sizeof(FAT32_FileInfo));
vfs_dirent_t *new_files = (vfs_dirent_t*)krealloc(files, capacity * sizeof(vfs_dirent_t));
if (!new_files) { kfree(files); return false; }
files = new_files;
count = fat32_list_directory(src_path, files, capacity);
count = vfs_list_directory(src_path, files, capacity);
}
for (int i = 0; i < count; i++) {
if (explorer_strcmp(files[i].name, ".") == 0 || explorer_strcmp(files[i].name, "..") == 0) continue;
@@ -498,22 +499,22 @@ static bool explorer_copy_recursive(const char *src_path, const char *dest_path)
kfree(files);
return true;
} else {
FAT32_FileHandle *src = fat32_open(src_path, "r");
FAT32_FileHandle *dst = fat32_open(dest_path, "w");
vfs_file_t *src = vfs_open(src_path, "r");
vfs_file_t *dst = vfs_open(dest_path, "w");
bool success = false;
if (src && dst) {
uint8_t *buf = (uint8_t*)kmalloc(4096);
if (buf) {
int bytes;
success = true;
while ((bytes = fat32_read(src, buf, 4096)) > 0) {
if (fat32_write(dst, buf, bytes) != bytes) { success = false; break; }
while ((bytes = vfs_read(src, buf, 4096)) > 0) {
if (vfs_write(dst, buf, bytes) != bytes) { success = false; break; }
}
kfree(buf);
}
}
if (src) fat32_close(src);
if (dst) fat32_close(dst);
if (src) vfs_close(src);
if (dst) vfs_close(dst);
return success;
}
}
@@ -544,10 +545,10 @@ static void explorer_perform_paste(Window *win, const char *dest_dir) {
explorer_copy_file_internal(clipboard_path, dest_dir);
if (clipboard_action == 2) {
if (fat32_is_directory(clipboard_path)) {
if (vfs_is_directory(clipboard_path)) {
explorer_delete_permanently(clipboard_path);
} else {
fat32_delete(clipboard_path);
vfs_delete(clipboard_path);
}
clipboard_action = 0;
}
@@ -580,7 +581,7 @@ void explorer_clipboard_paste(Window *win, const char *dest_dir) {
}
explorer_strcat(dest_path, filename);
if (fat32_exists(dest_path)) {
if (vfs_exists(dest_path)) {
state->dialog_state = DIALOG_REPLACE_CONFIRM;
explorer_strcpy(state->dialog_dest_dir, dest_dir);
return;
@@ -605,10 +606,10 @@ void explorer_create_shortcut(Window *win, const char *target_path) {
explorer_strcat(shortcut_path, filename);
explorer_strcat(shortcut_path, ".shortcut");
FAT32_FileHandle *fh = fat32_open(shortcut_path, "w");
vfs_file_t *fh = vfs_open(shortcut_path, "w");
if (fh) {
fat32_write(fh, target_path, explorer_strlen(target_path));
fat32_close(fh);
vfs_write(fh, target_path, explorer_strlen(target_path));
vfs_close(fh);
explorer_refresh_all();
}
}
@@ -680,18 +681,18 @@ static void explorer_restore_file(Window *win, int item_idx) {
explorer_strcat(origin_file_path, ".origin");
char original_path[FAT32_MAX_PATH] = {0};
FAT32_FileHandle *fh = fat32_open(origin_file_path, "r");
vfs_file_t *fh = vfs_open(origin_file_path, "r");
if (fh) {
int len = fat32_read(fh, original_path, FAT32_MAX_PATH - 1);
int len = vfs_read(fh, original_path, FAT32_MAX_PATH - 1);
if (len > 0) original_path[len] = 0;
fat32_close(fh);
vfs_close(fh);
}
if (original_path[0] == 0) return;
explorer_copy_recursive(recycle_path, original_path);
explorer_delete_permanently(recycle_path);
fat32_delete(origin_file_path);
vfs_delete(origin_file_path);
explorer_refresh_all();
}
@@ -704,16 +705,17 @@ static void explorer_load_directory(Window *win, const char *path) {
state->item_count = 0;
int capacity = EXPLORER_INITIAL_CAPACITY;
FAT32_FileInfo *entries = (FAT32_FileInfo*)kmalloc(capacity * sizeof(FAT32_FileInfo));
vfs_dirent_t *entries = (vfs_dirent_t*)kmalloc(capacity * sizeof(vfs_dirent_t));
if (!entries) return;
int count = fat32_list_directory(path, entries, capacity);
int count = vfs_list_directory(path, entries, capacity);
while (count == capacity) {
capacity *= 2;
FAT32_FileInfo *new_entries = (FAT32_FileInfo*)krealloc(entries, capacity * sizeof(FAT32_FileInfo));
vfs_dirent_t *new_entries = (vfs_dirent_t*)krealloc(entries, capacity * sizeof(vfs_dirent_t));
if (!new_entries) { kfree(entries); return; }
entries = new_entries;
count = fat32_list_directory(path, entries, capacity);
count = vfs_list_directory(path, entries, capacity);
}
if (state->items_capacity < count) {
@@ -804,7 +806,7 @@ void explorer_open_directory(const char *path) {
}
static void explorer_open_target(const char *path) {
if (fat32_is_directory(path)) {
if (vfs_is_directory(path)) {
explorer_open_directory(path);
} else {
if (explorer_str_ends_with(path, ".elf")) {
@@ -864,11 +866,11 @@ static void explorer_open_item(Window *win, int index) {
return;
}
FAT32_FileHandle *fh = fat32_open(full_path, "r");
vfs_file_t *fh = vfs_open(full_path, "r");
if (fh) {
char buf[FAT32_MAX_PATH];
int len = fat32_read(fh, buf, 255);
fat32_close(fh);
int len = vfs_read(fh, buf, 255);
vfs_close(fh);
if (len > 0) {
buf[len] = 0;
explorer_open_target(buf);
@@ -924,48 +926,11 @@ static void explorer_paint(Window *win) {
draw_rect(offset_x, offset_y, win->w - 8, win->h - 28, COLOR_DARK_BG);
char drive_label[20];
char current_drv = 'A';
if (state->current_path[0] && state->current_path[1] == ':') {
current_drv = state->current_path[0];
} else if (state->current_path[0] && (state->current_path[0] >= 'A' && state->current_path[0] <= 'Z')) {
current_drv = state->current_path[0];
}
const char *type_str = "RAM";
Disk *drv = disk_get_by_letter(current_drv);
if (drv) {
switch (drv->type) {
case DISK_TYPE_RAM: type_str = "RAM"; break;
case DISK_TYPE_IDE: type_str = "IDE"; break;
case DISK_TYPE_SATA: type_str = "SATA"; break;
case DISK_TYPE_USB: type_str = "USB"; break;
default: type_str = "???"; break;
}
}
int di = 0;
drive_label[di++] = '[';
drive_label[di++] = ' ';
drive_label[di++] = current_drv;
drive_label[di++] = ':';
const char *ts = type_str;
while (*ts) drive_label[di++] = *ts++;
drive_label[di++] = ' ';
drive_label[di++] = ' ';
drive_label[di++] = ' ';
drive_label[di++] = ']';
drive_label[di] = 0;
ttf_font_t *ttf_ = graphics_get_current_ttf();
int drive_label_w = ttf_ ? font_manager_get_string_width(ttf_, drive_label) + 16 : 80;
if (drive_label_w < 60) drive_label_w = 60;
draw_rounded_rect_filled(win->x + 4, offset_y + 3, drive_label_w, 22, 5, COLOR_DARK_PANEL);
draw_string(win->x + 12, offset_y + 8, drive_label, COLOR_DARK_TEXT);
int path_height = 22;
int path_x = offset_x + drive_label_w + 8;
int path_w = win->w - 16 - drive_label_w - 8;
int path_x = offset_x;
int path_w = win->w - 16 - 8;
draw_rounded_rect_filled(path_x, offset_y + 3, path_w, path_height, 5, COLOR_DARK_PANEL);
draw_string(path_x + 6, offset_y + 8, "Path:", COLOR_DARK_TEXT);
int path_label_w = ttf_ ? font_manager_get_string_width(ttf_, "Path:") : 40;
@@ -1013,35 +978,6 @@ static void explorer_paint(Window *win) {
graphics_pop_clipping(); // Pop content clipping
graphics_pop_clipping(); // Pop main window clipping
if (state->drive_menu_visible) {
int menu_x = win->x + 4;
int menu_y = offset_y + 26;
int menu_w = 80;
int count = disk_get_count();
int menu_h = count * 25;
draw_rounded_rect_filled(menu_x, menu_y, menu_w, menu_h, 6, COLOR_DARK_PANEL);
for (int i = 0; i < count; i++) {
Disk *d = disk_get_by_index(i);
if (d) {
char buf[16];
buf[0] = d->letter;
buf[1] = ':';
buf[2] = ' ';
int n = 0; while(d->name[n] && n < 10) { buf[3+n] = d->name[n]; n++; }
buf[3+n] = 0;
if (d->letter == current_drv) {
draw_rounded_rect_filled(menu_x + 2, menu_y + i*25 + 2, menu_w - 4, 21, 4, 0xFF4A90E2);
draw_string(menu_x + 5, menu_y + i*25 + 6, buf, COLOR_WHITE);
} else {
draw_string(menu_x + 5, menu_y + i*25 + 6, buf, COLOR_DARK_TEXT);
}
}
}
}
if (state->dropdown_menu_visible) {
int menu_x = dropdown_btn_x;
int menu_y = offset_y + 26;
@@ -1315,7 +1251,7 @@ static void explorer_handle_click(Window *win, int x, int y) {
if (new_path[explorer_strlen(new_path)-1] != '/') explorer_strcat(new_path, "/");
explorer_strcat(new_path, state->dialog_input);
if (fat32_rename(state->dialog_target_path, new_path)) explorer_refresh_all();
if (vfs_rename(state->dialog_target_path, new_path)) explorer_refresh_all();
dialog_close(win);
return;
}
@@ -1336,31 +1272,7 @@ static void explorer_handle_click(Window *win, int x, int y) {
return;
}
if (state->drive_menu_visible) {
int menu_x = 4;
int menu_y = 26;
int menu_w = 80;
int count = disk_get_count();
int menu_h = count * 25;
if (x >= menu_x && x < menu_x + menu_w && y >= menu_y && y < menu_y + menu_h) {
int idx = (y - menu_y) / 25;
Disk *d = disk_get_by_index(idx);
if (d) {
char path[4];
path[0] = d->letter;
path[1] = ':';
path[2] = '/';
path[3] = 0;
explorer_load_directory(win, path);
}
state->drive_menu_visible = false;
return;
}
state->drive_menu_visible = false;
return;
}
if (state->dropdown_menu_visible) {
int dropdown_btn_x = win->w - 90;
@@ -1396,17 +1308,11 @@ static void explorer_handle_click(Window *win, int x, int y) {
}
int button_y = 3;
if (x >= 4 && x < 64 && y >= button_y && y < button_y + 22) {
state->drive_menu_visible = !state->drive_menu_visible;
state->dropdown_menu_visible = false;
return;
}
if (WIDGET_CLICKED(&state->btn_dropdown, win->x + x, win->y + y + 20)) {
state->btn_dropdown.pressed = false;
dropdown_menu_toggle(win);
state->drive_menu_visible = false;
return;
}
@@ -1477,7 +1383,7 @@ static void explorer_handle_key(Window *win, char c, bool pressed) {
explorer_strcpy(new_path, state->current_path);
if (new_path[explorer_strlen(new_path)-1] != '/') explorer_strcat(new_path, "/");
explorer_strcat(new_path, state->dialog_input);
if (fat32_rename(state->dialog_target_path, new_path)) explorer_refresh(win);
if (vfs_rename(state->dialog_target_path, new_path)) explorer_refresh(win);
dialog_close(win);
}
} else if (c == 19) {
@@ -1793,10 +1699,10 @@ static void explorer_perform_move_internal(Window *win, const char *source_path,
char origin_path[FAT32_MAX_PATH];
explorer_strcpy(origin_path, dest_path);
explorer_strcat(origin_path, ".origin");
FAT32_FileHandle *fh = fat32_open(origin_path, "w");
vfs_file_t *fh = vfs_open(origin_path, "w");
if (fh) {
fat32_write(fh, source_path, explorer_strlen(source_path));
fat32_close(fh);
vfs_write(fh, source_path, explorer_strlen(source_path));
vfs_close(fh);
}
}
@@ -1804,7 +1710,7 @@ static void explorer_perform_move_internal(Window *win, const char *source_path,
char origin_path[FAT32_MAX_PATH];
explorer_strcpy(origin_path, source_path);
explorer_strcat(origin_path, ".origin");
fat32_delete(origin_path);
vfs_delete(origin_path);
}
if (explorer_copy_recursive(source_path, dest_path)) {
@@ -1838,7 +1744,7 @@ void explorer_import_file_to(Window *win, const char *source_path, const char *d
if (dest_path[explorer_strlen(dest_path) - 1] != '/') explorer_strcat(dest_path, "/");
explorer_strcat(dest_path, filename);
if (fat32_exists(dest_path) && explorer_strcmp(source_path, dest_path) != 0) {
if (vfs_exists(dest_path) && explorer_strcmp(source_path, dest_path) != 0) {
explorer_strcpy(state->dialog_move_src, source_path);
explorer_strcpy(state->dialog_dest_dir, dest_dir);
state->dialog_state = DIALOG_REPLACE_MOVE_CONFIRM;

View File

@@ -108,6 +108,14 @@ int get_screen_height(void) {
return g_fb ? g_fb->height : 0;
}
uint64_t graphics_get_fb_addr(void) {
return g_fb ? (uint64_t)g_fb->address : 0;
}
int graphics_get_fb_bpp(void) {
return g_fb ? g_fb->bpp : 0;
}
// Merge new dirty rect with existing one
static void merge_dirty_rect(int x, int y, int w, int h) {
if (!g_dirty.active) {

View File

@@ -42,6 +42,8 @@ void draw_boredos_logo(int x, int y, int scale);
// Get screen dimensions
int get_screen_width(void);
int get_screen_height(void);
uint64_t graphics_get_fb_addr(void);
int graphics_get_fb_bpp(void);
void graphics_update_resolution(int width, int height, int bpp, void* fb_addr, int color_mode);
// Dirty rectangle management

View File

@@ -188,7 +188,7 @@ static void refresh_desktop_icons(void) {
FAT32_FileInfo *files = (FAT32_FileInfo*)kmalloc(MAX_DESKTOP_ICONS * sizeof(FAT32_FileInfo));
if (!files) return;
int file_count = fat32_list_directory("/Desktop", files, MAX_DESKTOP_ICONS);
int file_count = fat32_list_directory("/root/Desktop", files, MAX_DESKTOP_ICONS);
// Temp array to hold new state
DesktopIcon new_icons[MAX_DESKTOP_ICONS];
@@ -310,7 +310,7 @@ void wm_refresh_desktop(void) {
}
static void create_desktop_shortcut(const char *app_name) {
char path[128] = "/Desktop/";
char path[128] = "/root/Desktop/";
int p = 9;
int n = 0; while(app_name[n]) path[p++] = app_name[n++];
const char *ext = ".shortcut";
@@ -1358,7 +1358,7 @@ static void wm_paint_region(int y_start, int y_end, DirtyRect dirty, int pass) {
if (str_ends_with(icon->name, ".elf")) draw_elf_icon(icon->x, icon->y, icon->name);
else if (str_ends_with(icon->name, ".pnt")) draw_paint_icon(icon->x, icon->y, icon->name);
else if (is_image_file(icon->name)) {
char full_path[128] = "/Desktop/"; int p=9; int n=0; while(icon->name[n] && p < 127) full_path[p++] = icon->name[n++]; full_path[p]=0;
char full_path[128] = "/root/Desktop/"; int p=14; int n=0; while(icon->name[n] && p < 127) full_path[p++] = icon->name[n++]; full_path[p]=0;
draw_image_icon(icon->x, icon->y, full_path);
draw_icon_label(icon->x, icon->y, icon->name);
}
@@ -1702,13 +1702,13 @@ void wm_handle_click(int x, int y) {
if (item == 0 && desktop_menu_target_icon != -1) { // Cut
DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon];
char path[128] = "/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
char path[128] = "/root/Desktop/";
int p=14; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
explorer_clipboard_cut(path);
} else if (item == 1 && desktop_menu_target_icon != -1) { // Copy
DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon];
char path[128] = "/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
char path[128] = "/root/Desktop/";
int p=14; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
explorer_clipboard_copy(path);
} else if (item == 0 && desktop_menu_target_icon == -1) { // New File
desktop_dialog_state = 1;
@@ -1729,13 +1729,13 @@ void wm_handle_click(int x, int y) {
int old_count = desktop_icon_count;
if (desktop_menu_target_icon != -1 && desktop_icons[desktop_menu_target_icon].type == 1) {
// Paste into folder
char path[128] = "/Desktop/";
char path[128] = "/root/Desktop/";
DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon];
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
int p=14; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
explorer_clipboard_paste(&win_explorer, path);
} else {
// Paste to desktop
explorer_clipboard_paste(&win_explorer, "/Desktop");
explorer_clipboard_paste(&win_explorer, "/root/Desktop");
}
refresh_desktop_icons();
@@ -1756,8 +1756,8 @@ void wm_handle_click(int x, int y) {
}
else if (item == 3 && desktop_menu_target_icon != -1) { // Delete
DesktopIcon *icon = &desktop_icons[desktop_menu_target_icon];
char path[128] = "/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
char path[128] = "/root/Desktop/";
int p=14; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
explorer_delete_recursive(path);
refresh_desktop_icons();
}
@@ -1782,10 +1782,10 @@ void wm_handle_click(int x, int y) {
int dlg_x = (sw - 300) / 2; int dlg_y = (sh - 110) / 2;
if (rect_contains(dlg_x + 50, dlg_y + 65, 80, 25, x, y)) { // Confirm
if (desktop_dialog_state == 8) { // Rename
char old_path[128] = "/Desktop/";
char new_path[128] = "/Desktop/";
int p=9; int n=0; while(desktop_icons[desktop_dialog_target].name[n]) old_path[p++] = desktop_icons[desktop_dialog_target].name[n++]; old_path[p]=0;
p=9; n=0; while(desktop_dialog_input[n]) new_path[p++] = desktop_dialog_input[n++]; new_path[p]=0;
char old_path[128] = "/root/Desktop/";
char new_path[128] = "/root/Desktop/";
int p=14; int n=0; while(desktop_icons[desktop_dialog_target].name[n]) old_path[p++] = desktop_icons[desktop_dialog_target].name[n++]; old_path[p]=0;
p=14; n=0; while(desktop_dialog_input[n]) new_path[p++] = desktop_dialog_input[n++]; new_path[p]=0;
if (fat32_rename(old_path, new_path)) {
refresh_desktop_icons();
@@ -1795,8 +1795,8 @@ void wm_handle_click(int x, int y) {
if (desktop_icon_count >= desktop_max_cols * desktop_max_rows_per_col) {
wm_show_message("Error", "Desktop is full!");
} else if (desktop_dialog_input[0] != 0) {
char path[128] = "/Desktop/";
int p=9; int n=0; while(desktop_dialog_input[n]) path[p++] = desktop_dialog_input[n++]; path[p]=0;
char path[128] = "/root/Desktop/";
int p=14; int n=0; while(desktop_dialog_input[n]) path[p++] = desktop_dialog_input[n++]; path[p]=0;
if (desktop_dialog_state == 1) {
FAT32_FileHandle *fh = fat32_open(path, "w");
if (fh) fat32_close(fh);
@@ -2130,8 +2130,8 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) {
drag_icon_orig_x = icon->x;
drag_icon_orig_y = icon->y;
// Construct path
char path[128] = "/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
char path[128] = "/root/Desktop/";
int p=14; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
int k=0; while(path[k]) { drag_file_path[k] = path[k]; k++; } drag_file_path[k]=0;
}
// 2. Check Explorer Items
@@ -2184,7 +2184,7 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) {
if (start_menu_pending_app) {
// Launch App
if (str_starts_with(start_menu_pending_app, "Files")) {
explorer_open_directory("/");
explorer_open_directory("/root");
} else if (str_starts_with(start_menu_pending_app, "Notepad")) {
Window *existing = wm_find_window_by_title_locked("Notepad");
if (existing) {
@@ -2279,8 +2279,8 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) {
if (!handled) {
// Generic Shortcut Handling
char path[128] = "/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
char path[128] = "/root/Desktop/";
int p=14; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
if (str_ends_with(icon->name, ".shortcut") && !str_starts_with(icon->name, "Recycle Bin")) {
FAT32_FileHandle *fh = fat32_open(path, "r");
@@ -2303,12 +2303,12 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) {
}
}
} else if (icon->type == 1) { // Folder
char path[128] = "/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
char path[128] = "/root/Desktop/";
int p=14; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
explorer_open_directory(path);
} else { // File
char path[128] = "/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
char path[128] = "/root/Desktop/";
int p=14; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
if (str_ends_with(icon->name, ".elf")) {
process_create_elf(path, NULL);
@@ -2354,7 +2354,7 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) {
explorer_import_file(drop_win, drag_file_path);
}
if (str_starts_with(drag_file_path, "/Desktop/")) {
if (str_starts_with(drag_file_path, "/root/Desktop/")) {
refresh_desktop_icons();
}
} else {
@@ -2366,15 +2366,15 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) {
bool dropped_on_target = false;
for (int i = 0; i < desktop_icon_count; i++) {
if (from_desktop) {
char path[128] = "/Desktop/";
int p=9; int n=0; while(desktop_icons[i].name[n]) path[p++] = desktop_icons[i].name[n++]; path[p]=0;
char path[128] = "/root/Desktop/";
int p=14; int n=0; while(desktop_icons[i].name[n]) path[p++] = desktop_icons[i].name[n++]; path[p]=0;
if (str_eq(path, drag_file_path) != 0) continue;
}
if (rect_contains(desktop_icons[i].x + 20, desktop_icons[i].y, 40, 40, mx, my)) {
if (desktop_icons[i].type == 1) {
char target_path[256] = "/Desktop/";
int p=9; int n=0; while(desktop_icons[i].name[n]) target_path[p++] = desktop_icons[i].name[n++]; target_path[p]=0;
char target_path[256] = "/root/Desktop/";
int p=14; int n=0; while(desktop_icons[i].name[n]) target_path[p++] = desktop_icons[i].name[n++]; target_path[p]=0;
explorer_import_file_to(&win_explorer, drag_file_path, target_path);
refresh_desktop_icons();
dropped_on_target = true;
@@ -2397,7 +2397,7 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) {
if (desktop_icon_count >= desktop_max_cols * desktop_max_rows_per_col) {
wm_show_message("Error", "Desktop is full!");
} else {
explorer_import_file_to(&win_explorer, drag_file_path, "/Desktop");
explorer_import_file_to(&win_explorer, drag_file_path, "/root/Desktop");
}
// Handle insertion at specific position
@@ -2453,8 +2453,8 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) {
} else if (!dropped_on_target) {
int dragged_idx = -1;
for(int i=0; i<desktop_icon_count; i++) {
char path[128] = "/Desktop/";
int p=9; int n=0; while(desktop_icons[i].name[n]) path[p++] = desktop_icons[i].name[n++]; path[p]=0;
char path[128] = "/root/Desktop/";
int p=14; int n=0; while(desktop_icons[i].name[n]) path[p++] = desktop_icons[i].name[n++]; path[p]=0;
if (str_eq(path, drag_file_path) != 0) {
dragged_idx = i;
break;
@@ -2634,10 +2634,10 @@ static void wm_dispatch_key(char c, bool pressed) {
int len = 0; while(desktop_dialog_input[len]) len++;
if (c == '\n') {
if (desktop_dialog_state == 8) { // Rename
char old_path[128] = "/Desktop/";
char new_path[128] = "/Desktop/";
int p=9; int n=0; while(desktop_icons[desktop_dialog_target].name[n]) old_path[p++] = desktop_icons[desktop_dialog_target].name[n++]; old_path[p]=0;
p=9; n=0; while(desktop_dialog_input[n]) new_path[p++] = desktop_dialog_input[n++]; new_path[p]=0;
char old_path[128] = "/root/Desktop/";
char new_path[128] = "/root/Desktop/";
int p=14; int n=0; while(desktop_icons[desktop_dialog_target].name[n]) old_path[p++] = desktop_icons[desktop_dialog_target].name[n++]; old_path[p]=0;
p=14; n=0; while(desktop_dialog_input[n]) new_path[p++] = desktop_dialog_input[n++]; new_path[p]=0;
if (fat32_rename(old_path, new_path)) {
refresh_desktop_icons();
explorer_refresh_all();
@@ -2646,8 +2646,8 @@ static void wm_dispatch_key(char c, bool pressed) {
if (desktop_icon_count >= desktop_max_cols * desktop_max_rows_per_col) {
wm_show_message("Error", "Desktop is full!");
} else if (desktop_dialog_input[0] != 0) {
char path[128] = "/Desktop/";
int p=9; int n=0; while(desktop_dialog_input[n]) path[p++] = desktop_dialog_input[n++]; path[p]=0;
char path[128] = "/root/Desktop/";
int p=14; int n=0; while(desktop_dialog_input[n]) path[p++] = desktop_dialog_input[n++]; path[p]=0;
if (desktop_dialog_state == 1) {
FAT32_FileHandle *fh = fat32_open(path, "w");
if (fh) fat32_close(fh);

View File

@@ -25,7 +25,7 @@ void wm_lock_release(uint64_t flags);
#define COLOR_ORANGE 0xFFFF7F00
#define COLOR_YELLOW 0xFFFFFF00
#define COLOR_GREEN 0xFF00FF00
#define COLOR_BLUE 0xFF0000FF
#define COLOR_LIGHTBLUE 0xFF0000FF
#define COLOR_APPLE_INDIGO 0xFF4B0082
#define COLOR_APPLE_VIOLET 0xFF9400D3