54 Commits
26.4 ... 26.4.2

Author SHA1 Message Date
boreddevnl
7e123b6429 VER: 4.0.1-stable --> v1.0-stable 2026-04-16 23:54:29 +02:00
boreddevnl
4177484366 VER: 26.4 --> 26.4.2 2026-04-16 23:53:50 +02:00
boreddevnl
8dc5ee5867 CREDIT: re-added original credit in 2048. Fixes #2
Co-authored-by: Lluciocc <Lluciocc@users.noreply.github.com>
2026-04-16 22:41:00 +02:00
boreddevnl
884c2f8980 FIX: update explorer spawns for new process_create_elf signature 2026-04-16 22:34:36 +02:00
boreddevnl
36d61e3b7b FEAT: Seperate run parameters for windows, mac and linux 2026-04-16 22:28:31 +02:00
boreddevnl
013f0b513f Merge branch 'main' of https://github.com/boreddevnl/BoredOS 2026-04-16 22:14:22 +02:00
boreddevnl
28108adde3 FIX: Retry to stop false application launch failures 2026-04-16 22:14:18 +02:00
boreddevnl
62ac2ab849 FIX: Redraw menubar upon application start 2026-04-16 22:13:21 +02:00
boreddevnl
7f510c6aa5 FIX: Race condition causing applications to print to serial out instead of the CLI 2026-04-16 22:12:20 +02:00
boreddevnl
7116de4152 TWEAK: rename TOPBAR --> MENUBAR 2026-04-16 22:11:44 +02:00
boreddevnl
049d67e821 FIX: Redraw menubar upon application launch 2026-04-16 22:10:58 +02:00
Chris
0f3971bb1c MERGE: Add 2048 game - LLuciocc
Add 2048 game
2026-04-16 22:03:06 +02:00
Lluciocc
66f55242a7 Update man_entries.h 2026-04-16 18:39:21 +02:00
Lluciocc
8a8fb7de27 Remove credit 2026-04-16 18:06:47 +02:00
Lluciocc
914c60e1f1 Adding 2048.c 2026-04-16 17:58:08 +02:00
boreddevnl
5141eaea60 FEAT: uname 2026-04-15 23:36:42 +02:00
boreddevnl
6e90c3e197 TWEAK: sysfetch added in startup.bsh 2026-04-15 23:36:11 +02:00
boreddevnl
bdd43f43cd FEATURE: add Bsh + userspace terminal, remove legacy cmd/cli utils 2026-04-15 22:47:24 +02:00
boreddevnl
a8866da3cb FEAT: mute terminal output from applications not launched via cli 2026-04-15 20:10:53 +02:00
boreddevnl
14decdd705 DOC: Update README.md 2026-04-15 11:00:06 +02:00
boreddevnl
ed73b88ec1 enable verbose by default 2026-04-15 10:59:44 +02:00
boreddevnl
f9bc6c7c38 FIX: FAT32 cluster management, allocation performance, and AHCI safety 2026-04-14 14:29:19 +02:00
boreddevnl
bb187faf79 DOC: small user manual 2026-04-14 10:59:52 +02:00
boreddevnl
fd7fa4f16e FIX: man entries 2026-04-14 10:59:28 +02:00
boreddevnl
5bd9e537c5 FEAT: bootfs 2026-04-13 16:04:47 +02:00
boreddevnl
e4603792b6 FEAT: Verbose boot 2026-04-13 12:17:39 +02:00
boreddevnl
a27b2c6423 RN: Renamed spotlight to lumos for legal reasons :kek: 2026-04-12 21:51:24 +02:00
boreddevnl
bb176f2193 FEAT: Lumos file searcher 2026-04-12 21:46:28 +02:00
boreddevnl
8dd756f25b FIX: cpuinfo stack overflow and add GUI tab character support 2026-04-12 19:07:08 +02:00
boreddevnl
d13fca2d4a CHECKP: vfs 2026-04-12 18:23:38 +02:00
boreddevnl
a1b6d58b77 Tweak: os_codename = Voyager 2026-04-12 17:59:10 +02:00
boreddevnl
cbc196a4b1 tweak: add -stable after kernel_version 2026-04-12 17:57:54 +02:00
boreddevnl
b4c14af48d TWEAK: kernel_version = 3.2.3 --> 4.0.0 2026-04-12 17:56:35 +02:00
boreddevnl
700839e6be FEAT: VFS overhaul 2026-04-12 17:53:31 +02:00
boreddevnl
921e8a5658 RM: Legacy drive selector in explorer 2026-04-12 00:34:22 +02:00
boreddevnl
437d57312f FIX: remove accidentally copied code 2026-04-12 00:28:03 +02:00
boreddevnl
afc4e16fcf STABILITY: SMP improvements 2026-04-12 00:26:04 +02:00
boreddevnl
38ed0b5ffa CHECKP: semi-stable vfs 2026-04-11 23:08:33 +02:00
boreddevnl
5933483009 CHECKP: shitty VFS 2026-04-11 21:41:11 +02:00
boreddevnl
6b6a22d518 OPT: use ui_draw_image in paint.c 2026-04-11 16:14:39 +02:00
boreddevnl
85427041de FEAT: Caps lock support in ps2 driver 2026-04-11 16:12:37 +02:00
boreddevnl
8b77e8c48e doc: update grapher with tri-axis marching 2026-04-04 19:39:49 +02:00
boreddevnl
1ce08c70b0 FEAT: add tri-axis marching pipeline and atomic depth-color updates to Grapher 2026-04-04 18:05:04 +02:00
boreddevnl
fca67f68a9 NEW: math.h/libmath.c 2026-04-03 23:28:29 +02:00
boreddevnl
c330382436 DOCS: math.h 2026-04-03 23:27:45 +02:00
boreddevnl
f0c2963793 CHECKPOINT: polygon rendering 2026-04-03 23:16:03 +02:00
boreddevnl
3b24bc882c FEAT: Add grapher to dock 2026-04-03 13:32:38 +02:00
boreddevnl
2b44e59e9f TWEAK: Adjust window size 2026-04-03 13:32:23 +02:00
boreddevnl
7a2769e8e3 UI: New main wallpaper (bored.jpg) 2026-04-03 13:23:52 +02:00
boreddevnl
1a6e30b52e Docs: Clean up README.md 2026-04-03 11:53:57 +02:00
boreddevnl
69847adee6 DOCS: Refine and add documentation for libwidget.c 2026-04-03 11:52:06 +02:00
boreddevnl
f402e5e4f0 Merge branch 'main' of https://github.com/boreddevnl/BoredOS 2026-04-03 11:47:05 +02:00
boreddevnl
684ed774ee TWEAK: Balanced resolution and cpu usage with GRID_3D resolution 2026-04-03 11:46:33 +02:00
Chris
9ed8eac3e5 Update Buy Me A Coffee link in README 2026-04-03 00:08:05 +02:00
118 changed files with 12639 additions and 4749 deletions

1
.gitignore vendored
View File

@@ -30,3 +30,4 @@ limine
.DS_Store .DS_Store
/build/ /build/
*.o *.o
disk.img

View File

