Compare commits

..

No commits in common. "main" and "26.5.1" have entirely different histories.
main ... 26.5.1

750 changed files with 3304 additions and 160553 deletions

View file

@ -1,31 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**1. System/Environment:**
* Are you running on QEMU, VirtualBox, VMware, or Bare Metal?
* What OS are you compiling from? (e.g., Ubuntu 22.04, Windows/WSL2)
* Which branch/commit of BoredOS are you on?
**2. Describe the Bug:**
A clear and concise description of what the bug is. What did you expect to happen, and what actually happened?
**3. Steps to Reproduce:**
1. Boot the OS using 'make run'
2. Open application 'X'
3. Click on 'Y'
4. See error...
**4. Logs and Screenshots:**
* Please attach screenshots of the kernel panic, GUI glitch, or terminal output.
* If you have serial logs, attach them as a `.txt` file or use a code block. Do NOT paste 500 lines of logs directly into the chat!
**5. Additional Context:**
Add any other context about the problem here (e.g., "This only happens when my mouse is moving").
**6. Please add tags to your issue to help with organization.**

View file

@ -1,25 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**1. Is your feature request related to a problem?**
A clear and concise description of what the problem is. (e.g., "I'm always frustrated when I can't save files persistently across reboots...")
**2. Describe the Solution you'd like:**
A clear and concise description of what you want to happen. Are you proposing a new syscall, a new userspace app, or a kernel change?
**3. Describe alternatives you've considered:**
A clear and concise description of any alternative solutions or features you've considered. (e.g., "Instead of ext2, we could just implement FAT32 first.")
**4. Can you help build it?**
Let us know if this is just an idea you'd like to see, or if you plan on submitting a Pull Request for it yourself!
**5. Additional Context:**
Add any other context, mockup screenshots, or links to technical documentation (e.g., OSDev Wiki links) here.
**6. Please add tags to your issue to help with organization.**

View file

@ -1,36 +0,0 @@
## Description
Describe the changes made in this PR.
---
## Testing
- [ ] Code has been tested
- [ ] Existing tests pass
### Platform / Environment
What platform and environment were used for development and testing?
Examples:
- Windows 11 / macOS / Linux
- MSYS2 / WSL2 / Debian
Notes:
<!-- Add anything relevant about testing -->
---
## Documentation
- [ ] Code contains appropriate comments (REQUIRED for medium to large PR's.)
- [ ] Documentation updated if needed
Notes:
<!-- Add anything relevant about documentation -->
---
## Additional Notes
<!-- Anything reviewers should know -->

View file

@ -1,13 +1,13 @@
name: Nightly Build
on:
push:
branches:
- 'main'
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
permissions:
contents: write
jobs:
build-and-release:
runs-on: ubuntu-latest
@ -25,8 +25,7 @@ jobs:
nasm \
xorriso
sudo ln -sf /usr/bin/x86_64-linux-gnu-gcc /usr/local/bin/x86_64-elf-gcc
sudo ln -sf /usr/bin/x86_64-linux-gnu-ld /usr/local/bin/x86_64-elf-ld
sudo ln -sf /usr/bin/x86_64-linux-gnu-ar /usr/local/bin/x86_64-elf-ar
sudo ln -sf /usr/bin/x86_64-linux-gnu-ld /usr/local/bin/x86_64-elf-ld
- name: Build ISO
run: make -j4
@ -50,6 +49,7 @@ jobs:
name: Nightly Build (${{ steps.metadata.outputs.short_sha }})
body: |
This is an automated nightly build of BoredOS, this is not a final release and may be unstable.
Built from commit:
- Full hash: `${{ github.sha }}`
- Short hash: `${{ steps.metadata.outputs.short_sha }}`

45
.gitignore vendored
View file

@ -1,24 +1,33 @@
# Build artifacts
/build/
.git 2/FETCH_HEAD
limine 2/BOOTAA64.EFI
limine 2/BOOTIA32.EFI
limine 2/BOOTRISCV64.EFI
limine 2/BOOTX64.EFI
limine 2/install-sh
limine 2/LICENSE
limine 2/limine-bios-cd.bin
limine 2/limine-bios-hdd.h
limine 2/limine-bios-pxe.bin
limine 2/limine-bios.sys
limine 2/limine-uefi-cd.bin
limine 2/limine.c
limine 2/limine.dSYM/Contents/Info.plist
limine 2/limine.dSYM/Contents/Resources/Relocations/aarch64/limine.yml
limine 2/limine.h
limine 2/Makefile
limine 2/limine
limine 2/limine.dSYM/Contents/Resources/DWARF/limine
limine 2/limine.exe
boredos.dump
qemu-debug.log
iso_root/
limine/
src/userland/bin/
boredos.iso
disk.img
disk.qcow2
edk2-vars.fd
qemu-debug.log
boredos.dump
# Userland
src/userland/bin/
# Temporary files
limine
**/.DS_Store
.DS_Store
/build/
*.o
*.a
# Others
.gitignore
src/userland/cli/third_party/tcc/tcc.elf
src/userland/sdk/include/*
limine
disk.img

0
.gitmodules vendored
View file

368
Makefile
View file

@ -1,9 +1,10 @@
# BoredOS Makefile
# Target Architecture: x86_64
# Host: macOS
# Copyright (c) 2023-2026 Chris (boreddevnl)
# This software is released under the GNU General Public License v3.0. See LICENSE file for details.
# This header needs to maintain in any file it is present in, as per the GPL license terms.
export MAKEFLAGS += -j4
CC = x86_64-elf-gcc
LD = x86_64-elf-ld
NASM = nasm
@ -38,21 +39,37 @@ USERLAND_METADATA_ICONS = $(shell { \
} | tr ';' '\n' | sed 's@.*/@@' | sed '/^[[:space:]]*$$/d' | sort -u)
COLLOID_ICONS = $(sort $(DOCK_COLLOID_ICONS) $(USERLAND_COLLOID_ICONS) $(USERLAND_METADATA_ICONS) xterm.png)
C_SOURCES := $(shell find $(SRC_DIR) -type f -name '*.c' \
! -path '$(SRC_DIR)/userland/*' \
! -path '*/third_party/lwip/netif/slipif.c')
ASM_SOURCES := $(shell find $(SRC_DIR) -type f -name '*.asm' ! -path '$(SRC_DIR)/userland/*')
C_SOURCES = $(wildcard $(SRC_DIR)/core/*.c) \
$(wildcard $(SRC_DIR)/sys/*.c) \
$(wildcard $(SRC_DIR)/mem/*.c) \
$(wildcard $(SRC_DIR)/dev/*.c) \
$(wildcard $(SRC_DIR)/input/*.c) \
$(wildcard $(SRC_DIR)/net/*.c) \
$(wildcard $(SRC_DIR)/net/nic/*.c) \
$(wildcard $(SRC_DIR)/fs/*.c) \
$(wildcard $(SRC_DIR)/wm/*.c) \
$(wildcard $(SRC_DIR)/net/third_party/lwip/core/*.c) \
$(wildcard $(SRC_DIR)/net/third_party/lwip/core/ipv4/*.c) \
$(SRC_DIR)/net/third_party/lwip/netif/ethernet.c \
$(SRC_DIR)/net/third_party/lwip/netif/bridgeif.c
OBJ_FILES := $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(C_SOURCES)) \
$(patsubst $(SRC_DIR)/%.asm, $(BUILD_DIR)/%.o, $(ASM_SOURCES))
INCLUDE_DIRS := $(shell find $(SRC_DIR) -type d ! -path '$(SRC_DIR)/userland*')
INCLUDES := $(patsubst %, -I%, $(INCLUDE_DIRS))
ASM_SOURCES = $(wildcard $(SRC_DIR)/arch/*.asm)
OBJ_FILES = $(patsubst $(SRC_DIR)/core/%.c, $(BUILD_DIR)/%.o, $(wildcard $(SRC_DIR)/core/*.c)) \
$(patsubst $(SRC_DIR)/sys/%.c, $(BUILD_DIR)/%.o, $(wildcard $(SRC_DIR)/sys/*.c)) \
$(patsubst $(SRC_DIR)/mem/%.c, $(BUILD_DIR)/%.o, $(wildcard $(SRC_DIR)/mem/*.c)) \
$(patsubst $(SRC_DIR)/dev/%.c, $(BUILD_DIR)/%.o, $(wildcard $(SRC_DIR)/dev/*.c)) \
$(patsubst $(SRC_DIR)/input/%.c, $(BUILD_DIR)/%.o, $(wildcard $(SRC_DIR)/input/*.c)) \
$(patsubst $(SRC_DIR)/net/%.c, $(BUILD_DIR)/%.o, $(wildcard $(SRC_DIR)/net/*.c)) \
$(patsubst $(SRC_DIR)/net/nic/%.c, $(BUILD_DIR)/%.o, $(wildcard $(SRC_DIR)/net/nic/*.c)) \
$(patsubst $(SRC_DIR)/fs/%.c, $(BUILD_DIR)/%.o, $(wildcard $(SRC_DIR)/fs/*.c)) \
$(patsubst $(SRC_DIR)/wm/%.c, $(BUILD_DIR)/%.o, $(wildcard $(SRC_DIR)/wm/*.c)) \
$(patsubst $(SRC_DIR)/net/third_party/lwip/%.c, $(BUILD_DIR)/lwip/%.o, $(filter $(SRC_DIR)/net/third_party/lwip/%.c, $(C_SOURCES))) \
$(patsubst $(SRC_DIR)/arch/%.asm, $(BUILD_DIR)/%.o, $(ASM_SOURCES))
CFLAGS = -g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding \
-fno-stack-protector -fno-stack-check -fno-lto -fPIE \
-m64 -march=x86-64 -msse -msse2 -mstackrealign -mno-red-zone \
$(INCLUDES)
-I$(SRC_DIR) -I$(SRC_DIR)/net/third_party/lwip -I$(SRC_DIR)/core -I$(SRC_DIR)/sys -I$(SRC_DIR)/mem -I$(SRC_DIR)/dev -I$(SRC_DIR)/net -I$(SRC_DIR)/net/nic -I$(SRC_DIR)/fs -I$(SRC_DIR)/wm -I$(SRC_DIR)/input
LDFLAGS = -m elf_x86_64 -nostdlib -static -pie --no-dynamic-linker \
-z text -z max-page-size=0x1000 -T linker.ld
@ -62,9 +79,7 @@ NASMFLAGS = -f elf64
LIMINE_VERSION = 10.8.2
LIMINE_URL_BASE = https://github.com/limine-bootloader/limine/raw/v$(LIMINE_VERSION)
HOST_OS := $(shell uname -s 2>/dev/null || echo Windows)
.PHONY: all clean run run-hd limine-setup run-windows run-mac run-linux run-hd-mac run-hd-windows run-hd-linux
.PHONY: all clean run limine-setup run-windows run-mac run-linux
all:
$(call PRINT_STEP,STARTING BOREDOS BUILD)
@ -74,223 +89,228 @@ all:
$(BUILD_DIR):
$(call PRINT_STEP,CREATING BUILD DIRECTORY)
mkdir -p $(BUILD_DIR)
mkdir -p $(BUILD_DIR)
limine-setup:
$(call PRINT_STEP,SETTING UP LIMINE)
@if [ ! -f limine/limine-bios.sys ]; then \
printf "$(YELLOW)[LIMINE] Limine binaries missing or invalid. Cloning v$(LIMINE_VERSION)-binary...$(RESET)\n"; \
printf "$(YELLOW)[LIMINE] Limine binaries missing or invalid. Cloning v$(LIMINE_VERSION)-binary...$(RESET)"; \
rm -rf limine; \
git clone https://github.com/limine-bootloader/limine.git --branch=v$(LIMINE_VERSION)-binary --depth=1 limine; \
else \
printf "$(YELLOW)[LIMINE] Existing Limine binaries found.$(RESET)\n"; \
printf "$(YELLOW)[LIMINE] Existing Limine binaries found.$(RESET)"; \
fi
@if [ ! -f $(SRC_DIR)/core/limine.h ]; then \
printf "$(YELLOW)[LIMINE] Copying limine.h...$(RESET)\n"; \
printf "$(YELLOW)[LIMINE] Copying limine.h...$(RESET)"; \
cp limine/limine.h $(SRC_DIR)/core/limine.h; \
else \
printf "$(YELLOW)[LIMINE] limine.h already present.$(RESET)\n"; \
printf "$(YELLOW)[LIMINE] limine.h already present.$(RESET)"; \
fi
@printf "$(YELLOW)[LIMINE] Building Limine host utility...$(RESET)\n"
@printf "$(YELLOW)[LIMINE] Building Limine host utility...$(RESET)"
$(MAKE) -C limine
@printf "$(GREEN)[OK] Limine setup complete.$(RESET)\n"
@printf "$(GREEN)[OK] Limine setup complete.$(RESET)"
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) limine-setup
@printf "$(YELLOW)[CC]$(RESET) $< -> $@\n"
@mkdir -p $(dir $@)
@printf "$(YELLOW)[CC]$(RESET) $< -> $@"
mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.asm | $(BUILD_DIR)
@printf "$(YELLOW)[ASM]$(RESET) $< -> $@\n"
@mkdir -p $(dir $@)
$(BUILD_DIR)/%.o: $(SRC_DIR)/core/%.c | $(BUILD_DIR) limine-setup
@printf "$(YELLOW)[CC]$(RESET)[core] $< -> $@"
mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
$(BUILD_DIR)/%.o: $(SRC_DIR)/sys/%.c | $(BUILD_DIR) limine-setup
@printf "$(YELLOW)[CC]$(RESET)[sys] $< -> $@"
mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
$(BUILD_DIR)/%.o: $(SRC_DIR)/mem/%.c | $(BUILD_DIR) limine-setup
@printf "$(YELLOW)[CC]$(RESET)[mem] $< -> $@"
mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
$(BUILD_DIR)/%.o: $(SRC_DIR)/dev/%.c | $(BUILD_DIR) limine-setup
@printf "$(YELLOW)[CC]$(RESET)[dev] $< -> $@"
mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
$(BUILD_DIR)/%.o: $(SRC_DIR)/input/%.c | $(BUILD_DIR) limine-setup
@printf "$(YELLOW)[CC]$(RESET)[input] $< -> $@"
mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
$(BUILD_DIR)/%.o: $(SRC_DIR)/net/%.c | $(BUILD_DIR) limine-setup
@printf "$(YELLOW)[CC]$(RESET)[net] $< -> $@"
mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
$(BUILD_DIR)/%.o: $(SRC_DIR)/net/nic/%.c | $(BUILD_DIR) limine-setup
@printf "$(YELLOW)[CC]$(RESET)[net/nic] $< -> $@"
mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
$(BUILD_DIR)/%.o: $(SRC_DIR)/fs/%.c | $(BUILD_DIR) limine-setup
@printf "$(YELLOW)[CC]$(RESET)[fs] $< -> $@"
mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
$(BUILD_DIR)/%.o: $(SRC_DIR)/wm/%.c | $(BUILD_DIR) limine-setup
@printf "$(YELLOW)[CC]$(RESET)[wm] $< -> $@"
mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
$(BUILD_DIR)/lwip/%.o: $(SRC_DIR)/net/third_party/lwip/%.c | $(BUILD_DIR) limine-setup
@printf "$(YELLOW)[CC]$(RESET)[lwIP] $< -> $@"
mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
$(BUILD_DIR)/%.o: $(SRC_DIR)/arch/%.asm | $(BUILD_DIR)
@printf "$(YELLOW)[ASM]$(RESET) $< -> $@"
$(NASM) $(NASMFLAGS) $< -o $@
$(BUILD_DIR)/test_syscall.o: $(SRC_DIR)/arch/test_syscall.asm | $(BUILD_DIR)
@printf "$(YELLOW)[ASM][test_syscall]$(RESET) $< -> $@"
$(NASM) $(NASMFLAGS) $< -o $@
$(BUILD_DIR)/user_test.o: $(SRC_DIR)/arch/user_test.asm | $(BUILD_DIR)
@printf "$(YELLOW)[ASM][user_test]$(RESET) $< -> $@"
$(NASM) $(NASMFLAGS) $< -o $@
$(BUILD_DIR)/process_asm.o: $(SRC_DIR)/arch/process_asm.asm | $(BUILD_DIR)
@printf "$(YELLOW)[ASM][process]$(RESET) $< -> $@"
$(NASM) $(NASMFLAGS) $< -o $@
$(KERNEL_ELF): $(OBJ_FILES)
$(call PRINT_STEP,LINKING KERNEL)
@printf "$(YELLOW)[LD]$(RESET) Linking kernel ELF: $@\n"
@printf "$(YELLOW)[LD]$(RESET) Linking kernel ELF: $@"
$(LD) $(LDFLAGS) -o $@ $(OBJ_FILES)
@printf "$(GREEN)[OK]$(RESET) Kernel ELF built: $@\n"
@printf "$(GREEN)[OK]$(RESET) Kernel ELF built: $@"
$(call PRINT_STEP,BUILDING USERLAND)
$(MAKE) -C $(SRC_DIR)/userland
@printf "$(GREEN)[OK]$(RESET) Userland build complete.\n"
@printf "$(GREEN)[OK]$(RESET) Userland build complete."
$(BUILD_DIR)/initrd.tar: $(KERNEL_ELF)
$(call PRINT_STEP,BUILDING INITRD)
@printf "$(YELLOW)[INITRD]$(RESET) Cleaning previous initrd directory...\n"
@printf "$(YELLOW)[INITRD]$(RESET) Cleaning previous initrd directory..."
rm -rf $(BUILD_DIR)/initrd
@printf "$(YELLOW)[INITRD]$(RESET) Creating directory structure...\n"
@printf "$(YELLOW)[INITRD]$(RESET) Creating directory structure..."
mkdir -p $(BUILD_DIR)/initrd/bin
mkdir -p $(BUILD_DIR)/initrd/Library/images/Wallpapers
mkdir -p $(BUILD_DIR)/initrd/Library/images/gif
mkdir -p $(BUILD_DIR)/initrd/Library/images/icons/colloid
mkdir -p $(BUILD_DIR)/initrd/Library/Fonts/Emoji
mkdir -p $(BUILD_DIR)/initrd/Library/DOOM
mkdir -p $(BUILD_DIR)/initrd/Library/conf
mkdir -p $(BUILD_DIR)/initrd/Library/bsh
mkdir -p $(BUILD_DIR)/initrd/Library/BWM/Wallpaper
mkdir -p $(BUILD_DIR)/initrd/Library/art
mkdir -p $(BUILD_DIR)/initrd/Library/images/branding
mkdir -p $(BUILD_DIR)/initrd/docs
mkdir -p $(BUILD_DIR)/initrd/boot
mkdir -p $(BUILD_DIR)/initrd/mnt
mkdir -p $(BUILD_DIR)/initrd/dev
mkdir -p $(BUILD_DIR)/initrd/root/Desktop
mkdir -p $(BUILD_DIR)/initrd/root/Pictures
mkdir -p $(BUILD_DIR)/initrd/root/Documents
mkdir -p $(BUILD_DIR)/initrd/root/Downloads
mkdir -p $(BUILD_DIR)/initrd/etc
mkdir -p $(BUILD_DIR)/initrd/usr/lib/tcc/include
mkdir -p $(BUILD_DIR)/initrd/usr/local/include
mkdir -p $(BUILD_DIR)/initrd/usr/include/sys
mkdir -p $(BUILD_DIR)/initrd/usr/include/libc
mkdir -p $(BUILD_DIR)/initrd/usr/lib
@printf "$(YELLOW)[COPY]$(RESET) Limine binaries + kernel for installer...\n"
@if [ -f limine/BOOTX64.EFI ]; then cp limine/BOOTX64.EFI $(BUILD_DIR)/initrd/boot/; fi
@if [ -f limine/BOOTIA32.EFI ]; then cp limine/BOOTIA32.EFI $(BUILD_DIR)/initrd/boot/; fi
@if [ -f limine/limine-bios.sys ]; then cp limine/limine-bios.sys $(BUILD_DIR)/initrd/boot/; fi
@cp $(KERNEL_ELF) $(BUILD_DIR)/initrd/boot/boredos.elf
@printf "$(YELLOW)[COPY]$(RESET) Userland binaries...\n"
@printf "$(YELLOW)[COPY]$(RESET) Userland binaries..."
@for f in $(SRC_DIR)/userland/bin/*.elf; do \
if [ -f "$$f" ]; then \
printf " -> $$f\n"; \
printf " -> $$f"; \
cp "$$f" $(BUILD_DIR)/initrd/bin/; \
fi \
done
@printf "$(YELLOW)[COPY]$(RESET) TCC support files...\n"
@cp $(SRC_DIR)/userland/cli/third_party/tcc/libtcc1.a $(BUILD_DIR)/initrd/usr/lib/tcc/
@cp $(SRC_DIR)/userland/cli/third_party/tcc/libtcc1.a $(BUILD_DIR)/initrd/usr/lib/
@cp $(SRC_DIR)/userland/cli/third_party/tcc/include/*.h $(BUILD_DIR)/initrd/usr/lib/tcc/include/
@cp $(SRC_DIR)/userland/sdk/lib/libboredos.a $(BUILD_DIR)/initrd/usr/lib/
@cp $(SRC_DIR)/userland/sdk/lib/libc.a $(BUILD_DIR)/initrd/usr/lib/
@cp $(SRC_DIR)/userland/sdk/lib/libm.a $(BUILD_DIR)/initrd/usr/lib/
@cp $(SRC_DIR)/userland/bin/crt0.o $(BUILD_DIR)/initrd/usr/lib/crt0.o
@cp $(SRC_DIR)/userland/bin/crt0.o $(BUILD_DIR)/initrd/usr/lib/crt1.o
@cp $(SRC_DIR)/userland/bin/empty.o $(BUILD_DIR)/initrd/usr/lib/crti.o
@cp $(SRC_DIR)/userland/bin/empty.o $(BUILD_DIR)/initrd/usr/lib/crtn.o
@cp $(SRC_DIR)/userland/libc/*.h $(BUILD_DIR)/initrd/usr/include/
@cp $(SRC_DIR)/userland/libc/sys/*.h $(BUILD_DIR)/initrd/usr/include/sys/
@cp $(SRC_DIR)/userland/libc/*.h $(BUILD_DIR)/initrd/usr/include/libc/
@cp $(SRC_DIR)/userland/libc/*.h $(BUILD_DIR)/initrd/usr/local/include/
@cp $(SRC_DIR)/userland/stb_image.h $(BUILD_DIR)/initrd/usr/include/
@printf "$(YELLOW)[COPY]$(RESET) Wallpapers...\n"
@printf "$(YELLOW)[COPY]$(RESET) Wallpapers..."
@for f in $(SRC_DIR)/images/wallpapers/*; do \
if [ -f "$$f" ]; then \
printf " -> $$f\n"; \
printf " -> $$f"; \
cp "$$f" $(BUILD_DIR)/initrd/Library/images/Wallpapers/; \
fi \
done
@printf "$(YELLOW)[COPY]$(RESET) GIF assets...\n"
@printf "$(YELLOW)[COPY]$(RESET) GIF assets..."
@for f in $(SRC_DIR)/images/gif/*.gif; do \
if [ -f "$$f" ]; then \
printf " -> $$f\n"; \
printf " -> $$f"; \
cp "$$f" $(BUILD_DIR)/initrd/Library/images/gif/; \
fi \
done
@printf "$(YELLOW)[COPY]$(RESET) Colloid icons...\n"
@printf "$(YELLOW)[COPY]$(RESET) Colloid icons..."
@for f in $(COLLOID_ICONS); do \
src="$(SRC_DIR)/images/icons/colloid/$$f"; \
if [ -f "$$src" ]; then \
printf " -> $$src\n"; \
printf " -> $$src"; \
cp "$$src" $(BUILD_DIR)/initrd/Library/images/icons/colloid/; \
fi \
done
@printf "$(YELLOW)[COPY]$(RESET) BoredOS icons...\n"
@mkdir -p $(BUILD_DIR)/initrd/Library/images/icons/boredos
@for f in $(SRC_DIR)/images/icons/boredos/*.png; do \
if [ -f "$$f" ]; then \
printf " -> $$f\n"; \
cp "$$f" $(BUILD_DIR)/initrd/Library/images/icons/boredos/; \
fi \
done
@printf "$(YELLOW)[COPY]$(RESET) Branding assets...\n"
@cp -r branding/* $(BUILD_DIR)/initrd/Library/images/branding/
@printf "$(YELLOW)[COPY]$(RESET) Fonts...\n"
@printf "$(YELLOW)[COPY]$(RESET) Fonts..."
@for f in $(SRC_DIR)/fonts/*.ttf; do \
if [ -f "$$f" ]; then \
printf " -> $$f\n"; \
printf " -> $$f"; \
cp "$$f" $(BUILD_DIR)/initrd/Library/Fonts/; \
fi \
done
@printf "$(YELLOW)[COPY]$(RESET) Emoji fonts...\n"
@printf "$(YELLOW)[COPY]$(RESET) Emoji fonts..."
@for f in $(SRC_DIR)/fonts/Emoji/*.ttf; do \
if [ -f "$$f" ]; then \
printf " -> $$f\n"; \
printf " -> $$f"; \
cp "$$f" $(BUILD_DIR)/initrd/Library/Fonts/Emoji/; \
fi \
done
@printf "$(YELLOW)[COPY]$(RESET) bsh configuration...\n"
@if [ -f $(SRC_DIR)/library/bsh/bshrc ]; then printf " -> bshrc\n"; cp $(SRC_DIR)/library/bsh/bshrc $(BUILD_DIR)/initrd/Library/bsh/; fi
@if [ -f $(SRC_DIR)/library/bsh/startup.bsh ]; then printf " -> startup.bsh\n"; cp $(SRC_DIR)/library/bsh/startup.bsh $(BUILD_DIR)/initrd/Library/bsh/; fi
@if [ -f $(SRC_DIR)/library/bsh/boot.bsh ]; then printf " -> boot.bsh\n"; cp $(SRC_DIR)/library/bsh/boot.bsh $(BUILD_DIR)/initrd/Library/bsh/; fi
@if [ -f $(SRC_DIR)/library/conf/sysfetch.cfg ]; then printf " -> sysfetch.cfg\n"; cp $(SRC_DIR)/library/conf/sysfetch.cfg $(BUILD_DIR)/initrd/Library/conf/; fi
@printf "$(YELLOW)[COPY]$(RESET) bsh configuration..."
@if [ -f $(SRC_DIR)/library/bsh/bshrc ]; then printf " -> bshrc"; cp $(SRC_DIR)/library/bsh/bshrc $(BUILD_DIR)/initrd/Library/bsh/; fi
@if [ -f $(SRC_DIR)/library/bsh/startup.bsh ]; then printf " -> startup.bsh"; cp $(SRC_DIR)/library/bsh/startup.bsh $(BUILD_DIR)/initrd/Library/bsh/; fi
@if [ -f $(SRC_DIR)/library/bsh/boot.bsh ]; then printf " -> boot.bsh"; cp $(SRC_DIR)/library/bsh/boot.bsh $(BUILD_DIR)/initrd/Library/bsh/; fi
@printf "$(YELLOW)[COPY]$(RESET) DOOM assets...\n"
@if [ -f $(SRC_DIR)/userland/games/doom/doom1.wad ]; then printf " -> doom1.wad\n"; cp $(SRC_DIR)/userland/games/doom/doom1.wad $(BUILD_DIR)/initrd/Library/DOOM/; fi
@printf "$(YELLOW)[COPY]$(RESET) DOOM assets..."
@if [ -f $(SRC_DIR)/userland/games/doom/doom1.wad ]; then printf " -> doom1.wad"; cp $(SRC_DIR)/userland/games/doom/doom1.wad $(BUILD_DIR)/initrd/Library/DOOM/; fi
@printf "$(YELLOW)[COPY]$(RESET) ASCII art...\n"
@if [ -f $(SRC_DIR)/library/art/boredos.txt ]; then printf " -> boredos.txt\n"; cp $(SRC_DIR)/library/art/boredos.txt $(BUILD_DIR)/initrd/Library/art/; fi
@printf "$(YELLOW)[COPY]$(RESET) Documentation...\n"
@printf "$(YELLOW)[COPY]$(RESET) Documentation..."
@for f in $$(find docs -name '*.md' 2>/dev/null); do \
if [ -f "$$f" ]; then \
printf " -> $$f\n"; \
printf " -> $$f"; \
dir=$$(dirname "$$f"); \
mkdir -p $(BUILD_DIR)/initrd/"$$dir"; \
cp "$$f" $(BUILD_DIR)/initrd/"$$dir"/; \
fi \
done
@printf "$(YELLOW)[COPY]$(RESET) Root files...\n"
@if [ -f README.md ]; then printf " -> README.md\n"; cp README.md $(BUILD_DIR)/initrd/; fi
@if [ -f LICENSE ]; then printf " -> LICENSE\n"; cp LICENSE $(BUILD_DIR)/initrd/; fi
@if [ -f limine.conf ]; then printf " -> limine.conf\n"; cp limine.conf $(BUILD_DIR)/initrd/; fi
@printf "$(YELLOW)[COPY]$(RESET) Root files..."
@if [ -f README.md ]; then printf " -> README.md"; cp README.md $(BUILD_DIR)/initrd/; fi
@if [ -f LICENSE ]; then printf " -> LICENSE"; cp LICENSE $(BUILD_DIR)/initrd/; fi
@if [ -f limine.conf ]; then printf " -> limine.conf"; cp limine.conf $(BUILD_DIR)/initrd/; fi
@printf "$(YELLOW)[TAR]$(RESET) Creating initrd.tar...\n"
@printf "$(YELLOW)[TAR]$(RESET) Creating initrd.tar..."
cd $(BUILD_DIR)/initrd && COPYFILE_DISABLE=1 tar --exclude="._*" -cf ../initrd.tar *
@printf "$(GREEN)[OK]$(RESET) Initrd created: $(BUILD_DIR)/initrd.tar\n"
@printf "$(GREEN)[OK]$(RESET) Initrd created: $(BUILD_DIR)/initrd.tar"
$(ISO_IMAGE): $(KERNEL_ELF) $(BUILD_DIR)/initrd.tar limine.conf limine-setup
$(call PRINT_STEP,CREATING ISO IMAGE)
@printf "$(YELLOW)[ISO]$(RESET) Cleaning previous ISO root...\n"
@printf "$(YELLOW)[ISO]$(RESET) Cleaning previous ISO root..."
rm -rf $(ISO_DIR)
@printf "$(YELLOW)[ISO]$(RESET) Creating ISO directory structure...\n"
@printf "$(YELLOW)[ISO]$(RESET) Creating ISO directory structure..."
mkdir -p $(ISO_DIR)
mkdir -p $(ISO_DIR)/EFI/BOOT
@printf "$(YELLOW)[COPY]$(RESET) Kernel ELF...\n"
@printf "$(YELLOW)[COPY]$(RESET) Kernel ELF..."
cp $(KERNEL_ELF) $(ISO_DIR)/
@printf "$(YELLOW)[COPY]$(RESET) Limine config...\n"
@printf "$(YELLOW)[COPY]$(RESET) Limine config..."
cp limine.conf $(ISO_DIR)/
@printf "$(YELLOW)[COPY]$(RESET) Initrd...\n"
@printf "$(YELLOW)[COPY]$(RESET) Initrd..."
cp $(BUILD_DIR)/initrd.tar $(ISO_DIR)/
@printf "$(YELLOW)[CONFIG]$(RESET) Adding initrd module path...\n"
printf " module_path: boot():/initrd.tar\n" >> $(ISO_DIR)/limine.conf
@printf "$(YELLOW)[CONFIG]$(RESET) Adding initrd module path..."
printf " module_path: boot():/initrd.tar" >> $(ISO_DIR)/limine.conf
@printf "$(YELLOW)[COPY]$(RESET) Optional splash image...\n"
@if [ -f branding/splash.jpg ]; then printf " -> splash.jpg\n"; cp branding/splash.jpg $(ISO_DIR)/splash.jpg; else printf " -> no splash.jpg found\n"; fi
@printf "$(YELLOW)[COPY]$(RESET) Optional splash image..."
@if [ -f splash.jpg ]; then printf " -> splash.jpg"; cp splash.jpg $(ISO_DIR)/; else printf " -> no splash.jpg found"; fi
@printf "$(YELLOW)[COPY]$(RESET) Limine boot files...\n"
@printf "$(YELLOW)[COPY]$(RESET) Limine boot files..."
cp limine/limine-bios.sys $(ISO_DIR)/
cp limine/limine-bios-cd.bin $(ISO_DIR)/
cp limine/limine-uefi-cd.bin $(ISO_DIR)/
@printf "$(YELLOW)[COPY]$(RESET) EFI bootloaders...\n"
@printf "$(YELLOW)[COPY]$(RESET) EFI bootloaders..."
cp limine/BOOTX64.EFI $(ISO_DIR)/EFI/BOOT/
cp limine/BOOTIA32.EFI $(ISO_DIR)/EFI/BOOT/
@ -301,122 +321,40 @@ $(ISO_IMAGE): $(KERNEL_ELF) $(BUILD_DIR)/initrd.tar limine.conf limine-setup
-efi-boot-part --efi-boot-image --protective-msdos-label \
$(ISO_DIR) -o $(ISO_IMAGE)
@printf "$(YELLOW)[LIMINE]$(RESET) Installing BIOS bootloader...\n"
@printf "$(YELLOW)[LIMINE]$(RESET) Installing BIOS bootloader..."
./limine/limine bios-install $(ISO_IMAGE)
@printf "$(GREEN)[OK]$(RESET) ISO image ready: $(ISO_IMAGE)\n"
@printf "$(GREEN)[OK]$(RESET) ISO image ready: $(ISO_IMAGE)"
clean:
$(call PRINT_STEP,CLEANING BUILD OUTPUT)
rm -rf $(BUILD_DIR) $(ISO_DIR) $(ISO_IMAGE)
$(MAKE) -C $(SRC_DIR)/userland clean
@printf "$(GREEN)[OK]$(RESET) Clean complete.\n"
@printf "$(GREEN)[OK]$(RESET) Clean complete."
disk.qcow2:
$(call PRINT_STEP,CREATING 10GB EXPANDABLE DISK IMAGE)
qemu-img create -f qcow2 disk.qcow2 10G
run: $(ISO_IMAGE) disk.qcow2
$(call PRINT_STEP,DETECTING PLATFORM AND RUNNING BOREDOS)
@if [ "$(HOST_OS)" = "Darwin" ]; then \
printf "$(GREEN)[RUN]$(RESET) Detected macOS\n"; \
$(MAKE) run-mac; \
elif [ "$(HOST_OS)" = "Linux" ]; then \
printf "$(GREEN)[RUN]$(RESET) Detected Linux\n"; \
$(MAKE) run-linux; \
else \
printf "$(GREEN)[RUN]$(RESET) Detected Windows\n"; \
$(MAKE) run-windows; \
fi
run-hd: disk.qcow2 $(OVMF_VARS)
$(call PRINT_STEP,DETECTING PLATFORM AND BOOTING FROM HARD DRIVE)
@if [ "$(HOST_OS)" = "Darwin" ]; then \
printf "$(GREEN)[RUN-HD]$(RESET) Detected macOS\n"; \
$(MAKE) run-hd-mac; \
elif [ "$(HOST_OS)" = "Linux" ]; then \
printf "$(GREEN)[RUN-HD]$(RESET) Detected Linux\n"; \
$(MAKE) run-hd-linux; \
else \
printf "$(GREEN)[RUN-HD]$(RESET) Detected Windows\n"; \
$(MAKE) run-hd-windows; \
fi
run-windows: $(ISO_IMAGE) disk.qcow2
run-windows: $(ISO_IMAGE)
$(call PRINT_STEP,RUNNING BOREDOS IN QEMU ON WINDOWS)
qemu-system-x86_64 -m 4G -serial stdio -cdrom $< -boot d \
-smp 4 \
-audiodev dsound,id=audio0 -machine pcspk-audiodev=audio0 \
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \
-drive file=disk.qcow2,format=qcow2,file.locking=off
-drive file=disk.img,format=raw,file.locking=off
run-mac: $(ISO_IMAGE) disk.qcow2
run-mac: $(ISO_IMAGE)
$(call PRINT_STEP,RUNNING BOREDOS IN QEMU ON MACOS)
qemu-system-x86_64 -m 4G -serial stdio -cdrom $< -boot d \
-smp 4 \
-audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \
-display cocoa,show-cursor=off \
-device ahci,id=ahci -drive file=disk.qcow2,format=qcow2,if=none,id=disk0 -device ide-hd,bus=ahci.0,drive=disk0 \
-drive file=disk.img,format=raw,file.locking=off \
-cpu max
OVMF_CODE := /opt/homebrew/share/qemu/edk2-x86_64-code.fd
OVMF_VARS_TMPL := /opt/homebrew/share/qemu/edk2-i386-vars.fd
OVMF_VARS := edk2-vars.fd
ifeq ($(shell test -f $(OVMF_CODE) && echo 1),)
OVMF_CODE := /usr/local/share/qemu/edk2-x86_64-code.fd
OVMF_VARS_TMPL := /usr/local/share/qemu/edk2-i386-vars.fd
endif
$(OVMF_VARS):
@if [ -f $(OVMF_VARS_TMPL) ]; then \
printf "$(YELLOW)[UEFI]$(RESET) Creating local NVRAM vars...\n"; \
cp $(OVMF_VARS_TMPL) $(OVMF_VARS); \
fi
run-hd-mac: disk.qcow2 $(OVMF_VARS)
$(call PRINT_STEP,BOOTING BOREDOS FROM HARD DRIVE ON MACOS)
qemu-system-x86_64 -m 4G -serial stdio -boot c \
-smp 4 \
-audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \
-display cocoa,show-cursor=off \
-drive if=pflash,format=raw,readonly=on,file=$(OVMF_CODE) \
-drive if=pflash,format=raw,file=$(OVMF_VARS) \
-device ahci,id=ahci \
-drive file=disk.qcow2,format=qcow2,if=none,id=disk0 -device ide-hd,bus=ahci.0,drive=disk0 \
-drive file=disk.img,format=raw,if=none,id=disk1 -device ide-hd,bus=ahci.1,drive=disk1 \
-cpu max
run-linux: $(ISO_IMAGE) disk.qcow2
run-linux: $(ISO_IMAGE)
$(call PRINT_STEP,RUNNING BOREDOS IN QEMU ON LINUX)
qemu-system-x86_64 -m 4G -serial stdio -cdrom $< -boot d \
-smp 4 \
-audiodev pa,id=audio0 -machine pcspk-audiodev=audio0 \
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \
-display gtk,show-cursor=off \
-device ahci,id=ahci -drive file=disk.qcow2,format=qcow2,if=none,id=disk0 -device ide-hd,bus=ahci.0,drive=disk0 \
-cpu max
run-hd-windows: disk.qcow2
$(call PRINT_STEP,BOOTING BOREDOS FROM HARD DRIVE ON WINDOWS)
qemu-system-x86_64 -m 4G -serial stdio -boot c \
-smp 4 \
-audiodev dsound,id=audio0 -machine pcspk-audiodev=audio0 \
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \
-device ahci,id=ahci \
-drive file=disk.qcow2,format=qcow2,if=none,id=disk0 -device ide-hd,bus=ahci.0,drive=disk0 \
-cpu max
run-hd-linux: disk.qcow2 $(OVMF_VARS)
$(call PRINT_STEP,BOOTING BOREDOS FROM HARD DRIVE ON LINUX)
qemu-system-x86_64 -m 4G -serial stdio -boot c \
-smp 4 \
-audiodev pa,id=audio0 -machine pcspk-audiodev=audio0 \
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \
-display gtk,show-cursor=off \
-drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE.fd \
-drive if=pflash,format=raw,file=$(OVMF_VARS) \
-device ahci,id=ahci \
-drive file=disk.qcow2,format=qcow2,if=none,id=disk0 -device ide-hd,bus=ahci.0,drive=disk0 \
-drive file=disk.img,format=raw,file.locking=off \
-cpu max

154
POLICY.md Normal file
View file

@ -0,0 +1,154 @@
# BoredOS — Usage Policy & Legal Notice
Version 2.0 | Last updated: May 2026
---
## 1. Purpose of This Document
This document provides additional context about the intended use of BoredOS and outlines important legal and practical considerations.
**It is not a software license.**
BoredOS is licensed exclusively under the GNU General Public License v3.0 (GPLv3), which governs your rights to use, modify, and distribute the software.
In the event of any conflict, the GPLv3 takes full precedence.
---
## 2. License
BoredOS is free software released under the **GNU General Public License v3.0 (GPLv3)**.
You are free to:
- Use the software for any purpose
- Study and modify the source code
- Redistribute original or modified versions
A copy of the GPLv3 is provided in the [`LICENSE`](./LICENSE) file.
---
## 3. No Age Verification or Regulatory Features
BoredOS is designed as a **local operating system** and:
- Does not implement **age verification**
- Does not implement **age-gating**
- Does not perform **identity checks**
- Does not collect or process **personal data**
There are currently no plans to introduce such features.
---
## 4. Responsibility for Legal Compliance
Laws and regulations vary between jurisdictions and may apply differently depending on how software is used or distributed.
**You are solely responsible for ensuring that your use, distribution, or deployment of BoredOS complies with applicable laws and regulations in your jurisdiction.**
This includes, but is not limited to, laws relating to:
- Child safety and age-appropriate design
- Data protection and privacy
- Software distribution and platform obligations
If your use case requires features that BoredOS does not provide (such as age verification), you are responsible for implementing those features yourself or choosing alternative software.
---
## 5. No Representation of Legal Compliance
The author makes **no representation or warranty** that BoredOS complies with any specific legal or regulatory framework, including but not limited to:
- The California Age-Appropriate Design Code Act (AB 2273)
- The Childrens Online Privacy Protection Act (COPPA)
- The UK Age Appropriate Design Code (Childrens Code)
- Any similar laws in other jurisdictions
BoredOS is general-purpose software and is not designed or certified for compliance with regulatory regimes.
---
## 6. No Monitoring or Enforcement
BoredOS:
- Does not track users
- Does not verify location or identity
- Does not enforce jurisdiction-specific restrictions
The author has no technical ability to monitor or control how the software is used.
---
## 7. Privacy
**BoredOS collects no data.**
- No telemetry
- No analytics
- No crash reporting
- No accounts
- No background network communication
All operation is local to the users device.
The source code is hosted on GitHub, which is operated by GitHub, Inc. Their data practices are governed by their own privacy policy.
---
## 8. Disclaimer of Warranty
BoredOS is provided **"as is"**, without warranty of any kind, express or implied, including:
- Merchantability
- Fitness for a particular purpose
- Non-infringement
The author does not guarantee that:
- The software is free of bugs or vulnerabilities
- The software will work on any specific hardware
- The software is suitable for any particular use case
---
## 9. Limitation of Liability
To the maximum extent permitted by applicable law, the author shall not be liable for:
- Data loss
- Hardware damage
- Loss of profits
- Business interruption
- Any indirect or consequential damages
Nothing in this section excludes liability where such exclusion is not permitted by law.
---
## 10. Trademarks and Attribution
"BoredOS" and associated branding may not be used to endorse or promote derived products without prior permission.
This does not limit your rights under the GPLv3, but applies to branding and representation.
---
## 11. Changes to This Document
This document may be updated to reflect changes in legal context or project direction.
The latest version will always be available in the repository.
Previous versions will remain accessible via version history.
---
## 12. Contact
For questions or concerns, open an issue on the GitHub repository or contact the author via email at chris@boreddev.nl.
---
*BoredOS Usage Policy v2.0 — May 2026*

View file

@ -1,99 +1,93 @@
<div align="center">
<img src="branding/bOS_full_gradient_cropped.png" alt="BoredOS Logo" width="450" />
# BoredOS
<h3>A modern x86_64 hobbyist operating system built from the ground up.</h3>
<div align="center">
<img src="boredos.svg" alt="BoredOS Logo" width="450" />
<p><em>A modern x86_64 hobbyist operating system built from the ground up.</em></p>
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
![Platform: x86_64](https://img.shields.io/badge/Platform-x86_64-lightgrey)
![Status: Active](https://img.shields.io/badge/Status-Active-brightgreen)
![GitHub all releases](https://img.shields.io/github/downloads/boreddevnl/BoredOS/total?color=brightgreen)
<br />
[Docs](docs/README.md) · [Build & Run](docs/build/usage.md) · [AppDev SDK](docs/appdev/sdk_reference.md) · [Discord](https://discord.gg/J2BxWaFAgY) · [Support](https://buymeacoffee.com/boreddevhq)
</div>
---
![Screenshot](branding/screenshot.jpg)
BoredOS is a x86_64 operating system featuring a custom Desktop Environment (DE), a dedicated Window Manager (BoredWM), and a FAT32 filesystem. It balances low-level kernel exploration with a surprisingly capable userspace.
![Screenshot](screenshot.jpg)
> [!NOTE]
> The screenshot above may represent a previous build and is subject to change as the UI evolves.
> *The screenshot above may represent a previous build and is subject to change as the UI evolves.*
---
## Features
### Kernel and Architecture
- **Long Mode Architecture** — Native x86_64 implementation utilizing 64-bit address space and registers
- **Symmetric Multi-Processing** — Scalable multi-core support with IPI-based scheduling and synchronization
- **Advanced Memory Management** — Custom slab allocator with object pooling and efficient physical/virtual page mapping
- **Hybrid VFS Layer** — Unified filesystem interface supporting FAT32, TAR, ProcFS, and SysFS
- **Preemptive Multitasking** — Prioritized process scheduling with full context isolation
- **Hardware Abstraction** — Comprehensive driver support for PCI, AHCI, PS/2, and ACPI
### System Architecture
* **64-bit Long Mode:** Fully utilizing the x86_64 architecture.
* **Symmetric Multi-Processing (SMP):** Full support for multi-core CPUs via Limine SMP.
* **LAPIC & IPI Scheduling:** Advanced interrupt handling and inter-processor communication for task distribution.
* **SMP-Safe Spinlocks:** Robust kernel-wide synchronization for VFS, process management, and the GUI.
* **Multiboot2 Compliant:** Bootable on real hardware and modern emulators.
* **Kernel Core:** Interrupt Descriptor Table (IDT) management and a robust syscall interface.
* **Filesystem:** Full **FAT32** support for persistent and in-memory storage.
* **Networking:** Includes the lwIP networking stack and a basic web browser.
### Graphical Desktop Environment
- **BoredWM** — High-performance window manager featuring window stacking, focus management, and drag-and-drop interactions
- **Typography Engine** — Integrated font manager with TrueType (TTF) support and efficient glyph caching
- **Rich Media Subsystem** — Native hardware-independent decoding for PNG, JPEG, GIF, BMP, and TGA formats
- **LibWidget Toolkit** — Native UI component library for rapid application development
### Networking Stack
- **TCP/IP Integration** — Full lwIP-based network stack featuring DHCP, DNS, and Berkeley-style sockets
- **Network Services** — Integrated support for basic web browsing and real-time network telemetry
### Application Ecosystem
| Category | Applications |
|----------|--------------|
| Productivity | Text Editor, Markdown Viewer, BoredWord Processor, Web Browser, Calculator |
| Development | TCC (Tiny C Compiler), Lua|
| System | Explorer (File Manager), Task Manager, System Monitor, Graphing Utility |
| Games | doomgeneric, Minesweeper, 2048, Snake |
### Graphical User Interface
* **BoredWM:** A custom Window Manager with drag-and-drop, mouse-centered interaction.
* **Customization:** Adjustable UI to suit your aesthetic.
* **Media Support:** Built-in image decoding. (PNG, GIF, JPEG, TGA, BMP)
### Included Applications
* **Productivity:** GUI Text Editor calculator, Markdown Viewer, a simple browser and BoredWord.
* **Creativity:** A Paint application.
* **Utilities:** Terminal, Task Manager, File Explorer, Clock and a (limited) C Compiler.
* **Games:** Minesweeper and DOOM.
---
## 📚 Documentation
| Guide | Description |
|-------|-------------|
| [Documentation Index](docs/README.md) | Start here! |
| [Architecture Overview](docs/architecture/README.md) | Deep dive into the kernel |
| [Building and Running](docs/build/usage.md) | Set up your build environment |
| [AppDev SDK](docs/appdev/custom_apps.md) | Build your own apps for BoredOS |
Explore the internal workings of BoredOS via our comprehensive guides in the [`docs/`](docs/) directory.
* 📖 **[Documentation Index](docs/README.md)** Start here.
* 🏗️ **[Architecture Overview](docs/architecture/core.md)** Deep dive into the kernel.
* 🔨 **[Building and Running](docs/build/usage.md)** Setup your build environment.
* 🚀 **[AppDev SDK](docs/appdev/custom_apps.md)** Build your own apps for BoredOS.
---
## Support the Journey
## ☕ Support the Journey
If you find BoredOS interesting or useful, consider fueling development with a coffee!
If you find this project interesting or helpful, consider fueling the development with a coffee!
<a href="https://buymeacoffee.com/boreddevhq" target="_blank">
<img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="50" style="border-radius: 8px;" />
</a>
---
## History
## Project Disclaimer & Heritage
**BoredOS** is the successor to **[BrewKernel](https://github.com/boreddevnl/brewkernel)**, a project started in 2023. BrewKernel served as the foundational learning ground but has since been officially deprecated and archived — it no longer receives updates, bug fixes, or pull request reviews.
**BoredOS** is the successor to **[BrewKernel](https://github.com/boreddevnl/brewkernel)**, a project initiated in 2023.
BoredOS is a complete architectural reboot, applying years of lessons learned to build a cleaner, more modular, and more capable system.
While BrewKernel served as the foundational learning ground for this OS, it has been officially **deprecated and archived**. It no longer receives updates, bug fixes, or pull request reviews. BoredOS represents a complete architectural reboot, applying years of lessons learned to create a cleaner, more modular, and more capable system.
> [!IMPORTANT]
> Please direct all issues, discussions, and contributions to this repository. Legacy BrewKernel code is preserved for historical purposes only and is not compatible with BoredOS.
> Please ensure all issues, discussions, and contributions are directed to this repository. Legacy BrewKernel code is preserved for historical purposes only and is not compatible with BoredOS.
---
## Contributors
- **BoredDevNL** — Project creator and lead maintainer.
- **Lluciocc** — Contributor.
## License
**Copyright (C) 20232026 boreddevnl**
**Copyright (C) 2023-2026 boreddevnl**
Distributed under the **GNU General Public License v3**. See [`LICENSE`](LICENSE) for details.
Distributed under the **GNU General Public License v3**. See the `LICENSE` file for details.
> [!IMPORTANT]
> You must retain all copyright headers and include the original attribution in any redistributions or derivative works. See the [`NOTICE`](NOTICE) file for more details.
> This product includes software developed by Chris ("boreddevnl"). You must retain all copyright headers and include the original attribution in any redistributions or derivative works. See the `NOTICE` file for more details.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

BIN
disk.img

Binary file not shown.

View file

@ -58,7 +58,6 @@ The SDK and toolchain guides for creating your own `.elf` userland binaries.
- [`ELF App Metadata`](appdev/elf_metadata.md): How to declare app icons and descriptions using source annotations, how the build system embeds them into `.note.boredos.app` ELF sections, and how the kernel reads them at runtime.
- [`Example Apps`](appdev/examples/README.md): A collection of sample C applications ranging from basic terminal output to advanced TCP networking.
- [`Grapher`](appdev/grapher.md): Full reference for the built-in mathematical graphing application — equation syntax, keyboard controls, architecture, and configuration.
- [`Native TCC`](appdev/tcc.md): How to use the Tiny C Compiler (TCC) to build and run C applications directly on BoredOS.
### 4. [Usage](usage/)
General guides on how to interact with the OS.

View file

@ -11,7 +11,7 @@ This example demonstrates how to create an empty window that stays active on the
* Including `libui.h` and the event structure.
* Creating a `ui_window_t` handle.
* Creating an infinite event loop using `ui_get_event()`.
* Yielding CPU time via `sleep(ms)`.
* Yielding CPU time to the kernel via `sys_yield()`.
* Declaring app metadata via source annotations.
---
@ -50,11 +50,10 @@ int main(void) {
}
}
// 4. CRITICAL: Throttle our loop to save CPU
// 4. CRITICAL: Yield the remainder of our timeslice
// If we don't do this, the while(1) loop will consume 100% of the CPU
// and starve the rest of the OS! A 10ms sleep allows for ~100 FPS
// event polling while letting the CPU actually idle.
sys_system(SYSTEM_CMD_SLEEP, 10, 0, 0, 0);
// and starve the rest of the OS!
sys_yield();
}
// Returning from main will automatically destroy the window and exit the process.
@ -67,7 +66,7 @@ int main(void) {
1. **Window Handle (`wid`)**: `ui_window_create` sends a request to the kernel. The kernel allocates the memory for the window and returns a numerical ID (the handle) that we use for all future interactions with that specific window.
2. **The Event Loop**: Graphical programs run forever until closed. The `while (1)` loop serves this purpose.
3. **Polling**: `ui_get_event` asks the kernel, "Hey, did the user click my window or press a key since the last time I asked?". It is non-blocking, so it immediately returns `false` if nothing happened.
4. **CPU Throttling**: Since we are constantly polling in a loop, we call `sys_system(SYSTEM_CMD_SLEEP, 10, ...)` at the end of the loop frame. This tells the OS scheduler, "I'm done checking for events, don't run me again for at least 10ms." This allows the CPU to actually enter a low-power state and makes the system much smoother.
4. **CPU Yielding**: Since we are constantly polling in a tight loop, we call `sys_yield()` at the end of the loop frame. This politely tells the OS scheduler, "I'm done checking for events, go ahead and let another program run for a bit."
5. **`BOREDOS_APP_DESC` / `BOREDOS_APP_ICONS`**: Embedded into the `.elf` by the build system as a BoredOS NOTE section. The Window Manager reads this at runtime to render the app's icon on the Desktop and in the File Explorer. See [`elf_metadata.md`](../elf_metadata.md) for full details.
## Running It

View file

@ -76,8 +76,8 @@ int main(void) {
// Step D: Instruct the compositor to flush our drawing buffer to the physical screen
ui_mark_dirty(wid, 0, 0, W_WIDTH, W_HEIGHT);
// 4. Yield and throttle (targeting ~60 FPS)
sys_system(SYSTEM_CMD_SLEEP, 16, 0, 0, 0);
// 4. Yield and throttle
sys_yield();
}
return 0;
@ -93,4 +93,4 @@ int main(void) {
5. **`BOREDOS_APP_DESC` / `BOREDOS_APP_ICONS`**: Embedded into the compiled `.elf` as a BoredOS NOTE section. The Desktop and File Explorer read this to show the game's icon instead of the generic binary icon. See [`elf_metadata.md`](../elf_metadata.md) for full details.
> [!WARNING]
> Because `sys_system(SYSTEM_CMD_SLEEP, ...)`'s pause duration depends heavily on CPU load and how many other processes are running (or QEMU emulation speed), tying physics/movement strictly to loops can make the game run faster on faster computers. Advanced developers will want to calculate delta time (time elapsed since the last frame) for smooth motion.
> Because `sys_yield()`'s pause duration depends heavily on CPU load and how many other processes are running (or QEMU emulation speed), tying physics/movement strictly to loops can make the game run faster on faster computers. Advanced developers will want to calculate delta time (time elapsed since the last frame) for smooth motion.

View file

@ -24,7 +24,6 @@ Primary headers are in `src/userland/libc/` and UI helpers are in `src/wm/`.
- [`Syscalls`](syscalls.md): syscall numbers, FS/SYSTEM command IDs, and wrappers
- [`UI API`](ui_api.md): drawing and event APIs
- [`Widget API`](widget_api.md): common widgets and interaction helpers
- [`Native TCC`](tcc.md): Native C compilation directly on BoredOS
## Typical Include Set

View file

@ -15,7 +15,6 @@ Use libc wrappers when possible instead of calling raw syscall numbers directly.
| 3 | `SYS_GUI` | Window manager and drawing commands |
| 4 | `SYS_FS` | Filesystem and fd commands |
| 5 | `SYS_SYSTEM` | System-wide command multiplexer |
| 8 | `SYS_DEBUG_SERIAL` | Debug serial output (kernel only) |
| 9 | `SYS_SBRK` (userland header) | Heap break management |
| 10 | `SYS_KILL` (userland header) | Kill process by PID |
| 60 | `SYS_EXIT` (kernel header) | Internal kernel syscall number map |
@ -47,32 +46,6 @@ Notes:
| 17 | `FS_CMD_PIPE` | Create pipe |
| 18 | `FS_CMD_FCNTL` | `fcntl` flags ops |
## GUI Command IDs (`SYS_GUI`)
| ID | Macro | Meaning |
|---|---|---|
| 1 | `GUI_CMD_WINDOW_CREATE` | Create a new window |
| 2 | `GUI_CMD_DRAW_RECT` | Draw a rectangle |
| 3 | `GUI_CMD_DRAW_STRING` | Draw a string (TTF) |
| 4 | `GUI_CMD_MARK_DIRTY` | Mark region dirty / dual-buffer commit |
| 5 | `GUI_CMD_GET_EVENT` | Retrieve next GUI event |
| 6 | `GUI_CMD_DRAW_ROUNDED_RECT_FILLED` | Draw filled rounded rectangle |
| 7 | `GUI_CMD_DRAW_IMAGE` | Draw raw image data |
| 8 | `GUI_CMD_GET_STRING_WIDTH` | Get TTF string width |
| 9 | `GUI_CMD_GET_FONT_HEIGHT` | Get TTF font height |
| 10 | `GUI_CMD_DRAW_STRING_BITMAP` | Draw legacy bitmap string |
| 11 | `GUI_CMD_DRAW_STRING_SCALED` | Draw scaled TTF string |
| 12 | `GUI_CMD_GET_STRING_WIDTH_SCALED` | Get scaled TTF string width |
| 13 | `GUI_CMD_GET_FONT_HEIGHT_SCALED` | Get scaled TTF font height |
| 14 | `GUI_CMD_WINDOW_SET_RESIZABLE` | Toggle window resizability |
| 15 | `GUI_CMD_WINDOW_SET_TITLE` | Update window title |
| 16 | `GUI_CMD_SET_FONT` | Load/set window-specific font |
| 18 | `GUI_CMD_DRAW_STRING_SCALED_SLOPED` | Draw sloped/scaled TTF string |
| 50 | `GUI_CMD_GET_SCREEN_SIZE` | Get display resolution |
| 51 | `GUI_CMD_GET_SCREENBUFFER` | Copy screen contents to buffer |
| 52 | `GUI_CMD_SHOW_NOTIFICATION` | Show desktop notification |
| 53 | `GUI_CMD_GET_DATETIME` | Get RTC datetime |
## SYSTEM Command IDs (`SYS_SYSTEM`)
### Desktop and display
@ -92,10 +65,6 @@ Notes:
| 31 | `SYSTEM_CMD_SET_WALLPAPER_PATH` | Set wallpaper from path |
| 40 | `SYSTEM_CMD_SET_FONT` | Set active font |
| 47 | `SYSTEM_CMD_SET_RESOLUTION` | Set display mode |
| 49 | `SYSTEM_CMD_SET_KEYBOARD_LAYOUT` | Set active keyboard layout ID |
| 51 | `SYSTEM_CMD_GET_KEYBOARD_LAYOUT` | Get current keyboard layout ID |
| 52 | `SYSTEM_GET_CURSOR_SCALE` | Get the current BoredWM cursor scale |
| 53 | `SYSTEM_SET_CURSOR_SCALE` | Set the BoredWM cursor scale |
### Time, power, and system state
@ -110,8 +79,8 @@ Notes:
| 28 | `SYSTEM_CMD_GET_SHELL_CONFIG` | Read shell config value |
| 32 | `SYSTEM_CMD_RTC_SET` | Set RTC datetime |
| 41 | `SYSTEM_CMD_SET_RAW_MODE` | Terminal raw-mode control |
| 43 | `SYSTEM_CMD_YIELD` | Yield scheduler timeslice (Not recommended for idle loops) |
| 46 | `SYSTEM_CMD_SLEEP` | Sleep current process (Recommended for throttling) |
| 43 | `SYSTEM_CMD_YIELD` | Yield scheduler timeslice |
| 46 | `SYSTEM_CMD_SLEEP` | Sleep current process |
### Network
@ -169,25 +138,10 @@ Notes:
| 76 | `SYSTEM_CMD_GET_ELF_METADATA` | Read full app metadata from an ELF |
| 77 | `SYSTEM_CMD_GET_ELF_PRIMARY_IMAGE` | Read primary icon path from an ELF |
### Disk Management
| ID | Macro | Meaning |
|---|---|---|
| 100 | `SYSTEM_CMD_DISK_GET_COUNT` | Get number of detected disks |
| 101 | `SYSTEM_CMD_DISK_GET_INFO` | Get metadata for a specific disk/partition |
| 102 | `SYSTEM_CMD_DISK_WRITE_GPT` | Write GPT partition table to disk |
| 103 | `SYSTEM_CMD_DISK_WRITE_MBR` | Write MBR partition table to disk |
| 104 | `SYSTEM_CMD_DISK_MKFS_FAT32` | Format a partition as FAT32 |
| 105 | `SYSTEM_CMD_DISK_MOUNT` | Mount a filesystem |
| 106 | `SYSTEM_CMD_DISK_UMOUNT` | Unmount a filesystem |
| 107 | `SYSTEM_CMD_DISK_RESCAN` | Rescan disk for partition changes |
| 108 | `SYSTEM_CMD_DISK_REPLACE_KERNEL` | Copy new kernel to ESP / boot partition |
| 109 | `SYSTEM_CMD_DISK_SYNC` | Flush disk caches for a mountpoint |
## Common Wrapper API (`src/userland/libc/syscall.h`)
Typical wrappers used by apps:
- Process/system: `sys_exit`, `sys_yield`, `sys_system` (with `SYSTEM_CMD_SLEEP`), `sys_spawn`, `sys_exec`, `sys_waitpid`
- Process/system: `sys_exit`, `sys_yield`, `sys_spawn`, `sys_exec`, `sys_waitpid`, `sys_kill_signal`
- Filesystem: `sys_open`, `sys_read`, `sys_write_fs`, `sys_close`, `sys_seek`, `sys_tell`, `sys_size`, `sys_list`
- Network: `sys_network_init`, `sys_network_dhcp_acquire`, `sys_udp_send`, `sys_tcp_connect`, `sys_tcp_recv_nb`, `sys_dns_lookup`
- TTY: `sys_tty_create`, `sys_tty_read_out`, `sys_tty_write_in`, `sys_tty_set_fg`

View file

@ -1,99 +0,0 @@
# Native Development with TCC
BoredOS includes a native port of the **Tiny C Compiler (TCC)**, allowing you to compile and run C programs directly within the operating system.
## Basic Usage
The compiler is available as `tcc`. You can use it much like you would on a standard Unix-like system.
### Compiling a Simple CLI Program
Create a file named `hello.c`:
```c
#include <stdio.h>
int main() {
printf("Hello from BoredOS native TCC!\n");
return 0;
}
```
Compile and run it:
```bash
tcc hello.c -o hello.elf
./hello.elf
```
## Developing GUI Applications
To develop applications that use the BoredOS Window Manager and UI library, you need to link against `libboredos`.
### Example GUI App (`hello_gui.c`)
```c
#include <libc/libui.h>
#include <libc/syscall.h>
int main() {
ui_window_t win = ui_window_create("Hello TCC", 100, 100, 300, 200);
if (!win) return 1;
gui_event_t ev;
while (1) {
if (ui_get_event(win, &ev)) {
if (ev.type == GUI_EVENT_PAINT) {
ui_draw_string(win, 20, 40, "Compiled natively!", 0xFFFFFFFF);
ui_mark_dirty(win, 0, 0, 300, 200);
} else if (ev.type == GUI_EVENT_CLOSE) {
break;
}
}
}
return 0;
}
```
### Compilation Command
```bash
tcc hello_gui.c -o hello_gui.elf -lboredos
```
> [!NOTE]
> The compiler automatically searches `/usr/include` for headers and `/usr/lib` for libraries. The BoredOS SDK headers and `libboredos.a` are pre-installed in these locations.
## Technical Details
### Standard Paths
- **Headers**: `/usr/include`, `/usr/local/include`
- **Libraries**: `/usr/lib`
- **TCC Internal**: `/usr/lib/tcc`
### Compilation Process
BoredOS TCC generates standard **ELF64** binaries. It automatically links with:
1. **`crt0.o`**: Entry point initialization.
2. **`crti.o` / `crtn.o`**: Constructor/Destructor support.
3. **`libc.a`**: The BoredOS standard C library.
4. **`libtcc1.a`**: TCC runtime support.
### Memory & Storage Requirements
- **Static Linking Only**: BoredOS currently only supports static linking for native binaries.
- **Live ISO Mode**: You are limited by the 128MB RAMFS capacity. Compiling very large projects may fail if this limit is reached.
- **Disk Installation**: The compiler writes directly to your persistent disk. Your storage capacity is limited only by the size of your partition, and your work persists across reboots.
- **System RAM**: The kernel statically reserves 128MB for the internal RAMFS regardless of boot mode, though this does not limit your storage on a disk install.
- **No JIT**: The `tcc -run` feature is currently unsupported due to kernel memory protection and the lack of `mmap` with execution permissions in userland.
## Troubleshooting
### I/O Error during compilation
If you encounter an "I/O Error" while writing the output file, you may have run out of space.
- **Live ISO**: You have exceeded the 128MB RAMFS limit.
- **Disk Installation**: Your disk partition is full.
### Missing Headers
Ensure that you are including headers using the standard syntax: `#include <stdio.h>`. If you are using custom paths, use the `-I` flag:
```bash
tcc myapp.c -I/root/my_headers -o myapp.elf
```

View file

@ -68,9 +68,9 @@ Applications must continuously poll for events inside an infinite `$while(1)` lo
Returns `true` if an event was waiting in the queue, populating the `ev` structure. Returns `false` if the queue is empty.
> [!IMPORTANT]
> Because `ui_get_event` is non-blocking, you must call `sleep(ms);` or `sys_system(SYSTEM_CMD_SLEEP, ms, ...)` inside your event loop if no event was received.
> Because `ui_get_event` is non-blocking, you must call `sys_yield();` inside your event loop if no event was received. In BoredOS's **Multi-Core (SMP)** architecture, failing to yield will pin a CPU core to 100% usage, potentially starving other processes.
>
> Historically, BoredOS used `sys_yield()`, but in the **Multi-Core (SMP)** architecture, yielding alone will still pin a CPU core to 100% usage. Using a short sleep (e.g., 5-10ms) ensures your app remains responsive while allowing the CPU to actually idle.
> All UI syscalls are **Thread-Safe** at the kernel level via the global GUI spinlock.
### Graphical Event Structure

View file

@ -1,87 +0,0 @@
<div align="center">
<h1>ACPI</h1>
<p><em>Power management and hardware enumeration via the Advanced Configuration and Power Interface.</em></p>
</div>
---
BoredOS implements an ACPI subsystem that handles power state transitions, hardware discovery, and I2C device enumeration from the firmware tables. The implementation lives in `src/drivers/ACPI/`.
## Table Discovery
At boot, the kernel requests the RSDP (Root System Description Pointer) from Limine. If the pointer is absent or fails its checksum, the kernel panics - this is a hard requirement, not a graceful fallback.
From the RSDP, the kernel determines which top-level descriptor table to use:
- **XSDT** (64-bit pointers) - used when RSDP revision ≥ 2 and a valid `xsdt_address` is present.
- **RSDT** (32-bit pointers) - fallback for older firmware.
This selection is handled transparently by `acpi_get_sdt()`, which walks whichever table is available and returns the first SDT matching a given 4-char signature.
> [!NOTE]
> The DSDT is a special case - it is not listed in the XSDT/RSDT. It is instead pointed to directly by the `dsdt` field in the FADT. Use `acpi_get_dsdt()` to retrieve it.
---
## FADT and ACPI Enable
The FADT (Fixed ACPI Description Table, signature `"FACP"`) is located first. It contains the port addresses needed for power management and the SMI command port used to hand off hardware control from firmware to the OS.
If `smi_cmd` and `acpi_enable` are both non-zero, the kernel writes the enable byte to the SMI command port and spins on `PM1a_CNT` bit 0 until the hardware acknowledges. A timeout here is a fatal error.
---
## MADT and Interrupt Routing
The MADT (Multiple APIC Description Table, signature `"APIC"`) is parsed for Interrupt Source Override entries (type 2). These describe cases where a legacy ISA IRQ is wired to a different GSI on the I/O APIC - for example, IRQ 0 (PIT) may be redirected to GSI 2 on some platforms.
Two functions expose this mapping to the rest of the kernel:
- `acpi_irq_to_gsi(irq)` - returns the GSI for a given IRQ, or the IRQ itself if no override exists.
- `acpi_irq_flags(irq)` - returns the polarity and trigger mode flags for the override.
---
## AML Parsing
The DSDT and any SSDTs present in the firmware contain AML (ACPI Machine Language) bytecode describing the hardware namespace. BoredOS does not implement a full AML interpreter - instead `src/drivers/ACPI/acpi_aml.c` implements a targeted byte-stream walker sufficient for I2C device enumeration.
The walker handles:
- **`_HID`** - device identification, either as an inline string or a packed EISAID integer.
- **`_CRS`** - current resource settings; specifically scans for `I2cSerialBusV2` descriptors (large item tag `0x8E`) to extract slave address, bus speed, and addressing mode.
- **`_DSM`** - device-specific method; scans the raw method body for the I2C-HID GUID (`3cdff6f7-4267-4555-ad05-b30a3d8938de`) and extracts the HID descriptor register address from the static return package.
- **Power states** - records presence of `_PS0`, `_PS3`, `_PR0`, `_PR3` as flags.
> [!IMPORTANT]
> `_CRS` and `_DSM` must be statically defined `Name()` objects for the walker to read them. Dynamically computed resources via AML method evaluation are not supported.
---
## I2C Enumeration
`acpi_i2c_enumerate()` is called at the end of `acpi_init()`. It walks the DSDT via `acpi_get_dsdt()` followed by all SSDTs found in the XSDT/RSDT, feeding each into `aml_walk_table()`.
Devices are emitted into a flat table when both a `_HID` and a valid `I2cSerialBusV2` resource (non-zero `connection_speed`) are found. The result is accessible via:
- `acpi_i2c_count()` - number of enumerated devices.
- `acpi_i2c_get(index)` - pointer to an `aml_i2c_dev_t` record containing the name, HID, slave address, speed, DSM result, and power flags.
---
## Shutdown
Performing a clean S5 (soft power-off) requires writing the correct `SLP_TYP` value to the PM1 Control Block. This value is firmware-specific and must be read from the `_S5_` object in the DSDT.
`aml_parse_s5()` scans the DSDT AML for the `_S5_` name and extracts `SLP_TYPa` and `SLP_TYPb`, pre-shifted to bit position 10 ready for `outw`. If the parse fails, the kernel falls back to `SLP_TYP = 5`, which works on the majority of hardware.
### Shutdown sequence
On compliant hardware, step 1 is all that runs and the machine powers off immediately. The remaining steps target known virtual machine implementations that don't respond to standard ACPI.
1. Write `SLP_TYPa | SLP_EN` to `PM1a_CNT_BLK` (and `PM1b_CNT_BLK` if present).
2. Write known virtualizer-specific magic values - QEMU (`0x604`), VirtualBox (`0x4004`), Bochs (`0xB004`), Cloud Hypervisor (`0x600`) - to trigger their respective shutdown paths.
3. Disable interrupts and halt indefinitely as a last resort.
> [!NOTE]
> The virtualizer ports are sent after the ACPI write deliberately. On real hardware, sending those port values is harmless, but the ordering avoids any ambiguity about what triggered the power-off.

View file

@ -1,32 +0,0 @@
# BoredOS Architecture
This folder gathers the architecture documentation that explains how BoredOS is built from the kernel up.
## Architecture roadmap
The documentation is split by area so you can go directly to the subsystem you want to understand.
| Area | Document | Description |
| --- | --- | --- |
| Graphics | [`graphics/window_manager.md`](architecture/graphics/window_manager.md) | Window manager design and display composition. |
| Hardware | [`hardware/input.md`](architecture/hardware/input.md) | Hardware-level input support and device wiring. |
| Hardware | [`hardware/pci.md`](architecture/hardware/pci.md) | PCI bus management and device enumeration. |
| Input | [`input/keyboard.md`](architecture/input/keyboard.md) | Keyboard input handling and key mapping. |
| Memory | [`memory/memory.md`](architecture/memory/memory.md) | Memory architecture, paging, and address space layout. |
| Memory | [`memory/memory_manager.md`](architecture/memory/memory_manager.md) | Memory allocation and management systems. |
| Network | [`network/network_stack.md`](architecture/network/network_stack.md) | TCP/IP stack design, protocol flow, and packet handling. |
| Network | [`network/network_drivers.md`](architecture/network/network_drivers.md) | Network driver architecture and interface support. |
| Storage | [`storage/filesystem.md`](architecture/storage/filesystem.md) | File system structure and storage access. |
| Storage | [`storage/ahci_drivers.md`](architecture/storage/ahci_drivers.md) | AHCI driver implementation and disk controller support. |
| System | [`system/core.md`](architecture/system/core.md) | Core kernel architecture and main subsystems. |
| System | [`system/interrupts.md`](architecture/system/interrupts.md) | Interrupt handling and low-level event dispatch. |
| System | [`system/processes.md`](architecture/system/processes.md) | Process management, scheduling, and execution model. |
| General | [`versioning.md`](architecture/versioning.md) | Release versioning and project numbering conventions. |
## Quick start
- **Read `system/core.md` first** for the kernel overview.
- Then explore the subsystem area you need: `memory/`, `network/`, `storage/`, `graphics/`, or `system/`.
- Use `versioning.md` to understand BoredOS version rules.
> Note: The links above point directly to the most important architecture documents in this folder.

View file

@ -42,12 +42,6 @@ With the introduction of Symmetric Multi-Processing (SMP), the Window Manager (W
2. **Per-CPU Rendering State**: To facilitate simultaneous GUI system calls across all CPU cores, the low-level rendering context (`g_render_target` array) is isolated per-CPU using the core ID. This allows completely lockless multi-core pixel rasterization, drastically reducing rendering bottlenecks.
3. **Deferred Compositing**: Final screen composition (`wm_paint`) is scheduled to the main kernel idle loop on the Bootstrap Processor (BSP). This enables application cores to continue processing logic seamlessly while the GUI asynchronously handles flipping the physical framebuffer.
## Cursor Rendering
The cursor is drawn by BoredWM rather than by userland. Its shape is a small bitmap mask where transparent cells are skipped, white cells draw the outline, and black cells draw the filled body. The WM expands each source cell by the active cursor scale before writing pixels into the back buffer.
The current scale is exposed to userland through `SYSTEM_GET_CURSOR_SCALE` and can be changed with `SYSTEM_SET_CURSOR_SCALE`. Settings uses those commands for the mouse panel, while the WM clamps the requested scale and forces a redraw so the new cursor size appears immediately.
> [!IMPORTANT]
> Because application rendering (rasterizing geometry into a window's backbuffer) is SMP-safe and lock-free across cores, GUI performance scales linearly with the number of CPUs active.

View file

@ -2,20 +2,13 @@
BoredOS is built cross-compiled from a host system (such as macOS or Linux) to target the generic `x86_64-elf` platform.
## Table of Contents
- [Prerequisites](#prerequisites)
- [Building the Cross-Compiler on Linux](#building-the-cross-compiler-on-linux)
- [Installing the Toolchain on Windows](#installing-the-toolchain-on-windows)
## Prerequisites
To build BoredOS, you need the following tools:
1. **x86_64 ELF GCC Cross-Compiler**:
- `x86_64-elf-gcc`: The C compiler targeting the freestanding overarching ELF environment.
- `x86_64-elf-ld`: The linker to combine object files into the final `boredos.elf` kernel and userland binaries.
- `x86_64-elf-ld`: The linker to combine object files into the final `boredos.elf` kernel binary and userland variables.
2. **NASM**:
- Required to compile the `.asm` files in `src/arch/` and `src/userland/crt0.asm`. It formats the output as `elf64` objects to be linked alongside the C code.
@ -26,169 +19,3 @@ To build BoredOS, you need the following tools:
4. **QEMU** (Optional but highly recommended for testing):
- `qemu-system-x86_64` is used to virtualize the OS for testing or to mess around.
## Building the Cross-Compiler on Linux
### Availability Issue
On most Linux distributions, the `x86_64-elf-gcc` cross-compiler binary is **not pre-packaged** in standard repositories. The only notable exception is **Arch Linux** and Arch-based distributions (Manjaro, EndeavourOS, etc.), where it can be installed via `pacman`:
```bash
pacman -S x86_64-elf-gcc x86_64-elf-binutils
```
For all other Linux distributions (Debian, Ubuntu, Fedora, openSUSE, etc.), you **must build the cross-compiler from source**.
### Building from Source
To build the x86_64-ELF GCC cross-compiler:
1. **Download prerequisites**:
- GNU Binutils source
- GCC source
2. **Configure and build Binutils**:
```bash
../binutils-*/configure --target=x86_64-elf --prefix=/usr/local/cross
make && make install
```
3. **Configure and build GCC**:
```bash
../gcc-*/configure --target=x86_64-elf --prefix=/usr/local/cross \
--without-headers --enable-languages=c
make all-gcc && make install-gcc
```
4. **Add to PATH**:
```bash
export PATH="/usr/local/cross/bin:$PATH"
```
Verify the installation:
```bash
x86_64-elf-gcc --version
```
> **Note**: Building the cross-compiler can take 20-30 minutes depending on system performance. This is a one-time setup cost.
## Installing the Toolchain on Windows
### Recommended Environment: MSYS2
On Windows, the recommended way to build BoredOS is using **MSYS2**.
MSYS2 provides a Unix-like environment with the `pacman` package manager, making it easy to install the required development tools.
---
## 1. Install MSYS2
Download and install MSYS2 from the official website:
- https://www.msys2.org/
After installation, launch the **MSYS2 UCRT64** terminal.
---
## 2. Update MSYS2
Before installing packages, fully update the environment:
```bash
pacman -Syu
```
You may be asked to close the terminal after the first update.
If so:
1. Close the MSYS2 window
2. Reopen **MSYS2 UCRT64**
3. Run the update command again:
```bash
pacman -Syu
```
Repeat until no further updates are available.
---
## 3. Install Required Packages
Install the required development tools:
```bash
pacman -S make nasm xorriso git
```
---
## 4. Install QEMU for Windows
Download the Windows version of QEMU from:
- https://qemu.weilnetz.de/w64/
Install QEMU normally and make sure the installation directory is added to your Windows `PATH`.
Note that if it breaks when building, you need too add `qemu-img` to your `PATH`:
`export PATH="/c/Program Files/qemu:$PATH"`
You can verify the installation with:
```bash
qemu-system-x86_64 --version
```
---
## 5. Install the x86_64 ELF Cross Toolchain
Download the prebuilt `x86_64-elf` toolchain for Windows:
- https://github.com/lordmilko/i686-elf-tools/releases/download/15.2.0/x86_64-elf-tools-windows.zip
Extract the archive somewhere convenient.
---
## 6. Add the Toolchain to PATH
Inside the **MSYS2 UCRT64** terminal, add the toolchain binaries to your `PATH`:
```bash
export PATH="/c/Users/your/path/to/the/binaries/x86_64-elf-tools-windows/bin:$PATH"
```
To make this permanent, add the line to your `~/.bashrc` file:
```bash
echo 'export PATH="/c/Users/your/path/to/the/binaries/x86_64-elf-tools-windows/bin:$PATH"' >> ~/.bashrc
```
Then reload the shell:
```bash
source ~/.bashrc
```
---
## 7. Verify the Installation
Verify that the cross compiler is available:
```bash
x86_64-elf-gcc --version
```
You should also verify NASM and QEMU:
```bash
nasm -v
qemu-system-x86_64 --version
```
If all commands work, the development environment is correctly configured.

15
docs/build/usage.md vendored
View file

@ -33,14 +33,23 @@ To run BoredOS successfully (either in emulation or on bare metal), your target
To test the generated ISO quickly without real hardware, use the QEMU emulator:
For MacOS:
```sh
make run
make run-mac
```
This command automatically detects your operating system and invokes QEMU with specific arguments:
For Linux:
```sh
make run-linux
```
For Windows:
```sh
make run-windows
```
This command invokes QEMU with specific arguments:
- `-m 4G`: Allocates 4 Gigabytes of RAM.
- `-cdrom boredos.iso`: Mounts the built OS image as a CD-ROM.
- `-smp 4`: Enables 4 CPU cores.
- `-drive file=disk.img...`: Attaches a raw disk image included in this release of BoredOS.
## Running on Bare Metal

View file

@ -1,27 +0,0 @@
# BoredOS Installation
## Requirements
- Disk with at least **1 GB** (2,097,152 sectors)
- UEFI firmware
- A running BoredOS live environment (ISO)
## Quick Start (UEFI)
```
boredos_install --uefi /dev/sda
```
After installation, reboot and select the target disk from your firmware boot menu.
## Manual Steps
```
fdisk /dev/sda
mkfs_fat -F 32 -n EFI /dev/sda1
mkfs_fat -F 32 -n BOREDOS /dev/sda2
boredos_install --no-partition --no-format --uefi /dev/sda
```
See `install_guide.md` for a full walkthrough and `internals.md` for a deep dive into how the process works.

View file

@ -1,72 +0,0 @@
# Advanced Installation
## Skipping Steps
All major phases can be skipped independently:
```
boredos_install --no-partition --no-format --uefi /dev/sda
```
The destructive warning is only shown when `--no-partition` AND `--no-format` are both absent.
## `/mnt/boot` Operations
The installer mounts the ESP at `/mnt/boot` and unmounts it when finished.
## FAT32 Limitations
FAT32 does not support Unix file permissions, ownership, or timestamps. These attributes are **not** preserved during installation. This is expected and documented.
## Custom Device Names
Use `--esp-dev` and `--root-dev` to bypass auto-detection:
```
boredos_install --uefi --esp-dev sda1 --root-dev sda2 /dev/sda --no-partition --no-format
```
Provided device names are still validated via `sys_disk_get_info` before use.
### Step 1: Partitioning
Run `fdisk` interactively on your target device.
```bash
fdisk /dev/sda
```
Inside the interactive shell:
1. Type `n` to create a new partition (the ESP).
2. Press Enter for the default start offset (1mb).
3. Enter the size using suffixes like `b`, `mb`, or `gb` (e.g., `512mb` for a 512 MB ESP).
4. Type `n` again for the second partition (the Root).
5. Press Enter for the default start offset (aligned after the ESP).
6. Press Enter for the default size (rest of the disk).
7. Type `w` to write the partition table.
8. Type 'Q' to quit.
### Step 2: Formatting
Initialize the partitions with FAT32. Use the labels `EFI` and `BOREDOS` to match the expected system layout.
```bash
mkfs_fat -F 32 -n EFI /dev/sda1
mkfs_fat -F 32 -n BOREDOS /dev/sda2
```
### Step 3: Installation
The easiest way to perform the file copy and bootloader setup is to use the installer in "copy only" mode. This ensures that hidden flags (like the root detection file) are placed correctly.
```bash
boredos_install --no-partition --no-format --uefi --esp-dev sda1 /dev/sda
```
#### What this step does:
1. **Mounts** `/dev/sda2` to `/mnt` and `/dev/sda1` to `/mnt/boot`.
2. **Identifies** the root by creating an empty file at `/mnt/Library/.boredos_root`.
3. **Copies** the system structure: `/bin`, `/Library`, `/docs`, and `/root` are mirrored to `/mnt`.
4. **Deploys** the kernel and initrd: `boredos.elf` and `initrd.tar` are copied to the ESP (`/mnt/boot/`).
5. **Configures** Limine: Writes a `limine.conf` to the ESP and copies the EFI bootloader to `/mnt/boot/EFI/BOOT/BOOTX64.EFI`.
For a deep dive into why these steps are performed, see the [Installation Internals](internals.md) guide.

View file

@ -1,30 +0,0 @@
# Install Guide
## UEFI Installation
1. Boot from the BoredOS ISO.
2. Run `boredos_install --uefi /dev/sda`.
3. Type `y` when prompted and press Enter.
4. After completion, reboot and select the disk.
## limine.conf Notes
**UEFI** uses `boot():/boredos.elf` — the `boot():` URL scheme refers to the EFI System Partition.
**Root selection** uses `root=/dev/<partition>` in `cmdline:` to choose the writable root partition (for example `root=/dev/sda2`).
**Live vs disk override** supports `--live` and `--disk` in `cmdline:`. Use `--live` for ISO/USB live boots and `--disk` for installed systems.
## Options
| Flag | Description |
|---|---|
| `--no-partition` | Skip fdisk (use existing partitions) |
| `--no-format` | Skip mkfs (use existing filesystem) |
| `--no-files` | Skip file copy |
| `--no-bootloader` | Skip limine.conf and EFI file copy |
| `--esp-size N` | ESP size in MB (default: 512) |
| `--esp-dev DEV` | Explicit ESP device name |
| `--root-dev DEV` | Explicit root device name |
| `-y` | Auto-accept the destructive warning |

View file

@ -1,57 +0,0 @@
# Installation Internals: How, Why, and What
This document explains the technical details of the BoredOS installation process, the design decisions behind it, and the resulting system layout.
## How the installation works
The BoredOS installation follows a strict five-phase sequence:
1. **Partitioning (GPT/MBR)**: The disk is prepared with a partition table. For UEFI systems, we use GPT (GUID Partition Table) and create at least two partitions: an EFI System Partition (ESP) and a data partition.
2. **Formatting (FAT32)**: Both partitions are formatted with FAT32.
- The ESP is labeled `EFI`.
- The root partition is labeled `BOREDOS`.
3. **Mounting & Flagging**: The root partition is mounted to `/mnt`. A special marker file is created at `/Library/.boredos_root`. This file is used by the kernel during boot to differentiate between a persistent disk and a live ISO/RAM disk.
4. **System Mirroring**: The installer performs a recursive copy of the live system's essential including but not limited to:
- `/bin`: All userland binaries.
- `/Library`: System assets, fonts, icons, and configuration.
- `/docs`: Documentation and manuals.
- `/root`: The default user home directory.
5. **Bootloader Setup**:
- The ESP is mounted to `/mnt/boot`.
- The kernel (`boredos.elf`) and the initial RAM disk (`initrd.tar`) are copied to the root of the ESP.
- The Limine EFI binary is placed at `/EFI/BOOT/BOOTX64.EFI`.
- A `limine.conf` is generated with the correct `root=/dev/sdXX` parameter.
## Why it works this way
### FAT32 for Everything
BoredOS currently uses FAT32 as its primary filesystem for both the boot partition and the root partition.
- **Universal Support**: FAT32 is natively understood by UEFI firmware, making it the only choice for the ESP.
- **Simplicity**: By using FAT32 for the root partition as well, the kernel only needs one robust filesystem driver to handle both boot-time loading and runtime persistent storage.
- **Interoperability**: You can easily mount BoredOS partitions on other operating systems (Windows, Linux, macOS) to transfer files.
### The ESP/Root Split
Even though both use FAT32, we split the disk into two partitions to follow the UEFI specification. The ESP is meant to be small and strictly for bootloader files, while the root partition contains the entire userland. This separation allows for cleaner upgrades and multi-boot scenarios.
### Limine Bootloader
We chose [Limine](https://limine-bootloader.org/) because of its excellent support for modern protocols, its simplicity in configuration, and its lightweight footprint. It handles the transition from UEFI/BIOS environment to the kernel seamlessly.
## What the installation does
After a successful installation, your disk will look like this:
### Partition 1: EFI System Partition (ESP)
- `/boredos.elf`: The actual kernel binary.
- `/initrd.tar`: The basic system image loaded into RAM at boot.
- `/limine.conf`: The bootloader configuration.
- `/EFI/BOOT/BOOTX64.EFI`: The bootloader entry point for UEFI firmware.
### Partition 2: BoredOS Root
- `/bin/`: Executables.
- `/Library/`: System data.
- `.boredos_root`: Hidden marker file.
- `fonts/`, `images/`, `man/`: System resources.
- `/docs/`: System documentation.
- `/root/`: Persistent user files.

33
docs/usage/booting.md Normal file
View file

@ -0,0 +1,33 @@
# Booting BoredOS
BoredOS uses the Limine bootloader, which provides a flexible way to configure the boot process and pass parameters to the kernel.
## Boot Parameters
You can modify system behavior at startup by passing specific boot flags.
### Verbose Boot (`-v`)
The `-v` flag enables the kernel console (`kconsole`) during the boot process. When enabled, the kernel will display detailed initialization logs on the screen. By default, this is often disabled in the included configuration for a cleaner "splash-only" boot experience.
#### Toggling Verbose Boot at Runtime
You can enable or disable the verbose boot log directly from the Limine boot menu without modifying the source files:
1. **Select Entry**: When the Limine boot menu appears, highlight the **BoredOS** entry.
2. **Edit**: Press `E` to enter the entry editor.
3. **Modify Flag**: Find the line containing `cmdline: -v`.
- To **Enable**: Remove the `#` character if the line is commented out (change `# cmdline: -v` to `cmdline: -v`).
- To **Disable**: Add a `# ` at the start of the line.
4. **Boot**: Press `F10` to boot using the modified parameters.
#### Persistent Configuration
To change the default behavior permanently, modify the `limine.conf` file in the repository root before building the ISO:
```conf
/BoredOS
protocol: limine
path: boot():/boredos.elf
cmdline: -v
```

View file

@ -1,90 +0,0 @@
# du
`du` (disk usage) reports the disk space used by files and directories.
## Usage
```sh
du [OPTIONS]... [FILE]...
```
## Description
By default, `du` prints human-readable sizes for each file and directory it encounters, starting from the current directory (`.`) if no path is given.
## Options
| Option | Description |
| :--- | :--- |
| `-s, --summarize` | Show only a total for each argument, suppressing per-entry output. |
| `-a, --all` | Write counts for all files, not just directories. |
| `-d, --max-depth=N` | Stop at depth N; show only entries at or above depth N. |
| `-c, --total` | Print a grand total after all arguments have been processed. |
| `-b, --bytes` | Print sizes in exact bytes instead of human-readable units. |
| `-H, --human-readable` | Accepted for compatibility; human-readable is the default. |
| `--help` | Display usage information and exit. |
## Output Format
Each line shows a size followed by the path:
```
SIZE PATH
```
Sizes are formatted as `B`, `KB`, `MB`, or `GB` by default, with one decimal place when appropriate (e.g., `1.5 GB`). The `-b` option overrides this to show exact byte counts.
## Examples
Show disk usage for the current directory:
```sh
du
```
Show disk usage for a specific path:
```sh
du /bin
```
Show only totals per argument (`-s`):
```sh
du -s /bin /home
```
Show all files and directories recursively (`-a`):
```sh
du -a /bin
```
Limit output to depth 1 (`-d`):
```sh
du -d 1 /
```
Print a grand total after processing (`-c`):
```sh
du -c /bin /home
```
Show exact byte counts (`-b`):
```sh
du -b /bin
```
## How It Works
`du` uses `sys_get_file_info()` to read file sizes and `sys_list()` to enumerate directory contents recursively. The command skips the synthetic `.` and `..` entries and continues processing remaining paths if one path is inaccessible, printing an error for the failed path.
The size reported is the **apparent file size** (the logical size stored in the directory entry), not the allocated disk blocks. This is consistent with how BoredOS reports file sizes through the filesystem API.
## Exit Status
- `0`: Success
- `1`: One or more paths could not be accessed or listed

View file

@ -1,106 +0,0 @@
# lsblk
`lsblk` lists the block devices detected by BoredOS, including whole disks and their partitions.
## Usage
```sh
lsblk
lsblk /dev/sda
lsblk -r
lsblk --json
```
## Output
By default, `lsblk` prints a compact tree view:
```text
/dev/sda 2 GB disk
└─ sda1 2 GB part FAT32 BOREDOS
```
Fields shown by the default output:
- device name, such as `/dev/sda` or `sda1`
- human-readable size, such as `512 MB` or `2 GB`
- device type, either `disk` or `part`
- filesystem type, currently `FAT32` when detected
- volume label when available
- `[ESP]` flag for EFI System Partitions
> [!NOTE]
> Mount points are not shown yet because BoredOS does not currently expose mountpoint information through the disk info syscall.
## Options
| Option | Description |
| :--- | :--- |
| `-r` | Print raw output without tree characters. |
| `--json` | Print machine-readable JSON output. |
| `/dev/DEVICE` | Show only one disk or partition. |
## Examples
List all block devices:
```sh
lsblk
```
Example output:
```text
/dev/sda 2 GB disk
└─ sda1 2 GB part FAT32 BOREDOS
/dev/sdb 16 GB disk
```
Show one disk and its partitions:
```sh
lsblk /dev/sda
```
Example output:
```text
/dev/sda 2 GB disk
└─ sda1 2 GB part FAT32 BOREDOS
```
Print raw output for scripts:
```sh
lsblk -r
```
Example output:
```text
/dev/sda 2GB disk
/dev/sda1 2GB part FAT32 BOREDOS
```
Print JSON output:
```sh
lsblk --json
```
Example output:
```json
{"devices":[{"name":"/dev/sda","size":"2 GB","type":"disk","fstype":"","label":"","flags":[],"children":[{"name":"/dev/sda1","size":"2 GB","type":"part","fstype":"FAT32","label":"BOREDOS","flags":[]}]}]}
```
## How It Works
`lsblk` reads disk metadata through the disk syscalls exposed by BoredOS:
- `sys_disk_get_count()` gets the number of registered block devices.
- `sys_disk_get_info()` reads each device's name, size, type, FAT32 status, label, and flags.
The command treats non-partition entries as parent disks, then groups partition entries under the matching disk name. For example, `sda1` is displayed under `/dev/sda`.
Sizes are calculated from sector counts using 512-byte sectors, then formatted as `KB`, `MB`, or `GB`.

View file

@ -1,100 +0,0 @@
# Settings
Settings is a system configuration application providing a graphical interface to manage BoredOS preferences across multiple categories.
## Main Menu
The Settings application presents seven configuration categories, each with its own icon and panel:
- **Wallpaper** — Manage desktop background images and patterns
- **Network** — Configure network interfaces and connectivity
- **Desktop** — Control desktop layout and icon alignment
- **Mouse** — Adjust mouse speed and cursor appearance
- **Fonts** — Browse and select system fonts
- **Display** — Configure screen resolution
- **Keyboard** — Select keyboard layout
## Wallpaper
### Image Selection
- Browse wallpapers stored in `/Library/images/Wallpapers/`
- Supported formats: JPEG (`.jpg`)
- Thumbnails (80×50 pixels) are generated for preview
- Selected wallpaper is applied immediately to the desktop background
### Patterns
The Wallpaper panel provides built-in pattern options:
- **Lumberjack Pattern** — Checkered pattern with red, dark grey, and black colors
- **Blue Diamond Pattern** — Geometric diamond design
Patterns are rendered procedurally (128×128 pixels) and can be applied as alternatives to image wallpapers.
### Color Settings
Six color presets are available for quick selection, with RGB textbox inputs for custom color values.
## Network
### Configuration
- Set static IP address via textbox input
- Configure DNS server address
- Network status is displayed after initialization
- Settings are applied through the `NET_INIT`, `NET_SET_IP`, and `NET_SET_DNS` controls
## Desktop
### Layout Control
- **Snap to Grid** — Enable/disable automatic icon alignment to grid positions
- **Auto Align** — Automatically reorganize icons when enabled
- **Columns** — Adjust maximum number of columns for icon layout (configurable with +/- buttons)
- **Rows per Column** — Set maximum rows within each column
Desktop grid settings are stored as `desktop_max_rows_per_col` and `desktop_max_cols`.
## Mouse
### Cursor Control
- **Mouse Speed** — Adjust pointer movement sensitivity
- **Cursor Scale** — Increase or decrease cursor size using +/ buttons
- Settings communicates with the kernel WM using `SYSTEM_GET_CURSOR_SCALE` and `SYSTEM_SET_CURSOR_SCALE` syscalls
- Cursor changes are applied instantly and visibly in real-time
## Fonts
### System Fonts
- Browse available fonts from `/Library/Fonts/`
- Font list is dynamically loaded with scrollbar for navigation
- Each font displays an icon and name
- Fonts are listed with entry structures containing path and name information
## Display
### Resolution Selection
- Choose from dynamic resolution options based on physical screen size:
- 50% of screen resolution
- 75% of screen resolution
- Full screen resolution (100%)
- Custom resolution entry via textbox (width and height)
- Apply button commits the selected resolution change
## Keyboard
### Layout Selection
- Available keyboard layouts can be selected from a dropdown
- Layout state is maintained as `keyboard_layout`
- Selection applies to system-wide keyboard input
---
[Return to Documentation Index](../README.md)

View file

@ -7,88 +7,11 @@ The BoredOS Terminal provides a powerful command-line interface (CLI) for advanc
The default shell in BoredOS is **BoredShell (Bsh)**, a userspace shell with a dedicated terminal app. It features:
- **ANSI Color Support**: Rich text output with colors and styles.
- **Command History**: Use the **Up** and **Down** arrow keys to navigate through your previous commands (up to 64 history entries).
- **Redirection**:
- **Output Redirection**:
- `command > file`: Write output to a new file (or overwrite existing).
- `command >> file`: Append output to an existing file.
- `command < file`: Use a file as command input.
- **Piping**:
- `command1 | command2`: Pass the output of the first command as input to the second.
- **Command Chaining**:
- `cmd1 ; cmd2`: Run `cmd2` after `cmd1`.
- `cmd1 && cmd2`: Run `cmd2` only if `cmd1` succeeds.
- `cmd1 || cmd2`: Run `cmd2` only if `cmd1` fails.
- **Background Launch**:
- `command &`: Start command without blocking the prompt.
## Shell Operators & Scripting
Bsh provides a suite of operators for managing I/O and controlling execution flow.
### I/O Redirection
Redirection allows you to change where a command reads its input from or writes its output to by manipulating kernel-level file descriptors (FDs).
| Operator | Action | Description |
| :--- | :--- | :--- |
| `>` | **Overwrite** | Redirects standard output (FD 1) to a file, creating it if it doesn't exist or truncating it if it does. |
| `>>` | **Append** | Redirects standard output (FD 1) to a file, appending new data to the end without clearing existing content. |
| `<` | **Input** | Redirects standard input (FD 0) to read from a file instead of the terminal. |
**Example:**
```bash
echo "Hello World" > greetings.txt
echo "Second line" >> greetings.txt
cat < greetings.txt
```
**How it works:**
The shell uses `sys_open` to obtain a handle for the target file, then uses `sys_dup2` to replace the process's standard FD (0 or 1) with the new file handle. This ensures the command's standard library calls (like `printf` or `scanf`) interact with the file instead of the terminal.
### Pipelines (`|`)
Pipelines connect the output of one command directly to the input of another.
**Usage:** `cmd1 | cmd2 | cmd3`
**Example:**
```bash
ls /bin | cat | cat
```
**How it works:**
The shell creates an anonymous pipe using `sys_pipe`, which returns two FDs: a read end and a write end.
- The shell duplicates the **write end** to FD 1 for the first command.
- The shell duplicates the **read end** to FD 0 for the second command.
- Both commands run in parallel, and the kernel manages the data buffer between them.
### Execution Control
| Operator | Name | Description |
| :--- | :--- | :--- |
| `;` | **Semicolon** | Executes commands sequentially. `cmd2` runs only after `cmd1` finishes. |
| `&&` | **Logical AND** | Executes `cmd2` only if `cmd1` returns a success status (exit code 0). |
| `||` | **Logical OR** | Executes `cmd2` only if `cmd1` fails (returns a non-zero exit code). |
**Example:**
```bash
# Compile and run only on success
make && ./boredos.elf
# Recover from a missing command
missing_tool || echo "Tool not found, using fallback"
```
### Background Execution (`&`)
Appending `&` to a command tells the shell not to wait for the process to complete before returning to the prompt.
**How it works:**
Normally, the shell calls `sys_waitpid` to block until a child process exits. With `&`, the shell skips this wait, allowing the process to run asynchronously while you continue using the terminal.
Operator precedence follows common POSIX shell rules:
1. Redirection and pipelines (`<`, `>`, `>>`, `|`)
2. Conditionals (`&&`, `||`)
3. List separators (`;`, `&`)
### Bsh Configuration
@ -126,13 +49,11 @@ Below are some of the most used commands available in `/bin`:
| `ls` | List files and directories in the current path. |
| `cd` | Change the current working directory. |
| `cat` | Display the contents of a file. |
| `ls` | List directory contents. |
| `rm` | Remove a file. |
| `mkdir` | Create a new directory. |
| `man` | View the manual for a specific command (e.g., `man ls`). |
| `lsblk` | List block devices and partitions. |
| `du` | Report disk usage for files and directories. |
| `sysfetch` | Display system information and BoredOS branding. |
| `time` | Measure command execution time. |
| `sysfetch` | Display system and hardware information. |
---

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,22 +0,0 @@
Copyright (C) 2019-2026 Mintsuki and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,19 +0,0 @@
.POSIX:
SHELL=/bin/sh
CC=cc
CFLAGS=-g -O2 -pipe
CPPFLAGS=
LDFLAGS=
LIBS=
.PHONY: all
all: limine
.PHONY: clean
clean:
rm -f limine limine.exe
limine: limine.c
$(CC) $(CFLAGS) -std=c99 $(CPPFLAGS) $(LDFLAGS) $< $(LIBS) -o $@

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleIdentifier</key>
<string>com.apple.xcode.dsym.limine</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>dSYM</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View file

@ -1,5 +0,0 @@
---
triple: 'arm64-apple-darwin'
binary-path: limine
relocations: []
...

BIN
screenshot.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

BIN
splash.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View file

@ -18,7 +18,7 @@ process_jump_usermode:
; Build the IRETQ stack frame
; 1. SS (User Data Segment)
push 0x1B
push 0x23
; 2. RSP (User Stack)
push rsi
@ -27,7 +27,7 @@ process_jump_usermode:
push 0x202
; 4. CS (User Code Segment)
push 0x23
push 0x1B
; 5. RIP (Entry Point)
push rdi
@ -40,12 +40,6 @@ process_jump_usermode:
global context_switch_to
context_switch_to:
mov rsp, rdi
mov rcx, [rsp + 8192]
xor ecx, ecx
xgetbv
xrstor [rsp]
add rsp, 8192
add rsp, rcx
pop r15
pop r14

View file

@ -49,6 +49,7 @@ static void kconsole_putc_nolock(char c) {
cursor_x = 10;
cursor_y += CHAR_HEIGHT;
kconsole_scroll();
graphics_flip_buffer();
return;
}
@ -88,9 +89,12 @@ void kconsole_write(const char *s) {
spinlock_release_irqrestore(&console_lock, flags);
return;
}
while (*s) {
kconsole_putc_nolock(*s++);
}
// Flip once after a write batch to keep console updates coherent.
graphics_flip_buffer();
spinlock_release_irqrestore(&console_lock, flags);
}

View file

@ -10,13 +10,4 @@ void kconsole_putc(char c);
void kconsole_write(const char *s);
void kconsole_set_active(bool active);
void serial_write(const char *str);
void serial_write_num_locked(uint32_t n);
void serial_write_num(uint32_t n);
void serial_write_hex_locked(uint64_t n);
void serial_write_hex(uint64_t n);
void log_ok(const char *msg);
void log_fail(const char *msg);
#endif // KCONSOLE_H

View file

@ -5,56 +5,24 @@
#include "wm.h"
#include "io.h"
#include "../drivers/ACPI/acpi.h"
void *memset(void *dest, int val, size_t len) {
void k_memset(void *dest, int val, size_t len) {
unsigned char *ptr = (unsigned char *)dest;
while (len-- > 0) *ptr++ = (unsigned char)val;
return dest;
}
void *memcpy(void *dest, const void *src, size_t len) {
void k_memcpy(void *dest, const void *src, size_t len) {
unsigned char *d = (unsigned char *)dest;
const unsigned char *s = (const unsigned char *)src;
while (len-- > 0) *d++ = *s++;
return dest;
}
int memcmp(const void *str1, const void *str2, size_t count) {
register const unsigned char *s1 = (const unsigned char*)str1;
register const unsigned char *s2 = (const unsigned char*)str2;
while (count-- > 0) {
if (*s1++ != *s2++)
return s1[-1] < s2[-1] ? -1 : 1;
}
return 0;
}
void *memmove(void *dest, const void *src, uint64_t n) {
uint8_t *pdest = (uint8_t *)dest;
const uint8_t *psrc = (const uint8_t *)src;
if (src > dest) {
for (uint64_t i = 0; i < n; i++) {
pdest[i] = psrc[i];
}
} else if (src < dest) {
for (uint64_t i = n; i > 0; i--) {
pdest[i-1] = psrc[i-1];
}
}
return dest;
}
size_t strlen(const char *str) {
size_t k_strlen(const char *str) {
size_t len = 0;
while (str[len]) len++;
return len;
}
int strcmp(const char *s1, const char *s2) {
int k_strcmp(const char *s1, const char *s2) {
while (*s1 && (*s1 == *s2)) {
s1++;
s2++;
@ -62,7 +30,7 @@ int strcmp(const char *s1, const char *s2) {
return *(const unsigned char*)s1 - *(const unsigned char*)s2;
}
int strncmp(const char *s1, const char *s2, size_t n) {
int k_strncmp(const char *s1, const char *s2, size_t n) {
while (n && *s1 && (*s1 == *s2)) {
s1++;
s2++;
@ -72,26 +40,12 @@ int strncmp(const char *s1, const char *s2, size_t n) {
return *(const unsigned char*)s1 - *(const unsigned char*)s2;
}
void strcpy(char *dest, const char *src) {
void k_strcpy(char *dest, const char *src) {
while (*src) *dest++ = *src++;
*dest = 0;
}
char *strncpy(char *dest, const char *src, size_t n) {
size_t i;
for (i = 0; i < n && src[i] != '\0'; i++) {
dest[i] = src[i];
}
for (; i < n; i++) {
dest[i] = '\0';
}
return dest;
}
int atoi(const char *str) {
int k_atoi(const char *str) {
int res = 0;
int sign = 1;
if (*str == '-') { sign = -1; str++; }
@ -102,7 +56,7 @@ int atoi(const char *str) {
return res * sign;
}
void itoa(int n, char *buf) {
void k_itoa(int n, char *buf) {
if (n == 0) {
buf[0] = '0'; buf[1] = 0; return;
}
@ -122,7 +76,7 @@ void itoa(int n, char *buf) {
}
}
void itoa_hex(uint64_t n, char *buf) {
void k_itoa_hex(uint64_t n, char *buf) {
const char *digits = "0123456789ABCDEF";
if (n == 0) {
buf[0] = '0';
@ -164,7 +118,9 @@ void k_reboot(void) {
}
void k_shutdown(void) {
acpi_shutdown();
outw(0xB004, 0x2000); // QEMU older / some pc machines
outw(0x604, 0x2000); // QEMU newer (i440fx/q35)
outw(0x4004, 0x3400); // VirtualBox fallback
}
volatile uint64_t beep_end_tick = 0;

View file

@ -9,18 +9,15 @@
#include <stdbool.h>
// Kernel string utilities
void *memmove(void *dest, const void *src, uint64_t n);
void *memset(void *dest, int val, size_t len);
void *memcpy(void *dest, const void *src, size_t len);
int memcmp (const void *str1, const void *str2, size_t count);
size_t strlen(const char *str);
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
void strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);
int atoi(const char *str);
void itoa(int n, char *buf);
void itoa_hex(uint64_t n, char *buf);
void k_memset(void *dest, int val, size_t len);
void k_memcpy(void *dest, const void *src, size_t len);
size_t k_strlen(const char *str);
int k_strcmp(const char *s1, const char *s2);
int k_strncmp(const char *s1, const char *s2, size_t n);
void k_strcpy(char *dest, const char *src);
int k_atoi(const char *str);
void k_itoa(int n, char *buf);
void k_itoa_hex(uint64_t n, char *buf);
// Kernel timing utilities
void k_delay(int iterations);

View file

@ -26,7 +26,6 @@
#include "smp.h"
#include "work_queue.h"
#include "lapic.h"
#include "panic.h"
#include "fs/sysfs.h"
#include "fs/procfs.h"
#include "fs/bootfs.h"
@ -35,7 +34,6 @@
#include "sys/bootfs_state.h"
#include "input/keymap.h"
#include "input/keyboard.h"
#include "../drivers/ACPI/acpi.h"
extern void sysfs_init_subsystems(void);
@ -80,12 +78,6 @@ static volatile struct limine_kernel_file_request kernel_file_request = {
.revision = 0
};
__attribute__((used, section(".requests")))
volatile struct limine_rsdp_request acpi_rsdp_request = {
.id = LIMINE_RSDP_REQUEST,
.revision = 0
};
__attribute__((used, section(".requests_start")))
static volatile struct limine_request *const requests_start_marker[] = {
(struct limine_request *)&framebuffer_request,
@ -94,7 +86,6 @@ static volatile struct limine_request *const requests_start_marker[] = {
(struct limine_request *)&smp_request,
(struct limine_request *)&bootloader_info_request,
(struct limine_request *)&kernel_file_request,
(struct limine_request *)&acpi_rsdp_request,
NULL
};
@ -134,7 +125,7 @@ void serial_write(const char *str) {
spinlock_release_irqrestore(&serial_lock, flags);
}
void serial_write_num_locked(uint32_t n) {
static void serial_write_num_locked(uint32_t n) {
if (n >= 10) serial_write_num_locked(n / 10);
char c = '0' + (n % 10);
while ((inb(0x3F8 + 5) & 0x20) == 0);
@ -148,7 +139,7 @@ void serial_write_num(uint32_t n) {
spinlock_release_irqrestore(&serial_lock, flags);
}
void serial_write_hex_locked(uint64_t n) {
static void serial_write_hex_locked(uint64_t n) {
char *hex = "0123456789ABCDEF";
if (n >= 16) serial_write_hex_locked(n / 16);
char c = hex[n % 16];
@ -184,25 +175,34 @@ void log_fail(const char *msg) {
}
static void print_verbose_boot_banner(void) {
kconsole_set_color(0xFF473ba3);
serial_write(" @@@@\n");
serial_write(" @@@@@@@\n");
serial_write(" @@@@@@\n");
serial_write(" @@@@@@@\n");
serial_write(" @@@@@@@ @@@@@@\n");
serial_write(" @@@@@@ @@@@@@@@@@@@\n");
serial_write(" @@@@@@ @@@@@@@@@@@@@@a\n");
serial_write(" @@@@@@@@@@@X @@@@@@@@w\n");
serial_write(" @@@@@@@@ @@@@@@@\n");
serial_write(" @@@@@@M @@@@@@\n");
serial_write(" @@@@@@@ @@@@@@\n");
serial_write(" @@@@@@@ @@@@@@@@\n");
serial_write(" @@@@@@@@@@@@@@@@@@\n");
serial_write(" i@@@@@@@@@@@@@@@\n");
serial_write(" @@@@@@@\n");
serial_write(" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
serial_write(" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
serial_write(" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
kconsole_set_color(0xFFB589D6);
serial_write("==================== ");
kconsole_set_color(0xFFFFFFFF);
serial_write("__ ____ ____ \n");
kconsole_set_color(0xFFB589D6);
serial_write("=================== ");
kconsole_set_color(0xFFFFFFFF);
serial_write("/ /_ / __ \\/ ___\\\n");
kconsole_set_color(0xFF569CD6);
serial_write("================== ");
kconsole_set_color(0xFFFFFFFF);
serial_write("/ __ \\/ / / /\\___ \\\n");
kconsole_set_color(0xFF569CD6);
serial_write("================= ");
kconsole_set_color(0xFFFFFFFF);
serial_write("/ /_/ / /_/ /____/ /\n");
kconsole_set_color(0xFF4EC9B0);
serial_write("================ ");
kconsole_set_color(0xFFFFFFFF);
serial_write("/_.___/\\____//_____/ \n");
kconsole_set_color(0xFF4EC9B0);
serial_write("=============== \n");
kconsole_set_color(0xFFFFFFFF);
serial_write("\n");
}
@ -235,78 +235,48 @@ static void fat32_mkdir_recursive(const char *path) {
}
}
static bool cmdline_has_flag(const char *cmdline, const char *flag) {
if (!cmdline || !flag || !flag[0]) return false;
int flag_len = (int)strlen(flag);
const char *p = cmdline;
while (*p) {
while (*p == ' ') p++;
if (!*p) break;
const char *start = p;
while (*p && *p != ' ') p++;
int len = (int)(p - start);
if (len == flag_len && strncmp(start, flag, (size_t)flag_len) == 0) return true;
}
return false;
}
static bool usage_policy_prompt(void) {
kconsole_set_active(true);
serial_write("BoredOS - Please read the Usage Policy provided with this software before continuing.\n");
serial_write("Do you agree to the terms? [y/N]\n");
static bool cmdline_read_value(const char *cmdline, const char *key, char *out, int out_len) {
if (!cmdline || !key || !out || out_len <= 1) return false;
int key_len = (int)strlen(key);
const char *p = cmdline;
while (*p) {
while (*p == ' ') p++;
if (!*p) break;
if (strncmp(p, key, (size_t)key_len) == 0) {
const char *val = p + key_len;
int i = 0;
while (*val && *val != ' ' && i < out_len - 1) {
out[i++] = *val++;
}
out[i] = '\0';
return i > 0;
while (1) {
if ((inb(0x64) & 1) == 0) {
asm volatile("pause");
continue;
}
while (*p && *p != ' ') p++;
}
return false;
}
static void boot_parse_cmdline(const char *cmdline, uint32_t media_type) {
g_bootfs_state.boot_flags = 0;
g_bootfs_state.root_device[0] = '\0';
char root_arg[32];
if (cmdline_read_value(cmdline, "root=", root_arg, (int)sizeof(root_arg))) {
const char *dev = root_arg;
if (dev[0] == '/' && dev[1] == 'd' && dev[2] == 'e' && dev[3] == 'v' && dev[4] == '/') {
dev += 5;
uint8_t scancode = inb(0x60);
keyboard_event_t ev;
if (!keyboard_handle_set1_scancode(scancode, &ev)) {
continue;
}
int i = 0;
while (dev[i] && i < (int)sizeof(g_bootfs_state.root_device) - 1) {
g_bootfs_state.root_device[i] = dev[i];
i++;
if (!ev.pressed) {
continue;
}
g_bootfs_state.root_device[i] = '\0';
if (i > 0) g_bootfs_state.boot_flags |= BOOT_FLAG_ROOT_SET;
}
bool force_live = cmdline_has_flag(cmdline, "--live");
bool force_disk = cmdline_has_flag(cmdline, "--disk");
if (ev.keycode == KEY_ENTER || ev.keycode == KEY_KP_ENTER) {
serial_write("\n");
return false;
}
if (force_live) {
g_bootfs_state.boot_flags |= BOOT_FLAG_LIVE | BOOT_FLAG_FORCED;
} else if (force_disk) {
g_bootfs_state.boot_flags |= BOOT_FLAG_DISK | BOOT_FLAG_FORCED;
} else if (g_bootfs_state.boot_flags & BOOT_FLAG_ROOT_SET) {
g_bootfs_state.boot_flags |= BOOT_FLAG_DISK;
} else if (media_type == LIMINE_MEDIA_TYPE_OPTICAL || media_type == LIMINE_MEDIA_TYPE_TFTP) {
g_bootfs_state.boot_flags |= BOOT_FLAG_LIVE;
} else {
g_bootfs_state.boot_flags |= BOOT_FLAG_DISK;
if (!ev.is_text) {
continue;
}
if (ev.codepoint == 'y' || ev.codepoint == 'Y') {
serial_write("\n");
return true;
}
if (ev.codepoint == 'n' || ev.codepoint == 'N') {
serial_write("\n");
return false;
}
}
}
void kmain(void) {
init_serial();
vfs_init();
@ -375,12 +345,27 @@ void kmain(void) {
kconsole_set_color(0xFFFFFF55);
serial_write("Welcome to BoredOS!\n");
kconsole_set_color(0xFFFFFFFF);
acpi_init();
process_init();
fat32_init();
log_ok("FAT32 ready");
fat32_mkdir("/bin");
fat32_mkdir("/Library");
fat32_mkdir("/Library/images");
fat32_mkdir("/Library/images/Wallpapers");
fat32_mkdir("/Library/images/gif");
fat32_mkdir("/Library/Fonts");
fat32_mkdir("/Library/DOOM");
fat32_mkdir("/Library/conf");
fat32_mkdir("/Library/bsh");
fat32_mkdir("/docs");
fat32_mkdir("/root");
fat32_mkdir("/root/Desktop");
fat32_mkdir("/root/Pictures");
fat32_mkdir("/root/Documents");
fat32_mkdir("/root/Downloads");
sysfs_init_subsystems();
vfs_mount("/sys", "sysfs", "sysfs", sysfs_get_ops(), NULL);
@ -390,10 +375,10 @@ void kmain(void) {
if (bootloader_info_request.response != NULL) {
if (bootloader_info_request.response->name) {
strcpy(g_bootfs_state.bootloader_name, bootloader_info_request.response->name);
k_strcpy(g_bootfs_state.bootloader_name, bootloader_info_request.response->name);
}
if (bootloader_info_request.response->version) {
strcpy(g_bootfs_state.bootloader_version, bootloader_info_request.response->version);
k_strcpy(g_bootfs_state.bootloader_version, bootloader_info_request.response->version);
}
}
@ -403,14 +388,6 @@ void kmain(void) {
serial_write_hex(g_bootfs_state.kernel_size);
serial_write(" bytes\n");
}
if (kernel_file_request.response != NULL && kernel_file_request.response->kernel_file != NULL) {
const char *cmdline = kernel_file_request.response->kernel_file->cmdline;
uint32_t media_type = kernel_file_request.response->kernel_file->media_type;
boot_parse_cmdline(cmdline, media_type);
} else {
boot_parse_cmdline(NULL, LIMINE_MEDIA_TYPE_GENERIC);
}
extern uint32_t wm_get_ticks(void);
g_bootfs_state.boot_time_ms = wm_get_ticks();
@ -438,7 +415,6 @@ void kmain(void) {
if (path_len >= 5 && path[path_len-4] == '.' && path[path_len-3] == 't' &&
path[path_len-2] == 'a' && path[path_len-1] == 'r') {
g_bootfs_state.initrd_size = mod->size;
g_bootfs_state.initrd_ptr = mod->address;
serial_write("[INIT] -> Initrd detected\n");
}
}
@ -493,23 +469,16 @@ void kmain(void) {
serial_write_hex(current_rsp);
serial_write("\n");
serial_write("[DBG] before graphics_init_fonts\n");
graphics_init_fonts();
serial_write("[DBG] after graphics_init_fonts\n");
serial_write("[DBG] before ps2_init\n");
asm("cli");
ps2_init();
serial_write("[DBG] after ps2_init\n");
asm("sti"); // Enable interrupts
asm("sti");
serial_write("[DBG] before keymap_init\n");
keymap_init();
serial_write("[DBG] after keymap_init\n");
serial_write("[INIT] Keymap initialized\n");
serial_write("[INIT] Keymap initialized");
serial_write("[DBG] before lapic_init\n");
lapic_init();
serial_write("[DBG] after lapic_init\n");
if (smp_request.response != NULL) {
uint32_t online = smp_init(smp_request.response);
@ -519,6 +488,10 @@ void kmain(void) {
smp_init(NULL);
}
if (!usage_policy_prompt()) {
log_fail("Usage policy not accepted, halting");
hcf();
}
wm_init();
@ -527,5 +500,10 @@ void kmain(void) {
extern void bootfs_refresh_from_disk(void);
bootfs_refresh_from_disk();
wm_run_loop();
while (1) {
wm_process_input();
wm_process_deferred_thumbs();
wallpaper_process_pending();
asm("hlt");
}
}

View file

@ -17,98 +17,129 @@ static void draw_string_centered(int y, const char *s, uint32_t color) {
}
void kernel_panic(registers_t *regs, const char *error_name) {
// Disable interrupts to prevent nested panics
asm volatile("cli");
// Clear back buffer to black
graphics_clear_back_buffer(0x00000000);
int sh = get_screen_height();
int cy = sh / 2;
// Header
// Draw header
draw_string_centered(cy - 150, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", 0xFFFF0000);
draw_string_centered(cy - 130, "KERNEL PANIC", 0xFFFFFFFF);
draw_string_centered(cy - 130, "KERNEL EXCEPTION OCCURRED", 0xFFFFFFFF);
draw_string_centered(cy - 110, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", 0xFFFF0000);
// Error name
char buf[256];
char err_buf[256];
int pos = 0;
const char *prefix = "Error: ";
while (prefix[pos]) { buf[pos] = prefix[pos]; pos++; }
const char *prefix = "Exception: ";
while(prefix[pos]) { err_buf[pos] = prefix[pos]; pos++; }
int i = 0;
while (error_name[i]) { buf[pos++] = error_name[i++]; }
buf[pos] = 0;
draw_string_centered(cy - 70, buf, 0xFFFFCC00);
while(error_name[i]) { err_buf[pos++] = error_name[i++]; }
err_buf[pos] = 0;
draw_string_centered(cy - 70, err_buf, 0xFFFFCC00);
if (regs != NULL) {
// Exception details
char info_buf[64];
const char *digits = "0123456789ABCDEF";
// Details - simplified centering by drawing them as a block or individually centered
char info_buf[64];
// Vector
pos = 0;
prefix = "Vector: ";
while(prefix[pos]) { info_buf[pos] = prefix[pos]; pos++; }
uint64_t v = regs->int_no;
const char* digits = "0123456789ABCDEF";
info_buf[pos++] = '0'; info_buf[pos++] = 'x';
for (int i = 15; i >= 0; i--) {
info_buf[pos + i] = digits[v & 0xF];
v >>= 4;
}
info_buf[pos + 16] = 0;
draw_string_centered(cy - 40, info_buf, 0xFFFFFFFF);
#define FMT_HEX(prefix_str, value, color, y_offset) \
do { \
int pos = 0; \
const char *pfx = (prefix_str); \
while (pfx[pos]) { info_buf[pos] = pfx[pos]; pos++; } \
info_buf[pos++] = '0'; info_buf[pos++] = 'x'; \
uint64_t _v = (value); \
for (int _i = 15; _i >= 0; _i--) { \
info_buf[pos + _i] = digits[_v & 0xF]; _v >>= 4; \
} \
info_buf[pos + 16] = 0; \
draw_string_centered((y_offset), info_buf, (color)); \
} while (0)
// Error Code
pos = 0;
prefix = "Error Code: ";
while(prefix[pos]) { info_buf[pos] = prefix[pos]; pos++; }
v = regs->err_code;
info_buf[pos++] = '0'; info_buf[pos++] = 'x';
for (int i = 15; i >= 0; i--) {
info_buf[pos + i] = digits[v & 0xF];
v >>= 4;
}
info_buf[pos + 16] = 0;
draw_string_centered(cy - 20, info_buf, 0xFFFFFFFF);
FMT_HEX("Vector: ", regs->int_no, 0xFFFFFFFF, cy - 40);
FMT_HEX("Error Code: ", regs->err_code, 0xFFFFFFFF, cy - 20);
FMT_HEX("RIP: ", regs->rip, 0xFFFFFFFF, cy);
// RIP
pos = 0;
prefix = "RIP: ";
while(prefix[pos]) { info_buf[pos] = prefix[pos]; pos++; }
v = regs->rip;
info_buf[pos++] = '0'; info_buf[pos++] = 'x';
for (int i = 15; i >= 0; i--) {
info_buf[pos + i] = digits[v & 0xF];
v >>= 4;
}
info_buf[pos + 16] = 0;
draw_string_centered(cy, info_buf, 0xFFFFFFFF);
if (regs->int_no == 14) {
uint64_t cr2;
asm volatile("mov %%cr2, %0" : "=r"(cr2));
FMT_HEX("CR2: ", cr2, 0xFFFF5555, cy + 20);
// CR2 for page faults
if (regs->int_no == 14) {
uint64_t cr2;
asm volatile("mov %%cr2, %0" : "=r"(cr2));
pos = 0;
prefix = "CR2: ";
while(prefix[pos]) { info_buf[pos] = prefix[pos]; pos++; }
info_buf[pos++] = '0'; info_buf[pos++] = 'x';
for (int i = 15; i >= 0; i--) {
info_buf[pos + i] = digits[cr2 & 0xF];
cr2 >>= 4;
}
#undef FMT_HEX
info_buf[pos + 16] = 0;
draw_string_centered(cy + 20, info_buf, 0xFFFF5555);
}
// Message
draw_string_centered(cy + 100, "The system has been halted to prevent damage.", 0xFFFFFFFF);
draw_string_centered(cy + 120, "Please restart your computer.", 0xFFAAAAAA);
// Flip buffer to screen
graphics_mark_screen_dirty();
graphics_flip_buffer();
char hex_buf[17];
serial_write("\n*** KERNEL PANIC ***\n");
serial_write(error_name);
serial_write("\n");
if (regs != NULL) {
char hex_buf[17];
serial_write("Vector: 0x");
k_itoa_hex(regs->int_no, hex_buf);
serial_write(hex_buf);
serial_write("\n");
serial_write("Vector: 0x");
itoa_hex(regs->int_no, hex_buf);
serial_write("Error Code: 0x");
k_itoa_hex(regs->err_code, hex_buf);
serial_write(hex_buf);
serial_write("\n");
serial_write("RIP: 0x");
k_itoa_hex(regs->rip, hex_buf);
serial_write(hex_buf);
serial_write("\n");
if (regs->int_no == 14) {
uint64_t cr2;
asm volatile("mov %%cr2, %0" : "=r"(cr2));
serial_write("CR2: 0x");
k_itoa_hex(cr2, hex_buf);
serial_write(hex_buf);
serial_write("\n");
serial_write("Error Code: 0x");
itoa_hex(regs->err_code, hex_buf);
serial_write(hex_buf);
serial_write("\n");
serial_write("RIP: 0x");
itoa_hex(regs->rip, hex_buf);
serial_write(hex_buf);
serial_write("\n");
if (regs->int_no == 14) {
uint64_t cr2;
asm volatile("mov %%cr2, %0" : "=r"(cr2));
serial_write("CR2: 0x");
itoa_hex(cr2, hex_buf);
serial_write(hex_buf);
serial_write("\n");
}
}
while (1) {
// Halt
while(1) {
asm volatile("cli; hlt");
}
}

View file

@ -1,10 +0,0 @@
#ifndef PANIC_H
#define PANIC_H
#include "io.h"
#include "kutils.h"
#include "../sys/syscall.h"
void kernel_panic(registers_t *regs, const char *error_name);
#endif

View file

@ -105,48 +105,48 @@ void platform_get_cpu_flags(char *flags_str) {
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1));
// ECX flags
if (ecx & (1 << 0)) strcpy(flags_str + strlen(flags_str), "sse3 ");
if (ecx & (1 << 1)) strcpy(flags_str + strlen(flags_str), "pclmulqdq ");
if (ecx & (1 << 3)) strcpy(flags_str + strlen(flags_str), "monitor ");
if (ecx & (1 << 6)) strcpy(flags_str + strlen(flags_str), "ssse3 ");
if (ecx & (1 << 9)) strcpy(flags_str + strlen(flags_str), "sdbg ");
if (ecx & (1 << 12)) strcpy(flags_str + strlen(flags_str), "fma ");
if (ecx & (1 << 13)) strcpy(flags_str + strlen(flags_str), "cx16 ");
if (ecx & (1 << 19)) strcpy(flags_str + strlen(flags_str), "sse4_1 ");
if (ecx & (1 << 20)) strcpy(flags_str + strlen(flags_str), "sse4_2 ");
if (ecx & (1 << 23)) strcpy(flags_str + strlen(flags_str), "popcnt ");
if (ecx & (1 << 25)) strcpy(flags_str + strlen(flags_str), "aes ");
if (ecx & (1 << 26)) strcpy(flags_str + strlen(flags_str), "xsave ");
if (ecx & (1 << 28)) strcpy(flags_str + strlen(flags_str), "avx ");
if (ecx & (1 << 0)) k_strcpy(flags_str + k_strlen(flags_str), "sse3 ");
if (ecx & (1 << 1)) k_strcpy(flags_str + k_strlen(flags_str), "pclmulqdq ");
if (ecx & (1 << 3)) k_strcpy(flags_str + k_strlen(flags_str), "monitor ");
if (ecx & (1 << 6)) k_strcpy(flags_str + k_strlen(flags_str), "ssse3 ");
if (ecx & (1 << 9)) k_strcpy(flags_str + k_strlen(flags_str), "sdbg ");
if (ecx & (1 << 12)) k_strcpy(flags_str + k_strlen(flags_str), "fma ");
if (ecx & (1 << 13)) k_strcpy(flags_str + k_strlen(flags_str), "cx16 ");
if (ecx & (1 << 19)) k_strcpy(flags_str + k_strlen(flags_str), "sse4_1 ");
if (ecx & (1 << 20)) k_strcpy(flags_str + k_strlen(flags_str), "sse4_2 ");
if (ecx & (1 << 23)) k_strcpy(flags_str + k_strlen(flags_str), "popcnt ");
if (ecx & (1 << 25)) k_strcpy(flags_str + k_strlen(flags_str), "aes ");
if (ecx & (1 << 26)) k_strcpy(flags_str + k_strlen(flags_str), "xsave ");
if (ecx & (1 << 28)) k_strcpy(flags_str + k_strlen(flags_str), "avx ");
// EDX flags
if (edx & (1 << 0)) strcpy(flags_str + strlen(flags_str), "fpu ");
if (edx & (1 << 3)) strcpy(flags_str + strlen(flags_str), "pse ");
if (edx & (1 << 4)) strcpy(flags_str + strlen(flags_str), "tsc ");
if (edx & (1 << 6)) strcpy(flags_str + strlen(flags_str), "pae ");
if (edx & (1 << 8)) strcpy(flags_str + strlen(flags_str), "cx8 ");
if (edx & (1 << 9)) strcpy(flags_str + strlen(flags_str), "apic ");
if (edx & (1 << 11)) strcpy(flags_str + strlen(flags_str), "sep ");
if (edx & (1 << 15)) strcpy(flags_str + strlen(flags_str), "cmov ");
if (edx & (1 << 23)) strcpy(flags_str + strlen(flags_str), "mmx ");
if (edx & (1 << 24)) strcpy(flags_str + strlen(flags_str), "fxsr ");
if (edx & (1 << 25)) strcpy(flags_str + strlen(flags_str), "sse ");
if (edx & (1 << 26)) strcpy(flags_str + strlen(flags_str), "sse2 ");
if (edx & (1 << 0)) k_strcpy(flags_str + k_strlen(flags_str), "fpu ");
if (edx & (1 << 3)) k_strcpy(flags_str + k_strlen(flags_str), "pse ");
if (edx & (1 << 4)) k_strcpy(flags_str + k_strlen(flags_str), "tsc ");
if (edx & (1 << 6)) k_strcpy(flags_str + k_strlen(flags_str), "pae ");
if (edx & (1 << 8)) k_strcpy(flags_str + k_strlen(flags_str), "cx8 ");
if (edx & (1 << 9)) k_strcpy(flags_str + k_strlen(flags_str), "apic ");
if (edx & (1 << 11)) k_strcpy(flags_str + k_strlen(flags_str), "sep ");
if (edx & (1 << 15)) k_strcpy(flags_str + k_strlen(flags_str), "cmov ");
if (edx & (1 << 23)) k_strcpy(flags_str + k_strlen(flags_str), "mmx ");
if (edx & (1 << 24)) k_strcpy(flags_str + k_strlen(flags_str), "fxsr ");
if (edx & (1 << 25)) k_strcpy(flags_str + k_strlen(flags_str), "sse ");
if (edx & (1 << 26)) k_strcpy(flags_str + k_strlen(flags_str), "sse2 ");
// Extended leaf 0x80000001 for advanced flags
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(0x80000001));
if (edx & (1 << 11)) strcpy(flags_str + strlen(flags_str), "syscall ");
if (edx & (1 << 20)) strcpy(flags_str + strlen(flags_str), "nx ");
if (edx & (1 << 26)) strcpy(flags_str + strlen(flags_str), "pdpe1gb ");
if (edx & (1 << 27)) strcpy(flags_str + strlen(flags_str), "rdtscp ");
if (edx & (1 << 29)) strcpy(flags_str + strlen(flags_str), "lm ");
if (edx & (1 << 11)) k_strcpy(flags_str + k_strlen(flags_str), "syscall ");
if (edx & (1 << 20)) k_strcpy(flags_str + k_strlen(flags_str), "nx ");
if (edx & (1 << 26)) k_strcpy(flags_str + k_strlen(flags_str), "pdpe1gb ");
if (edx & (1 << 27)) k_strcpy(flags_str + k_strlen(flags_str), "rdtscp ");
if (edx & (1 << 29)) k_strcpy(flags_str + k_strlen(flags_str), "lm ");
if (ecx & (1 << 0)) strcpy(flags_str + strlen(flags_str), "lahf_lm ");
if (ecx & (1 << 5)) strcpy(flags_str + strlen(flags_str), "abm ");
if (ecx & (1 << 0)) k_strcpy(flags_str + k_strlen(flags_str), "lahf_lm ");
if (ecx & (1 << 5)) k_strcpy(flags_str + k_strlen(flags_str), "abm ");
// Remove trailing space
int len = strlen(flags_str);
int len = k_strlen(flags_str);
if (len > 0 && flags_str[len-1] == ' ') {
flags_str[len-1] = '\0';
}

View file

@ -13,10 +13,10 @@ void get_os_info(os_info_t *info) {
for (size_t i = 0; i < sizeof(os_info_t); i++) p[i] = 0;
const char *os_name = "BoredOS";
const char *os_version = "26.5.1-dev";
const char *os_version = "26.5.1-stable";
const char *os_codename = "Genesis";
const char *kernel_name = "Boredkernel";
const char *kernel_version = "4.2.1-dev";
const char *kernel_version = "4.2.1-stable";
const char *build_date = __DATE__;
const char *build_time = __TIME__;
const char *build_arch = "x86_64";

View file

@ -46,9 +46,10 @@ static void ahci_strcpy(char *d, const char *s) {
// Kernel virtual to physical address conversion
extern uint64_t v2p(uint64_t vaddr);
extern uint64_t p2v(uint64_t paddr);
static int ahci_disk_sync(Disk *disk);
static int ahci_find_free_slot(HBA_PORT *port);
// ============================================================================
// Port Setup
// ============================================================================
static void ahci_stop_cmd(HBA_PORT *port) {
// Clear ST (Start)
@ -115,17 +116,17 @@ static void ahci_port_rebase(ahci_port_state_t *ps) {
port->fb = (uint32_t)(fb_phys & 0xFFFFFFFF);
port->fbu = (uint32_t)(fb_phys >> 32);
int cmd_tbl_size = sizeof(HBA_CMD_TBL) + 32 * sizeof(HBA_PRDT_ENTRY);
// Allocate command table for slot 0 (256-byte aligned, room for 8 PRDT entries)
int cmd_tbl_size = sizeof(HBA_CMD_TBL) + 8 * sizeof(HBA_PRDT_ENTRY);
ps->cmd_tbl = (HBA_CMD_TBL*)kmalloc_aligned(cmd_tbl_size, 256);
if (!ps->cmd_tbl) return;
mem_memset(ps->cmd_tbl, 0, cmd_tbl_size);
// Set command header 0 to point to our command table
uint64_t ctba_phys = v2p((uint64_t)ps->cmd_tbl);
for (int i = 0; i < 32; i++) {
ps->cmd_list[i].ctba = (uint32_t)(ctba_phys & 0xFFFFFFFF);
ps->cmd_list[i].ctbau = (uint32_t)(ctba_phys >> 32);
ps->cmd_list[i].prdtl = 1;
}
ps->cmd_list[0].ctba = (uint32_t)(ctba_phys & 0xFFFFFFFF);
ps->cmd_list[0].ctbau = (uint32_t)(ctba_phys >> 32);
ps->cmd_list[0].prdtl = 1; // 1 PRDT entry default
// Clear error and interrupt status
port->serr = 0xFFFFFFFF;
@ -134,6 +135,10 @@ static void ahci_port_rebase(ahci_port_state_t *ps) {
ahci_start_cmd(port);
}
// ============================================================================
// Sector I/O
// ============================================================================
static int ahci_find_free_slot(HBA_PORT *port) {
uint32_t slots = (port->sact | port->ci);
for (int i = 0; i < 32; i++) {
@ -142,76 +147,6 @@ static int ahci_find_free_slot(HBA_PORT *port) {
return -1;
}
static int ahci_identify(int port_num, uint32_t *sectors, char *model) {
ahci_port_state_t *ps = &ports[port_num];
HBA_PORT *port = ps->port;
uint64_t rflags = spinlock_acquire_irqsave(&ps->lock);
port->is = 0xFFFFFFFF;
int slot = ahci_find_free_slot(port);
if (slot < 0) { spinlock_release_irqrestore(&ps->lock, rflags); return -1; }
HBA_CMD_HEADER *cmd_hdr = &ps->cmd_list[slot];
cmd_hdr->cfl = sizeof(FIS_REG_H2D) / sizeof(uint32_t);
cmd_hdr->w = 0;
cmd_hdr->prdtl = 1;
HBA_CMD_TBL *cmd_tbl = ps->cmd_tbl;
mem_memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + 32 * sizeof(HBA_PRDT_ENTRY));
uint16_t *buf = (uint16_t*)kmalloc_aligned(512, 512);
uint64_t phys = v2p((uint64_t)buf);
cmd_tbl->prdt[0].dba = (uint32_t)(phys & 0xFFFFFFFF);
cmd_tbl->prdt[0].dbau = (uint32_t)(phys >> 32);
cmd_tbl->prdt[0].dbc = 511; // 512 bytes
cmd_tbl->prdt[0].i = 1;
FIS_REG_H2D *fis = (FIS_REG_H2D*)(&cmd_tbl->cfis);
fis->fis_type = FIS_TYPE_REG_H2D;
fis->c = 1; // Command
fis->command = 0xEC; // IDENTIFY DEVICE
// Wait for port to be idle
int timeout = 1000000;
while ((port->tfd & (ATA_SR_BSY | ATA_SR_DRQ)) && --timeout > 0);
if (timeout <= 0) { kfree(buf); spinlock_release_irqrestore(&ps->lock, rflags); return -1; }
port->ci = (1 << slot);
while (1) {
if ((port->ci & (1 << slot)) == 0) break;
if (port->is & HBA_PORT_IS_TFES) { kfree(buf); spinlock_release_irqrestore(&ps->lock, rflags); return -1; }
}
// Extract sectors (28-bit LBA for now, or 48-bit if supported)
uint32_t s28 = *((uint32_t*)&buf[60]);
uint64_t s48 = *((uint64_t*)&buf[100]);
if (s48 > 0) *sectors = (uint32_t)s48;
else *sectors = s28;
// Extract model name (Words 27-46, 40 bytes, big-endian shorts)
for (int i = 0; i < 20; i++) {
model[i*2] = (char)(buf[27+i] >> 8);
model[i*2+1] = (char)(buf[27+i] & 0xFF);
}
model[40] = 0;
// Swap bytes in model string (ATA strings are byte-swapped)
for (int i = 0; i < 40; i += 2) {
char tmp = model[i];
model[i] = model[i+1];
model[i+1] = tmp;
}
kfree(buf);
spinlock_release_irqrestore(&ps->lock, rflags);
return 0;
}
int ahci_read_sectors(int port_num, uint64_t lba, uint32_t count, uint8_t *buffer) {
if (!ahci_initialized || port_num < 0 || port_num >= MAX_AHCI_PORTS) return -1;
ahci_port_state_t *ps = &ports[port_num];
@ -232,38 +167,14 @@ int ahci_read_sectors(int port_num, uint64_t lba, uint32_t count, uint8_t *buffe
cmd_hdr->prdtl = 1;
HBA_CMD_TBL *cmd_tbl = ps->cmd_tbl;
mem_memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + 32 * sizeof(HBA_PRDT_ENTRY));
mem_memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + sizeof(HBA_PRDT_ENTRY));
extern uint64_t paging_get_pml4_phys(void);
extern uint64_t paging_virt2phys(uint64_t pml4_phys, uint64_t virtual_addr);
uint64_t pml4 = paging_get_pml4_phys();
uint64_t buf_addr = (uint64_t)buffer;
uint32_t remaining = count * 512;
int prd_idx = 0;
while (remaining > 0 && prd_idx < 32) {
uint64_t phys = paging_virt2phys(pml4, buf_addr);
if (!phys) {
spinlock_release_irqrestore(&ps->lock, rflags);
return -1;
}
uint32_t offset = buf_addr & 0xFFF;
uint32_t can_do = 4096 - offset;
if (can_do > remaining) can_do = remaining;
cmd_tbl->prdt[prd_idx].dba = (uint32_t)(phys & 0xFFFFFFFF);
cmd_tbl->prdt[prd_idx].dbau = (uint32_t)(phys >> 32);
cmd_tbl->prdt[prd_idx].dbc = can_do - 1; // 0-based
cmd_tbl->prdt[prd_idx].i = 0;
buf_addr += can_do;
remaining -= can_do;
prd_idx++;
}
if (prd_idx > 0) cmd_tbl->prdt[prd_idx - 1].i = 1; // Interrupt on last
cmd_hdr->prdtl = prd_idx;
// Setup PRDT
uint64_t buf_phys = v2p((uint64_t)buffer);
cmd_tbl->prdt[0].dba = (uint32_t)(buf_phys & 0xFFFFFFFF);
cmd_tbl->prdt[0].dbau = (uint32_t)(buf_phys >> 32);
cmd_tbl->prdt[0].dbc = (count * 512) - 1; // 0-based byte count
cmd_tbl->prdt[0].i = 1;
// Setup Command FIS
FIS_REG_H2D *fis = (FIS_REG_H2D*)&cmd_tbl->cfis;
@ -327,39 +238,13 @@ int ahci_write_sectors(int port_num, uint64_t lba, uint32_t count, const uint8_t
cmd_hdr->prdtl = 1;
HBA_CMD_TBL *cmd_tbl = ps->cmd_tbl;
mem_memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + 32 * sizeof(HBA_PRDT_ENTRY));
mem_memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + sizeof(HBA_PRDT_ENTRY));
// Setup PRDT - handle buffers spanning multiple physical pages
extern uint64_t paging_get_pml4_phys(void);
extern uint64_t paging_virt2phys(uint64_t pml4_phys, uint64_t virtual_addr);
uint64_t pml4 = paging_get_pml4_phys();
uint64_t buf_addr = (uint64_t)buffer;
uint32_t remaining = count * 512;
int prd_idx = 0;
while (remaining > 0 && prd_idx < 32) {
uint64_t phys = paging_virt2phys(pml4, buf_addr);
if (!phys) {
spinlock_release_irqrestore(&ps->lock, rflags);
return -1;
}
uint32_t offset = buf_addr & 0xFFF;
uint32_t can_do = 4096 - offset;
if (can_do > remaining) can_do = remaining;
cmd_tbl->prdt[prd_idx].dba = (uint32_t)(phys & 0xFFFFFFFF);
cmd_tbl->prdt[prd_idx].dbau = (uint32_t)(phys >> 32);
cmd_tbl->prdt[prd_idx].dbc = can_do - 1; // 0-based
cmd_tbl->prdt[prd_idx].i = 0;
buf_addr += can_do;
remaining -= can_do;
prd_idx++;
}
if (prd_idx > 0) cmd_tbl->prdt[prd_idx - 1].i = 1; // Interrupt on last
cmd_hdr->prdtl = prd_idx;
uint64_t buf_phys = v2p((uint64_t)buffer);
cmd_tbl->prdt[0].dba = (uint32_t)(buf_phys & 0xFFFFFFFF);
cmd_tbl->prdt[0].dbau = (uint32_t)(buf_phys >> 32);
cmd_tbl->prdt[0].dbc = (count * 512) - 1;
cmd_tbl->prdt[0].i = 1;
FIS_REG_H2D *fis = (FIS_REG_H2D*)&cmd_tbl->cfis;
fis->fis_type = FIS_TYPE_REG_H2D;
@ -436,24 +321,6 @@ static int ahci_disk_write_sector(Disk *disk, uint32_t sector, const uint8_t *bu
return ahci_write_sectors(data->ahci_port, (uint64_t)sector, 1, buffer);
}
static int ahci_disk_read_sectors(Disk *disk, uint32_t sector, uint32_t count, uint8_t *buffer) {
AHCIDriverData *data = (AHCIDriverData*)disk->driver_data;
if (disk->is_partition && disk->parent) {
AHCIDriverData *pdata = (AHCIDriverData*)disk->parent->driver_data;
return ahci_read_sectors(pdata->ahci_port, (uint64_t)sector + disk->partition_lba_offset, count, buffer);
}
return ahci_read_sectors(data->ahci_port, (uint64_t)sector, count, buffer);
}
static int ahci_disk_write_sectors(Disk *disk, uint32_t sector, uint32_t count, const uint8_t *buffer) {
AHCIDriverData *data = (AHCIDriverData*)disk->driver_data;
if (disk->is_partition && disk->parent) {
AHCIDriverData *pdata = (AHCIDriverData*)disk->parent->driver_data;
return ahci_write_sectors(pdata->ahci_port, (uint64_t)sector + disk->partition_lba_offset, count, buffer);
}
return ahci_write_sectors(data->ahci_port, (uint64_t)sector, count, buffer);
}
// ============================================================================
// Initialization
// ============================================================================
@ -501,7 +368,9 @@ void ahci_init(void) {
serial_write_hex((uint32_t)abar_phys);
serial_write("\n");
uint64_t abar_virt = p2v(abar_phys);
// Map ABAR region into kernel virtual address space
// Identity-map several pages to cover the HBA memory (at least 0x1100 bytes)
uint64_t abar_virt = abar_phys; // Use identity mapping
for (uint64_t offset = 0; offset < 0x2000; offset += 4096) {
paging_map_page(paging_get_pml4_phys(), abar_virt + offset,
abar_phys + offset,
@ -540,7 +409,6 @@ void ahci_init(void) {
ahci_port_rebase(&ports[i]);
ports[i].active = true;
active_port_count++;
ahci_initialized = true;
// Register as a block device
Disk *disk = (Disk*)kmalloc(sizeof(Disk));
@ -550,36 +418,85 @@ void ahci_init(void) {
disk->devname[0] = 0; // Auto-assign
disk->type = DISK_TYPE_SATA;
uint32_t sectors = 0;
char model[64];
if (ahci_identify(i, &sectors, model) == 0) {
ahci_strcpy(disk->label, model);
disk->total_sectors = sectors;
} else {
ahci_strcpy(disk->label, "SATA Drive");
disk->total_sectors = 0;
}
disk->read_sector = ahci_disk_read_sector;
disk->write_sector = ahci_disk_write_sector;
disk->read_sectors = ahci_disk_read_sectors;
disk->write_sectors = ahci_disk_write_sectors;
disk->sync = ahci_disk_sync;
disk->driver_data = drv;
ahci_strcpy(disk->label, "SATA Drive");
disk->read_sector = ahci_disk_read_sector;
disk->write_sector = ahci_disk_write_sector;
disk->driver_data = drv;
disk->partition_lba_offset = 0;
disk->total_sectors = 0;
disk->parent = NULL;
disk->is_partition = false;
disk->is_fat32 = false;
disk_register(disk);
// Let disk_manager parse partitions — we call a scan function
extern void disk_manager_scan_partitions(Disk *disk);
// Inline MBR parse for this disk
extern void serial_write(const char *str);
extern int disk_rescan(Disk *disk);
serial_write("[AHCI] Probing partitions on /dev/");
serial_write(disk->devname);
serial_write("...\n");
disk_rescan(disk);
// Read MBR sector 0
uint8_t *mbr_buf = (uint8_t*)kmalloc(512);
if (mbr_buf) {
if (ahci_disk_read_sector(disk, 0, mbr_buf) == 0) {
if (mbr_buf[510] == 0x55 && mbr_buf[511] == 0xAA) {
// Parse MBR partition table
typedef struct {
uint8_t status;
uint8_t chs_first[3];
uint8_t type;
uint8_t chs_last[3];
uint32_t lba_start;
uint32_t sector_count;
} __attribute__((packed)) MBR_Part;
MBR_Part *parts = (MBR_Part*)&mbr_buf[446];
int pn = 1;
for (int p = 0; p < 4; p++) {
if (parts[p].type == 0x00 || parts[p].sector_count == 0)
continue;
bool fat32 = false;
if (parts[p].type == 0x0B || parts[p].type == 0x0C) {
// Verify BPB
uint8_t *pbuf = (uint8_t*)kmalloc(512);
if (pbuf) {
if (ahci_disk_read_sector(disk, parts[p].lba_start, pbuf) == 0) {
if (pbuf[510] == 0x55 && pbuf[511] == 0xAA) {
uint16_t bps = *(uint16_t*)&pbuf[11];
uint16_t spf16 = *(uint16_t*)&pbuf[22];
uint32_t spf32 = *(uint32_t*)&pbuf[36];
if (bps == 512 && spf16 == 0 && spf32 > 0)
fat32 = true;
}
}
kfree(pbuf);
}
}
disk_register_partition(disk, parts[p].lba_start,
parts[p].sector_count, fat32, pn);
pn++;
}
// Fallback: raw FAT32
if (pn == 1) {
uint16_t bps = *(uint16_t*)&mbr_buf[11];
uint16_t spf16 = *(uint16_t*)&mbr_buf[22];
uint32_t spf32 = *(uint32_t*)&mbr_buf[36];
if (bps == 512 && spf16 == 0 && spf32 > 0) {
disk->is_fat32 = true;
disk->partition_lba_offset = 0;
serial_write("[AHCI] Raw FAT32 volume detected\n");
}
}
}
}
kfree(mbr_buf);
}
}
} else if (type == 1) {
serial_write("[AHCI] Port ");
@ -597,52 +514,3 @@ void ahci_init(void) {
serial_write("[AHCI] No active SATA ports found\n");
}
}
int ahci_flush_cache(int port_num) {
HBA_PORT *port = &abar->ports[port_num];
ahci_port_state_t *ps = &ports[port_num];
if (port_num < 0 || port_num >= 32 || !ps->active) return -1;
uint64_t rflags = spinlock_acquire_irqsave(&ps->lock);
port->is = 0xFFFFFFFF; // Clear interrupts
int slot = ahci_find_free_slot(port);
if (slot == -1) { spinlock_release_irqrestore(&ps->lock, rflags); return -1; }
HBA_CMD_HEADER *cmd_header = (HBA_CMD_HEADER*)p2v(port->clb);
cmd_header += slot;
cmd_header->cfl = sizeof(FIS_REG_H2D) / 4;
cmd_header->w = 0;
cmd_header->prdtl = 0;
HBA_CMD_TBL *cmd_tbl = (HBA_CMD_TBL*)p2v(cmd_header->ctba);
for (int i = 0; i < 256; i++) ((uint8_t*)cmd_tbl)[i] = 0;
FIS_REG_H2D *fis = (FIS_REG_H2D*)(&cmd_tbl->cfis);
fis->fis_type = FIS_TYPE_REG_H2D;
fis->c = 1;
fis->command = 0xEA; // FLUSH CACHE EXT
// Wait for port to be ready
int timeout = 1000000;
while ((port->tfd & (ATA_SR_BSY | ATA_SR_DRQ)) && --timeout > 0);
if (timeout == 0) { spinlock_release_irqrestore(&ps->lock, rflags); return -1; }
port->ci = (1 << slot);
while (1) {
if ((port->ci & (1 << slot)) == 0) break;
if (port->is & HBA_PORT_IS_TFES) { spinlock_release_irqrestore(&ps->lock, rflags); return -1; }
}
spinlock_release_irqrestore(&ps->lock, rflags);
return 0;
}
static int ahci_disk_sync(Disk *disk) {
AHCIDriverData *drv = (AHCIDriverData*)disk->driver_data;
if (!drv) return -1;
return ahci_flush_cache(drv->ahci_port);
}

View file

@ -152,12 +152,6 @@ typedef struct {
#define HBA_PORT_CMD_FRE 0x0010 // FIS Receive Enable
#define HBA_PORT_CMD_FR 0x4000 // FIS Receive Running
#define HBA_PORT_CMD_CR 0x8000 // Command List Running
#define HBA_PORT_IS_TFES (1u << 30)
#define ATA_SR_BSY 0x80
#define ATA_SR_DRDY 0x40
#define ATA_SR_DRQ 0x08
#define ATA_SR_ERR 0x01
// ============================================================================
// ATA Commands

View file

@ -18,18 +18,16 @@ typedef enum {
} DiskType;
typedef struct Disk {
char devname[16];
char devname[16]; // Device name: "sda", "sdb", "sda1", etc.
DiskType type;
bool is_fat32;
bool is_esp;
char label[32];
uint32_t partition_lba_offset;
uint32_t total_sectors;
char label[32]; // Human-readable label
uint32_t partition_lba_offset; // LBA offset of partition (0 for whole disk)
uint32_t total_sectors; // Total sectors on this device/partition
// Function pointers for driver operations
int (*read_sector)(struct Disk *disk, uint32_t sector, uint8_t *buffer);
int (*write_sector)(struct Disk *disk, uint32_t sector, const uint8_t *buffer);
int (*read_sectors)(struct Disk *disk, uint32_t sector, uint32_t count, uint8_t *buffer);
int (*write_sectors)(struct Disk *disk, uint32_t sector, uint32_t count, const uint8_t *buffer);
int (*sync)(struct Disk *disk);
// Private driver data
void *driver_data;
@ -40,17 +38,6 @@ typedef struct Disk {
bool registered;
} Disk;
typedef struct {
uint32_t lba_start;
uint32_t sector_count;
uint8_t part_type;
uint8_t flags;
char label[36];
} disk_partition_spec_t;
#define PART_FLAG_ESP 0x01
#define MIN_INSTALL_SECTORS 2097152
// Initialization and scanning
void disk_manager_init(void);
void disk_manager_scan(void);
@ -58,7 +45,7 @@ void disk_manager_scan(void);
// Device registration
void disk_register(Disk *disk);
void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_count,
bool is_fat32, bool is_esp, int part_num);
bool is_fat32, int part_num);
// Lookup
Disk* disk_get_by_name(const char *devname);
@ -72,11 +59,4 @@ const char* disk_get_next_dev_name(void); // Returns "sda", "sdb", etc.
Disk* disk_get_by_letter(char letter);
char disk_get_next_free_letter(void);
int disk_write_gpt(Disk *disk, disk_partition_spec_t *parts, int count);
int disk_write_mbr(Disk *disk, disk_partition_spec_t *parts, int count);
int disk_sync(Disk *disk);
int disk_rescan(Disk *disk);
#endif

View file

@ -42,32 +42,6 @@ static int dm_strlen(const char *s) {
return n;
}
static void dm_copy_fat_label(char *dst, const uint8_t *src) {
int end = 11;
while (end > 0 && src[end - 1] == ' ') end--;
for (int i = 0; i < end && i < 31; i++) dst[i] = (char)src[i];
dst[end < 31 ? end : 31] = 0;
}
static void disk_load_fat32_label(Disk *disk) {
uint8_t *buffer;
FAT32_BootSector *bpb;
char label[32];
if (!disk || !disk->read_sector) return;
buffer = (uint8_t*)kmalloc(512);
if (!buffer) return;
if (disk->read_sector(disk, 0, buffer) == 0 && buffer[510] == 0x55 && buffer[511] == 0xAA) {
bpb = (FAT32_BootSector*)buffer;
dm_copy_fat_label(label, bpb->volume_label);
if (label[0]) dm_strcpy(disk->label, label);
}
kfree(buffer);
}
// === ATA Definitions (Legacy IDE PIO — kept as fallback) ===
#define ATA_PRIMARY_IO 0x1F0
@ -253,105 +227,6 @@ static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) {
return 0;
}
static int ata_read_sectors(Disk *disk, uint32_t lba, uint32_t count, uint8_t *buffer) {
ATADriverData *data = (ATADriverData*)disk->driver_data;
uint16_t port_base = data->port_base;
bool slave = data->slave;
if (disk->is_partition && disk->parent) {
lba += disk->partition_lba_offset;
data = (ATADriverData*)disk->parent->driver_data;
port_base = data->port_base;
slave = data->slave;
}
uint64_t flags = spinlock_acquire_irqsave(&ide_lock);
while (count > 0) {
uint8_t batch = (count > 255) ? 255 : (uint8_t)count;
if (ata_wait_bsy(port_base) != 0) {
spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
outb(port_base + ATA_REG_SEC_COUNT0, batch);
outb(port_base + ATA_REG_LBA0, (uint8_t)(lba));
outb(port_base + ATA_REG_LBA1, (uint8_t)(lba >> 8));
outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16));
outb(port_base + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
for (uint8_t b = 0; b < batch; b++) {
if (ata_wait_bsy(port_base) != 0 || ata_wait_drq(port_base) != 0) {
spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
uint16_t *pptr = (uint16_t*)(buffer + (b * 512));
for (int i = 0; i < 256; i++) {
pptr[i] = inw(port_base + ATA_REG_DATA);
}
}
lba += batch;
buffer += batch * 512;
count -= batch;
}
spinlock_release_irqrestore(&ide_lock, flags);
return 0;
}
static int ata_write_sectors(Disk *disk, uint32_t lba, uint32_t count, const uint8_t *buffer) {
ATADriverData *data = (ATADriverData*)disk->driver_data;
uint16_t port_base = data->port_base;
bool slave = data->slave;
if (disk->is_partition && disk->parent) {
lba += disk->partition_lba_offset;
data = (ATADriverData*)disk->parent->driver_data;
port_base = data->port_base;
slave = data->slave;
}
uint64_t flags = spinlock_acquire_irqsave(&ide_lock);
while (count > 0) {
uint8_t batch = (count > 255) ? 255 : (uint8_t)count;
if (ata_wait_bsy(port_base) != 0) {
spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
outb(port_base + ATA_REG_SEC_COUNT0, batch);
outb(port_base + ATA_REG_LBA0, (uint8_t)(lba));
outb(port_base + ATA_REG_LBA1, (uint8_t)(lba >> 8));
outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16));
outb(port_base + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
for (uint8_t b = 0; b < batch; b++) {
if (ata_wait_bsy(port_base) != 0 || ata_wait_drq(port_base) != 0) {
spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
const uint16_t *pptr = (const uint16_t*)(buffer + (b * 512));
for (int i = 0; i < 256; i++) {
outw(port_base + ATA_REG_DATA, pptr[i]);
}
}
outb(port_base + ATA_REG_COMMAND, 0xE7); // Cache Flush
if (ata_wait_bsy(port_base) != 0) {
spinlock_release_irqrestore(&ide_lock, flags);
return -1;
}
lba += batch;
buffer += batch * 512;
count -= batch;
}
spinlock_release_irqrestore(&ide_lock, flags);
return 0;
}
// === Device Naming ===
const char* disk_get_next_dev_name(void) {
@ -382,13 +257,11 @@ void disk_register(Disk *disk) {
serial_write(disk->devname);
serial_write(" (");
serial_write(disk->label);
serial_write(") size=");
serial_write_num(disk->total_sectors);
serial_write(" sectors\n");
serial_write(")\n");
}
void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_count,
bool is_fat32, bool is_esp, int part_num) {
bool is_fat32, int part_num) {
if (disk_count >= MAX_DISKS) return;
Disk *part = (Disk*)kmalloc(sizeof(Disk));
@ -402,8 +275,7 @@ void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_
part->type = parent->type;
part->is_fat32 = is_fat32;
part->is_esp = is_esp;
dm_strcpy(part->label, is_esp ? "EFI System Partition" : (is_fat32 ? "FAT32 Partition" : "Unknown Partition"));
dm_strcpy(part->label, is_fat32 ? "FAT32 Partition" : "Unknown Partition");
part->partition_lba_offset = lba_offset;
part->total_sectors = sector_count;
part->read_sector = parent->read_sector;
@ -413,8 +285,6 @@ void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_
part->is_partition = true;
part->registered = true;
if (is_fat32) disk_load_fat32_label(part);
disks[disk_count++] = part;
serial_write("[DISK] Registered /dev/");
@ -424,8 +294,8 @@ void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_
serial_write(", ");
serial_write_num(sector_count);
serial_write(" sectors, FAT32=");
serial_write(" sectors, FAT32=");
serial_write(is_fat32 ? "yes" : "no");
if (is_esp) serial_write(", ESP=yes");
serial_write(")\n");
if (is_fat32) {
@ -541,18 +411,12 @@ static void parse_mbr_partitions(Disk *disk) {
if (!buffer) return;
if (disk->read_sector(disk, 0, buffer) != 0) {
serial_write("[DISK] MBR read failed on /dev/");
serial_write(disk->devname);
serial_write("\n");
kfree(buffer);
return;
}
// Check for valid MBR signature
if (buffer[510] != 0x55 || buffer[511] != 0xAA) {
serial_write("[DISK] Invalid MBR signature on /dev/");
serial_write(disk->devname);
serial_write("\n");
kfree(buffer);
return;
}
@ -560,7 +424,6 @@ static void parse_mbr_partitions(Disk *disk) {
MBR_PartitionEntry *partitions = (MBR_PartitionEntry*)&buffer[446];
int part_num = 1;
int part_count = 0;
for (int i = 0; i < 4; i++) {
uint32_t start = partitions[i].lba_start;
uint32_t size = partitions[i].sector_count;
@ -583,8 +446,7 @@ static void parse_mbr_partitions(Disk *disk) {
}
disk_register_partition(disk, partitions[i].lba_start,
partitions[i].sector_count, fat32, false, part_num);
part_count++;
partitions[i].sector_count, fat32, part_num);
part_num++;
}
@ -595,11 +457,6 @@ static void parse_mbr_partitions(Disk *disk) {
serial_write("\n");
disk->is_fat32 = true;
disk->partition_lba_offset = 0;
disk_load_fat32_label(disk);
} else if (part_count == 0) {
serial_write("[DISK] No MBR partitions found on /dev/");
serial_write(disk->devname);
serial_write("\n");
}
kfree(buffer);
@ -622,8 +479,6 @@ static void try_add_ata_drive(uint16_t port, bool slave, const char *name) {
dm_strcpy(new_disk->label, name);
new_disk->read_sector = ata_read_sector;
new_disk->write_sector = ata_write_sector;
new_disk->read_sectors = ata_read_sectors;
new_disk->write_sectors = ata_write_sectors;
new_disk->driver_data = data;
new_disk->partition_lba_offset = 0;
new_disk->total_sectors = sectors;
@ -649,6 +504,8 @@ void disk_manager_init(void) {
next_drive_letter_idx = 0;
log_ok("Disk manager ready");
// NOTE: Ramdisk (A:) is no longer registered here.
// RAMFS is managed directly by fat32.c and mounted at "/" via VFS.
}
void disk_manager_scan(void) {
@ -665,408 +522,4 @@ void disk_manager_scan(void) {
} else {
log_ok("AHCI ports initialized, skipping IDE");
}
}
static uint32_t crc32_compute(const uint8_t *data, uint32_t len) {
uint32_t crc = 0xFFFFFFFF;
for (uint32_t i = 0; i < len; i++) {
crc ^= data[i];
for (int j = 0; j < 8; j++)
crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1));
}
return ~crc;
}
#define GPT_PART_ENTRY_COUNT 128
#define GPT_PART_ENTRY_SIZE 128
_Static_assert(GPT_PART_ENTRY_COUNT * GPT_PART_ENTRY_SIZE == 32 * 512,
"GPT partition array must be exactly 32 sectors");
static const uint8_t GPT_GUID_ESP[16] = {
0x28, 0x73, 0x2A, 0xC1, 0x1F, 0xF8, 0xD2, 0x11,
0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B
};
static const uint8_t GPT_GUID_BASIC_DATA[16] = {
0xA2, 0xA0, 0xD0, 0xEB, 0xE5, 0xB9, 0x33, 0x44,
0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7
};
typedef struct __attribute__((packed)) {
uint64_t signature;
uint32_t revision;
uint32_t header_size;
uint32_t crc32;
uint32_t reserved;
uint64_t my_lba;
uint64_t alternate_lba;
uint64_t first_usable_lba;
uint64_t last_usable_lba;
uint8_t disk_guid[16];
uint64_t partition_entry_lba;
uint32_t num_partition_entries;
uint32_t size_of_partition_entry;
uint32_t partition_entry_array_crc32;
} GPT_Header;
typedef struct __attribute__((packed)) {
uint8_t type_guid[16];
uint8_t partition_guid[16];
uint64_t start_lba;
uint64_t end_lba;
uint64_t attributes;
uint16_t name[36];
} GPT_Entry;
static void gpt_make_pseudo_guid(uint8_t *guid, const char *label, uint32_t total_sectors) {
uint32_t h = 5381;
for (int i = 0; label[i]; i++)
h = h * 33 + (unsigned char)label[i];
h ^= total_sectors;
for (int i = 0; i < 16; i++)
guid[i] = (uint8_t)(h >> ((i % 4) * 8));
guid[8] = (guid[8] & 0x3F) | 0x80;
guid[6] = (guid[6] & 0x0F) | 0x40;
}
int disk_write_gpt(Disk *disk, disk_partition_spec_t *parts, int count) {
if (!disk || !parts || count <= 0 || count > GPT_PART_ENTRY_COUNT)
return -1;
uint32_t first_usable = 2048;
uint32_t last_usable = disk->total_sectors - 34;
for (int i = 0; i < count; i++) {
if (parts[i].sector_count == 0) {
serial_write("[GPT] Error: zero-sized partition\n");
return -1;
}
uint32_t start = parts[i].lba_start;
uint32_t end = start + parts[i].sector_count - 1;
if (start % 2048 != 0) {
start = ((start + 2047) / 2048) * 2048;
parts[i].lba_start = start;
end = start + parts[i].sector_count - 1;
serial_write("[GPT] Warning: start rounded up to 2048 boundary\n");
}
if (start < first_usable || end > last_usable) {
serial_write("[GPT] Error: partition out of usable range\n");
return -1;
}
for (int j = 0; j < i; j++) {
uint32_t js = parts[j].lba_start;
uint32_t je = js + parts[j].sector_count - 1;
if (start <= je && end >= js) {
serial_write("[GPT] Error: overlapping partitions\n");
return -1;
}
}
}
uint8_t *entry_buf = (uint8_t *)kmalloc(GPT_PART_ENTRY_COUNT * GPT_PART_ENTRY_SIZE);
if (!entry_buf) return -1;
for (int i = 0; i < GPT_PART_ENTRY_COUNT * GPT_PART_ENTRY_SIZE; i++) entry_buf[i] = 0;
for (int i = 0; i < count; i++) {
GPT_Entry *e = (GPT_Entry *)(entry_buf + i * GPT_PART_ENTRY_SIZE);
if (parts[i].flags & PART_FLAG_ESP)
for (int b = 0; b < 16; b++) e->type_guid[b] = GPT_GUID_ESP[b];
else
for (int b = 0; b < 16; b++) e->type_guid[b] = GPT_GUID_BASIC_DATA[b];
gpt_make_pseudo_guid(e->partition_guid, parts[i].label, disk->total_sectors);
e->start_lba = parts[i].lba_start;
e->end_lba = parts[i].lba_start + parts[i].sector_count - 1;
e->attributes = (parts[i].flags & PART_FLAG_ESP) ? 0x01 : 0x00;
/* UTF-16LE name */
for (int c = 0; c < 36 && parts[i].label[c]; c++)
e->name[c] = (uint16_t)(unsigned char)parts[i].label[c];
}
uint32_t entry_crc = crc32_compute(entry_buf, GPT_PART_ENTRY_COUNT * GPT_PART_ENTRY_SIZE);
uint8_t *hdr_buf = (uint8_t *)kmalloc(512);
if (!hdr_buf) { kfree(entry_buf); return -1; }
for (int i = 0; i < 512; i++) hdr_buf[i] = 0;
GPT_Header *hdr = (GPT_Header *)hdr_buf;
hdr->signature = 0x5452415020494645ULL;
hdr->revision = 0x00010000;
hdr->header_size = 92;
hdr->crc32 = 0;
hdr->reserved = 0;
hdr->my_lba = 1;
hdr->alternate_lba = disk->total_sectors - 1;
hdr->first_usable_lba = first_usable;
hdr->last_usable_lba = last_usable;
gpt_make_pseudo_guid(hdr->disk_guid, disk->devname, disk->total_sectors);
hdr->partition_entry_lba = 2;
hdr->num_partition_entries = GPT_PART_ENTRY_COUNT;
hdr->size_of_partition_entry = GPT_PART_ENTRY_SIZE;
hdr->partition_entry_array_crc32 = entry_crc;
hdr->crc32 = 0;
hdr->crc32 = crc32_compute(hdr_buf, hdr->header_size);
uint8_t *mbr_buf = (uint8_t *)kmalloc(512);
if (!mbr_buf) { kfree(entry_buf); kfree(hdr_buf); return -1; }
for (int i = 0; i < 512; i++) mbr_buf[i] = 0;
mbr_buf[446] = 0x00; /* Status: Non-bootable */
mbr_buf[447] = 0x00; mbr_buf[448] = 0x02; mbr_buf[449] = 0x00; /* CHS Start: 0x000200 */
mbr_buf[450] = 0xEE; /* Type: GPT Protective */
mbr_buf[451] = 0xFF; mbr_buf[452] = 0xFF; mbr_buf[453] = 0xFF; /* CHS End: 0xFFFFFF */
mbr_buf[454] = 0x01; mbr_buf[455] = 0x00; mbr_buf[456] = 0x00; mbr_buf[457] = 0x00; /* LBA Start: 1 */
uint32_t pmbr_size = disk->total_sectors - 1;
mbr_buf[458] = (uint8_t)(pmbr_size);
mbr_buf[459] = (uint8_t)(pmbr_size >> 8);
mbr_buf[460] = (uint8_t)(pmbr_size >> 16);
mbr_buf[461] = (uint8_t)(pmbr_size >> 24);
mbr_buf[510] = 0x55;
mbr_buf[511] = 0xAA;
disk->write_sector(disk, 0, mbr_buf);
kfree(mbr_buf);
if (disk->write_sector(disk, 1, hdr_buf) != 0) {
serial_write("[GPT] Error: failed to write header\n");
kfree(entry_buf); kfree(hdr_buf); return -1;
}
for (int s = 0; s < 32; s++) {
if (disk->write_sector(disk, 2 + s, entry_buf + s * 512) != 0) {
serial_write("[GPT] Error: failed to write partition entries\n");
kfree(entry_buf); kfree(hdr_buf); return -1;
}
}
GPT_Header *bhdr = (GPT_Header *)hdr_buf;
for (int i = 0; i < 512; i++) hdr_buf[i] = 0;
bhdr->signature = 0x5452415020494645ULL;
bhdr->revision = 0x00010000;
bhdr->header_size = 92;
bhdr->my_lba = disk->total_sectors - 1;
bhdr->alternate_lba = 1;
bhdr->first_usable_lba = first_usable;
bhdr->last_usable_lba = last_usable;
gpt_make_pseudo_guid(bhdr->disk_guid, disk->devname, disk->total_sectors);
bhdr->partition_entry_lba = disk->total_sectors - 33;
bhdr->num_partition_entries = GPT_PART_ENTRY_COUNT;
bhdr->size_of_partition_entry = GPT_PART_ENTRY_SIZE;
bhdr->partition_entry_array_crc32 = entry_crc;
bhdr->crc32 = 0;
bhdr->crc32 = crc32_compute(hdr_buf, bhdr->header_size);
for (int s = 0; s < 32; s++) {
disk->write_sector(disk, disk->total_sectors - 33 + s, entry_buf + s * 512);
}
disk->write_sector(disk, disk->total_sectors - 1, hdr_buf);
kfree(entry_buf);
kfree(hdr_buf);
serial_write("[DISK] GPT written to /dev/");
serial_write(disk->devname);
serial_write("\n");
return 0;
}
int disk_write_mbr(Disk *disk, disk_partition_spec_t *parts, int count) {
if (!disk || !parts || count <= 0 || count > 4) return -1;
uint8_t *buf = (uint8_t *)kmalloc(512);
if (!buf) return -1;
for (int i = 0; i < 512; i++) buf[i] = 0;
for (int i = 0; i < count; i++) {
if (parts[i].sector_count == 0) { kfree(buf); return -1; }
uint8_t *entry = buf + 446 + i * 16;
entry[0] = 0x80;
entry[4] = 0x0C;
uint32_t lba_start = parts[i].lba_start;
uint32_t sec_count = parts[i].sector_count;
entry[8] = (uint8_t)(lba_start);
entry[9] = (uint8_t)(lba_start >> 8);
entry[10] = (uint8_t)(lba_start >> 16);
entry[11] = (uint8_t)(lba_start >> 24);
entry[12] = (uint8_t)(sec_count);
entry[13] = (uint8_t)(sec_count >> 8);
entry[14] = (uint8_t)(sec_count >> 16);
entry[15] = (uint8_t)(sec_count >> 24);
}
buf[510] = 0x55;
buf[511] = 0xAA;
int ret = disk->write_sector(disk, 0, buf);
kfree(buf);
if (ret == 0) {
serial_write("[DISK] MBR written to /dev/");
serial_write(disk->devname);
serial_write("\n");
}
return ret;
}
int disk_sync(Disk *disk) {
if (!disk) return -1;
Disk *target = disk->parent ? disk->parent : disk;
// Use device-specific sync if available
if (target->sync) return target->sync(target);
if (target->type == DISK_TYPE_IDE) {
ATADriverData *data = (ATADriverData *)target->driver_data;
if (!data) return -1;
uint64_t flags = spinlock_acquire_irqsave(&ide_lock);
if (ata_wait_bsy(data->port_base) == 0) {
outb(data->port_base + ATA_REG_HDDEVSEL, data->slave ? 0xB0 : 0xA0);
outb(data->port_base + ATA_REG_COMMAND, 0xE7);
ata_wait_bsy(data->port_base);
}
spinlock_release_irqrestore(&ide_lock, flags);
return 0;
}
return 0;
}
static void disk_remove_partitions(Disk *parent) {
for (int i = 0; i < disk_count; i++) {
if (disks[i] && disks[i]->parent == parent) {
Disk *p = disks[i];
// Unmount from VFS if it's mounted
char mount_path[32];
mount_path[0] = '/';
mount_path[1] = 'd'; mount_path[2] = 'e'; mount_path[3] = 'v'; mount_path[4] = '/';
dm_strcpy(mount_path + 5, p->devname);
extern bool vfs_umount(const char *mount_path);
vfs_umount(mount_path);
for (int j = i; j < disk_count - 1; j++) {
disks[j] = disks[j + 1];
}
disks[disk_count - 1] = NULL;
disk_count--;
i--;
kfree(p);
}
}
}
static void parse_gpt_partitions(Disk *disk) {
uint8_t *buffer = (uint8_t*)kmalloc(512);
if (!buffer) return;
if (disk->read_sector(disk, 1, buffer) != 0) {
serial_write("[DISK] GPT header read failed on /dev/");
serial_write(disk->devname);
serial_write("\n");
kfree(buffer);
return;
}
GPT_Header *hdr = (GPT_Header *)buffer;
if (hdr->signature != 0x5452415020494645ULL) {
serial_write("[DISK] GPT signature missing on /dev/");
serial_write(disk->devname);
serial_write("\n");
kfree(buffer);
return;
}
uint32_t num_entries = hdr->num_partition_entries;
uint32_t entry_size = hdr->size_of_partition_entry;
uint64_t entry_lba = hdr->partition_entry_lba;
uint8_t *entry_buf = (uint8_t*)kmalloc(512);
if (!entry_buf) { kfree(buffer); return; }
int part_num = 1;
int part_count = 0;
for (uint32_t i = 0; i < num_entries && i < 128; i++) {
uint32_t entry_lba_offset = (uint32_t)entry_lba + (i * entry_size) / 512;
uint32_t entry_sector_offset = (i * entry_size) % 512;
if (disk->read_sector(disk, entry_lba_offset, entry_buf) != 0) break;
GPT_Entry *entry = (GPT_Entry *)(entry_buf + entry_sector_offset);
bool zero = true;
for (int j = 0; j < 16; j++) if (entry->type_guid[j] != 0) { zero = false; break; }
if (zero) continue;
uint32_t start = (uint32_t)entry->start_lba;
uint32_t end = (uint32_t)entry->end_lba;
uint32_t size = end - start + 1;
if (size == 0) continue;
static const uint8_t esp_guid[16] = {
0x28, 0x73, 0x2A, 0xC1, 0x1F, 0xF8, 0xD2, 0x11,
0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B
};
bool is_esp = true;
for (int j = 0; j < 16; j++) if (entry->type_guid[j] != esp_guid[j]) { is_esp = false; break; }
bool fat32 = false;
if (is_esp) fat32 = true;
else {
uint8_t *pbuf = (uint8_t*)kmalloc(512);
if (pbuf) {
if (disk->read_sector(disk, start, pbuf) == 0) {
fat32 = is_fat32_bpb(pbuf);
}
kfree(pbuf);
}
}
disk_register_partition(disk, start, size, fat32, is_esp, part_num++);
part_count++;
}
if (part_count == 0) {
serial_write("[DISK] GPT found but no partitions registered on /dev/");
serial_write(disk->devname);
serial_write("\n");
}
kfree(entry_buf);
kfree(buffer);
}
int disk_rescan(Disk *disk) {
if (!disk || disk->is_partition) return -1;
disk_remove_partitions(disk);
serial_write("[DISK] Rescanning /dev/");
serial_write(disk->devname);
serial_write("\n");
uint8_t *buffer = (uint8_t*)kmalloc(512);
if (buffer) {
if (disk->read_sector(disk, 1, buffer) == 0) {
GPT_Header *hdr = (GPT_Header*)buffer;
if (hdr->signature == 0x5452415020494645ULL) {
serial_write("[DISK] GPT detected on /dev/");
serial_write(disk->devname);
serial_write("\n");
kfree(buffer);
parse_gpt_partitions(disk);
return 0;
}
} else {
serial_write("[DISK] GPT probe read failed on /dev/");
serial_write(disk->devname);
serial_write("\n");
}
kfree(buffer);
}
parse_mbr_partitions(disk);
return 0;
}
}

View file

@ -11,8 +11,6 @@
#include "input/keyboard.h"
#include "input/keymap.h"
extern void serial_write(const char *s);
extern void serial_write_num(uint32_t n);
extern void serial_print(const char *s);
extern void serial_print_hex(uint64_t n);
@ -111,9 +109,6 @@ uint8_t mouse_read(void) {
void mouse_init(void) {
uint8_t status;
int limit = 128;
while (limit-- > 0 && (inb(PS2_STATUS_PORT) & PS2_STATUS_OUT_FULL))
(void)inb(PS2_DATA_PORT);
// Enable Aux Device
mouse_wait(0);

View file

@ -4,19 +4,11 @@
#ifndef PS2_H
#define PS2_H
#define PS2_DATA_PORT 0x60
#define PS2_CMD_PORT 0x64
#define PS2_STATUS_PORT 0x64
#define PS2_STATUS_OUT_FULL 0x01
#define PS2_STATUS_IN_FULL 0x02
#define PS2_STATUS_AUX_DATA 0x20
#include <stdint.h>
void ps2_init(void);
#include "process.h"
uint64_t timer_handler(registers_t *regs);
uint64_t keyboard_handler(registers_t *regs);
uint64_t mouse_handler(registers_t *regs);

View file

@ -1,233 +0,0 @@
// Copyright (c) 2026 Myles Wilson (myles@bleedkernel.com)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include <stdint.h>
#include <stddef.h>
#include "acpi_structures.h"
#include "../I2C/acpi_i2c.h"
#include "acpi.h"
#include "../sys/idt.h"
#include "../core/limine.h"
#include "../core/panic.h"
#include "../core/platform.h"
#include "../core/kconsole.h"
#define MAX_ISO 16
static struct acpi_rsdp *acpi_rsdp = NULL;
static struct acpi_madt *acpi_madt = NULL;
static fadt_t *acpi_fadt = NULL; // my header file sucks ill make this nicer but its fine
static struct {
uint8_t source;
uint32_t gsi;
uint16_t flags;
} iso_table[MAX_ISO];
static uint8_t iso_count = 0;
/*
Each ACPI table contains an 8-bit checksum field.
When all bytes in the table are added together (including the checksum byte),
the lower 8 bits of the total sum must be zero for it to pass.
*/
static int acpi_checksum(void *ptr, size_t len) {
uint8_t sum = 0;
uint8_t *p = ptr;
for (size_t i = 0; i < len; i++)
sum += p[i];
return sum == 0;
}
struct acpi_rsdp *acpi_get_rsdp(void){
extern volatile struct limine_rsdp_request acpi_rsdp_request;
if (!acpi_rsdp_request.response)
kernel_panic(NULL, "ACPI RSDP not provided by the Bootloader");
if (!acpi_rsdp_request.response->address)
kernel_panic(NULL, "ACPI Invalid RSDP address provided by Bootloader");
return acpi_rsdp_request.response->address;
}
struct acpi_sdt *acpi_get_sdt(const char signature[4]) {
if (acpi_rsdp->revision >= 2 && acpi_rsdp->xsdt_address) {
struct acpi_xsdt *acpi_xsdt = (struct acpi_xsdt *)p2v(acpi_rsdp->xsdt_address);
if (acpi_checksum(acpi_xsdt, acpi_xsdt->header.length)) {
size_t entries = (acpi_xsdt->header.length - sizeof(struct acpi_sdt)) / 8;
for (size_t i = 0; i < entries; i++) {
struct acpi_sdt *tbl = (struct acpi_sdt *)p2v(acpi_xsdt->tables[i]);
if (!tbl) continue;
if (!memcmp(tbl->signature, signature, 4))
return tbl;
}
}
}
// RSDT fallback
if (!acpi_rsdp->rsdt_address)
return NULL;
struct acpi_sdt *acpi_rsdt = (struct acpi_sdt *)p2v(acpi_rsdp->rsdt_address);
if (!acpi_checksum(acpi_rsdt, acpi_rsdt->length))
return NULL;
uint32_t *tables = (uint32_t *)((uint8_t *)acpi_rsdt + sizeof(struct acpi_sdt));
size_t entries = (acpi_rsdt->length - sizeof(struct acpi_sdt)) / 4;
for (size_t i = 0; i < entries; i++) {
struct acpi_sdt *tbl = (struct acpi_sdt *)p2v(tables[i]);
if (!tbl) continue;
if (!memcmp(tbl->signature, signature, 4))
return tbl;
}
return NULL;
}
uint16_t SLP_TYPa = 0;
uint16_t SLP_TYPb = 0;
void acpi_parse_s5(void) {
if (!acpi_fadt || !acpi_fadt->dsdt) return;
char *dsdt = (char *)p2v((uintptr_t)acpi_fadt->dsdt);
if (memcmp(dsdt, "DSDT", 4) != 0) return;
uint32_t dsdt_len = *(uint32_t*)(dsdt + 4);
char *ptr = dsdt + 36;
char *end = dsdt + dsdt_len;
while (ptr < end) {
if (memcmp(ptr, "_S5_", 4) == 0) {
ptr += 4;
if (*ptr == 0x12) {
ptr += 3;
if (*ptr == 0x0A) ptr++;
SLP_TYPa = (*(uint8_t*)ptr) << 10;
ptr++;
if (*ptr == 0x0A) ptr++;
SLP_TYPb = (*(uint8_t*)ptr) << 10;
return;
}
}
ptr++;
}
}
__attribute__((noreturn))
void acpi_shutdown(void) {
if (SLP_TYPa == 0) acpi_parse_s5();
if (SLP_TYPa == 0) SLP_TYPa = (5 << 10);
outw((uint16_t)acpi_fadt->pm1a_cnt_blk, SLP_TYPa | SLP_EN);
if (acpi_fadt->pm1b_cnt_blk != 0) {
outw((uint16_t)acpi_fadt->pm1b_cnt_blk, SLP_TYPb | SLP_EN);
}
//virtulizers last just incase these have some sort of unintended effect on real hw
outw(0xB004, 0x2000); // bochs
outw(0x4004, 0x3400); // vbox
outw(0x604, 0x2000); // QEMU
outw(0x600, 0x34); // Cloud Hypervisor
asm volatile("cli");
for(;;) asm volatile("hlt");
}
int acpi_init(void){
acpi_rsdp = acpi_get_rsdp();
if (!acpi_rsdp)
kernel_panic(NULL, "ACPI does not provide a required RSDP");
size_t rsdp_len = acpi_rsdp->revision >= 2 ? acpi_rsdp->length : 20;
if (!acpi_checksum(acpi_rsdp, rsdp_len))
kernel_panic(NULL, "bad RSDP checksum");
acpi_fadt = (struct acpi_fadt *)acpi_get_sdt("FACP");
if (!acpi_fadt)
kernel_panic(NULL, "FADT not found");
if (!acpi_checksum(acpi_fadt, acpi_fadt->header.length))
kernel_panic(NULL, "bad FADT checksum");
if (acpi_fadt->smi_cmd && acpi_fadt->acpi_enable) {
outb(acpi_fadt->smi_cmd, acpi_fadt->acpi_enable);
int timeout = 1000000;
while (!(inw(acpi_fadt->pm1a_cnt_blk) & 1) && timeout-- > 0)
asm("pause");
if (timeout <= 0) {
kernel_panic(NULL, "Enable timeout");
}
}
acpi_madt = (struct acpi_madt *)acpi_get_sdt("APIC");
if (!acpi_madt)
kernel_panic(NULL, "MADT not found");
if (!acpi_checksum(acpi_madt, acpi_madt->header.length))
kernel_panic(NULL, "bad MADT checksum");
uint8_t *ptr = acpi_madt->entries;
uint8_t *end = (uint8_t *)acpi_madt + acpi_madt->header.length;
while (ptr < end) {
struct madt_entry_header *h = (void *)ptr;
if (h->length < sizeof(struct madt_entry_header))
break;
if (ptr + h->length > end)
break;
switch (h->type) {
case 2: {
if (iso_count < MAX_ISO) {
struct madt_iso *iso = (void *)ptr;
iso_table[iso_count].source = iso->source;
iso_table[iso_count].gsi = iso->gsi;
iso_table[iso_count].flags = iso->flags;
iso_count++;
}
break;
}
default:
break;
}
ptr += h->length;
}
acpi_i2c_enumerate();
return 0;
}
uint32_t acpi_irq_to_gsi(uint32_t irq) {
for (size_t i = 0; i < iso_count; i++) {
if (iso_table[i].source == irq)
return iso_table[i].gsi;
}
return irq;
}
uint16_t acpi_irq_flags(uint32_t irq) {
for (size_t i = 0; i < iso_count; i++) {
if (iso_table[i].source == irq)
return iso_table[i].flags;
}
return 0;
}
struct acpi_sdt *acpi_get_dsdt(void) {
if (!acpi_fadt || !acpi_fadt->dsdt) return NULL;
return (struct acpi_sdt *)p2v((uintptr_t)acpi_fadt->dsdt);
}

View file

@ -1,35 +0,0 @@
// Copyright (c) 2026 Myles Wilson (myles@bleedkernel.com)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef ACPI_H
#define ACPI_H
//power stuff
#define PM1A_CNT fadt->pm1a_cnt_blk
#define PM1B_CNT fadt->pm1b_cnt_blk
#define ACPI_S5 0x5
#define SLP_EN (1 << 13)
#define ACPI_PM1_SLEEP_CMD(slp_typ) (((slp_typ) << 10) | SLP_EN)
extern uint16_t SLP_TYPa;
extern uint16_t SLP_TYPb;
int acpi_init(void);
uint32_t acpi_irq_to_gsi(uint32_t irq);
uint16_t acpi_irq_flags(uint32_t irq);
struct acpi_rsdp *acpi_get_rsdp(void);
struct acpi_sdt *acpi_get_sdt(const char signature[4]);
struct acpi_sdt *acpi_get_dsdt(void);
void acpi_parse_s5(void);
// ACPI Functionality Implementations
// - do reboot properly
__attribute__((noreturn)) void acpi_shutdown(void);
#endif

View file

@ -1,417 +0,0 @@
// Copyright (c) 2026 Myles Wilson (myles@bleedkernel.com)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "acpi_aml.h"
#include "kutils.h"
#include "kconsole.h"
#include <stdint.h>
#include <stddef.h>
/// @brief Decode AML PkgLength field, advance past it
/// @param pp in/out byte pointer, advanced past the field on return
/// @param end hard bound; returns 0 if field exceeds it
/// @return total package length including the PkgLength bytes, 0 on error
static size_t decode_pkglen(const uint8_t **pp, const uint8_t *end) {
if (*pp >= end) return 0;
uint8_t b0 = *(*pp)++;
uint8_t extra = b0 >> 6;
size_t len;
if (extra == 0) {
len = b0 & 0x3F; // 1-byte form: bits[5:0]
} else {
len = b0 & 0x0F; // multi-byte form: bits[3:0] are low nibble
if (*pp + extra > end) return 0;
for (uint8_t i = 0; i < extra; i++)
len |= (size_t)(*(*pp)++) << (4 + 8 * i);
}
return len;
}
/// @brief Decode the last NameSeg of an AML NameString into out
/// @param pp in/out byte pointer, advanced past the full name on return
/// @param end hard bound
/// @param out receives null-terminated 4-char NameSeg
static void decode_nameseg(const uint8_t **pp, const uint8_t *end,
char out[AML_NAME_LEN]) {
out[0] = '\0';
if (*pp >= end) return;
while (*pp < end && (**pp == '\\' || **pp == '^')) (*pp)++;
if (*pp >= end) return;
uint8_t lead = **pp;
if (lead == 0x00) {
(*pp)++;
return;
} else if (lead == 0x2E) { // DualNamePath two 4-char segs
(*pp)++;
if (*pp + 8 > end) return;
(*pp) += 4;
for (int i = 0; i < 4; i++) out[i] = (char)(*pp)[i];
out[4] = '\0';
(*pp) += 4;
} else if (lead == 0x2F) { // MultiNamePath count + N segs
(*pp)++;
if (*pp >= end) return;
uint8_t count = *(*pp)++;
if (*pp + (size_t)count * 4 > end) return;
const uint8_t *last = *pp + (count - 1) * 4;
for (int i = 0; i < 4; i++) out[i] = (char)last[i];
out[4] = '\0';
*pp += (size_t)count * 4;
} else { // plain 4-char NameSeg
if (*pp + 4 > end) return;
for (int i = 0; i < 4; i++) out[i] = (char)(*pp)[i];
out[4] = '\0';
(*pp) += 4;
}
}
/// @brief Decode any AML integer data object (Zero, One, Byte, Word, DWord, QWord)
/// @param pp in/out byte pointer, advanced past the object on return
/// @param end hard bound
/// @return decoded integer value, 0 on unknown opcode (pointer not advanced)
static uint64_t decode_integer(const uint8_t **pp, const uint8_t *end) {
if (*pp >= end) return 0;
uint8_t op = *(*pp)++;
switch (op) {
case AML_ZERO_OP: return 0;
case AML_ONE_OP: return 1;
case AML_BYTE_PREFIX:
if (*pp + 1 > end) return 0;
return *(*pp)++;
case AML_WORD_PREFIX: {
if (*pp + 2 > end) return 0;
uint16_t v = (uint16_t)((*pp)[0]) | ((uint16_t)((*pp)[1]) << 8);
*pp += 2; return v;
}
case AML_DWORD_PREFIX: {
if (*pp + 4 > end) return 0;
uint32_t v = (uint32_t)((*pp)[0])
| ((uint32_t)((*pp)[1]) << 8)
| ((uint32_t)((*pp)[2]) << 16)
| ((uint32_t)((*pp)[3]) << 24);
*pp += 4; return v;
}
case AML_QWORD_PREFIX: {
if (*pp + 8 > end) return 0;
uint64_t v = 0;
for (int i = 0; i < 8; i++) v |= (uint64_t)((*pp)[i]) << (8 * i);
*pp += 8; return v;
}
default:
(*pp)--;
return 0;
}
}
/// @brief Convert 32-bit packed EISAID value to ASCII HID string (e.g. "PNP0C0E")
/// @param id EISAID as decoded by decode_integer (little-endian from AML)
/// @param out receives null-terminated 8-char string
static void eisaid_to_str(uint32_t id, char out[AML_HID_LEN]) {
uint32_t v = ((id & 0x000000FF) << 24)
| ((id & 0x0000FF00) << 8)
| ((id & 0x00FF0000) >> 8)
| ((id & 0xFF000000) >> 24);
out[0] = (char)('@' + ((v >> 26) & 0x1F));
out[1] = (char)('@' + ((v >> 21) & 0x1F));
out[2] = (char)('@' + ((v >> 16) & 0x1F));
static const char hex[] = "0123456789ABCDEF";
out[3] = hex[(v >> 12) & 0xF];
out[4] = hex[(v >> 8) & 0xF];
out[5] = hex[(v >> 4) & 0xF];
out[6] = hex[(v >> 0) & 0xF];
out[7] = '\0';
}
/// @brief Parse _HID data object (string or EISAID DWord) into dev->hid
/// @param pp in/out byte pointer positioned at the data object
/// @param end hard bound
/// @param dev target device record
static void parse_hid(const uint8_t **pp, const uint8_t *end,
aml_i2c_dev_t *dev) {
if (*pp >= end) return;
if (**pp == AML_STRING_PREFIX) {
(*pp)++;
const char *s = (const char *)*pp;
strncpy(dev->hid, s, AML_HID_LEN - 1);
dev->hid[AML_HID_LEN - 1] = '\0';
while (*pp < end && **pp) (*pp)++;
if (*pp < end) (*pp)++;
} else if (**pp == AML_DWORD_PREFIX) {
uint32_t id = (uint32_t)decode_integer(pp, end);
eisaid_to_str(id, dev->hid);
}
}
/// @brief Parse _CRS Buffer for an I2cSerialBusV2 descriptor, fill dev CRS fields
/// @param pp in/out byte pointer positioned at BufferOp
/// @param end hard bound
/// @param dev receives slave_address, speed_hz, ten_bit_addr on success
static void parse_crs(const uint8_t **pp, const uint8_t *end,
aml_i2c_dev_t *dev) {
if (*pp >= end || **pp != AML_BUFFER_OP) return;
(*pp)++;
const uint8_t *pkg_start = *pp;
size_t pkglen = decode_pkglen(pp, end);
if (!pkglen) return;
const uint8_t *buf_end = pkg_start + pkglen;
if (buf_end > end) buf_end = end;
decode_integer(pp, buf_end); // skip BufferSize
const uint8_t *res = *pp;
while (res < buf_end) {
uint8_t tag = *res;
if (tag == ACPI_RESOURCE_END_TAG) break;
if (tag == ACPI_LARGE_I2C_SERIAL_BUS) {
if (res + sizeof(aml_i2c_resource_t) > buf_end) break;
const aml_i2c_resource_t *r = (const aml_i2c_resource_t *)res;
if (r->serial_bus_type == ACPI_I2C_SERIAL_BUS_TYPE) {
dev->slave_address = r->slave_address;
dev->speed_hz = r->connection_speed;
dev->ten_bit_addr = (r->type_specific_flags & 0x01) ? 1 : 0;
}
res += sizeof(uint8_t) + sizeof(uint16_t) + r->length;
continue;
}
if (tag & 0x80) {
if (res + 3 > buf_end) break;
uint16_t len = (uint16_t)(res[1]) | ((uint16_t)(res[2]) << 8);
res += 3 + len;
} else {
res += 1 + (tag & 0x07);
}
}
*pp = buf_end;
}
static void record_power_state(const char name[AML_NAME_LEN],
aml_i2c_dev_t *dev) {
if (memcmp(name, "_PS0", 4) == 0) dev->power_flags |= AML_PWR_HAS_PS0;
else if (memcmp(name, "_PS3", 4) == 0) dev->power_flags |= AML_PWR_HAS_PS3;
else if (memcmp(name, "_PR0", 4) == 0) dev->power_flags |= AML_PWR_HAS_PR0;
else if (memcmp(name, "_PR3", 4) == 0) dev->power_flags |= AML_PWR_HAS_PR3;
}
/// @brief Best-effort skip of one AML data object to keep the scan on track
/// @param pp in/out byte pointer, advanced past the object on return
/// @param end hard bound
static void skip_object(const uint8_t **pp, const uint8_t *end) {
if (*pp >= end) return;
uint8_t op = **pp;
switch (op) {
case AML_ZERO_OP:
case AML_ONE_OP:
(*pp)++; return;
case AML_BYTE_PREFIX: (*pp) += 2; return;
case AML_WORD_PREFIX: (*pp) += 3; return;
case AML_DWORD_PREFIX: (*pp) += 5; return;
case AML_QWORD_PREFIX: (*pp) += 9; return;
case AML_STRING_PREFIX:
(*pp)++;
while (*pp < end && **pp) (*pp)++;
if (*pp < end) (*pp)++;
return;
case AML_BUFFER_OP:
case AML_PACKAGE_OP: {
(*pp)++;
const uint8_t *start = *pp;
size_t len = decode_pkglen(pp, end);
if (len) *pp = start + len;
return;
}
default:
(*pp)++;
}
}
/// @brief Scan one Device() scope body for _HID, _CRS, _DSM, and power objects
/// @param p first byte of the scope body (after NameSeg)
/// @param end one past last byte of the scope
/// @param dev device record to populate
static void scan_device_scope(const uint8_t *p, const uint8_t *end,
aml_i2c_dev_t *dev) {
while (p < end) {
uint8_t op = *p;
if (op == AML_NAME_OP) {
p++;
char seg[AML_NAME_LEN];
decode_nameseg(&p, end, seg);
if (memcmp(seg, "_HID", 4) == 0) parse_hid(&p, end, dev);
else if (memcmp(seg, "_CRS", 4) == 0) parse_crs(&p, end, dev);
else { record_power_state(seg, dev); skip_object(&p, end); }
continue;
}
if (op == AML_METHOD_OP) {
p++;
const uint8_t *method_start = p;
size_t pkglen = decode_pkglen(&p, end);
if (!pkglen) break;
const uint8_t *method_end = method_start + pkglen;
if (method_end > end) method_end = end;
char seg[AML_NAME_LEN];
const uint8_t *name_ptr = p;
decode_nameseg(&name_ptr, method_end, seg);
if (memcmp(seg, "_DSM", 4) == 0) {
const uint8_t *body = name_ptr + 1; // skip MethodFlags we dont care abt it rn
const uint8_t *scan = body;
const uint8_t *guid = (const uint8_t *)ACPI_I2C_HID_DSM_GUID;
while (scan + 16 < method_end) {
if (memcmp(scan, guid, 16) != 0) { scan++; continue; }
scan += 16;
while (scan + 4 < method_end) {
if (*scan != AML_RETURN_OP) { scan++; continue; }
scan++;
if (*scan != AML_PACKAGE_OP) { continue; }
scan++;
const uint8_t *ps = scan;
size_t pl = decode_pkglen(&scan, method_end);
if (!pl) break;
const uint8_t *pe = ps + pl;
if (pe > method_end || scan >= pe) break;
uint8_t nelem = *scan++;
if (nelem < 1) break;
uint64_t val = decode_integer(&scan, pe);
if (val <= 0xFFFF) {
dev->hid_desc_addr = (uint16_t)val;
dev->has_dsm = 1;
}
goto method_done;
}
break;
}
} else {
record_power_state(seg, dev);
}
method_done:
p = method_end;
continue;
}
// skip nested Device or Scope without recursing
if (op == AML_EXTOP_PREFIX && p + 1 < end && *(p + 1) == AML_DEVICE_OP) {
p += 2;
const uint8_t *s = p;
size_t l = decode_pkglen(&p, end);
if (l) p = s + l; else break;
continue;
}
if (op == AML_SCOPE_OP) {
p++;
const uint8_t *s = p;
size_t l = decode_pkglen(&p, end);
if (l) p = s + l; else break;
continue;
}
p++;
}
}
void aml_walk_table(const uint8_t *aml, size_t len, aml_walk_ctx_t *ctx) {
if (!aml || !len || !ctx || !ctx->devices) return;
const uint8_t *p = aml;
const uint8_t *end = aml + len;
while (p + 2 < end) {
if (p[0] != AML_EXTOP_PREFIX || p[1] != AML_DEVICE_OP) { p++; continue; }
p += 2;
const uint8_t *scope_start = p;
size_t pkglen = decode_pkglen(&p, end);
if (!pkglen) continue;
const uint8_t *scope_end = scope_start + pkglen;
if (scope_end > end) scope_end = end;
char dev_name[AML_NAME_LEN];
decode_nameseg(&p, scope_end, dev_name);
if (ctx->count >= ctx->capacity) break;
aml_i2c_dev_t *dev = &ctx->devices[ctx->count];
memset(dev, 0, sizeof(aml_i2c_dev_t));
memcpy(dev->name, dev_name, AML_NAME_LEN);
scan_device_scope(p, scope_end, dev);
scan_device_scope(p, scope_end, dev);
if (dev->hid[0] && dev->speed_hz) {
dev->valid = 1;
ctx->count++;
}
p = scope_end;
}
}
int aml_parse_s5(const uint8_t *aml, size_t len,
uint16_t *slp_typa, uint16_t *slp_typb) {
if (!aml || !len || !slp_typa || !slp_typb) return 0;
const uint8_t *end = aml + len;
const uint8_t *p = aml;
while (p + 4 < end) {
if (p[0] != '_' || p[1] != 'S' || p[2] != '5' || p[3] != '_') {
p++; continue;
}
p += 4;
// scan ahead up to 4 bytes for PackageOp; on diff firmware layouts YMMV
int found_pkg = 0;
for (int skip = 0; skip < 4 && p + skip < end; skip++) {
if (p[skip] == AML_PACKAGE_OP) {
p += skip + 1;
found_pkg = 1;
break;
}
}
if (!found_pkg) continue;
const uint8_t *pkg_start = p;
size_t pkglen = decode_pkglen(&p, end);
if (!pkglen) continue;
const uint8_t *pkg_end = pkg_start + pkglen;
if (pkg_end > end) pkg_end = end;
if (p >= pkg_end) continue;
uint8_t num = *p++;
if (num < 2) continue;
uint64_t typa = decode_integer(&p, pkg_end);
uint64_t typb = decode_integer(&p, pkg_end);
*slp_typa = (uint16_t)((typa & 0x7) << 10);
*slp_typb = (uint16_t)((typb & 0x7) << 10);
return 1;
}
return 0;
}

View file

@ -1,96 +0,0 @@
// Copyright (c) 2026 Myles Wilson (myles@bleedkernel.com)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef ACPI_AML_H
#define ACPI_AML_H
#include <stdint.h>
#include <stddef.h>
#define AML_ZERO_OP 0x00
#define AML_ONE_OP 0x01
#define AML_NAME_OP 0x08
#define AML_BYTE_PREFIX 0x0A
#define AML_WORD_PREFIX 0x0B
#define AML_DWORD_PREFIX 0x0C
#define AML_STRING_PREFIX 0x0D
#define AML_QWORD_PREFIX 0x0E
#define AML_SCOPE_OP 0x10
#define AML_BUFFER_OP 0x11
#define AML_PACKAGE_OP 0x12
#define AML_METHOD_OP 0x14
#define AML_EXTOP_PREFIX 0x5B
#define AML_DEVICE_OP 0x82 // always preceded by 0x5B
#define AML_RETURN_OP 0xA4
// ACPI resource descriptor tags
#define ACPI_RESOURCE_END_TAG 0x79
#define ACPI_LARGE_ITEM 0x80
#define ACPI_LARGE_I2C_SERIAL_BUS 0x8E
#define ACPI_I2C_SERIAL_BUS_TYPE 0x01
// I2cSerialBusV2 resource descriptor
typedef struct __attribute__((packed)) {
uint8_t tag;
uint16_t length;
uint8_t revision_id;
uint8_t resource_source_index;
uint8_t serial_bus_type;
uint8_t general_flags;
uint16_t type_specific_flags;
uint8_t type_specific_revision_id;
uint16_t type_data_length;
uint32_t connection_speed;
uint16_t slave_address;
} aml_i2c_resource_t;
/*
GUID: 3cdff6f7-4267-4555-ad05-b30a3d8938de, mixed-endian AML layout
sorry i know its a magic number but tis the way of things
*/
#define ACPI_I2C_HID_DSM_GUID \
"\xf7\xf6\xdf\x3c\x67\x42\x55\x45\xad\x05\xb3\x0a\x3d\x89\x38\xde"
#define AML_PWR_HAS_PS0 (1 << 0)
#define AML_PWR_HAS_PS3 (1 << 1)
#define AML_PWR_HAS_PR0 (1 << 2)
#define AML_PWR_HAS_PR3 (1 << 3)
#define AML_HID_LEN 9
#define AML_NAME_LEN 5
typedef struct {
char name[AML_NAME_LEN];
char hid[AML_HID_LEN];
uint16_t slave_address;
uint32_t speed_hz;
uint8_t ten_bit_addr;
uint16_t hid_desc_addr;
uint8_t has_dsm;
uint8_t power_flags;
uint8_t valid;
} aml_i2c_dev_t;
typedef struct {
aml_i2c_dev_t *devices;
size_t capacity;
size_t count;
} aml_walk_ctx_t;
/// @brief Walk one DSDT or SSDT AML region, emitting I2C device records
/// @param aml first AML byte (SDT base + 36)
/// @param len byte length of the AML region
/// @param ctx output context; devices array must be pre-allocated
void aml_walk_table(const uint8_t *aml, size_t len, aml_walk_ctx_t *ctx);
/// @brief Scan DSDT AML for _S5_ and extract SLP_TYPa/b for S5 power-off
/// @param aml first AML byte (DSDT base + 36)
/// @param len byte length of the AML region
/// @param slp_typa out - SLP_TYPa pre-shifted to PM1_CNT bit 10
/// @param slp_typb out - SLP_TYPb pre-shifted to PM1_CNT bit 10
/// @return 1 if _S5_ found, 0 if not found (caller should use fallback)
int aml_parse_s5(const uint8_t *aml, size_t len,
uint16_t *slp_typa, uint16_t *slp_typb);
#endif

View file

@ -1,133 +0,0 @@
// Copyright (c) 2026 Myles Wilson (myles@bleedkernel.com)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef ACPI_STRUCTURES_H
#define ACPI_STRUCTURES_H
#include <stdint.h>
struct acpi_sdt {
char signature[4];
uint32_t length;
uint8_t revision;
uint8_t checksum;
char oem_id[6];
char oem_table_id[8];
uint32_t oem_revision;
uint32_t creator_id;
uint32_t creator_revision;
} __attribute__((packed));
struct acpi_rsdp {
char signature[8];
uint8_t checksum;
char oem_id[6];
uint8_t revision;
uint32_t rsdt_address;
// acpi2
uint32_t length;
uint64_t xsdt_address;
uint8_t extended_checksum;
uint8_t reserved[3];
} __attribute__((packed));
struct acpi_xsdt {
struct acpi_sdt header;
uint64_t tables[];
} __attribute__((packed));
typedef struct
{
uint8_t address_space_id;
uint8_t register_bit_width;
uint8_t register_bit_offset;
uint8_t access_size;
uint64_t address;
} __attribute__((packed)) GenericAddressStructure;
typedef struct acpi_fadt
{
struct acpi_sdt header;
uint32_t firmware_ctrl;
uint32_t dsdt;
uint8_t __reserved;
uint8_t preferred_pm_profile;
uint16_t sci_int;
uint32_t smi_cmd;
uint8_t acpi_enable;
uint8_t acpi_disable;
uint8_t s4bios_req;
uint8_t pstate_cnt;
uint32_t pm1a_evt_blk;
uint32_t pm1b_evt_blk;
uint32_t pm1a_cnt_blk;
uint32_t pm1b_cnt_blk;
uint32_t pm2_cnt_blk;
uint32_t pm_tmr_blk;
uint32_t gpe0_blk;
uint32_t gpe1_blk;
uint8_t pm1_evt_len;
uint8_t pm1_cnt_len;
uint8_t pm2_cnt_len;
uint8_t pm_tmr_len;
uint8_t gpe0_blk_len;
uint8_t gpe1_blk_len;
uint8_t gpe1_base;
uint8_t cst_cnt;
uint16_t p_lvl2_lat;
uint16_t p_lvl3_lat;
uint16_t flush_size;
uint16_t flush_stride;
uint8_t duty_offset;
uint8_t duty_width;
uint8_t day_alrm;
uint8_t mon_alrm;
uint8_t century;
uint16_t iapc_boot_arch;
uint8_t __reserved2;
uint32_t flags;
GenericAddressStructure reset_reg;
uint8_t reset_value;
uint16_t arm_boot_arch;
uint8_t fadt_minor_version;
uint64_t x_firmware_ctrl;
uint64_t x_dsdt;
uint8_t x_pm1a_evt_blk[12];
uint8_t x_pm1b_evt_blk[12];
uint8_t x_pm1a_cnt_blk[12];
uint8_t x_pm1b_cnt_blk[12];
uint8_t x_pm2_cnt_blk[12];
uint8_t x_pm_tmr_blk[12];
uint8_t x_gpe0_blk[12];
uint8_t x_gpe1_blk[12];
uint8_t sleep_control_reg[12];
uint8_t sleep_status_reg[12];
uint64_t hypervison_vendor_identity;
} __attribute__((packed)) fadt_t;
struct acpi_madt {
struct acpi_sdt header;
uint32_t lapic_addr;
uint32_t flags;
uint8_t entries[];
} __attribute__((packed));
struct madt_iso {
uint8_t type;
uint8_t length;
uint8_t bus;
uint8_t source;
uint32_t gsi;
uint16_t flags;
} __attribute__((packed));
struct madt_entry_header {
uint8_t type;
uint8_t length;
} __attribute__((packed));
struct acpi_sdt *acpi_find_sdt(const char sig[4]);
#endif

View file

@ -1,115 +0,0 @@
// Copyright (c) 2026 Myles Wilson (myles@bleedkernel.com)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "acpi_i2c.h"
#include "../ACPI/acpi.h"
#include "../ACPI/acpi_aml.h"
#include "../ACPI/acpi_structures.h"
#include "../core/kconsole.h"
#include "../core/platform.h"
static aml_i2c_dev_t i2c_devices[ACPI_I2C_MAX_DEVICES];
static size_t i2c_device_count = 0;
// ACPI SDT header is 36 bytes AML starts concurrently, nice and convenient
#define ACPI_SDT_HEADER_LEN 36
static void walk_sdt(struct acpi_sdt *sdt, aml_walk_ctx_t *ctx) {
if (!sdt) return;
if (sdt->length <= ACPI_SDT_HEADER_LEN) return;
const uint8_t *aml = (const uint8_t *)sdt + ACPI_SDT_HEADER_LEN;
size_t len = sdt->length - ACPI_SDT_HEADER_LEN;
aml_walk_table(aml, len, ctx);
}
static void walk_all_ssdts(aml_walk_ctx_t *ctx) {
struct acpi_rsdp *rsdp = acpi_get_rsdp();
if (!rsdp) return;
if (rsdp->revision >= 2 && rsdp->xsdt_address) {
struct acpi_xsdt *xsdt = (struct acpi_xsdt *)p2v(rsdp->xsdt_address);
size_t entries = (xsdt->header.length - sizeof(struct acpi_sdt)) / 8;
for (size_t i = 0; i < entries; i++) {
struct acpi_sdt *sdt = (struct acpi_sdt *)p2v(xsdt->tables[i]);
if (!sdt) continue;
if (__builtin_memcmp(sdt->signature, "SSDT", 4) == 0)
walk_sdt(sdt, ctx);
}
return;
}
// RSDT fallback
if (!rsdp->rsdt_address) return;
struct acpi_sdt *rsdt = (struct acpi_sdt *)p2v(rsdp->rsdt_address);
uint32_t *tables = (uint32_t *)((uint8_t *)rsdt + sizeof(struct acpi_sdt));
size_t entries = (rsdt->length - sizeof(struct acpi_sdt)) / 4;
for (size_t i = 0; i < entries; i++) {
struct acpi_sdt *sdt = (struct acpi_sdt *)p2v(tables[i]);
if (!sdt) continue;
if (__builtin_memcmp(sdt->signature, "SSDT", 4) == 0)
walk_sdt(sdt, ctx);
}
}
int acpi_i2c_enumerate(void) {
i2c_device_count = 0;
aml_walk_ctx_t ctx = {
.devices = i2c_devices,
.capacity = ACPI_I2C_MAX_DEVICES,
.count = 0,
};
// Walk DSDT - pointed to by FADT, not listed in XSDT/RSDT
struct acpi_sdt *dsdt = acpi_get_dsdt();
if (dsdt) {
walk_sdt(dsdt, &ctx);
} else {
serial_write("[acpi_i2c] warning: DSDT not found\n");
}
// Walk all SSDTs
walk_all_ssdts(&ctx);
i2c_device_count = ctx.count;
for (size_t i = 0; i < i2c_device_count; i++) {
const aml_i2c_dev_t *d = &i2c_devices[i];
serial_write("[acpi_i2c] ");
serial_write(d->name);
serial_write(" hid=");
serial_write(d->hid);
serial_write(" addr=0x");
serial_write_hex(d->slave_address);
serial_write(" speed=");
serial_write_num(d->speed_hz);
serial_write(" Hz");
if (d->ten_bit_addr) serial_write(" 10-bit");
if (d->power_flags & (AML_PWR_HAS_PS0 | AML_PWR_HAS_PS3)) serial_write(" PS0/3");
if (d->power_flags & (AML_PWR_HAS_PR0 | AML_PWR_HAS_PR3)) serial_write(" PR0/3");
if (d->has_dsm) {
serial_write(" hid_desc_reg=0x");
serial_write_hex(d->hid_desc_addr);
}
serial_write("\n");
}
if (i2c_device_count != 0) {
log_ok("I2C enumeration complete");
} else {
log_fail("No I2C devices found in ACPI tables");
}
return (int)i2c_device_count;
}
size_t acpi_i2c_count(void) {
return i2c_device_count;
}
const aml_i2c_dev_t *acpi_i2c_get(size_t index) {
if (index >= i2c_device_count) return NULL;
return &i2c_devices[index];
}

View file

@ -1,20 +0,0 @@
// Copyright (c) 2026 Myles Wilson (myles@bleedkernel.com)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef ACPI_I2C_H
#define ACPI_I2C_H
#include <stdint.h>
#include <stddef.h>
#include "../ACPI/acpi_aml.h"
#define ACPI_I2C_MAX_DEVICES 32
int acpi_i2c_enumerate(void);
size_t acpi_i2c_count(void);
const aml_i2c_dev_t *acpi_i2c_get(size_t index);
#endif

View file

@ -3,8 +3,6 @@
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "bootfs.h"
#include "disk.h"
#include "fat32.h"
#include "../sys/bootfs_state.h"
#include "vfs.h"
#include "core/kutils.h"
@ -15,14 +13,6 @@
extern void serial_write(const char *str);
extern void serial_write_hex(uint64_t value);
typedef struct bootfs_custom_file {
char name[128]; /* filename relative to /boot, e.g. "boredos.elf" */
uint8_t *data;
uint32_t size;
uint32_t capacity; /* 0 = read-only initrd pointer; >0 = heap-allocated writable */
struct bootfs_custom_file *next;
} bootfs_custom_file_t;
typedef struct {
char path[512];
int offset;
@ -65,42 +55,16 @@ static vfs_fs_ops_t bootfs_ops = {
};
bootfs_state_t g_bootfs_state = {0};
static char g_limine_conf_path[64] = "";
static bootfs_custom_file_t *bootfs_find_custom(const char *name) {
bootfs_custom_file_t *f = (bootfs_custom_file_t*)g_bootfs_state.custom_files;
while (f) {
if (strcmp(f->name, name) == 0) return f;
f = f->next;
}
return NULL;
}
void bootfs_register_file(const char *name, void *data, uint32_t size) {
if (!name || !data) return;
bootfs_custom_file_t *f = bootfs_find_custom(name);
if (!f) {
f = (bootfs_custom_file_t*)kmalloc(sizeof(bootfs_custom_file_t));
if (!f) return;
memset(f, 0, sizeof(bootfs_custom_file_t));
strcpy(f->name, name);
f->next = (bootfs_custom_file_t*)g_bootfs_state.custom_files;
g_bootfs_state.custom_files = f;
}
f->data = (uint8_t*)data;
f->size = size;
f->capacity = 0;
}
static bool is_metadata_path(const char *path) {
if (!path) return false;
return strncmp(path, "metadata", 8) == 0;
return k_strncmp(path, "metadata", 8) == 0;
}
static bool is_metadata_file(const char *path) {
if (strcmp(path, "metadata/boot_time") == 0) return true;
if (strcmp(path, "metadata/boot_flags") == 0) return true;
if (strcmp(path, "metadata/version") == 0) return true;
if (k_strcmp(path, "metadata/boot_time") == 0) return true;
if (k_strcmp(path, "metadata/boot_flags") == 0) return true;
if (k_strcmp(path, "metadata/version") == 0) return true;
return false;
}
@ -111,8 +75,8 @@ static void* bootfs_open(void *fs_private, const char *path, const char *mode) {
bootfs_handle_t *h = (bootfs_handle_t*)kmalloc(sizeof(bootfs_handle_t));
if (!h) return NULL;
memset(h, 0, sizeof(bootfs_handle_t));
strcpy(h->path, path);
k_memset(h, 0, sizeof(bootfs_handle_t));
k_strcpy(h->path, path);
h->offset = 0;
if (path[0] == '\0') {
@ -134,28 +98,28 @@ static int generate_metadata_content(const char *file, char *buffer, int max_siz
buffer[0] = '\0';
int len = 0;
if (strcmp(file, "metadata/boot_time") == 0) {
if (k_strcmp(file, "metadata/boot_time") == 0) {
extern uint32_t wm_get_ticks(void);
uint32_t ticks = wm_get_ticks();
strcpy(buffer, "Boot time: ");
k_strcpy(buffer, "Boot time: ");
char time_buf[32];
itoa(g_bootfs_state.boot_time_ms, time_buf);
strcpy(buffer + strlen(buffer), time_buf);
strcpy(buffer + strlen(buffer), " ms\nTicks: ");
itoa(ticks, time_buf);
strcpy(buffer + strlen(buffer), time_buf);
strcpy(buffer + strlen(buffer), "\n");
len = strlen(buffer);
} else if (strcmp(file, "metadata/version") == 0) {
strcpy(buffer, "Bootloader: ");
strcpy(buffer + strlen(buffer), g_bootfs_state.bootloader_name);
strcpy(buffer + strlen(buffer), "\nVersion: ");
strcpy(buffer + strlen(buffer), g_bootfs_state.bootloader_version);
strcpy(buffer + strlen(buffer), "\n");
len = strlen(buffer);
} else if (strcmp(file, "metadata/boot_flags") == 0) {
strcpy(buffer, "Boot flags: 0x");
k_itoa(g_bootfs_state.boot_time_ms, time_buf);
k_strcpy(buffer + k_strlen(buffer), time_buf);
k_strcpy(buffer + k_strlen(buffer), " ms\nTicks: ");
k_itoa(ticks, time_buf);
k_strcpy(buffer + k_strlen(buffer), time_buf);
k_strcpy(buffer + k_strlen(buffer), "\n");
len = k_strlen(buffer);
} else if (k_strcmp(file, "metadata/version") == 0) {
k_strcpy(buffer, "Bootloader: ");
k_strcpy(buffer + k_strlen(buffer), g_bootfs_state.bootloader_name);
k_strcpy(buffer + k_strlen(buffer), "\nVersion: ");
k_strcpy(buffer + k_strlen(buffer), g_bootfs_state.bootloader_version);
k_strcpy(buffer + k_strlen(buffer), "\n");
len = k_strlen(buffer);
} else if (k_strcmp(file, "metadata/boot_flags") == 0) {
k_strcpy(buffer, "Boot flags: 0x");
char flags_buf[8];
uint8_t flags = g_bootfs_state.boot_flags;
int hex_digit = (flags >> 4) & 0xF;
@ -164,8 +128,8 @@ static int generate_metadata_content(const char *file, char *buffer, int max_siz
flags_buf[1] = hex_digit < 10 ? '0' + hex_digit : 'a' + (hex_digit - 10);
flags_buf[2] = '\n';
flags_buf[3] = '\0';
strcpy(buffer + strlen(buffer), flags_buf);
len = strlen(buffer);
k_strcpy(buffer + k_strlen(buffer), flags_buf);
len = k_strlen(buffer);
}
return len;
@ -180,45 +144,27 @@ static int bootfs_read(void *fs_private, void *handle, void *buf, int size) {
int content_len = 0;
if (strcmp(h->path, "limine.conf") == 0) {
memcpy(content_buffer, g_bootfs_state.limine_conf,
if (k_strcmp(h->path, "limine.conf") == 0) {
k_memcpy(content_buffer, g_bootfs_state.limine_conf,
g_bootfs_state.limine_conf_len);
content_len = g_bootfs_state.limine_conf_len;
} else if (strcmp(h->path, "kernel") == 0) {
strcpy(content_buffer, "Kernel reference\nSize: ");
} else if (k_strcmp(h->path, "kernel") == 0) {
k_strcpy(content_buffer, "Kernel reference\nSize: ");
char size_buf[32];
itoa(g_bootfs_state.kernel_size, size_buf);
strcpy(content_buffer + strlen(content_buffer), size_buf);
strcpy(content_buffer + strlen(content_buffer), " bytes\n");
content_len = strlen(content_buffer);
} else if (strcmp(h->path, "initrd") == 0) {
strcpy(content_buffer, "Initial ramdisk reference\nSize: ");
k_itoa(g_bootfs_state.kernel_size, size_buf);
k_strcpy(content_buffer + k_strlen(content_buffer), size_buf);
k_strcpy(content_buffer + k_strlen(content_buffer), " bytes\n");
content_len = k_strlen(content_buffer);
} else if (k_strcmp(h->path, "initrd") == 0) {
k_strcpy(content_buffer, "Initial ramdisk reference\nSize: ");
char size_buf[32];
itoa(g_bootfs_state.initrd_size, size_buf);
strcpy(content_buffer + strlen(content_buffer), size_buf);
strcpy(content_buffer + strlen(content_buffer), " bytes\n");
content_len = strlen(content_buffer);
} else if (strcmp(h->path, "initrd.tar") == 0) {
kfree(content_buffer);
if (h->offset >= (int)g_bootfs_state.initrd_size) return 0;
int avail = (int)g_bootfs_state.initrd_size - h->offset;
int to_read = (size < avail) ? size : avail;
memcpy(buf, (uint8_t*)g_bootfs_state.initrd_ptr + h->offset, to_read);
h->offset += to_read;
return to_read;
k_itoa(g_bootfs_state.initrd_size, size_buf);
k_strcpy(content_buffer + k_strlen(content_buffer), size_buf);
k_strcpy(content_buffer + k_strlen(content_buffer), " bytes\n");
content_len = k_strlen(content_buffer);
} else if (is_metadata_file(h->path)) {
content_len = generate_metadata_content(h->path, content_buffer, 4096);
} else {
bootfs_custom_file_t *cf = bootfs_find_custom(h->path);
if (cf) {
kfree(content_buffer);
if (h->offset >= (int)cf->size) return 0;
int avail = (int)cf->size - h->offset;
int to_read = (avail < size) ? avail : size;
memcpy(buf, cf->data + h->offset, to_read);
h->offset += to_read;
return to_read;
}
kfree(content_buffer);
return -1;
}
@ -232,7 +178,7 @@ static int bootfs_read(void *fs_private, void *handle, void *buf, int size) {
int available = content_len - h->offset;
int read_size = (available < size) ? available : size;
memcpy(buf, content_buffer + h->offset, read_size);
k_memcpy(buf, content_buffer + h->offset, read_size);
h->offset += read_size;
kfree(content_buffer);
@ -243,7 +189,7 @@ static int bootfs_write(void *fs_private, void *handle, const void *buf, int siz
bootfs_handle_t *h = (bootfs_handle_t*)handle;
if (!h || !buf || size <= 0) return -1;
if (strcmp(h->path, "limine.conf") != 0) {
if (k_strcmp(h->path, "limine.conf") != 0) {
return -1;
}
@ -251,19 +197,21 @@ static int bootfs_write(void *fs_private, void *handle, const void *buf, int siz
if (max_write <= 0) return -1;
int write_size = (size < max_write) ? size : max_write;
memcpy(g_bootfs_state.limine_conf + h->offset, buf, write_size);
k_memcpy(g_bootfs_state.limine_conf + h->offset, buf, write_size);
h->offset += write_size;
if (h->offset > g_bootfs_state.limine_conf_len) {
g_bootfs_state.limine_conf_len = h->offset;
}
if (g_limine_conf_path[0] != '\0') {
vfs_file_t *fat_conf = vfs_open(g_limine_conf_path, "w");
if (fat_conf) {
vfs_write(fat_conf, g_bootfs_state.limine_conf, g_bootfs_state.limine_conf_len);
vfs_close(fat_conf);
}
extern vfs_file_t* vfs_open(const char *path, const char *mode);
extern int vfs_write(vfs_file_t *file, const void *buf, int size);
extern void vfs_close(vfs_file_t *file);
vfs_file_t *fat_conf = vfs_open("/limine.conf", "w");
if (fat_conf) {
vfs_write(fat_conf, g_bootfs_state.limine_conf, g_bootfs_state.limine_conf_len);
vfs_close(fat_conf);
}
return write_size;
@ -299,49 +247,34 @@ static int bootfs_readdir(void *fs_private, const char *rel_path, vfs_dirent_t *
if (rel_path[0] == '\0') {
if (count < max) {
strcpy(entries[count].name, "limine.conf");
k_strcpy(entries[count].name, "limine.conf");
entries[count].size = g_bootfs_state.limine_conf_len;
entries[count].is_directory = 0;
count++;
}
if (count < max) {
strcpy(entries[count].name, "kernel");
k_strcpy(entries[count].name, "kernel");
entries[count].size = g_bootfs_state.kernel_size;
entries[count].is_directory = 0;
count++;
}
if (count < max) {
strcpy(entries[count].name, "initrd");
k_strcpy(entries[count].name, "initrd");
entries[count].size = g_bootfs_state.initrd_size;
entries[count].is_directory = 0;
count++;
}
if (count < max) {
strcpy(entries[count].name, "initrd.tar");
entries[count].size = g_bootfs_state.initrd_size;
entries[count].is_directory = 0;
count++;
}
if (count < max) {
strcpy(entries[count].name, "metadata");
k_strcpy(entries[count].name, "metadata");
entries[count].size = 0;
entries[count].is_directory = 1;
count++;
}
bootfs_custom_file_t *cf = (bootfs_custom_file_t*)g_bootfs_state.custom_files;
while (cf && count < max) {
strcpy(entries[count].name, cf->name);
entries[count].size = cf->size;
entries[count].is_directory = 0;
count++;
cf = cf->next;
}
}
else if (strcmp(rel_path, "metadata") == 0) {
else if (k_strcmp(rel_path, "metadata") == 0) {
const char *meta_files[] = {
"boot_time",
"boot_flags",
@ -349,7 +282,7 @@ static int bootfs_readdir(void *fs_private, const char *rel_path, vfs_dirent_t *
};
for (int i = 0; i < 3 && count < max; i++) {
strcpy(entries[count].name, meta_files[i]);
k_strcpy(entries[count].name, meta_files[i]);
entries[count].size = 0;
entries[count].is_directory = 0;
count++;
@ -367,7 +300,7 @@ static bool bootfs_rmdir(void *fs_private, const char *rel_path) {
if (!rel_path) rel_path = "";
if (rel_path[0] == '/') rel_path++;
if (strcmp(rel_path, "metadata") == 0) {
if (k_strcmp(rel_path, "metadata") == 0) {
return false; /* metadata directory is protected */
}
@ -379,7 +312,7 @@ static bool bootfs_unlink(void *fs_private, const char *rel_path) {
if (rel_path[0] == '/') rel_path++;
/* Only limine.conf can be deleted */
if (strcmp(rel_path, "limine.conf") != 0) {
if (k_strcmp(rel_path, "limine.conf") != 0) {
return false;
}
@ -411,24 +344,24 @@ static bool bootfs_rename(void *fs_private, const char *old_path, const char *ne
if (new_rel[0] == '/') new_rel++;
/* Only limine.conf can be renamed */
if (strcmp(old_rel, "limine.conf") != 0) {
if (k_strcmp(old_rel, "limine.conf") != 0) {
return false;
}
/* kernel and initrd are protected */
if (strcmp(new_rel, "kernel") == 0 || strcmp(new_rel, "initrd") == 0) {
if (k_strcmp(new_rel, "kernel") == 0 || k_strcmp(new_rel, "initrd") == 0) {
return false;
}
/* metadata directory is protected */
if (strncmp(new_rel, "metadata", 8) == 0) {
if (k_strncmp(new_rel, "metadata", 8) == 0) {
return false;
}
extern bool vfs_rename(const char *old_path, const char *new_path);
char new_partition_path[256];
strcpy(new_partition_path, "/");
k_strcpy(new_partition_path, "/");
/* Manually append new_rel to new_partition_path */
int path_len = 0;
@ -442,7 +375,7 @@ static bool bootfs_rename(void *fs_private, const char *old_path, const char *ne
return false;
}
memcpy(new_partition_path + path_len, new_rel, rel_len + 1);
k_memcpy(new_partition_path + path_len, new_rel, rel_len + 1);
/* Rename on partition filesystem */
bool result = vfs_rename("/limine.conf", new_partition_path);
@ -466,15 +399,12 @@ static bool bootfs_exists(void *fs_private, const char *rel_path) {
if (rel_path[0] == '\0') return true;
if (strcmp(rel_path, "limine.conf") == 0) return true;
if (strcmp(rel_path, "efi") == 0) return true;
if (strcmp(rel_path, "kernel") == 0) return true;
if (strcmp(rel_path, "initrd") == 0) return true;
if (strcmp(rel_path, "initrd.tar") == 0) return true;
if (k_strcmp(rel_path, "limine.conf") == 0) return true;
if (k_strcmp(rel_path, "kernel") == 0) return true;
if (k_strcmp(rel_path, "initrd") == 0) return true;
if (strcmp(rel_path, "metadata") == 0) return true;
if (k_strcmp(rel_path, "metadata") == 0) return true;
if (is_metadata_file(rel_path)) return true;
if (bootfs_find_custom(rel_path)) return true;
return false;
}
@ -484,8 +414,7 @@ static bool bootfs_is_dir(void *fs_private, const char *rel_path) {
if (rel_path[0] == '/') rel_path++;
if (rel_path[0] == '\0') return true;
if (strcmp(rel_path, "efi") == 0) return true;
if (strcmp(rel_path, "metadata") == 0) return true;
if (k_strcmp(rel_path, "metadata") == 0) return true;
return false;
}
@ -495,42 +424,37 @@ static int bootfs_get_info(void *fs_private, const char *rel_path, vfs_dirent_t
if (!rel_path) rel_path = "";
if (rel_path[0] == '/') rel_path++;
memset(info, 0, sizeof(vfs_dirent_t));
k_memset(info, 0, sizeof(vfs_dirent_t));
if (rel_path[0] == '\0') {
strcpy(info->name, "/");
k_strcpy(info->name, "/");
info->is_directory = 1;
return 0;
}
if (strcmp(rel_path, "limine.conf") == 0) {
strcpy(info->name, "limine.conf");
if (k_strcmp(rel_path, "limine.conf") == 0) {
k_strcpy(info->name, "limine.conf");
info->size = g_bootfs_state.limine_conf_len;
info->is_directory = 0;
return 0;
}
if (strcmp(rel_path, "kernel") == 0) {
strcpy(info->name, "kernel");
if (k_strcmp(rel_path, "kernel") == 0) {
k_strcpy(info->name, "kernel");
info->size = g_bootfs_state.kernel_size;
info->is_directory = 0;
return 0;
} else if (strcmp(rel_path, "initrd") == 0) {
strcpy(info->name, "initrd");
}
if (k_strcmp(rel_path, "initrd") == 0) {
k_strcpy(info->name, "initrd");
info->size = g_bootfs_state.initrd_size;
info->is_directory = 0;
return 0;
} else if (strcmp(rel_path, "initrd.tar") == 0) {
strcpy(info->name, "initrd.tar");
info->size = g_bootfs_state.initrd_size;
info->is_directory = 0;
return 0;
} else if (strcmp(rel_path, "metadata") == 0) {
strcpy(info->name, "metadata");
info->is_directory = 1;
return 0;
} else if (strcmp(rel_path, "efi") == 0) {
strcpy(info->name, "efi");
}
if (k_strcmp(rel_path, "metadata") == 0) {
k_strcpy(info->name, "metadata");
info->is_directory = 1;
return 0;
}
@ -538,20 +462,12 @@ static int bootfs_get_info(void *fs_private, const char *rel_path, vfs_dirent_t
if (is_metadata_file(rel_path)) {
char temp_buf[4096];
int len = generate_metadata_content(rel_path, temp_buf, 4096);
strcpy(info->name, rel_path + 9);
k_strcpy(info->name, rel_path + 9);
info->size = len;
info->is_directory = 0;
return 0;
}
bootfs_custom_file_t *cf = bootfs_find_custom(rel_path);
if (cf) {
strcpy(info->name, cf->name);
info->size = cf->size;
info->is_directory = 0;
return 0;
}
return -1;
}
@ -565,22 +481,17 @@ static uint32_t bootfs_get_size(void *file_handle) {
bootfs_handle_t *h = (bootfs_handle_t*)file_handle;
if (!h) return 0;
if (strcmp(h->path, "limine.conf") == 0) {
if (k_strcmp(h->path, "limine.conf") == 0) {
return g_bootfs_state.limine_conf_len;
} else if (strcmp(h->path, "kernel") == 0) {
} else if (k_strcmp(h->path, "kernel") == 0) {
return g_bootfs_state.kernel_size;
} else if (strcmp(h->path, "initrd") == 0) {
return g_bootfs_state.initrd_size;
} else if (strcmp(h->path, "initrd.tar") == 0) {
} else if (k_strcmp(h->path, "initrd") == 0) {
return g_bootfs_state.initrd_size;
} else if (is_metadata_file(h->path)) {
char temp_buf[4096];
return generate_metadata_content(h->path, temp_buf, 4096);
}
bootfs_custom_file_t *cf = bootfs_find_custom(h->path);
if (cf) return cf->size;
return 0;
}
@ -589,10 +500,10 @@ vfs_fs_ops_t* bootfs_get_ops(void) {
}
void bootfs_state_init(void) {
memset(&g_bootfs_state, 0, sizeof(bootfs_state_t));
k_memset(&g_bootfs_state, 0, sizeof(bootfs_state_t));
strcpy(g_bootfs_state.bootloader_name, "Limine");
strcpy(g_bootfs_state.bootloader_version, "6.0.0");
k_strcpy(g_bootfs_state.bootloader_name, "Limine");
k_strcpy(g_bootfs_state.bootloader_version, "6.0.0");
g_bootfs_state.limine_conf[0] = '\0';
@ -607,55 +518,24 @@ void bootfs_init(void) {
bootfs_state_init();
}
void bootfs_mount_boot_partition(void) {
int count = disk_get_count();
Disk *esp = NULL;
for (int i = 0; i < count; i++) {
Disk *d = disk_get_by_index(i);
if (d && d->is_esp) {
esp = d;
break;
}
}
if (esp) {
void *fs_private = fat32_mount_volume(esp);
if (fs_private) {
vfs_mount("/boot/efi", esp->devname, "fat32", fat32_get_realfs_ops(), fs_private);
serial_write("[BOOTFS] Mounted ESP at /boot/efi\n");
}
} else {
serial_write("[BOOTFS] No ESP found for mounting\n");
}
}
void bootfs_refresh_from_disk(void) {
extern vfs_file_t* vfs_open(const char *path, const char *mode);
extern int vfs_read(vfs_file_t *file, void *buf, int size);
extern void vfs_close(vfs_file_t *file);
vfs_file_t *boot_conf = vfs_open("/boot/efi/limine.conf", "r");
if (boot_conf) {
strcpy(g_limine_conf_path, "/boot/efi/limine.conf");
} else {
boot_conf = vfs_open("/limine.conf", "r");
if (boot_conf) {
strcpy(g_limine_conf_path, "/limine.conf");
}
}
vfs_file_t *boot_conf = vfs_open("/limine.conf", "r");
if (boot_conf) {
int bytes_read = vfs_read(boot_conf, g_bootfs_state.limine_conf, 2047);
if (bytes_read > 0) {
g_bootfs_state.limine_conf[bytes_read] = '\0';
g_bootfs_state.limine_conf_len = bytes_read;
serial_write("[BOOTFS] Loaded limine.conf from ");
serial_write(g_limine_conf_path);
serial_write("\n");
serial_write("[BOOTFS] Loaded limine.conf from partition: ");
extern void serial_write_hex(uint64_t value);
serial_write_hex(bytes_read);
serial_write(" bytes\n");
}
vfs_close(boot_conf);
} else {
serial_write("[BOOTFS] Warning: limine.conf not found on disk\n");
serial_write("[BOOTFS] Warning: /limine.conf not found on partition\n");
}
}

View file

@ -7,9 +7,7 @@
#include "vfs.h"
void bootfs_init(void);
void bootfs_mount_boot_partition(void);
void bootfs_refresh_from_disk(void);
vfs_fs_ops_t* bootfs_get_ops(void);
void bootfs_register_file(const char *name, void *data, uint32_t size);
#endif

View file

@ -15,7 +15,7 @@
static spinlock_t ramfs_lock = SPINLOCK_INIT; // Protects the RAM-based filesystem (/)
#define MAX_CLUSTERS 32768
#define MAX_CLUSTERS 8192
#define MAX_OPEN_HANDLES 32
// In-memory FAT table
@ -49,7 +49,6 @@ typedef struct {
uint32_t sectors_per_cluster;
uint32_t root_cluster;
uint32_t fat_size; // sectors
uint32_t num_fats;
uint32_t total_sectors;
uint32_t partition_offset; // LBA offset of partition start
bool mounted;
@ -61,16 +60,11 @@ typedef struct {
// Dynamically allocated volumes (no longer A-Z indexed)
static bool realfs_mkdir_vol(FAT32_Volume *vol, const char *path);
extern void serial_write(const char *str);
extern void serial_write_hex(uint32_t val);
#define MAX_REAL_VOLUMES 8
static FAT32_Volume *real_volumes[MAX_REAL_VOLUMES];
static int real_volume_count = 0;
static FAT32_Volume *root_volume = NULL;
// Forward declarations for volume-aware functions
static uint32_t realfs_allocate_cluster(FAT32_Volume *vol);
static void handle_fat32_truncate(FAT32_FileHandle *handle);
static FAT32_FileHandle* realfs_open_from_vol(FAT32_Volume *vol, const char *path, const char *mode);
static int realfs_list_directory_vol(FAT32_Volume *vol, const char *path, FAT32_FileInfo *entries, int max_entries);
static bool realfs_delete_from_vol(FAT32_Volume *vol, const char *path);
@ -84,16 +78,6 @@ static bool realfs_find_contiguous_free(FAT32_Volume *vol, uint32_t dir_start_cl
static uint8_t fat_lfn_checksum(const uint8_t *short_name);
static void extract_lfn_chars(FAT32_LFNEntry *lfn, char *buffer);
static void to_dos_filename(const char *filename, char *dos_name);
static bool realfs_create_entry(FAT32_Volume *vol, uint32_t parent_cluster, const char *name, uint8_t attributes, uint32_t start_cluster, uint32_t file_size, uint32_t *out_sector, uint32_t *out_offset);
void fat32_set_root_volume(void *fs_private) {
root_volume = (FAT32_Volume*)fs_private;
}
static void fat32_sync_if_root(FAT32_Volume *vol) {
if (!vol || vol != root_volume) return;
disk_sync(vol->disk);
}
// === Helper Functions (Shared) ===
@ -161,7 +145,7 @@ static void extract_parent_path(const char *path, char *parent) {
int i = len - 1;
while (i > 0 && path[i] == '/') i--;
while (i > 0 && path[i] != '/') i--;
if (i <= 0) {
if (i == 0) {
parent[0] = '/';
parent[1] = 0;
} else {
@ -181,12 +165,9 @@ static char parse_drive_from_path(const char **path_ptr) {
*path_ptr = path + 2;
return drive;
}
if (root_volume != NULL) return 0; // Pseudo-drive 0 means "use root volume"
return 'A'; // Default to RAMFS
}
// Normalize path (remove .., ., etc)
void fat32_normalize_path(const char *path, char *normalized) {
char *temp = (char*)kmalloc(FAT32_MAX_PATH);
@ -486,7 +467,6 @@ static bool realfs_mount_volume(FAT32_Volume *vol, Disk *disk) {
vol->sectors_per_cluster = bpb->sectors_per_cluster;
vol->root_cluster = bpb->root_cluster;
vol->fat_size = bpb->sectors_per_fat_32;
vol->num_fats = bpb->num_fats;
vol->total_sectors = bpb->total_sectors_32;
vol->mounted = true;
vol->cached_fat_sector = 0xFFFFFFFF;
@ -551,32 +531,8 @@ static uint32_t realfs_next_cluster(FAT32_Volume *vol, uint32_t cluster) {
return next;
}
static void realfs_set_fat_entry(FAT32_Volume *vol, uint32_t cluster, uint32_t value) {
uint32_t offset = cluster * 4;
uint32_t sector_offset = offset / 512;
uint32_t byte_offset = offset % 512;
uint8_t *buf = (uint8_t*)kmalloc(512);
if (!buf) return;
for (uint32_t i = 0; i < vol->num_fats; i++) {
uint32_t sector = vol->fat_begin_lba + (i * vol->fat_size) + sector_offset;
if (vol->disk->read_sector(vol->disk, sector, buf) == 0) {
uint32_t *entry = (uint32_t*)(buf + byte_offset);
*entry = (*entry & 0xF0000000) | (value & 0x0FFFFFFF);
vol->disk->write_sector(vol->disk, sector, buf);
if (vol->cached_fat_sector == sector) vol->cached_fat_sector = 0xFFFFFFFF;
}
}
kfree(buf);
}
static int realfs_read_cluster(FAT32_Volume *vol, uint32_t cluster, uint8_t *buffer) {
if (!vol || cluster < 2) return -1;
uint32_t lba = vol->cluster_begin_lba + (cluster - 2) * vol->sectors_per_cluster;
if (vol->disk->read_sectors) {
return vol->disk->read_sectors(vol->disk, lba, vol->sectors_per_cluster, buffer);
}
for (uint32_t i = 0; i < vol->sectors_per_cluster; i++) {
if (vol->disk->read_sector(vol->disk, lba + i, buffer + (i * 512)) != 0) return -1;
}
@ -594,7 +550,7 @@ static void to_dos_filename(const char *filename, char *out) {
if (name_len > 8) name_len = 8;
for (int i = 0; i < name_len; i++) {
char c = filename[i];
if (c >= 'a' && c <= 'z') c -= 32;
// Preserve case - don't convert to uppercase
out[i] = c;
}
if (dot != -1) {
@ -602,11 +558,12 @@ static void to_dos_filename(const char *filename, char *out) {
if (ext_len > 3) ext_len = 3;
for (int i = 0; i < ext_len; i++) {
char c = filename[dot + 1 + i];
if (c >= 'a' && c <= 'z') c -= 32;
// Preserve case - don't convert to uppercase
out[8 + i] = c;
}
}
}
static uint8_t fat_lfn_checksum(const uint8_t *short_name) {
uint8_t sum = 0;
for (int i = 11; i > 0; i--) {
@ -615,91 +572,6 @@ static uint8_t fat_lfn_checksum(const uint8_t *short_name) {
return sum;
}
static bool realfs_create_entry(FAT32_Volume *vol, uint32_t parent_cluster, const char *name, uint8_t attributes, uint32_t start_cluster, uint32_t file_size, uint32_t *out_sector, uint32_t *out_offset) {
char dos_name[11];
to_dos_filename(name, dos_name);
int name_len = fs_strlen(name);
bool needs_lfn = false;
int dot_pos = -1;
for (int i = 0; i < name_len; i++) {
if (name[i] == '.') { dot_pos = i; break; }
if (name[i] >= 'a' && name[i] <= 'z') needs_lfn = true;
}
if (!needs_lfn) {
if (dot_pos == -1) needs_lfn = (name_len > 8);
else needs_lfn = (dot_pos > 8) || (name_len - dot_pos - 1 > 3);
}
if (!needs_lfn && dot_pos != -1) {
for (int i = dot_pos + 1; i < name_len; i++) {
if (name[i] >= 'a' && name[i] <= 'z') { needs_lfn = true; break; }
}
}
int lfn_entries = needs_lfn ? ((name_len + 12) / 13) : 0;
int total_entries = lfn_entries + 1;
uint32_t free_cluster = 0;
int start_idx = -1;
if (!realfs_find_contiguous_free(vol, parent_cluster, total_entries, &free_cluster, &start_idx)) {
return false;
}
uint8_t *buf = (uint8_t*)kmalloc(vol->sectors_per_cluster * 512);
if (!buf) return false;
if (realfs_read_cluster(vol, free_cluster, buf) != 0) {
kfree(buf);
return false;
}
FAT32_DirEntry *entries = (FAT32_DirEntry*)buf;
uint8_t checksum = fat_lfn_checksum((uint8_t*)dos_name);
for (int i = 0; i < lfn_entries; i++) {
FAT32_LFNEntry *lfn = (FAT32_LFNEntry*)&entries[start_idx + i];
lfn->order = (lfn_entries - i);
if (i == 0) lfn->order |= 0x40;
lfn->attr = ATTR_LFN;
lfn->type = 0;
lfn->checksum = checksum;
lfn->first_cluster = 0;
int char_offset = (lfn_entries - i - 1) * 13;
for (int k = 0; k < 13; k++) {
uint16_t c = 0xFFFF;
if (char_offset + k < name_len) c = name[char_offset + k];
else if (char_offset + k == name_len) c = 0x0000;
if (k < 5) lfn->name1[k] = c;
else if (k < 11) lfn->name2[k-5] = c;
else lfn->name3[k-11] = c;
}
}
FAT32_DirEntry *d = &entries[start_idx + lfn_entries];
for (int k = 0; k < 8; k++) d->filename[k] = dos_name[k];
for (int k = 0; k < 3; k++) d->extension[k] = dos_name[8+k];
d->attributes = attributes;
d->start_cluster_high = (start_cluster >> 16);
d->start_cluster_low = (start_cluster & 0xFFFF);
d->file_size = file_size;
if (realfs_write_cluster(vol, free_cluster, buf) != 0) {
kfree(buf);
return false;
}
uint32_t lba = vol->cluster_begin_lba + (free_cluster - 2) * vol->sectors_per_cluster;
*out_sector = lba + ((start_idx + lfn_entries) * 32) / 512;
*out_offset = ((start_idx + lfn_entries) * 32) % 512;
kfree(buf);
return true;
}
static FAT32_FileHandle* realfs_open_from_vol(FAT32_Volume *vol, const char *path, const char *mode) {
if (!vol || !vol->mounted) return NULL;
@ -740,10 +612,6 @@ static FAT32_FileHandle* realfs_open_from_vol(FAT32_Volume *vol, const char *pat
uint32_t entry_sector = 0;
uint32_t entry_offset = 0;
uint8_t *cluster_buf = (uint8_t*)kmalloc(vol->sectors_per_cluster * 512);
if (!cluster_buf) return NULL;
uint8_t *lfn_cl_buf = NULL;
while (*p) {
// Extract component
int i = 0;
@ -756,6 +624,8 @@ static FAT32_FileHandle* realfs_open_from_vol(FAT32_Volume *vol, const char *pat
// Search in current_cluster
found = false;
uint32_t search_cluster = current_cluster;
uint8_t *cluster_buf = (uint8_t*)kmalloc(vol->sectors_per_cluster * 512);
if (!cluster_buf) return NULL;
char lfn_buffer[256];
bool has_lfn = false;
@ -851,33 +721,112 @@ static FAT32_FileHandle* realfs_open_from_vol(FAT32_Volume *vol, const char *pat
if (!found) {
if ((mode[0] == 'w' || mode[0] == 'a') && *p == 0) {
if (realfs_create_entry(vol, current_cluster, component, ATTR_ARCHIVE, 0, 0, &entry_sector, &entry_offset)) {
FAT32_FileHandle *fh = ramfs_find_free_handle();
if (fh) {
fh->valid = true;
fh->volume = vol;
fh->start_cluster = 0;
fh->cluster = 0;
fh->position = 0;
fh->size = 0;
fh->mode = (mode[0] == 'a' ? 2 : 1);
fh->is_directory = false;
fh->attributes = ATTR_ARCHIVE;
fh->dir_sector = entry_sector;
fh->dir_offset = entry_offset;
if (cluster_buf) kfree(cluster_buf);
return fh;
}
}
if (cluster_buf) kfree(cluster_buf);
return NULL;
// Create file logic with LFN support
char dos_name[11];
to_dos_filename(component, dos_name);
int name_len = fs_strlen(component);
// Determine if LFN is needed:
// LFN is needed if the name contains characters that don't fit in 8.3 format
// or if the extension is longer than 3 chars or name part is longer than 8 chars
bool needs_lfn = false;
int dot_pos = -1;
for (int i = 0; i < name_len; i++) {
if (component[i] == '.') { dot_pos = i; break; }
}
if (dot_pos == -1) {
// No extension - need LFN if name > 8 chars
needs_lfn = (name_len > 8);
} else {
// Has extension
int name_part = dot_pos;
int ext_part = name_len - dot_pos - 1;
// Need LFN if name > 8 chars or extension > 3 chars
needs_lfn = (name_part > 8) || (ext_part > 3);
}
int lfn_entries = needs_lfn ? ((name_len + 12) / 13) : 0;
int total_entries = lfn_entries + 1;
uint32_t free_cluster = 0;
int start_entry_idx = -1;
uint8_t *lfn_cl_buf = (uint8_t*)kmalloc(vol->sectors_per_cluster * 512);
if (lfn_cl_buf && realfs_find_contiguous_free(vol, current_cluster, total_entries, &free_cluster, &start_entry_idx)) {
if (realfs_read_cluster(vol, free_cluster, lfn_cl_buf) == 0) {
FAT32_DirEntry *entries = (FAT32_DirEntry*)lfn_cl_buf;
uint8_t checksum = fat_lfn_checksum((uint8_t*)dos_name);
// Write LFN entries in reverse order
for (int i = 0; i < lfn_entries; i++) {
FAT32_LFNEntry *lfn = (FAT32_LFNEntry*)&entries[start_entry_idx + i];
lfn->order = (lfn_entries - i);
if (i == 0) lfn->order |= 0x40; // Last logical LFN entry
lfn->attr = ATTR_LFN;
lfn->type = 0;
lfn->checksum = checksum;
lfn->first_cluster = 0;
// Fill name chars (13 chars per entry)
int char_offset = (lfn_entries - i - 1) * 13;
for (int k = 0; k < 13; k++) {
uint16_t c = 0xFFFF;
if (char_offset + k < name_len) c = component[char_offset + k];
else if (char_offset + k == name_len) c = 0x0000;
if (k < 5) lfn->name1[k] = c;
else if (k < 11) lfn->name2[k-5] = c;
else lfn->name3[k-11] = c;
}
}
// Write 8.3 entry
FAT32_DirEntry *d = &entries[start_entry_idx + lfn_entries];
for(int k=0; k<8; k++) d->filename[k] = dos_name[k];
for(int k=0; k<3; k++) d->extension[k] = dos_name[8+k];
d->attributes = ATTR_ARCHIVE;
d->start_cluster_high = 0;
d->start_cluster_low = 0;
d->file_size = 0;
if (realfs_write_cluster(vol, free_cluster, lfn_cl_buf) == 0) {
uint32_t lba = vol->cluster_begin_lba + (free_cluster - 2) * vol->sectors_per_cluster;
entry_sector = lba + ((start_entry_idx + lfn_entries) * 32) / 512;
entry_offset = ((start_entry_idx + lfn_entries) * 32) % 512;
kfree(lfn_cl_buf);
if (cluster_buf) kfree(cluster_buf);
FAT32_FileHandle *fh = ramfs_find_free_handle();
if (fh) {
fh->valid = true;
fh->volume = vol;
fh->start_cluster = 0;
fh->cluster = 0;
fh->position = 0;
fh->size = 0;
fh->mode = (mode[0] == 'a' ? 2 : 1);
fh->is_directory = false;
fh->attributes = ATTR_ARCHIVE;
fh->dir_sector = entry_sector;
fh->dir_offset = entry_offset;
return fh;
}
}
}
}
if (lfn_cl_buf) kfree(lfn_cl_buf);
if (cluster_buf) kfree(cluster_buf);
return NULL;
}
if (cluster_buf) { kfree(cluster_buf); cluster_buf = NULL; }
kfree(cluster_buf);
return NULL;
}
kfree(cluster_buf);
}
// Found file/dir
FAT32_FileHandle *fh = ramfs_find_free_handle();
@ -894,10 +843,6 @@ static FAT32_FileHandle* realfs_open_from_vol(FAT32_Volume *vol, const char *pat
fh->dir_sector = entry_sector;
fh->dir_offset = entry_offset;
if (mode[0] == 'w' && !is_directory) {
handle_fat32_truncate(fh);
}
if (mode[0] == 'a') {
fh->position = fh->size;
// Seek to EOF cluster
@ -910,10 +855,10 @@ static FAT32_FileHandle* realfs_open_from_vol(FAT32_Volume *vol, const char *pat
pos += cluster_size;
}
}
return fh;
}
if (cluster_buf) kfree(cluster_buf);
if (lfn_cl_buf) kfree(lfn_cl_buf);
return fh;
return NULL;
}
static int realfs_read_file(FAT32_FileHandle *handle, void *buffer, int size, uint8_t *ext_cluster_buf) {
@ -960,11 +905,8 @@ static int realfs_read_file(FAT32_FileHandle *handle, void *buffer, int size, ui
}
static int realfs_write_cluster(FAT32_Volume *vol, uint32_t cluster, const uint8_t *buffer) {
if (!vol || cluster < 2 || cluster >= 0x0FFFFFF8) return -1;
if (cluster < 2 || cluster >= 0x0FFFFFF8) return -1;
uint32_t lba = vol->cluster_begin_lba + (cluster - 2) * vol->sectors_per_cluster;
if (vol->disk->write_sectors) {
return vol->disk->write_sectors(vol->disk, lba, vol->sectors_per_cluster, buffer);
}
for (uint32_t i = 0; i < vol->sectors_per_cluster; i++) {
if (vol->disk->write_sector(vol->disk, lba + i, buffer + (i * 512)) != 0) return -1;
}
@ -1046,11 +988,13 @@ static bool realfs_find_contiguous_free(FAT32_Volume *vol, uint32_t dir_start_cl
static uint32_t realfs_allocate_cluster(FAT32_Volume *vol) {
uint32_t current = vol->last_allocated_cluster;
if (current < 3) current = 3;
if (current < 2) current = 2;
uint32_t fat_entries = (vol->fat_size * 512) / 4;
uint32_t first_search = current;
// Skip cluster 2 as it's reserved for the root directory in FAT32
if (current == vol->root_cluster) current++;
uint8_t *fat_buf = (uint8_t*)kmalloc(512);
if (!fat_buf) return 0;
@ -1067,9 +1011,13 @@ static uint32_t realfs_allocate_cluster(FAT32_Volume *vol) {
uint32_t val = *(uint32_t*)&fat_buf[offset];
if ((val & 0x0FFFFFFF) == 0) {
kfree(fat_buf);
realfs_set_fat_entry(vol, current, 0x0FFFFFFF); // EOC
*(uint32_t*)&fat_buf[offset] = 0x0FFFFFFF; // EOC
vol->disk->write_sector(vol->disk, sector, fat_buf);
if (vol->cached_fat_sector == sector) {
vol->cached_fat_sector = 0xFFFFFFFF;
}
vol->last_allocated_cluster = current;
kfree(fat_buf);
return current;
}
current++;
@ -1080,32 +1028,6 @@ static uint32_t realfs_allocate_cluster(FAT32_Volume *vol) {
return 0; // Full
}
static void realfs_free_cluster_chain(FAT32_Volume *vol, uint32_t start_cluster) {
if (start_cluster == 0 || start_cluster >= 0x0FFFFFF8) return;
uint32_t current = start_cluster;
while (current < 0x0FFFFFF8 && current >= 2) {
uint32_t next = realfs_next_cluster(vol, current);
realfs_set_fat_entry(vol, current, 0);
if (next == current) break;
current = next;
}
}
static void handle_fat32_truncate(FAT32_FileHandle *handle) {
FAT32_Volume *vol = (FAT32_Volume*)handle->volume;
if (!vol || handle->start_cluster == 0) return;
uint32_t start = handle->start_cluster;
handle->start_cluster = 0;
handle->cluster = 0;
handle->size = 0;
handle->position = 0;
realfs_free_cluster_chain(vol, start);
realfs_update_dir_entry_size(vol, handle);
}
static int realfs_write_file(FAT32_FileHandle *handle, const void *buffer, int size, uint8_t *ext_cluster_buf) {
FAT32_Volume *vol = (FAT32_Volume*)handle->volume;
if (!vol) return 0;
@ -1148,24 +1070,31 @@ static int realfs_write_file(FAT32_FileHandle *handle, const void *buffer, int s
uint32_t new_cluster = realfs_allocate_cluster(vol);
if (new_cluster == 0) break;
realfs_set_fat_entry(vol, handle->cluster, new_cluster);
uint32_t fs = vol->fat_begin_lba + (handle->cluster * 4) / 512;
uint32_t fo = (handle->cluster * 4) % 512;
uint8_t fbuf[512];
if (vol->disk->read_sector(vol->disk, fs, fbuf) == 0) {
uint32_t old = *(uint32_t*)&fbuf[fo];
*(uint32_t*)&fbuf[fo] = (old & 0xF0000000) | (new_cluster & 0x0FFFFFFF);
vol->disk->write_sector(vol->disk, fs, fbuf);
if (vol->cached_fat_sector == fs) vol->cached_fat_sector = 0xFFFFFFFF;
}
next = new_cluster;
}
handle->cluster = next;
}
// Always zero the buffer first to ensure clean state
for (int i = 0; i < (int)cluster_size; i++) cluster_buf[i] = 0;
// If we're in the middle of a cluster, read the existing data first
if (offset > 0 || (handle->position < handle->size)) {
if (realfs_read_cluster(vol, handle->cluster, cluster_buf) != 0) break;
}
int to_copy = size - bytes_written;
int available = cluster_size - offset;
if (to_copy > available) to_copy = available;
if (offset > 0 || (handle->position < handle->size && (handle->position + to_copy) < handle->size)) {
if (realfs_read_cluster(vol, handle->cluster, cluster_buf) != 0) break;
} else {
if (to_copy < (int)cluster_size) {
for (int i = 0; i < (int)cluster_size; i++) cluster_buf[i] = 0;
}
}
// Copy new data into the cluster buffer
for (int i = 0; i < to_copy; i++) {
@ -1315,10 +1244,17 @@ static bool realfs_delete_from_vol(FAT32_Volume *vol, const char *path) {
}
// Mark the main entry as deleted
entry[e].filename[0] = 0xE5;
// Persist the changes to disk
// CRITICAL FIX: Write ALL sectors that contain modified entries
// LFN entries and main entry may span multiple sectors in the cluster
uint32_t lba = vol->cluster_begin_lba + (search_cluster - 2) * vol->sectors_per_cluster;
uint8_t sectors_to_write[8] = {0};
// Find all sectors touched by LFN and main entries
uint8_t sectors_to_write[8] = {0}; // Max 8 sectors per cluster
int num_sectors = 0;
// Mark sectors containing LFN entries
if (lfn_start_entry != -1) {
for (int k = lfn_start_entry; k < e; k++) {
int sect_idx = (k * 32) / 512;
@ -1376,8 +1312,30 @@ static bool realfs_delete_from_vol(FAT32_Volume *vol, const char *path) {
if (*p == 0) break; // End of path
}
realfs_free_cluster_chain(vol, file_start_cluster);
// 2. Free all clusters used by the file
if (file_start_cluster != 0 && file_start_cluster < 0x0FFFFFF8) {
uint32_t current = file_start_cluster;
while (current < 0x0FFFFFF8) {
uint32_t next = realfs_next_cluster(vol, current);
// Mark this cluster as free in FAT
uint32_t fat_sector = vol->fat_begin_lba + (current * 4) / 512;
uint32_t fat_offset = (current * 4) % 512;
uint8_t *fat_buf = (uint8_t*)kmalloc(512);
if (!fat_buf) break;
if (vol->disk->read_sector(vol->disk, fat_sector, fat_buf) == 0) {
*(uint32_t*)&fat_buf[fat_offset] = 0; // Free
vol->disk->write_sector(vol->disk, fat_sector, fat_buf);
if (vol->cached_fat_sector == fat_sector) vol->cached_fat_sector = 0xFFFFFFFF;
}
kfree(fat_buf);
current = next;
}
}
kfree(cluster_buf);
return true;
}
@ -1431,7 +1389,6 @@ static int realfs_list_directory_vol(FAT32_Volume *vol, const char *path, FAT32_
for (int e = 0; e < (int)((vol->sectors_per_cluster*512)/32) && count < max_entries; e++) {
if (de[e].filename[0] == 0) { current_cluster = 0xFFFFFFFF; break; }
if (de[e].filename[0] == 0xE5) { has_lfn = false; continue; }
if (de[e].filename[0] == 0x2E) { has_lfn = false; continue; } // Skip . and ..
if (de[e].attributes == ATTR_LFN) {
FAT32_LFNEntry *l = (FAT32_LFNEntry*)&de[e];
if (l->order & 0x40) for(int k=0; k<256; k++) lfn_buffer[k] = 0;
@ -1661,22 +1618,6 @@ static uint32_t vfs_fat_get_size(void *file_handle) {
return ((FAT32_FileHandle*)file_handle)->size;
}
static int vfs_ramfs_statfs(void *fs_private, vfs_statfs_t *stat) {
(void)fs_private;
uint64_t rflags = spinlock_acquire_irqsave(&ramfs_lock);
stat->total_blocks = MAX_CLUSTERS;
uint64_t free_count = 0;
for (int i = 0; i < MAX_CLUSTERS; i++) {
if (fat_table[i] == 0) free_count++;
}
stat->free_blocks = free_count;
stat->block_size = FAT32_CLUSTER_SIZE;
spinlock_release_irqrestore(&ramfs_lock, rflags);
return 0;
}
static struct vfs_fs_ops ramfs_ops = {
.open = vfs_ramfs_open,
.close = vfs_ramfs_close,
@ -1692,8 +1633,7 @@ static struct vfs_fs_ops ramfs_ops = {
.is_dir = vfs_ramfs_is_dir,
.get_info = vfs_ramfs_get_info,
.get_position = vfs_fat_get_position,
.get_size = vfs_fat_get_size,
.statfs = vfs_ramfs_statfs
.get_size = vfs_fat_get_size
};
struct vfs_fs_ops* fat32_get_ramfs_ops(void) {
@ -1767,9 +1707,6 @@ static int vfs_realfs_write(void *fs_private, void *file_handle, const void *buf
if (ret < to_write) break;
}
if (total_written > 0) {
fat32_sync_if_root(vol);
}
kfree(cluster_buf);
return total_written;
}
@ -1804,7 +1741,6 @@ static bool vfs_realfs_mkdir(void *fs_private, const char *rel_path) {
uint64_t rflags = spinlock_acquire_irqsave(&vol->lock);
bool ret = realfs_mkdir_vol((FAT32_Volume*)fs_private, rel_path);
spinlock_release_irqrestore(&vol->lock, rflags);
if (ret) fat32_sync_if_root(vol);
return ret;
}
@ -1818,7 +1754,6 @@ static bool vfs_realfs_unlink(void *fs_private, const char *rel_path) {
uint64_t rflags = spinlock_acquire_irqsave(&vol->lock);
bool ret = realfs_delete_from_vol((FAT32_Volume*)fs_private, rel_path);
spinlock_release_irqrestore(&vol->lock, rflags);
if (ret) fat32_sync_if_root(vol);
return ret;
}
@ -1871,49 +1806,10 @@ static int vfs_realfs_get_info(void *fs_private, const char *rel_path, vfs_diren
spinlock_release_irqrestore(&vol->lock, rflags);
return 0;
}
spinlock_release_irqrestore(&vol->lock, rflags);
spinlock_release_irqrestore(&ramfs_lock, rflags);
return -1;
}
static int vfs_realfs_statfs(void *fs_private, vfs_statfs_t *stat) {
FAT32_Volume *vol = (FAT32_Volume*)fs_private;
uint64_t rflags = spinlock_acquire_irqsave(&vol->lock);
stat->total_blocks = vol->total_sectors / vol->sectors_per_cluster;
stat->block_size = vol->sectors_per_cluster * 512;
// Instead of scanning the entire FAT which can be slow,
// we estimate or count a subset, but let's do a fast count.
uint64_t free_count = 0;
uint32_t fat_entries = (vol->fat_size * 512) / 4;
uint32_t current = 2;
uint8_t *fat_buf = (uint8_t*)kmalloc(512);
if (fat_buf) {
uint32_t cached_sector = 0xFFFFFFFF;
while (current < fat_entries) {
uint32_t sector = vol->fat_begin_lba + (current * 4) / 512;
uint32_t offset = (current * 4) % 512;
if (sector != cached_sector) {
if (vol->disk->read_sector(vol->disk, sector, fat_buf) != 0) break;
cached_sector = sector;
}
uint32_t val = *(uint32_t*)&fat_buf[offset];
if ((val & 0x0FFFFFFF) == 0) free_count++;
current++;
}
kfree(fat_buf);
}
stat->free_blocks = free_count;
spinlock_release_irqrestore(&vol->lock, rflags);
return 0;
}
static struct vfs_fs_ops realfs_ops = {
.open = vfs_realfs_open,
.close = vfs_realfs_close,
@ -1929,8 +1825,7 @@ static struct vfs_fs_ops realfs_ops = {
.is_dir = vfs_realfs_is_dir,
.get_info = vfs_realfs_get_info,
.get_position = vfs_fat_get_position,
.get_size = vfs_fat_get_size,
.statfs = vfs_realfs_statfs
.get_size = vfs_fat_get_size
};
struct vfs_fs_ops* fat32_get_realfs_ops(void) {
@ -2020,9 +1915,8 @@ FAT32_FileHandle* fat32_open_nolock(const char *path, const char *mode) {
fat32_normalize_path(p, normalized);
handle = ramfs_open(normalized, mode);
kfree(normalized);
} else if (drive == 0) {
handle = realfs_open_from_vol(root_volume, p, mode);
} else if (drive != 0) {
// Find volume by drive letter (B=0, C=1...)
Disk *d = disk_get_by_letter(drive);
if (d) {
for (int i = 0; i < real_volume_count; i++) {
@ -2068,7 +1962,6 @@ void fat32_close_nolock(FAT32_FileHandle *handle) {
kfree(buf);
}
}
fat32_sync_if_root(vol);
}
handle->valid = false;
}
@ -2112,10 +2005,6 @@ int fat32_write(FAT32_FileHandle *handle, const void *buffer, int size) {
ret = ramfs_write(handle, buffer, size);
} else {
ret = realfs_write_file(handle, buffer, size, NULL);
if (ret > 0) {
FAT32_Volume *vol = (FAT32_Volume*)handle->volume;
fat32_sync_if_root(vol);
}
}
spinlock_release_irqrestore(&ramfs_lock, rflags);
@ -2181,12 +2070,8 @@ static bool realfs_mkdir_vol(FAT32_Volume *vol, const char *path) {
// Open parent directory
FAT32_FileHandle *parent_fh = realfs_open_from_vol(vol, parent_path, "r");
kfree(parent_path);
if (!parent_fh) {
serial_write("[FAT32] mkdir ERROR: parent not found\n");
return false;
}
if (!parent_fh) return false;
uint32_t parent_cluster = parent_fh->start_cluster;
extern void fat32_close_nolock(FAT32_FileHandle *handle);
fat32_close_nolock(parent_fh);
@ -2232,14 +2117,73 @@ static bool realfs_mkdir_vol(FAT32_Volume *vol, const char *path) {
}
kfree(cluster_buf);
// Find free entry in parent directory
uint32_t search_cluster = parent_cluster;
uint8_t *sect_buf = (uint8_t*)kmalloc(512);
bool found_free = false;
uint32_t free_sector = 0;
uint32_t free_offset = 0;
if (!realfs_create_entry(vol, parent_cluster, dirname, ATTR_DIRECTORY, new_cluster, 0, &free_sector, &free_offset)) {
uint8_t *dir_cluster_buf = (uint8_t*)kmalloc(cluster_size);
if (!dir_cluster_buf) {
kfree(sect_buf);
return false;
}
return true;
while (search_cluster < 0x0FFFFFF8 && !found_free) {
if (realfs_read_cluster(vol, search_cluster, dir_cluster_buf) != 0) break;
FAT32_DirEntry *entries = (FAT32_DirEntry*)dir_cluster_buf;
int count = cluster_size / 32;
for (int e = 0; e < count; e++) {
if (entries[e].filename[0] == 0 || entries[e].filename[0] == 0xE5) {
uint32_t lba = vol->cluster_begin_lba + (search_cluster - 2) * vol->sectors_per_cluster;
free_sector = lba + (e * 32) / 512;
free_offset = (e * 32) % 512;
found_free = true;
break;
}
}
if (!found_free) {
uint32_t next = realfs_next_cluster(vol, search_cluster);
if (next >= 0x0FFFFFF8) {
// Need to expand directory?
uint32_t expanded = realfs_allocate_cluster(vol);
if (expanded == 0) break;
// Link, preserve high bits
uint32_t fat_sector = vol->fat_begin_lba + (search_cluster * 4) / 512;
uint32_t fat_offset = (search_cluster * 4) % 512;
if (vol->disk->read_sector(vol->disk, fat_sector, sect_buf) == 0) {
uint32_t old_val = *(uint32_t*)&sect_buf[fat_offset];
*(uint32_t*)&sect_buf[fat_offset] = (old_val & 0xF0000000) | (expanded & 0x0FFFFFFF);
vol->disk->write_sector(vol->disk, fat_sector, sect_buf);
if (vol->cached_fat_sector == fat_sector) vol->cached_fat_sector = 0xFFFFFFFF;
}
// Zero out new cluster
for(uint32_t k=0; k<cluster_size; k++) dir_cluster_buf[k] = 0;
realfs_write_cluster(vol, expanded, dir_cluster_buf);
next = expanded;
}
search_cluster = next;
}
}
kfree(dir_cluster_buf);
if (found_free) {
vol->disk->read_sector(vol->disk, free_sector, sect_buf);
FAT32_DirEntry *d = (FAT32_DirEntry*)(sect_buf + free_offset);
char dos_name[11];
to_dos_filename(dirname, dos_name);
for (int k = 0; k < 8; k++) d->filename[k] = dos_name[k];
for (int k = 0; k < 3; k++) d->extension[k] = dos_name[8+k];
d->attributes = ATTR_DIRECTORY;
d->start_cluster_high = (new_cluster >> 16);
d->start_cluster_low = (new_cluster & 0xFFFF);
d->file_size = 0;
vol->disk->write_sector(vol->disk, free_sector, sect_buf);
}
kfree(sect_buf);
return found_free;
}
bool fat32_mkdir(const char *path) {
@ -2249,12 +2193,7 @@ bool fat32_mkdir(const char *path) {
// SMP: Use FAT32 spinlock
uint64_t rflags = spinlock_acquire_irqsave(&ramfs_lock);
if (drive == 0) {
bool res = realfs_mkdir_vol(root_volume, p);
wm_notify_fs_change();
spinlock_release_irqrestore(&ramfs_lock, rflags);
return res;
} else if (drive != 'A') {
if (drive != 'A') {
Disk *d = disk_get_by_letter(drive);
if (d) {
for (int i = 0; i < real_volume_count; i++) {
@ -2359,20 +2298,8 @@ bool fat32_delete(const char *path) {
kfree(normalized);
if (result) wm_notify_fs_change();
}
} else if (drive == 0) {
result = realfs_delete_from_vol(root_volume, p);
if (result) wm_notify_fs_change();
} else {
Disk *d = disk_get_by_letter(drive);
if (d) {
for (int i = 0; i < real_volume_count; i++) {
if (real_volumes[i]->disk == d) {
result = realfs_delete_from_vol(real_volumes[i], p);
if (result) wm_notify_fs_change();
break;
}
}
}
// Obsolete RealFS realfs_delete
}
spinlock_release_irqrestore(&ramfs_lock, rflags);
@ -2464,12 +2391,6 @@ bool fat32_exists(const char *path) {
exists = (ramfs_find_file(normalized) != NULL);
kfree(normalized);
}
} else if (drive == 0) {
FAT32_FileHandle *fh = realfs_open_from_vol(root_volume, p, "r");
if (fh) {
exists = true;
fat32_close_nolock(fh);
}
} else {
// RealFS check
Disk *d = disk_get_by_letter(drive);
@ -2551,12 +2472,6 @@ bool fat32_is_directory_nolock(const char *path) {
is_dir = (entry && (entry->attributes & ATTR_DIRECTORY));
kfree(normalized);
}
} else if (drive == 0) {
FAT32_FileHandle *fh = realfs_open_from_vol(root_volume, p, "r");
if (fh) {
is_dir = fh->is_directory;
fat32_close_nolock(fh);
}
} else {
Disk *d = disk_get_by_letter(drive);
if (d) {

View file

@ -163,9 +163,6 @@ char fat32_get_current_drive(void);
void fat32_normalize_path(const char *path, char *normalized);
bool fs_starts_with(const char *str, const char *prefix);
// Root volume sync helpers
void fat32_set_root_volume(void *fs_private);
// Desktop Limit
void fat32_set_desktop_limit(int limit);

View file

@ -1,297 +0,0 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "mkfs_fat32.h"
#include "../dev/disk.h"
#include <stddef.h>
#include "memory_manager.h"
extern void serial_write(const char *str);
extern void serial_write_num(uint64_t num);
// Internal helpers
static void mf_memset(void *dst, int val, int len) {
unsigned char *p = (unsigned char *)dst;
while (len-- > 0) *p++ = (unsigned char)val;
}
static void mf_memcpy(void *dst, const void *src, int len) {
unsigned char *d = (unsigned char *)dst;
const unsigned char *s = (const unsigned char *)src;
while (len-- > 0) *d++ = *s++;
}
static void mf_strncpy(char *dst, const char *src, int n) {
int i = 0;
while (i < n && src[i]) { dst[i] = src[i]; i++; }
while (i < n) { dst[i++] = ' '; } /* FAT labels are space-padded */
}
static void mf_set_disk_label(Disk *disk, const char *label) {
int end = 11;
while (end > 0 && label[end - 1] == ' ') end--;
for (int i = 0; i < end && i < 31; i++) disk->label[i] = label[i];
disk->label[end < 31 ? end : 31] = 0;
}
// On-disk BPB structures
typedef struct __attribute__((packed)) {
/* DOS 2.0 BPB */
uint8_t jump_boot[3]; /* EB 58 90 */
char oem_name[8]; /* "MSDOS5.0" */
uint16_t bytes_per_sector; /* 512 */
uint8_t sectors_per_cluster;
uint16_t reserved_sector_count;
uint8_t num_fats; /* 2 */
uint16_t root_entry_count; /* 0 for FAT32 */
uint16_t total_sectors_16; /* 0 for FAT32 */
uint8_t media; /* 0xF8 for fixed disk */
uint16_t fat_size_16; /* 0 for FAT32 */
uint16_t sectors_per_track;
uint16_t num_heads;
uint32_t hidden_sectors;
uint32_t total_sectors_32;
/* FAT32 extended BPB */
uint32_t fat_size_32;
uint16_t ext_flags;
uint16_t fs_version; /* 0x0000 */
uint32_t root_cluster; /* 2 */
uint16_t fs_info; /* sector 1 */
uint16_t backup_boot_sector; /* sector 6 */
uint8_t reserved[12];
uint8_t drive_number; /* 0x80 for fixed disk */
uint8_t reserved1;
uint8_t boot_sig; /* 0x29 */
uint32_t volume_id;
char volume_label[11]; /* space-padded */
char fs_type[8]; /* "FAT32 " */
uint8_t boot_code[420];
uint16_t boot_signature; /* 0xAA55 */
} FAT32_BPB;
typedef struct __attribute__((packed)) {
uint32_t lead_sig; /* 0x41615252 */
uint8_t reserved1[480];
uint32_t struct_sig; /* 0x61417272 */
uint32_t free_count; /* 0xFFFFFFFF = unknown */
uint32_t next_free; /* 0xFFFFFFFF = unknown */
uint8_t reserved2[12];
uint32_t trail_sig; /* 0xAA550000 */
} FAT32_FSInfo;
// Public API
int mkfs_fat32_format(Disk *disk, uint32_t sector_count, const char *label) {
if (sector_count < MIN_FAT32_SECTORS) {
serial_write("[MKFS] Error: partition too small for FAT32 (< 32 MB)\n");
return -1;
}
if (!disk || !disk->write_sector) {
serial_write("[MKFS] Error: null disk or no write function\n");
return -1;
}
uint8_t spc; /* sectors per cluster */
if (sector_count < 532480) spc = 1; /* < 260 MB -> 512 B clusters */
else if (sector_count < 1064960) spc = 2; /* < 520 MB -> 1 KB */
else if (sector_count < 2097152) spc = 4; /* < 1 GB -> 2 KB */
else if (sector_count < 16777216) spc = 8; /* < 8 GB -> 4 KB */
else if (sector_count < 33554432) spc = 16; /* < 16 GB -> 8 KB */
else if (sector_count < 67108864) spc = 32; /* < 32 GB -> 16 KB */
else spc = 64; /* >= 32 GB -> 32 KB */
const uint32_t reserved_sectors = 32;
const uint8_t num_fats = 2;
const uint32_t root_cluster = 2;
uint32_t data_sectors = sector_count - reserved_sectors;
uint32_t cluster_count = data_sectors / spc;
uint32_t fat_bytes = (cluster_count + 2) * 4;
uint32_t sectors_per_fat = (fat_bytes + 511) / 512;
uint8_t *buf = (uint8_t *)kmalloc(512);
if (!buf) {
serial_write("[MKFS] Error: out of memory\n");
return -1;
}
FAT32_BPB *bpb = (FAT32_BPB *)buf;
mf_memset(bpb, 0, 512);
bpb->jump_boot[0] = 0xEB;
bpb->jump_boot[1] = 0x58;
bpb->jump_boot[2] = 0x90;
mf_memcpy(bpb->oem_name, "MSDOS5.0", 8);
bpb->bytes_per_sector = 512;
bpb->sectors_per_cluster = spc;
bpb->reserved_sector_count = (uint16_t)reserved_sectors;
bpb->num_fats = num_fats;
bpb->root_entry_count = 0;
bpb->total_sectors_16 = 0;
bpb->media = 0xF8;
bpb->fat_size_16 = 0;
bpb->sectors_per_track = 63;
bpb->num_heads = 255;
bpb->hidden_sectors = disk->partition_lba_offset;
bpb->total_sectors_32 = sector_count;
bpb->fat_size_32 = sectors_per_fat;
bpb->ext_flags = 0;
bpb->fs_version = 0;
bpb->root_cluster = root_cluster;
bpb->fs_info = 1;
bpb->backup_boot_sector = 6;
bpb->drive_number = 0x80;
bpb->boot_sig = 0x29;
bpb->volume_id = 0x12345678; /* arbitrary non-zero volume ID */
/* Volume label */
const char *vol_label = (label && label[0]) ? label : "NO NAME ";
char upper_label[11];
for (int i = 0; i < 11; i++) {
if (vol_label[i] == 0) {
for (int j = i; j < 11; j++) upper_label[j] = ' ';
break;
}
char c = vol_label[i];
if (c >= 'a' && c <= 'z') c -= 32;
upper_label[i] = c;
}
mf_memcpy(bpb->volume_label, upper_label, 11);
mf_memcpy(bpb->fs_type, "FAT32 ", 8);
/* Boot sector signature */
bpb->boot_signature = 0xAA55;
/* Write sector 0 (BPB) */
if (disk->write_sector(disk, 0, buf) != 0) {
serial_write("[MKFS] Error: failed to write BPB (sector 0)\n");
kfree(buf);
return -1;
}
FAT32_FSInfo *fsinfo = (FAT32_FSInfo *)buf;
mf_memset(fsinfo, 0, 512);
fsinfo->lead_sig = 0x41615252;
fsinfo->struct_sig = 0x61417272;
fsinfo->free_count = 0xFFFFFFFF;
fsinfo->next_free = 0xFFFFFFFF;
fsinfo->trail_sig = 0xAA550000;
if (disk->write_sector(disk, 1, buf) != 0) {
serial_write("[MKFS] Error: failed to write FSInfo (sector 1)\n");
kfree(buf);
return -1;
}
FAT32_BPB *bpb2 = (FAT32_BPB *)buf;
mf_memset(bpb2, 0, 512);
mf_memcpy(bpb2->jump_boot, "\xEB\x58\x90", 3);
mf_memcpy(bpb2->oem_name, "MSDOS5.0", 8);
bpb2->bytes_per_sector = 512;
bpb2->sectors_per_cluster = spc;
bpb2->reserved_sector_count = (uint16_t)reserved_sectors;
bpb2->num_fats = num_fats;
bpb2->root_entry_count = 0;
bpb2->total_sectors_16 = 0;
bpb2->media = 0xF8;
bpb2->fat_size_16 = 0;
bpb2->sectors_per_track = 63;
bpb2->num_heads = 255;
bpb2->hidden_sectors = disk->partition_lba_offset;
bpb2->total_sectors_32 = sector_count;
bpb2->fat_size_32 = sectors_per_fat;
bpb2->ext_flags = 0;
bpb2->fs_version = 0;
bpb2->root_cluster = root_cluster;
bpb2->fs_info = 1;
bpb2->backup_boot_sector = 6;
bpb2->drive_number = 0x80;
bpb2->boot_sig = 0x29;
bpb2->volume_id = 0x12345678;
mf_memcpy(bpb2->volume_label, upper_label, 11);
mf_memcpy(bpb2->fs_type, "FAT32 ", 8);
bpb2->boot_signature = 0xAA55;
if (disk->write_sector(disk, 6, buf) != 0) {
serial_write("[MKFS] Error: failed to write backup BPB (sector 6)\n");
kfree(buf);
return -1;
}
FAT32_FSInfo *fsinfo2 = (FAT32_FSInfo *)buf;
mf_memset(fsinfo2, 0, 512);
fsinfo2->lead_sig = 0x41615252;
fsinfo2->struct_sig = 0x61417272;
fsinfo2->free_count = 0xFFFFFFFF;
fsinfo2->next_free = 0xFFFFFFFF;
fsinfo2->trail_sig = 0xAA550000;
if (disk->write_sector(disk, 7, buf) != 0) {
serial_write("[MKFS] Error: failed to write backup FSInfo (sector 7)\n");
kfree(buf);
return -1;
}
/* Zero both FATs */
mf_memset(buf, 0, 512);
for (uint32_t f = 0; f < num_fats; f++) {
uint32_t fat_start = reserved_sectors + (f * sectors_per_fat);
for (uint32_t s = 0; s < sectors_per_fat; s++) {
if (disk->write_sector(disk, fat_start + s, buf) != 0) {
serial_write("[MKFS] Error: failed to zero FAT\n");
kfree(buf);
return -1;
}
}
}
/* Write markers to both FATs */
mf_memset(buf, 0, 512);
uint32_t *fat_buf = (uint32_t *)buf;
fat_buf[0] = 0x0FFFFFF8; // Media type
fat_buf[1] = 0x0FFFFFFF; // Reserved
fat_buf[2] = 0x0FFFFFFF; // Root directory (Cluster 2)
for (uint32_t f = 0; f < num_fats; f++) {
uint32_t fat_start = reserved_sectors + (f * sectors_per_fat);
if (disk->write_sector(disk, fat_start, buf) != 0) {
serial_write("[MKFS] Error: failed to write FAT markers\n");
kfree(buf);
return -1;
}
}
/* Zero root cluster */
mf_memset(buf, 0, 512);
uint32_t root_start = reserved_sectors + num_fats * sectors_per_fat;
for (uint32_t s = 0; s < (uint32_t)spc; s++) {
if (disk->write_sector(disk, root_start + s, buf) != 0) {
serial_write("[MKFS] Error: failed to zero root cluster\n");
kfree(buf);
return -1;
}
}
kfree(buf);
disk->is_fat32 = true;
mf_set_disk_label(disk, upper_label);
serial_write("[MKFS] FAT32 formatted: ");
serial_write(disk->devname);
serial_write(" label=");
char lb[12];
mf_memcpy(lb, upper_label, 11);
lb[11] = 0;
for (int i = 10; i >= 0 && lb[i] == ' '; i--) lb[i] = 0;
serial_write(lb);
serial_write(" spc=");
serial_write_num(spc);
serial_write(" fat_sectors=");
serial_write_num(sectors_per_fat);
serial_write("\n");
return 0;
}

View file

@ -1,12 +0,0 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef MKFS_FAT32_H
#define MKFS_FAT32_H
#include <stdint.h>
#include "../dev/disk.h"
#define MIN_FAT32_SECTORS 65536
int mkfs_fat32_format(Disk *disk, uint32_t sector_count, const char *label);
#endif

View file

@ -17,7 +17,7 @@ void* procfs_open(void *fs_private, const char *path, const char *mode) {
if (path[0] == '/') path++;
procfs_handle_t *h = (procfs_handle_t*)kmalloc(sizeof(procfs_handle_t));
memset(h, 0, sizeof(procfs_handle_t));
k_memset(h, 0, sizeof(procfs_handle_t));
h->offset = 0;
if (path[0] == '\0') {
@ -33,10 +33,10 @@ void* procfs_open(void *fs_private, const char *path, const char *mode) {
i++;
}
pid_str[i] = 0;
h->pid = atoi(pid_str);
h->pid = k_atoi(pid_str);
if (path[i] == '/') {
strcpy(h->type, path + i + 1);
k_strcpy(h->type, path + i + 1);
} else {
h->type[0] = 0;
}
@ -44,7 +44,7 @@ void* procfs_open(void *fs_private, const char *path, const char *mode) {
}
h->pid = 0xFFFFFFFF;
strcpy(h->type, path);
k_strcpy(h->type, path);
return h;
}
@ -61,33 +61,33 @@ int procfs_read(void *fs_private, void *handle, void *buf, int size) {
out[0] = 0;
if (h->pid == 0xFFFFFFFF) {
if (strcmp(h->type, "version") == 0) {
if (k_strcmp(h->type, "version") == 0) {
extern void get_os_info(os_info_t *info);
os_info_t info;
get_os_info(&info);
strcpy(out, info.os_name);
strcpy(out + strlen(out), " [");
strcpy(out + strlen(out), info.os_codename);
strcpy(out + strlen(out), "] Version ");
strcpy(out + strlen(out), info.os_version);
strcpy(out + strlen(out), "\nKernel: ");
strcpy(out + strlen(out), info.kernel_name);
strcpy(out + strlen(out), " ");
strcpy(out + strlen(out), info.kernel_version);
strcpy(out + strlen(out), "\nBuild: ");
strcpy(out + strlen(out), info.build_date);
strcpy(out + strlen(out), " ");
strcpy(out + strlen(out), info.build_time);
strcpy(out + strlen(out), "\n");
} else if (strcmp(h->type, "uptime") == 0) {
k_strcpy(out, info.os_name);
k_strcpy(out + k_strlen(out), " [");
k_strcpy(out + k_strlen(out), info.os_codename);
k_strcpy(out + k_strlen(out), "] Version ");
k_strcpy(out + k_strlen(out), info.os_version);
k_strcpy(out + k_strlen(out), "\nKernel: ");
k_strcpy(out + k_strlen(out), info.kernel_name);
k_strcpy(out + k_strlen(out), " ");
k_strcpy(out + k_strlen(out), info.kernel_version);
k_strcpy(out + k_strlen(out), "\nBuild: ");
k_strcpy(out + k_strlen(out), info.build_date);
k_strcpy(out + k_strlen(out), " ");
k_strcpy(out + k_strlen(out), info.build_time);
k_strcpy(out + k_strlen(out), "\n");
} else if (k_strcmp(h->type, "uptime") == 0) {
extern uint32_t wm_get_ticks(void);
uint32_t ticks = wm_get_ticks();
itoa(ticks / 60, out);
strcpy(out + strlen(out), " seconds\nRaw_Ticks:");
char t_s[16]; itoa(ticks, t_s);
strcpy(out + strlen(out), t_s);
strcpy(out + strlen(out), "\n");
} else if (strcmp(h->type, "cpuinfo") == 0) {
k_itoa(ticks / 60, out);
k_strcpy(out + k_strlen(out), " seconds\nRaw_Ticks:");
char t_s[16]; k_itoa(ticks, t_s);
k_strcpy(out + k_strlen(out), t_s);
k_strcpy(out + k_strlen(out), "\n");
} else if (k_strcmp(h->type, "cpuinfo") == 0) {
extern uint32_t smp_cpu_count(void);
extern void platform_get_cpu_model(char *model);
extern void platform_get_cpu_vendor(char *vendor);
@ -111,35 +111,35 @@ int procfs_read(void *fs_private, void *handle, void *buf, int size) {
for (uint32_t i = 0; i < cpu_count; i++) {
char buf[32];
strcpy(out + strlen(out), "processor\t: ");
itoa(i, buf);
strcpy(out + strlen(out), buf);
strcpy(out + strlen(out), "\n");
k_strcpy(out + k_strlen(out), "processor\t: ");
k_itoa(i, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
strcpy(out + strlen(out), "vendor_id\t: ");
strcpy(out + strlen(out), vendor);
strcpy(out + strlen(out), "\n");
k_strcpy(out + k_strlen(out), "vendor_id\t: ");
k_strcpy(out + k_strlen(out), vendor);
k_strcpy(out + k_strlen(out), "\n");
strcpy(out + strlen(out), "cpu family\t: ");
itoa(info.family, buf);
strcpy(out + strlen(out), buf);
strcpy(out + strlen(out), "\n");
k_strcpy(out + k_strlen(out), "cpu family\t: ");
k_itoa(info.family, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
strcpy(out + strlen(out), "model\t\t: ");
itoa(info.model, buf);
strcpy(out + strlen(out), buf);
strcpy(out + strlen(out), "\n");
k_strcpy(out + k_strlen(out), "model\t\t: ");
k_itoa(info.model, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
strcpy(out + strlen(out), "model name\t: ");
strcpy(out + strlen(out), model);
strcpy(out + strlen(out), "\n");
k_strcpy(out + k_strlen(out), "model name\t: ");
k_strcpy(out + k_strlen(out), model);
k_strcpy(out + k_strlen(out), "\n");
strcpy(out + strlen(out), "stepping\t: ");
itoa(info.stepping, buf);
strcpy(out + strlen(out), buf);
strcpy(out + strlen(out), "\n");
k_strcpy(out + k_strlen(out), "stepping\t: ");
k_itoa(info.stepping, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
strcpy(out + strlen(out), "microcode\t: 0x");
k_strcpy(out + k_strlen(out), "microcode\t: 0x");
char hex[16];
int temp = info.microcode;
int hex_pos = 0;
@ -148,217 +148,188 @@ int procfs_read(void *fs_private, void *handle, void *buf, int size) {
hex[hex_pos++] = digit < 10 ? '0' + digit : 'a' + (digit - 10);
}
hex[hex_pos] = '\0';
strcpy(out + strlen(out), hex);
strcpy(out + strlen(out), "\n");
k_strcpy(out + k_strlen(out), hex);
k_strcpy(out + k_strlen(out), "\n");
strcpy(out + strlen(out), "cache size\t: ");
itoa(info.cache_size, buf);
strcpy(out + strlen(out), buf);
strcpy(out + strlen(out), " KB\n");
k_strcpy(out + k_strlen(out), "cache size\t: ");
k_itoa(info.cache_size, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), " KB\n");
strcpy(out + strlen(out), "physical id\t: 0\n");
strcpy(out + strlen(out), "siblings\t: ");
itoa(cpu_count, buf);
strcpy(out + strlen(out), buf);
strcpy(out + strlen(out), "\n");
k_strcpy(out + k_strlen(out), "physical id\t: 0\n");
k_strcpy(out + k_strlen(out), "siblings\t: ");
k_itoa(cpu_count, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
strcpy(out + strlen(out), "core id\t\t: ");
itoa(i, buf);
strcpy(out + strlen(out), buf);
strcpy(out + strlen(out), "\n");
k_strcpy(out + k_strlen(out), "core id\t\t: ");
k_itoa(i, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
strcpy(out + strlen(out), "cpu cores\t: ");
itoa(cpu_count, buf);
strcpy(out + strlen(out), buf);
strcpy(out + strlen(out), "\n");
k_strcpy(out + k_strlen(out), "cpu cores\t: ");
k_itoa(cpu_count, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
strcpy(out + strlen(out), "apicid\t\t: ");
itoa(i, buf);
strcpy(out + strlen(out), buf);
strcpy(out + strlen(out), "\n");
k_strcpy(out + k_strlen(out), "apicid\t\t: ");
k_itoa(i, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
strcpy(out + strlen(out), "initial apicid\t: ");
itoa(i, buf);
strcpy(out + strlen(out), buf);
strcpy(out + strlen(out), "\n");
k_strcpy(out + k_strlen(out), "initial apicid\t: ");
k_itoa(i, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
strcpy(out + strlen(out), "fpu\t\t: yes\n");
strcpy(out + strlen(out), "fpu_exception\t: yes\n");
k_strcpy(out + k_strlen(out), "fpu\t\t: yes\n");
k_strcpy(out + k_strlen(out), "fpu_exception\t: yes\n");
strcpy(out + strlen(out), "cpuid level\t: 13\n");
k_strcpy(out + k_strlen(out), "cpuid level\t: 13\n");
strcpy(out + strlen(out), "wp\t\t: yes\n");
k_strcpy(out + k_strlen(out), "wp\t\t: yes\n");
strcpy(out + strlen(out), "flags\t\t: ");
strcpy(out + strlen(out), flags);
strcpy(out + strlen(out), "\n");
k_strcpy(out + k_strlen(out), "flags\t\t: ");
k_strcpy(out + k_strlen(out), flags);
k_strcpy(out + k_strlen(out), "\n");
strcpy(out + strlen(out), "bugs\t\t: \n");
strcpy(out + strlen(out), "bogomips\t: 4800.00\n");
k_strcpy(out + k_strlen(out), "bugs\t\t: \n");
k_strcpy(out + k_strlen(out), "bogomips\t: 4800.00\n");
if (i < cpu_count - 1) {
strcpy(out + strlen(out), "\n");
k_strcpy(out + k_strlen(out), "\n");
}
}
} else if (strcmp(h->type, "datetime") == 0) {
extern void rtc_get_datetime(int *year, int *month, int *day, int *hour, int *minute, int *second);
int y, m, d, h_val, min, s;
rtc_get_datetime(&y, &m, &d, &h_val, &min, &s);
char buf[16];
itoa(y, buf);
strcpy(out, buf);
strcpy(out + strlen(out), "-");
if (m < 10) strcpy(out + strlen(out), "0");
itoa(m, buf);
strcpy(out + strlen(out), buf);
strcpy(out + strlen(out), "-");
if (d < 10) strcpy(out + strlen(out), "0");
itoa(d, buf);
strcpy(out + strlen(out), buf);
strcpy(out + strlen(out), " ");
if (h_val < 10) strcpy(out + strlen(out), "0");
itoa(h_val, buf);
strcpy(out + strlen(out), buf);
strcpy(out + strlen(out), ":");
if (min < 10) strcpy(out + strlen(out), "0");
itoa(min, buf);
strcpy(out + strlen(out), buf);
strcpy(out + strlen(out), ":");
if (s < 10) strcpy(out + strlen(out), "0");
itoa(s, buf);
strcpy(out + strlen(out), buf);
strcpy(out + strlen(out), "\n");
} else if (strcmp(h->type, "meminfo") == 0) {
} else if (k_strcmp(h->type, "meminfo") == 0) {
extern MemStats memory_get_stats(void);
MemStats stats = memory_get_stats();
char m_s[32];
strcpy(out, "MemTotal:\t");
itoa(stats.total_memory / 1024, m_s);
strcpy(out + strlen(out), m_s);
strcpy(out + strlen(out), " kB\n");
k_strcpy(out, "MemTotal:\t");
k_itoa(stats.total_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\n");
strcpy(out + strlen(out), "MemFree:\t");
itoa(stats.available_memory / 1024, m_s);
strcpy(out + strlen(out), m_s);
strcpy(out + strlen(out), " kB\n");
k_strcpy(out + k_strlen(out), "MemFree:\t");
k_itoa(stats.available_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\n");
strcpy(out + strlen(out), "MemAvailable:\t");
itoa(stats.available_memory / 1024, m_s);
strcpy(out + strlen(out), m_s);
strcpy(out + strlen(out), " kB\n");
k_strcpy(out + k_strlen(out), "MemAvailable:\t");
k_itoa(stats.available_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\n");
strcpy(out + strlen(out), "Buffers:\t0 kB\n");
strcpy(out + strlen(out), "Cached:\t\t0 kB\n");
k_strcpy(out + k_strlen(out), "Buffers:\t0 kB\n");
k_strcpy(out + k_strlen(out), "Cached:\t\t0 kB\n");
strcpy(out + strlen(out), "MemUsed:\t");
itoa(stats.used_memory / 1024, m_s);
strcpy(out + strlen(out), m_s);
strcpy(out + strlen(out), " kB\n");
k_strcpy(out + k_strlen(out), "MemUsed:\t");
k_itoa(stats.used_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\n");
strcpy(out + strlen(out), "MemPeak:\t");
itoa(stats.peak_memory_used / 1024, m_s);
strcpy(out + strlen(out), m_s);
strcpy(out + strlen(out), " kB\n");
k_strcpy(out + k_strlen(out), "MemPeak:\t");
k_itoa(stats.peak_memory_used / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\n");
strcpy(out + strlen(out), "SwapTotal:\t0 kB\n");
strcpy(out + strlen(out), "SwapFree:\t0 kB\n");
k_strcpy(out + k_strlen(out), "SwapTotal:\t0 kB\n");
k_strcpy(out + k_strlen(out), "SwapFree:\t0 kB\n");
strcpy(out + strlen(out), "Dirty:\t\t0 kB\n");
strcpy(out + strlen(out), "Writeback:\t0 kB\n");
strcpy(out + strlen(out), "AnonPages:\t");
itoa(stats.used_memory / 1024, m_s);
strcpy(out + strlen(out), m_s);
strcpy(out + strlen(out), " kB\n");
k_strcpy(out + k_strlen(out), "Dirty:\t\t0 kB\n");
k_strcpy(out + k_strlen(out), "Writeback:\t0 kB\n");
k_strcpy(out + k_strlen(out), "AnonPages:\t");
k_itoa(stats.used_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\n");
strcpy(out + strlen(out), "Mapped:\t\t0 kB\n");
strcpy(out + strlen(out), "Shmem:\t\t0 kB\n");
k_strcpy(out + k_strlen(out), "Mapped:\t\t0 kB\n");
k_strcpy(out + k_strlen(out), "Shmem:\t\t0 kB\n");
strcpy(out + strlen(out), "Blocks:\t\t");
itoa(stats.allocated_blocks, m_s);
strcpy(out + strlen(out), m_s);
strcpy(out + strlen(out), "\n");
k_strcpy(out + k_strlen(out), "Blocks:\t\t");
k_itoa(stats.allocated_blocks, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), "\n");
strcpy(out + strlen(out), "FreeBlocks:\t");
itoa(stats.free_blocks, m_s);
strcpy(out + strlen(out), m_s);
strcpy(out + strlen(out), "\n");
k_strcpy(out + k_strlen(out), "FreeBlocks:\t");
k_itoa(stats.free_blocks, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), "\n");
strcpy(out + strlen(out), "Fragmentation:\t");
itoa(stats.fragmentation_percent, m_s);
strcpy(out + strlen(out), m_s);
strcpy(out + strlen(out), "%\n");
} else if (strcmp(h->type, "devices") == 0) {
k_strcpy(out + k_strlen(out), "Fragmentation:\t");
k_itoa(stats.fragmentation_percent, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), "%\n");
} else if (k_strcmp(h->type, "devices") == 0) {
extern int disk_get_count(void);
extern Disk* disk_get_by_index(int index);
int dcount = disk_get_count();
out[0] = '\0';
strcpy(out, "Character devices:\n");
strcpy(out + strlen(out), " 1 mem\n");
strcpy(out + strlen(out), " 4 tty\n");
strcpy(out + strlen(out), " 5 cua\n");
strcpy(out + strlen(out), " 7 vcs\n");
strcpy(out + strlen(out), " 8 stdin\n");
strcpy(out + strlen(out), " 13 input\n");
strcpy(out + strlen(out), " 14 sound\n");
strcpy(out + strlen(out), " 29 fb\n");
strcpy(out + strlen(out), "189 usb\n\n");
k_strcpy(out, "Character devices:\n");
k_strcpy(out + k_strlen(out), " 1 mem\n");
k_strcpy(out + k_strlen(out), " 4 tty\n");
k_strcpy(out + k_strlen(out), " 5 cua\n");
k_strcpy(out + k_strlen(out), " 7 vcs\n");
k_strcpy(out + k_strlen(out), " 8 stdin\n");
k_strcpy(out + k_strlen(out), " 13 input\n");
k_strcpy(out + k_strlen(out), " 14 sound\n");
k_strcpy(out + k_strlen(out), " 29 fb\n");
k_strcpy(out + k_strlen(out), "189 usb\n\n");
strcpy(out + strlen(out), "Block devices:\n");
k_strcpy(out + k_strlen(out), "Block devices:\n");
for (int i = 0; i < dcount; i++) {
Disk *d = disk_get_by_index(i);
if (d && !d->is_partition) {
strcpy(out + strlen(out), " 8 ");
strcpy(out + strlen(out), d->devname);
strcpy(out + strlen(out), "\n");
k_strcpy(out + k_strlen(out), " 8 ");
k_strcpy(out + k_strlen(out), d->devname);
k_strcpy(out + k_strlen(out), "\n");
}
}
strcpy(out + strlen(out), " 11 sr\n");
strcpy(out + strlen(out), "253 virtblk\n");
k_strcpy(out + k_strlen(out), " 11 sr\n");
k_strcpy(out + k_strlen(out), "253 virtblk\n");
}
}
else {
process_t *proc = process_get_by_pid(h->pid);
if (!proc) { kfree(out); return -1; }
if (strcmp(h->type, "name") == 0 || strcmp(h->type, "cmdline") == 0) {
strcpy(out, proc->name);
strcpy(out + strlen(out), "\n");
} else if (strcmp(h->type, "cwd") == 0) {
strcpy(out, proc->cwd);
strcpy(out + strlen(out), "\n");
} else if (strcmp(h->type, "status") == 0) {
strcpy(out, "Name: ");
strcpy(out + strlen(out), proc->name);
strcpy(out + strlen(out), "\nPID: ");
char pid_s[16]; itoa(proc->pid, pid_s);
strcpy(out + strlen(out), pid_s);
strcpy(out + strlen(out), "\nState: RUNNING\nMemory: ");
if (k_strcmp(h->type, "name") == 0 || k_strcmp(h->type, "cmdline") == 0) {
k_strcpy(out, proc->name);
k_strcpy(out + k_strlen(out), "\n");
} else if (k_strcmp(h->type, "cwd") == 0) {
k_strcpy(out, proc->cwd);
k_strcpy(out + k_strlen(out), "\n");
} else if (k_strcmp(h->type, "status") == 0) {
k_strcpy(out, "Name: ");
k_strcpy(out + k_strlen(out), proc->name);
k_strcpy(out + k_strlen(out), "\nPID: ");
char pid_s[16]; k_itoa(proc->pid, pid_s);
k_strcpy(out + k_strlen(out), pid_s);
k_strcpy(out + k_strlen(out), "\nState: RUNNING\nMemory: ");
uint64_t mem_val = proc->used_memory;
if (h->pid == 0) {
extern MemStats memory_get_stats(void);
mem_val = memory_get_stats().used_memory;
}
char mem_s[32]; itoa(mem_val / 1024, mem_s);
strcpy(out + strlen(out), mem_s);
strcpy(out + strlen(out), " KB\nTicks: ");
char tick_s[32]; itoa(proc->ticks, tick_s);
strcpy(out + strlen(out), tick_s);
strcpy(out + strlen(out), "\nIdle: ");
strcpy(out + strlen(out), proc->is_idle ? "1" : "0");
strcpy(out + strlen(out), "\n");
char mem_s[32]; k_itoa(mem_val / 1024, mem_s);
k_strcpy(out + k_strlen(out), mem_s);
k_strcpy(out + k_strlen(out), " KB\nTicks: ");
char tick_s[32]; k_itoa(proc->ticks, tick_s);
k_strcpy(out + k_strlen(out), tick_s);
k_strcpy(out + k_strlen(out), "\nIdle: ");
k_strcpy(out + k_strlen(out), proc->is_idle ? "1" : "0");
k_strcpy(out + k_strlen(out), "\n");
}
}
int len = strlen(out);
int len = k_strlen(out);
if (h->offset >= len) { kfree(out); return 0; }
int to_copy = len - h->offset;
if (to_copy > size) to_copy = size;
memcpy(buf, out + h->offset, to_copy);
k_memcpy(buf, out + h->offset, to_copy);
h->offset += to_copy;
kfree(out);
return to_copy;
@ -368,13 +339,13 @@ int procfs_write(void *fs_private, void *handle, const void *buf, int size) {
procfs_handle_t *h = (procfs_handle_t*)handle;
if (!h || h->pid == 0xFFFFFFFF) return -1;
if (strcmp(h->type, "signal") == 0) {
if (k_strcmp(h->type, "signal") == 0) {
char cmd[16];
int to_copy = size < 15 ? size : 15;
memcpy(cmd, buf, to_copy);
k_memcpy(cmd, buf, to_copy);
cmd[to_copy] = 0;
if (strcmp(cmd, "9") == 0 || strcmp(cmd, "kill") == 0) {
if (k_strcmp(cmd, "9") == 0 || k_strcmp(cmd, "kill") == 0) {
process_t *proc = process_get_by_pid(h->pid);
if (proc && proc->pid != 0) {
process_terminate(proc);
@ -391,23 +362,21 @@ int procfs_readdir(void *fs_private, const char *path, vfs_dirent_t *entries, in
if (path[0] == '\0') {
int out = 0;
strcpy(entries[out++].name, "version");
k_strcpy(entries[out++].name, "version");
entries[out-1].is_directory = 0;
strcpy(entries[out++].name, "uptime");
k_strcpy(entries[out++].name, "uptime");
entries[out-1].is_directory = 0;
strcpy(entries[out++].name, "cpuinfo");
k_strcpy(entries[out++].name, "cpuinfo");
entries[out-1].is_directory = 0;
strcpy(entries[out++].name, "meminfo");
k_strcpy(entries[out++].name, "meminfo");
entries[out-1].is_directory = 0;
strcpy(entries[out++].name, "datetime");
entries[out-1].is_directory = 0;
strcpy(entries[out++].name, "devices");
k_strcpy(entries[out++].name, "devices");
entries[out-1].is_directory = 0;
extern process_t processes[];
for (int i = 0; i < 16 && out < max; i++) {
if (processes[i].pid != 0xFFFFFFFF) {
itoa(processes[i].pid, entries[out].name);
k_itoa(processes[i].pid, entries[out].name);
entries[out].is_directory = 1;
entries[out].size = 0;
out++;
@ -418,11 +387,11 @@ int procfs_readdir(void *fs_private, const char *path, vfs_dirent_t *entries, in
if (path[0] >= '0' && path[0] <= '9') {
int out = 0;
strcpy(entries[out++].name, "name");
strcpy(entries[out++].name, "status");
strcpy(entries[out++].name, "cmdline");
strcpy(entries[out++].name, "cwd");
strcpy(entries[out++].name, "signal");
k_strcpy(entries[out++].name, "name");
k_strcpy(entries[out++].name, "status");
k_strcpy(entries[out++].name, "cmdline");
k_strcpy(entries[out++].name, "cwd");
k_strcpy(entries[out++].name, "signal");
for(int i=0; i<out; i++) entries[i].is_directory = 0;
return out;
}
@ -442,13 +411,13 @@ bool procfs_exists(void *fs_private, const char *path) {
i++;
}
pid_str[i] = 0;
uint32_t pid = atoi(pid_str);
uint32_t pid = k_atoi(pid_str);
if (process_get_by_pid(pid)) return true;
}
if (strcmp(path, "version") == 0 || strcmp(path, "uptime") == 0) return true;
if (strcmp(path, "cpuinfo") == 0 || strcmp(path, "meminfo") == 0) return true;
if (strcmp(path, "datetime") == 0 || strcmp(path, "devices") == 0) return true;
if (k_strcmp(path, "version") == 0 || k_strcmp(path, "uptime") == 0) return true;
if (k_strcmp(path, "cpuinfo") == 0 || k_strcmp(path, "meminfo") == 0) return true;
if (k_strcmp(path, "devices") == 0) return true;
return false;
}
@ -467,14 +436,6 @@ bool procfs_is_dir(void *fs_private, const char *path) {
return false;
}
static int procfs_statfs(void *fs_private, vfs_statfs_t *stat) {
(void)fs_private;
stat->total_blocks = 0;
stat->free_blocks = 0;
stat->block_size = 512;
return 0;
}
vfs_fs_ops_t procfs_ops = {
.open = procfs_open,
.close = procfs_close,
@ -482,8 +443,7 @@ vfs_fs_ops_t procfs_ops = {
.write = procfs_write,
.readdir = procfs_readdir,
.exists = procfs_exists,
.is_dir = procfs_is_dir,
.statfs = procfs_statfs
.is_dir = procfs_is_dir
};
vfs_fs_ops_t* procfs_get_ops(void) {

View file

@ -19,14 +19,14 @@ static void* sysfs_open(void *fs_private, const char *path, const char *mode) {
if (last_slash != -1) {
char prefix[64];
memcpy(prefix, path, last_slash);
k_memcpy(prefix, path, last_slash);
prefix[last_slash] = 0;
sub = subsystem_get_by_name(prefix);
if (sub) {
const char *filename = path + last_slash + 1;
for (int j = 0; j < sub->file_count; j++) {
if (strcmp(sub->files[j].name, filename) == 0) {
if (k_strcmp(sub->files[j].name, filename) == 0) {
sysfs_handle_t *h = (sysfs_handle_t*)kmalloc(sizeof(sysfs_handle_t));
h->sub = sub;
h->file = &sub->files[j];
@ -70,7 +70,7 @@ static int sysfs_readdir(void *fs_private, const char *path, vfs_dirent_t *entri
if (exact_sub) {
for (int i = 0; i < exact_sub->file_count && out < max; i++) {
strcpy(entries[out].name, exact_sub->files[i].name);
k_strcpy(entries[out].name, exact_sub->files[i].name);
entries[out].is_directory = 0;
entries[out].size = 0;
out++;
@ -78,11 +78,11 @@ static int sysfs_readdir(void *fs_private, const char *path, vfs_dirent_t *entri
}
int count = subsystem_get_count();
int path_len = strlen(path);
int path_len = k_strlen(path);
for (int i = 0; i < count && out < max; i++) {
kernel_subsystem_t *s = subsystem_get_by_index(i);
if (path_len == 0 || (strlen(s->name) > path_len && strncmp(s->name, path, path_len) == 0 && s->name[path_len] == '/')) {
if (path_len == 0 || (k_strlen(s->name) > path_len && k_strncmp(s->name, path, path_len) == 0 && s->name[path_len] == '/')) {
const char *sub_path = s->name + (path_len ? path_len + 1 : 0);
char comp[64];
int j = 0;
@ -96,13 +96,13 @@ static int sysfs_readdir(void *fs_private, const char *path, vfs_dirent_t *entri
bool found = false;
for (int k = 0; k < out; k++) {
if (strcmp(entries[k].name, comp) == 0) {
if (k_strcmp(entries[k].name, comp) == 0) {
found = true;
break;
}
}
if (!found) {
strcpy(entries[out].name, comp);
k_strcpy(entries[out].name, comp);
entries[out].is_directory = 1;
entries[out].size = 0;
out++;
@ -123,22 +123,22 @@ static bool sysfs_exists(void *fs_private, const char *path) {
for (int j = 0; path[j]; j++) if (path[j] == '/') last_slash = j;
if (last_slash != -1) {
char prefix[64];
memcpy(prefix, path, last_slash);
k_memcpy(prefix, path, last_slash);
prefix[last_slash] = 0;
kernel_subsystem_t *sub = subsystem_get_by_name(prefix);
if (sub) {
const char *filename = path + last_slash + 1;
for (int j = 0; j < sub->file_count; j++) {
if (strcmp(sub->files[j].name, filename) == 0) return true;
if (k_strcmp(sub->files[j].name, filename) == 0) return true;
}
}
}
int count = subsystem_get_count();
int path_len = strlen(path);
int path_len = k_strlen(path);
for (int i = 0; i < count; i++) {
kernel_subsystem_t *s = subsystem_get_by_index(i);
if (strlen(s->name) > path_len && strncmp(s->name, path, path_len) == 0 && s->name[path_len] == '/') return true;
if (k_strlen(s->name) > path_len && k_strncmp(s->name, path, path_len) == 0 && s->name[path_len] == '/') return true;
}
return false;
@ -152,13 +152,13 @@ static bool sysfs_is_dir(void *fs_private, const char *path) {
for (int j = 0; path[j]; j++) if (path[j] == '/') last_slash = j;
if (last_slash != -1) {
char prefix[64];
memcpy(prefix, path, last_slash);
k_memcpy(prefix, path, last_slash);
prefix[last_slash] = 0;
kernel_subsystem_t *sub = subsystem_get_by_name(prefix);
if (sub) {
const char *filename = path + last_slash + 1;
for (int j = 0; j < sub->file_count; j++) {
if (strcmp(sub->files[j].name, filename) == 0) return false;
if (k_strcmp(sub->files[j].name, filename) == 0) return false;
}
}
}
@ -166,14 +166,6 @@ static bool sysfs_is_dir(void *fs_private, const char *path) {
return sysfs_exists(fs_private, path);
}
static int sysfs_statfs(void *fs_private, vfs_statfs_t *stat) {
(void)fs_private;
stat->total_blocks = 0;
stat->free_blocks = 0;
stat->block_size = 512;
return 0;
}
vfs_fs_ops_t sysfs_ops = {
.open = sysfs_open,
.close = sysfs_close,
@ -181,8 +173,7 @@ vfs_fs_ops_t sysfs_ops = {
.write = sysfs_write,
.readdir = sysfs_readdir,
.exists = sysfs_exists,
.is_dir = sysfs_is_dir,
.statfs = sysfs_statfs
.is_dir = sysfs_is_dir
};
vfs_fs_ops_t* sysfs_get_ops(void) {

View file

@ -3,7 +3,6 @@
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "tar.h"
#include "fat32.h"
#include "bootfs.h"
// The standard TAR header block is 512 bytes.
struct tar_header {
@ -112,12 +111,8 @@ void tar_parse(void *archive, uint64_t archive_size) {
parent_path[last_slash] = '\0';
tar_mkdir_recursive(parent_path);
}
if (full_path[0] == '/' && full_path[1] == 'b' && full_path[2] == 'o' &&
full_path[3] == 'o' && full_path[4] == 't' && full_path[5] == '/') {
bootfs_register_file(full_path + 6, ptr + 512, (uint32_t)file_size);
}
// Extract the file data block directly into the VFS
FAT32_FileHandle *fh = fat32_open(full_path, "w");
if (fh && fh->valid) {
fat32_write(fh, ptr + 512, file_size);

View file

@ -710,26 +710,6 @@ bool vfs_is_directory(const char *path) {
return mount->ops->is_dir(mount->fs_private, rel_path);
}
int vfs_statfs(const char *path, vfs_statfs_t *stat) {
if (!path || !stat) return -1;
char normalized[VFS_MAX_PATH];
vfs_normalize_path("/", path, normalized);
const char *rel_path = NULL;
vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path);
if (!mount) return -1;
if (mount->ops->statfs) {
return mount->ops->statfs(mount->fs_private, stat);
}
stat->total_blocks = 0;
stat->free_blocks = 0;
stat->block_size = 512;
return 0;
}
int vfs_get_info(const char *path, vfs_dirent_t *info) {
if (!path || !info) return -1;

View file

@ -13,13 +13,6 @@
#define VFS_MAX_MOUNTS 16
#define VFS_MAX_OPEN_FILES 64
// statfs structure
typedef struct {
uint64_t total_blocks;
uint64_t free_blocks;
uint64_t block_size;
} vfs_statfs_t;
// Forward declarations
typedef struct vfs_mount vfs_mount_t;
typedef struct vfs_file vfs_file_t;
@ -54,7 +47,6 @@ typedef struct vfs_fs_ops {
bool (*exists)(void *fs_private, const char *rel_path);
bool (*is_dir)(void *fs_private, const char *rel_path);
int (*get_info)(void *fs_private, const char *rel_path, vfs_dirent_t *info);
int (*statfs)(void *fs_private, vfs_statfs_t *stat);
// Handle info (for backward compat with syscall position/size queries)
uint32_t (*get_position)(void *file_handle);
@ -107,7 +99,6 @@ bool vfs_rename(const char *old_path, const char *new_path);
bool vfs_exists(const char *path);
bool vfs_is_directory(const char *path);
int vfs_get_info(const char *path, vfs_dirent_t *info);
int vfs_statfs(const char *path, vfs_statfs_t *stat);
// Mount enumeration
int vfs_get_mount_count(void);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 830 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 817 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 499 KiB

Some files were not shown because too many files have changed in this diff Show more