223 lines
9.9 KiB
C++
223 lines
9.9 KiB
C++
/*
|
|
* 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;
|
|
}
|
|
|
|
};
|
|
};
|