diff --git a/src/arch/i386/gdt.h b/src/arch/i386/gdt.h index 68b527e..9f13152 100644 --- a/src/arch/i386/gdt.h +++ b/src/arch/i386/gdt.h @@ -23,14 +23,65 @@ typedef struct _GDTTable unsigned int prot; } GDTTable; +#pragma pack(1) +typedef struct _TSS +{ + uint16_t link; + uint16_t __padding0; + uint32_t esp0; + uint16_t ss0; + uint16_t __padding1; + uint32_t esp1; + uint16_t ss1; + uint16_t __padding2; + uint32_t esp2; + uint16_t ss2; + uint16_t __padding3; + 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 __padding4; + uint16_t cs; + uint16_t __padding5; + uint16_t ss; + uint16_t __padding6; + uint16_t ds; + uint16_t __padding7; + uint16_t fs; + uint16_t __padding8; + uint16_t gs; + uint16_t __padding9; + uint16_t ldtr; + uint16_t __padding10; + uint16_t __padding11; + uint16_t iopb; + uint32_t ssp; +} TSS; +#pragma pack() + + +/* constants */ +# define GDT_SYSTEM_TYPE_LDT 0x2 +# define GDT_SYSTEM_TYPE_TSS 0x9 + /* prototypes */ GDT * gdt_init(void); -int gdt_init_table(GDTTable const * table, size_t count); +int gdt_init_table(GDTTable const * table, size_t table_cnt, size_t tss_cnt); /* useful */ int gdt_append(GDT * gdt, vaddr_t base, size_t size, unsigned int prot); +int gdt_append_system(GDT * gdt, void * base, size_t size, unsigned int type); void gdt_apply(GDT * gdt); diff --git a/src/kernel/multiboot.c b/src/kernel/multiboot.c index 434b47c..d6f5f7e 100644 --- a/src/kernel/multiboot.c +++ b/src/kernel/multiboot.c @@ -1,5 +1,5 @@ /* $Id$ */ -/* Copyright (c) 2018-2019 Pierre Pronchery */ +/* Copyright (c) 2018-2025 Pierre Pronchery */ /* This file is part of DeforaOS uKernel */ @@ -92,7 +92,7 @@ int multiboot(const ukMultibootInfo * mi) if(_arch_setgdt64(_gdt_4gb, sizeof(_gdt_4gb) / sizeof(*_gdt_4gb)) != 0) #else if(gdt_init_table((const GDTTable *)&_gdt_4gb, - sizeof(_gdt_4gb) / sizeof(*_gdt_4gb)) != 0) + sizeof(_gdt_4gb) / sizeof(*_gdt_4gb), 1) != 0) #endif { puts("Could not setup the GDT"); diff --git a/src/loader/gdt.c b/src/loader/gdt.c index bfa5da1..a11101e 100644 --- a/src/loader/gdt.c +++ b/src/loader/gdt.c @@ -19,8 +19,8 @@ /* access */ # define GDT_ACCESS_SET 0x01 -# define GDT_ACCESS_RW 0x02 -# define GDT_ACCESS_X 0x08 +# define GDT_ACCESS_PROT_RW 0x02 +# define GDT_ACCESS_PROT_X 0x08 # define GDT_ACCESS_SEGMENT 0x10 # define GDT_ACCESS_RING(level) ((level) << 5) # define GDT_ACCESS_PRESENT 0x80 @@ -48,6 +48,21 @@ typedef struct _GDTEntry } GDTEntry; #pragma pack() +#pragma pack(1) +typedef struct _LDT +{ + 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; +} LDT; +#pragma pack() + struct _GDT { GDTEntry entries[GDT_ENTRIES_MAX]; @@ -58,9 +73,13 @@ struct _GDT /* prototypes */ extern void __arch_setgdt(GDTEntry const * entries, size_t count); +static int _gdt_append_entry(GDT * gdt, vaddr_t base, uint32_t limit, + uint8_t access, uint8_t flags); + /* variables */ static GDT _gdt; +static TSS _tss; /* functions */ @@ -74,17 +93,27 @@ GDT * gdt_init(void) /* gdt_init_table */ -int gdt_init_table(GDTTable const * table, size_t count) +int gdt_init_table(GDTTable const * table, size_t table_cnt, size_t tss_cnt) { int ret = 0; GDT * gdt; size_t i; gdt = gdt_init(); - for(i = 0; i < count; i++) + for(i = 0; i < table_cnt; i++) if((ret = gdt_append(gdt, table[i].base, table[i].size, table[i].prot)) != 0) return ret; + /* set the TSS */ + if(tss_cnt > 1) + { + errno = ENOSYS; + return -1; + } + else if(tss_cnt == 1 && (ret = gdt_append_system(gdt, &_tss, + sizeof(_tss), GDT_SYSTEM_TYPE_TSS)) + != 0) + return ret; gdt_apply(gdt); return 0; } @@ -94,10 +123,9 @@ int gdt_init_table(GDTTable const * table, size_t count) /* gdt_append */ int gdt_append(GDT * gdt, vaddr_t base, size_t size, unsigned int prot) { - GDTEntry * entry; uint32_t limit; - uint8_t access = GDT_ACCESS_PRESENT - | GDT_ACCESS_SEGMENT | GDT_ACCESS_RING(0); + uint8_t access = GDT_ACCESS_SEGMENT | GDT_ACCESS_PRESENT + | GDT_ACCESS_RING(0); uint8_t flags = GDT_FLAG_PROTECTED_MODE; /* check for errors */ @@ -106,9 +134,7 @@ int gdt_append(GDT * gdt, vaddr_t base, size_t size, unsigned int prot) errno = ERANGE; return -1; } - if(gdt->entries_cnt >= sizeof(gdt->entries) / sizeof(*gdt->entries) - || size > ULONG_MAX - || ULONG_MAX - size < base) + if(size > ULONG_MAX || ULONG_MAX - size < base) { errno = ENOMEM; return -1; @@ -120,41 +146,72 @@ int gdt_append(GDT * gdt, vaddr_t base, size_t size, unsigned int prot) 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--; + ? (size >> 12) - 1 + : (((size | 0xfff) + 1) >> 12) - 1; 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; + access |= GDT_ACCESS_PROT_RW | GDT_ACCESS_PROT_X; else if(prot == (PROT_READ | PROT_WRITE)) /* data segment (read/write) */ - access |= GDT_ACCESS_RW; + access |= GDT_ACCESS_PROT_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; + return _gdt_append_entry(gdt, base, limit, access, flags); +} + + +/* gdt_append_system */ +static int _append_system_ldt(GDT * gdt, void * base, size_t size); +static int _append_system_tss(GDT * gdt, void * base, size_t size); + +int gdt_append_system(GDT * gdt, void * base, size_t size, unsigned int type) +{ + switch(type) + { + case GDT_SYSTEM_TYPE_LDT: + return _append_system_ldt(gdt, base, size); + case GDT_SYSTEM_TYPE_TSS: + return _append_system_tss(gdt, base, size); + default: + errno = ENOSYS; + return -1; + } +} + +static int _append_system_ldt(GDT * gdt, void * base, size_t size) +{ + uint32_t access = GDT_ACCESS_PRESENT | GDT_SYSTEM_TYPE_LDT; + uint8_t flags = GDT_FLAG_PROTECTED_MODE; + + if(size != sizeof(LDT)) + { + errno = ERANGE; + return -1; + } + return _gdt_append_entry(gdt, (vaddr_t)base, size - 1, access, flags); +} + +static int _append_system_tss(GDT * gdt, void * base, size_t size) +{ + uint32_t access = GDT_ACCESS_PRESENT | GDT_SYSTEM_TYPE_TSS; + uint8_t flags = GDT_FLAG_PROTECTED_MODE; + + if(size != sizeof(TSS)) + { + errno = ERANGE; + return -1; + } + return _gdt_append_entry(gdt, (vaddr_t)base, size - 1, access, flags); } @@ -163,4 +220,32 @@ void gdt_apply(GDT * gdt) { __arch_setgdt(gdt->entries, gdt->entries_cnt); } + + +/* private */ +/* gdt_append_entry */ +static int _gdt_append_entry(GDT * gdt, vaddr_t base, uint32_t limit, + uint8_t access, uint8_t flags) +{ + GDTEntry * entry; + + /* check for errors */ + if(gdt->entries_cnt >= sizeof(gdt->entries) / sizeof(*gdt->entries)) + { + errno = ENOMEM; + return -1; + } + entry = &gdt->entries[gdt->entries_cnt]; + entry->base0 = base & 0xff; + entry->base1 = (base & 0xff00) >> 8; + entry->base2 = (base & 0xff0000) >> 16; + entry->base3 = (base & 0xff000000) >> 24; + entry->limit0 = limit & 0xff; + entry->limit1 = (limit & 0xff00) >> 8; + entry->limit2 = (limit & 0xf0000) >> 16; + entry->access = access; + entry->flags = flags; + gdt->entries_cnt++; + return 0; +} #endif diff --git a/src/loader/multiboot.c b/src/loader/multiboot.c index 44ba76b..51225ea 100644 --- a/src/loader/multiboot.c +++ b/src/loader/multiboot.c @@ -1,5 +1,5 @@ /* $Id$ */ -/* Copyright (c) 2018-2020 Pierre Pronchery */ +/* Copyright (c) 2018-2025 Pierre Pronchery */ /* This file is part of DeforaOS uKernel */ @@ -87,7 +87,7 @@ int multiboot(const ukMultibootInfo * mi) /* setup the GDT */ if(gdt_init_table((const GDTTable *)&_gdt_4gb, - sizeof(_gdt_4gb) / sizeof(*_gdt_4gb)) != 0) + sizeof(_gdt_4gb) / sizeof(*_gdt_4gb), 1) != 0) { puts("Could not setup the GDT"); return 4; diff --git a/tests/gdt.c b/tests/gdt.c index 36bc254..d5e38e8 100644 --- a/tests/gdt.c +++ b/tests/gdt.c @@ -12,6 +12,11 @@ #if defined(__amd64__) || defined(__i386__) +/* variables */ +static TSS tss; + + +/* functions */ /* __arch_setgdt */ void __arch_setgdt(GDTEntry const * entries, size_t count) { @@ -51,6 +56,7 @@ int main(int argc, char * argv[]) gdt = gdt_init(); gdt_append(gdt, 0x00000000, 0xffffffff, PROT_READ | PROT_EXEC); gdt_append(gdt, 0x00000000, 0xffffffff, PROT_READ | PROT_WRITE); + gdt_append_system(gdt, &tss, sizeof(tss), GDT_SYSTEM_TYPE_TSS); gdt_apply(gdt); /* 4 MB code + 4 MB data (read-write) + 4 MB data (read-only) */ @@ -59,6 +65,7 @@ int main(int argc, char * argv[]) 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_append_system(gdt, &tss, sizeof(tss), GDT_SYSTEM_TYPE_TSS); gdt_apply(gdt); #endif