feat: expanded ACPI support, initial support for S3 sleep
This commit is contained in:
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* AmlResource.cpp
|
||||
* ACPI resource descriptor parsing
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "AmlResource.hpp"
|
||||
|
||||
namespace Hal {
|
||||
namespace AML {
|
||||
|
||||
// ── 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) ───
|
||||
static constexpr uint8_t LargeMemory24Tag = 0x01;
|
||||
static constexpr uint8_t LargeVendorTag = 0x04;
|
||||
static constexpr uint8_t LargeMemory32Tag = 0x05;
|
||||
static constexpr uint8_t LargeMem32FixedTag = 0x06;
|
||||
static constexpr uint8_t LargeDWordAddrTag = 0x07;
|
||||
static constexpr uint8_t LargeWordAddrTag = 0x08;
|
||||
static constexpr uint8_t LargeExtIrqTag = 0x09;
|
||||
static constexpr uint8_t LargeQWordAddrTag = 0x0A;
|
||||
static constexpr uint8_t LargeGpioTag = 0x0C;
|
||||
|
||||
static uint16_t Read16(const uint8_t* p) {
|
||||
return (uint16_t)p[0] | ((uint16_t)p[1] << 8);
|
||||
}
|
||||
|
||||
static uint32_t Read32(const uint8_t* p) {
|
||||
return (uint32_t)p[0] | ((uint32_t)p[1] << 8)
|
||||
| ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
|
||||
}
|
||||
|
||||
static uint64_t Read64(const uint8_t* p) {
|
||||
uint64_t val = 0;
|
||||
for (int i = 0; i < 8; i++)
|
||||
val |= (uint64_t)p[i] << (i * 8);
|
||||
return val;
|
||||
}
|
||||
|
||||
// Find the lowest set bit in a mask. Returns the bit number, or -1.
|
||||
static int FirstSetBit(uint16_t mask) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (mask & (1 << i)) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool ParseResourceTemplate(const uint8_t* data, uint32_t length, ResourceList& result) {
|
||||
result.Count = 0;
|
||||
uint32_t pos = 0;
|
||||
|
||||
while (pos < length && result.Count < MaxResources) {
|
||||
uint8_t tag = data[pos];
|
||||
|
||||
// End tag
|
||||
if ((tag & 0x80) == 0 && ((tag >> 3) & 0x0F) == SmallEndTag)
|
||||
break;
|
||||
|
||||
if (tag & 0x80) {
|
||||
// ── Large resource descriptor ───────────────────────
|
||||
uint8_t largeType = tag & 0x7F;
|
||||
if (pos + 3 > length) break;
|
||||
uint16_t resLen = Read16(&data[pos + 1]);
|
||||
uint32_t dataStart = pos + 3;
|
||||
uint32_t dataEnd = dataStart + resLen;
|
||||
if (dataEnd > length) break;
|
||||
|
||||
auto& res = result.Resources[result.Count];
|
||||
|
||||
switch (largeType) {
|
||||
case LargeExtIrqTag: {
|
||||
if (resLen < 2) break;
|
||||
res.Type = ResourceType::ExtendedIrq;
|
||||
uint8_t flags = data[dataStart];
|
||||
res.ExtendedIrq.Flags = flags;
|
||||
res.ExtendedIrq.Shareable = (flags >> 3) & 1;
|
||||
uint8_t irqCount = data[dataStart + 1];
|
||||
if (irqCount > 0 && resLen >= 6) {
|
||||
res.ExtendedIrq.Interrupt = Read32(&data[dataStart + 2]);
|
||||
} else {
|
||||
res.ExtendedIrq.Interrupt = 0;
|
||||
}
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
case LargeMemory32Tag: {
|
||||
if (resLen < 17) break;
|
||||
res.Type = ResourceType::Memory32;
|
||||
res.Memory32.ReadWrite = data[dataStart] & 1;
|
||||
res.Memory32.Base = Read32(&data[dataStart + 1]);
|
||||
// Max = data[dataStart + 5..8]
|
||||
// Alignment = data[dataStart + 9..12]
|
||||
res.Memory32.Length = Read32(&data[dataStart + 13]);
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
case LargeMem32FixedTag: {
|
||||
if (resLen < 9) break;
|
||||
res.Type = ResourceType::Memory32;
|
||||
res.Memory32.ReadWrite = data[dataStart] & 1;
|
||||
res.Memory32.Base = Read32(&data[dataStart + 1]);
|
||||
res.Memory32.Length = Read32(&data[dataStart + 5]);
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
case LargeDWordAddrTag: {
|
||||
if (resLen < 23) break;
|
||||
res.Type = ResourceType::DWordAddress;
|
||||
// ResourceType at dataStart+0, GenFlags at +1, TypeFlags at +2
|
||||
res.AddressSpace.GranularityMin = Read32(&data[dataStart + 3]);
|
||||
res.AddressSpace.GranularityMax = Read32(&data[dataStart + 7]);
|
||||
// Min at +7, Max at +11, Translation at +15, Length at +19
|
||||
res.AddressSpace.Base = Read32(&data[dataStart + 7]);
|
||||
res.AddressSpace.Length = Read32(&data[dataStart + 19]);
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
case LargeQWordAddrTag: {
|
||||
if (resLen < 43) break;
|
||||
res.Type = ResourceType::QWordAddress;
|
||||
res.AddressSpace.GranularityMin = Read64(&data[dataStart + 3]);
|
||||
res.AddressSpace.GranularityMax = Read64(&data[dataStart + 11]);
|
||||
res.AddressSpace.Base = Read64(&data[dataStart + 11]);
|
||||
res.AddressSpace.Length = Read64(&data[dataStart + 35]);
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
case LargeWordAddrTag: {
|
||||
if (resLen < 13) break;
|
||||
res.Type = ResourceType::WordAddress;
|
||||
res.AddressSpace.GranularityMin = Read16(&data[dataStart + 3]);
|
||||
res.AddressSpace.GranularityMax = Read16(&data[dataStart + 5]);
|
||||
res.AddressSpace.Base = Read16(&data[dataStart + 5]);
|
||||
res.AddressSpace.Length = Read16(&data[dataStart + 11]);
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Unknown large descriptor — skip
|
||||
break;
|
||||
}
|
||||
|
||||
pos = dataEnd;
|
||||
} else {
|
||||
// ── Small resource descriptor ───────────────────────
|
||||
uint8_t smallType = (tag >> 3) & 0x0F;
|
||||
uint8_t resLen = tag & 0x07;
|
||||
uint32_t dataStart = pos + 1;
|
||||
uint32_t dataEnd = dataStart + resLen;
|
||||
if (dataEnd > length) break;
|
||||
|
||||
auto& res = result.Resources[result.Count];
|
||||
|
||||
switch (smallType) {
|
||||
case SmallIrqTag: {
|
||||
if (resLen < 2) break;
|
||||
res.Type = ResourceType::Irq;
|
||||
res.Irq.Mask = Read16(&data[dataStart]);
|
||||
res.Irq.Flags = (resLen >= 3) ? data[dataStart + 2] : 0;
|
||||
int irq = FirstSetBit(res.Irq.Mask);
|
||||
res.Irq.Irq = (irq >= 0) ? (uint8_t)irq : 0;
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
case SmallDmaTag: {
|
||||
if (resLen < 2) break;
|
||||
res.Type = ResourceType::Dma;
|
||||
res.Dma.Mask = data[dataStart];
|
||||
res.Dma.Flags = data[dataStart + 1];
|
||||
int ch = FirstSetBit(res.Dma.Mask);
|
||||
res.Dma.Channel = (ch >= 0) ? (uint8_t)ch : 0;
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
case SmallIoPortTag: {
|
||||
if (resLen < 7) break;
|
||||
res.Type = ResourceType::IoPort;
|
||||
res.IoPort.Decode16Bit = data[dataStart] & 1;
|
||||
res.IoPort.Base = Read16(&data[dataStart + 1]);
|
||||
// Max = data[dataStart + 3..4]
|
||||
res.IoPort.Alignment = data[dataStart + 5];
|
||||
res.IoPort.Length = data[dataStart + 6];
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
case SmallFixedIoTag: {
|
||||
if (resLen < 3) break;
|
||||
res.Type = ResourceType::FixedIoPort;
|
||||
res.FixedIoPort.Base = Read16(&data[dataStart]);
|
||||
res.FixedIoPort.Length = data[dataStart + 2];
|
||||
result.Count++;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pos = dataEnd;
|
||||
}
|
||||
}
|
||||
|
||||
return result.Count > 0;
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user