11 Commits

Author SHA1 Message Date
cf4915d9f4 Update README.md 2025-12-30 18:13:53 +01:00
834891fd2a DEBUG fix 2025-12-28 12:32:29 +01:00
3853a1ace3 Efficient DEBUG logging system with __FILE__ and fctprintf 2025-12-28 12:15:32 +01:00
ead0ed6ae1 Folder restructuration 2025-12-28 11:39:39 +01:00
fabe0b1a10 Merge pull request 'kbd' (#6) from kbd into main
Reviewed-on: #6
2025-12-28 11:17:08 +01:00
b886f03f7a Quick backspace fix 2025-12-28 11:14:22 +01:00
4607b5aba5 holy SHIFT 2025-12-28 11:06:33 +01:00
cc36c768cf Shitty broken keyboard driver BUT azerty-compatible 2025-12-28 10:28:17 +01:00
dbd068e55a Update README.md 2025-12-27 15:54:58 +01:00
53fb22cecd Merge pull request #5 from xamidev/time
1000Hz PIC timer working + IDT dispatch/handler fixes
2025-12-27 13:55:00 +01:00
bb556709d8 Update README.md 2025-12-23 11:20:16 +01:00
19 changed files with 418 additions and 53 deletions

View File

@@ -1,6 +1,8 @@
SOURCES = 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
build: build:
rm -f *.o rm -f *.o
x86_64-elf-gcc -g -c -I src src/time/timer.c src/idt/idt.c src/mem/utils.c src/mem/gdt.c src/io/serial.c src/io/term.c src/io/printf.c src/kmain.c -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 -I src $(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 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

@@ -1,4 +1,4 @@
# pepperOS: "will never be done" # <img width="40" height="40" alt="red-pepper" src="https://i.ibb.co/mrHH6d1m/pixil-frame-0-4.png" /> pepperOS: "will never be done"
## Trying the kernel ## Trying the kernel
@@ -7,6 +7,25 @@ First install the dependencies: `sudo apt install xorriso make qemu-system`
Then, to compile the kernel and make an ISO image file: `make build-iso` Then, to compile the kernel and make an ISO image file: `make build-iso`
To run it with QEMU, `make run` To run it with QEMU, `make run`
## TODO
The basics that I'm targeting are:
- 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
- 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
- Getting to userspace (syscalls)
- Porting musl libc or equivalent
In the future, maybe?
- SMP support
- Parsing the ACPI tables and using them for something
- Replacing the PIT timer with APIC
## Thanks ## Thanks
PepperOS wouldn't be possible without the following freely-licensed software: PepperOS wouldn't be possible without the following freely-licensed software:

View File

@@ -301,3 +301,10 @@ vector_32_handler:
push qword 0 push qword 0
push qword 32 push qword 32
jmp interrupt_stub jmp interrupt_stub
; PS/2 Keyboard
align 16
vector_33_handler:
push qword 0
push qword 33
jmp interrupt_stub

View File

@@ -1,7 +1,9 @@
#include "idt.h" #include "idt.h"
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include "../io/serial.h" #include "../io/serial/serial.h"
#include "../io/kbd/ps2.h"
#include <kernel.h>
struct interrupt_descriptor idt[256]; struct interrupt_descriptor idt[256];
struct idtr idt_reg; struct idtr idt_reg;
@@ -48,7 +50,7 @@ void idt_init()
idt_set_entry(i, vector_0_handler + (i*16), 0); idt_set_entry(i, vector_0_handler + (i*16), 0);
} }
idt_load(&idt); idt_load(&idt);
serial_kputs("kernel: idt: Initialized IDT!\n"); DEBUG("IDT initialized");
} }
struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context) struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
@@ -56,81 +58,85 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
switch(context->vector_number) switch(context->vector_number)
{ {
case 0: case 0:
serial_kputs("kernel: idt: Divide Error!\n"); DEBUG("Divide Error!");
break; break;
case 1: case 1:
serial_kputs("kernel: idt: Debug Exception!\n"); DEBUG("Debug Exception!");
break; break;
case 2: case 2:
serial_kputs("kernel: idt: NMI Interrupt!\n"); DEBUG("NMI Interrupt!");
break; break;
case 3: case 3:
serial_kputs("kernel: idt: Breakpoint Interrupt!\n"); DEBUG("Breakpoint Interrupt!");
break; break;
case 4: case 4:
serial_kputs("kernel: idt: Overflow Trap!\n"); DEBUG("Overflow Trap!");
break; break;
case 5: case 5:
serial_kputs("kernel: idt: BOUND Range Exceeded!\n"); DEBUG("BOUND Range Exceeded!");
break; break;
case 6: case 6:
serial_kputs("kernel: idt: Invalid Opcode!\n"); DEBUG("Invalid Opcode!");
break; break;
case 7: case 7:
serial_kputs("kernel: idt: Device Not Available!\n"); DEBUG("Device Not Available!");
break; break;
case 8: case 8:
serial_kputs("kernel: idt: Double Fault!\n"); DEBUG("Double Fault!");
break; break;
case 9: case 9:
serial_kputs("kernel: idt: Coprocessor Segment Overrun!\n"); DEBUG("Coprocessor Segment Overrun!");
break; break;
case 10: case 10:
serial_kputs("kernel: idt: Invalid TSS!\n"); DEBUG("Invalid TSS!");
break; break;
case 11: case 11:
serial_kputs("kernel: idt: Segment Not Present!\n"); DEBUG("Segment Not Present!");
break; break;
case 12: case 12:
serial_kputs("kernel: idt: Stack-Segment Fault!\n"); DEBUG("Stack-Segment Fault!");
break; break;
case 13: case 13:
serial_kputs("kernel: idt: General Protection Fault!\n"); DEBUG("General Protection Fault!");
break; break;
case 14: case 14:
serial_kputs("kernel: idt: Page Fault!\n"); DEBUG("Page Fault!");
break; break;
case 15: case 15:
serial_kputs("kernel: idt: Intel Reserved Interrupt! (Achievement unlocked: How Did We Get Here?)\n"); DEBUG("Intel Reserved Interrupt! (Achievement unlocked: How Did We Get Here?)");
break; break;
case 16: case 16:
serial_kputs("kernel: idt: x87 Floating-Point Error!\n"); DEBUG("x87 Floating-Point Error!");
break; break;
case 17: case 17:
serial_kputs("kernel: idt: Alignment Check Fault!\n"); DEBUG("Alignment Check Fault!");
break; break;
case 18: case 18:
serial_kputs("kernel: idt: Machine Check!\n"); DEBUG("Machine Check!");
break; break;
case 19: case 19:
serial_kputs("kernel: idt: SIMD Floating-Point Exception!\n"); DEBUG("SIMD Floating-Point Exception!");
break; break;
case 20: case 20:
serial_kputs("kernel: idt: Virtualization Exception!\n"); DEBUG("Virtualization Exception!");
break; break;
case 21: case 21:
serial_kputs("kernel: idt: Control Protection Exception!\n"); DEBUG("Control Protection Exception!");
break; break;
case 32: case 32:
serial_kputs("Tick!"); //DEBUG("Tick!");
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:
keyboard_handler();
break;
default: default:
serial_kputs("kernel: idt: Unexpected interrupt\n"); DEBUG("Unexpected interrupt");
break; break;
} }