@@ -139,6 +139,7 @@ $(BUILD_DIR)/initrd.tar: $(KERNEL_ELF)
mkdir -p $(BUILD_DIR)/initrd/Library/images/gif mkdir -p $(BUILD_DIR)/initrd/Library/images/gif
mkdir -p $(BUILD_DIR)/initrd/Library/Fonts/Emoji mkdir -p $(BUILD_DIR)/initrd/Library/Fonts/Emoji
mkdir -p $(BUILD_DIR)/initrd/Library/DOOM mkdir -p $(BUILD_DIR)/initrd/Library/DOOM
mkdir -p $(BUILD_DIR)/initrd/Library/bsh
mkdir -p $(BUILD_DIR)/initrd/docs mkdir -p $(BUILD_DIR)/initrd/docs
@for f in $(SRC_DIR)/userland/bin/*.elf; do \ @for f in $(SRC_DIR)/userland/bin/*.elf; do \
@@ -156,6 +157,9 @@ $(BUILD_DIR)/initrd.tar: $(KERNEL_ELF)
@for f in $(SRC_DIR)/fonts/Emoji/*.ttf; do \ @for f in $(SRC_DIR)/fonts/Emoji/*.ttf; do \
if [ -f "$$f" ]; then cp "$$f" $(BUILD_DIR)/initrd/Library/Fonts/Emoji/; fi \ if [ -f "$$f" ]; then cp "$$f" $(BUILD_DIR)/initrd/Library/Fonts/Emoji/; fi \
done done
@if [ -f $(SRC_DIR)/library/bsh/bshrc ]; then cp $(SRC_DIR)/library/bsh/bshrc $(BUILD_DIR)/initrd/Library/bsh/; fi
@if [ -f $(SRC_DIR)/library/bsh/startup.bsh ]; then cp $(SRC_DIR)/library/bsh/startup.bsh $(BUILD_DIR)/initrd/Library/bsh/; fi
@if [ -f $(SRC_DIR)/library/bsh/boot.bsh ]; then cp $(SRC_DIR)/library/bsh/boot.bsh $(BUILD_DIR)/initrd/Library/bsh/; fi
@if [ -f $(SRC_DIR)/userland/games/doom/doom1.wad ]; then cp $(SRC_DIR)/userland/games/doom/doom1.wad $(BUILD_DIR)/initrd/Library/DOOM/; fi @if [ -f $(SRC_DIR)/userland/games/doom/doom1.wad ]; then cp $(SRC_DIR)/userland/games/doom/doom1.wad $(BUILD_DIR)/initrd/Library/DOOM/; fi
@for f in $$(find docs -name '*.md' 2>/dev/null); do \ @for f in $$(find docs -name '*.md' 2>/dev/null); do \
if [ -f "$$f" ]; then \ if [ -f "$$f" ]; then \
@@ -166,6 +170,7 @@ $(BUILD_DIR)/initrd.tar: $(KERNEL_ELF)
done done
@if [ -f README.md ]; then cp README.md $(BUILD_DIR)/initrd/; fi @if [ -f README.md ]; then cp README.md $(BUILD_DIR)/initrd/; fi
@if [ -f LICENSE ]; then cp LICENSE $(BUILD_DIR)/initrd/; fi @if [ -f LICENSE ]; then cp LICENSE $(BUILD_DIR)/initrd/; fi
@if [ -f limine.conf ]; then cp limine.conf $(BUILD_DIR)/initrd/; fi
cd $(BUILD_DIR)/initrd && COPYFILE_DISABLE=1 tar --exclude="._*" -cf ../initrd.tar * cd $(BUILD_DIR)/initrd && COPYFILE_DISABLE=1 tar --exclude="._*" -cf ../initrd.tar *
@@ -201,12 +206,26 @@ clean:
rm -rf $(BUILD_DIR) $(ISO_DIR) $(ISO_IMAGE) rm -rf $(BUILD_DIR) $(ISO_DIR) $(ISO_IMAGE)
$(MAKE) -C $(SRC_DIR)/userland clean $(MAKE) -C $(SRC_DIR)/userland clean
run: $(ISO_IMAGE) run-windows: $(ISO_IMAGE)
qemu-system-x86_64 -m 4G -serial stdio -cdrom $< -boot d \
-smp 4 \
-audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \
-drive file=disk.img,format=raw,file.locking=off
run-mac: $(ISO_IMAGE)
qemu-system-x86_64 -m 4G -serial stdio -cdrom $< -boot d \ qemu-system-x86_64 -m 4G -serial stdio -cdrom $< -boot d \
-smp 4 \ -smp 4 \
-audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \ -audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \
-netdev user,id=net0,hostfwd=udp::12346-:12345 -device virtio-net-pci,netdev=net0 \
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \ -vga std -global VGA.xres=1920 -global VGA.yres=1080 \
-display cocoa,show-cursor=off \ -display cocoa,show-cursor=off \
-drive file=disk.img,format=raw,file.locking=off \ -drive file=disk.img,format=raw,file.locking=off \
-cpu max -cpu max
run-linux: $(ISO_IMAGE)
qemu-system-x86_64 -m 4G -serial stdio -cdrom $< -boot d \
-smp 4 \
-audiodev pa,id=audio0 -machine pcspk-audiodev=audio0 \
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \
-display gtk,show-cursor=off \
-drive file=disk.img,format=raw,file.locking=off \
-cpu max

View File

@@ -19,9 +19,9 @@ BoredOS is a x86_64 operating system featuring a custom Desktop Environment (DE)
--- ---
## 🚀 Features ## Features
### ⚙️ System Architecture ### System Architecture
* **64-bit Long Mode:** Fully utilizing the x86_64 architecture. * **64-bit Long Mode:** Fully utilizing the x86_64 architecture.
* **Symmetric Multi-Processing (SMP):** Full support for multi-core CPUs via Limine SMP. * **Symmetric Multi-Processing (SMP):** Full support for multi-core CPUs via Limine SMP.
* **LAPIC & IPI Scheduling:** Advanced interrupt handling and inter-processor communication for task distribution. * **LAPIC & IPI Scheduling:** Advanced interrupt handling and inter-processor communication for task distribution.
@@ -29,14 +29,14 @@ BoredOS is a x86_64 operating system featuring a custom Desktop Environment (DE)
* **Multiboot2 Compliant:** Bootable on real hardware and modern emulators. * **Multiboot2 Compliant:** Bootable on real hardware and modern emulators.
* **Kernel Core:** Interrupt Descriptor Table (IDT) management and a robust syscall interface. * **Kernel Core:** Interrupt Descriptor Table (IDT) management and a robust syscall interface.
* **Filesystem:** Full **FAT32** support for persistent and in-memory storage. * **Filesystem:** Full **FAT32** support for persistent and in-memory storage.
* **Networking:** Includes the lwIP networking stack. * **Networking:** Includes the lwIP networking stack and a basic web browser.
### 📺 Graphical User Interface ### Graphical User Interface
* **BoredWM:** A custom Window Manager with drag-and-drop, mouse-centered interaction. * **BoredWM:** A custom Window Manager with drag-and-drop, mouse-centered interaction.
* **Customization:** Adjustable UI to suit your aesthetic. * **Customization:** Adjustable UI to suit your aesthetic.
* **Media Support:** Built-in image decoding. * **Media Support:** Built-in image decoding.
### 🛠️ Included Applications ### Included Applications
* **Productivity:** GUI Text Editor calculator, Markdown Viewer, a simple browser and BoredWord. * **Productivity:** GUI Text Editor calculator, Markdown Viewer, a simple browser and BoredWord.
* **Creativity:** A Paint application. * **Creativity:** A Paint application.
* **Utilities:** Terminal, Task Manager, File Explorer, Clock and a (limited) C Compiler. * **Utilities:** Terminal, Task Manager, File Explorer, Clock and a (limited) C Compiler.
@@ -55,11 +55,11 @@ Explore the internal workings of BoredOS via our comprehensive guides in the [`d
--- ---
## Support the Journey ## Support the Journey
If you find this project interesting or helpful, consider fueling the development with a coffee! If you find this project interesting or helpful, consider fueling the development with a coffee!
<a href="https://buymeacoffee.com/boreddevnl" target="_blank"> <a href="https://buymeacoffee.com/boreddevhq" target="_blank">
<img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="50" style="border-radius: 8px;" /> <img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="50" style="border-radius: 8px;" />
</a> </a>
@@ -68,16 +68,16 @@ If you find this project interesting or helpful, consider fueling the developmen
## ⚠️ Project Disclaimer & Heritage ## ⚠️ Project Disclaimer & Heritage
**BoredOS** is the successor to **BrewKernel**, a text-only project initiated in 2023. **BoredOS** is the successor to **BrewKernel**, a project initiated in 2023.
While BrewKernel served as the foundational learning ground for this OS, it has been officially **deprecated and archived**. It no longer receives updates, bug fixes, or pull request reviews. BoredOS represents a complete architectural reboot, applying years of lessons learned to create a cleaner, more modular, and more capable 64-bit system. While BrewKernel served as the foundational learning ground for this OS, it has been officially **deprecated and archived**. It no longer receives updates, bug fixes, or pull request reviews. BoredOS represents a complete architectural reboot, applying years of lessons learned to create a cleaner, more modular, and more capable system.
> [!IMPORTANT] > [!IMPORTANT]
> Please ensure all issues, discussions, and contributions are directed to this repository. Legacy BrewKernel code is preserved for historical purposes only and is not compatible with BoredOS. > Please ensure all issues, discussions, and contributions are directed to this repository. Legacy BrewKernel code is preserved for historical purposes only and is not compatible with BoredOS.
--- ---
## ⚖️ License ## License
**Copyright (C) 2024-2026 boreddevnl** **Copyright (C) 2024-2026 boreddevnl**

BIN
disk.img

Binary file not shown.

View File

@@ -5,7 +5,7 @@
--- ---
Welcome to the internal documentation for BoredOS! This directory contains detailed guides on how the OS functions, how to build it, and how to develop applications for it. Welcome to the documentation for BoredOS! This directory contains detailed guides on how the OS functions, how to build it, and how to develop applications for it.
## 📚 Table of Contents ## 📚 Table of Contents
@@ -27,7 +27,17 @@ Instructions for compiling the OS from source.
The SDK and toolchain guides for creating your own `.elf` userland binaries. The SDK and toolchain guides for creating your own `.elf` userland binaries.
- [`SDK Reference`](appdev/sdk_reference.md): Explanation of the custom `libc` wrappers (`stdlib.h`, `string.h`) and system calls. - [`SDK Reference`](appdev/sdk_reference.md): Explanation of the custom `libc` wrappers (`stdlib.h`, `string.h`) and system calls.
- [`UI API`](appdev/ui_api.md): Drawing on the screen, creating windows, and polling the event loop using `libui.h`. - [`UI API`](appdev/ui_api.md): Drawing on the screen, creating windows, and polling the event loop using `libui.h`.
- [`Widget API`](appdev/widget_api.md): High-level UI components like buttons, textboxes, and scrollbars using `libwidget.h`.
- [`Custom Apps`](appdev/custom_apps.md): A step-by-step tutorial on writing a new graphical C application, editing the Makefile, and bundling it into the ISO. - [`Custom Apps`](appdev/custom_apps.md): A step-by-step tutorial on writing a new graphical C application, editing the Makefile, and bundling it into the ISO.
- [`Example Apps`](appdev/examples/README.md): A collection of sample C applications ranging from basic terminal output to advanced TCP networking. - [`Example Apps`](appdev/examples/README.md): A collection of sample C applications ranging from basic terminal output to advanced TCP networking.
- [`Grapher`](appdev/grapher.md): Full reference for the built-in mathematical graphing application — equation syntax, keyboard controls, architecture, and configuration.
### 4. [Usage](usage/)
General guides on how to interact with the OS.
- [`Booting`](usage/booting.md): How to use the Limine bootloader and toggle kernel boot flags like `-v`.
- [`Desktop`](usage/desktop.md): Window management, shortcuts, and desktop interaction.
- [`Lumos`](usage/lumos.md): Using the system-wide search (`Shift + Ctrl + Space`).
- [`Terminal`](usage/terminal.md): Command line interface, redirection, and common commands.
- [`Launching Apps`](usage/launching_apps.md): Ways to launch files and applications, plus a software overview.
--- ---

View File

@@ -10,7 +10,7 @@ This guide explains how to write a new "Hello World" application locally, compil
> [!TIP] > [!TIP]
> **Looking for working code?** Check out the [Examples Directory](examples/README.md) for full source code demonstrating basic CLI, Windows, Animations, and TCP Networking. > **Looking for working code?** Check out the [Examples Directory](examples/README.md) for full source code demonstrating basic CLI, Windows, Animations, and TCP Networking.
## 📝 Step 1: Write the C Source ## Step 1: Write the C Source
Applications reside entirely in the `src/userland/` directory. Create a new file, for example, `src/userland/gui/hello.c`. Applications reside entirely in the `src/userland/` directory. Create a new file, for example, `src/userland/gui/hello.c`.
@@ -52,7 +52,7 @@ int main(void) {
} }
``` ```
## ⚙️ Step 2: Edit the Makefile ## Step 2: Edit the Makefile
Now you need to tell the build system to compile `hello.c`. Fortunately, the `src/userland/Makefile` is designed to detect new C files largely automatically! Now you need to tell the build system to compile `hello.c`. Fortunately, the `src/userland/Makefile` is designed to detect new C files largely automatically!
@@ -64,7 +64,7 @@ Now you need to tell the build system to compile `hello.c`. Fortunately, the `sr
Since you placed the file in `gui/hello.c`, the wildcard logic will pick it up automatically. Since you placed the file in `gui/hello.c`, the wildcard logic will pick it up automatically.
3. The Makefile will generate `bin/hello.elf` during the build phase. 3. The Makefile will generate `bin/hello.elf` during the build phase.
## 📦 Step 3: Bundle it into the OS ## Step 3: Bundle it into the OS
The main overarching `Makefile` (in the project root) takes binaries from `src/userland/bin/*.elf` and places them into the `iso_root/bin/` directory, while also adding them to `limine.conf` as loadable boot modules. The main overarching `Makefile` (in the project root) takes binaries from `src/userland/bin/*.elf` and places them into the `iso_root/bin/` directory, while also adding them to `limine.conf` as loadable boot modules.
@@ -77,7 +77,7 @@ The main overarching `Makefile` (in the project root) takes binaries from `src/u
make clean && make run make clean && make run
``` ```
## 🚀 Step 4: Run it inside BoredOS ## Step 4: Run it inside BoredOS
1. When BoredOS boots, launch the **Terminal** application. 1. When BoredOS boots, launch the **Terminal** application.
2. The OS automatically maps built applications to standard shell commands. Simply type your application's filename (without the `.elf` extension). 2. The OS automatically maps built applications to standard shell commands. Simply type your application's filename (without the `.elf` extension).

345
docs/appdev/grapher.md Normal file
View File

@@ -0,0 +1,345 @@
<div align="center">
<h1>Grapher</h1>
<p><em>An interactive mathematical expression plotter for BoredOS, supporting both 2D and 3D visualizations.</em></p>
</div>
---
Grapher is a built-in GUI application that lets you type any mathematical equation and see it plotted in real time. It supports 2D explicit and implicit curves as well as full 3D surface visualization — including both explicit surfaces (`z = f(x, y)`) and implicit surfaces (`f(x, y, z) = c`).
> [!NOTE]
> Grapher is located at `src/userland/gui/grapher.c`. It runs as a standard BoredOS GUI process and can be launched from the terminal or from the dock.
---
## Features at a Glance
| Feature | Details |
|---|---|
| **2D Explicit** | Plot `y = f(x)` curves |
| **2D Implicit** | Plot any `f(x, y) = g(x, y)` contour via marching squares |
| **3D Explicit** | Plot `z = f(x, y)` surfaces |
| **3D Implicit** | Plot any `f(x, y, z) = c` surface |
| **Rendering modes** | Wireframe and filled polygon modes |
| **Height coloring** | Surfaces are colored by a blue→green→yellow→red gradient based on Z height |
| **Phong-style shading** | Filled mode computes per-face normals and applies diffuse + ambient lighting |
| **Parallel rendering** | Evaluation and projection are distributed across 4 worker threads via `sys_parallel_run` |
| **Preset equations** | 7 built-in presets accessible from the toolbar |
| **Auto-fit** | 2D view auto-fits the Y axis to the plotted curve on first plot |
| **Atomic Color-Depth Buffer** | All 3D drawing uses a 64-bit atomic buffer to prevent depth/color race conditions |
---
## Launching Grapher
From the BoredOS terminal:
```sh
grapher
```
Or click the **Grapher icon** in the system dock.
---
### Toolbar Controls
| Control | Function |
|---|---|
| **Equation box** | Type your mathematical expression, then press **Enter** or **Plot** |
| **Plot button** | Parse and render the current equation |
| **Wire / Filled button** | Toggle wireframe vs. shaded polygon mode (3D only) |
| **Presets button** | Open a dropdown of example equations |
### Status Bar Controls (3D mode)
| Control | Function |
|---|---|
| **`+` button** | Increase the 3D world range (zoom out in world space) |
| **`-` button** | Decrease the 3D world range (zoom in in world space) |
---
## Keyboard Shortcuts
| Shortcut | Action |
|---|---|
| **Enter** (in equation box) | Plot the equation |
| **Ctrl + R** | Reset the view to defaults |
| **F** | Toggle filled / wireframe rendering (3D mode) |
| **Scroll wheel** | Zoom in/out (2D mode adjusts viewport; 3D mode adjusts camera zoom) |
| **Right-click drag** | Rotate the 3D surface |
---
## Writing Equations
Grapher parses equations entered as plain text. It supports a subset of mathematical notation with automatic implicit multiplication.
### Supported Functions
| Syntax | Meaning |
|---|---|
| `sin(x)` | Sine |
| `cos(x)` | Cosine |
| `tan(x)` | Tangent |
| `sqrt(x)` | Square root |
| `abs(x)` | Absolute value |
| `log(x)` | Natural logarithm (base *e*) |
### Supported Operators
| Operator | Meaning |
|---|---|
| `+` `-` `*` `/` | Arithmetic |
| `^` | Exponentiation (right-associative) |
| `(` `)` | Grouping |
### Special Values
| Token | Value |
|---|---|
| `pi` or `PI` | π ≈ 3.14159… |
### Implicit Multiplication
Adjacent tokens that would normally require a `*` are multiplied automatically:
```
2x → 2 * x
3sin(x) → 3 * sin(x)
(x+1)(x) → (x+1) * x
```
### How Equations Are Classified
Grapher looks at which variables appear in your equation to automatically choose the rendering mode:
| Equation form | Auto-detected as |
|---|---|
| `y = f(x)` or just `f(x)` | 2D explicit |
| `f(x, y) = g(x, y)` | 2D implicit |
| `z = f(x, y)` | 3D explicit |
| `f(x, y, z) = c` | 3D implicit |
If you omit the `=` sign, Grapher treats the input as `y = <expression>` when no `y` or `z` is present, or as `<expression> = 0` otherwise.
---
## Example Equations
### 2D Examples
```
y = sin(x)
y = x^2
y = cos(x)*x
y = abs(x) - 2
x^2 + y^2 = 25 ← circle (implicit)
y = log(x)
```
### 3D Explicit Examples
```
z = sin(x)*cos(y)
z = x^2 - y^2 ← saddle surface
z = sqrt(25 - x^2 - y^2)
```
### 3D Implicit Examples
```
x^2 + y^2 + z^2 = 25 ← sphere
x^2 + y^2 = 16 ← cylinder
x^2 + y^2 - z^2 = 1 ← hyperboloid
```
---
## Navigation Controls
### 2D Mode
| Input | Action |
|---|---|
| **Scroll up** | Zoom in |
| **Scroll down** | Zoom out |
| **Ctrl+R** | Reset to default view (`x: [-10, 10]`) |
### 3D Mode
| Input | Action |
|---|---|
| **Right-click drag** | Rotate the surface (orbit camera) |
| **Scroll up** | Zoom camera in |
| **Scroll down** | Zoom camera out |
| **`+` / `-` buttons** | Increase / decrease world range |
| **Ctrl+R** | Reset rotation and zoom |
> [!TIP]
> In 3D mode, the surface auto-rotates slowly by default. This can be disabled by setting `#define ROTATE 0` in the source file.
---
## Architecture Overview
Grapher is implemented as a single self-contained C file. Below is a high-level breakdown of its major components:
### Math Library
Grapher uses the BoredOS freestanding **`libc/math.h`** library, which provides all the math functions it needs without depending on a host standard library:
| Function | Description |
|---|---|
| `sin`, `cos`, `tan` | Trigonometry via Taylor series (8 terms, range-reduced to `[-π, π]`) |
| `sqrt` | Newton-Raphson iteration (25 steps) |
| `log` | Natural logarithm via Padé-style series |
| `log2`, `log10` | Derived from `log` |
| `exp` | Range-reduced Taylor series for `e^x` |
| `pow` | Integer exponents use fast binary exponentiation; fractional exponents use `exp(e * log(b))` |
| `fabs`, `fmod` | Absolute value and floating-point remainder |
| `floor`, `ceil` | Rounding |
| `sinh`, `cosh`, `tanh` | Hyperbolic functions |
| `hypot`, `fmin`, `fmax`, `fclamp` | Utility helpers |
The constants `M_PI`, `M_E`, `M_LN2`, `M_SQRT2` are also defined in the header.
This library is automatically linked into every userland ELF — any app can `#include "math.h"` to use it.
### Expression Parser
Equations are parsed in three stages:
1. **Tokenizer** (`tokenize`) — converts the input string into a flat token array. Handles implicit multiplication by inserting `*` tokens where needed.
2. **Recursive Descent Parser** (`parse_expr`, `parse_term`, `parse_power`, `parse_unary`, `parse_atom`) — produces an Abstract Syntax Tree (AST) with up to `MAX_NODES = 128` nodes.
3. **Bytecode Compiler** (`compile_ast`) — walks the AST in post-order and emits a flat instruction sequence for a simple stack machine. This avoids recursive evaluation during rendering hot paths.
The resulting bytecode is then executed by `run_bc` for every sample point.
### Rendering Pipeline
#### 2D Rendering
- **Explicit** — evaluates `y = f(x)` at every pixel column and connects adjacent samples with Bresenham lines.
- **Implicit** — applies **marching squares** on a 200×130 grid to find sign changes in `f(x,y) - g(x,y)` and plots intersection pixels.
#### 3D Rendering
The 3D pipeline uses a multi-pass system parallelized across worker threads:
| Pass | Function | Description |
|---|---|---|
| 1 | **Evaluation** | Samples the surface at grid points. For implicit surfaces, this uses **tri-axis marching**. |
| 2 | **Projection** | Projects 3D world coordinates to 2D screen coordinates with perspective. |
| 3 | **Drawing** | Rasterizes wireframe lines or filled triangles with Z-buffering. |
##### Tri-Axis Marching (Implicit Surfaces)
Unlike explicit surfaces that only need one evaluation per grid point, implicit surfaces require finding roots of $f(x, y, z) = 0$. To ensure complete surface connectivity and eliminate "cracks," Grapher marches along all three primary axes:
1. **X-Axis Pass**: For every $(y, z)$ pair, march along $x$.
2. **Y-Axis Pass**: For every $(x, z)$ pair, march along $y$.
3. **Z-Axis Pass**: For every $(x, y)$ pair, march along $z$.
Each pass uses a multi-stage root finder (170 linear steps followed by 15 bisection iterations). By sampling along all three axes, the engine "catches" surfaces that are nearly parallel to any specific marching direction, ensuring that vertical walls and steep gradients are rendered solidly from any viewing angle.
##### Atomic Color-Depth Buffer
To prevent "z-fighting" and race conditions between parallel threads, Grapher uses a 64-bit atomic buffer (`graph_czb`). Each 64-bit word stores:
- **Upper 32 bits**: Z-depth (integer).
- **Lower 32 bits**: Pixel color (0xAARRGGBB).
A single `__atomic_compare_exchange_n` operation ensures that a pixel's color and depth are updated together only if the new depth is closer to the camera than the existing one.
Surface normals are estimated using central finite differences of the implicit function.
#### Filled Mod
When filled mode is active, each quad cell is split into two triangles. The average surface normal across the four corner vertices is computed and fed into `apply_shading`, which calculates:
```
intensity = ambient(0.3) + diffuse(0.7) * dot(normal, light_direction)
```
The light direction is fixed at `(0.577, 0.707, 0.408)` (normalized diagonal).
#### Z-Buffer
The depth buffer (`graph_zb`) stores integer depth values. `gfb_pixel_z` uses a **compare-and-swap (CAS) loop** via `__atomic_compare_exchange_n` so multiple parallel draw threads cannot produce race conditions.
### Coordinate Systems
#### 2D
World coordinates map linearly to screen pixels:
```c
screen_x = (wx - view_x_min) / (view_x_max - view_x_min) * graph_w
screen_y = (view_y_max - wy) / (view_y_max - view_y_min) * graph_h
```
#### 3D
Points are first rotated by two Euler angles (`rot_y`, `rot_x`) then projected with a simple perspective divide:
```
persp = d / (pz + d) // d = range_3d * 5
sx = px * scale * persp + screen_cx
sy = -py * scale * persp + screen_cy
```
---
## Configuration Constants
These can be changed at the top of `grapher.c` to tune behaviour:
| Constant | Default | Effect |
|---|---|---|
| `ROTATE` | `1` | Set to `0` to disable auto-rotation in 3D mode |
| `GRID_3D` | `41` | Grid resolution for 3D sampling. Higher = more detail, much slower |
> [!WARNING]
> Setting `GRID_3D` too high (e.g. 9000) will exhaust available memory. The `surf` grid and `surf_x`/`surf_y_3d` arrays are statically allocated at compile time: memory usage grows as **O(GRID_3D²)**. Values above ~512 are not recommended.
> [!TIP]
> `GRID_3D = 256` gives a good balance of detail and performance on typical BoredOS hardware emulation.
---
## Color Palette
3D surfaces are colored by height using a 4-stop rainbow ramp:
```
Low → Blue → Cyan → Green → Yellow → Red → High
```
---
## Preset Equations
The built-in presets are shown in the dropdown when you click **Presets**:
| Label | Type |
|---|---|
| `y = sin(x)` | 2D explicit |
| `y = x^2` | 2D explicit |
| `y = cos(x)*x` | 2D explicit |
| `z = sin(x)*cos(y)` | 3D explicit |
| `z = x^2 - y^2` | 3D explicit |
| `x^2+y^2+z^2=25` | 3D implicit (sphere) |
| `x^2+y^2=16` | 3D implicit (cylinder) |
---
## Known Limitations
- **No parameter slider** — equations are static; there is no way to animate a parameter.
- **No multiple equations** — only one equation can be graphed at a time.
- **Implicit surface precision** — extremely thin or high-frequency implicit surfaces may still have small artifacts if the grid resolution (`GRID_3D`) is too low.
- **3D implicit performance** — tri-axis marching evaluates the function significantly more times than explicit rendering; high resolutions will impact frame rate.
- **Integer axis labels only for large values** — very large axis values are capped at `>2G` or `<-2G` due to `itoa` limitations.

View File

@@ -7,9 +7,13 @@
BoredOS provides a custom `libc` implementation necessary for writing userland applications (`.elf` binaries). By avoiding a full-blown standard library like `glibc`, the OS ensures a minimal executable footprint tailored strictly to the existing kernel features. BoredOS provides a custom `libc` implementation necessary for writing userland applications (`.elf` binaries). By avoiding a full-blown standard library like `glibc`, the OS ensures a minimal executable footprint tailored strictly to the existing kernel features.
All headers are located in `src/userland/libc/`. All headers are located in `src/userland/libc/` (standard functions) and `src/wm/` (UI and widgets).
- `stdlib.h`: Memory, strings, and basic I/O.
- `math.h`: Freestanding floating-point math library.
- `libui.h`: Core window and drawing API.
- `libwidget.h`: High-level UI components.
## 📚 Standard Library (`stdlib.h` & `string.h`) ## Standard Library (`stdlib.h` & `string.h`)
The standard library wrappers provide memory management, string manipulation, and basic IO formatting without needing direct syscalls. The standard library wrappers provide memory management, string manipulation, and basic IO formatting without needing direct syscalls.
@@ -45,7 +49,60 @@ The standard library wrappers provide memory management, string manipulation, an
--- ---
## ⚙️ System Calls (`syscall.h`) ## Math Library (`math.h`)
BoredOS ships a freestanding floating-point math library in `libc/math.h`. It uses pure arithmetic — Taylor series, Newton-Raphson, and range-reduction — with no dependency on a host `libm` or hardware math intrinsics. It is automatically linked into every userland ELF.
```c
#include "math.h"
```
### Constants
| Constant | Value | Description |
|---|---|---|
| `M_PI` | 3.14159… | π |
| `M_E` | 2.71828… | Euler's number |
| `M_LN2` | 0.69315… | Natural log of 2 |
| `M_SQRT2` | 1.41421… | √2 |
| `HUGE_VAL` | ~+∞ | Overflow sentinel |
### Functions
#### Absolute value & remainder
* `double fabs(double x);` — Absolute value.
* `double fmod(double x, double y);` — Floating-point remainder. Returns `0` when `y == 0`.
#### Rounding
* `double floor(double x);` — Largest integer ≤ x.
* `double ceil(double x);` — Smallest integer ≥ x.
#### Trigonometry *(arguments in radians)*
* `double sin(double x);` — Sine. Range-reduced to `[-π, π]` then computed via 8-term Taylor series.
* `double cos(double x);` — Cosine. Computed via `sin(x + π/2)`.
* `double tan(double x);` — Tangent. Returns sentinel `1e15` near poles.
#### Exponential & logarithm
* `double sqrt(double x);` — Square root via Newton-Raphson (25 iterations). Returns `0` for `x ≤ 0`.
* `double log(double x);` — Natural logarithm (ln). Returns `-1e30` for `x ≤ 0`.
* `double log2(double x);` — Base-2 logarithm.
* `double log10(double x);` — Base-10 logarithm.
* `double exp(double x);` — e^x. Saturates to `1e300` for `x > 700`, `0` for `x < -700`.
* `double pow(double base, double exponent);` — General power. Integer exponents use fast binary exponentiation; fractional exponents use `exp(e * log(b))`.
#### Hyperbolic
* `double sinh(double x);` — Hyperbolic sine.
* `double cosh(double x);` — Hyperbolic cosine.
* `double tanh(double x);` — Hyperbolic tangent.
#### Utility
* `double hypot(double x, double y);``sqrt(x² + y²)` without intermediate overflow.
* `double fmin(double a, double b);` — Minimum of two values.
* `double fmax(double a, double b);` — Maximum of two values.
* `double fclamp(double x, double lo, double hi);` — Clamps `x` into `[lo, hi]`.
> [!NOTE]
> The implementation file is named `libc/libmath.c` (not `libc/math.c`) to avoid a name collision with the `math` CLI calculator app in userland. The public header is still included as `#include "math.h"`.
For advanced operations, `syscall.h` provides direct wrappers into the kernel. For advanced operations, `syscall.h` provides direct wrappers into the kernel.
@@ -108,7 +165,7 @@ BoredOS includes lwIP for hardware TCP/UDP networking.
--- ---
## 📑 Core Data Structures ## Core Data Structures
### `os_info_t` ### `os_info_t`
Contains detailed build and version information about the OS. Contains detailed build and version information about the OS.

View File

@@ -7,7 +7,7 @@
The UI library (`libui.h`) is the sole mechanism for Graphical Userland Applications to draw to the screen and receive input events in BoredOS. It wraps `SYS_GUI` kernel calls. The UI library (`libui.h`) is the sole mechanism for Graphical Userland Applications to draw to the screen and receive input events in BoredOS. It wraps `SYS_GUI` kernel calls.
## 🪟 Window Management ## Window Management
A "Window" is a reserved drawing canvas managed by the compositor. A "Window" is a reserved drawing canvas managed by the compositor.
@@ -21,7 +21,7 @@ A "Window" is a reserved drawing canvas managed by the compositor.
* `void ui_get_screen_size(uint64_t *out_w, uint64_t *out_h);` * `void ui_get_screen_size(uint64_t *out_w, uint64_t *out_h);`
Query the global screen resolution of the display. Query the global screen resolution of the display.
## 🎨 Drawing Primitives ## Drawing Primitives
All drawing functions write to an off-screen buffer associated with the window. **You must call `ui_mark_dirty()` to instruct the compositor to push your changes to the physical screen.** All drawing functions write to an off-screen buffer associated with the window. **You must call `ui_mark_dirty()` to instruct the compositor to push your changes to the physical screen.**
@@ -38,7 +38,7 @@ All drawing functions write to an off-screen buffer associated with the window.
> Colors are defined as 32-bit unsigned integers in **ARGB** format: `0xAARRGGBB`. > Colors are defined as 32-bit unsigned integers in **ARGB** format: `0xAARRGGBB`.
> E.g., `0xFF000000` is opaque black, `0xFFFF0000` is opaque red. > E.g., `0xFF000000` is opaque black, `0xFFFF0000` is opaque red.
## 🔤 Text Rendering ## Text Rendering
BoredOS provides multiple text rendering methodologies, including a default system font and scaled/bitmap alternatives. BoredOS provides multiple text rendering methodologies, including a default system font and scaled/bitmap alternatives.
@@ -60,7 +60,7 @@ Used for calculating layout bounds before drawing:
* `uint32_t ui_get_string_width_scaled(const char *str, float scale);` * `uint32_t ui_get_string_width_scaled(const char *str, float scale);`
* `uint32_t ui_get_font_height_scaled(float scale);` * `uint32_t ui_get_font_height_scaled(float scale);`
## 🔄 Event Handling ## Event Handling
Applications must continuously poll for events inside an infinite `$while(1)` loop. Applications must continuously poll for events inside an infinite `$while(1)` loop.
@@ -103,3 +103,9 @@ typedef struct {
*(Note: Coordinate arguments (`arg1`, `arg2`) for mouse events are typically relative to the top-left corner of the window's client area).* *(Note: Coordinate arguments (`arg1`, `arg2`) for mouse events are typically relative to the top-left corner of the window's client area).*
--- ---
> [!TIP]
> **Looking for Buttons, TextBoxes, or Scrollbars?**
> While `libui.h` provides the foundation for drawing, most applications should use the higher-level [**Widget API**](widget_api.md) (`libwidget.h`) for standard interactive components.
---

108
docs/appdev/widget_api.md Normal file
View File

@@ -0,0 +1,108 @@
<div align="center">
<h1>Widget API (<code>libwidget.h</code>)</h1>
<p><em>High-level UI components for BoredOS applications.</em></p>
</div>
---
The Widget library (`libwidget.h`) provides a set of reusable UI components built on top of `libui.h`. It uses an abstract `widget_context_t` to decouple component logic from specific drawing implementations, making it easier to build complex graphical interfaces.
## Widget Context
To use any widget, you must first define a `widget_context_t`. This structure contains function pointers for basic drawing operations (rects, strings) and theme preferences.
```c
typedef struct {
void *user_data;
void (*draw_rect)(void *user_data, int x, int y, int w, int h, uint32_t color);
void (*draw_rounded_rect_filled)(void *user_data, int x, int y, int w, int h, int r, uint32_t color);
void (*draw_string)(void *user_data, int x, int y, const char *str, uint32_t color);
int (*measure_string_width)(void *user_data, const char *str);
void (*mark_dirty)(void *user_data, int x, int y, int w, int h);
bool use_light_theme;
} widget_context_t;
```
> [!TIP]
> Usually, `user_data` is set to your `ui_window_t` handle, and the functions are simple wrappers around `ui_draw_rect`, `ui_draw_string`, etc.
---
## Button (`widget_button_t`)
Standard interactive button with hover and click states.
* `void widget_button_init(widget_button_t *btn, int x, int y, int w, int h, const char *text);`
* `void widget_button_draw(widget_context_t *ctx, widget_button_t *btn);`
* `bool widget_button_handle_mouse(widget_button_t *btn, int mx, int my, bool mouse_down, bool mouse_clicked, void *user_data);`
### Usage Example:
```c
widget_button_t my_btn;
widget_button_init(&my_btn, 10, 10, 80, 25, "Click Me");
my_btn.on_click = my_callback_func;
// In your event loop:
widget_button_handle_mouse(&my_btn, ev.arg1, ev.arg2, is_down, is_clicked, my_data);
```
---
## Scrollbar (`widget_scrollbar_t`)
Vertical scrollbar supporting dragging and track-paging.
* `void widget_scrollbar_init(widget_scrollbar_t *sb, int x, int y, int w, int h);`
* `void widget_scrollbar_update(widget_scrollbar_t *sb, int content_height, int scroll_y);`
* `void widget_scrollbar_draw(widget_context_t *ctx, widget_scrollbar_t *sb);`
* `bool widget_scrollbar_handle_mouse(widget_scrollbar_t *sb, int mx, int my, bool mouse_down, void *user_data);`
> [!NOTE]
> The scrollbar automatically calculates the "thumb" size based on the ratio of `h` to `content_height`.
---
## TextBox (`widget_textbox_t`)
Editable text field with focus support and keyboard handling.
* `void widget_textbox_init(widget_textbox_t *tb, int x, int y, int w, int h, char *buffer, int max_len);`
* `void widget_textbox_draw(widget_context_t *ctx, widget_textbox_t *tb);`
* `bool widget_textbox_handle_mouse(widget_textbox_t *tb, int mx, int my, bool mouse_clicked, void *user_data);`
* `bool widget_textbox_handle_key(widget_textbox_t *tb, char c, void *user_data);`
---
## Dropdown (`widget_dropdown_t`)
Selection menu for picking one item from a list.
* `void widget_dropdown_init(widget_dropdown_t *dd, int x, int y, int w, int h, const char **items, int count);`
* `void widget_dropdown_draw(widget_context_t *ctx, widget_dropdown_t *dd);`
* `bool widget_dropdown_handle_mouse(widget_dropdown_t *dd, int mx, int my, bool mouse_clicked, void *user_data);`
---
## Checkbox / Radio (`widget_checkbox_t`)
Toggleable options with support for circular "Radio" style or square "Checkbox" style.
* `void widget_checkbox_init(widget_checkbox_t *cb, int x, int y, int w, int h, const char *text, bool is_radio);`
* `void widget_checkbox_draw(widget_context_t *ctx, widget_checkbox_t *cb);`
* `bool widget_checkbox_handle_mouse(widget_checkbox_t *cb, int mx, int my, bool mouse_clicked, void *user_data);`
---
## Event Integration
Widgets are designed to be polled within your `libui` event loop. Most handle-mouse functions return `true` if the event was "consumed" by the widget, allowing you to stop further processing for that event.
```c
if (ui_get_event(win, &ev)) {
bool handled = false;
handled |= widget_button_handle_mouse(&btn, ev.arg1, ev.arg2, is_down, is_clicked, NULL);
if (!handled) {
// Handle global window events...
}
}
```

View File

@@ -9,7 +9,7 @@ BoredOS is a 64-bit hobbyist operating system designed for the x86_64 architectu
This document serves as an overview of the core architecture and the layout of the kernel source code. This document serves as an overview of the core architecture and the layout of the kernel source code.
## 📂 Source Code Layout (`src/`) ## Source Code Layout (`src/`)
The OS heavily relies on module separation. The `src/` directory is logically split into several domains: The OS heavily relies on module separation. The `src/` directory is logically split into several domains:
@@ -23,7 +23,7 @@ The OS heavily relies on module separation. The `src/` directory is logically sp
- **`wm/`**: The graphical subsystem. It handles drawing primitives, window structures, font rendering, and double-buffering. - **`wm/`**: The graphical subsystem. It handles drawing primitives, window structures, font rendering, and double-buffering.
- **`userland/`**: Out-of-kernel components. This includes the custom SDK/compiler environment (`libc/`) and user applications (`cli/`, `gui/`, `games/`). - **`userland/`**: Out-of-kernel components. This includes the custom SDK/compiler environment (`libc/`) and user applications (`cli/`, `gui/`, `games/`).
## 🚀 Boot Process ## Boot Process
BoredOS uses **Limine** as its primary bootloader. BoredOS uses **Limine** as its primary bootloader.
@@ -42,7 +42,7 @@ BoredOS utilizes Symmetric Multi-Processing (SMP) to distribute workloads across
- **Scheduler**: A round-robin scheduler runs on each core. Processes are pinned to specific CPUs (CPU Affinity) to maintain cache locality. The BSP timer interrupt (`60Hz`) broadcasts a scheduling IPI to all core to ensure balanced execution. - **Scheduler**: A round-robin scheduler runs on each core. Processes are pinned to specific CPUs (CPU Affinity) to maintain cache locality. The BSP timer interrupt (`60Hz`) broadcasts a scheduling IPI to all core to ensure balanced execution.
- **Spinlocks**: Since multiple cores can access kernel structures (VFS, Process List) simultaneously, the kernel uses **interrupt-safe spinlocks** to prevent race conditions. - **Spinlocks**: Since multiple cores can access kernel structures (VFS, Process List) simultaneously, the kernel uses **interrupt-safe spinlocks** to prevent race conditions.
## 🛡️ Userland Transition ## Userland Transition
The OS supports privilege separation (Ring 0 vs. Ring 3). When an application is launched, the kernel: The OS supports privilege separation (Ring 0 vs. Ring 3). When an application is launched, the kernel:

View File

@@ -7,7 +7,7 @@
BoredOS implements a rudimentary but functional filesystem layer designed to support reading system assets and user applications during runtime. BoredOS implements a rudimentary but functional filesystem layer designed to support reading system assets and user applications during runtime.
## 🗂️ Virtual File System (VFS) ## Virtual File System (VFS)
The Virtual File System acts as an abstraction layer across different underlying storage mechanisms (even if, currently, only one type is fully utilized). System calls targeting files (`SYS_FS`) route through the VFS rather than interacting with the disk directly. The Virtual File System acts as an abstraction layer across different underlying storage mechanisms (even if, currently, only one type is fully utilized). System calls targeting files (`SYS_FS`) route through the VFS rather than interacting with the disk directly.
@@ -17,11 +17,11 @@ Key VFS functionalities include:
- **Path Parsing**: Resolving absolute and relative paths. - **Path Parsing**: Resolving absolute and relative paths.
- **SMP Safety**: All VFS and underlying FAT32 operations are protected by a global **Spinlock**. This ensures that multiple cores can safely read from the filesystem simultaneously without corrupting internal file seek pointers or directory cache states. - **SMP Safety**: All VFS and underlying FAT32 operations are protected by a global **Spinlock**. This ensures that multiple cores can safely read from the filesystem simultaneously without corrupting internal file seek pointers or directory cache states.
## 💾 FAT32 Implementation ## FAT32 Implementation
The primary filesystem logic in `fat32.c` handles both in-memory RAM-based filesystem simulation and physical ATA block devices. The primary filesystem logic in `fat32.c` handles both in-memory RAM-based filesystem simulation and physical ATA block devices.
### 💿 Storage Support ### Storage Support
BoredOS supports two main types of storage for its FAT32 implementation: BoredOS supports two main types of storage for its FAT32 implementation:
@@ -30,7 +30,7 @@ BoredOS supports two main types of storage for its FAT32 implementation:
- **GPT is NOT supported**: Currently, only **MBR (Master Boot Record)** partition tables or **raw (partitionless)** disks are supported. - **GPT is NOT supported**: Currently, only **MBR (Master Boot Record)** partition tables or **raw (partitionless)** disks are supported.
- **Filesystem**: The partition must be formatted as **FAT32**. - **Filesystem**: The partition must be formatted as **FAT32**.
### 🔍 Auto-detection ### Auto-detection
The `Disk Manager` automatically probes primary and secondary IDE channels during initialization. If a valid FAT32 partition is found (either directly at sector 0 or via an MBR partition table), the disk is assigned a drive letter (starting from `B:`) and becomes accessible to the VFS. The `Disk Manager` automatically probes primary and secondary IDE channels during initialization. If a valid FAT32 partition is found (either directly at sector 0 or via an MBR partition table), the disk is assigned a drive letter (starting from `B:`) and becomes accessible to the VFS.

View File

@@ -7,7 +7,7 @@
Memory management in BoredOS is split into physical and virtual layers, designed to support both kernel operations and userland isolation on the x86_64 architecture. Memory management in BoredOS is split into physical and virtual layers, designed to support both kernel operations and userland isolation on the x86_64 architecture.
## 🧠 Physical Memory Management (PMM) ## Physical Memory Management (PMM)
The PMM is responsible for tracking which physical RAM frames (usually 4KB each) are free and which are in use. The PMM is responsible for tracking which physical RAM frames (usually 4KB each) are free and which are in use.
@@ -19,7 +19,7 @@ The PMM is responsible for tracking which physical RAM frames (usually 4KB each)
> [!NOTE] > [!NOTE]
> 4KB frame sizes strike a balance between allocation speed and minimal memory fragmentation, fitting directly with the page tables. > 4KB frame sizes strike a balance between allocation speed and minimal memory fragmentation, fitting directly with the page tables.
## 🗺️ Virtual Memory Management (VMM) and Paging ## Virtual Memory Management (VMM) and Paging
BoredOS uses 4-level paging (PML4), a requirement for x86_64 long mode, dividing the virtual address space between the kernel and userland. BoredOS uses 4-level paging (PML4), a requirement for x86_64 long mode, dividing the virtual address space between the kernel and userland.
@@ -30,7 +30,7 @@ BoredOS uses 4-level paging (PML4), a requirement for x86_64 long mode, dividing
- **User Space**: Userland applications are loaded into lower virtual addresses. - **User Space**: Userland applications are loaded into lower virtual addresses.
- **Page Faults**: The `mem/` subsystem registers an Interrupt Service Routine (ISR) for page faults (Interrupt 14). If a process accesses unmapped memory, the handler determines whether to allocate a new frame or terminate the process. - **Page Faults**: The `mem/` subsystem registers an Interrupt Service Routine (ISR) for page faults (Interrupt 14). If a process accesses unmapped memory, the handler determines whether to allocate a new frame or terminate the process.
## 🏗️ Kernel Heap ## Kernel Heap
Dynamic allocation within the kernel (`kmalloc` and `kfree`) is layered on top of the physical allocator. The kernel maintains its own heap area in virtual memory. When the heap requires more space, it requests physical frames from the PMM and maps them into the kernel's virtual address space. Dynamic allocation within the kernel (`kmalloc` and `kfree`) is layered on top of the physical allocator. The kernel maintains its own heap area in virtual memory. When the heap requires more space, it requests physical frames from the PMM and maps them into the kernel's virtual address space.

View File

@@ -7,7 +7,7 @@
BoredOS features a fully custom, graphical Window Manager built directly into the kernel, residing in the `src/wm/` directory. It is responsible for compositing the screen, handling window logic, rendering text, and dispatching UI events. BoredOS features a fully custom, graphical Window Manager built directly into the kernel, residing in the `src/wm/` directory. It is responsible for compositing the screen, handling window logic, rendering text, and dispatching UI events.
## 🖼️ Framebuffer and Rendering ## Framebuffer and Rendering
1. **Limine Framebuffer**: During boot, the Limine bootloader requests a graphical framebuffer from the hardware (e.g., GOP in UEFI environments) and passes a pointer to this linear memory buffer to the kernel. 1. **Limine Framebuffer**: During boot, the Limine bootloader requests a graphical framebuffer from the hardware (e.g., GOP in UEFI environments) and passes a pointer to this linear memory buffer to the kernel.
2. **Double Buffering**: To prevent screen tearing, the WM does not draw directly to the screen. It allocates a "back buffer" in kernel memory equal to the size of the screen. All drawing operations (lines, rectangles, windows) happen on this back buffer. 2. **Double Buffering**: To prevent screen tearing, the WM does not draw directly to the screen. It allocates a "back buffer" in kernel memory equal to the size of the screen. All drawing operations (lines, rectangles, windows) happen on this back buffer.
@@ -24,7 +24,7 @@ The windowing system is built around a linked list of `Window` structures.
- **Window Structures**: Each window object tracks its dimensions (`x`, `y`, `width`, `height`), title, background color, and an internal buffer if it's acting as a canvas for userland apps. - **Window Structures**: Each window object tracks its dimensions (`x`, `y`, `width`, `height`), title, background color, and an internal buffer if it's acting as a canvas for userland apps.
- **Decorations**: The kernel handles drawing window borders, title bars, and close buttons automatically unless a borderless style is specified. - **Decorations**: The kernel handles drawing window borders, title bars, and close buttons automatically unless a borderless style is specified.
## 🖱️ Input Handling and Events ## Input Handling and Events
The WM acts as the central hub for input routing. The WM acts as the central hub for input routing.

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

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

35
docs/usage/desktop.md Normal file
View File

@@ -0,0 +1,35 @@
# Using the Desktop
The BoredOS desktop environment is designed to be intuitive while providing powerful window management and icons for quick access to your files and applications.
## Window Management
BoredOS uses a stacking window manager (BoredWM) that allows you to overlap and organize multiple windows.
### Basic Actions
- **Focus**: Click anywhere on a window to bring it to the front and make it the active window.
- **Move**: Click and drag the **title bar** (the top bar of the window) to reposition it on the screen.
- **Close**: Click the red traffic light close button in the top-left corner of the window.
### System-wide Shortcuts
BoredOS includes several global shortcuts to help you manage your workflow:
- **`Ctrl + P`**: Take a screenshot. The image will be saved to `/root/Desktop` as `screenshot.jpg`.
- **`Shift + Ctrl + Space`**: Toggle **Lumos** search (see the [Lumos guide](lumos.md)).
## Desktop Icons
Your desktop represents the contents of the `/root/Desktop` directory.
- **Launching**: Double-click an icon to open the file or launch the application.
- **Snapping**: Icons automatically snap to a grid for a clean look. You can toggle "Snap to Grid" and "Auto Align" in the [Settings app](../launching_apps.md).
- **Context Menu**: Right-click on the desktop background to create new files, folders, or refresh the layout.
## The Bottom Dock
The dock at the bottom of the screen provides quick shortcuts to your most-used applications, with for example:
- **Files**: Browse the entire filesystem.
- **Terminal**: Access the command-line interface.
- **Calculator / Notepad / Grapher**: Essential productivity tools.
---
[Return to Documentation Index](../README.md)

View File

@@ -0,0 +1,31 @@
# Launching Applications
BoredOS provides several ways to launch applications and files, depending on your preferred workflow.
## 1. Using the File Explorer
The File Explorer is the primary way to navigate the filesystem and launch any `.elf` binary or associated document.
1. Open the **Explorer** from the dock or desktop.
2. Navigate to `/bin` for system applications or your own user folders.
3. **Double-click** any executable (`.elf`) to run it.
4. Standard files (like `.jpg` or `.txt`) will automatically open in their default viewer.
## 2. Desktop Shortcuts and Icons
Commonly used applications are placed directly on the desktop.
- Simply **Double-click** any icon on the desktop to launch it.
- You can also create desktop shortcuts by right-clicking on a file and selecting **"Create Shortcut"**.
## 3. Using Lumos (Global Search)
For the fastest access, use **Lumos** to search and launch by name:
1. Press **`Shift + Ctrl + Space`**.
2. Type the name of the app (e.g., "DOOM.elf").
3. Press **Enter** to launch.
---
[Return to Documentation Index](../README.md)

29
docs/usage/lumos.md Normal file
View File

@@ -0,0 +1,29 @@
# Lumos: System Search
**Lumos** is the powerful, system-wide search and launch assistant for BoredOS. It allows you to find applications, documents, and system files instantly without navigating through folders.
## Opening Lumos
To activate Lumos at any time, use the global keyboard shortcut:
**`Shift + Ctrl + Space`**
The Lumos search modal will appear in the center of your screen, ready for input.
## Features
- **Fuzzy Searching**: You don't need to type the exact name. Lumos uses fuzzy matching to find the most relevant results as you type.
- **Deep Indexing**: Lumos indexes files across the entire system.
- **Quick Launch**: Once you find what you're looking for, launching it is as simple as pressing `Enter`.
## Navigation
When the Lumos window is open:
- **Type**: Just start typing to filter results.
- **Arrow Keys (Up/Down)**: Move the selection highlight through the list of results.
- **Enter**: Launch the selected file or application.
- **Backspace**: Delete characters in your search query.
- **Escape**: Close Lumos and return to the desktop.
---
[Return to Documentation Index](../README.md)

60
docs/usage/terminal.md Normal file
View File

@@ -0,0 +1,60 @@
# Terminal & Command Line
The BoredOS Terminal provides a powerful command-line interface (CLI) for advanced users and developers. It supports standard Unix-like features and provides direct access to the kernel's system calls.
## The Shell
The default shell in BoredOS is **BoredShell (Bsh)**, a userspace shell with a dedicated terminal app. It features:
- **ANSI Color Support**: Rich text output with colors and styles.
- **Command History**: Use the **Up** and **Down** arrow keys to navigate through your previous commands (up to 64 history entries).
- **Output Redirection**:
- `command > file`: Write output to a new file (or overwrite existing).
- `command >> file`: Append output to an existing file.
- **Piping**:
- `command1 | command2`: Pass the output of the first command as input to the second.
### Bsh Configuration
Bsh loads its configuration from:
`/Library/bsh/bshrc`
This file is similar to `.zshrc` and can define:
- `PATH` for command lookup
- `STARTUP` for interactive shell startup scripts
- `BOOT_SCRIPT` for a once-per-boot script
- prompt templates (`PROMPT_LEFT`, `PROMPT_RIGHT`)
Prompt tokens:
- `%n` username
- `%h` hostname
- `%~` cwd ("~" for `/root`)
- `%T` time (HH:MM)
Example:
```
PATH=/bin:/root/Apps
PROMPT_LEFT=%n@%h:%~$
STARTUP=/Library/bsh/startup.bsh
BOOT_SCRIPT=/Library/bsh/boot.bsh
```
## Common Commands
Below are some of the most used commands available in `/bin`:
| Command | Description |
| :--- | :--- |
| `ls` | List files and directories in the current path. |
| `cd` | Change the current working directory. |
| `cat` | Display the contents of a file. |
| `ls` | List directory contents. |
| `rm` | Remove a file. |
| `mkdir` | Create a new directory. |
| `man` | View the manual for a specific command (e.g., `man ls`). |
| `sysfetch` | Display system and hardware information. |
---
[Return to Documentation Index](../README.md)

View File

@@ -13,3 +13,4 @@ backdrop: 000000
/BoredOS /BoredOS
protocol: limine protocol: limine
path: boot():/boredos.elf path: boot():/boredos.elf
cmdline: -v

View File

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

View File

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

95
src/core/kconsole.c Normal file
View File

@@ -0,0 +1,95 @@
#include "kconsole.h"
#include "graphics.h"
#include "sys/spinlock.h"
#include <stddef.h>
static spinlock_t console_lock = SPINLOCK_INIT;
static int cursor_x = 0;
static int cursor_y = 0;
static bool kconsole_active = false;
static uint32_t text_color = 0xFFFFFFFF; // White
#define CHAR_WIDTH 8
#define CHAR_HEIGHT 10
void kconsole_init(void) {
cursor_x = 10;
cursor_y = 10;
kconsole_active = false;
// Initial clear screen during boot
graphics_clear_back_buffer(0x00000000);
graphics_mark_screen_dirty();
graphics_flip_buffer();
}
void kconsole_set_active(bool active) {
kconsole_active = active;
}
void kconsole_set_color(uint32_t color) {
uint64_t flags = spinlock_acquire_irqsave(&console_lock);
text_color = color;
spinlock_release_irqrestore(&console_lock, flags);
}
static void kconsole_scroll(void) {
if (cursor_y + CHAR_HEIGHT >= get_screen_height() - 10) {
graphics_scroll_back_buffer(CHAR_HEIGHT);
cursor_y -= CHAR_HEIGHT;
graphics_mark_screen_dirty();
graphics_flip_buffer();
}
}
static void kconsole_putc_nolock(char c) {
if (!kconsole_active) return;
if (c == '\n') {
cursor_x = 10;
cursor_y += CHAR_HEIGHT;
kconsole_scroll();
graphics_flip_buffer();
return;
}
if (c == '\r') {
cursor_x = 10;
return;
}
if (c == '\t') {
cursor_x += CHAR_WIDTH * 4;
return;
}
// Draw character
draw_char_bitmap(cursor_x, cursor_y, c, text_color);
graphics_mark_screen_dirty();
cursor_x += CHAR_WIDTH;
if (cursor_x + CHAR_WIDTH >= get_screen_width() - 10) {
cursor_x = 10;
cursor_y += CHAR_HEIGHT;
kconsole_scroll();
}
}
void kconsole_putc(char c) {
uint64_t flags = spinlock_acquire_irqsave(&console_lock);
kconsole_putc_nolock(c);
spinlock_release_irqrestore(&console_lock, flags);
}
void kconsole_write(const char *s) {
if (!s) return;
uint64_t flags = spinlock_acquire_irqsave(&console_lock);
while (*s) {
kconsole_putc_nolock(*s++);
}
// Flip buffer after a write batch during boot
graphics_flip_buffer();
spinlock_release_irqrestore(&console_lock, flags);
}

13
src/core/kconsole.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef KCONSOLE_H
#define KCONSOLE_H
#include <stdint.h>
#include <stdbool.h>
void kconsole_init(void);
void kconsole_set_color(uint32_t color);
void kconsole_putc(char c);
void kconsole_write(const char *s);
void kconsole_set_active(bool active);
#endif // KCONSOLE_H

View File

@@ -30,6 +30,16 @@ int k_strcmp(const char *s1, const char *s2) {
return *(const unsigned char*)s1 - *(const unsigned char*)s2; return *(const unsigned char*)s1 - *(const unsigned char*)s2;
} }
int k_strncmp(const char *s1, const char *s2, size_t n) {
while (n && *s1 && (*s1 == *s2)) {
s1++;
s2++;
n--;
}
if (n == 0) return 0;
return *(const unsigned char*)s1 - *(const unsigned char*)s2;
}
void k_strcpy(char *dest, const char *src) { void k_strcpy(char *dest, const char *src) {
while (*src) *dest++ = *src++; while (*src) *dest++ = *src++;
*dest = 0; *dest = 0;
@@ -145,3 +155,17 @@ void k_beep_process(void) {
} }
} }
char *k_strstr(const char *haystack, const char *needle) {
if (!*needle) return (char *)haystack;
for (; *haystack; haystack++) {
const char *h = haystack;
const char *n = needle;
while (*h && *n && *h == *n) {
h++;
n++;
}
if (!*n) return (char *)haystack;
}
return NULL;
}

View File

@@ -13,6 +13,7 @@ void k_memset(void *dest, int val, size_t len);
void k_memcpy(void *dest, const void *src, size_t len); void k_memcpy(void *dest, const void *src, size_t len);
size_t k_strlen(const char *str); size_t k_strlen(const char *str);
int k_strcmp(const char *s1, const char *s2); int k_strcmp(const char *s1, const char *s2);
int k_strncmp(const char *s1, const char *s2, size_t n);
void k_strcpy(char *dest, const char *src); void k_strcpy(char *dest, const char *src);
int k_atoi(const char *str); int k_atoi(const char *str);
void k_itoa(int n, char *buf); void k_itoa(int n, char *buf);
@@ -25,5 +26,6 @@ void k_reboot(void);
void k_shutdown(void); void k_shutdown(void);
void k_beep(int freq, int ms); void k_beep(int freq, int ms);
void k_beep_process(void); void k_beep_process(void);
char *k_strstr(const char *haystack, const char *needle);
#endif #endif

View File

@@ -17,12 +17,23 @@
#include "io.h" #include "io.h"
#include "fat32.h" #include "fat32.h"
#include "tar.h" #include "tar.h"
#include "vfs.h"
#include "core/kconsole.h"
#include "core/kutils.h"
#include "memory_manager.h" #include "memory_manager.h"
#include "platform.h" #include "platform.h"
#include "wallpaper.h" #include "wallpaper.h"
#include "smp.h" #include "smp.h"
#include "work_queue.h" #include "work_queue.h"
#include "lapic.h" #include "lapic.h"
#include "fs/sysfs.h"
#include "fs/procfs.h"
#include "fs/bootfs.h"
#include "sys/kernel_subsystem.h"
#include "sys/module_manager.h"
#include "sys/bootfs_state.h"
extern void sysfs_init_subsystems(void);
// --- Limine Requests --- // --- Limine Requests ---
__attribute__((used, section(".requests"))) __attribute__((used, section(".requests")))
@@ -53,12 +64,26 @@ static volatile struct limine_smp_request smp_request = {
.flags = 0 .flags = 0
}; };
__attribute__((used, section(".requests")))
static volatile struct limine_bootloader_info_request bootloader_info_request = {
.id = LIMINE_BOOTLOADER_INFO_REQUEST,
.revision = 0
};
__attribute__((used, section(".requests")))
static volatile struct limine_kernel_file_request kernel_file_request = {
.id = LIMINE_KERNEL_FILE_REQUEST,
.revision = 0
};
__attribute__((used, section(".requests_start"))) __attribute__((used, section(".requests_start")))
static volatile struct limine_request *const requests_start_marker[] = { static volatile struct limine_request *const requests_start_marker[] = {
(struct limine_request *)&framebuffer_request, (struct limine_request *)&framebuffer_request,
(struct limine_request *)&memmap_request, (struct limine_request *)&memmap_request,
(struct limine_request *)&module_request, (struct limine_request *)&module_request,
(struct limine_request *)&smp_request, (struct limine_request *)&smp_request,
(struct limine_request *)&bootloader_info_request,
(struct limine_request *)&kernel_file_request,
NULL NULL
}; };
@@ -84,26 +109,70 @@ static void init_serial() {
outb(0x3F8 + 4, 0x0B); outb(0x3F8 + 4, 0x0B);
} }
static spinlock_t serial_lock = SPINLOCK_INIT;
void serial_write(const char *str) { void serial_write(const char *str) {
while (*str) { uint64_t flags = spinlock_acquire_irqsave(&serial_lock);
const char *p = str;
while (*p) {
char c = *p++;
while ((inb(0x3F8 + 5) & 0x20) == 0); while ((inb(0x3F8 + 5) & 0x20) == 0);
outb(0x3F8, *str++); outb(0x3F8, c);
} }
kconsole_write(str);
spinlock_release_irqrestore(&serial_lock, flags);
}
static void serial_write_num_locked(uint32_t n) {
if (n >= 10) serial_write_num_locked(n / 10);
char c = '0' + (n % 10);
while ((inb(0x3F8 + 5) & 0x20) == 0);
outb(0x3F8, c);
kconsole_putc(c);
} }
void serial_write_num(uint32_t n) { void serial_write_num(uint32_t n) {
if (n >= 10) serial_write_num(n / 10); uint64_t flags = spinlock_acquire_irqsave(&serial_lock);
serial_write_num_locked(n);
spinlock_release_irqrestore(&serial_lock, flags);
}
static void serial_write_hex_locked(uint64_t n) {
char *hex = "0123456789ABCDEF";
if (n >= 16) serial_write_hex_locked(n / 16);
char c = hex[n % 16];
while ((inb(0x3F8 + 5) & 0x20) == 0); while ((inb(0x3F8 + 5) & 0x20) == 0);
outb(0x3F8, '0' + (n % 10)); outb(0x3F8, c);
kconsole_putc(c);
} }
void serial_write_hex(uint64_t n) { void serial_write_hex(uint64_t n) {
char *hex = "0123456789ABCDEF"; uint64_t flags = spinlock_acquire_irqsave(&serial_lock);
if (n >= 16) serial_write_hex(n / 16); serial_write_hex_locked(n);
while ((inb(0x3F8 + 5) & 0x20) == 0); spinlock_release_irqrestore(&serial_lock, flags);
outb(0x3F8, hex[n % 16]);
} }
void log_ok(const char *msg) {
serial_write("[ ");
kconsole_set_color(0xFF00FF00);
serial_write("OK");
kconsole_set_color(0xFFFFFFFF);
serial_write(" ] ");
serial_write(msg);
serial_write("\n");
}
void log_fail(const char *msg) {
serial_write("[ ");
kconsole_set_color(0xFFFF0000);
serial_write("FAIL");
kconsole_set_color(0xFFFFFFFF);
serial_write(" ] ");
serial_write(msg);
serial_write("\n");
}
// Kernel Entry Point // Kernel Entry Point
static void fat32_mkdir_recursive(const char *path) { static void fat32_mkdir_recursive(const char *path) {
@@ -133,62 +202,74 @@ static void fat32_mkdir_recursive(const char *path) {
void kmain(void) { void kmain(void) {
init_serial(); init_serial();
serial_write("\n[DEBUG] Entering kmain...\n"); vfs_init();
serial_write("\n");
platform_init(); platform_init();
serial_write("[DEBUG] platform_init OK\n"); log_ok("Platform initialized");
extern uint64_t hhdm_offset; extern uint64_t hhdm_offset;
extern uint64_t kernel_phys_base; extern uint64_t kernel_phys_base;
extern uint64_t kernel_virt_base; extern uint64_t kernel_virt_base;
serial_write("[DEBUG] HHDM Offset: 0x"); serial_write("[INIT] HHDM Offset: 0x");
serial_write_hex(hhdm_offset); serial_write_hex(hhdm_offset);
serial_write("\n"); serial_write("\n");
serial_write("[DEBUG] Kernel Phys: 0x"); serial_write("[INIT] Kernel Phys: 0x");
serial_write_hex(kernel_phys_base); serial_write_hex(kernel_phys_base);
serial_write("\n"); serial_write("\n");
serial_write("[DEBUG] Kernel Virt: 0x"); serial_write("[INIT] Kernel Virt: 0x");
serial_write_hex(kernel_virt_base); serial_write_hex(kernel_virt_base);
serial_write("\n"); serial_write("\n");
if (memmap_request.response != NULL) {
// The memory manager will now scan the memory map and manage all usable regions.
memory_manager_init_from_memmap(memmap_request.response);
serial_write("[DEBUG] memory_manager_init OK\n");
} else {
serial_write("[DEBUG] ERROR: No usable memory for heap! Check Limine memmap.\n");
hcf();
}
if (framebuffer_request.response == NULL || framebuffer_request.response->framebuffer_count < 1) { if (framebuffer_request.response == NULL || framebuffer_request.response->framebuffer_count < 1) {
serial_write("[DEBUG] No framebuffer! Halting.\n"); serial_write("[INIT] No framebuffer! Halting.\n");
hcf(); hcf();
} }
struct limine_framebuffer *fb = framebuffer_request.response->framebuffers[0]; struct limine_framebuffer *fb = framebuffer_request.response->framebuffers[0];
graphics_init(fb); graphics_init(fb);
serial_write("[DEBUG] graphics_init OK\n"); kconsole_init();
// Check for verbose boot flag
if (kernel_file_request.response != NULL && kernel_file_request.response->kernel_file != NULL) {
const char *cmdline = kernel_file_request.response->kernel_file->cmdline;
if (cmdline != NULL && k_strstr(cmdline, "-v") != NULL) {
kconsole_set_active(true);
}
}
log_ok("Graphics and Console ready");
if (memmap_request.response != NULL) {
memory_manager_init_from_memmap(memmap_request.response);
log_ok("Memory manager ready");
smp_init_bsp();
log_ok("SMP BSP initialized");
} else {
log_fail("No usable memory for heap! Check Limine memmap.");
hcf();
}
gdt_init(); gdt_init();
serial_write("[DEBUG] gdt_init OK\n"); log_ok("GDT initialized");
paging_init(); paging_init();
serial_write("[DEBUG] paging_init OK\n"); log_ok("Paging ready");
syscall_init(); syscall_init();
serial_write("[DEBUG] syscall_init OK\n"); log_ok("Syscalls ready");
idt_init(); idt_init();
idt_register_interrupts(); idt_register_interrupts();
idt_load(); idt_load();
serial_write("[DEBUG] idt_init OK\n"); log_ok("IDT ready");
process_init(); process_init();
fat32_init(); fat32_init();
serial_write("[DEBUG] fat32_init OK\n"); log_ok("FAT32 ready");
fat32_mkdir("/bin"); fat32_mkdir("/bin");
fat32_mkdir("/Library"); fat32_mkdir("/Library");
fat32_mkdir("/Library/images"); fat32_mkdir("/Library/images");
@@ -196,14 +277,68 @@ void kmain(void) {
fat32_mkdir("/Library/images/gif"); fat32_mkdir("/Library/images/gif");
fat32_mkdir("/Library/Fonts"); fat32_mkdir("/Library/Fonts");
fat32_mkdir("/Library/DOOM"); fat32_mkdir("/Library/DOOM");
fat32_mkdir("/Library/bsh");
fat32_mkdir("/docs"); fat32_mkdir("/docs");
sysfs_init_subsystems();
vfs_mount("/sys", "sysfs", "sysfs", sysfs_get_ops(), NULL);
vfs_mount("/proc", "procfs", "procfs", procfs_get_ops(), NULL);
bootfs_init();
if (bootloader_info_request.response != NULL) {
if (bootloader_info_request.response->name) {
k_strcpy(g_bootfs_state.bootloader_name, bootloader_info_request.response->name);
}
if (bootloader_info_request.response->version) {
k_strcpy(g_bootfs_state.bootloader_version, bootloader_info_request.response->version);
}
}
if (kernel_file_request.response != NULL && kernel_file_request.response->kernel_file != NULL) {
g_bootfs_state.kernel_size = kernel_file_request.response->kernel_file->size;
serial_write("[INIT] Kernel size from bootloader: ");
serial_write_hex(g_bootfs_state.kernel_size);
serial_write(" bytes\n");
}
extern uint32_t wm_get_ticks(void);
g_bootfs_state.boot_time_ms = wm_get_ticks();
if (module_request.response != NULL) {
g_bootfs_state.num_modules = module_request.response->module_count;
serial_write("[INIT] Scanning modules for bootfs state...\n");
for (uint64_t i = 0; i < module_request.response->module_count; i++) {
struct limine_file *mod = module_request.response->modules[i];
const char *path = mod->path;
if (fs_starts_with(path, "boot():")) path += 7;
else if (fs_starts_with(path, "boot:///")) path += 8;
int path_len = 0;
while (path[path_len]) path_len++;
serial_write("[INIT] Module: ");
serial_write(path);
serial_write(" (");
serial_write_hex(mod->size);
serial_write(" bytes)\n");
if (path_len >= 5 && path[path_len-4] == '.' && path[path_len-3] == 't' &&
path[path_len-2] == 'a' && path[path_len-1] == 'r') {
g_bootfs_state.initrd_size = mod->size;
serial_write("[INIT] -> Initrd detected\n");
}
}
}
vfs_mount("/boot", "bootfs", "bootfs", bootfs_get_ops(), NULL);
if (module_request.response == NULL) { if (module_request.response == NULL) {
serial_write("[DEBUG] ERROR: Limine Module Response is NULL!\n"); log_fail("Limine module response NULL");
} else { } else {
serial_write("[DEBUG] Limine Module Response found. Count: "); log_ok("Limine modules loaded");
serial_write_num(module_request.response->module_count);
serial_write("\n");
for (uint64_t i = 0; i < module_request.response->module_count; i++) { for (uint64_t i = 0; i < module_request.response->module_count; i++) {
struct limine_file *mod = module_request.response->modules[i]; struct limine_file *mod = module_request.response->modules[i];
@@ -215,7 +350,7 @@ void kmain(void) {
while(clean_path[len]) len++; while(clean_path[len]) len++;
if (len >= 4 && clean_path[len-4] == '.' && clean_path[len-3] == 't' && clean_path[len-2] == 'a' && clean_path[len-1] == 'r') { if (len >= 4 && clean_path[len-4] == '.' && clean_path[len-3] == 't' && clean_path[len-2] == 'a' && clean_path[len-1] == 'r') {
serial_write("[DEBUG] Parsing TAR initrd: "); serial_write("[INIT] Parsing TAR initrd: ");
serial_write(clean_path); serial_write(clean_path);
serial_write("\n"); serial_write("\n");
tar_parse(mod->address, mod->size); tar_parse(mod->address, mod->size);
@@ -237,13 +372,13 @@ void kmain(void) {
fat32_close(fh); fat32_close(fh);
} }
} }
module_manager_register(clean_path, (uint64_t)mod->address, mod->size);
} }
} }
// Initialize fonts now that FAT32 and modules are loaded
uint64_t current_rsp; uint64_t current_rsp;
asm volatile("mov %%rsp, %0" : "=r"(current_rsp)); asm volatile("mov %%rsp, %0" : "=r"(current_rsp));
serial_write("[DEBUG] Stack Alignment: 0x"); serial_write("[INIT] Stack Alignment: 0x");
serial_write_hex(current_rsp); serial_write_hex(current_rsp);
serial_write("\n"); serial_write("\n");
@@ -253,18 +388,13 @@ void kmain(void) {
ps2_init(); ps2_init();
asm("sti"); asm("sti");
// Initialize LAPIC for IPI support
lapic_init(); lapic_init();
// Initialize SMP — bring up all CPU cores
if (smp_request.response != NULL) { if (smp_request.response != NULL) {
uint32_t online = smp_init(smp_request.response); uint32_t online = smp_init(smp_request.response);
serial_write("[DEBUG] SMP init complete, CPUs online: "); log_ok("SMP initialized");
serial_write_num(online);
serial_write("\n");
} else { } else {
serial_write("[DEBUG] No SMP response from bootloader\n"); serial_write("[INIT] No SMP response from bootloader\n");
// Still init as single-CPU
smp_init(NULL); smp_init(NULL);
} }
@@ -272,6 +402,9 @@ void kmain(void) {
asm volatile("sti"); asm volatile("sti");
extern void bootfs_refresh_from_disk(void);
bootfs_refresh_from_disk();
while (1) { while (1) {
wm_process_input(); wm_process_input();
wm_process_deferred_thumbs(); wm_process_deferred_thumbs();

View File

@@ -14,8 +14,8 @@ static size_t man_strlen(const char *str) {
} }
static void write_man_file(const char *name, const char *content) { static void write_man_file(const char *name, const char *content) {
char path[128] = "A:/Library/man/"; char path[128] = "/Library/man/";
int i = 15; int i = 13;
while (*name) path[i++] = *name++; while (*name) path[i++] = *name++;
path[i++] = '.'; path[i++] = '.';
path[i++] = 't'; path[i++] = 't';
@@ -31,8 +31,8 @@ static void write_man_file(const char *name, const char *content) {
} }
void create_man_entries(void) { void create_man_entries(void) {
fat32_mkdir("A:/Library"); fat32_mkdir("/Library");
fat32_mkdir("A:/Library/man"); fat32_mkdir("/Library/man");
write_man_file("ping", "PING - Send ICMP echo requests\n\nUsage: ping <ip>\n\nSends ICMP echo requests to the specified IP address and displays the response times."); write_man_file("ping", "PING - Send ICMP echo requests\n\nUsage: ping <ip>\n\nSends ICMP echo requests to the specified IP address and displays the response times.");
write_man_file("net", "NET - Network utilities\n\nUsage: net init\nnet info\nnet ipset >ip<\nnet udpsend >ip< >port< >message< net ping >ip< net help\n\nA collection of network-related commands."); write_man_file("net", "NET - Network utilities\n\nUsage: net init\nnet info\nnet ipset >ip<\nnet udpsend >ip< >port< >message< net ping >ip< net help\n\nA collection of network-related commands.");
@@ -55,7 +55,8 @@ void create_man_entries(void) {
write_man_file("touch", "TOUCH - Create empty file\n\nUsage: touch <filename>\n\nCreates a new empty file if it doesn't exist."); write_man_file("touch", "TOUCH - Create empty file\n\nUsage: touch <filename>\n\nCreates a new empty file if it doesn't exist.");
write_man_file("cc", "CC - C Compiler\n\nUsage: cc <file.c>\n\nThe BoredOS C Compiler. Compiles C source files into executables. (execute these with ./>file<)"); write_man_file("cc", "CC - C Compiler\n\nUsage: cc <file.c>\n\nThe BoredOS C Compiler. Compiles C source files into executables. (execute these with ./>file<)");
write_man_file("crash", "CRASH - Trigger kernel exception\n\nUsage: crash\n\nIntentionally triggers a null pointer dereference to test handlers."); write_man_file("crash", "CRASH - Trigger kernel exception\n\nUsage: crash\n\nIntentionally triggers a null pointer dereference to test handlers.");
write_man_file("sysfetch", "SYSFETCH - Show OS information\n\nUsage: sysfetch\n\nDisplays system information in a neofetch-like layout. Configurable via A:/Library/conf/sysfetch.cfg."); write_man_file("sysfetch", "SYSFETCH - Show OS information\n\nUsage: sysfetch\n\nDisplays system information in a neofetch-like layout. Configurable via /Library/conf/sysfetch.cfg.");
write_man_file("uname", "UNAME - Print system information\n\nUsage: uname [-amnoprsv]\n\nOptions:\n -a Print all information\n -s Kernel name\n -n Node name\n -r Kernel release\n -v Kernel build date and time\n -m Machine hardware name\n -p Processor type\n -o Operating system name");
write_man_file("meminfo", "MEMINFO - Memory usage stats\n\nUsage: meminfo\n\nDisplays current physical and virtual memory allocation statistics."); write_man_file("meminfo", "MEMINFO - Memory usage stats\n\nUsage: meminfo\n\nDisplays current physical and virtual memory allocation statistics.");
write_man_file("pci_list", "PCI_LIST - Scan PCI bus\n\nUsage: pci_list\n\nScans the PCI bus and lists all detected hardware devices."); write_man_file("pci_list", "PCI_LIST - Scan PCI bus\n\nUsage: pci_list\n\nScans the PCI bus and lists all detected hardware devices.");
write_man_file("reboot", "REBOOT - Restart system\n\nUsage: reboot\n\nRestarts the computer immediately."); write_man_file("reboot", "REBOOT - Restart system\n\nUsage: reboot\n\nRestarts the computer immediately.");
@@ -69,6 +70,7 @@ void create_man_entries(void) {
write_man_file("math", "MATH - Expression evaluator\n\nUsage: math <expression>\n\nEvaluates simple arithmetic expressions from the command line."); write_man_file("math", "MATH - Expression evaluator\n\nUsage: math <expression>\n\nEvaluates simple arithmetic expressions from the command line.");
write_man_file("viewer", "VIEWER - Image viewer\n\nUsage: viewer <file.ppm>\n\nA graphical application for viewing image files."); write_man_file("viewer", "VIEWER - Image viewer\n\nUsage: viewer <file.ppm>\n\nA graphical application for viewing image files.");
write_man_file("settings", "SETTINGS - System settings\n\nUsage: settings\n\nOpens the graphical system configuration tool."); write_man_file("settings", "SETTINGS - System settings\n\nUsage: settings\n\nOpens the graphical system configuration tool.");
write_man_file("2048", "2048 - Classic game\n\nUsage: 2048\n\nPlays the classic 2048 game.");
} }
#endif #endif

View File

@@ -4,6 +4,8 @@
#include <stdint.h> #include <stdint.h>
#include "limine.h" #include "limine.h"
#include <stddef.h> #include <stddef.h>
#include "platform.h"
#include "kutils.h"
static volatile struct limine_hhdm_request hhdm_request __attribute__((used, section(".requests"))) = { static volatile struct limine_hhdm_request hhdm_request __attribute__((used, section(".requests"))) = {
.id = LIMINE_HHDM_REQUEST, .id = LIMINE_HHDM_REQUEST,
.revision = 0, .revision = 0,
@@ -69,3 +71,83 @@ void platform_get_cpu_model(char *model) {
} }
model[48] = '\0'; model[48] = '\0';
} }
void platform_get_cpu_vendor(char *vendor) {
uint32_t eax, ebx, ecx, edx;
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(0));
char *p = (char *)vendor;
*((uint32_t *)&p[0]) = ebx;
*((uint32_t *)&p[4]) = edx;
*((uint32_t *)&p[8]) = ecx;
p[12] = '\0';
}
void platform_get_cpu_info(cpu_info_t *info) {
uint32_t eax, ebx, ecx, edx;
// CPUID leaf 1: basic feature information
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1));
info->stepping = eax & 0xF;
info->model = (eax >> 4) & 0xF;
info->family = (eax >> 8) & 0xF;
info->microcode = (ebx >> 8) & 0xFF;
info->flags = ((uint64_t)ecx << 32) | edx; // ECX and EDX contain feature flags
info->cache_size = (ebx >> 16) & 0xFF; // Cache line size in bytes
}
void platform_get_cpu_flags(char *flags_str) {
uint32_t eax, ebx, ecx, edx;
flags_str[0] = '\0';
// CPUID leaf 1
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1));
// ECX flags
if (ecx & (1 << 0)) k_strcpy(flags_str + k_strlen(flags_str), "sse3 ");
if (ecx & (1 << 1)) k_strcpy(flags_str + k_strlen(flags_str), "pclmulqdq ");
if (ecx & (1 << 3)) k_strcpy(flags_str + k_strlen(flags_str), "monitor ");
if (ecx & (1 << 6)) k_strcpy(flags_str + k_strlen(flags_str), "ssse3 ");
if (ecx & (1 << 9)) k_strcpy(flags_str + k_strlen(flags_str), "sdbg ");
if (ecx & (1 << 12)) k_strcpy(flags_str + k_strlen(flags_str), "fma ");
if (ecx & (1 << 13)) k_strcpy(flags_str + k_strlen(flags_str), "cx16 ");
if (ecx & (1 << 19)) k_strcpy(flags_str + k_strlen(flags_str), "sse4_1 ");
if (ecx & (1 << 20)) k_strcpy(flags_str + k_strlen(flags_str), "sse4_2 ");
if (ecx & (1 << 23)) k_strcpy(flags_str + k_strlen(flags_str), "popcnt ");
if (ecx & (1 << 25)) k_strcpy(flags_str + k_strlen(flags_str), "aes ");
if (ecx & (1 << 26)) k_strcpy(flags_str + k_strlen(flags_str), "xsave ");
if (ecx & (1 << 28)) k_strcpy(flags_str + k_strlen(flags_str), "avx ");
// EDX flags
if (edx & (1 << 0)) k_strcpy(flags_str + k_strlen(flags_str), "fpu ");
if (edx & (1 << 3)) k_strcpy(flags_str + k_strlen(flags_str), "pse ");
if (edx & (1 << 4)) k_strcpy(flags_str + k_strlen(flags_str), "tsc ");
if (edx & (1 << 6)) k_strcpy(flags_str + k_strlen(flags_str), "pae ");
if (edx & (1 << 8)) k_strcpy(flags_str + k_strlen(flags_str), "cx8 ");
if (edx & (1 << 9)) k_strcpy(flags_str + k_strlen(flags_str), "apic ");
if (edx & (1 << 11)) k_strcpy(flags_str + k_strlen(flags_str), "sep ");
if (edx & (1 << 15)) k_strcpy(flags_str + k_strlen(flags_str), "cmov ");
if (edx & (1 << 23)) k_strcpy(flags_str + k_strlen(flags_str), "mmx ");
if (edx & (1 << 24)) k_strcpy(flags_str + k_strlen(flags_str), "fxsr ");
if (edx & (1 << 25)) k_strcpy(flags_str + k_strlen(flags_str), "sse ");
if (edx & (1 << 26)) k_strcpy(flags_str + k_strlen(flags_str), "sse2 ");
// Extended leaf 0x80000001 for advanced flags
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(0x80000001));
if (edx & (1 << 11)) k_strcpy(flags_str + k_strlen(flags_str), "syscall ");
if (edx & (1 << 20)) k_strcpy(flags_str + k_strlen(flags_str), "nx ");
if (edx & (1 << 26)) k_strcpy(flags_str + k_strlen(flags_str), "pdpe1gb ");
if (edx & (1 << 27)) k_strcpy(flags_str + k_strlen(flags_str), "rdtscp ");
if (edx & (1 << 29)) k_strcpy(flags_str + k_strlen(flags_str), "lm ");
if (ecx & (1 << 0)) k_strcpy(flags_str + k_strlen(flags_str), "lahf_lm ");
if (ecx & (1 << 5)) k_strcpy(flags_str + k_strlen(flags_str), "abm ");
// Remove trailing space
int len = k_strlen(flags_str);
if (len > 0 && flags_str[len-1] == ' ') {
flags_str[len-1] = '\0';
}
}

View File

@@ -6,9 +6,21 @@
#include <stdint.h> #include <stdint.h>
typedef struct {
uint32_t family;
uint32_t model;
uint32_t stepping;
uint32_t microcode;
uint64_t flags;
uint32_t cache_size;
} cpu_info_t;
void platform_init(void); void platform_init(void);
uint64_t p2v(uint64_t phys); uint64_t p2v(uint64_t phys);
uint64_t v2p(uint64_t virt); uint64_t v2p(uint64_t virt);
void platform_get_cpu_model(char *model); void platform_get_cpu_model(char *model);
void platform_get_cpu_vendor(char *vendor);
void platform_get_cpu_info(cpu_info_t *info);
void platform_get_cpu_flags(char *flags_str);
#endif #endif

View File

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

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -13,4 +13,6 @@ uint64_t timer_handler(registers_t *regs);
uint64_t keyboard_handler(registers_t *regs); uint64_t keyboard_handler(registers_t *regs);
uint64_t mouse_handler(registers_t *regs); uint64_t mouse_handler(registers_t *regs);
bool ps2_shift_pressed(void);
#endif #endif

541
src/fs/bootfs.c Normal file
View File

@@ -0,0 +1,541 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "bootfs.h"
#include "../sys/bootfs_state.h"
#include "vfs.h"
#include "core/kutils.h"
#include "core/platform.h"
#include "core/kconsole.h"
#include "memory_manager.h"
extern void serial_write(const char *str);
extern void serial_write_hex(uint64_t value);
typedef struct {
char path[512];
int offset;
bool is_root;
bool is_metadata_dir;
} bootfs_handle_t;
static void* bootfs_open(void *fs_private, const char *path, const char *mode);
static void bootfs_close(void *fs_private, void *handle);
static int bootfs_read(void *fs_private, void *handle, void *buf, int size);
static int bootfs_write(void *fs_private, void *handle, const void *buf, int size);
static int bootfs_seek(void *fs_private, void *handle, int offset, int whence);
static int bootfs_readdir(void *fs_private, const char *rel_path, vfs_dirent_t *entries, int max);
static bool bootfs_mkdir(void *fs_private, const char *rel_path);
static bool bootfs_rmdir(void *fs_private, const char *rel_path);
static bool bootfs_unlink(void *fs_private, const char *rel_path);
static bool bootfs_rename(void *fs_private, const char *old_path, const char *new_path);
static bool bootfs_exists(void *fs_private, const char *rel_path);
static bool bootfs_is_dir(void *fs_private, const char *rel_path);
static int bootfs_get_info(void *fs_private, const char *rel_path, vfs_dirent_t *info);
static uint32_t bootfs_get_position(void *file_handle);
static uint32_t bootfs_get_size(void *file_handle);
static vfs_fs_ops_t bootfs_ops = {
.open = bootfs_open,
.close = bootfs_close,
.read = bootfs_read,
.write = bootfs_write,
.seek = bootfs_seek,
.readdir = bootfs_readdir,
.mkdir = bootfs_mkdir,
.rmdir = bootfs_rmdir,
.unlink = bootfs_unlink,
.rename = bootfs_rename,
.exists = bootfs_exists,
.is_dir = bootfs_is_dir,
.get_info = bootfs_get_info,
.get_position = bootfs_get_position,
.get_size = bootfs_get_size,
};
bootfs_state_t g_bootfs_state = {0};
static bool is_metadata_path(const char *path) {
if (!path) return false;
return k_strncmp(path, "metadata", 8) == 0;
}
static bool is_metadata_file(const char *path) {
if (k_strcmp(path, "metadata/boot_time") == 0) return true;
if (k_strcmp(path, "metadata/boot_flags") == 0) return true;
if (k_strcmp(path, "metadata/version") == 0) return true;
return false;
}
static void* bootfs_open(void *fs_private, const char *path, const char *mode) {
if (!path) path = "";
if (path[0] == '/') path++;
bootfs_handle_t *h = (bootfs_handle_t*)kmalloc(sizeof(bootfs_handle_t));
if (!h) return NULL;
k_memset(h, 0, sizeof(bootfs_handle_t));
k_strcpy(h->path, path);
h->offset = 0;
if (path[0] == '\0') {
h->is_root = true;
} else if (is_metadata_path(path) && path[8] == '\0') {
h->is_metadata_dir = true;
}
return h;
}
static void bootfs_close(void *fs_private, void *handle) {
if (handle) kfree(handle);
}
static int generate_metadata_content(const char *file, char *buffer, int max_size) {
if (!buffer || max_size <= 0) return 0;
buffer[0] = '\0';
int len = 0;
if (k_strcmp(file, "metadata/boot_time") == 0) {
extern uint32_t wm_get_ticks(void);
uint32_t ticks = wm_get_ticks();
k_strcpy(buffer, "Boot time: ");
char time_buf[32];
k_itoa(g_bootfs_state.boot_time_ms, time_buf);
k_strcpy(buffer + k_strlen(buffer), time_buf);
k_strcpy(buffer + k_strlen(buffer), " ms\nTicks: ");
k_itoa(ticks, time_buf);
k_strcpy(buffer + k_strlen(buffer), time_buf);
k_strcpy(buffer + k_strlen(buffer), "\n");
len = k_strlen(buffer);
} else if (k_strcmp(file, "metadata/version") == 0) {
k_strcpy(buffer, "Bootloader: ");
k_strcpy(buffer + k_strlen(buffer), g_bootfs_state.bootloader_name);
k_strcpy(buffer + k_strlen(buffer), "\nVersion: ");
k_strcpy(buffer + k_strlen(buffer), g_bootfs_state.bootloader_version);
k_strcpy(buffer + k_strlen(buffer), "\n");
len = k_strlen(buffer);
} else if (k_strcmp(file, "metadata/boot_flags") == 0) {
k_strcpy(buffer, "Boot flags: 0x");
char flags_buf[8];
uint8_t flags = g_bootfs_state.boot_flags;
int hex_digit = (flags >> 4) & 0xF;
flags_buf[0] = hex_digit < 10 ? '0' + hex_digit : 'a' + (hex_digit - 10);
hex_digit = flags & 0xF;
flags_buf[1] = hex_digit < 10 ? '0' + hex_digit : 'a' + (hex_digit - 10);
flags_buf[2] = '\n';
flags_buf[3] = '\0';
k_strcpy(buffer + k_strlen(buffer), flags_buf);
len = k_strlen(buffer);
}
return len;
}
static int bootfs_read(void *fs_private, void *handle, void *buf, int size) {
bootfs_handle_t *h = (bootfs_handle_t*)handle;
if (!h || !buf || size <= 0) return -1;
char *content_buffer = (char*)kmalloc(4096);
if (!content_buffer) return -1;
int content_len = 0;
if (k_strcmp(h->path, "limine.conf") == 0) {
k_memcpy(content_buffer, g_bootfs_state.limine_conf,
g_bootfs_state.limine_conf_len);
content_len = g_bootfs_state.limine_conf_len;
} else if (k_strcmp(h->path, "kernel") == 0) {
k_strcpy(content_buffer, "Kernel reference\nSize: ");
char size_buf[32];
k_itoa(g_bootfs_state.kernel_size, size_buf);
k_strcpy(content_buffer + k_strlen(content_buffer), size_buf);
k_strcpy(content_buffer + k_strlen(content_buffer), " bytes\n");
content_len = k_strlen(content_buffer);
} else if (k_strcmp(h->path, "initrd") == 0) {
k_strcpy(content_buffer, "Initial ramdisk reference\nSize: ");
char size_buf[32];
k_itoa(g_bootfs_state.initrd_size, size_buf);
k_strcpy(content_buffer + k_strlen(content_buffer), size_buf);
k_strcpy(content_buffer + k_strlen(content_buffer), " bytes\n");
content_len = k_strlen(content_buffer);
} else if (is_metadata_file(h->path)) {
content_len = generate_metadata_content(h->path, content_buffer, 4096);
} else {
kfree(content_buffer);
return -1;
}
// Handle offset and reading
if (h->offset >= content_len) {
kfree(content_buffer);
return 0;
}
int available = content_len - h->offset;
int read_size = (available < size) ? available : size;
k_memcpy(buf, content_buffer + h->offset, read_size);
h->offset += read_size;
kfree(content_buffer);
return read_size;
}
static int bootfs_write(void *fs_private, void *handle, const void *buf, int size) {
bootfs_handle_t *h = (bootfs_handle_t*)handle;
if (!h || !buf || size <= 0) return -1;
if (k_strcmp(h->path, "limine.conf") != 0) {
return -1;
}
int max_write = 2048 - h->offset;
if (max_write <= 0) return -1;
int write_size = (size < max_write) ? size : max_write;
k_memcpy(g_bootfs_state.limine_conf + h->offset, buf, write_size);
h->offset += write_size;
if (h->offset > g_bootfs_state.limine_conf_len) {
g_bootfs_state.limine_conf_len = h->offset;
}
extern vfs_file_t* vfs_open(const char *path, const char *mode);
extern int vfs_write(vfs_file_t *file, const void *buf, int size);
extern void vfs_close(vfs_file_t *file);
vfs_file_t *fat_conf = vfs_open("/limine.conf", "w");
if (fat_conf) {
vfs_write(fat_conf, g_bootfs_state.limine_conf, g_bootfs_state.limine_conf_len);
vfs_close(fat_conf);
}
return write_size;
}
static int bootfs_seek(void *fs_private, void *handle, int offset, int whence) {
bootfs_handle_t *h = (bootfs_handle_t*)handle;
if (!h) return -1;
switch (whence) {
case 0: // SEEK_SET
h->offset = offset;
break;
case 1: // SEEK_CUR
h->offset += offset;
break;
case 2: // SEEK_END
return -1;
default:
return -1;
}
return h->offset;
}
static int bootfs_readdir(void *fs_private, const char *rel_path, vfs_dirent_t *entries, int max) {
if (!entries || max <= 0) return 0;
if (!rel_path) rel_path = "";
if (rel_path[0] == '/') rel_path++;
int count = 0;
if (rel_path[0] == '\0') {
if (count < max) {
k_strcpy(entries[count].name, "limine.conf");
entries[count].size = g_bootfs_state.limine_conf_len;
entries[count].is_directory = 0;
count++;
}
if (count < max) {
k_strcpy(entries[count].name, "kernel");
entries[count].size = g_bootfs_state.kernel_size;
entries[count].is_directory = 0;
count++;
}
if (count < max) {
k_strcpy(entries[count].name, "initrd");
entries[count].size = g_bootfs_state.initrd_size;
entries[count].is_directory = 0;
count++;
}
if (count < max) {
k_strcpy(entries[count].name, "metadata");
entries[count].size = 0;
entries[count].is_directory = 1;
count++;
}
}
else if (k_strcmp(rel_path, "metadata") == 0) {
const char *meta_files[] = {
"boot_time",
"boot_flags",
"version"
};
for (int i = 0; i < 3 && count < max; i++) {
k_strcpy(entries[count].name, meta_files[i]);
entries[count].size = 0;
entries[count].is_directory = 0;
count++;
}
}
return count;
}
static bool bootfs_mkdir(void *fs_private, const char *rel_path) {
return false;
}
static bool bootfs_rmdir(void *fs_private, const char *rel_path) {
if (!rel_path) rel_path = "";
if (rel_path[0] == '/') rel_path++;
if (k_strcmp(rel_path, "metadata") == 0) {
return false; /* metadata directory is protected */
}
return false; /* no other directories to remove */
}
static bool bootfs_unlink(void *fs_private, const char *rel_path) {
if (!rel_path) return false;
if (rel_path[0] == '/') rel_path++;
/* Only limine.conf can be deleted */
if (k_strcmp(rel_path, "limine.conf") != 0) {
return false;
}
/* Clear the bootfs state */
g_bootfs_state.limine_conf[0] = '\0';
g_bootfs_state.limine_conf_len = 0;
/* Delete from partition */
extern bool vfs_delete(const char *path);
bool result = vfs_delete("/limine.conf");
if (result) {
serial_write("[BOOTFS] Deleted limine.conf from partition\n");
} else {
serial_write("[BOOTFS] Warning: Could not delete limine.conf from partition\n");
}
return result;
}
static bool bootfs_rename(void *fs_private, const char *old_path, const char *new_path) {
if (!old_path || !new_path) return false;
const char *old_rel = old_path;
const char *new_rel = new_path;
if (old_rel[0] == '/') old_rel++;
if (new_rel[0] == '/') new_rel++;
/* Only limine.conf can be renamed */
if (k_strcmp(old_rel, "limine.conf") != 0) {
return false;
}
/* kernel and initrd are protected */
if (k_strcmp(new_rel, "kernel") == 0 || k_strcmp(new_rel, "initrd") == 0) {
return false;
}
/* metadata directory is protected */
if (k_strncmp(new_rel, "metadata", 8) == 0) {
return false;
}
extern bool vfs_rename(const char *old_path, const char *new_path);
char new_partition_path[256];
k_strcpy(new_partition_path, "/");
/* Manually append new_rel to new_partition_path */
int path_len = 0;
while (new_partition_path[path_len]) path_len++;
int rel_len = 0;
while (new_rel[rel_len]) rel_len++;
if (path_len + rel_len >= 256) {
serial_write("[BOOTFS] Error: new path too long\n");
return false;
}
k_memcpy(new_partition_path + path_len, new_rel, rel_len + 1);
/* Rename on partition filesystem */
bool result = vfs_rename("/limine.conf", new_partition_path);
if (result) {
serial_write("[BOOTFS] Renamed limine.conf to ");
serial_write(new_rel);
serial_write("\n");
} else {
serial_write("[BOOTFS] Warning: Could not rename limine.conf to ");
serial_write(new_rel);
serial_write("\n");
}
return result;
}
static bool bootfs_exists(void *fs_private, const char *rel_path) {
if (!rel_path) rel_path = "";
if (rel_path[0] == '/') rel_path++;
if (rel_path[0] == '\0') return true;
if (k_strcmp(rel_path, "limine.conf") == 0) return true;
if (k_strcmp(rel_path, "kernel") == 0) return true;
if (k_strcmp(rel_path, "initrd") == 0) return true;
if (k_strcmp(rel_path, "metadata") == 0) return true;
if (is_metadata_file(rel_path)) return true;
return false;
}
static bool bootfs_is_dir(void *fs_private, const char *rel_path) {
if (!rel_path) rel_path = "";
if (rel_path[0] == '/') rel_path++;
if (rel_path[0] == '\0') return true;
if (k_strcmp(rel_path, "metadata") == 0) return true;
return false;
}
static int bootfs_get_info(void *fs_private, const char *rel_path, vfs_dirent_t *info) {
if (!info) return -1;
if (!rel_path) rel_path = "";
if (rel_path[0] == '/') rel_path++;
k_memset(info, 0, sizeof(vfs_dirent_t));
if (rel_path[0] == '\0') {
k_strcpy(info->name, "/");
info->is_directory = 1;
return 0;
}
if (k_strcmp(rel_path, "limine.conf") == 0) {
k_strcpy(info->name, "limine.conf");
info->size = g_bootfs_state.limine_conf_len;
info->is_directory = 0;
return 0;
}
if (k_strcmp(rel_path, "kernel") == 0) {
k_strcpy(info->name, "kernel");
info->size = g_bootfs_state.kernel_size;
info->is_directory = 0;
return 0;
}
if (k_strcmp(rel_path, "initrd") == 0) {
k_strcpy(info->name, "initrd");
info->size = g_bootfs_state.initrd_size;
info->is_directory = 0;
return 0;
}
if (k_strcmp(rel_path, "metadata") == 0) {
k_strcpy(info->name, "metadata");
info->is_directory = 1;
return 0;
}
if (is_metadata_file(rel_path)) {
char temp_buf[4096];
int len = generate_metadata_content(rel_path, temp_buf, 4096);
k_strcpy(info->name, rel_path + 9);
info->size = len;
info->is_directory = 0;
return 0;
}
return -1;
}
static uint32_t bootfs_get_position(void *file_handle) {
bootfs_handle_t *h = (bootfs_handle_t*)file_handle;
if (!h) return 0;
return h->offset;
}
static uint32_t bootfs_get_size(void *file_handle) {
bootfs_handle_t *h = (bootfs_handle_t*)file_handle;
if (!h) return 0;
if (k_strcmp(h->path, "limine.conf") == 0) {
return g_bootfs_state.limine_conf_len;
} else if (k_strcmp(h->path, "kernel") == 0) {
return g_bootfs_state.kernel_size;
} else if (k_strcmp(h->path, "initrd") == 0) {
return g_bootfs_state.initrd_size;
} else if (is_metadata_file(h->path)) {
char temp_buf[4096];
return generate_metadata_content(h->path, temp_buf, 4096);
}
return 0;
}
vfs_fs_ops_t* bootfs_get_ops(void) {
return &bootfs_ops;
}
void bootfs_state_init(void) {
k_memset(&g_bootfs_state, 0, sizeof(bootfs_state_t));
k_strcpy(g_bootfs_state.bootloader_name, "Limine");
k_strcpy(g_bootfs_state.bootloader_version, "6.0.0");
g_bootfs_state.limine_conf[0] = '\0';
g_bootfs_state.limine_conf_len = 0;
g_bootfs_state.kernel_size = 0;
g_bootfs_state.initrd_size = 0;
g_bootfs_state.boot_time_ms = 0;
}
void bootfs_init(void) {
bootfs_state_init();
}
void bootfs_refresh_from_disk(void) {
extern vfs_file_t* vfs_open(const char *path, const char *mode);
extern int vfs_read(vfs_file_t *file, void *buf, int size);
extern void vfs_close(vfs_file_t *file);
vfs_file_t *boot_conf = vfs_open("/limine.conf", "r");
if (boot_conf) {
int bytes_read = vfs_read(boot_conf, g_bootfs_state.limine_conf, 2047);
if (bytes_read > 0) {
g_bootfs_state.limine_conf[bytes_read] = '\0';
g_bootfs_state.limine_conf_len = bytes_read;
serial_write("[BOOTFS] Loaded limine.conf from partition: ");
extern void serial_write_hex(uint64_t value);
serial_write_hex(bytes_read);
serial_write(" bytes\n");
}
vfs_close(boot_conf);
} else {
serial_write("[BOOTFS] Warning: /limine.conf not found on partition\n");
}
}

