33 Commits

Author SHA1 Message Date
3f9b78b05e Scheduler returns to IDLE when.. idle. 2026-03-09 09:27:55 +01:00
42c7a55d3f Init struct + freeing a bit of kmain() 2026-03-08 13:21:19 +01:00
5e9c582833 Fixed kbd (buffer flush) 2026-03-08 09:54:45 +01:00
77d9df6f48 Merge pull request 'flanterm' (#11) from flanterm into main
Reviewed-on: #11
2026-03-08 09:18:34 +01:00
90dc26ee11 Flanterm support OK from kmain. No kbd. Writing from process = PF 2026-03-08 09:14:21 +01:00
c8a72244b1 remove old term support + PSFv1 font 2026-03-05 09:10:06 +01:00
b9f55d89f6 no more PF in kmain, but still PF in process OR corruption of fb 2026-03-05 08:08:50 +01:00
a7d9e70a61 Flanterm can write to fb but page fault before process creation. (BEFORE KHEAP UPDATE) 2026-03-04 12:21:20 +01:00
9df33b49d8 flanterm PAGE FAULT, tries to access NULL or NULL+small offset 2026-03-02 11:32:24 +01:00
1f055ab31c Flanterm integration? but page fault in flanterm_fb_double_buffer_flush 2026-02-22 18:27:57 +01:00
95c801b991 Merge pull request 'process_mem' (#10) from process_mem into main
Reviewed-on: #10
2026-02-21 19:57:30 +01:00
70f19ab299 symbols build files added to gitignore 2026-02-21 19:36:44 +01:00
9470dedb61 Stack trace with double linking to get symbol names 2026-02-21 19:28:17 +01:00
4cf4fb0dda Task switching fix? but doesnt exit process gracefully 2026-02-20 16:01:34 +01:00
ac7216d84a Setup kernel stack; but process is failing 2026-02-17 23:01:32 +01:00
458ba375f3 better panic 2026-02-07 02:18:15 +01:00
b920c87bab Merge pull request 'process' (#9) from process into main
Reviewed-on: #9
2026-02-06 21:46:07 +01:00
4fbd9b3987 minor fix 2026-02-06 21:44:51 +01:00
8aad1235c3 A bit of cleaning 2026-02-06 14:39:19 +01:00
38710653be Config header file + comment header 2026-02-06 13:59:46 +01:00
7f997f6611 alloc_stack ok (HHDM mapped from mmap) 2026-02-05 21:18:21 +01:00
7bb542d901 bump-allocated PID but kheap needs fix to kmalloc more than PAGE_SIZE 2026-02-02 11:05:27 +01:00
4a90de9521 10ms Round Robin scheduler (blank processes) 2026-02-01 11:25:43 +01:00
c46157fad0 Process linked list 2026-01-31 14:13:48 +01:00
6e633b44b7 Merge pull request 'term_fix' (#8) from term_fix into main
Reviewed-on: #8
2026-01-25 09:53:45 +01:00
b8a155fada Who cares 2026-01-25 09:51:28 +01:00
091f94f89e Broken term scrolling 2026-01-10 14:43:51 +01:00
b469952d91 scroll kinda works but keyboard is random 2026-01-10 11:32:27 +01:00
9cbecc1689 GP Fault handler 2026-01-10 11:04:08 +01:00
12ab12f1b2 serial Kernel panic 2026-01-10 09:45:20 +01:00
0f72987bc1 use boot_ctx 2026-01-04 11:18:20 +01:00
d9dfd4c749 version splash 2026-01-04 11:00:30 +01:00
be1be41a64 Merge pull request 'memory' (#7) from memory into main
Reviewed-on: #7
2026-01-04 09:27:59 +01:00
50 changed files with 5222 additions and 383 deletions

4
.gitignore vendored
View File

@@ -7,3 +7,7 @@ iso_root
*.gch
*/*.gch
*/*/*.gch
.gdb_history
symbols.map
symbols.S
*.log

24
DOOM.txt Normal file
View File

@@ -0,0 +1,24 @@
up to doom:
- Return from pedicel_main() normally (to idle)
** Checkpoint: ring0 process working
- VFS layer (open/read/write/...) with USTar filesystem (for initrd)
** Checkpoint: files not linked to but accessible by the kernel
- Ring3 memory mappings
- Ring3 privilege switch
** Checkpoint: welcome to userland
- Syscall interface
- Implement syscalls needed for doom
** Checkpoint: can run simple programs, ring 3, loaded from filesystem
- Properly handle the keyboard interrupt (keyboard buffer)
- Port DOOMgeneric (few functions with Framebuffer/ticks/etc.)
** Achievement: It runs doom!

View File

@@ -1,11 +1,16 @@
SOURCES = src/mem/heap/kheap.c src/mem/paging/vmm.c src/mem/paging/paging.c src/mem/paging/pmm.c src/string/string.c src/io/kbd/ps2.c src/io/serial/serial.c src/io/term/printf.c src/io/term/term.c src/idt/idt.c src/mem/gdt/gdt.c src/mem/misc/utils.c src/time/timer.c src/kmain.c
SOURCES = src/debug/misc.c src/io/term/flanterm_backends/fb.c src/io/term/flanterm.c src/debug/panic.c src/debug/stacktrace.c src/boot/boot.c src/sched/scheduler.c src/sched/process.c src/mem/heap/kheap.c src/mem/paging/vmm.c src/mem/paging/paging.c src/mem/paging/pmm.c src/string/string.c src/io/kbd/ps2.c src/io/serial/serial.c src/io/term/printf.c src/io/term/term.c src/idt/idt.c src/mem/gdt/gdt.c src/mem/misc/utils.c src/time/timer.c src/kmain.c
PROBLEMATIC_FLAGS=-Wno-unused-parameter -Wno-unused-variable
build:
rm -f *.o
x86_64-elf-gcc -g -c -Isrc $(SOURCES) -Wall -Wextra -std=gnu99 -nostdlib -ffreestanding -fno-stack-protector -fno-stack-check -fno-PIC -ffunction-sections -fdata-sections -mcmodel=kernel
objcopy -O elf64-x86-64 -B i386 -I binary zap-light16.psf zap-light16.o
x86_64-elf-gcc -g -c -Isrc $(SOURCES) $(PROBLEMATIC_FLAGS) -Wall -Wextra -std=gnu99 -nostdlib -ffreestanding -fno-stack-protector -fno-omit-frame-pointer -fno-stack-check -fno-PIC -ffunction-sections -fdata-sections -mcmodel=kernel
nasm -f elf64 src/idt/idt.S -o idt_stub.o
x86_64-elf-ld -o pepperk -T linker.ld *.o
nm -n pepperk | awk '$$2 ~ /[TtDdBbRr]/ {print $$1, $$3}' > symbols.map
python3 symbols.py
nasm -f elf64 symbols.S -o symbols.o
x86_64-elf-ld -o pepperk -T linker.ld *.o
limine/limine:
rm -rf limine
@@ -30,11 +35,15 @@ build-iso: limine/limine build
./limine/limine bios-install pepper.iso
debug:
qemu-system-x86_64 -drive file=pepper.iso -s -S -d int -no-reboot -no-shutdown &
/usr/bin/qemu-system-x86_64 -drive file=pepper.iso -s -S -d int -D qemu.log -no-reboot -no-shutdown &
gdb pepperk --command=debug.gdb
debug2:
/usr/bin/qemu-system-x86_64 -drive file=pepper.iso -s -S -d int -no-reboot -no-shutdown &
pwndbg pepperk --command=debug.gdb
run: build-iso
qemu-system-x86_64 -cdrom pepper.iso -serial stdio
/usr/bin/qemu-system-x86_64 -cdrom pepper.iso -serial stdio
clean:
rm -rf *.o pepperk iso_root pepper.iso limine
rm -rf *.o symbols.map symbols.S pepperk iso_root pepper.iso limine

View File

@@ -2,7 +2,7 @@
## Trying the kernel
First install the dependencies: `sudo apt install xorriso make qemu-system`
First install the dependencies: `sudo apt install python3 xorriso make qemu-system`
Then, to compile the kernel and make an ISO image file: `make build-iso`
To run it with QEMU, `make run`
@@ -11,18 +11,27 @@ To run it with QEMU, `make run`
The basics that I'm targeting are:
### Basic utility of what we call a "kernel"
- Fix terminal driver (backspace issues, scrolling) OR add Flanterm or equivalent
- Implement paging / see what Limine does at boot with memory management
- Implement tasks, and task switching
- Implement tasks, and task switching + context switching and spinlock acquire/release
- Load an executable
- Scheduler (round-robin using the PIT timer interrupt)
- Filesystem (TAR for read-only initfs, then maybe read-write using FAT12/16/32
- Filesystem (TAR for read-only initfs, then maybe read-write using FAT12/16/32 or easier fs) w/ VFS layer
- Getting to userspace (syscalls)
- Porting musl libc or equivalent
In the future, maybe?
### Scalability/maintenance/expansion features
- SMP support
- Documentation
- SOME error handling in functions
- Unit tests
- Good error codes (like Linux kernel: ENOMEM, ENOENT, ...)
- Make the panic function work within itself without dependencies + error message (and still get cpu context?)
### Optional features
In the future, maybe?
- SMP support (Limine provides functionality to make this easier)
- Parsing the ACPI tables and using them for something
- Replacing the PIT timer with APIC
@@ -32,8 +41,10 @@ PepperOS wouldn't be possible without the following freely-licensed software:
- the [Limine](https://codeberg.org/Limine/Limine) portable bootloader
- Marco Paland's freestanding [printf implementation](https://github.com/mpaland)
- the [ZAP](https://www.zap.org.au/projects/console-fonts-zap/) PSF console fonts
- Mintuski's [Flanterm](https://codeberg.org/Mintsuki/Flanterm) terminal emulator
...and without these amazing resources:
- the [OSDev](https://osdev.org) wiki & forums
- Intel 64 and IA-32 Architectures Software Developer's Manual
- Documentation for the [GNU Compiler Collection](https://gcc.gnu.org/onlinedocs/gcc/)

View File

@@ -1,3 +1,7 @@
target remote localhost:1234
set disassembly-flavor intel
display/8i $rip
display/4i $rip
# Trying to debug that flanterm page fault
# b plot_char_unscaled_uncanvas if $rdi == 0 || $rsi == 0 || $rdx == 0 || $r10 == 0

41
src/boot/boot.c Normal file
View File

@@ -0,0 +1,41 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Limine requests for boot
* @license GPL-3.0-only
*/
#include <limine.h>
// Framebuffer request
__attribute__((used, section(".limine_requests")))
volatile struct limine_framebuffer_request framebuffer_request = {
.id = LIMINE_FRAMEBUFFER_REQUEST,
.revision = 0
};
// Memory map request
__attribute__((used, section(".limine_requests")))
volatile struct limine_memmap_request memmap_request = {
.id = LIMINE_MEMMAP_REQUEST,
.revision = 0
};
// Higher Half Direct Map
__attribute__((used, section(".limine_requests")))
volatile struct limine_hhdm_request hhdm_request = {
.id = LIMINE_HHDM_REQUEST,
.revision = 0
};
// Executable Address/Kernel Address (find base phys/virt address of kernel)
__attribute__((used, section(".limine_requests")))
volatile struct limine_kernel_address_request kerneladdr_request = {
.id = LIMINE_KERNEL_ADDRESS_REQUEST,
.revision = 0
};
__attribute__((used, section(".limine_requests_start")))
volatile LIMINE_REQUESTS_START_MARKER;
__attribute__((used, section(".limine_requests_end")))
volatile LIMINE_REQUESTS_END_MARKER;

38
src/config.h Normal file
View File

@@ -0,0 +1,38 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief PepperOS configuration file
* @license GPL-3.0-only
*/
#ifndef CONFIG_H
#define CONFIG_H
/* version */
#define PEPPEROS_VERSION_MAJOR "0"
#define PEPPEROS_VERSION_MINOR "0"
#define PEPPEROS_VERSION_PATCH "58"
#define PEPPEROS_SPLASH "\x1b[38;5;196mPepperOS\x1b[0m version "PEPPEROS_VERSION_MAJOR"."PEPPEROS_VERSION_MINOR"."PEPPEROS_VERSION_PATCH"\n"
/* process */
#define PROCESS_NAME_MAX 64
#define PROCESS_STACK_SIZE 0x10000 // 64kb
#define PROCESS_BASE 0x400000
#define PROCESS_STACK_BASE 0x1000000
/* sched */
// 1 tick = 1 ms => quantum = 10ms
#define SCHEDULER_QUANTUM 10
/* kernel */
#define KERNEL_BASE 0xFFFFFFFF80000000ULL
// 2 MB should be enough (as of now, the whole kernel ELF is around 75kb)
#define KERNEL_SIZE 0x200000
#define KERNEL_STACK_SIZE 65536
/* heap */
#define KHEAP_SIZE (32*1024*1024)
/* term */
#define TERM_HISTORY_MAX_LINES 256
#endif

61
src/debug/misc.c Normal file
View File

@@ -0,0 +1,61 @@
#include <kernel.h>
#include "limine.h"
#include "string/string.h"
extern struct boot_context boot_ctx;
// Display the memmap so we see how the memory is laid out at handoff
void memmap_display(struct limine_memmap_response* response)
{
DEBUG("Got memory map from Limine: revision %u, %u entries", response->revision, response->entry_count);
for (size_t i=0; i<response->entry_count; i++)
{
struct limine_memmap_entry* entry = response->entries[i];
char type[32] = {0};
switch(entry->type)
{
case LIMINE_MEMMAP_USABLE:
strcpy(type, "USABLE");
break;
case LIMINE_MEMMAP_RESERVED:
strcpy(type, "RESERVED");
break;
case LIMINE_MEMMAP_ACPI_RECLAIMABLE:
strcpy(type, "ACPI_RECLAIMABLE");
break;
case LIMINE_MEMMAP_ACPI_NVS:
strcpy(type, "ACPI_NVS");
break;
case LIMINE_MEMMAP_BAD_MEMORY:
strcpy(type, "BAD_MEMORY");
break;
case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE:
strcpy(type, "BOOTLOADER_RECLAIMABLE");
break;
case LIMINE_MEMMAP_KERNEL_AND_MODULES:
strcpy(type, "KERNEL_AND_MODULES");
break;
case LIMINE_MEMMAP_FRAMEBUFFER:
strcpy(type, "FRAMEBUFFER");
break;
default:
strcpy(type, "UNKNOWN");
break;
}
DEBUG("entry %02u: [0x%016x | %016u bytes] - %s", i, entry->base, entry->length, type);
}
}
// Display the HHDM
void hhdm_display(struct limine_hhdm_response* hhdm)
{
DEBUG("Got HHDM revision=%u offset=0x%p", hhdm->revision, hhdm->offset);
}
void boot_mem_display()
{
memmap_display(boot_ctx.mmap);
hhdm_display(boot_ctx.hhdm);
DEBUG("kernel: phys_base=0x%p virt_base=0x%p", boot_ctx.kaddr->physical_base, boot_ctx.kaddr->virtual_base);
}

27
src/debug/panic.c Normal file
View File

@@ -0,0 +1,27 @@
#include <stddef.h>
#include "idt/idt.h"
#include "io/serial/serial.h"
#include "kernel.h"
void panic(struct cpu_status_t* ctx, const char* str)
{
CLEAR_INTERRUPTS;
if (ctx == NULL)
{
DEBUG("\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[0m Something went horribly wrong! (no cpu ctx)");
fctprintf((void*)&skputc, 0, "\x1b[38;5;231m\x1b[48;5;27m");
DIE_DEBUG(str);
fctprintf((void*)&skputc, 0, "\x1b[0m");
skputc('\r');
skputc('\n');
DEBUG("\x1b[38;5;231m\x1b[48;5;196mend Kernel panic - halting...\x1b[0m");
hcf();
}
DEBUG("\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[0m at rip=%p\r\nSomething went horribly wrong! (%s) vect=0x%.2x errcode=0x%x\n\rrax=%p rbx=%p rcx=%p rdx=%p\n\rrsi=%p rdi=%p r8=%p r9=%p\n\rr10=%p r11=%p r12=%p r13=%p\n\rr14=%p r15=%p\n\n\rflags=%p\n\rHalting...",
ctx->iret_rip,
str,
ctx->vector_number, ctx->error_code, ctx->rax, ctx->rbx, ctx->rcx, ctx->rdx, ctx->rsi, ctx->rdi,
ctx->r8, ctx->r9, ctx->r10, ctx->r11, ctx->r12, ctx->r13, ctx->r14, ctx->r15, ctx->iret_flags);
debug_stack_trace(100);
hcf();
}

75
src/debug/stacktrace.c Normal file
View File

@@ -0,0 +1,75 @@
#include <stdint.h>
#include "kernel.h"
void debug_stack_trace(unsigned int max_frames)
{
DEBUG("*** begin stack trace ***");
// Thanks GCC :)
uintptr_t* rbp = (uintptr_t*)__builtin_frame_address(0);
for (unsigned int frame=0; frame<max_frames && rbp != NULL; frame++)
{
// Return address, 1 word above saved rbp
uintptr_t rip = rbp[1];
uintptr_t offset = 0;
const char* name = debug_find_symbol(rip, &offset);
DEBUG("[%u] <0x%p> (%s+0x%x)", frame, (void*)rip, name, offset);
uintptr_t* next_rbp = (uintptr_t*)rbp[0];
// invalid rbp or we're at the end
if (next_rbp <= rbp || next_rbp == NULL)
{
break;
}
rbp = next_rbp;
}
DEBUG("*** end stack trace ***");
}
typedef struct
{
uint64_t addr;
const char *name;
} __attribute__((packed)) kernel_symbol_t;
__attribute__((weak)) extern kernel_symbol_t symbol_table[];
__attribute__((weak)) extern uint64_t symbol_count;
// binary search
const char* debug_find_symbol(uintptr_t rip, uintptr_t* offset)
{
if (!symbol_table || symbol_count == 0)
{
if (offset) *offset = 0;
return "???";
}
int low = 0, high = (int)symbol_count - 1;
int best = -1;
while (low <= high)
{
int mid = (low + high) / 2;
if (symbol_table[mid].addr <= rip)
{
best = mid;
low = mid + 1;
} else {
high = mid - 1;
}
}
if (best != -1)
{
if (offset)
{
*offset = rip - symbol_table[best].addr;
}
return symbol_table[best].name;
}
if (offset) *offset = 0;
return "unknown";
}

View File

@@ -1,4 +1,8 @@
; Assembly stub for the IDT
;
; @author xamidev <xamidev@riseup.net>
; @brief Stub for Interrupt Descriptor Table handlers
; @license GPL-3.0-only
;
bits 64
@@ -147,12 +151,13 @@ vector_7_handler:
align 16
vector_8_handler:
; No error code, we only push vector number
push qword 1
push qword 8
jmp interrupt_stub
; Coprocessor Segment Overrun
align 16
vector_9_handler:
push qword 0
push qword 9
jmp interrupt_stub

View File

@@ -1,9 +1,19 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Interrupt Descriptor Table setup and dispatching
* @license GPL-3.0-only
*/
#include "idt.h"
#include <stdint.h>
#include <stddef.h>
#include "io/serial/serial.h"
#include "io/kbd/ps2.h"
#include <kernel.h>
#include <stdbool.h>
#include "sched/scheduler.h"
#include "config.h"
#include "sched/process.h"
struct interrupt_descriptor idt[256];
struct idtr idt_reg;
@@ -12,7 +22,7 @@ struct idtr idt_reg;
extern char vector_0_handler[];
// Timer ticks
extern uint64_t ticks;
extern volatile uint64_t ticks;
void idt_set_entry(uint8_t vector, void* handler, uint8_t dpl)
{
@@ -42,9 +52,8 @@ void idt_load(void* idt_addr)
void idt_init()
{
// We set 256 entries, but we have only the first few stubs.
// Undefined behavior?
for (size_t i=0; i<256; i++)
// Hardcoded...
for (size_t i=0; i<=33; i++)
{
// Each vector handler is 16-byte aligned, so <vector_no>*16 = address of that handler
idt_set_entry(i, vector_0_handler + (i*16), 0);
@@ -53,8 +62,67 @@ void idt_init()
DEBUG("IDT initialized");
}
static inline uint64_t read_cr2(void)
{
uint64_t val;
asm volatile ("mov %%cr2, %0" : "=r"(val));
return val;
}
static void page_fault_handler(struct cpu_status_t* ctx)
{
// It could be used to remap pages etc. to fix the fault, but right now what I'm more
// interested in is getting more info out of those numbers cause i'm lost each time i have
// to read all this mess
uint64_t cr2 = read_cr2();
DEBUG("\x1b[38;5;231mPage Fault at rip=0x%p, err=%u (%s%s%s%s%s%s%s%s) when accessing addr=0x%p\x1b[0m", ctx->iret_rip, ctx->error_code,
CHECK_BIT(ctx->error_code, 0) ? "PAGE_PROTECTION_VIOLATION " : "PAGE_NOT_PRESENT ",
CHECK_BIT(ctx->error_code, 1) ? "ON_WRITE " : "ON_READ ",
CHECK_BIT(ctx->error_code, 2) ? "IN_USER_MODE" : "IN_KERNEL_MODE",
CHECK_BIT(ctx->error_code, 3) ? " WAS_RESERVED" : "",
CHECK_BIT(ctx->error_code, 4) ? " ON_INSTRUCTION_FETCH" : "",
CHECK_BIT(ctx->error_code, 5) ? " PK_VIOLATION" : "",
CHECK_BIT(ctx->error_code, 6) ? " ON_SHADOWSTACK_ACCESS" : "",
CHECK_BIT(ctx->error_code, 7) ? " SGX_VIOLATION" : "",
cr2);
panic(ctx, "page fault");
}
static void gp_fault_handler(struct cpu_status_t* ctx)
{
DEBUG("\x1b[38;5;231mGeneral Protection Fault at rip=0x%p, err=%u (%s)\x1b[0m",
ctx->iret_rip,
ctx->error_code,
(ctx->error_code == 0) ? "NOT_SEGMENT_RELATED" : "SEGMENT_RELATED");
// Segment-related
if (ctx->error_code != 0)
{
bool is_external = CHECK_BIT(ctx->error_code, 0);
// is it IDT, GDT, LDT?
uint8_t table = ctx->error_code & 0x6; // 0b110 (isolate table)
uint16_t index = ctx->error_code & 0xFFF8; // 13*1 1111111111111 + 000 = 1111111111111000
char* table_names[4] = {"GDT", "IDT", "LDT", "IDT"};
DEBUG("\x1b[38;5;231m%s in %s index %u\x1b[0m",
is_external ? "EXTERNAL" : "INTERNAL",
table_names[table],
index);
}
panic(ctx, "gp fault");
}
struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
{
if (context == NULL)
{
panic(NULL, "Interrupt dispatch recieved NULL context!");
}
switch(context->vector_number)
{
case 0:
@@ -77,6 +145,7 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
break;
case 6:
DEBUG("Invalid Opcode!");
panic(context, "Invalid Opcode!");
break;
case 7:
DEBUG("Device Not Available!");
@@ -97,10 +166,11 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
DEBUG("Stack-Segment Fault!");
break;
case 13:
DEBUG("General Protection Fault!");
gp_fault_handler(context);
break;
case 14:
DEBUG("Page Fault!");
// Better debugging for page faults...
page_fault_handler(context);
break;
case 15:
DEBUG("Intel Reserved Interrupt! (Achievement unlocked: How Did We Get Here?)");
@@ -124,15 +194,22 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
DEBUG("Control Protection Exception!");
break;
case 32:
//DEBUG("Tick!");
case 32: // Timer Interrupt
ticks++;
// Send an EOI so that we can continue having interrupts
outb(0x20, 0x20);
if (ticks % SCHEDULER_QUANTUM == 0)
{
return scheduler_schedule(context);
}
break;
case 33:
DEBUG("Keyboard Interrupt");
keyboard_handler();
outb(0x20, 0x20);
break;
default:

View File

@@ -1,3 +1,9 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Interrupt Descriptor Table setup and dispatching
* @license GPL-3.0-only
*/
#ifndef IDT_H
#define IDT_H

View File

@@ -1,4 +1,8 @@
// PS/2 Keyboard support
/*
* @author xamidev <xamidev@riseup.net>
* @brief PS/2 Keyboard driver
* @license GPL-3.0-only
*/
#include "io/serial/serial.h"
#include "ps2.h"
@@ -13,6 +17,8 @@ uint8_t key_status = 0b00000000;
unsigned char* keymap;
unsigned char* keymap_shifted;
extern struct init_status init;
unsigned char kbdus[128] =
{
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', /* 9 */
@@ -172,9 +178,6 @@ void keyboard_handler()
key_status &= ~ALT_PRESSED_BIT;
break;
}
// Send EOI
outb(0x20, 0x20);
return;
}
else
@@ -196,21 +199,25 @@ void keyboard_handler()
default:
{
// Should we get a SHIFTED char or a regular one?
unsigned char c = (key_status & SHIFT_PRESSED_BIT) ? keymap_shifted[scancode] : keymap[scancode];
if (c)
// Avoiding buffer overflow from extended keys lol
if (scancode < 128)
{
putchar(c);
// Should we get a SHIFTED char or a regular one?
unsigned char c = (key_status & SHIFT_PRESSED_BIT) ? keymap_shifted[scancode] : keymap[scancode];
if (c)
{
if (c == '\n')
{
_putchar('\r');
}
// Should probably have a keyboard buffer here... instead of this
_putchar(c);
}
}
}
}
skputs("key pressed!\n");
}
// End of Interrupt (to master PIC)
outb(0x20, 0x20);
}
void keyboard_init(unsigned char layout)
@@ -230,8 +237,21 @@ void keyboard_init(unsigned char layout)
break;
default:
skputs("Unsupported layout.");
panic(NULL, "Unsupported keyboard layout");
return;
}
// Flush keyboard buffer
while (inb(0x64) & 1)
{
inb(0x60);
}
// Unmask IRQ1
uint8_t mask = inb(0x21);
mask &= ~(1 << 1);
outb(0x21, mask);
DEBUG("PS/2 Keyboard initialized");
init.keyboard = true;
}

View File

@@ -1,3 +1,9 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief PS/2 Keyboard driver
* @license GPL-3.0-only
*/
#ifndef PS2_H
#define PS2_H

View File

@@ -1,6 +1,14 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Debug serial driver
* @license GPL-3.0-only
*/
#include <kernel.h>
#include "serial.h"
extern struct init_status init;
void outb(int port, unsigned char data)
{
__asm__ __volatile__("outb %%al, %%dx" :: "a" (data),"d" (port));
@@ -36,7 +44,8 @@ int serial_init()
// Set normal operation mode
outb(PORT + 4, 0x0F);
DEBUG("serial initialized");
DEBUG("*** Welcome to PepperOS! ***");
init.serial = true;
return 0;
}
@@ -48,6 +57,7 @@ static int is_transmit_empty()
// Serial kernel putchar
void skputc(char c)
{
// TODO: Spinlock here (serial access)
while (!is_transmit_empty()); // wait for free spot
outb(PORT, c);
}

View File

@@ -1,3 +1,9 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Debug serial driver
* @license GPL-3.0-only
*/
#ifndef SERIAL_H
#define SERIAL_H

2129
src/io/term/flanterm.c Normal file

File diff suppressed because it is too large Load Diff

72
src/io/term/flanterm.h Normal file
View File

@@ -0,0 +1,72 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/* Copyright (C) 2022-2026 Mintsuki and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FLANTERM_H
#define FLANTERM_H 1
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define FLANTERM_CB_DEC 10
#define FLANTERM_CB_BELL 20
#define FLANTERM_CB_PRIVATE_ID 30
#define FLANTERM_CB_STATUS_REPORT 40
#define FLANTERM_CB_POS_REPORT 50
#define FLANTERM_CB_KBD_LEDS 60
#define FLANTERM_CB_MODE 70
#define FLANTERM_CB_LINUX 80
#define FLANTERM_CB_OSC 90
#ifdef FLANTERM_IN_FLANTERM
#include "flanterm_private.h"
#else
struct flanterm_context;
#endif
void flanterm_write(struct flanterm_context *ctx, const char *buf, size_t count);
void flanterm_flush(struct flanterm_context *ctx);
void flanterm_full_refresh(struct flanterm_context *ctx);
void flanterm_deinit(struct flanterm_context *ctx, void (*_free)(void *ptr, size_t size));
void flanterm_get_dimensions(struct flanterm_context *ctx, size_t *cols, size_t *rows);
void flanterm_set_autoflush(struct flanterm_context *ctx, bool state);
void flanterm_set_callback(struct flanterm_context *ctx, void (*callback)(struct flanterm_context *, uint64_t, uint64_t, uint64_t, uint64_t));
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,79 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/* Copyright (C) 2022-2026 Mintsuki and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FLANTERM_FB_H
#define FLANTERM_FB_H 1
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "../flanterm.h"
#ifdef FLANTERM_IN_FLANTERM
#include "fb_private.h"
#endif
#define FLANTERM_FB_ROTATE_0 0
#define FLANTERM_FB_ROTATE_90 1
#define FLANTERM_FB_ROTATE_180 2
#define FLANTERM_FB_ROTATE_270 3
struct flanterm_context *flanterm_fb_init(
/* If _malloc and _free are nulled, use the bump allocated instance (1 use only). */
void *(*_malloc)(size_t size),
void (*_free)(void *ptr, size_t size),
uint32_t *framebuffer, size_t width, size_t height, size_t pitch,
uint8_t red_mask_size, uint8_t red_mask_shift,
uint8_t green_mask_size, uint8_t green_mask_shift,
uint8_t blue_mask_size, uint8_t blue_mask_shift,
uint32_t *canvas, /* If nulled, no canvas. */
uint32_t *ansi_colours, uint32_t *ansi_bright_colours, /* If nulled, default. */
uint32_t *default_bg, uint32_t *default_fg, /* If nulled, default. */
uint32_t *default_bg_bright, uint32_t *default_fg_bright, /* If nulled, default. */
/* If font is null, use default font and font_width and font_height ignored. */
void *font, size_t font_width, size_t font_height, size_t font_spacing,
/* If scale_x and scale_y are 0, automatically scale font based on resolution. */
size_t font_scale_x, size_t font_scale_y,
size_t margin,
/* One of FLANTERM_FB_ROTATE_* values. */
int rotation
);
void flanterm_fb_set_flush_callback(struct flanterm_context *ctx, void (*flush_callback)(volatile void *address, size_t length));
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,127 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/* Copyright (C) 2022-2026 Mintsuki and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FLANTERM_FB_PRIVATE_H
#define FLANTERM_FB_PRIVATE_H 1
#ifndef FLANTERM_IN_FLANTERM
#error "Do not use fb_private.h. Use interfaces defined in fb.h only."
#endif
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define FLANTERM_FB_FONT_GLYPHS 256
struct flanterm_fb_char {
uint32_t c;
uint32_t fg;
uint32_t bg;
};
struct flanterm_fb_queue_item {
size_t x, y;
struct flanterm_fb_char c;
};
struct flanterm_fb_context {
struct flanterm_context term;
void (*plot_char)(struct flanterm_context *ctx, struct flanterm_fb_char *c, size_t x, size_t y);
void (*flush_callback)(volatile void *address, size_t length);
size_t font_width;
size_t font_height;
size_t glyph_width;
size_t glyph_height;
size_t font_scale_x;
size_t font_scale_y;
size_t offset_x, offset_y;
volatile uint32_t *framebuffer;
size_t pitch;
size_t width;
size_t height;
size_t phys_height;
size_t bpp;
uint8_t red_mask_size, red_mask_shift;
uint8_t green_mask_size, green_mask_shift;
uint8_t blue_mask_size, blue_mask_shift;
int rotation;
size_t font_bits_size;
uint8_t *font_bits;
size_t font_bool_size;
bool *font_bool;
uint32_t ansi_colours[8];
uint32_t ansi_bright_colours[8];
uint32_t default_fg, default_bg;
uint32_t default_fg_bright, default_bg_bright;
size_t canvas_size;
uint32_t *canvas;
size_t grid_size;
size_t queue_size;
size_t map_size;
struct flanterm_fb_char *grid;
struct flanterm_fb_queue_item *queue;
size_t queue_i;
struct flanterm_fb_queue_item **map;
uint32_t text_fg;
uint32_t text_bg;
size_t cursor_x;
size_t cursor_y;
uint32_t saved_state_text_fg;
uint32_t saved_state_text_bg;
size_t saved_state_cursor_x;
size_t saved_state_cursor_y;
size_t old_cursor_x;
size_t old_cursor_y;
};
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,133 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/* Copyright (C) 2022-2026 Mintsuki and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FLANTERM_PRIVATE_H
#define FLANTERM_PRIVATE_H 1
#ifndef FLANTERM_IN_FLANTERM
#error "Do not use flanterm_private.h. Use interfaces defined in flanterm.h only."
#endif
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define FLANTERM_MAX_ESC_VALUES 16
struct flanterm_context {
/* internal use */
size_t tab_size;
bool autoflush;
bool cursor_enabled;
bool scroll_enabled;
bool wrap_enabled;
bool origin_mode;
bool control_sequence;
bool escape;
bool osc;
bool osc_escape;
size_t osc_buf_i;
uint8_t osc_buf[256];
bool rrr;
bool discard_next;
bool bold;
bool bg_bold;
bool reverse_video;
bool dec_private;
bool insert_mode;
bool csi_unhandled;
uint64_t code_point;
size_t unicode_remaining;
uint8_t g_select;
uint8_t charsets[2];
size_t current_charset;
size_t escape_offset;
size_t esc_values_i;
size_t saved_cursor_x;
size_t saved_cursor_y;
size_t current_primary;
size_t current_bg;
size_t scroll_top_margin;
size_t scroll_bottom_margin;
uint32_t esc_values[FLANTERM_MAX_ESC_VALUES];
uint8_t last_printed_char;
bool last_was_graphic;
bool saved_state_bold;
bool saved_state_bg_bold;
bool saved_state_reverse_video;
bool saved_state_origin_mode;
bool saved_state_wrap_enabled;
size_t saved_state_current_charset;
uint8_t saved_state_charsets[2];
size_t saved_state_current_primary;
size_t saved_state_current_bg;
/* to be set by backend */
size_t rows, cols;
void (*raw_putchar)(struct flanterm_context *, uint8_t c);
void (*clear)(struct flanterm_context *, bool move);
void (*set_cursor_pos)(struct flanterm_context *, size_t x, size_t y);
void (*get_cursor_pos)(struct flanterm_context *, size_t *x, size_t *y);
void (*set_text_fg)(struct flanterm_context *, size_t fg);
void (*set_text_bg)(struct flanterm_context *, size_t bg);
void (*set_text_fg_bright)(struct flanterm_context *, size_t fg);
void (*set_text_bg_bright)(struct flanterm_context *, size_t bg);
void (*set_text_fg_rgb)(struct flanterm_context *, uint32_t fg);
void (*set_text_bg_rgb)(struct flanterm_context *, uint32_t bg);
void (*set_text_fg_default)(struct flanterm_context *);
void (*set_text_bg_default)(struct flanterm_context *);
void (*set_text_fg_default_bright)(struct flanterm_context *);
void (*set_text_bg_default_bright)(struct flanterm_context *);
void (*move_character)(struct flanterm_context *, size_t new_x, size_t new_y, size_t old_x, size_t old_y);
void (*scroll)(struct flanterm_context *);
void (*revscroll)(struct flanterm_context *);
void (*swap_palette)(struct flanterm_context *);
void (*save_state)(struct flanterm_context *);
void (*restore_state)(struct flanterm_context *);
void (*double_buffer_flush)(struct flanterm_context *);
void (*full_refresh)(struct flanterm_context *);
void (*deinit)(struct flanterm_context *, void (*)(void *, size_t));
/* to be set by client */
void (*callback)(struct flanterm_context *, uint64_t, uint64_t, uint64_t, uint64_t);
};
void flanterm_context_reinit(struct flanterm_context *ctx);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,3 +1,9 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Framebuffer-based terminal driver
* @license GPL-3.0-only
*/
// Terminal output
/*
There are a couple of bugs here and there but for now I don't care too much
@@ -5,145 +11,64 @@ because this shitty implementation will be replaced one day by Flanterm
(once memory management is okay: paging & kernel malloc)
*/
#include <limine.h>
#include <stddef.h>
#include <kernel.h>
#include "term.h"
#include "config.h"
#include "flanterm.h"
#include "flanterm_backends/fb.h"
#include "mem/heap/kheap.h"
#include "limine.h"
extern struct limine_framebuffer* framebuffer;
// Importing the PSF object file
extern unsigned char _binary_zap_light16_psf_start[];
extern unsigned char _binary_zap_light16_psf_end[];
PSF1_Header* font = (PSF1_Header*)_binary_zap_light16_psf_start;
uint8_t* glyphs = _binary_zap_light16_psf_start + sizeof(PSF1_Header);
#define FONT_WIDTH 8
#define FONT_HEIGHT font->characterSize
// Character cursor
typedef struct
{
unsigned int x;
unsigned int y;
} Cursor;
Cursor cursor = {0};
unsigned char* fb;
int term_init()
{
// Get framebuffer address from Limine struct
if (framebuffer)
{
fb = framebuffer->address;
DEBUG("terminal initialized");
return 0;
}
return -ENOMEM;
}
// These are marked "static" because we don't wanna expose them all around
// AKA they should just be seen here (kind of like private functions in cpp)
static void putpixel(int x, int y, int color)
{
// Depth isn't part of limine_framebuffer attributes so it will be 4
unsigned pos = x*4 + y*framebuffer->pitch;
fb[pos] = color & 255; // blue channel
fb[pos+1] = (color >> 8) & 255; // green
fb[pos+2] = (color >> 16) & 255; // blue
}
static void draw_char(char c, int px, int py, int fg, int bg)
{
uint8_t* glyph = glyphs + ((unsigned char)c * FONT_HEIGHT);
for (size_t y=0; y<FONT_HEIGHT; y++)
{
uint8_t row = glyph[y];
for (size_t x=0; x<8; x++)
{
int color = (row & (0x80 >> x)) ? fg : bg;
putpixel(px+x, py+y, color);
}
}
}
static void erase_char(int px, int py)
{
for (size_t y=0; y<FONT_HEIGHT; y++)
{
for (size_t x=0; x<8; x++)
{
// Black
putpixel(px+x, py+y, 0);
}
}
}
void putchar(char c)
{
if (c == '\n')
{
cursor.x = 0;
cursor.y++;
return;
}
// TODO: Improperly handled.
// When we're on an empty line it should get to the upper line's last character
// NOT just the last position possible; we would need to track the last line's character amount for that
if (c == '\b')
{
if (cursor.x == 0 && cursor.y == 0)
{
// Top-left corner
return;
}
if (cursor.x == 0)
{
cursor.y--;
cursor.x = (framebuffer->width / FONT_WIDTH) -1; // here
}
else {
cursor.x--;
}
int px = cursor.x * FONT_WIDTH;
int py = cursor.y * FONT_HEIGHT;
erase_char(px, py);
return;
}
if ((cursor.x+1)*FONT_WIDTH >= framebuffer->width)
{
cursor.x = 0;
cursor.y++;
}
int px = cursor.x * FONT_WIDTH;
int py = cursor.y * FONT_HEIGHT;
draw_char(c, px, py, WHITE, BLACK);
cursor.x++;
}
extern struct flanterm_context* ft_ctx;
extern struct init_status init;
// Overhead that could be avoided, right? (for printf)
void _putchar(char character)
{
putchar(character);
// TODO: Spinlock here (terminal access)
flanterm_write(ft_ctx, &character, 1);
}
// Debug-printing
void kputs(const char* str)
{
unsigned int i=0;
size_t i=0;
while (str[i] != 0)
{
putchar(str[i]);
_putchar(str[i]);
i++;
}
_putchar('\r');
}
extern struct flanterm_context* ft_ctx;
extern struct boot_context boot_ctx;
void flanterm_free_wrapper(void* ptr, size_t size)
{
(void)size;
kfree(ptr);
}
void term_init()
{
uint32_t bgColor = 0x252525;
ft_ctx = flanterm_fb_init(
kmalloc,
flanterm_free_wrapper,
boot_ctx.fb->address, boot_ctx.fb->width, boot_ctx.fb->height, boot_ctx.fb->pitch,
boot_ctx.fb->red_mask_size, boot_ctx.fb->red_mask_shift,
boot_ctx.fb->green_mask_size, boot_ctx.fb->green_mask_shift,
boot_ctx.fb->blue_mask_size, boot_ctx.fb->blue_mask_shift,
NULL,
NULL, NULL,
&bgColor, NULL,
NULL, NULL,
NULL, 0, 0, 1,
0, 0,
0,
0
);
init.terminal = true;
}

View File

@@ -1,23 +1,14 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Framebuffer-based terminal driver
* @license GPL-3.0-only
*/
#ifndef TERM_H
#define TERM_H
int term_init();
void kputs(const char* str);
void putchar(char c);
enum TermColors
{
BLACK = 0x000000,
WHITE = 0xffffff
};
#define PSF1_FONT_MAGIC 0x0436
typedef struct
{
uint16_t magic;
uint8_t fontMode;
uint8_t characterSize; // height
} PSF1_Header;
void _putchar(char character);
void term_init();
#endif

View File

@@ -1,3 +1,9 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Kernel global macros
* @license GPL-3.0-only
*/
#ifndef KERNEL_H
#define KERNEL_H
@@ -12,12 +18,50 @@ enum ErrorCodes
#include "io/serial/serial.h"
#include "io/term/printf.h"
#include "idt/idt.h"
#include <stdbool.h>
#define DEBUG(log, ...) fctprintf((void*)&skputc, 0, "debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__)
extern volatile uint64_t ticks;
#define DEBUG(log, ...) fctprintf((void*)&skputc, 0, "[%8u] debug: <%s>: " log "\r\n", ticks, __func__, ##__VA_ARGS__)
/* #define DEBUG(log, ...) \
printf("debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__); \
fctprintf((void*)&skputc, 0, "debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__)
*/
#define DIE_DEBUG(str) fctprintf((void*)&skputc, 0, str)
#define CHECK_BIT(var,pos) ((var) & (1<<(pos)))
// printf("debug: [%s]: " log "\n", __FILE__, ##__VA_ARGS__);
void panic(struct cpu_status_t* ctx, const char* str);
void hcf();
void idle();
/* debug */
void debug_stack_trace(unsigned int max_frames);
const char* debug_find_symbol(uintptr_t rip, uintptr_t* offset);
void boot_mem_display();
#define assert(check) do { if(!(check)) hcf(); } while(0)
struct boot_context
{
struct limine_framebuffer* fb;
struct limine_memmap_response* mmap;
struct limine_hhdm_response* hhdm;
struct limine_kernel_address_response* kaddr;
};
// Are these modules initialized yet?
struct init_status
{
bool terminal;
bool serial;
bool keyboard;
bool timer;
};
#endif

View File

@@ -1,3 +1,9 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief PepperOS kernel entry point
* @license GPL-3.0-only
*/
#include <stdbool.h>
#include <stddef.h>
#include <limine.h>
@@ -14,49 +20,49 @@
#include "mem/paging/paging.h"
#include "mem/paging/vmm.h"
#include "mem/heap/kheap.h"
#include "sched/process.h"
#include "sched/scheduler.h"
#include "config.h"
#include "io/term/flanterm.h"
#include "io/term/flanterm_backends/fb.h"
// Limine version used
__attribute__((used, section(".limine_requests")))
static volatile LIMINE_BASE_REVISION(3);
volatile LIMINE_BASE_REVISION(3);
// Framebuffer request
__attribute__((used, section(".limine_requests")))
static volatile struct limine_framebuffer_request framebuffer_request = {
.id = LIMINE_FRAMEBUFFER_REQUEST,
.revision = 0
};
// Memory map request
__attribute__((used, section(".limine_requests")))
static volatile struct limine_memmap_request memmap_request = {
.id = LIMINE_MEMMAP_REQUEST,
.revision = 0
};
// Higher Half Direct Map
__attribute__((used, section(".limine_requests")))
static volatile struct limine_hhdm_request hhdm_request = {
.id = LIMINE_HHDM_REQUEST,
.revision = 0
};
// Executable Address/Kernel Address (find base phys/virt address of kernel)
__attribute__((used, section(".limine_requests")))
static volatile struct limine_kernel_address_request kerneladdr_request = {
.id = LIMINE_KERNEL_ADDRESS_REQUEST,
.revision = 0
};
__attribute__((used, section(".limine_requests_start")))
static volatile LIMINE_REQUESTS_START_MARKER;
__attribute__((used, section(".limine_requests_end")))
static volatile LIMINE_REQUESTS_END_MARKER;
struct limine_framebuffer* framebuffer;
// Panic (should dump registers etc. in the future)
// Halt and catch fire (makes machine stall)
void hcf()
{
CLEAR_INTERRUPTS; for (;;)asm("hlt");
}
// Doing nothing (can be interrupted)
void idle() {SET_INTERRUPTS; for(;;)asm("hlt");}
struct flanterm_context *ft_ctx;
struct boot_context boot_ctx;
struct init_status init = {0};
extern volatile struct limine_framebuffer_request framebuffer_request;
extern volatile struct limine_memmap_request memmap_request;
extern volatile struct limine_hhdm_request hhdm_request;
extern volatile struct limine_kernel_address_request kerneladdr_request;
extern struct process_t* processes_list;
extern struct process_t* current_process;
struct process_t* idle_proc;
bool iran = false;
// Never gets executed although pedicel is scheduled?
void pedicel_main(void* arg)
{
bool iran = true;
// FROM THE NEXT LINE ONWARDS, CANNOT WRITE TO FRAMEBUFFER WITHOUT PAGE FAULT!
//printf("\n\nWelcome to PepperOS! Pedicel speaking.\nNothing left to do, halting the system!");
}
void idle_main(void* arg)
{
for (;;)
{
@@ -64,56 +70,45 @@ void hcf()
}
}
extern uintptr_t kheap_start;
// This is our entry point
void kmain()
{
CLEAR_INTERRUPTS;
if (!LIMINE_BASE_REVISION_SUPPORTED) hcf();
if (framebuffer_request.response == NULL || framebuffer_request.response->framebuffer_count < 1) hcf();
// We should probably grab all the boot info in a boot context struct
// that would be a bit cleaner than this mess
// Get the first framebuffer from the response
framebuffer = framebuffer_request.response->framebuffers[0];
serial_init();
if (memmap_request.response == NULL) hcf();
memmap_display(memmap_request.response);
if (hhdm_request.response == NULL) hcf();
hhdm_display(hhdm_request.response);
if (kerneladdr_request.response == NULL) hcf();
DEBUG("kernel: phys_base=0x%p virt_base=0x%p", kerneladdr_request.response->physical_base, kerneladdr_request.response->virtual_base);
CLEAR_INTERRUPTS;
gdt_init();
idt_init();
timer_init();
SET_INTERRUPTS;
pmm_init(memmap_request.response, hhdm_request.response);
// Populate boot context
boot_ctx.fb = framebuffer_request.response ? framebuffer_request.response->framebuffers[0] : NULL;
boot_ctx.mmap = memmap_request.response ? memmap_request.response : NULL;
boot_ctx.hhdm = hhdm_request.response ? hhdm_request.response : NULL;
boot_ctx.kaddr = kerneladdr_request.response ? kerneladdr_request.response : NULL;
boot_mem_display();
pmm_init(boot_ctx.mmap, boot_ctx.hhdm);
// Remap kernel , HHDM and framebuffer
paging_init(kerneladdr_request.response, framebuffer);
paging_init(boot_ctx.kaddr, boot_ctx.fb);
kheap_init();
void* ptr = kmalloc(10); DEBUG("(KMALLOC TEST) Allocated 10 bytes at 0x%p", ptr);
void* ptr2 = kmalloc(200); DEBUG("(KMALLOC TEST) Allocated 200 bytes at 0x%p", ptr2);
kfree(ptr);
void* ptr3 = kmalloc(5); DEBUG("(KMALLOC TEST) Allocated 5 bytes at 0x%p", ptr3);
vmm_init();
keyboard_init(FR);
term_init();
// Draw something
printf("%s, %s!\n", "Hello", "world");
// Yoohoooooo!
//DEBUG("kernel initialized successfully! hanging... wow=%d", 42);
printf("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non justo a magna bibendum auctor viverra rutrum diam. In hac habitasse platea dictumst. Vestibulum suscipit ipsum eget tortor maximus lobortis. Donec vel ipsum id lacus fringilla bibendum id eget risus. Fusce vestibulum diam sit amet nunc ultricies, nec rutrum nibh congue. Donec fringilla a dui sit amet ullamcorper. Donec pharetra quis tortor id congue. Aliquam erat volutpat. Duis suscipit nulla vel ligula iaculis, in gravida mauris pellentesque. Vestibulum nunc nisl, posuere eu eros et, dictum molestie dolor. Donec posuere laoreet hendrerit. Suspendisse potenti. Proin fringilla vehicula malesuada. Quisque a dui est. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur nec aliquam lacus, at lacinia enim. ");
hcf();
gdt_init();
idt_init();
process_init();
idle_proc = process_create("idle", (void*)idle_main, 0);
struct process_t* pedicel = process_create("pedicel", (void*)pedicel_main, 0);
process_display_list(processes_list);
scheduler_init();
kputs(PEPPEROS_SPLASH);
idle();
}

View File

@@ -1,3 +1,9 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Global Descriptor Table (for legacy reasons)
* @license GPL-3.0-only
*/
#include "gdt.h"
#include <stdint.h>
#include "io/serial/serial.h"

View File

@@ -1,3 +1,9 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Global Descriptor Table (for legacy reasons)
* @license GPL-3.0-only
*/
#ifndef GDT_H
#define GDT_H

View File

@@ -1,8 +1,16 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Kernel heap
* @license GPL-3.0-only
*/
#include "kheap.h"
#include "mem/paging/paging.h"
#include "mem/paging/pmm.h"
#include <stddef.h>
#include <kernel.h>
#include "sched/process.h"
#include "config.h"
extern uint64_t kernel_phys_base;
extern uint64_t kernel_virt_base;
@@ -15,34 +23,43 @@ static uintptr_t end;
// Kernel root table (level 4)
extern uint64_t *kernel_pml4;
static void kheap_map_page()
{
uintptr_t phys = pmm_alloc();
paging_map_page(kernel_pml4, end, phys, PTE_PRESENT | PTE_WRITABLE | PTE_NOEXEC);
end += PAGE_SIZE;
DEBUG("Mapped first kheap page");
}
void kheap_init()
{
kheap_start = ALIGN_UP(kernel_virt_base + KERNEL_SIZE, PAGE_SIZE);
end = kheap_start;
// At least 1 page must be mapped for it to work
kheap_map_page();
size_t heap_pages = ALIGN_UP(KHEAP_SIZE, PAGE_SIZE) / PAGE_SIZE;
DEBUG("Mapping %d kernel heap pages at 0x%p", heap_pages, kheap_start);
uintptr_t current_addr = kheap_start;
// Map/alloc enough pages for heap (KHEAP_SIZE)
for (size_t i=0; i<heap_pages; i++)
{
uintptr_t phys = pmm_alloc();
if (phys == 0)
{
panic(NULL, "Not enough memory available to initialize kernel heap.");
}
paging_map_page(kernel_pml4, current_addr, phys, PTE_PRESENT | PTE_WRITABLE);
current_addr += PAGE_SIZE;
}
end = current_addr;
// Give linked list head its properties
head = (struct heap_block_t*)kheap_start;
head->size = PAGE_SIZE - sizeof(struct heap_block_t);
head->size = (end-kheap_start) - sizeof(struct heap_block_t);
head->free = true;
head->next = NULL;
DEBUG("kheap initialized, head=0x%p, size=%u", head, head->size);
DEBUG("Kernel heap initialized, head=0x%p, size=%u bytes", head, head->size);
}
void* kmalloc(size_t size)
{
// No size, no memory allocated!
if (!size) return NULL;
size = ALIGN(size);
struct heap_block_t* curr = head;
@@ -52,17 +69,16 @@ void* kmalloc(size_t size)
if (curr->free && curr->size >= size)
{
// We split the block if it is big enough
if (curr->size > size + sizeof(struct heap_block_t))
if (curr->size >= size + sizeof(struct heap_block_t) + 16)
{
struct heap_block_t* new_block = (struct heap_block_t*)((uintptr_t)curr + sizeof(struct heap_block_t) + size);
// We have to subtract the size of our block struct
new_block->size = curr->size - size - sizeof(struct heap_block_t);
new_block->free = true;
//struct heap_block_t* new_block = (struct heap_block_t*)((uintptr_t)curr + sizeof(struct heap_block_t) + size);
struct heap_block_t* split = (struct heap_block_t*)((uintptr_t)curr + sizeof(struct heap_block_t) + size);
// Then we chain up the block in the list
new_block->next = curr->next;
curr->next = new_block;
split->size = curr->size - size - sizeof(struct heap_block_t);
split->free = true;
split->next = curr->next;
curr->next = split;
curr->size = size;
}
@@ -74,25 +90,12 @@ void* kmalloc(size_t size)
curr = curr->next;
}
// If we're hear it means we didn't have enough memory
// for the block allocation. So we will allocate more..
uintptr_t old_end = end;
kheap_map_page();
struct heap_block_t* block = (struct heap_block_t*)old_end;
block->size = PAGE_SIZE - sizeof(struct heap_block_t);
block->free = false;
block->next = NULL;
// Put the block at the end of the list
curr = head;
while (curr->next)
{
curr = curr->next;
}
curr->next = block;
return (void*)((uintptr_t)block + sizeof(struct heap_block_t));
// No growing. If we're here it means the initial pool
// wasn't sufficient. Too bad.
DEBUG("Kernel heap is OUT OF MEMORY!");
// if we were terrorists maybe we should panic
// or just wait for others to free stuff?
return NULL;
}
void kfree(void* ptr)
@@ -103,4 +106,25 @@ void kfree(void* ptr)
// Set it free!
struct heap_block_t* block = (struct heap_block_t*)((uintptr_t)ptr - sizeof(struct heap_block_t));
block->free = true;
// merge adjacent free blocks (coalescing)
struct heap_block_t* curr = head;
while (curr && curr->next)
{
if (curr->free && curr->next->free)
{
curr->size += sizeof(*curr) + curr->next->size;
curr->next = curr->next->next;
continue;
}
curr = curr->next;
}
}
// Should alloc enough for a stack (at least 64kb) to be used for a process.
// Should return a pointer to top of the stack (as stack grows DOWNWARDS)
void* kalloc_stack()
{
uint8_t* ptr = kmalloc(PROCESS_STACK_SIZE); // As it's out of kmalloc, stack is already mapped into kernel space
return ptr ? ptr+PROCESS_STACK_SIZE : NULL;
}

