Merge branch 'khorben/gdt'

This commit is contained in:
Pierre Pronchery 2025-03-20 14:55:04 +01:00
commit 084bdd18a2
10 changed files with 271 additions and 102 deletions

View File

@ -9,20 +9,36 @@
.section .text .section .text
/* arch_setgdt */ /* arch_setgdt */
.global __arch_setgdt .global __arch_setgdt
#ifndef __clang__
.type __arch_setgdt, @function .type __arch_setgdt, @function
__arch_setgdt:
#if 0
lea gdt_descriptor, %rcx
#else
mov (gdt_descriptor), %rcx
#endif #endif
__arch_setgdt:
lea gdt_descriptor, %rcx
/* set the offset of the GDT */ /* set the offset of the GDT */
mov %rsi, 0x2(%rcx) mov %rsi, 0x2(%rcx)
/* set the size of the GDT */ /* set the size of the GDT */
shl $0x3, %rdi
dec %rdi dec %rdi
mov %di, (%rcx) mov %di, (%rcx)
/* load the GDT */ /* load the GDT */
lgdt (%rcx) 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 ret
@ -31,4 +47,4 @@ __arch_setgdt:
.align 16 .align 16
gdt_descriptor: gdt_descriptor:
.skip 2 /* size */ .skip 2 /* size */
.skip 4 /* offset */ .skip 8 /* offset */

View File

@ -14,15 +14,29 @@
#endif #endif
__arch_setgdt: __arch_setgdt:
lea gdt_descriptor, %ecx lea gdt_descriptor, %ecx
/* set the offset of the GDT */ /* set the offset of the GDT */
mov 0x4(%esp), %eax mov 0x4(%esp), %eax
mov %eax, 0x2(%ecx) mov %eax, 0x2(%ecx)
/* set the size of the GDT */ /* set the size of the GDT */
mov 0x8(%esp), %eax mov 0x8(%esp), %eax
shl $0x3, %eax
dec %eax dec %eax
mov %ax, (%ecx) mov %ax, (%ecx)
/* load the GDT */ /* 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 ret

View File

@ -1,5 +1,5 @@
/* $Id$ */ /* $Id$ */
/* Copyright (c) 2018 Pierre Pronchery <khorben@defora.org> */ /* Copyright (c) 2018-2025 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS uKernel */ /* This file is part of DeforaOS uKernel */
@ -7,21 +7,31 @@
#ifndef UKERNEL_ARCH_I386_GDT_H #ifndef UKERNEL_ARCH_I386_GDT_H
# define UKERNEL_ARCH_I386_GDT_H # define UKERNEL_ARCH_I386_GDT_H
# include <sys/mman.h>
# include <sys/types.h> # include <sys/types.h>
# include <stdint.h> # include <stdint.h>
/* public */ /* public */
/* types */ /* types */
typedef struct _GDT typedef struct _GDT GDT;
typedef struct _GDTTable
{ {
vaddr_t base; vaddr_t base;
vaddr_t limit; size_t size;
uint8_t type; unsigned int prot;
} GDT; } GDTTable;
/* prototypes */ /* 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 */ #endif /* !UKERNEL_ARCH_I386_GDT_H */

View File

@ -11,8 +11,6 @@
# include <stdio.h> # include <stdio.h>
# include <string.h> # include <string.h>
# include <elf.h> # include <elf.h>
# include "arch/amd64/gdt.h"
# include "arch/i386/gdt.h"
# include "drivers/boot/multiboot.h" # include "drivers/boot/multiboot.h"
# ifndef MAX # ifndef MAX

View File

