| git.druid.rocks | index | druid520 | kaboom | src/ | kernel/ | gdt.nsc |
src/kernel/gdt.nsc
/*
* kernel-owned gdt: null, kernel code, kernel data. boot.s's own gdt64
* (in its .rodata, built entirely by hand in raw asm) only exists to
* get the cpu into long mode at all -- this replaces it with one the
* kernel actually manages, so a tss entry (needed once idt.nsc wants
* a dedicated interrupt stack) or user-mode segments (needed once elf
* programs run in ring 3) have somewhere real to go later.
*
* gdt entries can't be a `struct` (nsc struct fields are word
* addressed, not byte-packed -- see the note in vga.nsc/kmain.nsc for
* the general version of this constraint) so each entry is packed
* into a u64 by hand, same technique proven while learning nsc's
* pointer model, then stored with one 8-byte *ptr write.
*/
void load_gdt(ptr gdtr_addr);
global i8 gdt_table[24];
global i8 gdtr[10];
void
gdt_set_entry(i32 idx, u32 base, u32 limit, u8 access, u8 gran)
{
ptr slot;
u64 lo;
u64 hi;
u64 packed;
lo = (u64)(limit & (u32)0xffff);
lo = lo | ((u64)(base & (u32)0xffffff) << (u64)16);
lo = lo | ((u64)access << (u64)40);
hi = (u64)((limit >> (u32)16) & (u32)0x0f);
hi = hi | (u64)gran;
hi = hi | ((u64)((base >> (u32)24) & (u32)0xff) << (u64)8);
packed = lo | (hi << (u64)48);
slot = &gdt_table[0] + idx * 8;
*slot = (i64)packed;
}
global void
gdt_init(void)
{
ptr tbl;
u64 addr;
i32 limit;
i32 i;
gdt_set_entry(0, 0, 0, (u8)0, (u8)0); /* null descriptor, required */
gdt_set_entry(1, 0, 0, (u8)0x9a, (u8)0xa0); /* 0x08: kernel code, 64-bit (L bit set in gran) */
gdt_set_entry(2, 0, 0, (u8)0x92, (u8)0xa0); /* 0x10: kernel data */
limit = 23; /* 3 entries * 8 bytes - 1 */
gdtr[0] = (i8)(limit & 0xff);
gdtr[1] = (i8)((limit >> 8) & 0xff);
tbl = &gdt_table[0];
addr = (u64)tbl;
i = 0;
while(i < 8)
{
gdtr[2 + i] = (i8)((addr >> (u64)(i * 8)) & (u64)0xff);
i = i + 1;
}
load_gdt(&gdtr[0]);
}