View File

@@ -1,3 +1,9 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Kernel heap
* @license GPL-3.0-only
*/
#ifndef KHEAP_H
#define KHEAP_H
@@ -6,21 +12,22 @@
// When the kernel heap is ready, we can alloc our VM object linked list
// and then continue working on the VMM.
// 16MB should be enough for some linked lists
#define KHEAP_SIZE (16*1024*1024)
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
struct heap_block_t
{
size_t size;
bool free;
bool free; // 1byte
uint8_t reserved[7]; // (7+1 = 8 bytes)
struct heap_block_t* next;
};
} __attribute__((aligned(16)));
void kheap_init();
void* kmalloc(size_t size);
void kfree(void* ptr);
void* kalloc_stack();
void kheap_map_page();
#endif

View File

@@ -1,3 +1,9 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Common memory utilities
* @license GPL-3.0-only
*/
#include <stddef.h>
#include <stdint.h>
#include <limine.h>
@@ -71,52 +77,3 @@ int memcmp(const void* s1, const void* s2, size_t n)
return 0;
}
// Display the memmap so we see how the memory is laid out at handoff
void memmap_display(struct limine_memmap_response* response)
{
DEBUG("Got memory map from Limine: revision %u, %u entries", response->revision, response->entry_count);
for (size_t i=0; i<response->entry_count; i++)
{
struct limine_memmap_entry* entry = response->entries[i];
char type[32] = {0};
switch(entry->type)
{
case LIMINE_MEMMAP_USABLE:
strcpy(type, "USABLE");
break;
case LIMINE_MEMMAP_RESERVED:
strcpy(type, "RESERVED");
break;
case LIMINE_MEMMAP_ACPI_RECLAIMABLE:
strcpy(type, "ACPI_RECLAIMABLE");
break;
case LIMINE_MEMMAP_ACPI_NVS:
strcpy(type, "ACPI_NVS");
break;
case LIMINE_MEMMAP_BAD_MEMORY:
strcpy(type, "BAD_MEMORY");
break;
case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE:
strcpy(type, "BOOTLOADER_RECLAIMABLE");
break;
case LIMINE_MEMMAP_KERNEL_AND_MODULES:
strcpy(type, "KERNEL_AND_MODULES");
break;
case LIMINE_MEMMAP_FRAMEBUFFER:
strcpy(type, "FRAMEBUFFER");
break;
default:
strcpy(type, "UNKNOWN");
break;
}
DEBUG("entry %02u: [0x%016x | %016u bytes] - %s", i, entry->base, entry->length, type);
}
}
// Display the HHDM
void hhdm_display(struct limine_hhdm_response* hhdm)
{
DEBUG("Got HHDM revision=%u offset=0x%p", hhdm->revision, hhdm->offset);
}

