Compare commits

...

38 Commits

Author SHA1 Message Date
nub31
dd3dbc3ab1 ... 2025-09-07 01:27:22 +02:00
nub31
ff12b6abf2 Remove true/false/bool since it is a compuler keyword in c23 2025-09-07 00:42:22 +02:00
nub31
a018dfd471 some lib docs 2025-09-07 00:22:54 +02:00
nub31
8cc90d6f2f ... 2025-09-07 00:08:31 +02:00
nub31
4f55956fee ... 2025-09-06 22:07:17 +02:00
nub31
45a9023bab ... 2025-09-06 21:21:59 +02:00
nub31
130e271461 only identity map the start of the kernel 2025-09-06 20:22:51 +02:00
nub31
46bc977104 .. 2025-09-06 19:45:33 +02:00
nub31
5f071104bc virtual page allocation 2025-09-06 18:32:52 +02:00
nub31
62680a05bc void* 2025-09-06 18:23:46 +02:00
nub31
ac0299d718 ... 2025-09-06 18:16:13 +02:00
nub31
b15c037b16 improve vmm api 2025-09-06 18:13:33 +02:00
nub31
02f0ba9aad mem cleanup 2025-09-06 17:42:05 +02:00
nub31
0ccb483047 ... 2025-09-06 17:31:14 +02:00
nub31
ad0ec03b0f ... 2025-09-06 01:31:20 +02:00
nub31
7b115e5b66 cleanup arch api 2025-09-06 01:27:13 +02:00
nub31
a3822dd350 basic memory management 2025-09-06 00:59:52 +02:00
nub31
bcf05c055e ... 2025-09-05 23:59:59 +02:00
nub31
2aa44eaa51 ... 2025-09-05 21:43:41 +02:00
nub31
0f6b66a486 ... 2025-09-05 21:41:00 +02:00
nub31
8e986807df ... 2025-09-03 19:25:58 +02:00
nub31
bb923621f9 better api 2025-09-03 19:22:53 +02:00
nub31
b7bbccca73 ... 2025-09-03 18:38:02 +02:00
nub31
f05e7c9e64 ... 2025-09-03 18:31:10 +02:00
nub31
0583c8c1a3 ... 2025-09-03 17:43:23 +02:00
nub31
0b8ee325aa ... 2025-09-03 17:20:00 +02:00
nub31
ad879af3f9 nl 2025-09-03 15:23:43 +02:00
nub31
020e09c4e1 ... 2025-09-03 15:22:35 +02:00
nub31
022dde6c7c ... 2025-09-03 14:58:56 +02:00
nub31
71c73dd493 ... 2025-09-03 14:57:07 +02:00
nub31
33239034a7 ... 2025-09-03 14:55:45 +02:00
nub31
a1205703f0 ... 2025-09-03 14:31:07 +02:00
nub31
08bbe517c5 ... 2025-09-03 13:44:44 +02:00
nub31
204c747c43 ... 2025-09-03 13:32:40 +02:00
nub31
87d1a291f7 ... 2025-09-03 13:13:16 +02:00
nub31
c338e05648 structure project 2025-09-03 11:18:17 +02:00
nub31
ad30071e9f pmm 2025-09-02 20:42:19 +02:00
nub31
b533485614 ... 2025-09-02 18:55:46 +02:00
50 changed files with 1704 additions and 788 deletions

View File

@@ -1,5 +1,5 @@
IndentWidth: 4
ColumnLimit: 100
ColumnLimit: 120
# Pointer formatting
DerivePointerAlignment: false

28
.vscode/c_cpp_properties.json vendored Normal file
View 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
View 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
View 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"
}
}

View File