13
src/fs/bootfs.h Normal file
View File

@@ -0,0 +1,13 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef BOOTFS_H
#define BOOTFS_H
#include "vfs.h"
void bootfs_init(void);
void bootfs_refresh_from_disk(void);
vfs_fs_ops_t* bootfs_get_ops(void);
#endif

File diff suppressed because it is too large Load Diff

View File

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

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

@@ -0,0 +1,451 @@
#include "vfs.h"
#include "../sys/process.h"
#include "../sys/syscall.h"
#include "../dev/disk.h"
#include "memory_manager.h"
#include "core/kutils.h"
#include "core/platform.h"
typedef struct {
uint32_t pid;
char type[32];
int offset;
bool is_root;
} procfs_handle_t;
void* procfs_open(void *fs_private, const char *path, const char *mode) {
if (path[0] == '/') path++;
procfs_handle_t *h = (procfs_handle_t*)kmalloc(sizeof(procfs_handle_t));
k_memset(h, 0, sizeof(procfs_handle_t));
h->offset = 0;
if (path[0] == '\0') {
h->is_root = true;
return h;
}
if (path[0] >= '0' && path[0] <= '9') {
char pid_str[16];
int i = 0;
while (path[i] && path[i] != '/' && i < 15) {
pid_str[i] = path[i];
i++;
}
pid_str[i] = 0;
h->pid = k_atoi(pid_str);
if (path[i] == '/') {
k_strcpy(h->type, path + i + 1);
} else {
h->type[0] = 0;
}
return h;
}
h->pid = 0xFFFFFFFF;
k_strcpy(h->type, path);
return h;
}
void procfs_close(void *fs_private, void *handle) {
if (handle) kfree(handle);
}
int procfs_read(void *fs_private, void *handle, void *buf, int size) {
procfs_handle_t *h = (procfs_handle_t*)handle;
if (!h) return -1;
char *out = (char*)kmalloc(16384);
if (!out) return -1;
out[0] = 0;
if (h->pid == 0xFFFFFFFF) {
if (k_strcmp(h->type, "version") == 0) {
extern void get_os_info(os_info_t *info);
os_info_t info;
get_os_info(&info);
k_strcpy(out, info.os_name);
k_strcpy(out + k_strlen(out), " [");
k_strcpy(out + k_strlen(out), info.os_codename);
k_strcpy(out + k_strlen(out), "] Version ");
k_strcpy(out + k_strlen(out), info.os_version);
k_strcpy(out + k_strlen(out), "\nKernel: ");
k_strcpy(out + k_strlen(out), info.kernel_name);
k_strcpy(out + k_strlen(out), " ");
k_strcpy(out + k_strlen(out), info.kernel_version);
k_strcpy(out + k_strlen(out), "\nBuild: ");
k_strcpy(out + k_strlen(out), info.build_date);
k_strcpy(out + k_strlen(out), " ");
k_strcpy(out + k_strlen(out), info.build_time);
k_strcpy(out + k_strlen(out), "\n");
} else if (k_strcmp(h->type, "uptime") == 0) {
extern uint32_t wm_get_ticks(void);
uint32_t ticks = wm_get_ticks();
k_itoa(ticks / 60, out);
k_strcpy(out + k_strlen(out), " seconds\nRaw_Ticks:");
char t_s[16]; k_itoa(ticks, t_s);
k_strcpy(out + k_strlen(out), t_s);
k_strcpy(out + k_strlen(out), "\n");
} else if (k_strcmp(h->type, "cpuinfo") == 0) {
extern uint32_t smp_cpu_count(void);
extern void platform_get_cpu_model(char *model);
extern void platform_get_cpu_vendor(char *vendor);
extern void platform_get_cpu_info(cpu_info_t *info);
extern void platform_get_cpu_flags(char *flags_str);
char model[64];
char vendor[16];
char flags[1024];
cpu_info_t info;
platform_get_cpu_model(model);
platform_get_cpu_vendor(vendor);
platform_get_cpu_info(&info);
platform_get_cpu_flags(flags);
uint32_t cpu_count = smp_cpu_count();
out[0] = '\0';
// Output info for each processor
for (uint32_t i = 0; i < cpu_count; i++) {
char buf[32];
k_strcpy(out + k_strlen(out), "processor\t: ");
k_itoa(i, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "vendor_id\t: ");
k_strcpy(out + k_strlen(out), vendor);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "cpu family\t: ");
k_itoa(info.family, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "model\t\t: ");
k_itoa(info.model, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "model name\t: ");
k_strcpy(out + k_strlen(out), model);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "stepping\t: ");
k_itoa(info.stepping, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "microcode\t: 0x");
char hex[16];
int temp = info.microcode;
int hex_pos = 0;
for (int j = 7; j >= 0; j--) {
int digit = (temp >> (j * 4)) & 0xF;
hex[hex_pos++] = digit < 10 ? '0' + digit : 'a' + (digit - 10);
}
hex[hex_pos] = '\0';
k_strcpy(out + k_strlen(out), hex);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "cache size\t: ");
k_itoa(info.cache_size, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), " KB\n");
k_strcpy(out + k_strlen(out), "physical id\t: 0\n");
k_strcpy(out + k_strlen(out), "siblings\t: ");
k_itoa(cpu_count, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "core id\t\t: ");
k_itoa(i, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "cpu cores\t: ");
k_itoa(cpu_count, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "apicid\t\t: ");
k_itoa(i, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "initial apicid\t: ");
k_itoa(i, buf);
k_strcpy(out + k_strlen(out), buf);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "fpu\t\t: yes\n");
k_strcpy(out + k_strlen(out), "fpu_exception\t: yes\n");
k_strcpy(out + k_strlen(out), "cpuid level\t: 13\n");
k_strcpy(out + k_strlen(out), "wp\t\t: yes\n");
k_strcpy(out + k_strlen(out), "flags\t\t: ");
k_strcpy(out + k_strlen(out), flags);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "bugs\t\t: \n");
k_strcpy(out + k_strlen(out), "bogomips\t: 4800.00\n");
if (i < cpu_count - 1) {
k_strcpy(out + k_strlen(out), "\n");
}
}
} else if (k_strcmp(h->type, "meminfo") == 0) {
extern MemStats memory_get_stats(void);
MemStats stats = memory_get_stats();
char m_s[32];
k_strcpy(out, "MemTotal:\t");
k_itoa(stats.total_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\n");
k_strcpy(out + k_strlen(out), "MemFree:\t");
k_itoa(stats.available_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\n");
k_strcpy(out + k_strlen(out), "MemAvailable:\t");
k_itoa(stats.available_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\n");
k_strcpy(out + k_strlen(out), "Buffers:\t0 kB\n");
k_strcpy(out + k_strlen(out), "Cached:\t\t0 kB\n");
k_strcpy(out + k_strlen(out), "MemUsed:\t");
k_itoa(stats.used_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\n");
k_strcpy(out + k_strlen(out), "MemPeak:\t");
k_itoa(stats.peak_memory_used / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\n");
k_strcpy(out + k_strlen(out), "SwapTotal:\t0 kB\n");
k_strcpy(out + k_strlen(out), "SwapFree:\t0 kB\n");
k_strcpy(out + k_strlen(out), "Dirty:\t\t0 kB\n");
k_strcpy(out + k_strlen(out), "Writeback:\t0 kB\n");
k_strcpy(out + k_strlen(out), "AnonPages:\t");
k_itoa(stats.used_memory / 1024, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), " kB\n");
k_strcpy(out + k_strlen(out), "Mapped:\t\t0 kB\n");
k_strcpy(out + k_strlen(out), "Shmem:\t\t0 kB\n");
k_strcpy(out + k_strlen(out), "Blocks:\t\t");
k_itoa(stats.allocated_blocks, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "FreeBlocks:\t");
k_itoa(stats.free_blocks, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "Fragmentation:\t");
k_itoa(stats.fragmentation_percent, m_s);
k_strcpy(out + k_strlen(out), m_s);
k_strcpy(out + k_strlen(out), "%\n");
} else if (k_strcmp(h->type, "devices") == 0) {
extern int disk_get_count(void);
extern Disk* disk_get_by_index(int index);
int dcount = disk_get_count();
out[0] = '\0';
k_strcpy(out, "Character devices:\n");
k_strcpy(out + k_strlen(out), " 1 mem\n");
k_strcpy(out + k_strlen(out), " 4 tty\n");
k_strcpy(out + k_strlen(out), " 5 cua\n");
k_strcpy(out + k_strlen(out), " 7 vcs\n");
k_strcpy(out + k_strlen(out), " 8 stdin\n");
k_strcpy(out + k_strlen(out), " 13 input\n");
k_strcpy(out + k_strlen(out), " 14 sound\n");
k_strcpy(out + k_strlen(out), " 29 fb\n");
k_strcpy(out + k_strlen(out), "189 usb\n\n");
k_strcpy(out + k_strlen(out), "Block devices:\n");
for (int i = 0; i < dcount; i++) {
Disk *d = disk_get_by_index(i);
if (d && !d->is_partition) {
k_strcpy(out + k_strlen(out), " 8 ");
k_strcpy(out + k_strlen(out), d->devname);
k_strcpy(out + k_strlen(out), "\n");
}
}
k_strcpy(out + k_strlen(out), " 11 sr\n");
k_strcpy(out + k_strlen(out), "253 virtblk\n");
}
}
else {
process_t *proc = process_get_by_pid(h->pid);
if (!proc) { kfree(out); return -1; }
if (k_strcmp(h->type, "name") == 0 || k_strcmp(h->type, "cmdline") == 0) {
k_strcpy(out, proc->name);
k_strcpy(out + k_strlen(out), "\n");
} else if (k_strcmp(h->type, "cwd") == 0) {
k_strcpy(out, proc->cwd);
k_strcpy(out + k_strlen(out), "\n");
} else if (k_strcmp(h->type, "status") == 0) {
k_strcpy(out, "Name: ");
k_strcpy(out + k_strlen(out), proc->name);
k_strcpy(out + k_strlen(out), "\nPID: ");
char pid_s[16]; k_itoa(proc->pid, pid_s);
k_strcpy(out + k_strlen(out), pid_s);
k_strcpy(out + k_strlen(out), "\nState: RUNNING\nMemory: ");
uint64_t mem_val = proc->used_memory;
if (h->pid == 0) {
extern MemStats memory_get_stats(void);
mem_val = memory_get_stats().used_memory;
}
char mem_s[32]; k_itoa(mem_val / 1024, mem_s);
k_strcpy(out + k_strlen(out), mem_s);
k_strcpy(out + k_strlen(out), " KB\nTicks: ");
char tick_s[32]; k_itoa(proc->ticks, tick_s);
k_strcpy(out + k_strlen(out), tick_s);
k_strcpy(out + k_strlen(out), "\nIdle: ");
k_strcpy(out + k_strlen(out), proc->is_idle ? "1" : "0");
k_strcpy(out + k_strlen(out), "\n");
}
}
int len = k_strlen(out);
if (h->offset >= len) { kfree(out); return 0; }
int to_copy = len - h->offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + h->offset, to_copy);
h->offset += to_copy;
kfree(out);
return to_copy;
}
int procfs_write(void *fs_private, void *handle, const void *buf, int size) {
procfs_handle_t *h = (procfs_handle_t*)handle;
if (!h || h->pid == 0xFFFFFFFF) return -1;
if (k_strcmp(h->type, "signal") == 0) {
char cmd[16];
int to_copy = size < 15 ? size : 15;
k_memcpy(cmd, buf, to_copy);
cmd[to_copy] = 0;
if (k_strcmp(cmd, "9") == 0 || k_strcmp(cmd, "kill") == 0) {
process_t *proc = process_get_by_pid(h->pid);
if (proc && proc->pid != 0) {
process_terminate(proc);
return size;
}
}
}
return -1;
}
int procfs_readdir(void *fs_private, const char *path, vfs_dirent_t *entries, int max) {
if (path[0] == '/') path++;
if (path[0] == '\0') {
int out = 0;
k_strcpy(entries[out++].name, "version");
entries[out-1].is_directory = 0;
k_strcpy(entries[out++].name, "uptime");
entries[out-1].is_directory = 0;
k_strcpy(entries[out++].name, "cpuinfo");
entries[out-1].is_directory = 0;
k_strcpy(entries[out++].name, "meminfo");
entries[out-1].is_directory = 0;
k_strcpy(entries[out++].name, "devices");
entries[out-1].is_directory = 0;
extern process_t processes[];
for (int i = 0; i < 16 && out < max; i++) {
if (processes[i].pid != 0xFFFFFFFF) {
k_itoa(processes[i].pid, entries[out].name);
entries[out].is_directory = 1;
entries[out].size = 0;
out++;
}
}
return out;
}
if (path[0] >= '0' && path[0] <= '9') {
int out = 0;
k_strcpy(entries[out++].name, "name");
k_strcpy(entries[out++].name, "status");
k_strcpy(entries[out++].name, "cmdline");
k_strcpy(entries[out++].name, "cwd");
k_strcpy(entries[out++].name, "signal");
for(int i=0; i<out; i++) entries[i].is_directory = 0;
return out;
}
return 0;
}
bool procfs_exists(void *fs_private, const char *path) {
if (path[0] == '/') path++;
if (path[0] == '\0') return true;
if (path[0] >= '0' && path[0] <= '9') {
char pid_str[16];
int i = 0;
while (path[i] && path[i] != '/' && i < 15) {
pid_str[i] = path[i];
i++;
}
pid_str[i] = 0;
uint32_t pid = k_atoi(pid_str);
if (process_get_by_pid(pid)) return true;
}
if (k_strcmp(path, "version") == 0 || k_strcmp(path, "uptime") == 0) return true;
if (k_strcmp(path, "cpuinfo") == 0 || k_strcmp(path, "meminfo") == 0) return true;
if (k_strcmp(path, "devices") == 0) return true;
return false;
}
bool procfs_is_dir(void *fs_private, const char *path) {
if (path[0] == '/') path++;
if (path[0] == '\0') return true;
if (path[0] >= '0' && path[0] <= '9') {
int i = 0;
while (path[i] && path[i] != '/') i++;
if (path[i] == '\0') return true;
return false;
}
return false;
}
vfs_fs_ops_t procfs_ops = {
.open = procfs_open,
.close = procfs_close,
.read = procfs_read,
.write = procfs_write,
.readdir = procfs_readdir,
.exists = procfs_exists,
.is_dir = procfs_is_dir
};
vfs_fs_ops_t* procfs_get_ops(void) {
return &procfs_ops;
}

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

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

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

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

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

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

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

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

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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 KiB

4
src/library/bsh/boot.bsh Normal file
View File

@@ -0,0 +1,4 @@
# BoredShell boot script
# Runs once on boot for the first shell.
# Example:
# echo "Boot script executed"

35
src/library/bsh/bshrc Normal file
View File

@@ -0,0 +1,35 @@
# BoredShell rc file
# Location: /Library/bsh/bshrc
#
# Format (initial): key=value
# Lines starting with # are comments.
#
# PATH controls command lookup order (colon-separated).
PATH=/bin:/root/Apps
# Scripts to run automatically.
# STARTUP runs for interactive shells.
# BOOT_SCRIPT runs once on boot (if enabled by the shell).
STARTUP=/Library/bsh/startup.bsh
BOOT_SCRIPT=/Library/bsh/boot.bsh
# Prompt templates.
# Tokens:
# %n = username
# %h = hostname
# %~ = cwd ("~" for /root)
# %T = time (HH:MM)
PROMPT_LEFT=%n@%h:%~$
PROMPT_RIGHT=%T
# History settings.
HISTORY_FILE=/Library/bsh/history
HISTORY_SIZE=200
# Feature toggles.
GLOB=true
COMPLETE=true
SUGGEST=true
# Example aliases (future support).
# alias ll=ls -la

View File

@@ -0,0 +1,5 @@
# BoredShell startup script
# Runs for interactive shells.
# Example:
# echo "Welcome to BoredShell"
sysfetch

View File

@@ -479,7 +479,7 @@ void memory_validate(void) {
} }
if (errors == 0) { if (errors == 0) {
cmd_write("Memory validation: OK\n"); cmd_write("Memory validation: [OK]\n");
} else { } else {
cmd_write("Memory validation failed with "); cmd_write("Memory validation failed with ");
cmd_write_int(errors); cmd_write_int(errors);

View File

@@ -1133,7 +1133,6 @@ tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port,
(cpcb->remote_port == port) && (cpcb->remote_port == port) &&
ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) && ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) &&
ip_addr_cmp(&cpcb->remote_ip, ipaddr)) { ip_addr_cmp(&cpcb->remote_ip, ipaddr)) {
/* linux returns EISCONN here, but ERR_USE should be OK for us */
return ERR_USE; return ERR_USE;
} }
} }

