128 lines
5.1 KiB
C++
128 lines
5.1 KiB
C++
/*
|
|
* AcpiWmi.cpp
|
|
* ACPI-WMI mapper (PNP0C14): find a WMI GUID's data block in _WDG and call
|
|
* its WMxx method
|
|
* Copyright (c) 2026 Daniel Hammer
|
|
*/
|
|
|
|
#include "AcpiWmi.hpp"
|
|
#include "AcpiDevices.hpp"
|
|
#include <ACPI/AML/AmlInterpreter.hpp>
|
|
#include <Libraries/Memory.hpp>
|
|
#include <CppLib/Stream.hpp>
|
|
#include <Terminal/Terminal.hpp>
|
|
|
|
using namespace Kt;
|
|
|
|
namespace Hal {
|
|
namespace AcpiWmi {
|
|
|
|
static constexpr const char* HidWmi = "PNP0C14";
|
|
static constexpr uint32_t WdgEntrySize = 20; // GUID, id, count, flags
|
|
|
|
static int HexDigit(char c) {
|
|
if (c >= '0' && c <= '9') return c - '0';
|
|
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
|
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
|
return -1;
|
|
}
|
|
|
|
bool ParseGuid(const char* text, uint8_t out[16]) {
|
|
// Bytes in text order, then the first three fields byte-swapped.
|
|
uint8_t bytes[16];
|
|
int count = 0;
|
|
for (int i = 0; text[i]; i++) {
|
|
if (text[i] == '-') continue;
|
|
int hi = HexDigit(text[i]);
|
|
int lo = text[i + 1] ? HexDigit(text[i + 1]) : -1;
|
|
if (hi < 0 || lo < 0 || count == 16) return false;
|
|
bytes[count++] = (uint8_t)(hi << 4 | lo);
|
|
i++;
|
|
}
|
|
if (count != 16) return false;
|
|
static constexpr int order[16] = {3, 2, 1, 0, 5, 4, 7, 6,
|
|
8, 9, 10, 11, 12, 13, 14, 15};
|
|
for (int i = 0; i < 16; i++) out[i] = bytes[order[i]];
|
|
return true;
|
|
}
|
|
|
|
static bool SearchDevice(int32_t device, const uint8_t guid[16], Block& out) {
|
|
auto& interp = AML::GetInterpreter();
|
|
AML::Object wdg{};
|
|
if (!interp.EvaluateChild(device, "_WDG", nullptr, 0, wdg)
|
|
|| wdg.Type != AML::ObjectType::Buffer)
|
|
return false;
|
|
|
|
// The interpreter caps buffers; a _WDG that fills the cap may have
|
|
// lost entries past it.
|
|
if (wdg.Buffer.Length >= (uint32_t)AML::MaxBufferLen) {
|
|
char path[128];
|
|
interp.GetNamespace().GetNodePath(device, path, sizeof(path));
|
|
KernelLogStream(WARNING, "WMI") << path << "._WDG may be truncated ("
|
|
<< base::dec << (uint64_t)wdg.Buffer.Length << " bytes read)";
|
|
}
|
|
|
|
for (uint32_t off = 0; off + WdgEntrySize <= wdg.Buffer.Length; off += WdgEntrySize) {
|
|
const uint8_t* entry = wdg.Buffer.Data + off;
|
|
if (memcmp(entry, guid, 16) != 0) continue;
|
|
out.Device = device;
|
|
out.ObjectId[0] = (char)entry[16];
|
|
out.ObjectId[1] = (char)entry[17];
|
|
out.InstanceCount = entry[18];
|
|
out.Flags = entry[19];
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Find(const uint8_t guid[16], Block& out) {
|
|
auto& interp = AML::GetInterpreter();
|
|
if (!interp.IsInitialized()) return false;
|
|
auto& ns = interp.GetNamespace();
|
|
|
|
bool found = false;
|
|
ns.WalkDescendants(ns.RootIndex(), AML::ObjectType::Device,
|
|
[&](int32_t idx, const AML::NamespaceNode*) {
|
|
if (found) return;
|
|
char hid[16];
|
|
if (!AcpiDevices::EvaluateHid(idx, hid, sizeof(hid))) return;
|
|
if (memcmp(hid, HidWmi, sizeof("PNP0C14")) != 0) return;
|
|
if (!(AcpiDevices::EvaluateSta(idx) & AcpiDevices::STA_PRESENT)) return;
|
|
found = SearchDevice(idx, guid, out);
|
|
});
|
|
return found;
|
|
}
|
|
|
|
bool EvaluateMethod(const Block& block, uint8_t instance, uint32_t methodId,
|
|
const uint8_t* input, uint32_t inputLength,
|
|
AML::Object& result) {
|
|
if (!(block.Flags & FlagMethod) || instance >= block.InstanceCount)
|
|
return false;
|
|
|
|
AML::Object args[3];
|
|
args[0].Type = AML::ObjectType::Integer;
|
|
args[0].Integer = instance;
|
|
args[1].Type = AML::ObjectType::Integer;
|
|
args[1].Integer = methodId;
|
|
if (block.Flags & FlagString) {
|
|
uint32_t n = inputLength;
|
|
if (n >= (uint32_t)AML::MaxStringLen) n = AML::MaxStringLen - 1;
|
|
args[2].Type = AML::ObjectType::String;
|
|
if (n) memcpy(args[2].String.Data, input, n);
|
|
args[2].String.Data[n] = '\0';
|
|
args[2].String.Length = (uint16_t)n;
|
|
} else {
|
|
uint32_t n = inputLength;
|
|
if (n > (uint32_t)AML::MaxBufferLen) n = AML::MaxBufferLen;
|
|
args[2].Type = AML::ObjectType::Buffer;
|
|
if (n) memcpy(args[2].Buffer.Data, input, n);
|
|
args[2].Buffer.Length = n;
|
|
}
|
|
|
|
char method[5] = {'W', 'M', block.ObjectId[0], block.ObjectId[1], '\0'};
|
|
return AML::GetInterpreter().EvaluateChild(block.Device, method, args, 3, result);
|
|
}
|
|
|
|
};
|
|
};
|