diff --git a/src/arch/amd64/gdt.S b/src/arch/amd64/gdt.S index c9105bc..b47a9f0 100644 --- a/src/arch/amd64/gdt.S +++ b/src/arch/amd64/gdt.S @@ -9,20 +9,36 @@ .section .text /* arch_setgdt */ .global __arch_setgdt +#ifndef __clang__ .type __arch_setgdt, @function -__arch_setgdt: -#if 0 - lea gdt_descriptor, %rcx -#else - mov (gdt_descriptor), %rcx #endif +__arch_setgdt: + lea gdt_descriptor, %rcx + /* set the offset of the GDT */ mov %rsi, 0x2(%rcx) + /* set the size of the GDT */ + shl $0x3, %rdi dec %rdi mov %di, (%rcx) + /* load the GDT */ lgdt (%rcx) + + push $0x8 + lea gdt_flush, %rax + push %rax + retf + +gdt_flush: + mov $0x10, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + ret @@ -31,4 +47,4 @@ __arch_setgdt: .align 16 gdt_descriptor: .skip 2 /* size */ -.skip 4 /* offset */ +.skip 8 /* offset */ diff --git a/src/arch/i386/gdt.S b/src/arch/i386/gdt.S index 6567cde..18785f4 100644 --- a/src/arch/i386/gdt.S +++ b/src/arch/i386/gdt.S @@ -14,15 +14,29 @@ #endif __arch_setgdt: lea gdt_descriptor, %ecx + /* set the offset of the GDT */ mov 0x4(%esp), %eax mov %eax, 0x2(%ecx) + /* set the size of the GDT */ mov 0x8(%esp), %eax + shl $0x3, %eax dec %eax mov %ax, (%ecx) /* load the GDT */ - lgdt (%ecx) + lgdt (gdt_descriptor) + + /* apply the GDT */ + mov $0x10, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + ljmp $0x08, $gdt_flush +gdt_flush: + ret diff --git a/src/arch/i386/gdt.h b/src/arch/i386/gdt.h index 154e480..68b527e 100644 --- a/src/arch/i386/gdt.h +++ b/src/arch/i386/gdt.h @@ -1,5 +1,5 @@ /* $Id$ */ -/* Copyright (c) 2018 Pierre Pronchery */ +/* Copyright (c) 2018-2025 Pierre Pronchery */ /* This file is part of DeforaOS uKernel */ @@ -7,21 +7,31 @@ #ifndef UKERNEL_ARCH_I386_GDT_H # define UKERNEL_ARCH_I386_GDT_H +# include # include # include /* public */ /* types */ -typedef struct _GDT +typedef struct _GDT GDT; + +typedef struct _GDTTable { vaddr_t base; - vaddr_t limit; - uint8_t type; -} GDT; + size_t size; + unsigned int prot; +} GDTTable; /* prototypes */ -int _arch_setgdt(GDT const * gdt, size_t count); +GDT * gdt_init(void); +int gdt_init_table(GDTTable const * table, size_t count); + + +/* useful */ +int gdt_append(GDT * gdt, vaddr_t base, size_t size, unsigned int prot); + +void gdt_apply(GDT * gdt); #endif /* !UKERNEL_ARCH_I386_GDT_H */ diff --git a/src/drivers/boot/multiboot.c b/src/drivers/boot/multiboot.c index 488c8f2..884d092 100644 --- a/src/drivers/boot/multiboot.c +++ b/src/drivers/boot/multiboot.c @@ -11,8 +11,6 @@ # include # include # include -# include "arch/amd64/gdt.h" -# include "arch/i386/gdt.h" # include "drivers/boot/multiboot.h" # ifndef MAX diff --git a/src/kernel/multiboot.c b/src/kernel/multiboot.c index 63b3786..434b47c 100644 --- a/src/kernel/multiboot.c +++ b/src/kernel/multiboot.c @@ -27,12 +27,10 @@ /* private */ /* constants */ /* GDT: 4GB flat memory setup */ -static const GDT _gdt_4gb[4] = +static const GDTTable _gdt_4gb[2] = { - { 0x00000000, 0x00000000, 0x00 }, - { 0x00000000, 0xffffffff, 0x9a }, - { 0x00000000, 0xffffffff, 0x92 }, - { 0x00000000, 0x00000000, 0x89 } + { 0x00000000, 0xffffffff, PROT_READ | PROT_EXEC }, + { 0x00000000, 0xffffffff, PROT_READ | PROT_WRITE } }; static const IDT _idt[] = @@ -93,7 +91,8 @@ int multiboot(const ukMultibootInfo * mi) #if defined(__amd64__) if(_arch_setgdt64(_gdt_4gb, sizeof(_gdt_4gb) / sizeof(*_gdt_4gb)) != 0) #else - if(_arch_setgdt(_gdt_4gb, sizeof(_gdt_4gb) / sizeof(*_gdt_4gb)) != 0) + if(gdt_init_table((const GDTTable *)&_gdt_4gb, + sizeof(_gdt_4gb) / sizeof(*_gdt_4gb)) != 0) #endif { puts("Could not setup the GDT"); diff --git a/src/loader/gdt.c b/src/loader/gdt.c index a914b12..bfa5da1 100644 --- a/src/loader/gdt.c +++ b/src/loader/gdt.c @@ -1,10 +1,11 @@ /* $Id$ */ -/* Copyright (c) 2018 Pierre Pronchery */ +/* Copyright (c) 2018-2025 Pierre Pronchery */ /* This file is part of DeforaOS uKernel */ #if defined(__amd64__) || defined(__i386__) +# include # include # include # include @@ -12,101 +13,154 @@ # include "arch/i386/gdt.h" +/* constants */ +# define GDT_ENTRIES_MAX 8192 +# define GDT_LIMIT_MAX 0x000fffff + +/* access */ +# define GDT_ACCESS_SET 0x01 +# define GDT_ACCESS_RW 0x02 +# define GDT_ACCESS_X 0x08 +# define GDT_ACCESS_SEGMENT 0x10 +# define GDT_ACCESS_RING(level) ((level) << 5) +# define GDT_ACCESS_PRESENT 0x80 + +/* flags */ +# define GDT_FLAG_LONG_MODE 0x2 /* 64-bit code segment */ +# define GDT_FLAG_PROTECTED_MODE 0x4 /* 32-bit protected mode + code segment */ +# define GDT_FLAG_PAGE_GRANULARITY 0x8 /* 4 KB page size */ + + +/* types */ +#pragma pack(1) +typedef struct _GDTEntry +{ + uint8_t limit0; + uint8_t limit1; + uint8_t base0; + uint8_t base1; + uint8_t base2; + uint8_t access; + unsigned int limit2:4; + unsigned int flags:4; + uint8_t base3; +} GDTEntry; +#pragma pack() + +struct _GDT +{ + GDTEntry entries[GDT_ENTRIES_MAX]; + size_t entries_cnt; +}; + + /* prototypes */ -extern void __arch_setgdt(uint8_t * buf, size_t count); +extern void __arch_setgdt(GDTEntry const * entries, size_t count); /* variables */ -static uint8_t _buf[65536]; +static GDT _gdt; /* functions */ -/* arch_setgdt */ -int _arch_setgdt(GDT const * gdt, size_t count) +/* gdt_init */ +GDT * gdt_init(void) { - uint8_t * buf = _buf; - size_t i; - GDT g; + memset(&_gdt.entries[0], 0, sizeof(_gdt.entries[0])); + _gdt.entries_cnt = 1; + return &_gdt; +} - memset(&_buf, 0, sizeof(_buf)); - /* check for errors */ - if(count == 0 || count > sizeof(_buf) / sizeof(*gdt)) - { - errno = ERANGE; - return -1; - } + +/* gdt_init_table */ +int gdt_init_table(GDTTable const * table, size_t count) +{ + int ret = 0; + GDT * gdt; + size_t i; + + gdt = gdt_init(); for(i = 0; i < count; i++) - { - g = gdt[i]; - buf = &_buf[sizeof(g) * i]; - if(g.limit > 65536) - { - /* make sure the limit can be encoded */ - if((g.limit & 0xfff) != 0xfff) - return -1; - g.limit = g.limit >> 12; - buf[6] = 0xc0; - } - else - buf[6] = 0x40; - //encode the limit - buf[0] = g.limit & 0xff; - buf[1] = (g.limit >> 8) & 0xff; - buf[6] |= (g.limit >> 16) & 0xf; - //encode the base - buf[2] = g.base & 0xff; - buf[3] = (g.base >> 8) & 0xff; - buf[4] = (g.base >> 16) & 0xff; - buf[7] = (g.base >> 24) & 0xff; - //encode the type - buf[5] = g.type; - } - __arch_setgdt(_buf, count); + if((ret = gdt_append(gdt, table[i].base, table[i].size, + table[i].prot)) != 0) + return ret; + gdt_apply(gdt); return 0; } -/* arch_setgdt64 */ -int _arch_setgdt64(GDT const * gdt, size_t count) +/* useful */ +/* gdt_append */ +int gdt_append(GDT * gdt, vaddr_t base, size_t size, unsigned int prot) { - uint8_t * buf; - size_t i; - GDT g; + GDTEntry * entry; + uint32_t limit; + uint8_t access = GDT_ACCESS_PRESENT + | GDT_ACCESS_SEGMENT | GDT_ACCESS_RING(0); + uint8_t flags = GDT_FLAG_PROTECTED_MODE; - memset(&_buf, 0, sizeof(_buf)); /* check for errors */ - if(count == 0 || count > sizeof(_buf) / sizeof(*gdt)) + if(size == 0) { errno = ERANGE; return -1; } - for(i = 0; i < count; i++) + if(gdt->entries_cnt >= sizeof(gdt->entries) / sizeof(*gdt->entries) + || size > ULONG_MAX + || ULONG_MAX - size < base) { - g = gdt[i]; - buf = &_buf[sizeof(g) * i]; - if(g.limit > 65536) - { - /* make sure the limit can be encoded */ - if((g.limit & 0xfff) != 0xfff) - return -1; - g.limit = g.limit >> 12; - buf[6] = 0xa0; - } - else - buf[6] = 0x20; - //encode the limit - buf[0] = g.limit & 0xff; - buf[1] = (g.limit >> 8) & 0xff; - buf[6] |= (g.limit >> 16) & 0xf; - //encode the base - buf[2] = g.base & 0xff; - buf[3] = (g.base >> 8) & 0xff; - buf[4] = (g.base >> 16) & 0xff; - buf[7] = (g.base >> 24) & 0xff; - //encode the type - buf[5] = g.type; + errno = ENOMEM; + return -1; } - __arch_setgdt(_buf, count); + if(prot != PROT_READ + && prot != (PROT_READ | PROT_WRITE) + && prot != (PROT_READ | PROT_EXEC)) + { + errno = EPERM; + return -1; + } + entry = &gdt->entries[gdt->entries_cnt]; + /* base */ + entry->base0 = base & 0xff; + entry->base1 = (base & 0xff00) >> 8; + entry->base2 = (base & 0xff0000) >> 16; + entry->base3 = (base & 0xff000000) >> 24; + /* limit */ + if(size - 1 > GDT_LIMIT_MAX) + { + limit = (size & 0xfff) == 0 + ? (size >> 12) + : (((size | 0xfff) + 1) >> 12); + limit--; + flags |= GDT_FLAG_PAGE_GRANULARITY; + } + else + limit = size - 1; + entry->limit0 = limit & 0xff; + entry->limit1 = (limit & 0xff00) >> 8; + entry->limit2 = (limit & 0xf0000) >> 16; + /* access */ + if(prot == (PROT_READ | PROT_EXEC)) + /* code segment */ + access |= GDT_ACCESS_RW | GDT_ACCESS_X; + else if(prot == (PROT_READ | PROT_WRITE)) + /* data segment (read/write) */ + access |= GDT_ACCESS_RW; + else if(prot == PROT_READ) + /* data segment (read-only) */ + access |= GDT_ACCESS_SET; + entry->access = access; + /* flags */ + entry->flags = flags; + gdt->entries_cnt++; return 0; } + + +/* gdt_apply */ +void gdt_apply(GDT * gdt) +{ + __arch_setgdt(gdt->entries, gdt->entries_cnt); +} #endif diff --git a/src/loader/multiboot.c b/src/loader/multiboot.c index f1972c0..44ba76b 100644 --- a/src/loader/multiboot.c +++ b/src/loader/multiboot.c @@ -29,12 +29,10 @@ /* private */ /* constants */ /* GDT: 4GB flat memory setup */ -static const GDT _gdt_4gb[4] = +static const GDTTable _gdt_4gb[2] = { - { 0x00000000, 0x00000000, 0x00 }, - { 0x00000000, 0xffffffff, 0x9a }, - { 0x00000000, 0xffffffff, 0x92 }, - { 0x00000000, 0x00000000, 0x89 } + { 0x00000000, 0xffffffff, PROT_READ | PROT_EXEC }, + { 0x00000000, 0xffffffff, PROT_READ | PROT_WRITE } }; @@ -88,7 +86,8 @@ int multiboot(const ukMultibootInfo * mi) printf("Booted from %#x\n", mi->boot_device_drive); /* setup the GDT */ - if(_arch_setgdt(_gdt_4gb, sizeof(_gdt_4gb) / sizeof(*_gdt_4gb)) != 0) + if(gdt_init_table((const GDTTable *)&_gdt_4gb, + sizeof(_gdt_4gb) / sizeof(*_gdt_4gb)) != 0) { puts("Could not setup the GDT"); return 4; diff --git a/src/loader/project.conf b/src/loader/project.conf index a08bed4..b242d23 100644 --- a/src/loader/project.conf +++ b/src/loader/project.conf @@ -85,7 +85,7 @@ depends=$(OBJDIR)../lib/libk.a depends=../drivers/boot/multiboot.h,../../include/kernel/drivers/bus.h,../../include/kernel/drivers/console.h [start.S] -depends=../arch/i386/intr.S,../arch/i386/loader.S,../arch/i386/multiboot.S +depends=../arch/i386/gdt.S,../arch/i386/intr.S,../arch/i386/loader.S,../arch/i386/multiboot.S [syscalls.S] depends=$(OBJDIR)../lib/libc/src/syscalls.o diff --git a/tests/gdt.c b/tests/gdt.c new file mode 100644 index 0000000..36bc254 --- /dev/null +++ b/tests/gdt.c @@ -0,0 +1,69 @@ +/* $Id$ */ +/* Copyright (c) 2025 Pierre Pronchery */ +/* This file is part of DeforaOS uKernel */ + + + +#include +#ifndef vaddr_t +# define vaddr_t unsigned long +#endif +#include "../src/loader/gdt.c" + + +#if defined(__amd64__) || defined(__i386__) +/* __arch_setgdt */ +void __arch_setgdt(GDTEntry const * entries, size_t count) +{ + size_t i; + GDTEntry const * entry; + + printf("sizeof(*entries) => %zu\n", sizeof(*entries)); + for(i = 0; i < count; i++) + { + entry = &entries[i]; + printf("base=0x%02x%02x%02x%02x ", + entry->base3, entry->base2, + entry->base1, entry->base0); + printf("limit=0x%x%02x%02x ", + entry->limit2, entry->limit1, entry->limit0); + printf("access=0x%02x ", entry->access); + printf("flags=0x%0x\n", entry->flags); + } + printf("count=0x%zx => size=0x%zx\n", count, (count << 3) - 1); +} +#endif + + +/* main */ +int main(int argc, char * argv[]) +{ +#if defined(__amd64__) || defined(__i386__) + GDT * gdt; + (void) argc; + (void) argv; + + printf("sizeof(GDTEntry) = %zu (expected: %u)\n\n", + sizeof(GDTEntry), 8); + + /* flat 4 GB */ + printf("Flat 4 GB:\n"); + gdt = gdt_init(); + gdt_append(gdt, 0x00000000, 0xffffffff, PROT_READ | PROT_EXEC); + gdt_append(gdt, 0x00000000, 0xffffffff, PROT_READ | PROT_WRITE); + gdt_apply(gdt); + + /* 4 MB code + 4 MB data (read-write) + 4 MB data (read-only) */ + printf("\n4 MB (rx) + 4 MB (rw) + 4 MB (ro):\n"); + gdt = gdt_init(); + gdt_append(gdt, 0x00400000, 0x00400000, PROT_READ | PROT_EXEC); + gdt_append(gdt, 0x00800000, 0x00400000, PROT_READ | PROT_WRITE); + gdt_append(gdt, 0x00c00000, 0x00400000, PROT_READ); + gdt_apply(gdt); + +#endif + (void) argc; + (void) argv; + + return 0; +} diff --git a/tests/project.conf b/tests/project.conf index 0f985b9..8bd17b7 100644 --- a/tests/project.conf +++ b/tests/project.conf @@ -1,4 +1,6 @@ -targets=clint.log,distcheck.log,fixme.log,grub.log +targets=clint.log,distcheck.log,fixme.log,gdt,grub.log +cppflags_force=-I../src +cflags_force=-W -Wall -g -O2 dist=Makefile,clint.sh,distcheck.sh,fixme.sh,grub.sh #targets @@ -20,8 +22,16 @@ script=./fixme.sh depends=fixme.sh enabled=0 +[gdt] +type=binary +sources=gdt.c + [grub.log] type=script script=./grub.sh depends=$(OBJDIR)../src/kernel/uKernel.bin,grub.sh enabled=0 + +#sources +[gdt.c] +depends=../src/loader/gdt.c,../src/arch/amd64/gdt.h,../src/arch/i386/gdt.h