7 Commits

Author SHA1 Message Date
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
10 changed files with 243 additions and 76 deletions

View File

@@ -2,7 +2,7 @@ SOURCES = src/mem/heap/kheap.c src/mem/paging/vmm.c src/mem/paging/paging.c src/
build: build:
rm -f *.o 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 x86_64-elf-gcc -g -c -Isrc $(SOURCES) -Wall -Wextra -std=gnu99 -nostdlib -ffreestanding -fno-stack-protector -fno-omit-frame-pointer -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 objcopy -O elf64-x86-64 -B i386 -I binary zap-light16.psf zap-light16.o
nasm -f elf64 src/idt/idt.S -o idt_stub.o nasm -f elf64 src/idt/idt.S -o idt_stub.o
x86_64-elf-ld -o pepperk -T linker.ld *.o x86_64-elf-ld -o pepperk -T linker.ld *.o

View File

@@ -4,6 +4,7 @@
#include "io/serial/serial.h" #include "io/serial/serial.h"
#include "io/kbd/ps2.h" #include "io/kbd/ps2.h"
#include <kernel.h> #include <kernel.h>
#include <stdbool.h>
struct interrupt_descriptor idt[256]; struct interrupt_descriptor idt[256];
struct idtr idt_reg; struct idtr idt_reg;
@@ -53,6 +54,64 @@ void idt_init()
DEBUG("IDT initialized"); 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);
/* if (CHECK_BIT(ctx->error_code, 0))
{
panic(ctx);
} */
panic(ctx);
}
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);
}
struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context) struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
{ {
switch(context->vector_number) switch(context->vector_number)
@@ -97,10 +156,11 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
DEBUG("Stack-Segment Fault!"); DEBUG("Stack-Segment Fault!");
break; break;
case 13: case 13:
DEBUG("General Protection Fault!"); gp_fault_handler(context);
break; break;
case 14: case 14:
DEBUG("Page Fault!"); // Better debugging for page faults...
page_fault_handler(context);
break; break;
case 15: case 15:
DEBUG("Intel Reserved Interrupt! (Achievement unlocked: How Did We Get Here?)"); DEBUG("Intel Reserved Interrupt! (Achievement unlocked: How Did We Get Here?)");
@@ -125,13 +185,14 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
break; break;
case 32: case 32:
//DEBUG("Tick!"); //DEBUG("Timer Interrupt");
ticks++; ticks++;
// Send an EOI so that we can continue having interrupts // Send an EOI so that we can continue having interrupts
outb(0x20, 0x20); outb(0x20, 0x20);
break; break;
case 33: case 33:
DEBUG("Keyboard Interrupt");
keyboard_handler(); keyboard_handler();
break; break;

View File

@@ -201,12 +201,11 @@ void keyboard_handler()
if (c) if (c)
{ {
// Should probably have a keyboard buffer here... instead of this
putchar(c); putchar(c);
} }
} }
} }
skputs("key pressed!\n");
} }
// End of Interrupt (to master PIC) // End of Interrupt (to master PIC)
@@ -233,5 +232,11 @@ void keyboard_init(unsigned char layout)
skputs("Unsupported layout."); skputs("Unsupported layout.");
return; return;
} }
// Unmask IRQ1
uint8_t mask = inb(0x21);
mask &= ~(1 << 1);
outb(0x21, mask);
DEBUG("PS/2 Keyboard initialized"); DEBUG("PS/2 Keyboard initialized");
} }

View File

