Compare commits
31 Commits
hello-worl
...
be1be41a64
| Author | SHA1 | Date | |
|---|---|---|---|
| be1be41a64 | |||
|
923758a4ea
|
|||
|
e18b73c8a0
|
|||
|
c065df6ff3
|
|||
|
bb5fb9db33
|
|||
|
075058a958
|
|||
|
05a862e97a
|
|||
| 8f5e2eae3e | |||
|
cf4915d9f4
|
|||
| 834891fd2a | |||
| 3853a1ace3 | |||
| ead0ed6ae1 | |||
| fabe0b1a10 | |||
| b886f03f7a | |||
| 4607b5aba5 | |||
| cc36c768cf | |||
| dbd068e55a | |||
| 53fb22cecd | |||
| 54f26c506e | |||
| bb556709d8 | |||
| 24d75463b8 | |||
| 42fc169e10 | |||
| d0b4da0596 | |||
| 0031c2fe03 | |||
| 282a423387 | |||
| c43be0bddd | |||
| 6fc7266716 | |||
| 29deb20cd7 | |||
| 62302e03d5 | |||
| e6f4200ae9 | |||
| c8df8934b5 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,5 +1,9 @@
|
|||||||
limine
|
limine
|
||||||
kernel
|
kernel
|
||||||
|
pepperk
|
||||||
iso_root
|
iso_root
|
||||||
*.o
|
*.o
|
||||||
*.iso
|
*.iso
|
||||||
|
*.gch
|
||||||
|
*/*.gch
|
||||||
|
*/*/*.gch
|
||||||
21
Makefile
21
Makefile
@@ -1,8 +1,11 @@
|
|||||||
|
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
|
||||||
|
|
||||||
build:
|
build:
|
||||||
rm -f *.o
|
rm -f *.o
|
||||||
x86_64-elf-gcc -c -I src 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 -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
|
objcopy -O elf64-x86-64 -B i386 -I binary zap-light16.psf zap-light16.o
|
||||||
x86_64-elf-ld -o kernel -T linker.ld *.o
|
nasm -f elf64 src/idt/idt.S -o idt_stub.o
|
||||||
|
x86_64-elf-ld -o pepperk -T linker.ld *.o
|
||||||
|
|
||||||
limine/limine:
|
limine/limine:
|
||||||
rm -rf limine
|
rm -rf limine
|
||||||
@@ -12,7 +15,7 @@ limine/limine:
|
|||||||
build-iso: limine/limine build
|
build-iso: limine/limine build
|
||||||
rm -rf iso_root
|
rm -rf iso_root
|
||||||
mkdir -p iso_root/boot
|
mkdir -p iso_root/boot
|
||||||
cp -v kernel iso_root/boot
|
cp -v pepperk iso_root/boot
|
||||||
mkdir -p iso_root/boot/limine
|
mkdir -p iso_root/boot/limine
|
||||||
cp -v limine.conf iso_root/boot/limine
|
cp -v limine.conf iso_root/boot/limine
|
||||||
mkdir -p iso_root/EFI/BOOT
|
mkdir -p iso_root/EFI/BOOT
|
||||||
@@ -23,11 +26,15 @@ build-iso: limine/limine build
|
|||||||
-no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus \
|
-no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus \
|
||||||
-apm-block-size 2048 --efi-boot boot/limine/limine-uefi-cd.bin \
|
-apm-block-size 2048 --efi-boot boot/limine/limine-uefi-cd.bin \
|
||||||
-efi-boot-part --efi-boot-image --protective-msdos-label \
|
-efi-boot-part --efi-boot-image --protective-msdos-label \
|
||||||
iso_root -o kernel.iso
|
iso_root -o pepper.iso
|
||||||
./limine/limine bios-install kernel.iso
|
./limine/limine bios-install pepper.iso
|
||||||
|
|
||||||
|
debug:
|
||||||
|
qemu-system-x86_64 -drive file=pepper.iso -s -S -d int -no-reboot -no-shutdown &
|
||||||
|
gdb pepperk --command=debug.gdb
|
||||||
|
|
||||||
run: build-iso
|
run: build-iso
|
||||||
qemu-system-x86_64 -cdrom kernel.iso
|
qemu-system-x86_64 -cdrom pepper.iso -serial stdio
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf *.o kernel iso_root kernel.iso limine
|
rm -rf *.o pepperk iso_root pepper.iso limine
|
||||||
|
|||||||
27
README.md
27
README.md
@@ -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,10 +7,33 @@ 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:
|
||||||
|
|
||||||
- the [Limine](https://codeberg.org/Limine/Limine) portable bootloader
|
- the [Limine](https://codeberg.org/Limine/Limine) portable bootloader
|
||||||
- Marco Paland's freestanding [printf implementation](https://github.com/mpaland)
|
- Marco Paland's freestanding [printf implementation](https://github.com/mpaland)
|
||||||
- the [ZAP](https://www.zap.org.au/projects/console-fonts-zap/) PSF console fonts
|
- the [ZAP](https://www.zap.org.au/projects/console-fonts-zap/) PSF console fonts
|
||||||
|
|
||||||
|
...and without these amazing resources:
|
||||||
|
|
||||||
|
- the [OSDev](https://osdev.org) wiki & forums
|
||||||
|
|||||||
3
debug.gdb
Normal file
3
debug.gdb
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
target remote localhost:1234
|
||||||
|
set disassembly-flavor intel
|
||||||
|
display/8i $rip
|
||||||
@@ -3,4 +3,4 @@ timeout: 3
|
|||||||
/PepperOS
|
/PepperOS
|
||||||
protocol: limine
|
protocol: limine
|
||||||
|
|
||||||
path: boot():/boot/kernel
|
path: boot():/boot/pepperk
|
||||||
|
|||||||
310
src/idt/idt.S
Normal file
310
src/idt/idt.S
Normal 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
|
||||||
144
src/idt/idt.c
Normal file
144
src/idt/idt.c
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
#include "idt.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "io/serial/serial.h"
|
||||||
|
#include "io/kbd/ps2.h"
|
||||||
|
#include <kernel.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);
|
||||||
|
DEBUG("IDT initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
|
||||||
|
{
|
||||||
|
switch(context->vector_number)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
DEBUG("Divide Error!");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
DEBUG("Debug Exception!");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
DEBUG("NMI Interrupt!");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
DEBUG("Breakpoint Interrupt!");
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
DEBUG("Overflow Trap!");
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
DEBUG("BOUND Range Exceeded!");
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
DEBUG("Invalid Opcode!");
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
DEBUG("Device Not Available!");
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
DEBUG("Double Fault!");
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
DEBUG("Coprocessor Segment Overrun!");
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
DEBUG("Invalid TSS!");
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
DEBUG("Segment Not Present!");
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
DEBUG("Stack-Segment Fault!");
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
DEBUG("General Protection Fault!");
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
DEBUG("Page Fault!");
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
DEBUG("Intel Reserved Interrupt! (Achievement unlocked: How Did We Get Here?)");
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
DEBUG("x87 Floating-Point Error!");
|
||||||
|
break;
|
||||||
|
case 17:
|
||||||
|
DEBUG("Alignment Check Fault!");
|
||||||
|
break;
|
||||||
|
case 18:
|
||||||
|
DEBUG("Machine Check!");
|
||||||
|
break;
|
||||||
|
case 19:
|
||||||
|
DEBUG("SIMD Floating-Point Exception!");
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
DEBUG("Virtualization Exception!");
|
||||||
|
break;
|
||||||
|
case 21:
|
||||||
|
DEBUG("Control Protection Exception!");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 32:
|
||||||
|
//DEBUG("Tick!");
|
||||||
|
ticks++;
|
||||||
|
// Send an EOI so that we can continue having interrupts
|
||||||
|
outb(0x20, 0x20);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 33:
|
||||||
|
keyboard_handler();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DEBUG("Unexpected interrupt");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
57
src/idt/idt.h
Normal file
57
src/idt/idt.h
Normal 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
|
||||||
237
src/io/kbd/ps2.c
Normal file
237
src/io/kbd/ps2.c
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
// PS/2 Keyboard support
|
||||||
|
|
||||||
|
#include "io/serial/serial.h"
|
||||||
|
#include "ps2.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "io/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
37
src/io/kbd/ps2.h
Normal 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
|
||||||
64
src/io/serial/serial.c
Normal file
64
src/io/serial/serial.c
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#include <kernel.h>
|
||||||
|
#include "serial.h"
|
||||||
|
|
||||||
|
void outb(int port, unsigned char data)
|
||||||
|
{
|
||||||
|
__asm__ __volatile__("outb %%al, %%dx" :: "a" (data),"d" (port));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char inb(int port)
|
||||||
|
{
|
||||||
|
unsigned char data = 0;
|
||||||
|
__asm__ __volatile__("inb %%dx, %%al" : "=a" (data) : "d" (port));
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// COM1
|
||||||
|
#define PORT 0x3F8
|
||||||
|
|
||||||
|
int serial_init()
|
||||||
|
{
|
||||||
|
outb(PORT + 1, 0x00); // Disable all interrupts
|
||||||
|
outb(PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
|
||||||
|
outb(PORT + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud
|
||||||
|
outb(PORT + 1, 0x00); // (hi byte)
|
||||||
|
outb(PORT + 3, 0x03); // 8 bits, no parity, one stop bit
|
||||||
|
outb(PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
|
||||||
|
outb(PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
|
||||||
|
outb(PORT + 4, 0x1E); // Set in loopback mode, test the serial chip
|
||||||
|
outb(PORT + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte)
|
||||||
|
|
||||||
|
if (inb(PORT) != 0xAE)
|
||||||
|
{
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set normal operation mode
|
||||||
|
outb(PORT + 4, 0x0F);
|
||||||
|
|
||||||
|
DEBUG("serial initialized");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_transmit_empty()
|
||||||
|
{
|
||||||
|
return inb(PORT + 5) & 0x20;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serial kernel putchar
|
||||||
|
void skputc(char c)
|
||||||
|
{
|
||||||
|
while (!is_transmit_empty()); // wait for free spot
|
||||||
|
outb(PORT, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serial kernel putstring
|
||||||
|
void skputs(const char* str)
|
||||||
|
{
|
||||||
|
unsigned int i=0;
|
||||||
|
while (str[i])
|
||||||
|
{
|
||||||
|
skputc(str[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/io/serial/serial.h
Normal file
11
src/io/serial/serial.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef SERIAL_H
|
||||||
|
#define SERIAL_H
|
||||||
|
|
||||||
|
void outb(int port, unsigned char data);
|
||||||
|
unsigned char inb(int port);
|
||||||
|
|
||||||
|
int serial_init();
|
||||||
|
void skputs(const char* str);
|
||||||
|
void skputc(char c);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
// Terminal output
|
// Terminal output
|
||||||
|
/*
|
||||||
|
There are a couple of bugs here and there but for now I don't care too much
|
||||||
|
because this shitty implementation will be replaced one day by Flanterm
|
||||||
|
(once memory management is okay: paging & kernel malloc)
|
||||||
|
*/
|
||||||
|
|
||||||
#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 +40,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 +72,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 +93,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 +131,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);
|
||||||
@@ -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
|
||||||
{
|
{
|
||||||
16
src/kernel.h
16
src/kernel.h
@@ -3,7 +3,21 @@
|
|||||||
|
|
||||||
enum ErrorCodes
|
enum ErrorCodes
|
||||||
{
|
{
|
||||||
ENOMEM
|
ENOMEM,
|
||||||
|
EIO
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define CLEAR_INTERRUPTS __asm__ volatile("cli")
|
||||||
|
#define SET_INTERRUPTS __asm__ volatile("sti")
|
||||||
|
|
||||||
|
#include "io/serial/serial.h"
|
||||||
|
#include "io/term/printf.h"
|
||||||
|
|
||||||
|
#define DEBUG(log, ...) fctprintf((void*)&skputc, 0, "debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
// printf("debug: [%s]: " log "\n", __FILE__, ##__VA_ARGS__);
|
||||||
|
|
||||||
|
void hcf();
|
||||||
|
#define assert(check) do { if(!(check)) hcf(); } while(0)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
151
src/kmain.c
151
src/kmain.c
@@ -1,8 +1,19 @@
|
|||||||
#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/serial.h"
|
||||||
|
#include "mem/gdt/gdt.h"
|
||||||
|
#include "mem/misc/utils.h"
|
||||||
|
#include "idt/idt.h"
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "time/timer.h"
|
||||||
|
#include "io/kbd/ps2.h"
|
||||||
|
#include "mem/paging/pmm.h"
|
||||||
|
#include "mem/paging/paging.h"
|
||||||
|
#include "mem/paging/vmm.h"
|
||||||
|
#include "mem/heap/kheap.h"
|
||||||
|
|
||||||
// Limine version used
|
// Limine version used
|
||||||
__attribute__((used, section(".limine_requests")))
|
__attribute__((used, section(".limine_requests")))
|
||||||
@@ -15,6 +26,27 @@ static volatile struct limine_framebuffer_request framebuffer_request = {
|
|||||||
.revision = 0
|
.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")))
|
__attribute__((used, section(".limine_requests_start")))
|
||||||
static volatile LIMINE_REQUESTS_START_MARKER;
|
static volatile LIMINE_REQUESTS_START_MARKER;
|
||||||
|
|
||||||
@@ -23,76 +55,8 @@ 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
|
// Panic (should dump registers etc. in the future)
|
||||||
// so everything goes allright with the compiler
|
void hcf()
|
||||||
|
|
||||||
// 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
|
|
||||||
static void hcf()
|
|
||||||
{
|
{
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
@@ -106,13 +70,50 @@ 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();
|
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
|
// Get the first framebuffer from the response
|
||||||
framebuffer = framebuffer_request.response->framebuffers[0];
|
framebuffer = framebuffer_request.response->framebuffers[0];
|
||||||
|
|
||||||
if (term_init()) hcf();
|
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);
|
||||||
|
|
||||||
|
// Remap kernel , HHDM and framebuffer
|
||||||
|
paging_init(kerneladdr_request.response, framebuffer);
|
||||||
|
|
||||||
|
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
|
// Draw something
|
||||||
printf("%s, %s!", "Hello", "world");
|
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();
|
hcf();
|
||||||
}
|
}
|
||||||
|
|||||||
83
src/mem/gdt/gdt.c
Normal file
83
src/mem/gdt/gdt.c
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#include "gdt.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "io/serial/serial.h"
|
||||||
|
#include <kernel.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();
|
||||||
|
|
||||||
|
DEBUG("GDT initialized");
|
||||||
|
}
|
||||||
26
src/mem/gdt/gdt.h
Normal file
26
src/mem/gdt/gdt.h
Normal 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
|
||||||
106
src/mem/heap/kheap.c
Normal file
106
src/mem/heap/kheap.c
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
#include "kheap.h"
|
||||||
|
#include "mem/paging/paging.h"
|
||||||
|
#include "mem/paging/pmm.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
|
||||||
|
extern uint64_t kernel_phys_base;
|
||||||
|
extern uint64_t kernel_virt_base;
|
||||||
|
|
||||||
|
uintptr_t kheap_start;
|
||||||
|
|
||||||
|
static struct heap_block_t* head = NULL;
|
||||||
|
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();
|
||||||
|
|
||||||
|
// Give linked list head its properties
|
||||||
|
head = (struct heap_block_t*)kheap_start;
|
||||||
|
head->size = PAGE_SIZE - sizeof(struct heap_block_t);
|
||||||
|
head->free = true;
|
||||||
|
head->next = NULL;
|
||||||
|
DEBUG("kheap initialized, head=0x%p, size=%u", head, head->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* kmalloc(size_t size)
|
||||||
|
{
|
||||||
|
// No size, no memory allocated!
|
||||||
|
if (!size) return NULL;
|
||||||
|
|
||||||
|
struct heap_block_t* curr = head;
|
||||||
|
|
||||||
|
while (curr)
|
||||||
|
{
|
||||||
|
// Is block free and big enough for us?
|
||||||
|
if (curr->free && curr->size >= size)
|
||||||
|
{
|
||||||
|
// We split the block if it is big enough
|
||||||
|
if (curr->size > size + sizeof(struct heap_block_t))
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Then we chain up the block in the list
|
||||||
|
new_block->next = curr->next;
|
||||||
|
curr->next = new_block;
|
||||||
|
|
||||||
|
curr->size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Found a good block, we return it
|
||||||
|
curr->free = false;
|
||||||
|
return (void*)((uintptr_t)curr + sizeof(struct heap_block_t));
|
||||||
|
}
|
||||||
|
// Continue browsing the list if nothing good was found yet
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
void kfree(void* ptr)
|
||||||
|
{
|
||||||
|
// Nothing to free
|
||||||
|
if (!ptr) return;
|
||||||
|
|
||||||
|
// Set it free!
|
||||||
|
struct heap_block_t* block = (struct heap_block_t*)((uintptr_t)ptr - sizeof(struct heap_block_t));
|
||||||
|
block->free = true;
|
||||||
|
}
|
||||||
26
src/mem/heap/kheap.h
Normal file
26
src/mem/heap/kheap.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#ifndef KHEAP_H
|
||||||
|
#define KHEAP_H
|
||||||
|
|
||||||
|
// We need some kind of simple kernel heap to make our linked list
|
||||||
|
// for the VMM, as we need "malloc" and "free" for that data structure.
|
||||||
|
// 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>
|
||||||
|
|
||||||
|
struct heap_block_t
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
bool free;
|
||||||
|
struct heap_block_t* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
void kheap_init();
|
||||||
|
void* kmalloc(size_t size);
|
||||||
|
void kfree(void* ptr);
|
||||||
|
|
||||||
|
#endif
|
||||||
122
src/mem/misc/utils.c
Normal file
122
src/mem/misc/utils.c
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <limine.h>
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "string/string.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
14
src/mem/misc/utils.h
Normal file
14
src/mem/misc/utils.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#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);
|
||||||
|
|
||||||
|
void memmap_display(struct limine_memmap_response* response);
|
||||||
|
void hhdm_display(struct limine_hhdm_response* hhdm);
|
||||||
|
|
||||||
|
#endif
|
||||||
159
src/mem/paging/paging.c
Normal file
159
src/mem/paging/paging.c
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
#include "paging.h"
|
||||||
|
#include "pmm.h"
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <limine.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
Paging on x86 uses four different page table levels:
|
||||||
|
cr3 register contains the phys address for the PML4 (root directory)
|
||||||
|
|
||||||
|
Each directory/table is made of 512 entries, each one uint64_t
|
||||||
|
Each of these entries have special bits (PRESENT/WRITEABLE/USER/etc.)
|
||||||
|
that dictates their attributes. Also these bits fall back on children tables.
|
||||||
|
|
||||||
|
If we use 1GB huge pages: PML4 -> PDPT -> 1gb pages
|
||||||
|
2MB huge pages: PML4 -> PDPT -> PD -> 2mb pages
|
||||||
|
4KB (regular size): PML4 -> PDPT -> PD -> PT -> 4kb pages
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline void load_cr3(uint64_t value) {
|
||||||
|
asm volatile ("mov %0, %%cr3" :: "r"(value) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
// To flush TLB
|
||||||
|
static inline void invlpg(void *addr)
|
||||||
|
{
|
||||||
|
asm volatile("invlpg (%0)" :: "r"(addr) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocates a 512-entry 64bit page table/directory/whatever (zeroed)
|
||||||
|
static uint64_t* alloc_page_table()
|
||||||
|
{
|
||||||
|
uint64_t* virt = (uint64_t*)PHYS_TO_VIRT(pmm_alloc());
|
||||||
|
|
||||||
|
for (size_t i=0; i<512; i++)
|
||||||
|
{
|
||||||
|
virt[i] = 0;
|
||||||
|
}
|
||||||
|
return virt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kernel paging root table, that will be placed in cr3
|
||||||
|
__attribute__((aligned(4096)))
|
||||||
|
uint64_t *kernel_pml4;
|
||||||
|
|
||||||
|
// Map a page, taking virt and phys address. This will go through the paging structures
|
||||||
|
// beginning at the given root table, translate the virtual address in indexes in
|
||||||
|
// page table/directories, and then mapping the correct page table entry with the
|
||||||
|
// given physical address + flags
|
||||||
|
void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags)
|
||||||
|
{
|
||||||
|
virt = PAGE_ALIGN_DOWN(virt);
|
||||||
|
phys = PAGE_ALIGN_DOWN(phys);
|
||||||
|
|
||||||
|
// Translate the virt address into page table indexes
|
||||||
|
uint64_t pml4_i = PML4_INDEX(virt);
|
||||||
|
uint64_t pdpt_i = PDPT_INDEX(virt);
|
||||||
|
uint64_t pd_i = PD_INDEX(virt);
|
||||||
|
uint64_t pt_i = PT_INDEX(virt);
|
||||||
|
|
||||||
|
uint64_t *pdpt, *pd, *pt;
|
||||||
|
|
||||||
|
// PML4
|
||||||
|
// If the entry at index is not present, allocate enough space for it
|
||||||
|
// then populate the entry with correct addr + flags
|
||||||
|
if (!(root_table[pml4_i] & PTE_PRESENT))
|
||||||
|
{
|
||||||
|
pdpt = alloc_page_table();
|
||||||
|
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: same here
|
||||||
|
if (!(pdpt[pdpt_i] & PTE_PRESENT))
|
||||||
|
{
|
||||||
|
pd = alloc_page_table();
|
||||||
|
pdpt[pdpt_i] = VIRT_TO_PHYS(pd) | PTE_PRESENT | PTE_WRITABLE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pd = (uint64_t *)PHYS_TO_VIRT(pdpt[pdpt_i] & ~0xFFFULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PD: and here
|
||||||
|
if (!(pd[pd_i] & PTE_PRESENT))
|
||||||
|
{
|
||||||
|
pt = alloc_page_table();
|
||||||
|
pd[pd_i] = VIRT_TO_PHYS(pt) | PTE_PRESENT | PTE_WRITABLE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pt = (uint64_t *)PHYS_TO_VIRT(pd[pd_i] & ~0xFFFULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PT: finally, populate the page table entry
|
||||||
|
pt[pt_i] = phys | flags | PTE_PRESENT;
|
||||||
|
|
||||||
|
// Flush TLB (apply changes)
|
||||||
|
invlpg((void *)virt);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t kernel_phys_base;
|
||||||
|
uint64_t kernel_virt_base;
|
||||||
|
|
||||||
|
void paging_init(struct limine_kernel_address_response* kaddr, struct limine_framebuffer* fb)
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
DEBUG("Kernel lives at virt=0x%p phys=0x%p", kernel_virt_base, kernel_phys_base);
|
||||||
|
|
||||||
|
kernel_pml4 = alloc_page_table();
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
//paging_kmap_page(i+hhdm_off, i, PTE_WRITABLE);
|
||||||
|
paging_map_page(kernel_pml4, i+hhdm_off, i, PTE_WRITABLE);
|
||||||
|
page_count++;
|
||||||
|
}
|
||||||
|
DEBUG("Mapped %u pages for first 1GB (HHDM)", page_count); 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
|
||||||
|
// .rodata as Read and .data as Read/Write
|
||||||
|
// For now who gives a shit, let's RWX all kernel
|
||||||
|
for (uint64_t i = 0; i < KERNEL_SIZE; i += PAGE_SIZE)
|
||||||
|
{
|
||||||
|
//paging_kmap_page(kernel_virt_base+i, kernel_phys_base+i, PTE_WRITABLE);
|
||||||
|
paging_map_page(kernel_pml4, kernel_virt_base+i, kernel_phys_base+i, PTE_WRITABLE);
|
||||||
|
page_count++;
|
||||||
|
}
|
||||||
|
DEBUG("Mapped %u pages for kernel", page_count); page_count = 0;
|
||||||
|
|
||||||
|
// Get the framebuffer phys/virt address, and size
|
||||||
|
uint64_t fb_virt = (uint64_t)fb->address;
|
||||||
|
uint64_t fb_phys = VIRT_TO_PHYS(fb_virt);
|
||||||
|
uint64_t fb_size = fb->pitch * fb->height;
|
||||||
|
uint64_t fb_pages = (fb_size + PAGE_SIZE-1)/PAGE_SIZE;
|
||||||
|
|
||||||
|
// Map the framebuffer (with cache-disable & write-through)
|
||||||
|
for (uint64_t i=0; i<fb_pages; i++)
|
||||||
|
{
|
||||||
|
//paging_kmap_page(fb_virt+i*PAGE_SIZE, fb_phys+i*PAGE_SIZE, PTE_WRITABLE | PTE_PCD | PTE_PWT);
|
||||||
|
paging_map_page(kernel_pml4, fb_virt+i*PAGE_SIZE, fb_phys+i*PAGE_SIZE, PTE_WRITABLE | PTE_PCD | PTE_PWT);
|
||||||
|
page_count++;
|
||||||
|
}
|
||||||
|
DEBUG("Mapped %u pages for framebuffer", page_count);
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
}
|
||||||
44
src/mem/paging/paging.h
Normal file
44
src/mem/paging/paging.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#ifndef PAGING_H
|
||||||
|
#define PAGING_H
|
||||||
|
|
||||||
|
#define PAGE_SIZE 4096
|
||||||
|
#define BITS_PER_ROW 64
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <limine.h>
|
||||||
|
|
||||||
|
void paging_init(struct limine_kernel_address_response* kaddr, struct limine_framebuffer* fb);
|
||||||
|
void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags);
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
// 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 PML4_INDEX(x) (((x) >> 39) & 0x1FF)
|
||||||
|
#define PDPT_INDEX(x) (((x) >> 30) & 0x1FF)
|
||||||
|
#define PD_INDEX(x) (((x) >> 21) & 0x1FF)
|
||||||
|
#define PT_INDEX(x) (((x) >> 12) & 0x1FF)
|
||||||
|
|
||||||
|
// Page entry special bits
|
||||||
|
// Bits set on a parent (directory, table) fall back to their children
|
||||||
|
#define PTE_PRESENT (1ULL << 0)
|
||||||
|
#define PTE_WRITABLE (1ULL << 1)
|
||||||
|
#define PTE_USER (1ULL << 2)
|
||||||
|
#define PTE_PWT (1ULL << 3)
|
||||||
|
#define PTE_PCD (1ULL << 4)
|
||||||
|
#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
|
||||||
103
src/mem/paging/pmm.c
Normal file
103
src/mem/paging/pmm.c
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
// OMG here we are. I'm cooked.
|
||||||
|
|
||||||
|
/*
|
||||||
|
pmm - Physical Memory Manager
|
||||||
|
will manage 4kb pages physically
|
||||||
|
it will probably need to get some info from Limine,
|
||||||
|
to see which pages are used by kernel/bootloader/mmio/fb etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "paging.h"
|
||||||
|
#include <limine.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include "mem/misc/utils.h"
|
||||||
|
#include "pmm.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
First we'll have to discover the physical memory layout,
|
||||||
|
and for that we can use a Limine request.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
We will look for the biggest usable physical memory region
|
||||||
|
and use this for the bitmap. The reserved memory will be ignored.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct limine_memmap_entry* biggest_entry;
|
||||||
|
|
||||||
|
static void pmm_find_biggest_usable_region(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm)
|
||||||
|
{
|
||||||
|
// Max length of a usable memory region
|
||||||
|
uint64_t length_max = 0;
|
||||||
|
uint64_t offset = hhdm->offset;
|
||||||
|
|
||||||
|
DEBUG("Usable Memory:");
|
||||||
|
for (size_t i=0; i<memmap->entry_count; i++)
|
||||||
|
{
|
||||||
|
struct limine_memmap_entry* entry = memmap->entries[i];
|
||||||
|
|
||||||
|
if (entry->type == LIMINE_MEMMAP_USABLE)
|
||||||
|
{
|
||||||
|
DEBUG("0x%p-0x%p mapped at 0x%p-0x%p", entry->base, entry->base+entry->length,
|
||||||
|
entry->base+offset, entry->base+entry->length+offset);
|
||||||
|
if (entry->length > length_max)
|
||||||
|
{
|
||||||
|
length_max = entry->length;
|
||||||
|
biggest_entry = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG("Biggest usable memory region:");
|
||||||
|
DEBUG("0x%p-0x%p mapped at 0x%p-0x%p", biggest_entry->base, biggest_entry->base + biggest_entry->length,
|
||||||
|
biggest_entry->base+offset, biggest_entry->base+biggest_entry->length+offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset from Higher Half Direct Map
|
||||||
|
uint64_t hhdm_off;
|
||||||
|
|
||||||
|
static uintptr_t g_freelist = 0;
|
||||||
|
|
||||||
|
uintptr_t pmm_alloc()
|
||||||
|
{
|
||||||
|
if (!g_freelist) return 0;
|
||||||
|
uintptr_t addr = g_freelist;
|
||||||
|
g_freelist = *(uintptr_t*) PHYS_TO_VIRT(g_freelist);
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pmm_free(uintptr_t addr)
|
||||||
|
{
|
||||||
|
*(uintptr_t*) PHYS_TO_VIRT(addr) = g_freelist;
|
||||||
|
g_freelist = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pmm_init_freelist()
|
||||||
|
{
|
||||||
|
// We simply call pmm_free() on each page that is marked USABLE
|
||||||
|
// in our big memory region.
|
||||||
|
uint64_t base = ALIGN_UP(biggest_entry->base, PAGE_SIZE);
|
||||||
|
uint64_t end = ALIGN_DOWN(biggest_entry->base + biggest_entry->length, PAGE_SIZE);
|
||||||
|
|
||||||
|
uint64_t page_count=0;
|
||||||
|
for (uint64_t addr = base; addr < end; addr += PAGE_SIZE)
|
||||||
|
{
|
||||||
|
pmm_free(addr);
|
||||||
|
//DEBUG("page %u lives at phys 0x%p (virt 0x%p)", page_count, addr, PHYS_TO_VIRT(addr));
|
||||||
|
page_count++;
|
||||||
|
}
|
||||||
|
DEBUG("%u frames in freelist, available for use (%u bytes)", page_count, page_count*PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pmm_init(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm)
|
||||||
|
{
|
||||||
|
hhdm_off = hhdm->offset;
|
||||||
|
pmm_find_biggest_usable_region(memmap, hhdm);
|
||||||
|
//pmm_allocate_bitmap(hhdm); too complicated for my small brain
|
||||||
|
|
||||||
|
// Now we have biggest USABLE region,
|
||||||
|
// so to populate the free list we just iterate through it
|
||||||
|
pmm_init_freelist();
|
||||||
|
}
|
||||||
10
src/mem/paging/pmm.h
Normal file
10
src/mem/paging/pmm.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef PAGING_PMM_H
|
||||||
|
#define PAGING_PMM_H
|
||||||
|
|
||||||
|
#include <limine.h>
|
||||||
|
|
||||||
|
void pmm_init(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm);
|
||||||
|
void pmm_free(uintptr_t addr);
|
||||||
|
uintptr_t pmm_alloc();
|
||||||
|
|
||||||
|
#endif
|
||||||
66
src/mem/paging/vmm.c
Normal file
66
src/mem/paging/vmm.c
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
The VMM (virtual memory manager) will have two roles:
|
||||||
|
- mapping pages
|
||||||
|
- unmapping pages
|
||||||
|
in a specified virtual space
|
||||||
|
|
||||||
|
compared to the PMM which allocs/frees 4kb frames ("physical pages").
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "vmm.h"
|
||||||
|
#include "paging.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "pmm.h"
|
||||||
|
#include <kernel.h>
|
||||||
|
|
||||||
|
void* vmm_pt_root = 0;
|
||||||
|
|
||||||
|
// Linked list head for virtual memory objects
|
||||||
|
struct vm_object* vm_objs = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
uint64_t convert_x86_vm_flags(size_t flags)
|
||||||
|
{
|
||||||
|
uint64_t value = 0;
|
||||||
|
if (flags & VM_FLAG_WRITE)
|
||||||
|
{
|
||||||
|
value |= PTE_WRITABLE;
|
||||||
|
}
|
||||||
|
if (flags & VM_FLAG_USER)
|
||||||
|
{
|
||||||
|
value |= PTE_USER;
|
||||||
|
}
|
||||||
|
if ((flags & VM_FLAG_EXEC) == 0)
|
||||||
|
{
|
||||||
|
value |= PTE_NOEXEC;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern uint64_t *kernel_pml4;
|
||||||
|
|
||||||
|
void vmm_setup_pt_root()
|
||||||
|
{
|
||||||
|
// We alloc a physical page (frame) for the pointer, then map it
|
||||||
|
// to virt (pointer)
|
||||||
|
uintptr_t phys = pmm_alloc();
|
||||||
|
vmm_pt_root = (void*)kernel_pml4;
|
||||||
|
paging_map_page(kernel_pml4, (uint64_t)vmm_pt_root, phys, convert_x86_vm_flags(VM_FLAG_WRITE | VM_FLAG_EXEC));
|
||||||
|
DEBUG("VMM setup: vmm_pt_root=0x%p (phys=0x%p)", vmm_pt_root, phys);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
size_t len = ALIGN_UP(length, PAGE_SIZE);
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
29
src/mem/paging/vmm.h
Normal file
29
src/mem/paging/vmm.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#ifndef VMM_H
|
||||||
|
#define VMM_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
This will be our linked list of virtual memory objects.
|
||||||
|
Flags here aren't x86 flags, they are platform-agnostic
|
||||||
|
kernel-defined flags.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct vm_object
|
||||||
|
{
|
||||||
|
uintptr_t base;
|
||||||
|
size_t length;
|
||||||
|
size_t flags;
|
||||||
|
struct vm_object* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Flags bitfield
|
||||||
|
#define VM_FLAG_NONE 0
|
||||||
|
#define VM_FLAG_WRITE (1 << 0)
|
||||||
|
#define VM_FLAG_EXEC (1 << 1)
|
||||||
|
#define VM_FLAG_USER (1 << 2)
|
||||||
|
|
||||||
|
void vmm_init();
|
||||||
|
|
||||||
|
#endif
|
||||||
6
src/string/string.c
Normal file
6
src/string/string.c
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
char* strcpy(char *dest, const char *src)
|
||||||
|
{
|
||||||
|
char *temp = dest;
|
||||||
|
while((*dest++ = *src++));
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
6
src/string/string.h
Normal file
6
src/string/string.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef STRING_H
|
||||||
|
#define STRING_H
|
||||||
|
|
||||||
|
char *strcpy(char *dest, const char *src);
|
||||||
|
|
||||||
|
#endif
|
||||||
77
src/time/timer.c
Normal file
77
src/time/timer.c
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include "io/serial/serial.h"
|
||||||
|
#include <kernel.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();
|
||||||
|
DEBUG("PIT initialized");
|
||||||
|
}
|
||||||
6
src/time/timer.h
Normal file
6
src/time/timer.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef TIMER_H
|
||||||
|
#define TIMER_H
|
||||||
|
|
||||||
|
void timer_init();
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user