View File

@@ -88,7 +88,7 @@ int network_init(void) {
ipv4_address_t ip; ipv4_address_t ip;
network_get_ipv4_address(&ip); network_get_ipv4_address(&ip);
extern void serial_write(const char *str); extern void serial_write(const char *str);
serial_write("[NETWORK] IP Assigned: "); serial_write("[NET] IP Assigned: ");
char buf[32]; char buf[32];
k_itoa(ip.bytes[0], buf); serial_write(buf); serial_write("."); k_itoa(ip.bytes[0], buf); serial_write(buf); serial_write(".");
k_itoa(ip.bytes[1], buf); serial_write(buf); serial_write("."); k_itoa(ip.bytes[1], buf); serial_write(buf); serial_write(".");
@@ -96,7 +96,7 @@ int network_init(void) {
k_itoa(ip.bytes[3], buf); serial_write(buf); serial_write("\n"); k_itoa(ip.bytes[3], buf); serial_write(buf); serial_write("\n");
} else { } else {
extern void serial_write(const char *str); extern void serial_write(const char *str);
serial_write("[NETWORK] DHCP Failed during init\n"); serial_write("[NET] DHCP Failed during init\n");
} }
// Set default DNS server (1.1.1.1) // Set default DNS server (1.1.1.1)

24
src/sys/bootfs_state.h Normal file
View File

@@ -0,0 +1,24 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
#ifndef BOOTFS_STATE_H
#define BOOTFS_STATE_H
#include <stdint.h>
#include <stdbool.h>
typedef struct {
char bootloader_name[64];
char bootloader_version[64];
uint64_t boot_time_ms;
uint8_t boot_flags;
char limine_conf[2048];
int limine_conf_len;
uint32_t num_modules;
uint32_t kernel_size;
uint32_t initrd_size;
} bootfs_state_t;
extern bootfs_state_t g_bootfs_state;
void bootfs_state_init(void);
#endif

View File

@@ -4,15 +4,15 @@
#ifndef CMD_H #ifndef CMD_H
#define CMD_H #define CMD_H
#include "wm.h" #include <stdbool.h>
#include <stddef.h>
extern Window win_cmd; #include <stdint.h>
void cmd_init(void); void cmd_init(void);
void cmd_reset(void); void cmd_reset(void);
// IO Functions
void cmd_write(const char *str); void cmd_write(const char *str);
void cmd_write_len(const char *str, size_t len);
void cmd_putchar(char c); void cmd_putchar(char c);
void cmd_write_int(int n); void cmd_write_int(int n);
void cmd_write_hex(uint64_t n); void cmd_write_hex(uint64_t n);
@@ -22,10 +22,9 @@ void cmd_screen_clear(void);
void cmd_increment_msg_count(void); void cmd_increment_msg_count(void);
void cmd_reset_msg_count(void); void cmd_reset_msg_count(void);
void cmd_handle_resize(Window *win, int w, int h);
void cmd_handle_click(Window *win, int x, int y);
uint32_t cmd_get_config_value(const char *key); uint32_t cmd_get_config_value(const char *key);
void cmd_set_current_color(uint32_t color); void cmd_set_current_color(uint32_t color);
void cmd_set_raw_mode(bool enabled);
void cmd_process_finished(void);
#endif #endif

76
src/sys/cmd_stub.c Normal file
View File

@@ -0,0 +1,76 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "cmd.h"
#include "core/kutils.h"
extern void serial_write(const char *str);
static void serial_write_char(char c) {
char buf[2] = { c, 0 };
serial_write(buf);
}
void cmd_init(void) {
}
void cmd_reset(void) {
}
void cmd_write(const char *str) {
if (!str) return;
serial_write(str);
}
void cmd_write_len(const char *str, size_t len) {
if (!str || len == 0) return;
for (size_t i = 0; i < len; i++) {
serial_write_char(str[i]);
}
}
void cmd_putchar(char c) {
serial_write_char(c);
}
void cmd_write_int(int n) {
char buf[32];
k_itoa(n, buf);
cmd_write(buf);
}
void cmd_write_hex(uint64_t n) {
char buf[17];
k_itoa_hex(n, buf);
cmd_write("0x");
cmd_write(buf);
}
int cmd_get_cursor_col(void) {
return 0;
}
void cmd_screen_clear(void) {
}
void cmd_increment_msg_count(void) {
}
void cmd_reset_msg_count(void) {
}
uint32_t cmd_get_config_value(const char *key) {
(void)key;
return 0;
}
void cmd_set_current_color(uint32_t color) {
(void)color;
}
void cmd_set_raw_mode(bool enabled) {
(void)enabled;
}
void cmd_process_finished(void) {
}

477
src/sys/file_index.c Normal file
View File

@@ -0,0 +1,477 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "file_index.h"
#include "vfs.h"
#include "memory_manager.h"
#include "spinlock.h"
#include <stddef.h>
static file_index_t g_file_index = {0};
static spinlock_t g_index_lock = SPINLOCK_INIT;
static bool g_index_valid = false;
static int str_len(const char *s) {
int n = 0;
while (s[n]) n++;
return n;
}
static void str_copy(char *d, const char *s) {
while ((*d++ = *s++));
}
static int str_cmp(const char *a, const char *b) {
while (*a && *a == *b) { a++; b++; }
return (unsigned char)*a - (unsigned char)*b;
}
static void str_cat(char *d, const char *s) {
while (*d) d++;
str_copy(d, s);
}
static bool str_starts_with(const char *str, const char *prefix) {
while (*prefix) {
if (*str++ != *prefix++) return false;
}
return true;
}
static int fuzzy_match_score(const char *query, const char *filename) {
if (!query || !filename) return 0;
int score = 0;
int query_idx = 0;
int consecutive = 0;
for (int i = 0; filename[i] && query_idx < 256; i++) {
char fc = filename[i];
char qc = query[query_idx];
if (fc >= 'A' && fc <= 'Z') fc += 32;
if (qc >= 'A' && qc <= 'Z') qc += 32;
if (fc == qc) {
score += 10;
consecutive++;
if (consecutive > 1) score += 5;
query_idx++;
} else {
consecutive = 0;
}
}
if (query_idx < str_len(query)) {
return 0;
}
if (str_starts_with(filename, query)) {
score += 20;
}
return score;
}
static void index_walk_directory(const char *path, int depth) {
if (depth > 16 || g_file_index.count >= FILE_INDEX_MAX_ENTRIES) {
return;
}
if (str_starts_with(path, "/proc") ||
str_starts_with(path, "/sys") ||
str_starts_with(path, "/dev")) {
return;
}
vfs_dirent_t *entries = (vfs_dirent_t *)kmalloc(sizeof(vfs_dirent_t) * 1024);
if (!entries) {
return;
}
int count = vfs_list_directory(path, entries, 1024);
if (count <= 0 || count > 1024) {
kfree(entries);
return;
}
for (int i = 0; i < count; i++) {
if (g_file_index.count >= FILE_INDEX_MAX_ENTRIES) {
break;
}
vfs_dirent_t *entry = &entries[i];
if (!entry) continue;
if (!entry->name || entry->name[0] == 0) {
continue;
}
if (str_cmp(entry->name, ".color") == 0 ||
str_cmp(entry->name, ".origin") == 0 ||
str_cmp(entry->name, ".") == 0 ||
str_cmp(entry->name, "..") == 0) {
continue;
}
char full_path[FILE_INDEX_MAX_PATH];
int path_len = 0;
for (int j = 0; path[j] && path_len < FILE_INDEX_MAX_PATH - 1; j++) {
full_path[path_len++] = path[j];
}
if (path_len > 0 && full_path[path_len - 1] != '/' && path_len < FILE_INDEX_MAX_PATH - 1) {
full_path[path_len++] = '/';
}
for (int j = 0; entry->name[j] && path_len < FILE_INDEX_MAX_PATH - 1; j++) {
full_path[path_len++] = entry->name[j];
}
full_path[path_len] = 0;
if (path_len >= FILE_INDEX_MAX_PATH - 1) {
continue;
}
file_index_entry_t *idx_entry = &g_file_index.entries[g_file_index.count];
str_copy(idx_entry->path, full_path);
idx_entry->size = entry->size;
idx_entry->mod_time_low = entry->write_date;
idx_entry->mod_time_high = entry->write_time;
idx_entry->is_directory = entry->is_directory;
g_file_index.count++;
if (entry->is_directory && !str_starts_with(full_path, "/proc") &&
!str_starts_with(full_path, "/sys") && !str_starts_with(full_path, "/dev")) {
index_walk_directory(full_path, depth + 1);
}
}
kfree(entries);
}
void file_index_init(void) {
g_file_index.count = 0;
g_file_index.capacity = FILE_INDEX_MAX_ENTRIES;
g_index_valid = false;
}
bool file_index_build(void) {
g_file_index.count = 0;
const char *safe_paths[] = {"/root", "/bin", "/Library", "/docs", NULL};
for (int p = 0; safe_paths[p] != NULL && g_file_index.count < FILE_INDEX_MAX_ENTRIES; p++) {
index_walk_directory(safe_paths[p], 0);
}
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
g_index_valid = true;
spinlock_release_irqrestore(&g_index_lock, flags);
file_index_save();
return true;
}
bool file_index_load(void) {
vfs_file_t *file = vfs_open(FILE_INDEX_CACHE_PATH, "r");
if (!file) {
return false;
}
uint32_t version = 0;
if (vfs_read(file, &version, sizeof(version)) != sizeof(version)) {
vfs_close(file);
return false;
}
if (version != FILE_INDEX_VERSION) {
vfs_close(file);
return false;
}
int count = 0;
if (vfs_read(file, &count, sizeof(count)) != sizeof(count)) {
vfs_close(file);
return false;
}
if (count < 0 || count > FILE_INDEX_MAX_ENTRIES) {
vfs_close(file);
return false;
}
file_index_entry_t temp_entries[FILE_INDEX_MAX_ENTRIES];
for (int i = 0; i < count; i++) {
file_index_entry_t *entry = &temp_entries[i];
int bytes_read = vfs_read(file, entry->path, FILE_INDEX_MAX_PATH);
if (bytes_read != FILE_INDEX_MAX_PATH) {
vfs_close(file);
return false;
}
if (vfs_read(file, &entry->size, sizeof(entry->size)) != sizeof(entry->size)) {
vfs_close(file);
return false;
}
if (vfs_read(file, &entry->mod_time_low, sizeof(entry->mod_time_low)) != sizeof(entry->mod_time_low)) {
vfs_close(file);
return false;
}
if (vfs_read(file, &entry->mod_time_high, sizeof(entry->mod_time_high)) != sizeof(entry->mod_time_high)) {
vfs_close(file);
return false;
}
if (vfs_read(file, &entry->is_directory, sizeof(entry->is_directory)) != sizeof(entry->is_directory)) {
vfs_close(file);
return false;
}
}
vfs_close(file);
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
g_file_index.count = 0;
for (int i = 0; i < count; i++) {
g_file_index.entries[i] = temp_entries[i];
g_file_index.count++;
}
g_index_valid = true;
spinlock_release_irqrestore(&g_index_lock, flags);
return true;
}
bool file_index_save(void) {
if (!vfs_mkdir("/Library")) {
}
if (!vfs_mkdir("/Library/Index")) {
}
vfs_file_t *file = vfs_open(FILE_INDEX_CACHE_PATH, "w");
if (!file) {
return false;
}
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
int count = g_file_index.count;
file_index_entry_t entries[FILE_INDEX_MAX_ENTRIES];
for (int i = 0; i < count; i++) {
entries[i] = g_file_index.entries[i];
}
spinlock_release_irqrestore(&g_index_lock, flags);
uint32_t version = FILE_INDEX_VERSION;
if (vfs_write(file, &version, sizeof(version)) != sizeof(version)) {
vfs_close(file);
return false;
}
if (vfs_write(file, &count, sizeof(count)) != sizeof(count)) {
vfs_close(file);
return false;
}
for (int i = 0; i < count; i++) {
file_index_entry_t *entry = &entries[i];
if (vfs_write(file, entry->path, FILE_INDEX_MAX_PATH) != FILE_INDEX_MAX_PATH) {
vfs_close(file);
return false;
}
if (vfs_write(file, &entry->size, sizeof(entry->size)) != sizeof(entry->size)) {
vfs_close(file);
return false;
}
if (vfs_write(file, &entry->mod_time_low, sizeof(entry->mod_time_low)) != sizeof(entry->mod_time_low)) {
vfs_close(file);
return false;
}
if (vfs_write(file, &entry->mod_time_high, sizeof(entry->mod_time_high)) != sizeof(entry->mod_time_high)) {
vfs_close(file);
return false;
}
if (vfs_write(file, &entry->is_directory, sizeof(entry->is_directory)) != sizeof(entry->is_directory)) {
vfs_close(file);
return false;
}
}
vfs_close(file);
return true;
}
int file_index_find_fuzzy(const char *query, file_index_result_t *results, int max_results) {
if (!query || !results || max_results <= 0) {
return 0;
}
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
int result_count = 0;
for (int i = 0; i < g_file_index.count && result_count < max_results; i++) {
if (i < 0 || i >= FILE_INDEX_MAX_ENTRIES) {
break;
}
const char *path = g_file_index.entries[i].path;
if (!path || path[0] == 0) {
continue;
}
const char *filename = path;
for (int j = 0; path[j]; j++) {
if (path[j] == '/') {
filename = &path[j + 1];
}
}
if (!filename || filename[0] == 0) {
continue;
}
int score = fuzzy_match_score(query, filename);
if (score > 0) {
results[result_count].entry = g_file_index.entries[i];
results[result_count].match_score = score;
result_count++;
}
}
spinlock_release_irqrestore(&g_index_lock, flags);
for (int i = 0; i < result_count; i++) {
for (int j = i + 1; j < result_count; j++) {
if (results[j].match_score > results[i].match_score) {
file_index_result_t tmp = results[i];
results[i] = results[j];
results[j] = tmp;
}
}
}
return result_count;
}
bool file_index_add_entry(const char *path, uint32_t size, uint32_t mod_time_low,
uint32_t mod_time_high, bool is_dir) {
if (!path || g_file_index.count >= FILE_INDEX_MAX_ENTRIES) {
return false;
}
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
for (int i = 0; i < g_file_index.count; i++) {
if (str_cmp(g_file_index.entries[i].path, path) == 0) {
g_file_index.entries[i].size = size;
g_file_index.entries[i].mod_time_low = mod_time_low;
g_file_index.entries[i].mod_time_high = mod_time_high;
spinlock_release_irqrestore(&g_index_lock, flags);
return true;
}
}
file_index_entry_t *entry = &g_file_index.entries[g_file_index.count];
str_copy(entry->path, path);
entry->size = size;
entry->mod_time_low = mod_time_low;
entry->mod_time_high = mod_time_high;
entry->is_directory = is_dir;
g_file_index.count++;
spinlock_release_irqrestore(&g_index_lock, flags);
return true;
}
bool file_index_remove_entry(const char *path) {
if (!path) {
return false;
}
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
for (int i = 0; i < g_file_index.count; i++) {
if (str_cmp(g_file_index.entries[i].path, path) == 0) {
for (int j = i; j < g_file_index.count - 1; j++) {
g_file_index.entries[j] = g_file_index.entries[j + 1];
}
g_file_index.count--;
spinlock_release_irqrestore(&g_index_lock, flags);
return true;
}
}
spinlock_release_irqrestore(&g_index_lock, flags);
return false;
}
int file_index_get_entry_count(void) {
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
int count = g_file_index.count;
spinlock_release_irqrestore(&g_index_lock, flags);
return count;
}
void file_index_clear(void) {
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
g_file_index.count = 0;
g_index_valid = false;
spinlock_release_irqrestore(&g_index_lock, flags);
}
void file_index_invalidate_cache(void) {
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
g_index_valid = false;
spinlock_release_irqrestore(&g_index_lock, flags);
}
bool file_index_is_valid(void) {
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
bool valid = g_index_valid;
spinlock_release_irqrestore(&g_index_lock, flags);
return valid;
}

50
src/sys/file_index.h Normal file
View File

@@ -0,0 +1,50 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef FILE_INDEX_H
#define FILE_INDEX_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#define FILE_INDEX_MAX_ENTRIES 50000
#define FILE_INDEX_MAX_PATH 1024
#define FILE_INDEX_CACHE_PATH "/Library/Index/file_index.dat"
#define FILE_INDEX_VERSION 1
typedef struct {
char path[FILE_INDEX_MAX_PATH];
uint32_t size;
uint32_t mod_time_low;
uint32_t mod_time_high;
bool is_directory;
} file_index_entry_t;
typedef struct {
file_index_entry_t entry;
int match_score;
} file_index_result_t;
typedef struct {
file_index_entry_t entries[FILE_INDEX_MAX_ENTRIES];
int count;
int capacity;
} file_index_t;
void file_index_init(void);
bool file_index_build(void);
bool file_index_load(void);
bool file_index_save(void);
int file_index_find_fuzzy(const char *query, file_index_result_t *results, int max_results);
bool file_index_add_entry(const char *path, uint32_t size, uint32_t mod_time_low, uint32_t mod_time_high, bool is_dir);
bool file_index_remove_entry(const char *path);
int file_index_get_entry_count(void);
void file_index_clear(void);
void file_index_invalidate_cache(void);
bool file_index_is_valid(void);
#endif

