No more double faults B-)

This commit is contained in:
2024-08-07 13:38:21 +02:00
parent cf7a7303e3
commit 1e385af53c
16 changed files with 496 additions and 155 deletions

View File

@@ -25,7 +25,9 @@ ASM_SOURCES := $(shell find $(SOURCE_DIR) -name '*.S')
OBJECTS := $(patsubst $(SOURCE_DIR)/%, $(BUILD_DIR)/%, $(C_SOURCES:.c=.o) $(ASM_SOURCES:.S=.o)) OBJECTS := $(patsubst $(SOURCE_DIR)/%, $(BUILD_DIR)/%, $(C_SOURCES:.c=.o) $(ASM_SOURCES:.S=.o))
DEPENDS := $(patsubst $(SOURCE_DIR)/%, $(BUILD_DIR)/%, $(C_SOURCES:.c=.d)) DEPENDS := $(patsubst $(SOURCE_DIR)/%, $(BUILD_DIR)/%, $(C_SOURCES:.c=.d))
CFLAGS := -MMD -ffreestanding -O1 -Wall -Wextra -Werror -std=c2x -I$(SOURCE_DIR)/include -no-pie -fstack-protector-strong CFLAGS := -MMD -ffreestanding -nostdlib -O1 -Wall -Wextra -Werror -std=c2x -I$(SOURCE_DIR)/include -no-pie -fstack-protector-strong -g3
CFLAGS += -Wno-unused-function
CFLAGS += -Wno-unused-variable
ASFLAGS := ASFLAGS :=
#$(info C_SOURCES is $(C_SOURCES)) #$(info C_SOURCES is $(C_SOURCES))
@@ -34,7 +36,7 @@ ASFLAGS :=
#$(info DEPENDS is $(DEPENDS)) #$(info DEPENDS is $(DEPENDS))
run: myos.iso run: myos.iso
qemu-system-i386 -cdrom myos.iso qemu-system-i386 -d int -no-reboot -cdrom myos.iso
cross-compiler: cross-compiler-image/Dockerfile cross-compiler: cross-compiler-image/Dockerfile
podman build cross-compiler-image -t cc-i686 podman build cross-compiler-image -t cc-i686

View File

@@ -1,3 +0,0 @@
#pragma once
__attribute__((interrupt)) void interrupt_handler_1(struct interrupt_frame* frame);

View File

