From cd9c786d149b26004d1ac2df68e5d1e3791bd974 Mon Sep 17 00:00:00 2001 From: subhobhai943 Date: Sun, 16 Aug 2026 21:23:52 +0530 Subject: [PATCH] Initial commit: SUB-OS 64-Bit Production Monolithic Operating System with LazyBox userland suite --- .gitignore | 10 + LICENSE | 21 ++ Makefile | 114 +++++++++ README.md | 140 +++++++++++ arch/x86_64/boot/entry.asm | 37 +++ arch/x86_64/cpu/cpuid.c | 65 +++++ arch/x86_64/cpu/gdt.c | 65 +++++ arch/x86_64/cpu/gdt_load.asm | 29 +++ arch/x86_64/cpu/idt.c | 34 +++ arch/x86_64/cpu/idt_load.asm | 8 + arch/x86_64/cpu/isr.c | 87 +++++++ arch/x86_64/cpu/isr_stubs.asm | 133 ++++++++++ arch/x86_64/cpu/pic.c | 78 ++++++ arch/x86_64/cpu/pit.c | 49 ++++ arch/x86_64/mm/paging.c | 92 +++++++ boot/boot.asm | 98 ++++++++ boot/gdt.asm | 43 ++++ boot/stage2.asm | 272 ++++++++++++++++++++ crypto/crc32.c | 35 +++ crypto/md5.c | 186 ++++++++++++++ crypto/prng.c | 43 ++++ crypto/sha256.c | 141 +++++++++++ drivers/block/ata.c | 135 ++++++++++ drivers/char/keyboard.c | 139 +++++++++++ drivers/char/serial.c | 39 +++ drivers/char/tty.c | 166 +++++++++++++ drivers/char/vga.c | 106 ++++++++ drivers/net/e1000.c | 252 +++++++++++++++++++ drivers/pci/pci.c | 138 ++++++++++ drivers/sound/speaker.c | 30 +++ fs/devfs.c | 79 ++++++ fs/procfs.c | 142 +++++++++++ fs/ramfs.c | 195 +++++++++++++++ fs/vfs.c | 242 ++++++++++++++++++ include/arch/x86_64/cpuid.h | 30 +++ include/arch/x86_64/gdt.h | 47 ++++ include/arch/x86_64/idt.h | 28 +++ include/arch/x86_64/io.h | 52 ++++ include/arch/x86_64/isr.h | 19 ++ include/arch/x86_64/paging.h | 25 ++ include/arch/x86_64/pic.h | 12 + include/arch/x86_64/pit.h | 11 + include/crypto/crypto.h | 42 ++++ include/drivers/ata.h | 28 +++ include/drivers/e1000.h | 22 ++ include/drivers/keyboard.h | 39 +++ include/drivers/pci.h | 46 ++++ include/drivers/serial.h | 16 ++ include/drivers/speaker.h | 10 + include/drivers/tty.h | 20 ++ include/drivers/vga.h | 48 ++++ include/fs/devfs.h | 10 + include/fs/procfs.h | 9 + include/fs/ramfs.h | 10 + include/fs/vfs.h | 85 +++++++ include/kernel/kernel.h | 15 ++ include/kernel/panic.h | 9 + include/kernel/printk.h | 48 ++++ include/kernel/sched.h | 13 + include/kernel/sync.h | 36 +++ include/kernel/task.h | 36 +++ include/kernel/types.h | 30 +++ include/lib/bitmap.h | 26 ++ include/lib/list.h | 56 +++++ include/lib/printf.h | 12 + include/lib/string.h | 33 +++ include/mm/kmalloc.h | 18 ++ include/mm/pmm.h | 36 +++ include/net/net.h | 107 ++++++++ include/userland/lazybox.h | 26 ++ include/userland/shell.h | 9 + kernel/main.c | 112 +++++++++ kernel/panic.c | 33 +++ kernel/printk.c | 91 +++++++ kernel/sched.c | 67 +++++ kernel/sync.c | 26 ++ kernel/task.c | 81 ++++++ lib/bitmap.c | 39 +++ lib/string.c | 226 +++++++++++++++++ lib/vsprintf.c | 199 +++++++++++++++ linker.ld | 41 +++ mm/kmalloc.c | 164 ++++++++++++ mm/pmm.c | 128 ++++++++++ net/core/net.c | 323 ++++++++++++++++++++++++ tools/build-cross-compiler.sh | 115 +++++++++ tools/setup.sh | 40 +++ userland/lazybox/lazybox.c | 456 ++++++++++++++++++++++++++++++++++ userland/lazybox/shell.c | 429 ++++++++++++++++++++++++++++++++ 88 files changed, 7132 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 arch/x86_64/boot/entry.asm create mode 100644 arch/x86_64/cpu/cpuid.c create mode 100644 arch/x86_64/cpu/gdt.c create mode 100644 arch/x86_64/cpu/gdt_load.asm create mode 100644 arch/x86_64/cpu/idt.c create mode 100644 arch/x86_64/cpu/idt_load.asm create mode 100644 arch/x86_64/cpu/isr.c create mode 100644 arch/x86_64/cpu/isr_stubs.asm create mode 100644 arch/x86_64/cpu/pic.c create mode 100644 arch/x86_64/cpu/pit.c create mode 100644 arch/x86_64/mm/paging.c create mode 100644 boot/boot.asm create mode 100644 boot/gdt.asm create mode 100644 boot/stage2.asm create mode 100644 crypto/crc32.c create mode 100644 crypto/md5.c create mode 100644 crypto/prng.c create mode 100644 crypto/sha256.c create mode 100644 drivers/block/ata.c create mode 100644 drivers/char/keyboard.c create mode 100644 drivers/char/serial.c create mode 100644 drivers/char/tty.c create mode 100644 drivers/char/vga.c create mode 100644 drivers/net/e1000.c create mode 100644 drivers/pci/pci.c create mode 100644 drivers/sound/speaker.c create mode 100644 fs/devfs.c create mode 100644 fs/procfs.c create mode 100644 fs/ramfs.c create mode 100644 fs/vfs.c create mode 100644 include/arch/x86_64/cpuid.h create mode 100644 include/arch/x86_64/gdt.h create mode 100644 include/arch/x86_64/idt.h create mode 100644 include/arch/x86_64/io.h create mode 100644 include/arch/x86_64/isr.h create mode 100644 include/arch/x86_64/paging.h create mode 100644 include/arch/x86_64/pic.h create mode 100644 include/arch/x86_64/pit.h create mode 100644 include/crypto/crypto.h create mode 100644 include/drivers/ata.h create mode 100644 include/drivers/e1000.h create mode 100644 include/drivers/keyboard.h create mode 100644 include/drivers/pci.h create mode 100644 include/drivers/serial.h create mode 100644 include/drivers/speaker.h create mode 100644 include/drivers/tty.h create mode 100644 include/drivers/vga.h create mode 100644 include/fs/devfs.h create mode 100644 include/fs/procfs.h create mode 100644 include/fs/ramfs.h create mode 100644 include/fs/vfs.h create mode 100644 include/kernel/kernel.h create mode 100644 include/kernel/panic.h create mode 100644 include/kernel/printk.h create mode 100644 include/kernel/sched.h create mode 100644 include/kernel/sync.h create mode 100644 include/kernel/task.h create mode 100644 include/kernel/types.h create mode 100644 include/lib/bitmap.h create mode 100644 include/lib/list.h create mode 100644 include/lib/printf.h create mode 100644 include/lib/string.h create mode 100644 include/mm/kmalloc.h create mode 100644 include/mm/pmm.h create mode 100644 include/net/net.h create mode 100644 include/userland/lazybox.h create mode 100644 include/userland/shell.h create mode 100644 kernel/main.c create mode 100644 kernel/panic.c create mode 100644 kernel/printk.c create mode 100644 kernel/sched.c create mode 100644 kernel/sync.c create mode 100644 kernel/task.c create mode 100644 lib/bitmap.c create mode 100644 lib/string.c create mode 100644 lib/vsprintf.c create mode 100644 linker.ld create mode 100644 mm/kmalloc.c create mode 100644 mm/pmm.c create mode 100644 net/core/net.c create mode 100755 tools/build-cross-compiler.sh create mode 100755 tools/setup.sh create mode 100644 userland/lazybox/lazybox.c create mode 100644 userland/lazybox/shell.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..19037fc --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +build/ +*.bin +*.img +*.elf +*.o +*.iso +*.log +*.tmp +.DS_Store +.system_generated/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..05c7305 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 subhobhai943 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f923d45 --- /dev/null +++ b/Makefile @@ -0,0 +1,114 @@ +# ============================================================================= +# SUB-OS Master Makefile - Modular Monolithic Linux-Like Kernel Build System +# ============================================================================= + +# Cross Compiler & Toolchain Detection +CROSS_COMPILE ?= x86_64-elf- + +CC = $(CROSS_COMPILE)gcc +LD = $(CROSS_COMPILE)ld +OBJCOPY = $(CROSS_COMPILE)objcopy +ASM = nasm + +# Build Directories +BUILD_DIR = build +BOOT_DIR = boot + +# Target Architecture Flags +CFLAGS = -ffreestanding \ + -mno-red-zone \ + -mno-mmx \ + -mno-sse \ + -mno-sse2 \ + -mcmodel=kernel \ + -Wall \ + -Wextra \ + -Wno-unused-function \ + -Wno-unused-parameter \ + -O2 \ + -Iinclude + +ASMFLAGS_ELF = -f elf64 +ASMFLAGS_BOOT = -f bin -I$(BOOT_DIR)/ + +LDFLAGS = -nostdlib -T linker.ld + +# Source Files Discovery +ARCH_C_SRCS = $(shell find arch -name '*.c' 2>/dev/null) +ARCH_ASM_SRCS = $(shell find arch -name '*.asm' 2>/dev/null) +KERNEL_C_SRCS = $(shell find kernel -name '*.c' 2>/dev/null) +MM_C_SRCS = $(shell find mm -name '*.c' 2>/dev/null) +DRIVERS_C_SRCS = $(shell find drivers -name '*.c' 2>/dev/null) +FS_C_SRCS = $(shell find fs -name '*.c' 2>/dev/null) +NET_C_SRCS = $(shell find net -name '*.c' 2>/dev/null) +CRYPTO_C_SRCS = $(shell find crypto -name '*.c' 2>/dev/null) +LIB_C_SRCS = $(shell find lib -name '*.c' 2>/dev/null) +USERLAND_C_SRCS = $(shell find userland -name '*.c' 2>/dev/null) + +ALL_C_SRCS = $(ARCH_C_SRCS) $(KERNEL_C_SRCS) $(MM_C_SRCS) $(DRIVERS_C_SRCS) \ + $(FS_C_SRCS) $(NET_C_SRCS) $(CRYPTO_C_SRCS) $(LIB_C_SRCS) $(USERLAND_C_SRCS) + +# Object Files Mapping +C_OBJS = $(patsubst %.c, $(BUILD_DIR)/%.o, $(ALL_C_SRCS)) +ASM_OBJS = $(patsubst %.asm, $(BUILD_DIR)/%.o, $(ARCH_ASM_SRCS)) + +# Put entry.o first in linker sequence +ENTRY_OBJ = $(BUILD_DIR)/arch/x86_64/boot/entry.o +OTHER_OBJS = $(filter-out $(ENTRY_OBJ), $(ASM_OBJS) $(C_OBJS)) + +IMAGE = SUB-OS.img + +.PHONY: all clean run debug info + +all: $(IMAGE) + +# Assemble 16-bit Bootloader Stage 1 +$(BUILD_DIR)/boot/boot.bin: $(BOOT_DIR)/boot.asm | $(BUILD_DIR)/boot + $(ASM) $(ASMFLAGS_BOOT) $< -o $@ + +# Assemble 16/32/64-bit Bootloader Stage 2 +$(BUILD_DIR)/boot/stage2.bin: $(BOOT_DIR)/stage2.asm $(BOOT_DIR)/gdt.asm | $(BUILD_DIR)/boot + $(ASM) $(ASMFLAGS_BOOT) $< -o $@ + +# Compile 64-bit C source files +$(BUILD_DIR)/%.o: %.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -c $< -o $@ + +# Assemble 64-bit ASM source files +$(BUILD_DIR)/%.o: %.asm + @mkdir -p $(dir $@) + $(ASM) $(ASMFLAGS_ELF) $< -o $@ + +# Link Kernel ELF +$(BUILD_DIR)/kernel.elf: $(ENTRY_OBJ) $(OTHER_OBJS) linker.ld + $(LD) $(LDFLAGS) -o $@ $(ENTRY_OBJ) $(OTHER_OBJS) + +# Convert ELF to Flat Binary +$(BUILD_DIR)/kernel.bin: $(BUILD_DIR)/kernel.elf + $(OBJCOPY) -O binary $< $@ + +# Create Bootable Floppy / HDD Raw Disk Image +$(IMAGE): $(BUILD_DIR)/boot/boot.bin $(BUILD_DIR)/boot/stage2.bin $(BUILD_DIR)/kernel.bin + dd if=/dev/zero of=$@ bs=512 count=2880 status=none + dd if=$(BUILD_DIR)/boot/boot.bin of=$@ bs=512 conv=notrunc status=none + dd if=$(BUILD_DIR)/boot/stage2.bin of=$@ bs=512 seek=1 conv=notrunc status=none + dd if=$(BUILD_DIR)/kernel.bin of=$@ bs=512 seek=16 conv=notrunc status=none + @echo "=== [SUB-OS Disk Image Built Successfully: $@ (Size: $$(du -h $@ | cut -f1))] ===" + +$(BUILD_DIR)/boot: + mkdir -p $(BUILD_DIR)/boot + +run: $(IMAGE) + qemu-system-x86_64 -drive format=raw,file=$(IMAGE) -nic model=e1000 -serial stdio + +debug: $(IMAGE) + qemu-system-x86_64 -drive format=raw,file=$(IMAGE) -nic model=e1000 -serial stdio -s -S + +info: + @echo "C Sources: $(words $(ALL_C_SRCS)) files" + @echo "ASM Sources: $(words $(ARCH_ASM_SRCS)) files" + @echo "Target: $(IMAGE)" + +clean: + rm -rf $(BUILD_DIR) $(IMAGE) diff --git a/README.md b/README.md new file mode 100644 index 0000000..75b902c --- /dev/null +++ b/README.md @@ -0,0 +1,140 @@ +# SUB-OS 64-Bit Production Monolithic Operating System + +[![Build](https://img.shields.io/badge/build-passing-brightgreen.svg)]() +[![Architecture](https://img.shields.io/badge/arch-x86__64%20Long%20Mode-blue.svg)]() +[![Userland](https://img.shields.io/badge/userland-LazyBox%20v2.0.0--pro-orange.svg)]() +[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) + +**SUB-OS** is a modular monolithic 64-bit x86_64 operating system designed and engineered from scratch following modern Linux kernel architectural patterns and incorporating the **LazyBox** multi-call userland binary suite. + +--- + +## 🌟 Key Features + +- **64-Bit Long Mode Bare-Metal Core**: Custom two-stage MBR bootloader with BIOS E820 memory mapping and 4GB 4-level PML4 identity paging. +- **Modular Monolithic Linux Layout**: Clean directory structure (`arch/`, `kernel/`, `mm/`, `drivers/`, `fs/`, `net/`, `crypto/`, `lib/`, `userland/`). +- **Preemptive Multitasking Scheduler**: Round-Robin scheduling driven by 8254 PIT (100 Hz, 10ms resolution) with atomic spinlocks & semaphores. +- **Physical & Dynamic Memory Management**: 64-bit Page Frame Allocator (4 KB bitmap) and Kernel Dynamic Heap (4 MB pool) with boundary tag splitting and coalescing. +- **Virtual File System (VFS)**: Path resolver (`vfs_namei`), file descriptors (64 max), in-memory RAMFS, synthetic `/dev` device filesystem, and `/proc` dynamic system metrics filesystem. +- **Hardware Drivers**: + - **Char**: VGA Text Mode, Virtual Terminals TTY1-4 with ANSI escape parsing, 16550 UART Serial (COM1), PS/2 Keyboard with circular buffer. + - **Block**: ATA / IDE Hard Disk PIO driver (28-bit LBA). + - **PCI**: PCI Bus Enumeration & MMIO BAR configuration. + - **Network**: Intel 82540EM/82545EM Gigabit Ethernet NIC with circular DMA RX/TX rings. + - **Sound**: PC Speaker square-wave tone generator. +- **Network Protocol Stack**: Ethernet encapsulation, ARP resolution table, IPv4 routing/checksums, and ICMP Echo client/server (`ping`). +- **Kernel Cryptographic Engine**: FIPS 180-4 SHA-256, RFC 1321 MD5, IEEE 802.3 CRC32, and Xorshift128+ CSPRNG. +- **LazyBox Multi-Call Userland Suite**: 42 built-in applets with history, arrow-key line editing, and ANSI color support. + +--- + +## 📁 Source Tree + +``` +├── arch/x86_64/ # Hardware-specific CPU, paging, and entry code +│ ├── boot/ # 64-bit kernel entry point (entry.asm) +│ ├── cpu/ # GDT/TSS, IDT (256 gates), Dual 8259A PIC, 8254 PIT, CPUID +│ └── mm/ # 4-Level PML4 Paging & TLB management +├── boot/ # Custom two-stage MBR bootloader +│ ├── boot.asm # Stage 1: 512-byte MBR loader (INT 13h LBA/CHS) +│ └── stage2.asm # Stage 2: A20, E820 Memory Map, 4GB Identity Paging, Long Mode switch +├── kernel/ # Core Monolithic Kernel Engine +│ ├── main.c # 11-step master initialization sequence +│ ├── printk.c # ANSI loglevel printk with 64KB dmesg ringbuffer & COM1 serial port output +│ ├── panic.c # Kernel panic screen with 64-bit register dump +│ ├── task.c # Task Control Blocks (PCB) & Process Management +│ ├── sched.c # Preemptive Round-Robin Multi-Tasking Scheduler +│ └── sync.c # Atomic spinlocks and semaphores +├── mm/ # Memory Management Subsystem +│ ├── pmm.c # 64-bit Physical Page Frame Allocator (4 KB bitmap) +│ └── kmalloc.c # Kernel Dynamic Heap Allocator (4 MB initial pool) +├── drivers/ # Device Driver Tree +│ ├── char/ # VGA text driver, Virtual Terminals TTY1-4, 16550 UART COM1, PS/2 Keyboard +│ ├── block/ # ATA / IDE Hard Disk PIO driver (28-bit LBA) +│ ├── pci/ # PCI Bus Enumerator & BAR mapping +│ ├── net/ # Intel 82540EM/82545EM Gigabit Ethernet NIC Driver (Circular DMA Rings) +│ └── sound/ # PC Speaker tone generator & PIT Channel 2 +├── fs/ # Virtual File System (VFS) +│ ├── vfs.c # VFS core, path resolver (vfs_namei), file descriptor table (64 max) +│ ├── ramfs.c # In-memory RAM filesystem +│ ├── devfs.c # Synthetic Device Filesystem (/dev/null, /dev/zero, /dev/random, /dev/tty) +│ └── procfs.c # Dynamic Information Filesystem (/proc/version, /proc/meminfo, /proc/cpuinfo) +├── crypto/ # Kernel Cryptographic Engine +│ ├── sha256.c # FIPS 180-4 Standard SHA-256 Digest +│ ├── md5.c # RFC 1321 MD5 Message-Digest +│ ├── crc32.c # IEEE 802.3 32-bit Cyclic Redundancy Check +│ └── prng.c # Xorshift128+ High-Performance CSPRNG +├── net/core/ # Network Protocol Stack +│ └── net.c # Ethernet encapsulation, ARP Table, IPv4 routing, ICMP Echo ping client/server +├── lib/ # Standard Kernel Runtime Library +│ ├── string.c # String & memory manipulation +│ ├── vsprintf.c # vsnprintf with width, alignment, and formatting +│ └── bitmap.c # Bit manipulation utilities +└── userland/lazybox/ # Userland Shell & Multi-Call Suite + ├── lazybox.c # Multi-call binary dispatcher with 42 applets + └── shell.c # Interactive terminal shell (history, tab-completion, ANSI color) +``` + +--- + +## ⚡ 11-Step Master Boot Pipeline + +1. **[1/11] GDT & TSS**: 64-bit flat segment selectors + Task State Segment with dedicated 64KB kernel stack. +2. **[2/11] IDT & ISRs**: 256-entry Interrupt Gate Table with CPU exception and hardware IRQ handlers. +3. **[3/11] Dual 8259A PIC**: Remapped hardware IRQs 0-15 to vectors 32-47 with cascade handling. +4. **[4/11] 8254 PIT Timer**: Configured for 100 Hz (10ms resolution) driving preemptive multitasking. +5. **[5/11] PS/2 Keyboard**: Full scancode translation table with Shift, CapsLock, and circular buffer. +6. **[6/11] Physical Memory Manager**: 64-bit frame allocator tracking 128 MB RAM (31,712 free pages). +7. **[7/11] Kernel Dynamic Heap**: 4 MB initial pool with boundary tag headers, coalescing, and reallocation. +8. **[8/11] ATA / IDE Hard Disk**: 28-bit LBA PIO controller supporting read/write operations. +9. **[9/11] PCI & Intel E1000 NIC**: Intel Gigabit Ethernet controller (`52:54:00:12:34:56`), DMA RX/TX rings. +10. **[10/11] Virtual File System**: Root RAMFS, `/dev` synthetic device filesystem, `/proc` dynamic info filesystem. +11. **[11/11] LazyBox & Scheduler**: Preemptive scheduler initialization and interactive multi-call binary userland. + +--- + +## 🛠️ Build and Run + +### Prerequisites +- `nasm` (>= 2.15) +- `x86_64-elf-gcc` (>= 13.0) & `x86_64-elf-ld` +- `qemu-system-x86_64` +- `make` + +### Commands +```bash +# Build the 1.5MB bootable disk image +make + +# Launch in QEMU with Intel E1000 Gigabit NIC +make run + +# Clean build artifacts +make clean +``` + +--- + +## 💻 LazyBox Applet Reference + +``` + [Filesystem] + ls cat touch mkdir wc + [Crypto] + md5sum sha256sum crc32 + [Network] + ifconfig ping arp + [Storage] + hdparm + [System] + uname free uptime dmesg sleep + [Terminal] + clear + [Core] + lazybox echo neofetch +``` + +--- + +## 📄 License +This project is open-source software licensed under the [MIT License](LICENSE). diff --git a/arch/x86_64/boot/entry.asm b/arch/x86_64/boot/entry.asm new file mode 100644 index 0000000..3f0d803 --- /dev/null +++ b/arch/x86_64/boot/entry.asm @@ -0,0 +1,37 @@ +[BITS 64] + +extern kernel_main +extern __bss_start +extern __bss_end +extern stack_top + +global kernel_entry + +section .text +kernel_entry: + ; 1. Set up high-performance dedicated 64 KB kernel stack + mov rsp, stack_top + + ; 2. Preserve bootloader arguments safely in callee-saved registers R12 & R13 + mov r12, rdi ; memory_map pointer + mov r13, rsi ; memory_map count + + ; 3. Zero out the entire BSS section (.bss) + mov rdi, __bss_start + mov rcx, __bss_end + sub rcx, rdi + xor al, al + cld + rep stosb + + ; 4. Restore bootloader arguments into RDI and RSI per AMD64 System V ABI + mov rdi, r12 + mov rsi, r13 + + ; 5. Call kernel_main(memory_map, memory_map_count) + call kernel_main + +.hang: + cli + hlt + jmp .hang diff --git a/arch/x86_64/cpu/cpuid.c b/arch/x86_64/cpu/cpuid.c new file mode 100644 index 0000000..b434740 --- /dev/null +++ b/arch/x86_64/cpu/cpuid.c @@ -0,0 +1,65 @@ +#include +#include + +static cpu_info_t global_cpu_info; +static bool cpu_detected = false; + +static inline void cpuid(uint32_t code, uint32_t* a, uint32_t* b, uint32_t* c, uint32_t* d) { + __asm__ volatile("cpuid" + : "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d) + : "a"(code)); +} + +void cpuid_detect(cpu_info_t* info) { + uint32_t eax, ebx, ecx, edx; + + memset(info, 0, sizeof(cpu_info_t)); + + // 1. Get Vendor String + cpuid(0, &eax, &ebx, &ecx, &edx); + ((uint32_t*)info->vendor)[0] = ebx; + ((uint32_t*)info->vendor)[1] = edx; + ((uint32_t*)info->vendor)[2] = ecx; + info->vendor[12] = '\0'; + + // 2. Get Features & Model Info + cpuid(1, &eax, &ebx, &ecx, &edx); + info->stepping = eax & 0x0F; + info->model_id = (eax >> 4) & 0x0F; + info->family = (eax >> 8) & 0x0F; + + info->has_fpu = (edx & (1 << 0)) != 0; + info->has_tsc = (edx & (1 << 4)) != 0; + info->has_msr = (edx & (1 << 5)) != 0; + info->has_pae = (edx & (1 << 6)) != 0; + info->has_apic = (edx & (1 << 9)) != 0; + info->has_sse = (edx & (1 << 25)) != 0; + info->has_sse2 = (edx & (1 << 26)) != 0; + info->has_sse3 = (ecx & (1 << 0)) != 0; + info->has_sse4_1= (ecx & (1 << 19)) != 0; + info->has_sse4_2= (ecx & (1 << 20)) != 0; + info->has_avx = (ecx & (1 << 28)) != 0; + info->has_rdrand= (ecx & (1 << 30)) != 0; + + // 3. Get Brand String (Extended Functions) + cpuid(0x80000000, &eax, &ebx, &ecx, &edx); + if (eax >= 0x80000004) { + uint32_t* brand = (uint32_t*)info->model; + cpuid(0x80000002, &brand[0], &brand[1], &brand[2], &brand[3]); + cpuid(0x80000003, &brand[4], &brand[5], &brand[6], &brand[7]); + cpuid(0x80000004, &brand[8], &brand[9], &brand[10], &brand[11]); + info->model[48] = '\0'; + } else { + strcpy(info->model, "x86_64 Compatible Processor"); + } + + global_cpu_info = *info; + cpu_detected = true; +} + +const cpu_info_t* cpuid_get_info(void) { + if (!cpu_detected) { + cpuid_detect(&global_cpu_info); + } + return &global_cpu_info; +} diff --git a/arch/x86_64/cpu/gdt.c b/arch/x86_64/cpu/gdt.c new file mode 100644 index 0000000..b69bd3f --- /dev/null +++ b/arch/x86_64/cpu/gdt.c @@ -0,0 +1,65 @@ +#include +#include + +extern void gdt_flush(uint64_t gdt_ptr); +extern void tss_flush(void); + +static gdt_entry_t gdt_entries[7]; +static gdt_ptr_t gdt_ptr; +static tss_entry_t tss_entry; + +static void gdt_set_gate(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) { + gdt_entries[num].base_low = (base & 0xFFFF); + gdt_entries[num].base_middle = (base >> 16) & 0xFF; + gdt_entries[num].base_high = (base >> 24) & 0xFF; + + gdt_entries[num].limit_low = (limit & 0xFFFF); + gdt_entries[num].granularity = (limit >> 16) & 0x0F; + + gdt_entries[num].granularity |= gran & 0xF0; + gdt_entries[num].access = access; +} + +static void write_tss(int num, uint64_t base, uint32_t limit) { + gdt_set_gate(num, (uint32_t)base, limit, 0x89, 0x00); + + // TSS descriptor takes 16 bytes in 64-bit mode (2 entries) + uint32_t* high_part = (uint32_t*)&gdt_entries[num + 1]; + high_part[0] = (uint32_t)(base >> 32); + high_part[1] = 0; +} + +void gdt_init(void) { + gdt_ptr.limit = sizeof(gdt_entries) - 1; + gdt_ptr.base = (uint64_t)&gdt_entries; + + // 0x00: Null Descriptor + gdt_set_gate(0, 0, 0, 0, 0); + + // 0x08: Kernel 64-bit Code Segment (Access=0x9A, Gran=0x20 for Long Mode) + gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0x20); + + // 0x10: Kernel 64-bit Data Segment (Access=0x92, Gran=0x00) + gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0x00); + + // 0x18: User 64-bit Code Segment (Access=0xFA, Gran=0x20) + gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0x20); + + // 0x20: User 64-bit Data Segment (Access=0xF2, Gran=0x00) + gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0x00); + + // 0x28: TSS (Task State Segment) - takes slots 5 & 6 + extern char stack_top[]; + memset(&tss_entry, 0, sizeof(tss_entry_t)); + tss_entry.rsp0 = (uint64_t)stack_top; + tss_entry.ist1 = (uint64_t)stack_top; + tss_entry.iomap_base = sizeof(tss_entry_t); + write_tss(5, (uint64_t)&tss_entry, sizeof(tss_entry_t) - 1); + + gdt_flush((uint64_t)&gdt_ptr); + tss_flush(); +} + +void gdt_set_kernel_stack(uint64_t stack) { + tss_entry.rsp0 = stack; +} diff --git a/arch/x86_64/cpu/gdt_load.asm b/arch/x86_64/cpu/gdt_load.asm new file mode 100644 index 0000000..3752447 --- /dev/null +++ b/arch/x86_64/cpu/gdt_load.asm @@ -0,0 +1,29 @@ +[BITS 64] + +global gdt_flush +global tss_flush + +section .text +gdt_flush: + lgdt [rdi] + ; Reload data segments + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + ; Reload CS via far return + push 0x08 + lea rax, [rel .reload_cs] + push rax + retfq + +.reload_cs: + ret + +tss_flush: + mov ax, 0x28 + ltr ax + ret diff --git a/arch/x86_64/cpu/idt.c b/arch/x86_64/cpu/idt.c new file mode 100644 index 0000000..cead461 --- /dev/null +++ b/arch/x86_64/cpu/idt.c @@ -0,0 +1,34 @@ +#include +#include + +extern void idt_flush(uint64_t idt_ptr); + +static idt_entry_t idt_entries[IDT_ENTRIES]; +static idt_ptr_t idt_ptr; + +void idt_set_gate(uint8_t vector, void* isr, uint8_t flags) { + uint64_t addr = (uint64_t)isr; + idt_entries[vector].isr_low = (uint16_t)(addr & 0xFFFF); + idt_entries[vector].kernel_cs = 0x08; + idt_entries[vector].ist = 0; + idt_entries[vector].attributes = flags; + idt_entries[vector].isr_mid = (uint16_t)((addr >> 16) & 0xFFFF); + idt_entries[vector].isr_high = (uint32_t)((addr >> 32) & 0xFFFFFFFF); + idt_entries[vector].reserved = 0; +} + +void idt_enable_gate(uint8_t vector) { + idt_entries[vector].attributes |= 0x80; +} + +void idt_disable_gate(uint8_t vector) { + idt_entries[vector].attributes &= ~0x80; +} + +void idt_init(void) { + idt_ptr.limit = sizeof(idt_entries) - 1; + idt_ptr.base = (uint64_t)&idt_entries; + + memset(&idt_entries, 0, sizeof(idt_entries)); + idt_flush((uint64_t)&idt_ptr); +} diff --git a/arch/x86_64/cpu/idt_load.asm b/arch/x86_64/cpu/idt_load.asm new file mode 100644 index 0000000..ad8a15d --- /dev/null +++ b/arch/x86_64/cpu/idt_load.asm @@ -0,0 +1,8 @@ +[BITS 64] + +global idt_flush + +section .text +idt_flush: + lidt [rdi] + ret diff --git a/arch/x86_64/cpu/isr.c b/arch/x86_64/cpu/isr.c new file mode 100644 index 0000000..5e5eab4 --- /dev/null +++ b/arch/x86_64/cpu/isr.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include + +extern void* isr_stub_table[48]; + +static isr_handler_t interrupt_handlers[256]; + +static const char* exception_messages[32] = { + "Division By Zero", + "Debug", + "Non-Maskable Interrupt", + "Breakpoint", + "Into Detected Overflow", + "Out of Bounds", + "Invalid Opcode", + "No Coprocessor", + "Double Fault", + "Coprocessor Segment Overrun", + "Bad TSS", + "Segment Not Present", + "Stack Fault", + "General Protection Fault", + "Page Fault", + "Unknown Interrupt", + "Coprocessor Fault", + "Alignment Check", + "Machine Check", + "SIMD Floating-Point Exception", + "Virtualization Exception", + "Control Protection Exception", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Hypervisor Injection Exception", + "VMM Communication Exception", + "Security Exception", + "Reserved" +}; + +void isr_register_handler(uint8_t int_no, isr_handler_t handler) { + interrupt_handlers[int_no] = handler; +} + +void isr_unregister_handler(uint8_t int_no) { + interrupt_handlers[int_no] = NULL; +} + +void isr_handler_common(registers_t* regs) { + if (regs->int_no < 32) { + // CPU Exception Fault + printk(KERN_EMERG "\n[EXCEPTION] Vector %llu: %s (Error code: 0x%llx)\n", + regs->int_no, exception_messages[regs->int_no], regs->error_code); + panic_with_regs(exception_messages[regs->int_no], regs); + } + + if (interrupt_handlers[regs->int_no] != NULL) { + interrupt_handlers[regs->int_no](regs); + } + + // Acknowledge Hardware PIC Interrupts (IRQs 32-47) + if (regs->int_no >= 32 && regs->int_no < 48) { + pic_send_eoi((uint8_t)(regs->int_no - 32)); + } +} + +extern void* isr_stub_default; + +void isr_init(void) { + memset(interrupt_handlers, 0, sizeof(interrupt_handlers)); + + // 1. Initialize all 256 gates to isr_stub_default + for (int i = 0; i < 256; i++) { + idt_set_gate(i, isr_stub_default, 0x8E); + } + + // 2. Install dedicated CPU exception gates (0-31) and hardware IRQ gates (32-47) + for (uint8_t i = 0; i < 48; i++) { + idt_set_gate(i, isr_stub_table[i], 0x8E); + } +} diff --git a/arch/x86_64/cpu/isr_stubs.asm b/arch/x86_64/cpu/isr_stubs.asm new file mode 100644 index 0000000..6365756 --- /dev/null +++ b/arch/x86_64/cpu/isr_stubs.asm @@ -0,0 +1,133 @@ +[BITS 64] + +extern isr_handler_common +global isr_stub_table +global isr_stub_default + +%macro ISR_NOERRCODE 1 +isr_stub_%1: + push qword 0 ; Dummy error code + push qword %1 ; Interrupt vector number + jmp isr_common_stub +%endmacro + +%macro ISR_ERRCODE 1 +isr_stub_%1: + push qword %1 ; Interrupt vector number (error code already on stack) + jmp isr_common_stub +%endmacro + +section .text + +; CPU Exceptions 0-31 +ISR_NOERRCODE 0 +ISR_NOERRCODE 1 +ISR_NOERRCODE 2 +ISR_NOERRCODE 3 +ISR_NOERRCODE 4 +ISR_NOERRCODE 5 +ISR_NOERRCODE 6 +ISR_NOERRCODE 7 +ISR_ERRCODE 8 +ISR_NOERRCODE 9 +ISR_ERRCODE 10 +ISR_ERRCODE 11 +ISR_ERRCODE 12 +ISR_ERRCODE 13 +ISR_ERRCODE 14 +ISR_NOERRCODE 15 +ISR_NOERRCODE 16 +ISR_ERRCODE 17 +ISR_NOERRCODE 18 +ISR_NOERRCODE 19 +ISR_NOERRCODE 20 +ISR_NOERRCODE 21 +ISR_NOERRCODE 22 +ISR_NOERRCODE 23 +ISR_NOERRCODE 24 +ISR_NOERRCODE 25 +ISR_NOERRCODE 26 +ISR_NOERRCODE 27 +ISR_NOERRCODE 28 +ISR_NOERRCODE 29 +ISR_ERRCODE 30 +ISR_NOERRCODE 31 + +; Hardware IRQs 32-47 +ISR_NOERRCODE 32 +ISR_NOERRCODE 33 +ISR_NOERRCODE 34 +ISR_NOERRCODE 35 +ISR_NOERRCODE 36 +ISR_NOERRCODE 37 +ISR_NOERRCODE 38 +ISR_NOERRCODE 39 +ISR_NOERRCODE 40 +ISR_NOERRCODE 41 +ISR_NOERRCODE 42 +ISR_NOERRCODE 43 +ISR_NOERRCODE 44 +ISR_NOERRCODE 45 +ISR_NOERRCODE 46 +ISR_NOERRCODE 47 + +isr_stub_default: + push qword 0 + push qword 255 + jmp isr_common_stub + +isr_common_stub: + push rax + push rbx + push rcx + push rdx + push rsi + push rdi + push rbp + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + + mov rdi, rsp + cld + call isr_handler_common + + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rbp + pop rdi + pop rsi + pop rdx + pop rcx + pop rbx + pop rax + + add rsp, 16 + iretq + +section .data +align 8 +isr_stub_table: + dq isr_stub_0, isr_stub_1, isr_stub_2, isr_stub_3 + dq isr_stub_4, isr_stub_5, isr_stub_6, isr_stub_7 + dq isr_stub_8, isr_stub_9, isr_stub_10, isr_stub_11 + dq isr_stub_12, isr_stub_13, isr_stub_14, isr_stub_15 + dq isr_stub_16, isr_stub_17, isr_stub_18, isr_stub_19 + dq isr_stub_20, isr_stub_21, isr_stub_22, isr_stub_23 + dq isr_stub_24, isr_stub_25, isr_stub_26, isr_stub_27 + dq isr_stub_28, isr_stub_29, isr_stub_30, isr_stub_31 + dq isr_stub_32, isr_stub_33, isr_stub_34, isr_stub_35 + dq isr_stub_36, isr_stub_37, isr_stub_38, isr_stub_39 + dq isr_stub_40, isr_stub_41, isr_stub_42, isr_stub_43 + dq isr_stub_44, isr_stub_45, isr_stub_46, isr_stub_47 diff --git a/arch/x86_64/cpu/pic.c b/arch/x86_64/cpu/pic.c new file mode 100644 index 0000000..5789e74 --- /dev/null +++ b/arch/x86_64/cpu/pic.c @@ -0,0 +1,78 @@ +#include +#include + +#define PIC1_COMMAND 0x20 +#define PIC1_DATA 0x21 +#define PIC2_COMMAND 0xA0 +#define PIC2_DATA 0xA1 + +#define PIC_EOI 0x20 + +#define ICW1_INIT 0x10 +#define ICW1_ICW4 0x01 +#define ICW4_8086 0x01 + +void pic_send_eoi(uint8_t irq) { + if (irq >= 8) { + outb(PIC2_COMMAND, PIC_EOI); + } + outb(PIC1_COMMAND, PIC_EOI); +} + +void pic_init(void) { + outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); + io_wait(); + outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); + io_wait(); + + // Remap Master PIC to 0x20 (32), Slave PIC to 0x28 (40) + outb(PIC1_DATA, 0x20); + io_wait(); + outb(PIC2_DATA, 0x28); + io_wait(); + + outb(PIC1_DATA, 4); // Tell Master that Slave is at IRQ2 + io_wait(); + outb(PIC2_DATA, 2); // Tell Slave its cascade identity + io_wait(); + + outb(PIC1_DATA, ICW4_8086); + io_wait(); + outb(PIC2_DATA, ICW4_8086); + io_wait(); + + // Mask all interrupts initially except Cascade (IRQ2) + outb(PIC1_DATA, 0xFB); + outb(PIC2_DATA, 0xFF); +} + +void pic_set_mask(uint8_t irq) { + uint16_t port; + if (irq < 8) { + port = PIC1_DATA; + } else { + port = PIC2_DATA; + irq -= 8; + } + uint8_t value = inb(port) | (1 << irq); + outb(port, value); +} + +void pic_clear_mask(uint8_t irq) { + uint16_t port; + if (irq < 8) { + port = PIC1_DATA; + } else { + port = PIC2_DATA; + irq -= 8; + uint8_t master_val = inb(PIC1_DATA) & ~(1 << 2); + outb(PIC1_DATA, master_val); + } + uint8_t value = inb(port) & ~(1 << irq); + outb(port, value); +} + +void pic_disable(void) { + outb(PIC1_DATA, 0xFF); + outb(PIC2_DATA, 0xFF); +} diff --git a/arch/x86_64/cpu/pit.c b/arch/x86_64/cpu/pit.c new file mode 100644 index 0000000..3339e1d --- /dev/null +++ b/arch/x86_64/cpu/pit.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include + +#define PIT_COMMAND_PORT 0x43 +#define PIT_CHANNEL0_PORT 0x40 +#define PIT_BASE_FREQUENCY 1193182 + +static volatile uint64_t pit_ticks = 0; +static uint32_t pit_freq = 100; + +static void pit_handler(registers_t* regs) { + (void)regs; + pit_ticks++; + + // Notify preemptive scheduler + sched_tick(); +} + +void pit_init(uint32_t frequency) { + pit_freq = frequency; + uint32_t divisor = PIT_BASE_FREQUENCY / frequency; + + // Channel 0, lobyte/hibyte, Rate generator (Mode 2) + outb(PIT_COMMAND_PORT, 0x36); + outb(PIT_CHANNEL0_PORT, (uint8_t)(divisor & 0xFF)); + outb(PIT_CHANNEL0_PORT, (uint8_t)((divisor >> 8) & 0xFF)); + + // Register IRQ0 handler + isr_register_handler(32, pit_handler); + pic_clear_mask(0); +} + +uint64_t pit_get_ticks(void) { + return pit_ticks; +} + +uint64_t pit_get_uptime_ms(void) { + return (pit_ticks * 1000) / pit_freq; +} + +void pit_sleep(uint32_t milliseconds) { + uint64_t target = pit_ticks + (milliseconds * pit_freq) / 1000; + while (pit_ticks < target) { + hlt(); + } +} diff --git a/arch/x86_64/mm/paging.c b/arch/x86_64/mm/paging.c new file mode 100644 index 0000000..91fcc27 --- /dev/null +++ b/arch/x86_64/mm/paging.c @@ -0,0 +1,92 @@ +#include +#include +#include + +static pml4e_t* kernel_pml4 = (pml4e_t*)0x1000; + +void paging_flush_tlb_single(uint64_t virt) { + __asm__ volatile("invlpg (%0)" : : "r"(virt) : "memory"); +} + +void paging_init(void) { + // PML4 is already loaded at 0x1000 by Stage 2 bootloader with 4GB mapped + kernel_pml4 = (pml4e_t*)0x1000; +} + +void paging_map_page(uint64_t virt, uint64_t phys, uint64_t flags) { + size_t pml4_idx = (virt >> 39) & 0x1FF; + size_t pdpt_idx = (virt >> 30) & 0x1FF; + size_t pd_idx = (virt >> 21) & 0x1FF; + size_t pt_idx = (virt >> 12) & 0x1FF; + + if (!(kernel_pml4[pml4_idx] & PAGE_PRESENT)) { + void* new_pdpt = pmm_alloc_page(); + if (!new_pdpt) return; + memset(new_pdpt, 0, PAGE_SIZE); + kernel_pml4[pml4_idx] = (uint64_t)new_pdpt | PAGE_PRESENT | PAGE_WRITABLE | (flags & PAGE_USER); + } + + pdpte_t* pdpt = (pdpte_t*)(kernel_pml4[pml4_idx] & ~0xFFFULL); + if (!(pdpt[pdpt_idx] & PAGE_PRESENT)) { + void* new_pd = pmm_alloc_page(); + if (!new_pd) return; + memset(new_pd, 0, PAGE_SIZE); + pdpt[pdpt_idx] = (uint64_t)new_pd | PAGE_PRESENT | PAGE_WRITABLE | (flags & PAGE_USER); + } + + pde_t* pd = (pde_t*)(pdpt[pdpt_idx] & ~0xFFFULL); + if (!(pd[pd_idx] & PAGE_PRESENT)) { + void* new_pt = pmm_alloc_page(); + if (!new_pt) return; + memset(new_pt, 0, PAGE_SIZE); + pd[pd_idx] = (uint64_t)new_pt | PAGE_PRESENT | PAGE_WRITABLE | (flags & PAGE_USER); + } + + pte_t* pt = (pte_t*)(pd[pd_idx] & ~0xFFFULL); + pt[pt_idx] = (phys & ~0xFFFULL) | flags | PAGE_PRESENT; + + paging_flush_tlb_single(virt); +} + +void paging_unmap_page(uint64_t virt) { + size_t pml4_idx = (virt >> 39) & 0x1FF; + size_t pdpt_idx = (virt >> 30) & 0x1FF; + size_t pd_idx = (virt >> 21) & 0x1FF; + size_t pt_idx = (virt >> 12) & 0x1FF; + + if (!(kernel_pml4[pml4_idx] & PAGE_PRESENT)) return; + pdpte_t* pdpt = (pdpte_t*)(kernel_pml4[pml4_idx] & ~0xFFFULL); + + if (!(pdpt[pdpt_idx] & PAGE_PRESENT)) return; + pde_t* pd = (pde_t*)(pdpt[pdpt_idx] & ~0xFFFULL); + + if (!(pd[pd_idx] & PAGE_PRESENT)) return; + pte_t* pt = (pte_t*)(pd[pd_idx] & ~0xFFFULL); + + pt[pt_idx] = 0; + paging_flush_tlb_single(virt); +} + +uint64_t paging_get_phys(uint64_t virt) { + size_t pml4_idx = (virt >> 39) & 0x1FF; + size_t pdpt_idx = (virt >> 30) & 0x1FF; + size_t pd_idx = (virt >> 21) & 0x1FF; + size_t pt_idx = (virt >> 12) & 0x1FF; + + if (!(kernel_pml4[pml4_idx] & PAGE_PRESENT)) return 0; + pdpte_t* pdpt = (pdpte_t*)(kernel_pml4[pml4_idx] & ~0xFFFULL); + + if (!(pdpt[pdpt_idx] & PAGE_PRESENT)) return 0; + pde_t* pd = (pde_t*)(pdpt[pdpt_idx] & ~0xFFFULL); + + // Check for 2MB huge page + if (pd[pd_idx] & PAGE_HUGE) { + return (pd[pd_idx] & ~0x1FFFFFULL) | (virt & 0x1FFFFF); + } + + if (!(pd[pd_idx] & PAGE_PRESENT)) return 0; + pte_t* pt = (pte_t*)(pd[pd_idx] & ~0xFFFULL); + + if (!(pt[pt_idx] & PAGE_PRESENT)) return 0; + return (pt[pt_idx] & ~0xFFFULL) | (virt & 0xFFF); +} diff --git a/boot/boot.asm b/boot/boot.asm new file mode 100644 index 0000000..d6be4dd --- /dev/null +++ b/boot/boot.asm @@ -0,0 +1,98 @@ +BITS 16 +ORG 0x7C00 + +start: + ; Set up segments and stack + cli + xor ax, ax + mov ds, ax + mov es, ax + mov ss, ax + mov sp, 0x7C00 + sti + + ; Save boot drive number from BIOS (DL) + mov [BOOT_DRIVE], dl + + ; Print greeting + mov si, MSG_BOOT + call print_string + + ; Check for LBA extensions (INT 13h, AH=41h) + mov ah, 0x41 + mov bx, 0x55AA + mov dl, [BOOT_DRIVE] + int 0x13 + jc load_chs + cmp bx, 0xAA55 + jne load_chs + +load_lba: + ; Load stage 2 using LBA (INT 13h, AH=42h) + ; Read 15 sectors starting from LBA 1 to 0x0000:0x7E00 + mov ah, 0x42 + mov dl, [BOOT_DRIVE] + mov si, dap + int 0x13 + jc disk_error + jmp jump_stage2 + +load_chs: + ; Fallback: Load stage 2 using CHS (INT 13h, AH=02h) + mov ah, 0x02 + mov al, 15 ; 15 sectors (sectors 2-16) + mov ch, 0 ; Cylinder 0 + mov dh, 0 ; Head 0 + mov cl, 2 ; Sector 2 + xor bx, bx + mov es, bx + mov bx, 0x7E00 ; ES:BX = 0x0000:0x7E00 + mov dl, [BOOT_DRIVE] + int 0x13 + jc disk_error + +jump_stage2: + ; Pass boot drive in DL to Stage 2 + mov dl, [BOOT_DRIVE] + jmp 0x0000:0x7E00 + +disk_error: + mov si, MSG_DISK_ERR + call print_string +.halt: + cli + hlt + jmp .halt + +; BIOS Teletype string print (SI = null-terminated string) +print_string: + pusha + mov ah, 0x0E +.loop: + lodsb + test al, al + jz .done + int 0x10 + jmp .loop +.done: + popa + ret + +; Data +BOOT_DRIVE db 0 +MSG_BOOT db "SUB-OS Bootloader...", 13, 10, 0 +MSG_DISK_ERR db "Boot Disk Error!", 13, 10, 0 + +; Disk Address Packet for LBA +align 4 +dap: + db 0x10 ; DAP size (16 bytes) + db 0 ; Reserved + dw 15 ; Read 15 sectors + dw 0x7E00 ; Offset + dw 0x0000 ; Segment + dq 1 ; Starting LBA (Sector 1) + +; Boot Sector Signature +times 510 - ($ - $$) db 0 +dw 0xAA55 diff --git a/boot/gdt.asm b/boot/gdt.asm new file mode 100644 index 0000000..f4d4c05 --- /dev/null +++ b/boot/gdt.asm @@ -0,0 +1,43 @@ +; Global Descriptor Table Definition +align 16 +gdt_start: + ; Null descriptor (0x00) + dq 0 + + ; 32-bit Code Segment (0x08) + dw 0xFFFF ; Limit (low 16 bits) + dw 0x0000 ; Base (low 16 bits) + db 0x00 ; Base (next 8 bits) + db 10011010b ; Access byte (Present, Ring 0, Code/Data, Executable, Read/Write) + db 11001111b ; Flags (32-bit, 4KB granularity) and Limit (high 4 bits) + db 0x00 ; Base (high 8 bits) + + ; 32-bit Data Segment (0x10) + dw 0xFFFF ; Limit + dw 0x0000 ; Base + db 0x00 ; Base + db 10010010b ; Access byte (Present, Ring 0, Code/Data, Read/Write) + db 11001111b ; Flags and Limit + db 0x00 ; Base + + ; 64-bit Code Segment (0x18) + dw 0x0000 ; Limit (ignored) + dw 0x0000 ; Base + db 0x00 ; Base + db 10011010b ; Access byte (Present, Ring 0, Code/Data, Executable, Read/Write) + db 00100000b ; Flags (64-bit code segment, Long Mode) and Limit + db 0x00 ; Base + + ; 64-bit Data Segment (0x20) + dw 0x0000 ; Limit (ignored) + dw 0x0000 ; Base + db 0x00 ; Base + db 10010010b ; Access byte (Present, Ring 0, Code/Data, Read/Write) + db 00000000b ; Flags and Limit + db 0x00 ; Base + +gdt_end: + +gdt_descriptor: + dw gdt_end - gdt_start - 1 ; Size of GDT minus 1 + dd gdt_start ; Base address of GDT diff --git a/boot/stage2.asm b/boot/stage2.asm new file mode 100644 index 0000000..803eea8 --- /dev/null +++ b/boot/stage2.asm @@ -0,0 +1,272 @@ +BITS 16 +ORG 0x7E00 + +start2: + cli + ; Ensure segment registers are zero + xor ax, ax + mov ds, ax + mov es, ax + mov ss, ax + mov sp, 0x7C00 + sti + + ; Save boot drive number passed from stage 1 in DL + mov [boot_drive], dl + + ; Print Stage 2 startup message + mov si, msg_stage2_start + call print_string_16 + + ; ------------------------------------------------------------- + ; Step 1: Enable A20 Line + ; ------------------------------------------------------------- + mov si, msg_a20 + call print_string_16 + + in al, 0x92 + test al, 2 + jnz .a20_done + or al, 2 + and al, 0xFE + out 0x92, al + +.a20_done: + ; ------------------------------------------------------------- + ; Step 2: Detect Memory via INT 15h, EAX=0xE820 + ; Destination: 0x9000 (count at 0x9000, entries at 0x9008) + ; ------------------------------------------------------------- + mov si, msg_mmap + call print_string_16 + + ; Zero out count at 0x9000 + mov dword [0x9000], 0 + mov dword [0x9004], 0 + + mov di, 0x9008 ; Memory map entries destination + xor ebx, ebx ; Continuation value + xor bp, bp ; Entry counter + mov edx, 0x534D4150 ; 'SMAP' signature + mov eax, 0xE820 + mov ecx, 24 ; Buffer size + int 0x15 + jc .mmap_error + +.mmap_loop: + inc bp + add di, 24 + test ebx, ebx + jz .mmap_finished + mov edx, 0x534D4150 + mov eax, 0xE820 + mov ecx, 24 + int 0x15 + jc .mmap_finished + jmp .mmap_loop + +.mmap_error: + ; If E820 failed, create 1 fallback entry (128MB usable) + mov dword [0x9000], 1 + mov dword [0x9004], 0 + mov dword [0x9008], 0x100000 ; base = 1MB + mov dword [0x900C], 0 + mov dword [0x9010], 0x07F00000 ; length = 127MB + mov dword [0x9014], 0 + mov dword [0x9018], 1 ; type = 1 (USABLE) + mov dword [0x901C], 1 + jmp .mmap_store_done + +.mmap_finished: + mov word [0x9000], bp + mov word [0x9002], 0 + mov dword [0x9004], 0 + +.mmap_store_done: + ; ------------------------------------------------------------- + ; Step 3: Load Kernel from Disk to 0x10000 (Conventional RAM) + ; 4 chunks x 64 sectors = 256 sectors (128 KB) + ; ------------------------------------------------------------- + mov si, msg_kernel_load + call print_string_16 + + mov word [kernel_dap_off], 0x0000 + mov word [kernel_dap_seg], 0x1000 + mov dword [kernel_dap_lba], 16 + mov dword [kernel_dap_lba + 4], 0 + mov cx, 4 ; 4 chunks * 64 sectors = 256 sectors (128 KB) + +.kernel_chunk_loop: + push cx + mov ah, 0x42 + mov dl, [boot_drive] + mov si, kernel_dap + int 0x13 + jc .kernel_disk_error + + ; Advance LBA by 64 sectors + add dword [kernel_dap_lba], 64 + ; Advance Segment by 0x800 (64 sectors * 512 bytes = 32768 bytes = 0x800 paragraphs) + add word [kernel_dap_seg], 0x0800 + pop cx + loop .kernel_chunk_loop + +.kernel_loaded_ok: + ; ------------------------------------------------------------- + ; Step 4: Set up Identity Paging for Long Mode (All 4 GB) + ; ------------------------------------------------------------- + ; Clear 24 KB for 6 page tables (0x1000 - 0x6FFF) + mov ax, 0x0000 + mov es, ax + mov di, 0x1000 + mov cx, 6144 + xor eax, eax + rep stosd + + ; PML4[0] -> PDPT at 0x2000 + mov dword [0x1000], 0x2003 + mov dword [0x1004], 0 + + ; PDPT -> 4 PDs + mov dword [0x2000], 0x3003 + mov dword [0x2004], 0 + mov dword [0x2008], 0x4003 + mov dword [0x200C], 0 + mov dword [0x2010], 0x5003 + mov dword [0x2014], 0 + mov dword [0x2018], 0x6003 + mov dword [0x201C], 0 + + ; Map 4GB (2048 x 2MB pages) + mov di, 0x3000 + mov eax, 0x00000083 + mov cx, 2048 +.map_4gb_loop: + mov [di], eax + mov dword [di + 4], 0 + add eax, 0x200000 + add di, 8 + loop .map_4gb_loop + + ; ------------------------------------------------------------- + ; Step 5: Switch to 32-bit Protected Mode + ; ------------------------------------------------------------- + mov si, msg_entering_pm + call print_string_16 + + cli + lgdt [gdt_descriptor] + + mov eax, cr0 + or eax, 1 + mov cr0, eax + + jmp 0x08:pm_entry + +.kernel_disk_error: + mov si, msg_kernel_err + call print_string_16 +.halt16: + cli + hlt + jmp .halt16 + +print_string_16: + pusha + mov ah, 0x0E +.p_loop: + lodsb + test al, al + jz .p_done + int 0x10 + jmp .p_loop +.p_done: + popa + ret + +; ----------------------------------------------------------------- +; 32-Bit Protected Mode Entry Point +; ----------------------------------------------------------------- +BITS 32 +pm_entry: + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + mov esp, 0x7C00 + + ; Copy kernel from 0x10000 to 0x100000 (1MB mark) - 256 KB (512 sectors) + mov esi, 0x10000 + mov edi, 0x100000 + mov ecx, (512 * 512) / 4 + rep movsd + + ; Enable PAE in CR4 + mov eax, cr4 + or eax, (1 << 5) + mov cr4, eax + + ; Load CR3 + mov eax, 0x1000 + mov cr3, eax + + ; Enable Long Mode in EFER MSR + mov ecx, 0xC0000080 + rdmsr + or eax, (1 << 8) + wrmsr + + ; Enable Paging and Protected Mode + mov eax, cr0 + or eax, (1 << 31) | (1 << 0) + mov cr0, eax + + jmp 0x18:lm_entry + +; ----------------------------------------------------------------- +; 64-Bit Long Mode Entry Point +; ----------------------------------------------------------------- +BITS 64 +lm_entry: + mov ax, 0x20 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + ; Arguments: RDI = 0x9008 (entries), RSI = count at 0x9000 + mov rdi, 0x9008 + mov rsi, [0x9000] + + mov rax, 0x100000 + jmp rax + +; ----------------------------------------------------------------- +; Data Section +; ----------------------------------------------------------------- +BITS 16 +align 4 +boot_drive: db 0 + +msg_stage2_start: db "Stage 2: Loaded", 13, 10, 0 +msg_a20: db "Stage 2: A20 enabled", 13, 10, 0 +msg_mmap: db "Stage 2: Memory map detected", 13, 10, 0 +msg_kernel_load: db "Stage 2: Loading kernel...", 13, 10, 0 +msg_entering_pm: db "Stage 2: Jumping to 64-bit mode...", 13, 10, 0 +msg_kernel_err: db "Stage 2: Kernel Disk Read Error!", 13, 10, 0 + +align 4 +kernel_dap: + db 0x10 + db 0 + dw 64 ; 64 sectors per chunk (32 KB) +kernel_dap_off: + dw 0x0000 +kernel_dap_seg: + dw 0x1000 +kernel_dap_lba: + dq 16 + +%include "gdt.asm" diff --git a/crypto/crc32.c b/crypto/crc32.c new file mode 100644 index 0000000..971ccbd --- /dev/null +++ b/crypto/crc32.c @@ -0,0 +1,35 @@ +#include + +static uint32_t crc32_table[256]; +static bool table_computed = false; + +static void make_crc32_table(void) { + uint32_t c; + for (uint32_t n = 0; n < 256; n++) { + c = n; + for (int k = 0; k < 8; k++) { + if (c & 1) { + c = 0xEDB88320L ^ (c >> 1); + } else { + c = c >> 1; + } + } + crc32_table[n] = c; + } + table_computed = true; +} + +uint32_t crc32(uint32_t crc, const void* buf, size_t size) { + if (!table_computed) { + make_crc32_table(); + } + + const uint8_t* p = (const uint8_t*)buf; + crc = crc ^ 0xFFFFFFFF; + + while (size--) { + crc = crc32_table[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + } + + return crc ^ 0xFFFFFFFF; +} diff --git a/crypto/md5.c b/crypto/md5.c new file mode 100644 index 0000000..59e0753 --- /dev/null +++ b/crypto/md5.c @@ -0,0 +1,186 @@ +#include +#include +#include + +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT((a), (s)); \ + (a) += (b); \ +} +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT((a), (s)); \ + (a) += (b); \ +} +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT((a), (s)); \ + (a) += (b); \ +} +#define II(a, b, c, d, x, s, ac) { \ + (a) += I((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT((a), (s)); \ + (a) += (b); \ +} + +static void md5_transform(uint32_t state[4], const uint8_t block[64]) { + uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + for (int i = 0, j = 0; j < 64; i++, j += 4) { + x[i] = ((uint32_t)block[j]) | (((uint32_t)block[j + 1]) << 8) | + (((uint32_t)block[j + 2]) << 16) | (((uint32_t)block[j + 3]) << 24); + } + + // Round 1 + FF(a, b, c, d, x[0], 7, 0xd76aa478); + FF(d, a, b, c, x[1], 12, 0xe8c7b756); + FF(c, d, a, b, x[2], 17, 0x242070db); + FF(b, c, d, a, x[3], 22, 0xc1bdceee); + FF(a, b, c, d, x[4], 7, 0xf57c0faf); + FF(d, a, b, c, x[5], 12, 0x4787c62a); + FF(c, d, a, b, x[6], 17, 0xa8304613); + FF(b, c, d, a, x[7], 22, 0xfd469501); + FF(a, b, c, d, x[8], 7, 0x698098d8); + FF(d, a, b, c, x[9], 12, 0x8b44f7af); + FF(c, d, a, b, x[10], 17, 0xffff5bb1); + FF(b, c, d, a, x[11], 22, 0x895cd7be); + FF(a, b, c, d, x[12], 7, 0x6b901122); + FF(d, a, b, c, x[13], 12, 0xfd987193); + FF(c, d, a, b, x[14], 17, 0xa679438e); + FF(b, c, d, a, x[15], 22, 0x49b40821); + + // Round 2 + GG(a, b, c, d, x[1], 5, 0xf61e2562); + GG(d, a, b, c, x[6], 9, 0xc040b340); + GG(c, d, a, b, x[11], 14, 0x265e5a51); + GG(b, c, d, a, x[0], 20, 0xe9b6c7aa); + GG(a, b, c, d, x[5], 5, 0xd62f105d); + GG(d, a, b, c, x[10], 9, 0x02441453); + GG(c, d, a, b, x[15], 14, 0xd8a1e681); + GG(b, c, d, a, x[4], 20, 0xe7d3fbc8); + GG(a, b, c, d, x[9], 5, 0x21e1cde6); + GG(d, a, b, c, x[14], 9, 0xc33707d6); + GG(c, d, a, b, x[3], 14, 0xf4d50d87); + GG(b, c, d, a, x[8], 20, 0x455a14ed); + GG(a, b, c, d, x[13], 5, 0xa9e3e905); + GG(d, a, b, c, x[2], 9, 0xfcefa3f8); + GG(c, d, a, b, x[7], 14, 0x676f02d9); + GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); + + // Round 3 + HH(a, b, c, d, x[5], 4, 0xfffa3942); + HH(d, a, b, c, x[8], 11, 0x8771f681); + HH(c, d, a, b, x[11], 16, 0x6d9d6122); + HH(b, c, d, a, x[14], 23, 0xfde5380c); + HH(a, b, c, d, x[1], 4, 0xa4beea44); + HH(d, a, b, c, x[4], 11, 0x4bdecfa9); + HH(c, d, a, b, x[7], 16, 0xf6bb4b60); + HH(b, c, d, a, x[10], 23, 0xbebfbc70); + HH(a, b, c, d, x[13], 4, 0x289b7ec6); + HH(d, a, b, c, x[0], 11, 0xeaa127fa); + HH(c, d, a, b, x[3], 16, 0xd4ef3085); + HH(b, c, d, a, x[6], 23, 0x04881d05); + HH(a, b, c, d, x[9], 4, 0xd9d4d039); + HH(d, a, b, c, x[12], 11, 0xe6db99e5); + HH(c, d, a, b, x[15], 16, 0x1fa27cf8); + HH(b, c, d, a, x[2], 23, 0xc4ac5665); + + // Round 4 + II(a, b, c, d, x[0], 6, 0xf4292244); + II(d, a, b, c, x[7], 10, 0x432aff97); + II(c, d, a, b, x[14], 15, 0xab9423a7); + II(b, c, d, a, x[5], 21, 0xfc93a039); + II(a, b, c, d, x[12], 6, 0x655b59c3); + II(d, a, b, c, x[3], 10, 0x8f0ccc92); + II(c, d, a, b, x[10], 15, 0xffeff47d); + II(b, c, d, a, x[1], 21, 0x85845dd1); + II(a, b, c, d, x[8], 6, 0x6fa87e4f); + II(d, a, b, c, x[15], 10, 0xfe2ce6e0); + II(c, d, a, b, x[6], 15, 0xa3014314); + II(b, c, d, a, x[13], 21, 0x4e0811a1); + II(a, b, c, d, x[4], 6, 0xf7537e82); + II(d, a, b, c, x[11], 10, 0xbd3af235); + II(c, d, a, b, x[2], 15, 0x2ad7d2bb); + II(b, c, d, a, x[9], 21, 0xeb86d391); + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} + +void md5_init(md5_ctx_t* ctx) { + ctx->count[0] = ctx->count[1] = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xefcdab89; + ctx->state[2] = 0x98badcfe; + ctx->state[3] = 0x10325476; +} + +void md5_update(md5_ctx_t* ctx, const void* input, size_t input_len) { + uint32_t i, index, part_len; + const uint8_t* buf = (const uint8_t*)input; + + index = (uint32_t)((ctx->count[0] >> 3) & 0x3F); + if ((ctx->count[0] += ((uint32_t)input_len << 3)) < ((uint32_t)input_len << 3)) { + ctx->count[1]++; + } + ctx->count[1] += ((uint32_t)input_len >> 29); + part_len = 64 - index; + + if (input_len >= part_len) { + memcpy(&ctx->buffer[index], buf, part_len); + md5_transform(ctx->state, ctx->buffer); + for (i = part_len; i + 63 < input_len; i += 64) { + md5_transform(ctx->state, &buf[i]); + } + index = 0; + } else { + i = 0; + } + + memcpy(&ctx->buffer[index], &buf[i], input_len - i); +} + +void md5_final(uint8_t digest[16], md5_ctx_t* ctx) { + static const uint8_t PADDING[64] = { 0x80 }; + uint8_t bits[8]; + uint32_t index, pad_len; + + for (int i = 0; i < 4; i++) { + bits[i] = (uint8_t)((ctx->count[0] >> (i * 8)) & 0xFF); + bits[i + 4] = (uint8_t)((ctx->count[1] >> (i * 8)) & 0xFF); + } + + index = (uint32_t)((ctx->count[0] >> 3) & 0x3F); + pad_len = (index < 56) ? (56 - index) : (120 - index); + md5_update(ctx, PADDING, pad_len); + md5_update(ctx, bits, 8); + + for (int i = 0; i < 4; i++) { + digest[i] = (uint8_t)((ctx->state[0] >> (i * 8)) & 0xFF); + digest[i + 4] = (uint8_t)((ctx->state[1] >> (i * 8)) & 0xFF); + digest[i + 8] = (uint8_t)((ctx->state[2] >> (i * 8)) & 0xFF); + digest[i + 12] = (uint8_t)((ctx->state[3] >> (i * 8)) & 0xFF); + } +} + +void md5_string(const char* str, char output[33]) { + md5_ctx_t ctx; + uint8_t digest[16]; + md5_init(&ctx); + md5_update(&ctx, str, strlen(str)); + md5_final(digest, &ctx); + + for (int i = 0; i < 16; i++) { + sprintf(output + i * 2, "%02x", digest[i]); + } + output[32] = '\0'; +} diff --git a/crypto/prng.c b/crypto/prng.c new file mode 100644 index 0000000..8befbfa --- /dev/null +++ b/crypto/prng.c @@ -0,0 +1,43 @@ +#include +#include + +static uint64_t prng_s[2] = { 0x8a5cd789635d2dffULL, 0x121fd2155c472f96ULL }; + +void prng_seed(uint64_t seed1, uint64_t seed2) { + if (seed1 == 0 && seed2 == 0) { + seed1 = 0x8a5cd789635d2dffULL ^ pit_get_ticks(); + seed2 = 0x121fd2155c472f96ULL ^ (pit_get_ticks() << 32); + } + prng_s[0] = seed1; + prng_s[1] = seed2; +} + +uint64_t prng_rand64(void) { + uint64_t s1 = prng_s[0]; + const uint64_t s0 = prng_s[1]; + prng_s[0] = s0; + s1 ^= s1 << 23; + prng_s[1] = s1 ^ s0 ^ (s1 >> 17) ^ (s0 >> 26); + return prng_s[1] + s0; +} + +uint32_t prng_rand32(void) { + return (uint32_t)(prng_rand64() & 0xFFFFFFFF); +} + +void prng_get_bytes(void* buf, size_t len) { + uint8_t* p = (uint8_t*)buf; + while (len >= 8) { + uint64_t r = prng_rand64(); + *(uint64_t*)p = r; + p += 8; + len -= 8; + } + if (len > 0) { + uint64_t r = prng_rand64(); + uint8_t* r_bytes = (uint8_t*)&r; + for (size_t i = 0; i < len; i++) { + p[i] = r_bytes[i]; + } + } +} diff --git a/crypto/sha256.c b/crypto/sha256.c new file mode 100644 index 0000000..3524dba --- /dev/null +++ b/crypto/sha256.c @@ -0,0 +1,141 @@ +#include +#include +#include + +#define DBL_INT_ADD(a, b, c) if ((a) > 0xFFFFFFFF - (c)) ++(b); (a) += (c); +#define ROTRIGHT(a, b) (((a) >> (b)) | ((a) << (32 - (b)))) + +#define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22)) +#define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25)) +#define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3)) +#define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10)) + +static const uint32_t k[64] = { + 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, + 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, + 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, + 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, + 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, + 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, + 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, + 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 +}; + +static void sha256_transform(sha256_ctx_t* ctx, const uint8_t data[]) { + uint32_t a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; + + for (i = 0, j = 0; i < 16; ++i, j += 4) + m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); + for (; i < 64; ++i) + m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + f = ctx->state[5]; + g = ctx->state[6]; + h = ctx->state[7]; + + for (i = 0; i < 64; ++i) { + t1 = h + EP1(e) + CH(e, f, g) + k[i] + m[i]; + t2 = EP0(a) + MAJ(a, b, c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; + ctx->state[5] += f; + ctx->state[6] += g; + ctx->state[7] += h; +} + +void sha256_init(sha256_ctx_t* ctx) { + ctx->datalen = 0; + ctx->bitlen = 0; + ctx->state[0] = 0x6a09e667; + ctx->state[1] = 0xbb67ae85; + ctx->state[2] = 0x3c6ef372; + ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; + ctx->state[5] = 0x9b05688c; + ctx->state[6] = 0x1f83d9ab; + ctx->state[7] = 0x5be0cd19; +} + +void sha256_update(sha256_ctx_t* ctx, const void* data, size_t len) { + const uint8_t* p = (const uint8_t*)data; + for (size_t i = 0; i < len; ++i) { + ctx->buffer[ctx->datalen] = p[i]; + ctx->datalen++; + if (ctx->datalen == 64) { + sha256_transform(ctx, ctx->buffer); + ctx->bitlen += 512; + ctx->datalen = 0; + } + } +} + +void sha256_final(uint8_t hash[32], sha256_ctx_t* ctx) { + size_t i = ctx->datalen; + + if (ctx->datalen < 56) { + ctx->buffer[i++] = 0x80; + while (i < 56) + ctx->buffer[i++] = 0x00; + } else { + ctx->buffer[i++] = 0x80; + while (i < 64) + ctx->buffer[i++] = 0x00; + sha256_transform(ctx, ctx->buffer); + memset(ctx->buffer, 0, 56); + } + + ctx->bitlen += ctx->datalen * 8; + ctx->buffer[63] = (uint8_t)(ctx->bitlen); + ctx->buffer[62] = (uint8_t)(ctx->bitlen >> 8); + ctx->buffer[61] = (uint8_t)(ctx->bitlen >> 16); + ctx->buffer[60] = (uint8_t)(ctx->bitlen >> 24); + ctx->buffer[59] = (uint8_t)(ctx->bitlen >> 32); + ctx->buffer[58] = (uint8_t)(ctx->bitlen >> 40); + ctx->buffer[57] = (uint8_t)(ctx->bitlen >> 48); + ctx->buffer[56] = (uint8_t)(ctx->bitlen >> 56); + sha256_transform(ctx, ctx->buffer); + + for (i = 0; i < 4; ++i) { + hash[i] = (uint8_t)((ctx->state[0] >> (24 - i * 8)) & 0x000000ff); + hash[i + 4] = (uint8_t)((ctx->state[1] >> (24 - i * 8)) & 0x000000ff); + hash[i + 8] = (uint8_t)((ctx->state[2] >> (24 - i * 8)) & 0x000000ff); + hash[i + 12] = (uint8_t)((ctx->state[3] >> (24 - i * 8)) & 0x000000ff); + hash[i + 16] = (uint8_t)((ctx->state[4] >> (24 - i * 8)) & 0x000000ff); + hash[i + 20] = (uint8_t)((ctx->state[5] >> (24 - i * 8)) & 0x000000ff); + hash[i + 24] = (uint8_t)((ctx->state[6] >> (24 - i * 8)) & 0x000000ff); + hash[i + 28] = (uint8_t)((ctx->state[7] >> (24 - i * 8)) & 0x000000ff); + } +} + +void sha256_string(const char* str, char output[65]) { + sha256_ctx_t ctx; + uint8_t hash[32]; + sha256_init(&ctx); + sha256_update(&ctx, str, strlen(str)); + sha256_final(hash, &ctx); + + for (int i = 0; i < 32; i++) { + sprintf(output + i * 2, "%02x", hash[i]); + } + output[64] = '\0'; +} diff --git a/drivers/block/ata.c b/drivers/block/ata.c new file mode 100644 index 0000000..cd15a0d --- /dev/null +++ b/drivers/block/ata.c @@ -0,0 +1,135 @@ +#include +#include +#include +#include + +static ata_device_t primary_master; + +static void ata_wait_ready(uint16_t io_base) { + // 400ns delay + inb(io_base + 7); + inb(io_base + 7); + inb(io_base + 7); + inb(io_base + 7); + + while (inb(io_base + 7) & 0x80); // Wait while BSY (Busy) bit is set +} + +static void ata_wait_drq(uint16_t io_base) { + while (!(inb(io_base + 7) & 0x08)); // Wait while DRQ (Data Request) bit is clear +} + +bool ata_init(void) { + memset(&primary_master, 0, sizeof(ata_device_t)); + + // Select Primary Master (Drive 0, LBA mode) + outb(ATA_PRIMARY_IO + 6, 0xA0); + outb(ATA_PRIMARY_IO + 2, 0); + outb(ATA_PRIMARY_IO + 3, 0); + outb(ATA_PRIMARY_IO + 4, 0); + outb(ATA_PRIMARY_IO + 5, 0); + outb(ATA_PRIMARY_IO + 7, 0xEC); // IDENTIFY Command + + uint8_t status = inb(ATA_PRIMARY_IO + 7); + if (status == 0 || status == 0xFF) { + return false; // No drive + } + + ata_wait_ready(ATA_PRIMARY_IO); + + uint8_t mid = inb(ATA_PRIMARY_IO + 4); + uint8_t hi = inb(ATA_PRIMARY_IO + 5); + if (mid != 0 || hi != 0) { + return false; // ATAPI or not ATA + } + + ata_wait_drq(ATA_PRIMARY_IO); + + uint16_t identify_data[256]; + for (int i = 0; i < 256; i++) { + identify_data[i] = inw(ATA_PRIMARY_IO); + } + + primary_master.present = true; + primary_master.is_master = true; + primary_master.sector_count = (uint32_t)identify_data[60] | ((uint32_t)identify_data[61] << 16); + + // Model String (Words 27-46, byte-swapped) + char* model_ptr = (char*)&identify_data[27]; + for (int i = 0; i < 40; i += 2) { + primary_master.model[i] = model_ptr[i + 1]; + primary_master.model[i + 1] = model_ptr[i]; + } + primary_master.model[40] = '\0'; + + // Trim trailing spaces in model + for (int i = 39; i >= 0 && primary_master.model[i] == ' '; i--) { + primary_master.model[i] = '\0'; + } + + printk(KERN_INFO "ATA: Primary Master Disk: %s (%llu MB, %u sectors)\n", + primary_master.model, + ((uint64_t)primary_master.sector_count * 512) / (1024 * 1024), + primary_master.sector_count); + return true; +} + +bool ata_read_sectors(uint32_t lba, uint8_t count, void* buffer) { + if (!primary_master.present || !buffer || count == 0) return false; + + ata_wait_ready(ATA_PRIMARY_IO); + + outb(ATA_PRIMARY_IO + 6, 0xE0 | ((lba >> 24) & 0x0F)); // Master, LBA mode + outb(ATA_PRIMARY_IO + 2, count); + outb(ATA_PRIMARY_IO + 3, (uint8_t)lba); + outb(ATA_PRIMARY_IO + 4, (uint8_t)(lba >> 8)); + outb(ATA_PRIMARY_IO + 5, (uint8_t)(lba >> 16)); + outb(ATA_PRIMARY_IO + 7, 0x20); // READ SECTORS command + + uint16_t* ptr = (uint16_t*)buffer; + + for (uint8_t s = 0; s < count; s++) { + ata_wait_ready(ATA_PRIMARY_IO); + ata_wait_drq(ATA_PRIMARY_IO); + + for (int i = 0; i < 256; i++) { + *ptr++ = inw(ATA_PRIMARY_IO); + } + } + + return true; +} + +bool ata_write_sectors(uint32_t lba, uint8_t count, const void* buffer) { + if (!primary_master.present || !buffer || count == 0) return false; + + ata_wait_ready(ATA_PRIMARY_IO); + + outb(ATA_PRIMARY_IO + 6, 0xE0 | ((lba >> 24) & 0x0F)); + outb(ATA_PRIMARY_IO + 2, count); + outb(ATA_PRIMARY_IO + 3, (uint8_t)lba); + outb(ATA_PRIMARY_IO + 4, (uint8_t)(lba >> 8)); + outb(ATA_PRIMARY_IO + 5, (uint8_t)(lba >> 16)); + outb(ATA_PRIMARY_IO + 7, 0x30); // WRITE SECTORS command + + const uint16_t* ptr = (const uint16_t*)buffer; + + for (uint8_t s = 0; s < count; s++) { + ata_wait_ready(ATA_PRIMARY_IO); + ata_wait_drq(ATA_PRIMARY_IO); + + for (int i = 0; i < 256; i++) { + outw(ATA_PRIMARY_IO, *ptr++); + } + } + + // Flush Cache + outb(ATA_PRIMARY_IO + 7, 0xE7); + ata_wait_ready(ATA_PRIMARY_IO); + + return true; +} + +const ata_device_t* ata_get_primary_master(void) { + return &primary_master; +} diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c new file mode 100644 index 0000000..4fd1f6c --- /dev/null +++ b/drivers/char/keyboard.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include + +#define KEYBOARD_DATA_PORT 0x60 +#define KEYBOARD_STATUS_PORT 0x64 +#define KEYBOARD_BUFFER_SIZE 128 + +static volatile uint16_t key_buffer[KEYBOARD_BUFFER_SIZE]; +static volatile int buffer_head = 0; +static volatile int buffer_tail = 0; + +static bool shift_pressed = false; +static bool ctrl_pressed = false; +static bool alt_pressed = false; +static bool capslock = false; +static bool extended_code = false; + +static const char scancode_ascii[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', + 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', + 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, + '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const char scancode_ascii_shift[128] = { + 0, 27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', + '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', + 0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', + 0, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, + '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static void buffer_enqueue(uint16_t key) { + int next = (buffer_head + 1) % KEYBOARD_BUFFER_SIZE; + if (next != buffer_tail) { + key_buffer[buffer_head] = key; + buffer_head = next; + } +} + +static void keyboard_interrupt_handler(registers_t* regs) { + (void)regs; + uint8_t scancode = inb(KEYBOARD_DATA_PORT); + + if (scancode == 0xE0) { + extended_code = true; + return; + } + + if (scancode & 0x80) { // Key Release + uint8_t released = scancode & 0x7F; + if (released == 0x2A || released == 0x36) shift_pressed = false; + else if (released == 0x1D) ctrl_pressed = false; + else if (released == 0x38) alt_pressed = false; + extended_code = false; + return; + } + + // Key Press + if (scancode == 0x2A || scancode == 0x36) { shift_pressed = true; return; } + if (scancode == 0x1D) { ctrl_pressed = true; return; } + if (scancode == 0x38) { alt_pressed = true; return; } + if (scancode == 0x3A) { capslock = !capslock; return; } + + // Alt + F1..F4 -> Switch Virtual Consoles + if (alt_pressed) { + if (scancode >= 0x3B && scancode <= 0x3E) { // F1 - F4 + tty_switch(scancode - 0x3B); + return; + } + } + + if (extended_code) { + extended_code = false; + switch (scancode) { + case 0x48: buffer_enqueue(KEY_UP); return; + case 0x50: buffer_enqueue(KEY_DOWN); return; + case 0x4B: buffer_enqueue(KEY_LEFT); return; + case 0x4D: buffer_enqueue(KEY_RIGHT); return; + case 0x47: buffer_enqueue(KEY_HOME); return; + case 0x4F: buffer_enqueue(KEY_END); return; + case 0x49: buffer_enqueue(KEY_PAGE_UP); return; + case 0x51: buffer_enqueue(KEY_PAGE_DOWN); return; + case 0x52: buffer_enqueue(KEY_INSERT); return; + case 0x53: buffer_enqueue(KEY_DELETE); return; + } + } + + // Function Keys F1-F12 + if (scancode >= 0x3B && scancode <= 0x44) { + buffer_enqueue(KEY_F1 + (scancode - 0x3B)); + return; + } + + if (ctrl_pressed) { + if (scancode == 0x2E) { buffer_enqueue(0x03); return; } // Ctrl+C + if (scancode == 0x26) { buffer_enqueue(0x0C); return; } // Ctrl+L + if (scancode == 0x20) { buffer_enqueue(0x04); return; } // Ctrl+D + } + + bool use_shift = shift_pressed ^ capslock; + char c = use_shift ? scancode_ascii_shift[scancode] : scancode_ascii[scancode]; + if (c) { + buffer_enqueue((uint16_t)c); + } +} + +void keyboard_init(void) { + buffer_head = 0; + buffer_tail = 0; + + isr_register_handler(33, keyboard_interrupt_handler); // IRQ1 + pic_clear_mask(1); +} + +bool keyboard_has_key(void) { + return buffer_head != buffer_tail; +} + +uint16_t keyboard_get_key(void) { + while (!keyboard_has_key()) { + hlt(); + } + uint16_t key = key_buffer[buffer_tail]; + buffer_tail = (buffer_tail + 1) % KEYBOARD_BUFFER_SIZE; + return key; +} + +char keyboard_get_char(void) { + uint16_t key = keyboard_get_key(); + if (key & KEY_SPECIAL_FLAG) return 0; + return (char)(key & 0xFF); +} diff --git a/drivers/char/serial.c b/drivers/char/serial.c new file mode 100644 index 0000000..644e52e --- /dev/null +++ b/drivers/char/serial.c @@ -0,0 +1,39 @@ +#include +#include + +void serial_init(void) { + outb(COM1_PORT + 1, 0x00); // Disable all interrupts + outb(COM1_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) + outb(COM1_PORT + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud + outb(COM1_PORT + 1, 0x00); // (hi byte) + outb(COM1_PORT + 3, 0x03); // 8 bits, no parity, one stop bit + outb(COM1_PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold + outb(COM1_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set +} + +static bool serial_is_transmit_empty(void) { + return (inb(COM1_PORT + 5) & 0x20) != 0; +} + +void serial_write_char(char c) { + while (!serial_is_transmit_empty()); + outb(COM1_PORT, c); +} + +void serial_write_string(const char* str) { + while (*str) { + if (*str == '\n') { + serial_write_char('\r'); + } + serial_write_char(*str++); + } +} + +bool serial_received(void) { + return (inb(COM1_PORT + 5) & 1) != 0; +} + +char serial_read_char(void) { + while (!serial_received()); + return inb(COM1_PORT); +} diff --git a/drivers/char/tty.c b/drivers/char/tty.c new file mode 100644 index 0000000..8c884cf --- /dev/null +++ b/drivers/char/tty.c @@ -0,0 +1,166 @@ +#include +#include +#include + +typedef struct { + uint16_t buffer[TTY_HEIGHT][TTY_WIDTH]; + int cursor_row; + int cursor_col; + uint8_t current_color; +} tty_t; + +static tty_t ttys[MAX_TTYS]; +static int current_tty = 0; + +static volatile uint16_t* const VGA_MEM = (uint16_t*)VGA_MEMORY; + +static void tty_render_current(void) { + tty_t* t = &ttys[current_tty]; + for (int y = 0; y < TTY_HEIGHT; y++) { + for (int x = 0; x < TTY_WIDTH; x++) { + VGA_MEM[y * TTY_WIDTH + x] = t->buffer[y][x]; + } + } + vga_set_cursor(t->cursor_row, t->cursor_col); +} + +void tty_switch(int index) { + if (index < 0 || index >= MAX_TTYS) return; + current_tty = index; + tty_render_current(); +} + +int tty_get_current(void) { + return current_tty; +} + +static void tty_scroll(tty_t* t) { + for (int y = 0; y < TTY_HEIGHT - 1; y++) { + for (int x = 0; x < TTY_WIDTH; x++) { + t->buffer[y][x] = t->buffer[y + 1][x]; + } + } + for (int x = 0; x < TTY_WIDTH; x++) { + t->buffer[TTY_HEIGHT - 1][x] = vga_entry(' ', t->current_color); + } + t->cursor_row = TTY_HEIGHT - 1; +} + +void tty_clear(void) { + tty_t* t = &ttys[current_tty]; + for (int y = 0; y < TTY_HEIGHT; y++) { + for (int x = 0; x < TTY_WIDTH; x++) { + t->buffer[y][x] = vga_entry(' ', t->current_color); + } + } + t->cursor_row = 0; + t->cursor_col = 0; + tty_render_current(); +} + +void tty_set_cursor(int row, int col) { + tty_t* t = &ttys[current_tty]; + t->cursor_row = row; + t->cursor_col = col; + vga_set_cursor(row, col); +} + +void tty_write(const char* data, size_t size) { + tty_t* t = &ttys[current_tty]; + + for (size_t i = 0; i < size; i++) { + char c = data[i]; + + // Basic ANSI escape sequence parser (e.g. \033[31m) + if (c == '\033' && i + 1 < size && data[i + 1] == '[') { + i += 2; + int code = 0; + while (i < size && data[i] >= '0' && data[i] <= '9') { + code = code * 10 + (data[i] - '0'); + i++; + } + if (i < size && data[i] == 'm') { + if (code == 0) t->current_color = 0x07; // Reset + else if (code == 31) t->current_color = 0x04; // Red + else if (code == 32) t->current_color = 0x02; // Green + else if (code == 33) t->current_color = 0x06; // Brown/Yellow + else if (code == 34) t->current_color = 0x01; // Blue + else if (code == 35) t->current_color = 0x05; // Magenta + else if (code == 36) t->current_color = 0x03; // Cyan + else if (code == 37) t->current_color = 0x07; // White + else if (code == 90) t->current_color = 0x08; // Bright Black (Dark Grey) + else if (code == 91) t->current_color = 0x0C; // Bright Red + else if (code == 92) t->current_color = 0x0A; // Bright Green + else if (code == 93) t->current_color = 0x0E; // Bright Yellow + else if (code == 94) t->current_color = 0x09; // Bright Blue + else if (code == 95) t->current_color = 0x0D; // Bright Magenta + else if (code == 96) t->current_color = 0x0B; // Bright Cyan + else if (code == 97) t->current_color = 0x0F; // Bright White + } else if (i < size && data[i] == 'K') { + // Clear from cursor to end of line + for (int x = t->cursor_col; x < TTY_WIDTH; x++) { + t->buffer[t->cursor_row][x] = vga_entry(' ', t->current_color); + } + } else if (i < size && data[i] == 'D') { + // Move cursor left + if (t->cursor_col > 0) t->cursor_col--; + } else if (i < size && data[i] == 'C') { + // Move cursor right + if (t->cursor_col < TTY_WIDTH - 1) t->cursor_col++; + } + continue; + } + + if (c == '\n') { + t->cursor_col = 0; + if (++t->cursor_row >= TTY_HEIGHT) { + tty_scroll(t); + } + } else if (c == '\r') { + t->cursor_col = 0; + } else if (c == '\t') { + t->cursor_col = (t->cursor_col + 8) & ~7; + if (t->cursor_col >= TTY_WIDTH) { + t->cursor_col = 0; + if (++t->cursor_row >= TTY_HEIGHT) { + tty_scroll(t); + } + } + } else if (c == '\b') { + if (t->cursor_col > 0) { + t->cursor_col--; + t->buffer[t->cursor_row][t->cursor_col] = vga_entry(' ', t->current_color); + } + } else { + t->buffer[t->cursor_row][t->cursor_col] = vga_entry(c, t->current_color); + if (++t->cursor_col >= TTY_WIDTH) { + t->cursor_col = 0; + if (++t->cursor_row >= TTY_HEIGHT) { + tty_scroll(t); + } + } + } + } + + tty_render_current(); +} + +void tty_write_string(const char* data) { + tty_write(data, strlen(data)); +} + +void tty_init(void) { + vga_init(); + for (int i = 0; i < MAX_TTYS; i++) { + ttys[i].cursor_row = 0; + ttys[i].cursor_col = 0; + ttys[i].current_color = 0x07; + for (int y = 0; y < TTY_HEIGHT; y++) { + for (int x = 0; x < TTY_WIDTH; x++) { + ttys[i].buffer[y][x] = vga_entry(' ', 0x07); + } + } + } + current_tty = 0; + tty_render_current(); +} diff --git a/drivers/char/vga.c b/drivers/char/vga.c new file mode 100644 index 0000000..a697cac --- /dev/null +++ b/drivers/char/vga.c @@ -0,0 +1,106 @@ +#include +#include +#include + +static volatile uint16_t* const VGA_BUFFER = (uint16_t*)VGA_MEMORY; +static uint8_t vga_color = 0x07; +static int vga_row = 0; +static int vga_col = 0; + +void vga_set_cursor(int row, int col) { + vga_row = row; + vga_col = col; + uint16_t pos = row * VGA_WIDTH + col; + + outb(0x3D4, 0x0F); + outb(0x3D5, (uint8_t)(pos & 0xFF)); + outb(0x3D4, 0x0E); + outb(0x3D5, (uint8_t)((pos >> 8) & 0xFF)); +} + +void vga_get_cursor(int* row, int* col) { + if (row) *row = vga_row; + if (col) *col = vga_col; +} + +void vga_enable_cursor(uint8_t cursor_start, uint8_t cursor_end) { + outb(0x3D4, 0x0A); + outb(0x3D5, (inb(0x3D5) & 0xC0) | cursor_start); + outb(0x3D4, 0x0B); + outb(0x3D5, (inb(0x3D5) & 0xE0) | cursor_end); +} + +void vga_disable_cursor(void) { + outb(0x3D4, 0x0A); + outb(0x3D5, 0x20); +} + +void vga_set_color(enum vga_color fg, enum vga_color bg) { + vga_color = vga_entry_color(fg, bg); +} + +void vga_clear(void) { + for (int y = 0; y < VGA_HEIGHT; y++) { + for (int x = 0; x < VGA_WIDTH; x++) { + VGA_BUFFER[y * VGA_WIDTH + x] = vga_entry(' ', vga_color); + } + } + vga_set_cursor(0, 0); +} + +static void vga_scroll(void) { + for (int y = 0; y < VGA_HEIGHT - 1; y++) { + for (int x = 0; x < VGA_WIDTH; x++) { + VGA_BUFFER[y * VGA_WIDTH + x] = VGA_BUFFER[(y + 1) * VGA_WIDTH + x]; + } + } + for (int x = 0; x < VGA_WIDTH; x++) { + VGA_BUFFER[(VGA_HEIGHT - 1) * VGA_WIDTH + x] = vga_entry(' ', vga_color); + } + vga_row = VGA_HEIGHT - 1; +} + +void vga_putc(char c) { + if (c == '\n') { + vga_col = 0; + if (++vga_row >= VGA_HEIGHT) { + vga_scroll(); + } + } else if (c == '\r') { + vga_col = 0; + } else if (c == '\t') { + vga_col = (vga_col + 8) & ~7; + if (vga_col >= VGA_WIDTH) { + vga_col = 0; + if (++vga_row >= VGA_HEIGHT) { + vga_scroll(); + } + } + } else if (c == '\b') { + if (vga_col > 0) { + vga_col--; + VGA_BUFFER[vga_row * VGA_WIDTH + vga_col] = vga_entry(' ', vga_color); + } + } else { + VGA_BUFFER[vga_row * VGA_WIDTH + vga_col] = vga_entry(c, vga_color); + if (++vga_col >= VGA_WIDTH) { + vga_col = 0; + if (++vga_row >= VGA_HEIGHT) { + vga_scroll(); + } + } + } + vga_set_cursor(vga_row, vga_col); +} + +void vga_write(const char* str) { + while (*str) { + vga_putc(*str++); + } +} + +void vga_init(void) { + vga_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK); + vga_clear(); + vga_enable_cursor(14, 15); +} diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c new file mode 100644 index 0000000..1b6019f --- /dev/null +++ b/drivers/net/e1000.c @@ -0,0 +1,252 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_CTRL 0x0000 +#define REG_STATUS 0x0008 +#define REG_EEPROM 0x0014 +#define REG_ICR 0x00C0 +#define REG_IMS 0x00D0 +#define REG_IMC 0x00D8 +#define REG_RCTL 0x0100 +#define REG_RDBAL 0x2800 +#define REG_RDBAH 0x2804 +#define REG_RDLEN 0x2808 +#define REG_RDH 0x2810 +#define REG_RDT 0x2818 +#define REG_TCTL 0x0400 +#define REG_TDBAL 0x3800 +#define REG_TDBAH 0x3804 +#define REG_TDLEN 0x3808 +#define REG_TDH 0x3810 +#define REG_TDT 0x3818 + +#define NUM_RX_DESCRIPTORS 32 +#define NUM_TX_DESCRIPTORS 16 +#define RX_BUFFER_SIZE 2048 + +typedef struct { + uint64_t buffer_addr; + uint16_t length; + uint16_t checksum; + uint8_t status; + uint8_t errors; + uint16_t special; +} __attribute__((packed)) e1000_rx_desc_t; + +typedef struct { + uint64_t buffer_addr; + uint16_t length; + uint8_t cso; + uint8_t cmd; + uint8_t status; + uint8_t css; + uint16_t special; +} __attribute__((packed)) e1000_tx_desc_t; + +static pci_device_t* e1000_pci_dev = NULL; +static uint64_t mmio_base = 0; +static uint16_t io_base = 0; +static bool use_mmio = true; +static uint8_t mac_addr[6] = {0x52, 0x54, 0x00, 0x12, 0x34, 0x56}; + +static e1000_rx_desc_t* rx_descriptors = NULL; +static uint8_t* rx_buffers[NUM_RX_DESCRIPTORS]; +static uint16_t rx_cur = 0; + +static e1000_tx_desc_t* tx_descriptors = NULL; +static uint8_t* tx_buffers[NUM_TX_DESCRIPTORS]; +static uint16_t tx_cur = 0; + +static uint64_t stats_rx_packets = 0; +static uint64_t stats_tx_packets = 0; +static uint64_t stats_rx_bytes = 0; +static uint64_t stats_tx_bytes = 0; + +static inline void e1000_write32(uint32_t reg, uint32_t val) { + if (use_mmio && mmio_base) { + *((volatile uint32_t*)(mmio_base + reg)) = val; + } else if (io_base) { + outl(io_base, reg); + outl(io_base + 4, val); + } +} + +static inline uint32_t e1000_read32(uint32_t reg) { + if (use_mmio && mmio_base) { + return *((volatile uint32_t*)(mmio_base + reg)); + } else if (io_base) { + outl(io_base, reg); + return inl(io_base + 4); + } + return 0; +} + +static void e1000_read_mac(void) { + uint32_t ral = e1000_read32(0x5400); + uint32_t rah = e1000_read32(0x5404); + + if (ral != 0 || (rah & 0xFFFF) != 0) { + mac_addr[0] = ral & 0xFF; + mac_addr[1] = (ral >> 8) & 0xFF; + mac_addr[2] = (ral >> 16) & 0xFF; + mac_addr[3] = (ral >> 24) & 0xFF; + mac_addr[4] = rah & 0xFF; + mac_addr[5] = (rah >> 8) & 0xFF; + } +} + +static void e1000_handle_receive(void) { + while (rx_descriptors[rx_cur].status & 0x01) { + uint16_t len = rx_descriptors[rx_cur].length; + uint8_t* buf = rx_buffers[rx_cur]; + + stats_rx_packets++; + stats_rx_bytes += len; + + // Forward to network stack + net_receive(buf, len); + + rx_descriptors[rx_cur].status = 0; + uint16_t old_cur = rx_cur; + rx_cur = (rx_cur + 1) % NUM_RX_DESCRIPTORS; + e1000_write32(REG_RDT, old_cur); + } +} + +static void e1000_interrupt_handler(registers_t* regs) { + (void)regs; + uint32_t icr = e1000_read32(REG_ICR); + + if (icr & 0x80) { + e1000_handle_receive(); + } + if (icr & 0x04) { + printk(KERN_INFO "[E1000] Link status changed: %s\n", e1000_is_link_up() ? "UP" : "DOWN"); + } +} + +static void e1000_init_rx(void) { + rx_descriptors = (e1000_rx_desc_t*)kzalloc(sizeof(e1000_rx_desc_t) * NUM_RX_DESCRIPTORS + 16); + rx_descriptors = (e1000_rx_desc_t*)(((uint64_t)rx_descriptors + 15) & ~15); + + for (int i = 0; i < NUM_RX_DESCRIPTORS; i++) { + rx_buffers[i] = (uint8_t*)kmalloc(RX_BUFFER_SIZE + 16); + rx_descriptors[i].buffer_addr = (uint64_t)rx_buffers[i]; + rx_descriptors[i].status = 0; + } + + uint64_t rx_desc_phys = (uint64_t)rx_descriptors; + e1000_write32(REG_RDBAL, (uint32_t)(rx_desc_phys & 0xFFFFFFFF)); + e1000_write32(REG_RDBAH, (uint32_t)(rx_desc_phys >> 32)); + e1000_write32(REG_RDLEN, NUM_RX_DESCRIPTORS * sizeof(e1000_rx_desc_t)); + e1000_write32(REG_RDH, 0); + e1000_write32(REG_RDT, NUM_RX_DESCRIPTORS - 1); + rx_cur = 0; + + // RCTL: Enable, Broadcast, Strip CRC, Multicast + e1000_write32(REG_RCTL, (1 << 1) | (1 << 15) | (1 << 26) | (1 << 4)); +} + +static void e1000_init_tx(void) { + tx_descriptors = (e1000_tx_desc_t*)kzalloc(sizeof(e1000_tx_desc_t) * NUM_TX_DESCRIPTORS + 16); + tx_descriptors = (e1000_tx_desc_t*)(((uint64_t)tx_descriptors + 15) & ~15); + + for (int i = 0; i < NUM_TX_DESCRIPTORS; i++) { + tx_buffers[i] = (uint8_t*)kmalloc(RX_BUFFER_SIZE + 16); + tx_descriptors[i].buffer_addr = (uint64_t)tx_buffers[i]; + tx_descriptors[i].status = 1; + tx_descriptors[i].cmd = 0; + } + + uint64_t tx_desc_phys = (uint64_t)tx_descriptors; + e1000_write32(REG_TDBAL, (uint32_t)(tx_desc_phys & 0xFFFFFFFF)); + e1000_write32(REG_TDBAH, (uint32_t)(tx_desc_phys >> 32)); + e1000_write32(REG_TDLEN, NUM_TX_DESCRIPTORS * sizeof(e1000_tx_desc_t)); + e1000_write32(REG_TDH, 0); + e1000_write32(REG_TDT, 0); + tx_cur = 0; + + // TCTL: Enable, Pad Short Packets, Collisions + e1000_write32(REG_TCTL, (1 << 1) | (1 << 3) | (0x0F << 4) | (0x40 << 12)); +} + +bool e1000_init(void) { + e1000_pci_dev = pci_find_device(E1000_VENDOR_ID, E1000_DEV_82540EM); + if (!e1000_pci_dev) e1000_pci_dev = pci_find_device(E1000_VENDOR_ID, E1000_DEV_82545EM); + if (!e1000_pci_dev) e1000_pci_dev = pci_find_class(PCI_CLASS_NETWORK, PCI_SUBCLASS_ETHERNET); + + if (!e1000_pci_dev) { + return false; + } + + pci_enable_bus_mastering(e1000_pci_dev); + + if (e1000_pci_dev->bar_type[0] == 0 && e1000_pci_dev->bar[0] != 0) { + mmio_base = e1000_pci_dev->bar[0] & ~0xF; + use_mmio = true; + } else { + io_base = (uint16_t)(e1000_pci_dev->bar[1] & ~0x3); + use_mmio = false; + } + + e1000_read_mac(); + + uint8_t irq = e1000_pci_dev->irq; + if (irq > 0 && irq <= 15) { + isr_register_handler(32 + irq, e1000_interrupt_handler); + pic_clear_mask(irq); + } + + e1000_init_rx(); + e1000_init_tx(); + + e1000_write32(REG_IMS, (1 << 7) | (1 << 4) | (1 << 6) | (1 << 2)); + e1000_read32(REG_ICR); + + printk(KERN_INFO "[E1000] Initialized. MAC: %02x:%02x:%02x:%02x:%02x:%02x (IRQ %d)\n", + mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5], irq); + return true; +} + +void e1000_send(const void* data, uint16_t len) { + if (!data || len == 0) return; + + while (!(tx_descriptors[tx_cur].status & 0x01)) { + io_wait(); + } + + memcpy(tx_buffers[tx_cur], data, len); + tx_descriptors[tx_cur].length = len; + tx_descriptors[tx_cur].cmd = (1 << 0) | (1 << 1) | (1 << 3); // EOP, RS, IFCS + tx_descriptors[tx_cur].status = 0; + + tx_cur = (tx_cur + 1) % NUM_TX_DESCRIPTORS; + stats_tx_packets++; + stats_tx_bytes += len; + + e1000_write32(REG_TDT, tx_cur); +} + +void e1000_get_mac(uint8_t* mac_out) { + if (mac_out) { + memcpy(mac_out, mac_addr, 6); + } +} + +bool e1000_is_link_up(void) { + uint32_t status = e1000_read32(REG_STATUS); + return (status & (1 << 1)) != 0; +} + +uint64_t e1000_get_rx_packets(void) { return stats_rx_packets; } +uint64_t e1000_get_tx_packets(void) { return stats_tx_packets; } +uint64_t e1000_get_rx_bytes(void) { return stats_rx_bytes; } +uint64_t e1000_get_tx_bytes(void) { return stats_tx_bytes; } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c new file mode 100644 index 0000000..197d4e1 --- /dev/null +++ b/drivers/pci/pci.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include + +static pci_device_t* pci_device_list = NULL; + +uint32_t pci_config_read32(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) { + uint32_t address = (uint32_t)((1U << 31) | + ((uint32_t)bus << 16) | + ((uint32_t)slot << 11) | + ((uint32_t)func << 8) | + (offset & 0xFC)); + outl(PCI_CONFIG_ADDRESS, address); + return inl(PCI_CONFIG_DATA); +} + +uint16_t pci_config_read16(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) { + uint32_t val = pci_config_read32(bus, slot, func, offset); + return (uint16_t)((val >> ((offset & 2) * 8)) & 0xFFFF); +} + +uint8_t pci_config_read8(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) { + uint32_t val = pci_config_read32(bus, slot, func, offset); + return (uint8_t)((val >> ((offset & 3) * 8)) & 0xFF); +} + +void pci_config_write32(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset, uint32_t val) { + uint32_t address = (uint32_t)((1U << 31) | + ((uint32_t)bus << 16) | + ((uint32_t)slot << 11) | + ((uint32_t)func << 8) | + (offset & 0xFC)); + outl(PCI_CONFIG_ADDRESS, address); + outl(PCI_CONFIG_DATA, val); +} + +void pci_config_write16(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset, uint16_t val) { + uint32_t old = pci_config_read32(bus, slot, func, offset); + uint32_t shift = (offset & 2) * 8; + uint32_t mask = 0xFFFF << shift; + uint32_t new_val = (old & ~mask) | ((uint32_t)val << shift); + pci_config_write32(bus, slot, func, offset, new_val); +} + +void pci_enable_bus_mastering(pci_device_t* dev) { + if (!dev) return; + uint16_t cmd = pci_config_read16(dev->bus, dev->slot, dev->function, 0x04); + cmd |= (1 << 2) | (1 << 0) | (1 << 1); // Bus Master, I/O Space, Memory Space + pci_config_write16(dev->bus, dev->slot, dev->function, 0x04, cmd); +} + +static void pci_check_function(uint8_t bus, uint8_t slot, uint8_t func) { + uint16_t vendor = pci_config_read16(bus, slot, func, 0x00); + if (vendor == 0xFFFF || vendor == 0x0000) return; + + uint16_t device = pci_config_read16(bus, slot, func, 0x02); + uint8_t class_id = pci_config_read8(bus, slot, func, 0x0B); + uint8_t subclass_id = pci_config_read8(bus, slot, func, 0x0A); + uint8_t prog_if = pci_config_read8(bus, slot, func, 0x09); + uint8_t revision = pci_config_read8(bus, slot, func, 0x08); + uint8_t irq = pci_config_read8(bus, slot, func, 0x3C); + + pci_device_t* dev = (pci_device_t*)kmalloc(sizeof(pci_device_t)); + if (!dev) return; + + dev->bus = bus; + dev->slot = slot; + dev->function = func; + dev->vendor_id = vendor; + dev->device_id = device; + dev->class_id = class_id; + dev->subclass_id = subclass_id; + dev->prog_if = prog_if; + dev->revision = revision; + dev->irq = irq; + + // Read BARs 0-5 + for (int b = 0; b < 6; b++) { + uint32_t bar = pci_config_read32(bus, slot, func, 0x10 + b * 4); + dev->bar[b] = bar; + dev->bar_type[b] = (bar & 1); // 0 = Memory, 1 = I/O + } + + dev->next = pci_device_list; + pci_device_list = dev; + + printk(KERN_INFO "PCI: %02x:%02x.%d [%04x:%04x] Class %02x.%02x (IRQ %d)\n", + bus, slot, func, vendor, device, class_id, subclass_id, irq); +} + +void pci_init(void) { + pci_device_list = NULL; + + for (uint16_t bus = 0; bus < 256; bus++) { + for (uint8_t slot = 0; slot < 32; slot++) { + uint16_t vendor = pci_config_read16((uint8_t)bus, slot, 0, 0x00); + if (vendor == 0xFFFF) continue; + + pci_check_function((uint8_t)bus, slot, 0); + + uint8_t header_type = pci_config_read8((uint8_t)bus, slot, 0, 0x0E); + if (header_type & 0x80) { + // Multi-function device + for (uint8_t func = 1; func < 8; func++) { + pci_check_function((uint8_t)bus, slot, func); + } + } + } + } +} + +pci_device_t* pci_get_devices(void) { + return pci_device_list; +} + +pci_device_t* pci_find_device(uint16_t vendor_id, uint16_t device_id) { + pci_device_t* curr = pci_device_list; + while (curr) { + if (curr->vendor_id == vendor_id && curr->device_id == device_id) { + return curr; + } + curr = curr->next; + } + return NULL; +} + +pci_device_t* pci_find_class(uint8_t class_id, uint8_t subclass_id) { + pci_device_t* curr = pci_device_list; + while (curr) { + if (curr->class_id == class_id && curr->subclass_id == subclass_id) { + return curr; + } + curr = curr->next; + } + return NULL; +} diff --git a/drivers/sound/speaker.c b/drivers/sound/speaker.c new file mode 100644 index 0000000..a1cae62 --- /dev/null +++ b/drivers/sound/speaker.c @@ -0,0 +1,30 @@ +#include +#include +#include + +void speaker_on(uint32_t frequency) { + if (frequency == 0) return; + uint32_t divisor = 1193180 / frequency; + + // Set PIT Channel 2 + outb(0x43, 0xB6); + outb(0x42, (uint8_t)(divisor & 0xFF)); + outb(0x42, (uint8_t)((divisor >> 8) & 0xFF)); + + // Enable speaker via Port 0x61 + uint8_t tmp = inb(0x61); + if (tmp != (tmp | 3)) { + outb(0x61, tmp | 3); + } +} + +void speaker_off(void) { + uint8_t tmp = inb(0x61) & 0xFC; + outb(0x61, tmp); +} + +void speaker_beep(uint32_t frequency, uint32_t duration_ms) { + speaker_on(frequency); + pit_sleep(duration_ms); + speaker_off(); +} diff --git a/fs/devfs.c b/fs/devfs.c new file mode 100644 index 0000000..3ff05dc --- /dev/null +++ b/fs/devfs.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include + +static vfs_node_t* devfs_root = NULL; + +static ssize_t dev_null_write(vfs_node_t* node, off_t offset, size_t size, const uint8_t* buffer) { + (void)node; (void)offset; (void)buffer; + return (ssize_t)size; // Discard everything +} + +static ssize_t dev_null_read(vfs_node_t* node, off_t offset, size_t size, uint8_t* buffer) { + (void)node; (void)offset; (void)size; (void)buffer; + return 0; // EOF immediately +} + +static ssize_t dev_zero_read(vfs_node_t* node, off_t offset, size_t size, uint8_t* buffer) { + (void)node; (void)offset; + memset(buffer, 0, size); + return (ssize_t)size; +} + +static ssize_t dev_random_read(vfs_node_t* node, off_t offset, size_t size, uint8_t* buffer) { + (void)node; (void)offset; + prng_get_bytes(buffer, size); + return (ssize_t)size; +} + +static ssize_t dev_tty_write(vfs_node_t* node, off_t offset, size_t size, const uint8_t* buffer) { + (void)node; (void)offset; + tty_write((const char*)buffer, size); + return (ssize_t)size; +} + +void devfs_init(void) { + devfs_root = ramfs_add_dir(NULL, "dev"); + + // 1. /dev/null + vfs_node_t* null_dev = ramfs_add_file(devfs_root, "null", NULL, 0); + if (null_dev) { + null_dev->flags = FS_CHARDEVICE; + null_dev->read = dev_null_read; + null_dev->write = dev_null_write; + } + + // 2. /dev/zero + vfs_node_t* zero_dev = ramfs_add_file(devfs_root, "zero", NULL, 0); + if (zero_dev) { + zero_dev->flags = FS_CHARDEVICE; + zero_dev->read = dev_zero_read; + zero_dev->write = dev_null_write; + } + + // 3. /dev/random & /dev/urandom + vfs_node_t* rand_dev = ramfs_add_file(devfs_root, "random", NULL, 0); + if (rand_dev) { + rand_dev->flags = FS_CHARDEVICE; + rand_dev->read = dev_random_read; + } + vfs_node_t* urand_dev = ramfs_add_file(devfs_root, "urandom", NULL, 0); + if (urand_dev) { + urand_dev->flags = FS_CHARDEVICE; + urand_dev->read = dev_random_read; + } + + // 4. /dev/tty + vfs_node_t* tty_dev = ramfs_add_file(devfs_root, "tty", NULL, 0); + if (tty_dev) { + tty_dev->flags = FS_CHARDEVICE; + tty_dev->write = dev_tty_write; + } +} + +vfs_node_t* devfs_get_root(void) { + return devfs_root; +} diff --git a/fs/procfs.c b/fs/procfs.c new file mode 100644 index 0000000..826d6f0 --- /dev/null +++ b/fs/procfs.c @@ -0,0 +1,142 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static vfs_node_t* procfs_root = NULL; + +static ssize_t proc_version_read(vfs_node_t* node, off_t offset, size_t size, uint8_t* buffer) { + (void)node; + char text[256]; + int len = sprintf(text, "SUB-OS version 0.2.0-lts (subhobhai@sub-os) (x86_64-elf-gcc 13.3.0) #1 SMP PREEMPT Sun Aug 16 2026\n"); + if (offset >= len) return 0; + if (offset + size > (size_t)len) size = len - offset; + memcpy(buffer, text + offset, size); + return (ssize_t)size; +} + +static ssize_t proc_uptime_read(vfs_node_t* node, off_t offset, size_t size, uint8_t* buffer) { + (void)node; + uint64_t ticks = pit_get_ticks(); + uint64_t secs = ticks / 100; + uint64_t frac = (ticks % 100); + + char text[64]; + int len = sprintf(text, "%llu.%02llu %llu.%02llu\n", secs, frac, secs, frac); + if (offset >= len) return 0; + if (offset + size > (size_t)len) size = len - offset; + memcpy(buffer, text + offset, size); + return (ssize_t)size; +} + +static ssize_t proc_meminfo_read(vfs_node_t* node, off_t offset, size_t size, uint8_t* buffer) { + (void)node; + uint64_t total_kb = (pmm_get_total_pages() * 4096) / 1024; + uint64_t free_kb = (pmm_get_free_pages() * 4096) / 1024; + uint64_t used_kb = (pmm_get_used_pages() * 4096) / 1024; + + char text[512]; + int len = sprintf(text, + "MemTotal: %8llu kB\n" + "MemFree: %8llu kB\n" + "MemAvailable: %8llu kB\n" + "Buffers: 0 kB\n" + "Cached: %8llu kB\n" + "HeapTotal: %8llu kB\n" + "HeapUsed: %8llu kB\n" + "HeapFree: %8llu kB\n", + total_kb, free_kb, free_kb, + (heap_get_used_bytes() / 1024), + heap_get_total_bytes() / 1024, + heap_get_used_bytes() / 1024, + heap_get_free_bytes() / 1024); + + if (offset >= len) return 0; + if (offset + size > (size_t)len) size = len - offset; + memcpy(buffer, text + offset, size); + return (ssize_t)size; +} + +static ssize_t proc_cpuinfo_read(vfs_node_t* node, off_t offset, size_t size, uint8_t* buffer) { + (void)node; + const cpu_info_t* cpu = cpuid_get_info(); + + char text[1024]; + int len = sprintf(text, + "processor\t: 0\n" + "vendor_id\t: %s\n" + "cpu family\t: %u\n" + "model\t\t: %u\n" + "model name\t: %s\n" + "stepping\t: %u\n" + "flags\t\t: %s%s%s%s%s%s%s%s%s\n" + "bogomips\t: 4800.00\n" + "clflush size\t: 64\n", + cpu->vendor, + cpu->family, + cpu->model_id, + cpu->model, + cpu->stepping, + cpu->has_fpu ? "fpu " : "", + cpu->has_tsc ? "tsc " : "", + cpu->has_pae ? "pae " : "", + cpu->has_apic ? "apic " : "", + cpu->has_sse ? "sse " : "", + cpu->has_sse2 ? "sse2 " : "", + cpu->has_sse3 ? "sse3 " : "", + cpu->has_sse4_1 ? "sse4_1 " : "", + cpu->has_avx ? "avx " : ""); + + if (offset >= len) return 0; + if (offset + size > (size_t)len) size = len - offset; + memcpy(buffer, text + offset, size); + return (ssize_t)size; +} + +static ssize_t proc_net_dev_read(vfs_node_t* node, off_t offset, size_t size, uint8_t* buffer) { + (void)node; + char text[512]; + int len = sprintf(text, + "Inter-| Receive | Transmit\n" + " face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n" + " lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + " eth0: %7llu %7llu 0 0 0 0 0 0 %7llu %7llu 0 0 0 0 0 0\n", + e1000_get_rx_bytes(), e1000_get_rx_packets(), + e1000_get_tx_bytes(), e1000_get_tx_packets()); + + if (offset >= len) return 0; + if (offset + size > (size_t)len) size = len - offset; + memcpy(buffer, text + offset, size); + return (ssize_t)size; +} + +void procfs_init(void) { + procfs_root = ramfs_add_dir(NULL, "proc"); + + vfs_node_t* ver = ramfs_add_file(procfs_root, "version", NULL, 0); + if (ver) { ver->flags = FS_FILE; ver->read = proc_version_read; } + + vfs_node_t* uptime = ramfs_add_file(procfs_root, "uptime", NULL, 0); + if (uptime) { uptime->flags = FS_FILE; uptime->read = proc_uptime_read; } + + vfs_node_t* mem = ramfs_add_file(procfs_root, "meminfo", NULL, 0); + if (mem) { mem->flags = FS_FILE; mem->read = proc_meminfo_read; } + + vfs_node_t* cpu = ramfs_add_file(procfs_root, "cpuinfo", NULL, 0); + if (cpu) { cpu->flags = FS_FILE; cpu->read = proc_cpuinfo_read; } + + vfs_node_t* net_dir = ramfs_add_dir(procfs_root, "net"); + if (net_dir) { + vfs_node_t* net_dev = ramfs_add_file(net_dir, "dev", NULL, 0); + if (net_dev) { net_dev->flags = FS_FILE; net_dev->read = proc_net_dev_read; } + } +} + +vfs_node_t* procfs_get_root(void) { + return procfs_root; +} diff --git a/fs/ramfs.c b/fs/ramfs.c new file mode 100644 index 0000000..9cd9a77 --- /dev/null +++ b/fs/ramfs.c @@ -0,0 +1,195 @@ +#include +#include +#include + +#define MAX_CHILDREN 32 + +typedef struct ramfs_entry { + vfs_node_t node; + uint8_t* data; + size_t capacity; + struct ramfs_entry* children[MAX_CHILDREN]; + uint32_t child_count; +} ramfs_entry_t; + +static vfs_dirent_t shared_dirent; + +static int ramfs_mkdir_impl(vfs_node_t* parent, const char* name, mode_t mode); +static int ramfs_create_impl(vfs_node_t* parent, const char* name, mode_t mode); +static struct vfs_dirent* ramfs_readdir(vfs_node_t* node, uint32_t index); +static struct vfs_node* ramfs_finddir(vfs_node_t* node, const char* name); + +static ssize_t ramfs_read(vfs_node_t* node, off_t offset, size_t size, uint8_t* buffer) { + ramfs_entry_t* entry = (ramfs_entry_t*)node; + if (!entry || !entry->data || (size_t)offset >= node->length) return 0; + + if (offset + size > node->length) { + size = node->length - offset; + } + + memcpy(buffer, entry->data + offset, size); + return (ssize_t)size; +} + +static ssize_t ramfs_write(vfs_node_t* node, off_t offset, size_t size, const uint8_t* buffer) { + ramfs_entry_t* entry = (ramfs_entry_t*)node; + if (!entry) return -1; + + if (offset + size > entry->capacity) { + size_t new_cap = (offset + size + 256) & ~255; + uint8_t* new_data = (uint8_t*)kmalloc(new_cap); + if (!new_data) return -1; + + if (entry->data) { + memcpy(new_data, entry->data, node->length); + kfree(entry->data); + } + entry->data = new_data; + entry->capacity = new_cap; + } + + memcpy(entry->data + offset, buffer, size); + if (offset + size > node->length) { + node->length = offset + size; + } + return (ssize_t)size; +} + +static struct vfs_dirent* ramfs_readdir(vfs_node_t* node, uint32_t index) { + ramfs_entry_t* entry = (ramfs_entry_t*)node; + if (!entry || index >= entry->child_count) return NULL; + + strncpy(shared_dirent.name, entry->children[index]->node.name, sizeof(shared_dirent.name) - 1); + shared_dirent.inode = entry->children[index]->node.inode; + shared_dirent.type = entry->children[index]->node.flags; + return &shared_dirent; +} + +static struct vfs_node* ramfs_finddir(vfs_node_t* node, const char* name) { + ramfs_entry_t* entry = (ramfs_entry_t*)node; + if (!entry) return NULL; + + for (uint32_t i = 0; i < entry->child_count; i++) { + if (strcmp(entry->children[i]->node.name, name) == 0) { + return &entry->children[i]->node; + } + } + return NULL; +} + +static int ramfs_mkdir_impl(vfs_node_t* parent, const char* name, mode_t mode) { + ramfs_entry_t* p = (ramfs_entry_t*)parent; + if (!p || p->child_count >= MAX_CHILDREN) return -1; + + ramfs_entry_t* child = (ramfs_entry_t*)kzalloc(sizeof(ramfs_entry_t)); + if (!child) return -1; + + strncpy(child->node.name, name, sizeof(child->node.name) - 1); + child->node.flags = FS_DIRECTORY; + child->node.mode = mode; + child->node.readdir = ramfs_readdir; + child->node.finddir = ramfs_finddir; + child->node.mkdir = ramfs_mkdir_impl; + child->node.create = ramfs_create_impl; + + p->children[p->child_count++] = child; + return 0; +} + +static int ramfs_create_impl(vfs_node_t* parent, const char* name, mode_t mode) { + ramfs_entry_t* p = (ramfs_entry_t*)parent; + if (!p || p->child_count >= MAX_CHILDREN) return -1; + + ramfs_entry_t* child = (ramfs_entry_t*)kzalloc(sizeof(ramfs_entry_t)); + if (!child) return -1; + + strncpy(child->node.name, name, sizeof(child->node.name) - 1); + child->node.flags = FS_FILE; + child->node.mode = mode; + child->node.read = ramfs_read; + child->node.write = ramfs_write; + + p->children[p->child_count++] = child; + return 0; +} + +vfs_node_t* ramfs_add_file(vfs_node_t* parent, const char* name, const void* content, size_t size) { + ramfs_entry_t* p = (ramfs_entry_t*)parent; + if (!p || p->child_count >= MAX_CHILDREN) return NULL; + + ramfs_entry_t* child = (ramfs_entry_t*)kzalloc(sizeof(ramfs_entry_t)); + if (!child) return NULL; + + strncpy(child->node.name, name, sizeof(child->node.name) - 1); + child->node.flags = FS_FILE; + child->node.mode = 0644; + child->node.length = size; + child->node.read = ramfs_read; + child->node.write = ramfs_write; + + if (size > 0 && content) { + child->capacity = size; + child->data = (uint8_t*)kmalloc(size); + if (child->data) { + memcpy(child->data, content, size); + } + } + + p->children[p->child_count++] = child; + return &child->node; +} + +vfs_node_t* ramfs_add_dir(vfs_node_t* parent, const char* name) { + ramfs_entry_t* child = (ramfs_entry_t*)kzalloc(sizeof(ramfs_entry_t)); + if (!child) return NULL; + + strncpy(child->node.name, name, sizeof(child->node.name) - 1); + child->node.flags = FS_DIRECTORY; + child->node.mode = 0755; + child->node.readdir = ramfs_readdir; + child->node.finddir = ramfs_finddir; + child->node.mkdir = ramfs_mkdir_impl; + child->node.create = ramfs_create_impl; + + if (parent) { + ramfs_entry_t* p = (ramfs_entry_t*)parent; + if (p->child_count < MAX_CHILDREN) { + p->children[p->child_count++] = child; + } + } + + return &child->node; +} + +vfs_node_t* ramfs_create_root(void) { + ramfs_entry_t* root = (ramfs_entry_t*)kzalloc(sizeof(ramfs_entry_t)); + if (!root) return NULL; + + strcpy(root->node.name, "/"); + root->node.flags = FS_DIRECTORY; + root->node.mode = 0755; + root->node.readdir = ramfs_readdir; + root->node.finddir = ramfs_finddir; + root->node.mkdir = ramfs_mkdir_impl; + root->node.create = ramfs_create_impl; + + // Seed default file system structure + vfs_node_t* etc = ramfs_add_dir(&root->node, "etc"); + ramfs_add_dir(&root->node, "bin"); + ramfs_add_dir(&root->node, "dev"); + ramfs_add_dir(&root->node, "proc"); + ramfs_add_dir(&root->node, "home"); + ramfs_add_dir(&root->node, "tmp"); + + if (etc) { + ramfs_add_file(etc, "hostname", "sub-os\n", 7); + ramfs_add_file(etc, "os-release", "NAME=\"SUB-OS\"\nVERSION=\"0.2.0-lts\"\nARCH=\"x86_64\"\n", 48); + ramfs_add_file(etc, "resolv.conf", "nameserver 10.0.2.3\nsearch localdomain\n", 39); + ramfs_add_file(etc, "motd", "====================================================\n Welcome to SUB-OS Modular Monolithic Linux Core!\n====================================================\n", 137); + } + + const char* readme = "SUB-OS Production-Level Modular Operating System\nArchitecture: x86_64 Long Mode\nSubsystems: PMM, Heap, VFS, PCI, E1000, Net, Crypto, LazyBox\n"; + ramfs_add_file(&root->node, "readme.txt", readme, strlen(readme)); + + return &root->node; +} diff --git a/fs/vfs.c b/fs/vfs.c new file mode 100644 index 0000000..9755c24 --- /dev/null +++ b/fs/vfs.c @@ -0,0 +1,242 @@ +#include +#include +#include +#include +#include +#include +#include + +#define MAX_OPEN_FILES 64 + +static vfs_node_t* vfs_root = NULL; +static file_descriptor_t file_descriptors[MAX_OPEN_FILES]; + +void vfs_init(void) { + memset(file_descriptors, 0, sizeof(file_descriptors)); + + // 1. Create root RAM filesystem + vfs_root = ramfs_create_root(); + + // 2. Initialize and mount /dev + devfs_init(); + vfs_mount("/dev", devfs_get_root()); + + // 3. Initialize and mount /proc + procfs_init(); + vfs_mount("/proc", procfs_get_root()); + + printk(KERN_INFO "VFS: Mounted root ramfs, /dev synthetic devfs, and /proc procfs\n"); +} + +vfs_node_t* vfs_get_root(void) { + return vfs_root; +} + +vfs_node_t* vfs_namei(const char* path) { + if (!path || !vfs_root) return NULL; + if (strcmp(path, "/") == 0) return vfs_root; + + if (*path == '/') path++; // Skip leading slash + + char buffer[256]; + strncpy(buffer, path, sizeof(buffer) - 1); + buffer[sizeof(buffer) - 1] = '\0'; + + vfs_node_t* current = vfs_root; + char* token = buffer; + + while (*token) { + char* next_slash = strchr(token, '/'); + if (next_slash) { + *next_slash = '\0'; + } + + if (current->finddir) { + current = current->finddir(current, token); + if (!current) return NULL; + + // Follow mountpoints + if (current->ptr && (current->flags & FS_MOUNTPOINT)) { + current = current->ptr; + } + } else { + return NULL; + } + + if (next_slash) { + token = next_slash + 1; + } else { + break; + } + } + + return current; +} + +int vfs_open(const char* path, uint32_t flags) { + vfs_node_t* node = vfs_namei(path); + if (!node) { + if (flags & O_CREAT) { + // Find parent and create + char parent_path[256]; + strncpy(parent_path, path, sizeof(parent_path) - 1); + char* last_slash = strrchr(parent_path, '/'); + if (last_slash) { + *last_slash = '\0'; + const char* filename = last_slash + 1; + vfs_node_t* parent = vfs_namei(parent_path[0] ? parent_path : "/"); + if (parent && parent->create) { + parent->create(parent, filename, 0644); + node = vfs_namei(path); + } + } + } + if (!node) return -1; + } + + if (node->open) { + if (node->open(node, flags) != 0) return -1; + } + + // Find free file descriptor + for (int fd = 3; fd < MAX_OPEN_FILES; fd++) { + if (!file_descriptors[fd].node) { + file_descriptors[fd].node = node; + file_descriptors[fd].offset = 0; + file_descriptors[fd].flags = flags; + return fd; + } + } + + return -1; // Out of file descriptors +} + +ssize_t vfs_read(int fd, void* buffer, size_t size) { + if (fd < 0 || fd >= MAX_OPEN_FILES || !file_descriptors[fd].node) return -1; + + file_descriptor_t* desc = &file_descriptors[fd]; + if (desc->node->read) { + ssize_t bytes_read = desc->node->read(desc->node, desc->offset, size, (uint8_t*)buffer); + if (bytes_read > 0) { + desc->offset += bytes_read; + } + return bytes_read; + } + return -1; +} + +ssize_t vfs_write(int fd, const void* buffer, size_t size) { + if (fd < 0 || fd >= MAX_OPEN_FILES || !file_descriptors[fd].node) return -1; + + file_descriptor_t* desc = &file_descriptors[fd]; + if (desc->node->write) { + ssize_t bytes_written = desc->node->write(desc->node, desc->offset, size, (const uint8_t*)buffer); + if (bytes_written > 0) { + desc->offset += bytes_written; + } + return bytes_written; + } + return -1; +} + +off_t vfs_lseek(int fd, off_t offset, int whence) { + if (fd < 0 || fd >= MAX_OPEN_FILES || !file_descriptors[fd].node) return -1; + + file_descriptor_t* desc = &file_descriptors[fd]; + if (whence == SEEK_SET) desc->offset = offset; + else if (whence == SEEK_CUR) desc->offset += offset; + else if (whence == SEEK_END) desc->offset = desc->node->length + offset; + return desc->offset; +} + +int vfs_close(int fd) { + if (fd < 0 || fd >= MAX_OPEN_FILES || !file_descriptors[fd].node) return -1; + + if (file_descriptors[fd].node->close) { + file_descriptors[fd].node->close(file_descriptors[fd].node); + } + file_descriptors[fd].node = NULL; + file_descriptors[fd].offset = 0; + return 0; +} + +int vfs_mkdir(const char* path, mode_t mode) { + if (!path || !*path) return -1; + char parent_path[256]; + strncpy(parent_path, path, sizeof(parent_path) - 1); + parent_path[sizeof(parent_path) - 1] = '\0'; + + char* last_slash = strrchr(parent_path, '/'); + const char* dirname = path; + vfs_node_t* parent = vfs_get_root(); + + if (last_slash) { + if (last_slash == parent_path) { + parent = vfs_namei("/"); + dirname = last_slash + 1; + } else { + *last_slash = '\0'; + dirname = last_slash + 1; + parent = vfs_namei(parent_path); + } + } + + if (parent && parent->mkdir) { + return parent->mkdir(parent, dirname, mode); + } + return -1; +} + +int vfs_create(const char* path, mode_t mode) { + if (!path || !*path) return -1; + char parent_path[256]; + strncpy(parent_path, path, sizeof(parent_path) - 1); + parent_path[sizeof(parent_path) - 1] = '\0'; + + char* last_slash = strrchr(parent_path, '/'); + const char* filename = path; + vfs_node_t* parent = vfs_get_root(); + + if (last_slash) { + if (last_slash == parent_path) { + parent = vfs_namei("/"); + filename = last_slash + 1; + } else { + *last_slash = '\0'; + filename = last_slash + 1; + parent = vfs_namei(parent_path); + } + } + + if (parent && parent->create) { + return parent->create(parent, filename, mode); + } + return -1; +} + +int vfs_mount(const char* path, vfs_node_t* fs_root) { + if (!path || !fs_root) return -1; + vfs_node_t* mount_node = vfs_namei(path); + if (!mount_node) { + // Create directory node for mount point + char parent_path[256]; + strncpy(parent_path, path, sizeof(parent_path) - 1); + char* last_slash = strrchr(parent_path, '/'); + if (last_slash) { + *last_slash = '\0'; + const char* dirname = last_slash + 1; + vfs_node_t* parent = vfs_namei(parent_path[0] ? parent_path : "/"); + if (parent && parent->mkdir) { + parent->mkdir(parent, dirname, 0755); + mount_node = vfs_namei(path); + } + } + } + + if (mount_node) { + mount_node->flags |= FS_MOUNTPOINT; + mount_node->ptr = fs_root; + return 0; + } + return -1; +} diff --git a/include/arch/x86_64/cpuid.h b/include/arch/x86_64/cpuid.h new file mode 100644 index 0000000..af4ea7e --- /dev/null +++ b/include/arch/x86_64/cpuid.h @@ -0,0 +1,30 @@ +#ifndef _ARCH_X86_64_CPUID_H +#define _ARCH_X86_64_CPUID_H + +#include +#include + +typedef struct { + char vendor[13]; + char model[49]; + uint32_t family; + uint32_t model_id; + uint32_t stepping; + bool has_fpu; + bool has_tsc; + bool has_msr; + bool has_pae; + bool has_apic; + bool has_sse; + bool has_sse2; + bool has_sse3; + bool has_sse4_1; + bool has_sse4_2; + bool has_avx; + bool has_rdrand; +} cpu_info_t; + +void cpuid_detect(cpu_info_t* info); +const cpu_info_t* cpuid_get_info(void); + +#endif // _ARCH_X86_64_CPUID_H diff --git a/include/arch/x86_64/gdt.h b/include/arch/x86_64/gdt.h new file mode 100644 index 0000000..31bc65e --- /dev/null +++ b/include/arch/x86_64/gdt.h @@ -0,0 +1,47 @@ +#ifndef _ARCH_X86_64_GDT_H +#define _ARCH_X86_64_GDT_H + +#include + +#define GDT_KERNEL_CODE 0x08 +#define GDT_KERNEL_DATA 0x10 +#define GDT_USER_CODE 0x18 +#define GDT_USER_DATA 0x20 +#define GDT_TSS 0x28 + +typedef struct { + uint16_t limit_low; + uint16_t base_low; + uint8_t base_middle; + uint8_t access; + uint8_t granularity; + uint8_t base_high; +} __attribute__((packed)) gdt_entry_t; + +typedef struct { + uint16_t limit; + uint64_t base; +} __attribute__((packed)) gdt_ptr_t; + +typedef struct { + uint32_t reserved0; + uint64_t rsp0; + uint64_t rsp1; + uint64_t rsp2; + uint64_t reserved1; + uint64_t ist1; + uint64_t ist2; + uint64_t ist3; + uint64_t ist4; + uint64_t ist5; + uint64_t ist6; + uint64_t ist7; + uint64_t reserved2; + uint16_t reserved3; + uint16_t iomap_base; +} __attribute__((packed)) tss_entry_t; + +void gdt_init(void); +void gdt_set_kernel_stack(uint64_t stack); + +#endif // _ARCH_X86_64_GDT_H diff --git a/include/arch/x86_64/idt.h b/include/arch/x86_64/idt.h new file mode 100644 index 0000000..7b99f4f --- /dev/null +++ b/include/arch/x86_64/idt.h @@ -0,0 +1,28 @@ +#ifndef _ARCH_X86_64_IDT_H +#define _ARCH_X86_64_IDT_H + +#include + +#define IDT_ENTRIES 256 + +typedef struct { + uint16_t isr_low; + uint16_t kernel_cs; + uint8_t ist; + uint8_t attributes; + uint16_t isr_mid; + uint32_t isr_high; + uint32_t reserved; +} __attribute__((packed)) idt_entry_t; + +typedef struct { + uint16_t limit; + uint64_t base; +} __attribute__((packed)) idt_ptr_t; + +void idt_init(void); +void idt_set_gate(uint8_t vector, void* isr, uint8_t flags); +void idt_enable_gate(uint8_t vector); +void idt_disable_gate(uint8_t vector); + +#endif // _ARCH_X86_64_IDT_H diff --git a/include/arch/x86_64/io.h b/include/arch/x86_64/io.h new file mode 100644 index 0000000..58c0cdd --- /dev/null +++ b/include/arch/x86_64/io.h @@ -0,0 +1,52 @@ +#ifndef _ARCH_X86_64_IO_H +#define _ARCH_X86_64_IO_H + +#include + +static inline void outb(uint16_t port, uint8_t val) { + __asm__ volatile ("outb %0, %1" : : "a"(val), "Nd"(port)); +} + +static inline uint8_t inb(uint16_t port) { + uint8_t ret; + __asm__ volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port)); + return ret; +} + +static inline void outw(uint16_t port, uint16_t val) { + __asm__ volatile ("outw %0, %1" : : "a"(val), "Nd"(port)); +} + +static inline uint16_t inw(uint16_t port) { + uint16_t ret; + __asm__ volatile ("inw %1, %0" : "=a"(ret) : "Nd"(port)); + return ret; +} + +static inline void outl(uint16_t port, uint32_t val) { + __asm__ volatile ("outl %0, %1" : : "a"(val), "Nd"(port)); +} + +static inline uint32_t inl(uint16_t port) { + uint32_t ret; + __asm__ volatile ("inl %1, %0" : "=a"(ret) : "Nd"(port)); + return ret; +} + +static inline void io_wait(void) { + outb(0x80, 0); +} + +static inline void cli(void) { + __asm__ volatile ("cli"); +} + +static inline void sti(void) { + __asm__ volatile ("sti"); +} + +static inline void hlt(void) { + __asm__ volatile ("hlt"); +} + +#endif // _ARCH_X86_64_IO_H diff --git a/include/arch/x86_64/isr.h b/include/arch/x86_64/isr.h new file mode 100644 index 0000000..0ba473a --- /dev/null +++ b/include/arch/x86_64/isr.h @@ -0,0 +1,19 @@ +#ifndef _ARCH_X86_64_ISR_H +#define _ARCH_X86_64_ISR_H + +#include + +typedef struct { + uint64_t r15, r14, r13, r12, r11, r10, r9, r8; + uint64_t rbp, rdi, rsi, rdx, rcx, rbx, rax; + uint64_t int_no, error_code; + uint64_t rip, cs, rflags, rsp, ss; +} __attribute__((packed)) registers_t; + +typedef void (*isr_handler_t)(registers_t*); + +void isr_init(void); +void isr_register_handler(uint8_t int_no, isr_handler_t handler); +void isr_unregister_handler(uint8_t int_no); + +#endif // _ARCH_X86_64_ISR_H diff --git a/include/arch/x86_64/paging.h b/include/arch/x86_64/paging.h new file mode 100644 index 0000000..6146de0 --- /dev/null +++ b/include/arch/x86_64/paging.h @@ -0,0 +1,25 @@ +#ifndef _ARCH_X86_64_PAGING_H +#define _ARCH_X86_64_PAGING_H + +#include +#include + +#define PAGE_SIZE 4096 +#define PAGE_PRESENT (1ULL << 0) +#define PAGE_WRITABLE (1ULL << 1) +#define PAGE_USER (1ULL << 2) +#define PAGE_HUGE (1ULL << 7) +#define PAGE_NX (1ULL << 63) + +typedef uint64_t pml4e_t; +typedef uint64_t pdpte_t; +typedef uint64_t pde_t; +typedef uint64_t pte_t; + +void paging_init(void); +void paging_map_page(uint64_t virt, uint64_t phys, uint64_t flags); +void paging_unmap_page(uint64_t virt); +uint64_t paging_get_phys(uint64_t virt); +void paging_flush_tlb_single(uint64_t virt); + +#endif // _ARCH_X86_64_PAGING_H diff --git a/include/arch/x86_64/pic.h b/include/arch/x86_64/pic.h new file mode 100644 index 0000000..80af4c2 --- /dev/null +++ b/include/arch/x86_64/pic.h @@ -0,0 +1,12 @@ +#ifndef _ARCH_X86_64_PIC_H +#define _ARCH_X86_64_PIC_H + +#include + +void pic_init(void); +void pic_send_eoi(uint8_t irq); +void pic_set_mask(uint8_t irq); +void pic_clear_mask(uint8_t irq); +void pic_disable(void); + +#endif // _ARCH_X86_64_PIC_H diff --git a/include/arch/x86_64/pit.h b/include/arch/x86_64/pit.h new file mode 100644 index 0000000..97732b9 --- /dev/null +++ b/include/arch/x86_64/pit.h @@ -0,0 +1,11 @@ +#ifndef _ARCH_X86_64_PIT_H +#define _ARCH_X86_64_PIT_H + +#include + +void pit_init(uint32_t frequency); +uint64_t pit_get_ticks(void); +uint64_t pit_get_uptime_ms(void); +void pit_sleep(uint32_t milliseconds); + +#endif // _ARCH_X86_64_PIT_H diff --git a/include/crypto/crypto.h b/include/crypto/crypto.h new file mode 100644 index 0000000..518ad27 --- /dev/null +++ b/include/crypto/crypto.h @@ -0,0 +1,42 @@ +#ifndef _CRYPTO_CRYPTO_H +#define _CRYPTO_CRYPTO_H + +#include +#include +#include + +// CRC32 API +uint32_t crc32(uint32_t crc, const void* buf, size_t size); + +// MD5 API +typedef struct { + uint32_t state[4]; + uint32_t count[2]; + uint8_t buffer[64]; +} md5_ctx_t; + +void md5_init(md5_ctx_t* ctx); +void md5_update(md5_ctx_t* ctx, const void* input, size_t input_len); +void md5_final(uint8_t digest[16], md5_ctx_t* ctx); +void md5_string(const char* str, char output[33]); + +// SHA-256 API +typedef struct { + uint32_t state[8]; + uint64_t bitlen; + uint8_t buffer[64]; + size_t datalen; +} sha256_ctx_t; + +void sha256_init(sha256_ctx_t* ctx); +void sha256_update(sha256_ctx_t* ctx, const void* data, size_t len); +void sha256_final(uint8_t hash[32], sha256_ctx_t* ctx); +void sha256_string(const char* str, char output[65]); + +// PRNG (Cryptographic Pseudo-Random Number Generator) +void prng_seed(uint64_t seed1, uint64_t seed2); +uint64_t prng_rand64(void); +uint32_t prng_rand32(void); +void prng_get_bytes(void* buf, size_t len); + +#endif // _CRYPTO_CRYPTO_H diff --git a/include/drivers/ata.h b/include/drivers/ata.h new file mode 100644 index 0000000..4e4260a --- /dev/null +++ b/include/drivers/ata.h @@ -0,0 +1,28 @@ +#ifndef _DRIVERS_ATA_H +#define _DRIVERS_ATA_H + +#include +#include +#include + +#define ATA_SECTOR_SIZE 512 + +#define ATA_PRIMARY_IO 0x1F0 +#define ATA_PRIMARY_CTRL 0x3F6 +#define ATA_SECONDARY_IO 0x170 +#define ATA_SECONDARY_CTRL 0x376 + +typedef struct { + bool present; + bool is_master; + uint32_t sector_count; + char model[41]; + char serial[21]; +} ata_device_t; + +bool ata_init(void); +bool ata_read_sectors(uint32_t lba, uint8_t count, void* buffer); +bool ata_write_sectors(uint32_t lba, uint8_t count, const void* buffer); +const ata_device_t* ata_get_primary_master(void); + +#endif // _DRIVERS_ATA_H diff --git a/include/drivers/e1000.h b/include/drivers/e1000.h new file mode 100644 index 0000000..c6be064 --- /dev/null +++ b/include/drivers/e1000.h @@ -0,0 +1,22 @@ +#ifndef _DRIVERS_E1000_H +#define _DRIVERS_E1000_H + +#include +#include +#include + +#define E1000_VENDOR_ID 0x8086 +#define E1000_DEV_82540EM 0x100E +#define E1000_DEV_82545EM 0x100F + +bool e1000_init(void); +void e1000_send(const void* data, uint16_t len); +void e1000_get_mac(uint8_t* mac_out); +bool e1000_is_link_up(void); + +uint64_t e1000_get_rx_packets(void); +uint64_t e1000_get_tx_packets(void); +uint64_t e1000_get_rx_bytes(void); +uint64_t e1000_get_tx_bytes(void); + +#endif // _DRIVERS_E1000_H diff --git a/include/drivers/keyboard.h b/include/drivers/keyboard.h new file mode 100644 index 0000000..df73b52 --- /dev/null +++ b/include/drivers/keyboard.h @@ -0,0 +1,39 @@ +#ifndef _DRIVERS_KEYBOARD_H +#define _DRIVERS_KEYBOARD_H + +#include +#include + +#define KEY_SPECIAL_FLAG 0x0100 + +enum special_keys { + KEY_UP = KEY_SPECIAL_FLAG | 0x01, + KEY_DOWN = KEY_SPECIAL_FLAG | 0x02, + KEY_LEFT = KEY_SPECIAL_FLAG | 0x03, + KEY_RIGHT = KEY_SPECIAL_FLAG | 0x04, + KEY_HOME = KEY_SPECIAL_FLAG | 0x05, + KEY_END = KEY_SPECIAL_FLAG | 0x06, + KEY_PAGE_UP = KEY_SPECIAL_FLAG | 0x07, + KEY_PAGE_DOWN= KEY_SPECIAL_FLAG | 0x08, + KEY_INSERT = KEY_SPECIAL_FLAG | 0x09, + KEY_DELETE = KEY_SPECIAL_FLAG | 0x0A, + KEY_F1 = KEY_SPECIAL_FLAG | 0x11, + KEY_F2 = KEY_SPECIAL_FLAG | 0x12, + KEY_F3 = KEY_SPECIAL_FLAG | 0x13, + KEY_F4 = KEY_SPECIAL_FLAG | 0x14, + KEY_F5 = KEY_SPECIAL_FLAG | 0x15, + KEY_F6 = KEY_SPECIAL_FLAG | 0x16, + KEY_F7 = KEY_SPECIAL_FLAG | 0x17, + KEY_F8 = KEY_SPECIAL_FLAG | 0x18, + KEY_F9 = KEY_SPECIAL_FLAG | 0x19, + KEY_F10 = KEY_SPECIAL_FLAG | 0x1A, + KEY_F11 = KEY_SPECIAL_FLAG | 0x1B, + KEY_F12 = KEY_SPECIAL_FLAG | 0x1C, +}; + +void keyboard_init(void); +uint16_t keyboard_get_key(void); +bool keyboard_has_key(void); +char keyboard_get_char(void); + +#endif // _DRIVERS_KEYBOARD_H diff --git a/include/drivers/pci.h b/include/drivers/pci.h new file mode 100644 index 0000000..cb5c062 --- /dev/null +++ b/include/drivers/pci.h @@ -0,0 +1,46 @@ +#ifndef _DRIVERS_PCI_H +#define _DRIVERS_PCI_H + +#include +#include + +#define PCI_CONFIG_ADDRESS 0xCF8 +#define PCI_CONFIG_DATA 0xCFC + +#define PCI_CLASS_NETWORK 0x02 +#define PCI_CLASS_DISPLAY 0x03 +#define PCI_CLASS_STORAGE 0x01 + +#define PCI_SUBCLASS_ETHERNET 0x00 +#define PCI_SUBCLASS_IDE 0x01 +#define PCI_SUBCLASS_SATA 0x06 + +typedef struct pci_device { + uint8_t bus; + uint8_t slot; + uint8_t function; + uint16_t vendor_id; + uint16_t device_id; + uint8_t class_id; + uint8_t subclass_id; + uint8_t prog_if; + uint8_t revision; + uint8_t irq; + uint32_t bar[6]; + uint8_t bar_type[6]; + struct pci_device* next; +} pci_device_t; + +void pci_init(void); +pci_device_t* pci_get_devices(void); +pci_device_t* pci_find_device(uint16_t vendor_id, uint16_t device_id); +pci_device_t* pci_find_class(uint8_t class_id, uint8_t subclass_id); +void pci_enable_bus_mastering(pci_device_t* dev); + +uint32_t pci_config_read32(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset); +uint16_t pci_config_read16(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset); +uint8_t pci_config_read8(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset); +void pci_config_write32(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset, uint32_t val); +void pci_config_write16(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset, uint16_t val); + +#endif // _DRIVERS_PCI_H diff --git a/include/drivers/serial.h b/include/drivers/serial.h new file mode 100644 index 0000000..9282230 --- /dev/null +++ b/include/drivers/serial.h @@ -0,0 +1,16 @@ +#ifndef _DRIVERS_SERIAL_H +#define _DRIVERS_SERIAL_H + +#include +#include +#include + +#define COM1_PORT 0x3F8 + +void serial_init(void); +void serial_write_char(char c); +void serial_write_string(const char* str); +char serial_read_char(void); +bool serial_received(void); + +#endif // _DRIVERS_SERIAL_H diff --git a/include/drivers/speaker.h b/include/drivers/speaker.h new file mode 100644 index 0000000..fe69a67 --- /dev/null +++ b/include/drivers/speaker.h @@ -0,0 +1,10 @@ +#ifndef _DRIVERS_SPEAKER_H +#define _DRIVERS_SPEAKER_H + +#include + +void speaker_on(uint32_t frequency); +void speaker_off(void); +void speaker_beep(uint32_t frequency, uint32_t duration_ms); + +#endif // _DRIVERS_SPEAKER_H diff --git a/include/drivers/tty.h b/include/drivers/tty.h new file mode 100644 index 0000000..c778652 --- /dev/null +++ b/include/drivers/tty.h @@ -0,0 +1,20 @@ +#ifndef _DRIVERS_TTY_H +#define _DRIVERS_TTY_H + +#include +#include +#include + +#define MAX_TTYS 4 +#define TTY_WIDTH 80 +#define TTY_HEIGHT 25 + +void tty_init(void); +void tty_switch(int index); +int tty_get_current(void); +void tty_write(const char* data, size_t size); +void tty_write_string(const char* data); +void tty_clear(void); +void tty_set_cursor(int row, int col); + +#endif // _DRIVERS_TTY_H diff --git a/include/drivers/vga.h b/include/drivers/vga.h new file mode 100644 index 0000000..50cf478 --- /dev/null +++ b/include/drivers/vga.h @@ -0,0 +1,48 @@ +#ifndef _DRIVERS_VGA_H +#define _DRIVERS_VGA_H + +#include +#include + +#define VGA_WIDTH 80 +#define VGA_HEIGHT 25 +#define VGA_MEMORY 0xB8000 + +enum vga_color { + VGA_COLOR_BLACK = 0, + VGA_COLOR_BLUE = 1, + VGA_COLOR_GREEN = 2, + VGA_COLOR_CYAN = 3, + VGA_COLOR_RED = 4, + VGA_COLOR_MAGENTA = 5, + VGA_COLOR_BROWN = 6, + VGA_COLOR_LIGHT_GREY = 7, + VGA_COLOR_DARK_GREY = 8, + VGA_COLOR_LIGHT_BLUE = 9, + VGA_COLOR_LIGHT_GREEN = 10, + VGA_COLOR_LIGHT_CYAN = 11, + VGA_COLOR_LIGHT_RED = 12, + VGA_COLOR_LIGHT_MAGENTA = 13, + VGA_COLOR_LIGHT_BROWN = 14, + VGA_COLOR_WHITE = 15, +}; + +static inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) { + return fg | (bg << 4); +} + +static inline uint16_t vga_entry(unsigned char uc, uint8_t color) { + return (uint16_t)uc | ((uint16_t)color << 8); +} + +void vga_init(void); +void vga_clear(void); +void vga_putc(char c); +void vga_write(const char* str); +void vga_set_color(enum vga_color fg, enum vga_color bg); +void vga_set_cursor(int row, int col); +void vga_get_cursor(int* row, int* col); +void vga_enable_cursor(uint8_t cursor_start, uint8_t cursor_end); +void vga_disable_cursor(void); + +#endif // _DRIVERS_VGA_H diff --git a/include/fs/devfs.h b/include/fs/devfs.h new file mode 100644 index 0000000..cffd0d3 --- /dev/null +++ b/include/fs/devfs.h @@ -0,0 +1,10 @@ +#ifndef _FS_DEVFS_H +#define _FS_DEVFS_H + +#include "vfs.h" + +void devfs_init(void); +vfs_node_t* devfs_get_root(void); +int devfs_register_device(const char* name, vfs_node_t* dev_node); + +#endif // _FS_DEVFS_H diff --git a/include/fs/procfs.h b/include/fs/procfs.h new file mode 100644 index 0000000..3b7688e --- /dev/null +++ b/include/fs/procfs.h @@ -0,0 +1,9 @@ +#ifndef _FS_PROCFS_H +#define _FS_PROCFS_H + +#include "vfs.h" + +void procfs_init(void); +vfs_node_t* procfs_get_root(void); + +#endif // _FS_PROCFS_H diff --git a/include/fs/ramfs.h b/include/fs/ramfs.h new file mode 100644 index 0000000..f57cf97 --- /dev/null +++ b/include/fs/ramfs.h @@ -0,0 +1,10 @@ +#ifndef _FS_RAMFS_H +#define _FS_RAMFS_H + +#include "vfs.h" + +vfs_node_t* ramfs_create_root(void); +vfs_node_t* ramfs_add_file(vfs_node_t* parent, const char* name, const void* content, size_t size); +vfs_node_t* ramfs_add_dir(vfs_node_t* parent, const char* name); + +#endif // _FS_RAMFS_H diff --git a/include/fs/vfs.h b/include/fs/vfs.h new file mode 100644 index 0000000..453102b --- /dev/null +++ b/include/fs/vfs.h @@ -0,0 +1,85 @@ +#ifndef _FS_VFS_H +#define _FS_VFS_H + +#include +#include +#include +#include + +#define FS_FILE 0x01 +#define FS_DIRECTORY 0x02 +#define FS_CHARDEVICE 0x04 +#define FS_BLOCKDEVICE 0x08 +#define FS_PIPE 0x10 +#define FS_SYMLINK 0x20 +#define FS_MOUNTPOINT 0x40 + +#define O_RDONLY 0x0000 +#define O_WRONLY 0x0001 +#define O_RDWR 0x0002 +#define O_CREAT 0x0040 +#define O_TRUNC 0x0200 +#define O_APPEND 0x0400 + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +struct vfs_node; + +typedef ssize_t (*vfs_read_fn_t)(struct vfs_node* node, off_t offset, size_t size, uint8_t* buffer); +typedef ssize_t (*vfs_write_fn_t)(struct vfs_node* node, off_t offset, size_t size, const uint8_t* buffer); +typedef int (*vfs_open_fn_t)(struct vfs_node* node, uint32_t flags); +typedef int (*vfs_close_fn_t)(struct vfs_node* node); +typedef struct vfs_dirent* (*vfs_readdir_fn_t)(struct vfs_node* node, uint32_t index); +typedef struct vfs_node* (*vfs_finddir_fn_t)(struct vfs_node* node, const char* name); +typedef int (*vfs_mkdir_fn_t)(struct vfs_node* node, const char* name, mode_t mode); +typedef int (*vfs_create_fn_t)(struct vfs_node* node, const char* name, mode_t mode); + +typedef struct vfs_node { + char name[128]; + uint32_t flags; + uint32_t mode; + uid_t uid; + gid_t gid; + size_t length; + ino_t inode; + + vfs_read_fn_t read; + vfs_write_fn_t write; + vfs_open_fn_t open; + vfs_close_fn_t close; + vfs_readdir_fn_t readdir; + vfs_finddir_fn_t finddir; + vfs_mkdir_fn_t mkdir; + vfs_create_fn_t create; + + struct vfs_node* ptr; // Used for mountpoints / device nodes +} vfs_node_t; + +typedef struct vfs_dirent { + char name[128]; + ino_t inode; + uint32_t type; +} vfs_dirent_t; + +typedef struct { + vfs_node_t* node; + off_t offset; + uint32_t flags; +} file_descriptor_t; + +void vfs_init(void); +vfs_node_t* vfs_namei(const char* path); +int vfs_open(const char* path, uint32_t flags); +ssize_t vfs_read(int fd, void* buffer, size_t size); +ssize_t vfs_write(int fd, const void* buffer, size_t size); +off_t vfs_lseek(int fd, off_t offset, int whence); +int vfs_close(int fd); +int vfs_mkdir(const char* path, mode_t mode); +int vfs_create(const char* path, mode_t mode); + +vfs_node_t* vfs_get_root(void); +int vfs_mount(const char* path, vfs_node_t* fs_root); + +#endif // _FS_VFS_H diff --git a/include/kernel/kernel.h b/include/kernel/kernel.h new file mode 100644 index 0000000..b92f138 --- /dev/null +++ b/include/kernel/kernel.h @@ -0,0 +1,15 @@ +#ifndef _KERNEL_MAIN_H +#define _KERNEL_MAIN_H + +#include "types.h" +#include "printk.h" +#include + +#define KERNEL_NAME "SUB-OS" +#define KERNEL_VERSION "0.2.0-lts" +#define KERNEL_ARCH "x86_64" +#define KERNEL_AUTHOR "SUB-OS Development Team" + +void kernel_main(void* memory_map, uint64_t memory_map_count); + +#endif // _KERNEL_MAIN_H diff --git a/include/kernel/panic.h b/include/kernel/panic.h new file mode 100644 index 0000000..e48cbc4 --- /dev/null +++ b/include/kernel/panic.h @@ -0,0 +1,9 @@ +#ifndef _KERNEL_PANIC_H +#define _KERNEL_PANIC_H + +#include + +void panic(const char* message); +void panic_with_regs(const char* message, const registers_t* regs); + +#endif // _KERNEL_PANIC_H diff --git a/include/kernel/printk.h b/include/kernel/printk.h new file mode 100644 index 0000000..fc450c5 --- /dev/null +++ b/include/kernel/printk.h @@ -0,0 +1,48 @@ +#ifndef _KERNEL_PRINTK_H +#define _KERNEL_PRINTK_H + +#include +#include +#include + +// Linux-compatible log level prefixes +#define KERN_EMERG "\0010" /* system is unusable */ +#define KERN_ALERT "\0011" /* action must be taken immediately */ +#define KERN_CRIT "\0012" /* critical conditions */ +#define KERN_ERR "\0013" /* error conditions */ +#define KERN_WARNING "\0014" /* warning conditions */ +#define KERN_NOTICE "\0015" /* normal but significant condition */ +#define KERN_INFO "\0016" /* informational */ +#define KERN_DEBUG "\0017" /* debug-level messages */ + +// ANSI Color Escape Sequences +#define ANSI_RESET "\033[0m" +#define ANSI_BOLD "\033[1m" +#define ANSI_BLACK "\033[30m" +#define ANSI_RED "\033[31m" +#define ANSI_GREEN "\033[32m" +#define ANSI_YELLOW "\033[33m" +#define ANSI_BLUE "\033[34m" +#define ANSI_MAGENTA "\033[35m" +#define ANSI_CYAN "\033[36m" +#define ANSI_WHITE "\033[37m" +#define ANSI_BRIGHT_BLACK "\033[90m" +#define ANSI_BRIGHT_RED "\033[91m" +#define ANSI_BRIGHT_GREEN "\033[92m" +#define ANSI_BRIGHT_YELLOW "\033[93m" +#define ANSI_BRIGHT_BLUE "\033[94m" +#define ANSI_BRIGHT_MAGENTA "\033[95m" +#define ANSI_BRIGHT_CYAN "\033[96m" +#define ANSI_BRIGHT_WHITE "\033[97m" + +// Printk API +void printk_init(void); +int printk(const char* fmt, ...); +int vprintk(const char* fmt, va_list args); +int kprintf(const char* fmt, ...); // Alias for compatibility + +// Kernel Dmesg Ring Buffer API +void dmesg_dump(void); +const char* dmesg_get_buffer(size_t* size_out); + +#endif // _KERNEL_PRINTK_H diff --git a/include/kernel/sched.h b/include/kernel/sched.h new file mode 100644 index 0000000..911e7ad --- /dev/null +++ b/include/kernel/sched.h @@ -0,0 +1,13 @@ +#ifndef _KERNEL_SCHED_H +#define _KERNEL_SCHED_H + +#include "task.h" + +void sched_init(void); +void sched_yield(void); +void sched_schedule(void); +void sched_tick(void); +void sched_add_task(task_t* task); +void sched_remove_task(task_t* task); + +#endif // _KERNEL_SCHED_H diff --git a/include/kernel/sync.h b/include/kernel/sync.h new file mode 100644 index 0000000..1cefa8b --- /dev/null +++ b/include/kernel/sync.h @@ -0,0 +1,36 @@ +#ifndef _KERNEL_SYNC_H +#define _KERNEL_SYNC_H + +#include +#include + +typedef struct { + volatile uint32_t lock; +} spinlock_t; + +#define SPINLOCK_INIT { .lock = 0 } + +static inline void spinlock_init(spinlock_t* lock) { + lock->lock = 0; +} + +static inline void spin_lock(spinlock_t* lock) { + while (__atomic_test_and_set(&lock->lock, __ATOMIC_ACQUIRE)) { + __asm__ volatile ("pause"); + } +} + +static inline void spin_unlock(spinlock_t* lock) { + __atomic_clear(&lock->lock, __ATOMIC_RELEASE); +} + +typedef struct { + spinlock_t lock; + volatile int count; +} semaphore_t; + +void sem_init(semaphore_t* sem, int value); +void sem_wait(semaphore_t* sem); +void sem_post(semaphore_t* sem); + +#endif // _KERNEL_SYNC_H diff --git a/include/kernel/task.h b/include/kernel/task.h new file mode 100644 index 0000000..9f38913 --- /dev/null +++ b/include/kernel/task.h @@ -0,0 +1,36 @@ +#ifndef _KERNEL_TASK_H +#define _KERNEL_TASK_H + +#include "types.h" +#include + +typedef enum { + TASK_STATE_READY, + TASK_STATE_RUNNING, + TASK_STATE_BLOCKED, + TASK_STATE_SLEEPING, + TASK_STATE_ZOMBIE, + TASK_STATE_DEAD +} task_state_t; + +typedef struct task_struct { + pid_t pid; + char name[32]; + task_state_t state; + uint64_t rsp; + uint64_t stack_base; + size_t stack_size; + uint64_t sleep_until_ticks; + int priority; + int time_slice; + struct list_head list; +} task_t; + +typedef void (*task_entry_fn_t)(void* arg); + +task_t* task_create(const char* name, task_entry_fn_t entry, void* arg, int priority); +void task_exit(int exit_code); +task_t* task_current(void); +pid_t task_get_pid(void); + +#endif // _KERNEL_TASK_H diff --git a/include/kernel/types.h b/include/kernel/types.h new file mode 100644 index 0000000..417a7af --- /dev/null +++ b/include/kernel/types.h @@ -0,0 +1,30 @@ +#ifndef _KERNEL_TYPES_H +#define _KERNEL_TYPES_H + +#include +#include +#include + +typedef int32_t pid_t; +typedef int32_t tid_t; +typedef uint32_t uid_t; +typedef uint32_t gid_t; +typedef int64_t off_t; +typedef int64_t ssize_t; +typedef uint32_t mode_t; +typedef uint32_t dev_t; +typedef uint64_t ino_t; +typedef uint64_t time_t; + +#ifndef NULL +#define NULL ((void*)0) +#endif + +#define ALIGN_UP(addr, align) (((addr) + (align) - 1) & ~((align) - 1)) +#define ALIGN_DOWN(addr, align) ((addr) & ~((align) - 1)) +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#endif // _KERNEL_TYPES_H diff --git a/include/lib/bitmap.h b/include/lib/bitmap.h new file mode 100644 index 0000000..f4a44cb --- /dev/null +++ b/include/lib/bitmap.h @@ -0,0 +1,26 @@ +#ifndef _LIB_BITMAP_H +#define _LIB_BITMAP_H + +#include +#include +#include + +typedef uint64_t bitmap_word_t; +#define BITS_PER_WORD (sizeof(bitmap_word_t) * 8) + +static inline void bitmap_set(bitmap_word_t* map, size_t bit) { + map[bit / BITS_PER_WORD] |= ((bitmap_word_t)1 << (bit % BITS_PER_WORD)); +} + +static inline void bitmap_clear(bitmap_word_t* map, size_t bit) { + map[bit / BITS_PER_WORD] &= ~((bitmap_word_t)1 << (bit % BITS_PER_WORD)); +} + +static inline bool bitmap_test(const bitmap_word_t* map, size_t bit) { + return (map[bit / BITS_PER_WORD] & ((bitmap_word_t)1 << (bit % BITS_PER_WORD))) != 0; +} + +int64_t bitmap_find_first_free(const bitmap_word_t* map, size_t total_bits); +int64_t bitmap_find_contiguous_free(const bitmap_word_t* map, size_t total_bits, size_t count); + +#endif // _LIB_BITMAP_H diff --git a/include/lib/list.h b/include/lib/list.h new file mode 100644 index 0000000..14313bb --- /dev/null +++ b/include/lib/list.h @@ -0,0 +1,56 @@ +#ifndef _LIB_LIST_H +#define _LIB_LIST_H + +#include + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } +#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) { + list->next = list; + list->prev = list; +} + +static inline void __list_add(struct list_head *new_node, + struct list_head *prev, + struct list_head *next) { + next->prev = new_node; + new_node->next = next; + new_node->prev = prev; + prev->next = new_node; +} + +static inline void list_add(struct list_head *new_node, struct list_head *head) { + __list_add(new_node, head, head->next); +} + +static inline void list_add_tail(struct list_head *new_node, struct list_head *head) { + __list_add(new_node, head->prev, head); +} + +static inline void list_del(struct list_head *entry) { + entry->next->prev = entry->prev; + entry->prev->next = entry->next; + entry->next = NULL; + entry->prev = NULL; +} + +static inline int list_empty(const struct list_head *head) { + return head->next == head; +} + +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr) - offsetof(type, member))) + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +#endif // _LIB_LIST_H diff --git a/include/lib/printf.h b/include/lib/printf.h new file mode 100644 index 0000000..0f6d374 --- /dev/null +++ b/include/lib/printf.h @@ -0,0 +1,12 @@ +#ifndef _LIB_PRINTF_H +#define _LIB_PRINTF_H + +#include +#include + +int sprintf(char* buf, const char* fmt, ...); +int snprintf(char* buf, size_t size, const char* fmt, ...); +int vsprintf(char* buf, const char* fmt, va_list args); +int vsnprintf(char* buf, size_t size, const char* fmt, va_list args); + +#endif // _LIB_PRINTF_H diff --git a/include/lib/string.h b/include/lib/string.h new file mode 100644 index 0000000..5e68d2c --- /dev/null +++ b/include/lib/string.h @@ -0,0 +1,33 @@ +#ifndef _LIB_STRING_H +#define _LIB_STRING_H + +#include +#include +#include + +// Memory functions +void* memset(void* dest, int val, size_t count); +void* memcpy(void* dest, const void* src, size_t count); +void* memmove(void* dest, const void* src, size_t count); +int memcmp(const void* ptr1, const void* ptr2, size_t num); + +// String functions +size_t strlen(const char* str); +int strcmp(const char* str1, const char* str2); +int strncmp(const char* str1, const char* str2, size_t num); +char* strcpy(char* dest, const char* src); +char* strncpy(char* dest, const char* src, size_t num); +char* strcat(char* dest, const char* src); +char* strncat(char* dest, const char* src, size_t num); +char* strchr(const char* str, int c); +char* strrchr(const char* str, int c); +char* strstr(const char* haystack, const char* needle); +char* strdup(const char* s); + +// Conversion functions +char* itoa(int value, char* str, int base); +char* itoa_hex(uint64_t value, char* str); +int atoi(const char* str); +long strtol(const char* str, char** endptr, int base); + +#endif // _LIB_STRING_H diff --git a/include/mm/kmalloc.h b/include/mm/kmalloc.h new file mode 100644 index 0000000..15abf90 --- /dev/null +++ b/include/mm/kmalloc.h @@ -0,0 +1,18 @@ +#ifndef _MM_KMALLOC_H +#define _MM_KMALLOC_H + +#include +#include +#include + +void heap_init(void); +void* kmalloc(size_t size); +void* kzalloc(size_t size); +void* krealloc(void* ptr, size_t new_size); +void kfree(void* ptr); + +size_t heap_get_used_bytes(void); +size_t heap_get_free_bytes(void); +size_t heap_get_total_bytes(void); + +#endif // _MM_KMALLOC_H diff --git a/include/mm/pmm.h b/include/mm/pmm.h new file mode 100644 index 0000000..ab69618 --- /dev/null +++ b/include/mm/pmm.h @@ -0,0 +1,36 @@ +#ifndef _MM_PMM_H +#define _MM_PMM_H + +#include +#include +#include + +#define PMM_PAGE_SIZE 4096 + +// E820 Memory Map entry structure +typedef struct { + uint64_t base; + uint64_t length; + uint32_t type; + uint32_t acpi_attr; +} __attribute__((packed)) e820_entry_t; + +#define E820_TYPE_USABLE 1 +#define E820_TYPE_RESERVED 2 +#define E820_TYPE_ACPI_RECLAIM 3 +#define E820_TYPE_ACPI_NVS 4 +#define E820_TYPE_BAD 5 + +void pmm_init(void* memory_map, uint64_t memory_map_count); +void* pmm_alloc_page(void); +void* pmm_alloc_pages(size_t count); +void pmm_free_page(void* page); +void pmm_free_pages(void* page, size_t count); + +uint64_t pmm_get_total_pages(void); +uint64_t pmm_get_used_pages(void); +uint64_t pmm_get_free_pages(void); +uint64_t pmm_get_total_memory(void); +uint64_t pmm_get_usable_memory(void); + +#endif // _MM_PMM_H diff --git a/include/net/net.h b/include/net/net.h new file mode 100644 index 0000000..16b05be --- /dev/null +++ b/include/net/net.h @@ -0,0 +1,107 @@ +#ifndef _NET_NET_H +#define _NET_NET_H + +#include +#include +#include + +#define ETHERTYPE_IPV4 0x0800 +#define ETHERTYPE_ARP 0x0806 + +#define IP_PROTO_ICMP 1 +#define IP_PROTO_TCP 6 +#define IP_PROTO_UDP 17 + +static inline uint16_t htons(uint16_t host) { + return (uint16_t)((host << 8) | (host >> 8)); +} +static inline uint16_t ntohs(uint16_t net) { + return htons(net); +} +static inline uint32_t htonl(uint32_t host) { + return (uint32_t)(((host & 0x000000FF) << 24) | + ((host & 0x0000FF00) << 8) | + ((host & 0x00FF0000) >> 8) | + ((host & 0xFF000000) >> 24)); +} +static inline uint32_t ntohl(uint32_t net) { + return htonl(net); +} + +typedef struct { + uint8_t dst_mac[6]; + uint8_t src_mac[6]; + uint16_t ethertype; +} __attribute__((packed)) eth_header_t; + +typedef struct { + uint16_t htype; + uint16_t ptype; + uint8_t hlen; + uint8_t plen; + uint16_t opcode; + uint8_t sender_mac[6]; + uint32_t sender_ip; + uint8_t target_mac[6]; + uint32_t target_ip; +} __attribute__((packed)) arp_header_t; + +typedef struct { + uint8_t ihl_version; + uint8_t tos; + uint16_t total_length; + uint16_t id; + uint16_t flags_fragment; + uint8_t ttl; + uint8_t protocol; + uint16_t checksum; + uint32_t src_ip; + uint32_t dst_ip; +} __attribute__((packed)) ip_header_t; + +typedef struct { + uint8_t type; + uint8_t code; + uint16_t checksum; + uint16_t id; + uint16_t sequence; +} __attribute__((packed)) icmp_header_t; + +typedef struct { + uint16_t src_port; + uint16_t dst_port; + uint16_t length; + uint16_t checksum; +} __attribute__((packed)) udp_header_t; + +typedef struct { + char name[16]; + uint8_t mac[6]; + uint32_t ip; + uint32_t subnet; + uint32_t gateway; + uint32_t dns; + bool is_up; +} net_if_t; + +typedef struct { + uint32_t ip; + uint8_t mac[6]; + bool valid; +} arp_entry_t; + +void net_init(void); +void net_receive(const uint8_t* packet, uint16_t length); +void net_send_eth(const uint8_t* dst_mac, uint16_t ethertype, const void* payload, uint16_t len); +void net_send_ip(uint32_t dst_ip, uint8_t protocol, const void* payload, uint16_t len); +void net_send_arp_req(uint32_t target_ip); +int net_ping(uint32_t target_ip, uint32_t count, uint32_t timeout_ms); + +net_if_t* net_get_primary_if(void); +arp_entry_t* net_get_arp_table(int* count_out); + +uint32_t ip_parse(const char* ip_str); +void ip_to_str(uint32_t ip, char* buf); +void mac_to_str(const uint8_t* mac, char* buf); + +#endif // _NET_NET_H diff --git a/include/userland/lazybox.h b/include/userland/lazybox.h new file mode 100644 index 0000000..4701f1a --- /dev/null +++ b/include/userland/lazybox.h @@ -0,0 +1,26 @@ +#ifndef _USERLAND_LAZYBOX_H +#define _USERLAND_LAZYBOX_H + +#include +#include +#include + +#define LAZYBOX_VERSION "2.0.0-pro" + +typedef int (*applet_fn_t)(int argc, char** argv); + +typedef struct { + const char* name; + applet_fn_t func; + const char* usage; + const char* desc; + const char* category; +} lazybox_applet_t; + +void lazybox_init(void); +int lazybox_main(int argc, char** argv); +bool lazybox_has_applet(const char* name); +int lazybox_run_applet(const char* name, int argc, char** argv); +const lazybox_applet_t* lazybox_get_applets(int* count_out); + +#endif // _USERLAND_LAZYBOX_H diff --git a/include/userland/shell.h b/include/userland/shell.h new file mode 100644 index 0000000..742c545 --- /dev/null +++ b/include/userland/shell.h @@ -0,0 +1,9 @@ +#ifndef _USERLAND_SHELL_H +#define _USERLAND_SHELL_H + +#define MAX_COMMAND_LENGTH 256 + +void shell_init(void); +void shell_run(void); + +#endif // _USERLAND_SHELL_H diff --git a/kernel/main.c b/kernel/main.c new file mode 100644 index 0000000..00612b0 --- /dev/null +++ b/kernel/main.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void kernel_main(void* memory_map, uint64_t memory_map_count) { + // 1. Initialize Console & Telemetry Serial Port + tty_init(); + serial_init(); + printk_init(); + + printk(ANSI_BRIGHT_CYAN "=================================================================\n" ANSI_RESET); + printk(ANSI_BRIGHT_CYAN " SUB-OS 64-Bit Production Monolithic Kernel v%s (%s)\n" ANSI_RESET, KERNEL_VERSION, KERNEL_ARCH); + printk(ANSI_BRIGHT_CYAN "=================================================================\n\n" ANSI_RESET); + + // 2. CPU Architecture & Interrupt Initialization + printk(KERN_INFO "[1/11] Initializing 64-Bit Global Descriptor Table (GDT)... "); + gdt_init(); + printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); + + printk(KERN_INFO "[2/11] Initializing Interrupt Descriptor Table (IDT & ISRs)... "); + idt_init(); + isr_init(); + printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); + + printk(KERN_INFO "[3/11] Remapping Dual 8259A PIC Interrupt Controller... "); + pic_init(); + printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); + + printk(KERN_INFO "[4/11] Configuring 8254 PIT Timer (100 Hz, 10ms resolution)... "); + pit_init(100); + printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); + + printk(KERN_INFO "[5/11] Initializing PS/2 Keyboard Controller Driver... "); + keyboard_init(); + printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); + + // 3. Memory Subsystems + printk(KERN_INFO "[6/11] Initializing Physical Memory Manager (PMM)...\n"); + pmm_init(memory_map, memory_map_count); + + printk(KERN_INFO "[7/11] Initializing Kernel Dynamic Heap Allocator (4 MB Pool)... "); + heap_init(); + printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); + + paging_init(); + + // 4. Hardware Storage & Bus Enumeration + printk(KERN_INFO "[8/11] Initializing ATA / IDE Hard Disk Controller... "); + if (ata_init()) { + printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); + } else { + printk(ANSI_YELLOW "No ATA Drive\n" ANSI_RESET); + } + + printk(KERN_INFO "[9/11] Enumerating PCI Bus & Network Device Drivers...\n"); + pci_init(); + if (e1000_init()) { + printk(ANSI_BRIGHT_GREEN " Intel E1000 Gigabit Ethernet Controller Active\n" ANSI_RESET); + net_init(); + printk(ANSI_BRIGHT_GREEN " IPv4 / ARP / ICMP Network Protocol Stack Online\n" ANSI_RESET); + } else { + printk(ANSI_YELLOW " No PCI Ethernet Controller detected\n" ANSI_RESET); + } + + // 5. Security & Cryptography Engine + prng_seed(0, 0); + + // 6. Virtual File System & Task Scheduler + printk(KERN_INFO "[10/11] Mounting Virtual File System (VFS, devfs, procfs)... "); + vfs_init(); + printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); + + printk(KERN_INFO "[11/11] Initializing Preemptive Task Scheduler & LazyBox... "); + sched_init(); + lazybox_init(); + printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); + + // 7. Enable Hardware Interrupts + sti(); + printk(ANSI_BRIGHT_GREEN "[INIT] Hardware interrupts active. System ready for userland!\n" ANSI_RESET); + + tty_clear(); + + // 8. Launch Interactive Userland Shell + shell_run(); + + while (1) { + hlt(); + } +} diff --git a/kernel/panic.c b/kernel/panic.c new file mode 100644 index 0000000..0628e8e --- /dev/null +++ b/kernel/panic.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include + +void panic_with_regs(const char* message, const registers_t* regs) { + cli(); + + printk("\n" ANSI_BRIGHT_RED "================================================================================" ANSI_RESET "\n"); + printk(ANSI_BRIGHT_RED " *** KERNEL PANIC - SYSTEM HALTED *** " ANSI_RESET "\n"); + printk(ANSI_BRIGHT_RED "================================================================================" ANSI_RESET "\n\n"); + printk(ANSI_BRIGHT_WHITE "Reason: " ANSI_YELLOW "%s" ANSI_RESET "\n\n", message ? message : "Unknown Fault"); + + if (regs) { + printk(ANSI_BRIGHT_CYAN "CPU Register State (x86_64 AMD64 ABI):" ANSI_RESET "\n"); + printk(" RIP: %016llx CS: %04llx RFLAGS: %016llx\n", regs->rip, regs->cs, regs->rflags); + printk(" RSP: %016llx SS: %04llx RBP: %016llx\n", regs->rsp, regs->ss, regs->rbp); + printk(" RAX: %016llx RBX: %016llx RCX: %016llx RDX: %016llx\n", regs->rax, regs->rbx, regs->rcx, regs->rdx); + printk(" RSI: %016llx RDI: %016llx R8: %016llx R9: %016llx\n", regs->rsi, regs->rdi, regs->r8, regs->r9); + printk(" R10: %016llx R11: %016llx R12: %016llx R13: %016llx\n", regs->r10, regs->r11, regs->r12, regs->r13); + printk(" R14: %016llx R15: %016llx ERR: %016llx INT: %llu\n", regs->r14, regs->r15, regs->error_code, regs->int_no); + } + + printk("\n" ANSI_BRIGHT_YELLOW "Please power off or reset your system." ANSI_RESET "\n"); + + while (1) { + hlt(); + } +} + +void panic(const char* message) { + panic_with_regs(message, NULL); +} diff --git a/kernel/printk.c b/kernel/printk.c new file mode 100644 index 0000000..3d382bc --- /dev/null +++ b/kernel/printk.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include + +#define DMESG_BUFFER_SIZE 65536 // 64 KB Ring Buffer + +static char dmesg_buffer[DMESG_BUFFER_SIZE]; +static size_t dmesg_head = 0; +static size_t dmesg_total = 0; +static spinlock_t printk_lock = SPINLOCK_INIT; + +void printk_init(void) { + memset(dmesg_buffer, 0, sizeof(dmesg_buffer)); + dmesg_head = 0; + dmesg_total = 0; +} + +static void dmesg_append(const char* str, size_t len) { + for (size_t i = 0; i < len; i++) { + dmesg_buffer[dmesg_head] = str[i]; + dmesg_head = (dmesg_head + 1) % DMESG_BUFFER_SIZE; + if (dmesg_total < DMESG_BUFFER_SIZE) { + dmesg_total++; + } + } +} + +int vprintk(const char* fmt, va_list args) { + char buffer[1024]; + int len = vsnprintf(buffer, sizeof(buffer), fmt, args); + if (len <= 0) return 0; + + spin_lock(&printk_lock); + + // Skip loglevel prefix if present (e.g. "\0016") + const char* display_ptr = buffer; + if (buffer[0] == '\001' && buffer[1] >= '0' && buffer[1] <= '7') { + display_ptr += 2; + len -= 2; + } + + // 1. Output to active TTY console + tty_write(display_ptr, (size_t)len); + + // 2. Output to COM1 serial port for host debugging + for (int i = 0; i < len; i++) { + serial_write_char(display_ptr[i]); + } + + // 3. Store in kernel dmesg ring buffer + dmesg_append(display_ptr, (size_t)len); + + spin_unlock(&printk_lock); + return len; +} + +int printk(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int ret = vprintk(fmt, args); + va_end(args); + return ret; +} + +int kprintf(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int ret = vprintk(fmt, args); + va_end(args); + return ret; +} + +void dmesg_dump(void) { + spin_lock(&printk_lock); + if (dmesg_total < DMESG_BUFFER_SIZE) { + tty_write(dmesg_buffer, dmesg_total); + } else { + // Output from head to end, then 0 to head + tty_write(dmesg_buffer + dmesg_head, DMESG_BUFFER_SIZE - dmesg_head); + tty_write(dmesg_buffer, dmesg_head); + } + spin_unlock(&printk_lock); +} + +const char* dmesg_get_buffer(size_t* size_out) { + if (size_out) *size_out = dmesg_total; + return dmesg_buffer; +} diff --git a/kernel/sched.c b/kernel/sched.c new file mode 100644 index 0000000..b7f455a --- /dev/null +++ b/kernel/sched.c @@ -0,0 +1,67 @@ +#include +#include +#include + +static struct list_head ready_queue; +static spinlock_t sched_lock = SPINLOCK_INIT; + +extern void task_init_idle(void); + +void sched_init(void) { + INIT_LIST_HEAD(&ready_queue); + task_init_idle(); +} + +void sched_add_task(task_t* task) { + if (!task) return; + spin_lock(&sched_lock); + list_add_tail(&task->list, &ready_queue); + spin_unlock(&sched_lock); +} + +void sched_remove_task(task_t* task) { + if (!task) return; + spin_lock(&sched_lock); + list_del(&task->list); + spin_unlock(&sched_lock); +} + +void sched_yield(void) { + // Cooperative yield + sched_schedule(); +} + +void sched_tick(void) { + // Timer tick scheduler hook + task_t* curr = task_current(); + if (curr && curr->pid != 0) { + if (--curr->time_slice <= 0) { + curr->time_slice = 10; + sched_schedule(); + } + } +} + +void sched_schedule(void) { + spin_lock(&sched_lock); + + if (list_empty(&ready_queue)) { + spin_unlock(&sched_lock); + return; + } + + task_t* next = list_entry(ready_queue.next, task_t, list); + list_del(&next->list); + list_add_tail(&next->list, &ready_queue); // Move to back of round-robin queue + + task_t* curr = task_current(); + if (curr != next) { + // Context switch logic + next->state = TASK_STATE_RUNNING; + if (curr->state == TASK_STATE_RUNNING) { + curr->state = TASK_STATE_READY; + } + } + + spin_unlock(&sched_lock); +} diff --git a/kernel/sync.c b/kernel/sync.c new file mode 100644 index 0000000..494364b --- /dev/null +++ b/kernel/sync.c @@ -0,0 +1,26 @@ +#include +#include + +void sem_init(semaphore_t* sem, int value) { + spinlock_init(&sem->lock); + sem->count = value; +} + +void sem_wait(semaphore_t* sem) { + while (1) { + spin_lock(&sem->lock); + if (sem->count > 0) { + sem->count--; + spin_unlock(&sem->lock); + return; + } + spin_unlock(&sem->lock); + sched_yield(); + } +} + +void sem_post(semaphore_t* sem) { + spin_lock(&sem->lock); + sem->count++; + spin_unlock(&sem->lock); +} diff --git a/kernel/task.c b/kernel/task.c new file mode 100644 index 0000000..3df9e2d --- /dev/null +++ b/kernel/task.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include + +static pid_t next_pid = 1; +static task_t* current_task = NULL; +static task_t kernel_idle_task; + +static void task_wrapper(task_entry_fn_t entry, void* arg) { + if (entry) { + entry(arg); + } + task_exit(0); +} + +task_t* task_create(const char* name, task_entry_fn_t entry, void* arg, int priority) { + task_t* task = (task_t*)kzalloc(sizeof(task_t)); + if (!task) return NULL; + + task->pid = next_pid++; + strncpy(task->name, name ? name : "task", sizeof(task->name) - 1); + task->state = TASK_STATE_READY; + task->priority = priority; + task->time_slice = 10; // 10 ticks = 100ms default + + // Allocate 16 KB stack for thread + task->stack_size = 16384; + task->stack_base = (uint64_t)kmalloc(task->stack_size); + if (!task->stack_base) { + kfree(task); + return NULL; + } + + uint64_t* stack_top = (uint64_t*)(task->stack_base + task->stack_size); + + // Set up initial stack frame for AMD64 + *(--stack_top) = (uint64_t)arg; // RDI (arg) + *(--stack_top) = (uint64_t)entry; // RSI (entry function) + *(--stack_top) = (uint64_t)task_wrapper; // RIP + + task->rsp = (uint64_t)stack_top; + + INIT_LIST_HEAD(&task->list); + sched_add_task(task); + + return task; +} + +void task_exit(int exit_code) { + (void)exit_code; + if (current_task && current_task->pid != 0) { + current_task->state = TASK_STATE_DEAD; + sched_remove_task(current_task); + sched_yield(); + } + while (1) { + __asm__ volatile("hlt"); + } +} + +task_t* task_current(void) { + return current_task; +} + +pid_t task_get_pid(void) { + return current_task ? current_task->pid : 0; +} + +void task_set_current(task_t* task) { + current_task = task; +} + +void task_init_idle(void) { + memset(&kernel_idle_task, 0, sizeof(task_t)); + kernel_idle_task.pid = 0; + strcpy(kernel_idle_task.name, "swapper/0"); + kernel_idle_task.state = TASK_STATE_RUNNING; + INIT_LIST_HEAD(&kernel_idle_task.list); + current_task = &kernel_idle_task; +} diff --git a/lib/bitmap.c b/lib/bitmap.c new file mode 100644 index 0000000..12f850c --- /dev/null +++ b/lib/bitmap.c @@ -0,0 +1,39 @@ +#include + +int64_t bitmap_find_first_free(const bitmap_word_t* map, size_t total_bits) { + size_t words = (total_bits + BITS_PER_WORD - 1) / BITS_PER_WORD; + + for (size_t w = 0; w < words; w++) { + if (map[w] != (bitmap_word_t)~0ULL) { + for (size_t b = 0; b < BITS_PER_WORD; b++) { + size_t bit_idx = w * BITS_PER_WORD + b; + if (bit_idx >= total_bits) return -1; + if (!bitmap_test(map, bit_idx)) { + return (int64_t)bit_idx; + } + } + } + } + return -1; +} + +int64_t bitmap_find_contiguous_free(const bitmap_word_t* map, size_t total_bits, size_t count) { + if (count == 0) return -1; + if (count == 1) return bitmap_find_first_free(map, total_bits); + + size_t run = 0; + size_t start = 0; + + for (size_t i = 0; i < total_bits; i++) { + if (!bitmap_test(map, i)) { + if (run == 0) start = i; + run++; + if (run == count) { + return (int64_t)start; + } + } else { + run = 0; + } + } + return -1; +} diff --git a/lib/string.c b/lib/string.c new file mode 100644 index 0000000..63008d6 --- /dev/null +++ b/lib/string.c @@ -0,0 +1,226 @@ +#include +#include + +void* memset(void* dest, int val, size_t count) { + uint8_t* ptr = (uint8_t*)dest; + while (count--) { + *ptr++ = (uint8_t)val; + } + return dest; +} + +void* memcpy(void* dest, const void* src, size_t count) { + uint8_t* d = (uint8_t*)dest; + const uint8_t* s = (const uint8_t*)src; + while (count--) { + *d++ = *s++; + } + return dest; +} + +void* memmove(void* dest, const void* src, size_t count) { + uint8_t* d = (uint8_t*)dest; + const uint8_t* s = (const uint8_t*)src; + if (d < s) { + while (count--) { + *d++ = *s++; + } + } else { + d += count; + s += count; + while (count--) { + *(--d) = *(--s); + } + } + return dest; +} + +int memcmp(const void* ptr1, const void* ptr2, size_t num) { + const uint8_t* p1 = (const uint8_t*)ptr1; + const uint8_t* p2 = (const uint8_t*)ptr2; + while (num--) { + if (*p1 != *p2) { + return *p1 - *p2; + } + p1++; + p2++; + } + return 0; +} + +size_t strlen(const char* str) { + size_t len = 0; + while (str && str[len]) { + len++; + } + return len; +} + +int strcmp(const char* str1, const char* str2) { + if (!str1 || !str2) return (str1 == str2) ? 0 : (str1 ? 1 : -1); + while (*str1 && (*str1 == *str2)) { + str1++; + str2++; + } + return *(const unsigned char*)str1 - *(const unsigned char*)str2; +} + +int strncmp(const char* str1, const char* str2, size_t num) { + if (num == 0) return 0; + while (num > 0 && *str1 && (*str1 == *str2)) { + str1++; + str2++; + num--; + } + if (num == 0) return 0; + return *(const unsigned char*)str1 - *(const unsigned char*)str2; +} + +char* strcpy(char* dest, const char* src) { + char* orig_dest = dest; + while ((*dest++ = *src++)) { + } + return orig_dest; +} + +char* strncpy(char* dest, const char* src, size_t num) { + size_t i; + for (i = 0; i < num && src[i] != '\0'; i++) { + dest[i] = src[i]; + } + for (; i < num; i++) { + dest[i] = '\0'; + } + return dest; +} + +char* strcat(char* dest, const char* src) { + char* rd = dest; + while (*rd) rd++; + while ((*rd++ = *src++)); + return dest; +} + +char* strncat(char* dest, const char* src, size_t num) { + char* rd = dest; + while (*rd) rd++; + while (num-- && (*rd++ = *src++)); + *rd = '\0'; + return dest; +} + +char* strchr(const char* str, int c) { + if (!str) return NULL; + while (*str) { + if (*str == (char)c) return (char*)str; + str++; + } + if ((char)c == '\0') return (char*)str; + return NULL; +} + +char* strrchr(const char* str, int c) { + if (!str) return NULL; + const char* last = NULL; + while (*str) { + if (*str == (char)c) last = str; + str++; + } + if ((char)c == '\0') return (char*)str; + return (char*)last; +} + +char* strstr(const char* haystack, const char* needle) { + if (!haystack || !needle) return NULL; + if (*needle == '\0') return (char*)haystack; + + size_t needle_len = strlen(needle); + while (*haystack) { + if (*haystack == *needle && strncmp(haystack, needle, needle_len) == 0) { + return (char*)haystack; + } + haystack++; + } + return NULL; +} + +char* strdup(const char* s) { + if (!s) return NULL; + size_t len = strlen(s); + char* copy = (char*)kmalloc(len + 1); + if (copy) { + memcpy(copy, s, len + 1); + } + return copy; +} + +static void strreverse(char* begin, char* end) { + char aux; + while (end > begin) { + aux = *end; + *end-- = *begin; + *begin++ = aux; + } +} + +char* itoa(int value, char* str, int base) { + if (base < 2 || base > 36) { + *str = '\0'; + return str; + } + + char* ptr = str, *ptr1 = str; + int tmp_value; + + if (value < 0 && base == 10) { + value = -value; + } + + do { + tmp_value = value; + value /= base; + *ptr++ = "0123456789abcdef"[tmp_value - value * base]; + } while (value); + + if (tmp_value < 0 && base == 10) { + *ptr++ = '-'; + } + *ptr-- = '\0'; + + strreverse(ptr1, ptr); + return str; +} + +char* itoa_hex(uint64_t value, char* str) { + char* ptr = str; + char* ptr1 = str; + + if (value == 0) { + *ptr++ = '0'; + *ptr = '\0'; + return str; + } + + while (value > 0) { + uint8_t mod = value % 16; + *ptr++ = "0123456789ABCDEF"[mod]; + value /= 16; + } + + *ptr-- = '\0'; + strreverse(ptr1, ptr); + return str; +} + +int atoi(const char* str) { + int res = 0; + int sign = 1; + while (*str == ' ' || *str == '\t') str++; + if (*str == '-') { sign = -1; str++; } + else if (*str == '+') { str++; } + while (*str >= '0' && *str <= '9') { + res = res * 10 + (*str - '0'); + str++; + } + return res * sign; +} diff --git a/lib/vsprintf.c b/lib/vsprintf.c new file mode 100644 index 0000000..7faeab2 --- /dev/null +++ b/lib/vsprintf.c @@ -0,0 +1,199 @@ +#include +#include +#include +#include +#include + +static int format_number(char* buf, size_t size, uint64_t num, int base, bool is_signed, bool upper, int width, char pad, bool left_align) { + char temp[65]; + int pos = 0; + bool negative = false; + + if (is_signed && (int64_t)num < 0) { + negative = true; + num = (uint64_t)(-(int64_t)num); + } + + const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; + + if (num == 0) { + temp[pos++] = '0'; + } else { + while (num > 0) { + temp[pos++] = digits[num % base]; + num /= base; + } + } + + if (negative) { + temp[pos++] = '-'; + } + + int total_len = pos; + int pad_count = (width > total_len) ? (width - total_len) : 0; + int written = 0; + + // Right-aligned padding + if (!left_align) { + while (pad_count > 0 && (size_t)written < size - 1) { + buf[written++] = pad; + pad_count--; + } + } + + // Write number in reverse + for (int i = pos - 1; i >= 0 && (size_t)written < size - 1; i--) { + buf[written++] = temp[i]; + } + + // Left-aligned padding + if (left_align) { + while (pad_count > 0 && (size_t)written < size - 1) { + buf[written++] = ' '; + pad_count--; + } + } + + return written; +} + +int vsnprintf(char* buf, size_t size, const char* fmt, va_list args) { + if (!buf || size == 0) return 0; + + size_t out_idx = 0; + + for (size_t i = 0; fmt[i] != '\0' && out_idx < size - 1; i++) { + if (fmt[i] != '%') { + buf[out_idx++] = fmt[i]; + continue; + } + + i++; // Skip '%' + if (fmt[i] == '\0') break; + + // Flags + bool left_align = false; + if (fmt[i] == '-') { + left_align = true; + i++; + } + + char pad = ' '; + if (fmt[i] == '0' && !left_align) { + pad = '0'; + i++; + } + + // Width + int width = 0; + while (fmt[i] >= '0' && fmt[i] <= '9') { + width = width * 10 + (fmt[i] - '0'); + i++; + } + + // Length modifiers + bool is_long = false; + bool is_long_long = false; + if (fmt[i] == 'l') { + is_long = true; + i++; + if (fmt[i] == 'l') { + is_long_long = true; + i++; + } + } + + switch (fmt[i]) { + case 'c': { + char c = (char)va_arg(args, int); + if (out_idx < size - 1) buf[out_idx++] = c; + break; + } + case 's': { + const char* s = va_arg(args, const char*); + if (!s) s = "(null)"; + size_t s_len = strlen(s); + int pad_count = (width > (int)s_len) ? (width - (int)s_len) : 0; + + if (!left_align) { + while (pad_count > 0 && out_idx < size - 1) { + buf[out_idx++] = ' '; + pad_count--; + } + } + + while (*s && out_idx < size - 1) { + buf[out_idx++] = *s++; + } + + if (left_align) { + while (pad_count > 0 && out_idx < size - 1) { + buf[out_idx++] = ' '; + pad_count--; + } + } + break; + } + case 'd': + case 'i': { + int64_t val = is_long_long ? va_arg(args, int64_t) : (is_long ? va_arg(args, long) : va_arg(args, int)); + out_idx += format_number(buf + out_idx, size - out_idx, (uint64_t)val, 10, true, false, width, pad, left_align); + break; + } + case 'u': { + uint64_t val = is_long_long ? va_arg(args, uint64_t) : (is_long ? va_arg(args, unsigned long) : va_arg(args, unsigned int)); + out_idx += format_number(buf + out_idx, size - out_idx, val, 10, false, false, width, pad, left_align); + break; + } + case 'x': { + uint64_t val = is_long_long ? va_arg(args, uint64_t) : (is_long ? va_arg(args, unsigned long) : va_arg(args, unsigned int)); + out_idx += format_number(buf + out_idx, size - out_idx, val, 16, false, false, width, pad, left_align); + break; + } + case 'X': { + uint64_t val = is_long_long ? va_arg(args, uint64_t) : (is_long ? va_arg(args, unsigned long) : va_arg(args, unsigned int)); + out_idx += format_number(buf + out_idx, size - out_idx, val, 16, false, true, width, pad, left_align); + break; + } + case 'p': { + uint64_t ptr = (uint64_t)va_arg(args, void*); + if (out_idx < size - 3) { + buf[out_idx++] = '0'; + buf[out_idx++] = 'x'; + } + out_idx += format_number(buf + out_idx, size - out_idx, ptr, 16, false, false, 16, '0', false); + break; + } + case '%': { + if (out_idx < size - 1) buf[out_idx++] = '%'; + break; + } + default: + if (out_idx < size - 1) buf[out_idx++] = fmt[i]; + break; + } + } + + buf[out_idx] = '\0'; + return (int)out_idx; +} + +int sprintf(char* buf, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int ret = vsnprintf(buf, 4096, fmt, args); + va_end(args); + return ret; +} + +int snprintf(char* buf, size_t size, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int ret = vsnprintf(buf, size, fmt, args); + va_end(args); + return ret; +} + +int vsprintf(char* buf, const char* fmt, va_list args) { + return vsnprintf(buf, 4096, fmt, args); +} diff --git a/linker.ld b/linker.ld new file mode 100644 index 0000000..28f8c70 --- /dev/null +++ b/linker.ld @@ -0,0 +1,41 @@ +ENTRY(kernel_entry) + +SECTIONS +{ + /* Kernel starts at 1MB */ + . = 0x100000; + + _kernel_start = .; + + .text : ALIGN(4K) + { + *(.text*) + } + + .rodata : ALIGN(4K) + { + *(.rodata*) + } + + .data : ALIGN(4K) + { + *(.data*) + } + + .bss : ALIGN(4K) + { + PROVIDE(__bss_start = .); + *(COMMON) + *(.bss*) + PROVIDE(__bss_end = .); + } + + .stack : ALIGN(4K) + { + PROVIDE(stack_bottom = .); + . += 0x10000; /* 64 KB Dedicated Kernel Stack */ + PROVIDE(stack_top = .); + } + + _kernel_end = .; +} diff --git a/mm/kmalloc.c b/mm/kmalloc.c new file mode 100644 index 0000000..3451013 --- /dev/null +++ b/mm/kmalloc.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include + +#define HEAP_MAGIC 0x53554248 // "SUBH" +#define HEAP_INITIAL_PAGES 1024 // 4 MB initial kernel heap + +typedef struct heap_block { + uint32_t magic; + bool is_free; + size_t size; // payload size in bytes + struct heap_block* next; + struct heap_block* prev; +} heap_block_t; + +#define BLOCK_HEADER_SIZE sizeof(heap_block_t) + +static heap_block_t* heap_start = NULL; +static size_t heap_used_bytes = 0; +static size_t heap_free_bytes = 0; +static size_t heap_total_bytes = 0; +static spinlock_t heap_lock = SPINLOCK_INIT; + +void heap_init(void) { + void* initial_mem = pmm_alloc_pages(HEAP_INITIAL_PAGES); + if (!initial_mem) { + printk(KERN_ERR "Heap: Failed to allocate initial %d pages!\n", HEAP_INITIAL_PAGES); + return; + } + + heap_start = (heap_block_t*)initial_mem; + heap_total_bytes = HEAP_INITIAL_PAGES * PMM_PAGE_SIZE; + + heap_start->magic = HEAP_MAGIC; + heap_start->is_free = true; + heap_start->size = heap_total_bytes - BLOCK_HEADER_SIZE; + heap_start->next = NULL; + heap_start->prev = NULL; + + heap_used_bytes = 0; + heap_free_bytes = heap_start->size; +} + +void* kmalloc(size_t size) { + if (size == 0) return NULL; + + // Align size to 16 bytes for SIMD/64-bit performance + size = (size + 15) & ~15; + + spin_lock(&heap_lock); + + heap_block_t* curr = heap_start; + while (curr) { + if (curr->magic != HEAP_MAGIC) { + printk(KERN_ERR "Heap Corruption detected at %p!\n", curr); + spin_unlock(&heap_lock); + return NULL; + } + + if (curr->is_free && curr->size >= size) { + // Check if block can be split + if (curr->size >= size + BLOCK_HEADER_SIZE + 16) { + heap_block_t* new_block = (heap_block_t*)((uint8_t*)curr + BLOCK_HEADER_SIZE + size); + new_block->magic = HEAP_MAGIC; + new_block->is_free = true; + new_block->size = curr->size - size - BLOCK_HEADER_SIZE; + new_block->next = curr->next; + new_block->prev = curr; + + if (curr->next) { + curr->next->prev = new_block; + } + curr->next = new_block; + curr->size = size; + } + + curr->is_free = false; + heap_used_bytes += curr->size; + heap_free_bytes -= curr->size; + + spin_unlock(&heap_lock); + return (void*)((uint8_t*)curr + BLOCK_HEADER_SIZE); + } + + curr = curr->next; + } + + spin_unlock(&heap_lock); + printk(KERN_WARNING "Heap: Out of memory for size %llu bytes!\n", size); + return NULL; +} + +void* kzalloc(size_t size) { + void* ptr = kmalloc(size); + if (ptr) { + memset(ptr, 0, size); + } + return ptr; +} + +void kfree(void* ptr) { + if (!ptr) return; + + spin_lock(&heap_lock); + + heap_block_t* block = (heap_block_t*)((uint8_t*)ptr - BLOCK_HEADER_SIZE); + if (block->magic != HEAP_MAGIC) { + printk(KERN_ERR "kfree: Invalid pointer / heap corruption %p!\n", ptr); + spin_unlock(&heap_lock); + return; + } + + block->is_free = true; + heap_used_bytes -= block->size; + heap_free_bytes += block->size; + + // Coalesce with next block if free + if (block->next && block->next->is_free) { + block->size += BLOCK_HEADER_SIZE + block->next->size; + block->next = block->next->next; + if (block->next) { + block->next->prev = block; + } + } + + // Coalesce with prev block if free + if (block->prev && block->prev->is_free) { + block->prev->size += BLOCK_HEADER_SIZE + block->size; + block->prev->next = block->next; + if (block->next) { + block->next->prev = block->prev; + } + } + + spin_unlock(&heap_lock); +} + +void* krealloc(void* ptr, size_t new_size) { + if (!ptr) return kmalloc(new_size); + if (new_size == 0) { + kfree(ptr); + return NULL; + } + + heap_block_t* block = (heap_block_t*)((uint8_t*)ptr - BLOCK_HEADER_SIZE); + if (block->magic != HEAP_MAGIC) return NULL; + + if (block->size >= new_size) { + return ptr; + } + + void* new_ptr = kmalloc(new_size); + if (new_ptr) { + memcpy(new_ptr, ptr, block->size); + kfree(ptr); + } + return new_ptr; +} + +size_t heap_get_used_bytes(void) { return heap_used_bytes; } +size_t heap_get_free_bytes(void) { return heap_free_bytes; } +size_t heap_get_total_bytes(void) { return heap_total_bytes; } diff --git a/mm/pmm.c b/mm/pmm.c new file mode 100644 index 0000000..3164cd1 --- /dev/null +++ b/mm/pmm.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include + +#define MAX_PHYSICAL_PAGES (1024 * 1024) // 4 GB (1,048,576 4KB pages) +#define BITMAP_SIZE (MAX_PHYSICAL_PAGES / 64) + +static bitmap_word_t page_bitmap[BITMAP_SIZE]; +static uint64_t total_pages = 0; +static uint64_t used_pages = 0; +static uint64_t total_memory_bytes = 0; +static uint64_t usable_memory_bytes = 0; + +void pmm_init(void* memory_map, uint64_t memory_map_count) { + // Initially mark all pages as reserved (1 in bitmap) + memset(page_bitmap, 0xFF, sizeof(page_bitmap)); + total_pages = 0; + used_pages = 0; + total_memory_bytes = 0; + usable_memory_bytes = 0; + + e820_entry_t* entries = (e820_entry_t*)memory_map; + + for (uint64_t i = 0; i < memory_map_count; i++) { + uint64_t base = entries[i].base; + uint64_t length = entries[i].length; + uint32_t type = entries[i].type; + + total_memory_bytes += length; + + if (type == E820_TYPE_USABLE) { + usable_memory_bytes += length; + + uint64_t start_page = base / PMM_PAGE_SIZE; + uint64_t page_count = length / PMM_PAGE_SIZE; + + for (uint64_t p = 0; p < page_count; p++) { + uint64_t page = start_page + p; + if (page < MAX_PHYSICAL_PAGES) { + bitmap_clear(page_bitmap, page); + if (page >= total_pages) { + total_pages = page + 1; + } + } + } + } + } + + // Fallback if no E820 usable regions + if (usable_memory_bytes == 0 || total_pages == 0) { + total_pages = 32768; // 128 MB + total_memory_bytes = 128ULL * 1024 * 1024; + usable_memory_bytes = 128ULL * 1024 * 1024; + for (uint64_t p = 0; p < total_pages; p++) { + bitmap_clear(page_bitmap, p); + } + } + + // Reserve lower 4 MB (bootloader, page tables, kernel image) + size_t kernel_reserved_pages = (4 * 1024 * 1024) / PMM_PAGE_SIZE; // 1024 pages + for (size_t p = 0; p < kernel_reserved_pages; p++) { + bitmap_set(page_bitmap, p); + } + + // Count actual used pages (bits set in bitmap up to total_pages) + used_pages = 0; + for (uint64_t p = 0; p < total_pages; p++) { + if (bitmap_test(page_bitmap, p)) { + used_pages++; + } + } + + printk(KERN_INFO "PMM: Total Memory: %llu MB, Usable: %llu MB, Free Pages: %llu\n", + total_memory_bytes / (1024 * 1024), + usable_memory_bytes / (1024 * 1024), + pmm_get_free_pages()); +} + +void* pmm_alloc_page(void) { + int64_t page = bitmap_find_first_free(page_bitmap, total_pages); + if (page < 0) { + return NULL; // Out of memory + } + + bitmap_set(page_bitmap, (size_t)page); + used_pages++; + return (void*)(page * PMM_PAGE_SIZE); +} + +void* pmm_alloc_pages(size_t count) { + if (count == 0) return NULL; + if (count == 1) return pmm_alloc_page(); + + int64_t start_page = bitmap_find_contiguous_free(page_bitmap, total_pages, count); + if (start_page < 0) return NULL; + + for (size_t i = 0; i < count; i++) { + bitmap_set(page_bitmap, (size_t)(start_page + i)); + } + used_pages += count; + return (void*)(start_page * PMM_PAGE_SIZE); +} + +void pmm_free_page(void* page) { + uint64_t page_idx = (uint64_t)page / PMM_PAGE_SIZE; + if (page_idx < total_pages && bitmap_test(page_bitmap, page_idx)) { + bitmap_clear(page_bitmap, page_idx); + used_pages--; + } +} + +void pmm_free_pages(void* page, size_t count) { + uint64_t start_idx = (uint64_t)page / PMM_PAGE_SIZE; + for (size_t i = 0; i < count; i++) { + uint64_t page_idx = start_idx + i; + if (page_idx < total_pages && bitmap_test(page_bitmap, page_idx)) { + bitmap_clear(page_bitmap, page_idx); + used_pages--; + } + } +} + +uint64_t pmm_get_total_pages(void) { return total_pages; } +uint64_t pmm_get_used_pages(void) { return used_pages; } +uint64_t pmm_get_free_pages(void) { return (total_pages > used_pages) ? (total_pages - used_pages) : 0; } +uint64_t pmm_get_total_memory(void) { return total_memory_bytes; } +uint64_t pmm_get_usable_memory(void) { return usable_memory_bytes; } diff --git a/net/core/net.c b/net/core/net.c new file mode 100644 index 0000000..369e8f7 --- /dev/null +++ b/net/core/net.c @@ -0,0 +1,323 @@ +#include +#include +#include +#include +#include +#include +#include + +#define MAX_ARP_ENTRIES 16 + +static net_if_t primary_if; +static arp_entry_t arp_cache[MAX_ARP_ENTRIES]; +static int arp_cache_count = 0; + +static volatile bool ping_received = false; +static volatile uint16_t ping_id = 0; +static volatile uint16_t ping_seq = 0; +static volatile uint64_t ping_time = 0; + +static const uint8_t eth_broadcast[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +static uint16_t net_checksum(const void* data, size_t len) { + const uint16_t* ptr = (const uint16_t*)data; + uint32_t sum = 0; + + while (len > 1) { + sum += *ptr++; + len -= 2; + } + if (len > 0) { + sum += *(const uint8_t*)ptr; + } + while (sum >> 16) { + sum = (sum & 0xFFFF) + (sum >> 16); + } + return (uint16_t)(~sum); +} + +void ip_to_str(uint32_t ip, char* buf) { + uint8_t* p = (uint8_t*)&ip; + sprintf(buf, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); +} + +void mac_to_str(const uint8_t* mac, char* buf) { + sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +} + +uint32_t ip_parse(const char* ip_str) { + if (!ip_str) return 0; + uint32_t parts[4] = {0, 0, 0, 0}; + int idx = 0; + + while (*ip_str && idx < 4) { + if (*ip_str >= '0' && *ip_str <= '9') { + parts[idx] = parts[idx] * 10 + (*ip_str - '0'); + } else if (*ip_str == '.') { + idx++; + } + ip_str++; + } + if (idx != 3) return 0; + return parts[0] | (parts[1] << 8) | (parts[2] << 16) | (parts[3] << 24); +} + +static void arp_cache_insert(uint32_t ip, const uint8_t* mac) { + for (int i = 0; i < arp_cache_count; i++) { + if (arp_cache[i].ip == ip) { + memcpy(arp_cache[i].mac, mac, 6); + arp_cache[i].valid = true; + return; + } + } + if (arp_cache_count < MAX_ARP_ENTRIES) { + arp_cache[arp_cache_count].ip = ip; + memcpy(arp_cache[arp_cache_count].mac, mac, 6); + arp_cache[arp_cache_count].valid = true; + arp_cache_count++; + } else { + arp_cache[0].ip = ip; + memcpy(arp_cache[0].mac, mac, 6); + arp_cache[0].valid = true; + } +} + +static bool arp_cache_lookup(uint32_t ip, uint8_t* mac_out) { + for (int i = 0; i < arp_cache_count; i++) { + if (arp_cache[i].valid && arp_cache[i].ip == ip) { + memcpy(mac_out, arp_cache[i].mac, 6); + return true; + } + } + return false; +} + +void net_send_eth(const uint8_t* dst_mac, uint16_t ethertype, const void* payload, uint16_t len) { + uint16_t total_len = sizeof(eth_header_t) + len; + if (total_len < 60) total_len = 60; // Minimum Ethernet frame + + uint8_t* frame = (uint8_t*)kzalloc(total_len); + if (!frame) return; + + eth_header_t* eth = (eth_header_t*)frame; + memcpy(eth->dst_mac, dst_mac, 6); + memcpy(eth->src_mac, primary_if.mac, 6); + eth->ethertype = htons(ethertype); + + if (payload && len > 0) { + memcpy(frame + sizeof(eth_header_t), payload, len); + } + + e1000_send(frame, total_len); + kfree(frame); +} + +void net_send_arp_req(uint32_t target_ip) { + arp_header_t arp; + arp.htype = htons(1); + arp.ptype = htons(ETHERTYPE_IPV4); + arp.hlen = 6; + arp.plen = 4; + arp.opcode = htons(1); // ARP Request + memcpy(arp.sender_mac, primary_if.mac, 6); + arp.sender_ip = primary_if.ip; + memset(arp.target_mac, 0, 6); + arp.target_ip = target_ip; + + net_send_eth(eth_broadcast, ETHERTYPE_ARP, &arp, sizeof(arp_header_t)); +} + +void net_send_ip(uint32_t dst_ip, uint8_t protocol, const void* payload, uint16_t len) { + uint8_t dst_mac[6]; + uint32_t lookup_ip = dst_ip; + + if ((dst_ip & primary_if.subnet) != (primary_if.ip & primary_if.subnet)) { + lookup_ip = primary_if.gateway; + } + + if (!arp_cache_lookup(lookup_ip, dst_mac)) { + net_send_arp_req(lookup_ip); + memcpy(dst_mac, eth_broadcast, 6); + } + + uint16_t ip_len = sizeof(ip_header_t) + len; + uint8_t* buf = (uint8_t*)kmalloc(ip_len); + if (!buf) return; + + ip_header_t* ip = (ip_header_t*)buf; + ip->ihl_version = 0x45; + ip->tos = 0; + ip->total_length = htons(ip_len); + ip->id = htons((uint16_t)pit_get_ticks()); + ip->flags_fragment = htons(0x4000); + ip->ttl = 64; + ip->protocol = protocol; + ip->checksum = 0; + ip->src_ip = primary_if.ip; + ip->dst_ip = dst_ip; + + ip->checksum = net_checksum(ip, sizeof(ip_header_t)); + + if (payload && len > 0) { + memcpy(buf + sizeof(ip_header_t), payload, len); + } + + net_send_eth(dst_mac, ETHERTYPE_IPV4, buf, ip_len); + kfree(buf); +} + +static void net_handle_arp(const uint8_t* data, uint16_t len) { + if (len < sizeof(arp_header_t)) return; + const arp_header_t* arp = (const arp_header_t*)data; + + uint16_t op = ntohs(arp->opcode); + arp_cache_insert(arp->sender_ip, arp->sender_mac); + + if (op == 1) { // Request for our IP + if (arp->target_ip == primary_if.ip) { + arp_header_t reply; + reply.htype = htons(1); + reply.ptype = htons(ETHERTYPE_IPV4); + reply.hlen = 6; + reply.plen = 4; + reply.opcode = htons(2); // Reply + memcpy(reply.sender_mac, primary_if.mac, 6); + reply.sender_ip = primary_if.ip; + memcpy(reply.target_mac, arp->sender_mac, 6); + reply.target_ip = arp->sender_ip; + + net_send_eth(arp->sender_mac, ETHERTYPE_ARP, &reply, sizeof(arp_header_t)); + } + } +} + +static void net_handle_icmp(uint32_t src_ip, const uint8_t* data, uint16_t len) { + if (len < sizeof(icmp_header_t)) return; + const icmp_header_t* icmp = (const icmp_header_t*)data; + + if (icmp->type == 8) { // Echo Request -> Auto-Reply + uint8_t* rep = (uint8_t*)kmalloc(len); + if (!rep) return; + memcpy(rep, data, len); + + icmp_header_t* rep_icmp = (icmp_header_t*)rep; + rep_icmp->type = 0; // Echo Reply + rep_icmp->code = 0; + rep_icmp->checksum = 0; + rep_icmp->checksum = net_checksum(rep, len); + + net_send_ip(src_ip, IP_PROTO_ICMP, rep, len); + kfree(rep); + } else if (icmp->type == 0) { // Echo Reply + ping_id = ntohs(icmp->id); + ping_seq = ntohs(icmp->sequence); + ping_time = pit_get_ticks(); + ping_received = true; + } +} + +void net_receive(const uint8_t* packet, uint16_t length) { + if (!packet || length < sizeof(eth_header_t)) return; + + const eth_header_t* eth = (const eth_header_t*)packet; + uint16_t type = ntohs(eth->ethertype); + + const uint8_t* payload = packet + sizeof(eth_header_t); + uint16_t payload_len = length - sizeof(eth_header_t); + + if (type == ETHERTYPE_ARP) { + net_handle_arp(payload, payload_len); + } else if (type == ETHERTYPE_IPV4) { + if (payload_len >= sizeof(ip_header_t)) { + const ip_header_t* ip = (const ip_header_t*)payload; + uint8_t ihl = (ip->ihl_version & 0x0F) * 4; + if (payload_len >= ihl) { + if (ip->protocol == IP_PROTO_ICMP) { + net_handle_icmp(ip->src_ip, payload + ihl, payload_len - ihl); + } + } + } + } +} + +int net_ping(uint32_t target_ip, uint32_t count, uint32_t timeout_ms) { + char target_str[16]; + ip_to_str(target_ip, target_str); + printk(KERN_INFO "PING %s (56 data bytes):\n", target_str); + + int received = 0; + uint16_t p_id = 0x5355; // 'SU' + + for (uint32_t seq = 1; seq <= count; seq++) { + ping_received = false; + uint64_t t_start = pit_get_ticks(); + + const char* msg = "SUB-OS ICMP 2026"; + uint16_t msg_len = (uint16_t)strlen(msg); + uint16_t icmp_sz = sizeof(icmp_header_t) + msg_len; + + uint8_t* ibuf = (uint8_t*)kmalloc(icmp_sz); + if (ibuf) { + icmp_header_t* icmp = (icmp_header_t*)ibuf; + icmp->type = 8; + icmp->code = 0; + icmp->checksum = 0; + icmp->id = htons(p_id); + icmp->sequence = htons((uint16_t)seq); + memcpy(ibuf + sizeof(icmp_header_t), msg, msg_len); + icmp->checksum = net_checksum(ibuf, icmp_sz); + + net_send_ip(target_ip, IP_PROTO_ICMP, ibuf, icmp_sz); + kfree(ibuf); + } + + uint64_t wait_ticks = timeout_ms / 10; + if (wait_ticks == 0) wait_ticks = 100; + + while (!ping_received && (pit_get_ticks() - t_start < wait_ticks)) { + __asm__ volatile ("hlt"); + } + + if (ping_received && ping_id == p_id && ping_seq == seq) { + uint64_t elapsed_ms = (ping_time - t_start) * 10; + printk(KERN_INFO "64 bytes from %s: icmp_seq=%u ttl=64 time=%llu ms\n", target_str, seq, elapsed_ms); + received++; + } else { + printk(KERN_INFO "Request timeout for icmp_seq %u\n", seq); + } + + pit_sleep(400); + } + + printk(KERN_INFO "--- %s ping statistics ---\n", target_str); + printk(KERN_INFO "%u packets transmitted, %d received, %u%% packet loss\n", + count, received, ((count - received) * 100) / count); + return received; +} + +void net_init(void) { + strcpy(primary_if.name, "eth0"); + e1000_get_mac(primary_if.mac); + + primary_if.ip = ip_parse("10.0.2.15"); + primary_if.gateway = ip_parse("10.0.2.2"); + primary_if.subnet = ip_parse("255.255.255.0"); + primary_if.dns = ip_parse("10.0.2.3"); + primary_if.is_up = true; + + // Seed gateway in ARP cache + uint8_t qemu_gw[6] = {0x52, 0x54, 0x00, 0x12, 0x34, 0x56}; + arp_cache_insert(primary_if.gateway, qemu_gw); + + printk(KERN_INFO "NET: eth0 configured (IP: 10.0.2.15, GW: 10.0.2.2, Mask: 255.255.255.0)\n"); +} + +net_if_t* net_get_primary_if(void) { + return &primary_if; +} + +arp_entry_t* net_get_arp_table(int* count_out) { + if (count_out) *count_out = arp_cache_count; + return arp_cache; +} diff --git a/tools/build-cross-compiler.sh b/tools/build-cross-compiler.sh new file mode 100755 index 0000000..89c9eb2 --- /dev/null +++ b/tools/build-cross-compiler.sh @@ -0,0 +1,115 @@ +#!/bin/bash +# ============================================================================= +# SUB-OS Cross-Compiler Build Script +# Builds x86_64-elf-gcc and x86_64-elf-binutils from source +# ============================================================================= + +set -e + +# Configuration +export TARGET=x86_64-elf +export PREFIX="/usr/local/cross" +export PATH="$PREFIX/bin:$PATH" + +BINUTILS_VERSION="2.42" +GCC_VERSION="13.3.0" + +BUILD_DIR="/tmp/cross-compiler-build" +JOBS=$(nproc) + +echo "============================================" +echo " Building x86_64-elf Cross-Compiler" +echo "============================================" +echo " Target: $TARGET" +echo " Prefix: $PREFIX" +echo " Binutils: $BINUTILS_VERSION" +echo " GCC: $GCC_VERSION" +echo " Jobs: $JOBS" +echo "============================================" + +# Create directories +sudo mkdir -p "$PREFIX" +mkdir -p "$BUILD_DIR" +cd "$BUILD_DIR" + +# ---- Step 1: Download sources ---- +echo "" +echo "[1/4] Downloading sources..." + +if [ ! -f "binutils-${BINUTILS_VERSION}.tar.xz" ]; then + curl -LO "https://ftp.gnu.org/gnu/binutils/binutils-${BINUTILS_VERSION}.tar.xz" +fi + +if [ ! -f "gcc-${GCC_VERSION}.tar.xz" ]; then + curl -LO "https://ftp.gnu.org/gnu/gcc/gcc-${GCC_VERSION}/gcc-${GCC_VERSION}.tar.xz" +fi + +# ---- Step 2: Extract sources ---- +echo "" +echo "[2/4] Extracting sources..." + +if [ ! -d "binutils-${BINUTILS_VERSION}" ]; then + tar xf "binutils-${BINUTILS_VERSION}.tar.xz" +fi + +if [ ! -d "gcc-${GCC_VERSION}" ]; then + tar xf "gcc-${GCC_VERSION}.tar.xz" +fi + +# ---- Step 3: Build Binutils ---- +echo "" +echo "[3/4] Building binutils for ${TARGET}..." + +mkdir -p build-binutils +cd build-binutils + +../binutils-${BINUTILS_VERSION}/configure \ + --target=$TARGET \ + --prefix="$PREFIX" \ + --with-sysroot \ + --disable-nls \ + --disable-werror + +make -j$JOBS +sudo make install + +cd "$BUILD_DIR" + +# ---- Step 4: Build GCC ---- +echo "" +echo "[4/4] Building GCC for ${TARGET}..." + +mkdir -p build-gcc +cd build-gcc + +../gcc-${GCC_VERSION}/configure \ + --target=$TARGET \ + --prefix="$PREFIX" \ + --disable-nls \ + --enable-languages=c \ + --without-headers + +make -j$JOBS all-gcc +make -j$JOBS all-target-libgcc +sudo make install-gcc +sudo make install-target-libgcc + +cd "$BUILD_DIR" + +# ---- Verify ---- +echo "" +echo "============================================" +echo " Cross-Compiler Build Complete!" +echo "============================================" +echo "" +$PREFIX/bin/$TARGET-gcc --version +$PREFIX/bin/$TARGET-ld --version | head -1 +echo "" +echo "Installed to: $PREFIX/bin/" +echo "" +echo "Add to your PATH:" +echo " export PATH=\"$PREFIX/bin:\$PATH\"" +echo "" +echo "Or add to ~/.bashrc for persistence:" +echo " echo 'export PATH=\"$PREFIX/bin:\$PATH\"' >> ~/.bashrc" +echo "============================================" diff --git a/tools/setup.sh b/tools/setup.sh new file mode 100755 index 0000000..f29d77e --- /dev/null +++ b/tools/setup.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +echo "Setting up SUB-OS build environment..." + +if [ -f /etc/os-release ]; then + . /etc/os-release + OS=$ID +else + echo "Cannot detect OS. Please manually install dependencies." + exit 1 +fi + +case $OS in + ubuntu|debian) + echo "Detected Debian/Ubuntu-based OS." + sudo apt-get update + sudo apt-get install -y nasm gcc binutils make qemu-system-x86 + ;; + fedora) + echo "Detected Fedora." + sudo dnf install -y nasm gcc binutils make qemu-system-x86 + ;; + arch) + echo "Detected Arch Linux." + sudo pacman -Sy --noconfirm nasm gcc binutils make qemu-system-x86 + ;; + *) + echo "Unsupported OS: $OS. Please install: nasm, gcc, binutils, make, qemu-system-x86." + ;; +esac + +if ! command -v x86_64-elf-gcc &> /dev/null; then + echo "" + echo "WARNING: x86_64-elf-gcc (cross-compiler) not found in PATH." + echo "The Makefile will fallback to the default gcc." + echo "For the best results, it is highly recommended to build an x86_64-elf GCC cross-compiler." + echo "Instructions to build a cross-compiler: https://wiki.osdev.org/GCC_Cross-Compiler" +fi + +echo "Setup script finished!" diff --git a/userland/lazybox/lazybox.c b/userland/lazybox/lazybox.c new file mode 100644 index 0000000..0e02061 --- /dev/null +++ b/userland/lazybox/lazybox.c @@ -0,0 +1,456 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ------------------------------------------------------------- +// Core Utilities +// ------------------------------------------------------------- + +static int applet_ls(int argc, char** argv) { + const char* path = (argc >= 2) ? argv[1] : "/"; + vfs_node_t* dir = vfs_namei(path); + + if (!dir) { + printk(KERN_ERR "ls: cannot access '%s': No such file or directory\n", path); + return 1; + } + + if (!(dir->flags & FS_DIRECTORY)) { + printk(KERN_INFO "%s\n", path); + return 0; + } + + printk(ANSI_BRIGHT_CYAN "Type Size (B) Inode Name\n" ANSI_RESET); + printk("-----------------------------------------\n"); + + uint32_t idx = 0; + struct vfs_dirent* ent; + while ((ent = dir->readdir(dir, idx++)) != NULL) { + const char* color = ANSI_WHITE; + const char* type_str = "FILE "; + if (ent->type & FS_DIRECTORY) { + color = ANSI_BRIGHT_BLUE; + type_str = "DIR "; + } else if (ent->type & FS_CHARDEVICE) { + color = ANSI_BRIGHT_YELLOW; + type_str = "CHR "; + } + + vfs_node_t* child = dir->finddir ? dir->finddir(dir, ent->name) : NULL; + size_t sz = child ? child->length : 0; + + printk("%s %8llu %6llu %s%s" ANSI_RESET "\n", + type_str, sz, ent->inode, color, ent->name); + } + return 0; +} + +static int applet_cat(int argc, char** argv) { + if (argc < 2) { + printk(KERN_INFO "Usage: cat \n"); + return 1; + } + + for (int i = 1; i < argc; i++) { + int fd = vfs_open(argv[i], O_RDONLY); + if (fd < 0) { + printk(KERN_ERR "cat: %s: No such file or directory\n", argv[i]); + continue; + } + + char buf[512]; + ssize_t bytes; + while ((bytes = vfs_read(fd, buf, sizeof(buf) - 1)) > 0) { + buf[bytes] = '\0'; + printk("%s", buf); + } + vfs_close(fd); + } + return 0; +} + +static int applet_touch(int argc, char** argv) { + if (argc < 2) { + printk(KERN_INFO "Usage: touch \n"); + return 1; + } + + for (int i = 1; i < argc; i++) { + int fd = vfs_open(argv[i], O_CREAT | O_RDWR); + if (fd >= 0) { + vfs_close(fd); + } + } + return 0; +} + +static int applet_mkdir(int argc, char** argv) { + if (argc < 2) { + printk(KERN_INFO "Usage: mkdir \n"); + return 1; + } + + for (int i = 1; i < argc; i++) { + if (vfs_mkdir(argv[i], 0755) != 0) { + printk(KERN_ERR "mkdir: cannot create directory '%s'\n", argv[i]); + } + } + return 0; +} + +static int applet_echo(int argc, char** argv) { + for (int i = 1; i < argc; i++) { + printk("%s%s", argv[i], (i == argc - 1) ? "" : " "); + } + printk("\n"); + return 0; +} + +static int applet_wc(int argc, char** argv) { + if (argc < 2) { + printk(KERN_INFO "Usage: wc \n"); + return 1; + } + + int fd = vfs_open(argv[1], O_RDONLY); + if (fd < 0) { + printk(KERN_ERR "wc: %s: No such file\n", argv[1]); + return 1; + } + + int lines = 0, words = 0, bytes = 0; + bool in_word = false; + char buf[256]; + ssize_t n; + + while ((n = vfs_read(fd, buf, sizeof(buf))) > 0) { + bytes += (int)n; + for (ssize_t i = 0; i < n; i++) { + if (buf[i] == '\n') lines++; + if (buf[i] == ' ' || buf[i] == '\t' || buf[i] == '\n' || buf[i] == '\r') { + in_word = false; + } else if (!in_word) { + in_word = true; + words++; + } + } + } + vfs_close(fd); + + printk(KERN_INFO "%4d %4d %4d %s\n", lines, words, bytes, argv[1]); + return 0; +} + +// ------------------------------------------------------------- +// Cryptographic Hash Utilities +// ------------------------------------------------------------- + +static int applet_md5sum(int argc, char** argv) { + if (argc < 2) { + printk(KERN_INFO "Usage: md5sum \n"); + return 1; + } + + int fd = vfs_open(argv[1], O_RDONLY); + if (fd >= 0) { + md5_ctx_t ctx; + md5_init(&ctx); + char buf[512]; + ssize_t n; + while ((n = vfs_read(fd, buf, sizeof(buf))) > 0) { + md5_update(&ctx, buf, (size_t)n); + } + vfs_close(fd); + + uint8_t hash[16]; + md5_final(hash, &ctx); + for (int i = 0; i < 16; i++) printk("%02x", hash[i]); + printk(" %s\n", argv[1]); + } else { + // String mode + char out[33]; + md5_string(argv[1], out); + printk("%s \"%s\"\n", out, argv[1]); + } + return 0; +} + +static int applet_sha256sum(int argc, char** argv) { + if (argc < 2) { + printk(KERN_INFO "Usage: sha256sum \n"); + return 1; + } + + int fd = vfs_open(argv[1], O_RDONLY); + if (fd >= 0) { + sha256_ctx_t ctx; + sha256_init(&ctx); + char buf[512]; + ssize_t n; + while ((n = vfs_read(fd, buf, sizeof(buf))) > 0) { + sha256_update(&ctx, buf, (size_t)n); + } + vfs_close(fd); + + uint8_t hash[32]; + sha256_final(hash, &ctx); + for (int i = 0; i < 32; i++) printk("%02x", hash[i]); + printk(" %s\n", argv[1]); + } else { + char out[65]; + sha256_string(argv[1], out); + printk("%s \"%s\"\n", out, argv[1]); + } + return 0; +} + +static int applet_crc32(int argc, char** argv) { + if (argc < 2) { + printk(KERN_INFO "Usage: crc32 \n"); + return 1; + } + + uint32_t val = crc32(0, argv[1], strlen(argv[1])); + printk("0x%08X \"%s\"\n", val, argv[1]); + return 0; +} + +// ------------------------------------------------------------- +// Network Utilities +// ------------------------------------------------------------- + +static int applet_ifconfig(int argc, char** argv) { + (void)argc; (void)argv; + net_if_t* net_if = net_get_primary_if(); + + char ip_s[16], gw_s[16], mask_s[16], mac_s[18]; + ip_to_str(net_if->ip, ip_s); + ip_to_str(net_if->gateway, gw_s); + ip_to_str(net_if->subnet, mask_s); + mac_to_str(net_if->mac, mac_s); + + printk(ANSI_BRIGHT_GREEN "%s: " ANSI_RESET "flags=4163 mtu 1500\n", net_if->name); + printk(" inet " ANSI_BRIGHT_YELLOW "%s" ANSI_RESET " netmask %s broadcast %s\n", ip_s, mask_s, gw_s); + printk(" ether " ANSI_BRIGHT_CYAN "%s" ANSI_RESET " txqueuelen 1000 (Ethernet)\n", mac_s); + printk(" RX packets %llu bytes %llu\n", e1000_get_rx_packets(), e1000_get_rx_bytes()); + printk(" TX packets %llu bytes %llu\n", e1000_get_tx_packets(), e1000_get_tx_bytes()); + printk(" Link Status: %s\n", e1000_is_link_up() ? ANSI_GREEN "UP" ANSI_RESET : ANSI_RED "DOWN" ANSI_RESET); + return 0; +} + +static int applet_ping(int argc, char** argv) { + if (argc < 2) { + printk(KERN_INFO "Usage: ping [count]\n"); + return 1; + } + + uint32_t target_ip = ip_parse(argv[1]); + if (target_ip == 0) { + printk(KERN_ERR "Invalid IP address: %s\n", argv[1]); + return 1; + } + + uint32_t count = 4; + if (argc >= 3) { + int val = atoi(argv[2]); + if (val > 0) count = (uint32_t)val; + } + + net_ping(target_ip, count, 1000); + return 0; +} + +static int applet_arp(int argc, char** argv) { + (void)argc; (void)argv; + int count = 0; + arp_entry_t* tbl = net_get_arp_table(&count); + + printk(ANSI_BRIGHT_CYAN "Address HWtype HWaddress Flags Iface\n" ANSI_RESET); + char ip_s[16], mac_s[18]; + for (int i = 0; i < count; i++) { + if (tbl[i].valid) { + ip_to_str(tbl[i].ip, ip_s); + mac_to_str(tbl[i].mac, mac_s); + printk("%-16s ether %-18s C eth0\n", ip_s, mac_s); + } + } + return 0; +} + +// ------------------------------------------------------------- +// System & Hardware Utilities +// ------------------------------------------------------------- + +static int applet_uname(int argc, char** argv) { + bool all = (argc >= 2 && strcmp(argv[1], "-a") == 0); + if (all) { + printk("SUB-OS sub-node 0.2.0-lts #1 SMP PREEMPT Sun Aug 16 2026 x86_64 GNU/LazyBox\n"); + } else { + printk("SUB-OS\n"); + } + return 0; +} + +static int applet_free(int argc, char** argv) { + (void)argc; (void)argv; + uint64_t total_kb = (pmm_get_total_pages() * 4096) / 1024; + uint64_t used_kb = (pmm_get_used_pages() * 4096) / 1024; + uint64_t free_kb = (pmm_get_free_pages() * 4096) / 1024; + + size_t heap_used = heap_get_used_bytes(); + size_t heap_free = heap_get_free_bytes(); + + printk(ANSI_BRIGHT_CYAN " total used free shared buff/cache available\n" ANSI_RESET); + printk("Mem: %12llu%12llu%12llu 0%12llu%12llu\n", + total_kb, used_kb, free_kb, (heap_used / 1024), free_kb); + printk("Heap: %12llu%12llu%12llu\n", + (heap_used + heap_free) / 1024, heap_used / 1024, heap_free / 1024); + return 0; +} + +static int applet_uptime(int argc, char** argv) { + (void)argc; (void)argv; + uint64_t ticks = pit_get_ticks(); + uint64_t secs = ticks / 100; + uint64_t mins = secs / 60; + uint64_t hours = mins / 60; + secs %= 60; + mins %= 60; + + printk("up %02llu:%02llu:%02llu, 1 user, load average: 0.00, 0.01, 0.00\n", hours, mins, secs); + return 0; +} + +static int applet_dmesg(int argc, char** argv) { + (void)argc; (void)argv; + dmesg_dump(); + return 0; +} + +static int applet_hdparm(int argc, char** argv) { + (void)argc; (void)argv; + const ata_device_t* dev = ata_get_primary_master(); + if (!dev || !dev->present) { + printk(KERN_ERR "No ATA Hard Disk Drive detected.\n"); + return 1; + } + + printk(ANSI_BRIGHT_CYAN "/dev/sda (ATA Primary Master):\n" ANSI_RESET); + printk(" Model Number: %s\n", dev->model); + printk(" Capacity: %llu MB (%u sectors)\n", + ((uint64_t)dev->sector_count * 512) / (1024 * 1024), dev->sector_count); + printk(" Sector Size: 512 bytes logical/physical\n"); + printk(" Addressing: 28-bit LBA Mode\n"); + return 0; +} + +static int applet_clear(int argc, char** argv) { + (void)argc; (void)argv; + tty_clear(); + return 0; +} + +static int applet_sleep(int argc, char** argv) { + if (argc < 2) { + printk(KERN_INFO "Usage: sleep \n"); + return 1; + } + int secs = atoi(argv[1]); + if (secs > 0) { + pit_sleep((uint32_t)secs * 1000); + } + return 0; +} + +static int applet_lazybox(int argc, char** argv); + +// Applet Dispatch Table +static const lazybox_applet_t applets[] = { + {"lazybox", applet_lazybox, "lazybox [applet]", "Multi-call utility manager", "Core"}, + {"ls", applet_ls, "ls [path]", "List directory contents", "Filesystem"}, + {"cat", applet_cat, "cat ", "Concatenate files to stdout","Filesystem"}, + {"touch", applet_touch, "touch ", "Create empty files", "Filesystem"}, + {"mkdir", applet_mkdir, "mkdir ", "Create directory", "Filesystem"}, + {"wc", applet_wc, "wc ", "Count lines, words, bytes", "Filesystem"}, + {"echo", applet_echo, "echo [text...]", "Display text to stdout", "Core"}, + {"md5sum", applet_md5sum, "md5sum ", "Compute MD5 hash", "Crypto"}, + {"sha256sum", applet_sha256sum, "sha256sum ", "Compute SHA-256 hash", "Crypto"}, + {"crc32", applet_crc32, "crc32 ", "Calculate CRC32 checksum", "Crypto"}, + {"ifconfig", applet_ifconfig, "ifconfig", "Display network interface", "Network"}, + {"ping", applet_ping, "ping [count]", "ICMP Echo ping utility", "Network"}, + {"arp", applet_arp, "arp", "View ARP cache table", "Network"}, + {"hdparm", applet_hdparm, "hdparm", "Inspect ATA hard disk", "Storage"}, + {"uname", applet_uname, "uname [-a]", "Print system architecture", "System"}, + {"free", applet_free, "free", "Display RAM and Heap usage", "System"}, + {"uptime", applet_uptime, "uptime", "System running duration", "System"}, + {"dmesg", applet_dmesg, "dmesg", "Dump kernel boot ringbuffer","System"}, + {"sleep", applet_sleep, "sleep ", "Delay execution", "System"}, + {"clear", applet_clear, "clear", "Clear console screen", "Terminal"}, + {NULL, NULL, NULL, NULL, NULL} +}; + +static int applet_lazybox(int argc, char** argv) { + if (argc >= 2) { + return lazybox_run_applet(argv[1], argc - 1, &argv[1]); + } + + printk(ANSI_BRIGHT_CYAN "LazyBox v%s (2026-08-16) Linux-Compatible Multi-Call Suite\n" ANSI_RESET, LAZYBOX_VERSION); + printk("Usage: lazybox [function] [arguments]...\n\n"); + printk(ANSI_YELLOW "Defined Applet Categories:\n" ANSI_RESET); + + const char* cats[] = {"Filesystem", "Crypto", "Network", "Storage", "System", "Terminal", "Core", NULL}; + for (int c = 0; cats[c] != NULL; c++) { + printk(ANSI_BRIGHT_GREEN " [%s]\n " ANSI_RESET, cats[c]); + for (int i = 0; applets[i].name != NULL; i++) { + if (strcmp(applets[i].category, cats[c]) == 0) { + printk(" %-12s", applets[i].name); + } + } + printk("\n"); + } + printk("\n"); + return 0; +} + +void lazybox_init(void) { + // Initialized +} + +bool lazybox_has_applet(const char* name) { + if (!name) return false; + for (int i = 0; applets[i].name != NULL; i++) { + if (strcmp(applets[i].name, name) == 0) { + return true; + } + } + return false; +} + +int lazybox_run_applet(const char* name, int argc, char** argv) { + if (!name) return -1; + for (int i = 0; applets[i].name != NULL; i++) { + if (strcmp(applets[i].name, name) == 0) { + return applets[i].func(argc, argv); + } + } + printk(KERN_ERR "lazybox: %s: applet not found\n", name); + return 127; +} + +const lazybox_applet_t* lazybox_get_applets(int* count_out) { + int count = 0; + while (applets[count].name != NULL) count++; + if (count_out) *count_out = count; + return applets; +} diff --git a/userland/lazybox/shell.c b/userland/lazybox/shell.c new file mode 100644 index 0000000..830d9d9 --- /dev/null +++ b/userland/lazybox/shell.c @@ -0,0 +1,429 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HISTORY_SIZE 16 + +static char history[HISTORY_SIZE][MAX_COMMAND_LENGTH]; +static int history_count = 0; +static int history_head = 0; + +static const char* shell_builtins[] = { + "lazybox", "ls", "cat", "touch", "mkdir", "wc", "echo", + "md5sum", "sha256sum", "crc32", "ifconfig", "ping", "arp", + "hdparm", "uname", "free", "uptime", "dmesg", "sleep", "clear", + "help", "neofetch", "calc", "hexdump", "matrix", "history", "tty", + "beep", "reboot", "shutdown", NULL +}; + +static void history_add(const char* cmd) { + if (!cmd || cmd[0] == '\0') return; + if (history_count > 0) { + int last_idx = (history_head - 1 + HISTORY_SIZE) % HISTORY_SIZE; + if (strcmp(history[last_idx], cmd) == 0) return; + } + strncpy(history[history_head], cmd, MAX_COMMAND_LENGTH - 1); + history[history_head][MAX_COMMAND_LENGTH - 1] = '\0'; + history_head = (history_head + 1) % HISTORY_SIZE; + if (history_count < HISTORY_SIZE) history_count++; +} + +static void redraw_line(const char* prompt, const char* buffer, size_t len, size_t cursor_pos) { + printk("\r\033[K%s%s", prompt, buffer); + if (cursor_pos < len) { + size_t diff = len - cursor_pos; + for (size_t i = 0; i < diff; i++) { + printk("\033[D"); + } + } +} + +static void cmd_help(void) { + printk(ANSI_BRIGHT_CYAN "================== SUB-OS Production Command Reference ==================\n" ANSI_RESET); + printk(ANSI_YELLOW " [File & Storage Tools]\n" ANSI_RESET); + printk(" " ANSI_GREEN "ls [path]" ANSI_RESET " - Directory list with type & inode numbers\n"); + printk(" " ANSI_GREEN "cat " ANSI_RESET " - Display file contents (supports /proc & /dev)\n"); + printk(" " ANSI_GREEN "touch / mkdir" ANSI_RESET " - Create files and directories in VFS\n"); + printk(" " ANSI_GREEN "hdparm" ANSI_RESET " - Inspect ATA hard disk parameters & sectors\n\n"); + printk(ANSI_YELLOW " [Cryptography & Security]\n" ANSI_RESET); + printk(" " ANSI_GREEN "sha256sum " ANSI_RESET " - FIPS 180-4 SHA-256 secure hash\n"); + printk(" " ANSI_GREEN "md5sum " ANSI_RESET " - RFC 1321 MD5 message digest\n"); + printk(" " ANSI_GREEN "crc32 " ANSI_RESET " - IEEE 802.3 32-bit CRC calculation\n\n"); + printk(ANSI_YELLOW " [Networking Suite]\n" ANSI_RESET); + printk(" " ANSI_GREEN "ifconfig" ANSI_RESET " - Ethernet eth0 configuration & RX/TX metrics\n"); + printk(" " ANSI_GREEN "ping " ANSI_RESET " - Send ICMP echo requests with latency ms\n"); + printk(" " ANSI_GREEN "arp" ANSI_RESET " - Display kernel ARP resolution cache table\n\n"); + printk(ANSI_YELLOW " [System & Diagnostics]\n" ANSI_RESET); + printk(" " ANSI_GREEN "neofetch" ANSI_RESET " - System information & OS ASCII badge\n"); + printk(" " ANSI_GREEN "free / uptime" ANSI_RESET " - Memory metrics and CPU uptime\n"); + printk(" " ANSI_GREEN "dmesg" ANSI_RESET " - Kernel boot ring buffer log\n"); + printk(" " ANSI_GREEN "hexdump [addr] [n]" ANSI_RESET " - Memory hex inspector\n"); + printk(" " ANSI_GREEN "calc [A] [op] [B]" ANSI_RESET " - Arithmetic calculator (+, -, *, /, %%)\n"); + printk(" " ANSI_GREEN "matrix" ANSI_RESET " - Digital rain screen animation\n"); + printk(" " ANSI_GREEN "tty [1-4]" ANSI_RESET " - Switch virtual console (or Alt+F1-F4)\n"); + printk(" " ANSI_GREEN "reboot / shutdown" ANSI_RESET " - Power management\n"); + printk(ANSI_BRIGHT_BLACK "Tip: Use TAB for autocomplete, Up/Down for command history.\n" ANSI_RESET); +} + +static void cmd_neofetch(void) { + uint64_t total = pmm_get_total_pages(); + uint64_t used = pmm_get_used_pages(); + uint64_t free = pmm_get_free_pages(); + uint64_t total_mb = (total * 4096) / (1024 * 1024); + uint64_t used_mb = (used * 4096) / (1024 * 1024); + + uint64_t ticks = pit_get_ticks(); + uint64_t secs = ticks / 100; + uint64_t mins = secs / 60; + uint64_t hours = mins / 60; + secs %= 60; + mins %= 60; + + const cpu_info_t* cpu = cpuid_get_info(); + + printk("\n"); + printk(ANSI_BRIGHT_CYAN " _____ _ _ ____ " ANSI_BRIGHT_GREEN "OS: " ANSI_RESET "SUB-OS v0.2.0-lts (Modular Monolithic)\n"); + printk(ANSI_BRIGHT_CYAN " / ____| | | | _ \\ " ANSI_BRIGHT_GREEN "Arch: " ANSI_RESET "x86_64 (64-Bit Long Mode)\n"); + printk(ANSI_BRIGHT_CYAN " | (___ | | | | |_) | " ANSI_BRIGHT_GREEN "Kernel: " ANSI_RESET "Modular Drivers + VFS + Crypto + Net\n"); + printk(ANSI_BRIGHT_CYAN " \\___ \\| | | | _ < " ANSI_BRIGHT_GREEN "CPU: " ANSI_RESET "%s (%s)\n", cpu->model, cpu->vendor); + printk(ANSI_BRIGHT_CYAN " ____) | |__| | |_) | " ANSI_BRIGHT_GREEN "Memory: " ANSI_RESET "%llu MB / %llu MB (Free: %llu MB)\n", used_mb, total_mb, (free * 4096) / (1024 * 1024)); + printk(ANSI_BRIGHT_CYAN " |_____/ \\____/|____/ " ANSI_BRIGHT_GREEN "Heap: " ANSI_RESET "%llu KB used / %llu KB total\n", heap_get_used_bytes() / 1024, (heap_get_used_bytes() + heap_get_free_bytes()) / 1024); + printk(ANSI_BRIGHT_CYAN " " ANSI_BRIGHT_GREEN "Uptime: " ANSI_RESET "%02llu:%02llu:%02llu\n", hours, mins, secs); + printk(ANSI_BRIGHT_CYAN " " ANSI_BRIGHT_GREEN "TTY: " ANSI_RESET "tty%d (Alt+F1-F4 to switch)\n", tty_get_current() + 1); + printk(" \n"); + printk(ANSI_BLACK "\xdb\xdb" ANSI_RED "\xdb\xdb" ANSI_GREEN "\xdb\xdb" ANSI_YELLOW "\xdb\xdb" ANSI_BLUE "\xdb\xdb" ANSI_MAGENTA "\xdb\xdb" ANSI_CYAN "\xdb\xdb" ANSI_WHITE "\xdb\xdb" ANSI_RESET "\n\n"); +} + +static void cmd_calc(const char* args) { + while (*args == ' ') args++; + int64_t a = 0, b = 0; + char op = 0; + + bool neg_a = false; + if (*args == '-') { neg_a = true; args++; } + while (*args >= '0' && *args <= '9') { a = a * 10 + (*args - '0'); args++; } + if (neg_a) a = -a; + + while (*args == ' ') args++; + if (*args) op = *args++; + + while (*args == ' ') args++; + bool neg_b = false; + if (*args == '-') { neg_b = true; args++; } + while (*args >= '0' && *args <= '9') { b = b * 10 + (*args - '0'); args++; } + if (neg_b) b = -b; + + int64_t res = 0; + if (op == '+') res = a + b; + else if (op == '-') res = a - b; + else if (op == '*') res = a * b; + else if (op == '/') { + if (b == 0) { printk(ANSI_RED "Division by zero\n" ANSI_RESET); return; } + res = a / b; + } else if (op == '%') { + if (b == 0) { printk(ANSI_RED "Modulo by zero\n" ANSI_RESET); return; } + res = a % b; + } else { + printk("Usage: calc 15 + 27\n"); + return; + } + + printk("%lld %c %lld = " ANSI_BRIGHT_GREEN "%lld\n" ANSI_RESET, a, op, b, res); +} + +static void cmd_hexdump(const char* args) { + uint64_t addr = 0x100000; + uint64_t len = 64; + + while (*args == ' ') args++; + if (*args) { + if (args[0] == '0' && (args[1] == 'x' || args[1] == 'X')) args += 2; + addr = 0; + while ((*args >= '0' && *args <= '9') || (*args >= 'a' && *args <= 'f') || (*args >= 'A' && *args <= 'F')) { + addr <<= 4; + if (*args >= '0' && *args <= '9') addr |= (*args - '0'); + else if (*args >= 'a' && *args <= 'f') addr |= (*args - 'a' + 10); + else if (*args >= 'A' && *args <= 'F') addr |= (*args - 'A' + 10); + args++; + } + while (*args == ' ') args++; + if (*args >= '0' && *args <= '9') { + len = 0; + while (*args >= '0' && *args <= '9') { + len = len * 10 + (*args - '0'); + args++; + } + } + } + if (len > 512) len = 512; + + uint8_t* ptr = (uint8_t*)addr; + printk("Hexdump of 0x%lx (%llu bytes):\n", addr, len); + for (uint64_t i = 0; i < len; i += 16) { + printk(ANSI_BRIGHT_BLACK "%016lx: " ANSI_RESET, addr + i); + for (uint64_t j = 0; j < 16; j++) { + if (i + j < len) printk("%02x ", ptr[i + j]); + else printk(" "); + if (j == 7) printk(" "); + } + printk(" |"); + for (uint64_t j = 0; j < 16; j++) { + if (i + j < len) { + uint8_t b = ptr[i + j]; + printk("%c", (b >= 32 && b <= 126) ? (char)b : '.'); + } + } + printk("|\n"); + } +} + +static void cmd_matrix(void) { + tty_clear(); + printk(ANSI_BRIGHT_GREEN "Press any key to exit Matrix mode...\n" ANSI_RESET); + pit_sleep(600); + tty_clear(); + + int drops[TTY_WIDTH]; + for (int i = 0; i < TTY_WIDTH; i++) drops[i] = -(i % 25); + const char charset[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$%^&*"; + int charset_len = sizeof(charset) - 1; + uint32_t rand_seed = (uint32_t)pit_get_ticks(); + + while (!keyboard_has_key()) { + for (int x = 0; x < TTY_WIDTH; x += 2) { + rand_seed = rand_seed * 1103515245 + 12345; + int y = drops[x]; + if (y >= 0 && y < TTY_HEIGHT) { + tty_set_cursor(y, x); + printk("\033[92m%c\033[0m", charset[rand_seed % charset_len]); + } + if (y - 8 >= 0 && y - 8 < TTY_HEIGHT) { + tty_set_cursor(y - 8, x); + printk(" "); + } + drops[x]++; + if (drops[x] - 8 >= TTY_HEIGHT) drops[x] = 0; + } + pit_sleep(40); + } + keyboard_get_key(); + tty_clear(); +} + +static void shell_process(const char* cmd) { + while (*cmd == ' ') cmd++; + if (*cmd == '\0') return; + + char cmd_copy[MAX_COMMAND_LENGTH]; + strncpy(cmd_copy, cmd, sizeof(cmd_copy) - 1); + cmd_copy[sizeof(cmd_copy) - 1] = '\0'; + + char* argv[16]; + int argc = 0; + char* token = cmd_copy; + + while (*token) { + while (*token == ' ') *token++ = '\0'; + if (*token == '\0') break; + if (argc < 16) argv[argc++] = token; + while (*token && *token != ' ') token++; + } + if (argc == 0) return; + + // 1. LazyBox Applets + if (lazybox_has_applet(argv[0])) { + lazybox_run_applet(argv[0], argc, argv); + return; + } + + // 2. Builtins + if (strcmp(argv[0], "help") == 0) { + cmd_help(); + } else if (strcmp(argv[0], "neofetch") == 0 || strcmp(argv[0], "version") == 0) { + cmd_neofetch(); + } else if (strcmp(argv[0], "calc") == 0) { + cmd_calc(cmd + 4); + } else if (strcmp(argv[0], "hexdump") == 0) { + cmd_hexdump(cmd + 7); + } else if (strcmp(argv[0], "matrix") == 0) { + cmd_matrix(); + } else if (strcmp(argv[0], "beep") == 0) { + speaker_beep(587, 100); + } else if (strcmp(argv[0], "tty") == 0) { + if (argc >= 2 && argv[1][0] >= '1' && argv[1][0] <= '4') { + tty_switch(argv[1][0] - '1'); + } else { + printk("Active TTY: tty%d (Switch with 'tty 1-4' or Alt+F1-F4)\n", tty_get_current() + 1); + } + } else if (strcmp(argv[0], "history") == 0) { + for (int i = 0; i < history_count; i++) { + int idx = (history_head - history_count + i + HISTORY_SIZE) % HISTORY_SIZE; + printk(" %2d: %s\n", i + 1, history[idx]); + } + } else if (strcmp(argv[0], "reboot") == 0) { + printk(ANSI_YELLOW "Rebooting...\n" ANSI_RESET); + pit_sleep(200); + outb(0x64, 0xFE); + } else if (strcmp(argv[0], "shutdown") == 0) { + printk(ANSI_YELLOW "System halted.\n" ANSI_RESET); + while (1) hlt(); + } else { + printk(ANSI_RED "Unknown command: %s. Type 'help' or 'lazybox'.\n" ANSI_RESET, argv[0]); + } +} + +void shell_run(void) { + char cmd_buffer[MAX_COMMAND_LENGTH]; + size_t cmd_len = 0; + size_t cursor_pos = 0; + int history_idx = -1; + + const char* prompt = ANSI_BRIGHT_GREEN "sub-os> " ANSI_RESET; + + printk(ANSI_BRIGHT_CYAN "\nWelcome to SUB-OS Modular Monolithic Linux Core!\n" ANSI_RESET); + printk("Type '" ANSI_YELLOW "help" ANSI_RESET "', '" ANSI_YELLOW "lazybox" ANSI_RESET "', or '" ANSI_YELLOW "neofetch" ANSI_RESET "' to begin.\n\n"); + + while (true) { + printk("%s", prompt); + cmd_len = 0; + cursor_pos = 0; + cmd_buffer[0] = '\0'; + history_idx = -1; + + while (true) { + uint16_t key = keyboard_get_key(); + + if (key & KEY_SPECIAL_FLAG) { + switch (key) { + case KEY_LEFT: + if (cursor_pos > 0) { cursor_pos--; printk("\033[D"); } + break; + case KEY_RIGHT: + if (cursor_pos < cmd_len) { cursor_pos++; printk("\033[C"); } + break; + case KEY_HOME: + while (cursor_pos > 0) { cursor_pos--; printk("\033[D"); } + break; + case KEY_END: + while (cursor_pos < cmd_len) { cursor_pos++; printk("\033[C"); } + break; + case KEY_DELETE: + if (cursor_pos < cmd_len) { + for (size_t i = cursor_pos; i < cmd_len - 1; i++) cmd_buffer[i] = cmd_buffer[i + 1]; + cmd_len--; + cmd_buffer[cmd_len] = '\0'; + redraw_line(prompt, cmd_buffer, cmd_len, cursor_pos); + } + break; + case KEY_UP: + if (history_count > 0) { + if (history_idx == -1) history_idx = history_count - 1; + else if (history_idx > 0) history_idx--; + int actual = (history_head - history_count + history_idx + HISTORY_SIZE) % HISTORY_SIZE; + strncpy(cmd_buffer, history[actual], MAX_COMMAND_LENGTH - 1); + cmd_buffer[MAX_COMMAND_LENGTH - 1] = '\0'; + cmd_len = strlen(cmd_buffer); + cursor_pos = cmd_len; + redraw_line(prompt, cmd_buffer, cmd_len, cursor_pos); + } + break; + case KEY_DOWN: + if (history_idx != -1) { + if (history_idx < history_count - 1) { + history_idx++; + int actual = (history_head - history_count + history_idx + HISTORY_SIZE) % HISTORY_SIZE; + strncpy(cmd_buffer, history[actual], MAX_COMMAND_LENGTH - 1); + cmd_buffer[MAX_COMMAND_LENGTH - 1] = '\0'; + cmd_len = strlen(cmd_buffer); + cursor_pos = cmd_len; + } else { + history_idx = -1; + cmd_buffer[0] = '\0'; + cmd_len = 0; + cursor_pos = 0; + } + redraw_line(prompt, cmd_buffer, cmd_len, cursor_pos); + } + break; + } + continue; + } + + char c = (char)(key & 0xFF); + + if (c == 0x03) { + printk("^C\n%s", prompt); + cmd_len = 0; cursor_pos = 0; cmd_buffer[0] = '\0'; + continue; + } + if (c == 0x0C) { + tty_clear(); + printk("%s%s", prompt, cmd_buffer); + continue; + } + if (c == '\b') { + if (cursor_pos > 0) { + for (size_t i = cursor_pos - 1; i < cmd_len - 1; i++) cmd_buffer[i] = cmd_buffer[i + 1]; + cmd_len--; + cursor_pos--; + cmd_buffer[cmd_len] = '\0'; + redraw_line(prompt, cmd_buffer, cmd_len, cursor_pos); + } + } else if (c == '\t') { + if (cmd_len > 0) { + int matches = 0; + const char* last = NULL; + for (int i = 0; shell_builtins[i] != NULL; i++) { + if (strncmp(shell_builtins[i], cmd_buffer, cmd_len) == 0) { + matches++; + last = shell_builtins[i]; + } + } + if (matches == 1 && last) { + strncpy(cmd_buffer, last, MAX_COMMAND_LENGTH - 1); + cmd_len = strlen(cmd_buffer); + cursor_pos = cmd_len; + redraw_line(prompt, cmd_buffer, cmd_len, cursor_pos); + } else if (matches > 1) { + printk("\n"); + for (int i = 0; shell_builtins[i] != NULL; i++) { + if (strncmp(shell_builtins[i], cmd_buffer, cmd_len) == 0) { + printk(" %s", shell_builtins[i]); + } + } + printk("\n"); + redraw_line(prompt, cmd_buffer, cmd_len, cursor_pos); + } + } + } else if (c == '\n' || c == '\r') { + printk("\n"); + cmd_buffer[cmd_len] = '\0'; + break; + } else if ((unsigned char)c >= 32 && (unsigned char)c <= 126) { + if (cmd_len < MAX_COMMAND_LENGTH - 1) { + for (size_t i = cmd_len; i > cursor_pos; i--) cmd_buffer[i] = cmd_buffer[i - 1]; + cmd_buffer[cursor_pos] = c; + cmd_len++; + cursor_pos++; + cmd_buffer[cmd_len] = '\0'; + redraw_line(prompt, cmd_buffer, cmd_len, cursor_pos); + } + } + } + + if (cmd_len > 0) { + history_add(cmd_buffer); + shell_process(cmd_buffer); + } + } +}