diff --git a/.gitignore b/.gitignore index 8c607865..05d6031b 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,9 @@ build/ .vscode/ bochsout.txt core +!src/kernel/core/ +!src/kernel/fs/core/ +!include/kernel/core/ usermode !src/usermode/ !src/usermode/* diff --git a/AGENTS.md b/AGENTS.md index 33b69b49..ebb4c643 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,6 +4,7 @@ Kernel sources live in `src/` (drivers, memory, scheduler, syscall) with exported headers under `include/`. Userland libc and runtime live in `user/`, while runnable programs (e.g., shell, Doom helpers) sit in `app/`. Build products land in `build/` and the assembled disk image in `menios.hdd`/`menios.iso`. Design notes and roadmaps are in `docs/`; third-party toolchains are vendored in `vendor/`. Tests reside in `test/` alongside Unity helpers and host stubs. ## Build, Test, and Development Commands +- `make binutils` – build the cross binutils toolchain into `build/bin/`. - `make userland` – build the libc, runtime, and user programs into `build/bin/`. - `make build` – produce the kernel, sync Limine assets, and pack the bootable HDD/ISO images. - `make run` – boot the built image in QEMU using the project default flags. @@ -18,4 +19,4 @@ Follow the two-space indentation rule across C and assembly; never commit tabs. Host tests live in `test/test_*.c` and use Unity assertions; name helpers after the feature under test (e.g., `test_heap_virtual.c`). Ensure new functionality ships with tests or updates existing cases. Run `make test` locally; the suite builds each test with kernel stubs and drops binaries in place. Boot the image with `make run` (or on hardware) when touching kernel low-level code, IPC, or filesystems to confirm no runtime regressions. ## Commit & Pull Request Guidelines -Author commits in present tense with a concise (<72 char) subject and an explanatory body covering what changes and why (e.g., `Fix buffer overflow in vsprintk`). Reference related issues or tasks using `#123` syntax and update `tasks.json` to reflect progress since it is the source of truth. Squash fix-ups before submitting. Pull requests should include a clear summary, testing notes, screenshots for UI-facing shells if relevant, and links to roadmap items being advanced. Flag breaking changes or tooling updates explicitly and update documentation (README, docs/) when behavior shifts. +Author commits in past tense with a concise (<72 char) subject and an explanatory body covering what changed and why (e.g., `Fixed buffer overflow in vsprintk`). Reference related issues or tasks using `#123` syntax and update `tasks.json` to reflect progress since it is the source of truth. Squash fix-ups before submitting. Pull requests should include a clear summary, testing notes, screenshots for UI-facing shells if relevant, and links to roadmap items being advanced. Flag breaking changes or tooling updates explicitly and update documentation (README, docs/) when behavior shifts. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b7cbc122..11bc1337 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -321,39 +321,44 @@ void test_memory_allocation_success(void) { ``` menios/ -├── src/ # Kernel source code -│ ├── kernel/ # Core kernel functionality -│ ├── drivers/ # Hardware drivers -│ └── libc/ # Basic C library functions -├── include/ # Header files -│ └── kernel/ # Kernel-specific headers -├── test/ # Unit tests -├── bin/ # Build artifacts -├── vendor/ # Third-party dependencies -└── docs/ # Project documentation +├── app/ # User-facing programs installed into /bin +├── build/ # Generated artifacts (kernel, userland, disk images) +├── docs/ # Architecture notes and roadmaps +├── include/ # Exported headers (mirrors source hierarchy) +├── src/ # Kernel and shared libc sources +│ ├── kernel/ # Kernel subsystems (core, arch, drivers, fs, …) +│ └── libc/ # Target libc implementation shared with userland +├── test/ # Unity-based host tests and stubs +├── user/ # Userland runtime (crt0, libc glue) +└── vendor/ # Third-party toolchains and helpers (doomgeneric, uACPI) ``` ### Key Components -1. **Memory Management** (`src/kernel/memory/`) +1. **Memory Management** (`src/kernel/mem/`) - Physical memory manager (PMM) - Virtual memory manager (VMM) - Kernel heap (kmalloc) -2. **Process Management** (`src/kernel/process/`) +2. **Process Management** (`src/kernel/proc/`) - Kernel threading - Basic scheduler - Synchronization primitives -3. **I/O Systems** (`src/drivers/`) - - PS/2 keyboard driver - - Console/framebuffer - - Future: VGA, audio, storage +3. **I/O Systems** (`src/kernel/drivers/`, `src/kernel/console/`, `src/kernel/framebuffer/`) + - PS/2 keyboard driver and input pipeline + - ANSI/serial/front-buffer consoles + - Storage controllers (AHCI) and future devices 4. **System Calls** (`src/kernel/syscall/`) - Syscall dispatcher - Individual syscall implementations +Additional detail on the layout can be found in +[`docs/architecture/code_structure.md`](docs/architecture/code_structure.md). When +adding new modules ensure sources, headers, and Makefile entries follow the +patterns outlined there. + ### Adding New Features When adding new features: diff --git a/Makefile b/Makefile index 38b0cc98..e6c7a708 100644 --- a/Makefile +++ b/Makefile @@ -64,13 +64,108 @@ LIBDIR = lib UACPI_OBJ = $(OBJDIR)/uacpi KERNEL_OBJ = $(OBJDIR)/kernel -KERNEL_SRC = $(shell find -L src -path 'src/usermode' -prune -o -type f -name '*.c' -print) -KERNEL_ASM = $(shell find -L src/kernel -type f \( -name '*.s' -o -name '*.S' \)) +KERNEL_SRC = \ + src/kernel/acpi/acpi.c \ + src/kernel/acpi/acpica.c \ + src/kernel/acpi/uacpi_menios.c \ + src/kernel/arch/x86_64/apic.c \ + src/kernel/arch/x86_64/cpu.c \ + src/kernel/arch/x86_64/gdt.c \ + src/kernel/arch/x86_64/idt.c \ + src/kernel/block/block_cache.c \ + src/kernel/block/block_device.c \ + src/kernel/console/ansi.c \ + src/kernel/console/console.c \ + src/kernel/console/vprintk.c \ + src/kernel/core/halt.c \ + src/kernel/core/logo.c \ + src/kernel/core/main.c \ + src/kernel/core/panic.c \ + src/kernel/core/services.c \ + src/kernel/drivers/audio/sb/sb.c \ + src/kernel/drivers/block/ahci/ahci.c \ + src/kernel/drivers/bus/pciroot/pci.c \ + src/kernel/drivers/bus/pciroot/pciroot.c \ + src/kernel/drivers/core/driver.c \ + src/kernel/drivers/input/ps2kb/ps2kb.c \ + src/kernel/fs/core/file.c \ + src/kernel/fs/core/pipe.c \ + src/kernel/fs/devfs/devfs.c \ + src/kernel/fs/fat32/fat32.c \ + src/kernel/fs/procfs/procfs.c \ + src/kernel/fs/tmpfs/tmpfs.c \ + src/kernel/fs/vfs/vfs.c \ + src/kernel/hw/hw.c \ + src/kernel/input/keyboard.c \ + src/kernel/ipc/shm.c \ + src/kernel/framebuffer/framebuffer.c \ + src/kernel/framebuffer/fonts.c \ + src/kernel/mem/compactor.c \ + src/kernel/mem/dma.c \ + src/kernel/mem/kmalloc.c \ + src/kernel/mem/kmmap.c \ + src/kernel/mem/mem.c \ + src/kernel/mem/mem_utils.c \ + src/kernel/mem/pmm.c \ + src/kernel/mem/sbrk.c \ + src/kernel/proc/kcondvar.c \ + src/kernel/proc/kmutex.c \ + src/kernel/proc/krwlock.c \ + src/kernel/proc/ksemaphore.c \ + src/kernel/proc/kthread.c \ + src/kernel/proc/proc.c \ + src/kernel/proc/signal.c \ + src/kernel/console/serial.c \ + src/kernel/syscall/entry.c \ + src/kernel/syscall/syscall.c \ + src/kernel/timer/hpet.c \ + src/kernel/timer/lapic.c \ + src/kernel/timer/rtc.c \ + src/kernel/timer/timer.c \ + src/kernel/timer/tsc.c \ + src/kernel/user/elf_loader.c \ + src/kernel/user/init.c \ + src/kernel/user/user_demo.c \ + src/kernel/user/vm.c \ + src/kernel/user/vm_region.c \ + src/libc/assert.c \ + src/libc/ctype.c \ + src/libc/errno.c \ + src/libc/itoa.c \ + src/libc/string.c \ + src/libc/time.c + +KERNEL_ASM = \ + src/kernel/arch/x86_64/context_switch.S \ + src/kernel/arch/x86_64/proc_entry.S \ + src/kernel/lgdt.s \ + src/kernel/lidt.s \ + src/kernel/pit.s \ + src/kernel/user/user_mode.S KERNEL_OBJS = $(patsubst %.c, %.o, $(KERNEL_SRC)) KERNEL_ASM_GAS_OBJS = $(patsubst %.S, %.o, $(filter %.S,$(KERNEL_ASM))) ARCH_NASM_OBJECTS := lgdt.o pit.o lidt.o -UACPI_SRC = $(shell find -L vendor/uacpi -type f -name '*.c') +UACPI_SRC = \ + vendor/uacpi/osi.c \ + vendor/uacpi/tables.c \ + vendor/uacpi/opregion.c \ + vendor/uacpi/io.c \ + vendor/uacpi/mutex.c \ + vendor/uacpi/default_handlers.c \ + vendor/uacpi/stdlib.c \ + vendor/uacpi/sleep.c \ + vendor/uacpi/event.c \ + vendor/uacpi/types.c \ + vendor/uacpi/shareable.c \ + vendor/uacpi/resources.c \ + vendor/uacpi/utilities.c \ + vendor/uacpi/interpreter.c \ + vendor/uacpi/registers.c \ + vendor/uacpi/notify.c \ + vendor/uacpi/opcodes.c \ + vendor/uacpi/namespace.c \ + vendor/uacpi/uacpi.c UACPI_OBJS := $(patsubst %.c, %.o, $(UACPI_SRC)) OBJS = $(KERNEL_OBJS) $(KERNEL_ASM_GAS_OBJS) $(UACPI_OBJS) @@ -102,9 +197,10 @@ MEM_ELF = $(OBJDIR)/usermode/mem.elf ALARM_DEMO_ELF = $(OBJDIR)/usermode/alarm_demo.elf TOUCH_ELF = $(OBJDIR)/usermode/touch.elf SHUTDOWN_ELF = $(OBJDIR)/usermode/shutdown.elf +MKNOD_ELF = $(OBJDIR)/usermode/mknod.elf -USER_PROGRAM_ELFS = $(MOSH_ELF) $(ECHO_ELF) $(CAT_ELF) $(ENV_ELF) $(TRUE_ELF) $(FALSE_ELF) $(LS_ELF) $(KILL_ELF) $(PS_ELF) $(STAT_ELF) $(REALPATH_ELF) $(MALLOC_STRESS_ELF) $(MEM_ELF) $(ALARM_DEMO_ELF) $(TOUCH_ELF) $(SHUTDOWN_ELF) -USERLAND_BINS = mosh echo cat env true false ls kill ps stat realpath malloc_stress mem alarm_demo touch shutdown +USER_PROGRAM_ELFS = $(MOSH_ELF) $(ECHO_ELF) $(CAT_ELF) $(ENV_ELF) $(TRUE_ELF) $(FALSE_ELF) $(LS_ELF) $(KILL_ELF) $(PS_ELF) $(STAT_ELF) $(REALPATH_ELF) $(MALLOC_STRESS_ELF) $(MEM_ELF) $(ALARM_DEMO_ELF) $(TOUCH_ELF) $(MKNOD_ELF) $(SHUTDOWN_ELF) +USERLAND_BINS = mosh echo cat env true false ls kill ps stat realpath malloc_stress mem alarm_demo touch mknod shutdown BINUTILS_TOOLS = as ld objdump nm ar ranlib readelf objcopy strip strings size addr2line @@ -569,6 +665,14 @@ else $(DOCKER) run --rm $(DOCKER_RUN_FLAGS) $(DOCKER_ENV) --mount type=bind,source=$$(pwd),target=/mnt $(DOCKER_IMAGE) /bin/sh -c "cd /mnt && make EXTRA_CFLAGS='$(EXTRA_CFLAGS)' $@" endif +$(MKNOD_ELF): app/mknod/mknod.c | sdk +ifeq ($(OS_NAME),linux) + @mkdir -p $(dir $@) + $(SDK_BIN_DIR)/menios-gcc $(EXTRA_CFLAGS) $< -o $@ +else + $(DOCKER) run --rm $(DOCKER_RUN_FLAGS) $(DOCKER_ENV) --mount type=bind,source=$$(pwd),target=/mnt $(DOCKER_IMAGE) /bin/sh -c "cd /mnt && make EXTRA_CFLAGS='$(EXTRA_CFLAGS)' $@" +endif + $(SHUTDOWN_ELF): app/shutdown/shutdown.c | sdk ifeq ($(OS_NAME),linux) @mkdir -p $(dir $@) @@ -778,17 +882,17 @@ ifneq ($(GCOV_ENABLED),) endif # Skip host-unsafe tests until proper stubs land. - for file in $(shell find -L test -type f -name 'test_*.c' ! -name 'test_kmalloc.c' ! -name 'test_malloc_stress.c' ! -name 'test_buddy_allocator.c' ! -name 'test_malloc_stats.c' ! -name 'test_malloc_direct.c' ! -name 'test_heap_virtual.c' ! -name 'test_scanf.c' ! -name 'test_system.c'); do \ + for file in $(HOST_TEST_SRCS); do \ gcc $(GCOV_FLAGS) -std=gnu11 -DMENIOS_NO_DEBUG -DMENIOS_HOST_TEST -DUNITY_EXCLUDE_SETJMP_H -I./include \ $$file \ test/unity.c \ test/stubs.c \ - src/kernel/file.c \ - src/kernel/fs/vfs.c \ - src/kernel/fs/pipe.c \ - src/kernel/fs/tmpfs.c \ - src/kernel/fs/procfs.c \ - src/kernel/fs/devfs.c \ + src/kernel/fs/core/file.c \ + src/kernel/fs/vfs/vfs.c \ + src/kernel/fs/core/pipe.c \ + src/kernel/fs/tmpfs/tmpfs.c \ + src/kernel/fs/procfs/procfs.c \ + src/kernel/fs/devfs/devfs.c \ src/kernel/syscall/syscall.c \ src/kernel/syscall/entry.c \ src/kernel/mem/pmm.c \ @@ -820,10 +924,11 @@ endif test/test_buddy_allocator.c \ test/unity.c \ test/stubs.c \ - src/kernel/file.c \ - src/kernel/fs/vfs.c \ - src/kernel/fs/pipe.c \ - src/kernel/fs/tmpfs.c \ + src/kernel/fs/core/file.c \ + src/kernel/fs/vfs/vfs.c \ + src/kernel/fs/core/pipe.c \ + src/kernel/fs/tmpfs/tmpfs.c \ + src/kernel/fs/devfs/devfs.c \ src/kernel/syscall/syscall.c \ src/kernel/syscall/entry.c \ src/kernel/mem/pmm.c \ @@ -855,10 +960,11 @@ endif test/test_malloc_direct.c \ test/unity.c \ test/stubs.c \ - src/kernel/file.c \ - src/kernel/fs/vfs.c \ - src/kernel/fs/pipe.c \ - src/kernel/fs/tmpfs.c \ + src/kernel/fs/core/file.c \ + src/kernel/fs/vfs/vfs.c \ + src/kernel/fs/core/pipe.c \ + src/kernel/fs/tmpfs/tmpfs.c \ + src/kernel/fs/devfs/devfs.c \ src/kernel/syscall/syscall.c \ src/kernel/syscall/entry.c \ src/kernel/mem/pmm.c \ @@ -890,10 +996,11 @@ src/libc/errno.c \ test/test_malloc_stats.c \ test/unity.c \ test/stubs.c \ - src/kernel/file.c \ - src/kernel/fs/vfs.c \ - src/kernel/fs/pipe.c \ - src/kernel/fs/tmpfs.c \ + src/kernel/fs/core/file.c \ + src/kernel/fs/vfs/vfs.c \ + src/kernel/fs/core/pipe.c \ + src/kernel/fs/tmpfs/tmpfs.c \ + src/kernel/fs/devfs/devfs.c \ src/kernel/syscall/syscall.c \ src/kernel/syscall/entry.c \ src/kernel/mem/pmm.c \ @@ -925,10 +1032,11 @@ src/libc/errno.c \ test/test_system.c \ test/unity.c \ test/stubs.c \ - src/kernel/file.c \ - src/kernel/fs/vfs.c \ - src/kernel/fs/pipe.c \ - src/kernel/fs/tmpfs.c \ + src/kernel/fs/core/file.c \ + src/kernel/fs/vfs/vfs.c \ + src/kernel/fs/core/pipe.c \ + src/kernel/fs/tmpfs/tmpfs.c \ + src/kernel/fs/devfs/devfs.c \ src/kernel/syscall/syscall.c \ src/kernel/syscall/entry.c \ src/kernel/mem/pmm.c \ @@ -960,10 +1068,11 @@ src/libc/errno.c \ test/test_heap_virtual.c \ test/unity.c \ test/stubs.c \ - src/kernel/file.c \ - src/kernel/fs/vfs.c \ - src/kernel/fs/pipe.c \ - src/kernel/fs/tmpfs.c \ + src/kernel/fs/core/file.c \ + src/kernel/fs/vfs/vfs.c \ + src/kernel/fs/core/pipe.c \ + src/kernel/fs/tmpfs/tmpfs.c \ + src/kernel/fs/devfs/devfs.c \ src/kernel/syscall/syscall.c \ src/kernel/syscall/entry.c \ src/kernel/mem/pmm.c \ @@ -996,10 +1105,11 @@ src/libc/errno.c \ test/test_malloc_stress.c \ test/unity.c \ test/stubs.c \ - src/kernel/file.c \ - src/kernel/fs/vfs.c \ - src/kernel/fs/pipe.c \ - src/kernel/fs/tmpfs.c \ + src/kernel/fs/core/file.c \ + src/kernel/fs/vfs/vfs.c \ + src/kernel/fs/core/pipe.c \ + src/kernel/fs/tmpfs/tmpfs.c \ + src/kernel/fs/devfs/devfs.c \ src/kernel/syscall/syscall.c \ src/kernel/syscall/entry.c \ src/kernel/mem/pmm.c \ @@ -1072,9 +1182,9 @@ shell: .PHONY: doom doom: sdk ifeq ($(OS_NAME),linux) - $(MAKE) -C app/doom -f Makefile.menios + $(MAKE) -C vendor/genericdoom -f Makefile.menios else - $(DOCKER) run --rm $(DOCKER_RUN_FLAGS) $(DOCKER_ENV) --mount type=bind,source=$$(pwd),target=/mnt $(DOCKER_IMAGE) /bin/sh -c "cd /mnt && make sdk && make -C app/doom -f Makefile.menios" + $(DOCKER) run --rm $(DOCKER_RUN_FLAGS) $(DOCKER_ENV) --mount type=bind,source=$$(pwd),target=/mnt $(DOCKER_IMAGE) /bin/sh -c "cd /mnt && make sdk && make -C vendor/genericdoom -f Makefile.menios" endif .PHONY: build-apps @@ -1181,3 +1291,43 @@ $(BINUTILS_NATIVE_BUILD_DIR)/Makefile: sdk --prefix=$(BINUTILS_PREFIX) \ $(BINUTILS_CONFIGURE_FLAGS) \ --with-zstd=no +HOST_TEST_SRCS = \ + test/test_ansi.c \ + test/test_atomic.c \ + test/test_char_device_registry.c \ + test/test_fat32_lfn.c \ + test/test_gpf_error.c \ + test/test_init_supervision.c \ + test/test_ioctl.c \ + test/test_kcondvar.c \ + test/test_kmutex.c \ + test/test_libc_string.c \ + test/test_malloc_realloc.c \ + test/test_mosh_exec.c \ + test/test_mosh_line.c \ + test/test_mosh_pipeline.c \ + test/test_pathconf.c \ + test/test_pf_error.c \ + test/test_pipe.c \ + test/test_pseudo_stat.c \ + test/test_shm_cleanup.c \ + test/test_shm_manager.c \ + test/test_shutdown_command.c \ + test/test_signal_syscalls.c \ + test/test_signal.c \ + test/test_spinlock.c \ + test/test_syscall_alarm.c \ + test/test_syscall_cwd.c \ + test/test_syscall_finalize.c \ + test/test_syscall_getpagesize.c \ + test/test_syscall_open.c \ + test/test_syscall_shm.c \ + test/test_syscall_shutdown.c \ + test/test_time_conv.c \ + test/test_tmpfs.c \ + test/test_tsc.c \ + test/test_vfs_open_create.c \ + test/test_vfs_open.c \ + test/test_virtual_to_physical.c \ + test/test_vsprintk.c \ + test/test_waitpid.c diff --git a/README.md b/README.md index 702cda73..7dcc8965 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ - ✅ Run classic Doom in userland - 🚀 Bring-up on real hardware (current long-term objective) +- 🎨 Complete graphical user interface with compositor and window manager (next major milestone) ## Quick Start @@ -110,9 +111,11 @@ See [issue #191 (CLOSED)](https://github.com/pbalduino/menios/issues/191) for th ## Documentation -- [Roadmaps](docs/road/) — milestone breakdowns for shell, buddy allocator, GCC toolchain, Doom integration, and more. +- [Roadmaps](docs/road/) — milestone breakdowns for shell, buddy allocator, GCC toolchain, Doom integration, **GUI stack**, and more. + - [docs/road/road_to_gui.md](docs/road/road_to_gui.md) — complete GUI roadmap (Cairo, compositor, window manager, desktop environment) - [docs/tools.md](docs/tools.md) — overview of the Menios toolchain wrapper scripts. - [docs/MILESTONES.md](docs/MILESTONES.md) — high-level progress tracker. +- [docs/architecture/code_structure.md](docs/architecture/code_structure.md) — reference for the current kernel/userland layout and naming conventions. - [scheduler_issues.md](scheduler_issues.md) — notes on ready-queue redesign. - [CONTRIBUTING.md](CONTRIBUTING.md) & [CODING.md](CODING.md) — contribution workflow and style guide. @@ -125,11 +128,14 @@ See [issue #191 (CLOSED)](https://github.com/pbalduino/menios/issues/191) for th ## Repository Layout ``` -app/ user programs (doom, shell utilities, demos) -include/ public kernel and libc headers -src/ kernel source (arch, drivers, subsystems) -user/ libc, crt, and test harnesses -docs/ design references and milestone plans +app/ user programs (doom helpers, shell utilities, demos) +build/ generated artifacts (kernel, userland, disk images, SDK) +docs/ architecture notes, roadmaps, design references +include/ exported kernel/libc headers (mirrors source hierarchy) +src/ kernel and shared libc sources +test/ Unity-based host tests and stubs +user/ libc runtime pieces (crt0, stdio glue) shared by apps +vendor/ third-party dependencies (uACPI, doomgeneric, binutils) tools/ build helpers and automation scripts ``` diff --git a/app/mknod/mknod.c b/app/mknod/mknod.c new file mode 100644 index 00000000..6dc150a8 --- /dev/null +++ b/app/mknod/mknod.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include + +static void usage(void) { + fprintf(stderr, "usage: mknod [mode]\n"); +} + +static int parse_number(const char* text, long* value) { + if(text == NULL || *text == '\0') { + return -EINVAL; + } + + char* end = NULL; + errno = 0; + long result = strtol(text, &end, 0); + if(errno != 0 || end == text || *end != '\0') { + return -EINVAL; + } + *value = result; + return 0; +} + +int main(int argc, char** argv) { + if(argc < 4) { + usage(); + return 1; + } + + const char* path = argv[1]; + if(path == NULL || *path == '\0') { + fprintf(stderr, "mknod: invalid path\n"); + return 1; + } + + long major_value = 0; + long minor_value = 0; + if(parse_number(argv[2], &major_value) != 0 || parse_number(argv[3], &minor_value) != 0) { + fprintf(stderr, "mknod: invalid major/minor numbers\n"); + return 1; + } + + if(major_value < 0 || major_value > 0xfffff || minor_value < 0 || minor_value > 0xfffff) { + fprintf(stderr, "mknod: major/minor out of range\n"); + return 1; + } + + mode_t mode = S_IFCHR | 0666; + if(argc >= 5) { + long mode_value = 0; + if(parse_number(argv[4], &mode_value) != 0) { + fprintf(stderr, "mknod: invalid mode\n"); + return 1; + } + mode = S_IFCHR | (mode_t)(mode_value & 0777); + } + + dev_t dev = MKDEV((unsigned int)major_value, (unsigned int)minor_value); + if(mknod(path, mode, dev) != 0) { + int err = errno; + fprintf(stderr, "mknod: cannot create %s: ", path); + errno = err; + perror(NULL); + return 1; + } + + return 0; +} diff --git a/docs/MILESTONES.md b/docs/MILESTONES.md index b3d9d6a7..e809533a 100644 --- a/docs/MILESTONES.md +++ b/docs/MILESTONES.md @@ -266,6 +266,79 @@ This document tracks the major milestones for meniOS development. --- +### 6. **GUI** (Graphical User Interface Milestone) +**Goal**: Complete graphical user interface stack with Cairo graphics, Wayland-style compositor, window manager, and desktop environment +**GitHub Milestone**: [GUI](https://github.com/pbalduino/menios/milestone/6) + +**Status**: 0/20 complete (0%) + +**Timeline**: 32-42 weeks (8-10 months) + +#### Milestone Structure + +**Phase 1: Graphics Foundation** (8-10 weeks) +- [ ] #397 - Port Pixman (pixel manipulation library, Cairo dependency) +- [ ] #398 - Port FreeType (font rendering library, Cairo dependency) +- [ ] #396 - Port Cairo (2D graphics library, core rendering engine) + +**Phase 2: Input Stack** (4-5 weeks) +- [ ] #399 - Implement GUI input event system +- [ ] #400 - Implement mouse cursor rendering +- Blocked on: #143 - PS/2 Mouse Driver (CRITICAL BLOCKER) + +**Phase 3: Shared Memory & IPC** (3-4 weeks) +- [ ] #401 - Implement shared memory (POSIX shm) for GUI +- [ ] #402 - Implement Unix domain sockets (AF_UNIX) for IPC + +**Phase 4: Compositor Core** (6-8 weeks) +- [ ] #403 - Implement compositor core +- [ ] #404 - Define window protocol for client-compositor communication +- [ ] #405 - Implement composition engine with damage tracking + +**Phase 5: Window Manager** (5-7 weeks) +- [ ] #406 - Implement window manager core +- [ ] #407 - Implement focus management for windows +- [ ] #408 - Implement window decorations (title bar, borders, buttons) + +**Phase 6: Desktop Environment** (6-8 weeks) +- [ ] #409 - Implement desktop shell (panel, wallpaper, launcher) +- [ ] #410 - Develop core GUI applications (terminal, editor, file manager) + +#### Related Issues (Supporting Infrastructure) +- [ ] #143 - PS/2 Mouse Driver (CRITICAL - blocks input stack) +- [ ] #129 - Unicode text rendering (complements Cairo text) +- [ ] #109 - pthread API (needed for compositor threading) +- [ ] #339 - Thread-safe libc (needed for multi-threaded apps) +- [ ] #394 - Terminal scrollback with mouse wheel (will use in GUI terminal) + +**Architecture**: Wayland-style compositor where the display server and compositor are unified. Cairo provides 2D graphics rendering, Pixman handles low-level pixel manipulation, and FreeType enables high-quality font rendering. + +**Key Features**: +- Vector graphics and anti-aliased rendering +- High-quality text with TrueType fonts +- Window management (move, resize, minimize, maximize, close) +- Desktop shell with panel/taskbar and application launcher +- Core GUI applications (terminal emulator, text editor, file manager) +- Foundation for future 3D graphics (OpenGL/Vulkan) + +**Dependencies**: +- #143 (PS/2 Mouse) - **CRITICAL BLOCKER** for input stack +- #109 (pthread API) - Required for compositor threading +- #339 (Thread-safe libc) - Required for multi-threaded GUI apps +- #193 (libc) - Foundation for all userland code + +**Documentation**: See [docs/road/road_to_gui.md](road/road_to_gui.md) for complete GUI roadmap with implementation details, architecture diagrams, and code examples. + +**Success Criteria**: +- Cairo renders to framebuffer with 60fps +- Mouse and keyboard input working in GUI +- Multiple windows can be displayed and managed +- Can move, resize, and close windows +- At least 3 GUI applications working (terminal, editor, file manager) +- Professional desktop appearance + +--- + ## 🎯 Dependency Flow Between Milestones ``` @@ -297,13 +370,14 @@ This document tracks the major milestones for meniOS development. ## 📈 Overall Progress -- **Total Issues Across Milestones**: 106 issues (includes foundational issues) -- **Completed**: 98 issues (92.5%) +- **Total Issues Across Milestones**: 126 issues (includes foundational issues) +- **Completed**: 98 issues (77.8%) - **In Progress**: 8 issues +- **GUI Milestone**: 0/20 issues (20 new GUI issues created) - **Ready to Start**: 2 issues (no dependencies: #190, #191) - **Recently Completed**: #191 ✅ (binutils port - ALL 11 TOOLS WORKING!), #365-#368 ✅ (filesystem metadata + pathconf COMPLETE), #317 ✅ (utime), #369 ✅ (system()), #370 ✅ (stat command), #372 ✅ (shutdown command), #373 ✅ (realpath command), #374 ✅ (head command), #376 ✅ (printf dynamic width), #387 ✅ (PCI device boot listing), #135 ✅ (Gcov integration), #236 ✅ (delete key), #350 ✅ (time command), #77 ✅ (tools docs) -- **Recently Created**: #382-#386 (audio subsystem - 5 issues), #394 (terminal scrollback with mouse wheel), #387 ✅ (PCI boot listing - COMPLETE) -- **Next Up**: 🎉 Doom milestone COMPLETE (33/33)! GCC milestone 85.7% (6/7) - only TCC remaining! Audio subsystem fully planned (5 issues). PCI diagnostics complete with color-coded driver detection. +- **Recently Created**: #382-#386 (audio subsystem - 5 issues), #394 (terminal scrollback with mouse wheel), #387 ✅ (PCI boot listing - COMPLETE), #396-#410 (GUI stack - 15 issues), #411 (source file headers) +- **Next Up**: 🎉 Doom milestone COMPLETE (33/33)! GCC milestone 85.7% (6/7) - only TCC remaining! 🎨 **GUI Milestone launched** - 20 issues created for complete graphical desktop environment! Audio subsystem fully planned (5 issues). PCI diagnostics complete with color-coded driver detection. ## 🚀 Immediate Next Steps @@ -677,7 +751,7 @@ Active work: - Milestone: Doom - Priority: HIGH - Game stability blocker - **Root cause**: Race condition - early Up arrow keypress before player->mo initialized - - **Fix**: Keyboard input gating in app/doom/i_input.c:280 - defers input until player ready + - **Fix**: Keyboard input gating in vendor/genericdoom/i_input.c:280 - defers input until player ready - **Testing**: No crashes with early keypresses, logs confirm gating behavior - **Status**: CLOSED ✅ - **#320** - Shell becomes unresponsive after Doom crashes @@ -708,11 +782,11 @@ Active work: - Should investigate together - fixing one likely fixes both - **2025-10-19**: Closed #319 (Doom null pointer crash) ✅ **FIXED** - **Root cause**: Race condition between keyboard input and player initialization - - **Fix implemented**: Keyboard input gating in app/doom/i_input.c:280 + - **Fix implemented**: Keyboard input gating in vendor/genericdoom/i_input.c:280 - Input deferred until players[consoleplayer].mo exists - Logs show "delaying keyboard input" → "resuming keyboard input" - **Enhanced diagnostics**: - - Diagnostic breadcrumb retained in app/doom/p_user.c:232 + - Diagnostic breadcrumb retained in vendor/genericdoom/p_user.c:232 - Kernel page-fault dump expanded (src/kernel/idt.c:190) with full user registers - **Testing**: No crashes with early keypresses, gating behavior confirmed - **Impact**: Doom no longer crashes from early keyboard input @@ -785,13 +859,25 @@ Active work: - **2025-10-29**: Created #394 (terminal scrollback with mouse wheel - blocked by #143) - **2025-10-29**: Updated #364 (stubbed functions tracker to 71% complete - 10/14 functions) - **2025-10-29**: GCC milestone reaches 85.7% (6/7 complete) - only TCC port remaining! +- **2025-10-30**: 🎨 **GUI Milestone launched!** Created comprehensive GUI roadmap with 20 issues: + - **Graphics Foundation**: #396 (Cairo), #397 (Pixman), #398 (FreeType) + - **Input Stack**: #399 (input events), #400 (mouse cursor) + - **IPC**: #401 (shared memory), #402 (Unix sockets) + - **Compositor**: #403 (core), #404 (protocol), #405 (composition engine) + - **Window Manager**: #406 (core), #407 (focus), #408 (decorations) + - **Desktop**: #409 (shell), #410 (GUI apps) + - **Infrastructure**: Assigned #143 (mouse), #129 (Unicode), #109 (pthread), #339 (thread-safe libc), #394 (scrollback) to GUI milestone + - **Documentation**: Created [docs/road/road_to_gui.md](road/road_to_gui.md) - complete roadmap with architecture, code examples, 6 milestones (8-10 months) + - **Other**: #411 (source file headers with MIT License) + - Total project issues now: **127** (was 106), GUI milestone: 0/20 (just launched!) --- -**Last Updated**: 2025-10-29 +**Last Updated**: 2025-10-30 **See Also**: - [Road to Shell](road/road_to_shell.md) - [Road to Buddy Allocator](road/road_to_buddy_allocator.md) - [Road to GCC](road/road_to_gcc.md) - [Road to Doom](road/road_to_doom.md) -- [Road to Real Hardware](road/road_to_real_hardware.md) 🆕 +- [Road to Real Hardware](road/road_to_real_hardware.md) +- [Road to GUI](road/road_to_gui.md) 🆕 - Complete graphical user interface stack diff --git a/docs/architecture/block_devices.md b/docs/architecture/block_devices.md index 8a7d3760..274c495c 100644 --- a/docs/architecture/block_devices.md +++ b/docs/architecture/block_devices.md @@ -26,7 +26,7 @@ to higher layers. ## Initialization Flow -1. `block_device_system_init()` runs early during boot (invoked from `_start` +1. `block_device_system_initialize()` runs early during boot (invoked from `_start` after the heap and file layer are available). 2. Storage drivers (e.g., the AHCI PCI controller) discover hardware, obtain memory-mapped register ranges, and allocate bookkeeping structures. diff --git a/docs/architecture/code_structure.md b/docs/architecture/code_structure.md new file mode 100644 index 00000000..5d9be351 --- /dev/null +++ b/docs/architecture/code_structure.md @@ -0,0 +1,100 @@ +# meniOS Code Structure + +This document provides a quick reference for the current meniOS layout and the +conventions to follow when adding new code. The goal is to keep subsystems +isolated, make ownership of modules obvious, and ensure the build remains +reproducible. + +## Top-Level Layout + +``` +app/ # User programs built into the image (e.g. mosh, ps, kill) +build/ # Generated artifacts (kernel, userland binaries, disk images) +docs/ # Architecture notes, roadmaps, design discussions +include/ # Public headers exported to kernel and userland +src/ # Kernel and shared libc sources +test/ # Unity-based host tests and stubs +user/ # libc implementation and runtime support objects +vendor/ # Third-party code (toolchains, Doom helper, etc.) +``` + +Only `build/` is expected to contain generated files; everything else should be +under version control. + +## Kernel Source Tree + +All kernel code lives under `src/kernel/` and is split by responsibility: + +| Directory | Purpose | +|-----------|---------| +| `core/` | Early boot, halt/panic paths, service bootstrap | +| `arch/x86_64/` | Architecture-specific support (APIC, GDT, IDT, LAPIC timers, syscall entry stubs) | +| `drivers/` | Hardware drivers grouped by bus/type (`audio/`, `block/`, `bus/`, `input/`, …) | +| `console/` | Console front-ends (`console.c`, ANSI parser, serial backend) | +| `framebuffer/` | Linear framebuffer console implementation and font tables | +| `fs/` | Filesystem layer (`core/` helpers, `devfs/`, `fat32/`, `procfs/`, `tmpfs/`, `vfs/`) | +| `hw/` | Generic hardware inventory helpers | +| `input/` | Higher-level input processing (keyboard event queue, etc.) | +| `ipc/` | Shared-memory manager and related primitives | +| `mem/` | Physical and virtual memory managers, heap implementation, DMA helpers | +| `proc/` | Process lifecycle, scheduler, signals, kernel threading primitives | +| `syscall/` | Syscall dispatch table and ABI entry plumbing | +| `timer/` | HPET, PIT, LAPIC timer drivers and generic timer services | +| `user/` | ELF loader, init launcher, and helpers that bridge kernel ↔ user space | + +### Headers + +Headers mirror the source hierarchy under `include/`. For example: + +``` +include/kernel/ +├── console/ +├── drivers/ +├── fs/ +├── mem/ +└── … +``` + +When introducing new modules add headers alongside existing ones so callers can +`#include /
.h>` without additional include paths. + +## Userland & Runtime + +Userland support code is split between: + +- `user/libc/` – User-mode entry glue, stdio shims, and libc-facing helpers. +- `user/crt/` – Startup/termination objects linked into every program. +- `app/` – Standalone programs that are installed into `/bin` at build time. +- `src/usermode/` – Boot-time userland payloads (`init`, `user_demo`). + +## Build Output Expectations + +- Object files and dependency files are written under `build/obj/**`. +- Generated binaries, disk images, and intermediate SDK artifacts live under + `build/bin/`. +- Source directories must remain free of `.o`, `.d`, and other generated files. +- When new modules are added update the explicit lists in `Makefile` (kernel + sources, host tests, uACPI files, user programs) so the build remains + deterministic. + +## Naming Conventions + +- Functions follow either `subsystem_action()` or `verb_noun()` format. + Examples: `scheduler_initialize()`, `buddy_alloc()`, `fs_path_resolve()`. +- File names match the module’s primary responsibility (`scheduler.c`, + `block_cache.c`, `proc/signal.c`). +- Prefix exported helpers with the subsystem (`proc_`, `fs_`, `char_device_`) + to make call sites self-documenting. + +## Adding New Code + +1. Place sources in the directory matching the subsystem (e.g., a new PCI + driver goes under `src/kernel/drivers/bus/`). +2. Add a header beneath `include/kernel/…` so other modules can include it. +3. Extend the explicit source list in `Makefile`. +4. Keep generated artifacts in `build/` only—never commit `.o`, `.d`, or other + build products. +5. Update relevant documentation (this file, subsystem READMEs) if the new code + introduces a new pattern others should follow. + +Refer back here when reviewing patches to ensure the layout remains consistent. diff --git a/docs/architecture/file_descriptors.md b/docs/architecture/file_descriptors.md index 244fd287..0036f7fa 100644 --- a/docs/architecture/file_descriptors.md +++ b/docs/architecture/file_descriptors.md @@ -12,7 +12,7 @@ opaque `file_t` handles with reference counting and close-on-exec semantics. - **Per-process tables** – `proc_file_table_init/clone/cleanup` manage the descriptor array embedded in `proc_info_t`. Cloning bumps refcounts while exec drops any descriptors flagged with `FD_CLOEXEC`. -- **Kernel bootstrap** – `file_system_init()` now runs after the heap is ready +- **Kernel bootstrap** – `file_system_initialize()` now runs after the heap is ready and installs a ring-buffer-backed stdin plus two serial-backed streams for stdout and stderr. Consoles and logging go through these descriptors rather than hard-coded serial writes, while keystrokes arriving from the PS/2 driver diff --git a/docs/architecture/gui.md b/docs/architecture/gui.md new file mode 100644 index 00000000..3a71adb5 --- /dev/null +++ b/docs/architecture/gui.md @@ -0,0 +1,816 @@ +# GUI Architecture - Graphical User Interface Stack + +## Goals + +- Provide a complete graphical user interface stack for meniOS with modern desktop capabilities +- Implement a Wayland-style compositor architecture where the display server and compositor are unified +- Enable rich graphical applications through Cairo 2D graphics with vector rendering and high-quality text +- Support multiple windows with full window management (move, resize, minimize, maximize, close) +- Deliver smooth 60fps rendering with efficient damage tracking and double buffering +- Create a foundation extensible to future 3D graphics (OpenGL/Vulkan) and accessibility features + +## Design Overview + +The GUI stack follows a **Wayland-style compositor model** rather than the legacy X11 client-server architecture. This design unifies the display server and compositor into a single component, eliminating unnecessary round-trips and improving both security and performance. + +### Architecture Layers + +``` +┌─────────────────────────────────────────────────────────────┐ +│ GUI Applications │ +│ (Terminal, Editor, File Manager, etc.) │ +└──────────────────────────┬──────────────────────────────────┘ + │ Window Protocol (IPC) +┌──────────────────────────▼──────────────────────────────────┐ +│ MeniOS Compositor (Display Server) │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ Window Manager (Integrated) │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ Scene Graph & Composition Engine │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ Input Event Dispatcher │ │ +│ └────────────────────────────────────────────────────────┘ │ +└──────────────────────────┬──────────────────────────────────┘ + │ +┌──────────────────────────▼──────────────────────────────────┐ +│ Cairo 2D Graphics Library │ +│ (Pixman + FreeType backends) │ +└──────────────────────────┬──────────────────────────────────┘ + │ +┌──────────────────────────▼──────────────────────────────────┐ +│ Kernel Graphics & Input │ +│ - Framebuffer (/dev/fb0) - PS/2 Keyboard (#37) │ +│ - Shared Memory - PS/2 Mouse (#143) │ +│ - Unix Sockets - Event Queues │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Why Wayland-Style Over X11? + +**X11 Problems:** +- Client-server separation adds latency (every draw requires server round-trip) +- Complex protocol accumulated over 30+ years (millions of lines of code) +- Security issues (any client can read keystrokes from any window) +- Difficult to implement features like tear-free rendering + +**Wayland-Style Benefits:** +- **Direct rendering**: Clients render to shared memory buffers, compositor just composites +- **Simpler protocol**: Only what's needed for modern graphics (a few thousand lines) +- **Better security**: Input isolation (only focused window receives keyboard events) +- **Modern features**: Easier to implement vsync, damage tracking, multi-monitor + +### Key Components + +#### 1. Cairo Graphics Library (#396) +**Purpose:** Industry-standard 2D vector graphics rendering + +**Capabilities:** +- Vector paths with anti-aliasing (rectangles, circles, Bezier curves) +- Gradients (linear, radial) and pattern fills +- Image compositing with alpha blending +- Transformation matrices (translate, rotate, scale) +- PDF and SVG output surfaces +- Text rendering via FreeType integration + +**Integration:** +- Renders to image surfaces backed by shared memory or framebuffer +- Used by both compositor (for window decorations, desktop shell) and applications +- Located in `vendor/cairo-1.18.0/` with meniOS-specific framebuffer backend + +**Example:** +```c +cairo_surface_t *surface = cairo_fb_surface_create("/dev/fb0"); +cairo_t *cr = cairo_create(surface); + +// Vector rectangle with gradient +cairo_pattern_t *grad = cairo_pattern_create_linear(0, 0, 200, 200); +cairo_pattern_add_color_stop_rgb(grad, 0, 1, 0, 0); // Red +cairo_pattern_add_color_stop_rgb(grad, 1, 0, 0, 1); // Blue +cairo_rectangle(cr, 100, 100, 400, 300); +cairo_set_source(cr, grad); +cairo_fill(cr); + +// Anti-aliased text +cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); +cairo_set_font_size(cr, 48); +cairo_set_source_rgb(cr, 1, 1, 1); +cairo_move_to(cr, 150, 250); +cairo_show_text(cr, "meniOS"); +``` + +#### 2. Pixman (#397) +**Purpose:** Low-level pixel manipulation and software rasterization (Cairo dependency) + +**Capabilities:** +- Porter-Duff compositing operators (SRC, OVER, ADD, etc.) +- Bilinear and bicubic image filtering +- Image transformations (rotation, scaling, affine) +- SIMD optimizations (SSE2, SSSE3, AVX2) for x86-64 + +**Integration:** +- Required by Cairo for all pixel-level operations +- Handles actual rasterization of vector paths to pixels +- Optimized fast paths for common operations +- Located in `vendor/pixman-0.42.2/` + +#### 3. FreeType (#398) +**Purpose:** Font rendering library (Cairo dependency for text) + +**Capabilities:** +- TrueType (TTF) and OpenType (OTF) font loading +- Glyph rasterization with hinting for small sizes +- Subpixel rendering (LCD optimization) +- Unicode character mapping + +**Integration:** +- Cairo FT font backend uses FreeType for text rendering +- Fonts stored in `/usr/share/fonts/` (e.g., DejaVu Sans, Liberation Sans) +- Glyph cache maintained for performance +- Located in `vendor/freetype-2.13.2/` + +#### 4. Compositor Core (#403) +**Purpose:** Central display server managing all windows and screen output + +**Responsibilities:** +- Accept client connections via Unix domain sockets (#402) +- Manage window surfaces allocated in shared memory (#401) +- Composite windows in Z-order to screen framebuffer +- Route input events to appropriate windows +- Implement damage tracking for efficient redraws + +**Key Data Structures:** +```c +// Compositor state (src/userland/compositor/compositor.c) +struct compositor { + cairo_surface_t *screen; // Framebuffer surface (1024x768 ARGB32) + struct window *windows; // Doubly-linked window list (Z-order) + struct window *focused; // Currently focused window + struct event_queue input_queue; // Keyboard/mouse events + int server_socket; // AF_UNIX listening socket + struct pollfd *client_fds; // Connected clients + bool damage_dirty; // Screen needs repaint +}; + +// Window representation +struct window { + uint32_t id; // Unique window ID + struct client *client; // Owning client connection + + // Geometry + int x, y; // Position on screen + int width, height; // Window dimensions + + // State + bool mapped; // Window is visible + bool focused; // Has keyboard focus + + // Rendering + cairo_surface_t *surface; // Shared memory surface + struct { + int x, y, width, height; // Dirty region + bool valid; + } damage; + + // Decoration state + char title[256]; // Window title + bool has_decorations; // Client-side or server-side + + // Z-order links + struct window *above, *below; +}; +``` + +**Compositor Main Loop:** +```c +while (compositor_running) { + // 1. Process client protocol messages + compositor_handle_client_messages(comp); + + // 2. Dispatch input events to windows + compositor_dispatch_input(comp); + + // 3. Render if damaged + if (comp->damage_dirty) { + compositor_render(comp); + comp->damage_dirty = false; + } + + // 4. Wait for next event (with vsync timeout) + poll(comp->client_fds, comp->num_clients, 16); // 60fps = 16.67ms +} +``` + +#### 5. Window Protocol (#404) +**Purpose:** IPC protocol between clients and compositor + +**Transport:** Unix domain sockets (AF_UNIX, SOCK_STREAM) over `/tmp/menios-compositor` + +**Message Format:** +```c +// Client → Compositor (requests) +enum request_type { + REQ_CREATE_SURFACE, // Create new window + REQ_DESTROY_SURFACE, // Destroy window + REQ_ATTACH_BUFFER, // Attach shared memory buffer + REQ_DAMAGE, // Mark region dirty + REQ_COMMIT, // Apply pending state + REQ_SET_TITLE, // Set window title + REQ_FRAME_CALLBACK, // Request vsync notification +}; + +struct request { + uint32_t type; + uint32_t surface_id; + union { + struct { + int width, height; + } create; + struct { + int fd; // Shared memory fd (passed via SCM_RIGHTS) + int width, height; + int stride; + } attach; + struct { + int x, y, width, height; + } damage; + struct { + char title[256]; + } set_title; + }; +}; + +// Compositor → Client (events) +enum event_type { + EVT_CONFIGURE, // Compositor requests size change + EVT_KEY_PRESS, // Keyboard key pressed + EVT_KEY_RELEASE, // Keyboard key released + EVT_MOUSE_MOTION, // Mouse moved + EVT_MOUSE_BUTTON, // Mouse button clicked + EVT_FRAME_DONE, // Vsync callback + EVT_CLOSE, // Window close requested +}; + +struct event { + uint32_t type; + uint32_t surface_id; + union { + struct { + int width, height; + } configure; + struct { + uint32_t keycode; + uint32_t modifiers; // Shift, Ctrl, Alt + } key; + struct { + int x, y; // Window-relative coordinates + } mouse_motion; + struct { + uint32_t button; // 1=left, 2=middle, 3=right + int x, y; + } mouse_button; + }; +}; +``` + +**Protocol Flow Example:** +``` +Client Compositor + | | + |--- REQ_CREATE_SURFACE --------->| + |<-- EVT_CONFIGURE(800x600) ------| + | | + | shm_open("/window_42") | + | mmap() shared buffer | + | cairo_create_for_data() | + | [render content to buffer] | + | | + |--- REQ_ATTACH_BUFFER(fd) ------>| (fd passed via SCM_RIGHTS) + |--- REQ_DAMAGE(0,0,800,600) ---->| + |--- REQ_COMMIT ------------------>| + | | [composites to screen] + |<-- EVT_FRAME_DONE --------------| + | | + | [user clicks window] | + |<-- EVT_MOUSE_BUTTON(1, x, y) ---| +``` + +#### 6. Window Manager (#406, #407, #408) +**Purpose:** Manage window placement, decorations, and user interactions + +**Integrated into compositor** rather than separate process (Wayland-style design). + +**Window Placement Policies:** +```c +// Cascade placement (default) +void wm_place_cascade(struct window *w) { + static int offset = 0; + w->x = 100 + offset; + w->y = 80 + offset; + offset = (offset + 30) % 300; +} + +// Centered placement +void wm_place_centered(struct window *w) { + w->x = (screen_width - w->width) / 2; + w->y = (screen_height - w->height) / 2; +} + +// Tiling (future) +void wm_place_tiled(struct window *w) { + // Automatic grid layout +} +``` + +**Window Operations:** +```c +void wm_move_window(struct window *w, int x, int y); +void wm_resize_window(struct window *w, int width, int height); +void wm_raise_window(struct window *w); // Bring to front +void wm_lower_window(struct window *w); // Send to back +void wm_minimize_window(struct window *w); // Hide from screen +void wm_maximize_window(struct window *w); // Expand to full screen +void wm_close_window(struct window *w); // Request close +``` + +**Focus Management (#407):** +- **Click-to-focus**: Mouse click on window brings to front and gives keyboard focus +- **Focus-follows-mouse** (optional): Hovering over window gives focus +- **Tab cycling**: Alt+Tab switches focus between windows +- Visual feedback: Focused window has blue title bar, unfocused windows have gray + +**Window Decorations (#408):** +```c +#define TITLEBAR_HEIGHT 24 +#define BORDER_WIDTH 1 + +void wm_draw_decorations(cairo_t *cr, struct window *w) { + // Title bar + cairo_rectangle(cr, w->x, w->y - TITLEBAR_HEIGHT, w->width, TITLEBAR_HEIGHT); + if (w->focused) { + cairo_set_source_rgb(cr, 0.2, 0.4, 0.7); // Blue + } else { + cairo_set_source_rgb(cr, 0.3, 0.3, 0.3); // Gray + } + cairo_fill(cr); + + // Title text + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cr, 12); + cairo_move_to(cr, w->x + 5, w->y - 8); + cairo_show_text(cr, w->title); + + // Close button + int btn_x = w->x + w->width - 18; + int btn_y = w->y - 20; + cairo_rectangle(cr, btn_x, btn_y, 14, 14); + cairo_set_source_rgb(cr, 0.8, 0.2, 0.2); // Red + cairo_fill(cr); + + // Border + cairo_rectangle(cr, w->x - BORDER_WIDTH, w->y - TITLEBAR_HEIGHT - BORDER_WIDTH, + w->width + 2*BORDER_WIDTH, w->height + TITLEBAR_HEIGHT + 2*BORDER_WIDTH); + cairo_set_line_width(cr, BORDER_WIDTH); + cairo_set_source_rgb(cr, 0.5, 0.5, 0.5); + cairo_stroke(cr); +} +``` + +#### 7. Input Event System (#399) +**Purpose:** Unified abstraction for keyboard, mouse, and future touch input + +**Event Queue:** +```c +#define EVENT_QUEUE_SIZE 256 + +struct input_event { + uint64_t timestamp; // Microseconds since boot + enum { + INPUT_KEY_PRESS, + INPUT_KEY_RELEASE, + INPUT_MOUSE_MOTION, + INPUT_MOUSE_BUTTON_PRESS, + INPUT_MOUSE_BUTTON_RELEASE, + INPUT_MOUSE_SCROLL, + } type; + union { + struct { + uint32_t keycode; // PS/2 scan code + uint32_t unicode; // UTF-32 codepoint + uint32_t modifiers; // Shift, Ctrl, Alt, etc. + } key; + struct { + int32_t x, y; // Absolute screen coordinates + int32_t dx, dy; // Relative motion + } motion; + struct { + uint32_t button; // 1=left, 2=middle, 3=right + int32_t x, y; + } button; + struct { + int32_t dx, dy; // Scroll wheel delta + } scroll; + }; +}; + +struct event_queue { + struct input_event events[EVENT_QUEUE_SIZE]; + size_t head, tail; + struct mutex lock; + struct semaphore sem; +}; + +int event_queue_push(struct event_queue *q, struct input_event *evt); +int event_queue_pop(struct event_queue *q, struct input_event *evt); +``` + +**Integration with Drivers:** +```c +// PS/2 keyboard driver (src/kernel/drivers/input/ps2_keyboard.c) +void ps2_keyboard_interrupt(struct registers *regs) { + uint8_t scancode = inb(0x60); + + struct input_event evt = { + .timestamp = get_timestamp_us(), + .type = (scancode & 0x80) ? INPUT_KEY_RELEASE : INPUT_KEY_PRESS, + .key = { + .keycode = scancode & 0x7F, + .unicode = scancode_to_unicode(scancode), + .modifiers = get_modifiers(), + } + }; + + event_queue_push(&global_event_queue, &evt); +} + +// PS/2 mouse driver (src/kernel/drivers/input/ps2_mouse.c) - #143 +void ps2_mouse_interrupt(struct registers *regs) { + static struct { + uint8_t buttons; + int16_t dx, dy; + } mouse_state; + + // Parse PS/2 mouse packet (3 bytes) + parse_ps2_packet(&mouse_state); + + struct input_event evt = { + .timestamp = get_timestamp_us(), + .type = INPUT_MOUSE_MOTION, + .motion = { + .x = cursor_x + mouse_state.dx, + .y = cursor_y + mouse_state.dy, + .dx = mouse_state.dx, + .dy = mouse_state.dy, + } + }; + + event_queue_push(&global_event_queue, &evt); +} +``` + +#### 8. Mouse Cursor (#400) +**Purpose:** Render hardware or software cursor on screen + +**Software Cursor Implementation:** +```c +struct cursor { + enum { + CURSOR_ARROW, + CURSOR_HAND, + CURSOR_TEXT, + CURSOR_CROSSHAIR, + CURSOR_WAIT, + CURSOR_RESIZE_NS, + CURSOR_RESIZE_EW, + } type; + + uint32_t *pixels; // ARGB32 cursor image + int width, height; // Cursor dimensions (e.g., 16x16) + int hotspot_x, hotspot_y; // Click point (e.g., arrow tip) +}; + +void cursor_render(cairo_t *cr, struct cursor *cursor, int x, int y) { + cairo_surface_t *cursor_surface = cairo_image_surface_create_for_data( + (unsigned char*)cursor->pixels, + CAIRO_FORMAT_ARGB32, + cursor->width, cursor->height, + cursor->width * 4 + ); + + cairo_set_source_surface(cr, cursor_surface, + x - cursor->hotspot_x, + y - cursor->hotspot_y); + cairo_rectangle(cr, x - cursor->hotspot_x, y - cursor->hotspot_y, + cursor->width, cursor->height); + cairo_fill(cr); + + cairo_surface_destroy(cursor_surface); +} +``` + +**Hit Testing:** +```c +struct window *compositor_window_at(struct compositor *comp, int x, int y) { + // Iterate windows from top to bottom (reverse Z-order) + for (struct window *w = comp->windows; w; w = w->below) { + if (!w->mapped) continue; + + // Check if point is in window (including decorations) + int wx1 = w->x; + int wy1 = w->y - TITLEBAR_HEIGHT; + int wx2 = w->x + w->width; + int wy2 = w->y + w->height; + + if (x >= wx1 && x < wx2 && y >= wy1 && y < wy2) { + return w; + } + } + return NULL; // Desktop background +} +``` + +#### 9. Shared Memory (#401) +**Purpose:** Zero-copy window buffer sharing between clients and compositor + +**POSIX Shared Memory API:** +```c +// Client creates shared buffer +int fd = shm_open("/menios_window_123", O_CREAT | O_RDWR, 0600); +ftruncate(fd, width * height * 4); // ARGB32 = 4 bytes/pixel + +void *pixels = mmap(NULL, width * height * 4, + PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + +// Client renders to buffer +cairo_surface_t *surface = cairo_image_surface_create_for_data( + pixels, CAIRO_FORMAT_ARGB32, width, height, width * 4); +cairo_t *cr = cairo_create(surface); +// ... render content ... + +// Send fd to compositor via Unix socket SCM_RIGHTS +struct msghdr msg = {0}; +struct cmsghdr *cmsg; +char cmsgbuf[CMSG_SPACE(sizeof(int))]; +msg.msg_control = cmsgbuf; +msg.msg_controllen = sizeof(cmsgbuf); + +cmsg = CMSG_FIRSTHDR(&msg); +cmsg->cmsg_level = SOL_SOCKET; +cmsg->cmsg_type = SCM_RIGHTS; +cmsg->cmsg_len = CMSG_LEN(sizeof(int)); +memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); + +sendmsg(compositor_socket, &msg, 0); +``` + +**Compositor maps same buffer:** +```c +// Compositor receives fd via SCM_RIGHTS +int client_fd; +recvmsg(client_socket, &msg, 0); +memcpy(&client_fd, CMSG_DATA(cmsg), sizeof(int)); + +// Map into compositor's address space +void *compositor_pixels = mmap(NULL, width * height * 4, + PROT_READ, // Read-only for compositor + MAP_SHARED, client_fd, 0); + +// Create Cairo surface pointing to same memory +cairo_surface_t *window_surface = cairo_image_surface_create_for_data( + compositor_pixels, CAIRO_FORMAT_ARGB32, width, height, width * 4); + +// Compositor can now composite this surface without copying +``` + +#### 10. Desktop Shell (#409) +**Purpose:** Desktop environment (panel, launcher, background) + +**Components:** +- **Desktop background**: Solid color or wallpaper image +- **Panel/Taskbar**: Top or bottom bar with: + - Application launcher button + - Window list (shows all open windows) + - System tray (clock, volume, network, battery) +- **Application launcher menu**: Grid or list of installed applications +- **Notification area**: Popup notifications (future) + +**Implementation:** +```c +// Desktop shell runs as special compositor client +struct desktop_shell { + struct window *panel; // Panel window (full width, 28px tall) + struct window *background; // Desktop background (full screen) + struct window *launcher; // Application launcher menu (popup) +}; + +void shell_draw_panel(cairo_t *cr) { + int panel_height = 28; + + // Panel background + cairo_rectangle(cr, 0, 0, screen_width, panel_height); + cairo_set_source_rgb(cr, 0.1, 0.1, 0.1); + cairo_fill(cr); + + // Application launcher button + draw_button(cr, 5, 2, 100, 24, "Applications"); + + // Window list + int x = 120; + for (struct window *w = compositor->windows; w; w = w->next) { + if (!w->mapped || w == panel) continue; + draw_button(cr, x, 2, 120, 24, w->title); + x += 125; + } + + // Clock (right side) + char time_str[32]; + time_t now = time(NULL); + strftime(time_str, sizeof(time_str), "%H:%M", localtime(&now)); + + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cr, 12); + cairo_move_to(cr, screen_width - 60, 18); + cairo_show_text(cr, time_str); +} +``` + +## Integration Points + +- **Compositor**: `src/userland/compositor/` - Main display server daemon + - `compositor.c` - Core compositor logic + - `window.c` - Window management + - `protocol.c` - Client protocol handling + - `input.c` - Input event dispatch + +- **Desktop Shell**: `src/userland/desktop/` - Desktop environment + - `shell.c` - Panel and launcher + - `panel.c` - Taskbar rendering + - `launcher.c` - Application menu + +- **Client Library**: `src/userland/libgui/` - Shared library for GUI apps + - `libgui.c` - Window creation, event handling + - `libgui.h` - Public API for applications + +- **Cairo Integration**: `vendor/cairo-1.18.0/` + - `cairo-menios.c` - Framebuffer backend implementation + - Patches for meniOS-specific features + +- **Example Applications**: `app/gui/` + - `terminal.c` - Terminal emulator + - `editor.c` - Text editor + - `filemanager.c` - File browser + - `calculator.c` - Calculator + +## Performance Considerations + +### Damage Tracking +Compositor only redraws changed regions rather than entire screen: +```c +void compositor_damage(struct compositor *comp, int x, int y, int w, int h) { + comp->damage_region = rect_union(comp->damage_region, + rect(x, y, w, h)); + comp->damage_dirty = true; +} + +void compositor_render(struct compositor *comp) { + if (!comp->damage_dirty) return; + + // Only redraw damaged region + cairo_rectangle(cr, comp->damage_region.x, comp->damage_region.y, + comp->damage_region.width, comp->damage_region.height); + cairo_clip(cr); + + // Composite windows + for (struct window *w = comp->windows; w; w = w->next) { + if (!w->mapped) continue; + if (!rect_intersects(w->bounds, comp->damage_region)) continue; + + cairo_set_source_surface(cr, w->surface, w->x, w->y); + cairo_paint(cr); + } + + comp->damage_dirty = false; +} +``` + +### Double Buffering +Compositor uses double buffering to prevent tearing: +```c +struct compositor { + cairo_surface_t *front_buffer; // Currently displayed + cairo_surface_t *back_buffer; // Being rendered to +}; + +void compositor_swap_buffers(struct compositor *comp) { + cairo_surface_t *tmp = comp->front_buffer; + comp->front_buffer = comp->back_buffer; + comp->back_buffer = tmp; + + // Blit front buffer to /dev/fb0 + memcpy(fb_mem, cairo_image_surface_get_data(comp->front_buffer), + screen_width * screen_height * 4); +} +``` + +### Vsync Synchronization +Rendering synchronized to vertical refresh (60Hz): +```c +void compositor_wait_vsync(struct compositor *comp) { + // Option 1: ioctl on /dev/fb0 + ioctl(fb_fd, FBIO_WAITFORVSYNC, 0); + + // Option 2: Timer-based (16.67ms for 60fps) + struct timespec ts = { .tv_sec = 0, .tv_nsec = 16666667 }; + nanosleep(&ts, NULL); +} +``` + +## Security Model + +- **Input isolation**: Only focused window receives keyboard events +- **Credential passing**: Compositor verifies client credentials via Unix socket SCM_CREDENTIALS +- **Sandboxing** (future): Clients run in restricted environment (seccomp, namespaces) +- **No global keyboard snooping**: Unlike X11, clients cannot spy on other windows + +## Testing Strategy + +### Unit Tests +- Window manager placement algorithms +- Damage tracking region calculations +- Event queue operations +- Protocol message encoding/decoding + +### Integration Tests +- Multi-window rendering +- Client connection lifecycle +- Input event routing +- Shared memory buffer mapping + +### Visual Tests +- Render test patterns to validate anti-aliasing +- Font rendering quality across sizes +- Window decoration consistency +- Cursor tracking accuracy + +### Performance Tests +- Measure frame rate during window movement +- Benchmark composition time for 10+ windows +- Test damage tracking efficiency +- Profile Cairo rendering operations + +## Risks and Open Questions + +### Memory Footprint +- **Problem**: Multiple Cairo surfaces (one per window) consume significant RAM +- **Mitigation**: Limit total window surfaces, implement surface eviction for minimized windows +- **Metrics**: Monitor via `menios_malloc_stats()`, aim for <64MB total for 10 windows + +### Threading Model +- **Question**: Should compositor be single-threaded or multi-threaded? +- **Current**: Single-threaded event loop (simpler, less race conditions) +- **Future**: Could parallelize window rendering if needed +- **Dependency**: #109 (pthread API), #339 (thread-safe libc) + +### GPU Acceleration +- **Current**: Software rendering via Cairo/Pixman +- **Performance**: Adequate for basic desktop (aiming for 60fps at 1024x768) +- **Future**: OpenGL/Vulkan backend for hardware acceleration +- **Blocker**: Requires GPU driver development (Intel i915, AMD, etc.) + +### Font Rendering Quality +- **Question**: Software rendering vs hardware acceleration for text? +- **Current**: FreeType software rasterization with subpixel anti-aliasing +- **Performance**: 10,000+ glyphs/sec should be sufficient +- **Tuning**: Font caching critical for editor/terminal performance + +### Window Protocol Versioning +- **Problem**: How to evolve protocol without breaking clients? +- **Solution**: Protocol version negotiation during handshake +- **Example**: Client sends `HELLO(version=1)`, compositor responds with supported version + +## Future Extensions + +- **Clipboard support**: Copy/paste between applications +- **Drag and drop**: File and window dragging +- **Notifications**: Desktop notification API (similar to libnotify) +- **3D rendering**: OpenGL/Vulkan context creation for games +- **Accessibility**: Screen reader, magnifier, high-contrast themes +- **Multi-monitor**: Support for multiple displays +- **Network transparency**: Remote application display (similar to X forwarding) +- **Widget toolkit**: Higher-level UI library (buttons, text boxes, menus) + +## References + +- **Wayland Protocol Specification**: https://wayland.freedesktop.org/docs/html/ +- **Cairo Graphics Manual**: https://www.cairographics.org/manual/ +- **Weston Compositor Source**: https://gitlab.freedesktop.org/wayland/weston +- **SerenityOS LibGUI**: https://github.com/SerenityOS/serenity/tree/master/Userland/Libraries/LibGUI +- **ToaruOS Compositor**: https://github.com/klange/toaruos/tree/master/apps + +## Document History + +- **2025-10-30**: Initial version documenting GUI architecture decisions diff --git a/docs/architecture/hardware.md b/docs/architecture/hardware.md index ab394004..3176bfe1 100644 --- a/docs/architecture/hardware.md +++ b/docs/architecture/hardware.md @@ -2,8 +2,8 @@ Issue #22 tracks hardware discovery in meniOS. The registry now works as follows: -* `driver_init()` seeds the driver list (PS/2 keyboard, PCI root bridge, etc.), and `driver_register()` keeps a copy in kernel memory. -* ACPI enumeration (`hardware_init() -> acpi_enumerate()`) walks the namespace, logging every device with a Hardware ID (HID) and calling `driver_load()`. +* `driver_registry_init()` seeds the driver list (PS/2 keyboard, PCI root bridge, etc.), and `driver_register()` keeps a copy in kernel memory. +* ACPI enumeration (`hardware_initialize() -> acpi_enumerate()`) walks the namespace, logging every device with a Hardware ID (HID) and calling `driver_load()`. * `driver_load()` now returns the matched driver descriptor and invokes its `start()` hook. * `hardware_device_register()` records every detected device, storing its ACPI path, HID, and matched driver. `hardware_log_devices()` prints a summary for diagnostics at the end of hardware discovery. diff --git a/docs/architecture/mem.md b/docs/architecture/mem.md index 990b287a..6cb61874 100644 --- a/docs/architecture/mem.md +++ b/docs/architecture/mem.md @@ -13,7 +13,7 @@ This note collects the decisions we have made around memory management in meniOS * The PMM is a bitmap allocator defined in `src/kernel/mem/pmm.c`. * Page size is 4 KiB; the bitmap is sized by `PAGE_BITMAP_SIZE` (see `include/kernel/pmm.h`). Each bit tracks the availability of one physical page. -* During `pmm_init()` we: +* During `pmm_initialize()` we: 1. Initialise the bitmap marking everything used (`init_page_bitmap`). 2. Iterate Limine's memory map and mark usable ranges as free (`bulk_page_bitmap_as_free`). 3. Capture the HHDM offset so we can translate physical↔virtual addresses via `physical_to_virtual()` / `virtual_to_physical()`. @@ -37,7 +37,7 @@ Other points: ## Heap & Kernel Allocations -* The kernel heap is initialised by `init_heap(NULL, PAGE_SIZE * HEAP_SIZE)` in `mem_init()`. At the moment the heap carves memory from the higher-half mapping; it is not yet virtual-memory aware. +* The kernel heap is initialised by `heap_initialize(NULL, PAGE_SIZE * HEAP_SIZE)` in `memory_initialize()`. At the moment the heap carves memory from the higher-half mapping; it is not yet virtual-memory aware. * The heap feeds `kmalloc`, `kfree`, and related APIs. Memory compaction is handled by a background thread (`mem_compactor`). ## User Address Spaces (Current State) diff --git a/docs/issue_dependencies_critical.dot b/docs/diagrams/issue_dependencies_critical.dot similarity index 100% rename from docs/issue_dependencies_critical.dot rename to docs/diagrams/issue_dependencies_critical.dot diff --git a/docs/issue_dependencies_critical.png b/docs/diagrams/issue_dependencies_critical.png similarity index 100% rename from docs/issue_dependencies_critical.png rename to docs/diagrams/issue_dependencies_critical.png diff --git a/docs/issue_dependencies_full.dot b/docs/diagrams/issue_dependencies_full.dot similarity index 100% rename from docs/issue_dependencies_full.dot rename to docs/diagrams/issue_dependencies_full.dot diff --git a/docs/issue_dependencies_full.png b/docs/diagrams/issue_dependencies_full.png similarity index 100% rename from docs/issue_dependencies_full.png rename to docs/diagrams/issue_dependencies_full.png diff --git a/docs/issue_dependencies_hardware.dot b/docs/diagrams/issue_dependencies_hardware.dot similarity index 100% rename from docs/issue_dependencies_hardware.dot rename to docs/diagrams/issue_dependencies_hardware.dot diff --git a/docs/issue_dependencies_hardware.png b/docs/diagrams/issue_dependencies_hardware.png similarity index 100% rename from docs/issue_dependencies_hardware.png rename to docs/diagrams/issue_dependencies_hardware.png diff --git a/docs/issue_dependencies_libc.dot b/docs/diagrams/issue_dependencies_libc.dot similarity index 100% rename from docs/issue_dependencies_libc.dot rename to docs/diagrams/issue_dependencies_libc.dot diff --git a/docs/issue_dependencies_libc.png b/docs/diagrams/issue_dependencies_libc.png similarity index 100% rename from docs/issue_dependencies_libc.png rename to docs/diagrams/issue_dependencies_libc.png diff --git a/docs/photo_4976602958796009157_y.jpg b/docs/logo/photo_4976602958796009157_y.jpg similarity index 100% rename from docs/photo_4976602958796009157_y.jpg rename to docs/logo/photo_4976602958796009157_y.jpg diff --git a/docs/photo_4976602958796009157_y_128.png b/docs/logo/photo_4976602958796009157_y_128.png similarity index 100% rename from docs/photo_4976602958796009157_y_128.png rename to docs/logo/photo_4976602958796009157_y_128.png diff --git a/docs/photo_4976602958796009158_y.jpg b/docs/logo/photo_4976602958796009158_y.jpg similarity index 100% rename from docs/photo_4976602958796009158_y.jpg rename to docs/logo/photo_4976602958796009158_y.jpg diff --git a/docs/photo_4976602958796009159_y.jpg b/docs/logo/photo_4976602958796009159_y.jpg similarity index 100% rename from docs/photo_4976602958796009159_y.jpg rename to docs/logo/photo_4976602958796009159_y.jpg diff --git a/docs/road/road_to_doom.md b/docs/road/road_to_doom.md index c9891377..d76d53ee 100644 --- a/docs/road/road_to_doom.md +++ b/docs/road/road_to_doom.md @@ -6,7 +6,7 @@ **✅ ALL 33 REQUIRED ISSUES COMPLETE (100%)!** The Doom milestone has been fully achieved in release v0.1.666. All infrastructure needed to run the classic 1993 Doom game in userland is now implemented, tested, and integrated into the build system. -**Using [doomgeneric](https://github.com/ozkl/doomgeneric)**: meniOS integrates the excellent doomgeneric port by [@ozkl](https://github.com/ozkl), which provides a clean platform abstraction layer. The meniOS-specific implementation is in `app/doom/doomgeneric_menios.c`, providing graphics, input, timing, and file I/O integration. +**Using [doomgeneric](https://github.com/ozkl/doomgeneric)**: meniOS integrates the excellent doomgeneric port by [@ozkl](https://github.com/ozkl), which provides a clean platform abstraction layer. The meniOS-specific implementation is in `vendor/genericdoom/doomgeneric_menios.c`, providing graphics, input, timing, and file I/O integration. ## 📊 **Progress Assessment** @@ -269,7 +269,7 @@ Port layer, graphics, input, and build integration for running Doom: - mmap support for direct video RAM access - ioctl interface for geometry/format queries (FBIOGET_VSCREENINFO) - Expose width, height, pitch, pixel format to userland -- **Current Gap**: `src/kernel/file.c:507` routes through `fb_putchar` only +- **Current Gap**: `src/kernel/fs/core/file.c:507` routes through `fb_putchar` only - **Impact**: DG_DrawFrame() needs raw scanline blitting to video memory #### **Real Key Events Delivery** (Issue #302) ✅ **COMPLETE** @@ -291,7 +291,7 @@ Port layer, graphics, input, and build integration for running Doom: - Core format parser for %d, %i, %u, %x, %o, %f, %s, %c, %n, %[...] specifiers - Width specifiers and assignment suppression (*) - Length modifiers (hh, h, l, ll, L, z, t) -- **Impact**: Doom config parsing (app/doom/m_config.c, m_misc.c) now functional +- **Impact**: Doom config parsing (vendor/genericdoom/m_config.c, m_misc.c) now functional #### **Wire Up meniOS Port Layer** (Issue #300) ✅ **COMPLETE** - ✅ **Status**: Doomgeneric now drives meniOS graphics, input, and timing end-to-end. @@ -305,7 +305,7 @@ Port layer, graphics, input, and build integration for running Doom: #### **Doom meniOS-Specific Build System** (Issue #311) ✅ **COMPLETE** - ✅ **Status**: meniOS now ships a dedicated Doom build flow. - **Highlights**: - - ✅ Added `app/doom/Makefile.menios` that compiles and links `build/bin/doom.elf` with the meniOS SDK. + - ✅ Added `vendor/genericdoom/Makefile.menios` that compiles and links `build/bin/doom.elf` with the meniOS SDK. - ✅ Top-level `make doom` target builds the full binary (Docker-aware) for rapid iteration. - ✅ Extended SDK headers (`stdio.h`, `stdlib.h`, `string.h`, etc.) so Doom sources compile cleanly. - **Impact**: Build infrastructure is done; final packaging and install steps move to #312 after libc work. diff --git a/docs/road/road_to_gui.md b/docs/road/road_to_gui.md new file mode 100644 index 00000000..363256c1 --- /dev/null +++ b/docs/road/road_to_gui.md @@ -0,0 +1,622 @@ +# Road to GUI - Graphical User Interface Stack + +**Status:** Planning +**Target:** Full graphical desktop environment with compositor and window manager +**Timeline:** 6-9 months (26-39 weeks) + +--- + +## Vision + +Build a complete graphical user interface stack for meniOS, enabling rich graphical applications, window management, and a modern desktop environment. The architecture follows a Wayland-style compositor model where the display server and compositor are unified. + +### What We'll Be Able to Do + +With a complete GUI stack, meniOS will support: + +#### Core Graphics Capabilities +- **Vector Graphics**: High-quality 2D rendering with anti-aliasing +- **Text Rendering**: Beautiful font rendering with subpixel anti-aliasing +- **Image Compositing**: Alpha blending, transformations, gradients +- **PDF/SVG Output**: Generate documents and vector graphics +- **Hardware Acceleration**: Future GPU-accelerated rendering + +#### Window Management +- **Multiple Windows**: Run many applications simultaneously +- **Window Decorations**: Title bars, borders, resize handles +- **Window Operations**: Move, resize, minimize, maximize, close +- **Focus Management**: Click-to-focus, focus-follows-mouse +- **Virtual Desktops**: Multiple workspaces (future) +- **Tiling Support**: Automatic window arrangement (future) + +#### Application Types +- **Terminal Emulator**: Graphical terminal with Unicode support +- **Text Editor**: GUI text editor with syntax highlighting +- **File Manager**: Visual file browser with icons +- **Image Viewer**: Display PNG, JPEG, BMP images +- **Web Browser**: Simple HTML renderer (future) +- **Development Tools**: GUI debugger, hex editor, profiler +- **Games**: 2D games, puzzle games, arcade classics +- **System Monitor**: CPU, memory, process viewer +- **Settings Panel**: System configuration GUI + +#### Desktop Environment Features +- **Desktop Background**: Wallpapers and color themes +- **Panel/Taskbar**: Application launcher, system tray, clock +- **Menus**: Context menus, application menus +- **Notifications**: System notifications and alerts +- **Drag and Drop**: File and window dragging +- **Clipboard**: Copy/paste between applications +- **Accessibility**: Screen reader support, high contrast themes (future) + +--- + +## Architecture Overview + +### Wayland-Style Compositor Model + +``` +┌─────────────────────────────────────────────────────────────┐ +│ GUI Applications │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ Terminal │ │ Editor │ │ Browser │ │ Game │ │ +│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ +│ │ │ │ │ │ +│ └─────────────┴──────────────┴─────────────┘ │ +│ │ │ +│ Client Protocol │ +│ (IPC/Sockets) │ +└──────────────────────────┬──────────────────────────────────┘ + │ +┌──────────────────────────▼──────────────────────────────────┐ +│ MeniOS Compositor (Display Server) │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ Window Manager (Integrated) │ │ +│ │ - Window placement, sizing, stacking │ │ +│ │ - Focus management, decorations │ │ +│ │ - Input event routing │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ Scene Graph & Compositing │ │ +│ │ - Damage tracking, dirty rectangles │ │ +│ │ - Double buffering, vsync │ │ +│ │ - Window textures, transformations │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ Input Management │ │ +│ │ - Keyboard events → focused window │ │ +│ │ - Mouse events → window hit testing │ │ +│ │ - Touch events (future) │ │ +│ └────────────────────────────────────────────────────────┘ │ +└──────────────────────────┬──────────────────────────────────┘ + │ +┌──────────────────────────▼──────────────────────────────────┐ +│ Graphics Stack (Cairo) │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ Cairo 2D Graphics Library (Vector Rendering) │ │ +│ │ - Paths, fills, strokes │ │ +│ │ - Text rendering with FreeType │ │ +│ │ - Image compositing, transformations │ │ +│ └───────────────────┬────────────────────────────────────┘ │ +│ ┌──────────────────▼─────────────────────────────────────┐ │ +│ │ Pixman (Low-Level Pixel Manipulation) │ │ +│ │ - Software rasterization │ │ +│ │ - Compositing operations │ │ +│ │ - SIMD optimizations (SSE2/AVX) │ │ +│ └────────────────────────────────────────────────────────┘ │ +└──────────────────────────┬──────────────────────────────────┘ + │ +┌──────────────────────────▼──────────────────────────────────┐ +│ Kernel Graphics Support │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ Framebuffer Driver (/dev/fb0) │ │ +│ │ - Linear framebuffer access │ │ +│ │ - Mode setting (resolution, bpp) │ │ +│ │ - Page flipping (vsync) │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ Input Drivers (Keyboard, Mouse, Touch) │ │ +│ │ - PS/2 keyboard (#37) │ │ +│ │ - PS/2 mouse (#143) │ │ +│ │ - USB HID (future) │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ Shared Memory & IPC (Client Communication) │ │ +│ │ - Shared framebuffers │ │ +│ │ - Unix domain sockets │ │ +│ │ - Event queues │ │ +│ └────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Key Design Decisions + +1. **Wayland-Style Architecture**: Compositor is the display server (not X11) + - Simpler, more secure + - Better performance (direct rendering) + - Modern approach used by most Linux desktops + +2. **Cairo for 2D Graphics**: Industry-standard, mature library + - Used by GTK, Firefox, Inkscape, LibreOffice + - Excellent text rendering with FreeType + - PDF/SVG support built-in + +3. **Integrated Window Manager**: Part of compositor, not separate + - Tighter integration, better performance + - Easier to maintain consistency + - Can still support pluggable policies + +4. **Shared Memory for Performance**: Zero-copy window buffers + - Clients render to shared memory + - Compositor composites without copying + - Critical for video and games + +--- + +## Milestone Breakdown + +### Milestone 1: Graphics Foundation (8-10 weeks) + +**Goal:** Get Cairo rendering to the framebuffer + +#### Issues +- **#397: Port Pixman** (3-4 weeks) + - Low-level pixel manipulation library + - Cairo dependency + - SIMD optimizations for x86-64 + +- **#398: Port FreeType** (2-3 weeks) + - TrueType/OpenType font rendering + - Required for Cairo text support + +- **#396: Port Cairo** (3-4 weeks) + - Core 2D graphics library + - Framebuffer backend + - Image and PDF surfaces + +#### Deliverables +- [ ] Cairo library compiled and running on meniOS +- [ ] Can render basic shapes to framebuffer +- [ ] Can render text with TrueType fonts +- [ ] Example programs: hello world, clock, graphics demo + +#### Example Code +```c +// Simple Cairo demo +#include + +int main() { + // Create Cairo surface for framebuffer + cairo_surface_t *surface = cairo_image_surface_create( + CAIRO_FORMAT_RGB24, 1024, 768); + cairo_t *cr = cairo_create(surface); + + // Draw a blue rectangle + cairo_set_source_rgb(cr, 0.2, 0.3, 0.8); + cairo_rectangle(cr, 100, 100, 400, 300); + cairo_fill(cr); + + // Draw text + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_select_font_face(cr, "Sans", + CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size(cr, 48); + cairo_move_to(cr, 150, 250); + cairo_show_text(cr, "Hello, meniOS!"); + + // Blit to framebuffer + int fb = open("/dev/fb0", O_RDWR); + write(fb, cairo_image_surface_get_data(surface), 1024*768*4); + + cairo_destroy(cr); + cairo_surface_destroy(surface); +} +``` + +--- + +### Milestone 2: Input Stack (4-5 weeks) + +**Goal:** Unified input event system for GUI + +#### Issues +- **#399: Input Event System** (2-3 weeks) + - Abstract input events (keyboard, mouse, touch) + - Event queue and dispatch + - Integration with existing PS/2 drivers + +- **#400: Mouse Cursor Rendering** (1-2 weeks) + - Hardware cursor or software cursor + - Cursor themes and animations + - Hit testing for window selection + +#### Deliverables +- [ ] Unified input event API +- [ ] Mouse cursor visible on screen +- [ ] Can click and receive events +- [ ] Example: Interactive drawing program + +#### Dependencies +- Blocks on: #143 (PS/2 Mouse Driver) +- Uses: #37 (PS/2 Keyboard - already complete) + +--- + +### Milestone 3: Shared Memory & IPC (3-4 weeks) + +**Goal:** Client-server communication infrastructure + +#### Issues +- **#401: Shared Memory Implementation** (2-3 weeks) + - POSIX shared memory (shm_open, mmap) + - Shared framebuffers for windows + - Synchronization primitives + +- **#402: Unix Domain Sockets** (1-2 weeks) + - AF_UNIX socket support + - Compositor protocol transport + - Credential passing (SCM_RIGHTS) + +#### Deliverables +- [ ] Clients can allocate shared memory +- [ ] Compositor can map client buffers +- [ ] Socket communication working +- [ ] Example: Client renders, compositor displays + +#### Example Usage +```c +// Client allocates window buffer +int fd = shm_open("/window_123", O_CREAT|O_RDWR, 0600); +ftruncate(fd, 1024 * 768 * 4); +void *pixels = mmap(NULL, 1024*768*4, PROT_READ|PROT_WRITE, + MAP_SHARED, fd, 0); + +// Client renders to shared memory +cairo_surface_t *surface = + cairo_image_surface_create_for_data(pixels, + CAIRO_FORMAT_ARGB32, 1024, 768, 1024*4); +// ... render with Cairo ... + +// Send buffer to compositor via socket +compositor_attach_buffer(fd, 1024, 768); +``` + +--- + +### Milestone 4: Compositor Core (6-8 weeks) + +**Goal:** Basic compositor that can display windows + +#### Issues +- **#403: Compositor Core** (3-4 weeks) + - Window surface management + - Scene graph and rendering pipeline + - Damage tracking + +- **#404: Window Protocol** (2-3 weeks) + - Define client-compositor protocol + - Surface creation, buffer attachment + - Frame callbacks, damage reporting + +- **#405: Composition Engine** (2-3 weeks) + - Render all windows to screen + - Z-order management + - Double buffering, vsync + - Dirty rectangle optimization + +#### Deliverables +- [ ] Compositor runs as userland daemon +- [ ] Can create and display windows +- [ ] Multiple windows composited correctly +- [ ] Smooth rendering, no tearing +- [ ] Example: Run multiple Cairo apps + +#### Architecture +```c +// Compositor state +struct compositor { + cairo_surface_t *screen; // Framebuffer surface + struct window *windows; // Linked list + struct window *focused; // Current focus + int damage_dirty; // Needs repaint +}; + +struct window { + uint32_t id; + int x, y, width, height; + int mapped, visible; + cairo_surface_t *surface; // Shared memory surface + struct window *next; +}; + +// Render loop +void compositor_render(struct compositor *comp) { + if (!comp->damage_dirty) return; + + // Clear screen + cairo_t *cr = cairo_create(comp->screen); + cairo_set_source_rgb(cr, 0.2, 0.2, 0.2); + cairo_paint(cr); + + // Composite windows back-to-front + for (struct window *w = comp->windows; w; w = w->next) { + if (!w->visible) continue; + + cairo_set_source_surface(cr, w->surface, w->x, w->y); + cairo_rectangle(cr, w->x, w->y, w->width, w->height); + cairo_fill(cr); + } + + cairo_destroy(cr); + comp->damage_dirty = 0; +} +``` + +--- + +### Milestone 5: Window Manager (5-7 weeks) + +**Goal:** Full window management capabilities + +#### Issues +- **#406: Window Manager Core** (3-4 weeks) + - Window placement policies + - Window decorations (title bar, borders) + - Move, resize, minimize, maximize, close + +- **#407: Focus Management** (1-2 weeks) + - Click-to-focus + - Focus-follows-mouse (optional) + - Keyboard focus and tab cycling + +- **#408: Window Decorations** (2-3 weeks) + - Title bar rendering + - Resize handles + - Minimize/maximize/close buttons + - Active/inactive styling + +#### Deliverables +- [ ] Windows have decorations +- [ ] Can move windows by dragging title bar +- [ ] Can resize windows by dragging edges +- [ ] Can close windows via close button +- [ ] Keyboard focus works correctly +- [ ] Example: Multiple interactive apps + +#### User Experience +``` +┌─────────────── Terminal ──────────────────[_][□][X]─┐ +│ $ cat /etc/motd │ +│ Welcome to meniOS! │ +│ $ │ +│ │ +└──────────────────────────────────────────────────────┘ + +┌─────────────── Text Editor ───────────────[_][□][X]─┐ +│ File Edit View Help │ +│ ─────────────────────────────────────────────────── │ +│ /* Hello, World! */ │ +│ int main() { │ +│ printf("Hello, meniOS!\n"); │ +│ return 0; │ +│ } │ +└──────────────────────────────────────────────────────┘ +``` + +--- + +### Milestone 6: Desktop Environment (6-8 weeks) + +**Goal:** Complete desktop with panel, menus, applications + +#### Issues +- **#409: Desktop Shell** (3-4 weeks) + - Desktop background + - Panel/taskbar + - Application launcher + - System tray + +- **#410: Core GUI Applications** (3-4 weeks) + - Terminal emulator + - Text editor + - File manager + - Image viewer + - Calculator + +#### Deliverables +- [ ] Desktop with panel and background +- [ ] Can launch applications from menu +- [ ] System tray with clock +- [ ] At least 3 GUI applications working +- [ ] Cohesive look and feel + +#### Desktop Layout +``` +┌────────────────────────────────────────────────────────────┐ +│ Applications System Help 🔊 🔋 │ ← Panel +├────────────────────────────────────────────────────────────┤ +│ │ +│ [Desktop Background] │ +│ │ +│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ +│ │ Term │ │ Edit │ │ Files│ │ Calc │ ← App icons │ +│ └──────┘ └──────┘ └──────┘ └──────┘ │ +│ │ +│ │ +│ │ +└────────────────────────────────────────────────────────────┘ +``` + +--- + +## Implementation Phases + +### Phase 1: Foundation (Weeks 1-10) +- Port Pixman, FreeType, Cairo +- Get basic rendering working +- **Milestone:** "Hello, World!" with Cairo + +### Phase 2: Input & IPC (Weeks 11-18) +- Input event system +- Shared memory +- Unix sockets +- **Milestone:** Interactive Cairo demo + +### Phase 3: Compositor (Weeks 19-26) +- Compositor core +- Window protocol +- Composition engine +- **Milestone:** Multiple windows displaying + +### Phase 4: Window Management (Weeks 27-33) +- Window manager +- Decorations +- Focus management +- **Milestone:** Full window operations + +### Phase 5: Desktop (Weeks 34-41) +- Desktop shell +- Panel and menus +- Core applications +- **Milestone:** Complete desktop environment + +### Phase 6: Polish & Extensions (Weeks 42+) +- Performance optimization +- Additional applications +- Accessibility features +- **Future:** 3D support (OpenGL/Vulkan) + +--- + +## Dependencies + +### Kernel Features Required +- ✅ Framebuffer driver (`/dev/fb0`) +- ✅ Keyboard driver (#37 - PS/2 keyboard) +- ⏳ Mouse driver (#143 - PS/2 mouse) - **CRITICAL BLOCKER** +- ⏳ Shared memory (new) +- ⏳ Unix domain sockets (new) +- ✅ Process management (fork/exec) +- ✅ Signals +- ⏳ Robust IPC + +### Userland Features Required +- ✅ libc (#193 - mostly complete) +- ✅ Dynamic memory (malloc/free) +- ⏳ Threading (#109 - pthread API) +- ⏳ Thread-safe libc (#339) +- ✅ File I/O +- ⏳ Socket API (#341 - future) + +### External Libraries to Port +1. **Pixman** (0.42.x) + - ~50,000 lines of C + - SIMD optimizations (SSE2, SSSE3, AVX2) + - No external dependencies + +2. **FreeType** (2.13.x) + - ~100,000 lines of C + - Minimal dependencies + - Optional: libpng for PNG fonts + +3. **Cairo** (1.18.x) + - ~150,000 lines of C + - Depends on: Pixman, FreeType + - Multiple backends (image, PDF, SVG) + +--- + +## Timeline Summary + +| Milestone | Duration | Cumulative | +|-----------|----------|------------| +| 1. Graphics Foundation | 8-10 weeks | 10 weeks | +| 2. Input Stack | 4-5 weeks | 15 weeks | +| 3. Shared Memory & IPC | 3-4 weeks | 19 weeks | +| 4. Compositor Core | 6-8 weeks | 27 weeks | +| 5. Window Manager | 5-7 weeks | 34 weeks | +| 6. Desktop Environment | 6-8 weeks | 42 weeks | + +**Total:** 32-42 weeks (8-10 months) + +--- + +## Success Criteria + +### Minimal GUI (6 months) +- [ ] Cairo rendering to framebuffer +- [ ] Mouse and keyboard input working +- [ ] Compositor displaying multiple windows +- [ ] Basic window management (move, resize, close) +- [ ] At least 1 GUI application (terminal emulator) + +### Full Desktop (9 months) +- [ ] Desktop shell with panel +- [ ] Application launcher and menus +- [ ] Window decorations and full WM features +- [ ] 3+ GUI applications (terminal, editor, file manager) +- [ ] Smooth performance, no visible tearing +- [ ] Professional look and feel + +### Future Extensions +- [ ] Widget toolkit for easy app development +- [ ] More applications (browser, media player, games) +- [ ] Accessibility features +- [ ] OpenGL/Vulkan 3D support +- [ ] Multi-monitor support +- [ ] Network transparency (remote apps) + +--- + +## Related Issues + +### Blockers +- #143 - PS/2 Mouse Driver (CRITICAL - needed for GUI input) +- #109 - pthread API (needed for compositor threading) +- #339 - Thread-safe libc (needed for multi-threaded apps) + +### Related +- #129 - Unicode text rendering (complements Cairo text) +- #394 - Terminal scrollback (will use in GUI terminal) +- #170 - Character device infrastructure (input devices) + +### Enables +- Advanced text editor +- Web browser +- Image editor +- Video player +- Games with rich graphics +- Development tools (GUI debugger, profiler) + +--- + +## References + +### Technical Specifications +- **Wayland Protocol**: https://wayland.freedesktop.org/docs/html/ +- **Cairo Documentation**: https://www.cairographics.org/manual/ +- **Pixman**: https://www.pixman.org/ +- **FreeType**: https://freetype.org/ + +### Inspiration +- **Weston** (Reference Wayland compositor) +- **Sway** (Tiling Wayland compositor) +- **Arcan** (Novel display server architecture) +- **SerenityOS** (Hobby OS with complete GUI) +- **ToaruOS** (Compositor and window manager reference) + +### Example Compositor Projects +- **tinywl** - Minimal Wayland compositor (~500 lines) +- **wlroots** - Compositor library used by Sway +- **Smithay** - Rust Wayland compositor library + +--- + +## Notes + +- **Why not X11?** Too complex, legacy design, security issues +- **Why Cairo?** Industry standard, excellent quality, well documented +- **Why Wayland-style?** Modern, secure, better performance +- **GPU acceleration?** Future work after software rendering proven + +This roadmap represents a significant but achievable goal for meniOS. Each milestone builds incrementally, with working deliverables at every stage. diff --git a/docs/road_to_multiuser.md b/docs/road/road_to_multiuser.md similarity index 100% rename from docs/road_to_multiuser.md rename to docs/road/road_to_multiuser.md diff --git a/docs/road_to_smp.md b/docs/road/road_to_smp.md similarity index 100% rename from docs/road_to_smp.md rename to docs/road/road_to_smp.md diff --git a/docs/tools.md b/docs/tools.md index a2782a17..4e8617c2 100644 --- a/docs/tools.md +++ b/docs/tools.md @@ -34,7 +34,7 @@ When `make sdk` runs, the build copies these wrappers into `build/sdk/bin/menios-*` (and the matching `x86_64-menios-*` aliases) so that userland components can rely on a self-contained toolchain. User applications and helper makefiles reference the staged copies—for example, -`app/doom/Makefile.menios` exports `MENIOS_GCC=$(ROOT_DIR)/build/sdk/bin/menios-gcc`. +`vendor/genericdoom/Makefile.menios` exports `MENIOS_GCC=$(ROOT_DIR)/build/sdk/bin/menios-gcc`. The same wrappers are threaded through optional host toolchain builds. During `make toolchain`, the top-level makefile sets `AR=$(abspath tools/menios-ar.sh)` diff --git a/include/kernel/acpi.h b/include/kernel/acpi.h index b4eef5d1..1b20da01 100644 --- a/include/kernel/acpi.h +++ b/include/kernel/acpi.h @@ -74,7 +74,7 @@ typedef struct acpi_address_t { phys_addr_t address; } acpi_address_t; -int acpi_init(); +int acpi_initialize(void); int acpi_shutdown(); #ifdef __cplusplus @@ -82,4 +82,3 @@ int acpi_shutdown(); #endif #endif /* MENIOS_INCLUDE_KERNEL_ACPI_H */ - diff --git a/include/kernel/ahci.h b/include/kernel/ahci.h index a204b819..787d17aa 100644 --- a/include/kernel/ahci.h +++ b/include/kernel/ahci.h @@ -32,7 +32,7 @@ typedef struct ahci_controller_t { typedef ahci_controller_t* ahci_controller_p; -void ahci_init(void); +void ahci_initialize(void); void ahci_pci_probe(const pci_device_location_t* location, uint32_t vendor_device, uint32_t class_reg); diff --git a/include/kernel/apic.h b/include/kernel/arch/x86_64/apic.h similarity index 82% rename from include/kernel/apic.h rename to include/kernel/arch/x86_64/apic.h index b691e034..72ac72b6 100644 --- a/include/kernel/apic.h +++ b/include/kernel/arch/x86_64/apic.h @@ -1,5 +1,5 @@ -#ifndef MENIOS_INCLUDE_KERNEL_APIC_H -#define MENIOS_INCLUDE_KERNEL_APIC_H +#ifndef MENIOS_INCLUDE_KERNEL_ARCH_X86_64_APIC_H +#define MENIOS_INCLUDE_KERNEL_ARCH_X86_64_APIC_H #include #include @@ -33,8 +33,8 @@ #define DIV_BY_128 0x0a #define DIV_BY_1 0x0b -void apic_init(); -void lapic_timer_init(); +void apic_initialize(void); +void lapic_timer_initialize(void); void timer_frequency(uint32_t freq); void write_lapic(uintptr_t reg, uint32_t value); @@ -47,4 +47,4 @@ bool apic_configure_irq(uint32_t gsi, void apic_send_eoi(void); -#endif +#endif /* MENIOS_INCLUDE_KERNEL_ARCH_X86_64_APIC_H */ diff --git a/include/kernel/arch/x86_64/cpu.h b/include/kernel/arch/x86_64/cpu.h new file mode 100644 index 00000000..f090060f --- /dev/null +++ b/include/kernel/arch/x86_64/cpu.h @@ -0,0 +1,8 @@ +#ifndef MENIOS_INCLUDE_KERNEL_ARCH_X86_64_CPU_H +#define MENIOS_INCLUDE_KERNEL_ARCH_X86_64_CPU_H + +#include + +void cpu_enable_sse(void); + +#endif /* MENIOS_INCLUDE_KERNEL_ARCH_X86_64_CPU_H */ diff --git a/include/kernel/gdt.h b/include/kernel/arch/x86_64/gdt.h similarity index 88% rename from include/kernel/gdt.h rename to include/kernel/arch/x86_64/gdt.h index cf0554e1..117edd9a 100644 --- a/include/kernel/gdt.h +++ b/include/kernel/arch/x86_64/gdt.h @@ -1,5 +1,5 @@ -#ifndef MENIOS_INCLUDE_KERNEL_GDT_H -#define MENIOS_INCLUDE_KERNEL_GDT_H +#ifndef MENIOS_INCLUDE_KERNEL_ARCH_X86_64_GDT_H +#define MENIOS_INCLUDE_KERNEL_ARCH_X86_64_GDT_H #include @@ -45,9 +45,9 @@ typedef struct __attribute__((packed)) { extern void gdt_load(gdt_pointer_t* gdt_descriptor); -void gdt_init(); +void gdt_initialize(void); void gdt_set_entry(int index, uint64_t base, uint32_t limit, uint8_t access, uint8_t granularity); void tss_update_kernel_stack(uint64_t stack_top); uint64_t gdt_get_tss_stack_top(void); -#endif +#endif /* MENIOS_INCLUDE_KERNEL_ARCH_X86_64_GDT_H */ diff --git a/include/kernel/idt.h b/include/kernel/arch/x86_64/idt.h similarity index 87% rename from include/kernel/idt.h rename to include/kernel/arch/x86_64/idt.h index 5ac62e83..4bfc1469 100644 --- a/include/kernel/idt.h +++ b/include/kernel/arch/x86_64/idt.h @@ -1,5 +1,5 @@ -#ifndef MENIOS_INCLUDE_KERNEL_IDT_H -#define MENIOS_INCLUDE_KERNEL_IDT_H +#ifndef MENIOS_INCLUDE_KERNEL_ARCH_X86_64_IDT_H +#define MENIOS_INCLUDE_KERNEL_ARCH_X86_64_IDT_H #include #include @@ -91,19 +91,19 @@ typedef struct { uint16_t descriptor_index; } idt_gpf_error_info_t; -extern void idt_df_isr_asm_handler(); -extern void idt_generic_isr_asm_handler(); -extern void idt_gpf_isr_asm_handler(); +extern void idt_df_isr_asm_handler(void); +extern void idt_generic_isr_asm_handler(void); +extern void idt_gpf_isr_asm_handler(void); extern void idt_load(idt_pointer_t *idt_ptr); -extern void idt_pf_isr_asm_handler(); -extern void idt_period_timer_isr_asm_handler(); -extern void ps2kb_isr_handler(); -extern void ahci_isr_handler(); -extern void syscall_isr_handler(); +extern void idt_pf_isr_asm_handler(void); +extern void idt_period_timer_isr_asm_handler(void); +extern void ps2kb_isr_handler(void); +extern void ahci_isr_handler(void); +extern void syscall_isr_handler(void); void idt_add_isr(int interruption, void* handler); void idt_add_user_isr(int interruption, void* handler); -void idt_init(); +void idt_initialize(void); static inline void idt_decode_page_fault(uint64_t error_code, idt_pf_error_info_t *info) { info->present = (error_code & (1ull << 0)) != 0; info->write = (error_code & (1ull << 1)) != 0; @@ -122,4 +122,4 @@ static inline void idt_decode_gpf(uint64_t error_code, idt_gpf_error_info_t *inf info->descriptor_index = (uint16_t)((error_code >> 3) & 0x1fffull); } -#endif +#endif /* MENIOS_INCLUDE_KERNEL_ARCH_X86_64_IDT_H */ diff --git a/include/kernel/block_cache.h b/include/kernel/block_cache.h index a34e8deb..74c955a7 100644 --- a/include/kernel/block_cache.h +++ b/include/kernel/block_cache.h @@ -26,7 +26,7 @@ typedef struct buffer_head { struct buffer_head* lru_next; } buffer_head_t; -void block_cache_init(void); +void block_cache_initialize(void); void block_cache_shutdown(void); buffer_head_t* bread(block_device_t* device, uint64_t lba); diff --git a/include/kernel/block_device.h b/include/kernel/block_device.h index 435d33a3..9feceac4 100644 --- a/include/kernel/block_device.h +++ b/include/kernel/block_device.h @@ -42,7 +42,7 @@ struct block_device_t { typedef bool (*block_device_iter_t)(block_device_t* device, void* context); -void block_device_system_init(void); +void block_device_system_initialize(void); bool block_device_register(block_device_t* device); void block_device_unregister(block_device_t* device); block_device_t* block_device_lookup(const char* name); diff --git a/include/kernel/core/logo.h b/include/kernel/core/logo.h new file mode 100644 index 00000000..2622bc96 --- /dev/null +++ b/include/kernel/core/logo.h @@ -0,0 +1,6 @@ +#ifndef MENIOS_INCLUDE_KERNEL_CORE_LOGO_H +#define MENIOS_INCLUDE_KERNEL_CORE_LOGO_H + +void print_logo(void); + +#endif /* MENIOS_INCLUDE_KERNEL_CORE_LOGO_H */ diff --git a/include/kernel/core/services.h b/include/kernel/core/services.h new file mode 100644 index 00000000..54bb8e13 --- /dev/null +++ b/include/kernel/core/services.h @@ -0,0 +1,6 @@ +#ifndef MENIOS_INCLUDE_KERNEL_CORE_SERVICES_H +#define MENIOS_INCLUDE_KERNEL_CORE_SERVICES_H + +void services_initialize(void); + +#endif /* MENIOS_INCLUDE_KERNEL_CORE_SERVICES_H */ diff --git a/include/kernel/cpu.h b/include/kernel/cpu.h deleted file mode 100644 index 66f6a253..00000000 --- a/include/kernel/cpu.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef KERNEL_CPU_H -#define KERNEL_CPU_H - -#include - -void cpu_enable_sse(void); - -#endif /* KERNEL_CPU_H */ diff --git a/include/kernel/devfs.h b/include/kernel/devfs.h deleted file mode 100644 index 862a7e87..00000000 --- a/include/kernel/devfs.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef MENIOS_INCLUDE_KERNEL_DEVFS_H -#define MENIOS_INCLUDE_KERNEL_DEVFS_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -bool devfs_mount(void); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/include/kernel/driver.h b/include/kernel/driver.h index 6f5279a9..168c1ca1 100644 --- a/include/kernel/driver.h +++ b/include/kernel/driver.h @@ -30,7 +30,7 @@ typedef struct driver_list_t { typedef struct driver_list_t* driver_list_p; -void driver_init(); +void driver_registry_init(void); void driver_register(driver_t*); driver_p driver_load(const char* hid); diff --git a/include/kernel/driver/pciroot.h b/include/kernel/driver/pciroot.h deleted file mode 100644 index e23a3eea..00000000 --- a/include/kernel/driver/pciroot.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void pciroot_init(); \ No newline at end of file diff --git a/include/kernel/driver/ps2kb.h b/include/kernel/driver/ps2kb.h deleted file mode 100644 index a4df21d9..00000000 --- a/include/kernel/driver/ps2kb.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -void ps2kb_init(); -int kgetchar(void); diff --git a/include/kernel/drivers/bus/pciroot.h b/include/kernel/drivers/bus/pciroot.h new file mode 100644 index 00000000..02079b05 --- /dev/null +++ b/include/kernel/drivers/bus/pciroot.h @@ -0,0 +1,6 @@ +#ifndef MENIOS_INCLUDE_KERNEL_DRIVERS_BUS_PCIROOT_H +#define MENIOS_INCLUDE_KERNEL_DRIVERS_BUS_PCIROOT_H + +void pciroot_register_driver(void); + +#endif /* MENIOS_INCLUDE_KERNEL_DRIVERS_BUS_PCIROOT_H */ diff --git a/include/kernel/driver/ps2.h b/include/kernel/drivers/input/ps2.h similarity index 57% rename from include/kernel/driver/ps2.h rename to include/kernel/drivers/input/ps2.h index f8793f66..1bdebda9 100644 --- a/include/kernel/driver/ps2.h +++ b/include/kernel/drivers/input/ps2.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef MENIOS_INCLUDE_KERNEL_DRIVERS_INPUT_PS2_H +#define MENIOS_INCLUDE_KERNEL_DRIVERS_INPUT_PS2_H // PS/2 Controller Ports #define PS2_DATA_PORT 0x60 @@ -9,4 +10,6 @@ #define PS2_DISABLE_FIRST_PORT 0xad #define PS2_WRITE_MODE 0x60 #define PS2_ENABLE_SCANNING 0xf4 -#define PS2_RESET_COMMAND 0xff \ No newline at end of file +#define PS2_RESET_COMMAND 0xff + +#endif /* MENIOS_INCLUDE_KERNEL_DRIVERS_INPUT_PS2_H */ diff --git a/include/kernel/drivers/input/ps2kb.h b/include/kernel/drivers/input/ps2kb.h new file mode 100644 index 00000000..73658ab7 --- /dev/null +++ b/include/kernel/drivers/input/ps2kb.h @@ -0,0 +1,7 @@ +#ifndef MENIOS_INCLUDE_KERNEL_DRIVERS_INPUT_PS2KB_H +#define MENIOS_INCLUDE_KERNEL_DRIVERS_INPUT_PS2KB_H + +void ps2kb_register_driver(void); +int kgetchar(void); + +#endif /* MENIOS_INCLUDE_KERNEL_DRIVERS_INPUT_PS2KB_H */ diff --git a/include/kernel/file.h b/include/kernel/file.h index 83248a02..c05beaab 100644 --- a/include/kernel/file.h +++ b/include/kernel/file.h @@ -59,7 +59,7 @@ typedef struct file_descriptor_entry_t { #define FD_FLAG_CLOEXEC (1u << 0) -void file_system_init(void); +void file_system_initialize(void); file_t* file_create(const file_ops_t* ops, void* private_data, uint32_t mode); void file_ref(file_t* file); void file_unref(file_t* file); @@ -73,7 +73,7 @@ int file_mmap(file_t* file, int file_chmod(file_t* file, mode_t mode); int file_utimens(file_t* file, const struct timespec times[2]); -void proc_file_table_init(struct proc_info_t* proc); +void proc_file_table_initialize(struct proc_info_t* proc); void proc_file_table_clone(struct proc_info_t* child, struct proc_info_t* parent); void proc_file_table_cleanup(struct proc_info_t* proc); void proc_file_table_prepare_exec(struct proc_info_t* proc); diff --git a/include/kernel/fonts.h b/include/kernel/fonts.h index 04103f66..46c8db19 100644 --- a/include/kernel/fonts.h +++ b/include/kernel/fonts.h @@ -21,7 +21,7 @@ typedef struct { glyph_t glyphs[192]; } font2_t; -void font_init(); +void font_initialize(void); glypht_t font_glyph(uint8_t ascii); #endif diff --git a/include/kernel/framebuffer.h b/include/kernel/framebuffer.h index eadd73c7..3b661417 100644 --- a/include/kernel/framebuffer.h +++ b/include/kernel/framebuffer.h @@ -66,7 +66,7 @@ bool fb_is_boot_mode(uint64_t width, uint64_t height, uint16_t bpp); void fb_get_boot_mode(framebuffer_mode_info_t* out); void fb_draw(); -void fb_init(); +void framebuffer_initialize(void); void fb_putpixel(uint32_t x, uint32_t y, uint32_t rgb); int fb_putchar(int c); void fb_list_modes(); diff --git a/include/kernel/fs.h b/include/kernel/fs/core.h similarity index 93% rename from include/kernel/fs.h rename to include/kernel/fs/core.h index fbf2070b..eb6f571f 100644 --- a/include/kernel/fs.h +++ b/include/kernel/fs/core.h @@ -1,9 +1,9 @@ -#ifndef MENIOS_INCLUDE_KERNEL_FS_H -#define MENIOS_INCLUDE_KERNEL_FS_H +#ifndef MENIOS_INCLUDE_KERNEL_FS_CORE_H +#define MENIOS_INCLUDE_KERNEL_FS_CORE_H #ifdef __cplusplus extern "C" { -#endif +#endif /* MENIOS_INCLUDE_KERNEL_FS_CORE_H */ #include #include @@ -38,6 +38,8 @@ typedef struct fs_path_info_t { uint64_t size; bool has_mode; mode_t mode; + bool has_rdev; + dev_t rdev; bool has_times; struct timespec atime; struct timespec mtime; @@ -81,7 +83,11 @@ static inline void fs_path_info_to_stat(const fs_path_info_t* info, struct stat* out_stat->st_blocks = (blkcnt_t)((info->size + 511ull) / 512ull); out_stat->st_ino = (ino_t)info->inode; out_stat->st_dev = 0; - out_stat->st_rdev = 0; + if(info->has_rdev) { + out_stat->st_rdev = info->rdev; + } else { + out_stat->st_rdev = 0; + } out_stat->st_uid = 0; out_stat->st_gid = 0; if(info->has_times) { diff --git a/include/kernel/fs/devfs/devfs.h b/include/kernel/fs/devfs/devfs.h new file mode 100644 index 00000000..d53e68a9 --- /dev/null +++ b/include/kernel/fs/devfs/devfs.h @@ -0,0 +1,43 @@ +#ifndef MENIOS_INCLUDE_KERNEL_FS_DEVFS_H +#define MENIOS_INCLUDE_KERNEL_FS_DEVFS_H + +#ifdef __cplusplus +extern "C" { +#endif /* MENIOS_INCLUDE_KERNEL_FS_DEVFS_H */ + +#include +#include +#include + +struct file; +typedef struct file file_t; + +typedef struct char_device char_device_t; + +typedef int (*char_device_open_fn)(char_device_t* device, int flags, file_t** out_file); + +struct char_device { + const char* name; + uint32_t access_mode; + char_device_open_fn open; + void* driver_data; + dev_t dev; + unsigned int minor_count; +}; + +void char_device_system_initialize(void); +int char_device_register(char_device_t* device); +void char_device_unregister(char_device_t* device); + +typedef void (*char_device_iter_fn)(const char_device_t* device, void* context); +void char_device_iterate(char_device_iter_fn fn, void* context); +char_device_t* char_device_lookup(dev_t dev); +void char_device_reserve_major(unsigned int major); + +bool devfs_mount(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/fs/procfs/procfs.h b/include/kernel/fs/procfs/procfs.h new file mode 100644 index 00000000..9501b015 --- /dev/null +++ b/include/kernel/fs/procfs/procfs.h @@ -0,0 +1,16 @@ +#ifndef MENIOS_INCLUDE_KERNEL_FS_PROCFS_H +#define MENIOS_INCLUDE_KERNEL_FS_PROCFS_H + +#ifdef __cplusplus +extern "C" { +#endif /* MENIOS_INCLUDE_KERNEL_FS_PROCFS_H */ + +#include + +bool procfs_mount(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/fs/tmpfs/tmpfs.h b/include/kernel/fs/tmpfs/tmpfs.h new file mode 100644 index 00000000..6f6b4491 --- /dev/null +++ b/include/kernel/fs/tmpfs/tmpfs.h @@ -0,0 +1,16 @@ +#ifndef MENIOS_INCLUDE_KERNEL_FS_TMPFS_H +#define MENIOS_INCLUDE_KERNEL_FS_TMPFS_H + +#ifdef __cplusplus +extern "C" { +#endif /* MENIOS_INCLUDE_KERNEL_FS_TMPFS_H */ + +#include + +bool tmpfs_mount(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/vfs.h b/include/kernel/fs/vfs/vfs.h similarity index 89% rename from include/kernel/vfs.h rename to include/kernel/fs/vfs/vfs.h index af764973..481d9f93 100644 --- a/include/kernel/vfs.h +++ b/include/kernel/fs/vfs/vfs.h @@ -1,16 +1,16 @@ -#ifndef MENIOS_INCLUDE_KERNEL_VFS_H -#define MENIOS_INCLUDE_KERNEL_VFS_H +#ifndef MENIOS_INCLUDE_KERNEL_FS_VFS_H +#define MENIOS_INCLUDE_KERNEL_FS_VFS_H #ifdef __cplusplus extern "C" { -#endif +#endif /* MENIOS_INCLUDE_KERNEL_FS_VFS_H */ #include #include #include #include -#include +#include #include #include @@ -33,12 +33,13 @@ typedef struct vfs_fs_driver_t { int (*mkdir)(void* fs_ctx, const char* path, bool exclusive); int (*rmdir)(void* fs_ctx, const char* path); int (*rename)(void* fs_ctx, const char* old_path, const char* new_path); + int (*mknod)(void* fs_ctx, const char* path, mode_t mode, dev_t dev); int (*chmod)(void* fs_ctx, const char* path, mode_t mode); int (*utimens)(void* fs_ctx, const char* path, const struct timespec times[2]); void (*destroy)(void* fs_ctx); } vfs_fs_driver_t; -bool vfs_init(void); +bool vfs_initialize(void); void vfs_shutdown(void); bool vfs_mount(const char* path, const vfs_fs_driver_t* driver, void* fs_ctx, bool read_only); bool vfs_mount_root(const vfs_fs_driver_t* driver, void* fs_ctx, bool read_only); @@ -53,6 +54,7 @@ int vfs_unlink(const char* path); int vfs_rmdir(const char* path); int vfs_mkdir(const char* path); int vfs_rename(const char* old_path, const char* new_path); +int vfs_mknod(const char* path, mode_t mode, dev_t dev); int vfs_chmod(const char* path, mode_t mode); int vfs_utimens(const char* path, const struct timespec times[2]); diff --git a/include/kernel/heap.h b/include/kernel/heap.h index debf513b..ba4c5201 100644 --- a/include/kernel/heap.h +++ b/include/kernel/heap.h @@ -45,7 +45,7 @@ typedef struct heap_stats_t { HEAP_INSPECT_RESULT inspect_heap(uint32_t node_index, heap_node_p* node); -void init_heap(void* addr, size_t size); +void heap_initialize(void* addr, size_t size); void* kmalloc(size_t size); void* kcalloc(size_t nelem, size_t elsize); diff --git a/include/kernel/hpet.h b/include/kernel/hpet.h index 216162a1..620106c1 100644 --- a/include/kernel/hpet.h +++ b/include/kernel/hpet.h @@ -49,10 +49,10 @@ typedef struct hpet_table_t { uint8_t page_protection; } __attribute__((packed)) hpet_table_t; -hpet_status_t hpet_timer_init(); +hpet_status_t hpet_timer_initialize(void); #ifdef __cplusplus } #endif -#endif //MENIOS_INCLUDE_KERNEL_HPET_H \ No newline at end of file +#endif //MENIOS_INCLUDE_KERNEL_HPET_H diff --git a/include/kernel/hw.h b/include/kernel/hw.h index b91a2645..db340848 100644 --- a/include/kernel/hw.h +++ b/include/kernel/hw.h @@ -17,7 +17,7 @@ struct hardware_device_t { hardware_device_p next; }; -void hardware_init(); +void hardware_initialize(void); hardware_device_p hardware_devices(void); void hardware_log_devices(void); diff --git a/include/kernel/mem.h b/include/kernel/mem.h index df34f701..1a8a1a05 100644 --- a/include/kernel/mem.h +++ b/include/kernel/mem.h @@ -3,7 +3,7 @@ #include -void mem_init(); -void init_memory_compactor(); +void memory_initialize(void); +void mem_compactor_initialize(void); -#endif \ No newline at end of file +#endif diff --git a/include/kernel/pmm.h b/include/kernel/pmm.h index 5dffe44e..76e8ff4c 100644 --- a/include/kernel/pmm.h +++ b/include/kernel/pmm.h @@ -128,7 +128,7 @@ uintptr_t read_cr2(); phys_addr_t read_cr3(); void write_cr3(phys_addr_t value); -void pmm_init(); +void pmm_initialize(void); uint64_t get_first_free_page(); diff --git a/include/kernel/proc.h b/include/kernel/proc.h index 63a8e2fd..165e05ff 100644 --- a/include/kernel/proc.h +++ b/include/kernel/proc.h @@ -208,7 +208,7 @@ extern proc_info_t kernel_process_info; extern proc_info_p procs[PROC_MAX]; extern proc_info_p current; -void scheduler_init(); +void scheduler_initialize(void); void proc_create(proc_info_p proc, const char* name, void (*entrypoint)(void *), void* arg); void proc_execute(proc_info_p proc); void proc_exit(int code); diff --git a/include/kernel/procfs.h b/include/kernel/procfs.h deleted file mode 100644 index c5df62aa..00000000 --- a/include/kernel/procfs.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef MENIOS_INCLUDE_KERNEL_PROCFS_H -#define MENIOS_INCLUDE_KERNEL_PROCFS_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -bool procfs_mount(void); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/include/kernel/rwlock.h b/include/kernel/rwlock.h index b3466ff0..c4fd1954 100644 --- a/include/kernel/rwlock.h +++ b/include/kernel/rwlock.h @@ -20,7 +20,7 @@ typedef struct krwlock_t { bool writer_active; } krwlock_t; -void krwlock_init(krwlock_t* rwlock); +void krwlock_initialize(krwlock_t* rwlock); void krwlock_destroy(krwlock_t* rwlock); void krwlock_rdlock(krwlock_t* rwlock); diff --git a/include/kernel/semaphore.h b/include/kernel/semaphore.h index 022ee523..c3717597 100644 --- a/include/kernel/semaphore.h +++ b/include/kernel/semaphore.h @@ -19,7 +19,7 @@ typedef struct ksem_t { typedef ksem_t* ksem_p; -void ksem_init(ksem_t* sem, int64_t value); +void ksem_initialize(ksem_t* sem, int64_t value); void ksem_destroy(ksem_t* sem); bool ksem_trywait(ksem_t* sem); void ksem_wait(ksem_t* sem); diff --git a/include/kernel/serial.h b/include/kernel/serial.h index 65a52c3a..2a78a922 100644 --- a/include/kernel/serial.h +++ b/include/kernel/serial.h @@ -5,7 +5,7 @@ #include #ifdef MENIOS_KERNEL -void serial_init(); +void serial_initialize(void); int serial_putchar(int ch); int serial_puts(const char* text); int serial_printf(const char* format, ...); @@ -17,7 +17,7 @@ int serial_vprintf(const char *format, va_list args); #else #ifdef MENIOS_NO_DEBUG - #define serial_init() + #define serial_initialize() #define serial_putchar(a) #define serial_puts(a) #define serial_printf(fmt, ...) @@ -28,7 +28,7 @@ int serial_vprintf(const char *format, va_list args); #define serial_error(a) #else #warning Calling printf as serial_printf - #define serial_init() + #define serial_initialize() #define serial_putchar(a) putchar(a) #define serial_puts(a) puts(a) #define serial_printf(fmt, ...) printf(fmt, ##__VA_ARGS__) diff --git a/include/kernel/services.h b/include/kernel/services.h deleted file mode 100644 index b9c5b503..00000000 --- a/include/kernel/services.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void init_services(); \ No newline at end of file diff --git a/include/kernel/shm.h b/include/kernel/shm.h index 91e82c3f..0cc14e7c 100644 --- a/include/kernel/shm.h +++ b/include/kernel/shm.h @@ -54,7 +54,7 @@ typedef enum { typedef phys_addr_t (*shm_page_alloc_fn)(size_t page_count); typedef void (*shm_page_free_fn)(phys_addr_t base, size_t page_count); -void shm_manager_init(void); +void shm_manager_initialize(void); void shm_set_allocator(shm_page_alloc_fn alloc_fn, shm_page_free_fn free_fn); diff --git a/include/kernel/signal.h b/include/kernel/signal.h index c575dd8b..23f467e9 100644 --- a/include/kernel/signal.h +++ b/include/kernel/signal.h @@ -24,7 +24,7 @@ static inline uint32_t sigbit(int signo) { return (uint32_t)(1u << (signo - 1)); } -void proc_signal_state_init(proc_info_p proc); +void proc_signal_state_initialize(proc_info_p proc); void proc_signal_state_copy(proc_info_p dst, proc_info_p src); void proc_signal_set_blocked(proc_info_p proc, uint32_t mask); void proc_signal_enqueue(proc_info_p proc, int signo); diff --git a/include/kernel/syscall.h b/include/kernel/syscall.h index 875114a0..a2d51bf6 100644 --- a/include/kernel/syscall.h +++ b/include/kernel/syscall.h @@ -33,7 +33,7 @@ typedef struct syscall_frame_t { typedef uint64_t (*syscall_handler_t)(syscall_frame_t* frame); -void syscall_init(void); +void syscall_initialize(void); uint64_t syscall_dispatch(syscall_frame_t* frame); #ifdef __cplusplus diff --git a/include/kernel/syscall_entry.h b/include/kernel/syscall_entry.h index 2401a302..104affec 100644 --- a/include/kernel/syscall_entry.h +++ b/include/kernel/syscall_entry.h @@ -7,7 +7,7 @@ extern "C" { #endif -void syscall_arch_init(void); +void syscall_arch_initialize(void); void syscall_set_kernel_stack(uint64_t rsp); #ifdef __cplusplus diff --git a/include/kernel/timer.h b/include/kernel/timer.h index 93267ea6..01927c0c 100644 --- a/include/kernel/timer.h +++ b/include/kernel/timer.h @@ -7,7 +7,7 @@ extern "C" { #include -void timer_init(); +void timer_initialize(void); void timer_eoi(); uint64_t boot_time(); diff --git a/include/kernel/tmpfs.h b/include/kernel/tmpfs.h deleted file mode 100644 index 7d2b7456..00000000 --- a/include/kernel/tmpfs.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef MENIOS_INCLUDE_KERNEL_TMPFS_H -#define MENIOS_INCLUDE_KERNEL_TMPFS_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -bool tmpfs_mount(void); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/include/kernel/tsc.h b/include/kernel/tsc.h index 36f69177..4ebf14da 100644 --- a/include/kernel/tsc.h +++ b/include/kernel/tsc.h @@ -10,7 +10,7 @@ extern "C" { #define TICKS_PER_SECOND 1000000000 -void tsc_init(); +void tsc_initialize(void); void tsc_override_calibration(uint64_t frequency_hz, uint64_t boot_seconds); diff --git a/include/menios/syscall.h b/include/menios/syscall.h index 17b887ff..7fca35bf 100644 --- a/include/menios/syscall.h +++ b/include/menios/syscall.h @@ -65,6 +65,7 @@ extern "C" { #define SYS_FCHMOD 103 #define SYS_UTIME 104 #define SYS_SHUTDOWN 105 +#define SYS_MKNOD 106 #ifdef __cplusplus } diff --git a/include/sys/stat.h b/include/sys/stat.h index b5556b61..2f005aa5 100644 --- a/include/sys/stat.h +++ b/include/sys/stat.h @@ -58,6 +58,7 @@ struct stat { #define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH) int mkdir(const char* path, mode_t mode); +int mknod(const char* path, mode_t mode, dev_t dev); int fstat(int fd, struct stat* buf); int stat(const char* path, struct stat* buf); int lstat(const char* path, struct stat* buf); diff --git a/include/sys/types.h b/include/sys/types.h index 33082e71..9ac045a9 100644 --- a/include/sys/types.h +++ b/include/sys/types.h @@ -19,6 +19,18 @@ typedef unsigned long nlink_t; typedef long blksize_t; typedef long long blkcnt_t; +#define MENIOS_DEV_MAJOR_BITS 12u +#define MENIOS_DEV_MINOR_BITS 20u +#define MENIOS_DEV_MAJOR_MASK ((dev_t)((1ull << MENIOS_DEV_MAJOR_BITS) - 1ull)) +#define MENIOS_DEV_MINOR_MASK ((dev_t)((1ull << MENIOS_DEV_MINOR_BITS) - 1ull)) + +#define MKDEV(ma, mi) \ + ((dev_t)(((((dev_t)(ma)) & MENIOS_DEV_MAJOR_MASK) << MENIOS_DEV_MINOR_BITS) | \ + (((dev_t)(mi)) & MENIOS_DEV_MINOR_MASK))) + +#define MAJOR(dev) ((unsigned int)((((dev_t)(dev)) >> MENIOS_DEV_MINOR_BITS) & MENIOS_DEV_MAJOR_MASK)) +#define MINOR(dev) ((unsigned int)(((dev_t)(dev)) & MENIOS_DEV_MINOR_MASK)) + #ifdef __cplusplus } #endif diff --git a/src/kernel/acpi/acpi.c b/src/kernel/acpi/acpi.c index 013a03e2..4dfbaf93 100644 --- a/src/kernel/acpi/acpi.c +++ b/src/kernel/acpi/acpi.c @@ -35,7 +35,7 @@ static uacpi_interrupt_ret handle_power_button(uacpi_handle ctx) { return UACPI_INTERRUPT_HANDLED; } -int power_button_init(void) { +int power_button_initialize(void) { serial_printf("power_button_init: Initializing power button.\n"); uacpi_status ret = uacpi_install_fixed_event_handler( @@ -51,9 +51,9 @@ int power_button_init(void) { return 0; } -int acpi_init() { +int acpi_initialize(void) { logk("Initializing ACPI."); - serial_printf("acpi_init: Initializing ACPI.\n"); + serial_printf("acpi_initialize: Initializing ACPI.\n"); uacpi_setup_early_table_access((void*)uacpi_arena, UACPI_ARENA_SIZE); printf("."); @@ -85,14 +85,14 @@ int acpi_init() { return -ENODEV; } - ret = power_button_init(); + ret = power_button_initialize(); if(uacpi_unlikely_error(ret)) { serial_printf("power_button_init error: %s", uacpi_status_to_string(ret)); return -ENODEV; } printf(".OK\n"); - serial_printf("acpi_init: uacpi initialized.\n"); + serial_printf("acpi_initialize: uacpi initialized.\n"); return 0; } @@ -119,4 +119,4 @@ int acpi_shutdown() { return 0; -} \ No newline at end of file +} diff --git a/src/kernel/acpi/uacpi_menios.c b/src/kernel/acpi/uacpi_menios.c index 5a96aad5..8f2d7606 100644 --- a/src/kernel/acpi/uacpi_menios.c +++ b/src/kernel/acpi/uacpi_menios.c @@ -72,13 +72,13 @@ void uacpi_kernel_stall(uacpi_u8 usec) { uacpi_status uacpi_kernel_get_rsdp(uacpi_phys_addr *out_rdsp_address) { if(rsdp_request.response == NULL) { printf(">>> Error loading device table.\n"); - serial_printf("acpi_init: Error loading device table.\n"); + serial_printf("acpi_initialize: Error loading device table.\n"); halt(); } uintptr_t addr = virtual_to_physical((uintptr_t)rsdp_request.response->address); - serial_printf("acpi_init: RSDP address: %p\n", addr); + serial_printf("acpi_initialize: RSDP address: %p\n", addr); *out_rdsp_address = addr; @@ -226,7 +226,7 @@ uacpi_handle uacpi_kernel_create_event(void) { return NULL; } - ksem_init(&event->sem, 0); + ksem_initialize(&event->sem, 0); return event; } diff --git a/src/kernel/apic.c b/src/kernel/arch/x86_64/apic.c similarity index 95% rename from src/kernel/apic.c rename to src/kernel/arch/x86_64/apic.c index a7529d00..0decd809 100644 --- a/src/kernel/apic.c +++ b/src/kernel/arch/x86_64/apic.c @@ -1,7 +1,7 @@ -#include +#include #include #include -#include +#include #include #include #include @@ -110,7 +110,7 @@ static inline void *getentry(int type, int n) { return NULL; } -void apic_init() { +void apic_initialize(void) { logk("Enabling APIC"); uacpi_table tbl; @@ -130,7 +130,7 @@ void apic_init() { lapiccount = getcount(ACPI_MADT_ENTRY_TYPE_LAPIC); lapicnmicount = getcount(ACPI_MADT_ENTRY_TYPE_LAPIC_NMI); - serial_printf("acpi_init: table @ %p\n", tbl.ptr); + serial_printf("acpi_initialize: table @ %p\n", tbl.ptr); struct acpi_madt_lapic_address_override *lapic64 = getentry(ACPI_MADT_ENTRY_TYPE_LAPIC_ADDRESS_OVERRIDE, 0); @@ -140,13 +140,13 @@ void apic_init() { serial_printf("\e[94mUsing 64 bit override for the local APIC address\n\e[0m"); } - serial_printf("acpi_init: local APIC address: %p\n", paddr); + serial_printf("acpi_initialize: local APIC address: %p\n", paddr); lapicaddr = (void*)physical_to_virtual((uintptr_t)paddr); ioapics = (ioapicdesc_t*)kmalloc(sizeof(ioapicdesc_t) * iocount); - serial_printf("acpi_init: iocount: %d\n", iocount); + serial_printf("acpi_initialize: iocount: %d\n", iocount); for(size_t i = 0; i < iocount; ++i) { struct acpi_madt_ioapic *entry = getentry(ACPI_MADT_ENTRY_TYPE_IOAPIC, i); diff --git a/src/kernel/cpu.c b/src/kernel/arch/x86_64/cpu.c similarity index 96% rename from src/kernel/cpu.c rename to src/kernel/arch/x86_64/cpu.c index 19f41cdf..affd4dc9 100644 --- a/src/kernel/cpu.c +++ b/src/kernel/arch/x86_64/cpu.c @@ -1,4 +1,4 @@ -#include +#include static inline uint64_t read_cr0(void) { uint64_t value; diff --git a/src/kernel/gdt.c b/src/kernel/arch/x86_64/gdt.c similarity index 95% rename from src/kernel/gdt.c rename to src/kernel/arch/x86_64/gdt.c index 5cf7666c..5cb0486b 100644 --- a/src/kernel/gdt.c +++ b/src/kernel/arch/x86_64/gdt.c @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -76,8 +76,8 @@ static void gdt_initialize_tss(void) { gdt_set_tss(GDT_ENTRY_TSS_LOW, (uint64_t)&tss, sizeof(tss) - 1); } -void gdt_init() { - serial_log("Entering gdt_init"); +void gdt_initialize(void) { + serial_log("Entering gdt_initialize"); logk("Setting GDT"); gdt_set_entry(GDT_ENTRY_NULL, 0, 0, 0, 0); @@ -103,7 +103,7 @@ void gdt_init() { asm volatile("ltr %w0" : : "r" (tss_selector) : "memory"); puts("OK\n"); - serial_log("Leaving gdt_init"); + serial_log("Leaving gdt_initialize"); } void tss_update_kernel_stack(uint64_t stack_top) { diff --git a/src/kernel/idt.c b/src/kernel/arch/x86_64/idt.c similarity index 97% rename from src/kernel/idt.c rename to src/kernel/arch/x86_64/idt.c index 841b21e4..b42fd905 100644 --- a/src/kernel/idt.c +++ b/src/kernel/arch/x86_64/idt.c @@ -1,6 +1,6 @@ #include -#include -#include +#include +#include #include #include #include @@ -57,11 +57,11 @@ void idt_add_user_isr(int interruption, void* handler) { idt_set_entry(interruption, handler, 0xee); } -void idt_init() { +void idt_initialize(void) { logk("Setting IDT"); idt_p.size = sizeof(idt) - 1; idt_p.offset = (uintptr_t)&idt; - serial_printf("idt_init: idt_p @ %p - offset: %lx\n", idt_p, idt_p.offset); + serial_printf("idt_initialize: idt_p @ %p - offset: %lx\n", idt_p, idt_p.offset); idt_add_isr(ISR_DIVISION_BY_ZERO, &idt_generic_isr_asm_handler); idt_add_isr(ISR_DEBUG, &idt_generic_isr_asm_handler); idt_add_isr(ISR_BREAKPOINT, &idt_generic_isr_asm_handler); diff --git a/src/kernel/block/block_cache.c b/src/kernel/block/block_cache.c index 79f0f95b..9b58d8ae 100644 --- a/src/kernel/block/block_cache.c +++ b/src/kernel/block/block_cache.c @@ -211,7 +211,7 @@ static void bcache_release_locked(buffer_head_t* bh) { kcondvar_broadcast(&bcache_cv); } -void block_cache_init(void) { +void block_cache_initialize(void) { if(bcache_initialized) { return; } diff --git a/src/kernel/block/block_device.c b/src/kernel/block/block_device.c index 17cfddaf..36844177 100644 --- a/src/kernel/block/block_device.c +++ b/src/kernel/block/block_device.c @@ -34,9 +34,9 @@ static bool block_device_submit(block_device_t* device, bool write); #endif -void block_device_system_init(void) { +void block_device_system_initialize(void) { kmutex_init(&block_device_lock); - block_cache_init(); + block_cache_initialize(); block_device_head = NULL; block_device_initialized = true; } diff --git a/src/kernel/serial.c b/src/kernel/console/serial.c similarity index 98% rename from src/kernel/serial.c rename to src/kernel/console/serial.c index 0e4de632..87bc84b9 100644 --- a/src/kernel/serial.c +++ b/src/kernel/console/serial.c @@ -9,7 +9,7 @@ bool serial_debug = false; static spinlock_t serial_printf_lock; -void serial_init() { +void serial_initialize(void) { // Disable interrupts outb(0x3f8 + 1, 0x00); diff --git a/src/kernel/halt.c b/src/kernel/core/halt.c similarity index 100% rename from src/kernel/halt.c rename to src/kernel/core/halt.c diff --git a/src/kernel/logo.c b/src/kernel/core/logo.c similarity index 99% rename from src/kernel/logo.c rename to src/kernel/core/logo.c index cdf282bd..8300a9c5 100644 --- a/src/kernel/logo.c +++ b/src/kernel/core/logo.c @@ -1,6 +1,7 @@ -#include +#include #include #include +#include uint8_t logo[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -117,7 +118,7 @@ uint8_t logo[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -void print_logo() { +void print_logo(void) { int line = 10; int column = 10; @@ -138,4 +139,4 @@ void print_logo() { } gotoxy(0, 7); -} \ No newline at end of file +} diff --git a/src/kernel/main.c b/src/kernel/core/main.c similarity index 84% rename from src/kernel/main.c rename to src/kernel/core/main.c index 9a5a8a32..affa51d2 100644 --- a/src/kernel/main.c +++ b/src/kernel/core/main.c @@ -34,33 +34,33 @@ #include #include -#include +#include #include #include #include #include +#include +#include #include -#include -#include +#include +#include #include #include -#include +#include #include #include #include #include #include -#include #include #include #include #include #include #include -#include -#include +#include +#include -void print_logo(); static void heap_demo(void) { heap_stats_t before = heap_get_stats(); @@ -108,9 +108,9 @@ static void heap_demo(void) { (unsigned long)final.free_bytes); } -void boot_graphics_init() { - fb_init(); - font_init(); +static void boot_graphics_initialize(void) { + framebuffer_initialize(); + font_initialize(); print_logo(); @@ -144,44 +144,44 @@ static void kernel_idle_loop(void) { void _start() { serial_debug = true; - tsc_init(); + tsc_initialize(); - serial_init(); + serial_initialize(); - mem_init(); + memory_initialize(); - shm_manager_init(); + shm_manager_initialize(); - file_system_init(); + file_system_initialize(); - block_device_system_init(); + block_device_system_initialize(); - vfs_init(); + vfs_initialize(); - boot_graphics_init(); + boot_graphics_initialize(); - gdt_init(); + gdt_initialize(); - idt_init(); + idt_initialize(); cpu_enable_sse(); - syscall_init(); + syscall_initialize(); printf("Heap demo\n"); logk("Heap demo\n"); heap_demo(); logk("Finished heap demo\n"); - acpi_init(); + acpi_initialize(); - apic_init(); + apic_initialize(); - timer_init(); + timer_initialize(); - scheduler_init(); + scheduler_initialize(); - hardware_init(); + hardware_initialize(); user_init_launch(); diff --git a/src/kernel/panic.c b/src/kernel/core/panic.c similarity index 100% rename from src/kernel/panic.c rename to src/kernel/core/panic.c diff --git a/src/kernel/services.c b/src/kernel/core/services.c similarity index 51% rename from src/kernel/services.c rename to src/kernel/core/services.c index c9eca060..4265e7f0 100644 --- a/src/kernel/services.c +++ b/src/kernel/core/services.c @@ -1,11 +1,11 @@ #include -#include +#include #include -void init_services() { +void services_initialize(void) { printf("- Initing background services."); - // init_memory_compactor(); + // mem_compactor_initialize(); printf("OK\n"); -} \ No newline at end of file +} diff --git a/src/kernel/drivers/README.md b/src/kernel/drivers/README.md new file mode 100644 index 00000000..da6aaf1f --- /dev/null +++ b/src/kernel/drivers/README.md @@ -0,0 +1,27 @@ +# meniOS Kernel Drivers + +This directory groups device drivers by subsystem so it is easier to find and +extend hardware support. Each subfolder maps to the structure outlined in +issue #23 (driver refactor phase): + +- `audio/` – sound and mixer drivers (e.g., Sound Blaster stubs). +- `block/` – block-storage controllers such as AHCI/SATA. +- `bus/` – bus discovery and enumeration logic (PCI, ACPI hooks, …). +- `core/` – driver registry and shared infrastructure helpers. +- `input/` – human-interface devices like PS/2 keyboards or mice. + +When introducing a new driver, place it in the appropriate subsystem directory +and add a matching header under `include/kernel/drivers//`. + +## Character Device Registration Quick Notes + +- Use `char_device_register()` from `include/kernel/fs/devfs/devfs.h` to expose + new `/dev/*` nodes. The helper allocates `dev_t` numbers on demand and wires + the device into `devfs`. +- Dynamic majors now start at **10**; values below that range are reserved for + the built-in pseudo devices (mem, tty, console, etc.). Drivers that relied on + low-numbered majors should request an explicit `dev` if they need a legacy + assignment. +- If a driver needs to hold on to a static major in advance (e.g., multi-device + families), call `char_device_reserve_major()` before registering to keep the + allocator from reusing the slot. diff --git a/src/kernel/driver/sb/sb.c b/src/kernel/drivers/audio/sb/sb.c similarity index 100% rename from src/kernel/driver/sb/sb.c rename to src/kernel/drivers/audio/sb/sb.c diff --git a/src/kernel/driver/ahci/ahci.c b/src/kernel/drivers/block/ahci/ahci.c similarity index 98% rename from src/kernel/driver/ahci/ahci.c rename to src/kernel/drivers/block/ahci/ahci.c index a7497702..1db1823e 100644 --- a/src/kernel/driver/ahci/ahci.c +++ b/src/kernel/drivers/block/ahci/ahci.c @@ -1,9 +1,9 @@ #include -#include +#include #include #include #include -#include +#include #include #include #include @@ -192,7 +192,7 @@ static void ahci_register_controller(uint16_t segment, static void ahci_controller_configure(ahci_controller_t* controller); static bool ahci_controller_enable_interrupts(ahci_controller_t* controller); static void ahci_controller_discover_ports(ahci_controller_t* controller); -static void ahci_port_init(ahci_port_t* port, ahci_controller_t* controller, uint8_t index); +static void ahci_port_initialize(ahci_port_t* port, ahci_controller_t* controller, uint8_t index); static bool ahci_port_configure_dma(ahci_port_t* port); static void ahci_port_shutdown_dma(ahci_port_t* port); static bool ahci_port_device_present(ahci_port_t* port); @@ -374,7 +374,7 @@ static void ahci_controller_discover_ports(ahci_controller_t* controller) { } ahci_port_t* port = &ports[port_index]; - ahci_port_init(port, controller, (uint8_t)port_index); + ahci_port_initialize(port, controller, (uint8_t)port_index); if(port->state == AHCI_PORT_STATE_ONLINE) { controller->port_count++; @@ -382,7 +382,7 @@ static void ahci_controller_discover_ports(ahci_controller_t* controller) { } } -static void ahci_port_init(ahci_port_t* port, ahci_controller_t* controller, uint8_t index) { +static void ahci_port_initialize(ahci_port_t* port, ahci_controller_t* controller, uint8_t index) { memset(port, 0, sizeof(*port)); port->controller = controller; port->index = index; @@ -1016,7 +1016,7 @@ void ahci_irq_handler(void) { #endif } -void ahci_init(void) { +void ahci_initialize(void) { if(ahci_initialized) { return; } @@ -1042,7 +1042,7 @@ void ahci_pci_probe(const pci_device_location_t* location, } if(!ahci_initialized) { - ahci_init(); + ahci_initialize(); } if(!ahci_is_candidate(class_reg)) { diff --git a/src/kernel/driver/pciroot/pci.c b/src/kernel/drivers/bus/pciroot/pci.c similarity index 100% rename from src/kernel/driver/pciroot/pci.c rename to src/kernel/drivers/bus/pciroot/pci.c diff --git a/src/kernel/driver/pciroot/pciroot.c b/src/kernel/drivers/bus/pciroot/pciroot.c similarity index 99% rename from src/kernel/driver/pciroot/pciroot.c rename to src/kernel/drivers/bus/pciroot/pciroot.c index 8b64ee17..8883cf34 100644 --- a/src/kernel/driver/pciroot/pciroot.c +++ b/src/kernel/drivers/bus/pciroot/pciroot.c @@ -269,7 +269,7 @@ void pciroot_start(void) { } } - ahci_init(); + ahci_initialize(); logk("Enumerating PCI devices:\n"); pci_enumerate_devices(pciroot_visit_device, NULL); } @@ -302,6 +302,6 @@ static struct driver_t pciroot_driver = { .shutdown = &pciroot_shutdown }; -void pciroot_init(void) { +void pciroot_register_driver(void) { driver_register(&pciroot_driver); } diff --git a/src/kernel/driver/driver.c b/src/kernel/drivers/core/driver.c similarity index 86% rename from src/kernel/driver/driver.c rename to src/kernel/drivers/core/driver.c index fc9fa282..fe785161 100644 --- a/src/kernel/driver/driver.c +++ b/src/kernel/drivers/core/driver.c @@ -1,20 +1,20 @@ #include #include -#include -#include +#include +#include #include #include #include static driver_list_p driver_list; -void driver_init() { +void driver_registry_init() { logk("Initing driver lookup table\n"); serial_line("Initing driver lookup table"); driver_list = NULL; - ps2kb_init(); - pciroot_init(); + ps2kb_register_driver(); + pciroot_register_driver(); } void driver_register(driver_p driver) { diff --git a/src/kernel/driver/ps2kb/ps2kb.c b/src/kernel/drivers/input/ps2kb/ps2kb.c similarity index 80% rename from src/kernel/driver/ps2kb/ps2kb.c rename to src/kernel/drivers/input/ps2kb/ps2kb.c index 1e53a7f9..4345ab03 100644 --- a/src/kernel/driver/ps2kb/ps2kb.c +++ b/src/kernel/drivers/input/ps2kb/ps2kb.c @@ -1,14 +1,16 @@ #include #include #include -#include -#include +#include +#include #include +#include #include #include #include -#include +#include #include +#include #include #include @@ -41,6 +43,7 @@ static bool right_alt; static bool caps_lock; static bool extended_code; static bool pic_remapped; +static bool ps2kb_device_registered; static const char scancode_unshift[128] = { 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', /* 9 */ @@ -117,6 +120,8 @@ static uint8_t buffer_pop(void) { return value; } +static uint8_t ps2kb_read(void); + static bool is_alpha(char ch) { return (ch >= 'a' && ch <= 'z'); } @@ -368,6 +373,82 @@ static bool ps2_read_byte(uint8_t *out) { return false; } +static int64_t ps2kb_file_read(file_t* file, void* buffer, size_t length) { + (void)file; + if(buffer == NULL) { + return -EINVAL; + } + if(length == 0) { + return 0; + } + + size_t produced = 0; + uint8_t* out = (uint8_t*)buffer; + while(produced < length) { + uint8_t ch = ps2kb_read(); + if(ch == 0) { + break; + } + out[produced++] = ch; + } + + return (int64_t)produced; +} + +static int ps2kb_file_close(file_t* file) { + (void)file; + return 0; +} + +static int ps2kb_file_stat(file_t* file, struct stat* out_stat) { + if(file == NULL || out_stat == NULL) { + return -EINVAL; + } + + memset(out_stat, 0, sizeof(*out_stat)); + out_stat->st_mode = S_IFCHR | 0444; + out_stat->st_nlink = 1; + out_stat->st_blksize = 4096; + char_device_t* device = (char_device_t*)file->private_data; + out_stat->st_rdev = device ? device->dev : 0; + return 0; +} + +static const file_ops_t ps2kb_file_ops = { + .read = ps2kb_file_read, + .write = NULL, + .close = ps2kb_file_close, + .seek = NULL, + .ioctl = NULL, + .mmap = NULL, + .stat = ps2kb_file_stat, + .chmod = NULL, + .utimens = NULL, +}; + +static int ps2kb_device_open(char_device_t* device, int flags, file_t** out_file) { + (void)flags; + if(out_file == NULL) { + return -EINVAL; + } + file_t* file = file_create(&ps2kb_file_ops, device, FILE_MODE_READ); + if(file == NULL) { + return -ENOMEM; + } + file->private_data = device; + *out_file = file; + return 0; +} + +static char_device_t ps2kb_char_device = { + .name = "kbd0", + .access_mode = FILE_MODE_READ, + .open = ps2kb_device_open, + .driver_data = NULL, + .dev = 0, + .minor_count = 1, +}; + void ps2kb_start(void) { buffer_head = buffer_tail = 0; left_shift = right_shift = left_ctrl = right_ctrl = left_alt = right_alt = caps_lock = false; @@ -426,11 +507,31 @@ void ps2kb_start(void) { outb(PIC1_DATA, mask); serial_printf("ps2kb_start: PS/2 keyboard initialised\n"); + + if(!ps2kb_device_registered) { + int rc = char_device_register(&ps2kb_char_device); + if(rc == 0) { + ps2kb_device_registered = true; + serial_printf("ps2kb_start: registered /dev/%s (major=%u minor=%u)\n", + ps2kb_char_device.name, + MAJOR(ps2kb_char_device.dev), + MINOR(ps2kb_char_device.dev)); + } else { + serial_printf("ps2kb_start: failed to register /dev/%s (%d)\n", + ps2kb_char_device.name, + rc); + } + } } void ps2kb_shutdown(void) { ps2_wait_write(); ps2_write_command(PS2_DISABLE_FIRST_PORT); + if(ps2kb_device_registered) { + char_device_unregister(&ps2kb_char_device); + ps2kb_device_registered = false; + serial_printf("ps2kb_shutdown: unregistered /dev/%s\n", ps2kb_char_device.name); + } } static uint8_t ps2kb_read(void) { @@ -461,8 +562,8 @@ static struct driver_t ps2kb_driver = { .write = ps2kb_write, }; -void ps2kb_init(void) { - serial_printf("ps2kb_init: Registering driver '%s' for HID '%s'\n", ps2kb_driver.name, ps2kb_driver.hid); +void ps2kb_register_driver(void) { + serial_printf("ps2kb_register_driver: registering '%s' for HID '%s'\n", ps2kb_driver.name, ps2kb_driver.hid); driver_register(&ps2kb_driver); } diff --git a/src/kernel/fonts.c b/src/kernel/framebuffer/fonts.c similarity index 99% rename from src/kernel/fonts.c rename to src/kernel/framebuffer/fonts.c index 44d52c80..ffceb5a5 100644 --- a/src/kernel/fonts.c +++ b/src/kernel/framebuffer/fonts.c @@ -488,7 +488,7 @@ const glypht_t GL_TILDE = { extern uint8_t font_terminus[]; -void font_init() { +void font_initialize(void) { for(int c = 0; c < 0x100; c++) { font_list.glyphs[c] = GL_NULL; } diff --git a/src/kernel/framebuffer.c b/src/kernel/framebuffer/framebuffer.c similarity index 99% rename from src/kernel/framebuffer.c rename to src/kernel/framebuffer/framebuffer.c index 441e985d..00b95468 100644 --- a/src/kernel/framebuffer.c +++ b/src/kernel/framebuffer/framebuffer.c @@ -353,9 +353,9 @@ static void render_viewport(void) { } } -void fb_init() { +void framebuffer_initialize(void) { if(framebuffer_request.response == NULL || framebuffer_request.response->framebuffer_count < 1) { - serial_error("Panic in framebuffer.c:fb_init"); + serial_error("Panic in framebuffer.c:framebuffer_initialize"); halt(); } diff --git a/src/kernel/fs/README.md b/src/kernel/fs/README.md new file mode 100644 index 00000000..a8f09a9c --- /dev/null +++ b/src/kernel/fs/README.md @@ -0,0 +1,13 @@ +# meniOS Kernel Filesystems + +The filesystem stack is split by responsibility: + +- `core/` – shared helpers (e.g., pipes) and VFS wiring. +- `vfs/` – the virtual filesystem layer and adapters into mounted filesystems. +- `devfs/` – device filesystem nodes (`/dev/*`). +- `procfs/` – process information under `/proc`. +- `tmpfs/` – in-memory temporary filesystem implementation. +- `fat32/` – FAT32 block filesystem support and user adapters. + +Headers follow the same layout under `include/kernel/fs/`, so new code should +add interfaces alongside the implementation directory. diff --git a/src/kernel/file.c b/src/kernel/fs/core/file.c similarity index 96% rename from src/kernel/file.c rename to src/kernel/fs/core/file.c index df853175..5aa93b73 100644 --- a/src/kernel/file.c +++ b/src/kernel/fs/core/file.c @@ -12,10 +12,10 @@ #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #ifdef MENIOS_KERNEL #include @@ -220,7 +220,7 @@ static inline void set_errno(int err) { } } -static inline void stdin_buffer_init(void) { +static inline void stdin_buffer_initialize(void) { if(stdin_initialized) { return; } @@ -307,7 +307,7 @@ static int64_t stdin_read_impl(file_t* file, void* buffer, size_t length) { void stdin_enqueue_char(uint8_t ch) { if(!stdin_initialized) { - stdin_buffer_init(); + stdin_buffer_initialize(); } (void)stdin_buffer_push(ch); kcondvar_signal(&stdin_buffer.waiters); @@ -498,7 +498,7 @@ int file_utimens(file_t* file, const struct timespec times[2]) { return rc; } -void proc_file_table_init(struct proc_info_t* proc) { +void proc_file_table_initialize(struct proc_info_t* proc) { if(proc == NULL) { return; } @@ -763,7 +763,16 @@ static int generic_char_stat(file_t* file, struct stat* out_stat) { info.is_read_only = (file != NULL && (file->mode & FILE_MODE_WRITE) == 0); fs_path_info_to_stat(&info, out_stat); - out_stat->st_rdev = 0; + if(file != NULL) { + char_device_t* device = (char_device_t*)file->private_data; + if(device != NULL) { + out_stat->st_rdev = device->dev; + } else { + out_stat->st_rdev = 0; + } + } else { + out_stat->st_rdev = 0; + } return 0; } @@ -1056,9 +1065,9 @@ static const file_ops_t tty_console_file_ops = { #ifdef MENIOS_KERNEL static void install_standard_streams(void) { - proc_file_table_init(&kernel_process_info); + proc_file_table_initialize(&kernel_process_info); - stdin_buffer_init(); + stdin_buffer_initialize(); stdin_stream_file = file_create(&stdin_file_ops, NULL, FILE_MODE_READ); if(stdin_stream_file != NULL) { @@ -1080,17 +1089,18 @@ static void install_standard_streams(void) { } #endif -void file_system_init(void) { +void file_system_initialize(void) { #ifdef MENIOS_KERNEL install_standard_streams(); + char_device_system_initialize(); if(!devfs_mount()) { - serial_printf("file_system_init: failed to mount devfs\n"); + serial_printf("file_system_initialize: failed to mount devfs\n"); } if(!tmpfs_mount()) { - serial_printf("file_system_init: failed to mount tmpfs\n"); + serial_printf("file_system_initialize: failed to mount tmpfs\n"); } if(!procfs_mount()) { - serial_printf("file_system_init: failed to mount procfs\n"); + serial_printf("file_system_initialize: failed to mount procfs\n"); } #endif } @@ -1228,6 +1238,6 @@ file_t* file_create_framebuffer_device_file(void) { } file_t* file_create_tty_console_file(void) { - stdin_buffer_init(); + stdin_buffer_initialize(); return file_create(&tty_console_file_ops, NULL, FILE_MODE_READ | FILE_MODE_WRITE); } diff --git a/src/kernel/fs/pipe.c b/src/kernel/fs/core/pipe.c similarity index 99% rename from src/kernel/fs/pipe.c rename to src/kernel/fs/core/pipe.c index 3ea03587..d9cae6b4 100644 --- a/src/kernel/fs/pipe.c +++ b/src/kernel/fs/core/pipe.c @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/kernel/fs/devfs.c b/src/kernel/fs/devfs.c deleted file mode 100644 index cea12965..00000000 --- a/src/kernel/fs/devfs.c +++ /dev/null @@ -1,328 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -typedef struct devfs_node_t { - const char* name; - uint32_t access_mode; - file_t* (*factory)(const struct devfs_node_t* node); -} devfs_node_t; - -static void devfs_fill_info(const devfs_node_t* node, fs_path_info_t* out_info); -static int devfs_file_stat(file_t* file, struct stat* out_stat); - -static int64_t devfs_null_read(file_t* file, void* buffer, size_t length) { - (void)file; - (void)buffer; - (void)length; - return 0; -} - -static int64_t devfs_null_write(file_t* file, const void* buffer, size_t length) { - (void)file; - (void)buffer; - return (int64_t)length; -} - -static int64_t devfs_zero_read(file_t* file, void* buffer, size_t length) { - if(buffer == NULL) { - return -EINVAL; - } - memset(buffer, 0, length); - return (int64_t)length; -} - -static const file_ops_t devfs_null_ops = { - .read = devfs_null_read, - .write = devfs_null_write, - .close = NULL, - .seek = NULL, - .ioctl = NULL, - .mmap = NULL, - .stat = devfs_file_stat, - .chmod = NULL, - .utimens = NULL, -}; - -static const file_ops_t devfs_zero_ops = { - .read = devfs_zero_read, - .write = devfs_null_write, - .close = NULL, - .seek = NULL, - .ioctl = NULL, - .mmap = NULL, - .stat = devfs_file_stat, - .chmod = NULL, - .utimens = NULL, -}; - -static file_t* devfs_create_null(const devfs_node_t* node) { - return file_create(&devfs_null_ops, (void*)node, node->access_mode); -} - -static file_t* devfs_create_zero(const devfs_node_t* node) { - return file_create(&devfs_zero_ops, (void*)node, node->access_mode); -} - -static file_t* devfs_create_tty0(const devfs_node_t* node) { - file_t* file = file_create_tty_console_file(); - if(file != NULL) { - file->private_data = (void*)node; - } - return file; -} - -static file_t* devfs_create_console(const devfs_node_t* node) { - file_t* file = file_create_framebuffer_console_file(); - if(file != NULL) { - file->private_data = (void*)node; - } - return file; -} - -static file_t* devfs_create_fb0(const devfs_node_t* node) { - file_t* file = file_create_framebuffer_device_file(); - if(file != NULL) { - file->private_data = (void*)node; - } - return file; -} - -static file_t* devfs_create_ttys0(const devfs_node_t* node) { - file_t* file = file_create_serial_console_file(); - if(file != NULL) { - file->private_data = (void*)node; - } - return file; -} - -static const devfs_node_t devfs_nodes[] = { - { "null", FILE_MODE_WRITE, devfs_create_null }, - { "zero", FILE_MODE_READ, devfs_create_zero }, - { "tty0", FILE_MODE_READ | FILE_MODE_WRITE, devfs_create_tty0 }, - { "fb0", FILE_MODE_READ | FILE_MODE_WRITE, devfs_create_fb0 }, - { "console", FILE_MODE_WRITE, devfs_create_console }, - { "ttyS0", FILE_MODE_WRITE, devfs_create_ttys0 }, -}; - -static size_t devfs_node_count(void) { - return sizeof(devfs_nodes) / sizeof(devfs_nodes[0]); -} - -static bool devfs_list(void* fs_ctx, const char* path, vfs_dir_iter_t iter, void* context) { - (void)fs_ctx; - if(path != NULL && path[0] != '\0' && strcmp(path, "/") != 0) { - return false; - } - - for(size_t i = 0; i < devfs_node_count(); i++) { - vfs_dir_entry_t entry; - memset(&entry, 0, sizeof(entry)); - strncpy(entry.name, devfs_nodes[i].name, sizeof(entry.name) - 1); - entry.name[sizeof(entry.name) - 1] = '\0'; - entry.size = 0; - entry.is_directory = false; - if(!iter(&entry, context)) { - break; - } - } - return true; -} - -static bool devfs_read(void* fs_ctx, - const char* path, - size_t offset, - void* buffer, - size_t length, - size_t* bytes_read) { - (void)fs_ctx; - (void)path; - (void)offset; - (void)buffer; - (void)length; - (void)bytes_read; - return false; -} - -static bool devfs_read_all(void* fs_ctx, const char* path, void** out_buffer, size_t* out_size) { - (void)fs_ctx; - (void)path; - (void)out_buffer; - (void)out_size; - return false; -} - -static int devfs_open(void* fs_ctx, const char* path, int flags, file_t** out_file) { - (void)fs_ctx; - if(path == NULL || out_file == NULL) { - return -EINVAL; - } - - while(*path == '/') { - path++; - } - - uint32_t requested = 0; - switch(flags & 0x3) { - case O_RDONLY: - requested = FILE_MODE_READ; - break; - case O_WRONLY: - requested = FILE_MODE_WRITE; - break; - case O_RDWR: - requested = FILE_MODE_READ | FILE_MODE_WRITE; - break; - default: - requested = FILE_MODE_READ; - break; - } - - for(size_t i = 0; i < devfs_node_count(); i++) { - const devfs_node_t* node = &devfs_nodes[i]; - if(strcmp(path, node->name) != 0) { - continue; - } - if((node->access_mode & requested) != requested) { - return -EACCES; - } - file_t* file = node->factory(node); - if(file == NULL) { - return -ENOMEM; - } - file->mode = node->access_mode; - *out_file = file; - return 0; - } - - return -ENOENT; -} - -static int devfs_unlink(void* fs_ctx, const char* path) { - (void)fs_ctx; - (void)path; - return -ENOSYS; -} - -static void devfs_destroy(void* fs_ctx) { - (void)fs_ctx; -} - -static mode_t devfs_mode_for_node(const devfs_node_t* node) { - mode_t perms = 0; - if(node->access_mode & FILE_MODE_READ) { - perms |= 0444; - } - if(node->access_mode & FILE_MODE_WRITE) { - perms |= 0222; - } - return S_IFCHR | perms; -} - -static void devfs_fill_info(const devfs_node_t* node, fs_path_info_t* out_info) { - memset(out_info, 0, sizeof(*out_info)); - out_info->block_size = 4096; - out_info->inode = node ? (uint64_t)(node - devfs_nodes + 1) : 0; - out_info->is_directory = false; - out_info->size = 0; - out_info->has_mode = true; - out_info->mode = devfs_mode_for_node(node); - out_info->is_read_only = (node->access_mode & FILE_MODE_WRITE) == 0; - out_info->has_times = true; - uint64_t usec = unix_time_us(); - struct timespec now = { - .tv_sec = (time_t)(usec / 1000000ull), - .tv_nsec = (long)((usec % 1000000ull) * 1000ull), - }; - out_info->atime = now; - out_info->mtime = now; - out_info->ctime = now; -} - -static bool devfs_stat(void* fs_ctx, const char* path, fs_path_info_t* out_info) { - (void)fs_ctx; - if(path == NULL || out_info == NULL) { - return false; - } - - if(path[0] == '\0' || strcmp(path, "/") == 0) { - memset(out_info, 0, sizeof(*out_info)); - out_info->is_directory = true; - out_info->has_mode = true; - out_info->mode = S_IFDIR | 0555; - out_info->is_read_only = true; - out_info->block_size = 4096; - out_info->has_times = true; - uint64_t usec = unix_time_us(); - struct timespec now = { - .tv_sec = (time_t)(usec / 1000000ull), - .tv_nsec = (long)((usec % 1000000ull) * 1000ull), - }; - out_info->atime = now; - out_info->mtime = now; - out_info->ctime = now; - return true; - } - - while(*path == '/') { - path++; - } - - for(size_t i = 0; i < devfs_node_count(); i++) { - const devfs_node_t* node = &devfs_nodes[i]; - if(strcmp(path, node->name) == 0) { - devfs_fill_info(node, out_info); - return true; - } - } - return false; -} - -static int devfs_file_stat(file_t* file, struct stat* out_stat) { - if(file == NULL || out_stat == NULL) { - return -EINVAL; - } - const devfs_node_t* node = (const devfs_node_t*)file->private_data; - if(node == NULL) { - return -EINVAL; - } - fs_path_info_t info; - devfs_fill_info(node, &info); - fs_path_info_to_stat(&info, out_stat); - out_stat->st_rdev = (dev_t)(node - devfs_nodes + 1); - return 0; -} - -static const vfs_fs_driver_t devfs_driver = { - .list = devfs_list, - .read = devfs_read, - .read_all = devfs_read_all, - .write = NULL, - .write_all = NULL, - .create_file = NULL, - .truncate_file = NULL, - .stat = devfs_stat, - .open = devfs_open, - .unlink = devfs_unlink, - .mkdir = NULL, - .rmdir = NULL, - .rename = NULL, - .chmod = NULL, - .utimens = NULL, - .destroy = devfs_destroy, -}; - -bool devfs_mount(void) { - return vfs_mount("/dev", &devfs_driver, NULL, true); -} diff --git a/src/kernel/fs/devfs/devfs.c b/src/kernel/fs/devfs/devfs.c new file mode 100644 index 00000000..4de8bcce --- /dev/null +++ b/src/kernel/fs/devfs/devfs.c @@ -0,0 +1,751 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define CHAR_DEVICE_DYNAMIC_MAJOR_MIN 10u +#define CHAR_DEVICE_MAJOR_COUNT (MENIOS_DEV_MAJOR_MASK + 1u) + +typedef struct devfs_entry_t { + char_device_t* device; + struct devfs_entry_t* next; +} devfs_entry_t; + +typedef struct char_minor_allocation_t { + unsigned int start; + unsigned int count; + char_device_t* device; + struct char_minor_allocation_t* next; +} char_minor_allocation_t; + +typedef struct { + bool reserved; + char_minor_allocation_t* allocations; +} char_major_state_t; + +static kmutex_t devfs_lock; +static devfs_entry_t* devfs_devices; +static bool devfs_ready; +static unsigned int next_dynamic_major = CHAR_DEVICE_DYNAMIC_MAJOR_MIN; +static char_major_state_t char_major_table[CHAR_DEVICE_MAJOR_COUNT]; + +static void devfs_init_once(void) { + if(devfs_ready) { + return; + } + kmutex_init(&devfs_lock); + devfs_devices = NULL; + next_dynamic_major = CHAR_DEVICE_DYNAMIC_MAJOR_MIN; + for(unsigned int major = 0; major < CHAR_DEVICE_DYNAMIC_MAJOR_MIN && major < CHAR_DEVICE_MAJOR_COUNT; ++major) { + char_major_table[major].reserved = true; + } + devfs_ready = true; +} + +static devfs_entry_t* devfs_find_by_name(const char* name) { + for(devfs_entry_t* entry = devfs_devices; entry != NULL; entry = entry->next) { + if(strcmp(entry->device->name, name) == 0) { + return entry; + } + } + return NULL; +} + +static devfs_entry_t* devfs_find_by_dev(dev_t dev) { + unsigned int target_major = MAJOR(dev); + unsigned int target_minor = MINOR(dev); + for(devfs_entry_t* entry = devfs_devices; entry != NULL; entry = entry->next) { + const char_device_t* device = entry->device; + unsigned int device_major = MAJOR(device->dev); + if(device_major != target_major) { + continue; + } + unsigned int device_minor = MINOR(device->dev); + unsigned int range = device->minor_count == 0 ? 1u : device->minor_count; + if(target_minor >= device_minor && target_minor < device_minor + range) { + return entry; + } + } + return NULL; +} + +static bool char_major_has_overlap(unsigned int major, unsigned int start_minor, unsigned int count) { + if(major >= CHAR_DEVICE_MAJOR_COUNT) { + return true; + } + char_minor_allocation_t* allocation = char_major_table[major].allocations; + unsigned int end = start_minor + count - 1u; + while(allocation != NULL) { + unsigned int allocation_start = allocation->start; + unsigned int allocation_end = allocation_start + allocation->count - 1u; + if(!(end < allocation_start || start_minor > allocation_end)) { + return true; + } + allocation = allocation->next; + } + return false; +} + +static int char_major_track_range(char_device_t* device, + unsigned int major, + unsigned int start_minor, + unsigned int count) { + if(major >= CHAR_DEVICE_MAJOR_COUNT) { + return -ERANGE; + } + char_major_state_t* state = &char_major_table[major]; + if(char_major_has_overlap(major, start_minor, count)) { + return -EEXIST; + } + char_minor_allocation_t* allocation = kmalloc(sizeof(char_minor_allocation_t)); + if(allocation == NULL) { + return -ENOMEM; + } + allocation->start = start_minor; + allocation->count = count; + allocation->device = device; + allocation->next = state->allocations; + state->allocations = allocation; + return 0; +} + +static void char_major_untrack_range(char_device_t* device) { + if(device == NULL) { + return; + } + unsigned int major = MAJOR(device->dev); + if(major >= CHAR_DEVICE_MAJOR_COUNT) { + return; + } + char_major_state_t* state = &char_major_table[major]; + char_minor_allocation_t** cursor = &state->allocations; + while(*cursor != NULL) { + if((*cursor)->device == device) { + char_minor_allocation_t* victim = *cursor; + *cursor = victim->next; + kfree(victim); + return; + } + cursor = &(*cursor)->next; + } +} + +static bool char_major_is_reserved(unsigned int major) { + if(major >= CHAR_DEVICE_MAJOR_COUNT) { + return true; + } + return char_major_table[major].reserved; +} + +static bool char_major_is_unused(unsigned int major) { + if(major >= CHAR_DEVICE_MAJOR_COUNT) { + return false; + } + if(char_major_table[major].reserved) { + return false; + } + return char_major_table[major].allocations == NULL; +} + +static dev_t allocate_dynamic_dev(unsigned int minor_count) { + unsigned int count = minor_count == 0 ? 1u : minor_count; + if(count == 0 || count > (MENIOS_DEV_MINOR_MASK + 1u)) { + return 0; + } + unsigned int start_major = next_dynamic_major; + if(start_major < CHAR_DEVICE_DYNAMIC_MAJOR_MIN) { + start_major = CHAR_DEVICE_DYNAMIC_MAJOR_MIN; + } + + unsigned int major = start_major; + for(int pass = 0; pass < 2; ++pass) { + while(major < CHAR_DEVICE_MAJOR_COUNT) { + if(char_major_is_unused(major)) { + next_dynamic_major = major + 1; + return MKDEV(major, 0); + } + major++; + } + major = CHAR_DEVICE_DYNAMIC_MAJOR_MIN; + } + return 0; +} + +static mode_t devfs_mode_for_device(const char_device_t* device) { + mode_t perms = 0; + if(device->access_mode & FILE_MODE_READ) { + perms |= 0444; + } + if(device->access_mode & FILE_MODE_WRITE) { + perms |= 0222; + } + if(perms == 0) { + perms = 0000; + } + return S_IFCHR | perms; +} + +static void devfs_fill_info(const char_device_t* device, fs_path_info_t* out_info) { + memset(out_info, 0, sizeof(*out_info)); + out_info->block_size = 4096; + out_info->inode = (uint64_t)device->dev; + out_info->is_directory = false; + out_info->size = 0; + out_info->has_mode = true; + out_info->mode = devfs_mode_for_device(device); + out_info->is_read_only = (device->access_mode & FILE_MODE_WRITE) == 0; + out_info->has_rdev = true; + out_info->rdev = device->dev; + out_info->has_times = true; + uint64_t usec = unix_time_us(); + struct timespec now = { + .tv_sec = (time_t)(usec / 1000000ull), + .tv_nsec = (long)((usec % 1000000ull) * 1000ull), + }; + out_info->atime = now; + out_info->mtime = now; + out_info->ctime = now; +} + +static bool devfs_list(void* fs_ctx, const char* path, vfs_dir_iter_t iter, void* context) { + (void)fs_ctx; + if(path != NULL && path[0] != '\0' && strcmp(path, "/") != 0) { + return false; + } + + kmutex_lock(&devfs_lock); + for(devfs_entry_t* entry = devfs_devices; entry != NULL; entry = entry->next) { + vfs_dir_entry_t dir_entry; + memset(&dir_entry, 0, sizeof(dir_entry)); + strncpy(dir_entry.name, entry->device->name, sizeof(dir_entry.name) - 1); + dir_entry.name[sizeof(dir_entry.name) - 1] = '\0'; + dir_entry.size = 0; + dir_entry.is_directory = false; + if(!iter(&dir_entry, context)) { + break; + } + } + kmutex_unlock(&devfs_lock); + return true; +} + +static bool devfs_read(void* fs_ctx, + const char* path, + size_t offset, + void* buffer, + size_t length, + size_t* bytes_read) { + (void)fs_ctx; + (void)path; + (void)offset; + (void)buffer; + (void)length; + (void)bytes_read; + return false; +} + +static bool devfs_read_all(void* fs_ctx, const char* path, void** out_buffer, size_t* out_size) { + (void)fs_ctx; + (void)path; + (void)out_buffer; + (void)out_size; + return false; +} + +static bool devfs_lookup_device(const char* path, char_device_t** out_device) { + while(*path == '/') { + path++; + } + kmutex_lock(&devfs_lock); + devfs_entry_t* entry = devfs_find_by_name(path); + if(entry == NULL) { + kmutex_unlock(&devfs_lock); + return false; + } + *out_device = entry->device; + kmutex_unlock(&devfs_lock); + return true; +} + +static int devfs_open(void* fs_ctx, const char* path, int flags, file_t** out_file) { + (void)fs_ctx; + if(path == NULL || out_file == NULL) { + return -EINVAL; + } + + uint32_t requested = 0; + switch(flags & 0x3) { + case O_RDONLY: + requested = FILE_MODE_READ; + break; + case O_WRONLY: + requested = FILE_MODE_WRITE; + break; + case O_RDWR: + requested = FILE_MODE_READ | FILE_MODE_WRITE; + break; + default: + requested = FILE_MODE_READ; + break; + } + + char_device_t* device = NULL; + if(!devfs_lookup_device(path, &device)) { + return -ENOENT; + } + + if((device->access_mode & requested) != requested) { + return -EACCES; + } + + file_t* file = NULL; + int rc = device->open(device, flags, &file); + if(rc != 0) { + return rc; + } + if(file == NULL) { + return -ENOMEM; + } + + file->mode = device->access_mode; + file->private_data = device; + *out_file = file; + return 0; +} + +static int devfs_unlink(void* fs_ctx, const char* path) { + (void)fs_ctx; + (void)path; + return -ENOSYS; +} + +static void devfs_destroy(void* fs_ctx) { + (void)fs_ctx; +} + +static bool devfs_stat(void* fs_ctx, const char* path, fs_path_info_t* out_info) { + (void)fs_ctx; + if(path == NULL || out_info == NULL) { + return false; + } + + if(path[0] == '\0' || strcmp(path, "/") == 0) { + memset(out_info, 0, sizeof(*out_info)); + out_info->is_directory = true; + out_info->has_mode = true; + out_info->mode = S_IFDIR | 0555; + out_info->is_read_only = true; + out_info->block_size = 4096; + out_info->has_times = true; + uint64_t usec = unix_time_us(); + struct timespec now = { + .tv_sec = (time_t)(usec / 1000000ull), + .tv_nsec = (long)((usec % 1000000ull) * 1000ull), + }; + out_info->atime = now; + out_info->mtime = now; + out_info->ctime = now; + return true; + } + + char_device_t* device = NULL; + if(!devfs_lookup_device(path, &device)) { + return false; + } + + devfs_fill_info(device, out_info); + return true; +} + +static int devfs_file_stat(file_t* file, struct stat* out_stat) { + if(file == NULL || out_stat == NULL) { + return -EINVAL; + } + + char_device_t* device = (char_device_t*)file->private_data; + if(device == NULL) { + return -EINVAL; + } + + fs_path_info_t info; + devfs_fill_info(device, &info); + fs_path_info_to_stat(&info, out_stat); + out_stat->st_rdev = device->dev; + return 0; +} + +static const vfs_fs_driver_t devfs_driver = { + .list = devfs_list, + .read = devfs_read, + .read_all = devfs_read_all, + .write = NULL, + .write_all = NULL, + .create_file = NULL, + .truncate_file = NULL, + .stat = devfs_stat, + .open = devfs_open, + .unlink = devfs_unlink, + .mkdir = NULL, + .rmdir = NULL, + .rename = NULL, + .mknod = NULL, + .chmod = NULL, + .utimens = NULL, + .destroy = devfs_destroy, +}; + +static int devfs_register_entry(char_device_t* device) { + devfs_entry_t* entry = kmalloc(sizeof(devfs_entry_t)); + if(entry == NULL) { + return -ENOMEM; + } + entry->device = device; + entry->next = devfs_devices; + devfs_devices = entry; + return 0; +} + +static void devfs_unregister_entry(char_device_t* device) { + devfs_entry_t** cursor = &devfs_devices; + while(*cursor) { + if((*cursor)->device == device) { + devfs_entry_t* victim = *cursor; + *cursor = victim->next; + kfree(victim); + return; + } + cursor = &(*cursor)->next; + } +} + +int char_device_register(char_device_t* device) { + if(device == NULL || device->name == NULL || device->open == NULL) { + return -EINVAL; + } + if(device->access_mode == 0) { + return -EINVAL; + } + + devfs_init_once(); + + kmutex_lock(&devfs_lock); + + if(devfs_find_by_name(device->name) != NULL) { + kmutex_unlock(&devfs_lock); + return -EEXIST; + } + + unsigned int requested_count = device->minor_count == 0 ? 1u : device->minor_count; + if(requested_count > (MENIOS_DEV_MINOR_MASK + 1u)) { + kmutex_unlock(&devfs_lock); + return -ERANGE; + } + + bool allocated_dynamic = false; + dev_t original_dev = device->dev; + + if(device->dev == 0) { + dev_t dev = allocate_dynamic_dev(requested_count); + if(dev == 0) { + kmutex_unlock(&devfs_lock); + return -ENOSPC; + } + device->dev = dev; + allocated_dynamic = true; + } + + unsigned int major = MAJOR(device->dev); + unsigned int base_minor = MINOR(device->dev); + if(base_minor + requested_count - 1u > MENIOS_DEV_MINOR_MASK) { + device->dev = allocated_dynamic ? original_dev : device->dev; + kmutex_unlock(&devfs_lock); + return -ERANGE; + } + + if(char_major_has_overlap(major, base_minor, requested_count)) { + if(allocated_dynamic) { + device->dev = original_dev; + } + kmutex_unlock(&devfs_lock); + return -EEXIST; + } + + int range_rc = char_major_track_range(device, major, base_minor, requested_count); + if(range_rc != 0) { + if(allocated_dynamic) { + device->dev = original_dev; + } + kmutex_unlock(&devfs_lock); + return range_rc; + } + + int rc = devfs_register_entry(device); + if(rc == 0) { + device->minor_count = requested_count; + unsigned int major = MAJOR(device->dev); + if(major >= next_dynamic_major) { + next_dynamic_major = major + 1; + } + } else { + char_major_untrack_range(device); + if(allocated_dynamic) { + device->dev = original_dev; + } + } + kmutex_unlock(&devfs_lock); + return rc; +} + +void char_device_unregister(char_device_t* device) { + if(device == NULL) { + return; + } + + devfs_init_once(); + kmutex_lock(&devfs_lock); + devfs_unregister_entry(device); + char_major_untrack_range(device); + kmutex_unlock(&devfs_lock); +} + +static int64_t devfs_null_read(file_t* file, void* buffer, size_t length) { + (void)file; + (void)buffer; + (void)length; + return 0; +} + +static int64_t devfs_null_write(file_t* file, const void* buffer, size_t length) { + (void)file; + (void)buffer; + return (int64_t)length; +} + +static int64_t devfs_zero_read(file_t* file, void* buffer, size_t length) { + if(buffer == NULL) { + return -EINVAL; + } + memset(buffer, 0, length); + return (int64_t)length; +} + +static const file_ops_t devfs_null_ops = { + .read = devfs_null_read, + .write = devfs_null_write, + .close = NULL, + .seek = NULL, + .ioctl = NULL, + .mmap = NULL, + .stat = devfs_file_stat, + .chmod = NULL, + .utimens = NULL, +}; + +static const file_ops_t devfs_zero_ops = { + .read = devfs_zero_read, + .write = devfs_null_write, + .close = NULL, + .seek = NULL, + .ioctl = NULL, + .mmap = NULL, + .stat = devfs_file_stat, + .chmod = NULL, + .utimens = NULL, +}; + +static int devfs_null_open(char_device_t* device, int flags, file_t** out_file) { + (void)flags; + file_t* file = file_create(&devfs_null_ops, device, device->access_mode); + if(file == NULL) { + return -ENOMEM; + } + file->private_data = device; + *out_file = file; + return 0; +} + +static int devfs_zero_open(char_device_t* device, int flags, file_t** out_file) { + (void)flags; + file_t* file = file_create(&devfs_zero_ops, device, device->access_mode); + if(file == NULL) { + return -ENOMEM; + } + file->private_data = device; + *out_file = file; + return 0; +} + +static int devfs_tty0_open(char_device_t* device, int flags, file_t** out_file) { + (void)flags; + file_t* file = file_create_tty_console_file(); + if(file == NULL) { + return -ENOMEM; + } + file->private_data = device; + *out_file = file; + return 0; +} + +static int devfs_console_open(char_device_t* device, int flags, file_t** out_file) { + (void)flags; + file_t* file = file_create_framebuffer_console_file(); + if(file == NULL) { + return -ENOMEM; + } + file->private_data = device; + *out_file = file; + return 0; +} + +static int devfs_fb0_open(char_device_t* device, int flags, file_t** out_file) { + (void)flags; + file_t* file = file_create_framebuffer_device_file(); + if(file == NULL) { + return -ENOMEM; + } + file->private_data = device; + *out_file = file; + return 0; +} + +static int devfs_serial_open(char_device_t* device, int flags, file_t** out_file) { + (void)flags; + file_t* file = file_create_serial_console_file(); + if(file == NULL) { + return -ENOMEM; + } + file->private_data = device; + *out_file = file; + return 0; +} + +static char_device_t null_device = { + .name = "null", + .access_mode = FILE_MODE_WRITE, + .open = devfs_null_open, + .driver_data = NULL, + .dev = MKDEV(1, 3), + .minor_count = 1, +}; + +static char_device_t zero_device = { + .name = "zero", + .access_mode = FILE_MODE_READ, + .open = devfs_zero_open, + .driver_data = NULL, + .dev = MKDEV(1, 5), + .minor_count = 1, +}; + +static char_device_t tty0_device = { + .name = "tty0", + .access_mode = FILE_MODE_READ | FILE_MODE_WRITE, + .open = devfs_tty0_open, + .driver_data = NULL, + .dev = MKDEV(4, 0), + .minor_count = 1, +}; + +static char_device_t fb0_device = { + .name = "fb0", + .access_mode = FILE_MODE_READ | FILE_MODE_WRITE, + .open = devfs_fb0_open, + .driver_data = NULL, + .dev = MKDEV(29, 0), + .minor_count = 1, +}; + +static char_device_t console_device = { + .name = "console", + .access_mode = FILE_MODE_WRITE, + .open = devfs_console_open, + .driver_data = NULL, + .dev = MKDEV(5, 1), + .minor_count = 1, +}; + +static char_device_t ttyS0_device = { + .name = "ttyS0", + .access_mode = FILE_MODE_WRITE, + .open = devfs_serial_open, + .driver_data = NULL, + .dev = MKDEV(4, 64), + .minor_count = 1, +}; + +void char_device_system_initialize(void) { + devfs_init_once(); + + static bool builtins_registered = false; + if(builtins_registered) { + return; + } + builtins_registered = true; + + if(char_device_register(&null_device) != 0) { + serial_printf("char_device_system_initialize: failed to register /dev/null\n"); + } + if(char_device_register(&zero_device) != 0) { + serial_printf("char_device_system_initialize: failed to register /dev/zero\n"); + } + if(char_device_register(&tty0_device) != 0) { + serial_printf("char_device_system_initialize: failed to register /dev/tty0\n"); + } + if(char_device_register(&fb0_device) != 0) { + serial_printf("char_device_system_initialize: failed to register /dev/fb0\n"); + } + if(char_device_register(&console_device) != 0) { + serial_printf("char_device_system_initialize: failed to register /dev/console\n"); + } + if(char_device_register(&ttyS0_device) != 0) { + serial_printf("char_device_system_initialize: failed to register /dev/ttyS0\n"); + } +} + +void char_device_iterate(char_device_iter_fn fn, void* context) { + if(fn == NULL) { + return; + } + devfs_init_once(); + kmutex_lock(&devfs_lock); + for(devfs_entry_t* entry = devfs_devices; entry != NULL; entry = entry->next) { + fn(entry->device, context); + } + kmutex_unlock(&devfs_lock); +} + +char_device_t* char_device_lookup(dev_t dev) { + devfs_init_once(); + kmutex_lock(&devfs_lock); + devfs_entry_t* entry = devfs_find_by_dev(dev); + char_device_t* device = entry ? entry->device : NULL; + kmutex_unlock(&devfs_lock); + return device; +} + +void char_device_reserve_major(unsigned int major) { + devfs_init_once(); + if(major >= CHAR_DEVICE_MAJOR_COUNT) { + return; + } + kmutex_lock(&devfs_lock); + char_major_table[major].reserved = true; + kmutex_unlock(&devfs_lock); +} + +bool devfs_mount(void) { + char_device_system_initialize(); + return vfs_mount("/dev", &devfs_driver, NULL, true); +} diff --git a/src/kernel/fs/fat32.c b/src/kernel/fs/fat32/fat32.c similarity index 99% rename from src/kernel/fs/fat32.c rename to src/kernel/fs/fat32/fat32.c index 2c79da29..606b7d0a 100644 --- a/src/kernel/fs/fat32.c +++ b/src/kernel/fs/fat32/fat32.c @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/src/kernel/fs/procfs.c b/src/kernel/fs/procfs/procfs.c similarity index 83% rename from src/kernel/fs/procfs.c rename to src/kernel/fs/procfs/procfs.c index b90056aa..a0f412b8 100644 --- a/src/kernel/fs/procfs.c +++ b/src/kernel/fs/procfs/procfs.c @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -8,11 +8,12 @@ #include #include -#include +#include +#include #include #include #include -#include +#include #define PROCFS_MAX_NAME 64 @@ -34,9 +35,11 @@ typedef struct procfs_file_state_t { } procfs_file_state_t; static bool procfs_generate_meminfo(char** out_buffer, size_t* out_size); +static bool procfs_generate_devices(char** out_buffer, size_t* out_size); static const procfs_entry_t procfs_entries[] = { { "meminfo", procfs_generate_meminfo }, + { "devices", procfs_generate_devices }, }; static size_t procfs_entry_count(void) { @@ -329,6 +332,80 @@ static void procfs_destroy(void* fs_ctx) { (void)fs_ctx; } +typedef struct { + char* buffer; + size_t length; + size_t capacity; + bool truncated; +} procfs_device_list_ctx_t; + +static void procfs_devices_iterate_cb(const char_device_t* device, void* data) { + procfs_device_list_ctx_t* ctx = (procfs_device_list_ctx_t*)data; + if(device == NULL || ctx == NULL || ctx->truncated) { + return; + } + + char line[128]; + int line_len = vprintk(line, " %u %s\n", MAJOR(device->dev), device->name); + if(line_len <= 0) { + return; + } + + size_t needed = ctx->length + (size_t)line_len; + if(needed >= ctx->capacity) { + size_t new_capacity = ctx->capacity * 2; + while(new_capacity <= needed) { + new_capacity *= 2; + } + char* new_buffer = krealloc(ctx->buffer, new_capacity); + if(new_buffer == NULL) { + ctx->truncated = true; + return; + } + ctx->buffer = new_buffer; + ctx->capacity = new_capacity; + } + + memcpy(ctx->buffer + ctx->length, line, (size_t)line_len); + ctx->length += (size_t)line_len; +} + +static bool procfs_generate_devices(char** out_buffer, size_t* out_size) { + if(out_buffer == NULL || out_size == NULL) { + return false; + } + + size_t capacity = 256; + char* buffer = kmalloc(capacity); + if(buffer == NULL) { + return false; + } + + int header_len = vprintk(buffer, "Character devices:\n"); + if(header_len < 0) { + kfree(buffer); + return false; + } + + procfs_device_list_ctx_t ctx = { + .buffer = buffer, + .length = (size_t)header_len, + .capacity = capacity, + .truncated = false, + }; + + char_device_iterate(procfs_devices_iterate_cb, &ctx); + + if(ctx.truncated) { + kfree(ctx.buffer); + return false; + } + + *out_buffer = ctx.buffer; + *out_size = ctx.length; + return true; +} + static const vfs_fs_driver_t procfs_driver = { .list = procfs_list, .read = procfs_read, @@ -343,6 +420,7 @@ static const vfs_fs_driver_t procfs_driver = { .mkdir = NULL, .rmdir = NULL, .rename = NULL, + .mknod = NULL, .chmod = NULL, .utimens = NULL, .destroy = procfs_destroy, diff --git a/src/kernel/fs/tmpfs.c b/src/kernel/fs/tmpfs/tmpfs.c similarity index 84% rename from src/kernel/fs/tmpfs.c rename to src/kernel/fs/tmpfs/tmpfs.c index 90c03da0..b5856048 100644 --- a/src/kernel/fs/tmpfs.c +++ b/src/kernel/fs/tmpfs/tmpfs.c @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -14,9 +14,10 @@ #endif #include +#include #include #include -#include +#include #include #include @@ -32,6 +33,9 @@ typedef struct tmpfs_node_t { struct timespec atime; struct timespec mtime; struct timespec ctime; + bool is_char_device; + dev_t rdev; + char_device_t* char_device; } tmpfs_node_t; typedef struct tmpfs_ctx_t { @@ -91,7 +95,7 @@ static void tmpfs_detach_node(tmpfs_ctx_t* ctx, tmpfs_node_t* target) { kfree(node); } -static tmpfs_node_t* tmpfs_create_node(tmpfs_ctx_t* ctx, const char* name) { +static tmpfs_node_t* tmpfs_create_node(tmpfs_ctx_t* ctx, const char* name, mode_t mode) { tmpfs_node_t* node = kmalloc(sizeof(tmpfs_node_t)); if(node == NULL) { return NULL; @@ -104,7 +108,10 @@ static tmpfs_node_t* tmpfs_create_node(tmpfs_ctx_t* ctx, const char* name) { node->capacity = 0; node->deleted = false; node->refcount = 0; - node->mode = S_IFREG | 0644; + node->mode = mode; + node->is_char_device = S_ISCHR(mode); + node->rdev = 0; + node->char_device = NULL; struct timespec now = tmpfs_current_time(); node->atime = now; node->mtime = now; @@ -157,7 +164,7 @@ static bool tmpfs_read(void* fs_ctx, kmutex_lock(&ctx->lock); tmpfs_node_t* node = tmpfs_find_node(ctx, name); - if(node == NULL || node->deleted) { + if(node == NULL || node->deleted || node->is_char_device) { kmutex_unlock(&ctx->lock); return false; } @@ -192,7 +199,7 @@ static bool tmpfs_read_all(void* fs_ctx, const char* path, void** out_buffer, si kmutex_lock(&ctx->lock); tmpfs_node_t* node = tmpfs_find_node(ctx, name); - if(node == NULL || node->deleted) { + if(node == NULL || node->deleted || node->is_char_device) { kmutex_unlock(&ctx->lock); return false; } @@ -230,7 +237,7 @@ static bool tmpfs_driver_write(void* fs_ctx, kmutex_lock(&ctx->lock); tmpfs_node_t* node = tmpfs_find_node(ctx, name); - if(node == NULL || node->deleted) { + if(node == NULL || node->deleted || node->is_char_device) { kmutex_unlock(&ctx->lock); return false; } @@ -320,9 +327,7 @@ static bool tmpfs_stat_path(void* fs_ctx, const char* path, fs_path_info_t* out_ memset(out_info, 0, sizeof(*out_info)); out_info->is_directory = false; - out_info->is_read_only = false; out_info->block_size = 4096; - out_info->size = node->size; out_info->inode = (uint64_t)(uintptr_t)node; out_info->has_mode = true; out_info->mode = node->mode; @@ -330,6 +335,17 @@ static bool tmpfs_stat_path(void* fs_ctx, const char* path, fs_path_info_t* out_ out_info->atime = node->atime; out_info->mtime = node->mtime; out_info->ctime = node->ctime; + if(node->is_char_device) { + out_info->size = 0; + out_info->is_read_only = (node->char_device == NULL) ? true : ((node->char_device->access_mode & FILE_MODE_WRITE) == 0); + out_info->has_rdev = true; + out_info->rdev = node->rdev; + } else { + out_info->size = node->size; + out_info->is_read_only = false; + out_info->has_rdev = false; + out_info->rdev = 0; + } kmutex_unlock(&ctx->lock); return true; } @@ -450,6 +466,14 @@ static int tmpfs_stat_impl(file_t* file, struct stat* out_stat) { out_stat->st_blksize = 4096; out_stat->st_blocks = (blkcnt_t)((node->size + 511ull) / 512ull); out_stat->st_ino = (ino_t)(uintptr_t)node; + if(node->is_char_device) { + out_stat->st_mode = (out_stat->st_mode & ~S_IFMT) | S_IFCHR; + out_stat->st_size = 0; + out_stat->st_blocks = 0; + out_stat->st_rdev = node->rdev; + } else { + out_stat->st_rdev = 0; + } out_stat->st_atime = (time_t)node->atime.tv_sec; out_stat->st_mtime = (time_t)node->mtime.tv_sec; out_stat->st_ctime = (time_t)node->ctime.tv_sec; @@ -535,6 +559,10 @@ static int64_t tmpfs_read_impl(file_t* file, void* buffer, size_t length) { kmutex_unlock(&ctx->lock); return -ENOENT; } + if(node->is_char_device) { + kmutex_unlock(&ctx->lock); + return -ENOTTY; + } if(state->offset >= node->size) { kmutex_unlock(&ctx->lock); @@ -588,6 +616,10 @@ static int64_t tmpfs_write_impl(file_t* file, const void* buffer, size_t length) kmutex_unlock(&ctx->lock); return -ENOENT; } + if(node->is_char_device) { + kmutex_unlock(&ctx->lock); + return -ENOTTY; + } if((state->flags & O_APPEND) != 0) { state->offset = node->size; @@ -670,6 +702,60 @@ static uint32_t tmpfs_requested_mode(int flags) { } } +static int tmpfs_mknod(void* fs_ctx, const char* path, mode_t mode, dev_t dev) { + tmpfs_ctx_t* ctx = (tmpfs_ctx_t*)fs_ctx; + if(path == NULL) { + return -EINVAL; + } + + if(!S_ISCHR(mode)) { + return -EINVAL; + } + + char_device_t* device = char_device_lookup(dev); + if(device == NULL || device->open == NULL) { + return -ENODEV; + } + + const char* name = path; + while(*name == '/') { + name++; + } + if(*name == '\0') { + return -EINVAL; + } + for(const char* it = name; *it != '\0'; ++it) { + if(*it == '/') { + return -EINVAL; + } + } + + kmutex_lock(&ctx->lock); + tmpfs_node_t* existing = tmpfs_find_node(ctx, name); + if(existing != NULL) { + if(existing->deleted && existing->refcount == 0) { + tmpfs_detach_node(ctx, existing); + } else { + kmutex_unlock(&ctx->lock); + return existing->deleted ? -EBUSY : -EEXIST; + } + } + + mode_t final_mode = (mode & 07777) | S_IFCHR; + tmpfs_node_t* node = tmpfs_create_node(ctx, name, final_mode); + if(node == NULL) { + kmutex_unlock(&ctx->lock); + return -ENOMEM; + } + node->is_char_device = true; + node->char_device = device; + node->rdev = dev; + node->size = 0; + node->capacity = 0; + kmutex_unlock(&ctx->lock); + return 0; +} + static int tmpfs_open(void* fs_ctx, const char* path, int flags, file_t** out_file) { tmpfs_ctx_t* ctx = (tmpfs_ctx_t*)fs_ctx; if(path == NULL || out_file == NULL) { @@ -694,6 +780,7 @@ static int tmpfs_open(void* fs_ctx, const char* path, int flags, file_t** out_fi bool create = (flags & O_CREAT) != 0; bool exclusive = (flags & O_EXCL) != 0; bool trunc = (flags & O_TRUNC) != 0; + uint32_t requested_modes = tmpfs_requested_mode(flags); kmutex_lock(&ctx->lock); tmpfs_node_t* node = tmpfs_find_node(ctx, name); @@ -703,7 +790,7 @@ static int tmpfs_open(void* fs_ctx, const char* path, int flags, file_t** out_fi kmutex_unlock(&ctx->lock); return -ENOENT; } - node = tmpfs_create_node(ctx, name); + node = tmpfs_create_node(ctx, name, S_IFREG | 0644); if(node == NULL) { serial_printf("tmpfs_open: '%s' allocation failed\n", name); kmutex_unlock(&ctx->lock); @@ -711,6 +798,34 @@ static int tmpfs_open(void* fs_ctx, const char* path, int flags, file_t** out_fi } serial_printf("tmpfs_open: created '%s'\n", name); } else { + if(S_ISCHR(node->mode)) { + if(create && exclusive) { + kmutex_unlock(&ctx->lock); + return -EEXIST; + } + if(trunc) { + kmutex_unlock(&ctx->lock); + return -EINVAL; + } + char_device_t* device = node->char_device; + if(device == NULL) { + kmutex_unlock(&ctx->lock); + return -ENODEV; + } + if((device->access_mode & requested_modes) != requested_modes) { + kmutex_unlock(&ctx->lock); + return -EACCES; + } + node->atime = tmpfs_current_time(); + kmutex_unlock(&ctx->lock); + file_t* device_file = NULL; + int rc = device->open(device, flags, &device_file); + if(rc != 0) { + return rc; + } + *out_file = device_file; + return 0; + } if(create && exclusive) { serial_printf("tmpfs_open: '%s' exists with O_EXCL\n", name); kmutex_unlock(&ctx->lock); @@ -746,7 +861,7 @@ static int tmpfs_open(void* fs_ctx, const char* path, int flags, file_t** out_fi state->flags = flags; state->offset = ((flags & O_APPEND) != 0) ? node->size : 0; - uint32_t mode = tmpfs_requested_mode(flags); + uint32_t mode = requested_modes; file_t* file = file_create(&tmpfs_file_ops, state, mode); if(file == NULL) { kmutex_lock(&ctx->lock); @@ -825,6 +940,7 @@ static const vfs_fs_driver_t tmpfs_driver = { .mkdir = NULL, .rmdir = NULL, .rename = NULL, + .mknod = tmpfs_mknod, .chmod = tmpfs_chmod, .utimens = tmpfs_utimens, .destroy = tmpfs_destroy, diff --git a/src/kernel/fs/vfs.c b/src/kernel/fs/vfs/vfs.c similarity index 98% rename from src/kernel/fs/vfs.c rename to src/kernel/fs/vfs/vfs.c index 770b0279..fec004d8 100644 --- a/src/kernel/fs/vfs.c +++ b/src/kernel/fs/vfs/vfs.c @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include #include @@ -295,7 +295,7 @@ bool vfs_path_info(const char* path, fs_path_info_t* out_info) { return true; } -bool vfs_init(void) { +bool vfs_initialize(void) { if(vfs_initialized) { return true; } @@ -356,7 +356,7 @@ static vfs_mount_entry_t* vfs_find_mount_locked(const char* path, size_t path_le } bool vfs_mount(const char* path, const vfs_fs_driver_t* driver, void* fs_ctx, bool read_only) { - if(!vfs_initialized && !vfs_init()) { + if(!vfs_initialized && !vfs_initialize()) { return false; } @@ -1163,6 +1163,36 @@ int vfs_rmdir(const char* path) { return driver->rmdir(fs_ctx, relative); } +int vfs_mknod(const char* path, mode_t mode, dev_t dev) { + if(path == NULL) { + return -EINVAL; + } + + const vfs_fs_driver_t* driver = NULL; + void* fs_ctx = NULL; + char relative[VFS_PATH_MAX]; + bool read_only = true; + + if(!vfs_resolve(path, &driver, &fs_ctx, relative, sizeof(relative), &read_only)) { + return -ENOENT; + } + + if(read_only) { + return -EROFS; + } + + if(driver->mknod == NULL) { + return -ENOSYS; + } + + fs_path_info_t info; + if(vfs_path_info(path, &info)) { + return -EEXIST; + } + + return driver->mknod(fs_ctx, relative, mode, dev); +} + int vfs_mkdir(const char* path) { if(path == NULL) { return -EINVAL; @@ -1448,6 +1478,7 @@ static const vfs_fs_driver_t fat32_driver = { .mkdir = fat32_mkdir_adapter, .rmdir = fat32_rmdir_adapter, .rename = fat32_rename_adapter, + .mknod = NULL, .chmod = fat32_chmod_impl, .utimens = fat32_utimens_path, .destroy = fat32_destroy_adapter, diff --git a/src/kernel/hw/hw.c b/src/kernel/hw/hw.c index 1e89b08c..c0101d0c 100644 --- a/src/kernel/hw/hw.c +++ b/src/kernel/hw/hw.c @@ -87,9 +87,9 @@ void acpi_enumerate() { uacpi_namespace_for_each_node_depth_first(uacpi_namespace_root(), register_device, UACPI_NULL); } -void hardware_init() { +void hardware_initialize(void) { devices_head = NULL; - driver_init(); + driver_registry_init(); logk("Probing hardware\n"); acpi_enumerate(); hardware_log_devices(); diff --git a/src/kernel/ipc/shm.c b/src/kernel/ipc/shm.c index cfa53c5d..ddf34158 100644 --- a/src/kernel/ipc/shm.c +++ b/src/kernel/ipc/shm.c @@ -96,7 +96,7 @@ static int shm_manager_allocate_id_locked(void) { return -1; } -void shm_manager_init(void) { +void shm_manager_initialize(void) { kmutex_init(&shm_manager.lock); shm_manager.head = NULL; shm_manager.next_id = 1; diff --git a/src/kernel/mem/kmalloc.c b/src/kernel/mem/kmalloc.c index 16e83bad..8de34885 100644 --- a/src/kernel/mem/kmalloc.c +++ b/src/kernel/mem/kmalloc.c @@ -603,7 +603,7 @@ static void heap_set_errno(int err) { } } -void init_heap(void* addr, size_t size) { +void heap_initialize(void* addr, size_t size) { heap_reset_lock(); heap_freed = false; heap = NULL; @@ -615,7 +615,7 @@ void init_heap(void* addr, size_t size) { size_t aligned_size = align_down(size, PAGE_SIZE); if(aligned_size < PAGE_SIZE) { - serial_printf("init_heap: region too small (%lu)\n", size); + serial_printf("heap_initialize: region too small (%lu)\n", size); return; } @@ -631,7 +631,7 @@ void init_heap(void* addr, size_t size) { heap_tail = node; if(heap_register_region(node, aligned_size, 0, aligned_size / PAGE_SIZE, false) == NULL) { - serial_printf("init_heap: failed to register static region\n"); + serial_printf("heap_initialize: failed to register static region\n"); } serial_printf("Heap initialized at %p with size %ld\n", addr, (long)aligned_size); @@ -643,7 +643,7 @@ void init_heap(void* addr, size_t size) { } if(!heap_grow(size)) { - serial_printf("init_heap: unable to reserve %lu bytes\n", size); + serial_printf("heap_initialize: unable to reserve %lu bytes\n", size); } else { serial_printf("Heap initialized dynamically (%lu bytes)\n", align_up(size, PAGE_SIZE)); } diff --git a/src/kernel/mem/mem.c b/src/kernel/mem/mem.c index 050dfabe..46eeb995 100644 --- a/src/kernel/mem/mem.c +++ b/src/kernel/mem/mem.c @@ -10,12 +10,12 @@ #include #include -void mem_init() { +void memory_initialize(void) { serial_puts("\n- Initing memory management:\n"); // init the physical memory management - pmm_init(); + pmm_initialize(); - init_heap(NULL, PAGE_SIZE * HEAP_SIZE); + heap_initialize(NULL, PAGE_SIZE * HEAP_SIZE); } int mem_compactor(void *unused) { @@ -32,10 +32,10 @@ int mem_compactor(void *unused) { return 0; } -void init_memory_compactor() { +void mem_compactor_initialize(void) { kthread_p pthread = kmalloc(sizeof(kthread_t)); if(pthread == NULL) { - serial_error("init_memory_compactor: failed to allocate thread descriptor\n"); + serial_error("mem_compactor_initialize: failed to allocate thread descriptor\n"); return; } memzero(pthread, sizeof(kthread_t)); diff --git a/src/kernel/mem/pmm.c b/src/kernel/mem/pmm.c index d4aa4bc2..ad7177d1 100644 --- a/src/kernel/mem/pmm.c +++ b/src/kernel/mem/pmm.c @@ -789,9 +789,9 @@ void pmm_get_stats(pmm_stats_t* stats) { stats->free_pages = pmm_free_page_count; } -void init_kernel_offset() { +void pmm_kernel_offset_initialize(void) { logk("Getting kernel offset.\n"); - serial_printf("> init_kernel_offset\n"); + serial_printf("> pmm_kernel_offset_initialize\n"); kernel_offset = hhdm_request.response->offset; serial_printf(" Kernel offset %lx:\n", kernel_offset); } @@ -804,7 +804,7 @@ virt_addr_t get_kernel_offset() { return kernel_offset; } -void init_page_bitmap() { +void pmm_page_bitmap_initialize(void) { logk("Initing page bitmap.\n"); memsetl(&page_bitmap, PAGE_BITMAP_FULL, PAGE_BITMAP_SIZE); } @@ -843,7 +843,7 @@ phys_addr_t read_cr3() { #endif } -void init_cr3() { +void pmm_cr3_initialize(void) { kernel_cr3_phys = read_cr3(); cr3_vaddr = physical_to_virtual(kernel_cr3_phys); clear_kernel_user_permissions(kernel_cr3_phys); @@ -957,15 +957,15 @@ uintptr_t get_first_free_virtual_address(uintptr_t offset) { - set any value in the request address - read the value in the request address */ -void pmm_init() { +void pmm_initialize(void) { logk("Initing Physical memory manager\n"); serial_puts("\n- Initing Physical memory manager:\n"); - init_page_bitmap(); + pmm_page_bitmap_initialize(); list_memory_areas(); - init_kernel_offset(); + pmm_kernel_offset_initialize(); - init_cr3(); + pmm_cr3_initialize(); } diff --git a/src/kernel/proc/kcondvar.c b/src/kernel/proc/kcondvar.c index dad8f7ba..c7a828da 100644 --- a/src/kernel/proc/kcondvar.c +++ b/src/kernel/proc/kcondvar.c @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include static inline void kcondvar_wait_cycle(void) { diff --git a/src/kernel/proc/krwlock.c b/src/kernel/proc/krwlock.c index 93c727d7..01503921 100644 --- a/src/kernel/proc/krwlock.c +++ b/src/kernel/proc/krwlock.c @@ -1,6 +1,6 @@ #include -void krwlock_init(krwlock_t* rwlock) { +void krwlock_initialize(krwlock_t* rwlock) { if(rwlock == NULL) { return; } diff --git a/src/kernel/proc/ksemaphore.c b/src/kernel/proc/ksemaphore.c index 44acda5b..4a2b3495 100644 --- a/src/kernel/proc/ksemaphore.c +++ b/src/kernel/proc/ksemaphore.c @@ -1,6 +1,6 @@ #include -void ksem_init(ksem_t* sem, int64_t value) { +void ksem_initialize(ksem_t* sem, int64_t value) { if(sem == NULL) { return; } diff --git a/src/kernel/proc/proc.c b/src/kernel/proc/proc.c index b1fd9b1c..fdc5403d 100644 --- a/src/kernel/proc/proc.c +++ b/src/kernel/proc/proc.c @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include @@ -729,11 +729,11 @@ void proc_create(proc_info_p proc, const char* name, void (*entrypoint)(void *), proc->parent = current; proc->pid = last_pid++; proc_table_insert(proc); - proc_file_table_init(proc); + proc_file_table_initialize(proc); if(current != NULL) { proc_file_table_clone(proc, current); } - proc_signal_state_init(proc); + proc_signal_state_initialize(proc); proc->stopped = false; proc->stop_status_pending = false; proc->stop_status = 0; @@ -1251,7 +1251,7 @@ proc_info_p proc_fork(proc_info_p parent, const syscall_frame_t* frame, int* err return NULL; } memset(child, 0, sizeof(proc_info_t)); - proc_file_table_init(child); + proc_file_table_initialize(child); proc_file_table_clone(child, parent); if(parent->cwd_len > 0 && parent->cwd[0] != '\0') { size_t copy_len = parent->cwd_len; @@ -2071,10 +2071,10 @@ void proc_execute(proc_info_p proc) { SCHED_TRACE("proc_execute: queued process %s (priority %u)\n", proc->name, proc->priority); } -void scheduler_init() { +void scheduler_initialize(void) { logk("Initing scheduler"); current = &kernel_process_info; - proc_signal_state_init(current); + proc_signal_state_initialize(current); memset(ready_queues, 0, sizeof(ready_queues)); sleep_queue_head = NULL; scheduler_actions = 0; diff --git a/src/kernel/proc/signal.c b/src/kernel/proc/signal.c index c5aa21ec..9a4369af 100644 --- a/src/kernel/proc/signal.c +++ b/src/kernel/proc/signal.c @@ -71,7 +71,7 @@ int proc_signal_take_pending(proc_info_p proc, return 0; } -void proc_signal_state_init(proc_info_p proc) { +void proc_signal_state_initialize(proc_info_p proc) { if(proc == NULL) { return; } diff --git a/src/kernel/syscall/entry.c b/src/kernel/syscall/entry.c index 96697c64..a125e5fd 100644 --- a/src/kernel/syscall/entry.c +++ b/src/kernel/syscall/entry.c @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -27,7 +27,7 @@ static syscall_cpu_context_t syscall_cpu_context __attribute__((aligned(16))); uint64_t syscall_last_return_value = 0; uint64_t syscall_last_return_slot_value = 0; -void syscall_arch_init(void) { +void syscall_arch_initialize(void) { #ifdef MENIOS_HOST_TEST (void)0; #else @@ -53,7 +53,7 @@ void syscall_arch_init(void) { syscall_cpu_context.user_rip = 0; syscall_cpu_context.user_rflags = 0; - serial_printf("syscall_arch_init: fast syscall/sysret configured (LSTAR=%p)\n", &syscall_entry); + serial_printf("syscall_arch_initialize: fast syscall/sysret configured (LSTAR=%p)\n", &syscall_entry); #endif } diff --git a/src/kernel/syscall/syscall.c b/src/kernel/syscall/syscall.c index d0b7de2e..2ce99bdc 100644 --- a/src/kernel/syscall/syscall.c +++ b/src/kernel/syscall/syscall.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -115,9 +115,11 @@ static uint64_t syscall_unlink_handler(syscall_frame_t* frame); static uint64_t syscall_mkdir_handler(syscall_frame_t* frame); static uint64_t syscall_rmdir_handler(syscall_frame_t* frame); static uint64_t syscall_rename_handler(syscall_frame_t* frame); +static uint64_t syscall_mknod_handler(syscall_frame_t* frame); static uint64_t syscall_chmod_handler(syscall_frame_t* frame); static uint64_t syscall_fchmod_handler(syscall_frame_t* frame); static uint64_t syscall_utime_handler(syscall_frame_t* frame); +static uint64_t syscall_mknod_handler(syscall_frame_t* frame); static uint64_t syscall_proc_kill_handler(syscall_frame_t* frame); static uint64_t syscall_kill_handler(syscall_frame_t* frame); static uint64_t syscall_sigaction_handler(syscall_frame_t* frame); @@ -702,8 +704,8 @@ static void syscall_register(uint64_t number, syscall_handler_t handler) { syscall_table[number] = handler ? handler : syscall_stub_unimplemented; } -void syscall_init(void) { - syscall_arch_init(); +void syscall_initialize(void) { + syscall_arch_initialize(); for(size_t i = 0; i < SYSCALL_MAX; i++) { syscall_table[i] = syscall_stub_unimplemented; @@ -761,6 +763,7 @@ void syscall_init(void) { syscall_register(SYS_MKDIR, syscall_mkdir_handler); syscall_register(SYS_RMDIR, syscall_rmdir_handler); syscall_register(SYS_RENAME, syscall_rename_handler); + syscall_register(SYS_MKNOD, syscall_mknod_handler); syscall_register(SYS_CHMOD, syscall_chmod_handler); syscall_register(SYS_FCHMOD, syscall_fchmod_handler); syscall_register(SYS_UTIME, syscall_utime_handler); @@ -769,7 +772,7 @@ void syscall_init(void) { syscall_register(SYS_TIME, syscall_time_handler); syscall_register(SYS_GETTIMEOFDAY, syscall_gettimeofday_handler); - serial_printf("syscall_init: dispatcher ready (syscall/sysret)\n"); + serial_printf("syscall_initialize: dispatcher ready (syscall/sysret)\n"); } uint64_t syscall_dispatch(syscall_frame_t* frame) { @@ -1790,6 +1793,49 @@ static uint64_t syscall_rename_handler(syscall_frame_t* frame) { return frame->rax; } +static uint64_t syscall_mknod_handler(syscall_frame_t* frame) { + if(current == NULL) { + frame->rax = (uint64_t)(-EINVAL); + return frame->rax; + } + + const char* user_path = (const char*)frame->rdi; + mode_t mode = (mode_t)frame->rsi; + dev_t dev = (dev_t)frame->rdx; + + if(user_path == NULL) { + frame->rax = (uint64_t)(-EFAULT); + return frame->rax; + } + + if(!S_ISCHR(mode)) { + frame->rax = (uint64_t)(-EINVAL); + return frame->rax; + } + + char path[SYSCALL_PATH_MAX]; + if(!copy_user_string(user_path, path, sizeof(path))) { + frame->rax = (uint64_t)(-EFAULT); + return frame->rax; + } + + if(path[0] == '\0') { + frame->rax = (uint64_t)(-EINVAL); + return frame->rax; + } + + char absolute[VFS_PATH_MAX]; + if(!vfs_build_absolute_path(current->cwd, path, absolute, sizeof(absolute))) { + frame->rax = (uint64_t)(-ENAMETOOLONG); + return frame->rax; + } + + mode_t final_mode = (mode & 07777) | S_IFCHR; + int rc = vfs_mknod(absolute, final_mode, dev); + frame->rax = (uint64_t)rc; + return frame->rax; +} + static uint64_t syscall_chmod_handler(syscall_frame_t* frame) { if(current == NULL) { frame->rax = (uint64_t)(-EINVAL); diff --git a/src/kernel/timer/hpet.c b/src/kernel/timer/hpet.c index 8697e3ff..e57f2888 100644 --- a/src/kernel/timer/hpet.c +++ b/src/kernel/timer/hpet.c @@ -7,7 +7,7 @@ #include #include -hpet_status_t hpet_timer_init() { +hpet_status_t hpet_timer_initialize(void) { uacpi_table tbl; uacpi_status ret = uacpi_table_find_by_signature("APIC", &tbl); diff --git a/src/kernel/timer/lapic.c b/src/kernel/timer/lapic.c index a2102748..d737911c 100644 --- a/src/kernel/timer/lapic.c +++ b/src/kernel/timer/lapic.c @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -8,7 +8,7 @@ static uintptr_t addr; static uint32_t timer_freq = 10000000; -void lapic_timer_init() { +void lapic_timer_initialize(void) { serial_printf("lapic timer init\n"); addr = physical_to_virtual(DEFAULT_LAPIC_ADDRESS); serial_printf("lapic address: %lx - virt: %lx\n", DEFAULT_LAPIC_ADDRESS, addr); diff --git a/src/kernel/timer/timer.c b/src/kernel/timer/timer.c index 1571eb10..2400edad 100644 --- a/src/kernel/timer/timer.c +++ b/src/kernel/timer/timer.c @@ -1,6 +1,6 @@ #include -#include +#include #include #include #include @@ -41,17 +41,17 @@ void register_timer_callback(void (*cb)(void*)) { callback[last_callback++] = cb; } -void timer_init() { +void timer_initialize(void) { logk("Initing timer\n"); for(int i = 0; i < 16; i++) { callback[i] = NULL; }; logk(" Initing LAPIC timer\n"); - lapic_timer_init(); + lapic_timer_initialize(); logk(" Initing TSC\n"); - tsc_init(); + tsc_initialize(); if(!has_invariant_tsc()) { errk(" Invariant TSC not supported.\n"); @@ -59,7 +59,7 @@ void timer_init() { logk(" Invariant TSC supported, but ignored.\n"); } - if(hpet_timer_init() == HPET_OK) { + if(hpet_timer_initialize() == HPET_OK) { errk(" HPET timer supported, but ignored.\n"); } else { errk(" HPET timer not supported.\n"); @@ -74,4 +74,4 @@ uint64_t boot_time() { } else { return boot_time_request.response->boot_time; } -} \ No newline at end of file +} diff --git a/src/kernel/timer/tsc.c b/src/kernel/timer/tsc.c index dac58ff4..ed14c304 100644 --- a/src/kernel/timer/tsc.c +++ b/src/kernel/timer/tsc.c @@ -144,7 +144,7 @@ static inline uint64_t tsc_ns_to_ticks_internal(uint64_t ns) { return div_u128_u64(hi, lo, 1000000000ull); } -void tsc_init() { +void tsc_initialize(void) { boot_time_sec = boot_time(); tsc_calibrate(); tick_start = read_tsc(); diff --git a/src/kernel/user/init.c b/src/kernel/user/init.c index c96e6f3f..7fb8ceba 100644 --- a/src/kernel/user/init.c +++ b/src/kernel/user/init.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include extern const uint8_t init_elf_start[]; extern const uint8_t init_elf_end[]; diff --git a/src/kernel/user/user_demo.c b/src/kernel/user/user_demo.c index ce615311..ddc1b111 100644 --- a/src/kernel/user/user_demo.c +++ b/src/kernel/user/user_demo.c @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/libc/stat.c b/src/libc/stat.c index 574e2dc3..03670b8f 100644 --- a/src/libc/stat.c +++ b/src/libc/stat.c @@ -25,6 +25,17 @@ int mkdir(const char* path, mode_t mode) { return 0; } +int mknod(const char* path, mode_t mode, dev_t dev) { + long rc = __menios_syscall3(SYS_MKNOD, (long)path, (long)mode, (long)dev); + + if(rc < 0) { + errno = (int)(-rc); + return -1; + } + + return 0; +} + int fstat(int fd, struct stat* buf) { long rc = __menios_syscall2(SYS_FSTAT, (long)fd, (long)buf); diff --git a/tasks.json b/tasks.json index 40cdcc3c..2c083a1b 100644 --- a/tasks.json +++ b/tasks.json @@ -1,15 +1,16 @@ [ { - "title": "Display PCI device list on boot screen", + "title": "Terminal scrollback with mouse wheel support", "github_issue": { - "number": 387, - "url": "https://github.com/pbalduino/menios/issues/387", - "state": "CLOSED" + "number": 394, + "url": "https://github.com/pbalduino/menios/issues/394", + "state": "OPEN" }, "labels": [ "enhancement", "nice to have", - "kernel" + "mosh", + "drivers" ], "assignees": [ "pbalduino" @@ -82,9 +83,9 @@ { "title": "PCI audio driver (AC'97 or Intel HDA)", "github_issue": { - "number": 368, - "url": "https://github.com/pbalduino/menios/issues/368", - "state": "CLOSED" + "number": 382, + "url": "https://github.com/pbalduino/menios/issues/382", + "state": "OPEN" }, "labels": [ "enhancement", @@ -111,52 +112,6 @@ "pbalduino" ] }, - { - "title": "Implement head command - display first lines of files", - "github_issue": { - "number": 374, - "url": "https://github.com/pbalduino/menios/issues/374", - "state": "OPEN" - }, - "labels": [ - "enhancement", - "good first issue", - "build" - ], - "assignees": [ - "pbalduino" - ] - }, - { - "title": "Implement shutdown command for ACPI power-off", - "github_issue": { - "number": 372, - "url": "https://github.com/pbalduino/menios/issues/372", - "state": "CLOSED" - }, - "labels": [ - "enhancement", - "kernel" - ], - "assignees": [ - "pbalduino" - ] - }, - { - "title": "Implement system() function for shell command execution", - "github_issue": { - "number": 369, - "url": "https://github.com/pbalduino/menios/issues/369", - "state": "CLOSED" - }, - "labels": [ - "enhancement", - "libc" - ], - "assignees": [ - "pbalduino" - ] - }, { "title": "Implement stubbed libc functions", "github_issue": { @@ -358,36 +313,6 @@ "pbalduino" ] }, - { - "title": "Implement time command for mosh shell", - "github_issue": { - "number": 350, - "url": "https://github.com/pbalduino/menios/issues/350", - "state": "CLOSED" - }, - "labels": [ - "mosh" - ], - "assignees": [ - "pbalduino" - ] - }, - { - "title": "Implement head command for file previews", - "github_issue": { - "number": 374, - "url": "https://github.com/pbalduino/menios/issues/374", - "state": "CLOSED" - }, - "labels": [ - "enhancement", - "good first issue", - "build" - ], - "assignees": [ - "pbalduino" - ] - }, { "title": "Fix Arch Linux build failures (doomtype.h enum error, ATOMIC_FLAG_INIT deprecation)", "github_issue": { @@ -966,21 +891,6 @@ "pbalduino" ] }, - { - "title": "bug: Delete key not working in mosh shell", - "github_issue": { - "number": 236, - "url": "https://github.com/pbalduino/menios/issues/236", - "state": "CLOSED" - }, - "labels": [ - "bug", - "mosh" - ], - "assignees": [ - "pbalduino" - ] - }, { "title": "Port xargs utility to meniOS", "github_issue": { @@ -1332,7 +1242,9 @@ "state": "OPEN" }, "labels": [ - "drivers" + "enhancement", + "drivers", + "kernel" ], "assignees": [ "pbalduino" @@ -1468,21 +1380,6 @@ "pbalduino" ] }, - { - "title": "Add code coverage reporting to unit tests with Gcov integration", - "github_issue": { - "number": 135, - "url": "https://github.com/pbalduino/menios/issues/135", - "state": "CLOSED" - }, - "labels": [ - "enhancement", - "good first issue" - ], - "assignees": [ - "pbalduino" - ] - }, { "title": "Create comprehensive Unicode test suite and validation", "github_issue": { @@ -2314,11 +2211,12 @@ "github_issue": { "number": 23, "url": "https://github.com/pbalduino/menios/issues/23", - "state": "OPEN" + "state": "CLOSED" }, "labels": [ "enhancement", - "nice to have" + "nice to have", + "kernel" ], "assignees": [ "pbalduino" diff --git a/test/stubs.c b/test/stubs.c index e8546e56..9992bf0e 100644 --- a/test/stubs.c +++ b/test/stubs.c @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include diff --git a/test/stubs_fat32.c b/test/stubs_fat32.c index 0fc9a8cb..647d48f7 100644 --- a/test/stubs_fat32.c +++ b/test/stubs_fat32.c @@ -3,7 +3,7 @@ #include #include -#include +#include #ifndef MENIOS_HOST_TEST #error "stubs_fat32.c should only be compiled for host tests" diff --git a/test/stubs_framebuffer.c b/test/stubs_framebuffer.c index 7386b4f5..e0dda7bf 100644 --- a/test/stubs_framebuffer.c +++ b/test/stubs_framebuffer.c @@ -133,7 +133,7 @@ void fb_get_boot_mode(framebuffer_mode_info_t* out) { } void fb_draw(void) {} -void fb_init(void) {} +void framebuffer_initialize(void) {} void fb_putpixel(uint32_t x, uint32_t y, uint32_t rgb) { (void)x; (void)y; diff --git a/test/test_char_device_registry.c b/test/test_char_device_registry.c new file mode 100644 index 00000000..12599d49 --- /dev/null +++ b/test/test_char_device_registry.c @@ -0,0 +1,116 @@ +#include "unity.h" + +#include +#include +#include + +#include +#include +#include +#include + +static int test_char_open(char_device_t* device, int flags, file_t** out_file) { + (void)device; + (void)flags; + (void)out_file; + return -ENOSYS; +} + +static char_device_t make_test_device(const char* name, uint32_t mode, unsigned int minors) { + char_device_t device = { + .name = name, + .access_mode = mode, + .open = test_char_open, + .driver_data = NULL, + .dev = 0, + .minor_count = minors, + }; + return device; +} + +void setUp(void) { + char_device_system_initialize(); +} + +void tearDown(void) { + vfs_shutdown(); +} + +void test_char_device_register_rejects_null(void) { + TEST_ASSERT_EQUAL_INT(-EINVAL, char_device_register(NULL)); +} + +void test_char_device_register_rejects_missing_access(void) { + char_device_t device = make_test_device("testdev_none", 0u, 1u); + TEST_ASSERT_EQUAL_INT(-EINVAL, char_device_register(&device)); +} + +void test_char_device_register_dynamic_assigns_reserved_major(void) { + char_device_t device = make_test_device("testdev_dyn0", FILE_MODE_READ, 1u); + TEST_ASSERT_EQUAL_INT(0, char_device_register(&device)); + unsigned int assigned_major = MAJOR(device.dev); + TEST_ASSERT_TRUE(assigned_major >= 10u); + + char_device_unregister(&device); +} + +void test_char_device_register_minor_range_lookup(void) { + char_device_t device = make_test_device("testdev_range", FILE_MODE_READ, 4u); + TEST_ASSERT_EQUAL_INT(0, char_device_register(&device)); + + unsigned int major = MAJOR(device.dev); + unsigned int base_minor = MINOR(device.dev); + + for(unsigned int offset = 0; offset < device.minor_count; ++offset) { + dev_t candidate = MKDEV(major, base_minor + offset); + TEST_ASSERT_EQUAL_PTR(&device, char_device_lookup(candidate)); + } + + dev_t out_of_range = MKDEV(major, base_minor + device.minor_count); + TEST_ASSERT_NULL(char_device_lookup(out_of_range)); + + char_device_unregister(&device); +} + +void test_char_device_unregister_removes_lookup_entry(void) { + char_device_t device = make_test_device("testdev_unregister", FILE_MODE_READ, 1u); + TEST_ASSERT_EQUAL_INT(0, char_device_register(&device)); + + TEST_ASSERT_EQUAL_PTR(&device, char_device_lookup(device.dev)); + char_device_unregister(&device); + TEST_ASSERT_NULL(char_device_lookup(device.dev)); +} + +void test_char_device_register_name_conflict(void) { + char_device_t first = make_test_device("testdev_conflict", FILE_MODE_READ, 1u); + char_device_t second = make_test_device("testdev_conflict", FILE_MODE_READ, 1u); + + TEST_ASSERT_EQUAL_INT(0, char_device_register(&first)); + TEST_ASSERT_EQUAL_INT(-EEXIST, char_device_register(&second)); + + char_device_unregister(&first); +} + +void test_char_device_register_range_conflict(void) { + char_device_t first = make_test_device("testdev_range1", FILE_MODE_READ, 2u); + first.dev = MKDEV(20u, 0u); + TEST_ASSERT_EQUAL_INT(0, char_device_register(&first)); + + char_device_t overlap = make_test_device("testdev_overlap", FILE_MODE_READ, 1u); + overlap.dev = MKDEV(20u, 1u); + TEST_ASSERT_EQUAL_INT(-EEXIST, char_device_register(&overlap)); + + char_device_unregister(&first); +} + +int main(void) { + UNITY_BEGIN(); + RUN_TEST(test_char_device_register_rejects_null); + RUN_TEST(test_char_device_register_rejects_missing_access); + RUN_TEST(test_char_device_register_dynamic_assigns_reserved_major); + RUN_TEST(test_char_device_register_minor_range_lookup); + RUN_TEST(test_char_device_unregister_removes_lookup_entry); + RUN_TEST(test_char_device_register_name_conflict); + RUN_TEST(test_char_device_register_range_conflict); + return UNITY_END(); +} diff --git a/test/test_fat32_lfn.c b/test/test_fat32_lfn.c index 201f871c..d95094d8 100644 --- a/test/test_fat32_lfn.c +++ b/test/test_fat32_lfn.c @@ -28,7 +28,7 @@ static const block_device_ops_t test_disk_ops = { .flush = test_disk_flush, }; -#include "../src/kernel/fs/fat32.c" +#include "../src/kernel/fs/fat32/fat32.c" typedef struct fat32_test_env_t { fat32_fs_t fs; diff --git a/test/test_gpf_error.c b/test/test_gpf_error.c index d972f2cc..229e3e2e 100644 --- a/test/test_gpf_error.c +++ b/test/test_gpf_error.c @@ -1,5 +1,5 @@ #include -#include +#include void setUp(void) {} void tearDown(void) {} diff --git a/test/test_init_supervision.c b/test/test_init_supervision.c index 18b73768..d0d600f6 100644 --- a/test/test_init_supervision.c +++ b/test/test_init_supervision.c @@ -17,7 +17,7 @@ static bool syscalls_ready = false; void setUp(void) { if(!syscalls_ready) { - syscall_init(); + syscall_initialize(); syscalls_ready = true; } diff --git a/test/test_ioctl.c b/test/test_ioctl.c index 50928678..43610912 100644 --- a/test/test_ioctl.c +++ b/test/test_ioctl.c @@ -37,9 +37,9 @@ static int install_tty_fd(void) { void setUp(void) { memset(&proc_state, 0, sizeof(proc_state)); - proc_file_table_init(&proc_state); + proc_file_table_initialize(&proc_state); current = &proc_state; - syscall_init(); + syscall_initialize(); } void tearDown(void) { diff --git a/test/test_kcondvar.c b/test/test_kcondvar.c index d17707d6..1d2efddf 100644 --- a/test/test_kcondvar.c +++ b/test/test_kcondvar.c @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include diff --git a/test/test_kmalloc.c b/test/test_kmalloc.c index 696666c0..b6960a8d 100644 --- a/test/test_kmalloc.c +++ b/test/test_kmalloc.c @@ -15,7 +15,7 @@ void setUp() { if(arena1 == NULL) { TEST_FAIL_MESSAGE("malloc failed"); } - init_heap(arena1, PAGE_SIZE); + heap_initialize(arena1, PAGE_SIZE); } void tearDown() { diff --git a/test/test_pf_error.c b/test/test_pf_error.c index e747ee67..37d4cc76 100644 --- a/test/test_pf_error.c +++ b/test/test_pf_error.c @@ -1,5 +1,5 @@ #include -#include +#include static void check_decode(uint64_t code, bool present, diff --git a/test/test_pipe.c b/test/test_pipe.c index 35c636ef..f5d40649 100644 --- a/test/test_pipe.c +++ b/test/test_pipe.c @@ -61,7 +61,7 @@ void test_pipe_write_after_reader_closed_returns_epipe(void) { void test_pipe_syscall_roundtrip(void) { reset_current(); current = &kernel_process_info; - proc_file_table_init(current); + proc_file_table_initialize(current); int fds[2] = { -1, -1 }; TEST_ASSERT_EQUAL_INT(0, pipe(fds)); diff --git a/test/test_pseudo_stat.c b/test/test_pseudo_stat.c index 3d703851..b7dfe12f 100644 --- a/test/test_pseudo_stat.c +++ b/test/test_pseudo_stat.c @@ -3,11 +3,11 @@ #include #include -#include +#include #include -#include -#include -#include +#include +#include +#include void setUp(void) { vfs_shutdown(); diff --git a/test/test_shm_cleanup.c b/test/test_shm_cleanup.c index c2c25e6a..a9b74c8b 100644 --- a/test/test_shm_cleanup.c +++ b/test/test_shm_cleanup.c @@ -55,7 +55,7 @@ static void init_proc(proc_info_p proc) { } void setUp(void) { - shm_manager_init(); + shm_manager_initialize(); shm_set_allocator(fake_alloc, fake_free); init_proc(&parent_proc); init_proc(&child_proc); diff --git a/test/test_shm_manager.c b/test/test_shm_manager.c index ae269e01..13277c65 100644 --- a/test/test_shm_manager.c +++ b/test/test_shm_manager.c @@ -19,7 +19,7 @@ static void fake_free(phys_addr_t base, size_t page_count) { } void setUp(void) { - shm_manager_init(); + shm_manager_initialize(); shm_set_allocator(fake_alloc, fake_free); } diff --git a/test/test_signal.c b/test/test_signal.c index 5be92d90..35e29422 100644 --- a/test/test_signal.c +++ b/test/test_signal.c @@ -14,8 +14,8 @@ static proc_info_t parent; void setUp(void) { memset(&proc, 0, sizeof(proc)); memset(&parent, 0, sizeof(parent)); - proc_signal_state_init(&proc); - proc_signal_state_init(&parent); + proc_signal_state_initialize(&proc); + proc_signal_state_initialize(&parent); proc.user_mode = true; parent.user_mode = true; proc.quantum_us = 1000; @@ -224,7 +224,7 @@ void test_signal_handle_pending_default_continues_process(void) { void test_proc_waitpid_reports_stop_status(void) { proc_info_t child; memset(&child, 0, sizeof(child)); - proc_signal_state_init(&child); + proc_signal_state_initialize(&child); child.pid = 1234; child.state = PROC_STATE_STOPPED; child.stop_status = ((SIGTSTP & 0x7f) << 8) | 0x7f; @@ -245,7 +245,7 @@ void test_proc_waitpid_reports_stop_status(void) { void test_proc_waitpid_reports_continued_status(void) { proc_info_t child; memset(&child, 0, sizeof(child)); - proc_signal_state_init(&child); + proc_signal_state_initialize(&child); child.pid = 4321; child.state = PROC_STATE_READY; child.continue_status = 0xffff; diff --git a/test/test_signal_syscalls.c b/test/test_signal_syscalls.c index 72f57efa..ac33a06b 100644 --- a/test/test_signal_syscalls.c +++ b/test/test_signal_syscalls.c @@ -20,8 +20,8 @@ void setUp(void) { init_proc.pid = 1; child_proc.pid = 42; - proc_signal_state_init(&init_proc); - proc_signal_state_init(&child_proc); + proc_signal_state_initialize(&init_proc); + proc_signal_state_initialize(&child_proc); procs[0] = &kernel_process_info; procs[1] = &init_proc; @@ -29,7 +29,7 @@ void setUp(void) { current = &init_proc; - syscall_init(); + syscall_initialize(); } void tearDown(void) { diff --git a/test/test_syscall_alarm.c b/test/test_syscall_alarm.c index 61cd1eb0..ff48a4a6 100644 --- a/test/test_syscall_alarm.c +++ b/test/test_syscall_alarm.c @@ -26,13 +26,13 @@ void setUp(void) { init_proc.user_mode = true; init_proc.quantum_us = 1000; - proc_signal_state_init(&init_proc); + proc_signal_state_initialize(&init_proc); current = &init_proc; procs[0] = &kernel_process_info; procs[1] = &init_proc; - syscall_init(); + syscall_initialize(); } void tearDown(void) { diff --git a/test/test_syscall_cwd.c b/test/test_syscall_cwd.c index acc9ba25..80fd3e9f 100644 --- a/test/test_syscall_cwd.c +++ b/test/test_syscall_cwd.c @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include @@ -124,8 +124,8 @@ void setUp(void) { test_proc.cpu_state = &cpu_state; current = &test_proc; - syscall_init(); - vfs_init(); + syscall_initialize(); + vfs_initialize(); bool mounted = vfs_mount("/", &stub_driver, &fs_state, true); TEST_ASSERT_TRUE_MESSAGE(mounted, "failed to mount stub fs"); } diff --git a/test/test_syscall_finalize.c b/test/test_syscall_finalize.c index a0c45d47..8026f3f9 100644 --- a/test/test_syscall_finalize.c +++ b/test/test_syscall_finalize.c @@ -16,7 +16,7 @@ static int init_done = 0; void setUp(void) { if(!init_done) { - syscall_init(); + syscall_initialize(); init_done = 1; } diff --git a/test/test_syscall_getpagesize.c b/test/test_syscall_getpagesize.c index b4e776c7..1cf00889 100644 --- a/test/test_syscall_getpagesize.c +++ b/test/test_syscall_getpagesize.c @@ -5,7 +5,7 @@ #include void setUp(void) { - syscall_init(); + syscall_initialize(); } void tearDown(void) {} diff --git a/test/test_syscall_open.c b/test/test_syscall_open.c index 34e2bdf3..f830c595 100644 --- a/test/test_syscall_open.c +++ b/test/test_syscall_open.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include extern proc_info_p current; @@ -95,7 +95,7 @@ static cpu_state_t cpu_state; void setUp(void) { memset(&proc_state, 0, sizeof(proc_state)); - proc_file_table_init(&proc_state); + proc_file_table_initialize(&proc_state); proc_state.cwd[0] = '/'; proc_state.cwd[1] = '\0'; proc_state.cwd_len = 1; @@ -104,7 +104,7 @@ void setUp(void) { current = &proc_state; vfs_shutdown(); TEST_ASSERT_TRUE(vfs_mount_root(&fake_driver, NULL, true)); - syscall_init(); + syscall_initialize(); } void tearDown(void) { diff --git a/test/test_syscall_shm.c b/test/test_syscall_shm.c index f699d3b1..1df96529 100644 --- a/test/test_syscall_shm.c +++ b/test/test_syscall_shm.c @@ -31,9 +31,9 @@ static void host_free_pages(phys_addr_t base, size_t page_count) { } void setUp(void) { - shm_manager_init(); + shm_manager_initialize(); shm_set_allocator(host_alloc_pages, host_free_pages); - syscall_init(); + syscall_initialize(); old_current = current; memset(&proc, 0, sizeof(proc)); diff --git a/test/test_syscall_shutdown.c b/test/test_syscall_shutdown.c index 1f86bef4..d0f4dcac 100644 --- a/test/test_syscall_shutdown.c +++ b/test/test_syscall_shutdown.c @@ -28,7 +28,7 @@ void setUp(void) { current = &proc_state; test_stub_acpi_shutdown_calls = 0; test_stub_acpi_shutdown_result = 0; - syscall_init(); + syscall_initialize(); } void tearDown(void) { diff --git a/test/test_tmpfs.c b/test/test_tmpfs.c index b8928848..cea14777 100644 --- a/test/test_tmpfs.c +++ b/test/test_tmpfs.c @@ -1,7 +1,7 @@ #include -#include -#include +#include +#include #include #include #include @@ -12,7 +12,7 @@ void setUp(void) {} void tearDown(void) {} static void mount_tmpfs(void) { - TEST_ASSERT_TRUE(vfs_init()); + TEST_ASSERT_TRUE(vfs_initialize()); TEST_ASSERT_TRUE(tmpfs_mount()); } diff --git a/test/test_vfs_open.c b/test/test_vfs_open.c index 93d1929d..72aa3fa3 100644 --- a/test/test_vfs_open.c +++ b/test/test_vfs_open.c @@ -9,7 +9,7 @@ #include #include -#include +#include typedef struct fake_file_entry_t { const char* path; diff --git a/test/test_vfs_open_create.c b/test/test_vfs_open_create.c index 0e832060..1ff738c8 100644 --- a/test/test_vfs_open_create.c +++ b/test/test_vfs_open_create.c @@ -9,7 +9,7 @@ #include #include -#include +#include typedef struct fake_file_t { const char* path; diff --git a/app/doom/LICENSE b/vendor/genericdoom/LICENSE similarity index 100% rename from app/doom/LICENSE rename to vendor/genericdoom/LICENSE diff --git a/app/doom/Makefile b/vendor/genericdoom/Makefile similarity index 100% rename from app/doom/Makefile rename to vendor/genericdoom/Makefile diff --git a/app/doom/Makefile.djgpp b/vendor/genericdoom/Makefile.djgpp similarity index 100% rename from app/doom/Makefile.djgpp rename to vendor/genericdoom/Makefile.djgpp diff --git a/app/doom/Makefile.emscripten b/vendor/genericdoom/Makefile.emscripten similarity index 100% rename from app/doom/Makefile.emscripten rename to vendor/genericdoom/Makefile.emscripten diff --git a/app/doom/Makefile.freebsd b/vendor/genericdoom/Makefile.freebsd similarity index 100% rename from app/doom/Makefile.freebsd rename to vendor/genericdoom/Makefile.freebsd diff --git a/app/doom/Makefile.linuxvt b/vendor/genericdoom/Makefile.linuxvt similarity index 100% rename from app/doom/Makefile.linuxvt rename to vendor/genericdoom/Makefile.linuxvt diff --git a/app/doom/Makefile.menios b/vendor/genericdoom/Makefile.menios similarity index 100% rename from app/doom/Makefile.menios rename to vendor/genericdoom/Makefile.menios diff --git a/app/doom/Makefile.sdl b/vendor/genericdoom/Makefile.sdl similarity index 100% rename from app/doom/Makefile.sdl rename to vendor/genericdoom/Makefile.sdl diff --git a/app/doom/Makefile.soso b/vendor/genericdoom/Makefile.soso similarity index 100% rename from app/doom/Makefile.soso rename to vendor/genericdoom/Makefile.soso diff --git a/app/doom/Makefile.sosox b/vendor/genericdoom/Makefile.sosox similarity index 100% rename from app/doom/Makefile.sosox rename to vendor/genericdoom/Makefile.sosox diff --git a/app/doom/README.TXT b/vendor/genericdoom/README.TXT similarity index 100% rename from app/doom/README.TXT rename to vendor/genericdoom/README.TXT diff --git a/app/doom/README.md b/vendor/genericdoom/README.md similarity index 100% rename from app/doom/README.md rename to vendor/genericdoom/README.md diff --git a/app/doom/am_map.c b/vendor/genericdoom/am_map.c similarity index 100% rename from app/doom/am_map.c rename to vendor/genericdoom/am_map.c diff --git a/app/doom/am_map.h b/vendor/genericdoom/am_map.h similarity index 100% rename from app/doom/am_map.h rename to vendor/genericdoom/am_map.h diff --git a/app/doom/config.h b/vendor/genericdoom/config.h similarity index 100% rename from app/doom/config.h rename to vendor/genericdoom/config.h diff --git a/app/doom/d_englsh.h b/vendor/genericdoom/d_englsh.h similarity index 100% rename from app/doom/d_englsh.h rename to vendor/genericdoom/d_englsh.h diff --git a/app/doom/d_event.c b/vendor/genericdoom/d_event.c similarity index 100% rename from app/doom/d_event.c rename to vendor/genericdoom/d_event.c diff --git a/app/doom/d_event.h b/vendor/genericdoom/d_event.h similarity index 100% rename from app/doom/d_event.h rename to vendor/genericdoom/d_event.h diff --git a/app/doom/d_items.c b/vendor/genericdoom/d_items.c similarity index 100% rename from app/doom/d_items.c rename to vendor/genericdoom/d_items.c diff --git a/app/doom/d_items.h b/vendor/genericdoom/d_items.h similarity index 100% rename from app/doom/d_items.h rename to vendor/genericdoom/d_items.h diff --git a/app/doom/d_iwad.c b/vendor/genericdoom/d_iwad.c similarity index 100% rename from app/doom/d_iwad.c rename to vendor/genericdoom/d_iwad.c diff --git a/app/doom/d_iwad.h b/vendor/genericdoom/d_iwad.h similarity index 100% rename from app/doom/d_iwad.h rename to vendor/genericdoom/d_iwad.h diff --git a/app/doom/d_loop.c b/vendor/genericdoom/d_loop.c similarity index 100% rename from app/doom/d_loop.c rename to vendor/genericdoom/d_loop.c diff --git a/app/doom/d_loop.h b/vendor/genericdoom/d_loop.h similarity index 100% rename from app/doom/d_loop.h rename to vendor/genericdoom/d_loop.h diff --git a/app/doom/d_main.c b/vendor/genericdoom/d_main.c similarity index 100% rename from app/doom/d_main.c rename to vendor/genericdoom/d_main.c diff --git a/app/doom/d_main.h b/vendor/genericdoom/d_main.h similarity index 100% rename from app/doom/d_main.h rename to vendor/genericdoom/d_main.h diff --git a/app/doom/d_mode.c b/vendor/genericdoom/d_mode.c similarity index 100% rename from app/doom/d_mode.c rename to vendor/genericdoom/d_mode.c diff --git a/app/doom/d_mode.h b/vendor/genericdoom/d_mode.h similarity index 100% rename from app/doom/d_mode.h rename to vendor/genericdoom/d_mode.h diff --git a/app/doom/d_net.c b/vendor/genericdoom/d_net.c similarity index 100% rename from app/doom/d_net.c rename to vendor/genericdoom/d_net.c diff --git a/app/doom/d_player.h b/vendor/genericdoom/d_player.h similarity index 100% rename from app/doom/d_player.h rename to vendor/genericdoom/d_player.h diff --git a/app/doom/d_textur.h b/vendor/genericdoom/d_textur.h similarity index 100% rename from app/doom/d_textur.h rename to vendor/genericdoom/d_textur.h diff --git a/app/doom/d_think.h b/vendor/genericdoom/d_think.h similarity index 100% rename from app/doom/d_think.h rename to vendor/genericdoom/d_think.h diff --git a/app/doom/d_ticcmd.h b/vendor/genericdoom/d_ticcmd.h similarity index 100% rename from app/doom/d_ticcmd.h rename to vendor/genericdoom/d_ticcmd.h diff --git a/app/doom/deh_main.h b/vendor/genericdoom/deh_main.h similarity index 100% rename from app/doom/deh_main.h rename to vendor/genericdoom/deh_main.h diff --git a/app/doom/deh_misc.h b/vendor/genericdoom/deh_misc.h similarity index 100% rename from app/doom/deh_misc.h rename to vendor/genericdoom/deh_misc.h diff --git a/app/doom/deh_str.h b/vendor/genericdoom/deh_str.h similarity index 100% rename from app/doom/deh_str.h rename to vendor/genericdoom/deh_str.h diff --git a/app/doom/doom.h b/vendor/genericdoom/doom.h similarity index 100% rename from app/doom/doom.h rename to vendor/genericdoom/doom.h diff --git a/app/doom/doomdata.h b/vendor/genericdoom/doomdata.h similarity index 100% rename from app/doom/doomdata.h rename to vendor/genericdoom/doomdata.h diff --git a/app/doom/doomdef.c b/vendor/genericdoom/doomdef.c similarity index 100% rename from app/doom/doomdef.c rename to vendor/genericdoom/doomdef.c diff --git a/app/doom/doomdef.h b/vendor/genericdoom/doomdef.h similarity index 100% rename from app/doom/doomdef.h rename to vendor/genericdoom/doomdef.h diff --git a/app/doom/doomfeatures.h b/vendor/genericdoom/doomfeatures.h similarity index 100% rename from app/doom/doomfeatures.h rename to vendor/genericdoom/doomfeatures.h diff --git a/app/doom/doomgeneric.c b/vendor/genericdoom/doomgeneric.c similarity index 100% rename from app/doom/doomgeneric.c rename to vendor/genericdoom/doomgeneric.c diff --git a/app/doom/doomgeneric.h b/vendor/genericdoom/doomgeneric.h similarity index 100% rename from app/doom/doomgeneric.h rename to vendor/genericdoom/doomgeneric.h diff --git a/app/doom/doomgeneric.vcxproj b/vendor/genericdoom/doomgeneric.vcxproj similarity index 100% rename from app/doom/doomgeneric.vcxproj rename to vendor/genericdoom/doomgeneric.vcxproj diff --git a/app/doom/doomgeneric.vcxproj.filters b/vendor/genericdoom/doomgeneric.vcxproj.filters similarity index 100% rename from app/doom/doomgeneric.vcxproj.filters rename to vendor/genericdoom/doomgeneric.vcxproj.filters diff --git a/app/doom/doomgeneric_allegro.c b/vendor/genericdoom/doomgeneric_allegro.c similarity index 100% rename from app/doom/doomgeneric_allegro.c rename to vendor/genericdoom/doomgeneric_allegro.c diff --git a/app/doom/doomgeneric_emscripten.c b/vendor/genericdoom/doomgeneric_emscripten.c similarity index 100% rename from app/doom/doomgeneric_emscripten.c rename to vendor/genericdoom/doomgeneric_emscripten.c diff --git a/app/doom/doomgeneric_linuxvt.c b/vendor/genericdoom/doomgeneric_linuxvt.c similarity index 100% rename from app/doom/doomgeneric_linuxvt.c rename to vendor/genericdoom/doomgeneric_linuxvt.c diff --git a/app/doom/doomgeneric_menios.c b/vendor/genericdoom/doomgeneric_menios.c similarity index 100% rename from app/doom/doomgeneric_menios.c rename to vendor/genericdoom/doomgeneric_menios.c diff --git a/app/doom/doomgeneric_sdl.c b/vendor/genericdoom/doomgeneric_sdl.c similarity index 100% rename from app/doom/doomgeneric_sdl.c rename to vendor/genericdoom/doomgeneric_sdl.c diff --git a/app/doom/doomgeneric_soso.c b/vendor/genericdoom/doomgeneric_soso.c similarity index 100% rename from app/doom/doomgeneric_soso.c rename to vendor/genericdoom/doomgeneric_soso.c diff --git a/app/doom/doomgeneric_sosox.c b/vendor/genericdoom/doomgeneric_sosox.c similarity index 100% rename from app/doom/doomgeneric_sosox.c rename to vendor/genericdoom/doomgeneric_sosox.c diff --git a/app/doom/doomgeneric_win.c b/vendor/genericdoom/doomgeneric_win.c similarity index 100% rename from app/doom/doomgeneric_win.c rename to vendor/genericdoom/doomgeneric_win.c diff --git a/app/doom/doomgeneric_xlib.c b/vendor/genericdoom/doomgeneric_xlib.c similarity index 100% rename from app/doom/doomgeneric_xlib.c rename to vendor/genericdoom/doomgeneric_xlib.c diff --git a/app/doom/doomkeys.h b/vendor/genericdoom/doomkeys.h similarity index 100% rename from app/doom/doomkeys.h rename to vendor/genericdoom/doomkeys.h diff --git a/app/doom/doomstat.c b/vendor/genericdoom/doomstat.c similarity index 100% rename from app/doom/doomstat.c rename to vendor/genericdoom/doomstat.c diff --git a/app/doom/doomstat.h b/vendor/genericdoom/doomstat.h similarity index 100% rename from app/doom/doomstat.h rename to vendor/genericdoom/doomstat.h diff --git a/app/doom/doomtype.h b/vendor/genericdoom/doomtype.h similarity index 100% rename from app/doom/doomtype.h rename to vendor/genericdoom/doomtype.h diff --git a/app/doom/dstrings.c b/vendor/genericdoom/dstrings.c similarity index 100% rename from app/doom/dstrings.c rename to vendor/genericdoom/dstrings.c diff --git a/app/doom/dstrings.h b/vendor/genericdoom/dstrings.h similarity index 100% rename from app/doom/dstrings.h rename to vendor/genericdoom/dstrings.h diff --git a/app/doom/dummy.c b/vendor/genericdoom/dummy.c similarity index 100% rename from app/doom/dummy.c rename to vendor/genericdoom/dummy.c diff --git a/app/doom/f_finale.c b/vendor/genericdoom/f_finale.c similarity index 100% rename from app/doom/f_finale.c rename to vendor/genericdoom/f_finale.c diff --git a/app/doom/f_finale.h b/vendor/genericdoom/f_finale.h similarity index 100% rename from app/doom/f_finale.h rename to vendor/genericdoom/f_finale.h diff --git a/app/doom/f_wipe.c b/vendor/genericdoom/f_wipe.c similarity index 100% rename from app/doom/f_wipe.c rename to vendor/genericdoom/f_wipe.c diff --git a/app/doom/f_wipe.h b/vendor/genericdoom/f_wipe.h similarity index 100% rename from app/doom/f_wipe.h rename to vendor/genericdoom/f_wipe.h diff --git a/app/doom/g_game.c b/vendor/genericdoom/g_game.c similarity index 100% rename from app/doom/g_game.c rename to vendor/genericdoom/g_game.c diff --git a/app/doom/g_game.h b/vendor/genericdoom/g_game.h similarity index 100% rename from app/doom/g_game.h rename to vendor/genericdoom/g_game.h diff --git a/app/doom/gusconf.c b/vendor/genericdoom/gusconf.c similarity index 100% rename from app/doom/gusconf.c rename to vendor/genericdoom/gusconf.c diff --git a/app/doom/gusconf.h b/vendor/genericdoom/gusconf.h similarity index 100% rename from app/doom/gusconf.h rename to vendor/genericdoom/gusconf.h diff --git a/app/doom/hu_lib.c b/vendor/genericdoom/hu_lib.c similarity index 100% rename from app/doom/hu_lib.c rename to vendor/genericdoom/hu_lib.c diff --git a/app/doom/hu_lib.h b/vendor/genericdoom/hu_lib.h similarity index 100% rename from app/doom/hu_lib.h rename to vendor/genericdoom/hu_lib.h diff --git a/app/doom/hu_stuff.c b/vendor/genericdoom/hu_stuff.c similarity index 100% rename from app/doom/hu_stuff.c rename to vendor/genericdoom/hu_stuff.c diff --git a/app/doom/hu_stuff.h b/vendor/genericdoom/hu_stuff.h similarity index 100% rename from app/doom/hu_stuff.h rename to vendor/genericdoom/hu_stuff.h diff --git a/app/doom/i_allegromusic.c b/vendor/genericdoom/i_allegromusic.c similarity index 100% rename from app/doom/i_allegromusic.c rename to vendor/genericdoom/i_allegromusic.c diff --git a/app/doom/i_allegrosound.c b/vendor/genericdoom/i_allegrosound.c similarity index 100% rename from app/doom/i_allegrosound.c rename to vendor/genericdoom/i_allegrosound.c diff --git a/app/doom/i_cdmus.c b/vendor/genericdoom/i_cdmus.c similarity index 100% rename from app/doom/i_cdmus.c rename to vendor/genericdoom/i_cdmus.c diff --git a/app/doom/i_cdmus.h b/vendor/genericdoom/i_cdmus.h similarity index 100% rename from app/doom/i_cdmus.h rename to vendor/genericdoom/i_cdmus.h diff --git a/app/doom/i_endoom.c b/vendor/genericdoom/i_endoom.c similarity index 100% rename from app/doom/i_endoom.c rename to vendor/genericdoom/i_endoom.c diff --git a/app/doom/i_endoom.h b/vendor/genericdoom/i_endoom.h similarity index 100% rename from app/doom/i_endoom.h rename to vendor/genericdoom/i_endoom.h diff --git a/app/doom/i_input.c b/vendor/genericdoom/i_input.c similarity index 100% rename from app/doom/i_input.c rename to vendor/genericdoom/i_input.c diff --git a/app/doom/i_joystick.c b/vendor/genericdoom/i_joystick.c similarity index 100% rename from app/doom/i_joystick.c rename to vendor/genericdoom/i_joystick.c diff --git a/app/doom/i_joystick.h b/vendor/genericdoom/i_joystick.h similarity index 100% rename from app/doom/i_joystick.h rename to vendor/genericdoom/i_joystick.h diff --git a/app/doom/i_scale.c b/vendor/genericdoom/i_scale.c similarity index 100% rename from app/doom/i_scale.c rename to vendor/genericdoom/i_scale.c diff --git a/app/doom/i_scale.h b/vendor/genericdoom/i_scale.h similarity index 100% rename from app/doom/i_scale.h rename to vendor/genericdoom/i_scale.h diff --git a/app/doom/i_sdlmusic.c b/vendor/genericdoom/i_sdlmusic.c similarity index 100% rename from app/doom/i_sdlmusic.c rename to vendor/genericdoom/i_sdlmusic.c diff --git a/app/doom/i_sdlsound.c b/vendor/genericdoom/i_sdlsound.c similarity index 100% rename from app/doom/i_sdlsound.c rename to vendor/genericdoom/i_sdlsound.c diff --git a/app/doom/i_sound.c b/vendor/genericdoom/i_sound.c similarity index 100% rename from app/doom/i_sound.c rename to vendor/genericdoom/i_sound.c diff --git a/app/doom/i_sound.h b/vendor/genericdoom/i_sound.h similarity index 100% rename from app/doom/i_sound.h rename to vendor/genericdoom/i_sound.h diff --git a/app/doom/i_swap.h b/vendor/genericdoom/i_swap.h similarity index 100% rename from app/doom/i_swap.h rename to vendor/genericdoom/i_swap.h diff --git a/app/doom/i_system.c b/vendor/genericdoom/i_system.c similarity index 100% rename from app/doom/i_system.c rename to vendor/genericdoom/i_system.c diff --git a/app/doom/i_system.h b/vendor/genericdoom/i_system.h similarity index 100% rename from app/doom/i_system.h rename to vendor/genericdoom/i_system.h diff --git a/app/doom/i_timer.c b/vendor/genericdoom/i_timer.c similarity index 100% rename from app/doom/i_timer.c rename to vendor/genericdoom/i_timer.c diff --git a/app/doom/i_timer.h b/vendor/genericdoom/i_timer.h similarity index 100% rename from app/doom/i_timer.h rename to vendor/genericdoom/i_timer.h diff --git a/app/doom/i_video.c b/vendor/genericdoom/i_video.c similarity index 100% rename from app/doom/i_video.c rename to vendor/genericdoom/i_video.c diff --git a/app/doom/i_video.h b/vendor/genericdoom/i_video.h similarity index 100% rename from app/doom/i_video.h rename to vendor/genericdoom/i_video.h diff --git a/app/doom/icon.c b/vendor/genericdoom/icon.c similarity index 100% rename from app/doom/icon.c rename to vendor/genericdoom/icon.c diff --git a/app/doom/info.c b/vendor/genericdoom/info.c similarity index 100% rename from app/doom/info.c rename to vendor/genericdoom/info.c diff --git a/app/doom/info.h b/vendor/genericdoom/info.h similarity index 100% rename from app/doom/info.h rename to vendor/genericdoom/info.h diff --git a/app/doom/m_argv.c b/vendor/genericdoom/m_argv.c similarity index 100% rename from app/doom/m_argv.c rename to vendor/genericdoom/m_argv.c diff --git a/app/doom/m_argv.h b/vendor/genericdoom/m_argv.h similarity index 100% rename from app/doom/m_argv.h rename to vendor/genericdoom/m_argv.h diff --git a/app/doom/m_bbox.c b/vendor/genericdoom/m_bbox.c similarity index 100% rename from app/doom/m_bbox.c rename to vendor/genericdoom/m_bbox.c diff --git a/app/doom/m_bbox.h b/vendor/genericdoom/m_bbox.h similarity index 100% rename from app/doom/m_bbox.h rename to vendor/genericdoom/m_bbox.h diff --git a/app/doom/m_cheat.c b/vendor/genericdoom/m_cheat.c similarity index 100% rename from app/doom/m_cheat.c rename to vendor/genericdoom/m_cheat.c diff --git a/app/doom/m_cheat.h b/vendor/genericdoom/m_cheat.h similarity index 100% rename from app/doom/m_cheat.h rename to vendor/genericdoom/m_cheat.h diff --git a/app/doom/m_config.c b/vendor/genericdoom/m_config.c similarity index 100% rename from app/doom/m_config.c rename to vendor/genericdoom/m_config.c diff --git a/app/doom/m_config.h b/vendor/genericdoom/m_config.h similarity index 100% rename from app/doom/m_config.h rename to vendor/genericdoom/m_config.h diff --git a/app/doom/m_controls.c b/vendor/genericdoom/m_controls.c similarity index 100% rename from app/doom/m_controls.c rename to vendor/genericdoom/m_controls.c diff --git a/app/doom/m_controls.h b/vendor/genericdoom/m_controls.h similarity index 100% rename from app/doom/m_controls.h rename to vendor/genericdoom/m_controls.h diff --git a/app/doom/m_fixed.c b/vendor/genericdoom/m_fixed.c similarity index 100% rename from app/doom/m_fixed.c rename to vendor/genericdoom/m_fixed.c diff --git a/app/doom/m_fixed.h b/vendor/genericdoom/m_fixed.h similarity index 100% rename from app/doom/m_fixed.h rename to vendor/genericdoom/m_fixed.h diff --git a/app/doom/m_menu.c b/vendor/genericdoom/m_menu.c similarity index 100% rename from app/doom/m_menu.c rename to vendor/genericdoom/m_menu.c diff --git a/app/doom/m_menu.h b/vendor/genericdoom/m_menu.h similarity index 100% rename from app/doom/m_menu.h rename to vendor/genericdoom/m_menu.h diff --git a/app/doom/m_misc.c b/vendor/genericdoom/m_misc.c similarity index 100% rename from app/doom/m_misc.c rename to vendor/genericdoom/m_misc.c diff --git a/app/doom/m_misc.h b/vendor/genericdoom/m_misc.h similarity index 100% rename from app/doom/m_misc.h rename to vendor/genericdoom/m_misc.h diff --git a/app/doom/m_random.c b/vendor/genericdoom/m_random.c similarity index 100% rename from app/doom/m_random.c rename to vendor/genericdoom/m_random.c diff --git a/app/doom/m_random.h b/vendor/genericdoom/m_random.h similarity index 100% rename from app/doom/m_random.h rename to vendor/genericdoom/m_random.h diff --git a/app/doom/memio.c b/vendor/genericdoom/memio.c similarity index 100% rename from app/doom/memio.c rename to vendor/genericdoom/memio.c diff --git a/app/doom/memio.h b/vendor/genericdoom/memio.h similarity index 100% rename from app/doom/memio.h rename to vendor/genericdoom/memio.h diff --git a/app/doom/mus2mid.c b/vendor/genericdoom/mus2mid.c similarity index 100% rename from app/doom/mus2mid.c rename to vendor/genericdoom/mus2mid.c diff --git a/app/doom/mus2mid.h b/vendor/genericdoom/mus2mid.h similarity index 100% rename from app/doom/mus2mid.h rename to vendor/genericdoom/mus2mid.h diff --git a/app/doom/net_client.h b/vendor/genericdoom/net_client.h similarity index 100% rename from app/doom/net_client.h rename to vendor/genericdoom/net_client.h diff --git a/app/doom/net_dedicated.h b/vendor/genericdoom/net_dedicated.h similarity index 100% rename from app/doom/net_dedicated.h rename to vendor/genericdoom/net_dedicated.h diff --git a/app/doom/net_defs.h b/vendor/genericdoom/net_defs.h similarity index 100% rename from app/doom/net_defs.h rename to vendor/genericdoom/net_defs.h diff --git a/app/doom/net_gui.h b/vendor/genericdoom/net_gui.h similarity index 100% rename from app/doom/net_gui.h rename to vendor/genericdoom/net_gui.h diff --git a/app/doom/net_io.h b/vendor/genericdoom/net_io.h similarity index 100% rename from app/doom/net_io.h rename to vendor/genericdoom/net_io.h diff --git a/app/doom/net_loop.h b/vendor/genericdoom/net_loop.h similarity index 100% rename from app/doom/net_loop.h rename to vendor/genericdoom/net_loop.h diff --git a/app/doom/net_packet.h b/vendor/genericdoom/net_packet.h similarity index 100% rename from app/doom/net_packet.h rename to vendor/genericdoom/net_packet.h diff --git a/app/doom/net_query.h b/vendor/genericdoom/net_query.h similarity index 100% rename from app/doom/net_query.h rename to vendor/genericdoom/net_query.h diff --git a/app/doom/net_sdl.h b/vendor/genericdoom/net_sdl.h similarity index 100% rename from app/doom/net_sdl.h rename to vendor/genericdoom/net_sdl.h diff --git a/app/doom/net_server.h b/vendor/genericdoom/net_server.h similarity index 100% rename from app/doom/net_server.h rename to vendor/genericdoom/net_server.h diff --git a/app/doom/p_ceilng.c b/vendor/genericdoom/p_ceilng.c similarity index 100% rename from app/doom/p_ceilng.c rename to vendor/genericdoom/p_ceilng.c diff --git a/app/doom/p_doors.c b/vendor/genericdoom/p_doors.c similarity index 100% rename from app/doom/p_doors.c rename to vendor/genericdoom/p_doors.c diff --git a/app/doom/p_enemy.c b/vendor/genericdoom/p_enemy.c similarity index 100% rename from app/doom/p_enemy.c rename to vendor/genericdoom/p_enemy.c diff --git a/app/doom/p_floor.c b/vendor/genericdoom/p_floor.c similarity index 100% rename from app/doom/p_floor.c rename to vendor/genericdoom/p_floor.c diff --git a/app/doom/p_inter.c b/vendor/genericdoom/p_inter.c similarity index 100% rename from app/doom/p_inter.c rename to vendor/genericdoom/p_inter.c diff --git a/app/doom/p_inter.h b/vendor/genericdoom/p_inter.h similarity index 100% rename from app/doom/p_inter.h rename to vendor/genericdoom/p_inter.h diff --git a/app/doom/p_lights.c b/vendor/genericdoom/p_lights.c similarity index 100% rename from app/doom/p_lights.c rename to vendor/genericdoom/p_lights.c diff --git a/app/doom/p_local.h b/vendor/genericdoom/p_local.h similarity index 100% rename from app/doom/p_local.h rename to vendor/genericdoom/p_local.h diff --git a/app/doom/p_map.c b/vendor/genericdoom/p_map.c similarity index 100% rename from app/doom/p_map.c rename to vendor/genericdoom/p_map.c diff --git a/app/doom/p_maputl.c b/vendor/genericdoom/p_maputl.c similarity index 100% rename from app/doom/p_maputl.c rename to vendor/genericdoom/p_maputl.c diff --git a/app/doom/p_mobj.c b/vendor/genericdoom/p_mobj.c similarity index 100% rename from app/doom/p_mobj.c rename to vendor/genericdoom/p_mobj.c diff --git a/app/doom/p_mobj.h b/vendor/genericdoom/p_mobj.h similarity index 100% rename from app/doom/p_mobj.h rename to vendor/genericdoom/p_mobj.h diff --git a/app/doom/p_plats.c b/vendor/genericdoom/p_plats.c similarity index 100% rename from app/doom/p_plats.c rename to vendor/genericdoom/p_plats.c diff --git a/app/doom/p_pspr.c b/vendor/genericdoom/p_pspr.c similarity index 100% rename from app/doom/p_pspr.c rename to vendor/genericdoom/p_pspr.c diff --git a/app/doom/p_pspr.h b/vendor/genericdoom/p_pspr.h similarity index 100% rename from app/doom/p_pspr.h rename to vendor/genericdoom/p_pspr.h diff --git a/app/doom/p_saveg.c b/vendor/genericdoom/p_saveg.c similarity index 100% rename from app/doom/p_saveg.c rename to vendor/genericdoom/p_saveg.c diff --git a/app/doom/p_saveg.h b/vendor/genericdoom/p_saveg.h similarity index 100% rename from app/doom/p_saveg.h rename to vendor/genericdoom/p_saveg.h diff --git a/app/doom/p_setup.c b/vendor/genericdoom/p_setup.c similarity index 100% rename from app/doom/p_setup.c rename to vendor/genericdoom/p_setup.c diff --git a/app/doom/p_setup.h b/vendor/genericdoom/p_setup.h similarity index 100% rename from app/doom/p_setup.h rename to vendor/genericdoom/p_setup.h diff --git a/app/doom/p_sight.c b/vendor/genericdoom/p_sight.c similarity index 100% rename from app/doom/p_sight.c rename to vendor/genericdoom/p_sight.c diff --git a/app/doom/p_spec.c b/vendor/genericdoom/p_spec.c similarity index 100% rename from app/doom/p_spec.c rename to vendor/genericdoom/p_spec.c diff --git a/app/doom/p_spec.h b/vendor/genericdoom/p_spec.h similarity index 100% rename from app/doom/p_spec.h rename to vendor/genericdoom/p_spec.h diff --git a/app/doom/p_switch.c b/vendor/genericdoom/p_switch.c similarity index 100% rename from app/doom/p_switch.c rename to vendor/genericdoom/p_switch.c diff --git a/app/doom/p_telept.c b/vendor/genericdoom/p_telept.c similarity index 100% rename from app/doom/p_telept.c rename to vendor/genericdoom/p_telept.c diff --git a/app/doom/p_tick.c b/vendor/genericdoom/p_tick.c similarity index 100% rename from app/doom/p_tick.c rename to vendor/genericdoom/p_tick.c diff --git a/app/doom/p_tick.h b/vendor/genericdoom/p_tick.h similarity index 100% rename from app/doom/p_tick.h rename to vendor/genericdoom/p_tick.h diff --git a/app/doom/p_user.c b/vendor/genericdoom/p_user.c similarity index 100% rename from app/doom/p_user.c rename to vendor/genericdoom/p_user.c diff --git a/app/doom/r_bsp.c b/vendor/genericdoom/r_bsp.c similarity index 100% rename from app/doom/r_bsp.c rename to vendor/genericdoom/r_bsp.c diff --git a/app/doom/r_bsp.h b/vendor/genericdoom/r_bsp.h similarity index 100% rename from app/doom/r_bsp.h rename to vendor/genericdoom/r_bsp.h diff --git a/app/doom/r_data.c b/vendor/genericdoom/r_data.c similarity index 100% rename from app/doom/r_data.c rename to vendor/genericdoom/r_data.c diff --git a/app/doom/r_data.h b/vendor/genericdoom/r_data.h similarity index 100% rename from app/doom/r_data.h rename to vendor/genericdoom/r_data.h diff --git a/app/doom/r_defs.h b/vendor/genericdoom/r_defs.h similarity index 100% rename from app/doom/r_defs.h rename to vendor/genericdoom/r_defs.h diff --git a/app/doom/r_draw.c b/vendor/genericdoom/r_draw.c similarity index 100% rename from app/doom/r_draw.c rename to vendor/genericdoom/r_draw.c diff --git a/app/doom/r_draw.h b/vendor/genericdoom/r_draw.h similarity index 100% rename from app/doom/r_draw.h rename to vendor/genericdoom/r_draw.h diff --git a/app/doom/r_local.h b/vendor/genericdoom/r_local.h similarity index 100% rename from app/doom/r_local.h rename to vendor/genericdoom/r_local.h diff --git a/app/doom/r_main.c b/vendor/genericdoom/r_main.c similarity index 100% rename from app/doom/r_main.c rename to vendor/genericdoom/r_main.c diff --git a/app/doom/r_main.h b/vendor/genericdoom/r_main.h similarity index 100% rename from app/doom/r_main.h rename to vendor/genericdoom/r_main.h diff --git a/app/doom/r_plane.c b/vendor/genericdoom/r_plane.c similarity index 100% rename from app/doom/r_plane.c rename to vendor/genericdoom/r_plane.c diff --git a/app/doom/r_plane.h b/vendor/genericdoom/r_plane.h similarity index 100% rename from app/doom/r_plane.h rename to vendor/genericdoom/r_plane.h diff --git a/app/doom/r_segs.c b/vendor/genericdoom/r_segs.c similarity index 100% rename from app/doom/r_segs.c rename to vendor/genericdoom/r_segs.c diff --git a/app/doom/r_segs.h b/vendor/genericdoom/r_segs.h similarity index 100% rename from app/doom/r_segs.h rename to vendor/genericdoom/r_segs.h diff --git a/app/doom/r_sky.c b/vendor/genericdoom/r_sky.c similarity index 100% rename from app/doom/r_sky.c rename to vendor/genericdoom/r_sky.c diff --git a/app/doom/r_sky.h b/vendor/genericdoom/r_sky.h similarity index 100% rename from app/doom/r_sky.h rename to vendor/genericdoom/r_sky.h diff --git a/app/doom/r_state.h b/vendor/genericdoom/r_state.h similarity index 100% rename from app/doom/r_state.h rename to vendor/genericdoom/r_state.h diff --git a/app/doom/r_things.c b/vendor/genericdoom/r_things.c similarity index 100% rename from app/doom/r_things.c rename to vendor/genericdoom/r_things.c diff --git a/app/doom/r_things.h b/vendor/genericdoom/r_things.h similarity index 100% rename from app/doom/r_things.h rename to vendor/genericdoom/r_things.h diff --git a/app/doom/s_sound.c b/vendor/genericdoom/s_sound.c similarity index 100% rename from app/doom/s_sound.c rename to vendor/genericdoom/s_sound.c diff --git a/app/doom/s_sound.h b/vendor/genericdoom/s_sound.h similarity index 100% rename from app/doom/s_sound.h rename to vendor/genericdoom/s_sound.h diff --git a/app/doom/sha1.c b/vendor/genericdoom/sha1.c similarity index 100% rename from app/doom/sha1.c rename to vendor/genericdoom/sha1.c diff --git a/app/doom/sha1.h b/vendor/genericdoom/sha1.h similarity index 100% rename from app/doom/sha1.h rename to vendor/genericdoom/sha1.h diff --git a/app/doom/sounds.c b/vendor/genericdoom/sounds.c similarity index 100% rename from app/doom/sounds.c rename to vendor/genericdoom/sounds.c diff --git a/app/doom/sounds.h b/vendor/genericdoom/sounds.h similarity index 100% rename from app/doom/sounds.h rename to vendor/genericdoom/sounds.h diff --git a/app/doom/st_lib.c b/vendor/genericdoom/st_lib.c similarity index 100% rename from app/doom/st_lib.c rename to vendor/genericdoom/st_lib.c diff --git a/app/doom/st_lib.h b/vendor/genericdoom/st_lib.h similarity index 100% rename from app/doom/st_lib.h rename to vendor/genericdoom/st_lib.h diff --git a/app/doom/st_stuff.c b/vendor/genericdoom/st_stuff.c similarity index 100% rename from app/doom/st_stuff.c rename to vendor/genericdoom/st_stuff.c diff --git a/app/doom/st_stuff.h b/vendor/genericdoom/st_stuff.h similarity index 100% rename from app/doom/st_stuff.h rename to vendor/genericdoom/st_stuff.h diff --git a/app/doom/statdump.c b/vendor/genericdoom/statdump.c similarity index 100% rename from app/doom/statdump.c rename to vendor/genericdoom/statdump.c diff --git a/app/doom/statdump.h b/vendor/genericdoom/statdump.h similarity index 100% rename from app/doom/statdump.h rename to vendor/genericdoom/statdump.h diff --git a/app/doom/tables.c b/vendor/genericdoom/tables.c similarity index 100% rename from app/doom/tables.c rename to vendor/genericdoom/tables.c diff --git a/app/doom/tables.h b/vendor/genericdoom/tables.h similarity index 100% rename from app/doom/tables.h rename to vendor/genericdoom/tables.h diff --git a/app/doom/v_patch.h b/vendor/genericdoom/v_patch.h similarity index 100% rename from app/doom/v_patch.h rename to vendor/genericdoom/v_patch.h diff --git a/app/doom/v_video.c b/vendor/genericdoom/v_video.c similarity index 100% rename from app/doom/v_video.c rename to vendor/genericdoom/v_video.c diff --git a/app/doom/v_video.h b/vendor/genericdoom/v_video.h similarity index 100% rename from app/doom/v_video.h rename to vendor/genericdoom/v_video.h diff --git a/app/doom/w_checksum.c b/vendor/genericdoom/w_checksum.c similarity index 100% rename from app/doom/w_checksum.c rename to vendor/genericdoom/w_checksum.c diff --git a/app/doom/w_checksum.h b/vendor/genericdoom/w_checksum.h similarity index 100% rename from app/doom/w_checksum.h rename to vendor/genericdoom/w_checksum.h diff --git a/app/doom/w_file.c b/vendor/genericdoom/w_file.c similarity index 100% rename from app/doom/w_file.c rename to vendor/genericdoom/w_file.c diff --git a/app/doom/w_file.h b/vendor/genericdoom/w_file.h similarity index 100% rename from app/doom/w_file.h rename to vendor/genericdoom/w_file.h diff --git a/app/doom/w_file_stdc.c b/vendor/genericdoom/w_file_stdc.c similarity index 100% rename from app/doom/w_file_stdc.c rename to vendor/genericdoom/w_file_stdc.c diff --git a/app/doom/w_main.c b/vendor/genericdoom/w_main.c similarity index 100% rename from app/doom/w_main.c rename to vendor/genericdoom/w_main.c diff --git a/app/doom/w_main.h b/vendor/genericdoom/w_main.h similarity index 100% rename from app/doom/w_main.h rename to vendor/genericdoom/w_main.h diff --git a/app/doom/w_merge.h b/vendor/genericdoom/w_merge.h similarity index 100% rename from app/doom/w_merge.h rename to vendor/genericdoom/w_merge.h diff --git a/app/doom/w_wad.c b/vendor/genericdoom/w_wad.c similarity index 100% rename from app/doom/w_wad.c rename to vendor/genericdoom/w_wad.c diff --git a/app/doom/w_wad.h b/vendor/genericdoom/w_wad.h similarity index 100% rename from app/doom/w_wad.h rename to vendor/genericdoom/w_wad.h diff --git a/app/doom/wi_stuff.c b/vendor/genericdoom/wi_stuff.c similarity index 100% rename from app/doom/wi_stuff.c rename to vendor/genericdoom/wi_stuff.c diff --git a/app/doom/wi_stuff.h b/vendor/genericdoom/wi_stuff.h similarity index 100% rename from app/doom/wi_stuff.h rename to vendor/genericdoom/wi_stuff.h diff --git a/app/doom/z_zone.c b/vendor/genericdoom/z_zone.c similarity index 100% rename from app/doom/z_zone.c rename to vendor/genericdoom/z_zone.c diff --git a/app/doom/z_zone.h b/vendor/genericdoom/z_zone.h similarity index 100% rename from app/doom/z_zone.h rename to vendor/genericdoom/z_zone.h