237
src/io/kbd/ps2.c Normal file
View File

@@ -0,0 +1,237 @@
// PS/2 Keyboard support
#include "../serial/serial.h"
#include "ps2.h"
#include <stdint.h>
#include "../term/term.h"
#include <kernel.h>
// The key status bitfield will be used to see if ALT, CONTROL, or SHIFT is pressed
uint8_t key_status = 0b00000000;
// Keymap pointers so we can change between different layouts
unsigned char* keymap;
unsigned char* keymap_shifted;
unsigned char kbdus[128] =
{
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', /* 9 */
'9', '0', '-', '=', '\b', /* Backspace */
'\t', /* Tab */
'q', 'w', 'e', 'r', /* 19 */
't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', /* Enter key */
CTRL, /* 29 - Control */
'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', /* 39 */
'\'', '`', SHIFT, /* Left shift */
'\\', 'z', 'x', 'c', 'v', 'b', 'n', /* 49 */
'm', ',', '.', '/', SHIFT, /* Right shift */
'*',
ALT, /* Alt */
' ', /* Space bar */
0, /* Caps lock */
0, /* 59 - F1 key ... > */
0, 0, 0, 0, 0, 0, 0, 0,
0, /* < ... F10 */
0, /* 69 - Num lock*/
0, /* Scroll Lock */
0, /* Home key */
0, /* Up Arrow */
0, /* Page Up */
'-',
0, /* Left Arrow */
0,
0, /* Right Arrow */
'+',
0, /* 79 - End key*/
0, /* Down Arrow */
0, /* Page Down */
0, /* Insert Key */
0, /* Delete Key */
0, 0, 0,
0, /* F11 Key */
0, /* F12 Key */
0, /* All other keys are undefined */
};
unsigned char kbdus_shifted[128] =
{
0, 27, '!', '@', '#', '$', '%', '^', '&', '*', /* 9 */
'(', ')', '_', '+', '\b', /* Backspace */
'\t', /* Tab */
'Q', 'W', 'E', 'R', /* 19 */
'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', /* Enter */
CTRL, /* 29 */
'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', /* 39 */
'"', '~', SHIFT, /* Left shift */
'|', 'Z', 'X', 'C', 'V', 'B', 'N', /* 49 */
'M', '<', '>', '?', SHIFT, /* Right shift */
'*',
ALT, /* Alt */
' ', /* Space */
0, /* Caps lock */
0, 0, 0, 0, 0, 0, 0, 0,
0, /* F10 */
0, /* Num lock */
0, /* Scroll lock */
0, 0, 0,
'-',
0, 0, 0,
'+',
0, 0, 0,
0, 0,
0, 0, 0,
0, /* F11 */
0 /* F12 */
};
// NOT THE REAL FR KEYMAP!!
// Some French keys have accents or weird symbols that aren't part of ASCII
// so they won't fit in 1 char. As a substitute for now, these will be
// changed to their ASCII counterparts (without accents, etc.)
unsigned char kbdfr[128] =
{
0, 27, '&', 'e', '"', '\'', '(', '-', 'e', '_',
'c', 'a', ')', '=', '\b',
'\t',
'a', 'z', 'e', 'r',
't', 'y', 'u', 'i', 'o', 'p', '^', '$', '\n',
CTRL,
'q', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm',
'u', '`', SHIFT,
'*', 'w', 'x', 'c', 'v', 'b', 'n',
',', ';', ':', '!', SHIFT,
'*',
ALT,
' ',
0,
0, 0, 0, 0, 0, 0, 0, 0,
0,
0,
0,
0, 0, 0,
'-',
0, 0, 0,
'+',
0, 0, 0,
0, 0,
0, 0, 0,
0,
0
};
unsigned char kbdfr_shifted[128] =
{
0, 27, '1', '2', '3', '4', '5', '6', '7', '8',
'9', '0', '^', '+', '\b',
'\t',
'A', 'Z', 'E', 'R',
'T', 'Y', 'U', 'I', 'O', 'P', '^', 'L', '\n',
CTRL,
'Q', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
'%', '~', SHIFT,
'u', 'W', 'X', 'C', 'V', 'B', 'N',
'?', '.', '/', 'S', SHIFT,
'*',
ALT,
' ',
0,
0, 0, 0, 0, 0, 0, 0, 0,
0,
0,
0,
0, 0, 0,
'-',
0, 0, 0,
'+',
0, 0, 0,
0, 0,
0, 0, 0,
0,
0
};
void keyboard_handler()
{
unsigned char scancode = inb(0x60);
// Key release (bit 7 set)
if (scancode & 0x80)
{
unsigned char code = scancode & 0x7F;
switch (code)
{
// Clear the corresponding bit if corresponding key is released
case LEFT_SHIFT_PRESSED:
case RIGHT_SHIFT_PRESSED:
key_status &= ~SHIFT_PRESSED_BIT;
break;
case CTRL_PRESSED:
key_status &= ~CTRL_PRESSED_BIT;
break;
case ALT_PRESSED:
key_status &= ~ALT_PRESSED_BIT;
break;
}
// Send EOI
outb(0x20, 0x20);
return;
}
else
{
// Key press
switch (scancode)
{
// Set bits for corresponding special key press
case LEFT_SHIFT_PRESSED:
case RIGHT_SHIFT_PRESSED:
key_status |= SHIFT_PRESSED_BIT;
break;
case CTRL_PRESSED:
key_status |= CTRL_PRESSED_BIT;
break;
case ALT_PRESSED:
key_status |= ALT_PRESSED_BIT;
break;
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)
{
putchar(c);
}
}
}
skputs("key pressed!\n");
}
// End of Interrupt (to master PIC)
outb(0x20, 0x20);
}
void keyboard_init(unsigned char layout)
{
// Here we might go and select PS/2, USB, or other... (once we implement multiple keyboard protocols)
// Keyboard layout selection
switch (layout)
{
case US:
keymap = kbdus;
keymap_shifted = kbdus_shifted;
break;
case FR:
keymap = kbdfr;
keymap_shifted = kbdfr_shifted;
break;
default:
skputs("Unsupported layout.");
return;
}
DEBUG("PS/2 Keyboard initialized");
}