@@ -33,7 +33,7 @@ undefined behavior.
.section .bss .section .bss
.align 16 .align 16
stack_bottom: stack_bottom:
.skip 16384 # 16 KiB .skip 1024 * 16 # 16384, 16 KiB
stack_top: stack_top:
/* /*

View File

@@ -4,7 +4,8 @@
#include "str.h" #include "str.h"
#include "tty.h" #include "tty.h"
__attribute__((noreturn)) void panic(struct str s); __attribute__((noreturn))
void panic(struct str s);
void* memmove(void *dest, const void *src, size_t n); void* memmove(void *dest, const void *src, size_t n);
void* memset(void *s, int c, size_t n); void* memset(void *s, int c, size_t n);

View File

@@ -32,23 +32,23 @@ struct gdt_table_entry gdt_encode_entry(struct gdt_entry_content content)
return entry; return entry;
} }
void gdt_set(uint16_t size, const struct gdt_table_entry base[], uint32_t offset) //void gdt_load(uint16_t size, const struct gdt_table_entry base[], uint32_t offset)
{ //{
base += offset; // base += offset;
//
/* the lgdt instruction requires a packed alignment */ // /* the lgdt instruction requires a packed alignment */
struct __attribute__((packed)) gdtr { // struct __attribute__((packed)) gdtr {
uint16_t size; // uint16_t size;
uint32_t base; // uint32_t base;
} gdt = { // } gdt = {
.size = size, // .size = size,
.base = (uint32_t)base, // .base = (uint32_t)base,
}; // };
//
__asm__ volatile ( // __asm__ volatile (
"lgdt %[gdt]" // "lgdt %[gdt]"
: // :
: [gdt] "m"(gdt) // : [gdt] "m"(gdt)
); // );
} //}

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include "types.h"
#define GDT_LIMIT_MAX 0xFFFFF #define GDT_LIMIT_MAX 0xFFFFF
@@ -46,23 +47,21 @@ enum gdt_access_flag : uint8_t {
}; };
enum gdt_flags : uint8_t { enum gdt_flags : uint8_t {
GDT_RESERVED = (1<<0), GDT_RESERVED = (1<<0),
GDT_LONG = (1<<1), GDT_LONG = (1<<1),
GDT_SIZE = (1<<2), GDT_32BIT = (1<<2),
GDT_GRANULARITY = (1<<3), GDT_GRANULARITY_PAGEWISE = (1<<3),
}; };
void gdt_set(uint16_t limit, const struct gdt_table_entry base[], uint32_t offset);
struct gdt_table_entry gdt_encode_entry(struct gdt_entry_content content); struct gdt_table_entry gdt_encode_entry(struct gdt_entry_content content);
// only compiles with O1 or higher ¯\_(ツ)_/¯, might have to make this a macro // only compiles with O1 or higher ¯\_(ツ)_/¯, might have to make this a macro
static inline void gdt_reload_granular(uint32_t code, static inline void gdt_flush_granular(uint32_t code,
uint32_t data, uint32_t data,
uint32_t extra_segment, uint32_t extra_segment,
uint32_t general_segment_1, uint32_t general_segment_1,
uint32_t general_segment_2, uint32_t general_segment_2,
uint32_t stack_segment) uint32_t stack_segment)
{ {
__asm__ volatile ( __asm__ volatile (
// Far jump to reload the CS register // Far jump to reload the CS register
@@ -88,10 +87,19 @@ static inline void gdt_reload_granular(uint32_t code,
: "ax" // Clobbered register : "ax" // Clobbered register
); );
} }
static inline void gdt_load(struct gdt_table_entry base[], uint16_t gdt_size, segment_t data, segment_t code)
static inline void gdt_reload(uint32_t data, uint32_t code)
{ {
/* the lgdt instruction requires a packed alignment */
struct __attribute__((packed)) {
uint16_t size;
uint32_t base;
} gdt = {
.size = gdt_size,
.base = (uint32_t)base,
};
__asm__ volatile ( __asm__ volatile (
"lgdt %[gdt]\n"
// Far jump to reload the CS register // Far jump to reload the CS register
"jmp %[cs], $reload_CS\n" "jmp %[cs], $reload_CS\n"
"reload_CS:\n" "reload_CS:\n"
@@ -103,8 +111,9 @@ static inline void gdt_reload(uint32_t data, uint32_t code)
"movw %%ax, %%gs\n" "movw %%ax, %%gs\n"
"movw %%ax, %%ss\n" "movw %%ax, %%ss\n"
: // No output operands : // No output operands
: [ds] "i"(data), : [gdt] "m"(gdt),
[cs] "i"(code) [ds] "i"(data),
[cs] "i"(code)
: "ax" // Clobbered register : "ax" // Clobbered register
); );
} }

View File

@@ -3,17 +3,18 @@
#include "idt.h" #include "idt.h"
struct idt_gate_descriptor idt_encode_descriptor(uint32_t offset, uint16_t segment_selector, uint8_t ist, enum idt_flags type) struct idt_gate_descriptor idt_encode_descriptor(void* func, segment_t segment_selector, enum idt_dpl dpl, enum idt_flags type)
{ {
struct idt_gate_descriptor output = {0}; struct idt_gate_descriptor output = {0};
uint32_t offset = (uint32_t)func;
output.offset_0 = offset & 0xFFFF; output.offset_0 = offset & 0xFFFF;
offset >>= 16; offset >>= 16;
output.offset_1 = offset & 0xFFFF; output.offset_1 = offset & 0xFFFF;
output.segment_selector = segment_selector; output.segment_selector = segment_selector;
output.ist = ist; output.flags = type | dpl | IDT_PRESENT;
output.flags = type | IDT_DPL_0 | IDT_PRESENT;
return output; return output;
} }

View File

@@ -1,11 +1,13 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include "types.h"
struct __attribute__((packed)) idt_gate_descriptor { struct __attribute__((packed)) idt_gate_descriptor {
uint16_t offset_0; uint16_t offset_0;
uint16_t segment_selector; uint16_t segment_selector;
uint8_t ist;
uint8_t ist; // unused in 32-bit
/* `flags` /* `flags`
======= =======
@@ -20,22 +22,26 @@ struct __attribute__((packed)) idt_gate_descriptor {
uint16_t offset_1; uint16_t offset_1;
}; };
enum idt_flags : uint8_t { _Static_assert(sizeof(struct idt_gate_descriptor) == 8);
IDT_GATE_TYPE_TASK_GATE = 0x5,
IDT_GATE_TYPE_INTERUPT_GATE_16 = 0x6,
IDT_GATE_TYPE_TRAP_GATE_16 = 0x7,
IDT_GATE_TYPE_INTERUPT_GATE_32 = 0xE,
IDT_GATE_TYPE_TRAP_GATE_32 = 0xF,
IDT_DPL_0 = 0 << 5, enum idt_flags : uint8_t {
IDT_DPL_1 = 1 << 5, IDT_GATE_TYPE_TASK = 0x5,
IDT_DPL_2 = 2 << 5, IDT_GATE_TYPE_INTERRUPT16 = 0x6,
IDT_DPL_3 = 3 << 5, IDT_GATE_TYPE_TRAP16 = 0x7,
IDT_GATE_TYPE_INTERRUPT32 = 0xE,
IDT_GATE_TYPE_TRAP32 = 0xF,
IDT_PRESENT = 1 << 7, IDT_PRESENT = 1 << 7,
}; };
struct idt_gate_descriptor idt_encode_descriptor(uint32_t offset, uint16_t segment_selector, uint8_t ist, enum idt_flags type); enum idt_dpl : uint8_t {
IDT_DPL_0 = 0 << 5,
IDT_DPL_1 = 1 << 5,
IDT_DPL_2 = 2 << 5,
IDT_DPL_3 = 3 << 5,
};
struct idt_gate_descriptor idt_encode_descriptor(void* func, segment_t segment_selector, enum idt_dpl dpl, enum idt_flags type);
void idt_load(struct idt_gate_descriptor table[], uint16_t size); void idt_load(struct idt_gate_descriptor table[], uint16_t size);

View File

@@ -1,11 +1,10 @@
#include <stdint.h> #include <stdint.h>
#include "libc.h" #include "libc.h"
#include "tty.h"
#include "interrupts.h"
#include "kernel_state.h"
#ifdef __x86_64__ #define EXCEPTION_DEPTH_MAX 5
typedef unsigned long long int uword_t;
#else
typedef unsigned int uword_t;
#endif
struct __attribute__((packed)) interrupt_frame { struct __attribute__((packed)) interrupt_frame {
uword_t ip; uword_t ip;
@@ -15,7 +14,131 @@ struct __attribute__((packed)) interrupt_frame {
uword_t ss; uword_t ss;
}; };
__attribute__((interrupt)) void interrupt_handler_1(struct interrupt_frame* frame) static void print_interrupt_frame(struct interrupt_frame* f)
{ {
(void)frame; printf(str_attach(
"Interrupt frame:\n"
"================\n"
"ip: {x32}\n"
"cs: {x32}\n"
"flags: {x32}\n"
"sp: {x32}\n"
"ss: {x32}\n"),
f->ip,
f->cs,
f->flags,
f->sp,
f->ss);
} }
/**
* Exceptions
* ==========
*/
__attribute__((interrupt, noreturn))
void exception_handler_general_protection_fault(struct interrupt_frame* frame, int err)
{
if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) {
panic(str_attach("fatal: too many nested exceptions\n"));
}
//terminal_clear();
terminal_set_color(VGA_COLOR_WHITE, VGA_COLOR_RED);
printf(str_attach(
"general protection fault by segment selector {str} :(\n"),
gdt_segment_index_str[err/8]);
if (frame != NULL) {
print_interrupt_frame(frame);
}
__asm__ volatile("cli; hlt");
__builtin_unreachable();
}
__attribute__((interrupt, noreturn))
void exception_handler_double_fault(struct interrupt_frame* frame)
{
(void)frame;
if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) {
panic(str_attach("fatal: too many nested exceptions\n"));
}
panic(str_attach("double fault :("));
}
__attribute__((interrupt, noreturn))
void exception_handler_div_by_zero(struct interrupt_frame* frame)
{
(void)frame;
if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) {
panic(str_attach("fatal: too many nested exceptions\n"));
}
panic(str_attach("div by zero occured :("));
}
__attribute__((interrupt, noreturn))
void exception_handler_page_fault(struct interrupt_frame* frame, int err)
{
(void)frame;
if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) {
panic(str_attach("fatal: too many nested exceptions\n"));
}
terminal_set_color(VGA_COLOR_WHITE, VGA_COLOR_RED);
printf(str_attach(
"page fault :(, err: 0x{x32}\n"),
err,
gdt_segment_index_str[err/8]);
__asm__ volatile("cli; hlt");
__builtin_unreachable();
}
__attribute__((interrupt, noreturn))
void exception_default(struct interrupt_frame* frame)
{
(void)frame;
if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) {
panic(str_attach("fatal: too many nested exceptions\n"));
}
panic(str_attach("non-implemented exception occured"));
kernel.nested_exception_counter = 0;
}
/**
* Interrupts
* ==========
*/
__attribute__((interrupt, noreturn))
void interrupt_default(struct interrupt_frame* frame)
{
if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) {
panic(str_attach("fatal: too many nested exceptions\n"));
}
(void)frame;
panic(str_attach("non-implemented interrupt invoked"));
kernel.nested_exception_counter = 0;
}
__attribute__((interrupt))
void interrupt_handler_1(struct interrupt_frame* frame)
{
if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) {
panic(str_attach("fatal: too many nested exceptions\n"));
}
(void)frame;
printf(str_attach("interrupt_handler_1 called!\n"));
kernel.nested_exception_counter = 0;
}
__attribute__((interrupt))
void interrupt_handler_userspace_exit(struct interrupt_frame* frame)
{
if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) {
panic(str_attach("fatal: too many nested exceptions\n"));
}
(void)frame;
printf(str_attach("interrupt_handler_userspace_exit called!\n"));
kernel.nested_exception_counter = 0;
}