View File

@@ -140,12 +140,12 @@ static void pic_remap(void) {
static void pit_setup(void) { static void pit_setup(void) {
uint16_t divisor = 1193182 / 60; // ~60Hz uint16_t divisor = 1193182 / 60; // ~60Hz
// Send command byte // Mode 2: Rate Generator (more appropriate for periodic interrupts)
outb(0x43, 0x36); // Channel 0, lobyte/hibyte, mode 3 (square wave), binary outb(0x43, 0x34); io_wait(); // Channel 0, lobyte/hibyte, mode 2, binary
// Send divisor // Send divisor
outb(0x40, divisor & 0xFF); outb(0x40, divisor & 0xFF); io_wait();
outb(0x40, (divisor >> 8) & 0xFF); outb(0x40, (divisor >> 8) & 0xFF); io_wait();
} }
void idt_init(void) { void idt_init(void) {

View File

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

View File

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

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

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

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

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

View File

@@ -20,30 +20,31 @@ extern void serial_write(const char *str);
#define MAX_PROCESSES 16 #define MAX_PROCESSES 16
#define MAX_CPUS_SCHED 32 #define MAX_CPUS_SCHED 32
process_t processes[MAX_PROCESSES] __attribute__((aligned(16))); process_t processes[MAX_PROCESSES] __attribute__((aligned(16)));
int process_count = 0;
static process_t* current_process[MAX_CPUS_SCHED] = {0}; // Per-CPU static process_t* current_process[MAX_CPUS_SCHED] = {0}; // Per-CPU
static uint32_t next_pid = 0; static uint32_t next_pid = 0;
static void *free_kernel_stack_later = NULL; static void *free_kernel_stack_later[MAX_CPUS_SCHED] = {0};
static uint64_t free_pml4_later = 0; static uint64_t free_pml4_later[MAX_CPUS_SCHED] = {0};
static spinlock_t runqueue_lock = SPINLOCK_INIT; static spinlock_t runqueue_lock = SPINLOCK_INIT;
static uint32_t next_cpu_assign = 1; // Round-robin CPU assignment (start from CPU 1) static uint32_t next_cpu_assign = 1;
static void process_cleanup_inner(process_t *proc);
void process_init(void) { void process_init(void) {
for (int i = 0; i < MAX_PROCESSES; i++) { for (int i = 0; i < MAX_PROCESSES; i++) {
processes[i].pid = 0xFFFFFFFF; processes[i].pid = 0xFFFFFFFF;
} }
// Current kernel execution is PID 0 process_t *kernel_proc = &processes[0];
process_t *kernel_proc = &processes[process_count++];
kernel_proc->pid = next_pid++; kernel_proc->pid = next_pid++;
kernel_proc->is_user = false; kernel_proc->is_user = false;
kernel_proc->is_idle = true;
kernel_proc->tty_id = -1;
kernel_proc->kill_pending = false;
// We don't have its RSP or PML4 yet, but it's already running.
// The timer interrupt will naturally capture its context on the first tick!
kernel_proc->pml4_phys = paging_get_pml4_phys(); kernel_proc->pml4_phys = paging_get_pml4_phys();
kernel_proc->kernel_stack = 0; kernel_proc->kernel_stack = 0;
// Initialize FPU/SSE state for kernel (first interrupt will capture it on stack)
kernel_proc->fpu_initialized = true; kernel_proc->fpu_initialized = true;
for (int i = 0; i < MAX_PROCESS_FDS; i++) kernel_proc->fds[i] = NULL; for (int i = 0; i < MAX_PROCESS_FDS; i++) kernel_proc->fds[i] = NULL;
@@ -55,20 +56,41 @@ void process_init(void) {
kernel_proc->next = kernel_proc; // Circular linked list kernel_proc->next = kernel_proc; // Circular linked list
kernel_proc->cpu_affinity = 0; // Kernel always on BSP kernel_proc->cpu_affinity = 0; // Kernel always on BSP
mem_memset(kernel_proc->cwd, 0, 1024);
kernel_proc->cwd[0] = '/';
current_process[0] = kernel_proc; current_process[0] = kernel_proc;
} }
process_t* process_create(void (*entry_point)(void), bool is_user) { process_t* process_create(void (*entry_point)(void), bool is_user) {
uint64_t rflags = spinlock_acquire_irqsave(&runqueue_lock); uint64_t rflags = spinlock_acquire_irqsave(&runqueue_lock);
if (process_count >= MAX_PROCESSES) { process_t *new_proc = NULL;
for (int i = 0; i < MAX_PROCESSES; i++) {
if (processes[i].pid == 0xFFFFFFFF) {
new_proc = &processes[i];
break;
}
}
if (!new_proc) {
spinlock_release_irqrestore(&runqueue_lock, rflags); spinlock_release_irqrestore(&runqueue_lock, rflags);
return NULL; return NULL;
} }
process_t *new_proc = &processes[process_count++];
new_proc->pid = next_pid++; new_proc->pid = next_pid++;
new_proc->is_user = is_user; new_proc->is_user = is_user;
new_proc->tty_id = -1;
new_proc->kill_pending = false;
process_t *parent = process_get_current();
if (parent) {
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy(new_proc->cwd, parent->cwd, 1024);
new_proc->tty_id = parent->tty_id;
} else {
mem_memset(new_proc->cwd, 0, 1024);
new_proc->cwd[0] = '/';
}
// 1. Setup Page Table // 1. Setup Page Table
if (is_user) { if (is_user) {
@@ -157,15 +179,14 @@ process_t* process_create(void (*entry_point)(void), bool is_user) {
return new_proc; return new_proc;
} }
process_t* process_create_elf(const char* filepath, const char* args_str) { process_t* process_create_elf(const char* filepath, const char* args_str, bool terminal_proc, int tty_id) {
uint64_t rflags = spinlock_acquire_irqsave(&runqueue_lock); uint64_t rflags = spinlock_acquire_irqsave(&runqueue_lock);
process_t *new_proc = NULL; process_t *new_proc = NULL;
// Find an available slot // Find an available slot
for (int i = 0; i < MAX_PROCESSES; i++) { for (int i = 0; i < MAX_PROCESSES; i++) {
if (processes[i].pid == 0xFFFFFFFF || i >= process_count) { if (processes[i].pid == 0xFFFFFFFF) {
new_proc = &processes[i]; new_proc = &processes[i];
if (i >= process_count) process_count = i + 1;
break; break;
} }
} }
@@ -189,16 +210,27 @@ process_t* process_create_elf(const char* filepath, const char* args_str) {
new_proc->ui_window = NULL; new_proc->ui_window = NULL;
new_proc->heap_start = 0x20000000; // 512MB mark new_proc->heap_start = 0x20000000; // 512MB mark
new_proc->heap_end = 0x20000000; new_proc->heap_end = 0x20000000;
new_proc->is_terminal_proc = false; new_proc->is_terminal_proc = terminal_proc;
new_proc->tty_id = tty_id;
new_proc->kill_pending = false;
process_t *parent = process_get_current();
if (parent) {
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy(new_proc->cwd, parent->cwd, 1024);
} else {
extern void mem_memset(void *dest, int val, size_t len);
mem_memset(new_proc->cwd, 0, 1024);
new_proc->cwd[0] = '/';
}
// 2. Load ELF executable // 2. Load ELF executable
size_t elf_load_size = 0; size_t elf_load_size = 0;
uint64_t entry_point = elf_load(filepath, new_proc->pml4_phys, &elf_load_size); uint64_t entry_point = elf_load(filepath, new_proc->pml4_phys, &elf_load_size);
if (entry_point == 0) { if (entry_point == 0) {
serial_write("[PROCESS] Failed to load ELF: "); serial_write("[PROC] Failed to load ELF: ");
serial_write(filepath); serial_write(filepath);
serial_write("\n"); serial_write("\n");
// We technically leak the page table here, but let's ignore cleanup for now
return NULL; return NULL;
} }
@@ -354,7 +386,7 @@ process_t* process_create_elf(const char* filepath, const char* args_str) {
current_process[0]->next = new_proc; current_process[0]->next = new_proc;
spinlock_release_irqrestore(&runqueue_lock, rflags); spinlock_release_irqrestore(&runqueue_lock, rflags);
serial_write("[PROCESS] Spawned ELF Executable: "); serial_write("[PROC] Exec: ");
serial_write(filepath); serial_write(filepath);
serial_write("\n"); serial_write("\n");
return new_proc; return new_proc;
@@ -376,25 +408,102 @@ process_t* process_get_current(void) {
} }
uint64_t process_schedule(uint64_t current_rsp) { uint64_t process_schedule(uint64_t current_rsp) {
if (free_kernel_stack_later) { uint32_t my_cpu = smp_this_cpu_id();
kfree(free_kernel_stack_later); uint64_t rflags = spinlock_acquire_irqsave(&runqueue_lock);
free_kernel_stack_later = NULL;
void *cleanup_stack = NULL;
uint64_t cleanup_pml4 = 0;
if (free_kernel_stack_later[my_cpu]) {
cleanup_stack = free_kernel_stack_later[my_cpu];
free_kernel_stack_later[my_cpu] = NULL;
} }
if (free_pml4_later) { if (free_pml4_later[my_cpu]) {
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys); cleanup_pml4 = free_pml4_later[my_cpu];
paging_destroy_user_pml4_phys(free_pml4_later); free_pml4_later[my_cpu] = 0;
free_pml4_later = 0;
} }
uint32_t my_cpu = smp_this_cpu_id();
process_t *cur = current_process[my_cpu]; process_t *cur = current_process[my_cpu];
if (!cur || !cur->next || cur == cur->next) if (!cur || !cur->next || cur == cur->next) {
spinlock_release_irqrestore(&runqueue_lock, rflags);
// Perform cleanup outside the lock
if (cleanup_stack) kfree(cleanup_stack);
if (cleanup_pml4) {
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
paging_destroy_user_pml4_phys(cleanup_pml4);
}
return current_rsp; return current_rsp;
}
// Save context // Save context
cur->rsp = current_rsp; cur->rsp = current_rsp;
if (cur->kill_pending && cur->pid != 0xFFFFFFFF && cur->pid != 0) {
process_cleanup_inner(cur);
process_t *prev = cur;
while (prev->next != cur) {
prev = prev->next;
}
if (prev != cur) {
prev->next = cur->next;
process_t *next_proc = cur->next;
while (next_proc != cur) {
if (next_proc->cpu_affinity == my_cpu && next_proc->pid != 0xFFFFFFFF && !next_proc->kill_pending) break;
next_proc = next_proc->next;
}
if (next_proc == cur || next_proc->cpu_affinity != my_cpu) {
for (int i = 0; i < MAX_PROCESSES; i++) {
if (processes[i].pid == 0 || (processes[i].cpu_affinity == my_cpu && processes[i].is_user == false)) {
next_proc = &processes[i];
break;
}
}
}
current_process[my_cpu] = next_proc;
cur->pid = 0xFFFFFFFF;
cur->cpu_affinity = 0xFFFFFFFF;
cur->ui_window = NULL;
cur->is_terminal_proc = false;
cur->kill_pending = false;
free_kernel_stack_later[my_cpu] = cur->kernel_stack_alloc;
cur->kernel_stack_alloc = NULL;
free_pml4_later[my_cpu] = cur->pml4_phys;
cur->pml4_phys = 0;
if (current_process[my_cpu]->is_user && current_process[my_cpu]->kernel_stack) {
tss_set_stack_cpu(my_cpu, current_process[my_cpu]->kernel_stack);
cpu_state_t *cpu_state = smp_get_cpu(my_cpu);
if (cpu_state) {
cpu_state->kernel_syscall_stack = current_process[my_cpu]->kernel_stack;
}
}
paging_switch_directory(current_process[my_cpu]->pml4_phys);
current_process[my_cpu]->ticks++;
uint64_t next_rsp = current_process[my_cpu]->rsp;
spinlock_release_irqrestore(&runqueue_lock, rflags);
if (cleanup_stack) kfree(cleanup_stack);
if (cleanup_pml4) {
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
paging_destroy_user_pml4_phys(cleanup_pml4);
}
return next_rsp;
}
}
// Switch to next ready process assigned to this CPU // Switch to next ready process assigned to this CPU
extern uint32_t wm_get_ticks(void); extern uint32_t wm_get_ticks(void);
uint32_t now = wm_get_ticks(); uint32_t now = wm_get_ticks();
@@ -404,7 +513,7 @@ uint64_t process_schedule(uint64_t current_rsp) {
while (next_proc != start) { while (next_proc != start) {
// Only consider processes assigned to our CPU and not terminated // Only consider processes assigned to our CPU and not terminated
if (next_proc->cpu_affinity == my_cpu && next_proc->pid != 0xFFFFFFFF) { if (next_proc->cpu_affinity == my_cpu && next_proc->pid != 0xFFFFFFFF && !next_proc->kill_pending) {
if (next_proc->pid == 0 || next_proc->sleep_until == 0 || next_proc->sleep_until <= now) { if (next_proc->pid == 0 || next_proc->sleep_until == 0 || next_proc->sleep_until <= now) {
break; break;
} }
@@ -412,11 +521,8 @@ uint64_t process_schedule(uint64_t current_rsp) {
next_proc = next_proc->next; next_proc = next_proc->next;
} }
// If we didn't find a ready process for our CPU, stay on current (unless we are terminated)
if (next_proc->cpu_affinity != my_cpu || next_proc->pid == 0xFFFFFFFF) { if (next_proc->cpu_affinity != my_cpu || next_proc->pid == 0xFFFFFFFF) {
// Fallback to idle if current is terminated
if (cur && cur->pid == 0xFFFFFFFF) { if (cur && cur->pid == 0xFFFFFFFF) {
// Find the idle process for this CPU
for (int i = 0; i < MAX_PROCESSES; i++) { for (int i = 0; i < MAX_PROCESSES; i++) {
if (processes[i].pid == 0 || (processes[i].cpu_affinity == my_cpu && processes[i].is_user == false)) { if (processes[i].pid == 0 || (processes[i].cpu_affinity == my_cpu && processes[i].is_user == false)) {
next_proc = &processes[i]; next_proc = &processes[i];
@@ -424,18 +530,25 @@ uint64_t process_schedule(uint64_t current_rsp) {
} }
} }
} else { } else {
spinlock_release_irqrestore(&runqueue_lock, rflags);
if (cleanup_stack) kfree(cleanup_stack);
if (cleanup_pml4) {
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
paging_destroy_user_pml4_phys(cleanup_pml4);
}
return current_rsp; return current_rsp;
} }
} }
current_process[my_cpu] = next_proc; current_process[my_cpu] = next_proc;
// Update Kernel Stack for User Mode interrupts and System Calls
if (current_process[my_cpu]->is_user && current_process[my_cpu]->kernel_stack) { if (current_process[my_cpu]->is_user && current_process[my_cpu]->kernel_stack) {
tss_set_stack_cpu(my_cpu, current_process[my_cpu]->kernel_stack); tss_set_stack_cpu(my_cpu, current_process[my_cpu]->kernel_stack);
if (my_cpu == 0) { cpu_state_t *cpu_state = smp_get_cpu(my_cpu);
extern uint64_t kernel_syscall_stack; if (cpu_state) {
kernel_syscall_stack = current_process[my_cpu]->kernel_stack; cpu_state->kernel_syscall_stack = current_process[my_cpu]->kernel_stack;
} }
} }
@@ -443,8 +556,16 @@ uint64_t process_schedule(uint64_t current_rsp) {
paging_switch_directory(current_process[my_cpu]->pml4_phys); paging_switch_directory(current_process[my_cpu]->pml4_phys);
current_process[my_cpu]->ticks++; current_process[my_cpu]->ticks++;
uint64_t next_rsp = current_process[my_cpu]->rsp;
return current_process[my_cpu]->rsp; spinlock_release_irqrestore(&runqueue_lock, rflags);
if (cleanup_stack) kfree(cleanup_stack);
if (cleanup_pml4) {
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
paging_destroy_user_pml4_phys(cleanup_pml4);
}
return next_rsp;
} }
process_t* process_get_by_pid(uint32_t pid) { process_t* process_get_by_pid(uint32_t pid) {
@@ -454,12 +575,20 @@ process_t* process_get_by_pid(uint32_t pid) {
return NULL; return NULL;
} }
void process_kill_by_tty(int tty_id) {
if (tty_id < 0) return;
for (int i = 0; i < MAX_PROCESSES; i++) {
if (processes[i].pid != 0xFFFFFFFF && processes[i].pid != 0 && processes[i].tty_id == tty_id) {
process_terminate(&processes[i]);
}
}
}
static void process_cleanup_inner(process_t *proc) { static void process_cleanup_inner(process_t *proc) {
if (!proc || proc->pid == 0xFFFFFFFF) return; if (!proc || proc->pid == 0xFFFFFFFF) return;
// 1. Cleanup side effects // 1. Cleanup side effects
extern Window win_cmd; if (proc->ui_window) {
if (proc->ui_window && (proc->ui_window != &win_cmd)) {
wm_remove_window((Window *)proc->ui_window); wm_remove_window((Window *)proc->ui_window);
proc->ui_window = NULL; proc->ui_window = NULL;
} }
@@ -486,6 +615,14 @@ static void process_cleanup_inner(process_t *proc) {
void process_terminate(process_t *to_delete) { void process_terminate(process_t *to_delete) {
if (!to_delete || to_delete->pid == 0xFFFFFFFF || to_delete->pid == 0) return; if (!to_delete || to_delete->pid == 0xFFFFFFFF || to_delete->pid == 0) return;
uint32_t cpu_count = smp_cpu_count();
for (uint32_t c = 0; c < cpu_count && c < MAX_CPUS_SCHED; c++) {
if (current_process[c] == to_delete) {
to_delete->kill_pending = true;
return;
}
}
uint64_t rflags = spinlock_acquire_irqsave(&runqueue_lock); uint64_t rflags = spinlock_acquire_irqsave(&runqueue_lock);
process_cleanup_inner(to_delete); process_cleanup_inner(to_delete);
@@ -506,7 +643,6 @@ void process_terminate(process_t *to_delete) {
prev->next = to_delete->next; prev->next = to_delete->next;
// Update per-CPU current_process if this was the current on any CPU // Update per-CPU current_process if this was the current on any CPU
uint32_t cpu_count = smp_cpu_count();
for (uint32_t c = 0; c < cpu_count && c < MAX_CPUS_SCHED; c++) { for (uint32_t c = 0; c < cpu_count && c < MAX_CPUS_SCHED; c++) {
if (current_process[c] == to_delete) { if (current_process[c] == to_delete) {
process_t *np = to_delete->next; process_t *np = to_delete->next;
@@ -528,6 +664,7 @@ void process_terminate(process_t *to_delete) {
// Mark slot as free // Mark slot as free
to_delete->pid = 0xFFFFFFFF; to_delete->pid = 0xFFFFFFFF;
to_delete->cpu_affinity = 0xFFFFFFFF; to_delete->cpu_affinity = 0xFFFFFFFF;
to_delete->kill_pending = false;
if (to_delete->user_stack_alloc) kfree(to_delete->user_stack_alloc); if (to_delete->user_stack_alloc) kfree(to_delete->user_stack_alloc);
// Defer kernel stack until we switch away from it // Defer kernel stack until we switch away from it
@@ -596,29 +733,22 @@ uint64_t process_terminate_current(void) {
to_delete->cpu_affinity = 0xFFFFFFFF; to_delete->cpu_affinity = 0xFFFFFFFF;
to_delete->ui_window = NULL; to_delete->ui_window = NULL;
to_delete->is_terminal_proc = false; to_delete->is_terminal_proc = false;
to_delete->kill_pending = false;
// 4. Load context for the NEXT process // 4. Load context for the NEXT process
if (current_process[my_cpu]->is_user && current_process[my_cpu]->kernel_stack) { if (current_process[my_cpu]->is_user && current_process[my_cpu]->kernel_stack) {
tss_set_stack_cpu(my_cpu, current_process[my_cpu]->kernel_stack); tss_set_stack_cpu(my_cpu, current_process[my_cpu]->kernel_stack);
if (my_cpu == 0) { cpu_state_t *cpu_state = smp_get_cpu(my_cpu);
extern uint64_t kernel_syscall_stack; if (cpu_state) {
kernel_syscall_stack = current_process[my_cpu]->kernel_stack; cpu_state->kernel_syscall_stack = current_process[my_cpu]->kernel_stack;
} }
} }
paging_switch_directory(current_process[my_cpu]->pml4_phys); paging_switch_directory(current_process[my_cpu]->pml4_phys);
// 5. Free memory free_kernel_stack_later[my_cpu] = to_delete->kernel_stack_alloc;
if (to_delete->user_stack_alloc) kfree(to_delete->user_stack_alloc);
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
if (to_delete->pml4_phys && to_delete->is_user) {
paging_destroy_user_pml4_phys(to_delete->pml4_phys);
}
to_delete->user_stack_alloc = NULL;
free_kernel_stack_later = to_delete->kernel_stack_alloc;
to_delete->kernel_stack_alloc = NULL; to_delete->kernel_stack_alloc = NULL;
free_pml4_later[my_cpu] = to_delete->pml4_phys;
to_delete->pml4_phys = 0; to_delete->pml4_phys = 0;
uint64_t next_rsp = current_process[my_cpu]->rsp; uint64_t next_rsp = current_process[my_cpu]->rsp;
@@ -666,4 +796,3 @@ process_t* process_get_by_ui_window(void *win) {
} }
return NULL; return NULL;
} }

View File

@@ -43,6 +43,8 @@ typedef struct process {
void *user_stack_alloc; void *user_stack_alloc;
bool is_terminal_proc; bool is_terminal_proc;
int tty_id;
bool kill_pending;
struct process *next; struct process *next;
@@ -52,7 +54,9 @@ typedef struct process {
uint64_t ticks; uint64_t ticks;
uint64_t sleep_until; uint64_t sleep_until;
size_t used_memory; size_t used_memory;
uint32_t cpu_affinity; // Which CPU this process runs on (0 = BSP) uint32_t cpu_affinity;
bool is_idle;
char cwd[1024];
} __attribute__((aligned(16))) process_t; } __attribute__((aligned(16))) process_t;
typedef struct { typedef struct {
@@ -60,11 +64,12 @@ typedef struct {
char name[64]; char name[64];
uint64_t ticks; uint64_t ticks;
size_t used_memory; size_t used_memory;
bool is_idle;
} ProcessInfo; } ProcessInfo;
void process_init(void); void process_init(void);
process_t* process_create(void (*entry_point)(void), bool is_user); process_t* process_create(void (*entry_point)(void), bool is_user);
process_t* process_create_elf(const char* filepath, const char* args_str); process_t* process_create_elf(const char* filepath, const char* args_str, bool terminal_proc, int tty_id);
process_t* process_get_current(void); process_t* process_get_current(void);
void process_set_current_for_cpu(uint32_t cpu_id, process_t* p); void process_set_current_for_cpu(uint32_t cpu_id, process_t* p);
process_t* process_get_current_for_cpu(uint32_t cpu_id); process_t* process_get_current_for_cpu(uint32_t cpu_id);
@@ -72,8 +77,9 @@ uint64_t process_schedule(uint64_t current_rsp);
uint64_t process_terminate_current(void); uint64_t process_terminate_current(void);
void process_terminate(process_t *proc); void process_terminate(process_t *proc);
process_t* process_get_by_pid(uint32_t pid); process_t* process_get_by_pid(uint32_t pid);
void process_kill_by_tty(int tty_id);
// SMP: IPI handler for AP scheduling (called from ISR) // SMP: IPI handler for AP scheduling
uint64_t sched_ipi_handler(registers_t *regs); uint64_t sched_ipi_handler(registers_t *regs);
void process_push_gui_event(process_t *proc, gui_event_t *ev); void process_push_gui_event(process_t *proc, gui_event_t *ev);

View File

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

View File

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

View File

@@ -8,6 +8,7 @@
#include "process.h" #include "process.h"
#include "wm.h" #include "wm.h"
#include "fat32.h" #include "fat32.h"
#include "vfs.h"
#include "paging.h" #include "paging.h"
#include "work_queue.h" #include "work_queue.h"
#include "smp.h" #include "smp.h"
@@ -18,11 +19,16 @@
#include "network.h" #include "network.h"
#include "icmp.h" #include "icmp.h"
#include "cmd.h" #include "cmd.h"
#include "tty.h"
#include "font_manager.h" #include "font_manager.h"
#include "graphics.h" #include "graphics.h"
extern bool ps2_ctrl_pressed; extern bool ps2_ctrl_pressed;
#define SPAWN_FLAG_TERMINAL 0x1
#define SPAWN_FLAG_INHERIT_TTY 0x2
#define SPAWN_FLAG_TTY_ID 0x4
// Read MSR // Read MSR
static inline uint64_t rdmsr(uint32_t msr) { static inline uint64_t rdmsr(uint32_t msr) {
uint32_t low, high; uint32_t low, high;
@@ -76,6 +82,14 @@ static void smp_user_wrapper(void *arg) {
} }
void syscall_init(void) { void syscall_init(void) {
uint64_t efer = rdmsr(MSR_EFER);
efer |= 1;
wrmsr(MSR_EFER, efer);
uint64_t star = ((uint64_t)0x001B << 48) | ((uint64_t)0x0008 << 32);
wrmsr(MSR_STAR, star);
extern void syscall_entry(void);
wrmsr(MSR_LSTAR, (uint64_t)syscall_entry);
wrmsr(MSR_FMASK, 0x200);
} }
static void user_window_close(Window *win) { static void user_window_close(Window *win) {
@@ -171,6 +185,12 @@ static void user_window_resize(Window *win, int w, int h) {
extern void mem_memset(void *dest, int val, size_t len); extern void mem_memset(void *dest, int val, size_t len);
mem_memset(win->pixels, 0, w * h * sizeof(uint32_t)); mem_memset(win->pixels, 0, w * h * sizeof(uint32_t));
} }
process_t *proc = process_get_by_ui_window(win);
if (proc) {
gui_event_t ev = { .type = GUI_EVENT_RESIZE, .arg1 = w, .arg2 = h };
process_push_gui_event(proc, &ev);
}
} }
@@ -187,19 +207,36 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
if (syscall_num == 1) { // SYS_WRITE if (syscall_num == 1) { // SYS_WRITE
extern void cmd_write_len(const char *str, size_t len); extern void cmd_write_len(const char *str, size_t len);
cmd_write_len((const char*)arg2, (size_t)arg3); process_t *proc = process_get_current();
const char *buf = (const char*)arg2;
size_t len = (size_t)arg3;
if (!proc || !proc->is_user) {
cmd_write_len(buf, len);
return len;
}
if (proc->is_terminal_proc) {
if (proc->tty_id >= 0) {
tty_write_output(proc->tty_id, buf, len);
return len;
}
cmd_write_len(buf, len);
return len;
}
return len;
} else if (syscall_num == 3) { // SYS_GUI } else if (syscall_num == 3) { // SYS_GUI
int cmd = (int)arg1; int cmd = (int)arg1;
process_t *proc = process_get_current(); process_t *proc = process_get_current();
if (cmd == GUI_CMD_WINDOW_CREATE) { if (cmd == GUI_CMD_WINDOW_CREATE) {
extern void serial_write(const char *str); extern void serial_write(const char *str);
serial_write("Kernel: GUI_CMD_WINDOW_CREATE\n");
const char *title = (const char *)arg2; const char *title = (const char *)arg2;
serial_write("[WM] CreateWindow: ");
serial_write(title ? title : "Unknown");
serial_write("\n");
uint64_t *u_params = (uint64_t *)arg3; uint64_t *u_params = (uint64_t *)arg3;
if (!u_params) { if (!u_params) {
serial_write("Kernel: Error - params is NULL\n"); serial_write("[WM] Error - params is NULL\n");
return 0; return 0;
} }
@@ -207,14 +244,14 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
uint64_t params[4]; uint64_t params[4];
for (int i = 0; i < 4; i++) params[i] = u_params[i]; for (int i = 0; i < 4; i++) params[i] = u_params[i];
serial_write("Kernel: Window params copied.\n"); // params verified
Window *win = kmalloc(sizeof(Window)); Window *win = kmalloc(sizeof(Window));
if (!win) { if (!win) {
serial_write("Kernel: Error - kmalloc failed for Window\n"); serial_write("[WM] Error - kmalloc failed for Window\n");
return 0; return 0;
} }
serial_write("Kernel: Window allocated.\n"); // win allocated
extern void mem_memset(void *dest, int val, size_t len); extern void mem_memset(void *dest, int val, size_t len);
mem_memset(win, 0, sizeof(Window)); mem_memset(win, 0, sizeof(Window));
@@ -231,11 +268,8 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
kernel_title[i] = title[i]; kernel_title[i] = title[i];
} }
kernel_title[title_len] = '\0'; kernel_title[title_len] = '\0';
serial_write("Kernel: Title copied: ");
serial_write(kernel_title);
serial_write("\n");
} else { } else {
serial_write("Kernel: Warning - kernel_title kmalloc failed\n"); serial_write("[WM] Warning: kernel_title kmalloc failed\n");
} }
// Basic initialization // Basic initialization
@@ -245,7 +279,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
win->w = (int)params[2]; win->w = (int)params[2];
win->h = (int)params[3]; win->h = (int)params[3];
serial_write("Kernel: Init win dims.\n"); // dims ready
// Sanity checks for dimensions // Sanity checks for dimensions
if (win->w <= 0 || win->w > 4096) win->w = 400; if (win->w <= 0 || win->w > 4096) win->w = 400;
@@ -259,7 +293,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
win->font = NULL; win->font = NULL;
win->lock = SPINLOCK_INIT; win->lock = SPINLOCK_INIT;
serial_write("Kernel: Dims initialized.\n"); // ready
size_t pixel_size = 0; size_t pixel_size = 0;
// Safe allocation // Safe allocation
@@ -274,7 +308,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
win->comp_pixels = kmalloc(pixel_size); win->comp_pixels = kmalloc(pixel_size);
} }
serial_write("Kernel: Buffers allocated.\n"); // buffs ok
if (win->pixels) { if (win->pixels) {
extern void mem_memset(void *dest, int val, size_t len); extern void mem_memset(void *dest, int val, size_t len);
@@ -285,7 +319,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
mem_memset(win->comp_pixels, 0, pixel_size); mem_memset(win->comp_pixels, 0, pixel_size);
} }
serial_write("Kernel: Buffers cleared.\n"); serial_write("[WM] Buffers ready\n");
// Set callbacks // Set callbacks
win->paint = user_window_paint; win->paint = user_window_paint;
@@ -298,6 +332,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
proc->ui_window = win; proc->ui_window = win;
wm_add_window(win); wm_add_window(win);
wm_mark_dirty(0, 0, get_screen_width(), 30);
return (uint64_t)win; return (uint64_t)win;
} else if (cmd == GUI_CMD_DRAW_RECT) { } else if (cmd == GUI_CMD_DRAW_RECT) {
@@ -406,10 +441,18 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int baseline = uy + font_manager_get_font_ascent_scaled(font, font->pixel_height) - 2; int baseline = uy + font_manager_get_font_ascent_scaled(font, font->pixel_height) - 2;
int cur_x = ux; int cur_x = ux;
const char *s = kernel_str; const char *s = kernel_str;
int start_x = cur_x;
while (*s) { while (*s) {
uint32_t codepoint = utf8_decode(&s); uint32_t codepoint = utf8_decode(&s);
font_manager_render_char_scaled(font, cur_x, baseline, codepoint, color, font->pixel_height, put_pixel); if (codepoint == '\n') {
cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, font->pixel_height); cur_x = start_x;
baseline += font_manager_get_font_line_height_scaled(font, font->pixel_height);
} else if (codepoint == '\t') {
cur_x += font_manager_get_codepoint_width_scaled(font, ' ', font->pixel_height) * 4;
} else {
font_manager_render_char_scaled(font, cur_x, baseline, codepoint, color, font->pixel_height, put_pixel);
cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, font->pixel_height);
}
} }
} else { } else {
draw_string(ux, uy, kernel_str, color); draw_string(ux, uy, kernel_str, color);
@@ -421,10 +464,18 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int baseline = win->y + uy + font_manager_get_font_ascent_scaled(font, font->pixel_height) - 2; int baseline = win->y + uy + font_manager_get_font_ascent_scaled(font, font->pixel_height) - 2;
int cur_x = win->x + ux; int cur_x = win->x + ux;
const char *s = kernel_str; const char *s = kernel_str;
int start_x = cur_x;
while (*s) { while (*s) {
uint32_t codepoint = utf8_decode(&s); uint32_t codepoint = utf8_decode(&s);
font_manager_render_char_scaled(font, cur_x, baseline, codepoint, color, font->pixel_height, put_pixel); if (codepoint == '\n') {
cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, font->pixel_height); cur_x = start_x;
baseline += font_manager_get_font_line_height_scaled(font, font->pixel_height);
} else if (codepoint == '\t') {
cur_x += font_manager_get_codepoint_width_scaled(font, ' ', font->pixel_height) * 4;
} else {
font_manager_render_char_scaled(font, cur_x, baseline, codepoint, color, font->pixel_height, put_pixel);
cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, font->pixel_height);
}
} }
} else { } else {
draw_string(win->x + ux, win->y + uy, kernel_str, color); draw_string(win->x + ux, win->y + uy, kernel_str, color);
@@ -510,10 +561,18 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int baseline = uy + font_manager_get_font_ascent_scaled(font, scale) - 2; int baseline = uy + font_manager_get_font_ascent_scaled(font, scale) - 2;
int cur_x = ux; int cur_x = ux;
const char *s = kernel_str; const char *s = kernel_str;
int start_x = cur_x;
while (*s) { while (*s) {
uint32_t codepoint = utf8_decode(&s); uint32_t codepoint = utf8_decode(&s);
font_manager_render_char_scaled(font, cur_x, baseline, codepoint, color, scale, put_pixel); if (codepoint == '\n') {
cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, scale); cur_x = start_x;
baseline += font_manager_get_font_line_height_scaled(font, scale);
} else if (codepoint == '\t') {
cur_x += font_manager_get_codepoint_width_scaled(font, ' ', scale) * 4;
} else {
font_manager_render_char_scaled(font, cur_x, baseline, codepoint, color, scale, put_pixel);
cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, scale);
}
} }
} else { } else {
draw_string_scaled(ux, uy, kernel_str, color, scale); draw_string_scaled(ux, uy, kernel_str, color, scale);
@@ -525,10 +584,18 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int baseline = win->y + uy + font_manager_get_font_ascent_scaled(font, scale) - 2; int baseline = win->y + uy + font_manager_get_font_ascent_scaled(font, scale) - 2;
int cur_x = win->x + ux; int cur_x = win->x + ux;
const char *s = kernel_str; const char *s = kernel_str;
int start_x = cur_x;
while (*s) { while (*s) {
uint32_t codepoint = utf8_decode(&s); uint32_t codepoint = utf8_decode(&s);
font_manager_render_char_scaled(font, cur_x, baseline, codepoint, color, scale, put_pixel); if (codepoint == '\n') {
cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, scale); cur_x = start_x;
baseline += font_manager_get_font_line_height_scaled(font, scale);
} else if (codepoint == '\t') {
cur_x += font_manager_get_codepoint_width_scaled(font, ' ', scale) * 4;
} else {
font_manager_render_char_scaled(font, cur_x, baseline, codepoint, color, scale, put_pixel);
cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, scale);
}
} }
} else { } else {
draw_string_scaled(win->x + ux, win->y + uy, kernel_str, color, scale); draw_string_scaled(win->x + ux, win->y + uy, kernel_str, color, scale);
@@ -586,11 +653,19 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int baseline = uy + font_manager_get_font_ascent_scaled(font, scale) - 2; int baseline = uy + font_manager_get_font_ascent_scaled(font, scale) - 2;
int cur_x = ux; int cur_x = ux;
const char *s = kernel_str; const char *s = kernel_str;
int start_x = cur_x;
while (*s) { while (*s) {
extern void font_manager_render_char_sloped(ttf_font_t *font, int x, int y, uint32_t codepoint, uint32_t color, float scale, float slope, void (*put_pixel_fn)(int, int, uint32_t)); extern void font_manager_render_char_sloped(ttf_font_t *font, int x, int y, uint32_t codepoint, uint32_t color, float scale, float slope, void (*put_pixel_fn)(int, int, uint32_t));
uint32_t codepoint = utf8_decode(&s); uint32_t codepoint = utf8_decode(&s);
font_manager_render_char_sloped(font, cur_x, baseline, codepoint, color, scale, slope, put_pixel); if (codepoint == '\n') {
cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, scale); cur_x = start_x;
baseline += font_manager_get_font_line_height_scaled(font, scale);
} else if (codepoint == '\t') {
cur_x += font_manager_get_codepoint_width_scaled(font, ' ', scale) * 4;
} else {
font_manager_render_char_sloped(font, cur_x, baseline, codepoint, color, scale, slope, put_pixel);
cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, scale);
}
} }
} else { } else {
draw_string_scaled_sloped(ux, uy, kernel_str, color, scale, slope); draw_string_scaled_sloped(ux, uy, kernel_str, color, scale, slope);
@@ -602,11 +677,19 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int baseline = win->y + uy + font_manager_get_font_ascent_scaled(font, scale) - 2; int baseline = win->y + uy + font_manager_get_font_ascent_scaled(font, scale) - 2;
int cur_x = win->x + ux; int cur_x = win->x + ux;
const char *s = kernel_str; const char *s = kernel_str;
int start_x = cur_x;
while (*s) { while (*s) {
extern void font_manager_render_char_sloped(ttf_font_t *font, int x, int y, uint32_t codepoint, uint32_t color, float scale, float slope, void (*put_pixel_fn)(int, int, uint32_t)); extern void font_manager_render_char_sloped(ttf_font_t *font, int x, int y, uint32_t codepoint, uint32_t color, float scale, float slope, void (*put_pixel_fn)(int, int, uint32_t));
uint32_t codepoint = utf8_decode(&s); uint32_t codepoint = utf8_decode(&s);
font_manager_render_char_sloped(font, cur_x, baseline, codepoint, color, scale, slope, put_pixel); if (codepoint == '\n') {
cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, scale); cur_x = start_x;
baseline += font_manager_get_font_line_height_scaled(font, scale);
} else if (codepoint == '\t') {
cur_x += font_manager_get_codepoint_width_scaled(font, ' ', scale) * 4;
} else {
font_manager_render_char_sloped(font, cur_x, baseline, codepoint, color, scale, slope, put_pixel);
cur_x += font_manager_get_codepoint_width_scaled(font, codepoint, scale);
}
} }
} else { } else {
draw_string_scaled_sloped(win->x + ux, win->y + uy, kernel_str, color, scale, slope); draw_string_scaled_sloped(win->x + ux, win->y + uy, kernel_str, color, scale, slope);
@@ -743,7 +826,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
Window *win = (Window *)arg2; Window *win = (Window *)arg2;
if (win) { if (win) {
extern void serial_write(const char *str); extern void serial_write(const char *str);
serial_write("Kernel: Setting window resizable to "); serial_write("[WM] Resizable: ");
serial_write(arg3 ? "true\n" : "false\n"); serial_write(arg3 ? "true\n" : "false\n");
win->resizable = (arg3 != 0); win->resizable = (arg3 != 0);
} }
@@ -852,33 +935,35 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
const char *mode = (const char *)arg3; const char *mode = (const char *)arg3;
if (!path || !mode) return -1; if (!path || !mode) return -1;
FAT32_FileHandle *fh = fat32_open(path, mode); // vfs_open now handles normalization internally with process_get_current()
if (!fh) return -1; // but let's be explicit if we can.
vfs_file_t *vf = vfs_open(path, mode);
if (!vf) return -1;
for (int i = 0; i < MAX_PROCESS_FDS; i++) { for (int i = 0; i < MAX_PROCESS_FDS; i++) {
if (proc->fds[i] == NULL) { if (proc->fds[i] == NULL) {
proc->fds[i] = fh; proc->fds[i] = vf;
return (uint64_t)i; return (uint64_t)i;
} }
} }
fat32_close(fh); vfs_close(vf);
return -1; return -1;
} else if (cmd == FS_CMD_READ) { } else if (cmd == FS_CMD_READ) {
int fd = (int)arg2; int fd = (int)arg2;
void *buf = (void *)arg3; void *buf = (void *)arg3;
uint32_t len = (uint32_t)arg4; uint32_t len = (uint32_t)arg4;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)fat32_read((FAT32_FileHandle*)proc->fds[fd], buf, (int)len); return (uint64_t)vfs_read((vfs_file_t*)proc->fds[fd], buf, (int)len);
} else if (cmd == FS_CMD_WRITE) { } else if (cmd == FS_CMD_WRITE) {
int fd = (int)arg2; int fd = (int)arg2;
const void *buf = (const void *)arg3; const void *buf = (const void *)arg3;
uint32_t len = (uint32_t)arg4; uint32_t len = (uint32_t)arg4;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)fat32_write((FAT32_FileHandle*)proc->fds[fd], buf, (int)len); return (uint64_t)vfs_write((vfs_file_t*)proc->fds[fd], buf, (int)len);
} else if (cmd == FS_CMD_CLOSE) { } else if (cmd == FS_CMD_CLOSE) {
int fd = (int)arg2; int fd = (int)arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
fat32_close((FAT32_FileHandle*)proc->fds[fd]); vfs_close((vfs_file_t*)proc->fds[fd]);
proc->fds[fd] = NULL; proc->fds[fd] = NULL;
return 0; return 0;
} else if (cmd == FS_CMD_SEEK) { } else if (cmd == FS_CMD_SEEK) {
@@ -886,49 +971,107 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int offset = (int)arg3; int offset = (int)arg3;
int whence = (int)arg4; // 0=SET, 1=CUR, 2=END int whence = (int)arg4; // 0=SET, 1=CUR, 2=END
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)fat32_seek((FAT32_FileHandle*)proc->fds[fd], offset, whence); return (uint64_t)vfs_seek((vfs_file_t*)proc->fds[fd], offset, whence);
} else if (cmd == FS_CMD_TELL) { } else if (cmd == FS_CMD_TELL) {
int fd = (int)arg2; int fd = (int)arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)((FAT32_FileHandle*)proc->fds[fd])->position; return (uint64_t)vfs_file_position((vfs_file_t*)proc->fds[fd]);
} else if (cmd == FS_CMD_SIZE) { } else if (cmd == FS_CMD_SIZE) {
int fd = (int)arg2; int fd = (int)arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)((FAT32_FileHandle*)proc->fds[fd])->size; return (uint64_t)vfs_file_size((vfs_file_t*)proc->fds[fd]);
} } else if (cmd == FS_CMD_GETCWD) {
else if (cmd == FS_CMD_LIST) { char *buf = (char *)arg2;
int size = (int)arg3;
if (!buf || size <= 0) return -1;
int len = (int)k_strlen(proc->cwd);
if (len >= size) return -1;
k_strcpy(buf, proc->cwd);
return (uint64_t)len;
} else if (cmd == FS_CMD_CHDIR) {
const char *path = (const char *)arg2; const char *path = (const char *)arg2;
FAT32_FileInfo *entries = (FAT32_FileInfo *)arg3; if (!path) return -1;
char normalized[VFS_MAX_PATH];
vfs_normalize_path(proc->cwd, path, normalized);
if (vfs_is_directory(normalized)) {
k_strcpy(proc->cwd, normalized);
return 0;
}
return -1;
} else if (cmd == FS_CMD_LIST) {
const char *path = (const char *)arg2;
FAT32_FileInfo *u_entries = (FAT32_FileInfo *)arg3;
int max_entries = (int)arg4; int max_entries = (int)arg4;
if (!path || !entries) return -1; if (!path || !u_entries) return -1;
return (uint64_t)fat32_list_directory(path, entries, max_entries);
char normalized[VFS_MAX_PATH];
vfs_normalize_path(proc->cwd, path, normalized);
// Safety cap for kernel allocation
if (max_entries > 256) max_entries = 256;
if (max_entries <= 0) return 0;
vfs_dirent_t *v_entries = (vfs_dirent_t *)kmalloc(sizeof(vfs_dirent_t) * max_entries);
if (!v_entries) return -1;
int count = vfs_list_directory(normalized, v_entries, max_entries);
if (count > 0) {
for (int i = 0; i < count; i++) {
// Direct copy as layouts are now aligned
k_strcpy(u_entries[i].name, v_entries[i].name);
u_entries[i].size = v_entries[i].size;
u_entries[i].is_directory = v_entries[i].is_directory;
u_entries[i].start_cluster = v_entries[i].start_cluster;
u_entries[i].write_date = v_entries[i].write_date;
u_entries[i].write_time = v_entries[i].write_time;
}
}
kfree(v_entries);
return (uint64_t)count;
} else if (cmd == FS_CMD_DELETE) { } else if (cmd == FS_CMD_DELETE) {
const char *path = (const char *)arg2; const char *path = (const char *)arg2;
if (!path) return -1; if (!path) return -1;
return fat32_delete(path) ? 0 : -1; char normalized[VFS_MAX_PATH];
vfs_normalize_path(proc->cwd, path, normalized);
return vfs_delete(normalized) ? 0 : -1;
} else if (cmd == FS_CMD_GET_INFO) { } else if (cmd == FS_CMD_GET_INFO) {
const char *path = (const char *)arg2; const char *path = (const char *)arg2;
FAT32_FileInfo *info = (FAT32_FileInfo *)arg3; FAT32_FileInfo *u_info = (FAT32_FileInfo *)arg3;
if (!path || !info) return -1; if (!path || !u_info) return -1;
extern int fat32_get_info(const char *path, FAT32_FileInfo *info);
return (uint64_t)fat32_get_info(path, info); char normalized[VFS_MAX_PATH];
vfs_normalize_path(proc->cwd, path, normalized);
vfs_dirent_t v_info;
int res = vfs_get_info(normalized, &v_info);
if (res == 0) {
k_strcpy(u_info->name, v_info.name);
u_info->size = v_info.size;
u_info->is_directory = v_info.is_directory;
u_info->start_cluster = v_info.start_cluster;
u_info->write_date = v_info.write_date;
u_info->write_time = v_info.write_time;
}
return (uint64_t)res;
} else if (cmd == FS_CMD_MKDIR) { } else if (cmd == FS_CMD_MKDIR) {
const char *path = (const char *)arg2; const char *path = (const char *)arg2;
if (!path) return -1; if (!path) return -1;
return fat32_mkdir(path) ? 0 : -1; return vfs_mkdir(path) ? 0 : -1;
} else if (cmd == FS_CMD_EXISTS) { } else if (cmd == FS_CMD_EXISTS) {
const char *path = (const char *)arg2; const char *path = (const char *)arg2;
if (!path) return 0; if (!path) return 0;
return fat32_exists(path) ? 1 : 0; return vfs_exists(path) ? 1 : 0;
} else if (cmd == FS_CMD_GETCWD) { } else if (cmd == FS_CMD_GETCWD) {
char *buf = (char *)arg2; char *buf = (char *)arg2;
int size = (int)arg3; int size = (int)arg3;
if (!buf) return -1; if (!buf) return -1;
extern void fat32_get_current_dir(char *buf, int size);
fat32_get_current_dir(buf, size); fat32_get_current_dir(buf, size);
return 0; return 0;
} else if (cmd == FS_CMD_CHDIR) { } else if (cmd == FS_CMD_CHDIR) {
const char *path = (const char *)arg2; const char *path = (const char *)arg2;
if (!path) return -1; if (!path) return -1;
extern bool fat32_chdir(const char *path);
return fat32_chdir(path) ? 0 : -1; return fat32_chdir(path) ? 0 : -1;
} }
return 0; return 0;
@@ -1055,15 +1198,6 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
extern void k_beep(int freq, int ms); extern void k_beep(int freq, int ms);
k_beep(freq, ms); k_beep(freq, ms);
return 0; return 0;
} else if (cmd == 15) { // SYSTEM_CMD_MEMINFO
uint64_t *out = (uint64_t *)arg2;
if (!out) return -1;
MemStats stats = memory_get_stats();
out[0] = stats.total_memory;
out[1] = stats.used_memory;
return 0;
} else if (cmd == 16) { // SYSTEM_CMD_UPTIME
return wm_get_ticks();
} else if (cmd == 17) { // SYSTEM_CMD_PCI_LIST } else if (cmd == 17) { // SYSTEM_CMD_PCI_LIST
typedef struct { typedef struct {
uint16_t vendor; uint16_t vendor;
@@ -1144,6 +1278,35 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
return cmd_get_config_value(key); return cmd_get_config_value(key);
} else if (cmd == 29) { // SYSTEM_CMD_SET_TEXT_COLOR } else if (cmd == 29) { // SYSTEM_CMD_SET_TEXT_COLOR
uint32_t color = (uint32_t)arg2; uint32_t color = (uint32_t)arg2;
if (proc->is_terminal_proc && proc->tty_id >= 0) {
char seq[32];
int pos = 0;
int r = (color >> 16) & 0xFF;
int g = (color >> 8) & 0xFF;
int b = color & 0xFF;
seq[pos++] = 0x1B;
seq[pos++] = '[';
seq[pos++] = '3';
seq[pos++] = '8';
seq[pos++] = ';';
seq[pos++] = '2';
seq[pos++] = ';';
char num[8];
k_itoa(r, num);
for (int i = 0; num[i] && pos < (int)sizeof(seq) - 1; i++) seq[pos++] = num[i];
seq[pos++] = ';';
k_itoa(g, num);
for (int i = 0; num[i] && pos < (int)sizeof(seq) - 1; i++) seq[pos++] = num[i];
seq[pos++] = ';';
k_itoa(b, num);
for (int i = 0; num[i] && pos < (int)sizeof(seq) - 1; i++) seq[pos++] = num[i];
seq[pos++] = 'm';
tty_write_output(proc->tty_id, seq, (size_t)pos);
return 0;
}
cmd_set_current_color(color); cmd_set_current_color(color);
return 0; return 0;
} else if (cmd == 31) { // SYSTEM_CMD_SET_WALLPAPER_PATH } else if (cmd == 31) { // SYSTEM_CMD_SET_WALLPAPER_PATH
@@ -1224,54 +1387,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
size_t max_len = (size_t)arg3; size_t max_len = (size_t)arg3;
extern int network_tcp_recv_nb(void *buf, size_t max_len); extern int network_tcp_recv_nb(void *buf, size_t max_len);
return (uint64_t)network_tcp_recv_nb(buf, max_len); return (uint64_t)network_tcp_recv_nb(buf, max_len);
} else if (cmd == SYSTEM_CMD_PROCESS_LIST) { return -1;
ProcessInfo *out = (ProcessInfo *)arg2;
int max_procs = (int)arg3;
if (!out) return 0;
extern process_t processes[];
// Dynamically calculate kernel usage as: Total System Used - User Process Sum
MemStats stats = memory_get_stats();
size_t total_used = stats.used_memory;
size_t user_used = 0;
for (int i = 0; i < 16; i++) {
if (processes[i].pid != 0xFFFFFFFF && processes[i].pid != 0 && processes[i].is_user) {
user_used += processes[i].used_memory;
}
}
if (total_used > user_used) processes[0].used_memory = total_used - user_used;
else processes[0].used_memory = 0;
int count = 0;
for (int i = 0; i < 16; i++) {
if (processes[i].pid != 0xFFFFFFFF && (processes[i].is_user || processes[i].pid == 0)) {
out[count].pid = processes[i].pid;
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy(out[count].name, processes[i].name, 64);
if (processes[i].pid == 0) {
out[count].name[0] = 'k'; out[count].name[1] = 'e'; out[count].name[2] = 'r';
out[count].name[3] = 'n'; out[count].name[4] = 'e'; out[count].name[5] = 'l';
out[count].name[6] = '\0';
}
out[count].ticks = processes[i].ticks;
out[count].used_memory = processes[i].used_memory;
count++;
if (count >= max_procs) break;
}
}
return (uint64_t)count;
} else if (cmd == SYSTEM_CMD_GET_CPU_MODEL) {
char *user_buf = (char *)arg2;
if (!user_buf) return -1;
char model[64];
platform_get_cpu_model(model);
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy(user_buf, model, 49);
return 0;
} else if (cmd == 47) { // SYSTEM_CMD_SET_RESOLUTION } else if (cmd == 47) { // SYSTEM_CMD_SET_RESOLUTION
uint16_t req_w = (uint16_t)arg2; uint16_t req_w = (uint16_t)arg2;
uint16_t req_h = (uint16_t)arg3; uint16_t req_h = (uint16_t)arg3;
@@ -1310,12 +1426,86 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
return 0; return 0;
} }
return -1; return -1;
} else if (cmd == 49) { // SYSTEM_CMD_GET_OS_INFO return -1;
os_info_t *info = (os_info_t *)arg2; } else if (cmd == 60) { // SYSTEM_CMD_TTY_CREATE
if (!info) return -1; return tty_create();
extern void get_os_info(os_info_t *info); } else if (cmd == 61) { // SYSTEM_CMD_TTY_READ_OUT
get_os_info(info); int tty_id = (int)arg2;
char *buf = (char *)arg3;
size_t len = (size_t)arg4;
if (!buf || len == 0) return 0;
return tty_read_output(tty_id, buf, len);
} else if (cmd == 62) { // SYSTEM_CMD_TTY_WRITE_IN
int tty_id = (int)arg2;
const char *buf = (const char *)arg3;
size_t len = (size_t)arg4;
if (!buf || len == 0) return 0;
return tty_write_input(tty_id, buf, len);
} else if (cmd == 63) { // SYSTEM_CMD_TTY_READ_IN
char *buf = (char *)arg2;
size_t len = (size_t)arg3;
if (!buf || len == 0) return 0;
if (proc->tty_id < 0) return 0;
return tty_read_input(proc->tty_id, buf, len);
} else if (cmd == 65) { // SYSTEM_CMD_TTY_SET_FG
int tty_id = (int)arg2;
int pid = (int)arg3;
return tty_set_foreground(tty_id, pid);
} else if (cmd == 66) { // SYSTEM_CMD_TTY_GET_FG
int tty_id = (int)arg2;
return tty_get_foreground(tty_id);
} else if (cmd == 67) { // SYSTEM_CMD_TTY_KILL_FG
int tty_id = (int)arg2;
int pid = tty_get_foreground(tty_id);
if (pid <= 0) return 0;
process_t *target = process_get_by_pid((uint32_t)pid);
if (target) process_terminate(target);
tty_set_foreground(tty_id, 0);
return 0; return 0;
} else if (cmd == 68) {
int tty_id = (int)arg2;
process_kill_by_tty(tty_id);
tty_set_foreground(tty_id, 0);
return 0;
} else if (cmd == 69) {
int tty_id = (int)arg2;
return tty_destroy(tty_id);
} else if (cmd == 64) {
const char *user_path = (const char *)arg2;
const char *user_args = (const char *)arg3;
uint64_t flags = arg4;
int tty_id = (int)arg5;
if (!user_path) return -1;
char path_buf[256];
int pi = 0;
while (pi < 255 && user_path[pi]) {
path_buf[pi] = user_path[pi];
pi++;
}
path_buf[pi] = 0;
char args_buf[512];
const char *args_ptr = NULL;
if (user_args) {
int ai = 0;
while (ai < 511 && user_args[ai]) {
args_buf[ai] = user_args[ai];
ai++;
}
args_buf[ai] = 0;
args_ptr = args_buf;
}
bool terminal_proc = (flags & SPAWN_FLAG_TERMINAL) != 0;
int effective_tty = -1;
if (flags & SPAWN_FLAG_TTY_ID) effective_tty = tty_id;
else if (flags & SPAWN_FLAG_INHERIT_TTY) effective_tty = proc ? proc->tty_id : -1;
process_t *child = process_create_elf(path_buf, args_ptr, terminal_proc, effective_tty);
if (!child) return -1;
return (uint64_t)child->pid;
} else if (cmd == SYSTEM_CMD_PARALLEL_RUN) { } else if (cmd == SYSTEM_CMD_PARALLEL_RUN) {
void (*user_fn)(void*) = (void (*)(void*))arg2; void (*user_fn)(void*) = (void (*)(void*))arg2;
void **args = (void **)arg3; void **args = (void **)arg3;

View File

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

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

@@ -0,0 +1,365 @@
#include "kernel_subsystem.h"
#include "smp.h"
#include "pci.h"
#include "memory_manager.h"
#include "module_manager.h"
#include "io.h"
#include "core/kutils.h"
#include "wm/graphics.h"
#include "core/platform.h"
#include "dev/disk.h"
// --- Helper: itoa ---
static void sys_itoa(int n, char *s) {
k_itoa(n, s);
}
// --- Graphics Implementation ---
static int read_gfx_drm(char *buf, int size, int offset) {
char out[512];
k_memset(out, 0, 512);
k_strcpy(out, "Driver: Simple Framebuffer\n");
k_strcpy(out + k_strlen(out), "Resolution: ");
char s[32]; k_itoa(get_screen_width(), s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), "x");
k_itoa(get_screen_height(), s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), "\nDepth: ");
k_itoa(graphics_get_fb_bpp(), s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), " bpp\nAddress: 0x");
k_itoa_hex(graphics_get_fb_addr(), s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), "\n");
int len = (int)k_strlen(out);
if (offset >= len) return 0;
int to_copy = len - offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + offset, to_copy);
return to_copy;
}
// --- Memory Tracking Implementation ---
static int read_mem_tracking(char *buf, int size, int offset) {
MemStats stats = memory_get_stats();
char out[1024];
k_memset(out, 0, 1024);
k_strcpy(out, "--- Kernel Heap Tracking ---\n");
k_strcpy(out + k_strlen(out), "Allocated Blocks: ");
char s[32]; k_itoa(stats.allocated_blocks, s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), "\nFragmentation: ");
k_itoa(stats.fragmentation_percent, s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), "%\n");
int len = (int)k_strlen(out);
if (offset >= len) return 0;
int to_copy = len - offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + offset, to_copy);
return to_copy;
}
// --- Module Implementation ---
static int read_sys_modules(char *buf, int size, int offset) {
int count = module_manager_get_count();
char out[2048] = "Loaded Modules:\n";
for (int i = 0; i < count; i++) {
kernel_module_t *mod = module_manager_get_index(i);
k_strcpy(out + k_strlen(out), " - ");
k_strcpy(out + k_strlen(out), mod->name);
k_strcpy(out + k_strlen(out), " (");
char sz_s[16]; k_itoa(mod->size / 1024, sz_s);
k_strcpy(out + k_strlen(out), sz_s);
k_strcpy(out + k_strlen(out), " KB)\n");
}
int len = k_strlen(out);
if (offset >= len) return 0;
int to_copy = len - offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + offset, to_copy);
return to_copy;
}
// --- PCI Bus Implementation ---
static int read_pci_bus(char *buf, int size, int offset) {
pci_device_t devices[64];
int count = pci_enumerate_devices(devices, 64);
char out[4096];
k_memset(out, 0, 4096);
k_strcpy(out, "PCI Bus Devices:\n");
for (int i = 0; i < count; i++) {
char line[128];
k_strcpy(line, " [");
char b_s[8]; k_itoa(devices[i].bus, b_s);
k_strcpy(line + k_strlen(line), b_s);
k_strcpy(line + k_strlen(line), ":");
k_itoa(devices[i].device, b_s);
k_strcpy(line + k_strlen(line), b_s);
k_strcpy(line + k_strlen(line), ":");
k_itoa(devices[i].function, b_s);
k_strcpy(line + k_strlen(line), b_s);
k_strcpy(line + k_strlen(line), "] Vendor:");
k_itoa_hex(devices[i].vendor_id, b_s);
k_strcpy(line + k_strlen(line), b_s);
k_strcpy(line + k_strlen(line), " Device:");
k_itoa_hex(devices[i].device_id, b_s);
k_strcpy(line + k_strlen(line), b_s);
k_strcpy(line + k_strlen(line), " Class:");
k_itoa_hex(devices[i].class_code, b_s);
k_strcpy(line + k_strlen(line), b_s);
k_strcpy(line + k_strlen(line), "\n");
if (k_strlen(out) + k_strlen(line) < 4095) {
k_strcpy(out + k_strlen(out), line);
}
}
int len = (int)k_strlen(out);
if (offset >= len) return 0;
int to_copy = len - offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + offset, to_copy);
return to_copy;
}
// --- CPU System Implementation ---
static int read_cpu_info(char *buf, int size, int offset) {
char *out = (char*)kmalloc(16384);
if (!out) return 0;
out[0] = 0;
char vendor[16];
char model[64];
char flags[1024];
cpu_info_t info;
platform_get_cpu_vendor(vendor);
platform_get_cpu_model(model);
platform_get_cpu_info(&info);
platform_get_cpu_flags(flags);
uint32_t cpu_count = smp_cpu_count();
for (uint32_t i = 0; i < cpu_count; i++) {
char c_s[32];
k_strcpy(out + k_strlen(out), "processor\t: ");
k_itoa(i, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "vendor_id\t: ");
k_strcpy(out + k_strlen(out), vendor);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "cpu family\t: ");
k_itoa(info.family, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "model\t\t: ");
k_itoa(info.model, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "model name\t: ");
k_strcpy(out + k_strlen(out), model);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "stepping\t: ");
k_itoa(info.stepping, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "microcode\t: 0x");
char hex[16];
int temp = info.microcode;
int hex_pos = 0;
for (int j = 7; j >= 0; j--) {
int digit = (temp >> (j * 4)) & 0xF;
hex[hex_pos++] = digit < 10 ? '0' + digit : 'a' + (digit - 10);
}
hex[hex_pos] = '\0';
k_strcpy(out + k_strlen(out), hex);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "cache size\t: ");
k_itoa(info.cache_size, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), " KB\n");
k_strcpy(out + k_strlen(out), "physical id\t: 0\n");
k_strcpy(out + k_strlen(out), "siblings\t: ");
k_itoa(cpu_count, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "core id\t\t: ");
k_itoa(i, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "cpu cores\t: ");
k_itoa(cpu_count, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "apicid\t\t: ");
k_itoa(i, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "initial apicid\t: ");
k_itoa(i, c_s);
k_strcpy(out + k_strlen(out), c_s);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "fpu\t\t: yes\n");
k_strcpy(out + k_strlen(out), "fpu_exception\t: yes\n");
k_strcpy(out + k_strlen(out), "cpuid level\t: 13\n");
k_strcpy(out + k_strlen(out), "wp\t\t: yes\n");
k_strcpy(out + k_strlen(out), "flags\t\t: ");
k_strcpy(out + k_strlen(out), flags);
k_strcpy(out + k_strlen(out), "\n");
k_strcpy(out + k_strlen(out), "bugs\t\t: \n");
k_strcpy(out + k_strlen(out), "bogomips\t: 4800.00\n");
if (i < cpu_count - 1) {
k_strcpy(out + k_strlen(out), "\n");
}
}
int len = (int)k_strlen(out);
if (offset >= len) { kfree(out); return 0; }
int to_copy = len - offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + offset, to_copy);
kfree(out);
return to_copy;
}
// --- Devices Implementation ---
static int read_sys_devices(char *buf, int size, int offset) {
char out[2048];
k_memset(out, 0, 2048);
extern int disk_get_count(void);
extern Disk* disk_get_by_index(int index);
int dcount = disk_get_count();
k_strcpy(out, "Block Devices:\n");
for (int i = 0; i < dcount; i++) {
Disk *d = disk_get_by_index(i);
if (d && !d->is_partition) {
k_strcpy(out + k_strlen(out), " ");
k_strcpy(out + k_strlen(out), d->devname);
k_strcpy(out + k_strlen(out), " - ");
k_strcpy(out + k_strlen(out), d->label);
k_strcpy(out + k_strlen(out), "\n");
}
}
k_strcpy(out + k_strlen(out), "\nCharacter Devices:\n");
k_strcpy(out + k_strlen(out), " console - System console\n");
k_strcpy(out + k_strlen(out), " tty - Terminal devices\n");
k_strcpy(out + k_strlen(out), " psmouse - Mouse input\n");
k_strcpy(out + k_strlen(out), " keyboard - Keyboard input\n");
k_strcpy(out + k_strlen(out), " framebuffer - Framebuffer device\n");
int len = (int)k_strlen(out);
if (offset >= len) return 0;
int to_copy = len - offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + offset, to_copy);
return to_copy;
}
// --- Class Implementation ---
static int read_sys_class(char *buf, int size, int offset) {
char out[1024];
k_memset(out, 0, 1024);
k_strcpy(out, "Classes:\n");
k_strcpy(out + k_strlen(out), " block - Block device class\n");
k_strcpy(out + k_strlen(out), " input - Input device class\n");
k_strcpy(out + k_strlen(out), " tty - TTY device class\n");
k_strcpy(out + k_strlen(out), " sound - Sound device class\n");
k_strcpy(out + k_strlen(out), " video - Video device class\n");
k_strcpy(out + k_strlen(out), " net - Network device class\n");
int len = (int)k_strlen(out);
if (offset >= len) return 0;
int to_copy = len - offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + offset, to_copy);
return to_copy;
}
// --- GPIO Implementation ---
static int read_gpio_debug(char *buf, int size, int offset) {
uint8_t p64 = inb(0x64);
char out[64] = "Port 0x64 Status: ";
char s[16]; k_itoa(p64, s);
k_strcpy(out + k_strlen(out), s);
k_strcpy(out + k_strlen(out), "\n");
int len = k_strlen(out);
if (offset >= len) return 0;
int to_copy = len - offset;
if (to_copy > size) to_copy = size;
k_memcpy(buf, out + offset, to_copy);
return to_copy;
}
void sysfs_init_subsystems(void) {
kernel_subsystem_t *kernel, *devices, *bus, *class, *debug, *mem_debug;
subsystem_register("kernel", &kernel);
subsystem_register("devices", &devices);
subsystem_register("bus", &bus);
subsystem_register("class", &class);
subsystem_register("kernel/debug", &debug);
// Devices info
subsystem_add_file(devices, "list", read_sys_devices, NULL);
// Class info
subsystem_add_file(class, "list", read_sys_class, NULL);
// CPU info
subsystem_add_file(kernel, "cpuinfo", read_cpu_info, NULL);
// Bus info
kernel_subsystem_t *pci_bus;
subsystem_register("bus/pci", &pci_bus);
subsystem_add_file(pci_bus, "devices", read_pci_bus, NULL);
// Module info
kernel_subsystem_t *modules_sub;
subsystem_register("module", &modules_sub);
subsystem_add_file(modules_sub, "loaded", read_sys_modules, NULL);
// Memory Tracking
subsystem_register("kernel/debug/memory", &mem_debug);
subsystem_add_file(mem_debug, "tracking", read_mem_tracking, NULL);
// Graphics DRM
kernel_subsystem_t *gfx_debug;
subsystem_register("kernel/debug/graphics", &gfx_debug);
subsystem_add_file(gfx_debug, "drm", read_gfx_drm, NULL);
// GPIO
subsystem_add_file(debug, "gpio", read_gpio_debug, NULL);
}