View File

@@ -1,3 +1,9 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Common memory utilities
* @license GPL-3.0-only
*/
#ifndef MEM_UTILS_H
#define MEM_UTILS_H
@@ -8,6 +14,7 @@ void* memset(void* s, int c, size_t n);
void* memmove(void *dest, const void* src, size_t n);
int memcmp(const void* s1, const void* s2, size_t n);
// DEBUG
void memmap_display(struct limine_memmap_response* response);
void hhdm_display(struct limine_hhdm_response* hhdm);

View File

@@ -1,8 +1,15 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief x64 4-level paging implementation
* @license GPL-3.0-only
*/
#include "paging.h"
#include "pmm.h"
#include <kernel.h>
#include <stddef.h>
#include <limine.h>
#include "config.h"
/*
Paging on x86 uses four different page table levels:
@@ -17,7 +24,7 @@ If we use 1GB huge pages: PML4 -> PDPT -> 1gb pages
4KB (regular size): PML4 -> PDPT -> PD -> PT -> 4kb pages
*/
static inline void load_cr3(uint64_t value) {
void load_cr3(uint64_t value) {
asm volatile ("mov %0, %%cr3" :: "r"(value) : "memory");
}
@@ -69,7 +76,7 @@ void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_
root_table[pml4_i] = VIRT_TO_PHYS(pdpt) | PTE_PRESENT | PTE_WRITABLE;
}
else {
pdpt = (uint64_t *)PHYS_TO_VIRT(root_table[pml4_i] & ~0xFFFULL);
pdpt = (uint64_t *)PHYS_TO_VIRT(root_table[pml4_i] & PTE_ADDR_MASK);
}
// PDPT: same here
@@ -79,7 +86,7 @@ void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_
pdpt[pdpt_i] = VIRT_TO_PHYS(pd) | PTE_PRESENT | PTE_WRITABLE;
}
else {
pd = (uint64_t *)PHYS_TO_VIRT(pdpt[pdpt_i] & ~0xFFFULL);
pd = (uint64_t *)PHYS_TO_VIRT(pdpt[pdpt_i] & PTE_ADDR_MASK);
}
// PD: and here
@@ -89,7 +96,7 @@ void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_
pd[pd_i] = VIRT_TO_PHYS(pt) | PTE_PRESENT | PTE_WRITABLE;
}
else {
pt = (uint64_t *)PHYS_TO_VIRT(pd[pd_i] & ~0xFFFULL);
pt = (uint64_t *)PHYS_TO_VIRT(pd[pd_i] & PTE_ADDR_MASK);
}
// PT: finally, populate the page table entry
@@ -102,13 +109,16 @@ void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_
uint64_t kernel_phys_base;
uint64_t kernel_virt_base;
void paging_init(struct limine_kernel_address_response* kaddr, struct limine_framebuffer* fb)
extern struct boot_context boot_ctx;
void paging_init()
{
// We should map the kernel, GDT, IDT, stack, framebuffer.
// Optionally we could map ACPI tables (we can find them in the Limine memmap)
kernel_phys_base = kaddr->physical_base;
kernel_virt_base = kaddr->virtual_base;
kernel_phys_base = boot_ctx.kaddr->physical_base;
kernel_virt_base = boot_ctx.kaddr->virtual_base;
struct limine_framebuffer* fb = boot_ctx.fb;
DEBUG("Kernel lives at virt=0x%p phys=0x%p", kernel_virt_base, kernel_phys_base);
@@ -117,14 +127,38 @@ void paging_init(struct limine_kernel_address_response* kaddr, struct limine_fra
// for debug
uint64_t page_count = 0;
// HHDM map first 1 GB using given offset
for (uint64_t i=0; i<0x40000000; i += PAGE_SIZE)
// Find max physical address from limine memmap
uint64_t max_phys = 0;
for (uint64_t i=0; i<boot_ctx.mmap->entry_count; i++)
{
struct limine_memmap_entry* entry = boot_ctx.mmap->entries[i];
if (entry->length == 0)
{
continue;
}
uint64_t top = entry->base + entry->length;
if (top > max_phys)
{
max_phys = top;
}
//DEBUG("max_phys=0x%p", max_phys);
}
// 4GB
if (max_phys > 0x100000000)
{
DEBUG("WARNING: max_phys capped to 4GB (0x100000000) (from max_phys=%p)", max_phys);
max_phys = 0x100000000;
}
// HHDM map up to max_phys or 4GB, whichever is smaller, using given offset
for (uint64_t i=0; i<max_phys; i += PAGE_SIZE)
{
//paging_kmap_page(i+hhdm_off, i, PTE_WRITABLE);
paging_map_page(kernel_pml4, i+hhdm_off, i, PTE_WRITABLE);
paging_map_page(kernel_pml4, i+hhdm_off, i, PTE_WRITABLE | PTE_PRESENT);
page_count++;
}
DEBUG("Mapped %u pages for first 1GB (HHDM)", page_count); page_count = 0;
DEBUG("Mapped %u pages up to 0x%p (HHDM)", page_count, max_phys); page_count = 0;
// Map the kernel (according to virt/phys_base given by Limine)
// SOME DAY when we want a safer kernel we should map .text as Read/Exec
@@ -153,6 +187,14 @@ void paging_init(struct limine_kernel_address_response* kaddr, struct limine_fra
}
DEBUG("Mapped %u pages for framebuffer", page_count);
// test for flanterm
// When 10 pages are mapped, SOMETIMES (1 out of 50 times) it prints everything without problem!
// Other times it prints garbage (almost full cursors) and/or panics.
/* for (uint64_t i=0; i<10; i++)
{
paging_map_page(kernel_pml4, 0, kernel_phys_base+KERNEL_SIZE+i*PAGE_SIZE, PTE_WRITABLE);
} */
// Finally, we load the physical address of our PML4 (root table) into cr3
load_cr3(VIRT_TO_PHYS(kernel_pml4));
DEBUG("cr3 loaded, we're still alive");

View File

@@ -1,24 +1,37 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief x64 4-level paging implementation
* @license GPL-3.0-only
*/
#ifndef PAGING_H
#define PAGING_H
#define PAGE_SIZE 4096
#define BITS_PER_ROW 64
#include <stdint.h>
#include <limine.h>
#include "mem/heap/kheap.h"
void paging_init(struct limine_kernel_address_response* kaddr, struct limine_framebuffer* fb);
void paging_init();
void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags);
// To swap root page tables
void load_cr3(uint64_t value);
extern uint64_t hhdm_off;
#define PHYS_TO_VIRT(x) ((void*)((uintptr_t)(x) + hhdm_off))
#define VIRT_TO_PHYS(x) ((uintptr_t)(x) - hhdm_off)
#define PTE_ADDR_MASK 0x000FFFFFFFFFF000
// Stole it
#define ALIGN_UP(x, align) (((x) + ((align) - 1)) & ~((align) - 1))
#define ALIGN_DOWN(x, align) ((x) & ~((align) - 1))
#define PAGE_ALIGN_DOWN(x) ((x) & ~0xFFFULL)
#define PAGE_ALIGN_DOWN(x) ((x) & PTE_ADDR_MASK)
#define ALIGN(size) ALIGN_UP(size, 16)
#define BLOCK_MIN_SIZE (sizeof(struct heap_block_t) + 16)
#define PML4_INDEX(x) (((x) >> 39) & 0x1FF)
#define PDPT_INDEX(x) (((x) >> 30) & 0x1FF)
@@ -35,10 +48,4 @@ extern uint64_t hhdm_off;
#define PTE_HUGE (1ULL << 7)
#define PTE_NOEXEC (1ULL << 63)
// Specified in linker.ld
#define KERNEL_BASE 0xFFFFFFFF80000000ULL
// 2 MB should be enough (as of now, the whole kernel ELF is around 75kb)
#define KERNEL_SIZE 0x200000
#endif

View File

@@ -1,4 +1,8 @@
// OMG here we are. I'm cooked.
/*
* @author xamidev <xamidev@riseup.net>
* @brief Physical memory manager from freelist
* @license GPL-3.0-only
*/
/*
pmm - Physical Memory Manager
@@ -62,7 +66,10 @@ static uintptr_t g_freelist = 0;
uintptr_t pmm_alloc()
{
if (!g_freelist) return 0;
if (!g_freelist)
{
panic(NULL, "PMM is out of memory!");
}
uintptr_t addr = g_freelist;
g_freelist = *(uintptr_t*) PHYS_TO_VIRT(g_freelist);
return addr;

View File

@@ -1,3 +1,9 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Physical memory manager from freelist
* @license GPL-3.0-only
*/
#ifndef PAGING_PMM_H
#define PAGING_PMM_H

View File

@@ -1,3 +1,9 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Virtual memory manager
* @license GPL-3.0-only
*/
/*
The VMM (virtual memory manager) will have two roles:
- mapping pages
@@ -49,7 +55,7 @@ void vmm_setup_pt_root()
DEBUG("VMM setup: vmm_pt_root=0x%p (phys=0x%p)", vmm_pt_root, phys);
}
void* vmm_alloc(size_t length, size_t flags)
/* void* vmm_alloc(size_t length, size_t flags)
{
// We will try to allocate at least length bytes, which have to be rounded UP to
// the next page so its coherent with the PMM
@@ -58,9 +64,10 @@ void* vmm_alloc(size_t length, size_t flags)
// Need to implement this (as linked list)
// but for now kernel heap is sufficient
// The VMM will prob be more useful when we have userspace
}
} */
void vmm_init()
{
vmm_setup_pt_root();
// NO U
//vmm_setup_pt_root();
}

View File

@@ -1,3 +1,9 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Virtual memory manager
* @license GPL-3.0-only
*/
#ifndef VMM_H
#define VMM_H

162
src/sched/process.c Normal file
View File

@@ -0,0 +1,162 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Process linked list implementation
* @license GPL-3.0-only
*/
#include <stddef.h>
#include "process.h"
#include "mem/heap/kheap.h"
#include "kernel.h"
#include "string/string.h"
#include "mem/gdt/gdt.h"
#include "config.h"
#include "io/serial/serial.h"
#include "io/term/flanterm.h"
extern struct flanterm_context* ft_ctx;
struct process_t* processes_list;
struct process_t* current_process;
extern uint64_t *kernel_pml4;
size_t next_free_pid = 0;
void process_init()
{
processes_list = NULL;
current_process = NULL;
}
// Only for debug
void process_display_list(struct process_t* processes_list)
{
int process_view_id = 0;
struct process_t* tmp = processes_list;
while (tmp != NULL)
{
DEBUG("{%d: %p} -> ", process_view_id, tmp);
tmp = tmp->next;
process_view_id++;
}
DEBUG("NULL");
}
struct process_t* process_create(char* name, void(*function)(void*), void* arg)
{
CLEAR_INTERRUPTS;
struct process_t* proc = (struct process_t*)kmalloc(sizeof(struct process_t));
struct cpu_status_t* ctx = (struct cpu_status_t*)kmalloc(sizeof(struct cpu_status_t));
// No more memory?
if (!proc) return NULL;
if (!ctx) return NULL;
strncpy(proc->name, name, PROCESS_NAME_MAX);
proc->pid = next_free_pid++;
proc->status = READY;
uint64_t* stack_top = (uint64_t*)kalloc_stack();
// push return address to the stack so when "ret" hits we jmp to exit instead of idk what
// stack grows DOWNWARDS!!
*(--stack_top) = (uint64_t)process_exit;
proc->context = ctx;
proc->context->iret_ss = KERNEL_DATA_SEGMENT; // process will live in kernel mode
proc->context->iret_rsp = (uint64_t)stack_top;
proc->context->iret_flags = 0x202; //bit 2 and 9 set (Interrupt Flag)
proc->context->iret_cs = KERNEL_CODE_SEGMENT;
proc->context->iret_rip = (uint64_t)function; // beginning of executable code
proc->context->rdi = (uint64_t)arg; // 1st arg is in rdi (as per x64 calling convention)
proc->context->rbp = 0;
// Kernel PML4 as it already maps code/stack (when switching to userland we'll have to change that)
proc->root_page_table = kernel_pml4;
proc->next = 0;
process_add(&processes_list, proc);
SET_INTERRUPTS;
return proc;
}
void process_add(struct process_t** processes_list, struct process_t* process)
{
if (!process) return;
process->next = NULL;
if (*processes_list == NULL)
{
// List is empty
*processes_list = process;
return;
}
struct process_t* tmp = *processes_list;
while (tmp->next != NULL)
{
tmp = tmp->next;
}
// We're at last process before NULL
tmp->next = process;
// process->next = NULL;
}
void process_delete(struct process_t** processes_list, struct process_t* process)
{
if (!processes_list || !*processes_list || !process) return;
if (*processes_list == process)
{
// process to delete is at head
*processes_list = process->next;
process->next = NULL;
kfree(process);
return;
}
struct process_t* tmp = *processes_list;
while (tmp->next && tmp->next != process)
{
tmp = tmp->next;
}
if (tmp->next == NULL)
{
// Didn't find the process
return;
}
// We're at process before the one we want to delete
tmp->next = process->next;
process->next = NULL;
kfree(process);
}
struct process_t* process_get_next(struct process_t* process)
{
if (!process) return NULL;
return process->next;
}
// Will be used to clean up resources (if any, when we implement it)
// Just mark as DEAD then idle. Scheduler will delete process at next timer interrupt % quantum.
void process_exit()
{
DEBUG("Exiting from process '%s'", current_process->name);
CLEAR_INTERRUPTS;
if (current_process)
{
current_process->status = DEAD;
}
SET_INTERRUPTS;
//outb(0x20, 0x20);
for (;;)
{
asm("hlt");
}
}

41
src/sched/process.h Normal file
View File

@@ -0,0 +1,41 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Process definition
* @license GPL-3.0-only
*/
#ifndef PROCESS_H
#define PROCESS_H
#include <stddef.h>
#include "config.h"
#include <stdint.h>
typedef enum
{
READY,
RUNNING,
DEAD
} status_t;
struct process_t
{
size_t pid;
char name[PROCESS_NAME_MAX];
status_t status;
struct cpu_status_t* context;
void* root_page_table; // Process PML4 (should contain kernel PML4 in higher half [256-511]
struct process_t* next;
};
void process_init();
struct process_t* process_create(char* name, void(*function)(void*), void* arg);
void process_add(struct process_t** processes_list, struct process_t* process);
void process_delete(struct process_t** processes_list, struct process_t* process);
struct process_t* process_get_next(struct process_t* process);
void process_exit();
void process_display_list(struct process_t* processes_list);
#endif

67
src/sched/scheduler.c Normal file
View File

@@ -0,0 +1,67 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Round-robin scheduler
* @license GPL-3.0-only
*/
#include "kernel.h"
#include "process.h"
#include "mem/paging/paging.h"
#include <stdint.h>
#include "io/serial/serial.h"
extern struct process_t* processes_list;
extern struct process_t* current_process;
extern struct process_t* idle_proc;
void scheduler_init()
{
// Choose first process?
current_process = processes_list;
}
struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context)
{
if (context == NULL)
{
panic(NULL, "Scheduler called with NULL context");
}
if (current_process == NULL)
{
// If no more processes, then set IDLE as the current process, that's it.
current_process = idle_proc;
}
current_process->context = context;
//current_process->status = READY;
for (;;) {
struct process_t* prev_process = current_process;
if (current_process->next != NULL)
{
current_process = current_process->next;
} else
{
current_process = processes_list;
}
if (current_process != NULL && current_process->status == DEAD)
{
process_delete(&prev_process, current_process);
current_process = NULL;
return idle_proc->context;
} else
{
current_process->status = RUNNING;
break;
}
}
DEBUG("current_process={pid=%u, name='%s', root_page_table[virt]=%p}", current_process->pid, current_process->name, current_process->root_page_table);
load_cr3(VIRT_TO_PHYS((uint64_t)current_process->root_page_table));
DEBUG("loaded process pml4 into cr3");
return current_process->context;
}

