15 Commits
serial ... kbd

Author SHA1 Message Date
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
54f26c506e 1000Hz PIC timer working + IDT dispatch/handler fixes 2025-12-27 13:52:05 +01:00
bb556709d8 Update README.md 2025-12-23 11:20:16 +01:00
24d75463b8 Merge pull request #4 from xamidev/idt
Idt
2025-12-22 21:05:42 +01:00
42fc169e10 Interrupt Dispatch and Handling (for first common vectors) 2025-12-22 21:04:45 +01:00
d0b4da0596 IDT: set entry, load into IDTR, interrupt stub + dispatcher for common faults 2025-12-22 19:38:50 +01:00
0031c2fe03 Woops.. it wasnt nonsense after all 2025-12-22 11:27:39 +01:00
282a423387 Delete GCH nonsense 2025-12-22 11:26:59 +01:00
c43be0bddd Merge pull request #3 from xamidev/gdt
GDT init (load + flush)
2025-12-22 11:24:15 +01:00
6fc7266716 GDT init (load + flush) 2025-12-22 11:20:24 +01:00
29deb20cd7 Merge pull request #2 from xamidev/serial
Serial communication
2025-12-21 20:35:02 +01:00
20 changed files with 1126 additions and 77 deletions

3
.gitignore vendored
View File

