196 lines
5.2 KiB
C
196 lines
5.2 KiB
C
//
|
|
// Created by rick on 19-03-21.
|
|
//
|
|
#include <attributes.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <myke/libk/libk.h>
|
|
#include <myke/cpu/gdt.h>
|
|
|
|
typedef struct gdt {
|
|
uint32_t base;
|
|
uint32_t limit;
|
|
union {
|
|
uint8_t access_byte;
|
|
struct {
|
|
bool accessed: 1;
|
|
bool read_write: 1; // for executable, if reading is allowed. For Non-exec if writing is allowed
|
|
// data sector: direction
|
|
// grows up(false) or down(true).
|
|
// executable sector: conforming
|
|
// if true higher privilege levels can execute this code (e.g. ring 3 can exec ring 0 code) the privilege level it self is not changed.
|
|
// if false, only the specified ring can execute the code
|
|
bool direction_conforming: 1;
|
|
bool executable: 1;
|
|
bool is_sector: 1;
|
|
uint8_t privilege: 2;
|
|
bool present: 1;
|
|
} att_packed;
|
|
};
|
|
union {
|
|
uint8_t flags_byte: 4;
|
|
struct {
|
|
uint8_t unused: 2;
|
|
bool size: 1; // false 16bit, true 32bit
|
|
bool granularity: 1; // false 1 byte, true 4KiB
|
|
} att_packed;
|
|
};
|
|
} gdt_t;
|
|
|
|
struct tss_ring {
|
|
uint32_t esp;
|
|
uint16_t ss;
|
|
uint16_t: 16; // skipped
|
|
} att_packed;
|
|
|
|
typedef struct tss {
|
|
uint16_t link;
|
|
uint16_t: 16; // skipped
|
|
struct tss_ring r0;
|
|
struct tss_ring r1;
|
|
struct tss_ring r2;
|
|
struct {
|
|
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: 16;
|
|
uint16_t cs;
|
|
uint16_t: 16;
|
|
uint16_t ss;
|
|
uint16_t: 16;
|
|
uint16_t ds;
|
|
uint16_t: 16;
|
|
uint16_t fs;
|
|
uint16_t: 16;
|
|
uint16_t gs;
|
|
uint16_t: 16;
|
|
uint16_t ldtr;
|
|
uint16_t: 16;
|
|
} att_packed regs;
|
|
uint16_t: 16;
|
|
uint16_t iopb_offset;
|
|
} att_packed tss_t;
|
|
_Static_assert(sizeof(tss_t) == 104, "TSS incorrect size");
|
|
|
|
typedef struct gdtr {
|
|
uint16_t size;
|
|
uint32_t offset;
|
|
} att_packed gdtr_t;
|
|
|
|
#define num_gdts 5
|
|
|
|
extern void _gdt_switch();
|
|
|
|
tss_t tss = {
|
|
.iopb_offset = sizeof(tss_t),
|
|
.r0.ss = 0x10,
|
|
};
|
|
|
|
gdt_t gdts[num_gdts] = {
|
|
{ // 0x08 kernel code
|
|
.present = true,
|
|
.base = 0,
|
|
.limit = 0xFFFFFFFF,
|
|
.is_sector = true,
|
|
.size = true,
|
|
.granularity = true,
|
|
.executable = true,
|
|
.read_write = true,
|
|
.privilege = 0,
|
|
},
|
|
{ // 0x10 kernel data
|
|
.present = true,
|
|
.base = 0,
|
|
.limit = 0xFFFFFFFF,
|
|
.is_sector = true,
|
|
.size = true,
|
|
.granularity = true,
|
|
.executable = false,
|
|
.read_write = true,
|
|
},
|
|
{ // 0x18 user code
|
|
.present = true,
|
|
.base = 0,
|
|
.limit = 0xFFFFFFFF,
|
|
.privilege = 3,
|
|
.is_sector = true,
|
|
.size = true,
|
|
.granularity = true,
|
|
.executable = true,
|
|
.read_write = true,
|
|
},
|
|
{ // 0x20 user data
|
|
.present = true,
|
|
.base = 0,
|
|
.limit = 0xFFFFFFFF,
|
|
.privilege = 3,
|
|
.is_sector = true,
|
|
.size = true,
|
|
.granularity = true,
|
|
.executable = true,
|
|
.read_write = true,
|
|
},
|
|
{ // 0x28 tss
|
|
.base = (uint32_t) &tss,
|
|
.limit = sizeof(tss_t),
|
|
.present = true,
|
|
.executable = true,
|
|
.accessed = true,
|
|
.size = true,
|
|
}
|
|
};
|
|
uint64_t gdt_values[num_gdts + 1] = {0};
|
|
gdtr_t gdt_ptr = {
|
|
.size = sizeof(uint64_t) * (num_gdts + 1) - 1,
|
|
.offset = (uint32_t) &gdt_values,
|
|
};
|
|
_Static_assert(sizeof(gdt_ptr) == 6, "GDT PTR incorrect size");
|
|
|
|
uint64_t gdt_encode(gdt_t *gdt) {
|
|
// high limit must be full 4k
|
|
uint64_t limit = gdt->limit;
|
|
if (limit > 65536 && (limit & 0xFFF) != 0xFFF && !gdt->granularity) {
|
|
return 0;
|
|
} else {
|
|
limit >>= 12;
|
|
}
|
|
uint64_t value = 0
|
|
| (limit & 0xFFFF)
|
|
| ((gdt->base & 0xFFFF) << 16)
|
|
| ((uint64_t) ((gdt->base >> 16) & 0xFF) << 32)
|
|
| ((uint64_t) (gdt->access_byte) << 40)
|
|
| ((limit >> 16) << 48)
|
|
| ((uint64_t) (gdt->flags_byte) << 52)
|
|
| ((uint64_t) (gdt->base >> 24) << 56);
|
|
return value;
|
|
}
|
|
|
|
void gdt_activate() {
|
|
gdt_values[0] = 0; // first is zero
|
|
for (int i = 0; i < num_gdts; ++i) {
|
|
gdt_values[i + 1] = gdt_encode(&gdts[i]);
|
|
if (gdt_values[i + 1] == 0) {
|
|
// todo failed to encode
|
|
k_panics("Failed to encode GDT\n");
|
|
}
|
|
}
|
|
_gdt_switch();
|
|
}
|
|
|
|
void gdt_init() {
|
|
// Replace the early gdt with a new one
|
|
// this one will be extensible and support stuff like TSS
|
|
|
|
tss.r0.esp = 0; // todo
|
|
|
|
gdt_activate();
|
|
} |