158
src/sys/tty.c Normal file
View File

@@ -0,0 +1,158 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "tty.h"
#include "spinlock.h"
#include <stdbool.h>
#include <stdint.h>
#define TTY_MAX 8
#define TTY_OUT_SIZE 16384
#define TTY_IN_SIZE 4096
typedef struct {
bool used;
int id;
char out_buf[TTY_OUT_SIZE];
uint32_t out_head;
uint32_t out_tail;
char in_buf[TTY_IN_SIZE];
uint32_t in_head;
uint32_t in_tail;
int fg_pid;
spinlock_t lock;
} tty_t;
static tty_t ttys[TTY_MAX] = {0};
extern void mem_memset(void *dest, int val, size_t len);
static tty_t *tty_get(int tty_id) {
if (tty_id < 0 || tty_id >= TTY_MAX) return NULL;
if (!ttys[tty_id].used) return NULL;
return &ttys[tty_id];
}
int tty_create(void) {
for (int i = 0; i < TTY_MAX; i++) {
if (!ttys[i].used) {
ttys[i].used = true;
ttys[i].id = i;
ttys[i].out_head = 0;
ttys[i].out_tail = 0;
ttys[i].in_head = 0;
ttys[i].in_tail = 0;
ttys[i].fg_pid = -1;
ttys[i].lock = SPINLOCK_INIT;
mem_memset(ttys[i].out_buf, 0, sizeof(ttys[i].out_buf));
mem_memset(ttys[i].in_buf, 0, sizeof(ttys[i].in_buf));
return i;
}
}
return -1;
}
int tty_destroy(int tty_id) {
if (tty_id < 0 || tty_id >= TTY_MAX) return -1;
tty_t *tty = &ttys[tty_id];
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
if (!tty->used) {
spinlock_release_irqrestore(&tty->lock, rflags);
return -1;
}
tty->used = false;
tty->id = -1;
tty->out_head = 0;
tty->out_tail = 0;
tty->in_head = 0;
tty->in_tail = 0;
tty->fg_pid = -1;
mem_memset(tty->out_buf, 0, sizeof(tty->out_buf));
mem_memset(tty->in_buf, 0, sizeof(tty->in_buf));
spinlock_release_irqrestore(&tty->lock, rflags);
return 0;
}
static int tty_write_ring(char *buf, uint32_t size, uint32_t *head, uint32_t *tail, const char *data, size_t len) {
int written = 0;
for (size_t i = 0; i < len; i++) {
uint32_t next = (*head + 1) % size;
if (next == *tail) break;
buf[*head] = data[i];
*head = next;
written++;
}
return written;
}
static int tty_read_ring(char *buf, uint32_t size, uint32_t *head, uint32_t *tail, char *out, size_t max_len) {
int read = 0;
while (*tail != *head && (size_t)read < max_len) {
out[read++] = buf[*tail];
*tail = (*tail + 1) % size;
}
return read;
}
int tty_write_output(int tty_id, const char *data, size_t len) {
tty_t *tty = tty_get(tty_id);
if (!tty || !data || len == 0) return 0;
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
int written = tty_write_ring(tty->out_buf, TTY_OUT_SIZE, &tty->out_head, &tty->out_tail, data, len);
spinlock_release_irqrestore(&tty->lock, rflags);
return written;
}
int tty_read_output(int tty_id, char *buf, size_t max_len) {
tty_t *tty = tty_get(tty_id);
if (!tty || !buf || max_len == 0) return 0;
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
int read = tty_read_ring(tty->out_buf, TTY_OUT_SIZE, &tty->out_head, &tty->out_tail, buf, max_len);
spinlock_release_irqrestore(&tty->lock, rflags);
return read;
}
int tty_write_input(int tty_id, const char *data, size_t len) {
tty_t *tty = tty_get(tty_id);
if (!tty || !data || len == 0) return 0;
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
int written = tty_write_ring(tty->in_buf, TTY_IN_SIZE, &tty->in_head, &tty->in_tail, data, len);
spinlock_release_irqrestore(&tty->lock, rflags);
return written;
}
int tty_read_input(int tty_id, char *buf, size_t max_len) {
tty_t *tty = tty_get(tty_id);
if (!tty || !buf || max_len == 0) return 0;
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
int read = tty_read_ring(tty->in_buf, TTY_IN_SIZE, &tty->in_head, &tty->in_tail, buf, max_len);
spinlock_release_irqrestore(&tty->lock, rflags);
return read;
}
int tty_set_foreground(int tty_id, int pid) {
tty_t *tty = tty_get(tty_id);
if (!tty) return -1;
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
tty->fg_pid = pid;
spinlock_release_irqrestore(&tty->lock, rflags);
return 0;
}
int tty_get_foreground(int tty_id) {
tty_t *tty = tty_get(tty_id);
if (!tty) return -1;
uint64_t rflags = spinlock_acquire_irqsave(&tty->lock);
int pid = tty->fg_pid;
spinlock_release_irqrestore(&tty->lock, rflags);
return pid;
}

