Compare commits
36 Commits
ad30071e9f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd3dbc3ab1 | ||
|
|
ff12b6abf2 | ||
|
|
a018dfd471 | ||
|
|
8cc90d6f2f | ||
|
|
4f55956fee | ||
|
|
45a9023bab | ||
|
|
130e271461 | ||
|
|
46bc977104 | ||
|
|
5f071104bc | ||
|
|
62680a05bc | ||
|
|
ac0299d718 | ||
|
|
b15c037b16 | ||
|
|
02f0ba9aad | ||
|
|
0ccb483047 | ||
|
|
ad0ec03b0f | ||
|
|
7b115e5b66 | ||
|
|
a3822dd350 | ||
|
|
bcf05c055e | ||
|
|
2aa44eaa51 | ||
|
|
0f6b66a486 | ||
|
|
8e986807df | ||
|
|
bb923621f9 | ||
|
|
b7bbccca73 | ||
|
|
f05e7c9e64 | ||
|
|
0583c8c1a3 | ||
|
|
0b8ee325aa | ||
|
|
ad879af3f9 | ||
|
|
020e09c4e1 | ||
|
|
022dde6c7c | ||
|
|
71c73dd493 | ||
|
|
33239034a7 | ||
|
|
a1205703f0 | ||
|
|
08bbe517c5 | ||
|
|
204c747c43 | ||
|
|
87d1a291f7 | ||
|
|
c338e05648 |
28
.vscode/c_cpp_properties.json
vendored
Normal file
28
.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"version": 4,
|
||||
"configurations": [
|
||||
{
|
||||
"name": "kernel",
|
||||
"defines": [
|
||||
"DEBUG",
|
||||
"true=1", // https://github.com/microsoft/vscode-cpptools/issues/10696
|
||||
"false=0" // https://github.com/microsoft/vscode-cpptools/issues/10696
|
||||
],
|
||||
"includePath": [
|
||||
"src/lib"
|
||||
],
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"compilerPath": "/usr/bin/x86_64-elf-gcc",
|
||||
"cStandard": "c23",
|
||||
"compilerArgs": [
|
||||
"-m64",
|
||||
"-ffreestanding",
|
||||
"-nostdinc",
|
||||
"-nostdlib",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wshadow"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
18
.vscode/launch.json
vendored
Normal file
18
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "gdb",
|
||||
"request": "attach",
|
||||
"name": "Attach to QEMU",
|
||||
"executable": "${workspaceFolder}/.build/kernel/kernel",
|
||||
"target": "localhost:1234",
|
||||
"remote": true,
|
||||
"cwd": "${workspaceRoot}",
|
||||
"gdbpath": "/usr/bin/gdb",
|
||||
"autorun": [
|
||||
"b _start"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
40
.vscode/settings.json
vendored
Normal file
40
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"bitset": "c",
|
||||
"algorithm": "c",
|
||||
"format": "c",
|
||||
"multiboot.h": "c",
|
||||
"irq.h": "c",
|
||||
"idt.h": "c",
|
||||
"util.h": "c",
|
||||
"deque": "c",
|
||||
"forward_list": "c",
|
||||
"list": "c",
|
||||
"string": "c",
|
||||
"unordered_map": "c",
|
||||
"unordered_set": "c",
|
||||
"vector": "c",
|
||||
"any": "c",
|
||||
"system_error": "c",
|
||||
"std.h": "c",
|
||||
"panic.h": "c",
|
||||
"pmm.h": "c",
|
||||
"array": "cpp",
|
||||
"string_view": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"ranges": "cpp",
|
||||
"span": "cpp",
|
||||
"valarray": "cpp",
|
||||
"alloc.h": "c",
|
||||
"arch.h": "c",
|
||||
"def.h": "c",
|
||||
"assert.h": "c",
|
||||
"io.h": "c",
|
||||
"mem.h": "c",
|
||||
"string.h": "c",
|
||||
"kernel.h": "c",
|
||||
"printf.h": "c",
|
||||
"vmm.h": "c",
|
||||
"console.h": "c"
|
||||
}
|
||||
}
|
||||
@@ -31,5 +31,5 @@ qemu-system-x86_64 -cdrom .build/nub-os.iso
|
||||
|
||||
```sh
|
||||
qemu-system-x86_64 -s -S -cdrom .build/nub-os.iso
|
||||
gdb -tui .build/kernel -ex "target remote localhost:1234"
|
||||
gdb -tui .build/kernel/kernel -ex "target remote localhost:1234"
|
||||
```
|
||||
|
||||
12
linker.ld
12
linker.ld
@@ -4,25 +4,27 @@ SECTIONS
|
||||
{
|
||||
. = 2M;
|
||||
|
||||
.text BLOCK(4K) : ALIGN(4K)
|
||||
.text :
|
||||
{
|
||||
*(.multiboot)
|
||||
*(.text)
|
||||
}
|
||||
|
||||
.rodata BLOCK(4K) : ALIGN(4K)
|
||||
.rodata :
|
||||
{
|
||||
*(.rodata)
|
||||
}
|
||||
|
||||
.data BLOCK(4K) : ALIGN(4K)
|
||||
.data :
|
||||
{
|
||||
*(.data)
|
||||
}
|
||||
|
||||
.bss BLOCK(4K) : ALIGN(4K)
|
||||
.bss :
|
||||
{
|
||||
*(COMMON)
|
||||
*(.bss)
|
||||
}
|
||||
}
|
||||
|
||||
kernel_end = .;
|
||||
}
|
||||
35
makefile
35
makefile
@@ -2,16 +2,20 @@ CC = x86_64-elf-gcc
|
||||
LD = x86_64-elf-ld
|
||||
AS = nasm
|
||||
|
||||
CFLAGS = -m64 -ffreestanding -fno-builtin -Wall -Wextra -Wshadow -std=c11 -g
|
||||
LDFLAGS = -g
|
||||
ASFLAGS = -f elf64 -g -F dwarf
|
||||
# Modify these settings here for defines and debug info
|
||||
CFLAGS = -g -D DEBUG
|
||||
LDFLAGS = -g
|
||||
ASFLAGS = -g -F dwarf
|
||||
|
||||
SRC_C := src/kernel.c src/mem.c src/pmm.c src/vmm.c src/string.c src/vga.c src/interrupts.c src/keyboard.c
|
||||
SRC_ASM := src/start.asm
|
||||
# Do not modify
|
||||
CFLAGS += -m64 -ffreestanding -nostdinc -nostdlib -Wall -Wextra -Wshadow -std=c23 -I src/lib
|
||||
LDFLAGS +=
|
||||
ASFLAGS += -f elf64
|
||||
|
||||
OBJ_C := $(SRC_C:src/%.c=.build/%.o)
|
||||
OBJ_ASM := $(SRC_ASM:src/%.asm=.build/%.o)
|
||||
OBJS := $(OBJ_C) $(OBJ_ASM)
|
||||
SRC_C := $(shell find src -name '*.c')
|
||||
SRC_ASM := $(shell find src -name '*.asm')
|
||||
|
||||
OBJS := $(patsubst src/%.c, .build/%.o, $(SRC_C)) $(patsubst src/%.asm, .build/%.o, $(SRC_ASM))
|
||||
|
||||
iso: .build/nub-os.iso
|
||||
@echo "ISO created at '.build/nub-os.iso'"
|
||||
@@ -19,20 +23,19 @@ iso: .build/nub-os.iso
|
||||
clean:
|
||||
@rm -r .build 2>/dev/null || true
|
||||
|
||||
build-dir:
|
||||
mkdir .build 2>/dev/null || true
|
||||
|
||||
.build/nub-os.iso: .build/kernel grub.cfg
|
||||
.build/nub-os.iso: .build/kernel/kernel grub.cfg
|
||||
mkdir -p .build/nub-os/boot/grub
|
||||
cp grub.cfg .build/nub-os/boot/grub
|
||||
cp .build/kernel .build/nub-os/boot/
|
||||
cp .build/kernel/kernel .build/nub-os/boot/
|
||||
grub-mkrescue -o .build/nub-os.iso .build/nub-os/
|
||||
|
||||
.build/kernel: $(OBJS)
|
||||
.build/kernel/kernel: $(OBJS)
|
||||
$(LD) $(LDFLAGS) -T linker.ld -o $@ $^
|
||||
|
||||
.build/%.o: src/%.c | build-dir
|
||||
.build/%.o: src/%.c
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
.build/%.o: src/%.asm | build-dir
|
||||
.build/%.o: src/%.asm
|
||||
@mkdir -p $(dir $@)
|
||||
$(AS) $(ASFLAGS) -o $@ $<
|
||||
93
src/arch/x86_64/arch.c
Normal file
93
src/arch/x86_64/arch.c
Normal file
@@ -0,0 +1,93 @@
|
||||
#include "console.h"
|
||||
#include "mem/pmm.h"
|
||||
#include "mem/vmm.h"
|
||||
#include "panic.h"
|
||||
#include "util.h"
|
||||
#include <arch.h>
|
||||
#include <def.h>
|
||||
#include <printf.h>
|
||||
|
||||
static void arch_halt()
|
||||
{
|
||||
halt();
|
||||
}
|
||||
|
||||
static void arch_disable_interrupts()
|
||||
{
|
||||
disable_interrupts();
|
||||
}
|
||||
|
||||
static void arch_enable_interrupts()
|
||||
{
|
||||
enable_interrupts();
|
||||
}
|
||||
|
||||
static void arch_panic()
|
||||
{
|
||||
panic();
|
||||
}
|
||||
|
||||
static void arch_console_putchar(char c)
|
||||
{
|
||||
console_putchar(c, VGA_DEFAULT_COLOR);
|
||||
}
|
||||
|
||||
static void arch_console_clear()
|
||||
{
|
||||
console_clear();
|
||||
}
|
||||
|
||||
static size_t arch_get_page_size()
|
||||
{
|
||||
return PAGE_SIZE;
|
||||
}
|
||||
|
||||
void* arch_alloc(size_t page_count)
|
||||
{
|
||||
u64 virtual_address = vmm_alloc_address(page_count);
|
||||
|
||||
for (size_t i = 0; i < page_count; i++)
|
||||
{
|
||||
u64 physical_address = pmm_alloc();
|
||||
if (!physical_address)
|
||||
{
|
||||
printf("Out of physical memory");
|
||||
panic();
|
||||
}
|
||||
|
||||
vmm_map(physical_address, virtual_address + (i * PAGE_SIZE), PTE_PRESENT | PTE_WRITABLE);
|
||||
}
|
||||
|
||||
return (void*)virtual_address;
|
||||
}
|
||||
|
||||
void arch_free(void* virtual_address, size_t page_count)
|
||||
{
|
||||
for (size_t i = 0; i < page_count; i++)
|
||||
{
|
||||
u64 physical_address = vmm_unmap((u64)virtual_address + (i * PAGE_SIZE));
|
||||
pmm_free(physical_address);
|
||||
}
|
||||
|
||||
vmm_free_address((u64)virtual_address, page_count);
|
||||
}
|
||||
|
||||
static const arch_mem_api_t arch_mem_api = {
|
||||
.get_page_size = arch_get_page_size,
|
||||
.alloc = arch_alloc,
|
||||
.free = arch_free,
|
||||
};
|
||||
|
||||
static const arch_console_api_t arch_console_api = {
|
||||
.putchar = arch_console_putchar,
|
||||
.clear = arch_console_clear,
|
||||
};
|
||||
|
||||
arch_api_t arch_api = {
|
||||
.halt = arch_halt,
|
||||
.disable_interrupts = arch_disable_interrupts,
|
||||
.enable_interrupts = arch_enable_interrupts,
|
||||
.panic = arch_panic,
|
||||
.console = arch_console_api,
|
||||
.mem = arch_mem_api,
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
global _start
|
||||
extern kmain
|
||||
extern handle_isr
|
||||
extern pml4
|
||||
global pml4
|
||||
extern x86_64_main
|
||||
extern kernel_end
|
||||
|
||||
%define FLAGS 0b10
|
||||
%define MAGIC 0x1BADB002
|
||||
@@ -41,20 +41,19 @@ section .data
|
||||
section .data
|
||||
align 8
|
||||
multiboot_info:
|
||||
dq 0
|
||||
dd 0
|
||||
multiboot_magic:
|
||||
dd 0
|
||||
kernel_page_count:
|
||||
dd 0
|
||||
|
||||
section .text
|
||||
bits 32
|
||||
_start:
|
||||
mov esp, stack_top
|
||||
|
||||
; Multiboot will place a magic value in eax
|
||||
; If this magic value is not present, then we might not have multiboot info in ebx,
|
||||
; therefore we throw an error
|
||||
cmp eax, 0x2BADB002
|
||||
jne error
|
||||
|
||||
; Save multiboot info pointer for later
|
||||
mov [multiboot_magic], eax
|
||||
mov [multiboot_info], ebx
|
||||
|
||||
; Check if cpuid is available by flipping bit 21
|
||||
@@ -102,17 +101,25 @@ section .text
|
||||
mov eax, pd
|
||||
or eax, 0x03
|
||||
mov [pdpt], eax
|
||||
; Map first 32 2mb pages for the kernel for a total of 64mb
|
||||
|
||||
; Calculate how many 2mb pages we need to identity map
|
||||
mov ecx, kernel_end
|
||||
add ecx, 0x1FFFFF ; Page align end of kernel
|
||||
shr ecx, 21 ; ecx now holds the required pages
|
||||
|
||||
; Save the page count so we can pass it to c later
|
||||
mov [kernel_page_count], ecx
|
||||
|
||||
; Identity map the 0x0 to kernel_end
|
||||
mov edi, pd
|
||||
mov eax, 0x83
|
||||
mov ecx, 32
|
||||
.setup_pd:
|
||||
mov [edi], eax
|
||||
add eax, 0x200000
|
||||
add edi, 8
|
||||
loop .setup_pd
|
||||
|
||||
; Load cr3 with the address of pml4
|
||||
; Tell the cpu where pml4 is
|
||||
mov eax, pml4
|
||||
mov cr3, eax
|
||||
|
||||
@@ -140,39 +147,6 @@ section .text
|
||||
hlt
|
||||
jmp .hang
|
||||
|
||||
section .bss
|
||||
align 4096
|
||||
idt64:
|
||||
resb 4096
|
||||
|
||||
section .data
|
||||
align 8
|
||||
idt64_descriptor:
|
||||
dw 4095
|
||||
dq idt64
|
||||
|
||||
section .data
|
||||
align 8
|
||||
isr_stub_table:
|
||||
%assign i 0
|
||||
%rep 256
|
||||
dq isr_stub_%[i]
|
||||
%assign i i+1
|
||||
%endrep
|
||||
|
||||
%macro ISR_NOERR 1
|
||||
isr_stub_%1:
|
||||
push qword 0
|
||||
push qword %1
|
||||
jmp isr_common
|
||||
%endmacro
|
||||
|
||||
%macro ISR_ERR 1
|
||||
isr_stub_%1:
|
||||
push qword %1
|
||||
jmp isr_common
|
||||
%endmacro
|
||||
|
||||
section .text
|
||||
bits 64
|
||||
long_mode:
|
||||
@@ -184,108 +158,11 @@ section .text
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
|
||||
; Fill in the idt table with the stub functions
|
||||
mov rdi, idt64
|
||||
mov rsi, isr_stub_table
|
||||
mov rcx, 256
|
||||
.loop:
|
||||
mov rax, [rsi]
|
||||
mov [rdi], ax
|
||||
mov word [rdi + 2], 0x08
|
||||
mov word [rdi + 4], 0x8E00
|
||||
shr rax, 16
|
||||
mov [rdi + 6], ax
|
||||
shr rax, 16
|
||||
mov [rdi + 8], eax
|
||||
mov dword [rdi + 12], 0
|
||||
add rdi, 16
|
||||
add rsi, 8
|
||||
loop .loop
|
||||
lidt [idt64_descriptor]
|
||||
|
||||
; Finally, we call in to c
|
||||
mov rdi, [multiboot_info]
|
||||
call kmain
|
||||
mov edi, [multiboot_magic]
|
||||
mov esi, [multiboot_info]
|
||||
mov edx, [kernel_page_count]
|
||||
call x86_64_main
|
||||
.hang:
|
||||
hlt
|
||||
jmp .hang
|
||||
|
||||
isr_common:
|
||||
push rax
|
||||
push rbx
|
||||
push rcx
|
||||
push rdx
|
||||
push rsi
|
||||
push rdi
|
||||
push rbp
|
||||
push r8
|
||||
push r9
|
||||
push r10
|
||||
push r11
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
push r15
|
||||
|
||||
mov rdi, rsp
|
||||
call handle_isr
|
||||
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop r11
|
||||
pop r10
|
||||
pop r9
|
||||
pop r8
|
||||
pop rbp
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop rbx
|
||||
pop rax
|
||||
|
||||
add rsp, 16
|
||||
iretq
|
||||
|
||||
; CPU exceptions 0-31. Some of these contain error codes, so we define them manually
|
||||
ISR_NOERR 0
|
||||
ISR_NOERR 1
|
||||
ISR_NOERR 2
|
||||
ISR_NOERR 3
|
||||
ISR_NOERR 4
|
||||
ISR_NOERR 5
|
||||
ISR_NOERR 6
|
||||
ISR_NOERR 7
|
||||
ISR_ERR 8
|
||||
ISR_NOERR 9
|
||||
ISR_ERR 10
|
||||
ISR_ERR 11
|
||||
ISR_ERR 12
|
||||
ISR_ERR 13
|
||||
ISR_ERR 14
|
||||
ISR_NOERR 15
|
||||
ISR_NOERR 16
|
||||
ISR_ERR 17
|
||||
ISR_NOERR 18
|
||||
ISR_NOERR 19
|
||||
ISR_NOERR 20
|
||||
ISR_NOERR 21
|
||||
ISR_NOERR 22
|
||||
ISR_NOERR 23
|
||||
ISR_NOERR 24
|
||||
ISR_NOERR 25
|
||||
ISR_NOERR 26
|
||||
ISR_NOERR 27
|
||||
ISR_NOERR 28
|
||||
ISR_NOERR 29
|
||||
ISR_ERR 30
|
||||
ISR_NOERR 31
|
||||
|
||||
; Hardware interrupts and user-defined interrupts (32-255). These don't have error codes
|
||||
%assign i 32
|
||||
%rep 224
|
||||
ISR_NOERR i
|
||||
%assign i i+1
|
||||
%endrep
|
||||
jmp .hang
|
||||
@@ -1,22 +1,21 @@
|
||||
#include "vga.h"
|
||||
#include <stddef.h>
|
||||
#include "console.h"
|
||||
|
||||
#define ROWS 25
|
||||
#define COLUMNS 80
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t character;
|
||||
uint8_t color;
|
||||
u8 character;
|
||||
u8 color;
|
||||
} vga_char_t;
|
||||
|
||||
static vga_char_t* vga_buffer = (vga_char_t*)0xb8000;
|
||||
static uint8_t cursor_row = 0;
|
||||
static uint8_t cursor_col = 0;
|
||||
static u8 cursor_row = 0;
|
||||
static u8 cursor_col = 0;
|
||||
|
||||
void vga_put_char(char character, uint8_t color)
|
||||
void console_putchar(char c, u8 color)
|
||||
{
|
||||
switch (character)
|
||||
switch (c)
|
||||
{
|
||||
case '\n':
|
||||
{
|
||||
@@ -31,7 +30,7 @@ void vga_put_char(char character, uint8_t color)
|
||||
}
|
||||
case '\t':
|
||||
{
|
||||
uint8_t remainder = 4 - (cursor_col % 4);
|
||||
u8 remainder = 4 - (cursor_col % 4);
|
||||
cursor_col += remainder;
|
||||
break;
|
||||
}
|
||||
@@ -66,7 +65,7 @@ void vga_put_char(char character, uint8_t color)
|
||||
default:
|
||||
{
|
||||
vga_buffer[COLUMNS * cursor_row + cursor_col] = (vga_char_t){
|
||||
.character = character,
|
||||
.character = c,
|
||||
.color = color,
|
||||
};
|
||||
cursor_col += 1;
|
||||
@@ -102,7 +101,7 @@ void vga_put_char(char character, uint8_t color)
|
||||
}
|
||||
}
|
||||
|
||||
void vga_clear()
|
||||
void console_clear()
|
||||
{
|
||||
for (size_t row = 0; row < ROWS; row++)
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <def.h>
|
||||
|
||||
#define VGA_BLACK 0
|
||||
#define VGA_BLUE 1
|
||||
@@ -21,5 +21,5 @@
|
||||
|
||||
#define VGA_DEFAULT_COLOR VGA_LIGHT_GRAY | VGA_BLACK << 4
|
||||
|
||||
void vga_put_char(char character, uint8_t color);
|
||||
void vga_clear();
|
||||
void console_putchar(char character, u8 color);
|
||||
void console_clear();
|
||||
44
src/arch/x86_64/interrupts/exceptions.c
Normal file
44
src/arch/x86_64/interrupts/exceptions.c
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "exceptions.h"
|
||||
#include "../panic.h"
|
||||
#include <printf.h>
|
||||
|
||||
static const char* exception_messages[32] = {
|
||||
"divide by zero",
|
||||
"debug",
|
||||
"non maskable interrupt",
|
||||
"breakpoint",
|
||||
"overflow",
|
||||
"bound range exceeded",
|
||||
"invalid opcode",
|
||||
"device not available",
|
||||
"double fault",
|
||||
"coprocessor segment overrun",
|
||||
"invalid tss",
|
||||
"segment not present",
|
||||
"stack-segment fault",
|
||||
"general protection fault",
|
||||
"page fault",
|
||||
"reserved",
|
||||
"x87 floating point exception",
|
||||
"alignment check",
|
||||
"machine check",
|
||||
"simd floating point exception",
|
||||
"virtualization exception",
|
||||
"control protection exception",
|
||||
"reserved",
|
||||
"reserved",
|
||||
"reserved",
|
||||
"reserved",
|
||||
"reserved",
|
||||
"reserved",
|
||||
"hypervisor injection exception",
|
||||
"vmm communication exception",
|
||||
"security exception",
|
||||
"reserved",
|
||||
};
|
||||
|
||||
void handle_exception(const isr_frame_t* frame)
|
||||
{
|
||||
printf("exception[%d]: %s, error code: %d\n", frame->int_no, exception_messages[frame->int_no], frame->err_code);
|
||||
panic();
|
||||
}
|
||||
5
src/arch/x86_64/interrupts/exceptions.h
Normal file
5
src/arch/x86_64/interrupts/exceptions.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "isr.h"
|
||||
|
||||
void handle_exception(const isr_frame_t* frame);
|
||||
136
src/arch/x86_64/interrupts/idt.asm
Normal file
136
src/arch/x86_64/interrupts/idt.asm
Normal file
@@ -0,0 +1,136 @@
|
||||
global idt_init
|
||||
extern handle_isr
|
||||
|
||||
%macro ISR_NOERR 1
|
||||
isr_stub_%1:
|
||||
push qword 0
|
||||
push qword %1
|
||||
jmp isr_common
|
||||
%endmacro
|
||||
|
||||
%macro ISR_ERR 1
|
||||
isr_stub_%1:
|
||||
push qword %1
|
||||
jmp isr_common
|
||||
%endmacro
|
||||
|
||||
section .bss
|
||||
align 4096
|
||||
idt64:
|
||||
resb 4096
|
||||
|
||||
section .data
|
||||
align 8
|
||||
isr_stub_table:
|
||||
%assign i 0
|
||||
%rep 256
|
||||
dq isr_stub_%[i]
|
||||
%assign i i+1
|
||||
%endrep
|
||||
|
||||
idt64_descriptor:
|
||||
dw 4095
|
||||
dq idt64
|
||||
|
||||
section .text
|
||||
bits 64
|
||||
idt_init:
|
||||
; Fill in the idt table with the stub functions
|
||||
mov rdi, idt64
|
||||
mov rsi, isr_stub_table
|
||||
mov rcx, 256
|
||||
.loop:
|
||||
mov rax, [rsi]
|
||||
mov [rdi], ax
|
||||
mov word [rdi + 2], 0x08
|
||||
mov word [rdi + 4], 0x8E00
|
||||
shr rax, 16
|
||||
mov [rdi + 6], ax
|
||||
shr rax, 16
|
||||
mov [rdi + 8], eax
|
||||
mov dword [rdi + 12], 0
|
||||
add rdi, 16
|
||||
add rsi, 8
|
||||
loop .loop
|
||||
lidt [idt64_descriptor]
|
||||
ret
|
||||
|
||||
isr_common:
|
||||
push rax
|
||||
push rbx
|
||||
push rcx
|
||||
push rdx
|
||||
push rsi
|
||||
push rdi
|
||||
push rbp
|
||||
push r8
|
||||
push r9
|
||||
push r10
|
||||
push r11
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
push r15
|
||||
|
||||
mov rdi, rsp
|
||||
call handle_isr
|
||||
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop r11
|
||||
pop r10
|
||||
pop r9
|
||||
pop r8
|
||||
pop rbp
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop rbx
|
||||
pop rax
|
||||
|
||||
add rsp, 16
|
||||
iretq
|
||||
|
||||
; CPU exceptions 0-31. Some of these contain error codes, so we define them manually
|
||||
ISR_NOERR 0
|
||||
ISR_NOERR 1
|
||||
ISR_NOERR 2
|
||||
ISR_NOERR 3
|
||||
ISR_NOERR 4
|
||||
ISR_NOERR 5
|
||||
ISR_NOERR 6
|
||||
ISR_NOERR 7
|
||||
ISR_ERR 8
|
||||
ISR_NOERR 9
|
||||
ISR_ERR 10
|
||||
ISR_ERR 11
|
||||
ISR_ERR 12
|
||||
ISR_ERR 13
|
||||
ISR_ERR 14
|
||||
ISR_NOERR 15
|
||||
ISR_NOERR 16
|
||||
ISR_ERR 17
|
||||
ISR_NOERR 18
|
||||
ISR_NOERR 19
|
||||
ISR_NOERR 20
|
||||
ISR_NOERR 21
|
||||
ISR_NOERR 22
|
||||
ISR_NOERR 23
|
||||
ISR_NOERR 24
|
||||
ISR_NOERR 25
|
||||
ISR_NOERR 26
|
||||
ISR_NOERR 27
|
||||
ISR_NOERR 28
|
||||
ISR_NOERR 29
|
||||
ISR_ERR 30
|
||||
ISR_NOERR 31
|
||||
|
||||
; Hardware interrupts and user-defined interrupts (32-255). These don't have error codes
|
||||
%assign i 32
|
||||
%rep 224
|
||||
ISR_NOERR i
|
||||
%assign i i+1
|
||||
%endrep
|
||||
3
src/arch/x86_64/interrupts/idt.h
Normal file
3
src/arch/x86_64/interrupts/idt.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
extern void idt_init();
|
||||
57
src/arch/x86_64/interrupts/irq.c
Normal file
57
src/arch/x86_64/interrupts/irq.c
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "irq.h"
|
||||
#include "../util.h"
|
||||
#include <printf.h>
|
||||
|
||||
#define PIC1_COMMAND 0x20
|
||||
#define PIC1_DATA 0x21
|
||||
#define PIC2_COMMAND 0xA0
|
||||
#define PIC2_DATA 0xA1
|
||||
|
||||
static irq_handler_t irq_handlers[16] = {0};
|
||||
|
||||
void remap_pic()
|
||||
{
|
||||
outb(PIC1_COMMAND, 0x11);
|
||||
outb(PIC2_COMMAND, 0x11);
|
||||
|
||||
outb(PIC1_DATA, 32);
|
||||
outb(PIC2_DATA, 40);
|
||||
|
||||
outb(PIC1_DATA, 4);
|
||||
outb(PIC2_DATA, 2);
|
||||
|
||||
outb(PIC1_DATA, 1);
|
||||
outb(PIC2_DATA, 1);
|
||||
|
||||
outb(PIC1_DATA, 0);
|
||||
outb(PIC2_DATA, 0);
|
||||
}
|
||||
|
||||
void register_irq_handler(u8 irq, irq_handler_t handler)
|
||||
{
|
||||
if (irq >= 16)
|
||||
{
|
||||
printf("Cannot register irq %d is out of bounds\n", irq);
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_handlers[irq] = handler;
|
||||
}
|
||||
}
|
||||
|
||||
void handle_irq(const isr_frame_t* frame)
|
||||
{
|
||||
u8 irq = frame->int_no - 32;
|
||||
|
||||
if (irq_handlers[irq])
|
||||
{
|
||||
irq_handlers[irq](frame);
|
||||
}
|
||||
|
||||
if (irq >= 8)
|
||||
{
|
||||
outb(PIC2_COMMAND, 0x20);
|
||||
}
|
||||
|
||||
outb(PIC1_COMMAND, 0x20);
|
||||
}
|
||||
9
src/arch/x86_64/interrupts/irq.h
Normal file
9
src/arch/x86_64/interrupts/irq.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "isr.h"
|
||||
|
||||
typedef void (*irq_handler_t)(const isr_frame_t*);
|
||||
|
||||
void remap_pic();
|
||||
void handle_irq(const isr_frame_t* frame);
|
||||
void register_irq_handler(u8 irq, irq_handler_t handler);
|
||||
21
src/arch/x86_64/interrupts/isr.c
Normal file
21
src/arch/x86_64/interrupts/isr.c
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "isr.h"
|
||||
#include "exceptions.h"
|
||||
#include "irq.h"
|
||||
#include <def.h>
|
||||
#include <printf.h>
|
||||
|
||||
void handle_isr(const isr_frame_t* frame)
|
||||
{
|
||||
if (frame->int_no < 32)
|
||||
{
|
||||
handle_exception(frame);
|
||||
}
|
||||
else if (frame->int_no < 48)
|
||||
{
|
||||
handle_irq(frame);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("interrupt[%d]: not implemented\n", frame->int_no);
|
||||
}
|
||||
}
|
||||
12
src/arch/x86_64/interrupts/isr.h
Normal file
12
src/arch/x86_64/interrupts/isr.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <def.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u64 r15, r14, r13, r12, r11, r10, r9, r8;
|
||||
u64 rbp, rdi, rsi, rdx, rcx, rbx, rax;
|
||||
u64 int_no;
|
||||
u64 err_code;
|
||||
u64 rip, cs, rflags, rsp, ss;
|
||||
} __attribute__((packed)) isr_frame_t;
|
||||
35
src/arch/x86_64/main.c
Normal file
35
src/arch/x86_64/main.c
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "console.h"
|
||||
#include "interrupts/idt.h"
|
||||
#include "interrupts/irq.h"
|
||||
#include "mem/pmm.h"
|
||||
#include "mem/vmm.h"
|
||||
#include "panic.h"
|
||||
#include "util.h"
|
||||
#include <printf.h>
|
||||
|
||||
extern void kernel_main();
|
||||
|
||||
void x86_64_main(u32 magic, multiboot_info_t* info, u32 kernel_page_count)
|
||||
{
|
||||
console_clear();
|
||||
|
||||
if (magic != 0x2BADB002)
|
||||
{
|
||||
printf("Multiboot magic does not match\n");
|
||||
panic();
|
||||
}
|
||||
|
||||
if (info == NULL)
|
||||
{
|
||||
printf("Multiboot info is NULL\n");
|
||||
panic();
|
||||
}
|
||||
|
||||
idt_init();
|
||||
remap_pic();
|
||||
|
||||
pmm_init(kernel_page_count, info);
|
||||
vmm_init(kernel_page_count);
|
||||
|
||||
kernel_main();
|
||||
}
|
||||
127
src/arch/x86_64/mem/pmm.c
Normal file
127
src/arch/x86_64/mem/pmm.c
Normal file
@@ -0,0 +1,127 @@
|
||||
#include "pmm.h"
|
||||
#include "../panic.h"
|
||||
#include <mem.h>
|
||||
#include <printf.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u64 base_address;
|
||||
size_t length;
|
||||
} memory_region_t;
|
||||
|
||||
#define USABLE_REGION_SIZE 32
|
||||
|
||||
static memory_region_t usable_regions[USABLE_REGION_SIZE];
|
||||
static size_t num_regions = 0;
|
||||
|
||||
#define BITMAP_PAGE_COUNT (MAX_MEMORY / PAGE_SIZE)
|
||||
#define BITMAP_SIZE (BITMAP_PAGE_COUNT / 8)
|
||||
|
||||
static u8 page_bitmap[BITMAP_SIZE];
|
||||
|
||||
void pmm_init(u32 kernel_page_count, multiboot_info_t* info)
|
||||
{
|
||||
if (!(info->flags & (1 << 6)))
|
||||
{
|
||||
printf("Invalid memory map given by bootloader\n");
|
||||
panic();
|
||||
}
|
||||
|
||||
u64 offset = 0;
|
||||
while (offset < info->mmap_length)
|
||||
{
|
||||
multiboot_memory_map_t* mmmt = (multiboot_memory_map_t*)(info->mmap_addr + offset);
|
||||
|
||||
if (mmmt->type == MULTIBOOT_MEMORY_AVAILABLE)
|
||||
{
|
||||
if (num_regions < USABLE_REGION_SIZE)
|
||||
{
|
||||
usable_regions[num_regions] = (memory_region_t){
|
||||
.base_address = mmmt->addr,
|
||||
.length = mmmt->len,
|
||||
};
|
||||
|
||||
num_regions++;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("System has more memory than the usable memory map can hold\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
offset += mmmt->size + sizeof(mmmt->size);
|
||||
}
|
||||
|
||||
memset(page_bitmap, 0xFF, BITMAP_SIZE);
|
||||
|
||||
for (size_t i = 0; i < num_regions; i++)
|
||||
{
|
||||
memory_region_t region = usable_regions[i];
|
||||
|
||||
u64 start_page = region.base_address / PAGE_SIZE;
|
||||
u64 num_pages = region.length / PAGE_SIZE;
|
||||
|
||||
for (u64 page = start_page; page < start_page + num_pages; page++)
|
||||
{
|
||||
if (page < BITMAP_PAGE_COUNT)
|
||||
{
|
||||
page_bitmap[page / 8] &= ~(1 << (page % 8));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("System has more ram than the bitmap allows!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The kernel was identity mapped by the kernel, so those bits should be marked as unavailable
|
||||
for (u64 page = 0; page < kernel_page_count; page++)
|
||||
{
|
||||
if (page >= BITMAP_PAGE_COUNT)
|
||||
{
|
||||
printf("Bitmap is not large enough to hold the memory reserved by the kernel");
|
||||
panic();
|
||||
}
|
||||
|
||||
page_bitmap[page / 8] |= (1 << (page % 8));
|
||||
}
|
||||
}
|
||||
|
||||
u64 pmm_alloc()
|
||||
{
|
||||
for (size_t i = 0; i < BITMAP_SIZE; i++)
|
||||
{
|
||||
if (page_bitmap[i] != 0xFF)
|
||||
{
|
||||
for (int bit = 0; bit < 8; bit++)
|
||||
{
|
||||
if (!(page_bitmap[i] & (1 << bit)))
|
||||
{
|
||||
page_bitmap[i] |= (1 << bit);
|
||||
return ((i * 8 + bit) * PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pmm_free(u64 physical_address)
|
||||
{
|
||||
u64 page = physical_address / PAGE_SIZE;
|
||||
if (page < BITMAP_SIZE * 8)
|
||||
{
|
||||
if (page_bitmap[page / 8] & (1 << (page % 8)))
|
||||
{
|
||||
page_bitmap[page / 8] &= ~(1 << (page % 8));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Physical address %x is already free", physical_address);
|
||||
panic();
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/arch/x86_64/mem/pmm.h
Normal file
20
src/arch/x86_64/mem/pmm.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "../multiboot.h"
|
||||
#include <def.h>
|
||||
|
||||
// todo(nub31): Fixed at 2mb for now, might add support for 4k pages later
|
||||
#define PAGE_SIZE MiB(2)
|
||||
|
||||
// Defines the theoretical max memory the kernel can allocate, not the actual memory of the system
|
||||
// The value must be a multible of 8
|
||||
#define MAX_MEMORY GiB(64)
|
||||
|
||||
void pmm_init(u32 kernel_page_count, multiboot_info_t* info);
|
||||
|
||||
// Low level function to allocate a 2mb physical page and return the physical address
|
||||
// A return value 0 indicates out of memory
|
||||
u64 pmm_alloc();
|
||||
|
||||
// Low level function to free a 2mb physical page
|
||||
void pmm_free(u64 physical_address);
|
||||
185
src/arch/x86_64/mem/vmm.c
Normal file
185
src/arch/x86_64/mem/vmm.c
Normal file
@@ -0,0 +1,185 @@
|
||||
#include "vmm.h"
|
||||
#include "../panic.h"
|
||||
#include "pmm.h"
|
||||
#include <printf.h>
|
||||
|
||||
#define PML4_INDEX(addr) (((addr) >> 39) & 0x1FF)
|
||||
#define PDPT_INDEX(addr) (((addr) >> 30) & 0x1FF)
|
||||
#define PD_INDEX(addr) (((addr) >> 21) & 0x1FF)
|
||||
|
||||
#define PTE_MASK 0x000FFFFFFFFFF000ULL
|
||||
|
||||
#define BITMAP_PAGE_COUNT (ADDRES_SPACE_SIZE / PAGE_SIZE)
|
||||
#define BITMAP_SIZE (BITMAP_PAGE_COUNT / 8)
|
||||
|
||||
static u8 page_bitmap[BITMAP_SIZE];
|
||||
|
||||
extern u64 pml4[];
|
||||
|
||||
void vmm_init(u32 kernel_page_count)
|
||||
{
|
||||
// The kernel was identity mapped when we entered long mode,
|
||||
// so those virtual addresses should be marked as unavailable
|
||||
for (u64 page = 0; page < kernel_page_count; page++)
|
||||
{
|
||||
if (page >= BITMAP_PAGE_COUNT)
|
||||
{
|
||||
printf("Bitmap is not large enough to hold the addresses reserved by the kernel");
|
||||
panic();
|
||||
}
|
||||
|
||||
page_bitmap[page / 8] |= (1 << (page % 8));
|
||||
}
|
||||
}
|
||||
|
||||
u64 vmm_alloc_address(size_t page_count)
|
||||
{
|
||||
size_t total_pages = BITMAP_PAGE_COUNT;
|
||||
|
||||
for (size_t start_page = 0; start_page <= total_pages - page_count; start_page++)
|
||||
{
|
||||
bool found_block = true;
|
||||
|
||||
for (size_t i = 0; i < page_count; i++)
|
||||
{
|
||||
size_t page = start_page + i;
|
||||
size_t byte_index = page / 8;
|
||||
size_t bit_index = page % 8;
|
||||
|
||||
if (page_bitmap[byte_index] & (1 << bit_index))
|
||||
{
|
||||
found_block = false;
|
||||
start_page = page;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_block)
|
||||
{
|
||||
for (size_t i = 0; i < page_count; i++)
|
||||
{
|
||||
size_t page = start_page + i;
|
||||
size_t byte_index = page / 8;
|
||||
size_t bit_index = page % 8;
|
||||
page_bitmap[byte_index] |= (1 << bit_index);
|
||||
}
|
||||
|
||||
return start_page * PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vmm_free_address(u64 virtual_address, size_t page_count)
|
||||
{
|
||||
u64 start_page = virtual_address / PAGE_SIZE;
|
||||
if (start_page + page_count > BITMAP_PAGE_COUNT)
|
||||
{
|
||||
printf("Virtual address range exceeds bitmap bounds\n");
|
||||
panic();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < page_count; i++)
|
||||
{
|
||||
size_t page = start_page + i;
|
||||
size_t byte_index = page / 8;
|
||||
size_t bit_index = page % 8;
|
||||
|
||||
if (!(page_bitmap[byte_index] & (1 << bit_index)))
|
||||
{
|
||||
printf("Virtual address 0x%x (page %u) is already free\n", virtual_address + (i * PAGE_SIZE), page);
|
||||
panic();
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < page_count; i++)
|
||||
{
|
||||
size_t page = start_page + i;
|
||||
size_t byte_index = page / 8;
|
||||
size_t bit_index = page % 8;
|
||||
page_bitmap[byte_index] &= ~(1 << bit_index);
|
||||
}
|
||||
}
|
||||
|
||||
static u64 create_2mb_pte(u64 physical_address, u32 flags)
|
||||
{
|
||||
if (physical_address & (PAGE_SIZE - 1))
|
||||
{
|
||||
printf("Physical address not page aligned (0x%x)\n", physical_address);
|
||||
panic();
|
||||
}
|
||||
|
||||
return (physical_address & PTE_MASK) | flags | PTE_PS;
|
||||
}
|
||||
|
||||
void vmm_map(u64 physical_address, u64 virtual_address, u32 flags)
|
||||
{
|
||||
u64 pml4_idx = PML4_INDEX(virtual_address);
|
||||
u64 pdpt_idx = PDPT_INDEX(virtual_address);
|
||||
u64 pd_idx = PD_INDEX(virtual_address);
|
||||
|
||||
u64 pdpt = pml4[pml4_idx];
|
||||
if (!(pdpt & PTE_PRESENT))
|
||||
{
|
||||
// todo(nub31): Dynamically create a pdpt table
|
||||
printf("PDPT not present at PML4 index %u\n", pml4_idx);
|
||||
panic();
|
||||
}
|
||||
|
||||
u64* pdpt_physical_address = (u64*)(pdpt & PTE_MASK);
|
||||
u64 pd = pdpt_physical_address[pdpt_idx];
|
||||
if (!(pd & PTE_PRESENT))
|
||||
{
|
||||
// todo(nub31): Dynamically create a pd table
|
||||
printf("PD not present at PDPT index %u\n", pdpt_idx);
|
||||
panic();
|
||||
}
|
||||
|
||||
u64* pd_physical_address = (u64*)(pd & PTE_MASK);
|
||||
u64 entry = pd_physical_address[pd_idx];
|
||||
|
||||
if (entry & PTE_PRESENT)
|
||||
{
|
||||
printf("Virtual address 0x%x is already mapped\n", virtual_address);
|
||||
panic();
|
||||
}
|
||||
|
||||
pd_physical_address[pd_idx] = create_2mb_pte(physical_address, flags);
|
||||
}
|
||||
|
||||
u64 vmm_unmap(u64 virtual_address)
|
||||
{
|
||||
u64 pml4_idx = PML4_INDEX(virtual_address);
|
||||
u64 pdpt_idx = PDPT_INDEX(virtual_address);
|
||||
u64 pd_idx = PD_INDEX(virtual_address);
|
||||
|
||||
u64 pdpt_entry = pml4[pml4_idx];
|
||||
if (!(pdpt_entry & PTE_PRESENT))
|
||||
{
|
||||
printf("PDPT not present at PML4 index %llu\n", pml4_idx);
|
||||
panic();
|
||||
}
|
||||
|
||||
u64* pdpt_physical_address = (u64*)(pdpt_entry & PTE_MASK);
|
||||
u64 pd_entry = pdpt_physical_address[pdpt_idx];
|
||||
if (!(pd_entry & PTE_PRESENT))
|
||||
{
|
||||
printf("PD not present at PDPT index %llu\n", pdpt_idx);
|
||||
panic();
|
||||
}
|
||||
|
||||
u64* pd_physical_address = (u64*)(pd_entry & PTE_MASK);
|
||||
if (!(pd_physical_address[pd_idx] & PTE_PRESENT))
|
||||
{
|
||||
printf("Virtual address 0x%llx is not mapped\n", virtual_address);
|
||||
panic();
|
||||
}
|
||||
|
||||
u64 physical_address = pd_physical_address[pd_idx] & PTE_MASK;
|
||||
pd_physical_address[pd_idx] = 0;
|
||||
|
||||
__asm__ volatile("invlpg (%0)" : : "r"(virtual_address) : "memory");
|
||||
|
||||
return physical_address;
|
||||
}
|
||||
27
src/arch/x86_64/mem/vmm.h
Normal file
27
src/arch/x86_64/mem/vmm.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <def.h>
|
||||
|
||||
// Defines the theoretical max virtual memory space the kernel can allocate
|
||||
// The value must be a multible of 8
|
||||
#define ADDRES_SPACE_SIZE GiB(64)
|
||||
|
||||
#define PTE_PRESENT (1ULL << 0)
|
||||
#define PTE_WRITABLE (1ULL << 1)
|
||||
#define PTE_USER (1ULL << 2)
|
||||
#define PTE_PS (1ULL << 7)
|
||||
|
||||
void vmm_init(u32 kernel_page_count);
|
||||
|
||||
// Allocates a free page aligned block of virtual addresses
|
||||
// A return value 0 indicates that there were not blocks
|
||||
// found which is large enought for the amount of pages requested
|
||||
u64 vmm_alloc_address(size_t page_count);
|
||||
// Frees a block of virtual addresses previously allocated via `vmm_alloc_address`
|
||||
// Only use this function for pages mapped via `vmm_alloc_address`
|
||||
void vmm_free_address(u64 virtual_address, size_t page_count);
|
||||
|
||||
// Low level function to map a virtual address to a physical address
|
||||
void vmm_map(u64 physical_address, u64 virtual_address, u32 flags);
|
||||
// Low level function to unmap a virtual address from a physical address
|
||||
u64 vmm_unmap(u64 virtual_address);
|
||||
8
src/arch/x86_64/panic.c
Normal file
8
src/arch/x86_64/panic.c
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "panic.h"
|
||||
#include "util.h"
|
||||
|
||||
void panic()
|
||||
{
|
||||
disable_interrupts();
|
||||
halt();
|
||||
}
|
||||
3
src/arch/x86_64/panic.h
Normal file
3
src/arch/x86_64/panic.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void panic();
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <def.h>
|
||||
|
||||
enum
|
||||
{
|
||||
@@ -68,14 +68,14 @@ enum
|
||||
CPUID_FEAT_EDX_PBE = 1 << 31
|
||||
};
|
||||
|
||||
static inline void outb(uint16_t port, uint8_t val)
|
||||
static inline void outb(u16 port, u8 val)
|
||||
{
|
||||
__asm__ volatile("outb %0, %1" : : "a"(val), "Nd"(port));
|
||||
}
|
||||
|
||||
static inline uint8_t inb(uint16_t port)
|
||||
static inline u8 inb(u16 port)
|
||||
{
|
||||
uint8_t ret;
|
||||
u8 ret;
|
||||
__asm__ volatile("inb %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
@@ -85,21 +85,39 @@ static inline void io_wait()
|
||||
outb(0x80, 0);
|
||||
}
|
||||
|
||||
static inline void cpuid(uint32_t code, uint32_t* a, uint32_t* d)
|
||||
static inline void cpuid(u32 code, u32* a, u32* d)
|
||||
{
|
||||
__asm__ volatile("cpuid" : "=a"(*a), "=d"(*d) : "a"(code) : "ecx", "ebx");
|
||||
}
|
||||
|
||||
static inline uint64_t rdmsr(uint32_t msr)
|
||||
static inline u64 rdmsr(u32 msr)
|
||||
{
|
||||
uint32_t lo, hi;
|
||||
u32 lo, hi;
|
||||
__asm__ volatile("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr));
|
||||
return ((uint64_t)hi << 32) | lo;
|
||||
return ((u64)hi << 32) | lo;
|
||||
}
|
||||
|
||||
static inline void wrmsr(uint32_t msr, uint64_t value)
|
||||
static inline void wrmsr(u32 msr, u64 value)
|
||||
{
|
||||
uint32_t lo = (uint32_t)value;
|
||||
uint32_t hi = (uint32_t)(value >> 32);
|
||||
u32 lo = (u32)value;
|
||||
u32 hi = (u32)(value >> 32);
|
||||
__asm__ volatile("wrmsr" : : "c"(msr), "a"(lo), "d"(hi));
|
||||
}
|
||||
|
||||
static inline void halt()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
__asm__ volatile("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void enable_interrupts()
|
||||
{
|
||||
__asm__ volatile("sti");
|
||||
}
|
||||
|
||||
static inline void disable_interrupts()
|
||||
{
|
||||
__asm__ volatile("cli");
|
||||
}
|
||||
137
src/interrupts.c
137
src/interrupts.c
@@ -1,137 +0,0 @@
|
||||
#include "interrupts.h"
|
||||
#include "util.h"
|
||||
|
||||
#define PIC1_COMMAND 0x20
|
||||
#define PIC1_DATA 0x21
|
||||
#define PIC2_COMMAND 0xA0
|
||||
#define PIC2_DATA 0xA1
|
||||
|
||||
static const char* exception_messages[32] = {
|
||||
"divide by zero",
|
||||
"debug",
|
||||
"non maskable interrupt",
|
||||
"breakpoint",
|
||||
"overflow",
|
||||
"bound range exceeded",
|
||||
"invalid opcode",
|
||||
"device not available",
|
||||
"double fault",
|
||||
"coprocessor segment overrun",
|
||||
"invalid tss",
|
||||
"segment not present",
|
||||
"stack-segment fault",
|
||||
"general protection fault",
|
||||
"page fault",
|
||||
"reserved",
|
||||
"x87 floating point exception",
|
||||
"alignment check",
|
||||
"machine check",
|
||||
"simd floating point exception",
|
||||
"virtualization exception",
|
||||
"control protection exception",
|
||||
"reserved",
|
||||
"reserved",
|
||||
"reserved",
|
||||
"reserved",
|
||||
"reserved",
|
||||
"reserved",
|
||||
"hypervisor injection exception",
|
||||
"vmm communication exception",
|
||||
"security exception",
|
||||
"reserved",
|
||||
};
|
||||
|
||||
static irq_handler_t irq_handlers[16] = {0};
|
||||
|
||||
bool cpu_has_apic()
|
||||
{
|
||||
uint32_t eax, edx;
|
||||
cpuid(1, &eax, &edx);
|
||||
return (edx & CPUID_FEAT_EDX_APIC) != 0;
|
||||
}
|
||||
|
||||
void enable_apic()
|
||||
{
|
||||
uint64_t apic_base = rdmsr(0x1B);
|
||||
apic_base |= (1 << 11);
|
||||
wrmsr(0x1B, apic_base);
|
||||
kprintf("APIC enabled\n");
|
||||
}
|
||||
|
||||
void remap_pic()
|
||||
{
|
||||
outb(PIC1_COMMAND, 0x11);
|
||||
outb(PIC2_COMMAND, 0x11);
|
||||
|
||||
outb(PIC1_DATA, 32);
|
||||
outb(PIC2_DATA, 40);
|
||||
|
||||
outb(PIC1_DATA, 4);
|
||||
outb(PIC2_DATA, 2);
|
||||
|
||||
outb(PIC1_DATA, 1);
|
||||
outb(PIC2_DATA, 1);
|
||||
|
||||
outb(PIC1_DATA, 0);
|
||||
outb(PIC2_DATA, 0);
|
||||
kprintf("PIC remapped\n");
|
||||
}
|
||||
|
||||
void disable_pic()
|
||||
{
|
||||
outb(PIC1_DATA, 0xFF);
|
||||
outb(PIC2_DATA, 0xFF);
|
||||
kprintf("PIC disabled\n");
|
||||
}
|
||||
|
||||
void handle_exception(const isr_frame_t* frame)
|
||||
{
|
||||
kprintf("exception[%d]: %s, error code: %d\n", frame->int_no, exception_messages[frame->int_no], frame->err_code);
|
||||
kpanic();
|
||||
}
|
||||
|
||||
void register_irq_handler(uint8_t irq, irq_handler_t handler)
|
||||
{
|
||||
if (irq >= 16)
|
||||
{
|
||||
kprintf("Cannot register irq %d is out of bounds\n", irq);
|
||||
}
|
||||
else
|
||||
{
|
||||
irq_handlers[irq] = handler;
|
||||
kprintf("Registered irq handler at vector %d\n", irq);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_irq(const isr_frame_t* frame)
|
||||
{
|
||||
uint8_t irq = frame->int_no - 32;
|
||||
|
||||
if (irq_handlers[irq])
|
||||
{
|
||||
irq_handlers[irq](frame);
|
||||
}
|
||||
|
||||
if (irq >= 8)
|
||||
{
|
||||
outb(PIC2_COMMAND, 0x20);
|
||||
}
|
||||
|
||||
outb(PIC1_COMMAND, 0x20);
|
||||
}
|
||||
|
||||
void handle_isr(const isr_frame_t* frame)
|
||||
{
|
||||
if (frame->int_no < 32)
|
||||
{
|
||||
handle_exception(frame);
|
||||
}
|
||||
else if (frame->int_no < 48)
|
||||
{
|
||||
handle_irq(frame);
|
||||
}
|
||||
else
|
||||
{
|
||||
kprintf("interrupt[%d]: not implemented\n", frame->int_no);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "kernel.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint64_t r15, r14, r13, r12, r11, r10, r9, r8;
|
||||
uint64_t rbp, rdi, rsi, rdx, rcx, rbx, rax;
|
||||
uint64_t int_no;
|
||||
uint64_t err_code;
|
||||
uint64_t rip, cs, rflags, rsp, ss;
|
||||
} __attribute__((packed)) isr_frame_t;
|
||||
|
||||
typedef void (*irq_handler_t)(const isr_frame_t*);
|
||||
|
||||
void remap_pic();
|
||||
void disable_pic();
|
||||
|
||||
bool cpu_has_apic();
|
||||
void enable_apic();
|
||||
|
||||
void register_irq_handler(uint8_t irq, irq_handler_t handler);
|
||||
|
||||
static inline void enable_interrupts()
|
||||
{
|
||||
__asm__ volatile("sti");
|
||||
kprintf("Interrupts enabled\n");
|
||||
}
|
||||
|
||||
static inline void disable_interrupts()
|
||||
{
|
||||
__asm__ volatile("cli");
|
||||
kprintf("Interrupts disabled\n");
|
||||
}
|
||||
15
src/kernel.h
15
src/kernel.h
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void kpanic();
|
||||
|
||||
void kprintf(const char* fmt, ...);
|
||||
|
||||
static inline void khalt()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
__asm__ volatile("hlt");
|
||||
}
|
||||
}
|
||||
49
src/kernel/alloc.c
Normal file
49
src/kernel/alloc.c
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "alloc.h"
|
||||
#include <assert.h>
|
||||
|
||||
typedef struct block_header
|
||||
{
|
||||
size_t size;
|
||||
bool is_free;
|
||||
struct block_header* next;
|
||||
struct block_header* prev;
|
||||
u8 data[];
|
||||
} block_header_t;
|
||||
|
||||
static block_header_t* heap_start = NULL;
|
||||
|
||||
#define HEADER_SIZE sizeof(block_header_t)
|
||||
|
||||
static block_header_t* find_free_block(size_t size)
|
||||
{
|
||||
block_header_t* current = heap_start;
|
||||
|
||||
while (current != NULL)
|
||||
{
|
||||
if (current->is_free && current->size >= size)
|
||||
{
|
||||
return current;
|
||||
}
|
||||
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* malloc(size_t size)
|
||||
{
|
||||
// Align to pointer size
|
||||
size = (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
|
||||
|
||||
block_header_t* block = find_free_block(size);
|
||||
block->is_free = false;
|
||||
return (u8*)block + HEADER_SIZE;
|
||||
}
|
||||
|
||||
void free(void* ptr)
|
||||
{
|
||||
block_header_t* block = (block_header_t*)((u8*)ptr - HEADER_SIZE);
|
||||
assert(block->is_free, "Block is already free");
|
||||
block->is_free = true;
|
||||
}
|
||||
6
src/kernel/alloc.h
Normal file
6
src/kernel/alloc.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "def.h"
|
||||
|
||||
void* malloc(size_t size);
|
||||
void free(void* ptr);
|
||||
12
src/kernel/kernel.c
Normal file
12
src/kernel/kernel.c
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "kernel.h"
|
||||
#include <arch.h>
|
||||
#include <printf.h>
|
||||
|
||||
void kernel_main()
|
||||
{
|
||||
arch_api.enable_interrupts();
|
||||
|
||||
printf("Welcome to nub OS :)\n");
|
||||
printf("Kernel has exited\n");
|
||||
arch_api.halt();
|
||||
}
|
||||
3
src/kernel/kernel.h
Normal file
3
src/kernel/kernel.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void kernel_main();
|
||||
110
src/keyboard.c
110
src/keyboard.c
@@ -1,110 +0,0 @@
|
||||
#include "keyboard.h"
|
||||
#include "interrupts.h"
|
||||
#include "kernel.h"
|
||||
#include "util.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#define SCANCODE_LEFT_SHIFT 42
|
||||
#define SCANCODE_RIGHT_SHIFT 54
|
||||
#define SCANCODE_CAPS_LOCK 58
|
||||
|
||||
#define KEYBOARD_HANDLERS_LENGTH 32
|
||||
|
||||
unsigned const char us_keymap[128] = {
|
||||
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', 'q', 'w',
|
||||
'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h',
|
||||
'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/',
|
||||
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, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
unsigned const char us_keymap_shift[128] = {
|
||||
0, 27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', '\t', 'Q', 'W',
|
||||
'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', 0, 'A', 'S', 'D', 'F', 'G', 'H',
|
||||
'J', 'K', 'L', ':', '"', '~', 0, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?',
|
||||
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, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
bool shift = false;
|
||||
bool caps_lock = false;
|
||||
|
||||
// todo(nub): make dynamic when a memory allocator is implemented
|
||||
static keyboard_handler_t keyboard_handlers[KEYBOARD_HANDLERS_LENGTH];
|
||||
static size_t handler_index = 0;
|
||||
|
||||
char scan_code_to_ascii(uint8_t scan_code)
|
||||
{
|
||||
if (scan_code >= 128)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((!caps_lock && shift) || (caps_lock && !shift))
|
||||
{
|
||||
return us_keymap_shift[scan_code];
|
||||
}
|
||||
else
|
||||
{
|
||||
return us_keymap[scan_code];
|
||||
}
|
||||
}
|
||||
|
||||
void handle_keyboard(const isr_frame_t* frame)
|
||||
{
|
||||
uint8_t code = inb(0x60);
|
||||
uint8_t scan_code = code & 0x7F;
|
||||
bool pressed = (code & 0x80) == 0;
|
||||
|
||||
switch (scan_code)
|
||||
{
|
||||
case SCANCODE_LEFT_SHIFT:
|
||||
case SCANCODE_RIGHT_SHIFT:
|
||||
{
|
||||
shift = pressed;
|
||||
break;
|
||||
}
|
||||
case SCANCODE_CAPS_LOCK:
|
||||
{
|
||||
if (pressed)
|
||||
{
|
||||
caps_lock = !caps_lock;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
keyboard_event_t event = {
|
||||
.scan_code = scan_code,
|
||||
.pressed = pressed,
|
||||
.caps_lock = caps_lock,
|
||||
.shift = shift,
|
||||
.ascii = scan_code_to_ascii(scan_code),
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < handler_index; i++)
|
||||
{
|
||||
keyboard_handlers[i](&event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void register_keypress_handler(const keyboard_handler_t handler)
|
||||
{
|
||||
// todo(nub31): remove when a memory allocator is implemented and
|
||||
// keyboard_handlers is a dynamic list
|
||||
if (handler_index >= KEYBOARD_HANDLERS_LENGTH)
|
||||
{
|
||||
kprintf("Maximum keyboard handlers reached\n");
|
||||
kpanic();
|
||||
}
|
||||
|
||||
keyboard_handlers[handler_index] = handler;
|
||||
handler_index += 1;
|
||||
}
|
||||
|
||||
void init_keyboard()
|
||||
{
|
||||
register_irq_handler(1, handle_keyboard);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t scan_code;
|
||||
bool pressed;
|
||||
bool shift;
|
||||
bool caps_lock;
|
||||
char ascii;
|
||||
} keyboard_event_t;
|
||||
|
||||
typedef void (*keyboard_handler_t)(const keyboard_event_t*);
|
||||
|
||||
void init_keyboard();
|
||||
void register_keypress_handler(keyboard_handler_t handler);
|
||||
char scan_code_to_ascii(uint8_t scan_code);
|
||||
40
src/lib/arch.h
Normal file
40
src/lib/arch.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <def.h>
|
||||
|
||||
typedef void (*arch_console_putchar_t)(char c);
|
||||
typedef void (*arch_console_clear_t)();
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const arch_console_putchar_t putchar;
|
||||
const arch_console_clear_t clear;
|
||||
} arch_console_api_t;
|
||||
|
||||
typedef size_t (*arch_mem_get_page_size_t)();
|
||||
typedef void* (*arch_mem_alloc_t)(size_t page_count);
|
||||
typedef void (*arch_mem_free_t)(void* virtual_address, size_t page_count);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const arch_mem_get_page_size_t get_page_size;
|
||||
const arch_mem_alloc_t alloc;
|
||||
const arch_mem_free_t free;
|
||||
} arch_mem_api_t;
|
||||
|
||||
typedef void (*arch_panic_t)();
|
||||
typedef void (*arch_enable_interrupts_t)();
|
||||
typedef void (*arch_disable_interrupts_t)();
|
||||
typedef void (*arch_halt_t)();
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const arch_panic_t panic;
|
||||
const arch_enable_interrupts_t enable_interrupts;
|
||||
const arch_disable_interrupts_t disable_interrupts;
|
||||
const arch_halt_t halt;
|
||||
const arch_console_api_t console;
|
||||
const arch_mem_api_t mem;
|
||||
} arch_api_t;
|
||||
|
||||
extern arch_api_t arch_api;
|
||||
17
src/lib/assert.h
Normal file
17
src/lib/assert.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <arch.h>
|
||||
#include <printf.h>
|
||||
|
||||
// Make sure `arch_api.panic` and `arch_api.console.putchar` is intialized if calling from architecure-specific code.
|
||||
// Will be compiled out if `DEBUG` is not defined.
|
||||
static inline void assert(bool condition, const char* msg)
|
||||
{
|
||||
#if DEBUG
|
||||
if (!condition)
|
||||
{
|
||||
printf(msg);
|
||||
arch_api.panic();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
67
src/lib/def.h
Normal file
67
src/lib/def.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
typedef __builtin_va_list va_list;
|
||||
|
||||
#define va_start(ap, last) __builtin_va_start(ap, last)
|
||||
#define va_arg(ap, type) __builtin_va_arg(ap, type)
|
||||
#define va_end(ap) __builtin_va_end(ap)
|
||||
#define va_copy(dest, src) __builtin_va_copy(dest, src)
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void*)0)
|
||||
#endif
|
||||
|
||||
typedef unsigned long size_t;
|
||||
typedef unsigned long uintptr_t;
|
||||
|
||||
#define offsetof(type, member) __builtin_offsetof(type, member)
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef signed char i8;
|
||||
typedef unsigned short u16;
|
||||
typedef signed short i16;
|
||||
typedef unsigned int u32;
|
||||
typedef signed int i32;
|
||||
typedef unsigned long long u64;
|
||||
typedef signed long long i64;
|
||||
|
||||
#define I8_MIN (-128)
|
||||
#define I8_MAX 127
|
||||
|
||||
#define U8_MIN 0x0
|
||||
#define U8_MAX 0xff
|
||||
|
||||
#define I16_MIN (-32768)
|
||||
#define I16_MAX 32767
|
||||
|
||||
#define U16_MIN 0x0
|
||||
#define U16_MAX 0xffff
|
||||
|
||||
#define I32_MIN (-2147483647 - 1)
|
||||
#define I32_MAX 2147483647
|
||||
|
||||
#define U32_MIN 0x0
|
||||
#define U32_MAX 0xffffffffU
|
||||
|
||||
#define I64_MIN (-9223372036854775807LL - 1)
|
||||
#define I64_MAX 9223372036854775807LL
|
||||
|
||||
#define U64_MIN 0x0
|
||||
#define U64_MAX 0xffffffffffffffffULL
|
||||
|
||||
#define I8_C(x) x
|
||||
#define U8_C(x) x##U
|
||||
|
||||
#define I16_C(x) x
|
||||
#define U16_C(x) x##U
|
||||
|
||||
#define I32_C(x) x
|
||||
#define U32_C(x) x##U
|
||||
|
||||
#define I64_C(x) x##LL
|
||||
#define U64_C(x) x##ULL
|
||||
|
||||
#define KiB(count) (U64_C(count) * U64_C(1024))
|
||||
#define MiB(count) (U64_C(count) * U64_C(1024) * U64_C(1024))
|
||||
#define GiB(count) (U64_C(count) * U64_C(1024) * U64_C(1024) * U64_C(1024))
|
||||
#define TiB(count) (U64_C(count) * U64_C(1024) * U64_C(1024) * U64_C(1024) * U64_C(1024))
|
||||
17
src/lib/mem.c
Normal file
17
src/lib/mem.c
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "mem.h"
|
||||
|
||||
void memset(void* destination, u8 value, size_t length)
|
||||
{
|
||||
for (size_t i = 0; i < length; i++)
|
||||
{
|
||||
((u8*)destination)[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
void memcpy(void* destination, void* source, size_t length)
|
||||
{
|
||||
for (size_t i = 0; i < length; i++)
|
||||
{
|
||||
((u8*)destination)[i] = ((u8*)source)[i];
|
||||
}
|
||||
}
|
||||
6
src/lib/mem.h
Normal file
6
src/lib/mem.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <def.h>
|
||||
|
||||
void memset(void* destination, u8 value, size_t length);
|
||||
void memcpy(void* destination, void* source, size_t length);
|
||||
@@ -1,32 +1,26 @@
|
||||
#include "kernel.h"
|
||||
#include "interrupts.h"
|
||||
#include "multiboot.h"
|
||||
#include "pmm.h"
|
||||
#include "string.h"
|
||||
#include "vga.h"
|
||||
#include <stdarg.h>
|
||||
#include "printf.h"
|
||||
#include <arch.h>
|
||||
#include <def.h>
|
||||
#include <string.h>
|
||||
|
||||
void kmain(multiboot_info_t* mbd)
|
||||
{
|
||||
vga_clear();
|
||||
|
||||
init_pmm(mbd);
|
||||
|
||||
remap_pic();
|
||||
enable_interrupts();
|
||||
|
||||
kprintf("Welcome to nub OS :)\n");
|
||||
khalt();
|
||||
}
|
||||
|
||||
void kpanic()
|
||||
{
|
||||
kprintf("Kernel panic!\n");
|
||||
disable_interrupts();
|
||||
khalt();
|
||||
}
|
||||
|
||||
void kprintf(const char* fmt, ...)
|
||||
// Make sure `arch_api.console.putchar` is intialized if calling from architecure-specific code.
|
||||
//
|
||||
// Supported formats:
|
||||
// - `d`: i64 (decimal)
|
||||
// - `u`: u64 (decimal)
|
||||
// - `x`: u64 (hex)
|
||||
// - `c`: char (ascii)
|
||||
// - `s`: char* (ascii string)
|
||||
//
|
||||
// ```c
|
||||
// printf(
|
||||
// "The answer is %d is located at offset %x in file %s",
|
||||
// 42,
|
||||
// 0x2000
|
||||
// "hitchhiker.txt"
|
||||
// );
|
||||
// ```
|
||||
void printf(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
@@ -41,54 +35,54 @@ void kprintf(const char* fmt, ...)
|
||||
|
||||
if (fmt[i] == '%')
|
||||
{
|
||||
vga_put_char('%', VGA_DEFAULT_COLOR);
|
||||
arch_api.console.putchar('%');
|
||||
}
|
||||
else if (fmt[i] == 's')
|
||||
{
|
||||
const char* str = va_arg(args, const char*);
|
||||
for (size_t j = 0; str[j] != '\0'; j++)
|
||||
{
|
||||
vga_put_char(str[j], VGA_DEFAULT_COLOR);
|
||||
arch_api.console.putchar(str[j]);
|
||||
}
|
||||
}
|
||||
else if (fmt[i] == 'c')
|
||||
{
|
||||
char character = (char)va_arg(args, int64_t);
|
||||
vga_put_char(character, VGA_DEFAULT_COLOR);
|
||||
char character = (char)va_arg(args, u64);
|
||||
arch_api.console.putchar(character);
|
||||
}
|
||||
else if (fmt[i] == 'd')
|
||||
{
|
||||
int64_t val = va_arg(args, int64_t);
|
||||
u64 val = va_arg(args, u64);
|
||||
char buf[21];
|
||||
itoa64(val, buf);
|
||||
for (size_t j = 0; buf[j] != '\0'; j++)
|
||||
{
|
||||
vga_put_char(buf[j], VGA_DEFAULT_COLOR);
|
||||
arch_api.console.putchar(buf[j]);
|
||||
}
|
||||
}
|
||||
else if (fmt[i] == 'u')
|
||||
{
|
||||
uint64_t val = va_arg(args, uint64_t);
|
||||
u64 val = va_arg(args, u64);
|
||||
char buf[21];
|
||||
uitoa64(val, buf);
|
||||
for (size_t j = 0; buf[j] != '\0'; j++)
|
||||
{
|
||||
vga_put_char(buf[j], VGA_DEFAULT_COLOR);
|
||||
arch_api.console.putchar(buf[j]);
|
||||
}
|
||||
}
|
||||
else if (fmt[i] == 'x')
|
||||
{
|
||||
uint64_t val = va_arg(args, uint64_t);
|
||||
u64 val = va_arg(args, u64);
|
||||
char buf[17];
|
||||
uitoa64_hex(val, buf);
|
||||
for (size_t j = 0; buf[j] != '\0'; j++)
|
||||
{
|
||||
vga_put_char(buf[j], VGA_DEFAULT_COLOR);
|
||||
arch_api.console.putchar(buf[j]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vga_put_char(fmt[i], VGA_DEFAULT_COLOR);
|
||||
arch_api.console.putchar(fmt[i]);
|
||||
}
|
||||
}
|
||||
else if (fmt[i] == '%')
|
||||
@@ -97,7 +91,7 @@ void kprintf(const char* fmt, ...)
|
||||
}
|
||||
else
|
||||
{
|
||||
vga_put_char(fmt[i], VGA_DEFAULT_COLOR);
|
||||
arch_api.console.putchar(fmt[i]);
|
||||
}
|
||||
}
|
||||
|
||||
4
src/lib/printf.h
Normal file
4
src/lib/printf.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
// In arch code, make sure arch_api.console.putchar is set up befire calling
|
||||
void printf(const char* fmt, ...);
|
||||
@@ -25,7 +25,11 @@ void reverse(char* str, size_t length)
|
||||
}
|
||||
}
|
||||
|
||||
void itoa64(int64_t value, char* buffer)
|
||||
// ```c
|
||||
// char buffer[21];
|
||||
// itoa64(-1234, buffer);
|
||||
// ```
|
||||
void itoa64(i64 value, char* buffer)
|
||||
{
|
||||
char temp[21];
|
||||
int i = 0, j = 0;
|
||||
@@ -34,7 +38,7 @@ void itoa64(int64_t value, char* buffer)
|
||||
if (value < 0)
|
||||
{
|
||||
negative = 1;
|
||||
if (value == INT64_MIN)
|
||||
if (value == I64_MIN)
|
||||
{
|
||||
const char* min_str = "9223372036854775808";
|
||||
for (i = 0; min_str[i] != '\0'; i++)
|
||||
@@ -54,7 +58,7 @@ void itoa64(int64_t value, char* buffer)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(negative && value == INT64_MIN))
|
||||
if (!(negative && value == I64_MIN))
|
||||
{
|
||||
while (value > 0)
|
||||
{
|
||||
@@ -75,7 +79,11 @@ void itoa64(int64_t value, char* buffer)
|
||||
buffer[j] = '\0';
|
||||
}
|
||||
|
||||
void uitoa64(uint64_t value, char* buffer)
|
||||
// ```c
|
||||
// char buffer[21];
|
||||
// uitoa64(1234, buffer);
|
||||
// ```
|
||||
void uitoa64(u64 value, char* buffer)
|
||||
{
|
||||
char temp[21];
|
||||
int i = 0, j = 0;
|
||||
@@ -100,7 +108,7 @@ void uitoa64(uint64_t value, char* buffer)
|
||||
buffer[j] = '\0';
|
||||
}
|
||||
|
||||
void uitoa64_hex(uint64_t value, char* buffer)
|
||||
void uitoa64_hex(u64 value, char* buffer)
|
||||
{
|
||||
const char* digits = "0123456789abcdef";
|
||||
char temp[17];
|
||||
10
src/lib/string.h
Normal file
10
src/lib/string.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <def.h>
|
||||
|
||||
int strcmp(const char* a, const char* b);
|
||||
void reverse(char* str, size_t length);
|
||||
|
||||
void itoa64(i64 value, char* buffer);
|
||||
void uitoa64(u64 value, char* buffer);
|
||||
void uitoa64_hex(u64 value, char* buffer);
|
||||
10
src/mem.c
10
src/mem.c
@@ -1,10 +0,0 @@
|
||||
#include "mem.h"
|
||||
#include <stdint.h>
|
||||
|
||||
void kmemset(void* destination, uint8_t value, size_t length)
|
||||
{
|
||||
for (size_t i = 0; i < length; i++)
|
||||
{
|
||||
((uint8_t*)destination)[i] = value;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void kmemset(void* destination, uint8_t value, size_t length);
|
||||
124
src/pmm.c
124
src/pmm.c
@@ -1,124 +0,0 @@
|
||||
#include "pmm.h"
|
||||
#include "kernel.h"
|
||||
#include "mem.h"
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define BITMAP_SIZE 32768 // Supports up to 1GB of RAM
|
||||
#define USABLE_REGION_SIZE 32
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint64_t base_address;
|
||||
uint64_t length;
|
||||
} memory_region_t;
|
||||
|
||||
static uint8_t page_bitmap[BITMAP_SIZE];
|
||||
static uint64_t total_pages = 0;
|
||||
static uint64_t free_pages = 0;
|
||||
|
||||
void init_pmm(multiboot_info_t* mbd)
|
||||
{
|
||||
memory_region_t usable_regions[USABLE_REGION_SIZE];
|
||||
size_t num_regions = 0;
|
||||
|
||||
if (!(mbd->flags & (1 << 6)))
|
||||
{
|
||||
kprintf("Invalid memory map given by bootloader\n");
|
||||
kpanic();
|
||||
}
|
||||
|
||||
uint64_t offset = 0;
|
||||
while (offset < mbd->mmap_length)
|
||||
{
|
||||
multiboot_memory_map_t* mmmt = (multiboot_memory_map_t*)(mbd->mmap_addr + offset);
|
||||
|
||||
if (mmmt->type == MULTIBOOT_MEMORY_AVAILABLE && num_regions < USABLE_REGION_SIZE)
|
||||
{
|
||||
usable_regions[num_regions] = (memory_region_t){
|
||||
.base_address = mmmt->addr,
|
||||
.length = mmmt->len,
|
||||
};
|
||||
|
||||
num_regions++;
|
||||
|
||||
kprintf("Available memory: 0x%x - 0x%x (%u KB)\n", mmmt->addr, mmmt->addr + mmmt->len, mmmt->len / 1024);
|
||||
}
|
||||
|
||||
offset += mmmt->size + sizeof(mmmt->size);
|
||||
}
|
||||
|
||||
kmemset(page_bitmap, 0xFF, BITMAP_SIZE);
|
||||
|
||||
for (size_t i = 0; i < num_regions; i++)
|
||||
{
|
||||
memory_region_t region = usable_regions[i];
|
||||
|
||||
uint64_t start_page = region.base_address / PAGE_SIZE;
|
||||
uint64_t num_pages = region.length / PAGE_SIZE;
|
||||
|
||||
for (uint64_t page = start_page; page < start_page + num_pages; page++)
|
||||
{
|
||||
if (page < BITMAP_SIZE * 8)
|
||||
{
|
||||
page_bitmap[page / 8] &= ~(1 << (page % 8));
|
||||
free_pages++;
|
||||
total_pages++;
|
||||
}
|
||||
else
|
||||
{
|
||||
kprintf("System has more ram than the bitmap allows!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reserve first 64MB which is reserved by boot code
|
||||
for (uint64_t page = 0; page < (64 * 1024 * 1024) / PAGE_SIZE; page++)
|
||||
{
|
||||
if (page < BITMAP_SIZE * 8)
|
||||
{
|
||||
if (!(page_bitmap[page / 8] & (1 << (page % 8))))
|
||||
{
|
||||
page_bitmap[page / 8] |= (1 << (page % 8));
|
||||
free_pages--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kprintf("PMM initialized\n");
|
||||
}
|
||||
|
||||
uint64_t pmm_alloc_page()
|
||||
{
|
||||
for (size_t i = 0; i < BITMAP_SIZE; i++)
|
||||
{
|
||||
if (page_bitmap[i] != 0xFF)
|
||||
{
|
||||
for (int bit = 0; bit < 8; bit++)
|
||||
{
|
||||
if (!(page_bitmap[i] & (1 << bit)))
|
||||
{
|
||||
page_bitmap[i] |= (1 << bit);
|
||||
free_pages--;
|
||||
return ((i * 8 + bit) * PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pmm_free_page(uint64_t addr)
|
||||
{
|
||||
uint64_t page = addr / PAGE_SIZE;
|
||||
if (page < BITMAP_SIZE * 8)
|
||||
{
|
||||
if (page_bitmap[page / 8] & (1 << (page % 8)))
|
||||
{
|
||||
page_bitmap[page / 8] &= ~(1 << (page % 8));
|
||||
free_pages++;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/pmm.h
10
src/pmm.h
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "multiboot.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define PAGE_SIZE 4096
|
||||
|
||||
void init_pmm(multiboot_info_t* mbd);
|
||||
uint64_t pmm_alloc_page();
|
||||
void pmm_free_page(uint64_t addr);
|
||||
10
src/string.h
10
src/string.h
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int strcmp(const char* a, const char* b);
|
||||
void reverse(char* str, size_t length);
|
||||
void uitoa64(uint64_t value, char* buffer);
|
||||
void itoa64(int64_t value, char* buffer);
|
||||
void uitoa64_hex(uint64_t value, char* buffer);
|
||||
Reference in New Issue
Block a user