Compare commits
No commits in common. "main" and "26.4.2.1" have entirely different histories.
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
|
@ -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.**
|
||||
25
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
|
@ -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.**
|
||||
36
.github/pull_request_template.md
vendored
|
|
@ -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 -->
|
||||
62
.github/workflows/nightly.yml
vendored
|
|
@ -1,62 +0,0 @@
|
|||
name: Nightly Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: write
|
||||
jobs:
|
||||
build-and-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
make \
|
||||
gcc-x86-64-linux-gnu \
|
||||
binutils-x86-64-linux-gnu \
|
||||
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
|
||||
|
||||
- name: Build ISO
|
||||
run: make -j4
|
||||
|
||||
- name: Update nightly tag
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git tag -fa nightly -m "Nightly build ${GITHUB_SHA}" "${GITHUB_SHA}"
|
||||
git push origin refs/tags/nightly --force
|
||||
|
||||
- name: Prepare release metadata
|
||||
id: metadata
|
||||
run: |
|
||||
echo "short_sha=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Publish nightly release asset
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: nightly
|
||||
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 }}`
|
||||
prerelease: true
|
||||
make_latest: false
|
||||
files: |
|
||||
boredos.iso
|
||||
overwrite_files: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
45
.gitignore
vendored
|
|
@ -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
391
Makefile
|
|
@ -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
|
||||
|
|
@ -16,43 +17,35 @@ ISO_DIR = iso_root
|
|||
KERNEL_ELF = $(BUILD_DIR)/boredos.elf
|
||||
ISO_IMAGE = boredos.iso
|
||||
|
||||
BLUE = \033[1;34m
|
||||
GREEN = \033[1;32m
|
||||
YELLOW= \033[1;33m
|
||||
RESET = \033[0m
|
||||
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)/net/*.c) \
|
||||
$(wildcard $(SRC_DIR)/net/nic/*.c) \
|
||||
$(wildcard $(SRC_DIR)/fs/*.c) \
|
||||
$(wildcard $(SRC_DIR)/wm/*.c) \
|
||||
$(wildcard $(SRC_DIR)/net/lwip/core/*.c) \
|
||||
$(wildcard $(SRC_DIR)/net/lwip/core/ipv4/*.c) \
|
||||
$(SRC_DIR)/net/lwip/netif/ethernet.c \
|
||||
$(SRC_DIR)/net/lwip/netif/bridgeif.c
|
||||
|
||||
define PRINT_STEP
|
||||
@printf ""
|
||||
@printf "\n$(BLUE)============================================================$(RESET)\n"
|
||||
@printf "$(BLUE)== %s$(RESET)\n" "$(1)"
|
||||
@printf "$(BLUE)============================================================$(RESET)\n"
|
||||
endef
|
||||
|
||||
DOCK_COLLOID_ICONS = $(shell sed -n 's/^[[:space:]]*{"\([^"]*\.png\)",[[:space:]]*DOCK_ICON_UNTRIED.*/\1/p' $(SRC_DIR)/wm/wm.c)
|
||||
USERLAND_COLLOID_ICONS = $(shell { \
|
||||
find $(SRC_DIR)/userland -type f -name '*.c' ! -path '*/third_party/*' -exec grep -hoE '"[^"]+\.png"' {} + 2>/dev/null; \
|
||||
find $(SRC_DIR)/userland -type f -name '*.h' ! -path '*/third_party/*' ! -name 'stb_image.h' -exec grep -hoE '"[^"]+\.png"' {} + 2>/dev/null; \
|
||||
} | sed 's/"//g' | sed 's@.*/@@' | sort -u)
|
||||
USERLAND_METADATA_ICONS = $(shell { \
|
||||
find $(SRC_DIR)/userland -type f -name '*.c' -exec sed -n 's@^[[:space:]]*//[[:space:]]*BOREDOS_APP_ICONS:[[:space:]]*@@p' {} + 2>/dev/null; \
|
||||
} | 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/*')
|
||||
|
||||
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)/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/lwip/%.c, $(BUILD_DIR)/lwip/%.o, $(filter $(SRC_DIR)/net/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/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
|
||||
|
||||
LDFLAGS = -m elf_x86_64 -nostdlib -static -pie --no-dynamic-linker \
|
||||
-z text -z max-page-size=0x1000 -T linker.ld
|
||||
|
|
@ -62,361 +55,177 @@ 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 limine-setup
|
||||
|
||||
.PHONY: all clean run run-hd limine-setup run-windows run-mac run-linux run-hd-mac run-hd-windows run-hd-linux
|
||||
|
||||
all:
|
||||
$(call PRINT_STEP,STARTING BOREDOS BUILD)
|
||||
$(MAKE) $(ISO_IMAGE)
|
||||
$(call PRINT_STEP,BUILD COMPLETE)
|
||||
all: $(ISO_IMAGE)
|
||||
|
||||
$(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"; \
|
||||
echo "Limine binaries missing or invalid. Cloning v$(LIMINE_VERSION)-binary..."; \
|
||||
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"; \
|
||||
fi
|
||||
@if [ ! -f $(SRC_DIR)/core/limine.h ]; then \
|
||||
printf "$(YELLOW)[LIMINE] Copying limine.h...$(RESET)\n"; \
|
||||
echo "Copying limine.h..."; \
|
||||
cp limine/limine.h $(SRC_DIR)/core/limine.h; \
|
||||
else \
|
||||
printf "$(YELLOW)[LIMINE] limine.h already present.$(RESET)\n"; \
|
||||
fi
|
||||
@printf "$(YELLOW)[LIMINE] Building Limine host utility...$(RESET)\n"
|
||||
@echo "Building Limine host utility..."; \
|
||||
$(MAKE) -C limine
|
||||
@printf "$(GREEN)[OK] Limine setup complete.$(RESET)\n"
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) limine-setup
|
||||
@printf "$(YELLOW)[CC]$(RESET) $< -> $@\n"
|
||||
@mkdir -p $(dir $@)
|
||||
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
|
||||
mkdir -p $(dir $@)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/sys/%.c | $(BUILD_DIR) limine-setup
|
||||
mkdir -p $(dir $@)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/mem/%.c | $(BUILD_DIR) limine-setup
|
||||
mkdir -p $(dir $@)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/dev/%.c | $(BUILD_DIR) limine-setup
|
||||
mkdir -p $(dir $@)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/net/%.c | $(BUILD_DIR) limine-setup
|
||||
mkdir -p $(dir $@)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/net/nic/%.c | $(BUILD_DIR) limine-setup
|
||||
mkdir -p $(dir $@)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/fs/%.c | $(BUILD_DIR) limine-setup
|
||||
mkdir -p $(dir $@)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/wm/%.c | $(BUILD_DIR) limine-setup
|
||||
mkdir -p $(dir $@)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD_DIR)/lwip/%.o: $(SRC_DIR)/net/lwip/%.c | $(BUILD_DIR) limine-setup
|
||||
mkdir -p $(dir $@)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/arch/%.asm | $(BUILD_DIR)
|
||||
$(NASM) $(NASMFLAGS) $< -o $@
|
||||
|
||||
$(BUILD_DIR)/test_syscall.o: $(SRC_DIR)/arch/test_syscall.asm | $(BUILD_DIR)
|
||||
$(NASM) $(NASMFLAGS) $< -o $@
|
||||
|
||||
$(BUILD_DIR)/user_test.o: $(SRC_DIR)/arch/user_test.asm | $(BUILD_DIR)
|
||||
$(NASM) $(NASMFLAGS) $< -o $@
|
||||
|
||||
$(BUILD_DIR)/process_asm.o: $(SRC_DIR)/arch/process_asm.asm | $(BUILD_DIR)
|
||||
$(NASM) $(NASMFLAGS) $< -o $@
|
||||
|
||||
$(KERNEL_ELF): $(OBJ_FILES)
|
||||
$(call PRINT_STEP,LINKING KERNEL)
|
||||
@printf "$(YELLOW)[LD]$(RESET) Linking kernel ELF: $@\n"
|
||||
$(LD) $(LDFLAGS) -o $@ $(OBJ_FILES)
|
||||
@printf "$(GREEN)[OK]$(RESET) Kernel ELF built: $@\n"
|
||||
$(call PRINT_STEP,BUILDING USERLAND)
|
||||
$(MAKE) -C $(SRC_DIR)/userland
|
||||
@printf "$(GREEN)[OK]$(RESET) Userland build complete.\n"
|
||||
|
||||
$(BUILD_DIR)/initrd.tar: $(KERNEL_ELF)
|
||||
$(call PRINT_STEP,BUILDING INITRD)
|
||||
@printf "$(YELLOW)[INITRD]$(RESET) Cleaning previous initrd directory...\n"
|
||||
rm -rf $(BUILD_DIR)/initrd
|
||||
|
||||
@printf "$(YELLOW)[INITRD]$(RESET) Creating directory structure...\n"
|
||||
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"
|
||||
@for f in $(SRC_DIR)/userland/bin/*.elf; do \
|
||||
if [ -f "$$f" ]; then \
|
||||
printf " -> $$f\n"; \
|
||||
cp "$$f" $(BUILD_DIR)/initrd/bin/; \
|
||||
fi \
|
||||
if [ -f "$$f" ]; then 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"
|
||||
@for f in $(SRC_DIR)/images/wallpapers/*; do \
|
||||
if [ -f "$$f" ]; then \
|
||||
printf " -> $$f\n"; \
|
||||
cp "$$f" $(BUILD_DIR)/initrd/Library/images/Wallpapers/; \
|
||||
fi \
|
||||
if [ -f "$$f" ]; then cp "$$f" $(BUILD_DIR)/initrd/Library/images/Wallpapers/; fi \
|
||||
done
|
||||
|
||||
@printf "$(YELLOW)[COPY]$(RESET) GIF assets...\n"
|
||||
@for f in $(SRC_DIR)/images/gif/*.gif; do \
|
||||
if [ -f "$$f" ]; then \
|
||||
printf " -> $$f\n"; \
|
||||
cp "$$f" $(BUILD_DIR)/initrd/Library/images/gif/; \
|
||||
fi \
|
||||
if [ -f "$$f" ]; then cp "$$f" $(BUILD_DIR)/initrd/Library/images/gif/; fi \
|
||||
done
|
||||
|
||||
@printf "$(YELLOW)[COPY]$(RESET) Colloid icons...\n"
|
||||
@for f in $(COLLOID_ICONS); do \
|
||||
src="$(SRC_DIR)/images/icons/colloid/$$f"; \
|
||||
if [ -f "$$src" ]; then \
|
||||
printf " -> $$src\n"; \
|
||||
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"
|
||||
@for f in $(SRC_DIR)/fonts/*.ttf; do \
|
||||
if [ -f "$$f" ]; then \
|
||||
printf " -> $$f\n"; \
|
||||
cp "$$f" $(BUILD_DIR)/initrd/Library/Fonts/; \
|
||||
fi \
|
||||
if [ -f "$$f" ]; then cp "$$f" $(BUILD_DIR)/initrd/Library/Fonts/; fi \
|
||||
done
|
||||
|
||||
@printf "$(YELLOW)[COPY]$(RESET) Emoji fonts...\n"
|
||||
@for f in $(SRC_DIR)/fonts/Emoji/*.ttf; do \
|
||||
if [ -f "$$f" ]; then \
|
||||
printf " -> $$f\n"; \
|
||||
cp "$$f" $(BUILD_DIR)/initrd/Library/Fonts/Emoji/; \
|
||||
fi \
|
||||
if [ -f "$$f" ]; then 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) 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) 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"
|
||||
@if [ -f $(SRC_DIR)/library/bsh/bshrc ]; then cp $(SRC_DIR)/library/bsh/bshrc $(BUILD_DIR)/initrd/Library/bsh/; fi
|
||||
@if [ -f $(SRC_DIR)/library/bsh/startup.bsh ]; then cp $(SRC_DIR)/library/bsh/startup.bsh $(BUILD_DIR)/initrd/Library/bsh/; fi
|
||||
@if [ -f $(SRC_DIR)/library/bsh/boot.bsh ]; then cp $(SRC_DIR)/library/bsh/boot.bsh $(BUILD_DIR)/initrd/Library/bsh/; fi
|
||||
@if [ -f $(SRC_DIR)/userland/games/doom/doom1.wad ]; then cp $(SRC_DIR)/userland/games/doom/doom1.wad $(BUILD_DIR)/initrd/Library/DOOM/; fi
|
||||
@for f in $$(find docs -name '*.md' 2>/dev/null); do \
|
||||
if [ -f "$$f" ]; then \
|
||||
printf " -> $$f\n"; \
|
||||
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
|
||||
@if [ -f README.md ]; then cp README.md $(BUILD_DIR)/initrd/; fi
|
||||
@if [ -f LICENSE ]; then cp LICENSE $(BUILD_DIR)/initrd/; fi
|
||||
@if [ -f limine.conf ]; then cp limine.conf $(BUILD_DIR)/initrd/; fi
|
||||
|
||||
@printf "$(YELLOW)[TAR]$(RESET) Creating initrd.tar...\n"
|
||||
cd $(BUILD_DIR)/initrd && COPYFILE_DISABLE=1 tar --exclude="._*" -cf ../initrd.tar *
|
||||
@printf "$(GREEN)[OK]$(RESET) Initrd created: $(BUILD_DIR)/initrd.tar\n"
|
||||
|
||||
$(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"
|
||||
rm -rf $(ISO_DIR)
|
||||
|
||||
@printf "$(YELLOW)[ISO]$(RESET) Creating ISO directory structure...\n"
|
||||
mkdir -p $(ISO_DIR)
|
||||
mkdir -p $(ISO_DIR)/EFI/BOOT
|
||||
|
||||
@printf "$(YELLOW)[COPY]$(RESET) Kernel ELF...\n"
|
||||
cp $(KERNEL_ELF) $(ISO_DIR)/
|
||||
|
||||
@printf "$(YELLOW)[COPY]$(RESET) Limine config...\n"
|
||||
cp limine.conf $(ISO_DIR)/
|
||||
|
||||
@printf "$(YELLOW)[COPY]$(RESET) Initrd...\n"
|
||||
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
|
||||
echo " 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
|
||||
@if [ -f splash.jpg ]; then cp splash.jpg $(ISO_DIR)/; fi
|
||||
|
||||
@printf "$(YELLOW)[COPY]$(RESET) Limine boot files...\n"
|
||||
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"
|
||||
cp limine/BOOTX64.EFI $(ISO_DIR)/EFI/BOOT/
|
||||
cp limine/BOOTIA32.EFI $(ISO_DIR)/EFI/BOOT/
|
||||
|
||||
$(call PRINT_STEP,GENERATING BOOTABLE ISO)
|
||||
$(XORRISO) -as mkisofs -R -J -b limine-bios-cd.bin \
|
||||
-no-emul-boot -boot-load-size 4 -boot-info-table \
|
||||
--efi-boot limine-uefi-cd.bin \
|
||||
-efi-boot-part --efi-boot-image --protective-msdos-label \
|
||||
$(ISO_DIR) -o $(ISO_IMAGE)
|
||||
|
||||
@printf "$(YELLOW)[LIMINE]$(RESET) Installing BIOS bootloader...\n"
|
||||
./limine/limine bios-install $(ISO_IMAGE)
|
||||
@printf "$(GREEN)[OK]$(RESET) ISO image ready: $(ISO_IMAGE)\n"
|
||||
|
||||
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"
|
||||
|
||||
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
|
||||
$(call PRINT_STEP,RUNNING BOREDOS IN QEMU ON WINDOWS)
|
||||
run-windows: $(ISO_IMAGE)
|
||||
qemu-system-x86_64 -m 4G -serial stdio -cdrom $< -boot d \
|
||||
-smp 4 \
|
||||
-audiodev dsound,id=audio0 -machine pcspk-audiodev=audio0 \
|
||||
-audiodev coreaudio,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
|
||||
|
||||
run-mac: $(ISO_IMAGE) disk.qcow2
|
||||
$(call PRINT_STEP,RUNNING BOREDOS IN QEMU ON MACOS)
|
||||
-drive file=disk.img,format=raw,file.locking=off
|
||||
run-mac: $(ISO_IMAGE)
|
||||
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
|
||||
$(call PRINT_STEP,RUNNING BOREDOS IN QEMU ON LINUX)
|
||||
run-linux: $(ISO_IMAGE)
|
||||
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 \
|
||||
-drive file=disk.img,format=raw,file.locking=off \
|
||||
-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 \
|
||||
-cpu max
|
||||
|
|
|
|||
100
README.md
|
|
@ -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>
|
||||
|
||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||

|
||||

|
||||

|
||||
|
||||
<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)
|
||||
|
||||
[](https://buymeacoffee.com/boreddevhq)
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||

|
||||
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.
|
||||
|
||||

|
||||
> [!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**, 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) 2023–2026 boreddevnl**
|
||||
**Copyright (C) 2024-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.
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 157 KiB |
|
Before Width: | Height: | Size: 205 KiB |
|
Before Width: | Height: | Size: 18 KiB |
BIN
disk.img
|
|
@ -7,58 +7,30 @@
|
|||
|
||||
Welcome to the documentation for BoredOS! This directory contains detailed guides on how the OS functions, how to build it, and how to develop applications for it.
|
||||
|
||||
## Table of Contents
|
||||
## 📚 Table of Contents
|
||||
|
||||
The documentation is organized into three main categories:
|
||||
|
||||
### 1. [Architecture](architecture/)
|
||||
### 1. 🏗️ [Architecture](architecture/)
|
||||
Explains the logical layout of the kernel and internal components.
|
||||
- [`Core`](architecture/core.md): Kernel source layout and the boot process (Limine, Multiboot2).
|
||||
- [`Memory`](architecture/memory.md): Physical Memory Management (PMM) and Virtual Memory Management (VMM).
|
||||
- [`Filesystem`](architecture/filesystem.md): Virtual File System (VFS) and the RAM-based FAT32 simulation.
|
||||
- [`Window Manager`](architecture/window_manager.md): How the built-in Window Manager natively handles graphics, events, and compositing.
|
||||
|
||||
#### System
|
||||
- [`Core`](architecture/system/core.md): Kernel source layout and the boot process (Limine, Multiboot2).
|
||||
- [`Processes & Scheduling`](architecture/system/processes.md): Multitasking, context switching, and ELF loading.
|
||||
- [`Interrupts & Exceptions`](architecture/system/interrupts.md): IDT, GDT, and exception handling.
|
||||
|
||||
#### Memory
|
||||
- [`Memory (PMM/VMM)`](architecture/memory/memory.md): Physical Memory Management and Virtual Memory Management.
|
||||
- [`Memory Manager`](architecture/memory/memory_manager.md): Slab allocator and block allocator for kernel heap.
|
||||
|
||||
#### Storage & Filesystems
|
||||
- [`Filesystem`](architecture/storage/filesystem.md): Virtual File System (VFS) and the RAM-based FAT32 simulation.
|
||||
- [`AHCI Drivers`](architecture/storage/ahci_drivers.md): Hardware communication for block storage devices.
|
||||
|
||||
#### Network
|
||||
- [`Network Stack`](architecture/network/network_stack.md): TCP/IP implementation and socket APIs.
|
||||
- [`Network Drivers`](architecture/network/network_drivers.md): Hardware interaction for network cards (e.g. e1000).
|
||||
|
||||
#### Graphics
|
||||
- [`Window Manager`](architecture/graphics/window_manager.md): Compositor, events, and overlapping windows.
|
||||
- [`Rendering`](architecture/graphics/rendering.md): Framebuffer, font rendering, and image loading.
|
||||
|
||||
#### Hardware
|
||||
- [`PCI`](architecture/hardware/pci.md): PCI bus enumeration and device binding.
|
||||
- [`Input`](architecture/hardware/input.md): PS/2 Keyboard and Mouse input handling.
|
||||
|
||||
#### Misc
|
||||
- [`Versioning`](architecture/versioning.md): The OS date-based version scheme (`YY.M[.x]`) and kernel semantic versioning (`MAJOR.MINOR.PATCH`).
|
||||
|
||||
### 2. [Building and Deployment](build/)
|
||||
### 2. 🔨 [Building and Deployment](build/)
|
||||
Instructions for compiling the OS from source.
|
||||
- [`Toolchain`](build/toolchain.md): Prerequisites and cross-compiler setup (`x86_64-elf-gcc`, `nasm`, `xorriso`).
|
||||
- [`Usage`](build/usage.md): Understanding the Makefile targets, QEMU emulation, and flashing to bare metal hardware.
|
||||
|
||||
### 3. [Application Development](appdev/)
|
||||
### 3. 🚀 [Application Development](appdev/)
|
||||
The SDK and toolchain guides for creating your own `.elf` userland binaries.
|
||||
- [`SDK Reference`](appdev/sdk_reference.md): Overview hub for SDK layout, includes, and links to detailed libc/syscall docs.
|
||||
- [`Syscalls`](appdev/syscalls.md): Current syscall numbers, FS/SYSTEM command IDs, and wrapper guidance.
|
||||
- [`libc Reference`](appdev/libc_reference.md): Current libc headers, implemented APIs, and behavior notes.
|
||||
- [`SDK Reference`](appdev/sdk_reference.md): Explanation of the custom `libc` wrappers (`stdlib.h`, `string.h`) and system calls.
|
||||
- [`UI API`](appdev/ui_api.md): Drawing on the screen, creating windows, and polling the event loop using `libui.h`.
|
||||
- [`Widget API`](appdev/widget_api.md): High-level UI components like buttons, textboxes, and scrollbars using `libwidget.h`.
|
||||
- [`Custom Apps`](appdev/custom_apps.md): A step-by-step tutorial on writing a new graphical C application, editing the Makefile, and bundling it into the ISO.
|
||||
- [`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.
|
||||
|
|
|
|||
|
|
@ -1,307 +0,0 @@
|
|||
<div align="center">
|
||||
<h1>ELF App Metadata</h1>
|
||||
<p><em>How BoredOS embeds and reads application identity and icon data from <code>.elf</code> binaries.</em></p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
BoredOS supports embedding **application metadata** including a display name, short description, and icon paths directly inside `.elf` executables using a standard ELF NOTE section. The kernel reads this metadata at runtime to display correct icons in the file explorer and on the desktop, without requiring any external sidecar files.
|
||||
|
||||
## Overview
|
||||
|
||||
When an ELF binary is compiled for BoredOS, the build system automatically injects a special ELF NOTE entry into a dedicated section called `.note.boredos.app`. This note holds a packed C struct (`boredos_app_metadata_t`) containing the app's metadata.
|
||||
|
||||
At runtime, the Window Manager (`wm.c`) and File Explorer (`explorer.c`) call `app_metadata_get_primary_image()` to extract the primary icon path from any `.elf` file before rendering its icon. This allows each app to display its own distinct icon instead of the generic binary icon.
|
||||
|
||||
---
|
||||
|
||||
## The `boredos_app_metadata_t` Structure
|
||||
|
||||
Defined in [`src/sys/elf.h`](../../src/sys/elf.h):
|
||||
|
||||
```c
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint32_t magic; // Must be BOREDOS_APP_METADATA_MAGIC (0x414d4431)
|
||||
uint16_t version; // Must be BOREDOS_APP_METADATA_VERSION (1)
|
||||
uint16_t image_count; // Number of valid icon paths (0–4)
|
||||
uint16_t reserved; // Padding, set to 0
|
||||
char app_name[BOREDOS_APP_METADATA_MAX_APP_NAME]; // Up to 63 chars + NUL
|
||||
char description[BOREDOS_APP_METADATA_MAX_DESCRIPTION]; // Up to 191 chars + NUL
|
||||
char images[BOREDOS_APP_METADATA_MAX_IMAGES][BOREDOS_APP_METADATA_MAX_IMAGE_PATH]; // Up to 4 icon paths
|
||||
} boredos_app_metadata_t;
|
||||
```
|
||||
|
||||
### Field Reference
|
||||
|
||||
| Field | Size | Description |
|
||||
|---|---|---|
|
||||
| `magic` | 4 bytes | Magic number `0x414D4431` — validates the struct is a real metadata blob. |
|
||||
| `version` | 2 bytes | Schema version. Currently always `1`. |
|
||||
| `image_count` | 2 bytes | How many entries in `images[]` are valid (0–4). |
|
||||
| `reserved` | 2 bytes | Must be 0. Reserved for future use. |
|
||||
| `app_name` | 64 bytes | Null-terminated display name of the app (e.g., `"Terminal"`). |
|
||||
| `description` | 192 bytes | Null-terminated short description (e.g., `"Terminal shell and command runner."`). |
|
||||
| `images[4][160]` | 640 bytes | Up to 4 absolute VFS paths to PNG icons. First entry is the primary icon. |
|
||||
|
||||
### Limits
|
||||
|
||||
| Constant | Value | Meaning |
|
||||
|---|---|---|
|
||||
| `BOREDOS_APP_METADATA_MAX_APP_NAME` | 64 | Max bytes for `app_name` including NUL |
|
||||
| `BOREDOS_APP_METADATA_MAX_DESCRIPTION` | 192 | Max bytes for `description` including NUL |
|
||||
| `BOREDOS_APP_METADATA_MAX_IMAGES` | 4 | Max number of icon paths |
|
||||
| `BOREDOS_APP_METADATA_MAX_IMAGE_PATH` | 160 | Max bytes per icon path including NUL |
|
||||
|
||||
---
|
||||
|
||||
## The ELF NOTE Format
|
||||
|
||||
The metadata is stored inside a standard ELF NOTE entry (defined by `Elf64_Nhdr` in `elf.h`) within the `.note.boredos.app` section.
|
||||
|
||||
```
|
||||
+------------------+
|
||||
| Elf64_Nhdr | namesz, descsz, type
|
||||
+------------------+
|
||||
| name: "BOREDOS\0"| 8 bytes (sizeof BOREDOS_APP_NOTE_NAME)
|
||||
+------------------+
|
||||
| boredos_app_ | sizeof(boredos_app_metadata_t)
|
||||
| metadata_t |
|
||||
+------------------+
|
||||
```
|
||||
|
||||
### Note Constants
|
||||
|
||||
| Constant | Value | Description |
|
||||
|---|---|---|
|
||||
| `BOREDOS_APP_NOTE_OWNER` | `"BOREDOS"` | The note owner/name string |
|
||||
| `BOREDOS_APP_NOTE_SECTION` | `".note.boredos.app"` | ELF section name |
|
||||
| `BOREDOS_APP_NOTE_TYPE` | `0x41505031` | Note type identifier (`"APP1"` in ASCII) |
|
||||
| `BOREDOS_APP_METADATA_MAGIC` | `0x414D4431` | Metadata struct magic (`"AMD1"`) |
|
||||
| `BOREDOS_APP_METADATA_VERSION` | `1` | Current schema version |
|
||||
|
||||
---
|
||||
|
||||
## Embedding Metadata into your applications
|
||||
|
||||
Developers declare metadata using **special comment annotations** at the top of their C source file. The build system reads these automatically during compilation.
|
||||
|
||||
```c
|
||||
// BOREDOS_APP_DESC: My application's short description.
|
||||
// BOREDOS_APP_ICONS: /Library/images/icons/colloid/my-icon.png
|
||||
```
|
||||
|
||||
### `BOREDOS_APP_DESC`
|
||||
|
||||
A single-line description of the application. Truncated to 191 characters.
|
||||
|
||||
### `BOREDOS_APP_ICONS`
|
||||
|
||||
A semicolon-separated list of absolute VFS paths to PNG icons. Up to 4 icons are supported. The **first** entry is used as the primary icon displayed in the File Explorer and on the Desktop.
|
||||
|
||||
```c
|
||||
// BOREDOS_APP_ICONS: /Library/images/icons/colloid/primary.png;/Library/images/icons/colloid/alternate.png
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> If no `BOREDOS_APP_ICONS` annotation is provided, the build tool falls back to `/Library/images/icons/colloid/xterm.png`.
|
||||
> If no `BOREDOS_APP_DESC` annotation is provided, the build tool uses `"BoredOS userspace application."`.
|
||||
|
||||
---
|
||||
|
||||
## Build System Integration
|
||||
|
||||
### The `gen_userland_note.sh` Tool
|
||||
|
||||
Located at [`tools/gen_userland_note.sh`](../../tools/gen_userland_note.sh), this script is invoked automatically by the `src/userland/Makefile` for every compiled application.
|
||||
|
||||
**Usage:**
|
||||
```sh
|
||||
gen_userland_note.sh <app-name> <source-file> <icon-source-dir> <output.note.c>
|
||||
```
|
||||
|
||||
| Argument | Description |
|
||||
|---|---|
|
||||
| `<app-name>` | The base name of the application (e.g., `terminal`) |
|
||||
| `<source-file>` | Path to the main `.c` source to extract annotations from |
|
||||
| `<icon-source-dir>` | Directory where icon files are expected to exist on the *host* (build-time validation) |
|
||||
| `<output.note.c>` | Path for the generated C source file |
|
||||
|
||||
The script:
|
||||
1. Reads `BOREDOS_APP_DESC` and `BOREDOS_APP_ICONS` from the source file.
|
||||
2. Validates that each declared icon file exists in `<icon-source-dir>` at build time.
|
||||
3. Generates a C file (e.g., `bin/terminal.note.c`) that defines a `__attribute__((section(".note.boredos.app")))` constant struct containing all metadata.
|
||||
|
||||
### Makefile Rules
|
||||
|
||||
In `src/userland/Makefile`, the following rules handle metadata generation and linking:
|
||||
|
||||
```make
|
||||
# Generate the .note.c for each app from its source annotations
|
||||
$(BIN_DIR)/%.note.c: $(APP_METADATA_TOOL) | $(BIN_DIR)
|
||||
src="$(call app_source_for,$*)"; \
|
||||
sh $(APP_METADATA_TOOL) "$*" "$$src" "$(APP_ICON_SOURCE_DIR)" "$@"
|
||||
|
||||
# Compile the generated note C file
|
||||
$(BIN_DIR)/%.note.o: $(BIN_DIR)/%.note.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
# Link note object into each ELF (generic rule)
|
||||
$(BIN_DIR)/%.elf: $(LIBC_OBJS) $(BIN_DIR)/%.o $(BIN_DIR)/%.note.o
|
||||
$(LD) $(LDFLAGS) $^ -o $@
|
||||
```
|
||||
|
||||
Special-cased apps (`doom`, `lua`, `viewer`, `settings`, `browser`, `screenshot`) also link in their own `.note.o` explicitly.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The `-I../sys` flag is added to `CFLAGS` so that generated `.note.c` files can `#include "elf.h"` when referencing the metadata constants.
|
||||
|
||||
---
|
||||
|
||||
## Runtime Parsing: `app_metadata.c`
|
||||
|
||||
At runtime, `src/sys/app_metadata.c` provides two public functions:
|
||||
|
||||
```c
|
||||
bool app_metadata_read(const char *path, boredos_app_metadata_t *out_metadata);
|
||||
bool app_metadata_get_primary_image(const char *path, char *out_path, size_t out_path_size);
|
||||
```
|
||||
|
||||
### `app_metadata_read`
|
||||
|
||||
Opens the ELF at `path` via VFS and searches for the `.note.boredos.app` section. It uses a **two-pass strategy**:
|
||||
|
||||
1. **Raw scan** (`am_scan_raw_notes`): For files up to 16 MiB, loads the entire binary into memory and byte-scans for a NOTE header matching the `BOREDOS` owner and `BOREDOS_APP_NOTE_TYPE`. This handles cases where the section header table is missing or unreadable.
|
||||
2. **Section-based scan** (`am_parse_note_section`): Reads the ELF section header table, locates the `.note.boredos.app` section by name, then parses NOTE entries within it.
|
||||
|
||||
After a successful parse, the struct is validated via `am_validate_metadata` (checks magic and version fields) and sanitized via `am_sanitize_metadata` (null-terminates all strings).
|
||||
|
||||
### `app_metadata_get_primary_image`
|
||||
|
||||
A convenience wrapper around `app_metadata_read` that returns just the first icon path:
|
||||
|
||||
```c
|
||||
bool app_metadata_get_primary_image(const char *path, char *out_path, size_t out_path_size);
|
||||
```
|
||||
|
||||
Returns `true` and populates `out_path` if the binary has at least one valid icon declared.
|
||||
|
||||
### Metadata Cache
|
||||
|
||||
To avoid re-reading ELF files on every frame redraw, results are stored in a **simple FIFO cache** of up to 64 entries:
|
||||
|
||||
```c
|
||||
#define APP_METADATA_CACHE_SIZE 64
|
||||
```
|
||||
|
||||
Both positive (metadata found) and negative (no metadata) results are cached. The cache uses a round-robin eviction strategy — no LRU, no invalidation. This is intentional for a kernel context where metadata does not change while the OS is running.
|
||||
|
||||
---
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Userspace API
|
||||
|
||||
Userspace applications can query the ELF metadata of any `.elf` binary on the VFS through two wrapper functions declared in [`src/userland/libc/syscall.h`](../../src/userland/libc/syscall.h).
|
||||
|
||||
### The `boredos_app_metadata_t` struct (userland)
|
||||
|
||||
The struct is redefined verbatim in the userland header so that apps do **not** need to include any kernel header:
|
||||
|
||||
```c
|
||||
#define BOREDOS_APP_METADATA_MAX_APP_NAME 64
|
||||
#define BOREDOS_APP_METADATA_MAX_DESCRIPTION 192
|
||||
#define BOREDOS_APP_METADATA_MAX_IMAGES 4
|
||||
#define BOREDOS_APP_METADATA_MAX_IMAGE_PATH 160
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint32_t magic;
|
||||
uint16_t version;
|
||||
uint16_t image_count;
|
||||
uint16_t reserved;
|
||||
char app_name[BOREDOS_APP_METADATA_MAX_APP_NAME];
|
||||
char description[BOREDOS_APP_METADATA_MAX_DESCRIPTION];
|
||||
char images[BOREDOS_APP_METADATA_MAX_IMAGES][BOREDOS_APP_METADATA_MAX_IMAGE_PATH];
|
||||
} boredos_app_metadata_t;
|
||||
```
|
||||
|
||||
### Functions
|
||||
|
||||
#### `sys_get_elf_metadata`
|
||||
|
||||
```c
|
||||
int sys_get_elf_metadata(const char *path, boredos_app_metadata_t *out_metadata);
|
||||
```
|
||||
|
||||
Reads the full metadata blob from the `.note.boredos.app` section of the ELF at `path` and writes it into `*out_metadata`.
|
||||
|
||||
Returns `1` on success, `0` on failure (file not found, no metadata note, or validation failure).
|
||||
|
||||
#### `sys_get_elf_primary_image`
|
||||
|
||||
```c
|
||||
int sys_get_elf_primary_image(const char *path, char *out_path, size_t out_path_size);
|
||||
```
|
||||
|
||||
Convenience wrapper that returns only the first icon path from the metadata. Useful when you just need to display an application icon without allocating a full `boredos_app_metadata_t`.
|
||||
|
||||
Returns `1` and writes a null-terminated VFS path into `out_path` if at least one icon was declared. Returns `0` otherwise.
|
||||
|
||||
### Syscall IDs
|
||||
|
||||
Both functions route through `SYS_SYSTEM` using dedicated command IDs:
|
||||
|
||||
| ID | Macro | Function |
|
||||
|---|---|---|
|
||||
| 76 | `SYSTEM_CMD_GET_ELF_METADATA` | `sys_get_elf_metadata` |
|
||||
| 77 | `SYSTEM_CMD_GET_ELF_PRIMARY_IMAGE` | `sys_get_elf_primary_image` |
|
||||
|
||||
### Caching
|
||||
|
||||
Both calls share the same kernel-side **64-entry FIFO metadata cache** used by the Window Manager and File Explorer. If the metadata for a path has already been read, the result is returned from cache without re-reading the file. Negative results (no metadata) are also cached.
|
||||
|
||||
### Example: reading full metadata
|
||||
|
||||
```c
|
||||
#include "syscall.h"
|
||||
#include "stdio.h"
|
||||
|
||||
void print_app_info(const char *elf_path) {
|
||||
boredos_app_metadata_t meta;
|
||||
if (!sys_get_elf_metadata(elf_path, &meta)) {
|
||||
printf("%s: no metadata\n", elf_path);
|
||||
return;
|
||||
}
|
||||
printf("Name: %s\n", meta.app_name);
|
||||
printf("Description: %s\n", meta.description);
|
||||
printf("Icons (%u):\n", meta.image_count);
|
||||
for (int i = 0; i < (int)meta.image_count; i++) {
|
||||
printf(" [%d] %s\n", i, meta.images[i]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Example: fetching just the icon path
|
||||
|
||||
```c
|
||||
#include "syscall.h"
|
||||
|
||||
void load_icon_for(const char *elf_path, Image *out_icon) {
|
||||
char icon_path[BOREDOS_APP_METADATA_MAX_IMAGE_PATH];
|
||||
if (sys_get_elf_primary_image(elf_path, icon_path, sizeof(icon_path))) {
|
||||
*out_icon = image_load(icon_path);
|
||||
} else {
|
||||
*out_icon = image_load("/Library/images/icons/colloid/xterm.png"); // fallback
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> The metadata is read **from the VFS**, so the ELF must already be present as a file. The kernel does **not** read metadata from an already-running process image in memory — it re-opens the file via the filesystem.
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
*See also: [`custom_apps.md`](custom_apps.md) for a full tutorial on building and bundling a new application, [`sdk_reference.md`](sdk_reference.md) for an overview of the SDK, and [`syscalls.md`](syscalls.md) for the complete SYSTEM command ID table.*
|
||||
|
|
@ -7,18 +7,16 @@
|
|||
|
||||
This example demonstrates the bare minimum structure of a BoredOS application that outputs text to the standard output (usually the Terminal executing the binary).
|
||||
|
||||
## Concepts Introduced
|
||||
## 📝 Concepts Introduced
|
||||
* Including `stdlib.h` for basic IO.
|
||||
* The `main()` entry point.
|
||||
* Using `printf()` for formatted output.
|
||||
* Declaring app metadata via source annotations.
|
||||
|
||||
---
|
||||
|
||||
## The Code (`src/userland/cli/hello_world.c`)
|
||||
## 💻 The Code (`src/userland/cli/hello_world.c`)
|
||||
|
||||
```c
|
||||
// BOREDOS_APP_DESC: Hello World — a minimal CLI demo.
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
|
@ -36,15 +34,14 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
```
|
||||
|
||||
## How it Works
|
||||
## 🛠️ How it Works
|
||||
|
||||
1. **`#include <stdlib.h>`**: We include the SDK's standard library header which gives us access to `printf`.
|
||||
2. **`int main(...)`**: Every process begins execution here (managed transparently by `crt0.asm`).
|
||||
3. **`printf(...)`**: The SDK routes this call internally directly to the `SYS_WRITE` system call, making it available on the terminal.
|
||||
4. **`return 0`**: A successful exit code.
|
||||
5. **`BOREDOS_APP_DESC` / `BOREDOS_APP_ICONS`**: These comment annotations are read by the build system (`gen_userland_note.sh`) and embedded as a `boredos_app_metadata_t` NOTE entry inside the compiled `.elf`. The File Explorer and Desktop use this to display the correct icon. See [`elf_metadata.md`](../elf_metadata.md) for full details.
|
||||
4. **`return 0`**: A successful exit code.
|
||||
|
||||
## Running It
|
||||
## 🚀 Running It
|
||||
|
||||
If you build the project, you can open the Terminal and type:
|
||||
```sh
|
||||
|
|
|
|||
|
|
@ -7,19 +7,17 @@
|
|||
|
||||
This example demonstrates how to create an empty window that stays active on the screen until the user explicitly closes it by clicking the 'X' button.
|
||||
|
||||
## Concepts Introduced
|
||||
## 📝 Concepts Introduced
|
||||
* 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)`.
|
||||
* Declaring app metadata via source annotations.
|
||||
* Yielding CPU time to the kernel via `sys_yield()`.
|
||||
|
||||
---
|
||||
|
||||
## The Code (`src/userland/gui/basic_window.c`)
|
||||
## 💻 The Code (`src/userland/gui/basic_window.c`)
|
||||
|
||||
```c
|
||||
// BOREDOS_APP_DESC: Basic Window — a minimal graphical window demo.
|
||||
#include <stdlib.h>
|
||||
#include <libui.h>
|
||||
#include <syscall.h>
|
||||
|
|
@ -50,11 +48,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,9 +64,8 @@ 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.
|
||||
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.
|
||||
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."
|
||||
|
||||
## Running It
|
||||
## 🚀 Running It
|
||||
|
||||
Launch the Terminal and type `basic_window`. You'll see an empty window appear that you can move around the screen!
|
||||
|
|
|
|||
|
|
@ -7,20 +7,17 @@
|
|||
|
||||
This example builds upon the `02_basic_window` guide. It demonstrates how to constantly update the screen to simulate a bouncing square moving freely inside the window bounds.
|
||||
|
||||
## Concepts Introduced
|
||||
## 📝 Concepts Introduced
|
||||
* Maintaining application state across frames (Velocity/Position).
|
||||
* Drawing primitives (`ui_fill_rect`, `ui_draw_string`).
|
||||
* The importance of clearing the screen on a new frame.
|
||||
* Explicitly forcing standard visual updates via `ui_mark_dirty()`.
|
||||
* Declaring app metadata via source annotations.
|
||||
|
||||
---
|
||||
|
||||
## The Code (`src/userland/gui/bounce.c`)
|
||||
## 💻 The Code (`src/userland/gui/bounce.c`)
|
||||
|
||||
```c
|
||||
// BOREDOS_APP_DESC: Bouncing ball animation demo.
|
||||
// BOREDOS_APP_ICONS: /Library/images/icons/colloid/applications-games.png
|
||||
#include <stdlib.h>
|
||||
#include <libui.h>
|
||||
#include <syscall.h>
|
||||
|
|
@ -76,21 +73,20 @@ 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;
|
||||
}
|
||||
```
|
||||
|
||||
## How it Works
|
||||
## 🛠️ How it Works
|
||||
|
||||
1. **State Management**: We store `pos_x`, `pos_y`, `vel_x`, and `vel_y`. These variables represent the "physics" of our system. Notice that they update *outside* the event-checking logic so that the animation runs even if the user isn't clicking the mouse.
|
||||
2. **Screen Clearing**: We *must* fill the screen with black (`ui_draw_rect(wid, 0, 0, W_WIDTH, W_HEIGHT, ...)`). If we don't clear the screen, the red square will leave a permanent trailing smear everywhere it goes!
|
||||
3. **The Double Buffer**: `ui_draw_rect` and `ui_draw_string` do not immediately appear on your monitor. They just color a hidden buffer within the kernel.
|
||||
3. **The Double Buffer**: `ui_draw_rect` and `ui_draw_string` do not immediately appear on your monitor. They just color a hidden buffer within the kernel.
|
||||
4. **`ui_mark_dirty`**: This is the crucial command that tells the kernel Window Manager, "I'm done drawing my frame. Can you quickly copy my hidden buffer over to the real screen now?"
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -12,15 +12,12 @@ This advanced example demonstrates the steps required to use the raw network sys
|
|||
* Performing DNS lookups manually via `sys_dns_lookup`.
|
||||
* Managing strict TCP flow logic (`sys_tcp_connect`, send, block for receive).
|
||||
* Using the terminal `SYS_WRITE` output for debugging.
|
||||
* Declaring app metadata via source annotations.
|
||||
|
||||
---
|
||||
|
||||
## The Code (`src/userland/cli/http_get.c`)
|
||||
## 💻 The Code (`src/userland/cli/http_get.c`)
|
||||
|
||||
```c
|
||||
// BOREDOS_APP_DESC: HTTP GET client — fetches a webpage over TCP.
|
||||
// BOREDOS_APP_ICONS: /Library/images/icons/colloid/network-wired.png
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syscall.h>
|
||||
|
|
@ -82,15 +79,14 @@ int main(void) {
|
|||
}
|
||||
```
|
||||
|
||||
## How it Works
|
||||
## 🛠️ How it Works
|
||||
|
||||
1. **Network Setup**: First, we must ensure the host machine or QEMU environment gave BoredOS a valid IP address via DHCP. The `sys_network_has_ip()` check prevents our app from hanging trying to route data to nowhere.
|
||||
2. **DNS (`sys_dns_lookup`)**: Since we want to connect to a domain name, not a raw IP, we query the DNS server configured by the OS (which it received via DHCP).
|
||||
3. **Connection (`sys_tcp_connect`)**: We block the application thread while the OS performs the 3-way TCP handshake over port 80.
|
||||
4. **Payload (`sys_tcp_send`)**: We format a compliant HTTP/1.1 payload representing a simple GET request for the root directory `/`.
|
||||
5. **Chunked Receiving (`sys_tcp_recv`)**: The server's response might be larger than our `recv_buf` (512 bytes). Therefore, we loop. `sys_tcp_recv` blocks execution until data arrives. If it returns `0`, the remote server cleanly closed the connection (which happens automatically because we specified `Connection: close` in our request payload!).
|
||||
6. **`BOREDOS_APP_DESC` / `BOREDOS_APP_ICONS`**: Embedded into the compiled `.elf` as a BoredOS NOTE section. The Desktop and File Explorer read this to display the app's icon. See [`elf_metadata.md`](../elf_metadata.md) for full details.
|
||||
|
||||
## Running It
|
||||
## 🚀 Running It
|
||||
|
||||
Make sure QEMU is running with networking enabled. Launch the terminal and type `http_get`. You will see the raw headers and HTML source of the target webpage scroll down the CLI interface!
|
||||
|
|
|
|||
|
|
@ -1,251 +0,0 @@
|
|||
# UTF-8 Library — Application Development Guide
|
||||
|
||||
## Overview
|
||||
|
||||
The userland libc provides a lightweight UTF-8 utility module located in:
|
||||
|
||||
- src/userland/libc/utf-8.c
|
||||
- src/userland/libc/utf-8.h
|
||||
|
||||
This module is designed for **direct use in applications** requiring UTF-8 handling. It provides basic primitives for decoding, encoding, and traversing UTF-8 strings safely.
|
||||
|
||||
It is intended for:
|
||||
|
||||
- text rendering
|
||||
- terminal input/output
|
||||
- cursor movement
|
||||
- string processing at the character level
|
||||
|
||||
---
|
||||
|
||||
## Synopsis
|
||||
|
||||
```c
|
||||
#include "utf-8.h"
|
||||
|
||||
uint32_t text_decode_utf8(const char *s, int *advance);
|
||||
int text_encode_utf8(uint32_t cp, char *out);
|
||||
|
||||
const char* text_next_utf8(const char *s);
|
||||
const char* text_prev_utf8(const char *start, const char *s);
|
||||
|
||||
int text_strlen_utf8(const char *s);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
### text_decode_utf8
|
||||
|
||||
```c
|
||||
uint32_t text_decode_utf8(const char *s, int *advance);
|
||||
```
|
||||
|
||||
Decodes a UTF-8 sequence into a Unicode code point.
|
||||
|
||||
- `s`: pointer to current position in a UTF-8 string
|
||||
- `advance`: receives number of bytes consumed
|
||||
|
||||
Returns:
|
||||
|
||||
- decoded Unicode code point (`uint32_t`)
|
||||
- `0` if input is null or empty
|
||||
- `0xFFFD` for invalid sequences
|
||||
|
||||
---
|
||||
|
||||
### text_encode_utf8
|
||||
|
||||
```c
|
||||
int text_encode_utf8(uint32_t cp, char *out);
|
||||
```
|
||||
|
||||
Encodes a Unicode code point into UTF-8.
|
||||
|
||||
- `cp`: Unicode code point
|
||||
- `out`: buffer receiving encoded bytes
|
||||
|
||||
Returns:
|
||||
|
||||
- number of bytes written (1–4)
|
||||
- writes replacement character if `cp` is invalid
|
||||
|
||||
---
|
||||
|
||||
### text_next_utf8
|
||||
|
||||
```c
|
||||
const char* text_next_utf8(const char *s);
|
||||
```
|
||||
|
||||
Advances to the next UTF-8 character.
|
||||
|
||||
Returns a pointer to the next character boundary.
|
||||
|
||||
---
|
||||
|
||||
### text_prev_utf8
|
||||
|
||||
```c
|
||||
const char* text_prev_utf8(const char *start, const char *s);
|
||||
```
|
||||
|
||||
Moves backward to the previous UTF-8 character.
|
||||
|
||||
- `start`: beginning of the buffer
|
||||
- `s`: current position
|
||||
|
||||
Used for reverse traversal and cursor movement.
|
||||
|
||||
---
|
||||
|
||||
### text_strlen_utf8
|
||||
|
||||
```c
|
||||
int text_strlen_utf8(const char *s);
|
||||
```
|
||||
|
||||
Counts UTF-8 characters (code points), not bytes.
|
||||
|
||||
---
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Iterating over UTF-8 characters
|
||||
|
||||
```c
|
||||
const char *p = text;
|
||||
|
||||
while (*p) {
|
||||
int adv;
|
||||
uint32_t cp = text_decode_utf8(p, &adv);
|
||||
|
||||
/* process cp */
|
||||
|
||||
p += adv;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Cursor movement
|
||||
|
||||
```c
|
||||
cursor = text_next_utf8(cursor);
|
||||
cursor = text_prev_utf8(buffer_start, cursor);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Encoding a character
|
||||
|
||||
```c
|
||||
char out[4];
|
||||
int len = text_encode_utf8(0x20AC, out);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Backspace handling
|
||||
|
||||
```c
|
||||
char *prev = (char*)text_prev_utf8(buffer, cursor);
|
||||
cursor = prev;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### UTF-8 Encoding
|
||||
|
||||
The implementation supports:
|
||||
|
||||
- 1 byte: `0x00 – 0x7F`
|
||||
- 2 bytes: `0x80 – 0x7FF`
|
||||
- 3 bytes: `0x800 – 0xFFFF`
|
||||
- 4 bytes: `0x10000 – 0x10FFFF`
|
||||
|
||||
---
|
||||
|
||||
### Replacement Character
|
||||
|
||||
Invalid sequences are replaced with:
|
||||
|
||||
- code point: `0xFFFD`
|
||||
- UTF-8 encoding: `0xEF 0xBF 0xBD`
|
||||
|
||||
---
|
||||
### UTF-8 Byte Structure
|
||||
|
||||
The following diagram illustrates how UTF-8 bytes are structured, including
|
||||
ASCII, continuation bytes, and multi-byte sequence headers:
|
||||
|
||||
<img width="815" height="1003" alt="image" src="https://github.com/user-attachments/assets/0d289a94-6037-4039-87a3-125c0c0e83d0" />
|
||||
<sub>Source: <a href="https://www.youtube.com/watch?v=vpSkBV5vydg">Nic Barker — "UTF-8, Explained Simply"</a> (YouTube)</sub>
|
||||
|
||||
---
|
||||
|
||||
### Control Signals
|
||||
|
||||
Some decoded code points correspond to control signals instead of printable characters.
|
||||
|
||||
ASCII control range:
|
||||
|
||||
- `0x00 – 0x1F`
|
||||
|
||||
Examples:
|
||||
|
||||
- `0x08` → Backspace
|
||||
- `0x09` → Tab
|
||||
- `0x0A` → Line Feed
|
||||
- `0x0D` → Carriage Return
|
||||
- `0x1B` → Escape
|
||||
|
||||
These are typically interpreted by:
|
||||
|
||||
- terminal logic
|
||||
- shell input handling
|
||||
- system interfaces
|
||||
|
||||
---
|
||||
|
||||
### Non-ASCII Characters
|
||||
|
||||
Characters outside the ASCII range (`0x00 – 0x7F`) are encoded using multi-byte UTF-8 sequences.
|
||||
|
||||
Examples:
|
||||
|
||||
- 'é' → `0xC3 0xA9`
|
||||
- '€' → `0xE2 0x82 0xAC`
|
||||
|
||||
Decoded values:
|
||||
|
||||
- 'é' → `U+00E9`
|
||||
- '€' → `U+20AC`
|
||||
|
||||
---
|
||||
|
||||
### Modifiers and Layout
|
||||
|
||||
Character output depends on:
|
||||
|
||||
- keyboard layout
|
||||
- modifier keys (Shift, Ctrl, AltGr)
|
||||
|
||||
Example:
|
||||
|
||||
- `KEY_E` → 'e'
|
||||
- `KEY_E + SHIFT` → 'E'
|
||||
- `KEY_E + AltGr` → '€'
|
||||
|
||||
---
|
||||
|
||||
## Also worth watching
|
||||
|
||||
If you want to dive deeper or simply get a better intuitive understanding of UTF-8, the video below is highly recommended:
|
||||
|
||||
[Nic Barker — "UTF-8, Explained Simply"](https://www.youtube.com/watch?v=vpSkBV5vydg)
|
||||
|
||||
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
# libc Reference
|
||||
|
||||
This page documents the current BoredOS userland libc surface from `src/userland/libc/`.
|
||||
|
||||
BoredOS libc is a compact implementation focused on the APIs used by in-tree apps. It is not a full glibc replacement.
|
||||
|
||||
## Header Overview
|
||||
|
||||
| Header | Focus |
|
||||
|---|---|
|
||||
| `stdlib.h` | allocation, conversion, process helpers |
|
||||
| `string.h` | memory/string primitives |
|
||||
| `stdio.h` | `FILE*` and formatted I/O |
|
||||
| `unistd.h` | POSIX-like fd/process calls |
|
||||
| `fcntl.h` | open/fcntl flags, `dup`, `pipe` |
|
||||
| `input.h` | keyboard keycode constants |
|
||||
| `signal.h` | signal handlers and masks |
|
||||
| `sys/stat.h` | `stat`/`fstat` and file mode bits |
|
||||
| `sys/types.h` | core typedefs (`pid_t`, `ssize_t`, ...) |
|
||||
| `sys/wait.h` | `waitpid` and wait macros |
|
||||
| `errno.h` | errno values |
|
||||
| `time.h` | time/date utilities |
|
||||
| `math.h` | floating-point math helpers |
|
||||
| `libui.h` | GUI/window drawing API |
|
||||
|
||||
## stdlib.h
|
||||
|
||||
Implemented core functions:
|
||||
- Memory: `malloc`, `free`, `calloc`, `realloc`
|
||||
- Memory aliases: `memset`, `memcpy`
|
||||
- Conversions: `atoi`, `itoa`, `strtod`, `abs`
|
||||
- Output: `puts`, `printf`
|
||||
- Process/environment: `exit`, `_exit`, `sleep`, `chdir`, `getcwd`, `access`, `system`, `getenv`, `abort`
|
||||
|
||||
Notes:
|
||||
- `sleep` is millisecond-based and maps to kernel sleep command.
|
||||
- `system` is a stub-style helper in this libc, not a full shell launcher equivalent.
|
||||
|
||||
## string.h
|
||||
|
||||
Implemented C string/memory set includes:
|
||||
- Memory: `memmove`, `memcmp`, `memcpy`, `memset`, `memchr`
|
||||
- Search: `strchr`, `strrchr`, `strpbrk`, `strstr`
|
||||
- Span: `strspn`, `strcspn`
|
||||
- Compare: `strcmp`, `strncmp`, `strcasecmp`, `strncasecmp`, `strcoll`
|
||||
- Build/copy: `strlen`, `strcpy`, `strcat`, `strdup`
|
||||
- Errors: `strerror`
|
||||
|
||||
## stdio.h
|
||||
|
||||
Provided API includes:
|
||||
- Stream open/close: `fopen`, `freopen`, `fclose`
|
||||
- Read/write: `fread`, `fwrite`, `fgets`, `fputs`, `getc`, `fputc`, `putchar`
|
||||
- Positioning: `fseek`, `ftell`, `filelength`
|
||||
- Formatting: `fprintf`, `vfprintf`, `snprintf`, `vsnprintf`, `sprintf`, `sscanf`
|
||||
- Stream state: `feof`, `ferror`, `clearerr`, `fflush`, `ungetc`
|
||||
- Temp/filesystem helpers: `remove`, `rename`, `tmpfile`, `tmpnam`
|
||||
|
||||
## unistd.h
|
||||
|
||||
Provided POSIX-like interfaces:
|
||||
- FD I/O: `read`, `write`, `close`, `lseek`, `isatty`
|
||||
- Filesystem: `unlink`
|
||||
- Exec family: `execv`, `execve`, `execvp`, `execl`, `execlp`, `execle`
|
||||
- Process wait: `waitpid`
|
||||
|
||||
Also defines:
|
||||
- `SEEK_SET`, `SEEK_CUR`, `SEEK_END`
|
||||
- `F_OK`, `X_OK`, `W_OK`, `R_OK`
|
||||
|
||||
## fcntl.h
|
||||
|
||||
Flags and fd control:
|
||||
- Open flags: `O_RDONLY`, `O_WRONLY`, `O_RDWR`, `O_CREAT`, `O_EXCL`, `O_TRUNC`, `O_APPEND`, `O_NONBLOCK`, `O_ACCMODE`
|
||||
- fcntl ops: `F_GETFL`, `F_SETFL`
|
||||
- FD flag: `FD_CLOEXEC` (declared)
|
||||
|
||||
Functions:
|
||||
- `open`
|
||||
- `fcntl`
|
||||
- `dup`
|
||||
- `dup2`
|
||||
- `pipe`
|
||||
|
||||
## input.h
|
||||
|
||||
Defines keyboard/control keycode constants used by apps that process
|
||||
|
||||
Current constants include:
|
||||
- Arrow keys: `KEY_UP`, `KEY_DOWN`, `KEY_LEFT`, `KEY_RIGHT`
|
||||
- Controls: `KEY_ENTER`, `KEY_BACKSPACE`, `KEY_ESCAPE`, `KEY_SPACE`, `KEY_ALT`, `KEY_CTRL_L`, `KEY_TAB`
|
||||
|
||||
## signal.h
|
||||
|
||||
Current signal surface:
|
||||
- Basic handler API: `signal`, `raise`, `kill`
|
||||
- POSIX-style API: `sigaction`, `sigprocmask`, `sigpending`
|
||||
- Types: `sighandler_t`, `sigset_t`, `struct sigaction`
|
||||
- Constants: `SIGINT`, `SIGTERM`, `SIGKILL`, `SIG_DFL`, `SIG_IGN`, `SIG_ERR`
|
||||
- Mask ops: `SIG_BLOCK`, `SIG_UNBLOCK`, `SIG_SETMASK`
|
||||
- Action flags: `SA_RESTART`, `SA_NODEFER`, `SA_RESETHAND`
|
||||
|
||||
## ctype.h
|
||||
|
||||
Character classification and case conversion:
|
||||
- `isdigit`, `isalpha`, `isalnum`, `isspace`
|
||||
- `isupper`, `islower`, `isxdigit`
|
||||
- `iscntrl`, `ispunct`, `isprint`, `isgraph`
|
||||
- `tolower`, `toupper`
|
||||
|
||||
## locale.h
|
||||
|
||||
Locale stubs and conventions:
|
||||
- `struct lconv`
|
||||
- `setlocale`
|
||||
- `localeconv`
|
||||
- `LC_ALL`
|
||||
|
||||
## limits.h
|
||||
|
||||
Integer and floating-point limit macros:
|
||||
- `CHAR_BIT`, `INT_MIN`, `INT_MAX`, `UINT_MAX`
|
||||
- `LONG_MIN`, `LONG_MAX`, `ULONG_MAX`
|
||||
- `LLONG_MIN`, `LLONG_MAX`, `ULLONG_MAX`
|
||||
- `DBL_MAX`
|
||||
|
||||
## setjmp.h
|
||||
|
||||
Non-local jump support:
|
||||
- `jmp_buf`
|
||||
- `setjmp`
|
||||
- `longjmp`
|
||||
|
||||
## time.h
|
||||
|
||||
Time/date APIs and types:
|
||||
- Types: `time_t`, `clock_t`, `struct tm`
|
||||
- Constants: `CLOCKS_PER_SEC`
|
||||
- Functions: `time`, `clock`, `localtime`, `gmtime`, `strftime`, `mktime`
|
||||
|
||||
## libui.h
|
||||
|
||||
Windowing and drawing API used by GUI apps:
|
||||
- Window/event: `ui_window_create`, `ui_get_event`, `ui_mark_dirty`, `ui_window_set_title`, `ui_window_set_resizable`
|
||||
- Drawing: `ui_draw_rect`, `ui_draw_rounded_rect_filled`, `ui_draw_string`, `ui_draw_string_bitmap`, `ui_draw_image`
|
||||
- Text metrics/scaled text: `ui_get_string_width`, `ui_get_font_height`, `ui_draw_string_scaled`, `ui_draw_string_scaled_sloped`, `ui_get_string_width_scaled`, `ui_get_font_height_scaled`
|
||||
- System UI helpers: `ui_get_screen_size`, `ui_set_font`
|
||||
|
||||
## sys/stat.h and sys/types.h
|
||||
|
||||
`sys/stat.h` provides:
|
||||
- `struct stat`
|
||||
- `stat`, `fstat`, `mkdir`
|
||||
- mode/type macros (`S_IFREG`, `S_IFDIR`, `S_ISREG`, `S_ISDIR`, permission bits)
|
||||
|
||||
Note:
|
||||
- `access` is declared in `stdlib.h` in this libc.
|
||||
|
||||
`sys/types.h` provides:
|
||||
- `ssize_t`, `off_t`, `mode_t`, `pid_t`, `uid_t`, `gid_t`
|
||||
|
||||
## sys/wait.h
|
||||
|
||||
- `waitpid`
|
||||
- `WNOHANG`
|
||||
- status macros: `WEXITSTATUS`, `WIFEXITED`, `WTERMSIG`, `WIFSIGNALED`
|
||||
|
||||
## errno.h
|
||||
|
||||
Defined errno values include:
|
||||
- Generic/input: `EINVAL`, `EDOM`, `ERANGE`, `E2BIG`
|
||||
- File/path: `ENOENT`, `EEXIST`, `EISDIR`, `ENOTDIR`, `EBADF`
|
||||
- Runtime/state: `ENOMEM`, `EACCES`, `EIO`, `EAGAIN`, `EINTR`, `ECHILD`, `EBUSY`, `EPIPE`, `ESPIPE`, `ENOSYS`, `ENOTSUP`
|
||||
|
||||
## Relationship to raw syscalls
|
||||
|
||||
- libc high-level I/O and process APIs are backed by wrappers in `src/userland/libc/syscall.c`.
|
||||
- Full syscall command IDs and multiplexer details are documented in `docs/appdev/syscalls.md`.
|
||||
|
||||
## Practical Guidance
|
||||
|
||||
- Prefer libc APIs (`open`, `read`, `write`, `waitpid`, `sigaction`) for portability inside BoredOS userland.
|
||||
- Use raw wrapper calls from `syscall.h` only for capabilities that do not yet have higher-level libc wrappers.
|
||||
- Avoid numeric `sys_system(...)` command literals in app code; use `SYSTEM_CMD_*` macros.
|
||||
|
|
@ -1,53 +1,214 @@
|
|||
<div align="center">
|
||||
<h1>Userland SDK Reference</h1>
|
||||
<p><em>Overview and entry point for BoredOS userland development.</em></p>
|
||||
<p><em>Comprehensive manual for custom libc and system calls in BoredOS.</em></p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
BoredOS provides a compact userland SDK for building `.elf` applications.
|
||||
This page is the high-level map; detailed API references now live in dedicated pages.
|
||||
BoredOS provides a custom `libc` implementation necessary for writing userland applications (`.elf` binaries). By avoiding a full-blown standard library like `glibc`, the OS ensures a minimal executable footprint tailored strictly to the existing kernel features.
|
||||
|
||||
## SDK Structure
|
||||
All headers are located in `src/userland/libc/` (standard functions) and `src/wm/` (UI and widgets).
|
||||
- `stdlib.h`: Memory, strings, and basic I/O.
|
||||
- `math.h`: Freestanding floating-point math library.
|
||||
- `libui.h`: Core window and drawing API.
|
||||
- `libwidget.h`: High-level UI components.
|
||||
|
||||
Primary headers are in `src/userland/libc/` and UI helpers are in `src/wm/`.
|
||||
## Standard Library (`stdlib.h` & `string.h`)
|
||||
|
||||
- `stdlib.h`, `string.h`, `stdio.h`, `unistd.h`: core libc surface
|
||||
- `syscall.h`: raw syscall wrappers and command constants
|
||||
- `libui.h`: window creation, drawing, and event polling
|
||||
- `libwidget.h`: higher-level reusable widgets
|
||||
- `math.h`: freestanding math helpers
|
||||
The standard library wrappers provide memory management, string manipulation, and basic IO formatting without needing direct syscalls.
|
||||
|
||||
## Detailed References
|
||||
### Memory Allocation
|
||||
* `void* malloc(size_t size);` - Allocate a block of memory on the heap.
|
||||
* `void free(void* ptr);` - Free a previously allocated memory block.
|
||||
* `void* calloc(size_t nmemb, size_t size);` - Allocate and zero-out a block of memory for an array.
|
||||
* `void* realloc(void* ptr, size_t size);` - Resize an existing memory block.
|
||||
|
||||
- [`libc Reference`](libc_reference.md): current libc headers and implemented APIs
|
||||
- [`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
|
||||
### Memory Manipulation (`string.h`)
|
||||
* `void* memset(void *s, int c, size_t n);` - Fill a block of memory with a specific byte.
|
||||
* `void* memcpy(void *dest, const void *src, size_t n);` - Copy memory from source to destination.
|
||||
* `void* memmove(void *dest, const void *src, size_t n);` - Safely copy overlapping memory blocks.
|
||||
* `int memcmp(const void *s1, const void *s2, size_t n);` - Compare two memory blocks.
|
||||
|
||||
## Typical Include Set
|
||||
### String Utilities
|
||||
* `size_t strlen(const char *s);` - Get the length of a string.
|
||||
* `int strcmp(const char *s1, const char *s2);` - Compare two strings lexicographically.
|
||||
* `char* strcpy(char *dest, const char *src);` - Copy a string to a destination buffer.
|
||||
* `char* strcat(char *dest, const char *src);` - Concatenate two strings.
|
||||
|
||||
### Conversion and Formatting
|
||||
* `int atoi(const char *nptr);` - String to integer conversion.
|
||||
* `void itoa(int n, char *buf);` - Integer to string conversion.
|
||||
|
||||
### Input / Output
|
||||
* `void puts(const char *s);` - Print a string followed by a newline to the standard output.
|
||||
* `void printf(const char *fmt, ...);` - Formatted print to standard output (supports `%d`, `%s`, `%x`, etc.).
|
||||
|
||||
### Process Control
|
||||
* `void exit(int status);` - Terminate the current process.
|
||||
* `void sleep(int ms);` - Pause execution for a specified number of milliseconds.
|
||||
|
||||
---
|
||||
|
||||
## Math Library (`math.h`)
|
||||
|
||||
BoredOS ships a freestanding floating-point math library in `libc/math.h`. It uses pure arithmetic — Taylor series, Newton-Raphson, and range-reduction — with no dependency on a host `libm` or hardware math intrinsics. It is automatically linked into every userland ELF.
|
||||
|
||||
```c
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <syscall.h>
|
||||
#include "math.h"
|
||||
```
|
||||
|
||||
For GUI apps:
|
||||
### Constants
|
||||
|
||||
| Constant | Value | Description |
|
||||
|---|---|---|
|
||||
| `M_PI` | 3.14159… | π |
|
||||
| `M_E` | 2.71828… | Euler's number |
|
||||
| `M_LN2` | 0.69315… | Natural log of 2 |
|
||||
| `M_SQRT2` | 1.41421… | √2 |
|
||||
| `HUGE_VAL` | ~+∞ | Overflow sentinel |
|
||||
|
||||
### Functions
|
||||
|
||||
#### Absolute value & remainder
|
||||
* `double fabs(double x);` — Absolute value.
|
||||
* `double fmod(double x, double y);` — Floating-point remainder. Returns `0` when `y == 0`.
|
||||
|
||||
#### Rounding
|
||||
* `double floor(double x);` — Largest integer ≤ x.
|
||||
* `double ceil(double x);` — Smallest integer ≥ x.
|
||||
|
||||
#### Trigonometry *(arguments in radians)*
|
||||
* `double sin(double x);` — Sine. Range-reduced to `[-π, π]` then computed via 8-term Taylor series.
|
||||
* `double cos(double x);` — Cosine. Computed via `sin(x + π/2)`.
|
||||
* `double tan(double x);` — Tangent. Returns sentinel `1e15` near poles.
|
||||
|
||||
#### Exponential & logarithm
|
||||
* `double sqrt(double x);` — Square root via Newton-Raphson (25 iterations). Returns `0` for `x ≤ 0`.
|
||||
* `double log(double x);` — Natural logarithm (ln). Returns `-1e30` for `x ≤ 0`.
|
||||
* `double log2(double x);` — Base-2 logarithm.
|
||||
* `double log10(double x);` — Base-10 logarithm.
|
||||
* `double exp(double x);` — e^x. Saturates to `1e300` for `x > 700`, `0` for `x < -700`.
|
||||
* `double pow(double base, double exponent);` — General power. Integer exponents use fast binary exponentiation; fractional exponents use `exp(e * log(b))`.
|
||||
|
||||
#### Hyperbolic
|
||||
* `double sinh(double x);` — Hyperbolic sine.
|
||||
* `double cosh(double x);` — Hyperbolic cosine.
|
||||
* `double tanh(double x);` — Hyperbolic tangent.
|
||||
|
||||
#### Utility
|
||||
* `double hypot(double x, double y);` — `sqrt(x² + y²)` without intermediate overflow.
|
||||
* `double fmin(double a, double b);` — Minimum of two values.
|
||||
* `double fmax(double a, double b);` — Maximum of two values.
|
||||
* `double fclamp(double x, double lo, double hi);` — Clamps `x` into `[lo, hi]`.
|
||||
|
||||
> [!NOTE]
|
||||
> The implementation file is named `libc/libmath.c` (not `libc/math.c`) to avoid a name collision with the `math` CLI calculator app in userland. The public header is still included as `#include "math.h"`.
|
||||
|
||||
For advanced operations, `syscall.h` provides direct wrappers into the kernel.
|
||||
|
||||
### Process & System Info
|
||||
* `void sys_exit(int status);` - Raw exit syscall.
|
||||
* `int sys_write(int fd, const char *buf, int len);` - Write to a file descriptor (usually fd 1 for stdout).
|
||||
* `void* sys_sbrk(int incr);` - Expand or shrink the process data segment (used internally by `malloc`).
|
||||
* `void sys_kill(int pid);` - Send a kill signal to a process.
|
||||
* `void sys_yield(void);` - Yield the remainder of the process's timeslice to the scheduler.
|
||||
* `int sys_system(int cmd, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4);` - Execute a privileged system command (e.g., reboot, shutdown, beep).
|
||||
* `int sys_get_os_info(os_info_t *info);` - Populate an `os_info_t` struct with version and environment details.
|
||||
* `uint64_t sys_get_shell_config(const char *key);` - Retrieve internal shell configuration values.
|
||||
* `void sys_set_text_color(uint32_t color);` - Set the raw CLI text output color.
|
||||
|
||||
### File System API (VFS)
|
||||
Interacting with files and directories using the Virtual File System.
|
||||
* `int sys_open(const char *path, const char *mode);` - Open a file, returning a file descriptor.
|
||||
* `int sys_read(int fd, void *buf, uint32_t len);` - Read from an open file.
|
||||
* `int sys_write_fs(int fd, const void *buf, uint32_t len);` - Write to an open file.
|
||||
* `void sys_close(int fd);` - Close an open file descriptor.
|
||||
* `int sys_seek(int fd, int offset, int whence);` - Reposition the file offset.
|
||||
* `uint32_t sys_tell(int fd);` - Get the current file offset.
|
||||
* `uint32_t sys_size(int fd);` - Get the total file size.
|
||||
* `int sys_delete(const char *path);` - Delete a file.
|
||||
* `int sys_mkdir(const char *path);` - Create a new directory.
|
||||
* `int sys_exists(const char *path);` - Check if a path exists on the filesystem.
|
||||
* `int sys_getcwd(char *buf, int size);` - Get the current working directory string.
|
||||
* `int sys_chdir(const char *path);` - Change the current working directory.
|
||||
* `int sys_list(const char *path, FAT32_FileInfo *entries, int max_entries);` - List directory contents into an array of `FAT32_FileInfo` structs.
|
||||
* `int sys_get_file_info(const char *path, FAT32_FileInfo *info);` - Retrieve metadata for a specific file.
|
||||
|
||||
### Networking Stack API
|
||||
BoredOS includes lwIP for hardware TCP/UDP networking.
|
||||
|
||||
#### Configuration and Status
|
||||
* `int sys_network_init(void);` - Initialize the network stack.
|
||||
* `int sys_network_is_initialized(void);` - Check stack status.
|
||||
* `int sys_network_dhcp_acquire(void);` - Request an IP configuration from a DHCP server.
|
||||
* `int sys_network_has_ip(void);` - Check if the system has a valid IP address.
|
||||
* `int sys_network_get_mac(net_mac_address_t *mac);` - Get the physical MAC address of the NIC.
|
||||
* `int sys_network_get_nic_name(char *name_out);` - Get the name of the active network interface card.
|
||||
* `int sys_network_get_ip(net_ipv4_address_t *ip);` - Get current local IPv4 address.
|
||||
* `int sys_network_set_ip(const net_ipv4_address_t *ip);` - Manually assign a static IP.
|
||||
* `int sys_network_get_gateway(net_ipv4_address_t *ip);` - Get the default gateway IP.
|
||||
* `int sys_network_get_dns(net_ipv4_address_t *ip);` - Get the primary DNS server IP.
|
||||
* `int sys_set_dns_server(const net_ipv4_address_t *ip);` - Set the primary DNS server.
|
||||
* `int sys_get_dns_server(net_ipv4_address_t *ip);` - Retrieve configured DNS server.
|
||||
* `int sys_network_get_stat(int stat_type);` - Get network statistics (packets in/out, drops, etc.).
|
||||
* `void sys_network_force_unlock(void);` - Force release of network stack locks (use with caution).
|
||||
|
||||
#### Communication
|
||||
* `int sys_icmp_ping(const net_ipv4_address_t *dest_ip);` - Send an ICMP echo request.
|
||||
* `int sys_udp_send(const net_ipv4_address_t *dest_ip, uint16_t dest_port, uint16_t src_port, const void *data, size_t data_len);` - Send a raw UDP datagram.
|
||||
* `int sys_dns_lookup(const char *name, net_ipv4_address_t *out_ip);` - Resolve a hostname to an IPv4 address.
|
||||
* `int sys_tcp_connect(const net_ipv4_address_t *ip, uint16_t port);` - Establish a TCP connection to a remote host.
|
||||
* `int sys_tcp_send(const void *data, size_t len);` - Send data over an active TCP socket.
|
||||
* `int sys_tcp_recv(void *buf, size_t max_len);` - Receive data from a TCP socket (blocking).
|
||||
* `int sys_tcp_recv_nb(void *buf, size_t max_len);` - Receive data from a TCP socket (non-blocking).
|
||||
* `int sys_tcp_close(void);` - Close the active TCP socket.
|
||||
|
||||
---
|
||||
|
||||
## Core Data Structures
|
||||
|
||||
### `os_info_t`
|
||||
Contains detailed build and version information about the OS.
|
||||
```c
|
||||
#include <libui.h>
|
||||
#include <libwidget.h>
|
||||
typedef struct {
|
||||
char os_name[64];
|
||||
char os_version[64];
|
||||
char os_codename[64];
|
||||
char kernel_name[64];
|
||||
char kernel_version[64];
|
||||
char build_date[64];
|
||||
char build_time[64];
|
||||
char build_arch[64];
|
||||
} os_info_t;
|
||||
```
|
||||
|
||||
## Build and Packaging
|
||||
|
||||
- Add app source under `src/userland/` (CLI, GUI, or games subfolder).
|
||||
- Ensure it is included in the userland build rules/targets.
|
||||
- Build from repo root with `make`.
|
||||
- Built binaries are copied into initrd under `/bin` by the top-level `Makefile`.
|
||||
### `FAT32_FileInfo`
|
||||
Represents a filesystem entry.
|
||||
```c
|
||||
typedef struct {
|
||||
char name[256];
|
||||
uint32_t size;
|
||||
uint8_t is_directory;
|
||||
uint32_t start_cluster;
|
||||
uint16_t write_date;
|
||||
uint16_t write_time;
|
||||
} FAT32_FileInfo;
|
||||
```
|
||||
|
||||
### `ProcessInfo`
|
||||
Provides status information for an active process.
|
||||
```c
|
||||
typedef struct {
|
||||
uint32_t pid;
|
||||
char name[64];
|
||||
uint64_t ticks;
|
||||
size_t used_memory;
|
||||
} ProcessInfo;
|
||||
```
|
||||
|
||||
### IP / MAC Addresses
|
||||
Wrappers for raw byte arrays.
|
||||
```c
|
||||
typedef struct { uint8_t bytes[6]; } net_mac_address_t;
|
||||
typedef struct { uint8_t bytes[4]; } net_ipv4_address_t;
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,200 +0,0 @@
|
|||
# Syscall Reference
|
||||
|
||||
This page documents the current syscall surface in BoredOS as implemented in:
|
||||
- `src/sys/syscall.h` (kernel command IDs)
|
||||
- `src/userland/libc/syscall.h` (userland wrappers)
|
||||
|
||||
Use libc wrappers when possible instead of calling raw syscall numbers directly.
|
||||
|
||||
## Top-Level Syscall Numbers
|
||||
|
||||
| Number | Name | Purpose |
|
||||
|---|---|---|
|
||||
| 0 | `SYS_EXIT` (userland header) | Terminate current process |
|
||||
| 1 | `SYS_WRITE` | Write to stdout/tty path |
|
||||
| 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 |
|
||||
|
||||
Notes:
|
||||
- Some numbers differ between kernel and userland headers for historical reasons. For app code, rely on wrapper functions in `src/userland/libc/syscall.c`.
|
||||
- `SYS_GUI`, `SYS_FS`, and `SYS_SYSTEM` are command multiplexers.
|
||||
|
||||
## FS Command IDs (`SYS_FS`)
|
||||
|
||||
| ID | Macro | Meaning |
|
||||
|---|---|---|
|
||||
| 1 | `FS_CMD_OPEN` | Open file |
|
||||
| 2 | `FS_CMD_READ` | Read from fd |
|
||||
| 3 | `FS_CMD_WRITE` | Write to fd |
|
||||
| 4 | `FS_CMD_CLOSE` | Close fd |
|
||||
| 5 | `FS_CMD_SEEK` | Seek in file |
|
||||
| 6 | `FS_CMD_TELL` | Current offset |
|
||||
| 7 | `FS_CMD_LIST` | Directory listing |
|
||||
| 8 | `FS_CMD_DELETE` | Delete file |
|
||||
| 9 | `FS_CMD_SIZE` | File size |
|
||||
| 10 | `FS_CMD_MKDIR` | Create directory |
|
||||
| 11 | `FS_CMD_EXISTS` | Path exists check |
|
||||
| 12 | `FS_CMD_GETCWD` | Get cwd |
|
||||
| 13 | `FS_CMD_CHDIR` | Change cwd |
|
||||
| 14 | `FS_CMD_GET_INFO` | File metadata |
|
||||
| 15 | `FS_CMD_DUP` | `dup` fd |
|
||||
| 16 | `FS_CMD_DUP2` | `dup2` fd |
|
||||
| 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
|
||||
|
||||
| ID | Macro | Meaning |
|
||||
|---|---|---|
|
||||
| 1 | `SYSTEM_CMD_SET_BG_COLOR` | Set desktop background color |
|
||||
| 2 | `SYSTEM_CMD_SET_BG_PATTERN` | Set desktop background pattern |
|
||||
| 3 | `SYSTEM_CMD_SET_WALLPAPER` | Legacy wallpaper command slot |
|
||||
| 4 | `SYSTEM_CMD_SET_DESKTOP_PROP` | Set desktop behavior property |
|
||||
| 5 | `SYSTEM_CMD_SET_MOUSE_SPEED` | Set mouse speed |
|
||||
| 7 | `SYSTEM_CMD_GET_DESKTOP_PROP` | Get desktop property |
|
||||
| 8 | `SYSTEM_CMD_GET_MOUSE_SPEED` | Get mouse speed |
|
||||
| 9 | `SYSTEM_CMD_GET_WALLPAPER_THUMB` | Legacy wallpaper thumb slot |
|
||||
| 10 | `SYSTEM_CMD_CLEAR_SCREEN` | Clear text console |
|
||||
| 29 | `SYSTEM_CMD_SET_TEXT_COLOR` | Set console text color |
|
||||
| 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
|
||||
|
||||
| ID | Macro | Meaning |
|
||||
|---|---|---|
|
||||
| 11 | `SYSTEM_CMD_RTC_GET` | Read RTC datetime |
|
||||
| 12 | `SYSTEM_CMD_REBOOT` | Reboot machine |
|
||||
| 13 | `SYSTEM_CMD_SHUTDOWN` | Power off machine |
|
||||
| 14 | `SYSTEM_CMD_BEEP` | PC speaker beep |
|
||||
| 15 | `SYSTEM_CMD_GET_MEM_INFO` | Return total/used memory |
|
||||
| 16 | `SYSTEM_CMD_GET_TICKS` | Return scheduler/WM tick count |
|
||||
| 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) |
|
||||
|
||||
### Network
|
||||
|
||||
| ID | Macro | Meaning |
|
||||
|---|---|---|
|
||||
| 6 | `SYSTEM_CMD_NETWORK_INIT` | Init networking |
|
||||
| 17 | `SYSTEM_CMD_PCI_LIST` | PCI device list access |
|
||||
| 18 | `SYSTEM_CMD_NETWORK_DHCP` | DHCP acquire |
|
||||
| 19 | `SYSTEM_CMD_NETWORK_GET_MAC` | Read NIC MAC |
|
||||
| 20 | `SYSTEM_CMD_NETWORK_GET_IP` | Read IPv4 |
|
||||
| 21 | `SYSTEM_CMD_NETWORK_SET_IP` | Set static IPv4 |
|
||||
| 22 | `SYSTEM_CMD_UDP_SEND` | Send UDP packet |
|
||||
| 23 | `SYSTEM_CMD_NETWORK_GET_STATS` | Network stats |
|
||||
| 24 | `SYSTEM_CMD_NETWORK_GET_GATEWAY` | Read gateway |
|
||||
| 25 | `SYSTEM_CMD_NETWORK_GET_DNS` | Read DNS server |
|
||||
| 26 | `SYSTEM_CMD_ICMP_PING` | ICMP ping |
|
||||
| 27 | `SYSTEM_CMD_NETWORK_IS_INIT` | Network initialized flag |
|
||||
| 30 | `SYSTEM_CMD_NETWORK_HAS_IP` | Has IPv4 address flag |
|
||||
| 33 | `SYSTEM_CMD_TCP_CONNECT` | TCP connect |
|
||||
| 34 | `SYSTEM_CMD_TCP_SEND` | TCP send |
|
||||
| 35 | `SYSTEM_CMD_TCP_RECV` | TCP recv (blocking) |
|
||||
| 36 | `SYSTEM_CMD_TCP_CLOSE` | TCP close |
|
||||
| 37 | `SYSTEM_CMD_DNS_LOOKUP` | DNS lookup |
|
||||
| 38 | `SYSTEM_CMD_SET_DNS` | Set DNS server |
|
||||
| 39 | `SYSTEM_CMD_NET_UNLOCK` | Force net lock release |
|
||||
| 42 | `SYSTEM_CMD_TCP_RECV_NB` | TCP recv (non-blocking) |
|
||||
| 48 | `SYSTEM_CMD_NETWORK_GET_NIC_NAME` | NIC name |
|
||||
|
||||
### Process, tty, signals
|
||||
|
||||
| ID | Macro | Meaning |
|
||||
|---|---|---|
|
||||
| 50 | `SYSTEM_CMD_PARALLEL_RUN` | Dispatch parallel job |
|
||||
| 60 | `SYSTEM_CMD_TTY_CREATE` | Create tty |
|
||||
| 61 | `SYSTEM_CMD_TTY_READ_OUT` | Read tty output buffer |
|
||||
| 62 | `SYSTEM_CMD_TTY_WRITE_IN` | Write tty input buffer |
|
||||
| 63 | `SYSTEM_CMD_TTY_READ_IN` | Read input for current tty |
|
||||
| 64 | `SYSTEM_CMD_SPAWN` | Spawn process |
|
||||
| 65 | `SYSTEM_CMD_TTY_SET_FG` | Set tty foreground PID |
|
||||
| 66 | `SYSTEM_CMD_TTY_GET_FG` | Get tty foreground PID |
|
||||
| 67 | `SYSTEM_CMD_TTY_KILL_FG` | Kill tty foreground PID |
|
||||
| 68 | `SYSTEM_CMD_TTY_KILL_ALL` | Kill tty process group |
|
||||
| 69 | `SYSTEM_CMD_TTY_DESTROY` | Destroy tty |
|
||||
| 70 | `SYSTEM_CMD_EXEC` | Exec replace current process |
|
||||
| 71 | `SYSTEM_CMD_WAITPID` | Wait/reap child |
|
||||
| 72 | `SYSTEM_CMD_KILL_SIGNAL` | Send signal |
|
||||
| 73 | `SYSTEM_CMD_SIGACTION` | Set/get handler |
|
||||
| 74 | `SYSTEM_CMD_SIGPROCMASK` | Signal mask ops |
|
||||
| 75 | `SYSTEM_CMD_SIGPENDING` | Get pending signals |
|
||||
|
||||
### ELF app metadata
|
||||
|
||||
| ID | Macro | Meaning |
|
||||
|---|---|---|
|
||||
| 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`
|
||||
- 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`
|
||||
- ELF metadata: `sys_get_elf_metadata`, `sys_get_elf_primary_image` — see [`elf_metadata.md`](elf_metadata.md) for full usage
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Do not hardcode numeric command IDs in app code.
|
||||
- Prefer high-level libc calls (`open`, `read`, `waitpid`, `sigaction`) where available.
|
||||
- Use `syscall.h` macros when a raw `sys_system` call is still needed.
|
||||
|
|
@ -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
|
||||
```
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -34,7 +34,7 @@ BoredOS uses **Limine** as its primary bootloader.
|
|||
5. **Driver Initialization**: PCI buses are scanned, finding the network card or disk controllers. The filesystem is mounted.
|
||||
6. **Window Manager**: The UI is drawn on top of the Limine-provided framebuffer.
|
||||
|
||||
## Multi-Core & Scheduling
|
||||
## 🧵 Multi-Core & Scheduling
|
||||
|
||||
BoredOS utilizes Symmetric Multi-Processing (SMP) to distribute workloads across all available CPU cores.
|
||||
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
# PCI Bus Subsystem
|
||||
|
||||
The Peripheral Component Interconnect (PCI) subsystem in BoredOS is responsible for discovering, enumerating, and configuring hardware devices connected to the motherboard. It provides the foundation for the OS to load specific device drivers (like Network Interface Cards or AHCI controllers).
|
||||
|
||||
## 1. Configuration Space Access
|
||||
|
||||
BoredOS interacts with the PCI bus via the legacy x86 I/O ports:
|
||||
- **`0xCF8`**: Address Port (used to select a specific bus, device, function, and register offset).
|
||||
- **`0xCFC`**: Data Port (used to read or write the 32-bit value at the selected address).
|
||||
|
||||
These are abstracted in `src/dev/pci.c` by the `pci_read_config()` and `pci_write_config()` functions. By writing a formatted 32-bit address to `0xCF8`, the CPU signals the PCI bridge to route the subsequent data read/write on `0xCFC` to the correct hardware device.
|
||||
|
||||
## 2. Device Enumeration
|
||||
|
||||
During boot, BoredOS recursively scans the PCI buses. The PCI bus topology is hierarchical:
|
||||
- Up to **256 buses**.
|
||||
- Each bus has up to **32 devices**.
|
||||
- Each device has up to **8 functions** (for multi-function devices).
|
||||
|
||||
The enumeration process (`pci_enumerate_devices`):
|
||||
1. Iterates through Bus 0 to 255.
|
||||
2. For each bus, iterates through Devices 0 to 31.
|
||||
3. For each device, it reads the `Vendor ID` at offset 0. If the value is `0xFFFF`, no device is present at that slot.
|
||||
4. If a valid Vendor ID is found, it populates a `pci_device_t` structure containing the:
|
||||
- `Vendor ID` and `Device ID` (used to uniquely identify the hardware model).
|
||||
- `Class Code`, `Subclass`, and `Prog IF` (used to identify the generic type of the device, e.g., Network Controller, Mass Storage Controller).
|
||||
|
||||
## 3. Base Address Registers (BARs)
|
||||
|
||||
PCI devices expose memory-mapped I/O (MMIO) regions or I/O port ranges via Base Address Registers (BARs).
|
||||
BoredOS provides the `pci_get_bar(dev, bar_num)` function to extract these base addresses.
|
||||
|
||||
Drivers use BARs to talk directly to the hardware. For example:
|
||||
- The AHCI driver reads BAR5 to find the base address of the AHCI memory registers (ABAR).
|
||||
- The E1000 driver uses a BAR to map the NIC's control registers into the kernel's virtual memory space.
|
||||
|
||||
## 4. Hardware Configuration
|
||||
|
||||
Once a device is found, drivers can call helper functions to enable specific PCI features:
|
||||
- **`pci_enable_bus_mastering(dev)`**: Sets the Bus Master bit in the PCI Command Register. This is critical for drivers that use DMA (Direct Memory Access), allowing the hardware to read/write system RAM independently of the CPU (used heavily by AHCI and Network drivers).
|
||||
- **`pci_enable_mmio(dev)`**: Sets the Memory Space Enable bit, allowing the CPU to access the device's MMIO regions.
|
||||
|
|
@ -1,273 +0,0 @@
|
|||
# Input Subsystem
|
||||
|
||||
## Overview
|
||||
The input subsystem is responsible for handling user input, primarily from the keyboard.
|
||||
|
||||
It provides a structured pipeline that transforms low-level hardware signals into usable data for the kernel and higher-level components. This subsystem abstracts hardware-specific behavior and exposes a consistent interface to the rest of the operating system.
|
||||
|
||||
---
|
||||
|
||||
## Scope
|
||||
The `/input` directory focuses on keyboard input. It includes:
|
||||
|
||||
- A keyboard driver responsible for handling hardware events
|
||||
- A keycode layer used as an intermediate representation
|
||||
- A keymap system that translates keycodes into characters
|
||||
|
||||
---
|
||||
|
||||
## Design Principles
|
||||
|
||||
- **Hardware abstraction**
|
||||
Hardware-specific logic is isolated from higher-level components.
|
||||
|
||||
- **Simplicity**
|
||||
The input path is kept minimal and efficient, especially in interrupt context.
|
||||
|
||||
- **Modularity**
|
||||
Each stage of input processing is handled by a dedicated component.
|
||||
|
||||
- **Extensibility**
|
||||
The system is designed to support additional input devices and layouts in the future.
|
||||
|
||||
---
|
||||
|
||||
## Directory Structure
|
||||
```
|
||||
input/
|
||||
├── keyboard.c
|
||||
├── keyboard.h
|
||||
├── keycodes.h
|
||||
├── keymap.c
|
||||
├── keymap.h
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Input Processing Model
|
||||
|
||||
Keyboard input is processed in three distinct stages:
|
||||
|
||||
1. Raw scancodes are received from the hardware
|
||||
2. Scancodes are converted into keycodes
|
||||
3. Keycodes are translated into characters or control signals
|
||||
|
||||
Each stage is handled independently to ensure clarity and maintainability.
|
||||
|
||||
---
|
||||
|
||||
## Components
|
||||
|
||||
### Keyboard Driver
|
||||
|
||||
#### Overview
|
||||
The keyboard driver interfaces directly with the keyboard hardware. It handles interrupts and processes raw input data from the controller.
|
||||
|
||||
#### Responsibilities
|
||||
|
||||
- Handle keyboard interrupts
|
||||
- Read scancodes from the PS/2 controller
|
||||
- Convert scancodes into keycodes
|
||||
- Forward processed data to higher layers
|
||||
|
||||
#### Behavior
|
||||
|
||||
The driver operates in an interrupt-driven context. When a key event occurs, the hardware triggers an interrupt. The driver reads the corresponding scancode and processes it immediately.
|
||||
|
||||
Because this code runs at a low level, it must be fast, predictable, and minimal.
|
||||
|
||||
#### Integration
|
||||
|
||||
The keyboard driver depends on:
|
||||
|
||||
- The PS/2 controller driver for hardware communication
|
||||
- The interrupt subsystem for event handling
|
||||
|
||||
It provides output to:
|
||||
|
||||
- The keycode system
|
||||
- The keymap system
|
||||
|
||||
#### Constraints
|
||||
|
||||
- Must not block execution
|
||||
- Must minimize processing time per interrupt
|
||||
- Must correctly handle key press and key release events
|
||||
|
||||
---
|
||||
|
||||
### Keycodes
|
||||
|
||||
#### Overview
|
||||
Keycodes define a hardware-independent representation of keyboard keys.
|
||||
|
||||
They serve as an abstraction layer between raw scancodes and higher-level logic.
|
||||
|
||||
#### Purpose
|
||||
|
||||
The keycode system standardizes keyboard input by mapping all physical key events to a consistent set of identifiers.
|
||||
|
||||
This allows the system to:
|
||||
|
||||
- Remain independent from specific hardware implementations
|
||||
- Simplify input handling logic
|
||||
- Support multiple layouts and configurations
|
||||
|
||||
#### Design
|
||||
|
||||
Each key is represented by a unique constant, such as:
|
||||
|
||||
- KEY_A
|
||||
- KEY_ENTER
|
||||
- KEY_SHIFT
|
||||
|
||||
#### Role in the System
|
||||
|
||||
Keycodes act as the intermediate layer between:
|
||||
|
||||
- Hardware-level scancodes
|
||||
- Character-level or command-level input
|
||||
|
||||
#### Usage
|
||||
|
||||
- Generated by the keyboard driver
|
||||
- Consumed by the keymap system
|
||||
|
||||
#### Extensibility
|
||||
|
||||
The keycode system can be extended to support:
|
||||
|
||||
- Additional keys (function keys, multimedia keys)
|
||||
- Non-standard input devices
|
||||
- Custom mappings
|
||||
|
||||
---
|
||||
|
||||
### Keymap
|
||||
|
||||
#### Overview
|
||||
The keymap system translates keycodes into characters or control signals.
|
||||
|
||||
It defines how physical key presses are interpreted based on layout and modifier state.
|
||||
|
||||
#### Responsibilities
|
||||
|
||||
- Convert keycodes into ASCII or equivalent representations
|
||||
- Apply modifier logic such as Shift and Control
|
||||
- Provide consistent character output
|
||||
|
||||
#### Behavior
|
||||
|
||||
The keymap takes a keycode as input and produces an output depending on:
|
||||
|
||||
- The current keyboard layout
|
||||
- Active modifier keys
|
||||
|
||||
The same keycode may produce different results depending on modifier state.
|
||||
|
||||
|
||||
#### Integration
|
||||
|
||||
- Receives keycodes from the keyboard driver
|
||||
- Outputs characters to the kernel or userland
|
||||
|
||||
---
|
||||
|
||||
## Control Signals
|
||||
|
||||
In addition to character generation, the input subsystem produces **control signals** representing non-printable keys and command-oriented input.
|
||||
|
||||
These signals are derived from keycodes that do not map directly to ASCII characters.
|
||||
|
||||
---
|
||||
|
||||
### Definition
|
||||
|
||||
A control signal is an abstract representation of a key event used to trigger system-level behavior rather than text output.
|
||||
|
||||
Typical control signals include:
|
||||
|
||||
- Enter
|
||||
- Backspace
|
||||
- Escape
|
||||
- Tab
|
||||
- Arrow keys
|
||||
- Function keys
|
||||
|
||||
---
|
||||
|
||||
### Encoding
|
||||
|
||||
Control signals may be represented in different ways depending on the layer:
|
||||
|
||||
#### ASCII Control Characters (when applicable)
|
||||
|
||||
Some keys map to standard ASCII control codes:
|
||||
|
||||
- `ENTER` → `0x0A` (Line Feed) or `0x0D` (Carriage Return)
|
||||
- `BACKSPACE` → `0x08`
|
||||
- `TAB` → `0x09`
|
||||
- `ESC` → `0x1B`
|
||||
|
||||
These values are part of the ASCII control range (`0x00`–`0x1F`).
|
||||
|
||||
---
|
||||
|
||||
#### Non-ASCII Keys
|
||||
|
||||
Keys that do not belong to the ASCII set are typically handled as **extended keycodes** or **internal constants**:
|
||||
|
||||
Examples:
|
||||
|
||||
- Arrow keys
|
||||
- Insert / Delete
|
||||
- Home / End
|
||||
- Function keys (F1–F12)
|
||||
|
||||
------
|
||||
## Non-ASCII Characters
|
||||
|
||||
Non-ASCII characters include any character outside the standard 7-bit ASCII range (`0x00`–`0x7F`).
|
||||
|
||||
Examples:
|
||||
|
||||
- Accented characters: `é`, `à`, `ç`
|
||||
- Symbols: `€`, `£`
|
||||
- Unicode characters from non-Latin scripts
|
||||
|
||||
---
|
||||
|
||||
### Encoding Considerations
|
||||
|
||||
The current system typically assumes ASCII output. However, supporting non-ASCII characters requires:
|
||||
|
||||
- A wider character encoding (e.g. UTF-8)
|
||||
- Extended keymaps capable of mapping key combinations to multi-byte sequences
|
||||
|
||||
Example:
|
||||
|
||||
- `'é'` in UTF-8 → `0xC3 0xA9`
|
||||
|
||||
---
|
||||
|
||||
### Modifier and Layout Impact
|
||||
|
||||
Non-ASCII characters are often produced through:
|
||||
|
||||
- Keyboard layout differences (AZERTY vs QWERTY)
|
||||
- Modifier combinations (Shift, AltGr)
|
||||
|
||||
Example:
|
||||
|
||||
- `AltGr + E` → `'€'` (depending on layout)
|
||||
- `KEY_E` → `'e'`
|
||||
- `KEY_E + SHIFT` → `'E'`
|
||||
|
||||
---
|
||||
|
||||
### Usage
|
||||
|
||||
- Control signals are used for command handling and system interaction
|
||||
- Non-ASCII characters are used for text input and require proper encoding support
|
||||
|
||||
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
# Kernel Memory Manager Architecture
|
||||
|
||||
BoredOS utilizes a highly optimized, two-tier kernel memory manager (`memory_manager.c`) designed for performance, concurrency safety, and long-term stability. The API provides the standard POSIX-like `kmalloc`, `krealloc`, and `kfree` functions used universally throughout the kernel.
|
||||
|
||||
## 1. High-Level Design
|
||||
|
||||
The memory manager delegates allocation requests to one of two internal sub-systems based on the requested size and alignment parameters:
|
||||
|
||||
1. **Slab Allocator**: Optimally handles all small allocations (<= 512 bytes) with an alignment restriction of <= 8 bytes.
|
||||
2. **Block-List Allocator**: Handles large allocations (> 512 bytes) and any request requiring aggressive alignment (such as page-aligned buffers).
|
||||
|
||||
All operations within the memory manager are secured by a global interrupt-safe spinlock (`mm_lock`), rendering the memory subsystem completely atomic and safe to use from any CPU or interrupt handler without triggering a race condition.
|
||||
|
||||
---
|
||||
|
||||
## 2. The Slab Allocator (Small Objects)
|
||||
|
||||
For frequent, small data structures, the overhead of standard heap fragmentation is unacceptable. The Slab Allocator addresses this by pre-allocating blocks of identical size.
|
||||
|
||||
### Classes & Geometry
|
||||
There are 7 active slab classes defined by `slab_sizes[]`: `8, 16, 32, 64, 128, 256, 512` bytes.
|
||||
Whenever an allocation requests a size within these bounds, it is rounded up to the nearest valid class.
|
||||
|
||||
Each active slab page maps precisely to one standard system `PAGE_SIZE` (4096 bytes).
|
||||
- The page header (`SlabPage`) is embedded at the very top (byte offset 0).
|
||||
- The rest of the page is sliced seamlessly into perfectly sized object slots.
|
||||
|
||||
### Intrusive LIFO Free-List
|
||||
To minimize metadata overhead, the Slab Allocator uses an *intrusive* LIFO (Last-In-First-Out) free-list to track empty object slots. The first 8 bytes of any unallocated slot act as a `next` pointer to the next free slot in that page. When a pointer is freed, it is immediately pushed back to the head of this list, making it the most likely candidate for the *next* allocation. This maximizes CPU cache locality.
|
||||
|
||||
### Guardrails & Safety
|
||||
The Slab Allocator implements highly restrictive checks to guard against fatal kernel errors:
|
||||
- **Canonical Address Checks:** The allocator verifies that the freelist head remains in the higher-half address space (`0xFFFF000000000000` or above), proactively detecting structural corruption.
|
||||
- **Strict Pointer Admittance:** Before freeing a pointer to a slab, the allocator validates a dual magic-number footprint, limits the pointer's bounds to verify it belongs geographically to the page, and executes a linked-list walk.
|
||||
- **Double-Free Detection:** When a slab is freed, the allocator walks the internal free-list. If the freed pointer is already in the free-list, the allocator intercepts the double-free attempt before the internal state can be damaged.
|
||||
|
||||
---
|
||||
|
||||
## 3. The Block-List Allocator (Large Objects)
|
||||
|
||||
If an allocation is larger than 512 bytes, the memory manager falls back to the Block-List allocator.
|
||||
|
||||
### First-Fit Search & Splitting
|
||||
The Block Allocator tracks all system memory chunks using an array of `MemBlock` structs ordered dynamically by address.
|
||||
- It iterates through the array utilizing a **First-Fit Search**. The first contiguous, unallocated block that satisfies the `size` requirement is immediately claimed.
|
||||
- If the requested alignment dictates it, the allocator splits the parent block. It yields up to three new fragments: `[head padding | exact requested allocation | tail remainder]`.
|
||||
|
||||
### Bootstrapping & Heap Migration
|
||||
To avoid infinite recursion when allocating memory to track new memory blocks, the block list is initially statically allocated in a `.bss` array (`_bootstrap_blocks`) with an initial capacity of 64 `MemBlocks`.
|
||||
|
||||
When the system runs out of capacity to track new blocks, the block list calls `grow_block_list()`, which reallocates the array space into the primary heap. It utilizes a `growing` lock-flag to prevent recursive faults while performing this relocation.
|
||||
|
||||
### Coalescing
|
||||
Upon `kfree()`, the chunk is marked as unallocated. The allocator inspects its immediate left and right address neighbors. If they are also free, the adjacent blocks are merged (coalesced) into one continuous block to reduce overall memory fragmentation.
|
||||
|
||||
---
|
||||
|
||||
## 4. API Caveats & Contracts
|
||||
|
||||
### Alignment guarantees
|
||||
`kmalloc` inherently returns a naturally aligned pointer (minimum 8-byte boundary) sufficient to satisfy scalar types natively on x86-64 without fetching faults. `kmalloc_aligned` can be utilized for strict power-of-two alignment boundaries (e.g., page directories that demand 4096 alignment).
|
||||
|
||||
### Resizing limits
|
||||
`krealloc` accepts an existing allocated pointer and transforms it to meet a new size requirement. To prevent memory starvation over long lifetimes, `krealloc` employs aggressive optimization strategies depending on the allocator layer:
|
||||
- **Block Allocator (Shrink-in-Place):** Large blocks actively support shrink-in-place maneuvers. If the reduction saves at least 32 bytes, the unused trailing memory is sliced off, injected into the free pool, and physically coalesced with adjacent free neighbors. The original pointer remains identical.
|
||||
- **Slab Allocator (Down-Migration):** Since slab slots have rigid geometries, true shrink-in-place is impossible. However, if a pointer shrinks enough to cleanly fall into a smaller slab class, `krealloc` triggers an internal copy-migration. This instantly relinquishes the highly-contested larger slab slot back to the system.
|
||||
|
||||
---
|
||||
|
||||
## 5. Telemetry & Metrics
|
||||
The `memory_get_stats()` API exports complete transparency over the current topological state of the system memory map. It calculates variables such as peak memory, overall fragmentation % (the ratio of stranded memory outside the largest single block), and explicit slab efficiency counters.
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
# Networking Stack
|
||||
|
||||
BoredOS features a robust networking stack capable of handling Ethernet, IPv4, TCP, UDP, ICMP, DHCP, and DNS. The stack is built on top of the **lwIP (Lightweight IP)** library, which is integrated with a custom hardware driver layer.
|
||||
|
||||
## 1. Architecture Overview
|
||||
|
||||
The network stack is split into three main layers:
|
||||
1. **Hardware/Driver Layer (`src/net/nic/`)**: Communicates with physical and virtual Network Interface Cards (NICs), handling raw Ethernet frame transmission and reception. Supported drivers include the Intel E1000 (`e1000.c`), Realtek RTL8139 (`rtl8139.c`), Realtek RTL8111 (`rtl8111.c`), and VirtIO network devices (`virtio_net.c`). A generic interface is provided via `nic.c`.
|
||||
2. **Protocol Layer (lwIP)**: Processes Ethernet frames, handles ARP resolution, routes IPv4 packets, and manages TCP state machines.
|
||||
3. **OS Interface Layer (`src/net/network.c`)**: Wraps the asynchronous lwIP API into a synchronous, easy-to-use API for BoredOS applications and the kernel.
|
||||
|
||||
## 2. Initialization & Polling
|
||||
|
||||
### `network_init()`
|
||||
When the kernel boots, it initializes the network subsystem by:
|
||||
1. Probing the PCI bus for supported NICs (e.g., the Intel E1000).
|
||||
2. Initializing the lwIP core (`lwip_init()`) and DNS subsystem.
|
||||
3. Binding the hardware NIC to lwIP using `netif_add`.
|
||||
4. Automatically attempting to acquire an IP address via DHCP (`network_dhcp_acquire()`).
|
||||
|
||||
### The Polling Mechanism (`network_process_frames`)
|
||||
Unlike some operating systems that process network packets entirely inside hardware interrupt handlers, BoredOS uses a **polled approach** to avoid re-entrancy issues in the TCP/IP stack.
|
||||
|
||||
The `network_process_frames()` function is called periodically (e.g., from the Window Manager loop or during blocking network calls). It calls:
|
||||
- `nic_netif_poll()`: Pulls raw packets from the NIC ring buffer and feeds them to lwIP (`ethernet_input`).
|
||||
- `sys_check_timeouts()`: Fires lwIP internal timers for TCP retransmissions, ARP cache expiration, and DHCP lease renewals.
|
||||
|
||||
A `network_processing` flag acts as a lightweight spinlock to prevent nested execution of the network poll loop.
|
||||
|
||||
## 3. TCP Implementation & Application API
|
||||
|
||||
While lwIP provides a callback-based raw API, BoredOS wraps this into a sequential API for userland applications.
|
||||
|
||||
Currently, the OS supports **one active TCP connection globally across the entire system**. The connection state is managed via a global Protocol Control Block (`current_tcp_pcb`). To prevent unauthorized cleanup, the OS tracks which process initiated the connection (`tcp_owner_pid`). If a new process attempts to connect while a connection is active, the existing connection is forcefully aborted.
|
||||
|
||||
### `network_tcp_connect(ip, port)`
|
||||
1. Allocates a new Protocol Control Block (`tcp_new()`).
|
||||
2. Registers callbacks for receive (`tcp_recv_callback`), error, and connection success.
|
||||
3. Blocks (while polling the network) until the connection succeeds or times out after 15 seconds.
|
||||
|
||||
### `network_tcp_recv(buf, max_len)`
|
||||
When packets arrive, `tcp_recv_callback` chains them into a `tcp_recv_queue` (`struct pbuf`).
|
||||
The `network_tcp_recv` function blocks until data is available in this queue, then copies it into the application's buffer and frees the `pbuf`. A non-blocking variant (`network_tcp_recv_nb`) is also provided.
|
||||
|
||||
### Process Cleanup (`network_cleanup`)
|
||||
If an application crashes or exits without closing its socket, the kernel's process manager calls `network_cleanup()`. This checks if the exiting process owns the current TCP PCB (`tcp_owner_pid`) and forcibly aborts the connection to prevent resource leaks.
|
||||
|
||||
## 4. Helper Protocols
|
||||
|
||||
- **DHCP:** Managed entirely by lwIP. BoredOS simply waits up to 10 seconds during boot for a lease.
|
||||
- **DNS (`network_dns_lookup`):** Uses lwIP's `dns_gethostbyname`. It blocks and polls until the callback is triggered with the resolved IP address.
|
||||
- **ICMP (Ping):** The kernel provides a `network_icmp_single_ping` function using an lwIP raw socket (`raw_pcb`) to construct, checksum, and transmit an ICMP Echo Request, blocking until a reply is received to calculate the Round-Trip Time (RTT).
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
# AHCI Storage Driver
|
||||
|
||||
BoredOS implements an Advanced Host Controller Interface (AHCI) driver to interface with Serial ATA (SATA) mass storage devices. The driver is located in `src/dev/ahci.c` and allows the OS to read and write sectors directly to physical hard drives or solid-state drives using DMA (Direct Memory Access).
|
||||
|
||||
## 1. Discovery and Initialization
|
||||
|
||||
The AHCI initialization process (`ahci_init`) starts by querying the PCI subsystem:
|
||||
1. It searches for a PCI device with Class `0x01` (Mass Storage) and Subclass `0x06` (SATA).
|
||||
2. It calls `pci_enable_bus_mastering` and `pci_enable_mmio` to ensure the controller can perform DMA and its registers are accessible.
|
||||
3. It retrieves the **ABAR** (AHCI Base Address Register) from PCI BAR5.
|
||||
4. The ABAR points to the `HBA_MEM` structure (Host Bus Adapter Memory Registers). The kernel iterates through the `pi` (Ports Implemented) bitmask to find active SATA ports.
|
||||
|
||||
## 2. Port Configuration
|
||||
|
||||
For every active SATA port found, the driver must allocate memory structures that the hardware will use to process commands:
|
||||
1. **Command List Base (`clb`)**: A 1KB memory region holding 32 Command Headers.
|
||||
2. **FIS Base (`fb`)**: A 256-byte memory region where the HBA writes incoming Frame Information Structures (FIS) from the drive.
|
||||
3. **Command Tables (`ctba`)**: A larger memory region allocated for each Command Header, containing the actual SATA command bytes and the scatter/gather lists (PRDT).
|
||||
|
||||
*Note:* All AHCI data structures must be allocated in physically contiguous memory and properly aligned (e.g., 1KB or 256-byte boundaries) because the HBA reads them directly from physical RAM via DMA.
|
||||
|
||||
## 3. Physical Region Descriptor Tables (PRDT)
|
||||
|
||||
When reading or writing data, BoredOS must tell the AHCI controller where in RAM the data should be stored or fetched. This is done using PRDT entries.
|
||||
|
||||
Each `HBA_PRDT_ENTRY` specifies:
|
||||
- A physical Data Base Address (`dba`).
|
||||
- A Byte Count (`dbc`), limited to a maximum of 4MB per entry.
|
||||
|
||||
If a read/write request spans multiple fragmented pages or exceeds 4MB, the driver constructs multiple PRDT entries within the Command Table to form a scatter/gather list. The AHCI hardware seamlessly processes these entries as a single contiguous disk operation.
|
||||
|
||||
## 4. Reading and Writing Sectors
|
||||
|
||||
To execute a command (e.g., `ahci_read_sectors` or `ahci_write_sectors`):
|
||||
1. The driver finds a free slot in the Command List.
|
||||
2. It populates the Command Header, setting the `cfl` (Command FIS Length) and `w` (Write) bit.
|
||||
3. It builds a Host-to-Device Register FIS (`FIS_REG_H2D`) in the Command Table, issuing the `ATA_CMD_READ_DMA_EX` or `ATA_CMD_WRITE_DMA_EX` command and specifying the starting LBA (Logical Block Address) and sector count.
|
||||
4. It sets up the PRDT entries pointing to the physical memory of the provided buffer.
|
||||
5. It sets the corresponding bit in the Port's Command Issue register (`ci`).
|
||||
6. The driver then polls the `ci` register (or waits for an interrupt) until the bit clears, indicating the hardware has completed the DMA transfer.
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
# Process Management & Scheduling
|
||||
|
||||
BoredOS implements a lightweight, symmetric multiprocessing (SMP) capable multitasking environment. This document outlines the architecture of the scheduler, process structures, context switching, and ELF binary loading.
|
||||
|
||||
## 1. Process Structure (`process_t`)
|
||||
|
||||
The core of the process management system is the `process_t` structure, defined in `src/sys/process.h`. Due to kernel memory constraints, BoredOS supports a maximum of 16 concurrent processes (`MAX_PROCESSES`), stored in a statically allocated array.
|
||||
|
||||
Key fields include:
|
||||
- **Identification:** `pid`, `parent_pid`, `pgid` (Process Group ID), and `name`.
|
||||
- **Memory & Context:**
|
||||
- `rsp`: The saved stack pointer during a context switch.
|
||||
- `pml4_phys`: The physical address of the Page Map Level 4 table (VMM root) for this process.
|
||||
- `kernel_stack` & `user_stack_alloc`: Pointers to allocated stack memory.
|
||||
- **Scheduler State:** `ticks`, `sleep_until`, `is_idle`, `cpu_affinity`.
|
||||
- **Resources:**
|
||||
- `fds`: File descriptor table tracking open files, pipes, and sockets (up to `MAX_PROCESS_FDS` = 16).
|
||||
- `gui_events`: A circular queue for Window Manager events (keyboard, mouse).
|
||||
- **Signals:** POSIX-like signal tracking via `signal_mask` and `signal_pending`.
|
||||
|
||||
## 2. The Scheduler
|
||||
|
||||
BoredOS uses a **Preemptive Round-Robin** scheduler implemented as a circular linked list.
|
||||
|
||||
### Symmetric Multiprocessing (SMP)
|
||||
Each CPU core maintains its own `current_process` pointer (`current_process[my_cpu]`). When a new user process is spawned via `process_create_elf`, the kernel assigns it to an Application Processor (AP) core using a simple round-robin assignment policy (`next_cpu_assign`), avoiding Core 0 (BSP) which is typically reserved for kernel tasks and driver interrupts.
|
||||
|
||||
### The `process_schedule` Loop
|
||||
When the timer interrupt fires, it calls `process_schedule(current_rsp)`:
|
||||
1. It saves the `current_rsp` into the current process's structure.
|
||||
2. It handles cleanup of killed processes (`kill_pending`).
|
||||
3. It traverses the circular linked list (`cur->next`) looking for a process where `cpu_affinity == my_cpu`.
|
||||
4. It checks if the process is sleeping (`sleep_until > now`).
|
||||
5. It switches the hardware context:
|
||||
- Updates the Task State Segment (TSS) ring 0 stack pointer.
|
||||
- Switches the page directory by writing the new `pml4_phys` to `CR3`.
|
||||
- Returns the new process's `rsp`, which the interrupt handler then pops into registers.
|
||||
|
||||
## 3. Context Switching
|
||||
|
||||
Context switching is achieved by manually constructing an interrupt stack frame (IRETQ frame).
|
||||
|
||||
When a process is created, the kernel sets up the top of its kernel stack with:
|
||||
- `SS` (Stack Segment: `0x1B` for user, `0x10` for kernel)
|
||||
- `RSP` (The process's stack pointer)
|
||||
- `RFLAGS` (`0x202` to ensure interrupts are enabled)
|
||||
- `CS` (Code Segment: `0x23` for user, `0x08` for kernel)
|
||||
- `RIP` (The entry point of the binary or function)
|
||||
- Zeroed space for General Purpose Registers and a 512-byte `fxsave` region for FPU/SSE state.
|
||||
|
||||
When `process_schedule` returns the new `rsp`, the assembly interrupt stub uses `pop` instructions to restore the general-purpose registers, and finally executes `iretq`, transitioning execution to the new process seamlessly.
|
||||
|
||||
## 4. ELF Loading
|
||||
|
||||
Userland applications in BoredOS are standard 64-bit ELF binaries.
|
||||
|
||||
The function `process_create_elf` orchestrates this:
|
||||
1. **Memory Allocation:** Creates a new PML4 page table for the user process.
|
||||
2. **Parsing:** Calls `elf_load(filepath, pml4, &size)` to parse the ELF headers, allocate required physical memory, and copy the executable segments (text, data, bss) into the process's virtual address space at the locations specified by the ELF program headers.
|
||||
3. **Stack Setup:** Allocates a 256KB user stack mapped at `0x800000`.
|
||||
4. **Argument Passing:** Parses the `args_str` passed to the executable and pushes an `argv` array onto the newly allocated user stack.
|
||||
5. **Execution:** Sets the stack frame's `RIP` to the ELF entry point and links the process into the scheduler's run queue.
|
||||
|
||||
## 5. Process Termination
|
||||
|
||||
When a process exits (or is killed), it is not immediately freed. The scheduler sets `kill_pending = true`. The actual destruction of the PML4 table and stack allocations is deferred to the next tick inside `process_schedule` to avoid freeing the memory of the code currently executing the cleanup.
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
# BoredOS Versioning
|
||||
|
||||
BoredOS uses two independent version numbers: one for the **OS release** and one for the **kernel**. They evolve at different rates and follow different conventions.
|
||||
|
||||
---
|
||||
|
||||
## OS Version
|
||||
|
||||
The OS version follows a **date-based** scheme:
|
||||
|
||||
```
|
||||
YY.M[.x]
|
||||
```
|
||||
|
||||
| Component | Meaning |
|
||||
|-----------|---------|
|
||||
| `YY` | Two-digit year (e.g. `26` for 2026) |
|
||||
| `M` | Month number, no leading zero (e.g. `4` for April, `12` for December) |
|
||||
| `.x` | Optional patch identifier — a small sequential integer that has **no relation to a specific day** |
|
||||
|
||||
### Examples
|
||||
|
||||
| Version | Meaning |
|
||||
|----------|---------|
|
||||
| `26.4` | Base release for April 2026 |
|
||||
| `26.5` | Base release for May 2026 |
|
||||
| `26.5.1` | First patch on top of the May 2026 release |
|
||||
| `26.5.2` | Second patch on top of the May 2026 release |
|
||||
| `26.12` | Base release for December 2026 |
|
||||
|
||||
### Rules
|
||||
|
||||
- The **base release** (`YY.M`) is cut once per month when a milestone is ready.
|
||||
- Patch releases (`YY.M.x`) are issued for fixes or smaller additions that land between two monthly milestones. The `.x` counter starts at `1` and increments sequentially — it is **not** tied to a calendar day.
|
||||
- A `-dev` or `-rc` suffix may be appended to any version string during active development (e.g. `26.5-dev`, `26.5.1-rc1`).
|
||||
- The version string is defined in [`src/core/version.c`](../../src/core/version.c) as `os_version`.
|
||||
|
||||
---
|
||||
|
||||
## Kernel Version
|
||||
|
||||
The kernel version follows **Semantic Versioning**:
|
||||
|
||||
```
|
||||
MAJOR.MINOR.PATCH
|
||||
```
|
||||
|
||||
| Component | When to increment |
|
||||
|-----------|------------------|
|
||||
| `MAJOR` | A breaking or fundamentally large architectural change (e.g. rewriting the syscall layer, introducing a new memory model) |
|
||||
| `MINOR` | A meaningful new feature or a notable internal improvement that does not break existing interfaces |
|
||||
| `PATCH` | A small fix, refactor, or incremental improvement |
|
||||
|
||||
### Examples
|
||||
|
||||
| Transition | Reason |
|
||||
|---------------------|--------|
|
||||
| `4.2.0` → `5.0.0` | Major kernel rework (e.g. full syscall dispatch-table refactor, new scheduler) |
|
||||
| `4.2.0` → `4.3.0` | New subsystem or feature addition (e.g. adding Lua runtime, new VFS driver) |
|
||||
| `4.2.0` → `4.2.1` | Small fix or minor tweak (e.g. PIT calibration fix, terminal newline correction) |
|
||||
|
||||
### Rules
|
||||
|
||||
- When `MAJOR` is bumped, `MINOR` and `PATCH` reset to `0`.
|
||||
- When `MINOR` is bumped, `PATCH` resets to `0`.
|
||||
- A `-dev` suffix may be appended during active development (e.g. `5.0.0-dev`).
|
||||
- The version string is defined in [`src/core/version.c`](../../src/core/version.c) as `kernel_version`.
|
||||
|
||||
---
|
||||
|
||||
## Where Versions Are Declared
|
||||
|
||||
Both version strings live in a single file:
|
||||
|
||||
```c
|
||||
// src/core/version.c
|
||||
|
||||
const char *os_version = "26.5-dev";
|
||||
const char *kernel_version = "4.2.0-dev";
|
||||
```
|
||||
|
||||
When cutting a release, update both strings, remove the `-dev` suffix, tag the commit (`git tag v26.5`), and then immediately bump to the next `-dev` version.
|
||||
|
||||
---
|
||||
|
||||
## Codename
|
||||
|
||||
Each release may carry an informal **codename**. A single word that gives the release a human-friendly identity. Codenames are stored in [`src/core/version.c`](../../src/core/version.c) as `os_codename` and exposed to userspace via the `get_os_info` syscall.
|
||||
|
||||
### Convention
|
||||
|
||||
- Codenames **generally change with each monthly base release** (`YY.M`), but this is not a hard rule. A codename may carry over into the next month if the release feels like a natural continuation of the previous one.
|
||||
- Patch releases (`YY.M.x`) **always keep the same codename** as the base release they belong to.
|
||||
- There is no enforced theme, but names tend to be short, memorable single words.
|
||||
|
||||
### Examples
|
||||
|
||||
| OS Version | Codename
|
||||
|------------|-----------|
|
||||
| `26.4` | Voyager
|
||||
| `26.5` | Genesis
|
||||
|
||||
### Where It Is Declared
|
||||
|
||||
```c
|
||||
// src/core/version.c
|
||||
|
||||
const char *os_codename = "Genesis";
|
||||
```
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ BoredOS features a fully custom, graphical Window Manager built directly into th
|
|||
> [!TIP]
|
||||
> The performance of the window manager heavily depends on minimizing the "dirty regions" drawn in the compositing loop rather than sweeping the whole screen.
|
||||
|
||||
## Window System (`wm.c`)
|
||||
## 🪟 Window System (`wm.c`)
|
||||
|
||||
The windowing system is built around a linked list of `Window` structures.
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ The WM acts as the central hub for input routing.
|
|||
|
||||
- **Event Polling**: The UI loop inside an app continuously calls `ui_poll_event()` to respond to mouse clicks and window movement dispatched by the kernel WM.
|
||||
|
||||
## Multi-Core Safety & Performance
|
||||
## 🧵 Multi-Core Safety & Performance
|
||||
|
||||
With the introduction of Symmetric Multi-Processing (SMP), the Window Manager (WM) was redesigned to ensure stability and high performance across multiple cores.
|
||||
|
||||
|
|
@ -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.
|
||||
|
||||
186
docs/build/toolchain.md
vendored
|
|
@ -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.
|
||||
|
|
@ -25,170 +18,35 @@ To build BoredOS, you need the following tools:
|
|||
- *Why?* `xorriso` packages the compiled kernel, Limine bootloader, and asset files (fonts, images, userland binaries) into the final bootable `boredos.iso` CD-ROM image.
|
||||
|
||||
4. **QEMU** (Optional but highly recommended for testing):
|
||||
- `qemu-system-x86_64` is used to virtualize the OS for testing or to mess around.
|
||||
- `qemu-system-x86_64` is used for rapid emulation and testing.
|
||||
|
||||
## Building the Cross-Compiler on Linux
|
||||
## Installation (macOS)
|
||||
|
||||
### Availability Issue
|
||||
You can easily install the complete toolchain using Homebrew:
|
||||
|
||||
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
|
||||
```sh
|
||||
brew install x86_64-elf-binutils x86_64-elf-gcc nasm xorriso qemu
|
||||
```
|
||||
|
||||
For all other Linux distributions (Debian, Ubuntu, Fedora, openSUSE, etc.), you **must build the cross-compiler from source**.
|
||||
## Installation (Linux)
|
||||
|
||||
### Building from Source
|
||||
Depending on your distribution, the installation commands vary. Note that some distributions may require you to build the `x86_64-elf` cross-compiler from source if it isn't available in their default repositories.
|
||||
|
||||
To build the x86_64-ELF GCC cross-compiler:
|
||||
### Debian / Ubuntu
|
||||
```sh
|
||||
sudo apt update
|
||||
sudo apt install build-essential bison flex libgmp3-dev libmpc-dev libmpfr-dev texinfo nasm xorriso qemu-system-x86
|
||||
```
|
||||
*(Note: You will need to build the `x86_64-elf` cross-compiler from source or find a compatible PPA, as it is not in the default Debian/Ubuntu repositories.)*
|
||||
|
||||
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
|
||||
### Arch Linux
|
||||
Arch Linux provides the regular tools in its standard repositories and the cross-compiler via the AUR:
|
||||
```sh
|
||||
sudo pacman -S nasm xorriso qemu-full
|
||||
yay -S x86_64-elf-gcc x86_64-elf-binutils
|
||||
```
|
||||
|
||||
> **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
|
||||
### Fedora
|
||||
```sh
|
||||
sudo dnf install make gcc gcc-c++ bison flex gmp-devel mpfr-devel libmpc-devel texinfo nasm xorriso qemu
|
||||
```
|
||||
|
||||
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
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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 |
|
||||
|
|
@ -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
|
|
@ -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
|
||||
```
|
||||
|
|
@ -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
|
||||
|
|
@ -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`.
|
||||
|
|
@ -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)
|
||||
|
|
@ -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. |
|
||||
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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 $@
|
||||
1417
limine/limine.c
|
|
@ -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>
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
triple: 'arm64-apple-darwin'
|
||||
binary-path: limine
|
||||
relocations: []
|
||||
...
|
||||
BIN
screenshot.png
Executable file
|
After Width: | Height: | Size: 988 KiB |
BIN
splash.jpg
Normal file
|
After Width: | Height: | Size: 40 KiB |
|
|
@ -7,13 +7,12 @@ extern syscall_handler_c
|
|||
section .text
|
||||
|
||||
; Syscall ABI:
|
||||
; RAX = syscall_num
|
||||
; RDI = arg1
|
||||
; RSI = arg2
|
||||
; RDX = arg3
|
||||
; R10 = arg4
|
||||
; R8 = arg5
|
||||
; R9 = arg6
|
||||
; RDI = syscall_num
|
||||
; RSI = arg1
|
||||
; RDX = arg2
|
||||
; R10 = arg3
|
||||
; R8 = arg4
|
||||
; R9 = arg5
|
||||
|
||||
syscall_entry:
|
||||
swapgs
|
||||
|
|
|
|||
19
src/arch/test_syscall.asm
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
; 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.
|
||||
global test_syscall
|
||||
section .text
|
||||
|
||||
test_syscall:
|
||||
; syscall number in RDI
|
||||
mov rdi, 1
|
||||
; string pointer in RSI
|
||||
lea rsi, [rel test_msg]
|
||||
|
||||
; The SYSCALL instruction
|
||||
syscall
|
||||
|
||||
ret
|
||||
|
||||
section .rodata
|
||||
test_msg: db "Hello from Syscall!", 10, 0
|
||||
25
src/arch/user_test.asm
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
; 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.
|
||||
global user_test_function
|
||||
|
||||
section .text
|
||||
user_test_function:
|
||||
; Syscall convention
|
||||
.loop:
|
||||
; Invoke SYS_WRITE (Syscall #1)
|
||||
mov rdi, 1 ; arg1: fd = 1 (stdout)
|
||||
lea rsi, [rel msg] ; arg2: buffer (RIP-relative)
|
||||
mov rdx, 15 ; arg3: length
|
||||
mov eax, 1 ; syscall_num = 1 (SYS_WRITE)
|
||||
syscall
|
||||
|
||||
; Some delay loop
|
||||
mov rcx, 100000000
|
||||
.delay:
|
||||
dec rcx
|
||||
jnz .delay
|
||||
|
||||
jmp .loop
|
||||
|
||||
msg: db "Hello syscall!", 10
|
||||
|
|
@ -49,6 +49,7 @@ static void kconsole_putc_nolock(char c) {
|
|||
cursor_x = 10;
|
||||
cursor_y += CHAR_HEIGHT;
|
||||
kconsole_scroll();
|
||||
graphics_flip_buffer();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -84,13 +85,11 @@ void kconsole_write(const char *s) {
|
|||
if (!s) return;
|
||||
|
||||
uint64_t flags = spinlock_acquire_irqsave(&console_lock);
|
||||
if (!kconsole_active) {
|
||||
spinlock_release_irqrestore(&console_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
while (*s) {
|
||||
kconsole_putc_nolock(*s++);
|
||||
}
|
||||
|
||||
// Flip buffer after a write batch during boot
|
||||
graphics_flip_buffer();
|
||||
spinlock_release_irqrestore(&console_lock, flags);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -5,54 +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;
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
|
||||
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++;
|
||||
|
|
@ -60,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++;
|
||||
|
|
@ -70,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++; }
|
||||
|
|
@ -100,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;
|
||||
}
|
||||
|
|
@ -120,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';
|
||||
|
|
@ -162,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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
157
src/core/main.c
|
|
@ -26,16 +26,12 @@
|
|||
#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"
|
||||
#include "sys/kernel_subsystem.h"
|
||||
#include "sys/module_manager.h"
|
||||
#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 +76,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 +84,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 +123,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 +137,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];
|
||||
|
|
@ -183,30 +172,6 @@ void log_fail(const char *msg) {
|
|||
serial_write("\n");
|
||||
}
|
||||
|
||||
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(0xFFFFFFFF);
|
||||
serial_write("\n");
|
||||
}
|
||||
|
||||
|
||||
// Kernel Entry Point
|
||||
|
||||
|
|
@ -235,78 +200,6 @@ 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 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 (*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;
|
||||
}
|
||||
int i = 0;
|
||||
while (dev[i] && i < (int)sizeof(g_bootfs_state.root_device) - 1) {
|
||||
g_bootfs_state.root_device[i] = dev[i];
|
||||
i++;
|
||||
}
|
||||
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 (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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void kmain(void) {
|
||||
init_serial();
|
||||
vfs_init();
|
||||
|
|
@ -371,16 +264,26 @@ void kmain(void) {
|
|||
idt_register_interrupts();
|
||||
idt_load();
|
||||
log_ok("IDT ready");
|
||||
print_verbose_boot_banner();
|
||||
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/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 +293,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 +306,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 +333,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");
|
||||
}
|
||||
}
|
||||
|
|
@ -499,9 +393,6 @@ void kmain(void) {
|
|||
ps2_init();
|
||||
asm("sti");
|
||||
|
||||
keymap_init();
|
||||
serial_write("[INIT] Keymap initialized");
|
||||
|
||||
lapic_init();
|
||||
|
||||
if (smp_request.response != NULL) {
|
||||
|
|
@ -512,7 +403,6 @@ void kmain(void) {
|
|||
smp_init(NULL);
|
||||
}
|
||||
|
||||
|
||||
wm_init();
|
||||
|
||||
asm volatile("sti");
|
||||
|
|
@ -520,5 +410,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");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
147
src/core/panic.c
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_codename = "Genesis";
|
||||
const char *os_version = "26.4.2.1";
|
||||
const char *os_codename = "Voyager";
|
||||
const char *kernel_name = "Boredkernel";
|
||||
const char *kernel_version = "4.2.1-dev";
|
||||
const char *kernel_version = "4.1.0-stable";
|
||||
const char *build_date = __DATE__;
|
||||
const char *build_time = __TIME__;
|
||||
const char *build_arch = "x86_64";
|
||||
|
|
|
|||
328
src/dev/ahci.c
|
|
@ -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, §ors, 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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
133
src/dev/ps2.c
|
|
@ -8,8 +8,6 @@
|
|||
#include "lapic.h"
|
||||
#include "smp.h"
|
||||
#include <stdbool.h>
|
||||
#include "input/keyboard.h"
|
||||
#include "input/keymap.h"
|
||||
|
||||
extern void serial_print(const char *s);
|
||||
extern void serial_print_hex(uint64_t n);
|
||||
|
|
@ -22,23 +20,28 @@ uint64_t timer_handler(registers_t *regs) {
|
|||
kernel_ticks++;
|
||||
wm_timer_tick();
|
||||
network_process_frames();
|
||||
|
||||
|
||||
extern void k_beep_process(void);
|
||||
k_beep_process();
|
||||
}
|
||||
|
||||
outb(0x20, 0x20);
|
||||
|
||||
outb(0x20, 0x20);
|
||||
extern uint64_t process_schedule(uint64_t current_rsp);
|
||||
uint64_t new_rsp = process_schedule((uint64_t)regs);
|
||||
|
||||
|
||||
if (smp_cpu_count() > 1) {
|
||||
lapic_send_ipi_all();
|
||||
}
|
||||
|
||||
|
||||
return new_rsp;
|
||||
}
|
||||
|
||||
// --- Keyboard ---
|
||||
static bool shift_pressed = false;
|
||||
static bool caps_lock_on = false;
|
||||
bool ps2_ctrl_pressed = false;
|
||||
static bool extended_scancode = false;
|
||||
|
||||
static void ps2_kbd_wait_write(void) {
|
||||
uint32_t timeout = 100000;
|
||||
while (timeout--) {
|
||||
|
|
@ -48,29 +51,95 @@ static void ps2_kbd_wait_write(void) {
|
|||
|
||||
static void ps2_update_leds(void) {
|
||||
uint8_t led_status = 0;
|
||||
uint32_t mods = keyboard_get_modifiers();
|
||||
|
||||
if (mods & KB_MOD_CAPS) led_status |= 4;
|
||||
if (mods & KB_MOD_NUM) led_status |= 2;
|
||||
if (mods & KB_MOD_SCROLL) led_status |= 1;
|
||||
|
||||
if (caps_lock_on) led_status |= 4;
|
||||
|
||||
ps2_kbd_wait_write();
|
||||
outb(0x60, 0xED);
|
||||
ps2_kbd_wait_write();
|
||||
outb(0x60, led_status);
|
||||
}
|
||||
|
||||
static char scancode_map[128] = {
|
||||
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
|
||||
'\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
|
||||
21, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0,
|
||||
'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*',
|
||||
22, ' ', 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
static char scancode_map_shift[128] = {
|
||||
0, 27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b',
|
||||
'\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',
|
||||
21, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0,
|
||||
'|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, '*',
|
||||
22, ' ', 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
uint64_t keyboard_handler(registers_t *regs) {
|
||||
uint8_t scancode = inb(0x60);
|
||||
|
||||
keyboard_event_t ev;
|
||||
if (keyboard_handle_set1_scancode(scancode, &ev)) {
|
||||
// Update LEDs if a lock key state changed
|
||||
if (ev.keycode == KEY_CAPS_LOCK || ev.keycode == KEY_NUM_LOCK || ev.keycode == KEY_SCROLL_LOCK) {
|
||||
if (ev.pressed) ps2_update_leds();
|
||||
}
|
||||
if (scancode == 0xE0) {
|
||||
extended_scancode = true;
|
||||
outb(0x20, 0x20);
|
||||
return (uint64_t)regs;
|
||||
}
|
||||
|
||||
wm_handle_key_event(ev.keycode, ev.codepoint, ev.mods, ev.pressed);
|
||||
if (scancode == 0x1D) {
|
||||
ps2_ctrl_pressed = true;
|
||||
extended_scancode = false;
|
||||
} else if (scancode == 0x9D) {
|
||||
ps2_ctrl_pressed = false;
|
||||
extended_scancode = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (scancode == 0x2A || scancode == 0x36) { // Shift Down
|
||||
shift_pressed = true;
|
||||
} else if (scancode == 0xAA || scancode == 0xB6) { // Shift Up
|
||||
shift_pressed = false;
|
||||
} else if (scancode == 0x3A) { // Caps Lock Down
|
||||
caps_lock_on = !caps_lock_on;
|
||||
ps2_update_leds();
|
||||
} else if (!(scancode & 0x80)) { // Key Press
|
||||
if (extended_scancode) {
|
||||
extended_scancode = false;
|
||||
switch (scancode) {
|
||||
case 0x48: wm_handle_key(17, true); break; // Up arrow
|
||||
case 0x50: wm_handle_key(18, true); break; // Down arrow
|
||||
case 0x4B: wm_handle_key(19, true); break; // Left arrow
|
||||
case 0x4D: wm_handle_key(20, true); break; // Right arrow
|
||||
}
|
||||
} else {
|
||||
char c = shift_pressed ? scancode_map_shift[scancode] : scancode_map[scancode];
|
||||
if (c) {
|
||||
if (caps_lock_on) {
|
||||
if (c >= 'a' && c <= 'z') c -= 32;
|
||||
else if (c >= 'A' && c <= 'Z') c += 32;
|
||||
}
|
||||
wm_handle_key(c, true);
|
||||
}
|
||||
}
|
||||
} else if (scancode & 0x80) { // Key release
|
||||
if (extended_scancode) {
|
||||
extended_scancode = false;
|
||||
switch (scancode & 0x7F) { // Strip the release bit
|
||||
case 0x48: wm_handle_key(17, false); break; // Up arrow
|
||||
case 0x50: wm_handle_key(18, false); break; // Down arrow
|
||||
case 0x4B: wm_handle_key(19, false); break; // Left arrow
|
||||
case 0x4D: wm_handle_key(20, false); break; // Right arrow
|
||||
}
|
||||
} else {
|
||||
uint8_t base_scancode = scancode & 0x7F;
|
||||
char c = shift_pressed ? scancode_map_shift[base_scancode] : scancode_map[base_scancode];
|
||||
if (c) {
|
||||
if (caps_lock_on) {
|
||||
if (c >= 'a' && c <= 'z') c -= 32;
|
||||
else if (c >= 'A' && c <= 'Z') c += 32;
|
||||
}
|
||||
wm_handle_key(c, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outb(0x20, 0x20); // EOI
|
||||
|
|
@ -109,14 +178,11 @@ 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);
|
||||
outb(0x64, 0xA8);
|
||||
|
||||
|
||||
// Enable Interrupts
|
||||
mouse_wait(0);
|
||||
outb(0x64, 0x20);
|
||||
|
|
@ -126,7 +192,7 @@ void mouse_init(void) {
|
|||
outb(0x64, 0x60);
|
||||
mouse_wait(0);
|
||||
outb(0x60, status);
|
||||
|
||||
|
||||
// Set Defaults
|
||||
mouse_write(0xF6);
|
||||
mouse_read();
|
||||
|
|
@ -135,12 +201,12 @@ void mouse_init(void) {
|
|||
mouse_write(0xF3); mouse_read(); mouse_write(200); mouse_read();
|
||||
mouse_write(0xF3); mouse_read(); mouse_write(100); mouse_read();
|
||||
mouse_write(0xF3); mouse_read(); mouse_write(80); mouse_read();
|
||||
|
||||
|
||||
mouse_write(0xF2);
|
||||
mouse_read();
|
||||
uint8_t id = mouse_read();
|
||||
if (id == 3) mouse_has_wheel = true;
|
||||
|
||||
|
||||
// Enable Streaming
|
||||
mouse_write(0xF4);
|
||||
mouse_read();
|
||||
|
|
@ -151,11 +217,11 @@ uint64_t mouse_handler(registers_t *regs) {
|
|||
if (!(status & 0x20)) {
|
||||
outb(0x20, 0x20);
|
||||
outb(0xA0, 0x20);
|
||||
return (uint64_t)regs;
|
||||
return (uint64_t)regs;
|
||||
}
|
||||
|
||||
uint8_t b = inb(0x60);
|
||||
|
||||
|
||||
if (mouse_cycle == 0) {
|
||||
if ((b & 0x08) == 0) {
|
||||
// Out of sync
|
||||
|
|
@ -191,12 +257,9 @@ uint64_t mouse_handler(registers_t *regs) {
|
|||
}
|
||||
|
||||
void ps2_init(void) {
|
||||
keymap_init();
|
||||
keyboard_init();
|
||||
mouse_init();
|
||||
ps2_update_leds();
|
||||
}
|
||||
|
||||
bool ps2_shift_pressed(void) {
|
||||
return keyboard_shift_pressed();
|
||||
}
|
||||
return shift_pressed;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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];
|
||||
}
|
||||
|
|
@ -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
|
||||