@@ -4,3 +4,6 @@ pepperk
iso_root iso_root
*.o *.o
*.iso *.iso
*.gch
*/*.gch
*/*/*.gch

View File

@@ -1,7 +1,8 @@
build: build:
rm -f *.o rm -f *.o
x86_64-elf-gcc -g -c -I src 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 src/kbd/ps2.c 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
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
x86_64-elf-ld -o pepperk -T linker.ld *.o x86_64-elf-ld -o pepperk -T linker.ld *.o
limine/limine: limine/limine:
@@ -27,7 +28,7 @@ build-iso: limine/limine build
./limine/limine bios-install pepper.iso ./limine/limine bios-install pepper.iso
debug: debug:
qemu-system-x86_64 -drive file=pepper.iso -s -S & qemu-system-x86_64 -drive file=pepper.iso -s -S -d int -no-reboot &
gdb pepperk --command=debug.gdb gdb pepperk --command=debug.gdb
run: build-iso run: build-iso

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
@@ -17,4 +17,4 @@ PepperOS wouldn't be possible without the following freely-licensed software:
...and without these amazing resources: ...and without these amazing resources:
- the [OSDev](https://osdev.org) wiki & forums - the [OSDev](https://osdev.org) wiki & forums

View File

@@ -1 +1,3 @@
target remote localhost:1234 target remote localhost:1234
set disassembly-flavor intel
display/8i $rip

310
src/idt/idt.S Normal file
View File

@@ -0,0 +1,310 @@
; Assembly stub for the IDT
bits 64
extern interrupt_dispatch
global interrupt_stub
global vector_0_handler
global vector_1_handler
global vector_2_handler
global vector_3_handler
global vector_4_handler
global vector_5_handler
global vector_6_handler
global vector_7_handler
global vector_8_handler
global vector_9_handler
global vector_10_handler
global vector_11_handler
global vector_12_handler
global vector_13_handler
global vector_14_handler
global vector_15_handler
global vector_16_handler
global vector_17_handler
global vector_18_handler
global vector_19_handler
global vector_20_handler
global vector_21_handler
interrupt_stub:
; We'll push all general-purpose registers to the stack,
; so they're intact and don't bother the code that was
; executed when the interrupt happened.
; (except rsp because it will already be saved in the iret frame)
push qword rax
push qword rbx
push qword rcx
push qword rdx
push qword rsi
push qword rdi
;push qword rsp
push qword rbp
push qword r8
push qword r9
push qword r10
push qword r11
push qword r12
push qword r13
push qword r14
push qword r15
; Put stack pointer as first argument of our function
mov rdi, rsp
call interrupt_dispatch
; What the function returns (new stack pointer) is saved in rbp
mov rsp, rax
pop qword r15
pop qword r14
pop qword r13
pop qword r12
pop qword r11
pop qword r10
pop qword r9
pop qword r8
pop qword rbp
;pop qword rsp
pop qword rdi
pop qword rsi
pop qword rdx
pop qword rcx
pop qword rbx
pop qword rax
; Removing the error code and vector number so stack doesn't
; get corrupted
add rsp, 16
; Restore ss, rsp, rflags, cs, rip of code that was executing
; before the interrupt
iretq
; Vector handlers will be 16-byte aligned so that we can loop over them
; like <vector_no> * 16 to get each one's address
; Divide Error
align 16
vector_0_handler:
; error code (nothing, so we push a dummy 0 quadword, 64bits/8bytes long)
push qword 0
; vector number (so our interrupt stub knows which one it is)
push qword 0
jmp interrupt_stub
; Debug Exception
align 16
vector_1_handler:
push qword 0
push qword 1
jmp interrupt_stub
; NMI
align 16
vector_2_handler:
push qword 0
push qword 2
jmp interrupt_stub
; Breakpoint
align 16
vector_3_handler:
push qword 0
push qword 3
jmp interrupt_stub
; Overflow
align 16
vector_4_handler:
push qword 0
push qword 4
jmp interrupt_stub
; BOUND Range exceeded
align 16
vector_5_handler:
push qword 0
push qword 5
jmp interrupt_stub
; Invalid Opcode
align 16
vector_6_handler:
push qword 0
push qword 6
jmp interrupt_stub
; Device Not Available
align 16
vector_7_handler:
push qword 0
push qword 7
jmp interrupt_stub
; Double Fault
align 16
vector_8_handler:
; No error code, we only push vector number
push qword 1
jmp interrupt_stub
; Coprocessor Segment Overrun
align 16
vector_9_handler:
push qword 9
jmp interrupt_stub
; Invalid TSS
align 16
vector_10_handler:
push qword 10
jmp interrupt_stub
; Segment Not Present
align 16
vector_11_handler:
push qword 11
jmp interrupt_stub
; Stack-Segment Fault
align 16
vector_12_handler:
push qword 12
jmp interrupt_stub
; General Protection
align 16
vector_13_handler:
push qword 13
jmp interrupt_stub
; Page Fault
align 16
vector_14_handler:
push qword 14
jmp interrupt_stub
; Intel reserved
align 16
vector_15_handler:
push qword 0
push qword 15
jmp interrupt_stub
; x87 FPU Floating-Point Error
align 16
vector_16_handler:
push qword 0
push qword 16
jmp interrupt_stub
; Alignment Check
align 16
vector_17_handler:
push qword 17
jmp interrupt_stub
; Machine Check
align 16
vector_18_handler:
push qword 0
push qword 18
jmp interrupt_stub
; SIMD Floating-Point Exception
align 16
vector_19_handler:
push qword 0
push qword 19
jmp interrupt_stub
; Virtualization Exception
align 16
vector_20_handler:
push qword 0
push qword 20
jmp interrupt_stub
; Control Protection Exception
align 16
vector_21_handler:
push qword 21
jmp interrupt_stub
; The others are reserved (22->31) or external (32->255) interrupts
align 16
vector_22_handler:
push qword 0
push qword 22
jmp interrupt_stub
align 16
vector_23_handler:
push qword 0
push qword 23
jmp interrupt_stub
align 16
vector_24_handler:
push qword 0
push qword 24
jmp interrupt_stub
align 16
vector_25_handler:
push qword 0
push qword 25
jmp interrupt_stub
align 16
vector_26_handler:
push qword 0
push qword 26
jmp interrupt_stub
align 16
vector_27_handler:
push qword 0
push qword 27
jmp interrupt_stub
align 16
vector_28_handler:
push qword 0
push qword 28
jmp interrupt_stub
align 16
vector_29_handler:
push qword 0
push qword 29
jmp interrupt_stub
align 16
vector_30_handler:
push qword 0
push qword 30
jmp interrupt_stub
align 16
vector_31_handler:
push qword 0
push qword 31
jmp interrupt_stub
; PIT timer
align 16
vector_32_handler:
push qword 0
push qword 32
jmp interrupt_stub
; PS/2 Keyboard
align 16
vector_33_handler:
push qword 0
push qword 33
jmp interrupt_stub

143
src/idt/idt.c Normal file
View File

@@ -0,0 +1,143 @@
#include "idt.h"
#include <stdint.h>
#include <stddef.h>
#include "../io/serial.h"
#include "../kbd/ps2.h"
struct interrupt_descriptor idt[256];
struct idtr idt_reg;
// Address to our first interrupt handler
extern char vector_0_handler[];
// Timer ticks
extern uint64_t ticks;
void idt_set_entry(uint8_t vector, void* handler, uint8_t dpl)
{
uint64_t handler_addr = (uint64_t)handler;
struct interrupt_descriptor* entry = &idt[vector];
// Address is split in three parts so we right-shift progressively to get it all
entry->address_low = handler_addr & 0xFFFF;
entry->address_mid = (handler_addr >> 16) & 0xFFFF;
entry->address_high = handler_addr >> 32;
// Kernel code selector (as set in GDT)
entry->selector = 0x8;
// Interrupt gate, present, DPL (having: max DPL = 3)
entry->flags = 0b1110 | ((dpl & 0b11) << 5) | (1 << 7);
// We won't use IST for now
entry->ist = 0;
}
void idt_load(void* idt_addr)
{
// "limit" = "size" = Size of the IDT - 1 byte = (16*256)-1 = 0xFFF
idt_reg.limit = 0xFFF;
idt_reg.base = (uint64_t)idt_addr;
asm volatile("lidt %0" :: "m"(idt_reg));
}
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++)
{
// 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);
}
idt_load(&idt);
serial_kputs("kernel: idt: Initialized IDT!\n");
}
struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
{
switch(context->vector_number)
{
case 0:
serial_kputs("kernel: idt: Divide Error!\n");
break;
case 1:
serial_kputs("kernel: idt: Debug Exception!\n");
break;
case 2:
serial_kputs("kernel: idt: NMI Interrupt!\n");
break;
case 3:
serial_kputs("kernel: idt: Breakpoint Interrupt!\n");
break;
case 4:
serial_kputs("kernel: idt: Overflow Trap!\n");
break;
case 5:
serial_kputs("kernel: idt: BOUND Range Exceeded!\n");
break;
case 6:
serial_kputs("kernel: idt: Invalid Opcode!\n");
break;
case 7:
serial_kputs("kernel: idt: Device Not Available!\n");
break;
case 8:
serial_kputs("kernel: idt: Double Fault!\n");
break;
case 9:
serial_kputs("kernel: idt: Coprocessor Segment Overrun!\n");
break;
case 10:
serial_kputs("kernel: idt: Invalid TSS!\n");
break;
case 11:
serial_kputs("kernel: idt: Segment Not Present!\n");
break;
case 12:
serial_kputs("kernel: idt: Stack-Segment Fault!\n");
break;
case 13:
serial_kputs("kernel: idt: General Protection Fault!\n");
break;
case 14:
serial_kputs("kernel: idt: Page Fault!\n");
break;
case 15:
serial_kputs("kernel: idt: Intel Reserved Interrupt! (Achievement unlocked: How Did We Get Here?)\n");
break;
case 16:
serial_kputs("kernel: idt: x87 Floating-Point Error!\n");
break;
case 17:
serial_kputs("kernel: idt: Alignment Check Fault!\n");
break;
case 18:
serial_kputs("kernel: idt: Machine Check!\n");
break;
case 19:
serial_kputs("kernel: idt: SIMD Floating-Point Exception!\n");
break;
case 20:
serial_kputs("kernel: idt: Virtualization Exception!\n");
break;
case 21:
serial_kputs("kernel: idt: Control Protection Exception!\n");
break;
case 32:
//serial_kputs("Tick!");
ticks++;
// Send an EOI so that we can continue having interrupts
outb(0x20, 0x20);
break;
case 33:
keyboard_handler();
break;
default:
serial_kputs("kernel: idt: Unexpected interrupt\n");
break;
}
return context;
}

57
src/idt/idt.h Normal file
View File

@@ -0,0 +1,57 @@
#ifndef IDT_H
#define IDT_H
#include <stdint.h>
void idt_init();
struct interrupt_descriptor
{
uint16_t address_low;
uint16_t selector;
uint8_t ist;
uint8_t flags;
uint16_t address_mid;
uint32_t address_high;
uint32_t reserved;
} __attribute__((packed));
struct idtr
{
uint16_t limit;
uint64_t base;
} __attribute__((packed));
// All general-purpose registers (except rsp) as stored on the stack,
// plus the values we pushed (vector number, error code) and the iret frame
// In reverse order because the stack grows downwards.
struct cpu_status_t
{
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t rbp;
//uint64_t rsp;
uint64_t rdi;
uint64_t rsi;
uint64_t rdx;
uint64_t rcx;
uint64_t rbx;
uint64_t rax;
uint64_t vector_number;
uint64_t error_code;
uint64_t iret_rip;
uint64_t iret_cs;
uint64_t iret_flags;
uint64_t iret_rsp;
uint64_t iret_ss;
};
#endif

View File

@@ -1,4 +1,5 @@
#include "../kernel.h" #include "../kernel.h"
#include "serial.h"
void outb(int port, unsigned char data) void outb(int port, unsigned char data)
{ {
@@ -34,6 +35,8 @@ 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");
return 0; return 0;
} }

View File

@@ -66,7 +66,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 +87,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 +125,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
{ {

236
src/kbd/ps2.c Normal file
View File

@@ -0,0 +1,236 @@
// PS/2 Keyboard support
#include "../io/serial.h"
#include "../io/printf.h"
#include "ps2.h"
#include <stdint.h>
#include "../io/term.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);
}
}
}
serial_kputs("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:
serial_kputs("Unsupported layout.");
break;
}
}

37
src/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

@@ -7,4 +7,7 @@ enum ErrorCodes
EIO EIO
}; };
#define CLEAR_INTERRUPTS __asm__ volatile("cli")
#define SET_INTERRUPTS __asm__ volatile("sti")
#endif #endif

View File

@@ -4,6 +4,12 @@
#include "io/term.h" #include "io/term.h"
#include "io/printf.h" #include "io/printf.h"
#include "io/serial.h" #include "io/serial.h"
#include "mem/gdt.h"
#include "mem/utils.h"
#include "idt/idt.h"
#include "kernel.h"
#include "time/timer.h"
#include "kbd/ps2.h"
// Limine version used // Limine version used
__attribute__((used, section(".limine_requests"))) __attribute__((used, section(".limine_requests")))
@@ -24,74 +30,6 @@ static volatile LIMINE_REQUESTS_END_MARKER;
struct limine_framebuffer* framebuffer; struct limine_framebuffer* framebuffer;
// We won't be linked to standard library, but still need the basic mem* functions
// so everything goes allright with the compiler
// We use the "restrict" keyword on pointers so that the compiler knows it can
// do more optimization on them (and as it's a much used function, it's good to
// be able to do that)
void* memcpy(void* restrict dest, const void* restrict src, size_t n)
{
uint8_t* restrict pdest = (uint8_t* restrict)dest;
const uint8_t* restrict psrc = (const uint8_t* restrict)src;
for (size_t i=0; i<n; i++)
{
pdest[i] = psrc[i];
}
return dest;
}
void* memset(void* s, int c, size_t n)
{
uint8_t* p = (uint8_t*)s;
for (size_t i=0; i<n; i++)
{
p[i] = (uint8_t)c;
}
return s;
}
void* memmove(void *dest, const void* src, size_t n)
{
uint8_t* pdest = (uint8_t*)dest;
const uint8_t* psrc = (uint8_t*)src;
if (src > dest)
{
for (size_t i=0; i<n; i++)
{
pdest[i] = psrc[i];
}
} else if (src < dest)
{
for (size_t i=n; i>0; i--)
{
pdest[i-1] = psrc[i-1];
}
}
return dest;
}
int memcmp(const void* s1, const void* s2, size_t n)
{
const uint8_t* p1 = (const uint8_t*)s1;
const uint8_t* p2 = (const uint8_t*)s2;
for (size_t i=0; i<n; i++)
{
if (p1[i] != p2[i])
{
return p1[i] < p2[i] ? -1 : 1;
}
}
return 0;
}
// Panic // Panic
static void hcf() static void hcf()
{ {
@@ -113,11 +51,18 @@ void kmain()
if (term_init()) hcf(); if (term_init()) hcf();
if (serial_init()) kputs("kernel: serial: error: Cannot init serial communication!"); if (serial_init()) kputs("kernel: serial: error: Cannot init serial communication!");
serial_kputs("\n\nkernel: serial: Hello, world from serial!"); CLEAR_INTERRUPTS;
gdt_init();
idt_init();
timer_init();
SET_INTERRUPTS;
keyboard_init(FR);
// Draw something // Draw something
printf("%s, %s!", "Hello", "world"); printf("%s, %s!", "Hello", "world");
//printf("%d", 4/0);
hcf(); hcf();
} }

82
src/mem/gdt.c Normal file
View File

@@ -0,0 +1,82 @@
#include "gdt.h"
#include <stdint.h>
#include "../io/serial.h"
// Descriptors are 8-byte wide (64bits)
// So the selectors will be (in bytes): 0x0, 0x8, 0x10, 0x18, etc..
uint64_t gdt_entries[NUM_GDT_ENTRIES];
struct GDTR gdtr;
static void gdt_load()
{
asm("lgdt %0" : : "m"(gdtr));
}
static void gdt_flush()
{
// Here, 0x8 is the kernel code selector
// and 0x10 is the kernel data selector
asm volatile (
"mov $0x10, %%ax \n" // Reload segments with kernel data selector
"mov %%ax, %%ds \n"
"mov %%ax, %%es \n"
"mov %%ax, %%fs \n"
"mov %%ax, %%gs \n"
"mov %%ax, %%ss \n"
"pushq $0x8 \n" // CS reload
"lea 1f(%%rip), %%rax \n"
"push %%rax \n"
"lretq \n"
"1: \n" // Execution continues here after CS reload
:
:
: "rax", "memory"
);
}
void gdt_init()
{
// Null descriptor (required)
gdt_entries[0] = 0;
// Kernel code segment
uint64_t kernel_code = 0;
kernel_code |= 0b1101 << 8; // Selector type: accessed, read-enable, no conforming
kernel_code |= 1 << 12; // not a system descriptor
kernel_code |= 0 << 13; // DPL field = 0
kernel_code |= 1 << 15; // Present
kernel_code |= 1 << 21; // Long mode
// Left shift 32 bits so we place our stuff in the upper 32 bits of the descriptor.
// The lower 32 bits contain limit and part of base and therefore are ignored in Long Mode
// (because we'll use paging; segmentation is used only for legacy)
gdt_entries[1] = kernel_code << 32;
uint64_t kernel_data = 0;
kernel_data |= 0b0011 << 8;
kernel_data |= 1 << 12;
kernel_data |= 0 << 13;
kernel_data |= 1 << 15;
kernel_data |= 1 << 21;
gdt_entries[2] = kernel_data << 32;
// We re-use the kernel descriptors here, and just update their DPL fields
// (Descriptor privilege level) from ring 0 -> to ring 3 (userspace)
uint64_t user_code = kernel_code | (3 << 13);
gdt_entries[3] = user_code;
uint64_t user_data = kernel_data | (3 << 13);
gdt_entries[4] = user_data;
// The -1 subtraction is some wizardry explained in the OSDev wiki -> GDT
gdtr.limit = NUM_GDT_ENTRIES * sizeof(uint64_t) - 1;
gdtr.address = (uint64_t)gdt_entries;
// Load the GDT we created, flush the old one
gdt_load();
gdt_flush();
serial_kputs("kernel: gdt: Initialized GDT!\n");
}

26
src/mem/gdt.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef GDT_H
#define GDT_H
#include <stdint.h>
// We're using the GDT for segmentation, but as we want to target Long Mode,
// we'll only use this as a requirement for paging, not more.
// This means base 0 and no limit (whole address space)
#define NUM_GDT_ENTRIES 5
#define NULL_SELECTOR 0x00
#define KERNEL_CODE_SEGMENT 0x08
#define KERNEL_DATA_SEGMENT 0x10
#define USER_CODE_SEGMENT 0x18
#define USER_DATA_SEGMENT 0x20
struct GDTR
{
uint16_t limit;
uint64_t address;
} __attribute__((packed));
void gdt_init();
#endif

70
src/mem/utils.c Normal file
View File

@@ -0,0 +1,70 @@
#include <stddef.h>
#include <stdint.h>
// We won't be linked to standard library, but still need the basic mem* functions
// so everything goes allright with the compiler
// We use the "restrict" keyword on pointers so that the compiler knows it can
// do more optimization on them (and as it's a much used function, it's good to
// be able to do that)
void* memcpy(void* restrict dest, const void* restrict src, size_t n)
{
uint8_t* restrict pdest = (uint8_t* restrict)dest;
const uint8_t* restrict psrc = (const uint8_t* restrict)src;
for (size_t i=0; i<n; i++)
{
pdest[i] = psrc[i];
}
return dest;
}
void* memset(void* s, int c, size_t n)
{
uint8_t* p = (uint8_t*)s;
for (size_t i=0; i<n; i++)
{
p[i] = (uint8_t)c;
}
return s;
}
void* memmove(void *dest, const void* src, size_t n)
{
uint8_t* pdest = (uint8_t*)dest;
const uint8_t* psrc = (uint8_t*)src;
if (src > dest)
{
for (size_t i=0; i<n; i++)
{
pdest[i] = psrc[i];
}
} else if (src < dest)
{
for (size_t i=n; i>0; i--)
{
pdest[i-1] = psrc[i-1];
}
}
return dest;
}
int memcmp(const void* s1, const void* s2, size_t n)
{
const uint8_t* p1 = (const uint8_t*)s1;
const uint8_t* p2 = (const uint8_t*)s2;
for (size_t i=0; i<n; i++)
{
if (p1[i] != p2[i])
{
return p1[i] < p2[i] ? -1 : 1;
}
}
return 0;
}

11
src/mem/utils.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef MEM_UTILS_H
#define MEM_UTILS_H
#include <stddef.h>
void* memcpy(void* restrict dest, const void* restrict src, size_t n);
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);
#endif

75
src/time/timer.c Normal file
View File

@@ -0,0 +1,75 @@
#include <stdint.h>
#include "../io/serial.h"
/*
For now, the timer module will be using the PIC.
Even though it's quite old, it's still supported by newer CPUs
and it will be precise enough for what we'll do. Also it's easier
to implement than ACPI etc. (we may upgrade to ACPI when we're
interested in multi-core functionnality like SMP)
*/
volatile uint64_t ticks = 0;
void pic_remap()
{
uint8_t master_mask = inb(0x21);
uint8_t slave_mask = inb(0xA1);
// ICW1: start initialization
outb(0x20, 0x11);
outb(0xA0, 0x11);
// ICW2: vector offsets
outb(0x21, 0x20); // Master PIC -> 0x20
outb(0xA1, 0x28); // Slave PIC -> 0x28
// ICW3: tell Master about Slave at IRQ2 (0000 0100)
outb(0x21, 0x04);
// ICW3: tell Slave its cascade identity (0000 0010)
outb(0xA1, 0x02);
// ICW4: 8086 mode
outb(0x21, 0x01);
outb(0xA1, 0x01);
// Restore saved masks
outb(0x21, master_mask);
outb(0xA1, slave_mask);
}
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)
outb(0x21, mask);
}
/*
Base frequency = 1.193182 MHz
1 tick per ms (divide by 1000) = roughly 1193 Hz
*/
void pit_init()
{
uint32_t frequency = 1000; // 1 kHz
uint32_t divisor = 1193182 / frequency;
// Set PIT to mode 3, channel 0
outb(0x43, 0x36); // 0x36
// Send divisor (low byte, then high byte)
outb(0x40, divisor & 0xFF);
outb(0x40, (divisor >> 8) & 0xFF);
}
void timer_init()
{
// Remapping the PIC, because at startup it conflicts with
// the reserved IRQs we have for faults/exceptions etc.
// so we move its IRQ0 to something not reserved (32)
pic_remap();
pic_enable();
pit_init();
}

6
src/time/timer.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef TIMER_H
#define TIMER_H
void timer_init();
#endif