@@ -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"
```

View File

@@ -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 = .;
}

View File

@@ -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=c23 -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/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
View 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,
};

View File

@@ -1,6 +1,7 @@
global _start
extern kmain
extern handle_isr
global pml4
extern x86_64_main
extern kernel_end
%define FLAGS 0b10
%define MAGIC 0x1BADB002
@@ -40,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
@@ -101,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
@@ -139,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:
@@ -183,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

View File

@@ -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++)
{

View File

@@ -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();

View 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();
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include "isr.h"
void handle_exception(const isr_frame_t* frame);

View 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

View File

@@ -0,0 +1,3 @@
#pragma once
extern void idt_init();

View 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);
}

View 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);

View 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);
}
}

View 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
View 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
View 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
View 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
View 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
View 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);

276
src/arch/x86_64/multiboot.h Normal file
View File

@@ -0,0 +1,276 @@
/* multiboot.h - Multiboot header file. */
/* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY
* DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef MULTIBOOT_HEADER
#define MULTIBOOT_HEADER 1
/* How many bytes from the start of the file we search for the header. */
#define MULTIBOOT_SEARCH 8192
#define MULTIBOOT_HEADER_ALIGN 4
/* The magic field should contain this. */
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
/* This should be in %eax. */
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
/* Alignment of multiboot modules. */
#define MULTIBOOT_MOD_ALIGN 0x00001000
/* Alignment of the multiboot info structure. */
#define MULTIBOOT_INFO_ALIGN 0x00000004
/* Flags set in the flags member of the multiboot header. */
/* Align all boot modules on i386 page (4KB) boundaries. */
#define MULTIBOOT_PAGE_ALIGN 0x00000001
/* Must pass memory information to OS. */
#define MULTIBOOT_MEMORY_INFO 0x00000002
/* Must pass video information to OS. */
#define MULTIBOOT_VIDEO_MODE 0x00000004
/* This flag indicates the use of the address fields in the header. */
#define MULTIBOOT_AOUT_KLUDGE 0x00010000
/* Flags to be set in the flags member of the multiboot info structure. */
/* is there basic lower/upper memory information? */
#define MULTIBOOT_INFO_MEMORY 0x00000001
/* is there a boot device set? */
#define MULTIBOOT_INFO_BOOTDEV 0x00000002
/* is the command-line defined? */
#define MULTIBOOT_INFO_CMDLINE 0x00000004
/* are there modules to do something with? */
#define MULTIBOOT_INFO_MODS 0x00000008
/* These next two are mutually exclusive */
/* is there a symbol table loaded? */
#define MULTIBOOT_INFO_AOUT_SYMS 0x00000010
/* is there an ELF section header table? */
#define MULTIBOOT_INFO_ELF_SHDR 0X00000020
/* is there a full memory map? */
#define MULTIBOOT_INFO_MEM_MAP 0x00000040
/* Is there drive info? */
#define MULTIBOOT_INFO_DRIVE_INFO 0x00000080
/* Is there a config table? */
#define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100
/* Is there a boot loader name? */
#define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200
/* Is there a APM table? */
#define MULTIBOOT_INFO_APM_TABLE 0x00000400
/* Is there video information? */
#define MULTIBOOT_INFO_VBE_INFO 0x00000800
#define MULTIBOOT_INFO_FRAMEBUFFER_INFO 0x00001000
#ifndef ASM_FILE
typedef unsigned char multiboot_uint8_t;
typedef unsigned short multiboot_uint16_t;
typedef unsigned int multiboot_uint32_t;
typedef unsigned long long multiboot_uint64_t;
struct multiboot_header
{
/* Must be MULTIBOOT_MAGIC - see above. */
multiboot_uint32_t magic;
/* Feature flags. */
multiboot_uint32_t flags;
/* The above fields plus this one must equal 0 mod 2^32. */
multiboot_uint32_t checksum;
/* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */
multiboot_uint32_t header_addr;
multiboot_uint32_t load_addr;
multiboot_uint32_t load_end_addr;
multiboot_uint32_t bss_end_addr;
multiboot_uint32_t entry_addr;
/* These are only valid if MULTIBOOT_VIDEO_MODE is set. */
multiboot_uint32_t mode_type;
multiboot_uint32_t width;
multiboot_uint32_t height;
multiboot_uint32_t depth;
};
/* The symbol table for a.out. */
struct multiboot_aout_symbol_table
{
multiboot_uint32_t tabsize;
multiboot_uint32_t strsize;
multiboot_uint32_t addr;
multiboot_uint32_t reserved;
};
typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t;
/* The section header table for ELF. */
struct multiboot_elf_section_header_table
{
multiboot_uint32_t num;
multiboot_uint32_t size;
multiboot_uint32_t addr;
multiboot_uint32_t shndx;
};
typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t;
struct multiboot_info
{
/* Multiboot info version number */
multiboot_uint32_t flags;
/* Available memory from BIOS */
multiboot_uint32_t mem_lower;
multiboot_uint32_t mem_upper;
/* "root" partition */
multiboot_uint32_t boot_device;
/* Kernel command line */
multiboot_uint32_t cmdline;
/* Boot-Module list */
multiboot_uint32_t mods_count;
multiboot_uint32_t mods_addr;
union
{
multiboot_aout_symbol_table_t aout_sym;
multiboot_elf_section_header_table_t elf_sec;
} u;
/* Memory Mapping buffer */
multiboot_uint32_t mmap_length;
multiboot_uint32_t mmap_addr;
/* Drive Info buffer */
multiboot_uint32_t drives_length;
multiboot_uint32_t drives_addr;
/* ROM configuration table */
multiboot_uint32_t config_table;
/* Boot Loader Name */
multiboot_uint32_t boot_loader_name;
/* APM table */
multiboot_uint32_t apm_table;
/* Video */
multiboot_uint32_t vbe_control_info;
multiboot_uint32_t vbe_mode_info;
multiboot_uint16_t vbe_mode;
multiboot_uint16_t vbe_interface_seg;
multiboot_uint16_t vbe_interface_off;
multiboot_uint16_t vbe_interface_len;
multiboot_uint64_t framebuffer_addr;
multiboot_uint32_t framebuffer_pitch;
multiboot_uint32_t framebuffer_width;
multiboot_uint32_t framebuffer_height;
multiboot_uint8_t framebuffer_bpp;
#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0
#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1
#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2
multiboot_uint8_t framebuffer_type;
union
{
struct
{
multiboot_uint32_t framebuffer_palette_addr;
multiboot_uint16_t framebuffer_palette_num_colors;
};
struct
{
multiboot_uint8_t framebuffer_red_field_position;
multiboot_uint8_t framebuffer_red_mask_size;
multiboot_uint8_t framebuffer_green_field_position;
multiboot_uint8_t framebuffer_green_mask_size;
multiboot_uint8_t framebuffer_blue_field_position;
multiboot_uint8_t framebuffer_blue_mask_size;
};
};
};
typedef struct multiboot_info multiboot_info_t;
struct multiboot_color
{
multiboot_uint8_t red;
multiboot_uint8_t green;
multiboot_uint8_t blue;
};
struct multiboot_mmap_entry
{
multiboot_uint32_t size;
multiboot_uint64_t addr;
multiboot_uint64_t len;
#define MULTIBOOT_MEMORY_AVAILABLE 1
#define MULTIBOOT_MEMORY_RESERVED 2
#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3
#define MULTIBOOT_MEMORY_NVS 4
#define MULTIBOOT_MEMORY_BADRAM 5
multiboot_uint32_t type;
} __attribute__((packed));
typedef struct multiboot_mmap_entry multiboot_memory_map_t;
struct multiboot_mod_list
{
/* the memory used goes from bytes mod_start to mod_end-1 inclusive */
multiboot_uint32_t mod_start;
multiboot_uint32_t mod_end;
/* Module command line */
multiboot_uint32_t cmdline;
/* padding to take it to 16 bytes (must be zero) */
multiboot_uint32_t pad;
};
typedef struct multiboot_mod_list multiboot_module_t;
/* APM BIOS info. */
struct multiboot_apm_info
{
multiboot_uint16_t version;
multiboot_uint16_t cseg;
multiboot_uint32_t offset;
multiboot_uint16_t cseg_16;
multiboot_uint16_t dseg;
multiboot_uint16_t flags;
multiboot_uint16_t cseg_len;
multiboot_uint16_t cseg_16_len;
multiboot_uint16_t dseg_len;
};
#endif /* ! ASM_FILE */
#endif /* ! MULTIBOOT_HEADER */

8
src/arch/x86_64/panic.c Normal file
View 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
View File

@@ -0,0 +1,3 @@
#pragma once
void panic();

123
src/arch/x86_64/util.h Normal file
View File

@@ -0,0 +1,123 @@
#pragma once
#include <def.h>
enum
{
CPUID_FEAT_ECX_SSE3 = 1 << 0,
CPUID_FEAT_ECX_PCLMUL = 1 << 1,
CPUID_FEAT_ECX_DTES64 = 1 << 2,
CPUID_FEAT_ECX_MONITOR = 1 << 3,
CPUID_FEAT_ECX_DS_CPL = 1 << 4,
CPUID_FEAT_ECX_VMX = 1 << 5,
CPUID_FEAT_ECX_SMX = 1 << 6,
CPUID_FEAT_ECX_EST = 1 << 7,
CPUID_FEAT_ECX_TM2 = 1 << 8,
CPUID_FEAT_ECX_SSSE3 = 1 << 9,
CPUID_FEAT_ECX_CID = 1 << 10,
CPUID_FEAT_ECX_SDBG = 1 << 11,
CPUID_FEAT_ECX_FMA = 1 << 12,
CPUID_FEAT_ECX_CX16 = 1 << 13,
CPUID_FEAT_ECX_XTPR = 1 << 14,
CPUID_FEAT_ECX_PDCM = 1 << 15,
CPUID_FEAT_ECX_PCID = 1 << 17,
CPUID_FEAT_ECX_DCA = 1 << 18,
CPUID_FEAT_ECX_SSE4_1 = 1 << 19,
CPUID_FEAT_ECX_SSE4_2 = 1 << 20,
CPUID_FEAT_ECX_X2APIC = 1 << 21,
CPUID_FEAT_ECX_MOVBE = 1 << 22,
CPUID_FEAT_ECX_POPCNT = 1 << 23,
CPUID_FEAT_ECX_TSC = 1 << 24,
CPUID_FEAT_ECX_AES = 1 << 25,
CPUID_FEAT_ECX_XSAVE = 1 << 26,
CPUID_FEAT_ECX_OSXSAVE = 1 << 27,
CPUID_FEAT_ECX_AVX = 1 << 28,
CPUID_FEAT_ECX_F16C = 1 << 29,
CPUID_FEAT_ECX_RDRAND = 1 << 30,
CPUID_FEAT_ECX_HYPERVISOR = 1 << 31,
CPUID_FEAT_EDX_FPU = 1 << 0,
CPUID_FEAT_EDX_VME = 1 << 1,
CPUID_FEAT_EDX_DE = 1 << 2,
CPUID_FEAT_EDX_PSE = 1 << 3,
CPUID_FEAT_EDX_TSC = 1 << 4,
CPUID_FEAT_EDX_MSR = 1 << 5,
CPUID_FEAT_EDX_PAE = 1 << 6,
CPUID_FEAT_EDX_MCE = 1 << 7,
CPUID_FEAT_EDX_CX8 = 1 << 8,
CPUID_FEAT_EDX_APIC = 1 << 9,
CPUID_FEAT_EDX_SEP = 1 << 11,
CPUID_FEAT_EDX_MTRR = 1 << 12,
CPUID_FEAT_EDX_PGE = 1 << 13,
CPUID_FEAT_EDX_MCA = 1 << 14,
CPUID_FEAT_EDX_CMOV = 1 << 15,
CPUID_FEAT_EDX_PAT = 1 << 16,
CPUID_FEAT_EDX_PSE36 = 1 << 17,
CPUID_FEAT_EDX_PSN = 1 << 18,
CPUID_FEAT_EDX_CLFLUSH = 1 << 19,
CPUID_FEAT_EDX_DS = 1 << 21,
CPUID_FEAT_EDX_ACPI = 1 << 22,
CPUID_FEAT_EDX_MMX = 1 << 23,
CPUID_FEAT_EDX_FXSR = 1 << 24,
CPUID_FEAT_EDX_SSE = 1 << 25,
CPUID_FEAT_EDX_SSE2 = 1 << 26,
CPUID_FEAT_EDX_SS = 1 << 27,
CPUID_FEAT_EDX_HTT = 1 << 28,
CPUID_FEAT_EDX_TM = 1 << 29,
CPUID_FEAT_EDX_IA64 = 1 << 30,
CPUID_FEAT_EDX_PBE = 1 << 31
};
static inline void outb(u16 port, u8 val)
{
__asm__ volatile("outb %0, %1" : : "a"(val), "Nd"(port));
}
static inline u8 inb(u16 port)
{
u8 ret;
__asm__ volatile("inb %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
static inline void io_wait()
{
outb(0x80, 0);
}
static inline void cpuid(u32 code, u32* a, u32* d)
{
__asm__ volatile("cpuid" : "=a"(*a), "=d"(*d) : "a"(code) : "ecx", "ebx");
}
static inline u64 rdmsr(u32 msr)
{
u32 lo, hi;
__asm__ volatile("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr));
return ((u64)hi << 32) | lo;
}
static inline void wrmsr(u32 msr, u64 value)
{
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");
}

View File

@@ -1,134 +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);
}
}

View File

@@ -1,35 +0,0 @@
#pragma once
#include "kernel.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");
}

View File

@@ -1,108 +0,0 @@
#include "kernel.h"
#include "interrupts.h"
#include "keyboard.h"
#include "string.h"
#include "vga.h"
#include <stdarg.h>
#include "multiboot.h"
void handle_keypress(const keyboard_event_t* event)
{
if (event->pressed && event->ascii)
{
kprintf("%c", event->ascii);
}
}
void kmain(multiboot_info_t* multiboot_info)
{
vga_clear();
remap_pic();
if (cpu_has_apic())
{
disable_pic();
enable_apic();
}
enable_interrupts();
register_keypress_handler(handle_keypress);
kprintf("Welcome to nub OS\n");
khalt();
}
void kpanic()
{
kprintf("Kernel panic!\n");
disable_interrupts();
khalt();
}
void kprintf(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
bool should_format = false;
for (size_t i = 0; fmt[i] != '\0'; i++)
{
if (should_format)
{
should_format = false;
if (fmt[i] == '%')
{
vga_put_char('%', VGA_DEFAULT_COLOR);
}
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);
}
}
else if (fmt[i] == 'c')
{
char character = (char)va_arg(args, int64_t);
vga_put_char(character, VGA_DEFAULT_COLOR);
}
else if (fmt[i] == 'd')
{
int64_t val = va_arg(args, int64_t);
char buf[21];
itoa64(val, buf);
for (size_t j = 0; buf[j] != '\0'; j++)
{
vga_put_char(buf[j], VGA_DEFAULT_COLOR);
}
}
else if (fmt[i] == 'u')
{
uint64_t val = va_arg(args, uint64_t);
char buf[21];
uitoa64(val, buf);
for (size_t j = 0; buf[j] != '\0'; j++)
{
vga_put_char(buf[j], VGA_DEFAULT_COLOR);
}
}
else
{
vga_put_char(fmt[i], VGA_DEFAULT_COLOR);
}
}
else if (fmt[i] == '%')
{
should_format = true;
}
else
{
vga_put_char(fmt[i], VGA_DEFAULT_COLOR);
}
}
va_end(args);
}

View File

@@ -1,13 +0,0 @@
#pragma once
void kpanic();
static inline void khalt()
{
while (true)
{
__asm__ volatile ("hlt");
}
}
void kprintf(const char* fmt, ...);

49
src/kernel/alloc.c Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1,3 @@
#pragma once
void kernel_main();

View File

@@ -1,111 +0,0 @@
#include "keyboard.h"
#include "interrupts.h"
#include "kernel.h"
#include "vga.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);
}

View File

@@ -1,18 +0,0 @@
#pragma once
#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
View 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
View 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
View 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
View 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
View 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);

99
src/lib/printf.c Normal file
View File

@@ -0,0 +1,99 @@
#include "printf.h"
#include <arch.h>
#include <def.h>
#include <string.h>
// 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);
bool should_format = false;
for (size_t i = 0; fmt[i] != '\0'; i++)
{
if (should_format)
{
should_format = false;
if (fmt[i] == '%')
{
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++)
{
arch_api.console.putchar(str[j]);
}
}
else if (fmt[i] == 'c')
{
char character = (char)va_arg(args, u64);
arch_api.console.putchar(character);
}
else if (fmt[i] == 'd')
{
u64 val = va_arg(args, u64);
char buf[21];
itoa64(val, buf);
for (size_t j = 0; buf[j] != '\0'; j++)
{
arch_api.console.putchar(buf[j]);
}
}
else if (fmt[i] == 'u')
{
u64 val = va_arg(args, u64);
char buf[21];
uitoa64(val, buf);
for (size_t j = 0; buf[j] != '\0'; j++)
{
arch_api.console.putchar(buf[j]);
}
}
else if (fmt[i] == 'x')
{
u64 val = va_arg(args, u64);
char buf[17];
uitoa64_hex(val, buf);
for (size_t j = 0; buf[j] != '\0'; j++)
{
arch_api.console.putchar(buf[j]);
}
}
else
{
arch_api.console.putchar(fmt[i]);
}
}
else if (fmt[i] == '%')
{
should_format = true;
}
else
{
arch_api.console.putchar(fmt[i]);
}
}
va_end(args);
}

4
src/lib/printf.h Normal file
View 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, ...);

View File

@@ -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;
@@ -98,4 +106,30 @@ void uitoa64(uint64_t value, char* buffer)
buffer[j++] = temp[--i];
}
buffer[j] = '\0';
}
void uitoa64_hex(u64 value, char* buffer)
{
const char* digits = "0123456789abcdef";
char temp[17];
int pos = 0;
if (value == 0)
{
buffer[0] = '0';
buffer[1] = '\0';
return;
}
while (value > 0 && pos < 16)
{
temp[pos++] = digits[value & 0xF];
value >>= 4;
}
for (int i = 0; i < pos; i++)
{
buffer[i] = temp[pos - i - 1];
}
buffer[pos] = '\0';
}

10
src/lib/string.h Normal file
View 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);

View File

@@ -1,71 +0,0 @@
#pragma once
#include <stdint.h>
#define MULTIBOOT_FLAG_MEMIFO 0b0000000000001
#define MULTIBOOT_FLAG_BOOT_DEVICE 0b0000000000010
#define MULTIBOOT_FLAG_CMDLINE 0b0000000000100
#define MULTIBOOT_FLAG_MODINFO 0b0000000001000
#define MULTIBOOT_FLAG_MMAP 0b0000001000000
#define MULTIBOOT_FLAG_DRIVES 0b0000010000000
#define MULTIBOOT_FLAG_CONFIG_TABLE 0b0000100000000
#define MULTIBOOT_FLAG_BOOTLOADER_NAME 0b0001000000000
#define MULTIBOOT_FLAG_APM_TABLE 0b0010000000000
#define MULTIBOOT_FLAG_VBEINFO 0b0100000000000
#define MULTIBOOT_FLAG_FRAMEBUFFER_INFO 0b1000000000000
typedef struct multiboot_info
{
uint32_t flags; // required
// Available if flags[0] is set
uint32_t mem_lower;
uint32_t mem_upper;
// Available if flags[1] is set
uint32_t boot_device;
// Available if flags[2] is set
uint32_t cmdline;
// Available if flags[3] is set
uint32_t mods_count;
uint32_t mods_addr;
// Available if flags[4] or flags[5] is set
uint8_t syms[16]; // 28 - 40 bytes, exact layout may differ depending on a.out or ELF
// Available if flags[6] is set
uint32_t mmap_length;
uint32_t mmap_addr;
// Available if flags[7] is set
uint32_t drives_length;
uint32_t drives_addr;
// Available if flags[8] is set
uint32_t config_table;
// Available if flags[9] is set
uint32_t boot_loader_name;
// Available if flags[10] is set
uint32_t apm_table;
// Available if flags[11] is set
uint32_t vbe_control_info;
uint32_t vbe_mode_info;
uint16_t vbe_mode;
uint16_t vbe_interface_seg;
uint16_t vbe_interface_off;
uint16_t vbe_interface_len;
// Available if flags[12] is set
uint64_t framebuffer_addr; // 64-bit for large memory addresses
uint32_t framebuffer_pitch;
uint32_t framebuffer_width;
uint32_t framebuffer_height;
uint8_t framebuffer_bpp;
uint8_t framebuffer_type;
uint8_t color_info[6]; // 110-115 bytes
} __attribute__((packed)) multiboot_info_t;

View File

@@ -1,9 +0,0 @@
#pragma once
#include <stdint.h>
#include <stddef.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);

View File

@@ -1,101 +0,0 @@
#pragma once
#include <stdint.h>
enum {
CPUID_FEAT_ECX_SSE3 = 1 << 0,
CPUID_FEAT_ECX_PCLMUL = 1 << 1,
CPUID_FEAT_ECX_DTES64 = 1 << 2,
CPUID_FEAT_ECX_MONITOR = 1 << 3,
CPUID_FEAT_ECX_DS_CPL = 1 << 4,
CPUID_FEAT_ECX_VMX = 1 << 5,
CPUID_FEAT_ECX_SMX = 1 << 6,
CPUID_FEAT_ECX_EST = 1 << 7,
CPUID_FEAT_ECX_TM2 = 1 << 8,
CPUID_FEAT_ECX_SSSE3 = 1 << 9,
CPUID_FEAT_ECX_CID = 1 << 10,
CPUID_FEAT_ECX_SDBG = 1 << 11,
CPUID_FEAT_ECX_FMA = 1 << 12,
CPUID_FEAT_ECX_CX16 = 1 << 13,
CPUID_FEAT_ECX_XTPR = 1 << 14,
CPUID_FEAT_ECX_PDCM = 1 << 15,
CPUID_FEAT_ECX_PCID = 1 << 17,
CPUID_FEAT_ECX_DCA = 1 << 18,
CPUID_FEAT_ECX_SSE4_1 = 1 << 19,
CPUID_FEAT_ECX_SSE4_2 = 1 << 20,
CPUID_FEAT_ECX_X2APIC = 1 << 21,
CPUID_FEAT_ECX_MOVBE = 1 << 22,
CPUID_FEAT_ECX_POPCNT = 1 << 23,
CPUID_FEAT_ECX_TSC = 1 << 24,
CPUID_FEAT_ECX_AES = 1 << 25,
CPUID_FEAT_ECX_XSAVE = 1 << 26,
CPUID_FEAT_ECX_OSXSAVE = 1 << 27,
CPUID_FEAT_ECX_AVX = 1 << 28,
CPUID_FEAT_ECX_F16C = 1 << 29,
CPUID_FEAT_ECX_RDRAND = 1 << 30,
CPUID_FEAT_ECX_HYPERVISOR = 1 << 31,
CPUID_FEAT_EDX_FPU = 1 << 0,
CPUID_FEAT_EDX_VME = 1 << 1,
CPUID_FEAT_EDX_DE = 1 << 2,
CPUID_FEAT_EDX_PSE = 1 << 3,
CPUID_FEAT_EDX_TSC = 1 << 4,
CPUID_FEAT_EDX_MSR = 1 << 5,
CPUID_FEAT_EDX_PAE = 1 << 6,
CPUID_FEAT_EDX_MCE = 1 << 7,
CPUID_FEAT_EDX_CX8 = 1 << 8,
CPUID_FEAT_EDX_APIC = 1 << 9,
CPUID_FEAT_EDX_SEP = 1 << 11,
CPUID_FEAT_EDX_MTRR = 1 << 12,
CPUID_FEAT_EDX_PGE = 1 << 13,
CPUID_FEAT_EDX_MCA = 1 << 14,
CPUID_FEAT_EDX_CMOV = 1 << 15,
CPUID_FEAT_EDX_PAT = 1 << 16,
CPUID_FEAT_EDX_PSE36 = 1 << 17,
CPUID_FEAT_EDX_PSN = 1 << 18,
CPUID_FEAT_EDX_CLFLUSH = 1 << 19,
CPUID_FEAT_EDX_DS = 1 << 21,
CPUID_FEAT_EDX_ACPI = 1 << 22,
CPUID_FEAT_EDX_MMX = 1 << 23,
CPUID_FEAT_EDX_FXSR = 1 << 24,
CPUID_FEAT_EDX_SSE = 1 << 25,
CPUID_FEAT_EDX_SSE2 = 1 << 26,
CPUID_FEAT_EDX_SS = 1 << 27,
CPUID_FEAT_EDX_HTT = 1 << 28,
CPUID_FEAT_EDX_TM = 1 << 29,
CPUID_FEAT_EDX_IA64 = 1 << 30,
CPUID_FEAT_EDX_PBE = 1 << 31
};
static inline void outb(uint16_t port, uint8_t val)
{
__asm__ volatile("outb %0, %1" : : "a"(val), "Nd"(port));
}
static inline uint8_t inb(uint16_t port)
{
uint8_t ret;
__asm__ volatile("inb %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
static inline void io_wait()
{
outb(0x80, 0);
}
static inline void cpuid(uint32_t code, uint32_t* a, uint32_t* d) {
__asm__ volatile("cpuid" : "=a"(*a), "=d"(*d) : "a"(code) : "ecx", "ebx");
}
static inline uint64_t rdmsr(uint32_t msr) {
uint32_t lo, hi;
__asm__ volatile("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr));
return ((uint64_t)hi << 32) | lo;
}
static inline void wrmsr(uint32_t msr, uint64_t value) {
uint32_t lo = (uint32_t)value;
uint32_t hi = (uint32_t)(value >> 32);
__asm__ volatile("wrmsr" : : "c"(msr), "a"(lo), "d"(hi));
}