Kernel V3.0.0

This commit is contained in:
boreddevnl 2026-02-25 22:59:50 +01:00
parent ca997072ce
commit 73a34edd0e
81 changed files with 272886 additions and 13685 deletions

View file

@ -83,6 +83,7 @@ $(BUILD_DIR)/process_asm.o: $(SRC_DIR)/process_asm.asm | $(BUILD_DIR)
# Link Kernel
$(KERNEL_ELF): $(OBJ_FILES)
$(LD) $(LDFLAGS) -o $@ $(OBJ_FILES)
$(MAKE) -C $(SRC_DIR)/userland
# Create ISO
$(ISO_IMAGE): $(KERNEL_ELF) limine.cfg limine-setup
@ -92,13 +93,18 @@ $(ISO_IMAGE): $(KERNEL_ELF) limine.cfg limine-setup
# Copy Kernel and Config
cp $(KERNEL_ELF) $(ISO_DIR)/
# Build ISO limine.cfg natively with modules
cp limine.cfg $(ISO_DIR)/
mkdir -p $(ISO_DIR)/bin
@for f in $(SRC_DIR)/userland/*.elf; do \
if [ -f "$$f" ]; then \
basename=$$(basename "$$f"); \
cp "$$f" $(ISO_DIR)/bin/; \
echo " MODULE_PATH=boot:///bin/$$basename" >> $(ISO_DIR)/limine.cfg; \
fi \
done
# Copy README
cp README.md $(ISO_DIR)/
# Copy user file.c if it exists
@if [ -f file.c ]; then cp file.c $(ISO_DIR)/; fi
# Copy Wallpaper (if it exists)
@if [ -f src/kernel/wallpaper.ppm ]; then cp src/kernel/wallpaper.ppm $(ISO_DIR)/; fi
@ -126,6 +132,8 @@ clean:
rm -rf $(BUILD_DIR) $(ISO_DIR) $(ISO_IMAGE)
run: $(ISO_IMAGE)
dd if=/dev/zero of=disk.img bs=1M count=64
mformat -i disk.img -F ::
qemu-system-x86_64 -m 2G -serial stdio -cdrom $< -boot d \
-audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \
-netdev user,id=net0,hostfwd=udp::12345-:12345 -device e1000,netdev=net0 \

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
disk.img

Binary file not shown.

View file

@ -1,166 +0,0 @@
# BoredOS
BoredOS has now exited Beta stage and is "stable enough" to be put out as a "stable" product.
<img src="bored.svg" width="200" /> </br>
BoredOS is a simple x86_64 hobbyist operating system.
It features a DE (and WM), a FAT32 filesystem, customizable UI and much much more!
![Screenshot](screenshot.jpg)
*this screenshot might be outdated*
## Features
- JPG image support
- Disk manager
- Drag and drop mouse centered UI
- Customizable UI
- Basic Networking Stack
- Bored WM
- Fat 32 FS
- 64-bit long mode support
- Multiboot2 compliant
- Text editor
- Markdown Viewer
- Minesweeper
- Markdown Viewer
- GUI Text editor
- Paint application
- IDT
- Ability to run on actual x86_64 hardware
- CLI
- (Limited) C Compiler
## Prerequisites
To build BoredOS, you'll need the following tools installed:
- **x86_64 ELF Toolchain**: `x86_64-elf-gcc`, `x86_64-elf-ld`
- **NASM**: Netwide Assembler for compiling assembly code
- **xorriso**: For creating bootable ISO images
- **QEMU** (optional): For testing the kernel in an emulator
On macOS, you can install these using Homebrew:
```sh
brew install x86_64-elf-binutils x86_64-elf-gcc nasm xorriso qemu
```
## Building
Simply run `make` from the project root:
```sh
make
```
This will:
1. Compile all kernel C sources and assembly files
2. Link the kernel ELF binary
3. Generate a bootable ISO image (`boredos.iso`)
The build output is organized as follows:
- Compiled object files: `build/`
- ISO root filesystem: `iso_root/`
- Final ISO image: `boredos.iso`
## Running
### QEMU Emulation
Run the kernel in QEMU:
```sh
make run
```
Or manually:
```sh
qemu-system-x86_64 -m 2G -serial stdio -cdrom boredos.iso -boot d
```
### Running on Real Hardware
*Warning: This is at YOUR OWN RISK. This software comes with ZERO warranty and may break your system.*
1. **Create bootable USB**: Use [Balena Etcher](https://www.balena.io/etcher/) to flash `boredos.iso` to a USB drive
2. **Prepare the system**:
- Enable legacy (BIOS) boot in your system BIOS/UEFI settings
- Disable Secure Boot if needed
3. **Boot**: Insert the USB drive and select it in the boot menu during startup
**Networking requires an Intel E1000 network card or similar while using Ethernet.**
4. **Tested Hardware**:
- HP EliteDesk 705 G4 DM (AMD Ryzen 5 PRO 2400G, Radeon Vega) **Tested, no networking.**
- Lenovo ThinkPad A475 20KL002VMH (AMD Pro A12-8830B, Radeon R7) **Tested, no networking.**
- Acer Aspire E5-573-311M (Intel Core i3-5005U, Intel HD Graphics) **Tested, no networking.**
## Project Structure
- `src/kernel/` - Main kernel implementation
- `boot.asm` - Boot assembly code
- `main.c` - Kernel entry point
- `*.c / *.h` - Core kernel modules (graphics, interrupts, filesystem, etc.)
- `cli_apps/` - Command-line applications
- `build/` - Compiled object files (generated during build)
- `iso_root/` - ISO filesystem layout (generated during build)
- `limine/` - Limine bootloader files (downloaded automatically)
- `linker.ld` - Linker script for x86_64 ELF
- `limine.cfg` - Limine bootloader configuration
- `Makefile` - Build configuration and targets
###
###
<h2 align="left">Help me brew some coffee! ☕️</h2>
###
<p align="left">
If you enjoy this project, and like what i'm doing here, consider buying me a coffee!
<br><br>
<a href="https://buymeacoffee.com/boreddevnl" target="_blank">
<img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="50" style="border-radius: 8px;" />
</a>
</p>
###
## This project was previously labeled as "BrewKernel"
Brewkernel was a text only very simple (and messy) project i started 3 years ago. It was my first work in OSDev and i absolutely loved it. It sadly just got too messy and i myself couldn't understand my own code anymore. About a year ago i started work on BoredOS, and pushed a *"working"* version of it a few days ago as of writing this *(Feb. 10 2026)*
Brewkernel has already been deprecated and will not be accepting any pull requests or fix any issues as it is now a public archive.
Thanks to everyone who helped me with Brewkernel, even if it were just ideas, and intend to keep working on this for the forseeable future!
## License
Copyright (C) 2024-2026 boreddevnl
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
NOTICE
------
This product includes software developed by Chris ("boreddevnl") as part of the BoredOS (Previously Brewkernel/BrewOS) project.
Copyright (C) 20242026 Chris / boreddevnl (previously boreddevhq)
All source files in this repository contain copyright and license
headers that must be preserved in redistributions and derivative works.
If you distribute or modify this project (in whole or in part),
you MUST:
- Retain all copyright and license headers at the top of each file.
- Include this NOTICE file along with any redistributions or
derivative works.
- Provide clear attribution to the original author in documentation
or credits where appropriate.
The above attribution requirements are informational and intended to
ensure proper credit is given. They do not alter or supersede the
terms of the GNU General Public License (GPL), which governs this work.

BIN
iso_root/bin/calculator.elf Executable file

Binary file not shown.

BIN
iso_root/bin/crash.elf Executable file

Binary file not shown.

BIN
iso_root/bin/hello.elf Executable file

Binary file not shown.

Binary file not shown.

View file

@ -4,6 +4,8 @@ TIMEOUT=3
PROTOCOL=limine
KERNEL_PATH=boot:///boredos.elf
#FRAMEBUFFER_WIDTH=1280
#FRAMEBUFFER_HEIGHT=720
MODULE_PATH=boot:///bin/calculator.elf
MODULE_PATH=boot:///bin/crash.elf
MODULE_PATH=boot:///bin/hello.elf

View file

@ -4,6 +4,5 @@ TIMEOUT=3
PROTOCOL=limine
KERNEL_PATH=boot:///boredos.elf
#FRAMEBUFFER_WIDTH=1280
#FRAMEBUFFER_HEIGHT=720

File diff suppressed because it is too large Load diff

View file

@ -14,8 +14,8 @@ static void about_paint(Window *win) {
// Version info
draw_string(offset_x, offset_y + 105, "BoredOS 'Panda'", COLOR_WHITE);
draw_string(offset_x, offset_y + 120, "BoredOS Version 1.63", COLOR_WHITE);
draw_string(offset_x, offset_y + 135, "Kernel Version 2.5.3", COLOR_WHITE);
draw_string(offset_x, offset_y + 120, "BoredOS Version 1.64", COLOR_WHITE);
draw_string(offset_x, offset_y + 135, "Kernel Version 3.0.0", COLOR_WHITE);
// Copyright
draw_string(offset_x, offset_y + 150, "(C) 2026 boreddevnl.", COLOR_WHITE);

View file

@ -1,10 +0,0 @@
#ifndef CALCULATOR_H
#define CALCULATOR_H
#include "wm.h"
extern Window win_calculator;
void calculator_init(void);
#endif

View file

@ -2,6 +2,6 @@
void cli_cmd_boredver(char *args) {
(void)args;
cli_write("BoredOS v1.63\n");
cli_write("BoredOS Kernel V2.5.3\n");
cli_write("BoredOS v1.64\n");
cli_write("BoredOS Kernel V3.0.0\n");
}

View file

@ -9,7 +9,6 @@ extern Window win_editor;
extern Window win_explorer;
extern Window win_cmd;
extern Window win_notepad;
extern Window win_calculator;
void cli_cmd_txtedit(char *args) {
// Parse the file path argument
@ -52,7 +51,6 @@ void cli_cmd_txtedit(char *args) {
if (win_explorer.z_index > max_z) max_z = win_explorer.z_index;
if (win_cmd.z_index > max_z) max_z = win_cmd.z_index;
if (win_notepad.z_index > max_z) max_z = win_notepad.z_index;
if (win_calculator.z_index > max_z) max_z = win_calculator.z_index;
win_editor.z_index = max_z + 1;
cli_write("Opening: ");

View file

@ -4,13 +4,13 @@
#include "io.h"
#include "rtc.h"
#include "notepad.h"
#include "calculator.h"
#include "fat32.h"
#include "disk.h"
#include "cli_apps/cli_apps.h"
#include "licensewr.h"
#include <stddef.h>
#include "memory_manager.h"
#include "process.h"
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
@ -61,6 +61,9 @@ static char pager_wrapped_lines[2000][CMD_COLS + 1];
static int pager_total_lines = 0;
static int pager_top_line = 0;
// Process Execution State
bool cmd_is_waiting_for_process = false;
// Boot time for uptime
int boot_time_init = 0;
@ -147,6 +150,13 @@ static void cmd_history_add(const char *cmd) {
}
static void cmd_print_prompt(void) {
// Clear the current line to prevent old output from corrupting the new command buffer
for (int i = 0; i < CMD_COLS; i++) {
screen_buffer[cursor_row][i].c = 0;
screen_buffer[cursor_row][i].color = current_color;
}
cursor_col = 0;
char buf[5];
buf[0] = cmd_state ? cmd_state->current_drive : 'A';
buf[1] = ':';
@ -235,6 +245,10 @@ void cmd_putchar(char c) {
cmd_scroll_up();
cursor_row = CMD_ROWS - 1;
}
// Trigger repaint so output from syscalls is immediately visible
wm_mark_dirty(win_cmd.x, win_cmd.y, win_cmd.w, win_cmd.h);
wm_refresh();
}
// Public for CLI apps to use
@ -265,6 +279,23 @@ void cmd_write_int(int n) {
cmd_write(buf);
}
void cmd_write_hex(uint64_t n) {
const char* digits = "0123456789ABCDEF";
char buf[19];
buf[0] = '0';
buf[1] = 'x';
buf[18] = '\0';
for (int i = 17; i >= 2; i--) {
buf[i] = digits[n & 0xF];
n >>= 4;
}
cmd_write(buf);
}
int cmd_get_cursor_col(void) {
return cursor_col;
}
// --- Pager Logic ---
// Public for CLI apps to use - clear the terminal screen
@ -524,7 +555,6 @@ static void internal_cmd_txtedit(char *args) {
extern Window win_explorer;
extern Window win_cmd;
extern Window win_notepad;
extern Window win_calculator;
win_editor.visible = true;
win_editor.focused = true;
@ -534,7 +564,6 @@ static void internal_cmd_txtedit(char *args) {
if (win_explorer.z_index > max_z) max_z = win_explorer.z_index;
if (win_cmd.z_index > max_z) max_z = win_cmd.z_index;
if (win_notepad.z_index > max_z) max_z = win_notepad.z_index;
if (win_calculator.z_index > max_z) max_z = win_calculator.z_index;
win_editor.z_index = max_z + 1;
cmd_write("Opening: ");
@ -580,7 +609,59 @@ static void internal_cmd_ls(char *args) {
kfree(files);
}
// --- Commands (now delegated to cli_apps/) ---
void cmd_exec_elf(char *args) {
if (!args || args[0] == '\0') {
cmd_write("Usage: exec <filename>\n");
return;
}
char full_exec_path[512] = {0};
int i = 0;
if (args[0] && args[1] != ':') {
if (cmd_state) {
full_exec_path[i++] = cmd_state->current_drive;
full_exec_path[i++] = ':';
const char *dir = cmd_state->current_dir;
while (*dir && i < 509) {
full_exec_path[i++] = *dir++;
}
if (i > 2 && full_exec_path[i-1] != '/') {
full_exec_path[i++] = '/';
}
}
const char *p = args;
while (*p && i < 509) {
full_exec_path[i++] = *p++;
}
full_exec_path[i] = 0;
} else {
// Just copy what they provided
const char *p = args;
while (*p && i < 511) {
full_exec_path[i++] = *p++;
}
full_exec_path[i] = 0;
}
cmd_is_waiting_for_process = true;
process_create_elf(full_exec_path);
}
// Public API for syscall exit
void cmd_process_finished(void) {
if (cmd_is_waiting_for_process) {
cmd_is_waiting_for_process = false;
extern int cursor_col;
void cmd_putchar(char c);
if (cursor_col > 0) {
cmd_putchar('\n');
}
cmd_print_prompt();
}
}
// Command dispatch table
typedef struct {
@ -589,6 +670,8 @@ typedef struct {
} CommandEntry;
static const CommandEntry commands[] = {
{"EXEC", cmd_exec_elf},
{"exec", cmd_exec_elf},
{"HELP", cli_cmd_help},
{"help", cli_cmd_help},
{"DATE", cli_cmd_date},
@ -1060,44 +1143,66 @@ static void cmd_exec_single(char *cmd) {
}
}
// Check for executable in /Apps/
char app_path[256];
int app_idx = 0;
// Check for executable in Current Directory or A:/bin/
char search_path[512];
// Add drive letter if on different drive
if (cmd_state && cmd_state->current_drive != 'A') {
app_path[app_idx++] = cmd_state->current_drive;
app_path[app_idx++] = ':';
}
const char *prefix = "/Apps/";
while (*prefix) app_path[app_idx++] = *prefix++;
char *c = cmd;
while (*c && app_idx < 255) app_path[app_idx++] = *c++;
app_path[app_idx] = 0;
FAT32_FileHandle *app_fh = fat32_open(app_path, "r");
if (app_fh) {
uint8_t *app_buffer = (uint8_t*)kmalloc(VM_MEMORY_SIZE);
if (app_buffer) {
int size = fat32_read(app_fh, app_buffer, VM_MEMORY_SIZE);
fat32_close(app_fh);
if (size > 0) {
int res = vm_exec(app_buffer, size);
if (res != 0) {
cmd_write("Execution failed (invalid format or runtime error).\n");
}
} else {
cmd_write("Error: Empty file.\n");
}
kfree(app_buffer);
} else {
fat32_close(app_fh);
cmd_write("Error: Out of memory.\n");
// Check if the command already ends in .elf (case insensitive)
bool has_elf_ext = false;
int cmd_len = cmd_strlen(cmd);
if (cmd_len > 4) {
const char *ext = cmd + cmd_len - 4;
if ((ext[0] == '.' && (ext[1] == 'e' || ext[1] == 'E') &&
(ext[2] == 'l' || ext[2] == 'L') && (ext[3] == 'f' || ext[3] == 'F'))) {
has_elf_ext = true;
}
return;
}
// 1. Try Current Directory + .elf
if (cmd_state) {
int idx = 0;
search_path[idx++] = cmd_state->current_drive;
search_path[idx++] = ':';
const char *dir = cmd_state->current_dir;
while (*dir && idx < 500) search_path[idx++] = *dir++;
if (idx > 2 && search_path[idx-1] != '/') search_path[idx++] = '/';
const char *c = cmd;
while (*c && idx < 500) search_path[idx++] = *c++;
if (!has_elf_ext) {
search_path[idx++] = '.'; search_path[idx++] = 'e'; search_path[idx++] = 'l'; search_path[idx++] = 'f';
}
search_path[idx] = 0;
FAT32_FileHandle *fh = fat32_open(search_path, "r");
if (fh) {
fat32_close(fh);
cmd_is_waiting_for_process = true;
process_create_elf(search_path);
return;
}
}
// 2. Try A:/bin/ + .elf
{
int idx = 0;
const char *bin_prefix = "A:/bin/";
while (*bin_prefix) search_path[idx++] = *bin_prefix++;
const char *c = cmd;
while (*c && idx < 500) search_path[idx++] = *c++;
if (!has_elf_ext) {
search_path[idx++] = '.'; search_path[idx++] = 'e'; search_path[idx++] = 'l'; search_path[idx++] = 'f';
}
search_path[idx] = 0;
FAT32_FileHandle *fh = fat32_open(search_path, "r");
if (fh) {
fat32_close(fh);
cmd_is_waiting_for_process = true;
process_create_elf(search_path);
return;
}
}
cmd_write("Unknown command: ");
cmd_write(cmd);
@ -1402,7 +1507,9 @@ static void cmd_key(Window *target, char c) {
cmd_exec(cmd_buf);
cmd_print_prompt();
if (!cmd_is_waiting_for_process) {
cmd_print_prompt();
}
} else if (c == 17) { // UP
if (history_len > 0) {
if (history_pos == -1) {
@ -1476,6 +1583,8 @@ static void create_test_files(void) {
if (!fat32_exists("Apps")) fat32_mkdir("Apps");
if (!fat32_exists("Desktop")) fat32_mkdir("Desktop");
if (!fat32_exists("RecycleBin")) fat32_mkdir("RecycleBin");
if (!fat32_exists("bin")) fat32_mkdir("bin");
// Always try to write README to ensure content exists
@ -1676,7 +1785,6 @@ static void create_test_files(void) {
void cmd_init(void) {
fat32_init(); // Init FAT32 filesystem
create_test_files();
win_cmd.title = "Command Prompt";

View file

@ -12,6 +12,8 @@ void cmd_reset(void);
void cmd_write(const char *str);
void cmd_putchar(char c);
void cmd_write_int(int n);
void cmd_write_hex(uint64_t n);
int cmd_get_cursor_col(void);
void cmd_screen_clear(void);
void cmd_increment_msg_count(void);

143
src/kernel/elf.c Normal file
View file

@ -0,0 +1,143 @@
#include "elf.h"
#include "fat32.h"
#include "memory_manager.h"
#include "paging.h"
#include "platform.h"
extern void serial_print(const char *s);
extern void serial_write(const char *str);
static void print_hex(uint64_t n) {
char buf[17];
char* x = "0123456789ABCDEF";
buf[16] = 0;
for (int i=15; i>=0; i--) { buf[i] = x[n & 0xF]; n >>= 4; }
serial_write(buf);
}
uint64_t elf_load(const char *path, uint64_t user_pml4) {
FAT32_FileHandle *file = fat32_open(path, "r");
if (!file || !file->valid) {
serial_write("[ELF] Error: Failed to open file ");
serial_write(path);
serial_write("\n");
return 0;
}
// Read the ELF Header
Elf64_Ehdr ehdr;
if (fat32_read(file, &ehdr, sizeof(Elf64_Ehdr)) != sizeof(Elf64_Ehdr)) {
serial_write("[ELF] Error: Could not read ELF Header\n");
fat32_close(file);
return 0;
}
// Validate Magic Number & Properties
if (ehdr.e_ident[0] != ELFMAG0 || ehdr.e_ident[1] != ELFMAG1 ||
ehdr.e_ident[2] != ELFMAG2 || ehdr.e_ident[3] != ELFMAG3) {
serial_write("[ELF] Error: Invalid ELF Magic Number\n");
fat32_close(file);
return 0;
}
if (ehdr.e_ident[4] != ELFCLASS64) {
serial_write("[ELF] Error: Not a 64-bit ELF\n");
fat32_close(file);
return 0;
}
if (ehdr.e_ident[5] != ELFDATA2LSB) {
serial_write("[ELF] Error: Not Little Endian\n");
fat32_close(file);
return 0;
}
if (ehdr.e_type != ET_EXEC && ehdr.e_type != ET_DYN) {
serial_write("[ELF] Error: Not an Executable\n");
fat32_close(file);
return 0;
}
if (ehdr.e_machine != EM_X86_64) {
serial_write("[ELF] Error: Not x86_64 Architecture\n");
fat32_close(file);
return 0;
}
serial_write("[ELF] Number of program headers: ");
print_hex(ehdr.e_phnum);
serial_write("\n");
// Iterate Over Program Headers
for (int i = 0; i < ehdr.e_phnum; i++) {
fat32_seek(file, ehdr.e_phoff + (i * ehdr.e_phentsize), 0);
Elf64_Phdr phdr;
if (fat32_read(file, &phdr, sizeof(Elf64_Phdr)) != sizeof(Elf64_Phdr)) {
serial_write("[ELF] Error: Failed to read Program Header\n");
continue;
}
serial_write("[ELF] Header type parsed: ");
print_hex(phdr.p_type);
serial_write("\n");
// Only load segments with type PT_LOAD
if (phdr.p_type == PT_LOAD) {
uint64_t p_vaddr = phdr.p_vaddr;
uint64_t p_memsz = phdr.p_memsz;
uint64_t p_filesz = phdr.p_filesz;
uint64_t p_offset = phdr.p_offset;
serial_write("[ELF] Loaded PT_LOAD segment vaddr=");
print_hex(p_vaddr);
serial_write("\n");
if (p_memsz == 0) continue;
// Calculate page-aligned boundaries
uint64_t align_offset = p_vaddr & 0xFFF;
uint64_t start_page = p_vaddr & ~0xFFF;
uint64_t num_pages = (p_memsz + align_offset + 0xFFF) / 4096;
// Allocate and Map Pages
for (uint64_t p = 0; p < num_pages; p++) {
uint64_t vaddr = start_page + (p * 4096);
void* phys = kmalloc_aligned(4096, 4096);
if (!phys) {
serial_write("[ELF] Error: Out of memory mapping PT_LOAD\n");
fat32_close(file);
return 0;
}
// Determine Flags
uint64_t flags = 0x07; // Present, RW, User
paging_map_page(user_pml4, vaddr, v2p((uint64_t)phys), flags);
// Initialize page memory
uint8_t* dest = (uint8_t*)phys;
for (int j=0; j<4096; j++) dest[j] = 0; // zero memory (handles BSS)
// If loading from file
if (p_filesz > 0) {
uint64_t copy_start_offset = 0;
uint64_t file_seek_pos = p_offset;
uint64_t bytes_to_copy = 4096;
if (p == 0) {
copy_start_offset = align_offset;
bytes_to_copy = 4096 - align_offset;
} else {
file_seek_pos += (p * 4096) - align_offset;
}
if (bytes_to_copy > p_filesz) {
bytes_to_copy = p_filesz;
}
if (bytes_to_copy > 0) {
fat32_seek(file, file_seek_pos, 0);
fat32_read(file, dest + copy_start_offset, bytes_to_copy);
p_filesz -= bytes_to_copy;
}
}
}
}
}
fat32_close(file);
return ehdr.e_entry;
}

74
src/kernel/elf.h Normal file
View file

@ -0,0 +1,74 @@
#ifndef ELF_H
#define ELF_H
#include <stdint.h>
typedef uint16_t Elf64_Half;
typedef uint32_t Elf64_Word;
typedef int32_t Elf64_Sword;
typedef uint64_t Elf64_Xword;
typedef int64_t Elf64_Sxword;
typedef uint64_t Elf64_Addr;
typedef uint64_t Elf64_Off;
#define EI_NIDENT 16
typedef struct {
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf64_Half e_type; /* Object file type */
Elf64_Half e_machine; /* Architecture */
Elf64_Word e_version; /* Object file version */
Elf64_Addr e_entry; /* Entry point virtual address */
Elf64_Off e_phoff; /* Program header table file offset */
Elf64_Off e_shoff; /* Section header table file offset */
Elf64_Word e_flags; /* Processor-specific flags */
Elf64_Half e_ehsize; /* ELF header size in bytes */
Elf64_Half e_phentsize; /* Program header table entry size */
Elf64_Half e_phnum; /* Program header table entry count */
Elf64_Half e_shentsize; /* Section header table entry size */
Elf64_Half e_shnum; /* Section header table entry count */
Elf64_Half e_shstrndx; /* Section header string table index */
} Elf64_Ehdr;
typedef struct {
Elf64_Word p_type; /* Segment type */
Elf64_Word p_flags; /* Segment flags */
Elf64_Off p_offset; /* Segment file offset */
Elf64_Addr p_vaddr; /* Segment virtual address */
Elf64_Addr p_paddr; /* Segment physical address */
Elf64_Xword p_filesz; /* Segment size in file */
Elf64_Xword p_memsz; /* Segment size in memory */
Elf64_Xword p_align; /* Segment alignment */
} Elf64_Phdr;
/* e_ident constants */
#define ELFMAG0 0x7f
#define ELFMAG1 'E'
#define ELFMAG2 'L'
#define ELFMAG3 'F'
#define ELFCLASS64 2
#define ELFDATA2LSB 1
#define EV_CURRENT 1
/* e_type constants */
#define ET_EXEC 2
#define ET_DYN 3
/* e_machine constants */
#define EM_X86_64 62
/* p_type constants */
#define PT_LOAD 1
/* p_flags constants */
#define PF_X 1
#define PF_W 2
#define PF_R 4
#include <stdbool.h>
// Loads the ELF executable at 'path' using fat32 into the pagemap given by user_pml4.
// Returns entry point address on success, or 0 on failure.
uint64_t elf_load(const char *path, uint64_t user_pml4);
#endif