13
src/sched/scheduler.h Normal file
View File

@@ -0,0 +1,13 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Round-robin scheduler
* @license GPL-3.0-only
*/
#ifndef SCHEDULER_H
#define SCHEDULER_H
struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context);
void scheduler_init();
#endif

View File

@@ -1,6 +1,32 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief String manipulation utilities
* @license GPL-3.0-only
*/
#include <stddef.h>
char* strcpy(char *dest, const char *src)
{
char *temp = dest;
while((*dest++ = *src++));
return temp;
}
// https://stackoverflow.com/questions/2488563/strcat-implementation
char *strcat(char *dest, const char *src){
size_t i,j;
for (i = 0; dest[i] != '\0'; i++)
;
for (j = 0; src[j] != '\0'; j++)
dest[i+j] = src[j];
dest[i+j] = '\0';
return dest;
}
// https://stackoverflow.com/questions/14159625/implementation-of-strncpy
void strncpy(char* dst, const char* src, size_t n)
{
size_t i = 0;
while(i++ != n && (*dst++ = *src++));
}

View File

@@ -1,6 +1,14 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief String manipulation functions
* @license GPL-3.0-only
*/
#ifndef STRING_H
#define STRING_H
char *strcpy(char *dest, const char *src);
char *strcat(char *dest, const char *src);
void strncpy(char* dst, const char* src, size_t n);
#endif