33
src/kernel/interrupts.h Normal file
View File

@@ -0,0 +1,33 @@
#pragma once
#include "types.h"
struct __attribute__((packed)) interrupt_frame {
uword_t ip;
uword_t cs;
uword_t flags;
uword_t sp;
uword_t ss;
};
__attribute__((interrupt, noreturn))
void exception_handler_div_by_zero(struct interrupt_frame* frame);
__attribute__((interrupt, noreturn))
void exception_handler_general_protection_fault(struct interrupt_frame* frame, int err);
__attribute__((interrupt, noreturn))
void exception_handler_double_fault(struct interrupt_frame* frame);
__attribute__((interrupt, noreturn))
void exception_default(struct interrupt_frame* frame);
__attribute__((interrupt, noreturn))
void interrupt_default(struct interrupt_frame* frame);
__attribute__((interrupt))
void interrupt_handler_1(struct interrupt_frame* frame);
__attribute__((interrupt))
void interrupt_handler_userspace_exit(struct interrupt_frame* frame);

View File

@@ -5,52 +5,69 @@
#include "gdt.h" #include "gdt.h"
#include "tss.h" #include "tss.h"
#include "idt.h" #include "idt.h"
#include "interrupts.h"
#include "types.h"
#include "kernel_state.h"
// Future user-space // Future user-space
#include "libc.h" #include "libc.h"
#include "tty.h" #include "tty.h"
#include "str.h"
// TOOD: clean up this static void pic_disable()
typedef void(*func_t)(void*);
static void ring3_mode(uint32_t, uint32_t, uint32_t, uint32_t, func_t);
static void user_mode_code(void*);
void gdt_setup_flat_model()
{ {
__asm__ volatile ("outb %0, %1"
:
: "a"((uint8_t)0xff), "Nd"((uint16_t)0x21)
: "memory");
__asm__ volatile ("outb %0, %1"
:
: "a"((uint8_t)0xff), "Nd"((uint16_t)0xA1)
: "memory");
} }
/* copied and only slightly modified from an osdev wiki article with poor
taste, TODO: change
----------------------------------------------*/
static void user_mode_code(void*) static void user_mode_code(void*)
{ {
printf(str_attach("hello world :)\n")); printf(str_attach("hello from user-space before interrupt :)\n"));
__asm__ volatile ("int $0x80");
__asm__ volatile ("int $0x81");
//while (1) /* busy loop */;
#if 0
printf(str_attach("hello from user-space before exception :)\n"));
/* test division by 0 exception */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdiv-by-zero"
volatile int a = 5/0;
(void)a;
#pragma GCC diagnostic pop
// should not happen
printf(str_attach("hello from userspace after interrupt and exception!\n"));
#endif
} }
static void ring3_mode(uint32_t udata_offset, uint32_t udata_rpl, uint32_t ucode_offset, uint32_t ucode_rpl, func_t callback) static void ring3_mode(segment_t udata_segment, segment_t ucode_segment, func_t callback)
{ {
const uint32_t udata = udata_offset | udata_rpl;
const uint32_t ucode = ucode_offset | ucode_rpl;
__asm__ volatile ( __asm__ volatile (
"mov %[udata], %%ax\n" "mov %[udata], %%ax\n"
"mov %%ax, %%ds\n" "mov %%ax, %%ds\n"
"mov %%ax, %%es\n" "mov %%ax, %%es\n"
"mov %%ax, %%fs\n" "mov %%ax, %%fs\n"
"mov %%ax, %%gs\n" "mov %%ax, %%gs\n"
"push %%ax\n" "push %%ax\n"
"mov %%esp, %%eax\n" "mov %%esp, %%eax\n"
"push %%eax\n" // current esp "push %%eax\n" // esp
"pushf\n" // eflags "pushf\n" // eflags
"push %[ucode]\n" "push %[ucode]\n"
"push %[callback]\n" // instruction address to return to "push %[callback]\n" // instruction address to return to
"iret" "iret"
: :
: [udata] "m"(udata), : [udata] "i"(udata_segment),
[ucode] "m"(ucode), [ucode] "i"(ucode_segment),
[callback] "m"(callback) [callback] "i"(callback)
: "eax" : "eax"
); );
} }
@@ -65,25 +82,12 @@ void kernel_main(void)
{ {
__asm__ volatile("cli"); __asm__ volatile("cli");
static struct tss tss = {0}; _Static_assert(sizeof(kernel.gdt) == 0x30);
enum segment_index : size_t { kernel.gdt[SEGMENT_NULL] = gdt_encode_entry((struct gdt_entry_content){0});
null_segment = 0,
kernel_code_segment = 1,
kernel_data_segment = 2,
user_code_segment = 3,
user_data_segment = 4,
task_state_segment = 5,
segment_count,
};
static struct gdt_table_entry gdt[segment_count];
_Static_assert(sizeof(gdt) == 0x30);
gdt[null_segment] = gdt_encode_entry((struct gdt_entry_content){0});
/* kernel */ /* kernel */
gdt[kernel_code_segment] = gdt_encode_entry((struct gdt_entry_content){ kernel.gdt[SEGMENT_KERNEL_CODE] = gdt_encode_entry((struct gdt_entry_content){
.base = 0, .base = 0,
.limit = GDT_LIMIT_MAX, .limit = GDT_LIMIT_MAX,
.access_byte = GDT_ACCESS_RW .access_byte = GDT_ACCESS_RW
@@ -91,21 +95,21 @@ void kernel_main(void)
| GDT_ACCESS_DESCRIPTOR | GDT_ACCESS_DESCRIPTOR
| GDT_ACCESS_DPL_0 | GDT_ACCESS_DPL_0
| GDT_ACCESS_PRESENT, | GDT_ACCESS_PRESENT,
.flags = GDT_SIZE .flags = GDT_32BIT
| GDT_GRANULARITY}); | GDT_GRANULARITY_PAGEWISE});
gdt[kernel_data_segment] = gdt_encode_entry((struct gdt_entry_content){ kernel.gdt[SEGMENT_KERNEL_DATA] = gdt_encode_entry((struct gdt_entry_content){
.base = 0, .base = 0,
.limit = GDT_LIMIT_MAX, .limit = GDT_LIMIT_MAX,
.access_byte = GDT_ACCESS_RW .access_byte = GDT_ACCESS_RW
| GDT_ACCESS_DESCRIPTOR | GDT_ACCESS_DESCRIPTOR
| GDT_ACCESS_DPL_0 | GDT_ACCESS_DPL_0
| GDT_ACCESS_PRESENT, | GDT_ACCESS_PRESENT,
.flags = GDT_SIZE .flags = GDT_32BIT
| GDT_GRANULARITY}); | GDT_GRANULARITY_PAGEWISE});
/* user */ /* user */
gdt[user_code_segment] = gdt_encode_entry((struct gdt_entry_content) { kernel.gdt[SEGMENT_USER_CODE] = gdt_encode_entry((struct gdt_entry_content) {
.base = 0, .base = 0,
.limit = GDT_LIMIT_MAX, .limit = GDT_LIMIT_MAX,
.access_byte = GDT_ACCESS_RW .access_byte = GDT_ACCESS_RW
@@ -113,54 +117,111 @@ void kernel_main(void)
| GDT_ACCESS_DESCRIPTOR | GDT_ACCESS_DESCRIPTOR
| GDT_ACCESS_DPL_3 | GDT_ACCESS_DPL_3
| GDT_ACCESS_PRESENT, | GDT_ACCESS_PRESENT,
.flags = GDT_SIZE .flags = GDT_32BIT
| GDT_GRANULARITY}); | GDT_GRANULARITY_PAGEWISE});
gdt[user_data_segment] = gdt_encode_entry((struct gdt_entry_content) { kernel.gdt[SEGMENT_USER_DATA] = gdt_encode_entry((struct gdt_entry_content) {
.base = 0, .base = 0,
.limit = GDT_LIMIT_MAX, .limit = GDT_LIMIT_MAX,
.access_byte = GDT_ACCESS_RW .access_byte = GDT_ACCESS_RW
| GDT_ACCESS_DESCRIPTOR | GDT_ACCESS_DESCRIPTOR
| GDT_ACCESS_DPL_3 | GDT_ACCESS_DPL_3
| GDT_ACCESS_PRESENT, | GDT_ACCESS_PRESENT,
.flags = GDT_SIZE .flags = GDT_32BIT
| GDT_GRANULARITY}); | GDT_GRANULARITY_PAGEWISE});
/* tss */ /* tss */
gdt[task_state_segment] = gdt_encode_entry((struct gdt_entry_content) { kernel.gdt[SEGMENT_TASK_STATE] = gdt_encode_entry((struct gdt_entry_content) {
.base = (uint32_t)&tss, .base = (uint32_t)&kernel.tss,
.limit = sizeof(tss)-1, .limit = sizeof(kernel.tss)-1,
.access_byte = GDT_ACCESS_ACCESSED .access_byte = GDT_ACCESS_ACCESSED
| GDT_ACCESS_EXEC | GDT_ACCESS_EXEC
| GDT_ACCESS_DPL_0 | GDT_ACCESS_DPL_0
| GDT_ACCESS_PRESENT, | GDT_ACCESS_PRESENT,
.flags = 0}); .flags = 0});
gdt_set(sizeof gdt, gdt, 0);
gdt_reload(kernel_data_segment * sizeof (struct gdt_table_entry),
kernel_code_segment * sizeof (struct gdt_table_entry));
/* Setup the TSS */ /* Setup the TSS */
tss.ss0 = kernel_data_segment * sizeof (struct gdt_table_entry); memset(&kernel.tss, 0, sizeof kernel.tss);
tss.esp0 = 0; /* TODO: set kernel stack pointer */ kernel.tss.ss0 = segment(SEGMENT_KERNEL_DATA, SEGMENT_GDT, 0);
#if 1
static uint8_t kernel_stack[1024];
kernel.tss.esp0 = (uint32_t)kernel_stack;
#else
kernel.tss.esp0 = 0;
#endif
tss_load(task_state_segment * sizeof (struct gdt_table_entry)); #if 1
/* Setup the IDT */
for (size_t i = 0; i < 32; i++) {
kernel.idt[i] = idt_encode_descriptor(
exception_default,
segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0),
IDT_DPL_3,
IDT_GATE_TYPE_TRAP32);
}
for (size_t i = 32; i < IDT_COUNT; i++) {
kernel.idt[i] = idt_encode_descriptor(
interrupt_default,
segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0),
IDT_DPL_3,
IDT_GATE_TYPE_INTERRUPT32);
}
/* Setup the IDT */ kernel.idt[0x0] = idt_encode_descriptor(
//static struct idt_gate_descriptor idt[256] = {0}; exception_handler_div_by_zero,
segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0),
IDT_DPL_3,
IDT_GATE_TYPE_TRAP32
);
//struct idt_gate_descriptor idt_encode_descriptor(uint32_t offset, uint16_t segment_selector, uint8_t ist, enum idt_flags type); kernel.idt[0x8] = idt_encode_descriptor(
//idt[80] = idt_encode_descriptor(interrupt_handler_1, kernel_code_segment, 0, IDT_GATE_TYPE_INTERUPT_GATE_32); exception_handler_double_fault,
segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0),
IDT_DPL_3,
IDT_GATE_TYPE_TRAP32
);
//idt_load(idt, sizeof idt); kernel.idt[0xD] = idt_encode_descriptor(
exception_handler_general_protection_fault,
segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0),
IDT_DPL_3,
IDT_GATE_TYPE_TRAP32
);
/* Finally go to ring 3 */ // Interrupts
ring3_mode(user_data_segment * sizeof (struct gdt_table_entry), 3, kernel.idt[0x80] = idt_encode_descriptor(
user_code_segment * sizeof (struct gdt_table_entry), 3, interrupt_handler_1,
user_mode_code); segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0),
IDT_DPL_3,
IDT_GATE_TYPE_INTERRUPT32
);
kernel.idt[0x81] = idt_encode_descriptor(
interrupt_handler_userspace_exit,
segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0),
IDT_DPL_3,
IDT_GATE_TYPE_INTERRUPT32
);
#endif
/* Flush the gdt and tss */
gdt_load(kernel.gdt,
sizeof kernel.gdt,
segment(SEGMENT_KERNEL_DATA, SEGMENT_GDT, 0),
segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0));
tss_load(segment(SEGMENT_TASK_STATE, SEGMENT_GDT, 0));
idt_load(kernel.idt, sizeof kernel.idt);
/* enable interrupts */
__asm__ volatile("sti"); __asm__ volatile("sti");
/* Finally go to ring 3 */
printf(str_attach("hello from kernel space!\n"));
pic_disable(); // temporary duct-tape
ring3_mode(segment(SEGMENT_USER_DATA, SEGMENT_GDT, 3),
segment(SEGMENT_USER_CODE, SEGMENT_GDT, 3),
user_mode_code);
} }

18
src/kernel/kernel_state.c Normal file
View File

@@ -0,0 +1,18 @@
#include "str.h"
#include "kernel_state.h"
const struct str gdt_segment_index_str[SEGMENT_COUNT] = {
[SEGMENT_NULL ] = str_attach("SEGMENT_NULL"),
[SEGMENT_KERNEL_CODE] = str_attach("SEGMENT_KERNEL_CODE"),
[SEGMENT_KERNEL_DATA] = str_attach("SEGMENT_KERNEL_DATA"),
[SEGMENT_USER_CODE ] = str_attach("SEGMENT_USER_CODE"),
[SEGMENT_USER_DATA ] = str_attach("SEGMENT_USER_DATA"),
[SEGMENT_TASK_STATE ] = str_attach("SEGMENT_TASK_STATE"),
};
const struct str idt_index_str[IDT_COUNT] = {
};
struct kernel_state kernel = { 0 };

34
src/kernel/kernel_state.h Normal file
View File

@@ -0,0 +1,34 @@
#pragma once
#include "str.h"
#include "tss.h"
#include "idt.h"
#include "gdt.h"
enum gdt_segment_index : size_t {
SEGMENT_NULL,
SEGMENT_KERNEL_CODE,
SEGMENT_KERNEL_DATA,
SEGMENT_USER_CODE,
SEGMENT_USER_DATA,
SEGMENT_TASK_STATE,
SEGMENT_COUNT,
};
extern const struct str gdt_segment_index_str[SEGMENT_COUNT]; // reverse lookup enum -> str
enum idt_index : size_t {
IDT_COUNT = 256
};
extern const struct str idt_index_str[IDT_COUNT]; // reverse lookup enum -> str
constexpr size_t BACKTRACE_MAX = 256;
struct kernel_state {
struct tss tss;
struct gdt_table_entry gdt[SEGMENT_COUNT];
struct idt_gate_descriptor idt[IDT_COUNT];
int nested_exception_counter;
};
extern struct kernel_state kernel;

