feat: S3 sleep, PDF viewer graphics rendering, live user environment
This commit is contained in:
@@ -17,14 +17,22 @@ using namespace Kt;
|
||||
namespace Hal {
|
||||
namespace AML {
|
||||
|
||||
// ── Global instance ─────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Global instance
|
||||
|
||||
// ============================================================================
|
||||
static Interpreter g_interpreter;
|
||||
|
||||
Interpreter& GetInterpreter() {
|
||||
return g_interpreter;
|
||||
}
|
||||
|
||||
// ── Helper: is a byte a lead name character? ────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Helper: is a byte a lead name character?
|
||||
|
||||
// ============================================================================
|
||||
static bool IsLeadNameChar(uint8_t c) {
|
||||
return (c >= 'A' && c <= 'Z') || c == '_';
|
||||
}
|
||||
@@ -33,11 +41,19 @@ namespace Hal {
|
||||
return IsLeadNameChar(c) || (c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
// ── Constructor ─────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Constructor
|
||||
|
||||
// ============================================================================
|
||||
Interpreter::Interpreter()
|
||||
: m_dsdt(nullptr), m_dsdtLength(0), m_initialized(false) {}
|
||||
|
||||
// ── PkgLength decoding ──────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// PkgLength decoding
|
||||
|
||||
// ============================================================================
|
||||
uint32_t Interpreter::DecodePkgLength(const uint8_t* aml, uint32_t* pos) {
|
||||
uint8_t lead = aml[*pos];
|
||||
uint32_t byteCount = (lead >> 6) & 0x03;
|
||||
@@ -58,7 +74,11 @@ namespace Hal {
|
||||
return length;
|
||||
}
|
||||
|
||||
// ── Integer decoding (data objects) ─────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Integer decoding (data objects)
|
||||
|
||||
// ============================================================================
|
||||
uint64_t Interpreter::DecodeInteger(const uint8_t* aml, uint32_t* pos) {
|
||||
uint8_t op = aml[*pos];
|
||||
|
||||
@@ -107,7 +127,11 @@ namespace Hal {
|
||||
}
|
||||
}
|
||||
|
||||
// ── NameSeg reading ─────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// NameSeg reading
|
||||
|
||||
// ============================================================================
|
||||
void Interpreter::ReadNameSeg(const uint8_t* aml, uint32_t* pos, char* outSeg) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
outSeg[i] = (char)aml[*pos + i];
|
||||
@@ -116,7 +140,11 @@ namespace Hal {
|
||||
*pos += 4;
|
||||
}
|
||||
|
||||
// ── NameString reading ──────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// NameString reading
|
||||
|
||||
// ============================================================================
|
||||
// Reads a NameString (which may include root prefix, parent prefixes,
|
||||
// dual/multi name prefix) and produces an absolute path.
|
||||
int Interpreter::ReadNameString(const uint8_t* aml, uint32_t* pos, int32_t scopeNode,
|
||||
@@ -195,7 +223,11 @@ namespace Hal {
|
||||
return pathPos;
|
||||
}
|
||||
|
||||
// ── LoadTable ───────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// LoadTable
|
||||
|
||||
// ============================================================================
|
||||
bool Interpreter::LoadTable(void* tableData) {
|
||||
auto* header = (ACPI::CommonSDTHeader*)tableData;
|
||||
|
||||
@@ -231,7 +263,11 @@ namespace Hal {
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── ParseBlock ──────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// ParseBlock
|
||||
|
||||
// ============================================================================
|
||||
// Parse a block of AML opcodes, creating namespace objects.
|
||||
bool Interpreter::ParseBlock(const uint8_t* aml, uint32_t offset, uint32_t endOffset,
|
||||
int32_t scopeNode) {
|
||||
@@ -332,7 +368,11 @@ namespace Hal {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── ParseNamedObject ────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// ParseNamedObject
|
||||
|
||||
// ============================================================================
|
||||
bool Interpreter::ParseNamedObject(const uint8_t* aml, uint32_t* pos, uint32_t endOffset,
|
||||
int32_t scopeNode) {
|
||||
uint8_t op = aml[*pos];
|
||||
@@ -484,7 +524,11 @@ namespace Hal {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── ParseExtendedOp ─────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// ParseExtendedOp
|
||||
|
||||
// ============================================================================
|
||||
bool Interpreter::ParseExtendedOp(const uint8_t* aml, uint32_t* pos, uint32_t endOffset,
|
||||
int32_t scopeNode) {
|
||||
(*pos)++; // skip ExtOpPrefix
|
||||
@@ -743,7 +787,11 @@ namespace Hal {
|
||||
}
|
||||
}
|
||||
|
||||
// ── EvaluateObject ──────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// EvaluateObject
|
||||
|
||||
// ============================================================================
|
||||
bool Interpreter::EvaluateObject(const char* path, Object& result) {
|
||||
int32_t node = m_ns.FindNode(path);
|
||||
if (node < 0) return false;
|
||||
@@ -772,7 +820,11 @@ namespace Hal {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── EvaluateMethod ──────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// EvaluateMethod
|
||||
|
||||
// ============================================================================
|
||||
bool Interpreter::EvaluateMethod(const char* path, const Object* args, int argCount,
|
||||
Object& result) {
|
||||
int32_t node = m_ns.FindNode(path);
|
||||
@@ -812,7 +864,11 @@ namespace Hal {
|
||||
return ok;
|
||||
}
|
||||
|
||||
// ── ExecuteBlock ────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// ExecuteBlock
|
||||
|
||||
// ============================================================================
|
||||
bool Interpreter::ExecuteBlock(ExecContext& ctx, uint32_t offset, uint32_t endOffset) {
|
||||
uint32_t pos = offset;
|
||||
|
||||
@@ -824,7 +880,11 @@ namespace Hal {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── ExecuteOpcode ───────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// ExecuteOpcode
|
||||
|
||||
// ============================================================================
|
||||
bool Interpreter::ExecuteOpcode(ExecContext& ctx, uint32_t* pos, uint32_t endOffset) {
|
||||
if (*pos >= endOffset) return true;
|
||||
|
||||
@@ -1015,7 +1075,11 @@ namespace Hal {
|
||||
return EvalTerm(ctx, pos, endOffset, discard);
|
||||
}
|
||||
|
||||
// ── EvalTerm ────────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// EvalTerm
|
||||
|
||||
// ============================================================================
|
||||
// Evaluate an AML term that produces a value.
|
||||
bool Interpreter::EvalTerm(ExecContext& ctx, uint32_t* pos, uint32_t endOffset,
|
||||
Object& result) {
|
||||
@@ -1412,7 +1476,11 @@ namespace Hal {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── EvalTarget ──────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// EvalTarget
|
||||
|
||||
// ============================================================================
|
||||
bool Interpreter::EvalTarget(ExecContext& ctx, uint32_t* pos,
|
||||
int32_t& nodeIndex, bool& isLocal, int& localIdx,
|
||||
bool& isArg, int& argIdx) {
|
||||
@@ -1464,7 +1532,11 @@ namespace Hal {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── StoreToTarget ───────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// StoreToTarget
|
||||
|
||||
// ============================================================================
|
||||
void Interpreter::StoreToTarget(ExecContext& ctx, const Object& value,
|
||||
int32_t nodeIndex, bool isLocal, int localIdx,
|
||||
bool isArg, int argIdx) {
|
||||
@@ -1484,7 +1556,11 @@ namespace Hal {
|
||||
}
|
||||
}
|
||||
|
||||
// ── ReadField ───────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// ReadField
|
||||
|
||||
// ============================================================================
|
||||
bool Interpreter::ReadField(int32_t nodeIndex, uint64_t& value) {
|
||||
auto* node = m_ns.GetNode(nodeIndex);
|
||||
if (!node || node->Obj.Type != ObjectType::Field) return false;
|
||||
@@ -1505,7 +1581,11 @@ namespace Hal {
|
||||
return ReadRegion(regionNode->Obj.Region.Space, byteAddr, bitLen + bitShift, value);
|
||||
}
|
||||
|
||||
// ── WriteField ──────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// WriteField
|
||||
|
||||
// ============================================================================
|
||||
bool Interpreter::WriteField(int32_t nodeIndex, uint64_t value) {
|
||||
auto* node = m_ns.GetNode(nodeIndex);
|
||||
if (!node || node->Obj.Type != ObjectType::Field) return false;
|
||||
@@ -1534,7 +1614,11 @@ namespace Hal {
|
||||
return WriteRegion(regionNode->Obj.Region.Space, byteAddr, bitLen + bitShift, value);
|
||||
}
|
||||
|
||||
// ── ReadRegion ──────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// ReadRegion
|
||||
|
||||
// ============================================================================
|
||||
bool Interpreter::ReadRegion(RegionSpace space, uint64_t address, uint32_t bitWidth,
|
||||
uint64_t& value) {
|
||||
value = 0;
|
||||
@@ -1576,7 +1660,11 @@ namespace Hal {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ── WriteRegion ─────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// WriteRegion
|
||||
|
||||
// ============================================================================
|
||||
bool Interpreter::WriteRegion(RegionSpace space, uint64_t address, uint32_t bitWidth,
|
||||
uint64_t value) {
|
||||
uint32_t accessBytes = (bitWidth + 7) / 8;
|
||||
|
||||
@@ -12,7 +12,11 @@
|
||||
namespace Hal {
|
||||
namespace AML {
|
||||
|
||||
// ── Extended AML Opcodes ────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Extended AML Opcodes
|
||||
|
||||
// ============================================================================
|
||||
// Single-byte opcodes
|
||||
static constexpr uint8_t ZeroOp = 0x00;
|
||||
static constexpr uint8_t OneOp = 0x01;
|
||||
@@ -93,11 +97,19 @@ namespace Hal {
|
||||
static constexpr uint8_t ToHexStringOp = 0x98;
|
||||
static constexpr uint8_t ToDecimalStringOp = 0x97;
|
||||
|
||||
// ── Interpreter Configuration ───────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Interpreter Configuration
|
||||
|
||||
// ============================================================================
|
||||
static constexpr int MaxCallDepth = 16;
|
||||
static constexpr int MaxLoopIterations = 1024;
|
||||
|
||||
// ── Interpreter ─────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Interpreter
|
||||
|
||||
// ============================================================================
|
||||
class Interpreter {
|
||||
public:
|
||||
Interpreter();
|
||||
@@ -127,7 +139,9 @@ namespace Hal {
|
||||
bool IsInitialized() const { return m_initialized; }
|
||||
|
||||
private:
|
||||
// ── Parsing (table load) ────────────────────────────────────
|
||||
// ============================================================================
|
||||
// Parsing (table load)
|
||||
// ============================================================================
|
||||
bool ParseBlock(const uint8_t* aml, uint32_t offset, uint32_t endOffset,
|
||||
int32_t scopeNode);
|
||||
|
||||
@@ -137,7 +151,11 @@ namespace Hal {
|
||||
bool ParseExtendedOp(const uint8_t* aml, uint32_t* pos, uint32_t endOffset,
|
||||
int32_t scopeNode);
|
||||
|
||||
// ── Name resolution ─────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Name resolution
|
||||
|
||||
// ============================================================================
|
||||
// Read a NameString from AML and produce an absolute path.
|
||||
// Advances *pos past the name.
|
||||
int ReadNameString(const uint8_t* aml, uint32_t* pos, int32_t scopeNode,
|
||||
@@ -146,11 +164,19 @@ namespace Hal {
|
||||
// Read a single 4-char NameSeg from AML. Advances *pos.
|
||||
void ReadNameSeg(const uint8_t* aml, uint32_t* pos, char* outSeg);
|
||||
|
||||
// ── Value decoding ──────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Value decoding
|
||||
|
||||
// ============================================================================
|
||||
uint32_t DecodePkgLength(const uint8_t* aml, uint32_t* pos);
|
||||
uint64_t DecodeInteger(const uint8_t* aml, uint32_t* pos);
|
||||
|
||||
// ── Method execution ────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Method execution
|
||||
|
||||
// ============================================================================
|
||||
struct ExecContext {
|
||||
const uint8_t* Aml;
|
||||
uint32_t AmlBase; // start of the block within the table
|
||||
@@ -182,18 +208,30 @@ namespace Hal {
|
||||
int32_t nodeIndex, bool isLocal, int localIdx,
|
||||
bool isArg, int argIdx);
|
||||
|
||||
// ── Field I/O ───────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Field I/O
|
||||
|
||||
// ============================================================================
|
||||
bool ReadRegion(RegionSpace space, uint64_t address, uint32_t bitWidth, uint64_t& value);
|
||||
bool WriteRegion(RegionSpace space, uint64_t address, uint32_t bitWidth, uint64_t value);
|
||||
|
||||
// ── State ───────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// State
|
||||
|
||||
// ============================================================================
|
||||
Namespace m_ns;
|
||||
const uint8_t* m_dsdt;
|
||||
uint32_t m_dsdtLength;
|
||||
bool m_initialized;
|
||||
};
|
||||
|
||||
// ── Global interpreter instance ─────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Global interpreter instance
|
||||
|
||||
// ============================================================================
|
||||
Interpreter& GetInterpreter();
|
||||
|
||||
};
|
||||
|
||||
@@ -11,7 +11,11 @@
|
||||
namespace Hal {
|
||||
namespace AML {
|
||||
|
||||
// ── AML Object Types ────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// AML Object Types
|
||||
|
||||
// ============================================================================
|
||||
enum class ObjectType : uint8_t {
|
||||
None = 0,
|
||||
Integer,
|
||||
@@ -29,7 +33,11 @@ namespace Hal {
|
||||
BufferField,
|
||||
};
|
||||
|
||||
// ── Region address spaces (OperationRegion) ─────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Region address spaces (OperationRegion)
|
||||
|
||||
// ============================================================================
|
||||
enum class RegionSpace : uint8_t {
|
||||
SystemMemory = 0x00,
|
||||
SystemIO = 0x01,
|
||||
@@ -40,7 +48,11 @@ namespace Hal {
|
||||
PciBarTarget = 0x06,
|
||||
};
|
||||
|
||||
// ── Constants ───────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Constants
|
||||
|
||||
// ============================================================================
|
||||
static constexpr int MaxNameSegLen = 4;
|
||||
static constexpr int MaxPathDepth = 16;
|
||||
static constexpr int MaxChildren = 32;
|
||||
@@ -51,7 +63,11 @@ namespace Hal {
|
||||
static constexpr int MaxMethodLocals = 8;
|
||||
static constexpr int MaxNamespaceNodes = 256;
|
||||
|
||||
// ── AML Object ──────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// AML Object
|
||||
|
||||
// ============================================================================
|
||||
// Tagged union representing any AML value. Kept small for kernel use.
|
||||
struct Object {
|
||||
ObjectType Type = ObjectType::None;
|
||||
@@ -99,7 +115,11 @@ namespace Hal {
|
||||
Object() : Type(ObjectType::None), Integer(0) {}
|
||||
};
|
||||
|
||||
// ── Namespace Node ──────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Namespace Node
|
||||
|
||||
// ============================================================================
|
||||
// Each node has a 4-char name segment and an associated object.
|
||||
struct NamespaceNode {
|
||||
char Name[MaxNameSegLen + 1]; // null-terminated 4-char segment
|
||||
@@ -118,7 +138,11 @@ namespace Hal {
|
||||
}
|
||||
};
|
||||
|
||||
// ── Namespace ───────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Namespace
|
||||
|
||||
// ============================================================================
|
||||
// Flat array of nodes forming a tree via parent/child indices.
|
||||
class Namespace {
|
||||
public:
|
||||
|
||||
@@ -15,7 +15,11 @@ using namespace Kt;
|
||||
namespace Hal {
|
||||
namespace AML {
|
||||
|
||||
// ── Legacy S5 extraction (brute-force scan) ─────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Legacy S5 extraction (brute-force scan)
|
||||
|
||||
// ============================================================================
|
||||
// Kept for fast S5 extraction during early boot before the full
|
||||
// interpreter is loaded.
|
||||
|
||||
@@ -158,7 +162,11 @@ namespace Hal {
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── Generalized brute-force sleep state scanner ──────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Generalized brute-force sleep state scanner
|
||||
|
||||
// ============================================================================
|
||||
SleepObject FindSleepState(void* dsdtData, int state) {
|
||||
SleepObject result{};
|
||||
result.Valid = false;
|
||||
@@ -226,7 +234,11 @@ namespace Hal {
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── Full interpreter initialization ─────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Full interpreter initialization
|
||||
|
||||
// ============================================================================
|
||||
void InitializeInterpreter(void* dsdtData) {
|
||||
auto& interp = GetInterpreter();
|
||||
if (!interp.LoadTable(dsdtData)) {
|
||||
|
||||
@@ -9,14 +9,22 @@
|
||||
namespace Hal {
|
||||
namespace AML {
|
||||
|
||||
// ── Small Resource Tags (bits 6:3 of the tag byte) ──────────────
|
||||
// ============================================================================
|
||||
|
||||
// Small Resource Tags (bits 6:3 of the tag byte)
|
||||
|
||||
// ============================================================================
|
||||
static constexpr uint8_t SmallIrqTag = 0x04; // IRQ descriptor
|
||||
static constexpr uint8_t SmallDmaTag = 0x05; // DMA descriptor
|
||||
static constexpr uint8_t SmallIoPortTag = 0x08; // I/O port descriptor
|
||||
static constexpr uint8_t SmallFixedIoTag = 0x09; // Fixed I/O port descriptor
|
||||
static constexpr uint8_t SmallEndTag = 0x0F; // End tag
|
||||
|
||||
// ── Large Resource Tags (byte following the large tag prefix) ───
|
||||
// ============================================================================
|
||||
|
||||
// Large Resource Tags (byte following the large tag prefix)
|
||||
|
||||
// ============================================================================
|
||||
static constexpr uint8_t LargeMemory24Tag = 0x01;
|
||||
static constexpr uint8_t LargeVendorTag = 0x04;
|
||||
static constexpr uint8_t LargeMemory32Tag = 0x05;
|
||||
@@ -63,7 +71,9 @@ namespace Hal {
|
||||
break;
|
||||
|
||||
if (tag & 0x80) {
|
||||
// ── Large resource descriptor ───────────────────────
|
||||
// ============================================================================
|
||||
// Large resource descriptor
|
||||
// ============================================================================
|
||||
uint8_t largeType = tag & 0x7F;
|
||||
if (pos + 3 > length) break;
|
||||
uint16_t resLen = Read16(&data[pos + 1]);
|
||||
@@ -154,7 +164,9 @@ namespace Hal {
|
||||
|
||||
pos = dataEnd;
|
||||
} else {
|
||||
// ── Small resource descriptor ───────────────────────
|
||||
// ============================================================================
|
||||
// Small resource descriptor
|
||||
// ============================================================================
|
||||
uint8_t smallType = (tag >> 3) & 0x0F;
|
||||
uint8_t resLen = tag & 0x07;
|
||||
uint32_t dataStart = pos + 1;
|
||||
|
||||
@@ -10,7 +10,11 @@
|
||||
namespace Hal {
|
||||
namespace AML {
|
||||
|
||||
// ── Resource Types ──────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Resource Types
|
||||
|
||||
// ============================================================================
|
||||
enum class ResourceType : uint8_t {
|
||||
None = 0,
|
||||
Irq,
|
||||
@@ -27,7 +31,11 @@ namespace Hal {
|
||||
GpioConnection,
|
||||
};
|
||||
|
||||
// ── Single Resource Descriptor ──────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Single Resource Descriptor
|
||||
|
||||
// ============================================================================
|
||||
struct ResourceDescriptor {
|
||||
ResourceType Type;
|
||||
|
||||
@@ -82,7 +90,11 @@ namespace Hal {
|
||||
}
|
||||
};
|
||||
|
||||
// ── Parsed Resource List ────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Parsed Resource List
|
||||
|
||||
// ============================================================================
|
||||
static constexpr int MaxResources = 16;
|
||||
|
||||
struct ResourceList {
|
||||
|
||||
@@ -16,7 +16,11 @@ using namespace Kt;
|
||||
namespace Hal {
|
||||
namespace AcpiDevices {
|
||||
|
||||
// ── EISAID decoding ─────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// EISAID decoding
|
||||
|
||||
// ============================================================================
|
||||
// ACPI encodes PNP IDs as compressed 32-bit EISAIDs.
|
||||
static void DecodeEisaId(uint32_t id, char* out) {
|
||||
// EISA ID encoding:
|
||||
@@ -35,7 +39,11 @@ namespace Hal {
|
||||
out[7] = '\0';
|
||||
}
|
||||
|
||||
// ── String comparison ───────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// String comparison
|
||||
|
||||
// ============================================================================
|
||||
static bool StrEqual(const char* a, const char* b) {
|
||||
while (*a && *b) {
|
||||
if (*a != *b) return false;
|
||||
@@ -53,7 +61,11 @@ namespace Hal {
|
||||
dst[i] = '\0';
|
||||
}
|
||||
|
||||
// ── DeviceList methods ──────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// DeviceList methods
|
||||
|
||||
// ============================================================================
|
||||
const DeviceInfo* DeviceList::FindByHid(const char* hid) const {
|
||||
for (int i = 0; i < Count; i++) {
|
||||
if (StrEqual(Devices[i].HardwareId, hid))
|
||||
@@ -70,7 +82,11 @@ namespace Hal {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ── EvaluateSta ─────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// EvaluateSta
|
||||
|
||||
// ============================================================================
|
||||
uint32_t EvaluateSta(int32_t deviceNodeIndex) {
|
||||
auto& interp = AML::GetInterpreter();
|
||||
auto& ns = interp.GetNamespace();
|
||||
@@ -107,7 +123,11 @@ namespace Hal {
|
||||
return STA_DEFAULT;
|
||||
}
|
||||
|
||||
// ── EvaluateAdr ─────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// EvaluateAdr
|
||||
|
||||
// ============================================================================
|
||||
uint64_t EvaluateAdr(int32_t deviceNodeIndex) {
|
||||
auto& interp = AML::GetInterpreter();
|
||||
auto& ns = interp.GetNamespace();
|
||||
@@ -143,7 +163,11 @@ namespace Hal {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ── EvaluateHid ─────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// EvaluateHid
|
||||
|
||||
// ============================================================================
|
||||
bool EvaluateHid(int32_t deviceNodeIndex, char* outHid, int maxLen) {
|
||||
auto& interp = AML::GetInterpreter();
|
||||
auto& ns = interp.GetNamespace();
|
||||
@@ -182,7 +206,11 @@ namespace Hal {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ── EvaluateUid ─────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// EvaluateUid
|
||||
|
||||
// ============================================================================
|
||||
bool EvaluateUid(int32_t deviceNodeIndex, char* outUid, int maxLen) {
|
||||
auto& interp = AML::GetInterpreter();
|
||||
auto& ns = interp.GetNamespace();
|
||||
@@ -237,7 +265,11 @@ namespace Hal {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ── EvaluateCrs ─────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// EvaluateCrs
|
||||
|
||||
// ============================================================================
|
||||
bool EvaluateCrs(int32_t deviceNodeIndex, AML::ResourceList& result) {
|
||||
auto& interp = AML::GetInterpreter();
|
||||
auto& ns = interp.GetNamespace();
|
||||
@@ -268,7 +300,11 @@ namespace Hal {
|
||||
return AML::ParseResourceTemplate(crsResult.Buffer.Data, crsResult.Buffer.Length, result);
|
||||
}
|
||||
|
||||
// ── EnumerateAll ────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// EnumerateAll
|
||||
|
||||
// ============================================================================
|
||||
void EnumerateAll(DeviceList& result) {
|
||||
auto& interp = AML::GetInterpreter();
|
||||
if (!interp.IsInitialized()) return;
|
||||
@@ -323,7 +359,11 @@ namespace Hal {
|
||||
}
|
||||
}
|
||||
|
||||
// ── GetSleepState ───────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// GetSleepState
|
||||
|
||||
// ============================================================================
|
||||
SleepState GetSleepState(int state) {
|
||||
SleepState result{};
|
||||
result.Valid = false;
|
||||
|
||||
@@ -11,7 +11,11 @@
|
||||
namespace Hal {
|
||||
namespace AcpiDevices {
|
||||
|
||||
// ── Device Status Flags (_STA) ──────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Device Status Flags (_STA)
|
||||
|
||||
// ============================================================================
|
||||
static constexpr uint32_t STA_PRESENT = (1 << 0);
|
||||
static constexpr uint32_t STA_ENABLED = (1 << 1);
|
||||
static constexpr uint32_t STA_VISIBLE = (1 << 2);
|
||||
@@ -21,7 +25,11 @@ namespace Hal {
|
||||
// Default _STA when no _STA method exists: present + enabled + visible + functional
|
||||
static constexpr uint32_t STA_DEFAULT = 0x0F;
|
||||
|
||||
// ── Device Info ─────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Device Info
|
||||
|
||||
// ============================================================================
|
||||
struct DeviceInfo {
|
||||
char Path[128]; // full namespace path
|
||||
char HardwareId[16]; // _HID value (e.g. "PNP0A03", "ACPI0001")
|
||||
@@ -47,8 +55,11 @@ namespace Hal {
|
||||
const DeviceInfo* FindByPath(const char* path) const;
|
||||
};
|
||||
|
||||
// ── Enumeration API ─────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Enumeration API
|
||||
|
||||
// ============================================================================
|
||||
// Enumerate all ACPI devices in the namespace.
|
||||
// The interpreter must be initialized first (LoadTable called).
|
||||
void EnumerateAll(DeviceList& result);
|
||||
@@ -69,7 +80,11 @@ namespace Hal {
|
||||
// Evaluate _CRS (Current Resource Settings) for a device node.
|
||||
bool EvaluateCrs(int32_t deviceNodeIndex, AML::ResourceList& result);
|
||||
|
||||
// ── Well-known HIDs ─────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Well-known HIDs
|
||||
|
||||
// ============================================================================
|
||||
// PCI Host Bridge
|
||||
static constexpr const char* HID_PCI_HOST = "PNP0A03";
|
||||
static constexpr const char* HID_PCIE_HOST = "PNP0A08";
|
||||
@@ -98,7 +113,11 @@ namespace Hal {
|
||||
// ACPI AC Adapter
|
||||
static constexpr const char* HID_AC_ADAPTER = "ACPI0003";
|
||||
|
||||
// ── Sleep State Support ─────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Sleep State Support
|
||||
|
||||
// ============================================================================
|
||||
// Extract sleep state values from namespace objects (\_S0_ through \_S5_).
|
||||
struct SleepState {
|
||||
uint16_t SLP_TYPa;
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* AcpiEvents.cpp
|
||||
* ACPI fixed event handling (SCI interrupt, power button, etc.)
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "AcpiEvents.hpp"
|
||||
#include <ACPI/AcpiSleep.hpp>
|
||||
#include <Io/IoPort.hpp>
|
||||
#include <Hal/Apic/Apic.hpp>
|
||||
#include <Hal/Apic/IoApic.hpp>
|
||||
#include <Hal/Apic/Interrupts.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
|
||||
using namespace Kt;
|
||||
|
||||
namespace Hal {
|
||||
namespace AcpiEvents {
|
||||
|
||||
// ============================================================================
|
||||
// State
|
||||
// ============================================================================
|
||||
static uint32_t g_pm1aEventBlock = 0;
|
||||
static uint32_t g_pm1bEventBlock = 0;
|
||||
static uint8_t g_pm1EventLength = 0;
|
||||
static uint16_t g_sciIrq = 0;
|
||||
static bool g_initialized = false;
|
||||
|
||||
// ============================================================================
|
||||
// PM1 register helpers
|
||||
// ============================================================================
|
||||
static uint16_t ReadPM1Status() {
|
||||
uint16_t sts = 0;
|
||||
if (g_pm1aEventBlock != 0)
|
||||
sts |= Io::In16((uint16_t)g_pm1aEventBlock);
|
||||
if (g_pm1bEventBlock != 0)
|
||||
sts |= Io::In16((uint16_t)g_pm1bEventBlock);
|
||||
return sts;
|
||||
}
|
||||
|
||||
static void ClearPM1StatusBits(uint16_t bits) {
|
||||
// Write-1-to-clear semantics
|
||||
if (g_pm1aEventBlock != 0)
|
||||
Io::Out16(bits, (uint16_t)g_pm1aEventBlock);
|
||||
if (g_pm1bEventBlock != 0)
|
||||
Io::Out16(bits, (uint16_t)g_pm1bEventBlock);
|
||||
}
|
||||
|
||||
static void EnablePM1Events(uint16_t mask) {
|
||||
uint16_t enableOffset = g_pm1EventLength / 2;
|
||||
if (enableOffset == 0) return;
|
||||
|
||||
if (g_pm1aEventBlock != 0) {
|
||||
uint16_t en = Io::In16((uint16_t)(g_pm1aEventBlock + enableOffset));
|
||||
en |= mask;
|
||||
Io::Out16(en, (uint16_t)(g_pm1aEventBlock + enableOffset));
|
||||
}
|
||||
if (g_pm1bEventBlock != 0) {
|
||||
uint16_t en = Io::In16((uint16_t)(g_pm1bEventBlock + enableOffset));
|
||||
en |= mask;
|
||||
Io::Out16(en, (uint16_t)(g_pm1bEventBlock + enableOffset));
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SCI Interrupt Handler
|
||||
// ============================================================================
|
||||
static void SciHandler(uint8_t irq) {
|
||||
uint16_t sts = ReadPM1Status();
|
||||
|
||||
if (sts & AcpiSleep::PM1_PWRBTN_STS) {
|
||||
ClearPM1StatusBits(AcpiSleep::PM1_PWRBTN_STS);
|
||||
KernelLogStream(INFO, "ACPI") << "Power button pressed";
|
||||
}
|
||||
|
||||
if (sts & AcpiSleep::PM1_SLPBTN_STS) {
|
||||
ClearPM1StatusBits(AcpiSleep::PM1_SLPBTN_STS);
|
||||
KernelLogStream(INFO, "ACPI") << "Sleep button pressed";
|
||||
}
|
||||
|
||||
// Send EOI
|
||||
LocalApic::SendEOI();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
// Initialize
|
||||
|
||||
// ============================================================================
|
||||
void Initialize(const FADT::ParsedFADT& fadt) {
|
||||
g_pm1aEventBlock = fadt.PM1aEventBlock;
|
||||
g_pm1bEventBlock = fadt.PM1bEventBlock;
|
||||
g_pm1EventLength = fadt.PM1EventLength;
|
||||
g_sciIrq = fadt.SCI_Interrupt;
|
||||
|
||||
if (g_pm1aEventBlock == 0) {
|
||||
KernelLogStream(ERROR, "ACPI") << "No PM1a event block - ACPI events unavailable";
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear any pending status bits
|
||||
ClearPM1StatusBits(
|
||||
AcpiSleep::PM1_PWRBTN_STS | AcpiSleep::PM1_SLPBTN_STS |
|
||||
AcpiSleep::PM1_WAK_STS | AcpiSleep::PM1_TMR_STS |
|
||||
AcpiSleep::PM1_GBL_STS | AcpiSleep::PM1_RTC_STS |
|
||||
AcpiSleep::PM1_BM_STS);
|
||||
|
||||
// Enable power button event
|
||||
EnablePM1Events(AcpiSleep::PM1_PWRBTN_EN);
|
||||
|
||||
// Route SCI to an IRQ vector. The SCI is level-triggered,
|
||||
// active-low (ACPI spec requirement). The MADT may have an
|
||||
// Interrupt Source Override for this IRQ that already sets
|
||||
// these flags; RouteIrq applies overrides automatically.
|
||||
uint8_t sciVector = IRQ_VECTOR_BASE + (uint8_t)g_sciIrq;
|
||||
uint8_t bspApicId = (uint8_t)LocalApic::GetId();
|
||||
|
||||
// Register the handler before routing to avoid missing events
|
||||
RegisterIrqHandler((uint8_t)g_sciIrq, SciHandler);
|
||||
|
||||
// Route via IoApic (applies MADT overrides for polarity/trigger)
|
||||
IoApic::RouteIrq((uint8_t)g_sciIrq, sciVector, bspApicId);
|
||||
|
||||
g_initialized = true;
|
||||
|
||||
KernelLogStream(OK, "ACPI") << "ACPI events initialized (SCI IRQ "
|
||||
<< base::dec << (uint64_t)g_sciIrq << ", vector "
|
||||
<< (uint64_t)sciVector << ")";
|
||||
}
|
||||
|
||||
void Reinitialize() {
|
||||
if (!g_initialized) return;
|
||||
|
||||
// Clear pending status and re-enable power button event
|
||||
ClearPM1StatusBits(
|
||||
AcpiSleep::PM1_PWRBTN_STS | AcpiSleep::PM1_SLPBTN_STS);
|
||||
EnablePM1Events(AcpiSleep::PM1_PWRBTN_EN);
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* AcpiEvents.hpp
|
||||
* ACPI fixed event handling (SCI interrupt, power button, etc.)
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <ACPI/ACPI.hpp>
|
||||
#include <ACPI/FADT.hpp>
|
||||
|
||||
namespace Hal {
|
||||
namespace AcpiEvents {
|
||||
|
||||
// Initialize ACPI event handling: route the SCI interrupt,
|
||||
// register the handler, and enable fixed events (power button).
|
||||
// Must be called after APIC and FADT initialization.
|
||||
void Initialize(const FADT::ParsedFADT& fadt);
|
||||
|
||||
// Re-enable ACPI events after S3 resume.
|
||||
void Reinitialize();
|
||||
};
|
||||
};
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "AcpiShutdown.hpp"
|
||||
#include "AcpiSleep.hpp"
|
||||
#include "AcpiEvents.hpp"
|
||||
#include <ACPI/FADT.hpp>
|
||||
#include <ACPI/AML/AmlParser.hpp>
|
||||
#include <ACPI/AML/AmlInterpreter.hpp>
|
||||
@@ -67,6 +68,9 @@ namespace Hal {
|
||||
|
||||
// Initialize S3 suspend support (reads FACS and \_S3_ from namespace)
|
||||
AcpiSleep::Initialize(xsdt);
|
||||
|
||||
// Initialize ACPI event handling (SCI interrupt, power button)
|
||||
AcpiEvents::Initialize(fadt);
|
||||
}
|
||||
|
||||
bool IsAvailable() {
|
||||
@@ -78,6 +82,19 @@ namespace Hal {
|
||||
|
||||
KernelLogStream(INFO, "ACPI") << "Performing ACPI S5 shutdown...";
|
||||
|
||||
// Evaluate _PTS(5) — Prepare To Sleep (S5 = soft-off)
|
||||
auto& interp = AML::GetInterpreter();
|
||||
if (interp.IsInitialized()) {
|
||||
int32_t node = interp.GetNamespace().FindNode("\\_PTS");
|
||||
if (node >= 0) {
|
||||
AML::Object arg{};
|
||||
arg.Type = AML::ObjectType::Integer;
|
||||
arg.Integer = 5;
|
||||
AML::Object result{};
|
||||
interp.EvaluateMethod("\\_PTS", &arg, 1, result);
|
||||
}
|
||||
}
|
||||
|
||||
asm volatile("cli");
|
||||
|
||||
// Phase 1: Write SLP_TYP to PM1x_CNT without SLP_EN,
|
||||
|
||||
+259
-94
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include "AcpiSleep.hpp"
|
||||
#include "AcpiEvents.hpp"
|
||||
#include <ACPI/FADT.hpp>
|
||||
#include <ACPI/AML/AmlParser.hpp>
|
||||
#include <ACPI/AML/AmlInterpreter.hpp>
|
||||
@@ -22,6 +23,8 @@
|
||||
#include <Drivers/PS2/PS2Controller.hpp>
|
||||
#include <Graphics/Cursor.hpp>
|
||||
#include <Libraries/Memory.hpp>
|
||||
#include <Hal/MSR.hpp>
|
||||
#include <Api/Syscall.hpp>
|
||||
|
||||
using namespace Kt;
|
||||
|
||||
@@ -31,6 +34,11 @@ extern "C" void AcpiResumeLongMode(Hal::AcpiSleep::CpuState* stateArea);
|
||||
extern "C" void AcpiWakeEntry();
|
||||
extern "C" void* g_wakeStatePtr;
|
||||
|
||||
// Called directly by AcpiResumeLongMode after restoring CPU state.
|
||||
// This avoids returning to Suspend() via ret, which is fragile because
|
||||
// the compiler's stack frame expectations may not match the restored state.
|
||||
extern "C" void AcpiResumeEntry();
|
||||
|
||||
// Real-mode trampoline from S3Trampoline.asm
|
||||
extern "C" char S3TrampolineStart[];
|
||||
extern "C" char S3Trampoline64[];
|
||||
@@ -40,6 +48,11 @@ extern "C" char S3TrampolineEnd[];
|
||||
// Physical address where the trampoline is copied (must be < 1MB, page-aligned)
|
||||
static constexpr uint32_t TRAMPOLINE_PHYS = 0x8000;
|
||||
|
||||
// Physical address for the shadow PML4 used by the trampoline (must be < 4GB).
|
||||
// The kernel PML4 may be above 4GB, but the 32-bit trampoline can only load
|
||||
// a 32-bit CR3. We copy the kernel PML4 entries here before entering S3.
|
||||
static constexpr uint32_t SHADOW_PML4_PHYS = 0x9000;
|
||||
|
||||
// GDT/TSS reload helpers
|
||||
namespace Hal {
|
||||
extern void BridgeLoadGDT();
|
||||
@@ -49,7 +62,11 @@ namespace Hal {
|
||||
namespace Hal {
|
||||
namespace AcpiSleep {
|
||||
|
||||
// ── State ───────────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// State
|
||||
|
||||
// ============================================================================
|
||||
static bool g_s3Available = false;
|
||||
static uint16_t g_s3SlpTypA = 0;
|
||||
static uint16_t g_s3SlpTypB = 0;
|
||||
@@ -65,10 +82,66 @@ namespace Hal {
|
||||
// CPU state save area (aligned for fxsave)
|
||||
static CpuState g_cpuState __attribute__((aligned(64)));
|
||||
|
||||
// ── Initialize ──────────────────────────────────────────────────
|
||||
// Set by AcpiResumeEntry to signal Suspend() that resume completed.
|
||||
static volatile int g_resumeComplete = 0;
|
||||
|
||||
// ============================================================================
|
||||
|
||||
// Initialize
|
||||
|
||||
// ============================================================================
|
||||
void Initialize(ACPI::CommonSDTHeader* xsdt) {
|
||||
g_s3Available = false;
|
||||
|
||||
// Check CMOS 0x72 for S3 wake progress from a previous attempt.
|
||||
// Non-zero means the last wake attempt reached a specific stage.
|
||||
Io::Out8(0xF2, 0x70); // CMOS register 0x72 | NMI disable
|
||||
uint8_t wakeProgress = Io::In8(0x71);
|
||||
// Check CMOS 0x73 — set to 0xDD if C resume code was ever reached
|
||||
// (survives trampoline overwrites of 0x72 during sleep-wake loops)
|
||||
Io::Out8(0xF3, 0x70);
|
||||
uint8_t cReached = Io::In8(0x71);
|
||||
if (wakeProgress != 0 || cReached != 0) {
|
||||
KernelLogStream(WARNING, "S3") << "Previous S3 wake progress: 0x"
|
||||
<< base::hex << (uint64_t)wakeProgress
|
||||
<< (wakeProgress == 0xA1 ? " (16-bit trampoline)" :
|
||||
wakeProgress == 0xA2 ? " (32-bit mode)" :
|
||||
wakeProgress == 0xA3 ? " (entering long mode)" :
|
||||
wakeProgress == 0xB1 ? " (64-bit UEFI entry)" :
|
||||
wakeProgress == 0xC1 ? " (AcpiResumeLongMode entry)" :
|
||||
wakeProgress == 0xC2 ? " (GDT+CS reloaded)" :
|
||||
wakeProgress == 0xC3 ? " (IDT+CR4/CR0/CR3 restored)" :
|
||||
wakeProgress == 0xC4 ? " (about to ret to Suspend)" :
|
||||
wakeProgress == 0xD1 ? " (C resume path)" : " (unknown)");
|
||||
if (cReached == 0xEE)
|
||||
KernelLogStream(WARNING, "S3") << "Full resume completed (all reinit done)";
|
||||
else if (cReached == 0xDD)
|
||||
KernelLogStream(WARNING, "S3") << "C resume entered but did NOT complete all reinit";
|
||||
else
|
||||
KernelLogStream(WARNING, "S3") << "C resume path was NOT reached";
|
||||
// Read reinit step progress from CMOS 0x74
|
||||
Io::Out8(0xF4, 0x70);
|
||||
uint8_t reinitStep = Io::In8(0x71);
|
||||
if (reinitStep != 0) {
|
||||
const char* stepNames[] = {
|
||||
"?", "GDT", "TSS", "Syscalls", "PAT", "PIC",
|
||||
"LocalAPIC", "IoAPIC", "APICTimer", "PM1Clear",
|
||||
"WAK", "PS2", "IntelGPU", "sti"
|
||||
};
|
||||
const char* stepName = reinitStep <= 0x0D ? stepNames[reinitStep] : "?";
|
||||
KernelLogStream(WARNING, "S3") << "Reinit stopped after step 0x"
|
||||
<< base::hex << (uint64_t)reinitStep << " (" << stepName
|
||||
<< ") - next step crashed/hung";
|
||||
}
|
||||
Io::Out8(0xF4, 0x70);
|
||||
Io::Out8(0x00, 0x71);
|
||||
// Clear both
|
||||
Io::Out8(0xF2, 0x70);
|
||||
Io::Out8(0x00, 0x71);
|
||||
Io::Out8(0xF3, 0x70);
|
||||
Io::Out8(0x00, 0x71);
|
||||
}
|
||||
|
||||
FADT::ParsedFADT fadt{};
|
||||
if (!FADT::Parse(xsdt, fadt) || !fadt.Valid)
|
||||
return;
|
||||
@@ -96,9 +169,9 @@ namespace Hal {
|
||||
g_pm1bControlBlock = fadt.PM1bControlBlock;
|
||||
g_pm1EventLength = fadt.PM1EventLength;
|
||||
|
||||
// Find \_S3_ via brute-force DSDT scan (same approach as \_S5_).
|
||||
// This works on any DSDT regardless of complexity — does not
|
||||
// require the AML interpreter or namespace to be loaded.
|
||||
// Find \_S3_ via brute-force DSDT scan. The AML interpreter stores
|
||||
// packages as raw bytecode (no structured element access yet), so
|
||||
// the brute-force scanner is the reliable path for now.
|
||||
auto* dsdt = (void*)Memory::HHDM(fadt.DsdtAddress);
|
||||
AML::SleepObject s3 = AML::FindSleepState(dsdt, 3);
|
||||
if (s3.Valid) {
|
||||
@@ -111,6 +184,11 @@ namespace Hal {
|
||||
<< " SLP_TYPb=" << base::hex << (uint64_t)g_s3SlpTypB << ")";
|
||||
KernelLogStream(INFO, "S3") << "Kernel PML4 phys = " << base::hex
|
||||
<< (uint64_t)Memory::VMM::g_paging->PML4;
|
||||
KernelLogStream(INFO, "S3") << "FACS at phys " << base::hex
|
||||
<< (uint64_t)fadt.FacsAddress << " version=" << base::dec
|
||||
<< (uint64_t)g_facs->Version << " flags=" << base::hex
|
||||
<< (uint64_t)g_facs->Flags
|
||||
<< (g_facs->Flags & FACS_64BIT_WAKE_F ? " (64BIT_WAKE)" : " (32BIT_WAKE only)");
|
||||
} else {
|
||||
KernelLogStream(INFO, "S3") << "\\_S3_ not found in DSDT - S3 unavailable";
|
||||
}
|
||||
@@ -120,7 +198,11 @@ namespace Hal {
|
||||
return g_s3Available;
|
||||
}
|
||||
|
||||
// ── Evaluate _PTS (Prepare To Sleep) ────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Evaluate _PTS (Prepare To Sleep)
|
||||
|
||||
// ============================================================================
|
||||
static void EvaluatePts(int sleepState) {
|
||||
auto& interp = AML::GetInterpreter();
|
||||
if (!interp.IsInitialized()) return;
|
||||
@@ -138,7 +220,11 @@ namespace Hal {
|
||||
KernelLogStream(DEBUG, "S3") << "Evaluated \\_PTS(" << base::dec << (uint64_t)sleepState << ")";
|
||||
}
|
||||
|
||||
// ── Evaluate _WAK (System Wake) ─────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Evaluate _WAK (System Wake)
|
||||
|
||||
// ============================================================================
|
||||
static void EvaluateWak(int sleepState) {
|
||||
auto& interp = AML::GetInterpreter();
|
||||
if (!interp.IsInitialized()) return;
|
||||
@@ -156,7 +242,11 @@ namespace Hal {
|
||||
KernelLogStream(DEBUG, "S3") << "Evaluated \\_WAK(" << base::dec << (uint64_t)sleepState << ")";
|
||||
}
|
||||
|
||||
// ── Clear PM1 Status Registers ──────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Clear PM1 Status Registers
|
||||
|
||||
// ============================================================================
|
||||
static void ClearPM1Status() {
|
||||
// Write 1 to clear all status bits (write-1-to-clear semantics)
|
||||
uint16_t clearMask = PM1_WAK_STS | PM1_PWRBTN_STS | PM1_SLPBTN_STS |
|
||||
@@ -168,7 +258,11 @@ namespace Hal {
|
||||
Io::Out16(clearMask, (uint16_t)g_pm1bEventBlock);
|
||||
}
|
||||
|
||||
// ── Enable Wake Events ──────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Enable Wake Events
|
||||
|
||||
// ============================================================================
|
||||
static void EnableWakeEvents() {
|
||||
// Enable register is at event block + PM1EventLength/2
|
||||
uint16_t enableOffset = g_pm1EventLength / 2;
|
||||
@@ -182,7 +276,11 @@ namespace Hal {
|
||||
Io::Out16(enableMask, (uint16_t)(g_pm1bEventBlock + enableOffset));
|
||||
}
|
||||
|
||||
// ── Install real-mode trampoline and set waking vector ────────────
|
||||
// ============================================================================
|
||||
|
||||
// Install real-mode trampoline and set waking vector
|
||||
|
||||
// ============================================================================
|
||||
static void SetWakingVector() {
|
||||
if (!g_facs) return;
|
||||
|
||||
@@ -197,26 +295,31 @@ namespace Hal {
|
||||
uint8_t* trampolineSrc = (uint8_t*)S3TrampolineStart;
|
||||
memcpy(trampolineDst, trampolineSrc, trampolineSize);
|
||||
|
||||
// Patch the trampoline data area with CR3 and resume addresses.
|
||||
// Patch the trampoline data area with CR3, resume addresses,
|
||||
// and PM1 control port addresses.
|
||||
// Data layout (from S3Trampoline.asm):
|
||||
// +0: uint64_t CR3 (full 64-bit PML4 physical address)
|
||||
// +8: uint64_t CpuState virtual address
|
||||
// +16: uint64_t AcpiResumeLongMode virtual address
|
||||
// +24: uint16_t PM1a control block I/O port
|
||||
// +26: uint16_t PM1b control block I/O port
|
||||
uint32_t dataOffset = (uint32_t)(S3TrampolineData - S3TrampolineStart);
|
||||
uint8_t* dataArea = trampolineDst + dataOffset;
|
||||
|
||||
// Use the kernel master PML4 (which has 0x8000 identity-mapped)
|
||||
// rather than the saved process PML4 (which doesn't).
|
||||
// AcpiResumeLongMode will restore the saved CR3 after we're
|
||||
// safely back in long mode with kernel GDT/IDT.
|
||||
uint64_t cr3val = (uint64_t)Memory::VMM::g_paging->PML4;
|
||||
// The kernel PML4 may be above 4GB (e.g. at 0x8BF7CC000), but
|
||||
// the 32-bit trampoline can only load a 32-bit CR3. Create a
|
||||
// shadow copy of the PML4 at a fixed low-memory address.
|
||||
// Only the PML4 page itself needs to be below 4GB — its entries
|
||||
// contain full 52-bit physical addresses for sub-tables.
|
||||
uint64_t kernelPml4Phys = (uint64_t)Memory::VMM::g_paging->PML4;
|
||||
uint8_t* kernelPml4Virt = (uint8_t*)Memory::HHDM(kernelPml4Phys);
|
||||
uint8_t* shadowPml4Virt = (uint8_t*)Memory::HHDM((uint64_t)SHADOW_PML4_PHYS);
|
||||
memcpy(shadowPml4Virt, kernelPml4Virt, 4096);
|
||||
|
||||
// The 16-bit->32-bit trampoline path can only load 32-bit CR3.
|
||||
// If the PML4 is above 4GB, the real-mode wake path would fail.
|
||||
if (cr3val > 0xFFFFFFFF) {
|
||||
KernelLogStream(ERROR, "S3") << "PML4 at " << base::hex << cr3val
|
||||
<< " is above 4GB - real-mode wake path will fail!";
|
||||
}
|
||||
uint64_t cr3val = (uint64_t)SHADOW_PML4_PHYS;
|
||||
|
||||
KernelLogStream(DEBUG, "S3") << "Shadow PML4 at " << base::hex << cr3val
|
||||
<< " (kernel PML4 at " << base::hex << kernelPml4Phys << ")";
|
||||
|
||||
memcpy(dataArea + 0, &cr3val, 8);
|
||||
|
||||
@@ -226,10 +329,24 @@ namespace Hal {
|
||||
uint64_t resumeAddr = (uint64_t)&AcpiResumeLongMode;
|
||||
memcpy(dataArea + 16, &resumeAddr, 8);
|
||||
|
||||
// Set the 32-bit waking vector only. Setting X_FirmwareWakingVector
|
||||
// to non-zero causes this laptop's firmware to hang during wake.
|
||||
uint16_t pm1a = (uint16_t)g_pm1aControlBlock;
|
||||
uint16_t pm1b = (uint16_t)g_pm1bControlBlock;
|
||||
memcpy(dataArea + 24, &pm1a, 2);
|
||||
memcpy(dataArea + 26, &pm1b, 2);
|
||||
|
||||
// Always set the 32-bit real-mode waking vector (universal fallback).
|
||||
g_facs->FirmwareWakingVector = TRAMPOLINE_PHYS;
|
||||
g_facs->X_FirmwareWakingVector = 0;
|
||||
|
||||
// Use the 64-bit waking vector if the firmware advertises support.
|
||||
// FACS flags bit 1 (64BIT_WAKE_F) indicates the platform can
|
||||
// transfer control in 64-bit mode.
|
||||
if (g_facs->Flags & FACS_64BIT_WAKE_F) {
|
||||
g_facs->X_FirmwareWakingVector = TRAMPOLINE_PHYS + 0x100;
|
||||
KernelLogStream(DEBUG, "S3") << "Using 64-bit waking vector (0x8100)";
|
||||
} else {
|
||||
g_facs->X_FirmwareWakingVector = 0;
|
||||
KernelLogStream(DEBUG, "S3") << "Using 32-bit waking vector (0x8000)";
|
||||
}
|
||||
|
||||
KernelLogStream(DEBUG, "S3") << "Trampoline installed at " << base::hex
|
||||
<< (uint64_t)TRAMPOLINE_PHYS << " (" << base::dec
|
||||
@@ -237,7 +354,11 @@ namespace Hal {
|
||||
KernelLogStream(DEBUG, "S3") << "Trampoline CR3 = " << base::hex << cr3val;
|
||||
}
|
||||
|
||||
// ── Wait for WAK_STS ────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Wait for WAK_STS
|
||||
|
||||
// ============================================================================
|
||||
static void WaitForWake() {
|
||||
// After entering S3, the CPU halts. On resume, firmware runs the
|
||||
// waking vector. But if we somehow didn't enter sleep (e.g. immediate
|
||||
@@ -251,7 +372,101 @@ namespace Hal {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Suspend ─────────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Resume Entry (called directly by AcpiResumeLongMode)
|
||||
|
||||
// ============================================================================
|
||||
// This is a standalone function with its own stack frame, avoiding
|
||||
// any dependency on the compiler's layout of Suspend().
|
||||
extern "C" void AcpiResumeEntry() {
|
||||
// Progress: reached C resume path (0xD1)
|
||||
Io::Out8(0xF2, 0x70);
|
||||
Io::Out8(0xD1, 0x71);
|
||||
Io::Out8(0xF3, 0x70);
|
||||
Io::Out8(0xDD, 0x71);
|
||||
|
||||
// CRITICAL: Clear SLP_EN and SLP_TYP in PM1 control registers
|
||||
if (g_pm1aControlBlock != 0) {
|
||||
uint16_t pm1a = Io::In16((uint16_t)g_pm1aControlBlock);
|
||||
pm1a &= ~(PM1_SLP_EN | PM1_SLP_TYP_MASK);
|
||||
Io::Out16(pm1a, (uint16_t)g_pm1aControlBlock);
|
||||
}
|
||||
if (g_pm1bControlBlock != 0) {
|
||||
uint16_t pm1b = Io::In16((uint16_t)g_pm1bControlBlock);
|
||||
pm1b &= ~(PM1_SLP_EN | PM1_SLP_TYP_MASK);
|
||||
Io::Out16(pm1b, (uint16_t)g_pm1bControlBlock);
|
||||
}
|
||||
|
||||
// Debug: green rectangle on framebuffer
|
||||
{
|
||||
uint32_t* fb = ::Graphics::Cursor::GetFramebufferBase();
|
||||
uint64_t pitch = ::Graphics::Cursor::GetFramebufferPitch();
|
||||
if (fb && pitch > 0) {
|
||||
for (int y = 0; y < 32; y++) {
|
||||
uint32_t* row = (uint32_t*)((uint8_t*)fb + y * pitch);
|
||||
for (int x = 0; x < 32; x++) {
|
||||
row[x] = 0xFF00FF00;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use CMOS 0x74 as step-by-step progress through reinit
|
||||
auto reinitProgress = [](uint8_t step) {
|
||||
Io::Out8(0xF4, 0x70);
|
||||
Io::Out8(step, 0x71);
|
||||
};
|
||||
|
||||
reinitProgress(0x01); // GDT
|
||||
Hal::BridgeLoadGDT();
|
||||
reinitProgress(0x02); // TSS
|
||||
Hal::LoadTSS();
|
||||
reinitProgress(0x03); // Syscalls
|
||||
Montauk::InitializeSyscalls();
|
||||
reinitProgress(0x04); // PAT
|
||||
Hal::InitializePAT();
|
||||
reinitProgress(0x05); // PIC
|
||||
Hal::DisableLegacyPic();
|
||||
reinitProgress(0x06); // Local APIC
|
||||
Hal::LocalApic::Reinitialize();
|
||||
reinitProgress(0x07); // IO APIC
|
||||
Hal::IoApic::Reinitialize();
|
||||
reinitProgress(0x08); // APIC Timer
|
||||
Timekeeping::ApicTimerReinitialize();
|
||||
reinitProgress(0x09); // PM1 clear
|
||||
ClearPM1Status();
|
||||
reinitProgress(0x0A); // _WAK
|
||||
EvaluateWak(3);
|
||||
reinitProgress(0x0B); // PS/2
|
||||
Drivers::PS2::Reinitialize();
|
||||
// Re-enable ACPI events (power button) after S3
|
||||
AcpiEvents::Reinitialize();
|
||||
reinitProgress(0x0C); // Intel GPU
|
||||
if (Drivers::Graphics::IntelGPU::IsInitialized()) {
|
||||
Drivers::Graphics::IntelGPU::Reinitialize();
|
||||
}
|
||||
reinitProgress(0x0D); // sti
|
||||
asm volatile("sti");
|
||||
|
||||
KernelLogStream(OK, "S3") << "Resumed from S3 suspend";
|
||||
|
||||
// Progress: all reinit complete (0xEE in CMOS 0x73)
|
||||
Io::Out8(0xF3, 0x70);
|
||||
Io::Out8(0xEE, 0x71);
|
||||
|
||||
// Signal Suspend() that resume is complete. AcpiResumeEntry
|
||||
// was entered via jmp (not call), so our ret will pop the
|
||||
// original return address from call AcpiSaveAndSuspend,
|
||||
// returning to Suspend() which checks this flag.
|
||||
g_resumeComplete = 1;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
// Suspend
|
||||
|
||||
// ============================================================================
|
||||
int Suspend() {
|
||||
if (!g_s3Available) {
|
||||
KernelLogStream(ERROR, "S3") << "S3 suspend not available";
|
||||
@@ -263,77 +478,27 @@ namespace Hal {
|
||||
// 1. Evaluate _PTS(3) — Prepare To Sleep
|
||||
EvaluatePts(3);
|
||||
|
||||
// 2. Save CPU state. AcpiSaveAndSuspend returns 1 on initial call,
|
||||
// and 0 when we resume from S3 (AcpiResumeLongMode sets RAX=0).
|
||||
int resumed = !AcpiSaveAndSuspend(&g_cpuState);
|
||||
if (resumed) {
|
||||
// ── RESUME PATH ─────────────────────────────────────────
|
||||
// We just woke from S3. Firmware ran our waking vector which
|
||||
// called AcpiResumeLongMode, restoring all registers and
|
||||
// returning here with 0.
|
||||
|
||||
// Debug: write a green rectangle to the top-left corner of the
|
||||
// framebuffer. The framebuffer memory survives S3 (it's RAM).
|
||||
// This will be visible once the GPU display plane is restored,
|
||||
// confirming the CPU successfully completed the resume path.
|
||||
{
|
||||
uint32_t* fb = ::Graphics::Cursor::GetFramebufferBase();
|
||||
uint64_t pitch = ::Graphics::Cursor::GetFramebufferPitch();
|
||||
if (fb && pitch > 0) {
|
||||
for (int y = 0; y < 32; y++) {
|
||||
uint32_t* row = (uint32_t*)((uint8_t*)fb + y * pitch);
|
||||
for (int x = 0; x < 32; x++) {
|
||||
row[x] = 0xFF00FF00; // Green (ARGB)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reload GDT and TSS (firmware may have clobbered them)
|
||||
Hal::BridgeLoadGDT();
|
||||
Hal::LoadTSS();
|
||||
|
||||
// Re-disable legacy 8259 PIC. Firmware may have re-enabled
|
||||
// it during S3 resume POST, which could cause spurious
|
||||
// interrupts on conflicting vectors once we enable interrupts.
|
||||
Hal::DisableLegacyPic();
|
||||
|
||||
// Re-enable the Local APIC (MSR global enable + SVR + TPR)
|
||||
Hal::LocalApic::Reinitialize();
|
||||
|
||||
// Restore I/O APIC redirection entries (lost during S3).
|
||||
// Must be done before enabling interrupts so IRQ routing
|
||||
// is in place when devices start generating interrupts.
|
||||
Hal::IoApic::Reinitialize();
|
||||
|
||||
// Restart the APIC timer
|
||||
Timekeeping::ApicTimerReinitialize();
|
||||
|
||||
// Clear WAK_STS
|
||||
ClearPM1Status();
|
||||
|
||||
// Evaluate _WAK(3)
|
||||
EvaluateWak(3);
|
||||
|
||||
// Re-enable PS/2 controller (ports and interrupts may be
|
||||
// disabled after S3; skip full self-test to avoid resetting
|
||||
// attached devices)
|
||||
Drivers::PS2::Reinitialize();
|
||||
|
||||
// Restore Intel GPU display (GTT + display plane lost during S3)
|
||||
if (Drivers::Graphics::IntelGPU::IsInitialized()) {
|
||||
Drivers::Graphics::IntelGPU::Reinitialize();
|
||||
}
|
||||
|
||||
// Re-enable interrupts
|
||||
asm volatile("sti");
|
||||
|
||||
KernelLogStream(OK, "S3") << "Resumed from S3 suspend";
|
||||
// 2. Save CPU state. On resume, AcpiResumeLongMode jumps to
|
||||
// AcpiResumeEntry (not back here), which does all reinit and
|
||||
// sets g_resumeComplete=1, then returns here via the stack.
|
||||
g_resumeComplete = 0;
|
||||
AcpiSaveAndSuspend(&g_cpuState);
|
||||
|
||||
// If we get here after resume, AcpiResumeEntry already ran.
|
||||
if (g_resumeComplete) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ── SUSPEND PATH (initial save returned 1) ──────────────────
|
||||
// ============================================================================
|
||||
|
||||
// SUSPEND PATH (initial save returned 1)
|
||||
|
||||
// ============================================================================
|
||||
// Clear CMOS progress markers before entering S3.
|
||||
Io::Out8(0xF2, 0x70); // CMOS register 0x72 | NMI disable
|
||||
Io::Out8(0x00, 0x71);
|
||||
Io::Out8(0xF3, 0x70); // CMOS register 0x73
|
||||
Io::Out8(0x00, 0x71);
|
||||
|
||||
// 3. Disable interrupts
|
||||
asm volatile("cli");
|
||||
|
||||
@@ -11,7 +11,11 @@
|
||||
namespace Hal {
|
||||
namespace AcpiSleep {
|
||||
|
||||
// ── FACS (Firmware ACPI Control Structure) ──────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// FACS (Firmware ACPI Control Structure)
|
||||
|
||||
// ============================================================================
|
||||
struct FACS {
|
||||
char Signature[4]; // "FACS"
|
||||
uint32_t Length;
|
||||
@@ -33,7 +37,11 @@ namespace Hal {
|
||||
static constexpr uint32_t FADT_S4_RTC_STS_VALID = (1 << 16);
|
||||
static constexpr uint32_t FADT_HW_REDUCED_ACPI = (1 << 20);
|
||||
|
||||
// ── PM1 Status Register Bits ────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// PM1 Status Register Bits
|
||||
|
||||
// ============================================================================
|
||||
static constexpr uint16_t PM1_TMR_STS = (1 << 0);
|
||||
static constexpr uint16_t PM1_BM_STS = (1 << 4);
|
||||
static constexpr uint16_t PM1_GBL_STS = (1 << 5);
|
||||
@@ -42,20 +50,32 @@ namespace Hal {
|
||||
static constexpr uint16_t PM1_RTC_STS = (1 << 10);
|
||||
static constexpr uint16_t PM1_WAK_STS = (1 << 15);
|
||||
|
||||
// ── PM1 Enable Register Bits ────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// PM1 Enable Register Bits
|
||||
|
||||
// ============================================================================
|
||||
static constexpr uint16_t PM1_TMR_EN = (1 << 0);
|
||||
static constexpr uint16_t PM1_GBL_EN = (1 << 5);
|
||||
static constexpr uint16_t PM1_PWRBTN_EN = (1 << 8);
|
||||
static constexpr uint16_t PM1_SLPBTN_EN = (1 << 9);
|
||||
static constexpr uint16_t PM1_RTC_EN = (1 << 10);
|
||||
|
||||
// ── PM1 Control Register Bits ───────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// PM1 Control Register Bits
|
||||
|
||||
// ============================================================================
|
||||
static constexpr uint16_t PM1_SCI_EN = (1 << 0);
|
||||
static constexpr uint16_t PM1_BM_RLD = (1 << 1);
|
||||
static constexpr uint16_t PM1_SLP_TYP_MASK = 0x1C00; // bits 10-12
|
||||
static constexpr uint16_t PM1_SLP_EN = (1 << 13);
|
||||
|
||||
// ── CPU State (saved across S3) ─────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// CPU State (saved across S3)
|
||||
|
||||
// ============================================================================
|
||||
// Layout must match S3Wake.asm offsets exactly.
|
||||
// No FPU/SSE state — the kernel is compiled with -mno-sse.
|
||||
struct CpuState {
|
||||
@@ -71,8 +91,11 @@ namespace Hal {
|
||||
uint8_t IdtPtr[10]; // 0xB2
|
||||
} __attribute__((aligned(16)));
|
||||
|
||||
// ── Sleep API ───────────────────────────────────────────────────
|
||||
// ============================================================================
|
||||
|
||||
// Sleep API
|
||||
|
||||
// ============================================================================
|
||||
// Initialize sleep support. Called during boot after ACPI init.
|
||||
// xsdt is used to read FADT for FACS and PM register addresses.
|
||||
void Initialize(ACPI::CommonSDTHeader* xsdt);
|
||||
|
||||
@@ -19,8 +19,44 @@ S3TrampolineStart:
|
||||
cli
|
||||
cld
|
||||
|
||||
; Set up DS immediately so we can access the data area
|
||||
mov ax, 0x0800
|
||||
mov ds, ax
|
||||
|
||||
; Clear SLP_EN + SLP_TYP in PM1 control registers ASAP.
|
||||
; The chipset may still have these set from S3 entry and will
|
||||
; re-enter sleep if we don't clear them immediately.
|
||||
mov dx, [ds:data_pm1a_ctrl - S3TrampolineStart]
|
||||
or dx, dx
|
||||
jz .no_pm1a_16
|
||||
in ax, dx
|
||||
and ax, 0xC3FF ; clear SLP_EN (bit 13) + SLP_TYP (bits 12:10)
|
||||
out dx, ax
|
||||
.no_pm1a_16:
|
||||
mov dx, [ds:data_pm1b_ctrl - S3TrampolineStart]
|
||||
or dx, dx
|
||||
jz .no_pm1b_16
|
||||
in ax, dx
|
||||
and ax, 0xC3FF
|
||||
out dx, ax
|
||||
.no_pm1b_16:
|
||||
|
||||
; Progress: reached 16-bit trampoline (write 0xA1 to CMOS 0x72)
|
||||
mov al, 0xF2 ; register 0x72 | 0x80 (NMI disable)
|
||||
out 0x70, al
|
||||
mov al, 0xA1
|
||||
out 0x71, al
|
||||
|
||||
; Enable A20 gate via fast A20 (port 0x92). On real hardware, firmware
|
||||
; may leave A20 disabled after S3 wake, causing odd-megabyte addresses
|
||||
; to wrap. QEMU always has A20 enabled, masking this bug.
|
||||
in al, 0x92
|
||||
or al, 2 ; set A20 enable bit
|
||||
and al, 0xFE ; clear bit 0 (system reset)
|
||||
out 0x92, al
|
||||
|
||||
; Set up remaining segments and stack (DS already set above for PM1 clear)
|
||||
mov ax, 0x0800
|
||||
mov es, ax
|
||||
mov ss, ax
|
||||
mov sp, 0x00F0
|
||||
@@ -35,6 +71,12 @@ S3TrampolineStart:
|
||||
|
||||
[bits 32]
|
||||
pm32_entry:
|
||||
; Progress: reached 32-bit mode (0xA2)
|
||||
mov al, 0xF2
|
||||
out 0x70, al
|
||||
mov al, 0xA2
|
||||
out 0x71, al
|
||||
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
@@ -50,18 +92,32 @@ pm32_entry:
|
||||
mov eax, [0x8000 + data_cr3 - S3TrampolineStart]
|
||||
mov cr3, eax
|
||||
|
||||
mov ecx, 0xC0000080
|
||||
mov ecx, 0xC0000080 ; IA32_EFER
|
||||
rdmsr
|
||||
or eax, (1 << 8)
|
||||
or eax, (1 << 8) ; LME (Long Mode Enable)
|
||||
or eax, (1 << 11) ; NXE (No-Execute Enable) — kernel page tables
|
||||
; use NX bits; without NXE, bit 63 in PTEs is
|
||||
; reserved and any page walk through an NX entry
|
||||
; triggers a reserved-bit #PF → triple fault.
|
||||
or eax, (1 << 0) ; SCE (Syscall Enable) — must be set before
|
||||
; returning to code that may handle syscalls.
|
||||
wrmsr
|
||||
|
||||
mov eax, cr0
|
||||
or eax, (1 << 31)
|
||||
mov cr0, eax
|
||||
|
||||
; Progress: entering long mode (0xA3)
|
||||
mov al, 0xF2
|
||||
out 0x70, al
|
||||
mov al, 0xA3
|
||||
out 0x71, al
|
||||
|
||||
jmp dword 0x18:(0x8000 + lm64_common - S3TrampolineStart)
|
||||
|
||||
; ── Temporary GDT ──────────────────────────────────────────────────────
|
||||
; ═════════════════════════════════════════════════════════════════════════════
|
||||
; Temporary GDT
|
||||
; ═════════════════════════════════════════════════════════════════════════════
|
||||
align 16
|
||||
gdt_start:
|
||||
dq 0x0000000000000000 ; 0x00: Null
|
||||
@@ -75,6 +131,11 @@ gdt_ptr:
|
||||
dw gdt_end - gdt_start - 1
|
||||
dd 0x8000 + gdt_start - S3TrampolineStart
|
||||
|
||||
; 64-bit GDT pointer (lgdt in long mode reads 10 bytes: 2-byte limit + 8-byte base)
|
||||
gdt_ptr64:
|
||||
dw gdt_end - gdt_start - 1
|
||||
dq 0x8000 + gdt_start - S3TrampolineStart
|
||||
|
||||
|
||||
; ═════════════════════════════════════════════════════════════════════════
|
||||
; 64-bit entry at offset 0x100 (X_FirmwareWakingVector = 0x8100)
|
||||
@@ -88,14 +149,62 @@ S3Trampoline64:
|
||||
cli
|
||||
cld
|
||||
|
||||
; Load kernel PML4 (full 64-bit) - firmware identity-maps low memory
|
||||
; CRITICAL: Clear SLP_EN + SLP_TYP immediately (same as 16-bit path)
|
||||
mov dx, [0x8000 + data_pm1a_ctrl - S3TrampolineStart]
|
||||
or dx, dx
|
||||
jz .no_pm1a_64
|
||||
in ax, dx
|
||||
and ax, 0xC3FF
|
||||
out dx, ax
|
||||
.no_pm1a_64:
|
||||
mov dx, [0x8000 + data_pm1b_ctrl - S3TrampolineStart]
|
||||
or dx, dx
|
||||
jz .no_pm1b_64
|
||||
in ax, dx
|
||||
and ax, 0xC3FF
|
||||
out dx, ax
|
||||
.no_pm1b_64:
|
||||
|
||||
; Progress: reached 64-bit trampoline (0xB1)
|
||||
mov al, 0xF2
|
||||
out 0x70, al
|
||||
mov al, 0xB1
|
||||
out 0x71, al
|
||||
|
||||
; Enable NXE and SCE in EFER before loading kernel CR3.
|
||||
; Kernel page tables use NX bits — without NXE, loading CR3
|
||||
; would cause reserved-bit #PF on first page walk.
|
||||
mov ecx, 0xC0000080 ; IA32_EFER
|
||||
rdmsr
|
||||
or eax, (1 << 11) ; NXE
|
||||
or eax, (1 << 0) ; SCE
|
||||
wrmsr
|
||||
|
||||
; Load kernel PML4 (full 64-bit) - firmware identity-maps low memory.
|
||||
; Do NOT load the trampoline GDT here — UEFI's CS selector (e.g. 0x38)
|
||||
; would be out of bounds in our small GDT, causing #GP on the next
|
||||
; instruction fetch. UEFI's segments are fine for these few instructions;
|
||||
; AcpiResumeLongMode will load the kernel GDT and reload all segments.
|
||||
mov rax, [0x8000 + data_cr3 - S3TrampolineStart]
|
||||
mov cr3, rax
|
||||
|
||||
; Fall through to common path
|
||||
; Load state pointer and jump to resume (same as lm64_common below,
|
||||
; but we skip the segment reload which needs the trampoline GDT).
|
||||
mov rdi, [0x8000 + data_state_ptr - S3TrampolineStart]
|
||||
mov rax, [0x8000 + data_resume_addr - S3TrampolineStart]
|
||||
jmp rax
|
||||
|
||||
; ── Common 64-bit path (both entries converge here) ──────────────────
|
||||
; ═════════════════════════════════════════════════════════════════════════════
|
||||
; Common 64-bit path for 16-bit → 32-bit → 64-bit transition
|
||||
; ═════════════════════════════════════════════════════════════════════════════
|
||||
; The real-mode path arrives here with the trampoline GDT active,
|
||||
; so selector 0x20 (64-bit data) is valid.
|
||||
lm64_common:
|
||||
mov ax, 0x20
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov ss, ax
|
||||
|
||||
mov rdi, [0x8000 + data_state_ptr - S3TrampolineStart]
|
||||
mov rax, [0x8000 + data_resume_addr - S3TrampolineStart]
|
||||
jmp rax
|
||||
@@ -107,9 +216,11 @@ lm64_common:
|
||||
align 8
|
||||
global S3TrampolineData
|
||||
S3TrampolineData:
|
||||
data_cr3: dq 0 ; kernel PML4 physical address (full 64-bit)
|
||||
data_state_ptr: dq 0 ; virtual address of CpuState
|
||||
data_resume_addr: dq 0 ; virtual address of AcpiResumeLongMode
|
||||
data_cr3: dq 0 ; +0: kernel PML4 physical address (full 64-bit)
|
||||
data_state_ptr: dq 0 ; +8: virtual address of CpuState
|
||||
data_resume_addr: dq 0 ; +16: virtual address of AcpiResumeLongMode
|
||||
data_pm1a_ctrl: dw 0 ; +24: PM1a control block I/O port
|
||||
data_pm1b_ctrl: dw 0 ; +26: PM1b control block I/O port
|
||||
|
||||
global S3TrampolineEnd
|
||||
S3TrampolineEnd:
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
[bits 64]
|
||||
section .text
|
||||
|
||||
; ─── AcpiSaveAndSuspend ──────────────────────────────────────────────────
|
||||
; ═════════════════════════════════════════════════════════════════════════════
|
||||
; AcpiSaveAndSuspend
|
||||
; ═════════════════════════════════════════════════════════════════════════════
|
||||
; extern "C" int AcpiSaveAndSuspend(CpuState* stateArea)
|
||||
; rdi = pointer to CpuState structure
|
||||
;
|
||||
@@ -77,7 +79,9 @@ AcpiSaveAndSuspend:
|
||||
ret
|
||||
|
||||
|
||||
; ─── AcpiWakeEntry ───────────────────────────────────────────────────────
|
||||
; ═════════════════════════════════════════════════════════════════════════════
|
||||
; AcpiWakeEntry
|
||||
; ═════════════════════════════════════════════════════════════════════════════
|
||||
; This is the actual waking vector target. Firmware jumps here after S3
|
||||
; resume. It loads the CpuState pointer from a fixed location (set before
|
||||
; suspend) and falls through to AcpiResumeLongMode.
|
||||
@@ -103,14 +107,18 @@ AcpiWakeEntry:
|
||||
jmp AcpiResumeLongMode
|
||||
|
||||
|
||||
; ─── Well-known pointer to CpuState (set before suspend) ────────────────
|
||||
; ═════════════════════════════════════════════════════════════════════════════
|
||||
; Well-known pointer to CpuState (set before suspend)
|
||||
; ═════════════════════════════════════════════════════════════════════════════
|
||||
; Placed in .text so AcpiWakeEntry can use RIP-relative addressing
|
||||
; without a cross-section relocation warning.
|
||||
; extern "C" void* g_wakeStatePtr;
|
||||
global g_wakeStatePtr
|
||||
g_wakeStatePtr: dq 0
|
||||
|
||||
; ─── AcpiResumeLongMode ──────────────────────────────────────────────────
|
||||
; ═════════════════════════════════════════════════════════════════════════════
|
||||
; AcpiResumeLongMode
|
||||
; ═════════════════════════════════════════════════════════════════════════════
|
||||
; extern "C" void AcpiResumeLongMode(CpuState* stateArea)
|
||||
; rdi = pointer to CpuState structure
|
||||
;
|
||||
@@ -119,6 +127,12 @@ g_wakeStatePtr: dq 0
|
||||
;
|
||||
global AcpiResumeLongMode
|
||||
AcpiResumeLongMode:
|
||||
; Progress: reached AcpiResumeLongMode (0xC1)
|
||||
mov al, 0xF2
|
||||
out 0x70, al
|
||||
mov al, 0xC1
|
||||
out 0x71, al
|
||||
|
||||
; At entry: rdi = CpuState*, RSP = trampoline stack (unmapped junk),
|
||||
; CR3 = kernel PML4 (loaded by trampoline), CS = trampoline selector.
|
||||
;
|
||||
@@ -140,6 +154,12 @@ AcpiResumeLongMode:
|
||||
retfq
|
||||
.reload_cs:
|
||||
|
||||
; Progress: GDT + CS reloaded (0xC2)
|
||||
mov al, 0xF2
|
||||
out 0x70, al
|
||||
mov al, 0xC2
|
||||
out 0x71, al
|
||||
|
||||
; Reload data segment registers with kernel data selector
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
@@ -151,11 +171,28 @@ AcpiResumeLongMode:
|
||||
; Restore IDT
|
||||
lidt [rdi + 0xB2]
|
||||
|
||||
; Restore CR4 before CR3 — PGE, PAE, OSFXSR etc. must be set before
|
||||
; the page table switch. The trampoline only set minimal bits (PAE).
|
||||
; Real hardware needs the full saved CR4 (especially PGE for TLB
|
||||
; correctness and WP in CR0 for write-protect enforcement).
|
||||
mov rax, [rdi + 0xA0]
|
||||
mov cr4, rax
|
||||
|
||||
; Restore CR0 (WP, NE, etc.)
|
||||
mov rax, [rdi + 0x98]
|
||||
mov cr0, rax
|
||||
|
||||
; Restore saved CR3 (process PML4). The trampoline used the kernel
|
||||
; master PML4 to get here; now switch to the actual saved context.
|
||||
mov rax, [rdi + 0x90]
|
||||
mov cr3, rax
|
||||
|
||||
; Progress: IDT + CR4/CR0/CR3 restored (0xC3)
|
||||
mov al, 0xF2
|
||||
out 0x70, al
|
||||
mov al, 0xC3
|
||||
out 0x71, al
|
||||
|
||||
; Restore general-purpose registers (skip RSP — already restored above)
|
||||
mov rbx, [rdi + 0x08]
|
||||
mov rcx, [rdi + 0x10]
|
||||
@@ -176,9 +213,17 @@ AcpiResumeLongMode:
|
||||
push rax
|
||||
popfq
|
||||
|
||||
; Progress: about to call AcpiResumeEntry (0xC4)
|
||||
mov al, 0xF2
|
||||
out 0x70, al
|
||||
mov al, 0xC4
|
||||
out 0x71, al
|
||||
|
||||
; Restore rdi last
|
||||
mov rdi, [rdi + 0x28]
|
||||
|
||||
; Return 0 = "resumed from S3"
|
||||
xor rax, rax
|
||||
ret
|
||||
; Instead of returning to Suspend() via ret (which is fragile due to
|
||||
; compiler stack frame assumptions), jump directly to a dedicated C
|
||||
; resume function. This is how Linux and other kernels handle S3 resume.
|
||||
extern AcpiResumeEntry
|
||||
jmp AcpiResumeEntry
|
||||
|
||||
+77
-59
@@ -6,72 +6,52 @@
|
||||
#include "Panic.hpp"
|
||||
#include "../CppLib/BoxUI.hpp"
|
||||
|
||||
void Panic(const char *meditationString, System::PanicFrame* frame) {
|
||||
const int boxWidth = 72;
|
||||
static constexpr int BoxWidth = 72;
|
||||
|
||||
// Header
|
||||
kerr << BOXUI_ANSI_RED_BG << BOXUI_ANSI_WHITE_FG << BOXUI_ANSI_BOLD << "\n";
|
||||
kerr << BOXUI_TL;
|
||||
for (int i = 0; i < boxWidth - 2; ++i) kerr << BOXUI_H;
|
||||
kerr << BOXUI_TR << "\n";
|
||||
PrintBoxedLine(kerr, "!!! KERNEL PANIC !!!", boxWidth, true);
|
||||
PrintBoxedLine(kerr, "", boxWidth);
|
||||
PrintBoxedLine(kerr, "System halted. Please reboot.", boxWidth, true);
|
||||
PrintBoxedLine(kerr, "", boxWidth);
|
||||
PrintBoxedSeparator(kerr, boxWidth);
|
||||
PrintBoxedLine(kerr, "Meditation:", boxWidth, true);
|
||||
PrintBoxedLine(kerr, meditationString, boxWidth);
|
||||
PrintBoxedLine(kerr, "", boxWidth);
|
||||
static void PrintHorizontalEdge(const char* left, const char* right) {
|
||||
kerr << left;
|
||||
for (int i = 0; i < BoxWidth - 2; ++i) kerr << BOXUI_H;
|
||||
kerr << right << "\n";
|
||||
}
|
||||
|
||||
#if defined (__x86_64__)
|
||||
if (frame != nullptr) {
|
||||
PrintBoxedSeparator(kerr, boxWidth);
|
||||
PrintBoxedLine(kerr, "CPU State:", boxWidth, true);
|
||||
PrintBoxedHex(kerr, "Interrupt Vector", frame->InterruptVector, boxWidth);
|
||||
static void PrintPageFaultInfo(System::PanicFrame*& frame) {
|
||||
auto* pf = (System::PageFaultPanicFrame*)frame;
|
||||
frame = (System::PanicFrame*)&pf->IP;
|
||||
|
||||
if (frame->InterruptVector == 0xE) {
|
||||
auto pf_frame = (System::PageFaultPanicFrame*)frame;
|
||||
frame = (System::PanicFrame*)&pf_frame->IP;
|
||||
uint64_t cr2;
|
||||
asm volatile("mov %%cr2, %0" : "=r"(cr2));
|
||||
PrintBoxedHex(kerr, "Faulting Address (CR2)", cr2, BoxWidth);
|
||||
|
||||
// CR2 holds the faulting virtual address for page faults
|
||||
uint64_t cr2;
|
||||
asm volatile("mov %%cr2, %0" : "=r"(cr2));
|
||||
PrintBoxedHex(kerr, "Faulting Address (CR2)", cr2, boxWidth);
|
||||
PrintBoxedLine(kerr, "Page Fault Error:", BoxWidth, true);
|
||||
PrintBoxedDec(kerr, "Present", pf->PageFaultError.Present, BoxWidth);
|
||||
PrintBoxedDec(kerr, "Write", pf->PageFaultError.Write, BoxWidth);
|
||||
PrintBoxedDec(kerr, "User", pf->PageFaultError.User, BoxWidth);
|
||||
PrintBoxedDec(kerr, "Reserved Write", pf->PageFaultError.ReservedWrite, BoxWidth);
|
||||
PrintBoxedDec(kerr, "Instruction Fetch",pf->PageFaultError.InstructionFetch, BoxWidth);
|
||||
PrintBoxedDec(kerr, "Protection Key", pf->PageFaultError.ProtectionKey, BoxWidth);
|
||||
PrintBoxedDec(kerr, "Shadow Stack", pf->PageFaultError.ShadowStack, BoxWidth);
|
||||
PrintBoxedDec(kerr, "SGX", pf->PageFaultError.SGX, BoxWidth);
|
||||
}
|
||||
|
||||
PrintBoxedLine(kerr, "Page Fault Error:", boxWidth, true);
|
||||
PrintBoxedDec(kerr, "Present", pf_frame->PageFaultError.Present, boxWidth);
|
||||
PrintBoxedDec(kerr, "Write", pf_frame->PageFaultError.Write, boxWidth);
|
||||
PrintBoxedDec(kerr, "User", pf_frame->PageFaultError.User, boxWidth);
|
||||
PrintBoxedDec(kerr, "Reserved Write", pf_frame->PageFaultError.ReservedWrite, boxWidth);
|
||||
PrintBoxedDec(kerr, "Instruction Fetch", pf_frame->PageFaultError.InstructionFetch, boxWidth);
|
||||
PrintBoxedDec(kerr, "Protection Key", pf_frame->PageFaultError.ProtectionKey, boxWidth);
|
||||
PrintBoxedDec(kerr, "Shadow Stack", pf_frame->PageFaultError.ShadowStack, boxWidth);
|
||||
PrintBoxedDec(kerr, "SGX", pf_frame->PageFaultError.SGX, boxWidth);
|
||||
} else if (frame->InterruptVector == 0xD) {
|
||||
auto gpf_frame = (System::GPFPanicFrame*)frame;
|
||||
frame = (System::PanicFrame*)&frame->IP;
|
||||
PrintBoxedLine(kerr, "General Protection Fault:", boxWidth, true);
|
||||
PrintBoxedDec(kerr, "Error Code", gpf_frame->GeneralProtectionFaultError, boxWidth);
|
||||
}
|
||||
static void PrintGPFInfo(System::PanicFrame*& frame) {
|
||||
auto* gpf = (System::GPFPanicFrame*)frame;
|
||||
frame = (System::PanicFrame*)&gpf->IP;
|
||||
|
||||
PrintBoxedSeparator(kerr, boxWidth);
|
||||
PrintBoxedLine(kerr, "Registers:", boxWidth, true);
|
||||
PrintBoxedHex(kerr, "Instruction Pointer", frame->IP, boxWidth);
|
||||
PrintBoxedHex(kerr, "Code Segment", frame->CS, boxWidth);
|
||||
PrintBoxedHex(kerr, "Flags", frame->Flags, boxWidth);
|
||||
PrintBoxedHex(kerr, "Stack Pointer", frame->SP, boxWidth);
|
||||
PrintBoxedHex(kerr, "Stack Segment", frame->SS, boxWidth);
|
||||
}
|
||||
#endif
|
||||
PrintBoxedLine(kerr, "General Protection Fault:", BoxWidth, true);
|
||||
PrintBoxedDec(kerr, "Error Code", gpf->GeneralProtectionFaultError, BoxWidth);
|
||||
}
|
||||
|
||||
PrintBoxedLine(kerr, "", boxWidth);
|
||||
|
||||
// Footer
|
||||
kerr << BOXUI_BL;
|
||||
for (int i = 0; i < boxWidth - 2; ++i) kerr << BOXUI_H;
|
||||
kerr << BOXUI_BR << "\n";
|
||||
kerr << BOXUI_ANSI_RESET;
|
||||
static void PrintRegisters(System::PanicFrame* frame) {
|
||||
PrintBoxedSeparator(kerr, BoxWidth);
|
||||
PrintBoxedLine(kerr, "Registers:", BoxWidth, true);
|
||||
PrintBoxedHex(kerr, "Instruction Pointer", frame->IP, BoxWidth);
|
||||
PrintBoxedHex(kerr, "Code Segment", frame->CS, BoxWidth);
|
||||
PrintBoxedHex(kerr, "Flags", frame->Flags, BoxWidth);
|
||||
PrintBoxedHex(kerr, "Stack Pointer", frame->SP, BoxWidth);
|
||||
PrintBoxedHex(kerr, "Stack Segment", frame->SS, BoxWidth);
|
||||
}
|
||||
|
||||
[[noreturn]] static void Halt() {
|
||||
while (true) {
|
||||
#if defined (__x86_64__)
|
||||
asm ("cli");
|
||||
@@ -82,4 +62,42 @@ void Panic(const char *meditationString, System::PanicFrame* frame) {
|
||||
asm ("idle 0");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Panic(const char *meditationString, System::PanicFrame* frame) {
|
||||
// Header
|
||||
kerr << BOXUI_ANSI_RED_BG << BOXUI_ANSI_WHITE_FG << BOXUI_ANSI_BOLD << "\n";
|
||||
PrintHorizontalEdge(BOXUI_TL, BOXUI_TR);
|
||||
PrintBoxedLine(kerr, "!!! KERNEL PANIC !!!", BoxWidth, true);
|
||||
PrintBoxedLine(kerr, "", BoxWidth);
|
||||
PrintBoxedLine(kerr, "System halted. Please reboot.", BoxWidth, true);
|
||||
PrintBoxedLine(kerr, "", BoxWidth);
|
||||
|
||||
// Meditation string
|
||||
PrintBoxedSeparator(kerr, BoxWidth);
|
||||
PrintBoxedLine(kerr, meditationString, BoxWidth);
|
||||
PrintBoxedLine(kerr, "", BoxWidth);
|
||||
|
||||
// CPU state (x86_64 only)
|
||||
#if defined (__x86_64__)
|
||||
if (frame != nullptr) {
|
||||
PrintBoxedSeparator(kerr, BoxWidth);
|
||||
PrintBoxedLine(kerr, "CPU State:", BoxWidth, true);
|
||||
PrintBoxedHex(kerr, "Interrupt Vector", frame->InterruptVector, BoxWidth);
|
||||
|
||||
if (frame->InterruptVector == 0xE)
|
||||
PrintPageFaultInfo(frame);
|
||||
else if (frame->InterruptVector == 0xD)
|
||||
PrintGPFInfo(frame);
|
||||
|
||||
PrintRegisters(frame);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Footer
|
||||
PrintBoxedLine(kerr, "", BoxWidth);
|
||||
PrintHorizontalEdge(BOXUI_BL, BOXUI_BR);
|
||||
kerr << BOXUI_ANSI_RESET;
|
||||
|
||||
Halt();
|
||||
}
|
||||
|
||||
+32
-2
@@ -11,6 +11,7 @@
|
||||
#include <Platform/Registers.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
#include <Memory/PageFrameAllocator.hpp>
|
||||
#include <Sched/Scheduler.hpp>
|
||||
|
||||
namespace Hal {
|
||||
constexpr auto InterruptGate = 0x8E;
|
||||
@@ -53,11 +54,40 @@ namespace Hal {
|
||||
"Reserved"
|
||||
};
|
||||
|
||||
// Exceptions that push a hardware error code before IP/CS/FLAGS/SP/SS
|
||||
static bool ExceptionHasErrorCode(uint8_t vector) {
|
||||
return vector == 8 || (vector >= 10 && vector <= 14)
|
||||
|| vector == 17 || vector == 21 || vector == 29 || vector == 30;
|
||||
}
|
||||
|
||||
// Extract CS from the interrupt frame. For error-code exceptions, the
|
||||
// error code sits at offset 0, shifting IP to +8 and CS to +16.
|
||||
static uint64_t GetExceptionCS(uint8_t vector, System::PanicFrame* frame) {
|
||||
if (ExceptionHasErrorCode(vector)) {
|
||||
return *(uint64_t*)((uint8_t*)frame + 16);
|
||||
}
|
||||
return frame->CS;
|
||||
}
|
||||
|
||||
template<size_t i>
|
||||
__attribute__((interrupt)) void ExceptionHandler(System::PanicFrame* frame)
|
||||
{
|
||||
frame->InterruptVector = i;
|
||||
Panic(ExceptionStrings[i], frame);
|
||||
uint64_t cs = GetExceptionCS(i, frame);
|
||||
|
||||
// If the fault originated in user-mode (ring 3), kill the process
|
||||
// instead of panicking the entire system.
|
||||
if ((cs & 3) == 3 && Sched::GetCurrentPid() >= 0) {
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
Kt::KernelLogStream(Kt::ERROR, "Exception")
|
||||
<< ExceptionStrings[i] << " in process \""
|
||||
<< proc->name << "\" (pid " << proc->pid
|
||||
<< ") - process terminated";
|
||||
Sched::ExitProcess();
|
||||
__builtin_unreachable();
|
||||
} else {
|
||||
frame->InterruptVector = i;
|
||||
Panic(ExceptionStrings[i], frame);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadIDT(IDTRStruct& idtr) {
|
||||
|
||||
Reference in New Issue
Block a user