View File

@@ -1,3 +1,9 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief Programmable Interval Timer init and enabling
* @license GPL-3.0-only
*/
#include <stdint.h>
#include "io/serial/serial.h"
#include <kernel.h>
@@ -12,6 +18,8 @@ interested in multi-core functionnality like SMP)
volatile uint64_t ticks = 0;
extern struct init_status init;
void pic_remap()
{
uint8_t master_mask = inb(0x21);
@@ -44,7 +52,7 @@ void pic_enable()
// Enabling IRQ0 (unmasking it) but not the others
uint8_t mask = inb(0x21);
mask &= ~(1 << 0); // Set IRQ0 (timer, clear bit 0)
mask &= ~(1 << 1); // Set IRQ1 (PS/2 Keyboard, clear bit 1)
//mask &= ~(1 << 1); // Set IRQ1 (PS/2 Keyboard, clear bit 1)
outb(0x21, mask);
}
@@ -65,6 +73,17 @@ void pit_init()
outb(0x40, (divisor >> 8) & 0xFF);
}
// Wait n ticks
// Given that there's a tick every 1ms, wait n milliseconds
void timer_wait(uint64_t wait_ticks)
{
uint64_t then = ticks + wait_ticks;
while (ticks < then)
{
asm("hlt");
};
}
void timer_init()
{
// Remapping the PIC, because at startup it conflicts with
@@ -74,4 +93,5 @@ void timer_init()
pic_enable();
pit_init();
DEBUG("PIT initialized");
init.timer = true;
}