View file

@ -8,7 +8,7 @@
#include "markdown.h"
#include "cmd.h"
#include "notepad.h"
#include "calculator.h"
#include "process.h"
#include "minesweeper.h"
#include "viewer.h"
#include "control_panel.h"
@ -816,7 +816,9 @@ static void explorer_open_target(const char *path) {
if (fat32_is_directory(path)) {
explorer_open_directory(path);
} else {
if (explorer_is_markdown_file(path)) {
if (explorer_str_ends_with(path, ".elf")) {
process_create_elf(path);
} else if (explorer_is_markdown_file(path)) {
wm_bring_to_front(&win_markdown);
markdown_open_file(path);
} else if (explorer_str_ends_with(path, ".pnt")) {
@ -853,7 +855,7 @@ static void explorer_open_item(Window *win, int index) {
if (explorer_strcmp(state->items[index].name, "Notepad.shortcut") == 0) {
target = &win_notepad;
} else if (explorer_strcmp(state->items[index].name, "Calculator.shortcut") == 0) {
target = &win_calculator;
process_create_elf("/bin/calculator.elf"); return;
} else if (explorer_strcmp(state->items[index].name, "Terminal.shortcut") == 0) {
target = &win_cmd; cmd_reset();
} else if (explorer_strcmp(state->items[index].name, "Minesweeper.shortcut") == 0) {
@ -916,6 +918,8 @@ static void explorer_draw_file_icon(int x, int y, bool is_dir, uint32_t color, c
if (full_path[explorer_strlen(full_path) - 1] != '/') explorer_strcat(full_path, "/");
explorer_strcat(full_path, filename);
draw_image_icon(x + 5, y + 5, full_path);
} else if (explorer_str_ends_with(filename, ".elf")) {
draw_elf_icon(x + 5, y + 5, "");
} else {
draw_document_icon(x + 5, y + 5, "");
}
@ -1782,7 +1786,6 @@ static void explorer_handle_file_context_menu_click(Window *win, int x, int y) {
for (int i = 0; i < explorer_win_count; i++) if (explorer_wins[i]->z_index > max_z) max_z = explorer_wins[i]->z_index;
if (win_cmd.z_index > max_z) max_z = win_cmd.z_index;
if (win_notepad.z_index > max_z) max_z = win_notepad.z_index;
if (win_calculator.z_index > max_z) max_z = win_calculator.z_index;
if (win_editor.z_index > max_z) max_z = win_editor.z_index;
if (win_markdown.z_index > max_z) max_z = win_markdown.z_index;
if (win_control_panel.z_index > max_z) max_z = win_control_panel.z_index;

View file

@ -9,7 +9,6 @@ extern Window win_explorer;
extern Window win_editor;
extern Window win_cmd;
extern Window win_notepad;
extern Window win_calculator;
extern Window win_markdown;
#define EXPLORER_MAX_FILES 64

View file

@ -1301,9 +1301,17 @@ int fat32_seek(FAT32_FileHandle *handle, int offset, int whence) {
handle->position = new_position;
// For RealFS, we might need to walk cluster chain to update handle->cluster
// if we seeked far.
if (handle->drive != 'A') {
// Both RealFS and RAMFS must accurately re-walk their cluster chains
if (handle->drive == 'A') {
handle->cluster = handle->start_cluster;
uint32_t pos = 0;
while (pos + FAT32_CLUSTER_SIZE <= handle->position) {
uint32_t next = fat_table[handle->cluster];
if (next >= 0xFFFFFFF8) break;
handle->cluster = next;
pos += FAT32_CLUSTER_SIZE;
}
} else {
// Re-walk to find current cluster
int vol_idx = handle->drive - 'A';
FAT32_Volume *vol = &volumes[vol_idx];

View file

@ -283,7 +283,7 @@ void draw_char(int x, int y, char c, uint32_t color) {
if (uc > 127) return;
// Fast rejection: if the character is entirely outside the clipping/dirty rect, skip it
if (g_clip_enabled) {
if (g_clip_enabled && !g_render_target) {
if (x + 8 <= g_clip_x || x >= g_clip_x + g_clip_w ||
y + 8 <= g_clip_y || y >= g_clip_y + g_clip_h) {
return;
@ -460,3 +460,26 @@ void graphics_set_clipping(int x, int y, int w, int h) {
void graphics_clear_clipping(void) {
g_clip_enabled = false;
}
void graphics_blit_buffer(uint32_t *src, int dst_x, int dst_y, int w, int h) {
if (!g_fb || !src) return;
int sw = get_screen_width();
int sh = get_screen_height();
for (int y = 0; y < h; y++) {
int vy = dst_y + y;
if (vy < 0 || vy >= sh) continue;
for (int x = 0; x < w; x++) {
int vx = dst_x + x;
if (vx < 0 || vx >= sw) continue;
uint32_t pcol = src[y * w + x];
// Alpha blending support:
// If the alpha byte is 0, we treat it as transparent ONLY if the color is also 0.
// This handles common 0xRRGGBB as opaque.
if ((pcol & 0xFF000000) != 0 || (pcol & 0xFFFFFF) != 0) {
g_back_buffer[vy * sw + vx] = pcol;
}
}
}
}

View file

@ -23,6 +23,7 @@ void graphics_set_bg_color(uint32_t color);
void graphics_set_bg_pattern(const uint32_t *pattern); // 128x128 pattern
void graphics_set_bg_image(uint32_t *pixels, int w, int h); // Full-screen wallpaper image
void graphics_set_render_target(uint32_t *buffer, int w, int h);
void graphics_blit_buffer(uint32_t *src, int dst_x, int dst_y, int w, int h);
void draw_boredos_logo(int x, int y, int scale);

23
src/kernel/gui_ipc.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef GUI_IPC_H
#define GUI_IPC_H
#define GUI_CMD_WINDOW_CREATE 1
#define GUI_CMD_DRAW_RECT 2
#define GUI_CMD_DRAW_STRING 3
#define GUI_CMD_MARK_DIRTY 4
#define GUI_CMD_GET_EVENT 5
#define GUI_CMD_DRAW_ROUNDED_RECT_FILLED 6
#define GUI_EVENT_NONE 0
#define GUI_EVENT_PAINT 1
#define GUI_EVENT_CLICK 2
#define GUI_EVENT_RIGHT_CLICK 3
#define GUI_EVENT_CLOSE 4
typedef struct {
int type;
int arg1; // For click: x
int arg2; // For click: y
} gui_event_t;
#endif

View file

@ -16,17 +16,43 @@ static void print_hex(uint64_t val) {
serial_write(buf);
}
void exception_handler_c(uint64_t vector, uint64_t err_code, uint64_t rip, uint64_t cr2) {
#include "process.h"
#include "cmd.h"
uint64_t exception_handler_c(registers_t *regs) {
uint64_t vector = regs->int_no;
uint64_t rip = regs->rip;
uint64_t cr2;
asm volatile("mov %%cr2, %0" : "=r"(cr2));
serial_write("\n*** EXCEPTION ***\nVector: ");
print_hex(vector);
serial_write("\nError Code: ");
print_hex(err_code);
print_hex(regs->err_code);
serial_write("\nRIP: ");
print_hex(rip);
serial_write("\nCR2: ");
print_hex(cr2);
serial_write("\nCPU HALTED.\n");
// Mirror to shell
if (cmd_get_cursor_col() != 0) cmd_write("\n");
cmd_write("*** EXCEPTION ***\nVector: "); cmd_write_hex(vector);
cmd_write("\nError Code: "); cmd_write_hex(regs->err_code);
cmd_write("\nRIP: "); cmd_write_hex(rip);
cmd_write("\nCR2: "); cmd_write_hex(cr2);
cmd_write("\n");
// Filter by CS selector to check if we are in user mode (RPL=3)
if ((regs->cs & 0x3) != 0) {
serial_write("\nUSER MODE EXCEPTION - Terminating process.\n");
cmd_write("USER MODE EXCEPTION - Terminating process.\n");
return process_terminate_current();
}
serial_write("\nKERNEL PANIC - CPU HALTED.\n");
cmd_write("KERNEL PANIC - CPU HALTED.\n");
while(1) { asm volatile("cli; hlt"); }
return (uint64_t)regs; // Unreachable but for completeness
}
#define IDT_ENTRIES 256

View file

@ -18,7 +18,10 @@ send_eoi:
pop rax
ret
%macro ISR_NOERRCODE 1
%macro ISR_NOERRCODE 2
isr%2_wrapper:
push 0 ; Dummy error code
push %2 ; Vector
push rax
push rbx
push rcx
@ -35,7 +38,7 @@ send_eoi:
push r14
push r15
; Pass current RSP as 1st argument
; Pass current RSP as 1st argument (registers_t*)
mov rdi, rsp
call %1
@ -58,17 +61,18 @@ send_eoi:
pop rcx
pop rbx
pop rax
add rsp, 16 ; drop dummy vector and error code
iretq
%endmacro
isr0_wrapper:
ISR_NOERRCODE timer_handler
ISR_NOERRCODE timer_handler, 32
isr1_wrapper:
ISR_NOERRCODE keyboard_handler
ISR_NOERRCODE keyboard_handler, 33
isr12_wrapper:
ISR_NOERRCODE mouse_handler
ISR_NOERRCODE mouse_handler, 44
; Common exception macro
%macro EXCEPTION_ERRCODE 1
@ -101,16 +105,14 @@ exception_common:
push r14
push r15
; Call C handler: void exception_handler_c(uint64_t vector, uint64_t err_code, uint64_t rip, uint64_t cr2)
; Stack right now: 15 registers (15*8=120 bytes), then vector (8), then err_code (8), then RIP (8), CS (8), RFLAGS (8), RSP (8), SS (8)
mov rdi, [rsp + 120] ; vector
mov rsi, [rsp + 128] ; err_code
mov rdx, [rsp + 136] ; RIP
mov rcx, cr2 ; CR2
; Call C handler: uint64_t exception_handler_c(registers_t *regs)
mov rdi, rsp
call exception_handler_c
; Restore (in case we want to return, but usually we halt)
; Switch stack if needed (for process termination)
mov rsp, rax
; Restore (in case we want to return, but usually we halt if kernel)
pop r15
pop r14
pop r13

View file

@ -11,6 +11,7 @@
#include "ps2.h"
#include "wm.h"
#include "io.h"
#include "fat32.h"
#include "memory_manager.h"
#include "platform.h"
#include "wallpaper.h"
@ -32,10 +33,17 @@ static volatile struct limine_memmap_request memmap_request = {
.revision = 0
};
__attribute__((used, section(".requests")))
static volatile struct limine_module_request module_request = {
.id = LIMINE_MODULE_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,
NULL
};
@ -107,22 +115,7 @@ void kmain(void) {
serial_write("[DEBUG] idt_init OK\n");
process_init();
int ENABLE_USER_TEST = 1; // Set to 1 to test User Mode ring 3 jump
if (ENABLE_USER_TEST) {
// Create an isolated PML4 table for this "process"
uint64_t user_pml4_phys = paging_create_user_pml4_phys();
serial_write("[DEBUG] user_pml4 created OK\n");
if (user_pml4_phys) {
// Debug verify we can allocate
void* code_page = kmalloc_aligned(4096, 4096);
if (code_page) {
extern void user_test_function(void);
process_create(user_test_function, true);
serial_write("[DEBUG] User Process 1 Created.\n");
}
}
}
serial_write("[DEBUG] Skipping user mode test, proceeding with normal boot.\n");
@ -132,6 +125,7 @@ void kmain(void) {
for (uint64_t i = 0; i < memmap_request.response->entry_count; i++) {
struct limine_memmap_entry *entry = memmap_request.response->entries[i];
// Count usable memory regions
if (entry->type == LIMINE_MEMMAP_USABLE) {
total_usable_memory += entry->length;
@ -149,6 +143,30 @@ void kmain(void) {
memory_manager_init_with_size(pool_size);
// Initialize FAT32 RAMFS and mount Limine modules
fat32_init();
if (module_request.response != NULL) {
for (uint64_t i = 0; i < module_request.response->module_count; i++) {
struct limine_file *mod = module_request.response->modules[i];
// mod->path typically starts with a '/' from Limine
FAT32_FileHandle *fh = fat32_open(mod->path, "w");
if (fh && fh->valid) {
fat32_write(fh, mod->address, mod->size);
fat32_close(fh);
serial_write("[DEBUG] Limine Module loaded into RAMFS: ");
serial_write(mod->path);
serial_write("\n");
}
}
}
int ENABLE_USER_TEST = 1; // Set to 1 to test User Mode ring 3 jump
#ifdef ENABLE_USER_TEST
process_init();
// The desktop is PID 0
#endif
// 3. PS/2 Init (Mouse/Keyboard)
asm("cli");
ps2_init();

View file

@ -17,13 +17,21 @@ static bool initialized = false;
// --- Helper Functions ---
// Simple memset for internal use
static void mem_memset(void *dest, int val, size_t len) {
void mem_memset(void *dest, int val, size_t len) {
uint8_t *ptr = (uint8_t *)dest;
while (len-- > 0) {
*ptr++ = (uint8_t)val;
}
}
void mem_memcpy(void *dest, const void *src, size_t len) {
uint8_t *d = (uint8_t *)dest;
const uint8_t *s = (const uint8_t *)src;
while (len-- > 0) {
*d++ = *s++;
}
}
// Simple memmove
static void mem_memmove(void *dest, const void *src, size_t len) {
uint8_t *d = (uint8_t *)dest;

View file

@ -55,4 +55,7 @@ size_t memory_get_peak_usage(void);
void memory_reset_peak(void);
bool memory_is_valid_ptr(void *ptr);
void mem_memset(void *dest, int val, size_t len);
void mem_memcpy(void *dest, const void *src, size_t len);
#endif // MEMORY_MANAGER_H

View file

@ -5,6 +5,8 @@
#include "io.h"
#include "platform.h"
#include "memory_manager.h"
#include "elf.h"
#include "wm.h"
extern void cmd_write(const char *str);
extern void serial_write(const char *str);
@ -48,7 +50,7 @@ void process_create(void* entry_point, bool is_user) {
// 2. Allocate aligned stack
void* stack = kmalloc_aligned(4096, 4096);
void* kernel_stack = kmalloc_aligned(4096, 4096); // Needed for when user interrupts to Ring 0
void* kernel_stack = kmalloc_aligned(16384, 16384); // Needed for when user interrupts to Ring 0
if (is_user) {
// Map user stack to 0x800000
@ -62,18 +64,20 @@ void process_create(void* entry_point, bool is_user) {
// Build initial stack frame for iretq
// Stack grows down, start at top
uint64_t* stack_ptr = (uint64_t*)((uint64_t)kernel_stack + 4096);
uint64_t* stack_ptr = (uint64_t*)((uint64_t)kernel_stack + 16384);
*(--stack_ptr) = 0x1B; // SS (User Data)
*(--stack_ptr) = 0x800000 + 4096; // RSP
*(--stack_ptr) = 0x202; // RFLAGS (IF=1)
*(--stack_ptr) = 0x23; // CS (User Code)
*(--stack_ptr) = 0x400000; // RIP
*(--stack_ptr) = 0; // int_no
*(--stack_ptr) = 0; // err_code
// Push 15 zeros for general purpose registers (r15 -> rax)
for (int i = 0; i < 15; i++) *(--stack_ptr) = 0;
new_proc->kernel_stack = (uint64_t)kernel_stack + 4096;
new_proc->kernel_stack = (uint64_t)kernel_stack + 16384;
new_proc->rsp = (uint64_t)stack_ptr;
} else {
// Kernel thread
@ -84,6 +88,8 @@ void process_create(void* entry_point, bool is_user) {
*(--stack_ptr) = 0x202; // RFLAGS
*(--stack_ptr) = 0x08; // CS (Kernel Code)
*(--stack_ptr) = (uint64_t)entry_point; // RIP
*(--stack_ptr) = 0; // int_no
*(--stack_ptr) = 0; // err_code
for (int i = 0; i < 15; i++) *(--stack_ptr) = 0;
@ -96,6 +102,63 @@ void process_create(void* entry_point, bool is_user) {
current_process->next = new_proc;
}
void process_create_elf(const char* filepath) {
if (process_count >= MAX_PROCESSES) return;
process_t *new_proc = &processes[process_count];
new_proc->pid = next_pid++;
new_proc->is_user = true;
// 1. Setup Page Table
new_proc->pml4_phys = paging_create_user_pml4_phys();
if (!new_proc->pml4_phys) return;
// 2. Load ELF executable
uint64_t entry_point = elf_load(filepath, new_proc->pml4_phys);
if (entry_point == 0) {
serial_write("[PROCESS] 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;
}
// 3. Allocate generic User stack and Kernel stack for interrupts
void* stack = kmalloc_aligned(4096, 4096);
void* kernel_stack = kmalloc_aligned(16384, 16384);
// Map User stack to 0x800000 -> Note: ELFs might overwrite this if they load there!
// But our ELF loader defaults 0x400000 for standard code.
paging_map_page(new_proc->pml4_phys, 0x800000, v2p((uint64_t)stack), PT_PRESENT | PT_RW | PT_USER);
// 4. Build Stack Frame
uint64_t* stack_ptr = (uint64_t*)((uint64_t)kernel_stack + 16384);
*(--stack_ptr) = 0x1B; // SS (User Mode Data)
*(--stack_ptr) = 0x800000 + 4096; // RSP
*(--stack_ptr) = 0x202; // RFLAGS (Interrupts Enabled)
*(--stack_ptr) = 0x23; // CS (User Mode Code)
*(--stack_ptr) = entry_point; // RIP
*(--stack_ptr) = 0; // int_no
*(--stack_ptr) = 0; // err_code
// 15 General purpose registers
for (int i = 0; i < 15; i++) *(--stack_ptr) = 0;
new_proc->kernel_stack = (uint64_t)kernel_stack + 16384;
new_proc->rsp = (uint64_t)stack_ptr;
// We only increment process_count after success
process_count++;
// Add to linked list
new_proc->next = current_process->next;
current_process->next = new_proc;
serial_write("[PROCESS] Spawned ELF Executable: ");
serial_write(filepath);
serial_write("\n");
}
process_t* process_get_current(void) {
return current_process;
}
@ -112,9 +175,11 @@ uint64_t process_schedule(uint64_t current_rsp) {
// Switch process
current_process = current_process->next;
// Update Kernel Stack for User Mode interrupts
// Update Kernel Stack for User Mode interrupts and System Calls
if (current_process->is_user && current_process->kernel_stack) {
tss_set_stack(current_process->kernel_stack);
extern uint64_t kernel_syscall_stack;
kernel_syscall_stack = current_process->kernel_stack;
}
// Switch page table
@ -123,3 +188,58 @@ uint64_t process_schedule(uint64_t current_rsp) {
return current_process->rsp;
}
uint64_t process_terminate_current(void) {
if (!current_process) return 0;
// 1. Cleanup side effects
if (current_process->ui_window) {
wm_remove_window((Window *)current_process->ui_window);
current_process->ui_window = NULL;
}
extern void cmd_process_finished(void);
cmd_process_finished();
// 2. Find previous process in circular list
process_t *prev = current_process;
while (prev->next != current_process) {
prev = prev->next;
}
// 3. Remove current from list
process_t *to_delete = current_process;
if (prev == current_process) {
// Only one process (should be kernel), cannot terminate.
return to_delete->rsp;
}
prev->next = to_delete->next;
current_process = to_delete->next;
// Mark slot as freeish (simple version)
to_delete->pid = 0xFFFFFFFF;
// 4. Load context for the NEXT process
if (current_process->is_user && current_process->kernel_stack) {
tss_set_stack(current_process->kernel_stack);
extern uint64_t kernel_syscall_stack;
kernel_syscall_stack = current_process->kernel_stack;
}
paging_switch_directory(current_process->pml4_phys);
return current_process->rsp;
}
void process_push_gui_event(process_t *proc, gui_event_t *ev) {
if (!proc) return;
int next_tail = (proc->gui_event_tail + 1) % MAX_GUI_EVENTS;
// Drop event if queue is full
if (next_tail == proc->gui_event_head) {
return;
}
proc->gui_events[proc->gui_event_tail] = *ev;
proc->gui_event_tail = next_tail;
}

View file

@ -3,6 +3,9 @@
#include <stdint.h>
#include <stdbool.h>
#include "gui_ipc.h"
#define MAX_GUI_EVENTS 32
// Registers saved on the stack by interrupts/exceptions
typedef struct {
@ -18,13 +21,23 @@ typedef struct process {
uint64_t pml4_phys; // Physical address of the page table
uint64_t kernel_stack; // Ring 0 stack pointer for user mode switches
bool is_user;
gui_event_t gui_events[MAX_GUI_EVENTS];
int gui_event_head;
int gui_event_tail;
void *ui_window; // Pointer to the active Window
struct process *next;
} process_t;
void process_init(void);
void process_create(void* entry_point, bool is_user);
void process_create_elf(const char* filepath);
process_t* process_get_current(void);
uint64_t process_schedule(uint64_t current_rsp);
uint64_t process_terminate_current(void);
void process_push_gui_event(process_t *proc, gui_event_t *ev);
#endif

View file

@ -8,14 +8,14 @@ extern void serial_print(const char *s);
extern void serial_print_hex(uint64_t n);
// --- Timer Handler ---
uint64_t timer_handler(uint64_t rsp) {
uint64_t timer_handler(registers_t *regs) {
wm_timer_tick();
network_process_frames();
extern uint64_t process_schedule(uint64_t current_rsp);
outb(0x20, 0x20); // EOI to Master PIC
rsp = process_schedule(rsp);
uint64_t rsp = process_schedule((uint64_t)regs);
return rsp;
}
@ -41,13 +41,13 @@ static char scancode_map_shift[128] = {
0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
uint64_t keyboard_handler(uint64_t rsp) {
uint64_t keyboard_handler(registers_t *regs) {
uint8_t scancode = inb(0x60);
if (scancode == 0xE0) {
extended_scancode = true;
outb(0x20, 0x20);
return rsp;
return (uint64_t)regs;
}
if (scancode == 0x2A || scancode == 0x36) { // Shift Down
@ -77,7 +77,7 @@ uint64_t keyboard_handler(uint64_t rsp) {
}
outb(0x20, 0x20); // EOI
return rsp;
return (uint64_t)regs;
}
// --- Mouse ---
@ -135,12 +135,12 @@ void mouse_init(void) {
mouse_read();
}
uint64_t mouse_handler(uint64_t rsp) {
uint64_t mouse_handler(registers_t *regs) {
uint8_t status = inb(0x64);
if (!(status & 0x20)) {
outb(0x20, 0x20);
outb(0xA0, 0x20);
return rsp; // Return rsp here as well
return (uint64_t)regs;
}
uint8_t b = inb(0x60);
@ -169,7 +169,7 @@ uint64_t mouse_handler(uint64_t rsp) {
outb(0x20, 0x20);
outb(0xA0, 0x20); // Slave EOI
return rsp;
return (uint64_t)regs;
}
void ps2_init(void) {

View file

@ -4,8 +4,10 @@
#include <stdint.h>
void ps2_init(void);
uint64_t timer_handler(uint64_t rsp);
uint64_t keyboard_handler(uint64_t rsp);
uint64_t mouse_handler(uint64_t rsp);
#include "process.h"
uint64_t timer_handler(registers_t *regs);
uint64_t keyboard_handler(registers_t *regs);
uint64_t mouse_handler(registers_t *regs);
#endif

View file

@ -1,6 +1,9 @@
#include "syscall.h"
#include "gdt.h"
#include "memory_manager.h"
#include "gui_ipc.h"
#include "process.h"
#include "wm.h"
// Read MSR
static inline uint64_t rdmsr(uint32_t msr) {
@ -43,13 +46,232 @@ void syscall_init(void) {
wrmsr(MSR_FMASK, 0x200);
}
void syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5) {
static void user_window_close(Window *win) {
process_t *proc = (process_t *)win->data;
if (!proc) return;
gui_event_t ev = { .type = GUI_EVENT_CLOSE };
process_push_gui_event(proc, &ev);
}
static void user_window_paint(Window *win) {
process_t *proc = (process_t *)win->data;
if (!proc) return;
gui_event_t ev = { .type = GUI_EVENT_PAINT };
process_push_gui_event(proc, &ev);
}
static void user_window_click(Window *win, int x, int y) {
process_t *proc = (process_t *)win->data;
if (!proc) return;
gui_event_t ev = { .type = GUI_EVENT_CLICK, .arg1 = x, .arg2 = y };
process_push_gui_event(proc, &ev);
}
uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5) {
extern void cmd_write(const char *str);
extern void serial_write(const char *str);
extern void cmd_process_finished(void);
if (syscall_num == 1) { // SYS_WRITE
// arg2 is the buffer based on our user_test logic
cmd_write((const char*)arg2);
serial_write((const char*)arg2);
} else if (syscall_num == 0) { // SYS_EXIT
process_t *proc = process_get_current();
if (proc && proc->ui_window) {
wm_remove_window((Window *)proc->ui_window);
proc->ui_window = NULL;
}
cmd_process_finished();
// The actual process termination and scheduling will be handled later
// For now this just releases the CMD prompt lock.
// We will eventually need to mark the process_t as DEAD here.
} else if (syscall_num == 3) { // SYS_GUI
int cmd = (int)arg1;
process_t *proc = process_get_current();
if (cmd == GUI_CMD_WINDOW_CREATE) {
const char *title = (const char *)arg2;
uint64_t *params = (uint64_t *)arg3;
if (!params) return 0;
Window *win = kmalloc(sizeof(Window));
if (!win) return 0;
// Copy title from user space to kernel space so wm.c can access it safely
int title_len = 0;
while (title[title_len] && title_len < 255) title_len++;
char *kernel_title = kmalloc(title_len + 1);
if (kernel_title) {
for (int i = 0; i < title_len; i++) {
kernel_title[i] = title[i];
}
kernel_title[title_len] = '\0';
}
// Basic initialization
win->title = kernel_title ? kernel_title : "Unknown";
win->x = (int)params[0];
win->y = (int)params[1];
win->w = (int)params[2];
win->h = (int)params[3];
// Sanity checks for dimensions
if (win->w <= 0 || win->w > 4096) win->w = 400;
if (win->h <= 0 || win->h > 4096) win->h = 400;
win->visible = true;
win->focused = true;
win->z_index = 0;
win->buf_len = 0;
win->buffer[0] = 0;
win->data = proc;
// Safe allocation
size_t pixel_size = (size_t)win->w * win->h * 4;
win->pixels = kmalloc(pixel_size);
win->comp_pixels = kmalloc(pixel_size);
if (win->pixels) {
extern void mem_memset(void *dest, int val, size_t len);
mem_memset(win->pixels, 0, pixel_size);
}
if (win->comp_pixels) {
extern void mem_memset(void *dest, int val, size_t len);
mem_memset(win->comp_pixels, 0, pixel_size);
}
win->paint = user_window_paint;
win->handle_click = user_window_click;
win->handle_close = user_window_close;
win->handle_key = NULL;
win->handle_right_click = NULL;
proc->ui_window = win;
wm_add_window(win);
return (uint64_t)win;
} else if (cmd == GUI_CMD_DRAW_RECT) {
Window *win = (Window *)arg2;
uint64_t *p = (uint64_t *)arg3;
uint32_t color = (uint32_t)arg4;
if (win && p) {
extern void draw_rect(int x, int y, int w, int h, uint32_t color);
extern void graphics_set_render_target(uint32_t *buffer, int w, int h);
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
if (win->pixels) {
// Strict user-to-window relative clamping
int rx = (int)p[0]; int ry = (int)p[1];
int rw = (int)p[2]; int rh = (int)p[3];
if (rx < 0) { rw += rx; rx = 0; }
if (ry < 0) { rh += ry; ry = 0; }
if (rx + rw > win->w) rw = win->w - rx;
if (ry + rh > win->h) rh = win->h - ry;
if (rw > 0 && rh > 0) {
graphics_set_render_target(win->pixels, win->w, win->h);
draw_rect(rx, ry, rw, rh, color);
graphics_set_render_target(NULL, 0, 0);
}
} else {
draw_rect(win->x + p[0], win->y + p[1], p[2], p[3], color);
}
asm volatile("push %0; popfq" : : "r"(rflags));
}
} else if (cmd == GUI_CMD_DRAW_ROUNDED_RECT_FILLED) {
Window *win = (Window *)arg2;
uint64_t *p = (uint64_t *)arg3;
uint32_t color = (uint32_t)arg4;
if (win && p) {
extern void draw_rounded_rect_filled(int x, int y, int w, int h, int radius, uint32_t color);
extern void graphics_set_render_target(uint32_t *buffer, int w, int h);
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
if (win->pixels) {
int rx = (int)p[0]; int ry = (int)p[1];
int rw = (int)p[2]; int rh = (int)p[3];
int rr = (int)p[4];
if (rx < 0) { rw += rx; rx = 0; }
if (ry < 0) { rh += ry; ry = 0; }
if (rx + rw > win->w) rw = win->w - rx;
if (ry + rh > win->h) rh = win->h - ry;
if (rw > 0 && rh > 0) {
graphics_set_render_target(win->pixels, win->w, win->h);
draw_rounded_rect_filled(rx, ry, rw, rh, rr, color);
graphics_set_render_target(NULL, 0, 0);
}
} else {
draw_rounded_rect_filled(win->x + p[0], win->y + p[1], p[2], p[3], p[4], color);
}
asm volatile("push %0; popfq" : : "r"(rflags));
}
} else if (cmd == GUI_CMD_DRAW_STRING) {
Window *win = (Window *)arg2;
uint64_t coords = arg3;
int ux = coords & 0xFFFFFFFF;
int uy = coords >> 32;
const char *user_str = (const char *)arg4;
uint32_t color = (uint32_t)arg5;
if (win && user_str) {
extern void draw_string(int x, int y, const char *str, uint32_t color);
extern void graphics_set_render_target(uint32_t *buffer, int w, int h);
// Copy string safely to kernel stack buffer
char kernel_str[256];
int i = 0;
while (i < 255 && user_str[i]) {
kernel_str[i] = user_str[i];
i++;
}
kernel_str[i] = 0;
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
if (win->pixels) {
// String clipping is handled by draw_char -> put_pixel,
// but we ensure coordinate sanity here
if (ux >= -100 && ux < win->w && uy >= -100 && uy < win->h) {
graphics_set_render_target(win->pixels, win->w, win->h);
draw_string(ux, uy, kernel_str, color);
graphics_set_render_target(NULL, 0, 0);
}
} else {
draw_string(win->x + ux, win->y + uy, kernel_str, color);
}
asm volatile("push %0; popfq" : : "r"(rflags));
}
} else if (cmd == GUI_CMD_MARK_DIRTY) {
Window *win = (Window *)arg2;
uint64_t *p = (uint64_t *)arg3;
if (win && p) {
// Dual-buffer commit: copy pixels to comp_pixels
if (win->pixels && win->comp_pixels) {
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy(win->comp_pixels, win->pixels, (size_t)win->w * win->h * 4);
}
wm_mark_dirty(win->x + p[0], win->y + p[1], p[2], p[3]);
}
} else if (cmd == GUI_CMD_GET_EVENT) {
Window *win = (Window *)arg2;
gui_event_t *ev_out = (gui_event_t *)arg3;
if (!win || !ev_out) return 0;
if (proc->gui_event_head != proc->gui_event_tail) {
*ev_out = proc->gui_events[proc->gui_event_head];
proc->gui_event_head = (proc->gui_event_head + 1) % MAX_GUI_EVENTS;
return 1;
}
return 0;
}
}
return 0;
}

View file

@ -15,6 +15,6 @@
#define SYS_EXIT 60
void syscall_init(void);
void syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5);
uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5);
#endif // SYSCALL_H

View file

@ -12,19 +12,14 @@ section .text
; R9 = arg5
syscall_entry:
; We arrived here from Ring 3 via `syscall`.
; RAX = syscall_num
; RDI, RSI, RDX, R10, R8, R9 = args
; RCX = User RIP
; R11 = User RFLAGS
; Current RSP = User RSP
; 1. Save User RSP
; 1. Switch to Kernel Stack
; Use scratch temporarily to pivot (Risk: Task switch here is rare but possible)
mov [rel user_rsp_scratch], rsp
; 2. Switch to Kernel Stack
mov rsp, [rel kernel_syscall_stack]
; 2. Save User RSP on per-process kernel stack
push qword [rel user_rsp_scratch]
; 3. Save preserved registers (System V ABI)
push rbx
push rbp
@ -33,32 +28,18 @@ syscall_entry:
push r14
push r15
; We also need to save RCX (RIP) and R11 (RFLAGS) because C functions might clobber them
; 4. Save RCX (RIP) and R11 (RFLAGS)
push rcx
push r11
; Syscall convention: argument 4 is passed in R10, but C expects it in RCX
mov rcx, r10
; The syscall number is in RAX, let's put it in RDI (arg 0 for C)
; But wait, the ABI expects arg1 in RDI!
; Let's change our C handler signature or adapt here.
; C handler: void syscall_handler_c(uint64_t syscall_num, uint64_t arg1, ...)
; So:
; syscall_num -> RDI
; arg1 (was RDI) -> RSI
; arg2 (was RSI) -> RDX
; arg3 (was RDX) -> RCX
; arg4 (was R10) -> R8
; arg5 (was R8) -> R9
; arg6 (was R9) -> stack (if needed, but we have 6 regs)
; This shuffling is messy. Let's just push everything and call a struct-based handler,
; or carefully shuffle.
; For now, let's just make sure RAX goes to RDI, RDI to RSI, RSI to RDX, RDX to RCX, R10 to R8.
; Shuffling for SYS V C ABI:
; R9 is arg6 -> no room in registers, need to push to stack (but our handler takes 6 args total)
; arg5: R9 (remains R9 as 6th arg in C)
; arg4: R8 (was R9)
; arg3: RCX (was R10)
; arg2: RDX (was RSI)
; arg1: RSI (was RDI)
; num: RDI (was RAX)
mov r9, r8 ; arg5
mov r8, r10 ; arg4
mov rcx, rdx ; arg3
@ -66,14 +47,14 @@ syscall_entry:
mov rsi, rdi ; arg1
mov rdi, rax ; syscall_num
; 4. Call C handler
; 5. Call C handler
call syscall_handler_c
; 5. Restore RCX and R11
; 6. Restore RCX and R11
pop r11
pop rcx
; 6. Restore preserved registers
; 7. Restore preserved registers
pop r15
pop r14
pop r13
@ -81,14 +62,11 @@ syscall_entry:
pop rbp
pop rbx
; 7. Restore User RSP
mov rsp, [rel user_rsp_scratch]
; 8. Restore User RSP from kernel stack
pop rsp
; 8. Return to User Mode
; NASM syntax for 64-bit sysret requires the o64 prefix
; Force IF=1 (bit 9) in R11 (restored to RFLAGS) to ensure interrupts stay enabled!
or r11, 0x200
; 9. Return to User Mode (sysret)
or r11, 0x200 ; Force Interrupts enabled
o64 sysret
section .bss

View file

@ -0,0 +1,30 @@
CC = x86_64-elf-gcc
AS = nasm
LD = x86_64-elf-ld
CFLAGS = -Wall -Wextra -std=gnu11 -ffreestanding -O2 -fno-stack-protector -fno-stack-check -fno-lto -fno-pie -m64 -march=x86-64 -mno-80387 -mno-mmx -mno-sse -mno-sse2 -mno-red-zone -Ilibc
LDFLAGS = -m elf_x86_64 -nostdlib -static -no-pie -Ttext=0x40000000 --no-dynamic-linker -z text -z max-page-size=0x1000 -e _start
LIBC_SOURCES = $(wildcard libc/*.c)
LIBC_OBJS = $(LIBC_SOURCES:.c=.o) crt0.o
APP_SOURCES = $(wildcard *.c)
APP_OBJS = $(APP_SOURCES:.c=.o)
APP_ELFS = $(APP_SOURCES:.c=.elf)
all: $(APP_ELFS)
crt0.o: crt0.asm
$(AS) -f elf64 $< -o $@
libc/%.o: libc/%.c
$(CC) $(CFLAGS) -c $< -o $@
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
%.elf: $(LIBC_OBJS) %.o
$(LD) $(LDFLAGS) $^ -o $@
clean:
rm -f *.o libc/*.o *.elf

View file

@ -1,13 +1,17 @@
#include "calculator.h"
#include "graphics.h"
#include "wm.h"
#include "syscall.h"
#include "libui.h"
#include <stdbool.h>
#include <stddef.h>
Window win_calculator;
#define SCALE 1000000LL
// Dark Mode Colors mapping to libui
#define COLOR_DARK_BG 0xFF1E1E1E
#define COLOR_DARK_PANEL 0xFF2D2D2D
#define COLOR_DARK_TEXT 0xFFF0F0F0
#define COLOR_DARK_BORDER 0xFF3A3A3A
static ui_window_t win_calculator;
static long long calc_acc = 0;
static long long calc_curr = 0;
static char calc_op = 0;
@ -15,8 +19,9 @@ static bool calc_new_entry = true;
static bool calc_error = false;
static bool calc_decimal_mode = false;
static long long calc_decimal_divisor = 10;
static char display_buffer[1024];
static int display_buf_len = 0;
// Simple integer square root
static long long isqrt(long long n) {
if (n < 0) return -1;
if (n == 0) return 0;
@ -29,45 +34,31 @@ static long long isqrt(long long n) {
return x;
}
// Convert fixed point to string
static void fixed_to_str(long long n, char *buf) {
if (n == 0) {
buf[0] = '0'; buf[1] = 0; return;
}
char temp[64];
int pos = 0;
bool neg = n < 0;
if (neg) n = -n;
long long int_part = n / SCALE;
long long frac_part = n % SCALE;
// Fraction part
char frac_buf[16];
int f_idx = 0;
// Fill exactly 6 digits
for(int k=0; k<6; k++) {
long long div = 100000;
for(int m=0; m<k; m++) div /= 10;
frac_buf[f_idx++] = '0' + ((frac_part / div) % 10);
}
frac_buf[f_idx] = 0;
// Trim trailing zeros
while (f_idx > 0 && frac_buf[f_idx-1] == '0') {
f_idx--;
}
while (f_idx > 0 && frac_buf[f_idx-1] == '0') f_idx--;
frac_buf[f_idx] = 0;
if (f_idx > 0) {
for (int i = f_idx - 1; i >= 0; i--) {
temp[pos++] = frac_buf[i];
}
for (int i = f_idx - 1; i >= 0; i--) temp[pos++] = frac_buf[i];
temp[pos++] = '.';
}
// Integer part
if (int_part == 0) {
temp[pos++] = '0';
} else {
@ -76,40 +67,33 @@ static void fixed_to_str(long long n, char *buf) {
int_part /= 10;
}
}
if (neg) temp[pos++] = '-';
// Reverse into buf
int j = 0;
while (pos > 0) {
buf[j++] = temp[--pos];
}
while (pos > 0) buf[j++] = temp[--pos];
buf[j] = 0;
}
static void update_display(Window *win) {
static void update_display(void) {
if (calc_error) {
char *err = "Error";
int i = 0; while(err[i]) { win->buffer[i] = err[i]; i++; }
win->buffer[i] = 0;
int i = 0; while(err[i]) { display_buffer[i] = err[i]; i++; }
display_buffer[i] = 0;
} else {
fixed_to_str(calc_curr, win->buffer);
fixed_to_str(calc_curr, display_buffer);
}
win->buf_len = 0; while(win->buffer[win->buf_len]) win->buf_len++;
display_buf_len = 0; while(display_buffer[display_buf_len]) display_buf_len++;
}
static void calculator_paint(Window *win) {
// Background
draw_rect(win->x + 4, win->y + 30, win->w - 8, win->h - 34, COLOR_DARK_BG);
static void calculator_paint(void) {
int w = 180;
int h = 230;
ui_draw_rect(win_calculator, 4, 30, w - 8, h - 34, COLOR_DARK_BG);
ui_draw_rounded_rect_filled(win_calculator, 10, 36, w - 20, 25, 6, COLOR_DARK_PANEL);
// Display Area with dark mode styling
draw_rounded_rect_filled(win->x + 10, win->y + 36, win->w - 20, 25, 6, COLOR_DARK_PANEL);
// Right align text
int text_w = win->buf_len * 8;
int text_x = win->x + win->w - 15 - text_w;
draw_string(text_x, win->y + 44, win->buffer, COLOR_DARK_TEXT);
int text_w = display_buf_len * 8;
int text_x = w - 15 - text_w;
ui_draw_string(win_calculator, text_x, 44, display_buffer, COLOR_DARK_TEXT);
// Buttons - macOS style squircle buttons
const char *labels[] = {
"C", "sqr", "rt", "/",
"7", "8", "9", "*",
@ -121,19 +105,16 @@ static void calculator_paint(Window *win) {
int bw = 35;
int bh = 25;
int gap = 5;
int start_x = win->x + 10;
int start_y = win->y + 70;
int start_x = 10;
int start_y = 70;
for (int i = 0; i < 20; i++) {
int r = i / 4;
int c = i % 4;
// Draw rounded button backgrounds
draw_rounded_rect_filled(start_x + c*(bw+gap), start_y + r*(bh+gap), bw, bh, 4, COLOR_DARK_BORDER);
// Draw button text
ui_draw_rounded_rect_filled(win_calculator, start_x + c*(bw+gap), start_y + r*(bh+gap), bw, bh, 4, COLOR_DARK_BORDER);
int label_x = start_x + c*(bw+gap) + 5;
int label_y = start_y + r*(bh+gap) + 9;
draw_string(label_x, label_y, labels[i], COLOR_DARK_TEXT);
ui_draw_string(win_calculator, label_x, label_y, labels[i], COLOR_DARK_TEXT);
}
}
@ -145,20 +126,18 @@ static void do_op(void) {
}
else if (calc_op == '/') {
if (calc_curr == 0) calc_error = true;
else {
calc_acc = (calc_acc * SCALE) / calc_curr;
}
else calc_acc = (calc_acc * SCALE) / calc_curr;
} else {
calc_acc = calc_curr;
}
}
static void calculator_click(Window *win, int x, int y) {
static void calculator_click(int x, int y) {
int bw = 35;
int bh = 25;
int gap = 5;
int start_x = 10;
int start_y = 65;
int start_y = 65; // Matches the hitboxes
for (int i = 0; i < 20; i++) {
int r = i / 4;
@ -168,14 +147,13 @@ static void calculator_click(Window *win, int x, int y) {
if (x >= bx && x < bx + bw && y >= by && y < by + bh) {
const char *labels[] = {
"C", "sqr", "rt", "/",
"C", "s", "r", "/",
"7", "8", "9", "*",
"4", "5", "6", "-",
"1", "2", "3", "+",
"0", ".", "BS", "="
"0", ".", "B", "="
};
const char *lbl_str = labels[i];
char lbl = lbl_str[0];
char lbl = labels[i][0];
if (lbl >= '0' && lbl <= '9') {
if (calc_new_entry || calc_error) {
@ -184,7 +162,6 @@ static void calculator_click(Window *win, int x, int y) {
calc_decimal_mode = false;
} else {
if (calc_decimal_mode) {
// Add digit to fraction
if (calc_decimal_divisor <= SCALE) {
long long digit_val = ((long long)(lbl - '0') * SCALE) / calc_decimal_divisor;
if (calc_curr >= 0) calc_curr += digit_val;
@ -192,7 +169,6 @@ static void calculator_click(Window *win, int x, int y) {
calc_decimal_divisor *= 10;
}
} else {
// Integer shift
if (calc_curr >= 0) calc_curr = calc_curr * 10 + (lbl - '0') * SCALE;
else calc_curr = calc_curr * 10 - (lbl - '0') * SCALE;
}
@ -208,69 +184,81 @@ static void calculator_click(Window *win, int x, int y) {
calc_decimal_divisor = 10;
}
} else if (lbl == 'C') {
calc_curr = 0;
calc_acc = 0;
calc_op = 0;
calc_new_entry = true;
calc_error = false;
calc_decimal_mode = false;
} else if (lbl == 'B') { // BS (Backspace)
calc_curr = 0; calc_acc = 0; calc_op = 0;
calc_new_entry = true; calc_error = false; calc_decimal_mode = false;
} else if (lbl == 'B') {
if (!calc_new_entry && !calc_error) {
if (calc_decimal_mode) {
calc_curr = 0;
calc_new_entry = true;
if (calc_decimal_divisor > 10) {
calc_decimal_divisor /= 10;
long long unit = SCALE / calc_decimal_divisor;
// This removes the last decimal digit
calc_curr = (calc_curr / (unit * 10)) * (unit * 10);
} else {
// Backspace on the dot itself or first decimal
calc_decimal_mode = false;
calc_decimal_divisor = 10;
// Ensure the fractional part is cleared
calc_curr = (calc_curr / SCALE) * SCALE;
}
} else {
calc_curr /= 10;
// Integer backspace: remove last digit of integer part
calc_curr = (calc_curr / SCALE / 10) * SCALE;
}
}
} else if (lbl == 's') { // sqr
calc_curr = (calc_curr * calc_curr) / SCALE;
calc_new_entry = true;
} else if (lbl == 'r') { // rt (sqrt)
calc_curr = (calc_curr * calc_curr) / SCALE; calc_new_entry = true;
} else if (lbl == 'r') { // rt
long long s = isqrt(calc_curr);
if (s == -1) calc_error = true;
else calc_curr = s * 1000;
calc_new_entry = true;
} else if (lbl == '=') {
do_op();
calc_curr = calc_acc;
calc_op = 0;
calc_new_entry = true;
calc_decimal_mode = false;
calc_curr = calc_acc; calc_op = 0; calc_new_entry = true; calc_decimal_mode = false;
} else {
// Operator
if (!calc_new_entry) {
if (calc_op) do_op();
else calc_acc = calc_curr;
}
calc_op = lbl;
calc_new_entry = true;
calc_decimal_mode = false;
calc_op = lbl; calc_new_entry = true; calc_decimal_mode = false;
}
update_display(win);
wm_mark_dirty(win->x, win->y, win->w, win->h);
update_display();
calculator_paint();
ui_mark_dirty(win_calculator, 0, 0, 180, 230);
return;
}
}
}
void calculator_init(void) {
win_calculator.title = "Calculator";
win_calculator.x = 200;
win_calculator.y = 200;
win_calculator.w = 180; // Slightly wider for 4 cols with gaps
win_calculator.h = 230; // Taller for 5 rows
win_calculator.visible = false;
win_calculator.focused = false;
win_calculator.z_index = 0;
win_calculator.paint = calculator_paint;
win_calculator.handle_click = calculator_click;
win_calculator.handle_right_click = NULL;
int main(void) {
win_calculator = ui_window_create("Calculator", 200, 200, 180, 230);
calc_curr = 0;
calc_acc = 0;
calc_op = 0;
calc_new_entry = true;
update_display(&win_calculator);
}
update_display();
// First paint
calculator_paint();
ui_mark_dirty(win_calculator, 0, 0, 180, 230);
// Check events from Kernel User Interface queue
gui_event_t ev;
while (1) {
if (ui_get_event(win_calculator, &ev)) {
if (ev.type == GUI_EVENT_PAINT) {
calculator_paint();
ui_mark_dirty(win_calculator, 0, 0, 180, 230);
} else if (ev.type == GUI_EVENT_CLICK) {
calculator_click(ev.arg1, ev.arg2);
} else if (ev.type == GUI_EVENT_CLOSE) {
sys_exit(0);
}
}
}
return 0;
}

Binary file not shown.

View file

@ -0,0 +1,12 @@
#include "syscall.h"
int main() {
const char* msg = "Attempting to crash via null dereference...\n";
sys_write(1, msg, 45);
// Null pointer dereference
volatile int* p = (int*)0;
*p = 123;
return 0;
}

BIN
src/kernel/userland/crash.elf Executable file

Binary file not shown.

View file

@ -0,0 +1,26 @@
; userland/crt0.asm
global _start
extern main
extern sys_exit
section .text
_start:
; The kernel loads the ELF and jumps here.
; RSP should point to the 0x800000 stack.
; Align the stack to 16 bytes for C functions (System V ABI)
and rsp, -16
; Call main(argc, argv)
; We don't have argc or argv yet, pass 0
xor rdi, rdi
xor rsi, rsi
call main
; If main returns, call exit(status)
mov rdi, rax ; Pass main's return value to exit syscall
call sys_exit
; Fallback halt if exit miraculously returns
.hang:
jmp .hang

BIN
src/kernel/userland/crt0.o Normal file

Binary file not shown.

View file

@ -0,0 +1,7 @@
#include "syscall.h"
int main() {
const char* msg = "Hello from Userland ELF!\n";
sys_write(1, msg, 25);
return 0;
}

BIN
src/kernel/userland/hello.elf Executable file

Binary file not shown.

BIN
src/kernel/userland/hello.o Normal file

Binary file not shown.

View file

@ -0,0 +1,40 @@
#include "libui.h"
#include "syscall.h"
#include <stddef.h>
extern uint64_t syscall3(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3);
extern uint64_t syscall4(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4);
extern uint64_t syscall5(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5);
// sys_gui uses syscall #3
#define SYS_GUI 3
ui_window_t ui_window_create(const char *title, int x, int y, int w, int h) {
uint64_t params[4] = { (uint64_t)x, (uint64_t)y, (uint64_t)w, (uint64_t)h };
return (ui_window_t)syscall3(SYS_GUI, GUI_CMD_WINDOW_CREATE, (uint64_t)title, (uint64_t)params);
}
bool ui_get_event(ui_window_t win, gui_event_t *ev) {
int res = (int)syscall3(SYS_GUI, GUI_CMD_GET_EVENT, (uint64_t)win, (uint64_t)ev);
return res != 0;
}
void ui_draw_rect(ui_window_t win, int x, int y, int w, int h, uint32_t color) {
uint64_t params[4] = { (uint64_t)x, (uint64_t)y, (uint64_t)w, (uint64_t)h };
syscall4(SYS_GUI, GUI_CMD_DRAW_RECT, (uint64_t)win, (uint64_t)params, (uint64_t)color);
}
void ui_draw_rounded_rect_filled(ui_window_t win, int x, int y, int w, int h, int radius, uint32_t color) {
uint64_t params[5] = { (uint64_t)x, (uint64_t)y, (uint64_t)w, (uint64_t)h, (uint64_t)radius };
syscall4(SYS_GUI, GUI_CMD_DRAW_ROUNDED_RECT_FILLED, (uint64_t)win, (uint64_t)params, (uint64_t)color);
}
void ui_draw_string(ui_window_t win, int x, int y, const char *str, uint32_t color) {
uint64_t coords = ((uint64_t)x & 0xFFFFFFFF) | ((uint64_t)y << 32);
syscall5(SYS_GUI, GUI_CMD_DRAW_STRING, (uint64_t)win, coords, (uint64_t)str, (uint64_t)color);
}
void ui_mark_dirty(ui_window_t win, int x, int y, int w, int h) {
uint64_t params[4] = { (uint64_t)x, (uint64_t)y, (uint64_t)w, (uint64_t)h };
syscall3(SYS_GUI, GUI_CMD_MARK_DIRTY, (uint64_t)win, (uint64_t)params);
}

View file

@ -0,0 +1,40 @@
#ifndef LIBUI_H
#define LIBUI_H
#include <stdint.h>
#include <stdbool.h>
// GUI Command IDs
#define GUI_CMD_WINDOW_CREATE 1
#define GUI_CMD_DRAW_RECT 2
#define GUI_CMD_DRAW_STRING 3
#define GUI_CMD_MARK_DIRTY 4
#define GUI_CMD_GET_EVENT 5
#define GUI_CMD_DRAW_ROUNDED_RECT_FILLED 6
// Event Types
#define GUI_EVENT_NONE 0
#define GUI_EVENT_PAINT 1
#define GUI_EVENT_CLICK 2
#define GUI_EVENT_RIGHT_CLICK 3
#define GUI_EVENT_CLOSE 4
typedef struct {
int type;
int arg1; // For click: x
int arg2; // For click: y
} gui_event_t;
// Window Handle
typedef int ui_window_t;
// libui API
ui_window_t ui_window_create(const char *title, int x, int y, int w, int h);
bool ui_get_event(ui_window_t win, gui_event_t *ev);
void ui_draw_rect(ui_window_t win, int x, int y, int w, int h, uint32_t color);
void ui_draw_rounded_rect_filled(ui_window_t win, int x, int y, int w, int h, int radius, uint32_t color);
void ui_draw_string(ui_window_t win, int x, int y, const char *str, uint32_t color);
void ui_mark_dirty(ui_window_t win, int x, int y, int w, int h);
#endif

View file

@ -0,0 +1,71 @@
#include "syscall.h"
uint64_t syscall0(uint64_t sys_num) {
uint64_t ret;
asm volatile("syscall"
: "=a"(ret)
: "a"(sys_num)
: "rcx", "r11", "memory");
return ret;
}
uint64_t syscall1(uint64_t sys_num, uint64_t arg1) {
uint64_t ret;
asm volatile("syscall"
: "=a"(ret)
: "a"(sys_num), "D"(arg1)
: "rcx", "r11", "memory");
return ret;
}
uint64_t syscall2(uint64_t sys_num, uint64_t arg1, uint64_t arg2) {
uint64_t ret;
asm volatile("syscall"
: "=a"(ret)
: "a"(sys_num), "D"(arg1), "S"(arg2)
: "rcx", "r11", "memory");
return ret;
}
uint64_t syscall3(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3) {
uint64_t ret;
asm volatile("syscall"
: "=a"(ret)
: "a"(sys_num), "D"(arg1), "S"(arg2), "d"(arg3)
: "rcx", "r11", "memory", "r10", "r8", "r9");
return ret;
}
uint64_t syscall4(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4) {
uint64_t ret;
register uint64_t r10 asm("r10") = arg4;
asm volatile("syscall"
: "=a"(ret)
: "a"(sys_num), "D"(arg1), "S"(arg2), "d"(arg3), "r"(r10)
: "rcx", "r11", "memory", "r8", "r9");
return ret;
}
uint64_t syscall5(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5) {
uint64_t ret;
register uint64_t r10 asm("r10") = arg4;
register uint64_t r8 asm("r8") = arg5;
asm volatile("syscall"
: "=a"(ret)
: "a"(sys_num), "D"(arg1), "S"(arg2), "d"(arg3), "r"(r10), "r"(r8)
: "rcx", "r11", "memory", "r9");
return ret;
}
// C-Friendly Wrappers
void sys_exit(int status) {
syscall1(SYS_EXIT, (uint64_t)status);
while (1); // Halt
}
int sys_write(int fd, const char *buf, int len) {
return (int)syscall3(SYS_WRITE, (uint64_t)fd, (uint64_t)buf, (uint64_t)len);
}

View file

@ -0,0 +1,22 @@
#ifndef SYSCALL_H
#define SYSCALL_H
#include <stdint.h>
// Standard syscalls available from Kernel mode
#define SYS_EXIT 0
#define SYS_WRITE 1
// Internal assembly entry into Ring 0
extern uint64_t syscall0(uint64_t sys_num);
extern uint64_t syscall1(uint64_t sys_num, uint64_t arg1);
extern uint64_t syscall2(uint64_t sys_num, uint64_t arg1, uint64_t arg2);
extern uint64_t syscall3(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3);
extern uint64_t syscall4(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4);
extern uint64_t syscall5(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5);
// Public API
void sys_exit(int status);
int sys_write(int fd, const char *buf, int len);
#endif

Binary file not shown.

View file

@ -2,7 +2,7 @@
#include "graphics.h"
#include "io.h"
#include "cmd.h"
#include "calculator.h"
#include "process.h"
#include "cli_apps/cli_utils.h"
#include "explorer.h"
#include "editor.h"
@ -475,6 +475,40 @@ void draw_document_icon(int x, int y, const char *label) {
draw_icon_label(x, y, label);
}
void draw_elf_icon(int x, int y, const char *label) {
uint32_t icon_buf[48 * 48];
for (int i = 0; i < 48 * 48; i++) icon_buf[i] = 0xFFFF00FF;
graphics_set_render_target(icon_buf, 48, 48);
// Grey squircle (macOS detailed style)
draw_rounded_rect_filled(2, 2, 44, 44, 12, 0xFF353535); // Subtle shadow border
draw_rounded_rect_filled(4, 4, 40, 40, 10, 0xFF4A4A4A); // Main grey body
// Glossy top highlight
draw_rect(10, 5, 28, 1, 0xFF5A5A5A);
// Green "exec" text (fixed font 8x12)
draw_string(8, 12, "exec", 0xFF00FF00);
// Minor details to look "premium"
draw_rect(10, 28, 28, 1, 0xFF3D3D3D);
draw_rect(10, 34, 20, 1, 0xFF3D3D3D);
graphics_set_render_target(NULL, 0, 0);
int dx = x + 24, dy = y + 12;
for (int ty = 0; ty < 32; ty++) {
for (int tx = 0; tx < 32; tx++) {
int src_x = tx * 48 / 32;
int src_y = ty * 48 / 32;
uint32_t c1 = icon_buf[src_y * 48 + src_x];
if (c1 != 0xFFFF00FF) put_pixel(dx + tx, dy + ty, c1);
}
}
draw_icon_label(x, y, label);
}
// === Dynamic thumbnail cache for JPG explorer icons ===
#define THUMB_CACHE_SIZE 8
#define THUMB_PIXELS (48 * 48)
@ -967,6 +1001,12 @@ void draw_window(Window *win) {
draw_rounded_rect_filled(win->x, win->y + 20, win->w, win->h - 20, 8, COLOR_DARK_BG);
draw_rect(win->x, win->y + 20, win->w, 8, COLOR_DARK_BG);
if (win->comp_pixels) {
graphics_blit_buffer(win->comp_pixels, win->x, win->y, win->w, win->h);
} else if (win->pixels) {
graphics_blit_buffer(win->pixels, win->x, win->y, win->w, win->h);
}
if (win->paint) {
win->paint(win);
}
@ -1101,7 +1141,8 @@ void wm_paint(void) {
else if (str_starts_with(icon->name, "Paint")) draw_paint_icon(icon->x, icon->y, label);
else draw_icon(icon->x, icon->y, label);
} else {
if (str_ends_with(icon->name, ".pnt")) draw_paint_icon(icon->x, icon->y, icon->name);
if (str_ends_with(icon->name, ".elf")) draw_elf_icon(icon->x, icon->y, icon->name);
else if (str_ends_with(icon->name, ".pnt")) draw_paint_icon(icon->x, icon->y, icon->name);
else if (str_ends_with(icon->name, ".jpg") || str_ends_with(icon->name, ".JPG")) {
draw_image_icon(icon->x, icon->y, icon->name);
draw_icon_label(icon->x, icon->y, icon->name);
@ -1299,6 +1340,41 @@ void wm_bring_to_front(Window *win) {
void wm_add_window(Window *win) {
if (window_count < 32) {
all_windows[window_count++] = win;
wm_bring_to_front(win); // Ensure newly added windows are on top
}
}
void wm_remove_window(Window *win) {
if (!win) return;
int index = -1;
for (int i = 0; i < window_count; i++) {
if (all_windows[i] == win) {
index = i;
break;
}
}
if (index != -1) {
// Shift remaining windows
for (int i = index; i < window_count - 1; i++) {
all_windows[i] = all_windows[i + 1];
}
window_count--;
// Free resources
if (win->pixels) kfree(win->pixels);
if (win->comp_pixels) kfree(win->comp_pixels);
// If the title was allocated in syscall.c, we should free it,
// but currently we don't know for sure if it was kmalloc'd or a literal.
// In syscall.c it is kmalloc'd. Let's assume we should free it if it's not a known literal.
// Safer to just free it since userland windows always have kmalloc'd titles.
if (win->title && win->handle_close) { // Heuristic: user windows have handle_close set in syscall.c
kfree(win->title);
}
kfree(win);
force_redraw = true;
}
}
@ -1499,7 +1575,12 @@ void wm_handle_click(int x, int y) {
// Check traffic light close button (now at top-left)
if (rect_contains(topmost->x + 8, topmost->y + 2, 12, 12, x, y)) {
topmost->visible = false;
if (topmost->handle_close) {
topmost->handle_close(topmost);
} else {
topmost->visible = false;
}
// Reset window state on close
if (topmost == &win_explorer) {
explorer_reset();
@ -1749,7 +1830,7 @@ void wm_handle_right_click(int x, int y) {
} else if (str_starts_with(start_menu_pending_app, "Terminal")) {
cmd_reset(); wm_bring_to_front(&win_cmd);
} else if (str_starts_with(start_menu_pending_app, "Calculator")) {
wm_bring_to_front(&win_calculator);
process_create_elf("/bin/calculator.elf");
} else if (str_starts_with(start_menu_pending_app, "Minesweeper")) {
wm_bring_to_front(&win_minesweeper);
} else if (str_starts_with(start_menu_pending_app, "Settings")) {
@ -1779,7 +1860,7 @@ void wm_handle_right_click(int x, int y) {
if (str_ends_with(icon->name, "Notepad.shortcut")) {
wm_bring_to_front(&win_notepad); handled = true;
} else if (str_ends_with(icon->name, "Calculator.shortcut")) {
wm_bring_to_front(&win_calculator); handled = true;
process_create_elf("/bin/calculator.elf"); handled = true;
} else if (str_ends_with(icon->name, "Minesweeper.shortcut")) {
wm_bring_to_front(&win_minesweeper); handled = true;
} else if (str_ends_with(icon->name, "Settings.shortcut")) {
@ -1830,7 +1911,9 @@ void wm_handle_right_click(int x, int y) {
char path[128] = "/Desktop/";
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
if (str_ends_with(icon->name, ".pnt")) {
if (str_ends_with(icon->name, ".elf")) {
process_create_elf(path);
} else if (str_ends_with(icon->name, ".pnt")) {
paint_load(path);
wm_bring_to_front(&win_paint);
} else if (str_ends_with(icon->name, ".md")) {
@ -2173,7 +2256,6 @@ void wm_init(void) {
notepad_init();
cmd_init();
calculator_init();
explorer_init();
editor_init();
markdown_init();
@ -2189,27 +2271,25 @@ void wm_init(void) {
// Initialize z-indices
win_notepad.z_index = 0;
win_cmd.z_index = 1;
win_calculator.z_index = 2;
win_explorer.z_index = 3;
win_editor.z_index = 4;
win_markdown.z_index = 5;
win_control_panel.z_index = 6;
win_about.z_index = 7;
win_minesweeper.z_index = 8;
win_paint.z_index = 9;
win_explorer.z_index = 2;
win_editor.z_index = 3;
win_markdown.z_index = 4;
win_control_panel.z_index = 5;
win_about.z_index = 6;
win_minesweeper.z_index = 7;
win_paint.z_index = 8;
all_windows[0] = &win_notepad;
all_windows[1] = &win_cmd;
all_windows[2] = &win_calculator;
all_windows[3] = &win_explorer;
all_windows[4] = &win_editor;
all_windows[5] = &win_markdown;
all_windows[6] = &win_control_panel;
all_windows[7] = &win_about;
all_windows[8] = &win_minesweeper;
all_windows[9] = &win_paint;
all_windows[10] = &win_viewer;
window_count = 11;
all_windows[2] = &win_explorer;
all_windows[3] = &win_editor;
all_windows[4] = &win_markdown;
all_windows[5] = &win_control_panel;
all_windows[6] = &win_about;
all_windows[7] = &win_minesweeper;
all_windows[8] = &win_paint;
all_windows[9] = &win_viewer;
window_count = 10;
// Only show Explorer and Notepad on desktop (Explorer on top)
win_explorer.visible = false;
@ -2222,7 +2302,6 @@ void wm_init(void) {
// Rest are hidden initially
win_cmd.visible = false;
win_calculator.visible = false;
win_editor.visible = false;
win_markdown.visible = false;
win_control_panel.visible = false;

View file

@ -48,12 +48,15 @@ struct Window {
bool focused;
int z_index; // Layering depth (higher = on top)
void *data; // Per-window private data
uint32_t *pixels; // Pointer to backing buffer for UI events (Back Buffer)
uint32_t *comp_pixels; // Pointer to composition buffer (Front Buffer)
// Callbacks
void (*paint)(Window *win);
void (*handle_key)(Window *win, char c);
void (*handle_click)(Window *win, int x, int y);
void (*handle_right_click)(Window *win, int x, int y);
void (*handle_close)(Window *win);
};
void wm_init(void);
@ -63,6 +66,7 @@ void wm_handle_click(int x, int y);
void wm_handle_right_click(int x, int y);
void wm_process_input(void);
void wm_add_window(Window *win);
void wm_remove_window(Window *win);
void wm_bring_to_front(Window *win);
// Redraw system
@ -87,6 +91,7 @@ void draw_traffic_light(int x, int y); // Red close button only
void draw_icon(int x, int y, const char *label);
void draw_folder_icon(int x, int y, const char *label);
void draw_document_icon(int x, int y, const char *label);
void draw_elf_icon(int x, int y, const char *label);
void draw_image_icon(int x, int y, const char *label);
void draw_notepad_icon(int x, int y, const char *label);
void draw_calculator_icon(int x, int y, const char *label);