18
src/sys/tty.h Normal file
View File

@@ -0,0 +1,18 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef TTY_H
#define TTY_H
#include <stddef.h>
int tty_create(void);
int tty_destroy(int tty_id);
int tty_write_output(int tty_id, const char *data, size_t len);
int tty_read_output(int tty_id, char *buf, size_t max_len);
int tty_write_input(int tty_id, const char *data, size_t len);
int tty_read_input(int tty_id, char *buf, size_t max_len);
int tty_set_foreground(int tty_id, int pid);
int tty_get_foreground(int tty_id);
#endif

1454
src/userland/cli/bsh.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +0,0 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include <stdlib.h>
#include <syscall.h>
int main(int argc, char **argv) {
uint32_t error_color = (uint32_t)sys_get_shell_config("error_color");
uint32_t default_color = (uint32_t)sys_get_shell_config("default_text_color");
if (argc < 2) {
printf("Usage: cat <filename>\n");
return 1;
}
int fd = sys_open(argv[1], "r");
if (fd < 0) {
sys_set_text_color(error_color);
printf("Error: Cannot open %s\n", argv[1]);
sys_set_text_color(default_color);
return 1;
}
char buffer[4096];
int bytes;
while ((bytes = sys_read(fd, buffer, sizeof(buffer))) > 0) {
sys_write(1, buffer, bytes);
}
sys_close(fd);
return 0;
}

View File

@@ -1,166 +0,0 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include <stdlib.h>
#include <syscall.h>
typedef int bool;
#define true 1
#define false 0
void combine_path(char *dest, const char *path1, const char *path2) {
int i = 0;
while (path1[i]) {
dest[i] = path1[i];
i++;
}
if (i > 0 && dest[i-1] != '/') {
dest[i++] = '/';
}
int j = 0;
while (path2[j]) {
dest[i++] = path2[j++];
}
dest[i] = 0;
}
const char* get_basename(const char *path) {
const char *last_slash = NULL;
int len = 0;
while (path[len]) {
if (path[len] == '/') last_slash = path + len;
len++;
}
if (!last_slash) return path;
// If it ends with a slash, skip it and find the previous one
if (last_slash[1] == '\0') {
if (len <= 1) return path; // root "/"
int i = len - 2;
while (i >= 0 && path[i] != '/') i--;
if (i < 0) return path;
return path + i + 1;
}
return last_slash + 1;
}
void copy_recursive(const char *src, const char *dst) {
FAT32_FileInfo info;
if (sys_get_file_info(src, &info) < 0) {
printf("Error: Cannot get info for %s\n", src);
return;
}
if (info.is_directory) {
if (sys_mkdir(dst) < 0) {
// Might already exist
}
FAT32_FileInfo entries[64];
int count = sys_list(src, entries, 64);
for (int i = 0; i < count; i++) {
if (strcmp(entries[i].name, ".") == 0 || strcmp(entries[i].name, "..") == 0) continue;
char sub_src[512], sub_dst[512];
combine_path(sub_src, src, entries[i].name);
combine_path(sub_dst, dst, entries[i].name);
copy_recursive(sub_src, sub_dst);
}
} else {
int fd_in = sys_open(src, "r");
if (fd_in < 0) {
printf("Error: Cannot open source %s\n", src);
return;
}
int fd_out = sys_open(dst, "w");
if (fd_out < 0) {
printf("Error: Cannot create destination %s\n", dst);
sys_close(fd_in);
return;
}
char buffer[4096];
int bytes;
while ((bytes = sys_read(fd_in, buffer, sizeof(buffer))) > 0) {
sys_write_fs(fd_out, buffer, bytes);
}
sys_close(fd_in);
sys_close(fd_out);
}
}
int main(int argc, char **argv) {
bool recursive = false;
char *src_path = NULL;
char *dst_path = NULL;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-r") == 0) {
recursive = true;
} else if (!src_path) {
src_path = argv[i];
} else if (!dst_path) {
dst_path = argv[i];
}
}
if (!src_path || !dst_path) {
printf("Usage: cp [-r] <source> <dest>\n");
return 1;
}
FAT32_FileInfo info_src;
if (sys_get_file_info(src_path, &info_src) < 0) {
printf("Error: Source %s does not exist\n", src_path);
return 1;
}
if (info_src.is_directory && !recursive) {
printf("Error: %s is a directory (use -r to copy recursively)\n", src_path);
return 1;
}
char actual_dst[512];
FAT32_FileInfo info_dst;
if (sys_get_file_info(dst_path, &info_dst) == 0 && info_dst.is_directory) {
// If destination is a directory, copy INTO it
const char *base = get_basename(src_path);
// Clean up trailing slash from basename if any (get_basename handles it mostly)
char clean_base[256];
int k = 0;
while (base[k] && base[k] != '/') {
clean_base[k] = base[k];
k++;
}
clean_base[k] = 0;
combine_path(actual_dst, dst_path, clean_base);
} else {
strcpy(actual_dst, dst_path);
}
if (recursive) {
copy_recursive(src_path, actual_dst);
} else {
int fd_in = sys_open(src_path, "r");
if (fd_in < 0) {
printf("Error: Cannot open source %s\n", src_path);
return 1;
}
int fd_out = sys_open(actual_dst, "w");
if (fd_out < 0) {
printf("Error: Cannot create destination %s\n", actual_dst);
sys_close(fd_in);
return 1;
}
char buffer[4096];
int bytes;
while ((bytes = sys_read(fd_in, buffer, sizeof(buffer))) > 0) {
sys_write_fs(fd_out, buffer, bytes);
}
sys_close(fd_in);
sys_close(fd_out);
}
return 0;
}

View File

@@ -1,66 +0,0 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include <stdlib.h>
#include <syscall.h>
int main(int argc, char **argv) {
uint64_t dir_color = sys_get_shell_config("dir_color");
uint64_t file_color = sys_get_shell_config("file_color");
uint64_t size_color = sys_get_shell_config("size_color");
uint64_t error_color = sys_get_shell_config("error_color");
uint64_t default_color = sys_get_shell_config("default_text_color");
char path[256];
if (argc > 1) {
strcpy(path, argv[1]);
} else {
if (!sys_getcwd(path, sizeof(path))) {
strcpy(path, "/");
}
}
FAT32_FileInfo info;
if (sys_get_file_info(path, &info) < 0) {
sys_set_text_color(error_color);
printf("Error: Path '%s' does not exist\n", path);
sys_set_text_color(default_color);
return 1;
}
if (!info.is_directory) {
sys_set_text_color(file_color);
printf("[FILE] %s", info.name);
sys_set_text_color(size_color);
printf(" (%d bytes)\n", info.size);
sys_set_text_color(default_color);
printf("\nTotal: 1 items\n");
return 0;
}
FAT32_FileInfo entries[128];
int count = sys_list(path, entries, 128);
if (count < 0) {
sys_set_text_color(error_color);
printf("Error: Cannot list directory %s\n", path);
sys_set_text_color(default_color);
return 1;
}
for (int i = 0; i < count; i++) {
if (entries[i].is_directory) {
sys_set_text_color(dir_color);
printf("[DIR] %s\n", entries[i].name);
} else {
sys_set_text_color(file_color);
printf("[FILE] %s", entries[i].name);
sys_set_text_color(size_color);
printf(" (%d bytes)\n", entries[i].size);
}
}
sys_set_text_color(default_color);
printf("\nTotal: %d items\n", count);
return 0;
}

View File

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

View File

@@ -1,28 +0,0 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include <stdlib.h>
#include <syscall.h>
int main(int argc, char **argv) {
uint32_t error_color = (uint32_t)sys_get_shell_config("error_color");
uint32_t success_color = (uint32_t)sys_get_shell_config("success_color");
uint32_t default_color = (uint32_t)sys_get_shell_config("default_text_color");
if (argc < 2) {
printf("Usage: mkdir <dirname>\n");
return 1;
}
if (sys_mkdir(argv[1]) == 0) {
sys_set_text_color(success_color);
printf("Created directory: %s\n", argv[1]);
} else {
sys_set_text_color(error_color);
printf("Error: Cannot create directory %s\n", argv[1]);
sys_set_text_color(default_color);
return 1;
}
sys_set_text_color(default_color);
return 0;
}

View File

@@ -1,128 +0,0 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include <stdlib.h>
#include <syscall.h>
void combine_path(char *dest, const char *path1, const char *path2) {
int i = 0;
while (path1[i]) {
dest[i] = path1[i];
i++;
}
if (i > 0 && dest[i-1] != '/') {
dest[i++] = '/';
}
int j = 0;
while (path2[j]) {
dest[i++] = path2[j++];
}
dest[i] = 0;
}
const char* get_basename(const char *path) {
const char *last_slash = NULL;
int len = 0;
while (path[len]) {
if (path[len] == '/') last_slash = path + len;
len++;
}
if (!last_slash) return path;
if (last_slash[1] == '\0') {
if (len <= 1) return path;
int i = len - 2;
while (i >= 0 && path[i] != '/') i--;
if (i < 0) return path;
return path + i + 1;
}
return last_slash + 1;
}
void copy_recursive(const char *src, const char *dst) {
FAT32_FileInfo info;
if (sys_get_file_info(src, &info) < 0) return;
if (info.is_directory) {
sys_mkdir(dst);
FAT32_FileInfo entries[64];
int count = sys_list(src, entries, 64);
for (int i = 0; i < count; i++) {
if (strcmp(entries[i].name, ".") == 0 || strcmp(entries[i].name, "..") == 0) continue;
char sub_src[512], sub_dst[512];
combine_path(sub_src, src, entries[i].name);
combine_path(sub_dst, dst, entries[i].name);
copy_recursive(sub_src, sub_dst);
}
} else {
int fd_in = sys_open(src, "r");
if (fd_in < 0) return;
int fd_out = sys_open(dst, "w");
if (fd_out < 0) { sys_close(fd_in); return; }
char buffer[4096];
int bytes;
while ((bytes = sys_read(fd_in, buffer, sizeof(buffer))) > 0) {
sys_write_fs(fd_out, buffer, bytes);
}
sys_close(fd_in);
sys_close(fd_out);
}
}
void delete_recursive(const char *path) {
FAT32_FileInfo info;
if (sys_get_file_info(path, &info) < 0) return;
if (info.is_directory) {
FAT32_FileInfo entries[64];
int count = sys_list(path, entries, 64);
for (int i = 0; i < count; i++) {
if (strcmp(entries[i].name, ".") == 0 || strcmp(entries[i].name, "..") == 0) continue;
char sub_path[512];
combine_path(sub_path, path, entries[i].name);
delete_recursive(sub_path);
}
sys_delete(path);
} else {
sys_delete(path);
}
}
int main(int argc, char **argv) {
if (argc < 3) {
printf("Usage: mv <source> <dest>\n");
return 1;
}
char *src_path = argv[1];
char *dst_path = argv[2];
FAT32_FileInfo info_src;
if (sys_get_file_info(src_path, &info_src) < 0) {
printf("Error: Cannot open source %s\n", src_path);
return 1;
}
char actual_dst[512];
FAT32_FileInfo info_dst;
if (sys_get_file_info(dst_path, &info_dst) == 0 && info_dst.is_directory) {
const char *base = get_basename(src_path);
char clean_base[256];
int k = 0;
while (base[k] && base[k] != '/') {
clean_base[k] = base[k];
k++;
}
clean_base[k] = 0;
combine_path(actual_dst, dst_path, clean_base);
} else {
strcpy(actual_dst, dst_path);
}
copy_recursive(src_path, actual_dst);
delete_recursive(src_path);
return 0;
}

View File

@@ -1,29 +0,0 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include <stdlib.h>
#include <syscall.h>
int main(int argc, char **argv) {
uint32_t error_color = (uint32_t)sys_get_shell_config("error_color");
uint32_t success_color = (uint32_t)sys_get_shell_config("success_color");
uint32_t default_color = (uint32_t)sys_get_shell_config("default_text_color");
if (argc < 2) {
printf("Usage: rm <path>\n");
return 1;
}
// Simple rm (no recursive support yet for simplicity, but can be added)
if (sys_delete(argv[1]) == 0) {
sys_set_text_color(success_color);
printf("Deleted: %s\n", argv[1]);
} else {
sys_set_text_color(error_color);
printf("Error: Cannot delete %s\n", argv[1]);
sys_set_text_color(default_color);
return 1;
}
sys_set_text_color(default_color);
return 0;
}

View File

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

View File

@@ -1,27 +0,0 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include <stdlib.h>
#include <syscall.h>
int main(int argc, char **argv) {
if (argc < 2) {
printf("Usage: touch <filename>\n");
return 1;
}
// Check if file already exists
if (sys_exists(argv[1])) {
// Just return success if it exists (simplification)
return 0;
}
int fd = sys_open(argv[1], "w");
if (fd < 0) {
printf("Error: Cannot create %s\n", argv[1]);
return 1;
}
sys_close(fd);
return 0;
}

236
src/userland/cli/uname.c Normal file
View File

@@ -0,0 +1,236 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include <stdlib.h>
#include <syscall.h>
#include <stdbool.h>
static void str_copy(char *dst, const char *src, int max_len) {
int i = 0;
if (!dst || max_len <= 0) return;
if (!src) { dst[0] = 0; return; }
while (i < max_len - 1 && src[i]) {
dst[i] = src[i];
i++;
}
dst[i] = 0;
}
static bool starts_with(const char *s, const char *prefix) {
if (!s || !prefix) return false;
while (*prefix) {
if (*s != *prefix) return false;
s++; prefix++;
}
return true;
}
static void trim_end(char *s) {
int len;
if (!s) return;
len = (int)strlen(s);
while (len > 0) {
char c = s[len - 1];
if (c == '\n' || c == '\r' || c == ' ' || c == '\t') {
s[len - 1] = 0;
len--;
} else {
break;
}
}
}
static const char *trim_start(const char *s) {
if (!s) return "";
while (*s == ' ' || *s == '\t') s++;
return s;
}
static void copy_range(char *dst, const char *start, int len, int max_len) {
int i;
if (!dst || max_len <= 0) return;
if (!start || len <= 0) { dst[0] = 0; return; }
if (len > max_len - 1) len = max_len - 1;
for (i = 0; i < len; i++) dst[i] = start[i];
dst[len] = 0;
trim_end(dst);
}
static void copy_line(const char *start, char *out, int out_len) {
int i = 0;
if (!start || !out || out_len <= 0) return;
while (start[i] && start[i] != '\n' && start[i] != '\r' && i < out_len - 1) {
out[i] = start[i];
i++;
}
out[i] = 0;
trim_end(out);
}
static int read_file_to_buf(const char *path, char *buf, int max_len) {
int fd;
int bytes;
if (!buf || max_len <= 0) return -1;
fd = sys_open(path, "r");
if (fd < 0) return -1;
bytes = sys_read(fd, buf, max_len - 1);
sys_close(fd);
if (bytes < 0) return -1;
buf[bytes] = 0;
return bytes;
}
static void parse_version_info(const char *vbuf,
char *os_name, int os_name_len,
char *kernel_name, int kernel_name_len,
char *kernel_ver, int kernel_ver_len,
char *build_str, int build_str_len) {
char line1[256] = {0};
char line2[256] = {0};
char line3[256] = {0};
const char *l2;
const char *l3;
const char *end;
if (!vbuf || !vbuf[0]) return;
copy_line(vbuf, line1, sizeof(line1));
l2 = strchr(vbuf, '\n');
if (l2) {
l2++;
copy_line(l2, line2, sizeof(line2));
l3 = strchr(l2, '\n');
if (l3) {
l3++;
copy_line(l3, line3, sizeof(line3));
}
}
if (line1[0]) {
end = strstr(line1, " [");
if (!end) end = strstr(line1, " Version");
if (!end) end = line1 + (int)strlen(line1);
copy_range(os_name, line1, (int)(end - line1), os_name_len);
}
if (line2[0]) {
const char *p = line2;
if (starts_with(p, "Kernel:")) {
p += 7;
if (*p == ' ') p++;
}
p = trim_start(p);
if (*p) {
const char *t = p;
while (*t && *t != ' ') t++;
copy_range(kernel_name, p, (int)(t - p), kernel_name_len);
while (*t == ' ') t++;
if (*t) copy_range(kernel_ver, t, (int)strlen(t), kernel_ver_len);
}
}
if (line3[0]) {
const char *p = line3;
if (starts_with(p, "Build:")) {
p += 6;
if (*p == ' ') p++;
}
p = trim_start(p);
if (*p) copy_range(build_str, p, (int)strlen(p), build_str_len);
}
}
static void parse_cpu_model(const char *buf, char *out, int out_len) {
const char *p;
const char *colon;
const char *end;
if (!buf || !out || out_len <= 0) return;
p = strstr(buf, "model name");
if (!p) return;
colon = strchr(p, ':');
if (!colon) return;
colon++;
colon = trim_start(colon);
end = colon;
while (*end && *end != '\n' && *end != '\r') end++;
copy_range(out, colon, (int)(end - colon), out_len);
}
static void print_usage(void) {
printf("Usage: uname [-amnoprsv]\n");
}
static void print_field(const char *value, bool *first) {
if (!value || !value[0]) value = "Unknown";
if (!*first) printf(" ");
printf("%s", value);
*first = false;
}
int main(int argc, char **argv) {
bool want_s = false;
bool want_n = false;
bool want_r = false;
bool want_v = false;
bool want_m = false;
bool want_p = false;
bool want_o = false;
bool any_flags = false;
for (int i = 1; i < argc; i++) {
const char *arg = argv[i];
if (!arg || arg[0] != '-' || arg[1] == 0) {
print_usage();
return 1;
}
for (int j = 1; arg[j]; j++) {
char c = arg[j];
if (c == 'a') {
want_s = want_n = want_r = want_v = want_m = want_p = want_o = true;
any_flags = true;
} else if (c == 's') { want_s = true; any_flags = true; }
else if (c == 'n') { want_n = true; any_flags = true; }
else if (c == 'r') { want_r = true; any_flags = true; }
else if (c == 'v') { want_v = true; any_flags = true; }
else if (c == 'm') { want_m = true; any_flags = true; }
else if (c == 'p') { want_p = true; any_flags = true; }
else if (c == 'o') { want_o = true; any_flags = true; }
else { print_usage(); return 1; }
}
}
if (!any_flags) want_s = true;
char vbuf[1024] = {0};
char cpubuf[2048] = {0};
char os_name[64] = "Unknown";
char kernel_name[64] = "Unknown";
char kernel_ver[64] = "Unknown";
char build_str[64] = "Unknown";
char processor[128] = {0};
if (read_file_to_buf("/proc/version", vbuf, sizeof(vbuf)) > 0) {
parse_version_info(vbuf, os_name, sizeof(os_name), kernel_name, sizeof(kernel_name),
kernel_ver, sizeof(kernel_ver), build_str, sizeof(build_str));
}
if (read_file_to_buf("/proc/cpuinfo", cpubuf, sizeof(cpubuf)) > 0) {
parse_cpu_model(cpubuf, processor, sizeof(processor));
}
const char *nodename = "boredos";
const char *machine = "x86_64";
if (!processor[0]) str_copy(processor, machine, sizeof(processor));
bool first = true;
if (want_s) print_field(kernel_name, &first);
if (want_n) print_field(nodename, &first);
if (want_r) print_field(kernel_ver, &first);
if (want_v) print_field(build_str, &first);
if (want_m) print_field(machine, &first);
if (want_p) print_field(processor, &first);
if (want_o) print_field(os_name, &first);
printf("\n");
return 0;
}

519
src/userland/games/2048.c Normal file
View File

@@ -0,0 +1,519 @@
#include "libc/syscall.h"
#include "libc/libui.h"
#include "libc/stdlib.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/*
@Lluciocc
2048 for BoredOS
Controls:
- WASD keys or arrow keys or numpad keys to move
- R key to restart
*/
#define WINDOW_W 300
#define WINDOW_H 430
#define BOARD_SIZE 4
#define TILE_SIZE 56
#define TILE_GAP 8
#define BOARD_X 18
#define BOARD_Y 96
#define BTN_W 54
#define BTN_H 30
#define COLOR_BG 0xFF121212
#define COLOR_PANEL 0xFF202020
#define COLOR_PANEL_2 0xFF2A2A2A
#define COLOR_BORDER 0xFF3D3D3D
#define COLOR_TEXT 0xFFF2F2F2
#define COLOR_TEXT_DARK 0xFF202020
#define COLOR_MUTED 0xFFBBBBBB
#define COLOR_ACCENT 0xFF6EA8FE
#define COLOR_GREEN 0xFF69DB7C
#define COLOR_RED 0xFFFF6B6B
#define COLOR_EMPTY_TILE 0xFF2D2D2D
static int board[BOARD_SIZE][BOARD_SIZE];
static int score = 0;
static int best_tile = 0;
static bool game_over = false;
static bool game_won = false;
static bool has_moved_last_turn = false;
static uint32_t random_seed = 0xC0FFEE12u;
static uint32_t random_next(void) {
random_seed = random_seed * 1664525u + 1013904223u;
return random_seed;
}
static int max_int(int a, int b) {
return (a > b) ? a : b;
}
static void clear_board(void) {
for (int y = 0; y < BOARD_SIZE; y++) {
for (int x = 0; x < BOARD_SIZE; x++) {
board[y][x] = 0;
}
}
}
static int count_empty_cells(void) {
int count = 0;
for (int y = 0; y < BOARD_SIZE; y++) {
for (int x = 0; x < BOARD_SIZE; x++) {
if (board[y][x] == 0) {
count++;
}
}
}
return count;
}
static void add_random_tile(void) {
int empty_count = count_empty_cells();
if (empty_count <= 0) {
return;
}
int pick = (int)(random_next() % (uint32_t)empty_count);
for (int y = 0; y < BOARD_SIZE; y++) {
for (int x = 0; x < BOARD_SIZE; x++) {
if (board[y][x] == 0) {
if (pick == 0) {
/* 90% chance of a 2, 10% chance of a 4 */
board[y][x] = ((random_next() % 10u) == 0u) ? 4 : 2;
best_tile = max_int(best_tile, board[y][x]);
return;
}
pick--;
}
}
}
}
static bool can_make_any_move(void) {
for (int y = 0; y < BOARD_SIZE; y++) {
for (int x = 0; x < BOARD_SIZE; x++) {
if (board[y][x] == 0) {
return true;
}
if (x + 1 < BOARD_SIZE && board[y][x] == board[y][x + 1]) {
return true;
}
if (y + 1 < BOARD_SIZE && board[y][x] == board[y + 1][x]) {
return true;
}
}
}
return false;
}
static void refresh_end_state(void) {
game_won = false;
for (int y = 0; y < BOARD_SIZE; y++) {
for (int x = 0; x < BOARD_SIZE; x++) {
if (board[y][x] >= 2048) {
game_won = true;
}
if (board[y][x] > best_tile) {
best_tile = board[y][x];
}
}
}
game_over = !can_make_any_move();
}
static void init_game(void) {
clear_board();
score = 0;
best_tile = 0;
game_over = false;
game_won = false;
has_moved_last_turn = false;
add_random_tile();
add_random_tile();
refresh_end_state();
}
static void copy_line_from_row(int row, int out[BOARD_SIZE]) {
for (int i = 0; i < BOARD_SIZE; i++) {
out[i] = board[row][i];
}
}
static void copy_line_to_row(int row, const int in[BOARD_SIZE]) {
for (int i = 0; i < BOARD_SIZE; i++) {
board[row][i] = in[i];
}
}
static void copy_line_from_col(int col, int out[BOARD_SIZE]) {
for (int i = 0; i < BOARD_SIZE; i++) {
out[i] = board[i][col];
}
}
static void copy_line_to_col(int col, const int in[BOARD_SIZE]) {
for (int i = 0; i < BOARD_SIZE; i++) {
board[i][col] = in[i];
}
}
static void reverse_line(int line[BOARD_SIZE]) {
for (int i = 0; i < BOARD_SIZE / 2; i++) {
int tmp = line[i];
line[i] = line[BOARD_SIZE - 1 - i];
line[BOARD_SIZE - 1 - i] = tmp;
}
}
static bool slide_and_merge_line_left(int line[BOARD_SIZE]) {
int compact[BOARD_SIZE];
int merged[BOARD_SIZE];
int compact_len = 0;
int write_index = 0;
bool changed = false;
for (int i = 0; i < BOARD_SIZE; i++) {
merged[i] = 0;
if (line[i] != 0) {
compact[compact_len++] = line[i];
}
}
for (int i = 0; i < compact_len; i++) {
if (i + 1 < compact_len && compact[i] == compact[i + 1]) {
int value = compact[i] * 2;
merged[write_index++] = value;
score += value;
best_tile = max_int(best_tile, value);
i++; /* Skip the second tile, it has been merged. */
} else {
merged[write_index++] = compact[i];
}
}
while (write_index < BOARD_SIZE) {
merged[write_index++] = 0;
}
for (int i = 0; i < BOARD_SIZE; i++) {
if (line[i] != merged[i]) {
changed = true;
}
line[i] = merged[i];
}
return changed;
}
typedef enum {
MOVE_LEFT,
MOVE_RIGHT,
MOVE_UP,
MOVE_DOWN
} move_dir_t;
static bool apply_move(move_dir_t dir) {
bool changed = false;
if (game_over) {
return false;
}
if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
for (int row = 0; row < BOARD_SIZE; row++) {
int line[BOARD_SIZE];
copy_line_from_row(row, line);
if (dir == MOVE_RIGHT) {
reverse_line(line);
}
if (slide_and_merge_line_left(line)) {
changed = true;
}
if (dir == MOVE_RIGHT) {
reverse_line(line);
}
copy_line_to_row(row, line);
}
} else {
for (int col = 0; col < BOARD_SIZE; col++) {
int line[BOARD_SIZE];
copy_line_from_col(col, line);
if (dir == MOVE_DOWN) {
reverse_line(line);
}
if (slide_and_merge_line_left(line)) {
changed = true;
}
if (dir == MOVE_DOWN) {
reverse_line(line);
}
copy_line_to_col(col, line);
}
}
if (changed) {
add_random_tile();
}
has_moved_last_turn = changed;
refresh_end_state();
return changed;
}
static uint32_t get_tile_color(int value) {
switch (value) {
case 0: return COLOR_EMPTY_TILE;
case 2: return 0xFFEEE4DA;
case 4: return 0xFFEDE0C8;
case 8: return 0xFFF2B179;
case 16: return 0xFFF59563;
case 32: return 0xFFF67C5F;
case 64: return 0xFFF65E3B;
case 128: return 0xFFEDCF72;
case 256: return 0xFFEDCC61;
case 512: return 0xFFEDC850;
case 1024: return 0xFFEDC53F;
case 2048: return 0xFFEDC22E;
default: return 0xFF3C91E6;
}
}
static uint32_t get_tile_text_color(int value) {
(void)value;
return 0xFF000000; // for visibility
}
static void int_to_text(int value, char *out) {
itoa(value, out);
}
static void draw_centered_text(ui_window_t win, int x, int y, int w, int h,
const char *text, uint32_t color, float scale) {
(void)scale;
uint32_t text_w = ui_get_string_width(text);
uint32_t text_h = ui_get_font_height();
int draw_x = x + (w - (int)text_w) / 2;
int draw_y = y + (h - (int)text_h) / 2;
ui_draw_string(win, draw_x, draw_y, text, color);
}
static void draw_button(ui_window_t win, int x, int y, int w, int h,
const char *label, uint32_t color) {
ui_draw_rounded_rect_filled(win, x, y, w, h, 6, color);
draw_centered_text(win, x, y, w, h, label, COLOR_TEXT, 1.0f);
}
static void draw_score_box(ui_window_t win, int x, int y, int w, int h,
const char *title, int value) {
char buf[16];
int_to_text(value, buf);
ui_draw_rounded_rect_filled(win, x, y, w, h, 8, COLOR_PANEL_2);
draw_centered_text(win, x, y + 3, w, 14, title, COLOR_MUTED, 0.8f);
draw_centered_text(win, x, y + 16, w, 18, buf, COLOR_TEXT, 1.0f);
}
static void draw_tile(ui_window_t win, int x, int y, int value) {
char buf[16];
float scale = 1.6f;
ui_draw_rounded_rect_filled(win, x, y, TILE_SIZE, TILE_SIZE, 8, get_tile_color(value));
if (value == 0) {
return;
}
int_to_text(value, buf);
if (value >= 1000) {
scale = 1.2f;
} else if (value >= 100) {
scale = 1.4f;
}
draw_centered_text(
win,
x, y,
TILE_SIZE, TILE_SIZE,
buf,
0xFF000000,
scale
);
}
static void draw_board(ui_window_t win) {
int board_w = BOARD_SIZE * TILE_SIZE + (BOARD_SIZE + 1) * TILE_GAP;
int board_h = board_w;
ui_draw_rounded_rect_filled(win, BOARD_X, BOARD_Y, board_w, board_h, 10, COLOR_PANEL_2);
for (int y = 0; y < BOARD_SIZE; y++) {
for (int x = 0; x < BOARD_SIZE; x++) {
int px = BOARD_X + TILE_GAP + x * (TILE_SIZE + TILE_GAP);
int py = BOARD_Y + TILE_GAP + y * (TILE_SIZE + TILE_GAP);
draw_tile(win, px, py, board[y][x]);
}
}
}
static void draw_status(ui_window_t win) {
if (game_over) {
ui_draw_string(win, 18, 70, "Game over", COLOR_RED);
} else if (game_won) {
ui_draw_string(win, 18, 70, "2048 reached - keep going", COLOR_GREEN);
} else {
ui_draw_string(win, 18, 70, "Combine tiles to reach 2048", COLOR_MUTED);
}
}
static void game_paint(ui_window_t win) {
ui_draw_rect(win, 0, 0, WINDOW_W, WINDOW_H, COLOR_BG);
ui_draw_string_scaled(win, 18, 12, "2048", COLOR_TEXT, 1.6f);
ui_draw_string(win, 18, 42, "Use WASD keys", COLOR_MUTED);
draw_score_box(win, 155, 14, 56, 40, "SCORE", score);
draw_score_box(win, 220, 14, 62, 40, "BEST", best_tile);
draw_status(win);
draw_board(win);
/* Not recommended to use
draw_button(win, 18, 352, 86, 30, "Restart", COLOR_ACCENT);
draw_button(win, 123, 352, BTN_W, BTN_H, "Left", COLOR_BORDER);
draw_button(win, 186, 352, BTN_W, BTN_H, "Right", COLOR_BORDER);
draw_button(win, 92, 390, BTN_W, BTN_H, "Up", COLOR_BORDER);
draw_button(win, 155, 390, BTN_W, BTN_H, "Down", COLOR_BORDER);
*/
}
static bool point_in_rect(int px, int py, int x, int y, int w, int h) {
return px >= x && px < x + w && py >= y && py < y + h;
}
/*
37 = left arrow
38 = up arrow
39 = right arrow
40 = down arrow
72 = numpad 8
75 = numpad 4
77 = numpad 6
80 = numpad 2
key = letter
*/
static bool is_left_key(int key) {
return key == 'a' || key == 'A' || key == 37 || key == 75;
}
static bool is_right_key(int key) {
return key == 'd' || key == 'D' || key == 39 || key == 77;
}
static bool is_up_key(int key) {
return key == 'w' || key == 'W' || key == 38 || key == 72;
}
static bool is_down_key(int key) {
return key == 's' || key == 'S' || key == 40 || key == 80;
}
static void handle_click(int x, int y) {
if (point_in_rect(x, y, 18, 352, 86, 30)) {
init_game();
return;
}
if (point_in_rect(x, y, 123, 352, BTN_W, BTN_H)) {
apply_move(MOVE_LEFT);
return;
}
if (point_in_rect(x, y, 186, 352, BTN_W, BTN_H)) {
apply_move(MOVE_RIGHT);
return;
}
if (point_in_rect(x, y, 92, 390, BTN_W, BTN_H)) {
apply_move(MOVE_UP);
return;
}
if (point_in_rect(x, y, 155, 390, BTN_W, BTN_H)) {
apply_move(MOVE_DOWN);
return;
}
}
static void handle_key(int key) {
if (key == 'r' || key == 'R') {
init_game();
return;
}
if (is_left_key(key)) {
apply_move(MOVE_LEFT);
} else if (is_right_key(key)) {
apply_move(MOVE_RIGHT);
} else if (is_up_key(key)) {
apply_move(MOVE_UP);
} else if (is_down_key(key)) {
apply_move(MOVE_DOWN);
}
}
int main(int argc, char **argv) {
(void)argc;
(void)argv;
ui_window_t win = ui_window_create("2048", 240, 120, WINDOW_W, WINDOW_H);
if (!win) {
return 1;
}
init_game();
gui_event_t ev;
while (1) {
if (ui_get_event(win, &ev)) {
if (ev.type == GUI_EVENT_PAINT) {
game_paint(win);
ui_mark_dirty(win, 0, 0, WINDOW_W, WINDOW_H);
} else if (ev.type == GUI_EVENT_CLICK) {
handle_click(ev.arg1, ev.arg2);
game_paint(win);
ui_mark_dirty(win, 0, 0, WINDOW_W, WINDOW_H);
} else if (ev.type == GUI_EVENT_KEY) {
handle_key(ev.arg1);
game_paint(win);
ui_mark_dirty(win, 0, 0, WINDOW_W, WINDOW_H);
} else if (ev.type == GUI_EVENT_CLOSE) {
sys_exit(0);
} else if (ev.type == GUI_EVENT_RESIZE) {
game_paint(win);
ui_mark_dirty(win, 0, 0, WINDOW_W, WINDOW_H);
}
} else {
sys_yield();
}
}
return 0;
}

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

893
src/userland/gui/terminal.c Normal file
View File

@@ -0,0 +1,893 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include <stdlib.h>
#include <syscall.h>
#include "libc/libui.h"
#include <stdbool.h>
#include <stdint.h>
#define DEFAULT_COLS 116
#define DEFAULT_ROWS 41
#define SCROLLBACK_LINES 800
#define SCROLLBACK_COLS 256
#define CHAR_W 8
#define DEFAULT_LINE_H 10
#define TAB_BAR_H 20
#define CONTENT_PAD_BOTTOM 20
#define TAB_CLOSE_W 12
#define TAB_CLOSE_PAD 6
#define MAX_TABS 4
#define TTY_READ_CHUNK 512
typedef struct {
char c;
uint32_t color;
} CharCell;
typedef struct {
int tty_id;
int bsh_pid;
CharCell *cells;
CharCell *scrollback;
int *scroll_cols;
int scroll_head;
int scroll_count;
int scroll_offset;
int cursor_row;
int cursor_col;
uint32_t fg_color;
uint32_t bg_color;
int ansi_state;
int ansi_params[8];
int ansi_param_count;
int saved_row;
int saved_col;
} TerminalSession;
static ui_window_t g_win;
static TerminalSession g_tabs[MAX_TABS];
static int g_tab_count = 0;
static int g_active_tab = 0;
static int g_cols = DEFAULT_COLS;
static int g_rows = DEFAULT_ROWS;
static int g_line_h = DEFAULT_LINE_H;
static int g_win_w = DEFAULT_COLS * CHAR_W;
static int g_win_h = TAB_BAR_H + (DEFAULT_ROWS * DEFAULT_LINE_H);
static void str_copy(char *dst, const char *src, int max_len) {
int i = 0;
if (max_len <= 0) return;
while (i < max_len - 1 && src && src[i]) {
dst[i] = src[i];
i++;
}
dst[i] = 0;
}
static void str_append(char *dst, const char *src, int max_len) {
if (!dst || !src || max_len <= 0) return;
int dlen = (int)strlen(dst);
int i = 0;
while (dlen + i < max_len - 1 && src[i]) {
dst[dlen + i] = src[i];
i++;
}
dst[dlen + i] = 0;
}
static void trim_end(char *s) {
if (!s) return;
int len = (int)strlen(s);
while (len > 0 && (s[len - 1] == '\n' || s[len - 1] == '\r' || s[len - 1] == ' ' || s[len - 1] == '\t')) {
s[len - 1] = 0;
len--;
}
}
static void scrollback_init(TerminalSession *s) {
s->scroll_head = 0;
s->scroll_count = 0;
s->scroll_offset = 0;
if (s->scrollback) {
for (int i = 0; i < SCROLLBACK_LINES * SCROLLBACK_COLS; i++) {
s->scrollback[i].c = ' ';
s->scrollback[i].color = s->fg_color;
}
}
if (s->scroll_cols) {
for (int i = 0; i < SCROLLBACK_LINES; i++) s->scroll_cols[i] = 0;
}
}
static void scrollback_push_row(TerminalSession *s, const CharCell *row, int cols) {
if (!s->scrollback || !s->scroll_cols) return;
int idx = s->scroll_head;
CharCell *dest = s->scrollback + (idx * SCROLLBACK_COLS);
int copy_cols = cols;
if (copy_cols > SCROLLBACK_COLS) copy_cols = SCROLLBACK_COLS;
for (int i = 0; i < SCROLLBACK_COLS; i++) {
dest[i].c = ' ';
dest[i].color = s->fg_color;
}
for (int i = 0; i < copy_cols; i++) {
dest[i] = row[i];
}
s->scroll_cols[idx] = copy_cols;
s->scroll_head = (idx + 1) % SCROLLBACK_LINES;
if (s->scroll_count < SCROLLBACK_LINES) {
s->scroll_count++;
} else if (s->scroll_offset > 0) {
s->scroll_offset--;
}
if (s->scroll_offset > 0) {
s->scroll_offset++;
}
}
static int scrollback_max_offset(TerminalSession *s) {
int total = s->scroll_count + g_rows;
if (total <= g_rows) return 0;
return total - g_rows;
}
static void session_adjust_scroll(TerminalSession *s, int delta) {
if (!s) return;
int max_offset = scrollback_max_offset(s);
s->scroll_offset += delta;
if (s->scroll_offset < 0) s->scroll_offset = 0;
if (s->scroll_offset > max_offset) s->scroll_offset = max_offset;
}
static CharCell *scrollback_get_line(TerminalSession *s, int line_index, int *out_cols) {
if (!s || line_index < 0 || line_index >= s->scroll_count) return NULL;
int start = s->scroll_head - s->scroll_count;
if (start < 0) start += SCROLLBACK_LINES;
int idx = (start + line_index) % SCROLLBACK_LINES;
if (out_cols) *out_cols = s->scroll_cols[idx];
return s->scrollback + (idx * SCROLLBACK_COLS);
}
static uint32_t ansi_color_16(int idx) {
static uint32_t base[16] = {
0xFF000000, 0xFFAA0000, 0xFF00AA00, 0xFFAA5500,
0xFF0000AA, 0xFFAA00AA, 0xFF00AAAA, 0xFFAAAAAA,
0xFF555555, 0xFFFF5555, 0xFF55FF55, 0xFFFFFF55,
0xFF5555FF, 0xFFFF55FF, 0xFF55FFFF, 0xFFFFFFFF
};
if (idx < 0) idx = 0;
if (idx > 15) idx = 15;
return base[idx];
}
static void session_reset_colors(TerminalSession *s) {
s->fg_color = 0xFFFFFFFF;
s->bg_color = 0xFF1E1E1E;
}
static void session_clear(TerminalSession *s) {
for (int i = 0; i < g_cols * g_rows; i++) {
s->cells[i].c = ' ';
s->cells[i].color = s->fg_color;
}
s->cursor_row = 0;
s->cursor_col = 0;
s->scroll_offset = 0;
}
static void session_scroll(TerminalSession *s) {
int row_bytes = g_cols * (int)sizeof(CharCell);
if (g_rows > 0) {
scrollback_push_row(s, s->cells, g_cols);
}
memmove(s->cells, s->cells + g_cols, row_bytes * (g_rows - 1));
for (int i = 0; i < g_cols; i++) {
int idx = (g_rows - 1) * g_cols + i;
s->cells[idx].c = ' ';
s->cells[idx].color = s->fg_color;
}
s->cursor_row = g_rows - 1;
s->cursor_col = 0;
}
static void session_put_char(TerminalSession *s, char c) {
if (c == '\n') {
s->cursor_row++;
s->cursor_col = 0;
if (s->cursor_row >= g_rows) session_scroll(s);
return;
}
if (c == '\r') {
s->cursor_col = 0;
return;
}
if (c == '\b') {
if (s->cursor_col > 0) s->cursor_col--;
int idx = s->cursor_row * g_cols + s->cursor_col;
if (idx >= 0 && idx < g_cols * g_rows) {
s->cells[idx].c = ' ';
s->cells[idx].color = s->fg_color;
}
return;
}
if (c == '\t') {
int next = ((s->cursor_col / 4) + 1) * 4;
while (s->cursor_col < next) {
session_put_char(s, ' ');
}
return;
}
if (c < 32) return;
int idx = s->cursor_row * g_cols + s->cursor_col;
if (idx >= 0 && idx < g_cols * g_rows) {
s->cells[idx].c = c;
s->cells[idx].color = s->fg_color;
}
s->cursor_col++;
if (s->cursor_col >= g_cols) {
s->cursor_col = 0;
s->cursor_row++;
if (s->cursor_row >= g_rows) session_scroll(s);
}
}
static void ansi_handle_sgr(TerminalSession *s) {
if (s->ansi_param_count == 0) {
session_reset_colors(s);
return;
}
for (int i = 0; i < s->ansi_param_count; i++) {
int p = s->ansi_params[i];
if (p == 0) session_reset_colors(s);
else if (p >= 30 && p <= 37) s->fg_color = ansi_color_16(p - 30);
else if (p >= 90 && p <= 97) s->fg_color = ansi_color_16(8 + (p - 90));
else if (p >= 40 && p <= 47) s->bg_color = ansi_color_16(p - 40);
else if (p >= 100 && p <= 107) s->bg_color = ansi_color_16(8 + (p - 100));
else if (p == 38 || p == 48) {
if (i + 4 < s->ansi_param_count && s->ansi_params[i + 1] == 2) {
int r = s->ansi_params[i + 2] & 0xFF;
int g = s->ansi_params[i + 3] & 0xFF;
int b = s->ansi_params[i + 4] & 0xFF;
uint32_t color = 0xFF000000 | ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b;
if (p == 38) s->fg_color = color;
else s->bg_color = color;
i += 4;
}
}
}
}
static void ansi_finalize(TerminalSession *s, char cmd) {
if (cmd == 'm') {
ansi_handle_sgr(s);
} else if (cmd == 'J') {
int mode = s->ansi_param_count > 0 ? s->ansi_params[0] : 0;
if (mode == 2 || mode == 0) session_clear(s);
} else if (cmd == 'K') {
int row = s->cursor_row;
for (int col = s->cursor_col; col < g_cols; col++) {
int idx = row * g_cols + col;
s->cells[idx].c = ' ';
s->cells[idx].color = s->fg_color;
}
} else if (cmd == 'H' || cmd == 'f') {
int row = (s->ansi_param_count > 0) ? s->ansi_params[0] : 1;
int col = (s->ansi_param_count > 1) ? s->ansi_params[1] : 1;
if (row < 1) row = 1;
if (col < 1) col = 1;
s->cursor_row = row - 1;
s->cursor_col = col - 1;
} else if (cmd == 'A') {
int n = (s->ansi_param_count > 0) ? s->ansi_params[0] : 1;
s->cursor_row -= n;
if (s->cursor_row < 0) s->cursor_row = 0;
} else if (cmd == 'B') {
int n = (s->ansi_param_count > 0) ? s->ansi_params[0] : 1;
s->cursor_row += n;
if (s->cursor_row >= g_rows) s->cursor_row = g_rows - 1;
} else if (cmd == 'C') {
int n = (s->ansi_param_count > 0) ? s->ansi_params[0] : 1;
s->cursor_col += n;
if (s->cursor_col >= g_cols) s->cursor_col = g_cols - 1;
} else if (cmd == 'D') {
int n = (s->ansi_param_count > 0) ? s->ansi_params[0] : 1;
s->cursor_col -= n;
if (s->cursor_col < 0) s->cursor_col = 0;
} else if (cmd == 's') {
s->saved_row = s->cursor_row;
s->saved_col = s->cursor_col;
} else if (cmd == 'u') {
s->cursor_row = s->saved_row;
s->cursor_col = s->saved_col;
}
s->ansi_state = 0;
s->ansi_param_count = 0;
}
static void session_process_char(TerminalSession *s, char c) {
if (s->ansi_state == 0) {
if (c == 27) {
s->ansi_state = 1;
s->ansi_param_count = 0;
s->ansi_params[0] = 0;
return;
}
session_put_char(s, c);
return;
}
if (s->ansi_state == 1) {
if (c == '[') {
s->ansi_state = 2;
s->ansi_param_count = 0;
s->ansi_params[0] = 0;
return;
}
s->ansi_state = 0;
session_put_char(s, c);
return;
}
if (s->ansi_state == 2) {
if (c >= '0' && c <= '9') {
int idx = s->ansi_param_count;
s->ansi_params[idx] = s->ansi_params[idx] * 10 + (c - '0');
return;
}
if (c == ';') {
if (s->ansi_param_count < 7) {
s->ansi_param_count++;
s->ansi_params[s->ansi_param_count] = 0;
}
return;
}
s->ansi_param_count++;
ansi_finalize(s, c);
return;
}
}
static void session_process_output(TerminalSession *s, const char *buf, int len) {
for (int i = 0; i < len; i++) {
session_process_char(s, buf[i]);
}
}
static int get_tab_width(void) {
if (g_tab_count <= 0) return g_win_w;
int tab_w = g_win_w / g_tab_count;
if (tab_w < 60) tab_w = 60;
return tab_w;
}
static int read_proc_field(int pid, const char *field, char *out, int max_len) {
if (!out || max_len <= 0) return -1;
char path[64];
path[0] = 0;
str_append(path, "/proc/", sizeof(path));
char pid_buf[16];
itoa(pid, pid_buf);
str_append(path, pid_buf, sizeof(path));
str_append(path, "/", sizeof(path));
str_append(path, field, sizeof(path));
int fd = sys_open(path, "r");
if (fd < 0) return -1;
int bytes = sys_read(fd, out, max_len - 1);
sys_close(fd);
if (bytes <= 0) return -1;
out[bytes] = 0;
trim_end(out);
return 0;
}
static void truncate_label(const char *src, char *out, int max_chars) {
if (!out || max_chars <= 0) return;
int len = (int)strlen(src);
if (len <= max_chars) {
str_copy(out, src, max_chars + 1);
return;
}
if (max_chars <= 3) {
for (int i = 0; i < max_chars; i++) out[i] = src[i];
out[max_chars] = 0;
return;
}
for (int i = 0; i < max_chars - 3; i++) out[i] = src[i];
out[max_chars - 3] = '.';
out[max_chars - 2] = '.';
out[max_chars - 1] = '.';
out[max_chars] = 0;
}
static void get_tab_title(TerminalSession *s, char *out, int max_len) {
if (!s || !out) return;
int fg = sys_tty_get_fg(s->tty_id);
if (fg > 0) {
if (read_proc_field(fg, "name", out, max_len) == 0) return;
}
if (s->bsh_pid > 0) {
if (read_proc_field(s->bsh_pid, "cwd", out, max_len) == 0) return;
}
str_copy(out, "Bsh", max_len);
}
static int read_config_value(const char *key, char *out, int max_len) {
if (!key || !out || max_len <= 0) return -1;
int fd = sys_open("/Library/bsh/bshrc", "r");
if (fd < 0) return -1;
char buf[4096];
int bytes = sys_read(fd, buf, sizeof(buf) - 1);
sys_close(fd);
if (bytes <= 0) return -1;
buf[bytes] = 0;
char *line = buf;
while (*line) {
char *end = line;
while (*end && *end != '\n' && *end != '\r') end++;
char saved = *end;
*end = 0;
trim_end(line);
if (line[0] != '#' && line[0] != 0) {
char *sep = line;
while (*sep && *sep != '=') sep++;
if (*sep == '=') {
*sep = 0;
if (strcmp(line, key) == 0) {
str_copy(out, sep + 1, max_len);
return 0;
}
}
}
line = end + (saved ? 1 : 0);
if (saved == '\r' && *line == '\n') line++;
}
return -1;
}
static void resolve_bsh_path(char *out, int max_len) {
char path_line[256];
if (read_config_value("PATH", path_line, sizeof(path_line)) != 0) {
str_copy(path_line, "/bin", sizeof(path_line));
}
int i = 0;
int start = 0;
while (1) {
if (path_line[i] == ':' || path_line[i] == 0) {
int len = i - start;
if (len > 0) {
char base[128];
if (len >= (int)sizeof(base)) len = (int)sizeof(base) - 1;
for (int j = 0; j < len; j++) base[j] = path_line[start + j];
base[len] = 0;
char candidate[160];
candidate[0] = 0;
str_append(candidate, base, sizeof(candidate));
if (candidate[0] && candidate[strlen(candidate) - 1] != '/') str_append(candidate, "/", sizeof(candidate));
str_append(candidate, "bsh.elf", sizeof(candidate));
if (sys_exists(candidate)) {
str_copy(out, candidate, max_len);
return;
}
candidate[0] = 0;
str_append(candidate, base, sizeof(candidate));
if (candidate[0] && candidate[strlen(candidate) - 1] != '/') str_append(candidate, "/", sizeof(candidate));
str_append(candidate, "bsh", sizeof(candidate));
if (sys_exists(candidate)) {
str_copy(out, candidate, max_len);
return;
}
}
start = i + 1;
}
if (path_line[i] == 0) break;
i++;
}
str_copy(out, "/bin/bsh.elf", max_len);
}
static bool has_space(const char *s) {
if (!s) return false;
while (*s) {
if (*s == ' ') return true;
s++;
}
return false;
}
static void terminal_resize(int w, int h) {
int min_w = CHAR_W * 40;
int min_h = TAB_BAR_H + (g_line_h * 10);
if (w < min_w) w = min_w;
if (h < min_h) h = min_h;
g_win_w = w;
g_win_h = h;
int new_cols = w / CHAR_W;
int content_h = h - TAB_BAR_H - CONTENT_PAD_BOTTOM;
if (g_line_h <= 0) g_line_h = DEFAULT_LINE_H;
if (content_h < g_line_h) content_h = g_line_h;
int new_rows = content_h / g_line_h;
if (new_cols < 10) new_cols = 10;
if (new_rows < 5) new_rows = 5;
if (new_cols == g_cols && new_rows == g_rows) return;
int old_cols = g_cols;
int old_rows = g_rows;
g_cols = new_cols;
g_rows = new_rows;
for (int i = 0; i < g_tab_count; i++) {
TerminalSession *s = &g_tabs[i];
int old_scroll_count = s->scroll_count;
int old_scroll_offset = s->scroll_offset;
int old_total_lines = old_scroll_count + old_rows;
int old_bottom_line = old_total_lines - 1 - old_scroll_offset;
int old_top_line = old_bottom_line - (old_rows - 1);
CharCell *old_cells = s->cells;
int old_cursor_row = s->cursor_row;
int old_cursor_col = s->cursor_col;
s->cells = (CharCell *)malloc(sizeof(CharCell) * g_cols * g_rows);
for (int j = 0; j < g_cols * g_rows; j++) {
s->cells[j].c = ' ';
s->cells[j].color = s->fg_color;
}
int row_start = 0;
if (old_rows > g_rows) {
if (old_cursor_row >= g_rows) {
row_start = old_cursor_row - (g_rows - 1);
} else {
row_start = 0;
}
int max_start = old_rows - g_rows;
if (row_start > max_start) row_start = max_start;
if (row_start < 0) row_start = 0;
}
int dropped = 0;
if (row_start > 0) {
int projected = old_scroll_count + row_start;
if (projected > SCROLLBACK_LINES) dropped = projected - SCROLLBACK_LINES;
}
int desired_top_line = old_top_line - dropped;
if (desired_top_line < 0) desired_top_line = 0;
for (int r = 0; r < row_start; r++) {
scrollback_push_row(s, old_cells + (r * old_cols), old_cols);
}
int copy_rows = old_rows;
if (copy_rows > g_rows) copy_rows = g_rows;
int copy_cols = old_cols < g_cols ? old_cols : g_cols;
for (int r = 0; r < copy_rows; r++) {
CharCell *src = old_cells + ((row_start + r) * old_cols);
CharCell *dst = s->cells + (r * g_cols);
for (int c = 0; c < copy_cols; c++) {
dst[c] = src[c];
}
}
s->cursor_row = old_cursor_row - row_start;
if (old_rows <= g_rows) s->cursor_row = old_cursor_row;
if (s->cursor_row < 0) s->cursor_row = 0;
if (s->cursor_row >= g_rows) s->cursor_row = g_rows - 1;
s->cursor_col = old_cursor_col;
if (s->cursor_col < 0) s->cursor_col = 0;
if (s->cursor_col >= g_cols) s->cursor_col = g_cols - 1;
if (old_scroll_offset == 0) {
s->scroll_offset = 0;
} else {
int new_total_lines = s->scroll_count + g_rows;
int desired_bottom = desired_top_line + (g_rows - 1);
s->scroll_offset = (new_total_lines - 1) - desired_bottom;
}
session_adjust_scroll(s, 0);
if (old_cells) free(old_cells);
}
}
static void draw_tabs(void) {
if (g_tab_count <= 0) return;
ui_draw_rect(g_win, 0, 0, g_win_w, TAB_BAR_H, 0xFF1A1A1A);
int tab_w = get_tab_width();
for (int i = 0; i < g_tab_count; i++) {
int x = i * tab_w;
uint32_t bg = (i == g_active_tab) ? 0xFF333333 : 0xFF222222;
ui_draw_rect(g_win, x, 0, tab_w - 2, TAB_BAR_H, bg);
int close_size = TAB_CLOSE_W;
int close_x = x + tab_w - TAB_CLOSE_PAD - close_size;
int close_y = (TAB_BAR_H - close_size) / 2;
uint32_t close_bg = (i == g_active_tab) ? 0xFF444444 : 0xFF333333;
ui_draw_rect(g_win, close_x, close_y, close_size, close_size, close_bg);
ui_draw_string(g_win, close_x + 2, close_y + 1, "x", 0xFFFFFFFF);
char title[64];
char label[64];
get_tab_title(&g_tabs[i], title, sizeof(title));
int text_w = tab_w - 10 - (TAB_CLOSE_PAD + TAB_CLOSE_W);
int max_chars = text_w / CHAR_W;
if (max_chars < 4) max_chars = 4;
truncate_label(title, label, max_chars);
ui_draw_string(g_win, x + 6, 4, label, 0xFFFFFFFF);
}
}
static void draw_session(TerminalSession *s) {
int base_y = TAB_BAR_H;
ui_draw_rect(g_win, 0, base_y, g_win_w, g_win_h - base_y, s->bg_color);
int max_offset = scrollback_max_offset(s);
if (s->scroll_offset > max_offset) s->scroll_offset = max_offset;
int total_lines = s->scroll_count + g_rows;
int bottom_line = total_lines - 1 - s->scroll_offset;
int top_line = bottom_line - (g_rows - 1);
for (int row = 0; row < g_rows; row++) {
int line_index = top_line + row;
CharCell *line = NULL;
int line_cols = 0;
if (line_index >= 0 && line_index < s->scroll_count) {
line = scrollback_get_line(s, line_index, &line_cols);
} else if (line_index >= s->scroll_count && line_index < total_lines) {
int live_row = line_index - s->scroll_count;
if (live_row >= 0 && live_row < g_rows) {
line = s->cells + (live_row * g_cols);
line_cols = g_cols;
}
}
for (int col = 0; col < g_cols; col++) {
char ch = ' ';
uint32_t color = s->fg_color;
if (line && col < line_cols) {
ch = line[col].c;
if (ch == 0) ch = ' ';
color = line[col].color;
}
char str[2] = { ch, 0 };
int x = col * CHAR_W;
int y = base_y + row * g_line_h;
ui_draw_string_bitmap(g_win, x, y, str, color);
}
}
if (s->scroll_offset == 0) {
int cx = s->cursor_col * CHAR_W;
int cy = base_y + s->cursor_row * g_line_h;
ui_draw_rect(g_win, cx, cy + g_line_h - 2, CHAR_W, 2, 0xFFFFFFFF);
}
ui_mark_dirty(g_win, 0, 0, g_win_w, g_win_h);
}
static void tab_init(TerminalSession *s, int tty_id, int bsh_pid) {
s->tty_id = tty_id;
s->bsh_pid = bsh_pid;
s->cells = (CharCell *)malloc(sizeof(CharCell) * g_cols * g_rows);
s->scrollback = (CharCell *)malloc(sizeof(CharCell) * SCROLLBACK_LINES * SCROLLBACK_COLS);
s->scroll_cols = (int *)malloc(sizeof(int) * SCROLLBACK_LINES);
s->cursor_row = 0;
s->cursor_col = 0;
s->ansi_state = 0;
s->ansi_param_count = 0;
s->saved_row = 0;
s->saved_col = 0;
session_reset_colors(s);
scrollback_init(s);
session_clear(s);
}
static void tab_free(TerminalSession *s) {
if (!s) return;
if (s->cells) {
free(s->cells);
s->cells = NULL;
}
if (s->scrollback) {
free(s->scrollback);
s->scrollback = NULL;
}
if (s->scroll_cols) {
free(s->scroll_cols);
s->scroll_cols = NULL;
}
}
static void close_tab(int idx) {
if (idx < 0 || idx >= g_tab_count) return;
TerminalSession *s = &g_tabs[idx];
sys_tty_kill_all(s->tty_id);
sys_tty_destroy(s->tty_id);
tab_free(s);
for (int i = idx; i < g_tab_count - 1; i++) {
g_tabs[i] = g_tabs[i + 1];
}
g_tab_count--;
if (g_tab_count <= 0) {
sys_exit(0);
}
if (g_active_tab > idx) {
g_active_tab--;
} else if (g_active_tab == idx) {
if (g_active_tab >= g_tab_count) g_active_tab = g_tab_count - 1;
}
}
static int create_tab(void) {
if (g_tab_count >= MAX_TABS) return -1;
int tty_id = sys_tty_create();
if (tty_id < 0) return -1;
char start_dir[256];
start_dir[0] = 0;
if (g_tab_count > 0) {
TerminalSession *active = &g_tabs[g_active_tab];
if (active->bsh_pid > 0) {
read_proc_field(active->bsh_pid, "cwd", start_dir, sizeof(start_dir));
}
}
char args[32];
args[0] = 0;
str_append(args, "-t ", sizeof(args));
char id_buf[8];
itoa(tty_id, id_buf);
str_append(args, id_buf, sizeof(args));
if (start_dir[0]) {
str_append(args, " -d ", sizeof(args));
if (has_space(start_dir)) {
str_append(args, "\"", sizeof(args));
str_append(args, start_dir, sizeof(args));
str_append(args, "\"", sizeof(args));
} else {
str_append(args, start_dir, sizeof(args));
}
}
char bsh_path[128];
resolve_bsh_path(bsh_path, sizeof(bsh_path));
int pid = sys_spawn(bsh_path, args, SPAWN_FLAG_TERMINAL | SPAWN_FLAG_TTY_ID, tty_id);
if (pid < 0) return -1;
tab_init(&g_tabs[g_tab_count], tty_id, pid);
g_tab_count++;
return g_tab_count - 1;
}
static void handle_key(gui_event_t *ev) {
TerminalSession *s = &g_tabs[g_active_tab];
char c = (char)ev->arg1;
bool ctrl = ev->arg3 != 0;
if (ctrl && c == 't') {
int idx = create_tab();
if (idx >= 0) g_active_tab = idx;
return;
}
if (ctrl && c == 20) {
if (g_tab_count > 0) g_active_tab = (g_active_tab + 1) % g_tab_count;
return;
}
if (ctrl && c == 19) {
if (g_tab_count > 0) g_active_tab = (g_active_tab + g_tab_count - 1) % g_tab_count;
return;
}
if (ctrl && (c == 'c' || c == 'C')) {
int fg = sys_tty_get_fg(s->tty_id);
if (fg > 0) {
sys_tty_kill_fg(s->tty_id);
sys_tty_set_fg(s->tty_id, 0);
return;
}
char ch = 3;
sys_tty_write_in(s->tty_id, &ch, 1);
return;
}
sys_tty_write_in(s->tty_id, &c, 1);
}
int main(void) {
g_win = ui_window_create("Terminal", 60, 60, g_win_w, g_win_h);
ui_window_set_resizable(g_win, true);
int fh = (int)ui_get_font_height();
if (fh > 0) g_line_h = fh;
terminal_resize(g_win_w, g_win_h);
int idx = create_tab();
if (idx < 0) return 1;
g_active_tab = idx;
gui_event_t ev;
char out_buf[TTY_READ_CHUNK];
while (1) {
bool dirty = false;
for (int i = 0; i < g_tab_count; i++) {
TerminalSession *s = &g_tabs[i];
int read = 0;
while ((read = sys_tty_read_out(s->tty_id, out_buf, sizeof(out_buf))) > 0) {
session_process_output(s, out_buf, read);
if (i == g_active_tab) dirty = true;
}
}
while (ui_get_event(g_win, &ev)) {
if (ev.type == GUI_EVENT_CLOSE) {
for (int i = 0; i < g_tab_count; i++) {
if (g_tabs[i].bsh_pid > 0) sys_kill(g_tabs[i].bsh_pid);
}
sys_exit(0);
} else if (ev.type == GUI_EVENT_KEY) {
handle_key(&ev);
dirty = true;
} else if (ev.type == GUI_EVENT_CLICK) {
if (ev.arg2 < TAB_BAR_H) {
int tab_w = get_tab_width();
int tab = ev.arg1 / tab_w;
if (tab >= 0 && tab < g_tab_count) {
int close_size = TAB_CLOSE_W;
int close_x = tab * tab_w + tab_w - TAB_CLOSE_PAD - close_size;
int close_y = (TAB_BAR_H - close_size) / 2;
if (ev.arg1 >= close_x && ev.arg1 < close_x + close_size &&
ev.arg2 >= close_y && ev.arg2 < close_y + close_size) {
close_tab(tab);
} else {
g_active_tab = tab;
}
dirty = true;
}
}
} else if (ev.type == GUI_EVENT_MOUSE_WHEEL) {
int lines = ev.arg1 * 3;
if (lines != 0) {
session_adjust_scroll(&g_tabs[g_active_tab], lines);
dirty = true;
}
} else if (ev.type == GUI_EVENT_RESIZE) {
terminal_resize(ev.arg1, ev.arg2);
dirty = true;
} else if (ev.type == GUI_EVENT_PAINT) {
dirty = true;
}
}
if (dirty) {
draw_tabs();
draw_session(&g_tabs[g_active_tab]);
} else {
sys_yield();
}
}
return 0;
}

View File

@@ -40,6 +40,46 @@ static void editor_strcpy(char *dest, const char *src) {
*dest = 0; *dest = 0;
} }
static int editor_strlen(const char *s) {
int len = 0;
while (s && s[len]) len++;
return len;
}
static void editor_strncat(char *dst, const char *src, int max_len) {
if (!dst || !src || max_len <= 0) return;
int dlen = editor_strlen(dst);
int i = 0;
while (dlen + i < max_len - 1 && src[i]) {
dst[dlen + i] = src[i];
i++;
}
dst[dlen + i] = 0;
}
static void editor_resolve_path(const char *input, char *out, int max_len) {
if (!out || max_len <= 0) return;
if (!input || input[0] == 0) {
out[0] = 0;
return;
}
if (input[0] == '/') {
editor_strcpy(out, input);
return;
}
char cwd[256];
if (sys_getcwd(cwd, sizeof(cwd)) < 0) {
editor_strcpy(out, input);
return;
}
editor_strcpy(out, cwd);
int len = editor_strlen(out);
if (len > 0 && out[len - 1] != '/') editor_strncat(out, "/", max_len);
editor_strncat(out, input, max_len);
}
static void editor_clear_all(void) { static void editor_clear_all(void) {
for (int i = 0; i < EDITOR_MAX_LINES; i++) { for (int i = 0; i < EDITOR_MAX_LINES; i++) {
for (int j = 0; j < EDITOR_MAX_LINE_LEN; j++) { for (int j = 0; j < EDITOR_MAX_LINE_LEN; j++) {
@@ -71,9 +111,11 @@ static void editor_ensure_cursor_visible(void) {
void editor_open_file(const char *filename) { void editor_open_file(const char *filename) {
editor_clear_all(); editor_clear_all();
editor_strcpy(open_filename, filename); char resolved[256];
editor_resolve_path(filename, resolved, sizeof(resolved));
editor_strcpy(open_filename, resolved);
int fd = sys_open(filename, "r"); int fd = sys_open(resolved, "r");
if (fd < 0) { if (fd < 0) {
file_modified = 0; file_modified = 0;
return; return;

View File

@@ -41,6 +41,40 @@ static int viewer_strlen(const char *s) {
return len; return len;
} }
static void viewer_strncat(char *dst, const char *src, int max_len) {
if (!dst || !src || max_len <= 0) return;
int dlen = viewer_strlen(dst);
int i = 0;
while (dlen + i < max_len - 1 && src[i]) {
dst[dlen + i] = src[i];
i++;
}
dst[dlen + i] = 0;
}
static void viewer_resolve_path(const char *input, char *out, int max_len) {
if (!out || max_len <= 0) return;
if (!input || input[0] == 0) {
out[0] = 0;
return;
}
if (input[0] == '/') {
viewer_strcpy(out, input);
return;
}
char cwd[256];
if (sys_getcwd(cwd, sizeof(cwd)) < 0) {
viewer_strcpy(out, input);
return;
}
viewer_strcpy(out, cwd);
int len = viewer_strlen(out);
if (len > 0 && out[len - 1] != '/') viewer_strncat(out, "/", max_len);
viewer_strncat(out, input, max_len);
}
static void viewer_scale_rgba_to_argb(const unsigned char *rgba, int src_w, int src_h, static void viewer_scale_rgba_to_argb(const unsigned char *rgba, int src_w, int src_h,
uint32_t *dst, int dst_w, int dst_h) { uint32_t *dst, int dst_w, int dst_h) {
if (src_w == dst_w && src_h == dst_h) { if (src_w == dst_w && src_h == dst_h) {
@@ -141,7 +175,9 @@ static void viewer_paint(ui_window_t win) {
} }
void viewer_open_file(const char *path) { void viewer_open_file(const char *path) {
int fd = sys_open(path, "r"); char resolved[256];
viewer_resolve_path(path, resolved, sizeof(resolved));
int fd = sys_open(resolved, "r");
if (fd < 0) return; if (fd < 0) return;
uint32_t file_size = sys_size(fd); uint32_t file_size = sys_size(fd);
@@ -250,13 +286,13 @@ void viewer_open_file(const char *path) {
stbi_image_free(rgba); stbi_image_free(rgba);
free(buf); free(buf);
viewer_strcpy(viewer_file_path, path); viewer_strcpy(viewer_file_path, resolved);
const char *fname = path; const char *fname = resolved;
int plen = viewer_strlen(path); int plen = viewer_strlen(resolved);
for (int i = plen - 1; i >= 0; i--) { for (int i = plen - 1; i >= 0; i--) {
if (path[i] == '/') { if (resolved[i] == '/') {
fname = &path[i + 1]; fname = &resolved[i + 1];
break; break;
} }
} }

157
src/userland/libc/libmath.c Normal file
View File

@@ -0,0 +1,157 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "math.h"
static double _pow_int(double b, int e) {
if (e == 0) return 1.0;
if (e < 0) { return 1.0 / _pow_int(b, -e); }
double r = 1.0;
while (e > 0) {
if (e & 1) r *= b;
b *= b;
e >>= 1;
}
return r;
}
double fabs(double x) {
return x < 0.0 ? -x : x;
}
double fmod(double x, double y) {
if (y == 0.0) return 0.0;
return x - (int)(x / y) * y;
}
double floor(double x) {
int i = (int)x;
return (x < 0.0 && (double)i != x) ? (double)(i - 1) : (double)i;
}
double ceil(double x) {
int i = (int)x;
return (x > 0.0 && (double)i != x) ? (double)(i + 1) : (double)i;
}
double sin(double x) {
x = fmod(x, 2.0 * M_PI);
if (x > M_PI) x -= 2.0 * M_PI;
if (x < -M_PI) x += 2.0 * M_PI;
double x2 = x * x;
double term = x;
double sum = x;
for (int i = 1; i <= 8; i++) {
term *= -x2 / ((2*i) * (2*i + 1));
sum += term;
}
return sum;
}
double cos(double x) {
return sin(x + M_PI / 2.0);
}
double tan(double x) {
double c = cos(x);
if (fabs(c) < 1e-10) return 1e15;
return sin(x) / c;
}
double sqrt(double x) {
if (x <= 0.0) return 0.0;
double g = x * 0.5;
for (int i = 0; i < 25; i++) {
g = (g + x / g) * 0.5;
}
return g;
}
double log(double x) {
if (x <= 0.0) return -1e30;
int e = 0;
while (x > 2.0) { x /= 2.0; e++; }
while (x < 0.5) { x *= 2.0; e--; }
double t = (x - 1.0) / (x + 1.0);
double t2 = t * t;
double sum = t, term = t;
for (int i = 1; i <= 20; i++) {
term *= t2;
sum += term / (2*i + 1);
}
return 2.0 * sum + e * M_LN2;
}
double log2(double x) {
// log2(x) = ln(x) / ln(2)
return log(x) / M_LN2;
}
double log10(double x) {
return log(x) / 2.302585092994046;
}
double exp(double x) {
if (x > 700.0) return 1e300;
if (x < -700.0) return 0.0;
int k = (int)(x / M_LN2);
if (x < 0.0 && (double)k * M_LN2 > x) k--;
double r = x - (double)k * M_LN2;
double sum = 1.0, term = 1.0;
for (int i = 1; i <= 20; i++) {
term *= r / (double)i;
sum += term;
}
double result = sum;
if (k >= 0) { for (int i = 0; i < k; i++) result *= 2.0; }
else { for (int i = 0; i < -k; i++) result /= 2.0; }
return result;
}
double pow(double base, double exponent) {
if (base == 0.0) return 0.0;
if (exponent == 0.0) return 1.0;
int ie = (int)exponent;
if ((double)ie == exponent) return _pow_int(base, ie);
if (base < 0.0) return 0.0;
return exp(exponent * log(base));
}
double sinh(double x) {
double ep = exp(x);
double em = exp(-x);
return (ep - em) * 0.5;
}
double cosh(double x) {
double ep = exp(x);
double em = exp(-x);
return (ep + em) * 0.5;
}
double tanh(double x) {
double e2 = exp(2.0 * x);
return (e2 - 1.0) / (e2 + 1.0);
}
double hypot(double x, double y) {
return sqrt(x*x + y*y);
}
double fmin(double a, double b) {
return a < b ? a : b;
}
double fmax(double a, double b) {
return a > b ? a : b;
}
double fclamp(double x, double lo, double hi) {
if (x < lo) return lo;
if (x > hi) return hi;
return x;
}

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