37
src/io/kbd/ps2.h Normal file
View File

@@ -0,0 +1,37 @@
#ifndef PS2_H
#define PS2_H
void keyboard_handler();
#define SHIFT_PRESSED_BIT 0b00000001
#define ALT_PRESSED_BIT 0b00000010
#define CTRL_PRESSED_BIT 0b00000100
enum SpecialKeys
{
SHIFT = 255,
ALT = 254,
CTRL = 253
};
enum SpecialScancodes
{
LEFT_SHIFT_PRESSED = 0x2A,
LEFT_SHIFT_RELEASED = 0xAA,
RIGHT_SHIFT_PRESSED = 0x36,
RIGHT_SHIFT_RELEASED = 0xB6,
CTRL_PRESSED = 0x1D,
CTRL_RELEASED = 0x9D,
ALT_PRESSED = 0x38,
ALT_RELEASED = 0xB8
};
enum KeyboardLayout
{
US,
FR
};
void keyboard_init(unsigned char layout);
#endif

View File

@@ -1,4 +1,4 @@
#include "../kernel.h" #include <kernel.h>
#include "serial.h" #include "serial.h"
void outb(int port, unsigned char data) void outb(int port, unsigned char data)
@@ -36,7 +36,7 @@ int serial_init()
// Set normal operation mode // Set normal operation mode
outb(PORT + 4, 0x0F); outb(PORT + 4, 0x0F);
serial_kputs("\n\nkernel: serial: Serial initialization OK!\n"); DEBUG("serial initialized");
return 0; return 0;
} }
@@ -45,18 +45,20 @@ static int is_transmit_empty()
return inb(PORT + 5) & 0x20; return inb(PORT + 5) & 0x20;
} }
void write_serial(char c) // Serial kernel putchar
void skputc(char c)
{ {
while (!is_transmit_empty()); // wait for free spot while (!is_transmit_empty()); // wait for free spot
outb(PORT, c); outb(PORT, c);
} }
void serial_kputs(const char* str) // Serial kernel putstring
void skputs(const char* str)
{ {
unsigned int i=0; unsigned int i=0;
while (str[i]) while (str[i])
{ {
write_serial(str[i]); skputc(str[i]);
i++; i++;
} }
} }