View File

@@ -1,6 +1,13 @@
/*
* @author xamidev <xamidev@riseup.net>
* @brief PIT functions
* @license GPL-3.0-only
*/
#ifndef TIMER_H
#define TIMER_H
void timer_init();
void timer_wait(unsigned int wait_ticks);
#endif

33
symbols.py Normal file
View File

@@ -0,0 +1,33 @@
# Make assembly file from ELF symbols map
# Then link it to kernel so it's aware of symbol names
# then we can use that for the stack trace.
print("Extracting symbols from map file to assembly...")
with open("symbols.map", "r") as f:
lines = f.readlines()
symbols = []
for line in lines:
parts = line.split()
# output is formed like "address name"
symbols.append((parts[0], parts[1]))
with open("symbols.S", "w") as f:
f.write("section .rodata\n")
f.write("global symbol_table\n")
f.write("global symbol_count\n")
f.write("symbol_table:\n")
for i, (addr, name) in enumerate(symbols):
f.write(f" dq 0x{addr}\n")
f.write(f" dq sym_name_{i}\n")
f.write("\nsymbol_count: dq " + str(len(symbols)) + "\n\n")
for i, (addr, name) in enumerate(symbols):
# escaping quotes
safe_name = name.replace('"', '\\"')
f.write(f'sym_name_{i}: db "{safe_name}", 0\n')
print("Done!")

1
test.bin Normal file
View File

@@ -0,0 +1 @@
<EFBFBD>ュ゙<EFBFBD>

Binary file not shown.