mirror of
https://github.com/TomHarte/CLK.git
synced 2025-04-21 02:37:44 +00:00
Further flesh out descriptors: decode all bits, add printf warnings.
This commit is contained in:
parent
fa58cc05f3
commit
7721f74200
@ -21,85 +21,89 @@ struct DescriptorTablePointer {
|
||||
uint32_t base;
|
||||
};
|
||||
|
||||
struct DescriptorBounds {
|
||||
uint32_t begin, end;
|
||||
};
|
||||
|
||||
struct Descriptor {
|
||||
Descriptor() = default;
|
||||
|
||||
/// Creates a new descriptor with four 16-bit from a descriptor table.
|
||||
Descriptor(const uint16_t descriptor[4]) noexcept {
|
||||
// printf("%04x %04x %04x %04x", descriptor[0], descriptor[1], descriptor[2], descriptor[3]);
|
||||
|
||||
base_ = uint32_t(descriptor[1] | ((descriptor[2] & 0xff) << 16));
|
||||
limit_ = descriptor[0];
|
||||
type_ = descriptor[2] >> 8;
|
||||
|
||||
// printf(" -> %04x -> +%04x\n", base_, limit_);
|
||||
|
||||
present_ = descriptor[2] & 0x8000;
|
||||
privilege_level_ = (descriptor[2] >> 13) & 3;
|
||||
|
||||
// TODO: need to know more about the below.
|
||||
if(descriptor[2] & 0x1000) {
|
||||
executable_ = descriptor[2] & 0x800;
|
||||
|
||||
if(executable_) {
|
||||
conforming_ = descriptor[2] & 0x400;
|
||||
|
||||
readable_ = descriptor[2] & 0x200;
|
||||
writeable_ = true;
|
||||
} else {
|
||||
// expand down = descriptor[2] & 0x400;
|
||||
|
||||
writeable_ = descriptor[2] & 0x200;
|
||||
readable_ = true;
|
||||
}
|
||||
offset_ = descriptor[0];
|
||||
if(!code_or_data() || executable() || !expand_down()) {
|
||||
bounds_ = DescriptorBounds{ 0, offset_ };
|
||||
} else {
|
||||
assert(false);
|
||||
if(offset_ != std::numeric_limits<uint32_t>::max()) {
|
||||
bounds_ = DescriptorBounds{ uint32_t(offset_ + 1), std::numeric_limits<uint32_t>::max() };
|
||||
} else {
|
||||
// This descriptor is impossible to satisfy for reasons that aren't
|
||||
// properly expressed if the lower bound is incremented, so make it
|
||||
// impossible to satisfy in a more prosaic sense.
|
||||
bounds_ = DescriptorBounds{ 1, 0 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Rewrites this descriptor as a real-mode segment.
|
||||
void set_segment(const uint16_t segment) {
|
||||
base_ = uint32_t(segment) << 4;
|
||||
limit_ = 0xffff;
|
||||
|
||||
present_ = true;
|
||||
readable_ = writeable_ = true;
|
||||
bounds_ = DescriptorBounds{ 0x0000, 0xffff };
|
||||
offset_ = 0;
|
||||
type_ = 0b1'00'1'001'0; // Present, privilege level 0, expand-up writeable data, unaccessed.
|
||||
}
|
||||
|
||||
/// @returns The linear address for offest @c address within the segment described by this descriptor.
|
||||
uint32_t to_linear(const uint32_t address) const {
|
||||
return base_ + address;
|
||||
}
|
||||
|
||||
/// @returns The base of this segment descriptor.
|
||||
uint32_t base() const { return base_; }
|
||||
uint32_t limit() const { return limit_; }
|
||||
|
||||
int privilege_level() const {
|
||||
return privilege_level_;
|
||||
}
|
||||
/// @returns The bounds of this segment descriptor; will be either [0, limit] or [limit, INT_MAX] depending on descriptor type.
|
||||
/// Accesses must be `>= bounds().begin` and `<= bounds().end`.
|
||||
DescriptorBounds bounds() const { return bounds_; }
|
||||
|
||||
bool readable() const { return readable_; }
|
||||
bool writeable() const { return writeable_; }
|
||||
bool present() const { return type_ & 0x80; }
|
||||
int privilege_level() const { return (type_ >> 5) & 3; }
|
||||
bool code_or_data() const { return type_ & 0x10; }
|
||||
|
||||
private:
|
||||
uint32_t base_;
|
||||
uint32_t limit_;
|
||||
// TODO: permissions, type, etc.
|
||||
// If code_or_data():
|
||||
bool executable() const { return type_ & 0x08; }
|
||||
bool accessed() const { return type_ & 0x01; }
|
||||
|
||||
int privilege_level_;
|
||||
// If code_or_data() and not executable():
|
||||
bool expand_down() const { return type_ & 0x04; }
|
||||
bool writeable() const { return type_ & 0x02; }
|
||||
|
||||
// If code_or_data() and executable():
|
||||
bool conforming() const { return type_ & 0x04; }
|
||||
bool readable() const { return type_ & 0x02; }
|
||||
|
||||
// If not code_or_data():
|
||||
enum class Type {
|
||||
AvailableTaskStateSegment = 1,
|
||||
LDTDescriptor = 2,
|
||||
BusyTaskStateSegment = 3,
|
||||
|
||||
Invalid0 = 0, Invalid8 = 8,
|
||||
Invalid0 = 0, Invalid8 = 8,
|
||||
|
||||
Control4 = 4, Control5 = 5, Control6 = 6, Control7 = 7,
|
||||
Control4 = 4, Control5 = 5, Control6 = 6, Control7 = 7,
|
||||
|
||||
Reserved9 = 9, ReservedA = 10, ReservedB = 11, ReservedC = 12,
|
||||
ReservedD = 13, ReservedE = 14, ReservedF = 15,
|
||||
} type_;
|
||||
Reserved9 = 9, ReservedA = 10, ReservedB = 11, ReservedC = 12,
|
||||
ReservedD = 13, ReservedE = 14, ReservedF = 15,
|
||||
};
|
||||
Type type() const { return Type(type_ & 0x0f); }
|
||||
|
||||
bool present_;
|
||||
bool readable_;
|
||||
bool writeable_;
|
||||
bool conforming_;
|
||||
bool executable_;
|
||||
private:
|
||||
uint32_t base_;
|
||||
uint32_t offset_;
|
||||
DescriptorBounds bounds_;
|
||||
uint8_t type_;
|
||||
};
|
||||
|
||||
template <typename SegmentT>
|
||||
|
@ -596,12 +596,17 @@ template <
|
||||
context.memory.preauthorise_read(address, sizeof(uint16_t) * 2);
|
||||
context.memory.preauthorise_stack_write(sizeof(uint16_t) * 3);
|
||||
|
||||
if constexpr (ContextT::model >= Model::i80286) {
|
||||
if(context.registers.msw() & MachineStatus::ProtectedModeEnable) {
|
||||
// TODO: use the IDT, ummm, somehow.
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: I think (?) these are always physical addresses, not linear ones.
|
||||
// Indicate that when fetching.
|
||||
const uint16_t ip = context.linear_memory.template read<uint16_t>(address);
|
||||
const uint16_t cs = context.linear_memory.template read<uint16_t>(address + 2);
|
||||
// const uint16_t ip = context.linear_memory.template access<uint16_t, AccessType::PreauthorisedRead>(address);
|
||||
// const uint16_t cs = context.linear_memory.template access<uint16_t, AccessType::PreauthorisedRead>(address + 2);
|
||||
|
||||
const auto flags = context.flags.get();
|
||||
Primitive::push<uint16_t, true>(flags, context);
|
||||
|
@ -31,7 +31,7 @@ template <InstructionSet::x86::Model model> struct ProgramFetcher {
|
||||
const uint32_t start = descriptor.to_linear(ip) & (linear_memory.MaxAddress - 1);
|
||||
return std::make_pair(
|
||||
linear_memory.at(start),
|
||||
std::min<size_t>(linear_memory.MaxAddress - start, 1 + descriptor.limit() - ip)
|
||||
std::min<size_t>(linear_memory.MaxAddress - start, 1 + descriptor.bounds().end - ip)
|
||||
);
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ template <InstructionSet::x86::Model model> struct ProgramFetcher {
|
||||
const auto base = descriptor.base() & (linear_memory.MaxAddress - 1);
|
||||
return std::make_pair(
|
||||
linear_memory.at(base),
|
||||
std::min<size_t>(0x1'0000, 1 + descriptor.limit() - base)
|
||||
std::min<size_t>(0x1'0000, 1 + descriptor.bounds().end - base)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include "InstructionSets/x86/Mode.hpp"
|
||||
#include "InstructionSets/x86/Model.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace PCCompatible {
|
||||
|
||||
template <InstructionSet::x86::Model model>
|
||||
@ -46,7 +48,8 @@ public:
|
||||
// Check privilege level.
|
||||
const auto requested_privilege_level = value & 3;
|
||||
if(requested_privilege_level < descriptors[Source::CS].privilege_level()) {
|
||||
printf("TODO: privilege exception.");
|
||||
printf("TODO: privilege exception.\n");
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// Check segment range.
|
||||
@ -57,6 +60,7 @@ public:
|
||||
const uint32_t table_address = table.base + (value & ~7);
|
||||
if(table_address > table.base + table.limit - 8) {
|
||||
printf("TODO: descriptor table overrun exception.\n");
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// Get descriptor contents.
|
||||
@ -72,21 +76,40 @@ public:
|
||||
// printf("%s [%04x -> %08x]: ", InstructionSet::x86::to_string(segment, InstructionSet::x86::DataSize::Word).c_str(), value, table_address);
|
||||
const Descriptor incoming(entry);
|
||||
|
||||
// switch(segment) {
|
||||
// case Source::CS:
|
||||
// break;
|
||||
// }
|
||||
switch(segment) {
|
||||
case Source::DS:
|
||||
case Source::ES:
|
||||
if(!incoming.code_or_data() || (incoming.executable() && !incoming.readable())) {
|
||||
printf("TODO: throw for unreadable DS or ES source.\n");
|
||||
assert(false);
|
||||
}
|
||||
break;
|
||||
|
||||
case Source::SS:
|
||||
if(!incoming.code_or_data() || incoming.executable() || !incoming.writeable()) {
|
||||
printf("TODO: throw for invalid SS target.\n");
|
||||
assert(false);
|
||||
}
|
||||
break;
|
||||
|
||||
case Source::CS:
|
||||
if(!incoming.code_or_data() || !incoming.executable()) {
|
||||
// TODO: throw.
|
||||
printf("TODO: throw for illegal CS destination.\n");
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if(!incoming.code_or_data()) {
|
||||
printf("TODO: handle jump to system descriptor of type %d\n", int(incoming.type()));
|
||||
assert(false);
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
// 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] = incoming;
|
||||
// TODO: set descriptor accessed bit in memory if it's a segment.
|
||||
|
Loading…
x
Reference in New Issue
Block a user