View File

@@ -5,6 +5,7 @@ void outb(int port, unsigned char data);
unsigned char inb(int port); unsigned char inb(int port);
int serial_init(); int serial_init();
void serial_kputs(const char* str); void skputs(const char* str);
void skputc(char c);
#endif #endif

View File

@@ -2,7 +2,7 @@
#include <limine.h> #include <limine.h>
#include <stddef.h> #include <stddef.h>
#include "kernel.h" #include <kernel.h>
#include "term.h" #include "term.h"
extern struct limine_framebuffer* framebuffer; extern struct limine_framebuffer* framebuffer;
@@ -35,6 +35,7 @@ int term_init()
if (framebuffer) if (framebuffer)
{ {
fb = framebuffer->address; fb = framebuffer->address;
DEBUG("terminal initialized");
return 0; return 0;
} }
return -ENOMEM; return -ENOMEM;
@@ -66,7 +67,19 @@ static void draw_char(char c, int px, int py, int fg, int bg)
} }
} }
static void putchar(char c) 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') if (c == '\n')
{ {
@@ -75,6 +88,32 @@ static void putchar(char c)
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 (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) if ((cursor.x+1)*FONT_WIDTH >= framebuffer->width)
{ {
cursor.x = 0; cursor.x = 0;
@@ -87,7 +126,7 @@ static void putchar(char c)
cursor.x++; cursor.x++;
} }
// Overhead that could be avoided, right? // Overhead that could be avoided, right? (for printf)
void _putchar(char character) void _putchar(char character)
{ {
putchar(character); putchar(character);

View File

@@ -3,6 +3,7 @@
int term_init(); int term_init();
void kputs(const char* str); void kputs(const char* str);
void putchar(char c);
enum TermColors enum TermColors
{ {

View File

@@ -10,4 +10,12 @@ enum ErrorCodes
#define CLEAR_INTERRUPTS __asm__ volatile("cli") #define CLEAR_INTERRUPTS __asm__ volatile("cli")
#define SET_INTERRUPTS __asm__ volatile("sti") #define SET_INTERRUPTS __asm__ volatile("sti")
#include "io/serial/serial.h"
#include "io/term/printf.h"
// Still lacks print formatting...
#define DEBUG(log, ...) \
printf("debug: [%s]: " log "\n", __FILE__, ##__VA_ARGS__); \
fctprintf((void*)&skputc, 0, "debug: [%s]: %s\n", __FILE__, log)
#endif #endif

View File

@@ -1,14 +1,15 @@
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <limine.h> #include <limine.h>
#include "io/term.h" #include "io/term/term.h"
#include "io/printf.h" #include "io/term/printf.h"
#include "io/serial.h" #include "io/serial/serial.h"
#include "mem/gdt.h" #include "mem/gdt/gdt.h"
#include "mem/utils.h" #include "mem/misc/utils.h"
#include "idt/idt.h" #include "idt/idt.h"
#include "kernel.h" #include "kernel.h"
#include "time/timer.h" #include "time/timer.h"
#include "io/kbd/ps2.h"
// Limine version used // Limine version used
__attribute__((used, section(".limine_requests"))) __attribute__((used, section(".limine_requests")))
@@ -47,9 +48,8 @@ void kmain()
// Get the first framebuffer from the response // Get the first framebuffer from the response
framebuffer = framebuffer_request.response->framebuffers[0]; framebuffer = framebuffer_request.response->framebuffers[0];
if (term_init()) hcf(); term_init();
serial_init();
if (serial_init()) kputs("kernel: serial: error: Cannot init serial communication!");
CLEAR_INTERRUPTS; CLEAR_INTERRUPTS;
gdt_init(); gdt_init();
@@ -57,9 +57,11 @@ void kmain()
timer_init(); timer_init();
SET_INTERRUPTS; SET_INTERRUPTS;
// Draw something keyboard_init(FR);
printf("%s, %s!", "Hello", "world");
//printf("%d", 4/0); // Draw something
printf("%s, %s!\n", "Hello", "world");
// Yoohoooooo!
DEBUG("kernel initialized successfully! hanging... wow=%d", 42);
hcf(); hcf();
} }

View File

@@ -1,6 +1,7 @@
#include "gdt.h" #include "gdt.h"
#include <stdint.h> #include <stdint.h>
#include "../io/serial.h" #include "../../io/serial/serial.h"
#include <kernel.h>
// Descriptors are 8-byte wide (64bits) // Descriptors are 8-byte wide (64bits)
// So the selectors will be (in bytes): 0x0, 0x8, 0x10, 0x18, etc.. // So the selectors will be (in bytes): 0x0, 0x8, 0x10, 0x18, etc..
@@ -78,5 +79,5 @@ void gdt_init()
gdt_load(); gdt_load();
gdt_flush(); gdt_flush();
serial_kputs("kernel: gdt: Initialized GDT!\n"); DEBUG("GDT initialized");
} }

View File

@@ -1,5 +1,6 @@
#include <stdint.h> #include <stdint.h>
#include "../io/serial.h" #include "../io/serial/serial.h"
#include <kernel.h>
/* /*
For now, the timer module will be using the PIC. For now, the timer module will be using the PIC.
@@ -42,7 +43,8 @@ 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); // Clear bit 0 (IRQ0) mask &= ~(1 << 0); // Set IRQ0 (timer, clear bit 0)
mask &= ~(1 << 1); // Set IRQ1 (PS/2 Keyboard, clear bit 1)
outb(0x21, mask); outb(0x21, mask);
} }
@@ -71,4 +73,5 @@ void timer_init()
pic_remap(); pic_remap();
pic_enable(); pic_enable();
pit_init(); pit_init();
DEBUG("PIT initialized");
} }