Files
MontaukOS/kernel/src/ACPI/AML/AmlResource.cpp
T

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;
}
};
};