27
src/kernel/types.h Normal file
View File

@@ -0,0 +1,27 @@
#pragma once
typedef void(*func_t)(void*);
/* define uword_t per the instruction set manual */
#ifdef __x86_64__
typedef unsigned long long int uword_t;
#else
typedef unsigned int uword_t;
#endif
/* segment selectors identifies segments either in the gdt or the ldt */
typedef uint16_t segment_t;
enum segment_ti : uint16_t {
SEGMENT_GDT = 0,
SEGMENT_LDT = 1,
};
static inline segment_t segment(uint16_t index, enum segment_ti ti, uint16_t rpl)
{
return (index << 3) | ((ti & 0xF) << 1) | (rpl & 0xFF);
}

View File

@@ -15,10 +15,10 @@
__attribute__((noreturn)) __attribute__((noreturn))
void panic(struct str s) void panic(struct str s)
{ {
terminal_clear(); //terminal_clear();
terminal_set_color(VGA_COLOR_WHITE, VGA_COLOR_RED); terminal_set_color(VGA_COLOR_WHITE, VGA_COLOR_RED);
terminal_write(s); terminal_write(s);
__asm__ volatile("hlt"); __asm__ volatile("cli; hlt");
__builtin_unreachable(); __builtin_unreachable();
} }

View File