@ -27,12 +27,10 @@
/* private */ /* private */
/* constants */ /* constants */
/* GDT: 4GB flat memory setup */ /* GDT: 4GB flat memory setup */
static const GDT _gdt_4gb[4] = static const GDTTable _gdt_4gb[2] =
{ {
{ 0x00000000, 0x00000000, 0x00 }, { 0x00000000, 0xffffffff, PROT_READ | PROT_EXEC },
{ 0x00000000, 0xffffffff, 0x9a }, { 0x00000000, 0xffffffff, PROT_READ | PROT_WRITE }
{ 0x00000000, 0xffffffff, 0x92 },
{ 0x00000000, 0x00000000, 0x89 }
}; };
static const IDT _idt[] = static const IDT _idt[] =
@ -93,7 +91,8 @@ int multiboot(const ukMultibootInfo * mi)
#if defined(__amd64__) #if defined(__amd64__)
if(_arch_setgdt64(_gdt_4gb, sizeof(_gdt_4gb) / sizeof(*_gdt_4gb)) != 0) if(_arch_setgdt64(_gdt_4gb, sizeof(_gdt_4gb) / sizeof(*_gdt_4gb)) != 0)
#else #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 #endif
{ {
puts("Could not setup the GDT"); puts("Could not setup the GDT");

View File

@ -1,10 +1,11 @@
/* $Id$ */ /* $Id$ */
/* Copyright (c) 2018 Pierre Pronchery <khorben@defora.org> */ /* Copyright (c) 2018-2025 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS uKernel */ /* This file is part of DeforaOS uKernel */
#if defined(__amd64__) || defined(__i386__) #if defined(__amd64__) || defined(__i386__)
# include <limits.h>
# include <stdint.h> # include <stdint.h>
# include <string.h> # include <string.h>
# include <errno.h> # include <errno.h>
@ -12,101 +13,154 @@
# include "arch/i386/gdt.h" # 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 */ /* prototypes */
extern void __arch_setgdt(uint8_t * buf, size_t count); extern void __arch_setgdt(GDTEntry const * entries, size_t count);
/* variables */ /* variables */
static uint8_t _buf[65536]; static GDT _gdt;
/* functions */ /* functions */
/* arch_setgdt */ /* gdt_init */
int _arch_setgdt(GDT const * gdt, size_t count) GDT * gdt_init(void)
{ {
uint8_t * buf = _buf; memset(&_gdt.entries[0], 0, sizeof(_gdt.entries[0]));
size_t i; _gdt.entries_cnt = 1;
GDT g; return &_gdt;
}
memset(&_buf, 0, sizeof(_buf));
/* check for errors */ /* gdt_init_table */
if(count == 0 || count > sizeof(_buf) / sizeof(*gdt)) int gdt_init_table(GDTTable const * table, size_t count)
{ {
errno = ERANGE; int ret = 0;
return -1; GDT * gdt;
} size_t i;
gdt = gdt_init();
for(i = 0; i < count; i++) for(i = 0; i < count; i++)
{ if((ret = gdt_append(gdt, table[i].base, table[i].size,
g = gdt[i]; table[i].prot)) != 0)
buf = &_buf[sizeof(g) * i]; return ret;
if(g.limit > 65536) gdt_apply(gdt);
{
/* 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);
return 0; return 0;
} }
/* arch_setgdt64 */ /* useful */
int _arch_setgdt64(GDT const * gdt, size_t count) /* gdt_append */
int gdt_append(GDT * gdt, vaddr_t base, size_t size, unsigned int prot)
{ {
uint8_t * buf; GDTEntry * entry;
size_t i; uint32_t limit;
GDT g; 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 */ /* check for errors */
if(count == 0 || count > sizeof(_buf) / sizeof(*gdt)) if(size == 0)
{ {
errno = ERANGE; errno = ERANGE;
return -1; 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]; errno = ENOMEM;
buf = &_buf[sizeof(g) * i];
if(g.limit > 65536)
{
/* make sure the limit can be encoded */
if((g.limit & 0xfff) != 0xfff)
return -1; return -1;
g.limit = g.limit >> 12; }
buf[6] = 0xa0; 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 else
buf[6] = 0x20; limit = size - 1;
//encode the limit entry->limit0 = limit & 0xff;
buf[0] = g.limit & 0xff; entry->limit1 = (limit & 0xff00) >> 8;
buf[1] = (g.limit >> 8) & 0xff; entry->limit2 = (limit & 0xf0000) >> 16;
buf[6] |= (g.limit >> 16) & 0xf; /* access */
//encode the base if(prot == (PROT_READ | PROT_EXEC))
buf[2] = g.base & 0xff; /* code segment */
buf[3] = (g.base >> 8) & 0xff; access |= GDT_ACCESS_RW | GDT_ACCESS_X;
buf[4] = (g.base >> 16) & 0xff; else if(prot == (PROT_READ | PROT_WRITE))
buf[7] = (g.base >> 24) & 0xff; /* data segment (read/write) */
//encode the type access |= GDT_ACCESS_RW;
buf[5] = g.type; else if(prot == PROT_READ)
} /* data segment (read-only) */
__arch_setgdt(_buf, count); access |= GDT_ACCESS_SET;
entry->access = access;
/* flags */
entry->flags = flags;
gdt->entries_cnt++;
return 0; return 0;
} }
/* gdt_apply */
void gdt_apply(GDT * gdt)
{
__arch_setgdt(gdt->entries, gdt->entries_cnt);
}
#endif #endif

View File

@ -29,12 +29,10 @@
/* private */ /* private */
/* constants */ /* constants */
/* GDT: 4GB flat memory setup */ /* GDT: 4GB flat memory setup */
static const GDT _gdt_4gb[4] = static const GDTTable _gdt_4gb[2] =
{ {
{ 0x00000000, 0x00000000, 0x00 }, { 0x00000000, 0xffffffff, PROT_READ | PROT_EXEC },
{ 0x00000000, 0xffffffff, 0x9a }, { 0x00000000, 0xffffffff, PROT_READ | PROT_WRITE }
{ 0x00000000, 0xffffffff, 0x92 },
{ 0x00000000, 0x00000000, 0x89 }
}; };
@ -88,7 +86,8 @@ int multiboot(const ukMultibootInfo * mi)
printf("Booted from %#x\n", mi->boot_device_drive); printf("Booted from %#x\n", mi->boot_device_drive);
/* setup the GDT */ /* 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"); puts("Could not setup the GDT");
return 4; return 4;

View File

@ -85,7 +85,7 @@ depends=$(OBJDIR)../lib/libk.a
depends=../drivers/boot/multiboot.h,../../include/kernel/drivers/bus.h,../../include/kernel/drivers/console.h depends=../drivers/boot/multiboot.h,../../include/kernel/drivers/bus.h,../../include/kernel/drivers/console.h
[start.S] [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] [syscalls.S]
depends=$(OBJDIR)../lib/libc/src/syscalls.o depends=$(OBJDIR)../lib/libc/src/syscalls.o

69
tests/gdt.c Normal file
View File

@ -0,0 +1,69 @@
/* $Id$ */
/* Copyright (c) 2025 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS uKernel */
#include <stdio.h>
#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;
}

View File

@ -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 dist=Makefile,clint.sh,distcheck.sh,fixme.sh,grub.sh
#targets #targets
@ -20,8 +22,16 @@ script=./fixme.sh
depends=fixme.sh depends=fixme.sh
enabled=0 enabled=0
[gdt]
type=binary
sources=gdt.c
[grub.log] [grub.log]
type=script type=script
script=./grub.sh script=./grub.sh
depends=$(OBJDIR)../src/kernel/uKernel.bin,grub.sh depends=$(OBJDIR)../src/kernel/uKernel.bin,grub.sh
enabled=0 enabled=0
#sources
[gdt.c]
depends=../src/loader/gdt.c,../src/arch/amd64/gdt.h,../src/arch/i386/gdt.h