diff --git a/src/Descriptors.cc b/src/Descriptors.cc index d71de9b..7370326 100644 --- a/src/Descriptors.cc +++ b/src/Descriptors.cc @@ -10,106 +10,115 @@ #include #include "Descriptors.hh" -namespace kernel { +namespace { /** - * SegmentDescriptors are entries in the GDT and LDT that describe memory - * segments. Each descriptor is two double-words (8 bytes, 64 bits) long. + * Six byte field containing the length and a linear address where a descriptor + * table livs. */ -typedef uint64_t SegmentDescriptor; - - -/** 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 +struct PseudoDescriptor { uint16_t limit; uint32_t base; } __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(type); - uint8_t d = static_cast(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 */ -void -initGDT() +GDT& +GDT::systemGDT() { - sGDT[0] = 0; // First descriptor is always NULL. - sGDT[1] = createSegmentDescriptor(0x00000000, 0x000FFFFF, Type::CodeEXR, DPL::Ring0); - sGDT[2] = createSegmentDescriptor(0x00000000, 0x000FFFFF, Type::DataRW, DPL::Ring0); - sGDT[3] = 0; // Unused for now. - sGDT[4] = 0; // Unused for now. + static GDT sGDT; + return sGDT; +} - 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(hasCoarseGranularity); + uint8_t db = static_cast(has32BitOperations); + uint8_t l = static_cast(hasNative64BitCode); + uint8_t avl = static_cast(hasNative64BitCode); + uint8_t p = static_cast(isPresent); + uint8_t dpl = static_cast(privilegeLevel); + uint8_t s = static_cast(isCodeDataSegment); + uint8_t typ = static_cast(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 diff --git a/src/Descriptors.hh b/src/Descriptors.hh index 5cc5997..3b24fa6 100644 --- a/src/Descriptors.hh +++ b/src/Descriptors.hh @@ -11,4 +11,86 @@ namespace kernel { 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 */ diff --git a/src/Main.cc b/src/Main.cc index da26650..d735f69 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -16,26 +16,15 @@ extern "C" void 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.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" void 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"); +}