diff --git a/InstructionSets/x86/Descriptors.hpp b/InstructionSets/x86/Descriptors.hpp index 0dd8ff7e8..9849f86dc 100644 --- a/InstructionSets/x86/Descriptors.hpp +++ b/InstructionSets/x86/Descriptors.hpp @@ -30,40 +30,38 @@ struct Descriptor { } void set(const uint16_t descriptor[4]) { - // TODO: something. - base_ = uint32_t(descriptor[1] | ((descriptor[2] & 0xff) << 16)); limit_ = descriptor[0]; printf("%04x %04x %04x %04x\n", descriptor[0], descriptor[1], descriptor[2], descriptor[3]); + present_ = descriptor[2] & 0x8000; + privilege_level_ = (descriptor[2] >> 13) & 3; + + // TODO: need to know more about the below. if(descriptor[2] & 0x1000) { - // Is segment descriptor. - present_ = descriptor[2] & 0x8000; - privilege_level_ = (descriptor[2] >> 13) & 3; - accessed_ = descriptor[2] & 0x100; - readable_ = descriptor[2] & 0x200; - conforming_ = descriptor[2] & 0x400; executable_ = descriptor[2] & 0x800; + + if(executable_) { + conforming_ = descriptor[2] & 0x400; + readable_ = descriptor[2] & 0x200; + } else { + // expand down = descriptor[2] & 0x400; + // writeable_ = descriptor[2] & 0x200; + } } else { assert(false); } } - // 286: - // 47...32 = 16-bit limit; - // 31 = P - // 30...29 = DPL - // 28 = S - // 27...24 = type; - // 23...00 = 4-bit base. - uint32_t to_linear(const uint32_t address) const { return base_ + address; } + uint32_t base() const { return base_; } + uint32_t limit() const { return limit_; } - uint32_t base() const { - return base_; + int privilege_level() const { + return privilege_level_; } private: @@ -71,8 +69,7 @@ private: uint32_t limit_; // TODO: permissions, type, etc. - bool present_; - uint8_t privilege_level_; + int privilege_level_; enum class Type { AvailableTaskStateSegment = 1, LDTDescriptor = 2, @@ -85,7 +82,8 @@ private: Reserved9 = 9, ReservedA = 10, ReservedB = 11, ReservedC = 12, ReservedD = 13, ReservedE = 14, ReservedF = 15, } type_; - bool accessed_; + + bool present_; bool readable_; bool conforming_; bool executable_; diff --git a/InstructionSets/x86/Interrupts.hpp b/InstructionSets/x86/Interrupts.hpp index acefa35f9..7f4eb0ed7 100644 --- a/InstructionSets/x86/Interrupts.hpp +++ b/InstructionSets/x86/Interrupts.hpp @@ -33,4 +33,17 @@ enum Interrupt { }; +struct ProtectedException { + Interrupt cause; + uint16_t code; + + // Code: + // b3–b15: IDT/GDT/LDT entry + // b2: 1 => in LDT; 0 => in GDT; + // b1: 1 => in IDT, ignore b2; 0 => use b2; + // b0: + // 1 => trigger was external to program code; + // 0 => trigger was caused by the instruction described by the CS:IP that is on the stack. +}; + } diff --git a/Machines/PCCompatible/SegmentedMemory.hpp b/Machines/PCCompatible/SegmentedMemory.hpp index 7e39e219c..572d9106c 100644 --- a/Machines/PCCompatible/SegmentedMemory.hpp +++ b/Machines/PCCompatible/SegmentedMemory.hpp @@ -19,6 +19,7 @@ namespace PCCompatible { +// TODO: the following need to apply linear memory semantics, including potential A20 wrapping. template struct ProgramFetcher { std::pair next_code( const Registers ®isters, @@ -26,11 +27,11 @@ template struct ProgramFetcher { LinearMemory &linear_memory ) const { const uint16_t ip = registers.ip(); - const uint32_t start = - segments.descriptors[InstructionSet::x86::Source::CS].to_linear(ip) & (linear_memory.MaxAddress - 1); + const auto &descriptor = segments.descriptors[InstructionSet::x86::Source::CS]; + const uint32_t start = descriptor.to_linear(ip) & (linear_memory.MaxAddress - 1); return std::make_pair( linear_memory.at(start), - std::min(linear_memory.MaxAddress - start, 0x10000 - ip) + std::min(linear_memory.MaxAddress - start, 1 + descriptor.limit() - ip) ); } @@ -38,10 +39,11 @@ template struct ProgramFetcher { const Segments &segments, LinearMemory &linear_memory ) const { - const auto base = segments.descriptors[InstructionSet::x86::Source::CS].base(); + const auto &descriptor = segments.descriptors[InstructionSet::x86::Source::CS]; + const auto base = descriptor.base() & (linear_memory.MaxAddress - 1); return std::make_pair( linear_memory.at(base), - std::min(0x1'0000, linear_memory.MaxAddress - base) + std::min(0x1'0000, 1 + descriptor.limit() - base) ); } diff --git a/Machines/PCCompatible/Segments.hpp b/Machines/PCCompatible/Segments.hpp index 16aa532a2..2c04b2082 100644 --- a/Machines/PCCompatible/Segments.hpp +++ b/Machines/PCCompatible/Segments.hpp @@ -43,12 +43,23 @@ public: break; case Mode::Protected286: { - // TODO: when to use the local table? Is this right? - assert(!(value & 0x8000)); - const auto &table = registers_.template get(); - const uint32_t table_address = table.base + value; + // Check privilege level. + const auto requested_privilege_level = value & 3; + if(requested_privilege_level < descriptors[Source::CS].privilege_level()) { + printf("TODO: privilege exception."); + } - // TODO: authorisation required, possibly. + // Check segment range. + const auto &table = + (value & 4) ? + registers_.template get() : + registers_.template get(); + const uint32_t table_address = table.base + (value & ~7); + if(table_address > table.base + table.limit - 8) { + printf("TODO: descriptor table overrun exception.\n"); + } + + // Get descriptor contents. using AccessType = InstructionSet::x86::AccessType; const uint32_t table_end = table.base + table.limit; const uint16_t entry[] = { @@ -57,7 +68,20 @@ public: memory_.template access(table_address + 4, table_end), memory_.template access(table_address + 6, table_end) }; + + // TODO: is this descriptor privilege within reach? + // TODO: is this an empty descriptor*? If so: exception! + // (* other than the 0 descriptor, which can be loaded to DS or ES) + // TODO: + // bool is_compatible(const Source reg) const { + // switch(reg) + // DS/ES: either readable code, or any sort of data + // SS: writeable data + // CS: any sort of code + // } + descriptors[segment].set(entry); + // TODO: set descriptor accessed bit in memory if it's a segment. } break; } }