@@ -9,8 +9,9 @@ because this shitty implementation will be replaced one day by Flanterm
#include <stddef.h> #include <stddef.h>
#include <kernel.h> #include <kernel.h>
#include "term.h" #include "term.h"
#include "mem/misc/utils.h"
extern struct limine_framebuffer* framebuffer; extern struct boot_context boot_ctx;
// Importing the PSF object file // Importing the PSF object file
extern unsigned char _binary_zap_light16_psf_start[]; extern unsigned char _binary_zap_light16_psf_start[];
@@ -22,61 +23,88 @@ uint8_t* glyphs = _binary_zap_light16_psf_start + sizeof(PSF1_Header);
#define FONT_WIDTH 8 #define FONT_WIDTH 8
#define FONT_HEIGHT font->characterSize #define FONT_HEIGHT font->characterSize
// Character cursor // Character cursor
typedef struct typedef struct
{ {
unsigned int x; size_t x;
unsigned int y; size_t y;
} Cursor; } Cursor;
Cursor cursor = {0}; static Cursor cursor = {0, 0};
unsigned char* fb; static uint8_t* fb;
static struct limine_framebuffer* framebuffer;
uint8_t lines_length[MAX_LINES];
static inline size_t term_max_cols(void)
{
return framebuffer->width / FONT_WIDTH;
}
static inline size_t term_max_lines(void)
{
return framebuffer->height / FONT_HEIGHT;
}
int term_init() int term_init()
{ {
// Get framebuffer address from Limine struct // Get framebuffer address from Limine struct
if (framebuffer) if (!boot_ctx.fb)
{ {
fb = framebuffer->address; return -ENOMEM;
DEBUG("terminal initialized");
return 0;
} }
return -ENOMEM;
framebuffer = boot_ctx.fb;
fb = (uint8_t*)framebuffer->address;
memset(lines_length, 0, sizeof(lines_length));
DEBUG("terminal initialized, fb=0x%p (width=%u height=%u pitch=%u bpp=%u)", fb, framebuffer->width, framebuffer->height, framebuffer->pitch, framebuffer->bpp);
return 0;
} }
// These are marked "static" because we don't wanna expose them all around // 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) // AKA they should just be seen here (kind of like private functions in cpp)
static void putpixel(int x, int y, int color) static inline void putpixel(size_t x, size_t y, uint32_t color)
{ {
// Guard so we don't write past fb boundaries
if (x >= framebuffer->width || y >= framebuffer->height) return;
// Depth isn't part of limine_framebuffer attributes so it will be 4 // Depth isn't part of limine_framebuffer attributes so it will be 4
unsigned pos = x*4 + y*framebuffer->pitch; size_t pos = x*4 + y*framebuffer->pitch;
fb[pos] = color & 255; // blue channel fb[pos] = color & 255; // blue channel
fb[pos+1] = (color >> 8) & 255; // green fb[pos+1] = (color >> 8) & 255; // green
fb[pos+2] = (color >> 16) & 255; // blue fb[pos+2] = (color >> 16) & 255; // blue
} }
static void draw_char(char c, int px, int py, int fg, int bg) static void draw_char(char c, size_t px, size_t py, uint32_t fg, uint32_t bg)
{ {
// So we cannot write past fb
if (px+FONT_WIDTH > framebuffer->width || py+FONT_HEIGHT > framebuffer->height) return;
uint8_t* glyph = glyphs + ((unsigned char)c * FONT_HEIGHT); uint8_t* glyph = glyphs + ((unsigned char)c * FONT_HEIGHT);
for (size_t y=0; y<FONT_HEIGHT; y++) for (size_t y=0; y<FONT_HEIGHT; y++)
{ {
uint8_t row = glyph[y]; uint8_t row = glyph[y];
for (size_t x=0; x<8; x++) for (size_t x=0; x<FONT_WIDTH; x++)
{ {
int color = (row & (0x80 >> x)) ? fg : bg; uint32_t color = (row & (0x80 >> x)) ? fg : bg;
putpixel(px+x, py+y, color); putpixel(px+x, py+y, color);
} }
} }
} }
static void erase_char(int px, int py) static void erase_char(size_t px, size_t py)
{ {
if (px+FONT_WIDTH > framebuffer->width || py+FONT_HEIGHT > framebuffer->height) return;
for (size_t y=0; y<FONT_HEIGHT; y++) for (size_t y=0; y<FONT_HEIGHT; y++)
{ {
for (size_t x=0; x<8; x++) for (size_t x=0; x<FONT_WIDTH; x++)
{ {
// Black // Black
putpixel(px+x, py+y, 0); putpixel(px+x, py+y, 0);
@@ -84,50 +112,81 @@ static void erase_char(int px, int py)
} }
} }
void term_scroll()
{
// Erase first text line
memset(fb, 255, FONT_HEIGHT*framebuffer->pitch);
// Move whole framebuffer up by one text line
memmove(fb, fb+(FONT_HEIGHT*framebuffer->pitch), (framebuffer->height-FONT_HEIGHT)*framebuffer->pitch);
// Clear last text line
size_t clear_start = (framebuffer->height - FONT_HEIGHT) * framebuffer->pitch;
memset(fb + clear_start, 255, FONT_HEIGHT * framebuffer->pitch);
// Shift line lengths by 1 (for backspace handling)
size_t max_lines = term_max_lines();
for (size_t i = 1; i < max_lines; i++)
{
lines_length[i - 1] = lines_length[i];
}
lines_length[max_lines - 1] = 0;
}
void putchar(char c) void putchar(char c)
{ {
if (c == '\n') const size_t max_cols = term_max_cols();
{ const size_t max_lines = term_max_lines();
if (c == '\n') {
lines_length[cursor.y] = cursor.x;
cursor.x = 0; cursor.x = 0;
cursor.y++;
if (cursor.y + 1 >= max_lines)
{
term_scroll();
}
else
{
cursor.y++;
}
return; 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 (c == '\b')
{ {
if (cursor.x == 0 && cursor.y == 0) if (cursor.x > 0)
{
cursor.x--;
}
else if (cursor.y > 0)
{
cursor.y--;
cursor.x = lines_length[cursor.y];
}
else
{ {
// Top-left corner
return; return;
} }
if (cursor.x == 0) erase_char(cursor.x * FONT_WIDTH, cursor.y * FONT_HEIGHT);
{
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; return;
} }
if ((cursor.x+1)*FONT_WIDTH >= framebuffer->width) if (cursor.x >= max_cols)
{ {
cursor.x = 0; cursor.x = 0;
cursor.y++; if (cursor.y + 1 >= max_lines)
{
term_scroll();
}
else
{
cursor.y++;
}
} }
int px = cursor.x * FONT_WIDTH; draw_char(c, cursor.x * FONT_WIDTH, cursor.y * FONT_HEIGHT, WHITE, BLACK);
int py = cursor.y * FONT_HEIGHT;
draw_char(c, px, py, WHITE, BLACK);
cursor.x++; cursor.x++;
} }
@@ -140,7 +199,7 @@ void _putchar(char character)
// Debug-printing // Debug-printing
void kputs(const char* str) void kputs(const char* str)
{ {
unsigned int i=0; size_t i=0;
while (str[i] != 0) while (str[i] != 0)
{ {
putchar(str[i]); putchar(str[i]);

View File

@@ -11,6 +11,8 @@ enum TermColors
WHITE = 0xffffff WHITE = 0xffffff
}; };
#define MAX_LINES 256
#define PSF1_FONT_MAGIC 0x0436 #define PSF1_FONT_MAGIC 0x0436
typedef struct typedef struct
@@ -20,4 +22,7 @@ typedef struct
uint8_t characterSize; // height uint8_t characterSize; // height
} PSF1_Header; } PSF1_Header;
// debug
void term_scroll();
#endif #endif

View File

@@ -1,6 +1,10 @@
#ifndef KERNEL_H #ifndef KERNEL_H
#define KERNEL_H #define KERNEL_H
#define PEPPEROS_VERSION_MAJOR "0"
#define PEPPEROS_VERSION_MINOR "0"
#define PEPPEROS_VERSION_PATCH "1"
enum ErrorCodes enum ErrorCodes
{ {
ENOMEM, ENOMEM,
@@ -12,12 +16,24 @@ enum ErrorCodes
#include "io/serial/serial.h" #include "io/serial/serial.h"
#include "io/term/printf.h" #include "io/term/printf.h"
#include "idt/idt.h"
#define DEBUG(log, ...) fctprintf((void*)&skputc, 0, "debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__) #define DEBUG(log, ...) fctprintf((void*)&skputc, 0, "debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__)
#define CHECK_BIT(var,pos) ((var) & (1<<(pos)))
// printf("debug: [%s]: " log "\n", __FILE__, ##__VA_ARGS__); // printf("debug: [%s]: " log "\n", __FILE__, ##__VA_ARGS__);
void panic(struct cpu_status_t* ctx);
void hcf(); void hcf();
#define assert(check) do { if(!(check)) hcf(); } while(0) #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;
};
#endif #endif

View File

@@ -53,8 +53,6 @@ static volatile LIMINE_REQUESTS_START_MARKER;
__attribute__((used, section(".limine_requests_end"))) __attribute__((used, section(".limine_requests_end")))
static volatile LIMINE_REQUESTS_END_MARKER; static volatile LIMINE_REQUESTS_END_MARKER;
struct limine_framebuffer* framebuffer;
// Panic (should dump registers etc. in the future) // Panic (should dump registers etc. in the future)
void hcf() void hcf()
{ {
@@ -64,28 +62,36 @@ void hcf()
} }
} }
void panic(struct cpu_status_t* ctx)
{
DEBUG("\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[0m at rip=%p\nSomething went horribly wrong! vect=0x%.2x errcode=0x%x\nrax=%p rbx=%p rcx=%p rdx=%p\nrsi=%p rdi=%p r8=%p r9=%p\nr10=%p r11=%p r12=%p r13=%p\nr14=%p r15=%p\n\nflags=%p\nstack at rbp=%p\nHalting...",
ctx->iret_rip,
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,
ctx->rbp);
hcf();
}
const char* splash = "pepperOS version "PEPPEROS_VERSION_MAJOR"."PEPPEROS_VERSION_MINOR"."PEPPEROS_VERSION_PATCH"\n";
struct boot_context boot_ctx;
// This is our entry point // This is our entry point
void kmain() void kmain()
{ {
if (!LIMINE_BASE_REVISION_SUPPORTED) hcf(); 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 // Populate boot context
// that would be a bit cleaner than this mess boot_ctx.fb = framebuffer_request.response ? framebuffer_request.response->framebuffers[0] : NULL;
boot_ctx.mmap = memmap_request.response ? memmap_request.response : NULL;
// Get the first framebuffer from the response boot_ctx.hhdm = hhdm_request.response ? hhdm_request.response : NULL;
framebuffer = framebuffer_request.response->framebuffers[0]; boot_ctx.kaddr = kerneladdr_request.response ? kerneladdr_request.response : NULL;
serial_init(); serial_init();
if (memmap_request.response == NULL) hcf(); memmap_display(boot_ctx.mmap);
memmap_display(memmap_request.response); 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);
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; CLEAR_INTERRUPTS;
gdt_init(); gdt_init();
@@ -93,10 +99,10 @@ void kmain()
timer_init(); timer_init();
SET_INTERRUPTS; SET_INTERRUPTS;
pmm_init(memmap_request.response, hhdm_request.response); pmm_init(boot_ctx.mmap, boot_ctx.hhdm);
// Remap kernel , HHDM and framebuffer // Remap kernel , HHDM and framebuffer
paging_init(kerneladdr_request.response, framebuffer); paging_init(boot_ctx.kaddr, boot_ctx.fb);
kheap_init(); kheap_init();
@@ -110,10 +116,13 @@ void kmain()
keyboard_init(FR); keyboard_init(FR);
term_init(); term_init();
// Draw something kputs(splash);
printf("%s, %s!\n", "Hello", "world");
// Yoohoooooo! for (int i=0; i<10; i++)
//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. "); printf("testing, attention please %d\n", i);
}
// term_scroll();
hcf(); hcf();
} }

View File

@@ -49,7 +49,7 @@ void vmm_setup_pt_root()
DEBUG("VMM setup: vmm_pt_root=0x%p (phys=0x%p)", vmm_pt_root, phys); 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 // 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 // the next page so its coherent with the PMM
@@ -58,7 +58,7 @@ void* vmm_alloc(size_t length, size_t flags)
// Need to implement this (as linked list) // Need to implement this (as linked list)
// but for now kernel heap is sufficient // but for now kernel heap is sufficient
// The VMM will prob be more useful when we have userspace // The VMM will prob be more useful when we have userspace
} } */
void vmm_init() void vmm_init()
{ {

View File

@@ -44,7 +44,7 @@ void pic_enable()
// Enabling IRQ0 (unmasking it) but not the others // Enabling IRQ0 (unmasking it) but not the others
uint8_t mask = inb(0x21); uint8_t mask = inb(0x21);
mask &= ~(1 << 0); // Set IRQ0 (timer, clear bit 0) 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); outb(0x21, mask);
} }
@@ -65,6 +65,17 @@ void pit_init()
outb(0x40, (divisor >> 8) & 0xFF); 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() void timer_init()
{ {
// Remapping the PIC, because at startup it conflicts with // Remapping the PIC, because at startup it conflicts with

View File

@@ -2,5 +2,6 @@
#define TIMER_H #define TIMER_H
void timer_init(); void timer_init();
void timer_wait(unsigned int wait_ticks);
#endif #endif