The enum_bitfields system never worked quite right, and always had edge cases where name resolution for the SFINAE would fail. Move everything over to use util::bitset, which can be constexpr and boils down to inline integer bitops in release mode. Improved util::bitset itself, moving the array-backed base implementation into a new util::sized_bitset, and making the single-inttype backed implementation the base case. Also added a distinction between | or |= (which work with real bit values) and + or += (which work with bit indexes).
172 lines
4.9 KiB
C++
172 lines
4.9 KiB
C++
#include <j6/memutils.h>
|
|
#include <util/no_construct.h>
|
|
|
|
#include "kassert.h"
|
|
#include "cpu.h"
|
|
#include "gdt.h"
|
|
#include "logger.h"
|
|
#include "tss.h"
|
|
|
|
extern "C" void gdt_write(const void *gdt_ptr, uint16_t cs, uint16_t ds, uint16_t tr);
|
|
|
|
static constexpr uint8_t kern_cs_index = 1;
|
|
static constexpr uint8_t kern_ss_index = 2;
|
|
static constexpr uint8_t user_cs32_index = 3;
|
|
static constexpr uint8_t user_ss_index = 4;
|
|
static constexpr uint8_t user_cs64_index = 5;
|
|
static constexpr uint8_t tss_index = 6; // Note that this takes TWO GDT entries
|
|
|
|
// The BSP's GDT is initialized _before_ global constructors are called,
|
|
// so we don't want it to have a global constructor, lest it overwrite
|
|
// the previous initialization.
|
|
static util::no_construct<GDT> __g_bsp_gdt_storage;
|
|
GDT &g_bsp_gdt = __g_bsp_gdt_storage.value;
|
|
|
|
static constexpr util::bitset8 ring3 = util::bitset8::of( GDT::type::ring1, GDT::type::ring2 );
|
|
static constexpr util::bitset8 rw = util::bitset8::of( GDT::type::read_write );
|
|
static constexpr util::bitset8 rwx = util::bitset8::of( GDT::type::read_write, GDT::type::execute );
|
|
|
|
GDT::GDT(TSS *tss) :
|
|
m_tss(tss)
|
|
{
|
|
memset(this, 0, sizeof(GDT));
|
|
|
|
m_ptr.limit = sizeof(m_entries) - 1;
|
|
m_ptr.base = &m_entries[0];
|
|
|
|
// Kernel CS/SS - always 64bit
|
|
set(kern_cs_index, 0, 0xfffff, true, rwx);
|
|
set(kern_ss_index, 0, 0xfffff, true, rw);
|
|
|
|
// User CS32/SS/CS64 - layout expected by SYSRET
|
|
set(user_cs32_index, 0, 0xfffff, false, ring3 | rwx);
|
|
set(user_ss_index, 0, 0xfffff, true, ring3 | rw);
|
|
set(user_cs64_index, 0, 0xfffff, true, ring3 | rwx);
|
|
|
|
set_tss(tss);
|
|
}
|
|
|
|
GDT &
|
|
GDT::current()
|
|
{
|
|
cpu_data &cpu = current_cpu();
|
|
return *cpu.gdt;
|
|
}
|
|
|
|
void
|
|
GDT::install() const
|
|
{
|
|
gdt_write(
|
|
static_cast<const void*>(&m_ptr),
|
|
kern_cs_index << 3,
|
|
kern_ss_index << 3,
|
|
tss_index << 3);
|
|
}
|
|
|
|
void
|
|
GDT::set(uint8_t i, uint32_t base, uint64_t limit, bool is64, util::bitset8 t)
|
|
{
|
|
m_entries[i].limit_low = limit & 0xffff;
|
|
m_entries[i].size = (limit >> 16) & 0xf;
|
|
m_entries[i].size |= (is64 ? 0xa0 : 0xc0);
|
|
|
|
m_entries[i].base_low = base & 0xffff;
|
|
m_entries[i].base_mid = (base >> 16) & 0xff;
|
|
m_entries[i].base_high = (base >> 24) & 0xff;
|
|
|
|
static constexpr util::bitset8 sp = util::bitset8::of( type::system, type::present );
|
|
m_entries[i].type = t;
|
|
m_entries[i].type |= sp;
|
|
}
|
|
|
|
struct tss_descriptor
|
|
{
|
|
uint16_t limit_low;
|
|
uint16_t base_00;
|
|
uint8_t base_16;
|
|
util::bitset8 type;
|
|
uint8_t size;
|
|
uint8_t base_24;
|
|
uint32_t base_32;
|
|
uint32_t reserved;
|
|
} __attribute__ ((packed));
|
|
|
|
void
|
|
GDT::set_tss(TSS *tss)
|
|
{
|
|
tss_descriptor tssd;
|
|
|
|
size_t limit = sizeof(TSS);
|
|
tssd.limit_low = limit & 0xffff;
|
|
tssd.size = (limit >> 16) & 0xf;
|
|
|
|
uintptr_t base = reinterpret_cast<uintptr_t>(tss);
|
|
tssd.base_00 = base & 0xffff;
|
|
tssd.base_16 = (base >> 16) & 0xff;
|
|
tssd.base_24 = (base >> 24) & 0xff;
|
|
tssd.base_32 = (base >> 32) & 0xffffffff;
|
|
tssd.reserved = 0;
|
|
|
|
static constexpr util::bitset8 tss_mark =
|
|
util::bitset8::of(type::accessed, type::execute, type::present);
|
|
tssd.type = ring3 | tss_mark;
|
|
|
|
memcpy(&m_entries[tss_index], &tssd, sizeof(tss_descriptor));
|
|
}
|
|
|
|
/*
|
|
void
|
|
GDT::dump(unsigned index) const
|
|
{
|
|
console *cons = console::get();
|
|
|
|
unsigned start = 0;
|
|
unsigned count = (m_ptr.limit + 1) / sizeof(descriptor);
|
|
if (index != -1) {
|
|
start = index;
|
|
count = 1;
|
|
} else {
|
|
cons->printf(" GDT: loc:%lx size:%d\n", m_ptr.base, m_ptr.limit+1);
|
|
}
|
|
|
|
const descriptor *gdt =
|
|
reinterpret_cast<const descriptor *>(m_ptr.base);
|
|
|
|
for (int i = start; i < start+count; ++i) {
|
|
uint32_t base =
|
|
(gdt[i].base_high << 24) |
|
|
(gdt[i].base_mid << 16) |
|
|
gdt[i].base_low;
|
|
|
|
uint32_t limit =
|
|
static_cast<uint32_t>(gdt[i].size & 0x0f) << 16 |
|
|
gdt[i].limit_low;
|
|
|
|
cons->printf(" %02d:", i);
|
|
if (! (gdt[i].type && type::present)) {
|
|
cons->puts(" Not Present\n");
|
|
continue;
|
|
}
|
|
|
|
cons->printf(" Base %08x limit %05x ", base, limit);
|
|
|
|
switch (gdt[i].type & type::ring3) {
|
|
case type::ring3: cons->puts("ring3"); break;
|
|
case type::ring2: cons->puts("ring2"); break;
|
|
case type::ring1: cons->puts("ring1"); break;
|
|
default: cons->puts("ring0"); break;
|
|
}
|
|
|
|
cons->printf(" %s %s %s %s %s %s %s\n",
|
|
(gdt[i].type && type::accessed) ? "A" : " ",
|
|
(gdt[i].type && type::read_write) ? "RW" : " ",
|
|
(gdt[i].type && type::conforming) ? "C" : " ",
|
|
(gdt[i].type && type::execute) ? "EX" : " ",
|
|
(gdt[i].type && type::system) ? "S" : " ",
|
|
(gdt[i].size & 0x80) ? "KB" : " B",
|
|
(gdt[i].size & 0x60) == 0x20 ? "64" :
|
|
(gdt[i].size & 0x60) == 0x40 ? "32" : "16");
|
|
}
|
|
}
|
|
*/
|