@@ -31,12 +31,16 @@ static inline int ps_get(struct printf_state* s)
return s->str.data[s->i++]; return s->str.data[s->i++];
} }
static int print_long(unsigned long n, struct str alphabet, bool is_signed) static int print_long(unsigned long n, struct str alphabet, bool is_signed, unsigned int padding, char pad_char)
{ {
constexpr size_t BUF_SZ = 21; constexpr size_t BUF_SZ = 128;
char buf[BUF_SZ]; char buf[BUF_SZ];
size_t i = 0; size_t i = 0;
if (padding > BUF_SZ) {
return -1;
}
if (n == 0) { if (n == 0) {
terminal_putchar('0'); terminal_putchar('0');
return 1; return 1;
@@ -59,37 +63,60 @@ static int print_long(unsigned long n, struct str alphabet, bool is_signed)
buf[BUF_SZ-i] = alphabet.data[n % alphabet.len]; buf[BUF_SZ-i] = alphabet.data[n % alphabet.len];
n /= alphabet.len; n /= alphabet.len;
} }
while (i < padding) {
i += 1;
buf[BUF_SZ-i] = pad_char;
}
terminal_write((struct str){.data = &buf[BUF_SZ-i], .len = i}); terminal_write((struct str){.data = &buf[BUF_SZ-i], .len = i});
return i; return i;
} }
static int print_i32(struct printf_state* s) static int print_i32(struct printf_state* s, int padding, char pad_char)
{ {
padding = padding ? padding : 0;
pad_char = pad_char ? pad_char : ' ';
long n = va_arg(s->ap, int32_t); long n = va_arg(s->ap, int32_t);
return print_long(n, str_attach("0123456789"), true); return print_long(n, str_attach("0123456789"), true, padding, pad_char);
} }
static int print_u32(struct printf_state* s) static int print_u32(struct printf_state* s, int padding, char pad_char)
{ {
padding = padding ? padding : 0;
pad_char = pad_char ? pad_char : ' ';
unsigned long n = va_arg(s->ap, uint32_t); unsigned long n = va_arg(s->ap, uint32_t);
return print_long(n, str_attach("0123456789"), false); return print_long(n, str_attach("0123456789"), false, padding, pad_char);
} }
static int print_x32(struct printf_state* s) static int print_x32(struct printf_state* s, int padding, char pad_char)
{ {
padding = padding ? padding : 0;
pad_char = pad_char ? pad_char : '0';
unsigned long n = va_arg(s->ap, uint32_t); unsigned long n = va_arg(s->ap, uint32_t);
return print_long(n, str_attach("0123456789abcdefgh"), false); struct str alphabet = str_attach("0123456789abcdef");
return print_long(n, alphabet, false, padding, pad_char);
} }
static int print_str(struct printf_state* s) static int print_b32(struct printf_state* s, int padding, char pad_char)
{ {
const struct str str = va_arg(s->ap, struct str); padding = padding ? padding : 32;
for (size_t i=0; i < str.len; i++) { pad_char = pad_char ? pad_char : '0';
terminal_putchar(str.data[i]); unsigned long n = va_arg(s->ap, uint32_t);
} struct str alphabet = str_attach("01");
return str.len; return print_long(n, alphabet, false, padding, pad_char);
}
static int print_str(struct printf_state* s, int padding, char pad_char)
{
// TODO: implement padding
(void)padding;
(void)pad_char;
const struct str str = va_arg(s->ap, struct str);
for (size_t i=0; i < str.len; i++) {
terminal_putchar(str.data[i]);
}
return str.len;
} }
#pragma GCC diagnostic ignored "-Wmultichar" #pragma GCC diagnostic ignored "-Wmultichar"
@@ -97,6 +124,8 @@ static int parse_format_cmd(struct printf_state* s)
{ {
int c; int c;
uint32_t cmd = 0; uint32_t cmd = 0;
int pad = 0;
char pad_char = '\0';
constexpr uint32_t CHAR_MASK = (1<<CHAR_BIT)-1; constexpr uint32_t CHAR_MASK = (1<<CHAR_BIT)-1;
@@ -109,21 +138,21 @@ static int parse_format_cmd(struct printf_state* s)
return -1; return -1;
switch (cmd) { switch (cmd) {
// 16 bit types are promoted to 32 bit by va_arg(...) anyways // 16 bit types are promoted to 32 bit by va_arg(...) anyways
case 'i16': case 'i16':
case 'i32': case 'i32':
return print_i32(s); return print_i32(s, pad, pad_char);
case 'u16': case 'u16':
case 'u32': case 'u32':
return print_u32(s); return print_u32(s, pad, pad_char);
case 'x16': case 'x16':
case 'x32': case 'x32':
return print_x32(s); return print_x32(s, pad, pad_char);
case 'str': case 'str':
return print_str(s); return print_str(s, pad, pad_char);
default: default:
return -1; return -1;