polka/src/Descriptors.cc

144 lines
3.4 KiB
C++
Raw Normal View History

/* Descriptors.cc
* vim: set tw=80:
* Eryn Wells <eryn@erynwells.me>
*/
/**
* Oh god oh god. Descriptor tables.
*/
2016-02-28 23:26:42 -05:00
#include <stddef.h>
#include <stdint.h>
2016-02-28 23:26:42 -05:00
#include "Descriptors.hh"
2016-03-01 12:01:51 -05:00
namespace {
2016-02-28 23:26:42 -05:00
/**
2016-03-01 12:01:51 -05:00
* Six byte field containing the length and a linear address where a descriptor
* table livs.
2016-02-28 23:26:42 -05:00
*/
2016-03-01 12:01:51 -05:00
struct PseudoDescriptor
2016-02-28 23:26:42 -05:00
{
uint16_t limit;
uint32_t base;
} __attribute((__packed__));
2016-03-01 12:01:51 -05:00
}
namespace kernel {
/*
* Static
*/
GDT&
GDT::systemGDT()
{
static GDT sGDT;
return 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};
}
2016-02-28 23:26:42 -05:00
2016-03-01 12:01:51 -05:00
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};
}
2016-02-28 23:26:42 -05:00
2016-03-01 12:01:51 -05:00
GDT::Descriptor
GDT::DescriptorSpec::descriptor()
const
{
2016-03-01 12:01:51 -05:00
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;
2016-03-01 12:01:51 -05:00
descriptor |= base << 16; // Bits 15:00 of the base address.
descriptor |= limit & 0x0000FFFF; // Bits 15:00 of the segment limit.
return descriptor;
}
2016-03-01 12:01:51 -05:00
GDT::GDT()
: table{0}
{ }
2016-02-28 23:26:42 -05:00
void
2016-03-01 12:01:51 -05:00
GDT::setDescriptor(size_t index,
const GDT::DescriptorSpec& spec)
2016-02-28 23:26:42 -05:00
{
2016-03-01 12:01:51 -05:00
table[index] = spec.descriptor();
}
2016-02-28 23:26:42 -05:00
2016-03-01 12:01:51 -05:00
void
GDT::setNullDescriptor(size_t index)
{
table[index] = 0;
}
void
GDT::load()
{
PseudoDescriptor gdt {Size * sizeof(Descriptor) - 1, uint32_t(&table)};
2016-02-28 23:26:42 -05:00
/*
* Load the new GDT with the pointer defined above. The GDT isn't actually
* used until the segment registers are reladed. Below, CS is reloaded by
* a long jump into the new code segment. The rest of the segment registers
* can be loaded directly.
*/
asm volatile(
"lgdt %0\n"
"ljmpl $0x08, $reloadSegments\n"
"reloadSegments:\n"
"movl $0x10, %%eax\n"
"movl %%eax, %%ds\n"
"movl %%eax, %%es\n"
"movl %%eax, %%fs\n"
"movl %%eax, %%gs\n"
"movl %%eax, %%ss\n"
: : "m" (gdt)
: "%eax");
}
} /* namespace kernel */