41 Commits

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
105 changed files with 11362 additions and 4395 deletions

3
.gitignore vendored
View File

@@ -29,4 +29,5 @@ limine
**/.DS_Store
.DS_Store
/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/Fonts/Emoji
mkdir -p $(BUILD_DIR)/initrd/Library/DOOM
mkdir -p $(BUILD_DIR)/initrd/Library/bsh
mkdir -p $(BUILD_DIR)/initrd/docs
@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 \
if [ -f "$$f" ]; then cp "$$f" $(BUILD_DIR)/initrd/Library/Fonts/Emoji/; fi \
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
@for f in $$(find docs -name '*.md' 2>/dev/null); do \
if [ -f "$$f" ]; then \
@@ -166,6 +170,7 @@ $(BUILD_DIR)/initrd.tar: $(KERNEL_ELF)
done
@if [ -f README.md ]; then cp README.md $(BUILD_DIR)/initrd/; fi
@if [ -f LICENSE ]; then cp LICENSE $(BUILD_DIR)/initrd/; fi
@if [ -f limine.conf ]; then cp limine.conf $(BUILD_DIR)/initrd/; fi
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)
$(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 \
-smp 4 \
-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 \
-display cocoa,show-cursor=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

@@ -29,7 +29,7 @@ BoredOS is a x86_64 operating system featuring a custom Desktop Environment (DE)
* **Multiboot2 Compliant:** Bootable on real hardware and modern emulators.
* **Kernel Core:** Interrupt Descriptor Table (IDT) management and a robust syscall interface.
* **Filesystem:** Full **FAT32** support for persistent and in-memory storage.
* **Networking:** Includes the lwIP networking stack.
* **Networking:** Includes the lwIP networking stack and a basic web browser.
### Graphical User Interface
* **BoredWM:** A custom Window Manager with drag-and-drop, mouse-centered interaction.

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
@@ -32,4 +32,12 @@ The SDK and toolchain guides for creating your own `.elf` userland binaries.
- [`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.
---

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
protocol: limine
path: boot():/boredos.elf
cmdline: -v

View File

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

View File

@@ -15,15 +15,14 @@ section .text
; R9 = arg5
syscall_entry:
; 1. Switch to Kernel Stack safely
; 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]
swapgs
mov [gs:40], rsp
mov rsp, [gs:48]
; 2. Build iretq frame (compatible with registers_t)
; 2. Build iretq frame
push 0x1B ; SS (User Data)
push qword [rel user_rsp_scratch] ; RSP
push qword [gs:40] ; RSP
push r11 ; RFLAGS (captured by syscall)
push 0x23 ; CS (User Code)
push rcx ; RIP (return address from syscall)
@@ -81,14 +80,7 @@ syscall_entry:
pop rax
add rsp, 16 ; drop int_no/err_code
; Debug: check RIP before iretq
; We can't easily print from here without destroying registers,
; but we can at least check if it's canonical.
swapgs
iretq
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;
}
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) {
while (*src) *dest++ = *src++;
*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);
size_t k_strlen(const char *str);
int k_strcmp(const char *s1, const char *s2);
int k_strncmp(const char *s1, const char *s2, size_t n);
void k_strcpy(char *dest, const char *src);
int k_atoi(const char *str);
void k_itoa(int n, char *buf);
@@ -25,5 +26,6 @@ void k_reboot(void);
void k_shutdown(void);
void k_beep(int freq, int ms);
void k_beep_process(void);
char *k_strstr(const char *haystack, const char *needle);
#endif

View File

@@ -17,12 +17,23 @@
#include "io.h"
#include "fat32.h"
#include "tar.h"
#include "vfs.h"
#include "core/kconsole.h"
#include "core/kutils.h"
#include "memory_manager.h"
#include "platform.h"
#include "wallpaper.h"
#include "smp.h"
#include "work_queue.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 ---
__attribute__((used, section(".requests")))
@@ -53,12 +64,26 @@ static volatile struct limine_smp_request smp_request = {
.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")))
static volatile struct limine_request *const requests_start_marker[] = {
(struct limine_request *)&framebuffer_request,
(struct limine_request *)&memmap_request,
(struct limine_request *)&module_request,
(struct limine_request *)&smp_request,
(struct limine_request *)&bootloader_info_request,
(struct limine_request *)&kernel_file_request,
NULL
};
@@ -84,26 +109,70 @@ static void init_serial() {
outb(0x3F8 + 4, 0x0B);
}
static spinlock_t serial_lock = SPINLOCK_INIT;
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);
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) {
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);
outb(0x3F8, '0' + (n % 10));
outb(0x3F8, c);
kconsole_putc(c);
}
void serial_write_hex(uint64_t n) {
char *hex = "0123456789ABCDEF";
if (n >= 16) serial_write_hex(n / 16);
while ((inb(0x3F8 + 5) & 0x20) == 0);
outb(0x3F8, hex[n % 16]);
uint64_t flags = spinlock_acquire_irqsave(&serial_lock);
serial_write_hex_locked(n);
spinlock_release_irqrestore(&serial_lock, flags);
}
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
static void fat32_mkdir_recursive(const char *path) {
@@ -133,62 +202,74 @@ static void fat32_mkdir_recursive(const char *path) {
void kmain(void) {
init_serial();
serial_write("\n[DEBUG] Entering kmain...\n");
vfs_init();
serial_write("\n");
platform_init();
serial_write("[DEBUG] platform_init OK\n");
log_ok("Platform initialized");
extern uint64_t hhdm_offset;
extern uint64_t kernel_phys_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("\n");
serial_write("[DEBUG] Kernel Phys: 0x");
serial_write("[INIT] Kernel Phys: 0x");
serial_write_hex(kernel_phys_base);
serial_write("\n");
serial_write("[DEBUG] Kernel Virt: 0x");
serial_write("[INIT] Kernel Virt: 0x");
serial_write_hex(kernel_virt_base);
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) {
serial_write("[DEBUG] No framebuffer! Halting.\n");
serial_write("[INIT] No framebuffer! Halting.\n");
hcf();
}
struct limine_framebuffer *fb = framebuffer_request.response->framebuffers[0];
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();
serial_write("[DEBUG] gdt_init OK\n");
log_ok("GDT initialized");
paging_init();
serial_write("[DEBUG] paging_init OK\n");
log_ok("Paging ready");
syscall_init();
serial_write("[DEBUG] syscall_init OK\n");
log_ok("Syscalls ready");
idt_init();
idt_register_interrupts();
idt_load();
serial_write("[DEBUG] idt_init OK\n");
log_ok("IDT ready");
process_init();
fat32_init();
serial_write("[DEBUG] fat32_init OK\n");
log_ok("FAT32 ready");
fat32_mkdir("/bin");
fat32_mkdir("/Library");
fat32_mkdir("/Library/images");
@@ -196,14 +277,68 @@ void kmain(void) {
fat32_mkdir("/Library/images/gif");
fat32_mkdir("/Library/Fonts");
fat32_mkdir("/Library/DOOM");
fat32_mkdir("/Library/bsh");
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) {
serial_write("[DEBUG] ERROR: Limine Module Response is NULL!\n");
log_fail("Limine module response NULL");
} else {
serial_write("[DEBUG] Limine Module Response found. Count: ");
serial_write_num(module_request.response->module_count);
serial_write("\n");
log_ok("Limine modules loaded");
for (uint64_t i = 0; i < module_request.response->module_count; i++) {
struct limine_file *mod = module_request.response->modules[i];
@@ -215,7 +350,7 @@ void kmain(void) {
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') {
serial_write("[DEBUG] Parsing TAR initrd: ");
serial_write("[INIT] Parsing TAR initrd: ");
serial_write(clean_path);
serial_write("\n");
tar_parse(mod->address, mod->size);
@@ -237,13 +372,13 @@ void kmain(void) {
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;
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("\n");
@@ -253,24 +388,22 @@ void kmain(void) {
ps2_init();
asm("sti");
// Initialize LAPIC for IPI support
lapic_init();
// Initialize SMP — bring up all CPU cores
if (smp_request.response != NULL) {
uint32_t online = smp_init(smp_request.response);
serial_write("[DEBUG] SMP init complete, CPUs online: ");
serial_write_num(online);
serial_write("\n");
log_ok("SMP initialized");
} else {
serial_write("[DEBUG] No SMP response from bootloader\n");
// Still init as single-CPU
serial_write("[INIT] No SMP response from bootloader\n");
smp_init(NULL);
}
wm_init();
asm volatile("sti");
extern void bootfs_refresh_from_disk(void);
bootfs_refresh_from_disk();
while (1) {
wm_process_input();

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) {
char path[128] = "A:/Library/man/";
int i = 15;
char path[128] = "/Library/man/";
int i = 13;
while (*name) path[i++] = *name++;
path[i++] = '.';
path[i++] = 't';
@@ -31,8 +31,8 @@ static void write_man_file(const char *name, const char *content) {
}
void create_man_entries(void) {
fat32_mkdir("A:/Library");
fat32_mkdir("A:/Library/man");
fat32_mkdir("/Library");
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("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("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("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("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.");
@@ -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("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("2048", "2048 - Classic game\n\nUsage: 2048\n\nPlays the classic 2048 game.");
}
#endif

View File

@@ -4,6 +4,8 @@
#include <stdint.h>
#include "limine.h"
#include <stddef.h>
#include "platform.h"
#include "kutils.h"
static volatile struct limine_hhdm_request hhdm_request __attribute__((used, section(".requests"))) = {
.id = LIMINE_HHDM_REQUEST,
.revision = 0,
@@ -69,3 +71,83 @@ void platform_get_cpu_model(char *model) {
}
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>
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);
uint64_t p2v(uint64_t phys);
uint64_t v2p(uint64_t virt);
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

View File

@@ -13,10 +13,10 @@ void get_os_info(os_info_t *info) {
for (size_t i = 0; i < sizeof(os_info_t); i++) p[i] = 0;
const char *os_name = "BoredOS";
const char *os_version = "26.4";
const char *os_codename = "Geometry";
const char *os_version = "26.4.2";
const char *os_codename = "Voyager";
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_time = __TIME__;
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>
#define SECTOR_SIZE 512
#define MAX_DISKS 16
typedef enum {
DISK_TYPE_RAM,
@@ -17,26 +18,45 @@ typedef enum {
} DiskType;
typedef struct Disk {
char letter;
char devname[16]; // Device name: "sda", "sdb", "sda1", etc.
DiskType type;
bool is_fat32;
char name[32];
uint32_t partition_lba_offset; // LBA offset of FAT32 partition (0 for raw)
char label[32]; // Human-readable label
uint32_t partition_lba_offset; // LBA offset of partition (0 for whole disk)
uint32_t total_sectors; // Total sectors on this device/partition
// Function pointers for driver operations
int (*read_sector)(struct Disk *disk, uint32_t sector, uint8_t *buffer);
int (*write_sector)(struct Disk *disk, uint32_t sector, const uint8_t *buffer);
// 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;
// Initialization and scanning
void disk_manager_init(void);
void disk_manager_scan(void); // Scans for new disks
Disk* disk_get_by_letter(char letter);
char disk_get_next_free_letter(void);
void disk_manager_scan(void);
// Device registration
void disk_register(Disk *disk);
void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_count,
bool is_fat32, int part_num);
// Lookup
Disk* disk_get_by_name(const char *devname);
int disk_get_count(void);
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

View File

@@ -5,15 +5,44 @@
#include "pci.h"
#include "memory_manager.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>
#define MAX_DISKS 26
static spinlock_t ide_lock = SPINLOCK_INIT;
static Disk *disks[MAX_DISKS];
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_CTRL 0x3F6
@@ -35,101 +64,91 @@ static int disk_count = 0;
#define ATA_CMD_WRITE_PIO 0x30
#define ATA_CMD_IDENTIFY 0xEC
#define ATA_SR_BSY 0x80 // Busy
#define ATA_SR_DRDY 0x40 // Drive ready
#define ATA_SR_DF 0x20 // Drive write fault
#define ATA_SR_DSC 0x10 // Drive seek complete
#define ATA_SR_DRQ 0x08 // Data request ready
#define ATA_SR_CORR 0x04 // Corrected data
#define ATA_SR_IDX 0x02 // Index
#define ATA_SR_ERR 0x01 // Error
#define ATA_SR_BSY 0x80
#define ATA_SR_DRDY 0x40
#define ATA_SR_DF 0x20
#define ATA_SR_DSC 0x10
#define ATA_SR_DRQ 0x08
#define ATA_SR_CORR 0x04
#define ATA_SR_IDX 0x02
#define ATA_SR_ERR 0x01
typedef struct {
uint16_t port_base;
bool slave;
} ATADriverData;
// === Helpers ===
// === ATA PIO Driver ===
static void dm_strcpy(char *dest, const char *src) {
while (*src) *dest++ = *src++;
*dest = 0;
static int ata_wait_bsy(uint16_t port_base) {
int timeout = 10000000;
while ((inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY) && --timeout > 0);
return timeout <= 0 ? -1 : 0;
}
void disk_register(Disk *disk);
static int ramdisk_read(Disk *disk, uint32_t sector, uint8_t *buffer) {
(void)disk; (void)sector; (void)buffer;
return 0;
}
static int ramdisk_write(Disk *disk, uint32_t sector, const uint8_t *buffer) {
(void)disk; (void)sector; (void)buffer;
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);
if (timeout <= 0 || (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR)) return -1;
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) {
// Select Drive
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_LBA0, 0);
outb(port_base + ATA_REG_LBA1, 0);
outb(port_base + ATA_REG_LBA2, 0);
// Send Identify command
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);
if (status == 0) return 0;
// Wait until BSY clears
int timeout = 10000;
while ((inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY) && --timeout > 0) {
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
// 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 (timeout <= 0) 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++) {
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) {
ATADriverData *data = (ATADriverData*)disk->driver_data;
uint16_t port_base = data->port_base;
bool slave = data->slave;
ata_wait_bsy(port_base);
// Select drive and send highest 4 bits of LBA
// 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;
}
outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
outb(port_base + ATA_REG_FEATURES, 0x00);
outb(port_base + ATA_REG_SEC_COUNT0, 1);
@@ -137,25 +156,45 @@ static int ata_read_sector(Disk *disk, uint32_t lba, uint8_t *buffer) {
outb(port_base + ATA_REG_LBA1, (uint8_t)(lba >> 8));
outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16));
outb(port_base + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
ata_wait_bsy(port_base);
ata_wait_drq(port_base);
if (ata_wait_bsy(port_base) != 0) {
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;
for (int i = 0; i < 256; i++) {
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) {
ATADriverData *data = (ATADriverData*)disk->driver_data;
uint16_t port_base = data->port_base;
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_FEATURES, 0x00);
outb(port_base + ATA_REG_SEC_COUNT0, 1);
@@ -163,75 +202,133 @@ static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) {
outb(port_base + ATA_REG_LBA1, (uint8_t)(lba >> 8));
outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16));
outb(port_base + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
ata_wait_bsy(port_base);
ata_wait_drq(port_base);
if (ata_wait_bsy(port_base) != 0) {
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;
for (int i = 0; i < 256; i++) {
outw(port_base + ATA_REG_DATA, ptr[i]);
}
// Flush / Sync
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;
}
// === Device Naming ===
char disk_get_next_free_letter(void) {
for (int i = 0; i < MAX_DISKS; i++) {
char letter = 'A' + i;
bool used = false;
for (int j = 0; j < disk_count; j++) {
if (disks[j]->letter == letter) {
used = true;
break;
}
}
if (!used) return letter;
}
return 0; // No free letters
const char* disk_get_next_dev_name(void) {
static char name[8];
name[0] = 's';
name[1] = 'd';
name[2] = 'a' + next_sd_index;
name[3] = 0;
next_sd_index++;
return name;
}
// === Registration ===
void disk_register(Disk *disk) {
if (disk_count >= MAX_DISKS) return;
// Ensure letter is unique
if (disk->letter == 0) {
disk->letter = disk_get_next_free_letter();
// Auto-assign devname if empty
if (disk->devname[0] == 0) {
const char *n = disk_get_next_dev_name();
dm_strcpy(disk->devname, n);
}
disk->registered = true;
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) {
for (int i = 0; i < MAX_DISKS; i++) {
disks[i] = NULL;
void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_count,
bool is_fat32, int part_num) {
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) {
// Uppercase
if (letter >= 'a' && letter <= 'z') letter -= 32;
// === Lookup ===
Disk* disk_get_by_name(const char *devname) {
if (!devname) return NULL;
for (int i = 0; i < disk_count; i++) {
if (disks[i]->letter == letter) {
if (dm_strcmp(disks[i]->devname, devname) == 0) {
return disks[i];
}
}
@@ -247,130 +344,182 @@ Disk* disk_get_by_index(int 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 {
uint8_t status; // 0x80 = bootable, 0x00 = inactive
uint8_t chs_first[3]; // CHS of first sector
uint8_t type; // Partition type
uint8_t chs_last[3]; // CHS of last sector
uint32_t lba_start; // LBA of first sector
uint32_t sector_count; // Number of sectors
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_PartitionEntry;
// FAT32 partition type codes
#define PART_TYPE_FAT32 0x0B
#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) {
// Must have 0xAA55 boot signature
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' &&
sector[85] == '3' && sector[86] == '2') {
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 spf16 = *(uint16_t*)&sector[22];
uint32_t spf32 = *(uint32_t*)&sector[36];
if (bps == 512 && spf16 == 0 && spf32 > 0) {
return true;
}
return false;
}
// Parse MBR partition table and find a FAT32 partition.
// Sets disk->partition_lba_offset and returns true if found.
static bool detect_fat32_partition(Disk *disk) {
// Parse MBR and register each partition as a child block device
static void parse_mbr_partitions(Disk *disk) {
uint8_t *buffer = (uint8_t*)kmalloc(512);
if (!buffer) return false;
// Read sector 0 (MBR or raw BPB)
if (!buffer) return;
if (disk->read_sector(disk, 0, buffer) != 0) {
kfree(buffer);
return false;
return;
}
// Must have 0xAA55 boot signature
// Check for valid MBR signature
if (buffer[510] != 0x55 || buffer[511] != 0xAA) {
kfree(buffer);
return false;
return;
}
// Check MBR partition table entries (4 entries at offset 446)
MBR_PartitionEntry *partitions = (MBR_PartitionEntry*)&buffer[446];
int part_num = 1;
for (int i = 0; i < 4; i++) {
if (partitions[i].type == PART_TYPE_FAT32 ||
partitions[i].type == PART_TYPE_FAT32_LBA) {
uint32_t part_lba = partitions[i].lba_start;
// Read the partition's first sector to verify it's a valid FAT32 BPB
uint32_t start = partitions[i].lba_start;
uint32_t size = partitions[i].sector_count;
uint8_t type = partitions[i].type;
if (type == 0x00) continue; // Empty entry
if (size == 0) continue;
if (start >= disk->total_sectors) continue; // Invalid start
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);
if (!pbuf) { kfree(buffer); return false; }
if (disk->read_sector(disk, part_lba, pbuf) == 0 && is_fat32_bpb(pbuf)) {
disk->partition_lba_offset = part_lba;
if (pbuf) {
if (disk->read_sector(disk, start, pbuf) == 0) {
fat32 = is_fat32_bpb(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)
if (is_fat32_bpb(buffer)) {
// Fallback: if no partitions found, check if entire disk is a raw FAT32 volume
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;
kfree(buffer);
return true;
}
kfree(buffer);
return false;
}
// === ATA Drive Discovery ===
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));
if (!new_disk) return;
ATADriverData *data = (ATADriverData*)kmalloc(sizeof(ATADriverData));
data->port_base = port;
data->slave = slave;
new_disk->letter = 0; // Auto-assign
new_disk->devname[0] = 0; // Auto-assign
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->write_sector = ata_write_sector;
new_disk->driver_data = data;
new_disk->partition_lba_offset = 0;
// Detect FAT32 (with MBR partition support)
if (detect_fat32_partition(new_disk)) {
new_disk->is_fat32 = true;
disk_register(new_disk);
} else {
kfree(data);
kfree(new_disk);
}
new_disk->total_sectors = sectors;
new_disk->parent = NULL;
new_disk->is_partition = false;
new_disk->is_fat32 = false;
disk_register(new_disk);
// Parse MBR to find partitions
parse_mbr_partitions(new_disk);
}
}
// === Init & Scan ===
void disk_manager_init(void) {
for (int i = 0; i < MAX_DISKS; i++) {
disks[i] = NULL;
}
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) {
// Probe Standard ATA Ports
try_add_ata_drive(ATA_PRIMARY_IO, false, "IDE1");
try_add_ata_drive(ATA_PRIMARY_IO, true, "IDE2");
try_add_ata_drive(ATA_SECONDARY_IO, false, "IDE3");
try_add_ata_drive(ATA_SECONDARY_IO, true, "IDE4");
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;
}
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_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);
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_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

View File

@@ -16,18 +16,19 @@ extern void serial_print_hex(uint64_t n);
volatile uint64_t kernel_ticks = 0;
uint64_t timer_handler(registers_t *regs) {
kernel_ticks++;
wm_timer_tick();
network_process_frames();
if (smp_this_cpu_id() == 0) {
kernel_ticks++;
wm_timer_tick();
network_process_frames();
extern void k_beep_process(void);
k_beep_process();
}
extern void k_beep_process(void);
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);
uint64_t new_rsp = process_schedule((uint64_t)regs);
// SMP: Wake AP cores to run their assigned processes
if (smp_cpu_count() > 1) {
lapic_send_ipi_all();
}
@@ -37,9 +38,27 @@ uint64_t timer_handler(registers_t *regs) {
// --- Keyboard ---
static bool shift_pressed = false;
static bool caps_lock_on = false;
bool ps2_ctrl_pressed = false;
static bool extended_scancode = false;
static void ps2_kbd_wait_write(void) {
uint32_t timeout = 100000;
while (timeout--) {
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] = {
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',
@@ -67,30 +86,22 @@ uint64_t keyboard_handler(registers_t *regs) {
if (scancode == 0x1D) {
ps2_ctrl_pressed = true;
extended_scancode = false; // Reset if Ctrl is pressed (prevents E0 1D bug)
extended_scancode = false;
} else if (scancode == 0x9D) {
ps2_ctrl_pressed = 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
shift_pressed = true;
} else if (scancode == 0xAA || scancode == 0xB6) { // Shift Up
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) {
extended_scancode = false;
switch (scancode) {
@@ -102,6 +113,10 @@ uint64_t keyboard_handler(registers_t *regs) {
} else {
char c = shift_pressed ? scancode_map_shift[scancode] : scancode_map[scancode];
if (c) {
if (caps_lock_on) {
if (c >= 'a' && c <= 'z') c -= 32;
else if (c >= 'A' && c <= 'Z') c += 32;
}
wm_handle_key(c, true);
}
}
@@ -115,8 +130,13 @@ uint64_t keyboard_handler(registers_t *regs) {
case 0x4D: wm_handle_key(20, false); break; // Right arrow
}
} 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 (caps_lock_on) {
if (c >= 'a' && c <= 'z') c -= 32;
else if (c >= 'A' && c <= 'Z') c += 32;
}
wm_handle_key(c, false);
}
}
@@ -177,7 +197,7 @@ void mouse_init(void) {
mouse_write(0xF6);
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(100); 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) {
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 mouse_handler(registers_t *regs);
bool ps2_shift_pressed(void);
#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

@@ -25,7 +25,7 @@ typedef struct {
uint16_t num_heads; // Number of heads
uint32_t hidden_sectors; // Hidden sectors
uint32_t total_sectors_32; // Total sectors 32-bit
// FAT32 Specific
uint32_t sectors_per_fat_32; // Sectors per FAT 32-bit
uint16_t flags; // Flags
@@ -61,6 +61,18 @@ typedef struct {
uint32_t file_size; // File size
} __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
#define ATTR_READ_ONLY 0x01
#define ATTR_HIDDEN 0x02
@@ -70,6 +82,7 @@ typedef struct {
#define ATTR_ARCHIVE 0x20
#define ATTR_DEVICE 0x40
#define ATTR_RESERVED 0x80
#define ATTR_LFN 0x0F // LFN marker (all of the above ORed)
// FAT32 Constants
#define FAT32_SECTOR_SIZE 512
@@ -88,7 +101,9 @@ typedef struct {
bool valid; // Is this handle valid?
uint32_t dir_sector; // Sector containing the directory entry
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;
// Directory Entry Info (for listing)
@@ -101,12 +116,23 @@ typedef struct {
uint16_t write_time;
} 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 ===
// Initialization
void fat32_init(void);
// File Operations
// File Operations (backward-compat wrappers — dispatch through VFS)
FAT32_FileHandle* fat32_open(const char *path, const char *mode);
void fat32_close(FAT32_FileHandle *handle);
int fat32_read(FAT32_FileHandle *handle, void *buffer, int size);
@@ -124,7 +150,10 @@ bool fat32_is_directory(const char *path);
// Listing
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);
void fat32_get_current_dir(char *buffer, int size);
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

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) {
cmd_write("Memory validation: OK\n");
cmd_write("Memory validation: [OK]\n");
} else {
cmd_write("Memory validation failed with ");
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) &&
ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) &&
ip_addr_cmp(&cpcb->remote_ip, ipaddr)) {
/* linux returns EISCONN here, but ERR_USE should be OK for us */
return ERR_USE;
}
}

View File

@@ -88,7 +88,7 @@ int network_init(void) {
ipv4_address_t ip;
network_get_ipv4_address(&ip);
extern void serial_write(const char *str);
serial_write("[NETWORK] IP Assigned: ");
serial_write("[NET] IP Assigned: ");
char buf[32];
k_itoa(ip.bytes[0], 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");
} else {
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)

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
#define CMD_H
#include "wm.h"
extern Window win_cmd;
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
void cmd_init(void);
void cmd_reset(void);
// IO Functions
void cmd_write(const char *str);
void cmd_write_len(const char *str, size_t len);
void cmd_putchar(char c);
void cmd_write_int(int 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_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);
void cmd_set_current_color(uint32_t color);
void cmd_set_raw_mode(bool enabled);
void cmd_process_finished(void);
#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) {
uint16_t divisor = 1193182 / 60; // ~60Hz
// Send command byte
outb(0x43, 0x36); // Channel 0, lobyte/hibyte, mode 3 (square wave), binary
// Mode 2: Rate Generator (more appropriate for periodic interrupts)
outb(0x43, 0x34); io_wait(); // Channel 0, lobyte/hibyte, mode 2, binary
// Send divisor
outb(0x40, divisor & 0xFF);
outb(0x40, (divisor >> 8) & 0xFF);
outb(0x40, divisor & 0xFF); io_wait();
outb(0x40, (divisor >> 8) & 0xFF); io_wait();
}
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_CPUS_SCHED 32
process_t processes[MAX_PROCESSES] __attribute__((aligned(16)));
int process_count = 0;
static process_t* current_process[MAX_CPUS_SCHED] = {0}; // Per-CPU
static uint32_t next_pid = 0;
static void *free_kernel_stack_later = NULL;
static uint64_t free_pml4_later = 0;
static void *free_kernel_stack_later[MAX_CPUS_SCHED] = {0};
static uint64_t free_pml4_later[MAX_CPUS_SCHED] = {0};
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) {
for (int i = 0; i < MAX_PROCESSES; i++) {
processes[i].pid = 0xFFFFFFFF;
}
// Current kernel execution is PID 0
process_t *kernel_proc = &processes[process_count++];
process_t *kernel_proc = &processes[0];
kernel_proc->pid = next_pid++;
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->kernel_stack = 0;
// Initialize FPU/SSE state for kernel (first interrupt will capture it on stack)
kernel_proc->fpu_initialized = true;
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->cpu_affinity = 0; // Kernel always on BSP
mem_memset(kernel_proc->cwd, 0, 1024);
kernel_proc->cwd[0] = '/';
current_process[0] = kernel_proc;
}
process_t* process_create(void (*entry_point)(void), bool is_user) {
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);
return NULL;
}
process_t *new_proc = &processes[process_count++];
new_proc->pid = next_pid++;
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
if (is_user) {
@@ -157,15 +179,14 @@ process_t* process_create(void (*entry_point)(void), bool is_user) {
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);
process_t *new_proc = NULL;
// Find an available slot
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];
if (i >= process_count) process_count = i + 1;
break;
}
}
@@ -189,16 +210,27 @@ process_t* process_create_elf(const char* filepath, const char* args_str) {
new_proc->ui_window = NULL;
new_proc->heap_start = 0x20000000; // 512MB mark
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
size_t elf_load_size = 0;
uint64_t entry_point = elf_load(filepath, new_proc->pml4_phys, &elf_load_size);
if (entry_point == 0) {
serial_write("[PROCESS] Failed to load ELF: ");
serial_write("[PROC] Failed to load ELF: ");
serial_write(filepath);
serial_write("\n");
// We technically leak the page table here, but let's ignore cleanup for now
return NULL;
}
@@ -354,7 +386,7 @@ process_t* process_create_elf(const char* filepath, const char* args_str) {
current_process[0]->next = new_proc;
spinlock_release_irqrestore(&runqueue_lock, rflags);
serial_write("[PROCESS] Spawned ELF Executable: ");
serial_write("[PROC] Exec: ");
serial_write(filepath);
serial_write("\n");
return new_proc;
@@ -376,25 +408,102 @@ process_t* process_get_current(void) {
}
uint64_t process_schedule(uint64_t current_rsp) {
if (free_kernel_stack_later) {
kfree(free_kernel_stack_later);
free_kernel_stack_later = NULL;
uint32_t my_cpu = smp_this_cpu_id();
uint64_t rflags = spinlock_acquire_irqsave(&runqueue_lock);
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) {
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
paging_destroy_user_pml4_phys(free_pml4_later);
free_pml4_later = 0;
if (free_pml4_later[my_cpu]) {
cleanup_pml4 = free_pml4_later[my_cpu];
free_pml4_later[my_cpu] = 0;
}
uint32_t my_cpu = smp_this_cpu_id();
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;
}
// Save context
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
extern uint32_t wm_get_ticks(void);
uint32_t now = wm_get_ticks();
@@ -404,7 +513,7 @@ uint64_t process_schedule(uint64_t current_rsp) {
while (next_proc != start) {
// 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) {
break;
}
@@ -412,11 +521,8 @@ uint64_t process_schedule(uint64_t current_rsp) {
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) {
// Fallback to idle if current is terminated
if (cur && cur->pid == 0xFFFFFFFF) {
// Find the idle process for this 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];
@@ -424,18 +530,25 @@ uint64_t process_schedule(uint64_t current_rsp) {
}
}
} 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;
}
}
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) {
tss_set_stack_cpu(my_cpu, current_process[my_cpu]->kernel_stack);
if (my_cpu == 0) {
extern uint64_t kernel_syscall_stack;
kernel_syscall_stack = 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;
}
}
@@ -443,8 +556,16 @@ uint64_t process_schedule(uint64_t current_rsp) {
paging_switch_directory(current_process[my_cpu]->pml4_phys);
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) {
@@ -454,12 +575,20 @@ process_t* process_get_by_pid(uint32_t pid) {
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) {
if (!proc || proc->pid == 0xFFFFFFFF) return;
// 1. Cleanup side effects
extern Window win_cmd;
if (proc->ui_window && (proc->ui_window != &win_cmd)) {
if (proc->ui_window) {
wm_remove_window((Window *)proc->ui_window);
proc->ui_window = NULL;
}
@@ -486,6 +615,14 @@ static void process_cleanup_inner(process_t *proc) {
void process_terminate(process_t *to_delete) {
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);
process_cleanup_inner(to_delete);
@@ -506,7 +643,6 @@ void process_terminate(process_t *to_delete) {
prev->next = to_delete->next;
// 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++) {
if (current_process[c] == to_delete) {
process_t *np = to_delete->next;
@@ -528,6 +664,7 @@ void process_terminate(process_t *to_delete) {
// Mark slot as free
to_delete->pid = 0xFFFFFFFF;
to_delete->cpu_affinity = 0xFFFFFFFF;
to_delete->kill_pending = false;
if (to_delete->user_stack_alloc) kfree(to_delete->user_stack_alloc);
// 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->ui_window = NULL;
to_delete->is_terminal_proc = false;
to_delete->kill_pending = false;
// 4. Load context for the NEXT process
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);
if (my_cpu == 0) {
extern uint64_t kernel_syscall_stack;
kernel_syscall_stack = 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);
// 5. Free memory
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;
free_kernel_stack_later[my_cpu] = to_delete->kernel_stack_alloc;
to_delete->kernel_stack_alloc = NULL;
free_pml4_later[my_cpu] = to_delete->pml4_phys;
to_delete->pml4_phys = 0;
uint64_t next_rsp = current_process[my_cpu]->rsp;
@@ -666,4 +796,3 @@ process_t* process_get_by_ui_window(void *win) {
}
return NULL;
}

View File

@@ -43,6 +43,8 @@ typedef struct process {
void *user_stack_alloc;
bool is_terminal_proc;
int tty_id;
bool kill_pending;
struct process *next;
@@ -52,7 +54,9 @@ typedef struct process {
uint64_t ticks;
uint64_t sleep_until;
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;
typedef struct {
@@ -60,11 +64,12 @@ typedef struct {
char name[64];
uint64_t ticks;
size_t used_memory;
bool is_idle;
} ProcessInfo;
void process_init(void);
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);
void process_set_current_for_cpu(uint32_t cpu_id, process_t* p);
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);
void process_terminate(process_t *proc);
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);
void process_push_gui_event(process_t *proc, gui_event_t *ev);

View File

@@ -10,6 +10,7 @@
#include "paging.h"
#include "process.h"
#include "work_queue.h"
#include "core/kutils.h"
extern void serial_write(const char *str);
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 uint32_t total_cpus = 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) {
uint32_t eax, ebx, ecx, edx;
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1));
return (ebx >> 24) & 0xFF;
extern uint64_t hhdm_offset;
volatile uint32_t *lapic = (volatile uint32_t *)(hhdm_offset + 0xFEE00000ULL);
return (lapic[0x020 / 4] >> 24) & 0xFF;
}
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();
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++) {
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) {
@@ -68,13 +85,21 @@ static void ap_entry(struct limine_smp_info *info) {
extern void idt_load(void);
idt_load();
extern void syscall_init(void);
syscall_init();
uint64_t kernel_cr3 = paging_get_pml4_phys();
asm volatile("mov %0, %%cr3" : : "r"(kernel_cr3));
extern void lapic_enable(void);
lapic_enable();
cpu_states[my_id].self = &cpu_states[my_id];
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_num(my_id);
@@ -84,12 +109,31 @@ static void ap_entry(struct limine_smp_info *info) {
process_t *ap_idle = process_create(NULL, false);
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);
asm volatile("sti");
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 ---
uint32_t smp_init(struct limine_smp_response *smp_resp) {
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;
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;
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_num(i);
serial_write(" (LAPIC ");

View File

@@ -8,29 +8,27 @@
#include <stdbool.h>
#include "spinlock.h"
// Per-CPU state. Dynamically allocated at boot based on actual CPU count.
typedef struct cpu_state {
uint32_t cpu_id; // Logical CPU index (0 = BSP)
uint32_t lapic_id; // Local APIC ID from Limine
uint64_t kernel_stack; // Top of kernel stack for this CPU
void *kernel_stack_alloc; // Base allocation for kfree
volatile bool online; // True once AP is fully initialized
struct cpu_state *self;
uint32_t cpu_id;
uint32_t lapic_id;
uint64_t kernel_stack;
void *kernel_stack_alloc;
volatile bool online;
uint64_t user_rsp_scratch;
uint64_t kernel_syscall_stack;
} cpu_state_t;
void smp_init_bsp(void);
// Initialize SMP — call after GDT/IDT/memory init but before wm_init.
// 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;
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);
// Total number of CPUs online.
uint32_t smp_cpu_count(void);
// Get per-CPU state by index.
cpu_state_t *smp_get_cpu(uint32_t cpu_id);
#endif

View File

@@ -8,6 +8,7 @@
#include "process.h"
#include "wm.h"
#include "fat32.h"
#include "vfs.h"
#include "paging.h"
#include "work_queue.h"
#include "smp.h"
@@ -18,11 +19,16 @@
#include "network.h"
#include "icmp.h"
#include "cmd.h"
#include "tty.h"
#include "font_manager.h"
#include "graphics.h"
extern bool ps2_ctrl_pressed;
#define SPAWN_FLAG_TERMINAL 0x1
#define SPAWN_FLAG_INHERIT_TTY 0x2
#define SPAWN_FLAG_TTY_ID 0x4
// Read MSR
static inline uint64_t rdmsr(uint32_t msr) {
uint32_t low, high;
@@ -76,6 +82,14 @@ static void smp_user_wrapper(void *arg) {
}
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) {
@@ -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);
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
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
int cmd = (int)arg1;
process_t *proc = process_get_current();
if (cmd == GUI_CMD_WINDOW_CREATE) {
extern void serial_write(const char *str);
serial_write("Kernel: GUI_CMD_WINDOW_CREATE\n");
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;
if (!u_params) {
serial_write("Kernel: Error - params is NULL\n");
serial_write("[WM] Error - params is NULL\n");
return 0;
}
@@ -207,14 +244,14 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
uint64_t params[4];
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));
if (!win) {
serial_write("Kernel: Error - kmalloc failed for Window\n");
serial_write("[WM] Error - kmalloc failed for Window\n");
return 0;
}
serial_write("Kernel: Window allocated.\n");
// win allocated
extern void mem_memset(void *dest, int val, size_t len);
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[title_len] = '\0';
serial_write("Kernel: Title copied: ");
serial_write(kernel_title);
serial_write("\n");
} else {
serial_write("Kernel: Warning - kernel_title kmalloc failed\n");
serial_write("[WM] Warning: kernel_title kmalloc failed\n");
}
// Basic initialization
@@ -245,7 +279,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
win->w = (int)params[2];
win->h = (int)params[3];
serial_write("Kernel: Init win dims.\n");
// dims ready
// Sanity checks for dimensions
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->lock = SPINLOCK_INIT;
serial_write("Kernel: Dims initialized.\n");
// ready
size_t pixel_size = 0;
// Safe allocation
@@ -274,7 +308,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
win->comp_pixels = kmalloc(pixel_size);
}
serial_write("Kernel: Buffers allocated.\n");
// buffs ok
if (win->pixels) {
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);
}
serial_write("Kernel: Buffers cleared.\n");
serial_write("[WM] Buffers ready\n");
// Set callbacks
win->paint = user_window_paint;
@@ -298,6 +332,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
proc->ui_window = win;
wm_add_window(win);
wm_mark_dirty(0, 0, get_screen_width(), 30);
return (uint64_t)win;
} 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 cur_x = ux;
const char *s = kernel_str;
int start_x = cur_x;
while (*s) {
uint32_t codepoint = utf8_decode(&s);
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);
if (codepoint == '\n') {
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 {
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 cur_x = win->x + ux;
const char *s = kernel_str;
int start_x = cur_x;
while (*s) {
uint32_t codepoint = utf8_decode(&s);
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);
if (codepoint == '\n') {
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 {
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 cur_x = ux;
const char *s = kernel_str;
int start_x = cur_x;
while (*s) {
uint32_t codepoint = utf8_decode(&s);
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);
if (codepoint == '\n') {
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 {
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 cur_x = win->x + ux;
const char *s = kernel_str;
int start_x = cur_x;
while (*s) {
uint32_t codepoint = utf8_decode(&s);
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);
if (codepoint == '\n') {
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 {
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 cur_x = ux;
const char *s = kernel_str;
int start_x = cur_x;
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));
uint32_t codepoint = utf8_decode(&s);
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);
if (codepoint == '\n') {
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 {
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 cur_x = win->x + ux;
const char *s = kernel_str;
int start_x = cur_x;
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));
uint32_t codepoint = utf8_decode(&s);
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);
if (codepoint == '\n') {
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 {
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;
if (win) {
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");
win->resizable = (arg3 != 0);
}
@@ -852,33 +935,35 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
const char *mode = (const char *)arg3;
if (!path || !mode) return -1;
FAT32_FileHandle *fh = fat32_open(path, mode);
if (!fh) return -1;
// vfs_open now handles normalization internally with process_get_current()
// 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++) {
if (proc->fds[i] == NULL) {
proc->fds[i] = fh;
proc->fds[i] = vf;
return (uint64_t)i;
}
}
fat32_close(fh);
vfs_close(vf);
return -1;
} else if (cmd == FS_CMD_READ) {
int fd = (int)arg2;
void *buf = (void *)arg3;
uint32_t len = (uint32_t)arg4;
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) {
int fd = (int)arg2;
const void *buf = (const void *)arg3;
uint32_t len = (uint32_t)arg4;
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) {
int fd = (int)arg2;
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;
return 0;
} else if (cmd == FS_CMD_SEEK) {
@@ -886,49 +971,107 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int offset = (int)arg3;
int whence = (int)arg4; // 0=SET, 1=CUR, 2=END
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) {
int fd = (int)arg2;
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) {
int fd = (int)arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)((FAT32_FileHandle*)proc->fds[fd])->size;
}
else if (cmd == FS_CMD_LIST) {
return (uint64_t)vfs_file_size((vfs_file_t*)proc->fds[fd]);
} else if (cmd == FS_CMD_GETCWD) {
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;
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;
if (!path || !entries) return -1;
return (uint64_t)fat32_list_directory(path, entries, max_entries);
if (!path || !u_entries) return -1;
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) {
const char *path = (const char *)arg2;
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) {
const char *path = (const char *)arg2;
FAT32_FileInfo *info = (FAT32_FileInfo *)arg3;
if (!path || !info) return -1;
extern int fat32_get_info(const char *path, FAT32_FileInfo *info);
return (uint64_t)fat32_get_info(path, info);
FAT32_FileInfo *u_info = (FAT32_FileInfo *)arg3;
if (!path || !u_info) return -1;
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) {
const char *path = (const char *)arg2;
if (!path) return -1;
return fat32_mkdir(path) ? 0 : -1;
return vfs_mkdir(path) ? 0 : -1;
} else if (cmd == FS_CMD_EXISTS) {
const char *path = (const char *)arg2;
if (!path) return 0;
return fat32_exists(path) ? 1 : 0;
return vfs_exists(path) ? 1 : 0;
} else if (cmd == FS_CMD_GETCWD) {
char *buf = (char *)arg2;
int size = (int)arg3;
if (!buf) return -1;
extern void fat32_get_current_dir(char *buf, int size);
fat32_get_current_dir(buf, size);
return 0;
} else if (cmd == FS_CMD_CHDIR) {
const char *path = (const char *)arg2;
if (!path) return -1;
extern bool fat32_chdir(const char *path);
return fat32_chdir(path) ? 0 : -1;
}
return 0;
@@ -1055,15 +1198,6 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
extern void k_beep(int freq, int ms);
k_beep(freq, ms);
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
typedef struct {
uint16_t vendor;
@@ -1144,6 +1278,35 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
return cmd_get_config_value(key);
} else if (cmd == 29) { // SYSTEM_CMD_SET_TEXT_COLOR
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);
return 0;
} 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;
extern int network_tcp_recv_nb(void *buf, size_t max_len);
return (uint64_t)network_tcp_recv_nb(buf, max_len);
} else if (cmd == SYSTEM_CMD_PROCESS_LIST) {
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;
return -1;
} else if (cmd == 47) { // SYSTEM_CMD_SET_RESOLUTION
uint16_t req_w = (uint16_t)arg2;
uint16_t req_h = (uint16_t)arg3;
@@ -1310,12 +1426,86 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
return 0;
}
return -1;
} else if (cmd == 49) { // SYSTEM_CMD_GET_OS_INFO
os_info_t *info = (os_info_t *)arg2;
if (!info) return -1;
extern void get_os_info(os_info_t *info);
get_os_info(info);
return -1;
} else if (cmd == 60) { // SYSTEM_CMD_TTY_CREATE
return tty_create();
} else if (cmd == 61) { // SYSTEM_CMD_TTY_READ_OUT
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;
} 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) {
void (*user_fn)(void*) = (void (*)(void*))arg2;
void **args = (void **)arg3;

View File

@@ -6,7 +6,6 @@
#include <stdint.h>
// Forward declarations
typedef struct Window Window;
typedef struct registers_t registers_t;
@@ -53,11 +52,8 @@ typedef struct {
#define SYSTEM_CMD_SET_RAW_MODE 41
#define SYSTEM_CMD_TCP_RECV_NB 42
#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_SET_RESOLUTION 47
#define SYSTEM_CMD_GET_OS_INFO 49
#define SYSTEM_CMD_PARALLEL_RUN 50
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("---------------------------\n");
strcpy(path, "A:/Library/man/");
strcpy(path, "/Library/man/");
strcat(path, argv[1]);
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_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) {
size_t 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() {
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.separator, "------------");
strcpy(config.os_label, "OS");
@@ -160,7 +151,7 @@ static void parse_config(char* buffer) {
static void load_config() {
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;
char *buffer = malloc(4096);
@@ -237,52 +228,82 @@ int main(int argc, char **argv) {
if (config.separator[0]) {
strcpy(info_lines[info_line_count++], config.separator);
}
os_info_t os_info;
sys_get_os_info(&os_info);
// Helper for proc parsing
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]) {
strcpy(info_lines[info_line_count], config.os_label);
strcat(info_lines[info_line_count], ": ");
strcat(info_lines[info_line_count], os_info.os_name);
strcat(info_lines[info_line_count], " V");
strcat(info_lines[info_line_count], os_info.os_version);
strcat(info_lines[info_line_count], " '");
strcat(info_lines[info_line_count], os_info.os_codename);
strcat(info_lines[info_line_count], "'");
// Parse "BoredOS [codename] Version X.Y.Z"
strcat(info_lines[info_line_count], v_buf);
// Truncate at newline
char *nl = strchr(info_lines[info_line_count], '\n');
if (nl) *nl = 0;
info_line_count++;
}
if (config.kernel_label[0]) {
strcpy(info_lines[info_line_count], config.kernel_label);
strcat(info_lines[info_line_count], ": ");
strcat(info_lines[info_line_count], os_info.kernel_name);
strcat(info_lines[info_line_count], " V");
strcat(info_lines[info_line_count], os_info.kernel_version);
strcat(info_lines[info_line_count], " ");
strcat(info_lines[info_line_count], os_info.build_arch);
char *kstart = strchr(v_buf, '\n');
if (kstart) {
strcat(info_lines[info_line_count], kstart + 1);
char *knext = strchr(info_lines[info_line_count], '\n');
if (knext) *knext = 0;
} else strcat(info_lines[info_line_count], "Unknown");
info_line_count++;
}
if (config.uptime_label[0]) {
uint64_t ticks = sys_system(16, 0, 0, 0, 0);
int minutes = ticks / 3600; // 60Hz timer
strcpy(info_lines[info_line_count], config.uptime_label);
strcat(info_lines[info_line_count], ": ");
itoa(minutes, temp_buf);
strcat(info_lines[info_line_count], temp_buf);
strcat(info_lines[info_line_count++], " mins");
int fd_u = sys_open("/proc/uptime", "r");
if (fd_u >= 0) {
char u_buf[64];
int b = sys_read(fd_u, u_buf, 63);
u_buf[b] = 0;
sys_close(fd_u);
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]) {
strcpy(info_lines[info_line_count], config.shell_label);
strcat(info_lines[info_line_count++], ": bsh");
}
if (config.memory_label[0]) {
uint64_t mem[2];
if (sys_system(15, (uint64_t)mem, 0, 0, 0) == 0) {
int fd_m = sys_open("/proc/meminfo", "r");
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);
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], "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++], "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;
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) {
size_t len = strlen(s) + 1;
char *dup = malloc(len);
if (dup) memcpy(dup, s, len);
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 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) {
uint64_t ticks = sys_system(16, 0, 0, 0, 0); // SYSTEM_CMD_UPTIME = 16 (100Hz)
return (uint32_t)(ticks * 10);
int fd = sys_open("/proc/uptime", "r");
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) {
@@ -108,7 +119,7 @@ int DG_GetKey(int* pressed, unsigned char* key) {
int main(int argc, char** argv) {
(void)argc;
(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);
while (1) {

View File

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

View File

@@ -32,19 +32,6 @@ static int win_h = 960;
static char history_stack[HISTORY_MAX][512];
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) {
if (!*needle) return (char*)haystack;
for (; *haystack; haystack++) {

View File

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

View File

@@ -28,7 +28,7 @@ static uint32_t *canvas_buffer = NULL;
static uint32_t current_color = COLOR_BLACK;
static int last_mx = -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) {
while (*src) *dest++ = *src++;
@@ -79,12 +79,7 @@ static void paint_paint(ui_window_t win) {
// Draw canvas content
if (canvas_buffer) {
for (int y = 0; y < CANVAS_H; y++) {
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);
}
}
ui_draw_image(win, canvas_x, canvas_y, CANVAS_W, CANVAS_H, canvas_buffer);
}
}
@@ -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;
if (px >= 0 && px < CANVAS_W && py >= 0 && py < CANVAS_H) {
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 (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) {
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);
}

View File

@@ -84,7 +84,7 @@ int main(int argc, char **argv) {
uint64_t dt[6] = {0};
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[1], 2); // Month
append_num((int)dt[2], 2); // Day
@@ -110,7 +110,7 @@ int main(int argc, char **argv) {
// Show notification
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);
} else {
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 used_mem_system = 0;
static char cpu_model_name[64] = "Unknown CPU";
static int cpu_cores = 1;
typedef struct {
size_t total_memory;
size_t used_memory;
size_t available_memory;
size_t allocated_blocks;
size_t free_blocks;
size_t largest_free_block;
size_t smallest_free_block;
size_t fragmentation_percent;
size_t peak_memory_used;
} MemStats;
static int find_value(const char *buf, const char *key) {
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++;
return atoi(p);
}
while (*p && *p != '\n') p++;
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) {
proc_count = sys_system(SYSTEM_CMD_PROCESS_LIST, (uint64_t)proc_list, 32, 0, 0);
uint64_t uptime_now = sys_system(SYSTEM_CMD_UPTIME, 0, 0, 0, 0);
FAT32_FileInfo entries[64];
int count = sys_list("/proc", entries, 64);
if (count < 0) return;
proc_count = 0;
uint64_t user_ticks_now = 0;
for (int i = 0; i < proc_count; i++) {
if (proc_list[i].pid != 0) {
user_ticks_now += proc_list[i].ticks;
for (int i = 0; i < count; i++) {
if (entries[i].is_directory) {
// 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) {
uint64_t total_delta = uptime_now - uptime_prev;
if (total_delta > 0) {
uint64_t used_delta = user_ticks_now - kernel_ticks_prev; // Reusing the global state variable for prev user_ticks
// On a 4 CPU system, theoretically used_delta can be 4x total_delta
int usage = (int)((used_delta * 100) / (total_delta * 4));
uint64_t used_delta = user_ticks_now - kernel_ticks_prev;
int cores = cpu_cores > 0 ? cpu_cores : 1;
int usage = (int)((used_delta * 100) / (total_delta * cores));
if (usage > 100) usage = 100;
cpu_history[history_idx] = usage;
}
@@ -72,11 +145,18 @@ static void update_proc_list(void) {
uptime_prev = uptime_now;
kernel_ticks_prev = user_ticks_now;
MemStats stats;
sys_system(SYSTEM_CMD_MEMINFO, (uint64_t)&stats, 0, 0, 0);
total_mem_system = stats.total_memory;
used_mem_system = stats.used_memory;
mem_history[history_idx] = (int)(stats.used_memory / 1024);
int fd_m = sys_open("/proc/meminfo", "r");
if (fd_m >= 0) {
char buf[1024];
int bytes = sys_read(fd_m, buf, 1023);
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;
}
@@ -168,9 +248,12 @@ static void draw_taskman(void) {
// Memory Graph Area
ui_draw_string(win_taskman, 205, 10, "MEMORY", COLOR_MEM);
char mem_pct_label[16];
int current_mem_pct = 0;
if (total_mem_system > 0) current_mem_pct = (int)((used_mem_system * 100) / total_mem_system);
itoa(current_mem_pct, mem_pct_label);
int current_mem_pct_x10 = 0;
if (total_mem_system > 0) current_mem_pct_x10 = (int)((used_mem_system * 1000) / total_mem_system);
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, "%");
ui_draw_string(win_taskman, 340, 10, mem_pct_label, COLOR_MEM);
@@ -255,8 +338,18 @@ static void draw_taskman(void) {
int main(void) {
win_taskman = ui_window_create("Task Manager", 100, 100, 400, 480);
// Fetch CPU model
sys_system(SYSTEM_CMD_GET_CPU_MODEL, (uint64_t)cpu_model_name, 0, 0, 0);
int fd_c = sys_open("/proc/cpuinfo", "r");
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; }

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;
}
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) {
for (int i = 0; i < EDITOR_MAX_LINES; i++) {
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) {
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) {
file_modified = 0;
return;

View File

@@ -41,6 +41,40 @@ static int viewer_strlen(const char *s) {
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,
uint32_t *dst, int dst_w, int 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) {
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;
uint32_t file_size = sys_size(fd);
@@ -250,13 +286,13 @@ void viewer_open_file(const char *path) {
stbi_image_free(rgba);
free(buf);
viewer_strcpy(viewer_file_path, path);
viewer_strcpy(viewer_file_path, resolved);
const char *fname = path;
int plen = viewer_strlen(path);
const char *fname = resolved;
int plen = viewer_strlen(resolved);
for (int i = plen - 1; i >= 0; i--) {
if (path[i] == '/') {
fname = &path[i + 1];
if (resolved[i] == '/') {
fname = &resolved[i + 1];
break;
}
}

View File

@@ -202,9 +202,27 @@ char* strcat(char *dest, const char *src) {
return ret;
}
char *strchr(const char *s, int c) {
while (*s != (char)c) {
if (!*s++) return NULL;
}
return (char *)s;
}
char *strstr(const char *haystack, const char *needle) {
size_t needle_len = strlen(needle);
if (!needle_len) return (char *)haystack;
while (*haystack) {
if (memcmp(haystack, needle, needle_len) == 0) return (char *)haystack;
haystack++;
}
return NULL;
}
int atoi(const char *nptr) {
int res = 0;
int sign = 1;
while (*nptr == ' ' || *nptr == '\t' || *nptr == '\n' || *nptr == '\r') nptr++;
if (*nptr == '-') {
sign = -1;
nptr++;
@@ -311,7 +329,7 @@ int chdir(const char *path) {
}
char* getcwd(char *buf, int size) {
if (sys_getcwd(buf, size) == 0) return buf;
if (sys_getcwd(buf, size) >= 0) return buf;
return NULL;
}

View File

@@ -11,11 +11,9 @@ void* realloc(void* ptr, size_t size);
void *memset(void *s, int c, size_t n);
void *memcpy(void *dest, const void *src, size_t n);
// String functions
size_t strlen(const char *s);
int strcmp(const char *s1, const char *s2);
char* strcpy(char *dest, const char *src);
char* strcat(char *dest, const char *src);
#include "string.h"
// Math/Utility functions
int atoi(const char *nptr);
void itoa(int n, char *buf);

View File

@@ -7,5 +7,11 @@ void *memmove(void *dest, const void *src, size_t n);
int memcmp(const void *s1, const void *s2, size_t n);
void *memcpy(void *dest, const void *src, size_t n);
void *memset(void *s, int c, size_t n);
char *strchr(const char *s, int c);
char *strstr(const char *haystack, const char *needle);
size_t strlen(const char *s);
int strcmp(const char *s1, const char *s2);
char* strcpy(char *dest, const char *src);
char* strcat(char *dest, const char *src);
#endif

View File

@@ -145,6 +145,46 @@ int sys_chdir(const char *path) {
return (int)syscall2(SYS_FS, FS_CMD_CHDIR, (uint64_t)path);
}
int sys_tty_create(void) {
return (int)syscall2(SYS_SYSTEM, SYSTEM_CMD_TTY_CREATE, 0);
}
int sys_tty_read_out(int tty_id, char *buf, int len) {
return (int)syscall5(SYS_SYSTEM, SYSTEM_CMD_TTY_READ_OUT, (uint64_t)tty_id, (uint64_t)buf, (uint64_t)len, 0);
}
int sys_tty_write_in(int tty_id, const char *buf, int len) {
return (int)syscall5(SYS_SYSTEM, SYSTEM_CMD_TTY_WRITE_IN, (uint64_t)tty_id, (uint64_t)buf, (uint64_t)len, 0);
}
int sys_tty_read_in(char *buf, int len) {
return (int)syscall4(SYS_SYSTEM, SYSTEM_CMD_TTY_READ_IN, (uint64_t)buf, (uint64_t)len, 0);
}
int sys_spawn(const char *path, const char *args, uint64_t flags, uint64_t tty_id) {
return (int)syscall5(SYS_SYSTEM, SYSTEM_CMD_SPAWN, (uint64_t)path, (uint64_t)args, flags, (uint64_t)tty_id);
}
int sys_tty_set_fg(int tty_id, int pid) {
return (int)syscall4(SYS_SYSTEM, SYSTEM_CMD_TTY_SET_FG, (uint64_t)tty_id, (uint64_t)pid, 0);
}
int sys_tty_get_fg(int tty_id) {
return (int)syscall3(SYS_SYSTEM, SYSTEM_CMD_TTY_GET_FG, (uint64_t)tty_id, 0);
}
int sys_tty_kill_fg(int tty_id) {
return (int)syscall3(SYS_SYSTEM, SYSTEM_CMD_TTY_KILL_FG, (uint64_t)tty_id, 0);
}
int sys_tty_kill_all(int tty_id) {
return (int)syscall3(SYS_SYSTEM, SYSTEM_CMD_TTY_KILL_ALL, (uint64_t)tty_id, 0);
}
int sys_tty_destroy(int tty_id) {
return (int)syscall3(SYS_SYSTEM, SYSTEM_CMD_TTY_DESTROY, (uint64_t)tty_id, 0);
}
void sys_kill(int pid) {
syscall1(SYS_KILL, (uint64_t)pid);
}
@@ -251,10 +291,6 @@ void sys_yield(void) {
syscall1(SYS_SYSTEM, SYSTEM_CMD_YIELD);
}
int sys_get_os_info(os_info_t *info) {
return (int)syscall5(SYS_SYSTEM, SYSTEM_CMD_GET_OS_INFO, (uint64_t)info, 0, 0, 0);
}
void sys_parallel_run(void (*fn)(void*), void **args, int count) {
syscall5(SYS_SYSTEM, SYSTEM_CMD_PARALLEL_RUN, (uint64_t)fn, (uint64_t)args, (uint64_t)count, 0);
}

View File

@@ -44,8 +44,6 @@
#define SYSTEM_CMD_REBOOT 12
#define SYSTEM_CMD_SHUTDOWN 13
#define SYSTEM_CMD_BEEP 14
#define SYSTEM_CMD_MEMINFO 15
#define SYSTEM_CMD_UPTIME 16
#define SYSTEM_CMD_PCI_LIST 17
#define SYSTEM_CMD_NETWORK_DHCP 18
#define SYSTEM_CMD_NETWORK_GET_MAC 19
@@ -69,14 +67,25 @@
#define SYSTEM_CMD_DNS_LOOKUP 37
#define SYSTEM_CMD_SET_DNS 38
#define SYSTEM_CMD_NET_UNLOCK 39
#define SYSTEM_CMD_PROCESS_LIST 44
#define SYSTEM_CMD_GET_CPU_MODEL 45
#define SYSTEM_CMD_SLEEP 46
#define SYSTEM_CMD_SET_RAW_MODE 41
#define SYSTEM_CMD_TCP_RECV_NB 42
#define SYSTEM_CMD_YIELD 43
#define SYSTEM_CMD_GET_OS_INFO 49
#define SYSTEM_CMD_PARALLEL_RUN 50
#define SYSTEM_CMD_TTY_CREATE 60
#define SYSTEM_CMD_TTY_READ_OUT 61
#define SYSTEM_CMD_TTY_WRITE_IN 62
#define SYSTEM_CMD_TTY_READ_IN 63
#define SYSTEM_CMD_SPAWN 64
#define SYSTEM_CMD_TTY_SET_FG 65
#define SYSTEM_CMD_TTY_GET_FG 66
#define SYSTEM_CMD_TTY_KILL_FG 67
#define SYSTEM_CMD_TTY_KILL_ALL 68
#define SYSTEM_CMD_TTY_DESTROY 69
#define SPAWN_FLAG_TERMINAL 0x1
#define SPAWN_FLAG_INHERIT_TTY 0x2
#define SPAWN_FLAG_TTY_ID 0x4
// Internal assembly entry into Ring 0
extern uint64_t syscall0(uint64_t sys_num);
@@ -120,6 +129,17 @@ int sys_exists(const char *path);
int sys_getcwd(char *buf, int size);
int sys_chdir(const char *path);
int sys_tty_create(void);
int sys_tty_read_out(int tty_id, char *buf, int len);
int sys_tty_write_in(int tty_id, const char *buf, int len);
int sys_tty_read_in(char *buf, int len);
int sys_spawn(const char *path, const char *args, uint64_t flags, uint64_t tty_id);
int sys_tty_set_fg(int tty_id, int pid);
int sys_tty_get_fg(int tty_id);
int sys_tty_kill_fg(int tty_id);
int sys_tty_kill_all(int tty_id);
int sys_tty_destroy(int tty_id);
typedef struct {
char name[256];
uint32_t size;
@@ -137,6 +157,7 @@ typedef struct {
char name[64];
uint64_t ticks;
size_t used_memory;
uint32_t is_idle;
} ProcessInfo;
// Network API

View File

@@ -239,8 +239,8 @@ int main(int argc, char** argv) {
}
if (strcmp(argv[1], "init") == 0) {
if (sys_network_init() == 0) printf("Network OK\n");
else printf("Network Fail\n");
if (sys_network_init() == 0) printf("Network [OK]\n");
else printf("Network [FAIL]\n");
return 0;
}

View File

@@ -3,8 +3,6 @@
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include <stdlib.h>
#include <syscall.h>
#include "libc/libui.h"
static int term_cols = 116;
static int term_rows = 41;
@@ -80,13 +78,10 @@ static void telnet_handle_option(uint8_t cmd, uint8_t opt) {
case WILL:
if (opt == OPT_SUPPRESS_GA) {
// Good — accept suppressed GA (most BBS systems do this)
telnet_send_3(IAC, DO, OPT_SUPPRESS_GA);
} else if (opt == OPT_ECHO) {
// Server will echo chars (remote echo) — accept it
telnet_send_3(IAC, DO, OPT_ECHO);
} else {
// Refuse other server offers
telnet_send_3(IAC, DONT, opt);
}
break;
@@ -115,14 +110,13 @@ static void telnet_handle_sb_terminal_type(const uint8_t *sb_data, int sb_len) {
}
typedef enum {
TS_DATA = 0,
TS_IAC,
TS_CMD,
TS_OPT,
TS_SB,
TS_SB_IAC
TS_CMD,
TS_OPT,
TS_SB,
TS_SB_IAC
} TelnetParseState;
static TelnetParseState ts_state = TS_DATA;
@@ -133,7 +127,7 @@ static int ts_sb_pos = 0;
// Output buffer — accumulate non-IAC bytes to write in bulk
static char out_buf[4096];
static int out_pos = 0;
static int out_pos = 0;
static void flush_out(void) {
if (out_pos > 0) {
@@ -159,7 +153,6 @@ static int telnet_process(const uint8_t *data, int len) {
if (b == IAC) {
ts_state = TS_IAC;
} else {
// Pass directly to display
out_char((char)b);
}
break;
@@ -167,7 +160,6 @@ static int telnet_process(const uint8_t *data, int len) {
case TS_IAC:
switch (b) {
case IAC:
// Escaped IAC — literal 0xFF
out_char((char)0xFF);
ts_state = TS_DATA;
break;
@@ -233,10 +225,9 @@ static int telnet_process(const uint8_t *data, int len) {
return 1;
}
static int map_key(char c, uint8_t *key_out) {
if (c == 29) {
// Ctrl+]
// Ctrl+]
return -1;
}
if (c == 17) {
@@ -260,12 +251,12 @@ static int map_key(char c, uint8_t *key_out) {
return 3;
}
if (c == '\n') {
// Enter
// Enter
key_out[0] = '\r'; key_out[1] = '\n';
return 2;
}
if (c == '\b') {
// Backspace
// Backspace
key_out[0] = '\x7f';
return 1;
}
@@ -276,7 +267,7 @@ static int map_key(char c, uint8_t *key_out) {
static int my_atoi(const char *s) {
int v = 0;
while (*s >= '0' && *s <= '9') { v = v*10 + (*s - '0'); s++; }
while (*s >= '0' && *s <= '9') { v = v * 10 + (*s - '0'); s++; }
return v;
}
@@ -284,7 +275,7 @@ static int parse_ip(const char *s, net_ipv4_address_t *ip) {
int part = 0, val = 0;
while (*s) {
if (*s >= '0' && *s <= '9') {
val = val*10 + (*s - '0');
val = val * 10 + (*s - '0');
if (val > 255) return -1;
} else if (*s == '.') {
if (part > 3) return -1;
@@ -340,8 +331,6 @@ int main(int argc, char **argv) {
return 1;
}
printf("Connected. Press Ctrl+] to disconnect.\n\n");
sys_system(41, 1, 0, 0, 0); // SYSTEM_CMD_SET_RAW_MODE = 1
uint8_t recv_buf[4096];
int total = 0;
@@ -349,19 +338,17 @@ int main(int argc, char **argv) {
int connected = 1;
while (connected) {
gui_event_t ev;
while (ui_get_event(0, &ev)) { // win=0 for console proc
if (ev.type == GUI_EVENT_KEY) {
uint8_t key_data[16];
int key_len = map_key((char)ev.arg1, key_data);
if (key_len < 0) {
connected = 0;
break;
}
telnet_send(key_data, key_len);
char ch = 0;
int got = sys_tty_read_in(&ch, 1);
if (got > 0) {
uint8_t key_data[16];
int key_len = map_key(ch, key_data);
if (key_len < 0) {
connected = 0;
break;
}
telnet_send(key_data, key_len);
}
if (!connected) break;
int len = sys_tcp_recv_nb(recv_buf, sizeof(recv_buf) - 1);
if (len < 0) {
@@ -371,8 +358,7 @@ int main(int argc, char **argv) {
}
if (len == 0) {
idle_count++;
// Don't timeout too fast if we are just waiting for user input
if (idle_count > 10000000) {
if (idle_count > 10000000) {
printf("\r\n[Connection timed out]\r\n");
connected = 0;
break;
@@ -395,11 +381,7 @@ int main(int argc, char **argv) {
}
}
// Disable raw mode before exiting
sys_system(41, 0, 0, 0, 0);
sys_tcp_close();
printf("\r\n[Telnet session ended]\r\n");
return 0;
}

View File

@@ -6,11 +6,18 @@
int main(int argc, char **argv) {
(void)argc; (void)argv;
uint64_t ticks = sys_system(16, 0, 0, 0, 0); // SYSTEM_CMD_UPTIME
uint64_t seconds = ticks / 100; // 100Hz timer assumed
uint64_t minutes = seconds / 60;
uint64_t hours = minutes / 60;
uint64_t days = hours / 24;
int fd = sys_open("/proc/uptime", "r");
if (fd < 0) return 1;
char buf[128];
int bytes = sys_read(fd, buf, 127);
sys_close(fd);
if (bytes <= 0) return 1;
buf[bytes] = 0;
int seconds = atoi(buf);
int minutes = seconds / 60;
int hours = minutes / 60;
int days = hours / 24;
printf("Uptime: %d days, %d hours, %d minutes, %d seconds\n",
(int)days, (int)(hours % 24), (int)(minutes % 60), (int)(seconds % 60));

File diff suppressed because it is too large Load Diff

View File

@@ -5,10 +5,10 @@
#include "graphics.h"
#include "font_manager.h"
#include "fat32.h"
#include "vfs.h"
#include "disk.h"
#include "wm.h"
#include "memory_manager.h"
#include "cmd.h"
#include "process.h"
#define EXPLORER_ITEM_HEIGHT 80
#define EXPLORER_ITEM_WIDTH 120
@@ -239,8 +239,8 @@ static void explorer_draw_icon_label(int x, int y, const char *label, uint32_t c
static bool check_desktop_limit_explorer(Window *win) {
ExplorerState *state = (ExplorerState*)win->data;
if (explorer_str_starts_with(state->current_path, "/Desktop")) {
if (explorer_strcmp(state->current_path, "/Desktop") == 0 || explorer_strcmp(state->current_path, "/Desktop/") == 0) { // Check if root desktop
if (explorer_str_starts_with(state->current_path, "/root/Desktop")) {
if (explorer_strcmp(state->current_path, "/root/Desktop") == 0 || explorer_strcmp(state->current_path, "/root/Desktop/") == 0) { // Check if root desktop
if (state->item_count >= desktop_max_cols * (desktop_max_rows_per_col > 1 ? desktop_max_rows_per_col - 1 : 0)) {
state->dialog_state = DIALOG_ERROR;
explorer_strcpy(state->dialog_input, "Desktop is full!");
@@ -302,14 +302,14 @@ static void dialog_confirm_create_file(Window *win) {
}
explorer_strcat(full_path, state->dialog_input);
if (fat32_exists(full_path)) {
if (vfs_exists(full_path)) {
state->dialog_state = DIALOG_CREATE_REPLACE_CONFIRM;
return;
}
FAT32_FileHandle *file = fat32_open(full_path, "w");
vfs_file_t *file = vfs_open(full_path, "w");
if (file) {
fat32_close(file);
vfs_close(file);
explorer_refresh_all();
}
@@ -325,9 +325,9 @@ static void dialog_force_create_file(Window *win) {
}
explorer_strcat(full_path, state->dialog_input);
FAT32_FileHandle *file = fat32_open(full_path, "w");
vfs_file_t *file = vfs_open(full_path, "w");
if (file) {
fat32_close(file);
vfs_close(file);
explorer_refresh_all();
}
dialog_close(win);
@@ -346,7 +346,7 @@ static void dialog_confirm_create_folder(Window *win) {
}
explorer_strcat(full_path, state->dialog_input);
if (fat32_mkdir(full_path)) {
if (vfs_mkdir(full_path)) {
explorer_refresh_all();
}
@@ -354,18 +354,18 @@ static void dialog_confirm_create_folder(Window *win) {
}
bool explorer_delete_permanently(const char *path) {
if (fat32_is_directory(path)) {
if (vfs_is_directory(path)) {
int capacity = 64;
FAT32_FileInfo *entries = (FAT32_FileInfo*)kmalloc(capacity * sizeof(FAT32_FileInfo));
vfs_dirent_t *entries = (vfs_dirent_t*)kmalloc(capacity * sizeof(vfs_dirent_t));
if (!entries) return false;
int count = fat32_list_directory(path, entries, capacity);
int count = vfs_list_directory(path, entries, capacity);
while (count == capacity) {
capacity *= 2;
FAT32_FileInfo *new_entries = (FAT32_FileInfo*)krealloc(entries, capacity * sizeof(FAT32_FileInfo));
vfs_dirent_t *new_entries = (vfs_dirent_t*)krealloc(entries, capacity * sizeof(vfs_dirent_t));
if (!new_entries) { kfree(entries); return false; }
entries = new_entries;
count = fat32_list_directory(path, entries, capacity);
count = vfs_list_directory(path, entries, capacity);
}
for (int i = 0; i < count; i++) {
@@ -381,13 +381,13 @@ bool explorer_delete_permanently(const char *path) {
if (entries[i].is_directory) {
explorer_delete_permanently(child_path);
} else {
fat32_delete(child_path);
vfs_delete(child_path);
}
}
kfree(entries);
return fat32_rmdir(path);
return vfs_rmdir(path);
} else {
return fat32_delete(path);
return vfs_delete(path);
}
}
@@ -421,10 +421,10 @@ bool explorer_delete_recursive(const char *path) {
char origin_path[FAT32_MAX_PATH];
explorer_strcpy(origin_path, dest_path);
explorer_strcat(origin_path, ".origin");
FAT32_FileHandle *fh = fat32_open(origin_path, "w");
vfs_file_t *fh = vfs_open(origin_path, "w");
if (fh) {
fat32_write(fh, path, explorer_strlen(path));
fat32_close(fh);
vfs_write(fh, path, explorer_strlen(path));
vfs_close(fh);
}
explorer_copy_recursive(path, dest_path);
@@ -467,19 +467,19 @@ bool explorer_clipboard_has_content(void) {
}
static bool explorer_copy_recursive(const char *src_path, const char *dest_path) {
if (fat32_is_directory(src_path)) {
if (!fat32_mkdir(dest_path)) return false;
if (vfs_is_directory(src_path)) {
if (!vfs_mkdir(dest_path)) return false;
int capacity = 64;
FAT32_FileInfo *files = (FAT32_FileInfo*)kmalloc(capacity * sizeof(FAT32_FileInfo));
vfs_dirent_t *files = (vfs_dirent_t*)kmalloc(capacity * sizeof(vfs_dirent_t));
if (!files) return false;
int count = fat32_list_directory(src_path, files, capacity);
int count = vfs_list_directory(src_path, files, capacity);
while (count == capacity) {
capacity *= 2;
FAT32_FileInfo *new_files = (FAT32_FileInfo*)krealloc(files, capacity * sizeof(FAT32_FileInfo));
vfs_dirent_t *new_files = (vfs_dirent_t*)krealloc(files, capacity * sizeof(vfs_dirent_t));
if (!new_files) { kfree(files); return false; }
files = new_files;
count = fat32_list_directory(src_path, files, capacity);
count = vfs_list_directory(src_path, files, capacity);
}
for (int i = 0; i < count; i++) {
if (explorer_strcmp(files[i].name, ".") == 0 || explorer_strcmp(files[i].name, "..") == 0) continue;
@@ -498,22 +498,22 @@ static bool explorer_copy_recursive(const char *src_path, const char *dest_path)
kfree(files);
return true;
} else {
FAT32_FileHandle *src = fat32_open(src_path, "r");
FAT32_FileHandle *dst = fat32_open(dest_path, "w");
vfs_file_t *src = vfs_open(src_path, "r");
vfs_file_t *dst = vfs_open(dest_path, "w");
bool success = false;
if (src && dst) {
uint8_t *buf = (uint8_t*)kmalloc(4096);
if (buf) {
int bytes;
success = true;
while ((bytes = fat32_read(src, buf, 4096)) > 0) {
if (fat32_write(dst, buf, bytes) != bytes) { success = false; break; }
while ((bytes = vfs_read(src, buf, 4096)) > 0) {
if (vfs_write(dst, buf, bytes) != bytes) { success = false; break; }
}
kfree(buf);
}
}
if (src) fat32_close(src);
if (dst) fat32_close(dst);
if (src) vfs_close(src);
if (dst) vfs_close(dst);
return success;
}
}
@@ -544,10 +544,10 @@ static void explorer_perform_paste(Window *win, const char *dest_dir) {
explorer_copy_file_internal(clipboard_path, dest_dir);
if (clipboard_action == 2) {
if (fat32_is_directory(clipboard_path)) {
if (vfs_is_directory(clipboard_path)) {
explorer_delete_permanently(clipboard_path);
} else {
fat32_delete(clipboard_path);
vfs_delete(clipboard_path);
}
clipboard_action = 0;
}
@@ -580,7 +580,7 @@ void explorer_clipboard_paste(Window *win, const char *dest_dir) {
}
explorer_strcat(dest_path, filename);
if (fat32_exists(dest_path)) {
if (vfs_exists(dest_path)) {
state->dialog_state = DIALOG_REPLACE_CONFIRM;
explorer_strcpy(state->dialog_dest_dir, dest_dir);
return;
@@ -605,10 +605,10 @@ void explorer_create_shortcut(Window *win, const char *target_path) {
explorer_strcat(shortcut_path, filename);
explorer_strcat(shortcut_path, ".shortcut");
FAT32_FileHandle *fh = fat32_open(shortcut_path, "w");
vfs_file_t *fh = vfs_open(shortcut_path, "w");
if (fh) {
fat32_write(fh, target_path, explorer_strlen(target_path));
fat32_close(fh);
vfs_write(fh, target_path, explorer_strlen(target_path));
vfs_close(fh);
explorer_refresh_all();
}
}
@@ -680,18 +680,18 @@ static void explorer_restore_file(Window *win, int item_idx) {
explorer_strcat(origin_file_path, ".origin");
char original_path[FAT32_MAX_PATH] = {0};
FAT32_FileHandle *fh = fat32_open(origin_file_path, "r");
vfs_file_t *fh = vfs_open(origin_file_path, "r");
if (fh) {
int len = fat32_read(fh, original_path, FAT32_MAX_PATH - 1);
int len = vfs_read(fh, original_path, FAT32_MAX_PATH - 1);
if (len > 0) original_path[len] = 0;
fat32_close(fh);
vfs_close(fh);
}
if (original_path[0] == 0) return;
explorer_copy_recursive(recycle_path, original_path);
explorer_delete_permanently(recycle_path);
fat32_delete(origin_file_path);
vfs_delete(origin_file_path);
explorer_refresh_all();
}
@@ -704,16 +704,17 @@ static void explorer_load_directory(Window *win, const char *path) {
state->item_count = 0;
int capacity = EXPLORER_INITIAL_CAPACITY;
FAT32_FileInfo *entries = (FAT32_FileInfo*)kmalloc(capacity * sizeof(FAT32_FileInfo));
vfs_dirent_t *entries = (vfs_dirent_t*)kmalloc(capacity * sizeof(vfs_dirent_t));
if (!entries) return;
int count = fat32_list_directory(path, entries, capacity);
int count = vfs_list_directory(path, entries, capacity);
while (count == capacity) {
capacity *= 2;
FAT32_FileInfo *new_entries = (FAT32_FileInfo*)krealloc(entries, capacity * sizeof(FAT32_FileInfo));
vfs_dirent_t *new_entries = (vfs_dirent_t*)krealloc(entries, capacity * sizeof(vfs_dirent_t));
if (!new_entries) { kfree(entries); return; }
entries = new_entries;
count = fat32_list_directory(path, entries, capacity);
count = vfs_list_directory(path, entries, capacity);
}
if (state->items_capacity < count) {
@@ -803,22 +804,22 @@ void explorer_open_directory(const char *path) {
explorer_create_window(path);
}
static void explorer_open_target(const char *path) {
if (fat32_is_directory(path)) {
void explorer_open_target(const char *path) {
if (vfs_is_directory(path)) {
explorer_open_directory(path);
} else {
if (explorer_str_ends_with(path, ".elf")) {
process_create_elf(path, NULL);
process_create_elf(path, NULL, false, -1);
} else if (explorer_str_ends_with(path, ".pdf")) {
process_create_elf("/bin/boredword.elf", path);
process_create_elf("/bin/boredword.elf", path, false, -1);
} else if (explorer_is_markdown_file(path)) {
process_create_elf("/bin/markdown.elf", path);
process_create_elf("/bin/markdown.elf", path, false, -1);
} else if (explorer_str_ends_with(path, ".pnt")) {
process_create_elf("/bin/paint.elf", path);
process_create_elf("/bin/paint.elf", path, false, -1);
} else if (explorer_is_image_file(path)) {
process_create_elf("/bin/viewer.elf", path);
process_create_elf("/bin/viewer.elf", path, false, -1);
} else {
process_create_elf("/bin/txtedit.elf", path);
process_create_elf("/bin/txtedit.elf", path, false, -1);
}
}
}
@@ -842,17 +843,17 @@ static void explorer_open_item(Window *win, int index) {
if (explorer_str_ends_with(state->items[index].name, ".shortcut")) {
Window *target = NULL;
if (explorer_strcmp(state->items[index].name, "Notepad.shortcut") == 0) {
process_create_elf("/bin/notepad.elf", NULL); return;
process_create_elf("/bin/notepad.elf", NULL, false, -1); return;
} else if (explorer_strcmp(state->items[index].name, "Calculator.shortcut") == 0) {
process_create_elf("/bin/calculator.elf", NULL); return;
process_create_elf("/bin/calculator.elf", NULL, false, -1); return;
} else if (explorer_strcmp(state->items[index].name, "Terminal.shortcut") == 0) {
target = &win_cmd; cmd_reset();
process_create_elf("/bin/terminal.elf", NULL, false, -1); return;
} else if (explorer_strcmp(state->items[index].name, "Minesweeper.shortcut") == 0) {
process_create_elf("/bin/minesweeper.elf", NULL); return;
process_create_elf("/bin/minesweeper.elf", NULL, false, -1); return;
} else if (explorer_strcmp(state->items[index].name, "Control Panel.shortcut") == 0 || explorer_strcmp(state->items[index].name, "Settings.shortcut") == 0) {
process_create_elf("/bin/settings.elf", NULL); return;
process_create_elf("/bin/settings.elf", NULL, false, -1); return;
} else if (explorer_strcmp(state->items[index].name, "About.shortcut") == 0) {
process_create_elf("/bin/about.elf", NULL); return;
process_create_elf("/bin/about.elf", NULL, false, -1); return;
} else if (explorer_strcmp(state->items[index].name, "Explorer.shortcut") == 0) {
explorer_open_directory("/"); return;
} else if (explorer_strcmp(state->items[index].name, "Recycle Bin.shortcut") == 0) {
@@ -864,11 +865,11 @@ static void explorer_open_item(Window *win, int index) {
return;
}
FAT32_FileHandle *fh = fat32_open(full_path, "r");
vfs_file_t *fh = vfs_open(full_path, "r");
if (fh) {
char buf[FAT32_MAX_PATH];
int len = fat32_read(fh, buf, 255);
fat32_close(fh);
int len = vfs_read(fh, buf, 255);
vfs_close(fh);
if (len > 0) {
buf[len] = 0;
explorer_open_target(buf);
@@ -924,48 +925,11 @@ static void explorer_paint(Window *win) {
draw_rect(offset_x, offset_y, win->w - 8, win->h - 28, COLOR_DARK_BG);
char drive_label[20];
char current_drv = 'A';
if (state->current_path[0] && state->current_path[1] == ':') {
current_drv = state->current_path[0];
} else if (state->current_path[0] && (state->current_path[0] >= 'A' && state->current_path[0] <= 'Z')) {
current_drv = state->current_path[0];
}
const char *type_str = "RAM";
Disk *drv = disk_get_by_letter(current_drv);
if (drv) {
switch (drv->type) {
case DISK_TYPE_RAM: type_str = "RAM"; break;
case DISK_TYPE_IDE: type_str = "IDE"; break;
case DISK_TYPE_SATA: type_str = "SATA"; break;
case DISK_TYPE_USB: type_str = "USB"; break;
default: type_str = "???"; break;
}
}
int di = 0;
drive_label[di++] = '[';
drive_label[di++] = ' ';
drive_label[di++] = current_drv;
drive_label[di++] = ':';
const char *ts = type_str;
while (*ts) drive_label[di++] = *ts++;
drive_label[di++] = ' ';
drive_label[di++] = ' ';
drive_label[di++] = ' ';
drive_label[di++] = ']';
drive_label[di] = 0;
ttf_font_t *ttf_ = graphics_get_current_ttf();
int drive_label_w = ttf_ ? font_manager_get_string_width(ttf_, drive_label) + 16 : 80;
if (drive_label_w < 60) drive_label_w = 60;
draw_rounded_rect_filled(win->x + 4, offset_y + 3, drive_label_w, 22, 5, COLOR_DARK_PANEL);
draw_string(win->x + 12, offset_y + 8, drive_label, COLOR_DARK_TEXT);
int path_height = 22;
int path_x = offset_x + drive_label_w + 8;
int path_w = win->w - 16 - drive_label_w - 8;
int path_x = offset_x;
int path_w = win->w - 16 - 8;
draw_rounded_rect_filled(path_x, offset_y + 3, path_w, path_height, 5, COLOR_DARK_PANEL);
draw_string(path_x + 6, offset_y + 8, "Path:", COLOR_DARK_TEXT);
int path_label_w = ttf_ ? font_manager_get_string_width(ttf_, "Path:") : 40;
@@ -1013,35 +977,6 @@ static void explorer_paint(Window *win) {
graphics_pop_clipping(); // Pop content clipping
graphics_pop_clipping(); // Pop main window clipping
if (state->drive_menu_visible) {
int menu_x = win->x + 4;
int menu_y = offset_y + 26;
int menu_w = 80;
int count = disk_get_count();
int menu_h = count * 25;
draw_rounded_rect_filled(menu_x, menu_y, menu_w, menu_h, 6, COLOR_DARK_PANEL);
for (int i = 0; i < count; i++) {
Disk *d = disk_get_by_index(i);
if (d) {
char buf[16];
buf[0] = d->letter;
buf[1] = ':';
buf[2] = ' ';
int n = 0; while(d->name[n] && n < 10) { buf[3+n] = d->name[n]; n++; }
buf[3+n] = 0;
if (d->letter == current_drv) {
draw_rounded_rect_filled(menu_x + 2, menu_y + i*25 + 2, menu_w - 4, 21, 4, 0xFF4A90E2);
draw_string(menu_x + 5, menu_y + i*25 + 6, buf, COLOR_WHITE);
} else {
draw_string(menu_x + 5, menu_y + i*25 + 6, buf, COLOR_DARK_TEXT);
}
}
}
}
if (state->dropdown_menu_visible) {
int menu_x = dropdown_btn_x;
int menu_y = offset_y + 26;
@@ -1315,7 +1250,7 @@ static void explorer_handle_click(Window *win, int x, int y) {
if (new_path[explorer_strlen(new_path)-1] != '/') explorer_strcat(new_path, "/");
explorer_strcat(new_path, state->dialog_input);
if (fat32_rename(state->dialog_target_path, new_path)) explorer_refresh_all();
if (vfs_rename(state->dialog_target_path, new_path)) explorer_refresh_all();
dialog_close(win);
return;
}
@@ -1336,31 +1271,7 @@ static void explorer_handle_click(Window *win, int x, int y) {
return;
}
if (state->drive_menu_visible) {
int menu_x = 4;
int menu_y = 26;
int menu_w = 80;
int count = disk_get_count();
int menu_h = count * 25;
if (x >= menu_x && x < menu_x + menu_w && y >= menu_y && y < menu_y + menu_h) {
int idx = (y - menu_y) / 25;
Disk *d = disk_get_by_index(idx);
if (d) {
char path[4];
path[0] = d->letter;
path[1] = ':';
path[2] = '/';
path[3] = 0;
explorer_load_directory(win, path);
}
state->drive_menu_visible = false;
return;
}
state->drive_menu_visible = false;
return;
}
if (state->dropdown_menu_visible) {
int dropdown_btn_x = win->w - 90;
@@ -1396,17 +1307,11 @@ static void explorer_handle_click(Window *win, int x, int y) {
}
int button_y = 3;
if (x >= 4 && x < 64 && y >= button_y && y < button_y + 22) {
state->drive_menu_visible = !state->drive_menu_visible;
state->dropdown_menu_visible = false;
return;
}
if (WIDGET_CLICKED(&state->btn_dropdown, win->x + x, win->y + y + 20)) {
state->btn_dropdown.pressed = false;
dropdown_menu_toggle(win);
state->drive_menu_visible = false;
return;
}
@@ -1477,7 +1382,7 @@ static void explorer_handle_key(Window *win, char c, bool pressed) {
explorer_strcpy(new_path, state->current_path);
if (new_path[explorer_strlen(new_path)-1] != '/') explorer_strcat(new_path, "/");
explorer_strcat(new_path, state->dialog_input);
if (fat32_rename(state->dialog_target_path, new_path)) explorer_refresh(win);
if (vfs_rename(state->dialog_target_path, new_path)) explorer_refresh(win);
dialog_close(win);
}
} else if (c == 19) {
@@ -1699,7 +1604,7 @@ static void explorer_handle_file_context_menu_click(Window *win, int x, int y) {
state->dialog_input_cursor = explorer_strlen(state->dialog_input);
explorer_strcpy(state->dialog_target_path, full_path);
} else if (clicked_action == 110) { // Open with Text Editor
process_create_elf("/bin/txtedit.elf", full_path);
process_create_elf("/bin/txtedit.elf", full_path, false, -1);
} else if (clicked_action == ACTION_RESTORE) {
explorer_restore_file(win, state->file_context_menu_item);
} else if (clicked_action == ACTION_CREATE_SHORTCUT) {
@@ -1793,10 +1698,10 @@ static void explorer_perform_move_internal(Window *win, const char *source_path,
char origin_path[FAT32_MAX_PATH];
explorer_strcpy(origin_path, dest_path);
explorer_strcat(origin_path, ".origin");
FAT32_FileHandle *fh = fat32_open(origin_path, "w");
vfs_file_t *fh = vfs_open(origin_path, "w");
if (fh) {
fat32_write(fh, source_path, explorer_strlen(source_path));
fat32_close(fh);
vfs_write(fh, source_path, explorer_strlen(source_path));
vfs_close(fh);
}
}
@@ -1804,7 +1709,7 @@ static void explorer_perform_move_internal(Window *win, const char *source_path,
char origin_path[FAT32_MAX_PATH];
explorer_strcpy(origin_path, source_path);
explorer_strcat(origin_path, ".origin");
fat32_delete(origin_path);
vfs_delete(origin_path);
}
if (explorer_copy_recursive(source_path, dest_path)) {
@@ -1838,7 +1743,7 @@ void explorer_import_file_to(Window *win, const char *source_path, const char *d
if (dest_path[explorer_strlen(dest_path) - 1] != '/') explorer_strcat(dest_path, "/");
explorer_strcat(dest_path, filename);
if (fat32_exists(dest_path) && explorer_strcmp(source_path, dest_path) != 0) {
if (vfs_exists(dest_path) && explorer_strcmp(source_path, dest_path) != 0) {
explorer_strcpy(state->dialog_move_src, source_path);
explorer_strcpy(state->dialog_dest_dir, dest_dir);
state->dialog_state = DIALOG_REPLACE_MOVE_CONFIRM;

View File

@@ -12,7 +12,6 @@
// External windows references (for opening other apps)
extern Window win_explorer;
extern Window win_editor;
extern Window win_cmd;
extern Window win_notepad;
extern Window win_markdown;
@@ -70,10 +69,10 @@ typedef struct {
void explorer_init(void);
void explorer_reset(void);
void explorer_open_directory(const char *path); // Creates a NEW window
void explorer_open_directory(const char *path);
void explorer_open_target(const char *path);
// Drag and Drop support
// This now needs to find WHICH explorer window is under the mouse
bool explorer_get_file_at(int screen_x, int screen_y, char *out_path, bool *is_dir);
void explorer_import_file(Window *win, const char *source_path); // To focused or default
void explorer_import_file_to(Window *win, const char *source_path, const char *dest_dir);
@@ -81,18 +80,15 @@ void explorer_refresh(Window *win);
void explorer_refresh_all(void);
void explorer_clear_click_state(Window *win);
// String Helpers
size_t explorer_strlen(const char *str);
void explorer_strcpy(char *dest, const char *src);
void explorer_strcat(char *dest, const char *src);
// Clipboard (System-wide)
void explorer_clipboard_copy(const char *path);
void explorer_clipboard_cut(const char *path);
void explorer_clipboard_paste(Window *win, const char *dest_dir);
bool explorer_clipboard_has_content(void);
// File Operations
bool explorer_delete_permanently(const char *path);
bool explorer_delete_recursive(const char *path);
void explorer_create_shortcut(Window *win, const char *target_path);

View File

@@ -330,6 +330,13 @@ int font_manager_get_string_width_scaled(ttf_font_t *font, const char *s, float
while (*s) {
int advance, lsb;
uint32_t codepoint = utf8_decode(&s);
if (codepoint == '\t') {
stbtt_GetCodepointHMetrics(info, ' ', &advance, &lsb);
width += (int)(advance * real_scale + 0.5f) * 4;
continue;
}
stbtt_fontinfo *current_info = info;
float current_scale = real_scale;
@@ -350,6 +357,12 @@ int font_manager_get_codepoint_width_scaled(ttf_font_t *font, uint32_t codepoint
stbtt_fontinfo *info = (stbtt_fontinfo *)font->info;
float real_scale = stbtt_ScaleForPixelHeight(info, scale);
if (codepoint == '\t') {
int advance, lsb;
stbtt_GetCodepointHMetrics(info, ' ', &advance, &lsb);
return (int)(advance * real_scale + 0.5f) * 4;
}
if (stbtt_FindGlyphIndex(info, codepoint) == 0 && fallback_font) {
info = (stbtt_fontinfo *)fallback_font->info;
real_scale = stbtt_ScaleForPixelHeight(info, scale);

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