Move to ring 3
This commit is contained in:
6
Makefile
6
Makefile
@@ -54,6 +54,11 @@ $(BUILD_DIR)/myos.bin: $(OBJECTS)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
$(BUILD_DIR)/kernel/interrupts.o: $(SOURCE_DIR)/kernel/interrupts.c Makefile
|
||||
@mkdir -p $(@D)
|
||||
$(CC) -c $(CFLAGS) -mgeneral-regs-only -mno-red-zone $< -o $@
|
||||
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.c Makefile
|
||||
@mkdir -p $(@D)
|
||||
$(CC) -c $(CFLAGS) $< -o $@
|
||||
@@ -61,3 +66,4 @@ $(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.c Makefile
|
||||
$(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.S Makefile
|
||||
@mkdir -p $(@D)
|
||||
$(AS) $(ASFLAGS) $< -o $@
|
||||
|
||||
|
||||
3
interrupts.h
Normal file
3
interrupts.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
__attribute__((interrupt)) void interrupt_handler_1(struct interrupt_frame* frame);
|
||||
@@ -16,3 +16,6 @@ static inline struct str str_slice(struct str s, size_t begin, size_t end)
|
||||
.len = s.len - begin - end,
|
||||
};
|
||||
}
|
||||
|
||||
#define CSTR_(x) #x
|
||||
#define CSTR(x) CSTR_(x)
|
||||
|
||||
54
src/kernel/gdt.c
Normal file
54
src/kernel/gdt.c
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "gdt.h"
|
||||
#include "libc.h"
|
||||
#include "str.h"
|
||||
|
||||
inline uint16_t gdt_segment_selector(uint16_t index, uint16_t table_indicator, uint16_t priv_level)
|
||||
{
|
||||
return (index << 3)
|
||||
| ((table_indicator << 2) & 0b100)
|
||||
| ((priv_level << 0) & 0b011);
|
||||
}
|
||||
|
||||
struct gdt_table_entry gdt_encode_entry(struct gdt_entry_content content)
|
||||
{
|
||||
if (content.limit > GDT_LIMIT_MAX)
|
||||
panic(str_attach("GDT cannot encode limits larger than " CSTR(GDT_LIMIT_MAX)));
|
||||
|
||||
struct gdt_table_entry entry;
|
||||
|
||||
entry.data[GDT_SDESC_LIMIT_0] = (content.limit >> 0) & 0xFF;
|
||||
entry.data[GDT_SDESC_LIMIT_1] = (content.limit >> 8) & 0xFF;
|
||||
entry.data[GDT_SDESC_LIMIT_2] = (content.limit >> 16) & 0xFF;
|
||||
|
||||
entry.data[GDT_SDESC_BASE_0] = (content.base >> 0) & 0xFF;
|
||||
entry.data[GDT_SDESC_BASE_1] = (content.base >> 8) & 0xFF;
|
||||
entry.data[GDT_SDESC_BASE_2] = (content.base >> 16) & 0xFF;
|
||||
entry.data[GDT_SDESC_BASE_3] = (content.base >> 24) & 0xFF;
|
||||
|
||||
entry.data[GDT_SDESC_ACCESSBYTE] = content.access_byte;
|
||||
|
||||
entry.data[GDT_SDESC_FLAGS] |= (content.flags << 4);
|
||||
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
110
src/kernel/gdt.h
Normal file
110
src/kernel/gdt.h
Normal file
@@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#define GDT_LIMIT_MAX 0xFFFFF
|
||||
|
||||
struct gdt_table_entry {
|
||||
volatile uint8_t data[8];
|
||||
};
|
||||
_Static_assert(sizeof(struct gdt_table_entry) == 8);
|
||||
|
||||
struct gdt_entry_content {
|
||||
volatile uint32_t base;
|
||||
volatile uint32_t limit;
|
||||
volatile uint8_t access_byte;
|
||||
volatile uint8_t flags;
|
||||
};
|
||||
|
||||
enum gdt_segment_descriptor_index {
|
||||
GDT_SDESC_BASE_0 = 2,
|
||||
GDT_SDESC_BASE_1 = 3,
|
||||
GDT_SDESC_BASE_2 = 4,
|
||||
GDT_SDESC_BASE_3 = 7,
|
||||
|
||||
GDT_SDESC_LIMIT_0 = 0,
|
||||
GDT_SDESC_LIMIT_1 = 1,
|
||||
GDT_SDESC_LIMIT_2 = 6,
|
||||
|
||||
GDT_SDESC_ACCESSBYTE = 5,
|
||||
|
||||
GDT_SDESC_FLAGS = 6,
|
||||
};
|
||||
|
||||
enum gdt_access_flag : uint8_t {
|
||||
GDT_ACCESS_ACCESSED = (1<<0),
|
||||
GDT_ACCESS_RW = (1<<1),
|
||||
GDT_ACCESS_DC = (1<<2),
|
||||
GDT_ACCESS_EXEC = (1<<3),
|
||||
GDT_ACCESS_DESCRIPTOR = (1<<4),
|
||||
|
||||
GDT_ACCESS_DPL_0 = (0<<5),
|
||||
GDT_ACCESS_DPL_1 = (1<<5),
|
||||
GDT_ACCESS_DPL_2 = (2<<5),
|
||||
GDT_ACCESS_DPL_3 = (3<<5),
|
||||
|
||||
GDT_ACCESS_PRESENT = (1<<7),
|
||||
};
|
||||
|
||||
enum gdt_flags : uint8_t {
|
||||
GDT_RESERVED = (1<<0),
|
||||
GDT_LONG = (1<<1),
|
||||
GDT_SIZE = (1<<2),
|
||||
GDT_GRANULARITY = (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)
|
||||
{
|
||||
__asm__ volatile (
|
||||
// Far jump to reload the CS register
|
||||
"jmp %[cs], $reload_CS\n"
|
||||
"reload_CS:\n"
|
||||
"movw %[ds], %%ax\n"
|
||||
"movw %%ax, %%ds\n"
|
||||
"movw %[es], %%ax\n"
|
||||
"movw %%ax, %%es\n"
|
||||
"movw %[fs], %%ax\n"
|
||||
"movw %%ax, %%fs\n"
|
||||
"movw %[gs], %%ax\n"
|
||||
"movw %%ax, %%gs\n"
|
||||
"movw %[ss], %%ax\n"
|
||||
"movw %%ax, %%ss\n"
|
||||
: // No output operands
|
||||
: [cs] "i"(code),
|
||||
[ds] "i"(data),
|
||||
[es] "i"(extra_segment),
|
||||
[fs] "i"(general_segment_1),
|
||||
[gs] "i"(general_segment_2),
|
||||
[ss] "i"(stack_segment)
|
||||
: "ax" // Clobbered register
|
||||
);
|
||||
}
|
||||
|
||||
static inline void gdt_reload(uint32_t data, uint32_t code)
|
||||
{
|
||||
__asm__ volatile (
|
||||
// Far jump to reload the CS register
|
||||
"jmp %[cs], $reload_CS\n"
|
||||
"reload_CS:\n"
|
||||
// Load data segment into AX and then move it to all segment registers
|
||||
"movw %[ds], %%ax\n"
|
||||
"movw %%ax, %%ds\n"
|
||||
"movw %%ax, %%es\n"
|
||||
"movw %%ax, %%fs\n"
|
||||
"movw %%ax, %%gs\n"
|
||||
"movw %%ax, %%ss\n"
|
||||
: // No output operands
|
||||
: [ds] "i"(data),
|
||||
[cs] "i"(code)
|
||||
: "ax" // Clobbered register
|
||||
);
|
||||
}
|
||||
43
src/kernel/idt.c
Normal file
43
src/kernel/idt.c
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
#include <stdint.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 output = {0};
|
||||
|
||||
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;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
* size: one less than the idt in bytes
|
||||
* offset: linear address of the idt
|
||||
* */
|
||||
void idt_load(struct idt_gate_descriptor table[], uint16_t size)
|
||||
{
|
||||
struct __attribute__((packed)) {
|
||||
uint16_t size;
|
||||
uint32_t offset;
|
||||
} idt = {
|
||||
.size = size,
|
||||
.offset = (uint32_t)table,
|
||||
};
|
||||
_Static_assert(sizeof idt.size == 2);
|
||||
_Static_assert(sizeof idt.offset == 4);
|
||||
_Static_assert(sizeof idt == 6);
|
||||
|
||||
__asm__ volatile (
|
||||
"lidt %[idt]"
|
||||
:
|
||||
: [idt] "m"(idt)
|
||||
);
|
||||
}
|
||||
41
src/kernel/idt.h
Normal file
41
src/kernel/idt.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct __attribute__((packed)) idt_gate_descriptor {
|
||||
uint16_t offset_0;
|
||||
uint16_t segment_selector;
|
||||
uint8_t ist;
|
||||
|
||||
/* `flags`
|
||||
=======
|
||||
bit 0-3: gate type
|
||||
bit 4: unused
|
||||
bit 5-6: dpl
|
||||
bit 7: present bit
|
||||
|
||||
DPL is ignored by hardware interupts and present bit must be 1 */
|
||||
uint8_t flags;
|
||||
|
||||
uint16_t offset_1;
|
||||
};
|
||||
|
||||
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_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);
|
||||
|
||||
void idt_load(struct idt_gate_descriptor table[], uint16_t size);
|
||||
|
||||
21
src/kernel/interrupts.c
Normal file
21
src/kernel/interrupts.c
Normal file
@@ -0,0 +1,21 @@
|
||||
#include <stdint.h>
|
||||
#include "libc.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
typedef unsigned long long int uword_t;
|
||||
#else
|
||||
typedef unsigned int uword_t;
|
||||
#endif
|
||||
|
||||
struct __attribute__((packed)) interrupt_frame {
|
||||
uword_t ip;
|
||||
uword_t cs;
|
||||
uword_t flags;
|
||||
uword_t sp;
|
||||
uword_t ss;
|
||||
};
|
||||
|
||||
__attribute__((interrupt)) void interrupt_handler_1(struct interrupt_frame* frame)
|
||||
{
|
||||
(void)frame;
|
||||
}
|
||||
@@ -2,166 +2,88 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "gdt.h"
|
||||
#include "tss.h"
|
||||
#include "idt.h"
|
||||
|
||||
// Future user-space
|
||||
#include "libc.h"
|
||||
#include "tty.h"
|
||||
|
||||
#define STR_(x) #x
|
||||
#define STR(x) STR_(x)
|
||||
// TOOD: clean up this
|
||||
typedef void(*func_t)(void*);
|
||||
|
||||
// TODO: remove
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
||||
/* 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"));
|
||||
}
|
||||
|
||||
static void ring3_mode(uint32_t udata_offset, uint32_t udata_rpl, uint32_t ucode_offset, uint32_t ucode_rpl, 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 %%esp, %%eax\n"
|
||||
"push %%eax\n" // current 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)
|
||||
: "eax"
|
||||
);
|
||||
}
|
||||
/*-----------------------------------------------*/
|
||||
|
||||
/**
|
||||
* GDT Setup
|
||||
* =========
|
||||
* Kernel entrypoint
|
||||
* =================
|
||||
* The kernel entrypoints sets up the GDT, TSS and IDT and moves to ring 3
|
||||
*/
|
||||
#define GDT_LIMIT_MAX 0xFFFFF
|
||||
|
||||
struct gdt_table_entry {
|
||||
uint8_t data[8];
|
||||
};
|
||||
|
||||
enum gdt_segment_descriptor_index : size_t {
|
||||
GDT_SDESC_BASE_0 = 2,
|
||||
GDT_SDESC_BASE_1 = 3,
|
||||
GDT_SDESC_BASE_2 = 4,
|
||||
GDT_SDESC_BASE_3 = 7,
|
||||
|
||||
GDT_SDESC_LIMIT_0 = 0,
|
||||
GDT_SDESC_LIMIT_1 = 1,
|
||||
GDT_SDESC_LIMIT_2 = 6,
|
||||
|
||||
GDT_SDESC_ACCESSBYTE = 5,
|
||||
|
||||
GDT_SDESC_FLAGS = 6,
|
||||
};
|
||||
|
||||
enum gdt_access_flag : uint8_t {
|
||||
GDT_ACCESS_ACCESSED = (1<<0),
|
||||
GDT_ACCESS_RW = (1<<1),
|
||||
GDT_ACCESS_DC = (1<<2),
|
||||
GDT_ACCESS_EXEC = (1<<3),
|
||||
GDT_ACCESS_DESCRIPTOR = (1<<4),
|
||||
|
||||
GDT_ACCESS_DPL_0 = (0<<5),
|
||||
GDT_ACCESS_DPL_1 = (1<<5),
|
||||
GDT_ACCESS_DPL_2 = (2<<5),
|
||||
GDT_ACCESS_DPL_3 = (3<<5),
|
||||
|
||||
GDT_ACCESS_PRESENT = (1<<7),
|
||||
};
|
||||
|
||||
enum gdt_flags : uint8_t {
|
||||
GDT_RESERVED = (1<<0),
|
||||
GDT_LONG = (1<<1),
|
||||
GDT_SIZE = (1<<2),
|
||||
GDT_GRANULARITY = (1<<3),
|
||||
};
|
||||
|
||||
static inline uint16_t gdt_segment_selector(uint16_t index, uint16_t table_indicator, uint16_t priv_level)
|
||||
void kernel_main(void)
|
||||
{
|
||||
return (index << 3)
|
||||
| ((table_indicator << 2) & 0b100)
|
||||
| ((priv_level << 0) & 0b011);
|
||||
}
|
||||
|
||||
struct gdt_entry_content {
|
||||
uint32_t base;
|
||||
uint32_t limit;
|
||||
uint8_t access_byte;
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
static void gdt_encode_entry(struct gdt_table_entry* target, struct gdt_entry_content content)
|
||||
{
|
||||
if (content.limit > GDT_LIMIT_MAX)
|
||||
panic(str_attach("GDT cannot encode limits larger than " STR(GDT_LIMIT_MAX)));
|
||||
|
||||
// encode the limit
|
||||
target->data[GDT_SDESC_LIMIT_0] = (content.limit >> 0) & 0xFF;
|
||||
target->data[GDT_SDESC_LIMIT_1] = (content.limit >> 8) & 0xFF;
|
||||
target->data[GDT_SDESC_LIMIT_2] = (content.limit >> 16) & 0xFF;
|
||||
|
||||
// encode the base
|
||||
target->data[GDT_SDESC_BASE_0] = (content.base >> 0) & 0xFF;
|
||||
target->data[GDT_SDESC_BASE_1] = (content.base >> 8) & 0xFF;
|
||||
target->data[GDT_SDESC_BASE_2] = (content.base >> 16) & 0xFF;
|
||||
target->data[GDT_SDESC_BASE_3] = (content.base >> 24) & 0xFF;
|
||||
|
||||
// encode the access byte
|
||||
target->data[GDT_SDESC_ACCESSBYTE] = content.access_byte;
|
||||
|
||||
// encode the flags
|
||||
target->data[GDT_SDESC_FLAGS] |= (content.flags << 4);
|
||||
}
|
||||
|
||||
static void gdt_set(uint16_t limit, uint32_t base, uint32_t offset)
|
||||
{
|
||||
base += offset;
|
||||
|
||||
/* the lgdt instruction requires a packed alignment */
|
||||
struct __attribute__((packed)) gdtr {
|
||||
uint16_t limit;
|
||||
uint32_t base;
|
||||
};
|
||||
|
||||
static struct gdtr gdtr;
|
||||
gdtr.limit = limit;
|
||||
gdtr.base = base;
|
||||
|
||||
__asm__ volatile (
|
||||
"lgdt %[gdtr]"
|
||||
:
|
||||
: [gdtr] "m"(gdtr)
|
||||
);
|
||||
}
|
||||
|
||||
// only compiles with O1 or higher ¯\_(ツ)_/¯, might have to make this a macro
|
||||
static inline void gdt_reload(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
|
||||
"jmp %[cs], $reload_CS\n"
|
||||
"reload_CS:\n"
|
||||
// Load data segment into AX and then move it to all segment registers
|
||||
//"movw %[ds], %%ax\n"
|
||||
"movw %[ds], %%ds\n"
|
||||
"movw %[es], %%es\n"
|
||||
"movw %[fs], %%fs\n"
|
||||
"movw %[gs], %%gs\n"
|
||||
"movw %[ss], %%ss\n"
|
||||
: // No output operands
|
||||
: [cs] "i"(code),
|
||||
[ds] "i"(data),
|
||||
[es] "i"(extra_segment),
|
||||
[fs] "i"(general_segment_1),
|
||||
[gs] "i"(general_segment_2),
|
||||
[ss] "i"(stack_segment)
|
||||
: "ax" // Clobbered register
|
||||
);
|
||||
}
|
||||
|
||||
// sets a flat model
|
||||
void test_gdt()
|
||||
{
|
||||
// disable interupts
|
||||
__asm__ volatile("cli");
|
||||
|
||||
static struct gdt_table_entry gdt_table[6];
|
||||
size_t i=0;
|
||||
_Static_assert(sizeof(gdt_table) == 0x30);
|
||||
static struct tss tss = {0};
|
||||
|
||||
// Null Descriptor
|
||||
gdt_encode_entry(&gdt_table[i++], (struct gdt_entry_content){0});
|
||||
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,
|
||||
};
|
||||
|
||||
// Kernel mode code segment
|
||||
gdt_encode_entry(&gdt_table[i++], (struct gdt_entry_content){
|
||||
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[kernel_code_segment] = gdt_encode_entry((struct gdt_entry_content){
|
||||
.base = 0,
|
||||
.limit = GDT_LIMIT_MAX,
|
||||
.access_byte = GDT_ACCESS_RW
|
||||
@@ -172,8 +94,7 @@ void test_gdt()
|
||||
.flags = GDT_SIZE
|
||||
| GDT_GRANULARITY});
|
||||
|
||||
// Kernel mode data segment
|
||||
gdt_encode_entry(&gdt_table[i++], (struct gdt_entry_content){
|
||||
gdt[kernel_data_segment] = gdt_encode_entry((struct gdt_entry_content){
|
||||
.base = 0,
|
||||
.limit = GDT_LIMIT_MAX,
|
||||
.access_byte = GDT_ACCESS_RW
|
||||
@@ -183,8 +104,8 @@ void test_gdt()
|
||||
.flags = GDT_SIZE
|
||||
| GDT_GRANULARITY});
|
||||
|
||||
// User mode code segment
|
||||
gdt_encode_entry(&gdt_table[i++], (struct gdt_entry_content){
|
||||
/* user */
|
||||
gdt[user_code_segment] = gdt_encode_entry((struct gdt_entry_content) {
|
||||
.base = 0,
|
||||
.limit = GDT_LIMIT_MAX,
|
||||
.access_byte = GDT_ACCESS_RW
|
||||
@@ -195,8 +116,7 @@ void test_gdt()
|
||||
.flags = GDT_SIZE
|
||||
| GDT_GRANULARITY});
|
||||
|
||||
// User mode data segment
|
||||
gdt_encode_entry(&gdt_table[i++], (struct gdt_entry_content){
|
||||
gdt[user_data_segment] = gdt_encode_entry((struct gdt_entry_content) {
|
||||
.base = 0,
|
||||
.limit = GDT_LIMIT_MAX,
|
||||
.access_byte = GDT_ACCESS_RW
|
||||
@@ -206,9 +126,9 @@ void test_gdt()
|
||||
.flags = GDT_SIZE
|
||||
| GDT_GRANULARITY});
|
||||
|
||||
// Task state segment
|
||||
gdt_encode_entry(&gdt_table[i++], (struct gdt_entry_content){
|
||||
.base = &tss,
|
||||
/* tss */
|
||||
gdt[task_state_segment] = gdt_encode_entry((struct gdt_entry_content) {
|
||||
.base = (uint32_t)&tss,
|
||||
.limit = sizeof(tss)-1,
|
||||
.access_byte = GDT_ACCESS_ACCESSED
|
||||
| GDT_ACCESS_EXEC
|
||||
@@ -216,25 +136,31 @@ void test_gdt()
|
||||
| GDT_ACCESS_PRESENT,
|
||||
.flags = 0});
|
||||
|
||||
gdt_set(0, 0, 0);
|
||||
const uint16_t code_segment = 0x8; // placeholder
|
||||
const uint16_t data_segment = 0x10; // placeholder
|
||||
gdt_reload(code_segment, data_segment);
|
||||
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 */
|
||||
|
||||
tss_load(task_state_segment * sizeof (struct gdt_table_entry));
|
||||
|
||||
/* Setup the IDT */
|
||||
//static struct idt_gate_descriptor idt[256] = {0};
|
||||
|
||||
//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);
|
||||
|
||||
//idt_load(idt, sizeof idt);
|
||||
|
||||
/* 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);
|
||||
|
||||
|
||||
__asm__ volatile("sti");
|
||||
}
|
||||
|
||||
/**
|
||||
* Kernel entrypoint
|
||||
* =================
|
||||
*/
|
||||
void kernel_main(void)
|
||||
{
|
||||
terminal_clear();
|
||||
|
||||
for (size_t i = 0; i < VGA_HEIGHT / 2 + 1; i++) {
|
||||
printf(str_attach("hello {u32}!\n"), i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: remove
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
6
src/kernel/tss.c
Normal file
6
src/kernel/tss.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "tss.h"
|
||||
|
||||
void tss_init(struct tss* t)
|
||||
{
|
||||
(void)t;
|
||||
}
|
||||
68
src/kernel/tss.h
Normal file
68
src/kernel/tss.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
struct __attribute__((packed)) tss {
|
||||
/* Contains the Segment Selector for the TSS of the previous task, with
|
||||
hardware task switching these form a kind of backward linked list. */
|
||||
uint16_t link;
|
||||
uint16_t link_reserved;
|
||||
|
||||
/* the stack pointer to load when changing to kernel mode */
|
||||
uint32_t esp0;
|
||||
|
||||
/* the stack segment pointer to load when changing to kernel mode */
|
||||
uint16_t ss0;
|
||||
|
||||
uint16_t ss0_reserved;
|
||||
|
||||
/* everything under here is unused in software task switching */
|
||||
struct __attribute__((packed)) {
|
||||
/* esp and ss 1 and 2 would be used when switching to ring 1 and 2 */
|
||||
uint32_t esp1;
|
||||
uint16_t ss1;
|
||||
uint16_t ss1_reserved;
|
||||
uint32_t esp2;
|
||||
uint16_t ss2;
|
||||
uint16_t ss2_reserved;
|
||||
uint32_t cr3;
|
||||
uint32_t eip;
|
||||
uint32_t eflags;
|
||||
uint32_t eax;
|
||||
uint32_t ecx;
|
||||
uint32_t edx;
|
||||
uint32_t ebx;
|
||||
uint32_t esp;
|
||||
uint32_t ebp;
|
||||
uint32_t esi;
|
||||
uint32_t edi;
|
||||
uint16_t es;
|
||||
uint16_t es_reserved;
|
||||
uint16_t cs;
|
||||
uint16_t cs_reserved;
|
||||
uint16_t ss;
|
||||
uint16_t ss_reserved;
|
||||
uint16_t ds;
|
||||
uint16_t ds_reserved;
|
||||
uint16_t fs;
|
||||
uint16_t fs_reserved;
|
||||
uint16_t gs;
|
||||
uint16_t gs_reserved;
|
||||
uint16_t ldtr;
|
||||
uint16_t ldtr_reserved;
|
||||
uint16_t reserved;
|
||||
uint16_t iopb;
|
||||
uint32_t ssp;
|
||||
} unused;
|
||||
};
|
||||
|
||||
/* offset: the offset in bytes of the tss descriptor in the gdt table */
|
||||
static inline void tss_load(uint32_t offset)
|
||||
{
|
||||
__asm__ volatile (
|
||||
"mov %[offset], %%ax\n\t"
|
||||
"ltr %%ax"
|
||||
:
|
||||
: [offset] "m"(offset)
|
||||
: "ax");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user