From 0425e5a7943bd2eaeca8f4dd1079da9b5493253d Mon Sep 17 00:00:00 2001 From: Aqua-sama Date: Sun, 7 Feb 2021 23:09:21 +0200 Subject: Loading GDT --- src/gdt.cc | 25 +++++++++++++++++++ src/gdt.h | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/kernel.cc | 15 +++++++++--- src/kernel/dump_gdt.cc | 8 ++++++ src/makefile | 4 ++- 5 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 src/gdt.cc create mode 100644 src/gdt.h create mode 100644 src/kernel/dump_gdt.cc diff --git a/src/gdt.cc b/src/gdt.cc new file mode 100644 index 0000000..202fe77 --- /dev/null +++ b/src/gdt.cc @@ -0,0 +1,25 @@ +#include "gdt.h" + +GDT::SegmentDescriptor::SegmentDescriptor(uint32_t base, uint32_t limit, uint8_t type) { + base_low = base & 0xffff; + base_high = (base >> 16) & 0xff; + base_vhigh = (base >> 24) & 0xff; +} +uint32_t GDT::SegmentDescriptor::base() const { + uint32_t base = base_vhigh; + base = (base << 8) + base_high; + base = (base << 8) + base_low; + return base; +} +uint32_t GDT::SegmentDescriptor::limit() const { + return 0; +} + +GDT::GDT() + : segments{{0, 0, 0}, // null segment + {0, 64 * 1024 * 1024 /* 64MB */, 0x9a}, // code segment + {0, 64 * 1024 * 1024 /* 64MB */, 0x92}} // data segment +{ + Pointer gdtr{.limit = sizeof(segments) - 1, .base = reinterpret_cast(segments)}; + asm volatile("lgdt %0" : : "p"(gdtr)); +} diff --git a/src/gdt.h b/src/gdt.h new file mode 100644 index 0000000..99a6da2 --- /dev/null +++ b/src/gdt.h @@ -0,0 +1,66 @@ +#pragma once + +#include + +/* From OSDev wiki: + * + * Segment + * a logically contiguous chunk of memory with consistent properties (CPU's speaking) + * + * Segment Register + * a register of your CPU that refers to a segment for a special use (e.g. SS, CS, DS ...) + * + * Selector + * a reference to a descriptor you can load into a segment register; the selector is an offset of a descriptor table + * entry. These entries are 8 bytes long. Therefore, bits 3 and up only declare the descriptor table entry offset, while + * bit 2 specifies if this selector is a GDT or LDT selector (LDT - bit set, GDT - bit cleared), and bits 0 - 1 declare + * the ring level that needs to correspond to the descriptor table entry's DPL field. If it doesn't, a General + * Protection Fault occurs; if it does correspond then the CPL level of the selector used is changed accordingly. + * + * Descriptor + * a memory structure (part of a table) that tells the CPU the attributes of a given segment + */ + +class GDT { +public: + GDT(); + ~GDT() = default; + + struct Pointer { + uint16_t limit; + uint32_t base; + } __attribute((packed)); + + class SegmentDescriptor { + public: + SegmentDescriptor(uint32_t base = 0, uint32_t limit = 0, uint8_t type = 0); + [[nodiscard]] uint32_t base() const; + [[nodiscard]] uint32_t limit() const; + + private: + /* + * |31| | | | | | |24|23|22| |20|19| | |16|15| | |12|11| | | 8| 7| | | | | | | 0| + * | base (24~31) | G|DB| | A| lim(16~19)| P| DPL | S| Type | base (16~23) | + * | base (0~15) | limit (0~15) | + * |31| | | | | | | | | | | | | | |16|15| | | | | | | | | | | | | | | 0| + * + * limit size of segment - 1, either in bytes or in 4KiB chunks (check flags) + * base address of segment + * access + * flags defines the segment chunks and 16/32 bit + */ + + uint16_t limit_low; + uint16_t base_low; + uint8_t base_high; + uint8_t type; + uint8_t flags_limit_high; + uint8_t base_vhigh; + } __attribute__((packed)); + +private: + SegmentDescriptor segments[256]; +}; + +static_assert(sizeof(GDT::Pointer) == 6); +static_assert(sizeof(GDT::SegmentDescriptor) == 8); diff --git a/src/kernel.cc b/src/kernel.cc index c083364..2fa90e3 100644 --- a/src/kernel.cc +++ b/src/kernel.cc @@ -10,17 +10,26 @@ #include #include +#include "gdt.h" #include "vga.h" -extern "C" void dump_multiboot(uint32_t mb_magic, uint32_t mb_addr); +extern "C" { +void dump_multiboot(uint32_t mb_magic, uint32_t mb_addr); +void dump_gdt(); -extern "C" void kernel_main(uint32_t mb_magic, uint32_t mb_addr) { +void kernel_main([[maybe_unused]] uint32_t mb_magic, [[maybe_unused]] uint32_t mb_addr) { VGA terminal; Console::set(&terminal); printk("Hello, kernel World!\n"); - dump_multiboot(mb_magic, mb_addr); + // dump_multiboot(mb_magic, mb_addr); + dump_gdt(); + + printk("Setting new GDT\n"); + GDT gdt; + dump_gdt(); abort(); } +} // extern "C" diff --git a/src/kernel/dump_gdt.cc b/src/kernel/dump_gdt.cc new file mode 100644 index 0000000..718ada6 --- /dev/null +++ b/src/kernel/dump_gdt.cc @@ -0,0 +1,8 @@ +#include +#include "../gdt.h" + +extern "C" void dump_gdt() { + GDT::Pointer gdtr{0, 0}; + asm volatile("sgdt %0" : "=m"(gdtr) :); + printk("GDT at ", uhex{gdtr.base}, " , limit=", gdtr.limit, '\n'); +} diff --git a/src/makefile b/src/makefile index 86c8877..7902dbf 100644 --- a/src/makefile +++ b/src/makefile @@ -1,5 +1,7 @@ AS_OBJ += src/boot.o CXX_OBJ += src/kernel.o \ + src/kernel/dump_gdt.o \ src/kernel/dump_multiboot.o \ - src/vga.o + src/vga.o \ + src/gdt.o -- cgit v1.2.1