No more double faults B-)
This commit is contained in:
6
Makefile
6
Makefile
@@ -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))
|
||||
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 :=
|
||||
|
||||
#$(info C_SOURCES is $(C_SOURCES))
|
||||
@@ -34,7 +36,7 @@ ASFLAGS :=
|
||||
#$(info DEPENDS is $(DEPENDS))
|
||||
|
||||
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
|
||||
podman build cross-compiler-image -t cc-i686
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
__attribute__((interrupt)) void interrupt_handler_1(struct interrupt_frame* frame);
|
||||
@@ -33,7 +33,7 @@ undefined behavior.
|
||||
.section .bss
|
||||
.align 16
|
||||
stack_bottom:
|
||||
.skip 16384 # 16 KiB
|
||||
.skip 1024 * 16 # 16384, 16 KiB
|
||||
stack_top:
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
#include "str.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* memset(void *s, int c, size_t n);
|
||||
|
||||
@@ -32,23 +32,23 @@ struct gdt_table_entry gdt_encode_entry(struct gdt_entry_content content)
|
||||
return entry;
|
||||
}
|
||||
|
||||
void gdt_set(uint16_t size, const struct gdt_table_entry base[], uint32_t offset)
|
||||
{
|
||||
base += offset;
|
||||
|
||||
/* the lgdt instruction requires a packed alignment */
|
||||
struct __attribute__((packed)) gdtr {
|
||||
uint16_t size;
|
||||
uint32_t base;
|
||||
} gdt = {
|
||||
.size = size,
|
||||
.base = (uint32_t)base,
|
||||
};
|
||||
|
||||
__asm__ volatile (
|
||||
"lgdt %[gdt]"
|
||||
:
|
||||
: [gdt] "m"(gdt)
|
||||
);
|
||||
}
|
||||
//void gdt_load(uint16_t size, const struct gdt_table_entry base[], uint32_t offset)
|
||||
//{
|
||||
// base += offset;
|
||||
//
|
||||
// /* the lgdt instruction requires a packed alignment */
|
||||
// struct __attribute__((packed)) gdtr {
|
||||
// uint16_t size;
|
||||
// uint32_t base;
|
||||
// } gdt = {
|
||||
// .size = size,
|
||||
// .base = (uint32_t)base,
|
||||
// };
|
||||
//
|
||||
// __asm__ volatile (
|
||||
// "lgdt %[gdt]"
|
||||
// :
|
||||
// : [gdt] "m"(gdt)
|
||||
// );
|
||||
//}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include "types.h"
|
||||
|
||||
#define GDT_LIMIT_MAX 0xFFFFF
|
||||
|
||||
@@ -46,23 +47,21 @@ enum gdt_access_flag : uint8_t {
|
||||
};
|
||||
|
||||
enum gdt_flags : uint8_t {
|
||||
GDT_RESERVED = (1<<0),
|
||||
GDT_LONG = (1<<1),
|
||||
GDT_SIZE = (1<<2),
|
||||
GDT_GRANULARITY = (1<<3),
|
||||
GDT_RESERVED = (1<<0),
|
||||
GDT_LONG = (1<<1),
|
||||
GDT_32BIT = (1<<2),
|
||||
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);
|
||||
|
||||
// only compiles with O1 or higher ¯\_(ツ)_/¯, might have to make this a macro
|
||||
static inline void gdt_reload_granular(uint32_t code,
|
||||
uint32_t data,
|
||||
uint32_t extra_segment,
|
||||
uint32_t general_segment_1,
|
||||
uint32_t general_segment_2,
|
||||
uint32_t stack_segment)
|
||||
static inline void gdt_flush_granular(uint32_t code,
|
||||
uint32_t data,
|
||||
uint32_t extra_segment,
|
||||
uint32_t general_segment_1,
|
||||
uint32_t general_segment_2,
|
||||
uint32_t stack_segment)
|
||||
{
|
||||
__asm__ volatile (
|
||||
// Far jump to reload the CS register
|
||||
@@ -88,10 +87,19 @@ static inline void gdt_reload_granular(uint32_t code,
|
||||
: "ax" // Clobbered register
|
||||
);
|
||||
}
|
||||
|
||||
static inline void gdt_reload(uint32_t data, uint32_t code)
|
||||
static inline void gdt_load(struct gdt_table_entry base[], uint16_t gdt_size, segment_t data, segment_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 (
|
||||
"lgdt %[gdt]\n"
|
||||
// Far jump to reload the CS register
|
||||
"jmp %[cs], $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, %%ss\n"
|
||||
: // No output operands
|
||||
: [ds] "i"(data),
|
||||
[cs] "i"(code)
|
||||
: [gdt] "m"(gdt),
|
||||
[ds] "i"(data),
|
||||
[cs] "i"(code)
|
||||
: "ax" // Clobbered register
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,17 +3,18 @@
|
||||
|
||||
#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};
|
||||
|
||||
uint32_t offset = (uint32_t)func;
|
||||
|
||||
output.offset_0 = offset & 0xFFFF;
|
||||
offset >>= 16;
|
||||
output.offset_1 = offset & 0xFFFF;
|
||||
|
||||
output.segment_selector = segment_selector;
|
||||
output.ist = ist;
|
||||
output.flags = type | IDT_DPL_0 | IDT_PRESENT;
|
||||
output.flags = type | dpl | IDT_PRESENT;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "types.h"
|
||||
|
||||
struct __attribute__((packed)) idt_gate_descriptor {
|
||||
uint16_t offset_0;
|
||||
uint16_t segment_selector;
|
||||
uint8_t ist;
|
||||
|
||||
uint8_t ist; // unused in 32-bit
|
||||
|
||||
/* `flags`
|
||||
=======
|
||||
@@ -20,22 +22,26 @@ struct __attribute__((packed)) idt_gate_descriptor {
|
||||
uint16_t offset_1;
|
||||
};
|
||||
|
||||
_Static_assert(sizeof(struct idt_gate_descriptor) == 8);
|
||||
|
||||
enum idt_flags : uint8_t {
|
||||
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_GATE_TYPE_TASK = 0x5,
|
||||
IDT_GATE_TYPE_INTERRUPT16 = 0x6,
|
||||
IDT_GATE_TYPE_TRAP16 = 0x7,
|
||||
IDT_GATE_TYPE_INTERRUPT32 = 0xE,
|
||||
IDT_GATE_TYPE_TRAP32 = 0xF,
|
||||
|
||||
IDT_PRESENT = 1 << 7,
|
||||
};
|
||||
|
||||
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,
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
#include <stdint.h>
|
||||
#include "libc.h"
|
||||
#include "tty.h"
|
||||
#include "interrupts.h"
|
||||
#include "kernel_state.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
typedef unsigned long long int uword_t;
|
||||
#else
|
||||
typedef unsigned int uword_t;
|
||||
#endif
|
||||
#define EXCEPTION_DEPTH_MAX 5
|
||||
|
||||
struct __attribute__((packed)) interrupt_frame {
|
||||
uword_t ip;
|
||||
@@ -15,7 +14,131 @@ struct __attribute__((packed)) interrupt_frame {
|
||||
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
33
src/kernel/interrupts.h
Normal 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);
|
||||
|
||||
@@ -5,52 +5,69 @@
|
||||
#include "gdt.h"
|
||||
#include "tss.h"
|
||||
#include "idt.h"
|
||||
#include "interrupts.h"
|
||||
#include "types.h"
|
||||
#include "kernel_state.h"
|
||||
|
||||
// Future user-space
|
||||
#include "libc.h"
|
||||
#include "tty.h"
|
||||
#include "str.h"
|
||||
|
||||
// TOOD: clean up this
|
||||
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()
|
||||
static void pic_disable()
|
||||
{
|
||||
__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*)
|
||||
{
|
||||
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 (
|
||||
"mov %[udata], %%ax\n"
|
||||
"mov %%ax, %%ds\n"
|
||||
"mov %%ax, %%es\n"
|
||||
"mov %%ax, %%fs\n"
|
||||
"mov %%ax, %%gs\n"
|
||||
"push %%ax\n"
|
||||
"mov %[udata], %%ax\n"
|
||||
"mov %%ax, %%ds\n"
|
||||
"mov %%ax, %%es\n"
|
||||
"mov %%ax, %%fs\n"
|
||||
"mov %%ax, %%gs\n"
|
||||
"push %%ax\n"
|
||||
|
||||
"mov %%esp, %%eax\n"
|
||||
"push %%eax\n" // current esp
|
||||
"push %%eax\n" // esp
|
||||
"pushf\n" // eflags
|
||||
"push %[ucode]\n"
|
||||
"push %[callback]\n" // instruction address to return to
|
||||
"iret"
|
||||
:
|
||||
: [udata] "m"(udata),
|
||||
[ucode] "m"(ucode),
|
||||
[callback] "m"(callback)
|
||||
: [udata] "i"(udata_segment),
|
||||
[ucode] "i"(ucode_segment),
|
||||
[callback] "i"(callback)
|
||||
: "eax"
|
||||
);
|
||||
}
|
||||
@@ -65,25 +82,12 @@ void kernel_main(void)
|
||||
{
|
||||
__asm__ volatile("cli");
|
||||
|
||||
static struct tss tss = {0};
|
||||
_Static_assert(sizeof(kernel.gdt) == 0x30);
|
||||
|
||||
enum segment_index : size_t {
|
||||
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.gdt[SEGMENT_NULL] = gdt_encode_entry((struct gdt_entry_content){0});
|
||||
|
||||
/* 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,
|
||||
.limit = GDT_LIMIT_MAX,
|
||||
.access_byte = GDT_ACCESS_RW
|
||||
@@ -91,21 +95,21 @@ void kernel_main(void)
|
||||
| GDT_ACCESS_DESCRIPTOR
|
||||
| GDT_ACCESS_DPL_0
|
||||
| GDT_ACCESS_PRESENT,
|
||||
.flags = GDT_SIZE
|
||||
| GDT_GRANULARITY});
|
||||
.flags = GDT_32BIT
|
||||
| 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,
|
||||
.limit = GDT_LIMIT_MAX,
|
||||
.access_byte = GDT_ACCESS_RW
|
||||
| GDT_ACCESS_DESCRIPTOR
|
||||
| GDT_ACCESS_DPL_0
|
||||
| GDT_ACCESS_PRESENT,
|
||||
.flags = GDT_SIZE
|
||||
| GDT_GRANULARITY});
|
||||
.flags = GDT_32BIT
|
||||
| GDT_GRANULARITY_PAGEWISE});
|
||||
|
||||
/* 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,
|
||||
.limit = GDT_LIMIT_MAX,
|
||||
.access_byte = GDT_ACCESS_RW
|
||||
@@ -113,54 +117,111 @@ void kernel_main(void)
|
||||
| GDT_ACCESS_DESCRIPTOR
|
||||
| GDT_ACCESS_DPL_3
|
||||
| GDT_ACCESS_PRESENT,
|
||||
.flags = GDT_SIZE
|
||||
| GDT_GRANULARITY});
|
||||
.flags = GDT_32BIT
|
||||
| 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,
|
||||
.limit = GDT_LIMIT_MAX,
|
||||
.access_byte = GDT_ACCESS_RW
|
||||
| GDT_ACCESS_DESCRIPTOR
|
||||
| GDT_ACCESS_DPL_3
|
||||
| GDT_ACCESS_PRESENT,
|
||||
.flags = GDT_SIZE
|
||||
| GDT_GRANULARITY});
|
||||
.flags = GDT_32BIT
|
||||
| GDT_GRANULARITY_PAGEWISE});
|
||||
|
||||
/* tss */
|
||||
gdt[task_state_segment] = gdt_encode_entry((struct gdt_entry_content) {
|
||||
.base = (uint32_t)&tss,
|
||||
.limit = sizeof(tss)-1,
|
||||
kernel.gdt[SEGMENT_TASK_STATE] = gdt_encode_entry((struct gdt_entry_content) {
|
||||
.base = (uint32_t)&kernel.tss,
|
||||
.limit = sizeof(kernel.tss)-1,
|
||||
.access_byte = GDT_ACCESS_ACCESSED
|
||||
| GDT_ACCESS_EXEC
|
||||
| GDT_ACCESS_DPL_0
|
||||
| GDT_ACCESS_PRESENT,
|
||||
.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 */
|
||||
tss.ss0 = kernel_data_segment * sizeof (struct gdt_table_entry);
|
||||
tss.esp0 = 0; /* TODO: set kernel stack pointer */
|
||||
memset(&kernel.tss, 0, sizeof kernel.tss);
|
||||
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 */
|
||||
//static struct idt_gate_descriptor idt[256] = {0};
|
||||
kernel.idt[0x0] = idt_encode_descriptor(
|
||||
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);
|
||||
//idt[80] = idt_encode_descriptor(interrupt_handler_1, kernel_code_segment, 0, IDT_GATE_TYPE_INTERUPT_GATE_32);
|
||||
kernel.idt[0x8] = idt_encode_descriptor(
|
||||
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 */
|
||||
ring3_mode(user_data_segment * sizeof (struct gdt_table_entry), 3,
|
||||
user_code_segment * sizeof (struct gdt_table_entry), 3,
|
||||
user_mode_code);
|
||||
// Interrupts
|
||||
kernel.idt[0x80] = idt_encode_descriptor(
|
||||
interrupt_handler_1,
|
||||
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");
|
||||
|
||||
/* 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
18
src/kernel/kernel_state.c
Normal 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
34
src/kernel/kernel_state.h
Normal 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
27
src/kernel/types.h
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
__attribute__((noreturn))
|
||||
void panic(struct str s)
|
||||
{
|
||||
terminal_clear();
|
||||
//terminal_clear();
|
||||
terminal_set_color(VGA_COLOR_WHITE, VGA_COLOR_RED);
|
||||
terminal_write(s);
|
||||
__asm__ volatile("hlt");
|
||||
__asm__ volatile("cli; hlt");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
|
||||
@@ -31,12 +31,16 @@ static inline int ps_get(struct printf_state* s)
|
||||
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];
|
||||
size_t i = 0;
|
||||
|
||||
if (padding > BUF_SZ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
terminal_putchar('0');
|
||||
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];
|
||||
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});
|
||||
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
for (size_t i=0; i < str.len; i++) {
|
||||
terminal_putchar(str.data[i]);
|
||||
}
|
||||
return str.len;
|
||||
padding = padding ? padding : 32;
|
||||
pad_char = pad_char ? pad_char : '0';
|
||||
unsigned long n = va_arg(s->ap, uint32_t);
|
||||
struct str alphabet = str_attach("01");
|
||||
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"
|
||||
@@ -97,6 +124,8 @@ static int parse_format_cmd(struct printf_state* s)
|
||||
{
|
||||
int c;
|
||||
uint32_t cmd = 0;
|
||||
int pad = 0;
|
||||
char pad_char = '\0';
|
||||
|
||||
constexpr uint32_t CHAR_MASK = (1<<CHAR_BIT)-1;
|
||||
|
||||
@@ -109,21 +138,21 @@ static int parse_format_cmd(struct printf_state* s)
|
||||
return -1;
|
||||
|
||||
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 'i32':
|
||||
return print_i32(s);
|
||||
return print_i32(s, pad, pad_char);
|
||||
|
||||
case 'u16':
|
||||
case 'u32':
|
||||
return print_u32(s);
|
||||
return print_u32(s, pad, pad_char);
|
||||
|
||||
case 'x16':
|
||||
case 'x32':
|
||||
return print_x32(s);
|
||||
return print_x32(s, pad, pad_char);
|
||||
|
||||
case 'str':
|
||||
return print_str(s);
|
||||
case 'str':
|
||||
return print_str(s, pad, pad_char);
|
||||
|
||||
default:
|
||||
return -1;
|
||||
|
||||
Reference in New Issue
Block a user