Move GDT to its own object
This commit is contained in:
parent
b893151fa4
commit
238e79f514
3 changed files with 198 additions and 103 deletions
|
@ -10,106 +10,115 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "Descriptors.hh"
|
#include "Descriptors.hh"
|
||||||
|
|
||||||
namespace kernel {
|
namespace {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SegmentDescriptors are entries in the GDT and LDT that describe memory
|
* Six byte field containing the length and a linear address where a descriptor
|
||||||
* segments. Each descriptor is two double-words (8 bytes, 64 bits) long.
|
* table livs.
|
||||||
*/
|
*/
|
||||||
typedef uint64_t SegmentDescriptor;
|
struct PseudoDescriptor
|
||||||
|
|
||||||
|
|
||||||
/** Descriptor privilege level. */
|
|
||||||
enum class DPL {
|
|
||||||
Ring0 = 0x0,
|
|
||||||
Ring1 = 0x1,
|
|
||||||
Ring2 = 0x2,
|
|
||||||
Ring3 = 0x3
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/** A four bit value describing the type of the segment. */
|
|
||||||
enum class Type {
|
|
||||||
// Data segment types
|
|
||||||
DataRO = 0x0, // Read-only
|
|
||||||
DataROA = 0x1, // Read-only, accessed
|
|
||||||
DataRW = 0x2, // Read/write
|
|
||||||
DataRWA = 0x3, // Read/write, accessed
|
|
||||||
DataROEX = 0x4, // Read-only, expand-down
|
|
||||||
DataROEXA = 0x5, // Read-only, expand-down, accessed
|
|
||||||
DataRWEX = 0x6, // Read/write, expand-down
|
|
||||||
DataRWEXA = 0x7, // Read/write, expand-down, accessed
|
|
||||||
|
|
||||||
// Code segment types
|
|
||||||
CodeEX = 0x8, // Execute-only
|
|
||||||
CodeEXA = 0x9, // Execute-only, accessed
|
|
||||||
CodeEXR = 0xa, // Execute/read
|
|
||||||
CodeEXRA = 0xb, // Execute/read, accessed
|
|
||||||
CodeEXC = 0xc, // Execute-only, conforming
|
|
||||||
CodeEXCA = 0xd, // Execute-only, conforming, accessed
|
|
||||||
CodeEXRC = 0xe, // Execute/read, conforming
|
|
||||||
CodeEXRCA = 0xf // Execute/read, conforming, accessed
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/** Six byte field containing the length and a linear address where the GDT lives. */
|
|
||||||
struct GDTPointer
|
|
||||||
{
|
{
|
||||||
uint16_t limit;
|
uint16_t limit;
|
||||||
uint32_t base;
|
uint32_t base;
|
||||||
} __attribute((__packed__));
|
} __attribute((__packed__));
|
||||||
|
|
||||||
|
|
||||||
static const size_t GDTSize = 5;
|
|
||||||
static SegmentDescriptor sGDT[GDTSize];
|
|
||||||
|
|
||||||
|
|
||||||
static inline SegmentDescriptor
|
|
||||||
createSegmentDescriptor(uint32_t base,
|
|
||||||
uint32_t limit,
|
|
||||||
Type type,
|
|
||||||
DPL dpl)
|
|
||||||
{
|
|
||||||
SegmentDescriptor descriptor = 0;
|
|
||||||
|
|
||||||
uint8_t t = static_cast<uint8_t>(type);
|
|
||||||
uint8_t d = static_cast<uint8_t>(dpl);
|
|
||||||
|
|
||||||
descriptor = base & 0xFF000000; // Bits 31:24 of the base address.
|
|
||||||
descriptor |= (0x1 << 23); // Granularity field: segment limit is interpreted in 4KB units.
|
|
||||||
descriptor |= (0x1 << 22); // D/B field: default operation/stack size flag, 1 for 32-bit.
|
|
||||||
descriptor |= (0x0 << 21); // L field: 64-bit code segment, 0 for 32-bit.
|
|
||||||
descriptor |= (0x0 << 20); // AVL field: system determined, unused here.
|
|
||||||
descriptor |= limit & 0x000F0000; // Bits 19:16 of the segment limit.
|
|
||||||
descriptor |= (0x1 << 15); // P field: segment is present.
|
|
||||||
descriptor |= (d << 13) & 0x00006000; // DPL field: privilege level of the segment.
|
|
||||||
descriptor |= (0x1 << 12); // S field: 0 for system, 1 for code/data.
|
|
||||||
descriptor |= (t << 8) & 0x00000F00; // Type field: see Type
|
|
||||||
descriptor |= (base >> 16) & 0x000000FF; // Bits 23:16 of the base address.
|
|
||||||
|
|
||||||
// Shift everything up by 32 to make room for the lower 4 bytes
|
|
||||||
descriptor <<= 32;
|
|
||||||
|
|
||||||
descriptor |= base << 16; // Bits 15:00 of the base address.
|
|
||||||
descriptor |= limit & 0x0000FFFF; // Bits 15:00 of the segment limit.
|
|
||||||
|
|
||||||
return descriptor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace kernel {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Static
|
* Static
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void
|
GDT&
|
||||||
initGDT()
|
GDT::systemGDT()
|
||||||
{
|
{
|
||||||
sGDT[0] = 0; // First descriptor is always NULL.
|
static GDT sGDT;
|
||||||
sGDT[1] = createSegmentDescriptor(0x00000000, 0x000FFFFF, Type::CodeEXR, DPL::Ring0);
|
return sGDT;
|
||||||
sGDT[2] = createSegmentDescriptor(0x00000000, 0x000FFFFF, Type::DataRW, DPL::Ring0);
|
}
|
||||||
sGDT[3] = 0; // Unused for now.
|
|
||||||
sGDT[4] = 0; // Unused for now.
|
|
||||||
|
|
||||||
GDTPointer gdt {GDTSize * sizeof(SegmentDescriptor) - 1, uint32_t(&sGDT)};
|
/*
|
||||||
|
* Public
|
||||||
|
*/
|
||||||
|
|
||||||
|
GDT::DescriptorSpec
|
||||||
|
GDT::DescriptorSpec::null()
|
||||||
|
{
|
||||||
|
// Specify ring 0 and RO data segment here because their values are 0.
|
||||||
|
return {0, 0, 0, 0, 0, 0, 0, DPL::Ring0, 0, Type::DataRO};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GDT::DescriptorSpec
|
||||||
|
GDT::DescriptorSpec::kernelSegment(uint32_t base,
|
||||||
|
uint32_t limit,
|
||||||
|
GDT::Type type)
|
||||||
|
{
|
||||||
|
return {base, limit, true, true, false, false, true, DPL::Ring0, true, type};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GDT::Descriptor
|
||||||
|
GDT::DescriptorSpec::descriptor()
|
||||||
|
const
|
||||||
|
{
|
||||||
|
Descriptor descriptor = 0;
|
||||||
|
|
||||||
|
uint8_t g = static_cast<uint8_t>(hasCoarseGranularity);
|
||||||
|
uint8_t db = static_cast<uint8_t>(has32BitOperations);
|
||||||
|
uint8_t l = static_cast<uint8_t>(hasNative64BitCode);
|
||||||
|
uint8_t avl = static_cast<uint8_t>(hasNative64BitCode);
|
||||||
|
uint8_t p = static_cast<uint8_t>(isPresent);
|
||||||
|
uint8_t dpl = static_cast<uint8_t>(privilegeLevel);
|
||||||
|
uint8_t s = static_cast<uint8_t>(isCodeDataSegment);
|
||||||
|
uint8_t typ = static_cast<uint8_t>(type);
|
||||||
|
|
||||||
|
descriptor = base & 0xFF000000; // Bits 31:24 of the base address.
|
||||||
|
descriptor |= (g << 23); // Granularity field
|
||||||
|
descriptor |= (db << 22); // D/B field
|
||||||
|
descriptor |= (l << 21); // L field
|
||||||
|
descriptor |= (avl << 20); // AVL field
|
||||||
|
descriptor |= limit & 0x000F0000; // Bits 19:16 of the segment limit.
|
||||||
|
descriptor |= (p << 15); // P field
|
||||||
|
descriptor |= (dpl << 13) & 0x00006000; // DPL field
|
||||||
|
descriptor |= (s << 12); // S field
|
||||||
|
descriptor |= (typ << 8) & 0x00000F00; // Type field: see Type
|
||||||
|
descriptor |= (base >> 16) & 0x000000FF; // Bits 23:16 of the base address.
|
||||||
|
|
||||||
|
// Shift everything up by 32 to make room for the lower 4 bytes
|
||||||
|
descriptor <<= 32;
|
||||||
|
|
||||||
|
descriptor |= base << 16; // Bits 15:00 of the base address.
|
||||||
|
descriptor |= limit & 0x0000FFFF; // Bits 15:00 of the segment limit.
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GDT::GDT()
|
||||||
|
: table{0}
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
GDT::setDescriptor(size_t index,
|
||||||
|
const GDT::DescriptorSpec& spec)
|
||||||
|
{
|
||||||
|
table[index] = spec.descriptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
GDT::setNullDescriptor(size_t index)
|
||||||
|
{
|
||||||
|
table[index] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
GDT::load()
|
||||||
|
{
|
||||||
|
PseudoDescriptor gdt {Size * sizeof(Descriptor) - 1, uint32_t(&table)};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Load the new GDT with the pointer defined above. The GDT isn't actually
|
* Load the new GDT with the pointer defined above. The GDT isn't actually
|
||||||
|
|
|
@ -11,4 +11,86 @@ namespace kernel {
|
||||||
|
|
||||||
void initGDT();
|
void initGDT();
|
||||||
|
|
||||||
|
struct GDT
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* SegmentDescriptors are entries in the GDT and LDT that describe memory
|
||||||
|
* segments. Each descriptor is two double-words (8 bytes, 64 bits) long.
|
||||||
|
*/
|
||||||
|
typedef uint64_t Descriptor;
|
||||||
|
|
||||||
|
/** Descriptor privilege level. */
|
||||||
|
enum class DPL {
|
||||||
|
Ring0 = 0x0,
|
||||||
|
Ring1 = 0x1,
|
||||||
|
Ring2 = 0x2,
|
||||||
|
Ring3 = 0x3
|
||||||
|
};
|
||||||
|
|
||||||
|
/** A four bit value describing the type of the segment. */
|
||||||
|
enum class Type {
|
||||||
|
// Data segment types
|
||||||
|
DataRO = 0x0, // Read-only
|
||||||
|
DataROA = 0x1, // Read-only, accessed
|
||||||
|
DataRW = 0x2, // Read/write
|
||||||
|
DataRWA = 0x3, // Read/write, accessed
|
||||||
|
DataROEX = 0x4, // Read-only, expand-down
|
||||||
|
DataROEXA = 0x5, // Read-only, expand-down, accessed
|
||||||
|
DataRWEX = 0x6, // Read/write, expand-down
|
||||||
|
DataRWEXA = 0x7, // Read/write, expand-down, accessed
|
||||||
|
|
||||||
|
// Code segment types
|
||||||
|
CodeEX = 0x8, // Execute-only
|
||||||
|
CodeEXA = 0x9, // Execute-only, accessed
|
||||||
|
CodeEXR = 0xa, // Execute/read
|
||||||
|
CodeEXRA = 0xb, // Execute/read, accessed
|
||||||
|
CodeEXC = 0xc, // Execute-only, conforming
|
||||||
|
CodeEXCA = 0xd, // Execute-only, conforming, accessed
|
||||||
|
CodeEXRC = 0xe, // Execute/read, conforming
|
||||||
|
CodeEXRCA = 0xf // Execute/read, conforming, accessed
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a memory segment for the GDT. See the Intel System Programming
|
||||||
|
* Guide, page 3-10, for details.
|
||||||
|
*/
|
||||||
|
struct DescriptorSpec
|
||||||
|
{
|
||||||
|
uint32_t base; // Base address of segment
|
||||||
|
uint32_t limit; // Extent/length/size/limit of segment; 24 bits used
|
||||||
|
bool hasCoarseGranularity; // G field; coarse = limit in 4KByte units
|
||||||
|
bool has32BitOperations; // D/B field
|
||||||
|
bool hasNative64BitCode; // L field; only valid in IA-32e mode
|
||||||
|
bool available; // AVL field; available for system software use
|
||||||
|
bool isPresent; // P field
|
||||||
|
DPL privilegeLevel; // DPL field
|
||||||
|
bool isCodeDataSegment; // S field
|
||||||
|
Type type; // Type field
|
||||||
|
|
||||||
|
static DescriptorSpec null();
|
||||||
|
static DescriptorSpec kernelSegment(uint32_t base, uint32_t limit, Type type);
|
||||||
|
|
||||||
|
Descriptor descriptor() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
static GDT& systemGDT();
|
||||||
|
|
||||||
|
GDT();
|
||||||
|
|
||||||
|
/** Set the descriptor at the given `index` to the value of `spec`. */
|
||||||
|
void setDescriptor(size_t index, const DescriptorSpec& spec);
|
||||||
|
|
||||||
|
/** Set the descriptor at the given `index` to the NULL descriptor. */
|
||||||
|
void setNullDescriptor(size_t index);
|
||||||
|
|
||||||
|
/** Load this GDT into the CPU and flush the registers. */
|
||||||
|
void load();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// TODO: Maybe eventually I can make this variable? Maybe use templates?
|
||||||
|
static const size_t Size = 5;
|
||||||
|
|
||||||
|
Descriptor table[Size];
|
||||||
|
};
|
||||||
|
|
||||||
} /* namespace kernel */
|
} /* namespace kernel */
|
||||||
|
|
42
src/Main.cc
42
src/Main.cc
|
@ -16,26 +16,15 @@ extern "C"
|
||||||
void
|
void
|
||||||
kearly()
|
kearly()
|
||||||
{
|
{
|
||||||
auto console = kernel::Console::systemConsole();
|
using kernel::Console;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a console object for early use because global initialization
|
||||||
|
* hasn't happened yet.
|
||||||
|
*/
|
||||||
|
Console console;
|
||||||
console.clear(kernel::Console::Color::Blue);
|
console.clear(kernel::Console::Color::Blue);
|
||||||
console.writeString("Loading system ...\n");
|
console.writeString("Loading system ...\n");
|
||||||
|
|
||||||
kernel::initGDT();
|
|
||||||
|
|
||||||
volatile int foo = 0;
|
|
||||||
int i = 0;
|
|
||||||
for (;;) {
|
|
||||||
if (i == 0) {
|
|
||||||
console.writeString("--- MARK ---\n");
|
|
||||||
}
|
|
||||||
console.writeChar('a' + i);
|
|
||||||
console.writeChar('\n');
|
|
||||||
i = (i + 1) % 26;
|
|
||||||
|
|
||||||
for (uint32_t k = 0; k < (2u << 20) - 1; k++) {
|
|
||||||
foo /= 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,4 +32,19 @@ kearly()
|
||||||
extern "C"
|
extern "C"
|
||||||
void
|
void
|
||||||
kmain()
|
kmain()
|
||||||
{ }
|
{
|
||||||
|
using kernel::Console;
|
||||||
|
using kernel::GDT;
|
||||||
|
|
||||||
|
// Reinitialize the system console now that we have global static objects.
|
||||||
|
auto console = Console::systemConsole();
|
||||||
|
console.clear(Console::Color::Blue);
|
||||||
|
|
||||||
|
auto gdt = GDT::systemGDT();
|
||||||
|
gdt.setNullDescriptor(0);
|
||||||
|
gdt.setDescriptor(1, GDT::DescriptorSpec::kernelSegment(0, 0x000FFFFF, GDT::Type::CodeEXR));
|
||||||
|
gdt.setDescriptor(2, GDT::DescriptorSpec::kernelSegment(0, 0x000FFFFF, GDT::Type::DataRW));
|
||||||
|
gdt.load();
|
||||||
|
|
||||||
|
console.writeString("GDT loaded\n");
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue