feat: power down dGPUs on hybrid graphics laptops
This commit is contained in:
1 parent
7f12e1a150
commit
24f88ec4c6
23 files changed
+4325
-1130
No files matched your search
@@ -9,6 +9,8 @@
|
||||
#include <ACPI/ACPI.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
#include <Memory/HHDM.hpp>
|
||||
#include <Memory/Heap.hpp>
|
||||
|
||||
using namespace Kt;
|
||||
|
||||
@@ -239,15 +241,144 @@ namespace Hal {
|
||||
// Full interpreter initialization
|
||||
|
||||
// ============================================================================
|
||||
void InitializeInterpreter(void* dsdtData) {
|
||||
void InitializeInterpreter(void* dsdtData, ACPI::CommonSDTHeader* xsdt) {
|
||||
auto& interp = GetInterpreter();
|
||||
if (!interp.LoadTable(dsdtData)) {
|
||||
KernelLogStream(ERROR, "AML") << "Failed to load DSDT into AML interpreter";
|
||||
}
|
||||
|
||||
// SSDTs extend the DSDT's namespace and are loaded after it, in
|
||||
// XSDT order. They are where most laptops keep CPU power tables,
|
||||
// and -- the reason they matter here -- the discrete GPU's power
|
||||
// control (_PR3 power resources, _PS3, NVIDIA/AMD _DSM).
|
||||
int ssdts = 0;
|
||||
if (xsdt) {
|
||||
uint32_t entryCount = (xsdt->Length - sizeof(ACPI::CommonSDTHeader)) / 8;
|
||||
auto* entries = (uint64_t*)((uint64_t)xsdt + sizeof(ACPI::CommonSDTHeader));
|
||||
for (uint32_t i = 0; i < entryCount; i++) {
|
||||
if (entries[i] == 0) continue;
|
||||
auto* table = (ACPI::CommonSDTHeader*)Memory::HHDM(entries[i]);
|
||||
const char* sig = table->Signature;
|
||||
bool isSsdt = sig[0] == 'S' && sig[1] == 'S' && sig[2] == 'D' && sig[3] == 'T';
|
||||
bool isPsdt = sig[0] == 'P' && sig[1] == 'S' && sig[2] == 'D' && sig[3] == 'T';
|
||||
if (!isSsdt && !isPsdt) continue;
|
||||
if (table->Length <= sizeof(ACPI::CommonSDTHeader)) continue;
|
||||
if (interp.LoadTable(table)) ssdts++;
|
||||
}
|
||||
}
|
||||
KernelLogStream(OK, "AML") << "Loaded DSDT + " << base::dec << (uint64_t)ssdts
|
||||
<< " SSDT(s)";
|
||||
|
||||
auto& ns = interp.GetNamespace();
|
||||
KernelLogStream(OK, "AML") << "Namespace: " << base::dec
|
||||
<< (uint64_t)ns.NodeCount() << " / " << (uint64_t)ns.MaxCapacity() << " nodes (dynamic)";
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
|
||||
// Namespace initialization (_REG, _INI)
|
||||
|
||||
// ============================================================================
|
||||
static bool SegIs(const char* name, const char* seg) {
|
||||
return name[0] == seg[0] && name[1] == seg[1] &&
|
||||
name[2] == seg[2] && name[3] == seg[3];
|
||||
}
|
||||
|
||||
void InitializeNamespaceObjects() {
|
||||
auto& interp = GetInterpreter();
|
||||
if (!interp.IsInitialized()) return;
|
||||
auto& ns = interp.GetNamespace();
|
||||
|
||||
// Collect first, evaluate second: executing AML creates nodes,
|
||||
// which reallocates a parent's child array under a live walk.
|
||||
int32_t capacity = ns.NodeCount();
|
||||
auto* regs = (int32_t*)Memory::g_heap->Request(sizeof(int32_t) * capacity);
|
||||
auto* devices = (int32_t*)Memory::g_heap->Request(sizeof(int32_t) * capacity);
|
||||
if (!regs || !devices) {
|
||||
if (regs) Memory::g_heap->Free(regs);
|
||||
if (devices) Memory::g_heap->Free(devices);
|
||||
return;
|
||||
}
|
||||
int regCount = 0, deviceCount = 0;
|
||||
|
||||
ns.WalkDescendants(ns.RootIndex(), ObjectType::None,
|
||||
[&](int32_t idx, const NamespaceNode* node) {
|
||||
if (node->Obj.Type == ObjectType::Method && SegIs(node->Name, "_REG") &&
|
||||
regCount < capacity)
|
||||
regs[regCount++] = idx;
|
||||
if (node->Obj.Type == ObjectType::Device && deviceCount < capacity)
|
||||
devices[deviceCount++] = idx;
|
||||
});
|
||||
|
||||
// _REG(PCI_Config, connect). A _REG for another space checks Arg0
|
||||
// and ignores the call, as with the EC's.
|
||||
Object args[2]{};
|
||||
args[0].Type = ObjectType::Integer;
|
||||
args[0].Integer = (uint64_t)RegionSpace::PciConfig;
|
||||
args[1].Type = ObjectType::Integer;
|
||||
args[1].Integer = 1;
|
||||
for (int i = 0; i < regCount; i++) {
|
||||
Object result{};
|
||||
interp.EvaluateNode(regs[i], args, 2, result);
|
||||
}
|
||||
|
||||
// \_SB._INI runs before any device's.
|
||||
int32_t sb = ns.FindNode("\\_SB_");
|
||||
if (sb >= 0) {
|
||||
Object result{};
|
||||
if (interp.EvaluateChild(sb, "_INI", nullptr, 0, result))
|
||||
KernelLogStream(INFO, "AML") << "Ran \\_SB._INI";
|
||||
}
|
||||
|
||||
// Then each device, parents first (WalkDescendants is pre-order).
|
||||
// A device that is neither present nor functional takes its whole
|
||||
// subtree with it; one that is functional but absent skips only
|
||||
// its own _INI.
|
||||
static constexpr int MaxSkipped = 256;
|
||||
int32_t skipped[MaxSkipped];
|
||||
int skippedCount = 0;
|
||||
int ran = 0;
|
||||
|
||||
for (int i = 0; i < deviceCount; i++) {
|
||||
int32_t dev = devices[i];
|
||||
|
||||
bool underSkipped = false;
|
||||
for (int32_t p = ns.GetNode(dev) ? ns.GetNode(dev)->ParentIndex : -1;
|
||||
p > 0 && !underSkipped; p = ns.GetNode(p) ? ns.GetNode(p)->ParentIndex : -1) {
|
||||
for (int k = 0; k < skippedCount; k++)
|
||||
if (skipped[k] == p) { underSkipped = true; break; }
|
||||
}
|
||||
if (underSkipped) continue;
|
||||
|
||||
uint64_t sta = 0x0F;
|
||||
Object staObj{};
|
||||
if (interp.EvaluateChild(dev, "_STA", nullptr, 0, staObj) &&
|
||||
staObj.Type == ObjectType::Integer)
|
||||
sta = staObj.Integer;
|
||||
|
||||
if (!(sta & 0x01)) {
|
||||
if (!(sta & 0x08) && skippedCount < MaxSkipped)
|
||||
skipped[skippedCount++] = dev;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (interp.FindChild(dev, "_INI") < 0) continue;
|
||||
Object result{};
|
||||
bool ok = interp.EvaluateChild(dev, "_INI", nullptr, 0, result);
|
||||
ran++;
|
||||
|
||||
char path[128];
|
||||
ns.GetNodePath(dev, path, sizeof(path));
|
||||
KernelLogStream(DEBUG, "AML") << " _INI " << path << (ok ? "" : " FAILED");
|
||||
}
|
||||
|
||||
KernelLogStream(OK, "AML") << "Namespace initialized: " << base::dec
|
||||
<< (uint64_t)regCount << " _REG, " << (uint64_t)ran << " device _INI";
|
||||
|
||||
Memory::g_heap->Free(regs);
|
||||
Memory::g_heap->Free(devices);
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
Reference in new issue
Block a user