CloverBootloader/rEFIt_UEFI/Platform/AcpiPatcher.cpp

2609 lines
92 KiB
C++

/**
initial concept of DSDT patching by mackerintel
Re-Work by Slice 2011.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**/
#include <Platform.h> // Only use angled for Platform, else, xcode project won't compile
#include <Efi.h>
#include "../libeg/BmLib.h"
#include "StateGenerator.h"
#include "AmlGenerator.h"
#include "AcpiPatcher.h"
#include "FixBiosDsdt.h"
#include "platformdata.h"
#include "smbios.h"
#include "cpu.h"
#include "../Settings/Self.h"
#include "../Settings/SelfOem.h"
#include "Settings.h"
#define EBDA_BASE_ADDRESS 0x40E
#define HPET_SIGN SIGNATURE_32('H','P','E','T')
#define APIC_SIGN SIGNATURE_32('A','P','I','C')
#define MCFG_SIGN SIGNATURE_32('M','C','F','G')
#define ECDT_SIGN SIGNATURE_32('E','C','D','T')
#define DMAR_SIGN SIGNATURE_32('D','M','A','R')
#define BGRT_SIGN SIGNATURE_32('B','G','R','T')
#define SLIC_SIGN SIGNATURE_32('S','L','I','C')
#define APPLE_OEM_ID { 'A', 'P', 'P', 'L', 'E', ' ' }
#define APPLE_OEM_TABLE_ID { 'A', 'p', 'p', 'l', 'e', '0', '0', ' ' }
#define APPLE_CREATOR_ID { 'L', 'o', 'k', 'i' }
#define IGNORE_INDEX (~((UINTN)0)) // index ignored for matching (not ignored for >= 0)
#define AUTOMERGE_PASS1 1 // load just those that match existing entries
#define AUTOMERGE_PASS2 2 // load the rest
CONST CHAR8 oemID[6] = APPLE_OEM_ID;
CONST CHAR8 oemTableID[8] = APPLE_OEM_TABLE_ID;
CONST CHAR8 creatorID[4] = APPLE_CREATOR_ID;
//Global pointers
RSDT_TABLE *Rsdt = NULL;
XSDT_TABLE *Xsdt = NULL;
UINTN *XsdtReplaceSizes = NULL;
#define IndexFromEntryPtr(xsdt_or_rsdt, entry_ptr) \
((UINT32)(((CHAR8*)(entry_ptr) - (CHAR8*)&(xsdt_or_rsdt)->Entry)/sizeof((xsdt_or_rsdt)->Entry)))
#define IndexFromRsdtEntryPtr(entry_ptr) IndexFromEntryPtr(Rsdt, entry_ptr)
#define IndexFromXsdtEntryPtr(entry_ptr) IndexFromEntryPtr(Xsdt, entry_ptr)
#define TableCount(xsdt_or_rsdt) \
(((xsdt_or_rsdt)->Header.Length - sizeof(EFI_ACPI_DESCRIPTION_HEADER)) / sizeof((xsdt_or_rsdt)->Entry))
#define RsdtTableCount() TableCount(Rsdt)
#define XsdtTableCount() TableCount(Xsdt)
#define RsdtEntryPtrFromIndex(index) (&Rsdt->Entry + index)
#define XsdtEntryPtrFromIndex(index) ((UINT64*)((CHAR8*)&Xsdt->Entry + sizeof(UINT64)*(index)))
#define RsdtEntryFromIndex(index) (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)*RsdtEntryPtrFromIndex(index)
#define XsdtEntryFromIndex(index) (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)ReadUnaligned64(XsdtEntryPtrFromIndex(index))
UINT64 BiosDsdt;
UINT32 BiosDsdtLen;
UINT8 acpi_cpu_count;
CHAR8* acpi_cpu_name[acpi_cpu_max];
UINT8 acpi_cpu_processor_id[acpi_cpu_max];
CHAR8* acpi_cpu_score;
UINT64 machineSignature;
extern OPER_REGION *gRegions;
//-----------------------------------
UINT8 pmBlock[] = {
/*0070: 0xA5, 0x84, 0x00, 0x00,*/ 0x01, 0x08, 0x00, 0x01, 0xF9, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*0080:*/ 0x06, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x67, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x6F, 0xBF,
/*0090:*/ 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*00A0:*/ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x02,
/*00B0:*/ 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*00C0:*/ 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x50, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/*00D0:*/ 0x01, 0x20, 0x00, 0x03, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x01,
/*00E0:*/ 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
/*00F0:*/ 0x00, 0x00, 0x00, 0x00
};
// RehabMan: for stripping trailing spaces
static void stripTrailingSpaces(CHAR8* sgn)
{
CHAR8* lastNonSpace = sgn-1;
for (; *sgn; sgn++) {
if (*sgn != ' ') {
lastNonSpace = sgn;
}
}
lastNonSpace[1] = 0;
}
void* FindAcpiRsdPtr()
{
UINTN Address;
UINTN Index;
//
// First Seach 0x0e0000 - 0x0fffff for RSD Ptr
//
for (Address = 0xe0000; Address < 0xfffff; Address += 0x10) {
if (*(UINT64 *)(Address) == EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER_SIGNATURE) {
return (void *)Address;
}
}
//
// Search EBDA
//
volatile UINT16 * volatile ebda;
ebda = (volatile UINT16 *)(UINTN)(EBDA_BASE_ADDRESS);
Address = LShiftU64((UINT64)(*ebda), 4);
// Address = (*(UINT16 *)(UINTN)(EBDA_BASE_ADDRESS)) << 4;
if ( Address == 0 ) return 0; // Jief : if Address==0, the first access at *(UINT64 *)(Address + Index) is at address 0. It's supposed to crash.
for (Index = 0; Index < 0x400 ; Index += 16) {
if (*(UINT64 *)(Address + Index) == EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER_SIGNATURE) {
return (void *)Address;
}
}
return NULL;
}
UINT8 Checksum8(void* startPtr, UINT32 len)
{
UINT8 Value = 0;
UINT8 *ptr = (UINT8*)startPtr;
UINT8 *endPtr = ptr + len;
while (ptr < endPtr)
Value += *ptr++;
return Value;
}
void FixChecksum(EFI_ACPI_DESCRIPTION_HEADER* Table)
{
Table->Checksum = 0;
Table->Checksum = (UINT8)(256-Checksum8(Table, Table->Length));
}
void SaveMergedXsdtEntrySize(UINT32 Index, UINTN Size)
{
// manage XsdtReplaceSizes (free existing, store new)
if (XsdtReplaceSizes) {
if (XsdtReplaceSizes[Index]) {
// came from patched table in ACPI/patched, so free original pages
gBS->FreePages((EFI_PHYSICAL_ADDRESS)(UINTN)XsdtEntryFromIndex(Index), XsdtReplaceSizes[Index]);
XsdtReplaceSizes[Index] = 0;
}
XsdtReplaceSizes[Index] = EFI_SIZE_TO_PAGES(Size);
}
}
XBool IsXsdtEntryMerged(UINT32 Index)
{
if (!XsdtReplaceSizes) {
return false;
}
return 0 != XsdtReplaceSizes[Index];
}
UINT32* ScanRSDT2(UINT32 Signature, UINT64 TableId, UINTN MatchIndex)
{
if (!Rsdt || (0 == Signature && 0 == TableId)) {
return NULL;
}
UINT32 Count = RsdtTableCount();
UINTN MatchingCount = 0;
UINT32* Ptr = RsdtEntryPtrFromIndex(0);
UINT32* EndPtr = RsdtEntryPtrFromIndex(Count);
for (; Ptr < EndPtr; Ptr++) {
EFI_ACPI_DESCRIPTION_HEADER* Table = (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)*Ptr;
if (!Table) {
// skip NULL entry
continue;
}
if (0 == Signature || Table->Signature == Signature) {
if ((0 == TableId || Table->OemTableId == TableId) && (IGNORE_INDEX == MatchIndex || MatchingCount == MatchIndex)) {
return Ptr; // pointer to the matching entry
}
++MatchingCount;
}
}
return NULL;
}
UINT32* ScanRSDT(UINT32 Signature, UINT64 TableId)
{
return ScanRSDT2(Signature, TableId, IGNORE_INDEX);
}
UINT64* ScanXSDT2(UINT32 Signature, UINT64 TableId, UINTN MatchIndex)
{
if (!Xsdt || (0 == Signature && 0 == TableId)) {
return NULL;
}
UINT32 Count = XsdtTableCount();
UINTN MatchingCount = 0;
UINT64* Ptr = XsdtEntryPtrFromIndex(0);
UINT64* EndPtr = XsdtEntryPtrFromIndex(Count);
for (; Ptr < EndPtr; Ptr++) {
EFI_ACPI_DESCRIPTION_HEADER* Table = (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)ReadUnaligned64(Ptr);
if (!Table) {
// skip NULL entry
continue;
}
if (0 == Signature || Table->Signature == Signature) {
if ((0 == TableId || Table->OemTableId == TableId) && (IGNORE_INDEX == MatchIndex || MatchingCount == MatchIndex)) {
return Ptr; // pointer to the matching entry
}
++MatchingCount;
}
}
return NULL;
}
UINT64* ScanXSDT(UINT32 Signature, UINT64 TableId)
{
return ScanXSDT2(Signature, TableId, IGNORE_INDEX);
}
void AddDropTable(EFI_ACPI_DESCRIPTION_HEADER* Table, UINT32 Index)
{
CHAR8 sign[5], OTID[9];
sign[4] = 0;
OTID[8] = 0;
CopyMem(&sign[0], &Table->Signature, 4);
CopyMem(&OTID[0], &Table->OemTableId, 8);
//DBG(" Found table: %s %s len=%d\n", sign, OTID, (INT32)Table->Length);
DBG(" - [%02d]: %s %s len=%d\n", Index, sign, OTID, (INT32)Table->Length);
ACPI_DROP_TABLE* DropTable = new ACPI_DROP_TABLE;
DropTable->Signature = Table->Signature;
DropTable->TableId = Table->OemTableId;
DropTable->Length = Table->Length;
DropTable->MenuItem.BValue = false;
GlobalConfig.ACPIDropTables.AddReference(DropTable, true);
}
/*
* There is the case when OemTableId ended by space like "TableID ".
* We will not see the space but comparison will fail.
*/
UINT64 OemTableId_NoSpace(UINT64 origin)
{
UINT64 mask = 0xffULL << 56;
UINT64 space = 0x20ULL << 56;
do {
if ((mask & origin) == space) {
origin &= ~mask;
}
mask >>= 8;
space >>= 8;
} while (mask != 0 && ((mask & origin) == 0 || (mask & origin) == space));
return origin;
}
void GetAcpiTablesList()
{
DbgHeader("GetAcpiTablesList");
GetFadt(); //this is a first call to acpi, we need it to make a pointer to Xsdt
GlobalConfig.ACPIDropTables.setEmpty();
DBG("Get Acpi Tables List ");
/*
//for test
CHAR8 OTID[9];
OTID[8] = 0;
UINT64 TestTableId = 0x204449656c626154ULL; // <54 61 62 6c 65 49 44 20>
CopyMem(&OTID[0], &TestTableId, 8);
DBG("\n test id=0x%08llx as str=%s\n", TestTableId, OTID);
TestTableId = OemTableId_NoSpace(TestTableId);
DBG("after convert id=0x%08llx as str=%s\n", TestTableId, OTID);
result:
test id=0x204449656c626154 as str=TableID
after convert id=0x4449656c626154 as str=TableID
*/
if (Xsdt) {
UINT32 Count = XsdtTableCount();
UINT64* Ptr = XsdtEntryPtrFromIndex(0);
UINT64* EndPtr = XsdtEntryPtrFromIndex(Count);
DBG("from XSDT:\n");
for (; Ptr < EndPtr; Ptr++) {
EFI_ACPI_DESCRIPTION_HEADER* Table = (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)ReadUnaligned64(Ptr);
if (!Table) {
// skip NULL entry
continue;
}
AddDropTable(Table, IndexFromXsdtEntryPtr(Ptr));
}
} else if (Rsdt) {
DBG("from RSDT:\n");
UINT32 Count = RsdtTableCount();
UINT32* Ptr = RsdtEntryPtrFromIndex(0);
UINT32* EndPtr = RsdtEntryPtrFromIndex(Count);
for (; Ptr < EndPtr; Ptr++) {
EFI_ACPI_DESCRIPTION_HEADER* Table = (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)*Ptr;
if (!Table) {
// skip NULL entry
continue;
}
AddDropTable(Table, IndexFromRsdtEntryPtr(Ptr));
}
} else {
DBG(": [!] Error! ACPI not found:\n");
}
}
void DropTableFromRSDT(UINT32 Signature, UINT64 TableId, UINT32 Length)
{
if (!Rsdt || (0 == Signature && 0 == TableId)) {
return;
}
CHAR8 sign[5], OTID[9];
sign[4] = 0;
OTID[8] = 0;
CopyMem(&sign[0], &Signature, 4);
CopyMem(&OTID[0], &TableId, 8);
DBG("Drop tables from RSDT, SIGN=%s TableID=%s Length=%d\n", sign, OTID, (INT32)Length);
UINT32 Count = RsdtTableCount();
//DBG(" Rsdt has tables count=%d\n", Count);
UINT32* Ptr = RsdtEntryPtrFromIndex(0);
UINT32* EndPtr = RsdtEntryPtrFromIndex(Count);
for (; Ptr < EndPtr; Ptr++) {
EFI_ACPI_DESCRIPTION_HEADER* Table = (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)*Ptr;
if (!Table) {
// skip NULL entry
continue;
}
CopyMem(&sign[0], &Table->Signature, 4);
CopyMem(&OTID[0], &Table->OemTableId, 8);
//DBG(" Found table: %s %s\n", sign, OTID);
if (!((Signature && Table->Signature == Signature) &&
(!TableId || OemTableId_NoSpace(Table->OemTableId) == TableId) &&
(!Length || Table->Length == Length))) {
continue;
}
if (IsXsdtEntryMerged(IndexFromXsdtEntryPtr(Ptr))) {
DBG(" attempt to drop already merged table[%d]: %s %s %d ignored\n", IndexFromXsdtEntryPtr(Ptr), sign, OTID, (INT32)Table->Length);
continue;
}
// drop matching table by simply replacing entry with NULL
*Ptr = 0;
DBG(" Table[%d]: %s %s %d dropped\n", IndexFromXsdtEntryPtr(Ptr), sign, OTID, (INT32)Table->Length);
}
}
void DropTableFromXSDT(UINT32 Signature, UINT64 TableId, UINT32 Length)
{
if (!Xsdt || (0 == Signature && 0 == TableId)) {
return;
}
CHAR8 sign[5], OTID[9];
sign[4] = 0;
OTID[8] = 0;
CopyMem(&sign[0], &Signature, 4);
CopyMem(&OTID[0], &TableId, 8);
DBG("Drop tables from XSDT, SIGN=%s TableID=%s Length=%d\n", sign, OTID, (INT32)Length);
UINT32 Count = XsdtTableCount();
//DBG(" Xsdt has tables count=%d\n", Count);
UINT64* Ptr = XsdtEntryPtrFromIndex(0);
UINT64* EndPtr = XsdtEntryPtrFromIndex(Count);
for (; Ptr < EndPtr; Ptr++) {
EFI_ACPI_DESCRIPTION_HEADER* Table = (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)ReadUnaligned64(Ptr);
if (!Table) {
// skip NULL entry
continue;
}
CopyMem(&sign[0], &Table->Signature, 4);
CopyMem(&OTID[0], &Table->OemTableId, 8);
//DBG(" Found table: %s %s\n", sign, OTID);
if (!((Signature && Table->Signature == Signature) &&
(!TableId || OemTableId_NoSpace(Table->OemTableId) == TableId) &&
(!Length || Table->Length == Length))) {
continue;
}
if (IsXsdtEntryMerged(IndexFromXsdtEntryPtr(Ptr))) {
DBG(" attempt to drop already merged table[%d]: %s %s %d ignored\n", IndexFromXsdtEntryPtr(Ptr), sign, OTID, (INT32)Table->Length);
continue;
}
// drop matching table by simply replacing entry with NULL
WriteUnaligned64(Ptr, 0);
DBG(" Table[%d]: %s %s %d dropped\n", IndexFromXsdtEntryPtr(Ptr), sign, OTID, (INT32)Table->Length);
}
}
// by cecekpawon, edited by Slice, further edits by RehabMan
XBool FixAsciiTableHeader(UINT8 *Str, UINTN Len)
{
XBool NonAscii = false;
UINT8* StrEnd = Str + Len;
for (; Str < StrEnd; Str++) {
if (!*Str) continue; // NUL is allowed
if (*Str < ' ') {
*Str = ' ';
NonAscii = true;
}
else if (*Str > 0x7e) {
*Str = '_';
NonAscii = true;
}
}
return NonAscii;
}
XBool PatchTableHeader(EFI_ACPI_DESCRIPTION_HEADER *Header)
{
XBool Ret1, Ret2, Ret3;
if ( !gSettings.ACPI.FixHeaders ) {
return false;
}
Ret1 = FixAsciiTableHeader((UINT8*)&Header->CreatorId, 4);
Ret2 = FixAsciiTableHeader((UINT8*)&Header->OemTableId, 8);
Ret3 = FixAsciiTableHeader((UINT8*)&Header->OemId, 6);
return (Ret1 || Ret2 || Ret3);
}
void PatchAllTables()
{
UINT32 Count = XsdtTableCount();
UINT64* Ptr = XsdtEntryPtrFromIndex(0);
UINT64* EndPtr = XsdtEntryPtrFromIndex(Count);
for (; Ptr < EndPtr; Ptr++) {
XBool Patched = false;
EFI_ACPI_DESCRIPTION_HEADER* Table = (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)ReadUnaligned64(Ptr);
if (!Table) {
// skip NULL entry
continue;
}
if (EFI_ACPI_3_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE == Table->Signature) {
// may be also EFI_ACPI_4_0_MULTIPLE_APIC_DESCRIPTION_TABLE_SIGNATURE?
continue; // will be patched elsewhere
}
//do new table with patched header
UINT32 Len = Table->Length;
EFI_PHYSICAL_ADDRESS BufferPtr = EFI_SYSTEM_TABLE_MAX_ADDRESS;
EFI_STATUS Status = gBS->AllocatePages(AllocateMaxAddress,
EfiACPIReclaimMemory,
EFI_SIZE_TO_PAGES(Len + 4096),
&BufferPtr);
if(EFI_ERROR(Status)) {
//DBG(" ... not patched\n");
continue;
}
EFI_ACPI_DESCRIPTION_HEADER* NewTable = (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)BufferPtr;
CopyMem(NewTable, Table, Len);
if ( gSettings.ACPI.FixHeaders ) {
// Merged tables already have the header patched, so no need to do it again
if (!IsXsdtEntryMerged(IndexFromXsdtEntryPtr(Ptr))) {
// table header NOT already patched
Patched = PatchTableHeader(NewTable);
}
}
if (NewTable->Signature == EFI_ACPI_4_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {
if (gSettings.ACPI.DSDT.DSDTPatchArray.size() > 0) {
DBG("Patching SSDTs: %zu patches each\n", gSettings.ACPI.DSDT.DSDTPatchArray.size());
// CHAR8 OTID[9];
// OTID[8] = 0;
// CopyMem(OTID, &NewTable->OemTableId, 8);
// DBG("Patching SSDT %s Length=%d\n", OTID, (INT32)Len);
for (UINT32 i = 0; i < gSettings.ACPI.DSDT.DSDTPatchArray.size(); i++) {
if ( gSettings.ACPI.DSDT.DSDTPatchArray[i].PatchDsdtFind.isEmpty() ) {
continue;
}
// DBG("%d. [%s]:", i, gSettings.PatchDsdtLabel[i]);
if (!gSettings.ACPI.DSDT.DSDTPatchArray[i].PatchDsdtMenuItem.BValue) {
// DBG(" disabled\n");
continue;
}
if ( gSettings.ACPI.DSDT.DSDTPatchArray[i].PatchDsdtTgt.isEmpty() ) {
Len = FixAny((UINT8*)NewTable, Len,
gSettings.ACPI.DSDT.DSDTPatchArray[i].PatchDsdtFind,
gSettings.ACPI.DSDT.DSDTPatchArray[i].PatchDsdtReplace,
gSettings.ACPI.DSDT.DSDTPatchArray[i].Skip,
gSettings.ACPI.DSDT.DSDTPatchArray[i].Count
);
//DBG(" OK\n");
}else{
//DBG("Patching: renaming in bridge\n");
Len = FixRenameByBridge2((UINT8*)NewTable, Len, gSettings.ACPI.DSDT.DSDTPatchArray[i].PatchDsdtTgt,
gSettings.ACPI.DSDT.DSDTPatchArray[i].PatchDsdtFind,
gSettings.ACPI.DSDT.DSDTPatchArray[i].PatchDsdtReplace,
gSettings.ACPI.DSDT.DSDTPatchArray[i].Skip,
gSettings.ACPI.DSDT.DSDTPatchArray[i].Count
);
}
}
}
// fixup length and checksum
NewTable->Length = Len;
RenameDevices((UINT8*)NewTable);
GetBiosRegions((UINT8*)NewTable); //take Regions from SSDT even if they will be dropped
Patched = true;
}
if (NewTable->Signature == MCFG_SIGN && gSettings.ACPI.FixMCFG) {
INTN Len1 = ((Len + 4 - 1) / 16 + 1) * 16 - 4;
CopyMem(NewTable, Table, Len1); //Len increased but less than EFI_PAGE
NewTable->Length = (UINT32)(UINTN)Len1; Patched = true;
}
if (Patched) {
WriteUnaligned64(Ptr, BufferPtr);
FixChecksum(NewTable);
}
else {
gBS->FreePages(BufferPtr, EFI_SIZE_TO_PAGES(Len + 4096));
}
}
}
EFI_STATUS InsertTable(void* TableEntry, UINTN Length)
{
if (!TableEntry) {
return EFI_NOT_FOUND;
}
EFI_PHYSICAL_ADDRESS BufferPtr = EFI_SYSTEM_TABLE_MAX_ADDRESS;
EFI_STATUS Status = gBS->AllocatePages (
AllocateMaxAddress,
EfiACPIReclaimMemory,
EFI_SIZE_TO_PAGES(Length),
&BufferPtr
);
//if success insert table pointer into ACPI tables
if(!EFI_ERROR(Status)) {
// DBG("page is allocated, write SSDT into\n");
EFI_ACPI_DESCRIPTION_HEADER* TableHeader = (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)BufferPtr;
CopyMem(TableHeader, TableEntry, Length);
// Now is a good time to fix the table header and checksum
PatchTableHeader(TableHeader);
FixChecksum(TableHeader);
//insert into RSDT
if (Rsdt) {
UINT32* Ptr = RsdtEntryPtrFromIndex(RsdtTableCount());
*Ptr = (UINT32)(UINTN)BufferPtr;
Rsdt->Header.Length += sizeof(UINT32);
//DBG("Rsdt->Length = %d\n", Rsdt->Header.Length);
}
//insert into XSDT
if (Xsdt) {
UINT64* Ptr = XsdtEntryPtrFromIndex(XsdtTableCount());
WriteUnaligned64(Ptr, BufferPtr);
Xsdt->Header.Length += sizeof(UINT64);
//DBG("Xsdt->Length = %d\n", Xsdt->Header.Length);
}
}
return Status;
}
UINTN IndexFromFileName(CONST CHAR16* FileName)
{
// FileName must be as "XXXX-number-..." or "XXXX-number.aml", such as "SSDT-9.aml", or "SSDT-11-SaSsdt.aml"
// But just checking for '-' or '.' following the number.
// search for '-'
UINTN Result = IGNORE_INDEX;
CONST CHAR16* temp = FileName;
for (; *temp != 0 && *temp != '-'; temp++);
if ('-' == *temp && 4 == temp-FileName) {
++temp;
if (*temp >= '0' && *temp <= '9') {
Result = 0;
for (; *temp >= '0' && *temp <= '9'; temp++) {
Result *= 10;
Result += *temp - '0';
}
// a FileName such as "SSDT-4x30s.aml" is not considered as "SSDT-4.aml"
if ('.' != *temp && '-' != *temp)
Result = IGNORE_INDEX;
}
}
return Result;
}
EFI_STATUS ReplaceOrInsertTable(void* TableEntry, UINTN Length, UINTN MatchIndex, INTN Pass)
{
if (!TableEntry) {
return EFI_NOT_FOUND;
}
EFI_PHYSICAL_ADDRESS BufferPtr = EFI_SYSTEM_TABLE_MAX_ADDRESS;
EFI_STATUS Status = gBS->AllocatePages (
AllocateMaxAddress,
EfiACPIReclaimMemory,
EFI_SIZE_TO_PAGES(Length),
&BufferPtr
);
EFI_ACPI_DESCRIPTION_HEADER* hdr = (EFI_ACPI_DESCRIPTION_HEADER*)TableEntry;
//if success insert or replace table pointer into ACPI tables
if(!EFI_ERROR(Status)) {
Status = EFI_ABORTED;
//DBG("page is allocated, write SSDT into\n");
EFI_ACPI_DESCRIPTION_HEADER* TableHeader = (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)BufferPtr;
CopyMem(TableHeader, TableEntry, Length);
#if 0 //REVIEW: seems as if Rsdt is always NULL for ReplaceOrInsertTable scenarios (macOS/OS X)
//insert/modify into RSDT
if (Rsdt) {
UINT32* Ptr = NULL;
if (hdr->Signature != EFI_ACPI_4_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE || MatchIndex != IGNORE_INDEX) {
// SSDT with target index or non-SSDT, try to find matching entry
Ptr = ScanRSDT2(hdr->Signature, hdr->OemTableId, MatchIndex);
}
if (Ptr) {
*Ptr = (UINT32)(UINTN)BufferPtr;
Status = EFI_SUCCESS;
} else if (AUTOMERGE_PASS2 == Pass) {
Ptr = RsdtEntryPtrFromIndex(RsdtTableCount());
*Ptr = (UINT32)(UINTN)BufferPtr;
Rsdt->Header.Length += sizeof(UINT32);
//DBG("Rsdt->Length = %d\n", Rsdt->Header.Length);
Status = EFI_SUCCESS;
}
}
#endif
//insert/modify into XSDT
if (Xsdt) {
UINT64* Ptr = NULL;
if (hdr->Signature != EFI_ACPI_4_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE || MatchIndex != IGNORE_INDEX) {
// SSDT with target index or non-SSDT, try to find matching entry
Ptr = ScanXSDT2(hdr->Signature, hdr->OemTableId, MatchIndex);
}
// Now is a good time to fix the table header and checksum (*MUST* be done after matching)
PatchTableHeader(TableHeader);
FixChecksum(TableHeader);
if (Ptr) {
UINT32 Index = IndexFromXsdtEntryPtr(Ptr);
DBG("@%llu ", (UINT64)Index);
// keep track of new table size in case it needs to be freed later
SaveMergedXsdtEntrySize(Index, Length);
WriteUnaligned64(Ptr, BufferPtr);
Status = EFI_SUCCESS;
} else if (AUTOMERGE_PASS2 == Pass) {
Ptr = XsdtEntryPtrFromIndex(XsdtTableCount());
WriteUnaligned64(Ptr, BufferPtr);
Xsdt->Header.Length += sizeof(UINT64);
//DBG("Xsdt->Length = %d\n", Xsdt->Header.Length);
Status = EFI_SUCCESS;
}
}
}
return Status;
}
void PreCleanupRSDT()
{
if (!Rsdt) {
return;
}
//REVIEW: really?
// Если адрес RSDT < адреса XSDT и хвост RSDT наползает на XSDT, то подрезаем хвост RSDT до начала XSDT
// English: If the RSDT address of the XSDT address and the tail of the RSDT crawls onto the XSDT, then we
// trim the RSDT tail before the XSDT starts
if ((UINTN)Rsdt < (UINTN)Xsdt && (UINTN)Rsdt + Rsdt->Header.Length > (UINTN)Xsdt) {
UINTN v = ((UINTN)Xsdt - (UINTN)Rsdt) & ~3;
Rsdt->Header.Length = (UINT32)v;
DBG("Cropped Rsdt->Header.Length=%d\n", (UINT32)Rsdt->Header.Length);
}
//REVIEW: why?
// terminate RSDT table at first double zero, if present
UINT32 Count = RsdtTableCount();
if (Count <= 2) {
return;
}
DBG("PreCleanup RSDT: count=%d, length=%d\n", Count, (UINT32)Rsdt->Header.Length);
UINT32* Ptr = RsdtEntryPtrFromIndex(0);
UINT32* EndPtr = RsdtEntryPtrFromIndex(Count-1);
for (; Ptr < EndPtr; Ptr++) {
if (0 == Ptr[0] && 0 == Ptr[1]) {
// double zero found, terminate RSDT entry table here
DBG("DoubleZero in RSDT table\n");
Rsdt->Header.Length = (UINT32)((CHAR8*)Ptr - (CHAR8*)Rsdt);
break;
}
}
DBG("PreCleanup RSDT, corrected RSDT: count=%d, length=%d\n", Count, (UINT32)Rsdt->Header.Length);
}
void PostCleanupRSDT()
{
if (!Rsdt) {
return;
}
// remove NULL entries from RSDT table
UINT32 Count = RsdtTableCount();
DBG("Cleanup RSDT: count=%d, length=%d\n", Count, (UINT32)Rsdt->Header.Length);
UINT32* Source = RsdtEntryPtrFromIndex(0);
UINT32* Dest = Source;
UINT32* EndPtr = RsdtEntryPtrFromIndex(Count);
while (Source < EndPtr) {
if (0 == *Source) {
// skip NULL entry
Source++;
continue;
}
*Dest++ = *Source++;
}
// fix header length
Rsdt->Header.Length = (UINT32)((CHAR8*)Dest - (CHAR8*)Rsdt);
Count = RsdtTableCount();
DBG("corrected RSDT: count=%d, length=%d\n", Count, (UINT32)Rsdt->Header.Length);
FixChecksum(&Rsdt->Header);
}
void PreCleanupXSDT()
{
UINT64 *Ptr, *EndPtr;
if (!Xsdt) {
return;
}
//REVIEW: why?
// terminate RSDT table at first double zero, if present
UINT32 Count = XsdtTableCount();
if (Count <= 2) {
return;
}
DBG("PreCleanup XSDT: count=%d, length=%d\n", Count, (UINT32)Xsdt->Header.Length);
Ptr = XsdtEntryPtrFromIndex(0);
EndPtr = XsdtEntryPtrFromIndex(Count-1);
for (; Ptr < EndPtr; Ptr++) {
if (0 == ReadUnaligned64(Ptr+0) && 0 == ReadUnaligned64(Ptr+1)) {
// double zero found, terminate XSDT entry table here
DBG("DoubleZero in XSDT table\n");
Xsdt->Header.Length = (UINT32)((CHAR8*)Ptr - (CHAR8*)Xsdt);
break;
}
}
DBG("PreCleanup XSDT, corrected XSDT: count=%d, length=%d\n", Count, (UINT32)Xsdt->Header.Length);
}
void PostCleanupXSDT()
{
UINT64 *Dest, *EndPtr, *Source;
if (!Xsdt) {
return;
}
// remove NULL entries from XSDT table
UINT32 Count = XsdtTableCount();
DBG("Cleanup XSDT: count=%d, length=%d\n", Count, (UINT32)Xsdt->Header.Length);
Source = XsdtEntryPtrFromIndex(0);
Dest = Source;
EndPtr = XsdtEntryPtrFromIndex(Count);
while (Source < EndPtr) {
if (0 == *Source) {
// skip NULL entry
Source++;
continue;
}
WriteUnaligned64(Dest++, ReadUnaligned64(Source++));
}
// fix header length
Xsdt->Header.Length = (UINT32)((CHAR8*)Dest - (CHAR8*)Xsdt);
Count = XsdtTableCount();
DBG("corrected XSDT count=%d, length=%d\n", Count, (UINT32)Xsdt->Header.Length);
FixChecksum(&Xsdt->Header);
}
/** Saves Buffer of Length to disk as OemDir\\DirName\\FileName. */
EFI_STATUS SaveBufferToDisk(void *Buffer, UINTN Length, CONST CHAR16 *DirName, CONST CHAR16 *FileName)
{
if (DirName == NULL || FileName == NULL) {
return EFI_INVALID_PARAMETER;
}
XStringW PathName = SWPrintf("%ls\\%ls", DirName, FileName);
EFI_STATUS Status = egSaveFile(&selfOem.getConfigDir(), PathName.wc_str(), Buffer, Length);
// Do not write outside OemDir
// if (EFI_ERROR(Status)) {
// Status = egSaveFile(NULL, PathName.wc_str(), Buffer, Length);
// }
return Status;
}
//
// Remembering saved tables
//
#define SAVED_TABLES_ALLOC_ENTRIES 64
void **mSavedTables = NULL;
UINTN mSavedTablesEntries = 0;
UINTN mSavedTablesNum = 0;
/** Returns true is TableEntry is already saved. */
XBool IsTableSaved(void *TableEntry)
{
UINTN Index;
if (mSavedTables != NULL) {
for (Index = 0; Index < mSavedTablesNum; Index++) {
if (mSavedTables[Index] == TableEntry) {
return true;
}
}
}
return false;
}
/** Adds TableEntry to mSavedTables if not already there. */
void MarkTableAsSaved(void *TableEntry)
{
//
// If mSavedTables does not exists yet - allocate it
//
if (mSavedTables == NULL) {
//DBG(" Allocaing mSavedTables");
mSavedTablesEntries = SAVED_TABLES_ALLOC_ENTRIES;
mSavedTablesNum = 0;
mSavedTables = (__typeof__(mSavedTables))AllocateZeroPool(sizeof(*mSavedTables) * mSavedTablesEntries);
if (mSavedTables == NULL) {
return;
}
}
//
// If TableEntry is not in mSavedTables - add it
//
//DBG(" MarkTableAsSaved %llx", TableEntry);
if (IsTableSaved(TableEntry)) {
// already saved
//DBG(" - already saved\n");
return;
}
//
// If mSavedTables is full - extend it
//
if (mSavedTablesNum + 1 >= mSavedTablesEntries) {
// not enough space
//DBG(" - extending mSavedTables from %d", mSavedTablesEntries);
mSavedTables = (__typeof__(mSavedTables))ReallocatePool(
sizeof(*mSavedTables) * mSavedTablesEntries,
sizeof(*mSavedTables) * (mSavedTablesEntries + SAVED_TABLES_ALLOC_ENTRIES),
mSavedTables
);
if (mSavedTables == NULL) {
return;
}
mSavedTablesEntries = mSavedTablesEntries + SAVED_TABLES_ALLOC_ENTRIES;
//DBG(" to %d", mSavedTablesEntries);
}
//
// Add TableEntry to mSavedTables
//
mSavedTables[mSavedTablesNum] = TableEntry;
//DBG(" - added to index %d\n", mSavedTablesNum);
mSavedTablesNum++;
}
#define AML_OP_NAME 0x08
#define AML_OP_PACKAGE 0x12
STATIC CHAR8 NameSSDT[] = {AML_OP_NAME, 'S', 'S', 'D', 'T', AML_OP_PACKAGE};
STATIC CHAR8 NameCSDT[] = {AML_OP_NAME, 'C', 'S', 'D', 'T', AML_OP_PACKAGE};
STATIC CHAR8 NameTSDT[] = {AML_OP_NAME, 'T', 'S', 'D', 'T', AML_OP_PACKAGE};
// OperationRegion (SSDT, SystemMemory, 0xDF5DAC18, 0x038C)
STATIC UINT8 NameSSDT2[] = {0x80, 0x53, 0x53, 0x44, 0x54};
// OperationRegion (CSDT, SystemMemory, 0xDF5DBE18, 0x84)
STATIC UINT8 NameCSDT2[] = {0x80, 0x43, 0x53, 0x44, 0x54};
//UINT32 get_size(UINT8 * An, UINT32 ); // Let borrow from FixBiosDsdt.
static XStringW GenerateFileName(CONST CHAR16* FileNamePrefix, UINTN SsdtCount, UINTN ChildCount, CHAR8 OemTableId[9])
// ChildCount == IGNORE_INDEX indicates normal SSDT
// SsdtCount == IGNORE_INDEX indicates dynamic SSDT in DSDT
// otherwise is child SSDT from normal SSDT
{
XStringW FileName;
CHAR8 Suffix[10]; // "-" + OemTableId + NUL
if (gSettings.ACPI.SSDT.NoOemTableId || 0 == OemTableId[0]) {
Suffix[0] = 0;
} else {
Suffix[0] = '-';
CopyMem(Suffix+1, OemTableId, 9);
}
if (IGNORE_INDEX == ChildCount) {
// normal SSDT
FileName = SWPrintf("%lsSSDT-%llu%s.aml", FileNamePrefix, SsdtCount, Suffix);
} else if (IGNORE_INDEX == SsdtCount) {
// dynamic SSDT in DSDT
FileName = SWPrintf("%lsSSDT-xDSDT_%llu%s.aml", FileNamePrefix, ChildCount, Suffix);
} else {
// dynamic SSDT in static SSDT
FileName = SWPrintf("%lsSSDT-x%llu_%llu%s.aml", FileNamePrefix, SsdtCount, ChildCount, Suffix);
}
return FileName;
}
void DumpChildSsdt(EFI_ACPI_DESCRIPTION_HEADER *TableEntry, CONST CHAR16 *DirName, CONST CHAR16 *FileNamePrefix, UINTN SsdtCount)
{
EFI_STATUS Status = EFI_SUCCESS;
INTN j, k, pacLen, pacCount;
CHAR8 Signature[5];
CHAR8 OemTableId[9];
UINTN adr, len;
UINT8 *Entry;
UINT8 *End;
UINT8 *pacBody;
INTN ChildCount = 0;
if (gSettings.ACPI.SSDT.NoDynamicExtract) {
return;
}
Entry = (UINT8*)TableEntry; //first entry is parent SSDT
End = Entry + TableEntry->Length;
while (Entry < End) {
if ((CompareMem(Entry, NameSSDT, sizeof (NameSSDT)) == 0) ||
(CompareMem(Entry, NameCSDT, sizeof (NameCSDT)) == 0) ||
(CompareMem(Entry, NameTSDT, sizeof (NameTSDT)) == 0)) {
pacLen = get_size(Entry, sizeof (NameSSDT));
pacBody = Entry + sizeof (NameSSDT) + (pacLen > 63 ? 2 : 1); // Our packages are not huge
pacCount = *pacBody++;
if (pacCount > 0 && pacCount % 3 == 0) {
pacCount /= 3;
DBG(" (Found hidden SSDT %lld pcs)\n", pacCount);
while (pacCount-- > 0) {
// Skip text marker and addr type tag
pacBody += 1 + 8 + 1 + 1;
adr = ReadUnaligned32((UINT32*)(pacBody));
len = 0;
pacBody += 4;
if (*pacBody == AML_CHUNK_DWORD) {
len = ReadUnaligned32((UINT32*)(pacBody + 1));
pacBody += 5;
} else if (*pacBody == AML_CHUNK_WORD) {
len = ReadUnaligned16((UINT16*)(pacBody + 1));
pacBody += 3;
}
// Take Signature and OemId for printing
CopyMem(&Signature[0], &((EFI_ACPI_DESCRIPTION_HEADER *)adr)->Signature, 4);
Signature[4] = 0;
CopyMem(&OemTableId[0], &((EFI_ACPI_DESCRIPTION_HEADER *)adr)->OemTableId, 8);
OemTableId[8] = 0;
stripTrailingSpaces(OemTableId);
int innLen = ((EFI_ACPI_DESCRIPTION_HEADER *)adr)->Length;
if (innLen < 0 || innLen > 0x20000) break;
DBG(" * %llu: '%s', '%s', Rev: %d, Len: %d ", adr, Signature, OemTableId,
((EFI_ACPI_DESCRIPTION_HEADER *)adr)->Revision, innLen);
for (k = 0; k < 16; k++) {
DBG("%02hhX ", ((UINT8*)adr)[k]);
}
if ((AsciiStrCmp(Signature, "SSDT") == 0) && (len < 0x20000) && DirName != NULL && !IsTableSaved((void*)adr)) {
XStringW FileName = GenerateFileName(FileNamePrefix, SsdtCount, ChildCount, OemTableId);
len = ((UINT16*)adr)[2];
DBG("Internal length = %llu", len);
Status = SaveBufferToDisk((void*)adr, len, DirName, FileName.wc_str());
if (!EFI_ERROR(Status)) {
DBG(" -> %ls", FileName.wc_str());
MarkTableAsSaved((void*)adr);
ChildCount++;
} else {
DBG(" -> %s", efiStrError(Status));
}
}
DBG("\n");
}
}
Entry += sizeof (NameSSDT) + pacLen;
} else if (CompareMem(Entry, NameSSDT2, 5) == 0 ||
CompareMem(Entry, NameCSDT2, 5) == 0) {
adr = ReadUnaligned32((UINT32*)(Entry + 7));
len = 0;
j = *(Entry + 11);
if (j == 0x0b) {
len = ReadUnaligned16((UINT16*)(Entry + 12));
} else if (j == 0x0a) {
len = *(Entry + 12);
} else {
//not a number so skip for security
Entry += 5;
continue;
}
if (len > 0) {
// Take Signature and OemId for printing
CopyMem(&Signature, &((EFI_ACPI_DESCRIPTION_HEADER *)adr)->Signature, 4);
Signature[4] = 0;
CopyMem(&OemTableId, &((EFI_ACPI_DESCRIPTION_HEADER *)adr)->OemTableId, 8);
OemTableId[8] = 0;
stripTrailingSpaces(OemTableId);
DBG(" * %llu: '%s', '%s', Rev: %d, Len: %d ", adr, Signature, OemTableId,
((EFI_ACPI_DESCRIPTION_HEADER *)adr)->Revision, ((EFI_ACPI_DESCRIPTION_HEADER *)adr)->Length);
for(k=0; k<16; k++){
DBG("%02hhX ", ((UINT8*)adr)[k]);
}
if ((AsciiStrCmp(Signature, "SSDT") == 0) && (len < 0x20000) && DirName != NULL && !IsTableSaved((void*)adr)) {
XStringW FileName = GenerateFileName(FileNamePrefix, SsdtCount, ChildCount, OemTableId);
Status = SaveBufferToDisk((void*)adr, len, DirName, FileName.wc_str());
if (!EFI_ERROR(Status)) {
DBG(" -> %ls", FileName.wc_str());
MarkTableAsSaved((void*)adr);
ChildCount++;
} else {
DBG(" -> %s", efiStrError(Status));
}
}
DBG("\n");
}
Entry += 5;
} else {
Entry++;
}
}
}
/** Saves Table to disk as DirName\\FileName (DirName != NULL)
* or just prints basic table data to log (DirName == NULL).
*/
EFI_STATUS DumpTable(EFI_ACPI_DESCRIPTION_HEADER *TableEntry, CONST CHAR8 *CheckSignature, CONST CHAR16 *DirName, const XStringW& FileName, CONST CHAR16 *FileNamePrefix, INTN *SsdtCount)
{
EFI_STATUS Status;
CHAR8 Signature[5];
CHAR8 OemTableId[9];
// Take Signature and OemId for printing
CopyMem(&Signature[0], &TableEntry->Signature, 4);
Signature[4] = 0;
CopyMem(&OemTableId[0], &TableEntry->OemTableId, 8);
OemTableId[8] = 0;
stripTrailingSpaces(OemTableId);
DBG(" %llx: '%s', '%s', Rev: %d, Len: %d", (uintptr_t)TableEntry, Signature, OemTableId, TableEntry->Revision, TableEntry->Length);
//
// Additional checks
//
if (CheckSignature != NULL && AsciiStrCmp(Signature, CheckSignature) != 0) {
DBG(" -> invalid signature, expecting %s\n", CheckSignature);
return EFI_INVALID_PARAMETER;
}
// XSDT checks
if (TableEntry->Signature == EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {
if (TableEntry->Length < sizeof(XSDT_TABLE)) {
DBG(" -> invalid length\n");
return EFI_INVALID_PARAMETER;
}
}
// RSDT checks
if (TableEntry->Signature == EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {
if (TableEntry->Length < sizeof(RSDT_TABLE)) {
DBG(" -> invalid length\n");
return EFI_INVALID_PARAMETER;
}
}
// FADT/FACP checks
if (TableEntry->Signature == EFI_ACPI_1_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) {
if (TableEntry->Length < sizeof(EFI_ACPI_1_0_FIXED_ACPI_DESCRIPTION_TABLE)) {
DBG(" -> invalid length\n");
return EFI_INVALID_PARAMETER;
}
}
// DSDT checks
if (TableEntry->Signature == EFI_ACPI_1_0_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {
if (TableEntry->Length < sizeof(EFI_ACPI_DESCRIPTION_HEADER)) {
DBG(" -> invalid length\n");
return EFI_INVALID_PARAMETER;
}
}
// SSDT checks
if (TableEntry->Signature == EFI_ACPI_1_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {
if (TableEntry->Length < sizeof(EFI_ACPI_DESCRIPTION_HEADER)) {
DBG(" -> invalid length\n");
return EFI_INVALID_PARAMETER;
}
}
if (DirName == NULL || IsTableSaved(TableEntry)) {
// just debug log dump
return EFI_SUCCESS;
}
if (FileNamePrefix == NULL) {
FileNamePrefix = L"";
}
XStringW ReleaseFileName = FileName;
if (ReleaseFileName.isEmpty()) {
// take the name from the signature
if (TableEntry->Signature == EFI_ACPI_1_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE && SsdtCount != NULL) {
ReleaseFileName = GenerateFileName(FileNamePrefix, *SsdtCount, IGNORE_INDEX, OemTableId);
} else {
ReleaseFileName = SWPrintf("%ls%s.aml", FileNamePrefix, Signature);
}
}
DBG(" -> %ls", ReleaseFileName.wc_str());
// Save it
Status = SaveBufferToDisk(TableEntry, TableEntry->Length, DirName, ReleaseFileName.wc_str());
MarkTableAsSaved(TableEntry);
if (TableEntry->Signature == EFI_ACPI_1_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE && SsdtCount != NULL) {
DumpChildSsdt(TableEntry, DirName, FileNamePrefix, *SsdtCount);
*SsdtCount += 1;
}
return Status;
}
/** Saves to disk (DirName != NULL) or prints to log (DirName == NULL) Fadt tables: Dsdt and Facs. */
EFI_STATUS DumpFadtTables(EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt, CONST CHAR16 *DirName, CONST CHAR16 *FileNamePrefix, INTN *SsdtCount)
{
EFI_ACPI_DESCRIPTION_HEADER *TableEntry;
EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;
EFI_STATUS Status = EFI_SUCCESS;
UINT64 DsdtAdr;
UINT64 FacsAdr;
CHAR8 Signature[5];
//
// if Fadt->Revision < 3 (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION), then it is Acpi 1.0
// and fields after Flags are not available
//
DBG(" (Dsdt: %X, Facs: %X", Fadt->Dsdt, Fadt->FirmwareCtrl);
// for Acpi 1.0
DsdtAdr = Fadt->Dsdt;
FacsAdr = Fadt->FirmwareCtrl;
if (Fadt->Header.Revision >= EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION) {
// Acpi 2.0 or up
// may have it in XDsdt or XFirmwareCtrl
DBG(", XDsdt: %llx, XFacs: %llx", Fadt->XDsdt, Fadt->XFirmwareCtrl);
if ((Fadt->XDsdt != 0) && (Fadt->XDsdt < 0xFFFFFFF0ull)){
DsdtAdr = Fadt->XDsdt;
}
if (Fadt->XFirmwareCtrl != 0) {
FacsAdr = Fadt->XFirmwareCtrl;
}
}
DBG(")\n");
//
// Save Dsdt
//
if (DsdtAdr != 0) {
DBG(" ");
TableEntry = (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)DsdtAdr;
Status = DumpTable(TableEntry, "DSDT", DirName, L""_XSW, FileNamePrefix, NULL);
if (EFI_ERROR(Status)) {
DBG(" - %s\n", efiStrError(Status));
return Status;
}
DBG("\n");
DumpChildSsdt(TableEntry, DirName, FileNamePrefix, IGNORE_INDEX);
}
//
// Save Facs
//
if (FacsAdr != 0) {
// Taking it as structure from Acpi 2.0 just to get Version (it's reserved field in Acpi 1.0 and == 0)
Facs = (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE*)(UINTN)FacsAdr;
// Take Signature for printing
CopyMem(&Signature[0], &Facs->Signature, 4);
Signature[4] = 0;
DBG(" %llx: '%s', Ver: %d, Len: %d", (uintptr_t)Facs, Signature, Facs->Version, Facs->Length);
// FACS checks
if (Facs->Signature != EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) {
DBG(" -> invalid signature, expecting FACS\n");
return EFI_INVALID_PARAMETER;
}
if (Facs->Length < sizeof(EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE)) {
DBG(" -> invalid length\n");
return EFI_INVALID_PARAMETER;
}
if (DirName != NULL && !IsTableSaved(Facs)) {
XStringW FileName = SWPrintf("%lsFACS.aml", FileNamePrefix);
DBG(" -> %ls", FileName.wc_str());
Status = SaveBufferToDisk(Facs, Facs->Length, DirName, FileName.wc_str());
MarkTableAsSaved(Facs);
if (EFI_ERROR(Status)) {
DBG(" - %s\n", efiStrError(Status));
return Status;
}
}
DBG("\n");
}
return Status;
}
EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE* GetFadt()
{
EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *RsdPtr;
EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *FadtPointer = NULL;
// EFI_STATUS Status;
RsdPtr = (__typeof__(RsdPtr))FindAcpiRsdPtr();
if (RsdPtr == NULL) {
/*Status = */EfiGetSystemConfigurationTable (&gEfiAcpi20TableGuid, (void **)&RsdPtr);
if (RsdPtr == NULL) {
/*Status = */EfiGetSystemConfigurationTable (&gEfiAcpi10TableGuid, (void **)&RsdPtr);
if (RsdPtr == NULL) {
return NULL;
}
}
}
Rsdt = (RSDT_TABLE*)(UINTN)(RsdPtr->RsdtAddress);
if (RsdPtr->Revision > 0) {
if (Rsdt == NULL || Rsdt->Header.Signature != EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {
Xsdt = (XSDT_TABLE *)(UINTN)(RsdPtr->XsdtAddress);
}
}
if (Rsdt == NULL && Xsdt == NULL) {
//
// Search Acpi 2.0 or newer in UEFI Sys.Tables
//
RsdPtr = NULL;
/*Status = */EfiGetSystemConfigurationTable (&gEfiAcpi20TableGuid, (void**)&RsdPtr);
if (RsdPtr != NULL) {
DBG("Found UEFI Acpi 2.0 RSDP at %llx\n", (uintptr_t)RsdPtr);
Rsdt = (RSDT_TABLE*)(UINTN)(RsdPtr->RsdtAddress);
if (RsdPtr->Revision > 0) {
if (Rsdt == NULL || Rsdt->Header.Signature != EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {
Xsdt = (XSDT_TABLE *)(UINTN)(RsdPtr->XsdtAddress);
}
}
}
if (Rsdt == NULL && Xsdt == NULL) {
DBG("No RSDT or XSDT found!\n");
return NULL;
}
}
if (Rsdt) {
FadtPointer = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE*)(UINTN)(Rsdt->Entry);
}
if (Xsdt) {
//overwrite previous find as xsdt priority
FadtPointer = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE*)(UINTN)(Xsdt->Entry);
}
return FadtPointer;
}
/** Saves to disk (DirName != NULL)
* or prints to debug log (DirName == NULL)
* ACPI tables given by RsdPtr.
* Takes tables from Xsdt if present or from Rsdt if Xsdt is not present.
*/
void DumpTables(void *RsdPtrVoid, CONST CHAR16 *DirName)
{
EFI_STATUS Status;
UINTN Length;
INTN SsdtCount;
CONST CHAR16 *FileNamePrefix;
//
// RSDP
// Take it as Acpi 2.0, but take care that if RsdPtr->Revision == 0
// then it is actually Acpi 1.0 and fields after RsdtAddress (like XsdtAddress)
// are not available
//
EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER* RsdPtr = (EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER*)RsdPtrVoid;
if (DirName != NULL) {
DBG("Saving ACPI tables from RSDP %llx to %ls ...\n", (uintptr_t)RsdPtr, DirName);
} else {
DBG("Printing ACPI tables from RSDP %llx ...\n", (uintptr_t)RsdPtr);
}
if (RsdPtr->Signature != EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER_SIGNATURE) {
DBG(" RsdPrt at %llx has invaid signature 0x%llx - exiting.\n", (uintptr_t)RsdPtr, RsdPtr->Signature);
return;
}
// Take Signature for printing
CHAR8 Signature[9];
CopyMem(&Signature[0], &RsdPtr->Signature, 8);
Signature[8] = 0;
// Take Rsdt and Xsdt
Rsdt = NULL;
Xsdt = NULL;
DBG(" %llx: '%s', Rev: %d", (uintptr_t)RsdPtr, Signature, RsdPtr->Revision);
if (RsdPtr->Revision == 0) {
// Acpi 1.0
Length = sizeof(EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER);
DBG(" (Acpi 1.0)");
} else {
// Acpi 2.0 or newer
Length = RsdPtr->Length;
DBG(" (Acpi 2.0 or newer)");
}
DBG(", Len: %llu", Length);
//
// Save RsdPtr
//
if (DirName != NULL && !IsTableSaved(RsdPtr)) {
DBG(" -> RSDP.aml");
Status = SaveBufferToDisk(RsdPtr, Length, DirName, L"RSDP.aml");
MarkTableAsSaved(RsdPtr);
if (EFI_ERROR(Status)) {
DBG(" - %s\n", efiStrError(Status));
return;
}
}
DBG("\n");
if (RsdPtr->Revision == 0) {
// Acpi 1.0 - no Xsdt
Rsdt = (RSDT_TABLE*)(UINTN)(RsdPtr->RsdtAddress);
DBG(" (Rsdt: %llx)\n", (UINTN)Rsdt);
} else {
// Acpi 2.0 or newer - may have Xsdt and/or Rsdt
Rsdt = (RSDT_TABLE*)(UINTN)(RsdPtr->RsdtAddress);
Xsdt = (XSDT_TABLE *)(UINTN)(RsdPtr->XsdtAddress);
DBG(" (Xsdt: %llx, Rsdt: %llx)\n", (UINTN)Xsdt, (UINTN)Rsdt);
}
if (Rsdt == NULL && Xsdt == NULL) {
DBG(" No Rsdt and Xsdt - exiting.\n");
return;
}
FileNamePrefix = L"";
//
// Save Xsdt
//
if (Xsdt != NULL) {
DBG(" ");
Status = DumpTable((EFI_ACPI_DESCRIPTION_HEADER *)Xsdt, "XSDT", DirName, L"XSDT.aml"_XSW, FileNamePrefix, NULL);
if (EFI_ERROR(Status)) {
DBG(" - %s", efiStrError(Status));
Xsdt = NULL;
}
DBG("\n");
}
//
// Save Rsdt
//
if (Rsdt != NULL) {
DBG(" ");
Status = DumpTable((EFI_ACPI_DESCRIPTION_HEADER *)Rsdt, "RSDT", DirName, L"RSDT.aml"_XSW, FileNamePrefix, NULL);
if (EFI_ERROR(Status)) {
DBG(" - %s", efiStrError(Status));
Rsdt = NULL;
}
DBG("\n");
}
//
// Check once more since they might be invalid
//
if (Rsdt == NULL && Xsdt == NULL) {
DBG(" No Rsdt and Xsdt - exiting.\n");
return;
}
UINT32 Count = 0;
if (Xsdt) {
UINT64 *Ptr, *EndPtr;
Count = XsdtTableCount();
DBG(" Tables in Xsdt: %d\n", Count);
if (Count > 100) Count = 100; //it's enough
Ptr = XsdtEntryPtrFromIndex(0);
EndPtr = XsdtEntryPtrFromIndex(Count);
SsdtCount = 0;
for (; Ptr < EndPtr; Ptr++) {
DBG(" %d.", IndexFromXsdtEntryPtr(Ptr));
EFI_ACPI_DESCRIPTION_HEADER* Table = (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)ReadUnaligned64(Ptr);
if (!Table) {
DBG(" = 0\n");
// skip NULL entry
continue;
}
if (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE == Table->Signature) {
// Fadt - save Dsdt and Facs
Status = DumpTable(Table, NULL, DirName, L""_XSW, FileNamePrefix, &SsdtCount);
if (EFI_ERROR(Status)) {
DBG(" - %s\n", efiStrError(Status));
return;
}
DBG("\n");
EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE* Fadt = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE*)Table;
Status = DumpFadtTables(Fadt, DirName, FileNamePrefix, &SsdtCount);
if (EFI_ERROR(Status)) {
return;
}
} else {
Status = DumpTable(Table, NULL, DirName, L""_XSW, FileNamePrefix, &SsdtCount);
if (EFI_ERROR(Status)) {
DBG(" - %s\n", efiStrError(Status));
return;
}
DBG("\n");
}
}
} // if Xsdt
if (!Count && Rsdt) {
UINT32 *Ptr, *EndPtr;
// additional Rsdt tables which are not present in Xsdt will have "RSDT-" prefix, like RSDT-FACS.aml
FileNamePrefix = L"RSDT-";
// Take tables from Rsdt
// if saved from Xsdt already, then just print debug
Count = RsdtTableCount();
DBG(" Tables in Rsdt: %d\n", Count);
if (Count > 100) Count = 100; //it's enough
Ptr = RsdtEntryPtrFromIndex(0);
EndPtr = RsdtEntryPtrFromIndex(Count);
for (; Ptr < EndPtr; Ptr++) {
DBG(" %d.", IndexFromRsdtEntryPtr(Ptr));
EFI_ACPI_DESCRIPTION_HEADER* Table = (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)*Ptr;
if (!Table) {
DBG(" = 0\n");
// skip NULL entry
continue;
}
if (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE == Table->Signature) {
// Fadt - save Dsdt and Facs
Status = DumpTable(Table, NULL, DirName, L""_XSW, FileNamePrefix, &SsdtCount);
if (EFI_ERROR(Status)) {
DBG(" - %s\n", efiStrError(Status));
return;
}
DBG("\n");
EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE* Fadt = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE*)Table;
Status = DumpFadtTables(Fadt, DirName, FileNamePrefix, &SsdtCount);
if (EFI_ERROR(Status)) {
return;
}
} else {
Status = DumpTable(Table, NULL, DirName, L""_XSW, FileNamePrefix, &SsdtCount);
if (EFI_ERROR(Status)) {
DBG(" - %s\n", efiStrError(Status));
return;
}
DBG("\n");
}
}
} // if Rsdt
}
/** Saves OEM ACPI tables to disk.
* Searches BIOS, then UEFI Sys.Tables for Acpi 2.0 or newer tables, then for Acpi 1.0 tables
* CloverEFI:
* - saves first one found, dump others to log
* UEFI:
* - saves first one found in UEFI Sys.Tables, dump others to log
*
* Dumping of other tables to log can be removed if it turns out that there is no value in doing it.
*/
void SaveOemTables()
{
// EFI_STATUS Status;
void *RsdPtr;
XStringW AcpiOriginPath = L"ACPI\\origin"_XSW;
XBool Saved = false;
CHAR8 *MemLogStart;
UINTN MemLogStartLen;
MemLogStartLen = GetMemLogLen();
MemLogStart = GetMemLogBuffer() + MemLogStartLen;
//
// Search in BIOS
// CloverEFI - Save
// UEFI - just print to log
//
// RsdPtr = NULL;
RsdPtr = FindAcpiRsdPtr();
if (RsdPtr != NULL) {
DBG("Found BIOS RSDP at %llx\n", (UINTN)RsdPtr);
if (gFirmwareClover) {
// Save it
DumpTables(RsdPtr, AcpiOriginPath.wc_str());
Saved = true;
} else {
// just print to log
DumpTables(RsdPtr, NULL);
}
}
//
// Search Acpi 2.0 or newer in UEFI Sys.Tables
//
RsdPtr = NULL;
/*Status = */EfiGetSystemConfigurationTable (&gEfiAcpi20TableGuid, &RsdPtr);
if (RsdPtr != NULL) { //it may be EFI_SUCCESS but null pointer
DBG("Found UEFI Acpi 2.0 RSDP at %llx\n", (UINTN)RsdPtr);
// if tables already saved, then just print to log
DumpTables(RsdPtr, Saved ? NULL : AcpiOriginPath.wc_str());
Saved = true;
}
//
// Then search Acpi 1.0 UEFI Sys.Tables
//
RsdPtr = NULL;
/*Status = */EfiGetSystemConfigurationTable (&gEfiAcpi10TableGuid, &RsdPtr);
if (RsdPtr != NULL) {
DBG("Found UEFI Acpi 1.0 RSDP at %llx\n", (UINTN)RsdPtr);
// if tables already saved, then just print to log
DumpTables(RsdPtr, Saved ? NULL : AcpiOriginPath.wc_str());
// Saved = true;
}
SaveBufferToDisk(MemLogStart, GetMemLogLen() - MemLogStartLen, AcpiOriginPath.wc_str(), L"DumpLog.txt");
FreePool(mSavedTables);
}
void SaveOemDsdt(XBool FullPatch)
{
EFI_STATUS Status = EFI_NOT_FOUND;
EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *FadtPointer = NULL;
EFI_PHYSICAL_ADDRESS dsdt = EFI_SYSTEM_TABLE_MAX_ADDRESS;
UINTN Pages;
UINT8 *buffer = NULL;
UINTN DsdtLen = 0;
XStringW OriginDsdt = SWPrintf("ACPI\\origin\\DSDT.aml");
XStringW OriginDsdtFixed = SWPrintf("ACPI\\origin\\DSDT-%x.aml", gSettings.ACPI.DSDT.FixDsdt);
// constexpr LStringW PathPatched = L"\\EFI\\CL OVER\\ACPI\\patched";
// XStringW PathDsdt;
// XStringW AcpiOemPath = SWPrintf("ACPI\\patched");
// PathDsdt.SWPrintf("\\%ls", gSettings.ACPI.DSDT.FixDsdt.wc_str());
if (FileExists(selfOem.getConfigDir(), SWPrintf("ACPI\\patched\\%ls", gSettings.ACPI.DSDT.DsdtName.wc_str()))) {
DBG("SaveOemDsdt: DSDT found in Clover volume OEM folder: \\%ls\\ACPI\\patched\\%ls\n", selfOem.getConfigDirFullPath().wc_str(), gSettings.ACPI.DSDT.DsdtName.wc_str());
Status = egLoadFile(&selfOem.getConfigDir(), SWPrintf("ACPI\\patched\\%ls", gSettings.ACPI.DSDT.DsdtName.wc_str()).wc_str(), &buffer, &DsdtLen);
}
// Jief : Do not write outside OemPath
// if (EFI_ERROR(Status) && FileExists(&self.getSelfRootDir(), SWPrintf("%ls%ls", PathPatched.wc_str(), PathDsdt.wc_str()))) {
// DBG("SaveOemDsdt: DSDT found in Clover volume common folder: %ls%ls\n", PathPatched.wc_str(), PathDsdt.wc_str());
// Status = egLoadFile(&self.getSelfRootDir(), SWPrintf("%ls%ls", PathPatched.wc_str(), PathDsdt.wc_str()).wc_str(), &buffer, &DsdtLen);
// }
if (EFI_ERROR(Status)) {
FadtPointer = GetFadt();
if (FadtPointer == NULL) {
DBG("Cannot found FADT in BIOS or in UEFI!\n"); //really?!
return;
}
BiosDsdt = FadtPointer->Dsdt;
if (FadtPointer->Header.Revision >= EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION &&
FadtPointer->XDsdt != 0) {
BiosDsdt = FadtPointer->XDsdt;
}
buffer = (UINT8*)(UINTN)BiosDsdt;
}
if (!buffer) {
DBG("Cannot found DSDT in BIOS or in files!\n");
return;
}
DsdtLen = ((EFI_ACPI_DESCRIPTION_HEADER*)buffer)->Length;
Pages = EFI_SIZE_TO_PAGES(DsdtLen + DsdtLen / 8); // take some extra space for patches
Status = gBS->AllocatePages (
AllocateMaxAddress,
EfiBootServicesData,
Pages,
&dsdt
);
//if success insert dsdt pointer into ACPI tables
if(!EFI_ERROR(Status))
{
CopyMem((void*)(UINTN)dsdt, buffer, DsdtLen);
buffer = (UINT8*)(UINTN)dsdt;
if (FullPatch) {
FixBiosDsdt(buffer, FadtPointer, NullXString8);
DsdtLen = ((EFI_ACPI_DESCRIPTION_HEADER*)buffer)->Length;
OriginDsdt = OriginDsdtFixed;
}
Status = egSaveFile(&selfOem.getConfigDir(), OriginDsdt.wc_str(), buffer, DsdtLen);
// Jief : do not write outside of OemDir
// if (EFI_ERROR(Status)) {
// Status = egSaveFile(NULL, OriginDsdt.wc_str(), buffer, DsdtLen);
// }
if (!EFI_ERROR(Status)) {
MsgLog("DSDT saved to %ls\\%ls\n", selfOem.getConfigDirFullPath().wc_str(), OriginDsdt.wc_str());
} else {
MsgLog("Saving DSDT to %ls\\%ls failed - %s\n", selfOem.getConfigDirFullPath().wc_str(), OriginDsdt.wc_str(), efiStrError(Status));
}
gBS->FreePages(dsdt, Pages);
}
}
XBool LoadPatchedAML(const EFI_FILE& dir, const XStringW& acpiOemPath, CONST CHAR16* PartName, UINTN Pass)
{
// pass1 prefilter based on file names (optimization that avoids loading same files twice)
UINTN Index = IGNORE_INDEX;
if (AUTOMERGE_PASS1 == Pass) {
Index = IndexFromFileName(PartName);
// gSettings.ACPI.AutoMerge always true in this case
// file names such as: ECDT.aml, SSDT-0.aml, SSDT-1-CpuPm.aml, attempt merge on pass1
// others: no attempt for merge
// special case for SSDT.aml: no attempt to merge
if (0 == StriCmp(PartName, L"SSDT.aml") || (8 != StrLen(PartName) && IGNORE_INDEX == Index)) {
DBG("ignore on pass 1\n");
return false;
}
}
UINT8 *buffer = NULL;
UINTN bufferLen = 0;
EFI_STATUS Status = egLoadFile(&dir, SWPrintf("%ls\\%ls", acpiOemPath.wc_str(), PartName).wc_str(), &buffer, &bufferLen);
if (!EFI_ERROR(Status)) {
if (buffer) {
EFI_ACPI_DESCRIPTION_HEADER* TableHeader = (EFI_ACPI_DESCRIPTION_HEADER*)buffer;
if (TableHeader->Length > 500 * Kilo) {
DBG("wrong table\n");
return false;
}
}
DBG("size=%lld ", (UINT64)bufferLen);
if (!gSettings.ACPI.AutoMerge) {
// Note: with AutoMerge=false, Pass is only AUTOMERGE_PASS2 here
Status = InsertTable(buffer, bufferLen);
} else {
Status = ReplaceOrInsertTable(buffer, bufferLen, Index, Pass);
}
FreePool(buffer);
}
DBG("... %s\n", efiStrError(Status));
return !EFI_ERROR(Status);
}
#define BVALUE_ATTEMPTED 2 // special value for MenuItem.BValue to avoid excessive log output
void LoadAllPatchedAML(const XStringW& acpiPathUnderOem, UINTN Pass)
{
if (!gSettings.ACPI.AutoMerge && AUTOMERGE_PASS1 == Pass) {
// nothing to do in this case, since AutoMerge=false -> no tables ever merged
return;
}
if ( ACPIPatchedAML.notEmpty() ) {
DbgHeader("ACPIPatchedAML");
if (gSettings.ACPI.AutoMerge) {
DBG("AutoMerge pass %llu\n", Pass);
}
//DBG("Start: Processing Patched AML(s): ");
if (gSettings.ACPI.SortedACPI.size()) {
UINTN Index;
DBG("Sorted\n");
for (Index = 0; Index < gSettings.ACPI.SortedACPI.size(); Index++) {
size_t idx;
for ( idx = 0 ; idx < ACPIPatchedAML.size() ; ++idx) {
ACPI_PATCHED_AML& ACPIPatchedAMLTmp = ACPIPatchedAML[idx];
if ( ACPIPatchedAMLTmp.FileName == gSettings.ACPI.SortedACPI[Index] && ACPIPatchedAMLTmp.MenuItem.BValue) {
if (BVALUE_ATTEMPTED != ACPIPatchedAMLTmp.MenuItem.BValue)
DBG("Disabled: %s, skip\n", ACPIPatchedAMLTmp.FileName.c_str());
ACPIPatchedAMLTmp.MenuItem.BValue = BVALUE_ATTEMPTED;
break;
}
}
if ( idx == ACPIPatchedAML.size() ) { // NULL when not disabled
DBG("Inserting table[%llu]:%s from %ls\\%ls: ", Index, gSettings.ACPI.SortedACPI[Index].c_str(), selfOem.getConfigDirFullPath().wc_str(), acpiPathUnderOem.wc_str());
if (LoadPatchedAML(selfOem.getConfigDir(), acpiPathUnderOem, XStringW(gSettings.ACPI.SortedACPI[Index]).wc_str(), Pass)) {
// avoid inserting table again on second pass
for ( idx = 0 ; idx < ACPIPatchedAML.size() ; ++idx) {
ACPI_PATCHED_AML& temp2 = ACPIPatchedAML[idx];
if ( temp2.FileName == gSettings.ACPI.SortedACPI[Index] ) {
temp2.MenuItem.BValue = BVALUE_ATTEMPTED;
break;
}
}
}
}
}
} else {
DBG("Unsorted\n");
for ( size_t idx = 0 ; idx < ACPIPatchedAML.size() ; ++idx) {
ACPI_PATCHED_AML& ACPIPatchedAMLTmp = ACPIPatchedAML[idx];
if (!ACPIPatchedAMLTmp.MenuItem.BValue) {
DBG("Inserting %s from %ls\\%ls: ", ACPIPatchedAMLTmp.FileName.c_str(), selfOem.getConfigDirFullPath().wc_str(), acpiPathUnderOem.wc_str());
if (LoadPatchedAML(selfOem.getConfigDir(), acpiPathUnderOem, XStringW(ACPIPatchedAMLTmp.FileName).wc_str(), Pass)) {
// avoid inserting table again on second pass
ACPIPatchedAMLTmp.MenuItem.BValue = BVALUE_ATTEMPTED;
}
} else {
if (BVALUE_ATTEMPTED != ACPIPatchedAMLTmp.MenuItem.BValue)
DBG("Disabled: %s, skip\n", ACPIPatchedAMLTmp.FileName.c_str());
ACPIPatchedAMLTmp.MenuItem.BValue = BVALUE_ATTEMPTED;
}
}
}
//DBG("End: Processing Patched AML(s)\n");
}
}
EFI_STATUS PatchACPI(IN REFIT_VOLUME *Volume, const MacOsVersion& OSVersion)
{
EFI_STATUS Status = EFI_SUCCESS;
UINTN Index;
EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *RsdPointer = NULL;
EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *FadtPointer = NULL;
EFI_ACPI_4_0_FIXED_ACPI_DESCRIPTION_TABLE *newFadt = NULL;
// EFI_ACPI_HIGH_PRECISION_EVENT_TIMER_TABLE_HEADER *Hpet = NULL;
EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs = NULL;
EFI_PHYSICAL_ADDRESS dsdt = EFI_SYSTEM_TABLE_MAX_ADDRESS; //0xFE000000;
EFI_PHYSICAL_ADDRESS BufferPtr;
SSDT_TABLE *Ssdt = NULL;
UINT8 *buffer = NULL;
UINTN bufferLen = 0;
UINT32* rf = NULL;
UINT64* xf = NULL;
UINT64 XDsdt; //save values if present
UINT64 XFirmwareCtrl;
UINT32 eCntR; //, eCntX;
UINT32 *pEntryR;
CHAR8 *pEntry;
EFI_ACPI_DESCRIPTION_HEADER *TableHeader;
// -===== APIC =====-
EFI_ACPI_DESCRIPTION_HEADER *ApicTable;
// EFI_ACPI_2_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER *ApicHeader;
EFI_ACPI_2_0_PROCESSOR_LOCAL_APIC_STRUCTURE *ProcLocalApic;
EFI_ACPI_2_0_LOCAL_APIC_NMI_STRUCTURE *LocalApicNMI;
// UINTN ApicLen;
UINTN ApicCPUNum;
UINT8 *SubTable;
XBool DsdtLoaded = false;
XBool NeedUpdate = false;
OPER_REGION *tmpRegion;
DbgHeader("PatchACPI");
//try to find in SystemTable
for(Index = 0; Index < gST->NumberOfTableEntries; Index++) {
if( gST->ConfigurationTable[Index].VendorGuid == gEfiAcpi20TableGuid ) {
// Acpi 2.0
RsdPointer = (EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER*)gST->ConfigurationTable[Index].VendorTable;
break;
}
else if( gST->ConfigurationTable[Index].VendorGuid == gEfiAcpi10TableGuid ) {
// Acpi 1.0 - RSDT only
RsdPointer = (EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER*)gST->ConfigurationTable[Index].VendorTable;
continue;
}
}
if (!RsdPointer) {
return EFI_UNSUPPORTED;
}
Rsdt = (RSDT_TABLE*)(UINTN)RsdPointer->RsdtAddress;
// DBG("RSDT 0x%llx\n", Rsdt);
rf = ScanRSDT(EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE, 0);
if(rf) {
FadtPointer = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE*)(UINTN)(*rf);
// DBG("FADT from RSDT: 0x%llx\n", FadtPointer);
}
Xsdt = NULL;
if (RsdPointer->Revision >=2 && (RsdPointer->XsdtAddress < (UINT64)(UINTN)-1)) {
Xsdt = (XSDT_TABLE*)(UINTN)RsdPointer->XsdtAddress;
// DBG("XSDT 0x%llx\n", Xsdt);
xf = ScanXSDT(EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE, 0);
if(xf) {
//Slice - change priority. First Xsdt, second Rsdt
if (*xf) {
FadtPointer = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE*)(UINTN)(*xf);
// DBG("FADT from XSDT: 0x%llx\n", FadtPointer);
} else {
*xf = (UINT64)(UINTN)FadtPointer;
// DBG("reuse FADT\n"); //never happens
}
}
}
if(!xf && Rsdt) {
DBG("Xsdt is not found! Creating new one\n");
//We should make here ACPI20 RSDP with all needed subtables based on ACPI10
BufferPtr = EFI_SYSTEM_TABLE_MAX_ADDRESS;
Status = gBS->AllocatePages(AllocateMaxAddress, EfiACPIReclaimMemory, 1, &BufferPtr);
if(!EFI_ERROR(Status)) {
if (RsdPointer->Revision == 0) {
// Acpi 1.0 RsdPtr, but we need Acpi 2.0
EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *NewRsdPointer;
DBG("RsdPointer is Acpi 1.0 - creating new one Acpi 2.0\n");
// add new pointer to the beginning of a new buffer
NewRsdPointer = (EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER*)(UINTN)BufferPtr;
// and Xsdt will come after it
BufferPtr += 0x30;
// DBG("::pointers %llx %llx\n", NewRsdPointer, RsdPointer);
// Signature, Checksum, OemId, Reserved/Revision, RsdtAddress
CopyMem(NewRsdPointer, RsdPointer, sizeof(EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER));
NewRsdPointer->Revision = 2;
NewRsdPointer->Length = sizeof(EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER);
RsdPointer = NewRsdPointer;
NeedUpdate = true;
DBG("RsdPointer Acpi 2.0 installed\n");
}
Xsdt = (XSDT_TABLE*)(UINTN)BufferPtr;
// DBG("XSDT = 0x%llx\n", uintptr_t(Xsdt));
Xsdt->Header.Signature = 0x54445358; //EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
eCntR = (Rsdt->Header.Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) / sizeof(UINT32);
Xsdt->Header.Length = eCntR * sizeof(UINT64) + sizeof (EFI_ACPI_DESCRIPTION_HEADER);
Xsdt->Header.Revision = 1;
CopyMem(&Xsdt->Header.OemId, &FadtPointer->Header.OemId, 6);
// Xsdt->Header.OemTableId = Rsdt->Header.OemTableId;
CopyMem(&Xsdt->Header.OemTableId, &Rsdt->Header.OemTableId, 8);
Xsdt->Header.OemRevision = Rsdt->Header.OemRevision;
Xsdt->Header.CreatorId = Rsdt->Header.CreatorId;
Xsdt->Header.CreatorRevision = Rsdt->Header.CreatorRevision;
pEntryR = (UINT32*)(&(Rsdt->Entry));
pEntry = (CHAR8*)(&(Xsdt->Entry));
DBG("RSDT entries = %d\n", eCntR);
for (Index = 0; Index < eCntR; Index ++)
{
UINT64 *pEntryX = (UINT64 *)pEntry;
// DBG("RSDT entry = 0x%X\n", *pEntryR);
if (*pEntryR != 0) {
*pEntryX = 0;
CopyMem(pEntryX, pEntryR, sizeof(UINT32));
pEntryR++;
pEntry += sizeof(UINT64);
} else {
DBG("RSDT entry %llu = 0 ... skip it\n", Index);
Xsdt->Header.Length -= sizeof(UINT64);
pEntryR++;
}
}
}
}
if (Xsdt) {
//Now we need no more Rsdt
Rsdt = NULL;
RsdPointer->RsdtAddress = 0;
//and we want to reallocate Xsdt
BufferPtr = EFI_SYSTEM_TABLE_MAX_ADDRESS;
Status = gBS->AllocatePages(AllocateMaxAddress, EfiACPIReclaimMemory, 1, &BufferPtr);
if(!EFI_ERROR(Status))
{
CopyMem((void*)(UINTN)BufferPtr, Xsdt, Xsdt->Header.Length);
Xsdt = (XSDT_TABLE*)(UINTN)BufferPtr;
}
// DBG("Finishing RsdPointer\n");
RsdPointer->XsdtAddress = (UINT64)(UINTN)Xsdt;
RsdPointer->Checksum = 0;
RsdPointer->Checksum = (UINT8)(256-Checksum8((CHAR8*)RsdPointer, 20));
RsdPointer->ExtendedChecksum = 0;
RsdPointer->ExtendedChecksum = (UINT8)(256-Checksum8((CHAR8*)RsdPointer, RsdPointer->Length));
DBG("Xsdt reallocation done\n");
}
// DBG("FADT pointer = %X\n", (UINTN)FadtPointer);
if(!FadtPointer) {
return EFI_NOT_FOUND;
}
//Slice - then we do FADT patch no matter if we don't have DSDT.aml
BufferPtr = EFI_SYSTEM_TABLE_MAX_ADDRESS;
Status = gBS->AllocatePages(AllocateMaxAddress, EfiACPIReclaimMemory, 1, &BufferPtr);
if(!EFI_ERROR(Status))
{
UINT32 oldLength = ((EFI_ACPI_DESCRIPTION_HEADER*)FadtPointer)->Length;
newFadt = (EFI_ACPI_4_0_FIXED_ACPI_DESCRIPTION_TABLE*)(UINTN)BufferPtr;
DBG("old FADT length=%X\n", oldLength);
CopyMem(newFadt, FadtPointer, oldLength); //old data
newFadt->Header.Length = 0xF4;
CopyMem(newFadt->Header.OemId, AppleBiosVendor.c_str(), 6);
if (newFadt->Header.Revision < EFI_ACPI_4_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION) {
newFadt->Header.Revision = EFI_ACPI_4_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION;
}
newFadt->Reserved0 = 0; //ACPIspec said it should be 0, while 1 is possible, but no more
//should correct headers if needed and if asked
PatchTableHeader((EFI_ACPI_DESCRIPTION_HEADER*)newFadt);
if (gSettings.ACPI.smartUPS==true) {
newFadt->PreferredPmProfile = 3;
} else {
newFadt->PreferredPmProfile = gMobile?2:1; //as calculated before
}
if (GlobalConfig.EnableC6 || gSettings.ACPI.SSDT.EnableISS) {
newFadt->CstCnt = 0x85; //as in Mac
}
if (GlobalConfig.EnableC2) newFadt->PLvl2Lat = 0x65;
if (GlobalConfig.C3Latency > 0) {
newFadt->PLvl3Lat = GlobalConfig.C3Latency;
} else if (GlobalConfig.EnableC4) {
newFadt->PLvl3Lat = 0x3E9;
}
if (GlobalConfig.C3Latency == 0) {
GlobalConfig.C3Latency = newFadt->PLvl3Lat;
}
newFadt->IaPcBootArch = 0x3;
if (gSettings.ACPI.NoASPM) {
newFadt->IaPcBootArch |= 0x10; // disable ASPM
}
newFadt->Flags |= 0x420; //Reset Register Supported and SleepButton active
newFadt->Flags &= ~0x10010; //RTC_STS not valid and PowerButton disable
XDsdt = newFadt->XDsdt; //save values if present
XFirmwareCtrl = newFadt->XFirmwareCtrl;
CopyMem(&newFadt->ResetReg, pmBlock, 0x80);
//but these common values are not specific, so adjust
//ACPIspec said that if Xdsdt !=0 then Dsdt must be =0. But real Mac no! Both values present
if (BiosDsdt) {
newFadt->XDsdt = BiosDsdt;
newFadt->Dsdt = (UINT32)BiosDsdt;
} else if (newFadt->Dsdt) {
newFadt->XDsdt = (UINT64)(newFadt->Dsdt);
} else if (XDsdt) {
newFadt->Dsdt = (UINT32)XDsdt;
}
if (newFadt->FirmwareCtrl) {
Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE*)(UINTN)newFadt->FirmwareCtrl;
newFadt->XFirmwareCtrl = (UINT64)(UINTN)(Facs);
} else if (newFadt->XFirmwareCtrl) {
newFadt->FirmwareCtrl = (UINT32)XFirmwareCtrl;
Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE*)(UINTN)XFirmwareCtrl;
}
//patch for FACS included here
Facs->Version = EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION;
if (gSettings.Boot.SignatureFixup) {
DBG(" SignatureFixup: 0x%X -> 0x%llX\n", Facs->HardwareSignature, machineSignature);
Facs->HardwareSignature = (UINT32)machineSignature;
} else {
DBG(" SignatureFixup: 0x%X -> 0x0\n", Facs->HardwareSignature);
Facs->HardwareSignature = 0x0;
}
Facs->Flags = 0; //dont' support S4BIOS, as well as 64bit wake
//
if ((gSettings.ACPI.ResetAddr == 0) && ((oldLength < 0x80) || (newFadt->ResetReg.Address == 0))) {
newFadt->ResetReg.Address = 0x64;
newFadt->ResetValue = 0xFE;
gSettings.ACPI.ResetAddr = 0x64;
gSettings.ACPI.ResetVal = 0xFE;
} else if (gSettings.ACPI.ResetAddr != 0) {
newFadt->ResetReg.Address = gSettings.ACPI.ResetAddr;
newFadt->ResetValue = gSettings.ACPI.ResetVal;
}
newFadt->XPm1aEvtBlk.Address = (UINT64)(newFadt->Pm1aEvtBlk);
newFadt->XPm1bEvtBlk.Address = (UINT64)(newFadt->Pm1bEvtBlk);
newFadt->XPm1aCntBlk.Address = (UINT64)(newFadt->Pm1aCntBlk);
newFadt->XPm1bCntBlk.Address = (UINT64)(newFadt->Pm1bCntBlk);
newFadt->XPm2CntBlk.Address = (UINT64)(newFadt->Pm2CntBlk);
newFadt->XPmTmrBlk.Address = (UINT64)(newFadt->PmTmrBlk);
newFadt->XGpe0Blk.Address = (UINT64)(newFadt->Gpe0Blk);
newFadt->XGpe1Blk.Address = (UINT64)(newFadt->Gpe1Blk);
FadtPointer = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE*)newFadt;
//We are sure that Fadt is the first entry in RSDT/XSDT table
if (Rsdt!=NULL) {
Rsdt->Entry = (UINT32)(UINTN)newFadt;
}
if (Xsdt!=NULL) {
Xsdt->Entry = (UINT64)((UINT32)(UINTN)newFadt);
}
FixChecksum(&FadtPointer->Header);
if (gSettings.ACPI.SlpSmiEnable) {
UINT32 *SlpSmiEn = (UINT32*)((UINTN)(newFadt->Pm1aEvtBlk) + 0x30);
UINT32 Value = *SlpSmiEn;
Value &= ~ bit(4);
*SlpSmiEn = Value;
}
}
//Get regions from BIOS DSDT
if ((gSettings.ACPI.DSDT.FixDsdt & FIX_REGIONS) != 0) {
GetBiosRegions((UINT8*)(UINTN)(newFadt->Dsdt));
}
// DBG("DSDT finding\n");
if (!Volume) {
DBG("Volume not found!\n");
return EFI_NOT_FOUND;
}
// RootDir = Volume->RootDir;
Status = EFI_NOT_FOUND;
XStringW acpiPath = SWPrintf("ACPI\\patched\\%ls", gSettings.ACPI.DSDT.DsdtName.wc_str());
if ( selfOem.oemDirExists() ) {
if ( FileExists(&selfOem.getOemDir(), acpiPath) ) {
DBG("DSDT found in Clover volume OEM folder: %ls\\%ls\n", selfOem.getOemFullPath().wc_str(), acpiPath.wc_str());
Status = egLoadFile(&selfOem.getOemDir(), acpiPath.wc_str(), &buffer, &bufferLen);
//REVIEW: memory leak... buffer
}
}
//Slice: the idea was from past
// first priority DSDT.aml from the root of booted volume. It allows to keep different DSDT for different systems
// second priority is DSDT from OEM folder
// third priority is {Clover folder}/ACPI/patched/DSDT*.aml choosen from GUI.
XStringW PathDsdt = SWPrintf("\\%ls", gSettings.ACPI.DSDT.DsdtName.wc_str());
if (EFI_ERROR(Status) && FileExists(Volume->RootDir, PathDsdt)) {
DBG("DSDT found in booted volume\n");
Status = egLoadFile(Volume->RootDir, PathDsdt.wc_str(), &buffer, &bufferLen);
}
// Jief : may I suggest to remove that. Loading from outside of OemPath might be confusing
if ( EFI_ERROR(Status) && FileExists(&self.getCloverDir(), acpiPath) ) {
DBG("DSDT found in Clover volume: %ls\\%ls\n", self.getCloverDirFullPath().wc_str(), acpiPath.wc_str());
Status = egLoadFile(&self.getCloverDir(), acpiPath.wc_str(), &buffer, &bufferLen);
}
//
//apply DSDT loaded from a file into buffer
//else FADT will contain old BIOS DSDT
//
DsdtLoaded = false;
if (!EFI_ERROR(Status)) {
// if we will apply fixes, allocate additional space
bufferLen = bufferLen + bufferLen / 8;
dsdt = EFI_SYSTEM_TABLE_MAX_ADDRESS;
Status = gBS->AllocatePages (
AllocateMaxAddress,
EfiACPIReclaimMemory,
EFI_SIZE_TO_PAGES(bufferLen),
&dsdt
);
//if success insert dsdt pointer into ACPI tables
if(!EFI_ERROR(Status)) {
// DBG("page is allocated, write DSDT into\n");
CopyMem((void*)(UINTN)dsdt, buffer, bufferLen);
//once we copied buffer to other place we can free the buffer
FadtPointer->Dsdt = (UINT32)dsdt;
FadtPointer->XDsdt = dsdt;
FixChecksum(&FadtPointer->Header);
DsdtLoaded = true;
}
}
if(buffer) FreePool(buffer); //the buffer is allocated if egLoadFile() is success. Else the pointer must be nullptr
if (!DsdtLoaded) {
// allocate space for fixes
TableHeader = (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)FadtPointer->Dsdt;
bufferLen = TableHeader->Length;
// DBG("DSDT len = 0x%X", bufferLen);
// bufferLen = bufferLen + bufferLen / 8;
// DBG(" new len = 0x%X\n", bufferLen);
//Slice: new buffer is greater then origin by 12.5%. It is dirty hack but we live with it
// there will be the sense reallocate buffer if one patch requires increasing the buffer
// this is headache to predict how many new bytes we need.
dsdt = EFI_SYSTEM_TABLE_MAX_ADDRESS;
Status = gBS->AllocatePages(AllocateMaxAddress,
EfiACPIReclaimMemory,
EFI_SIZE_TO_PAGES(bufferLen + bufferLen / 8),
&dsdt);
//if success insert dsdt pointer into ACPI tables
if(!EFI_ERROR(Status)) {
CopyMem((void*)(UINTN)dsdt, TableHeader, bufferLen); //can't free TableHeader because we are not allocate it
FadtPointer->Dsdt = (UINT32)dsdt;
FadtPointer->XDsdt = dsdt;
FixChecksum(&FadtPointer->Header);
}
}
// dropDSM = 0xFFFF; //by default we drop all OEM _DSM. They have no sense for us.
// if (defDSM) {
// dropDSM = gSettings.DropOEM_DSM; //if set by user
// }
if (gSettings.ACPI.DSDT.DebugDSDT) {
TableHeader = (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)FadtPointer->XDsdt;
bufferLen = TableHeader->Length;
DBG("Output DSDT before patch to %ls\\ACPI\\origin\\DSDT-or.aml\n", selfOem.getConfigDirFullPath().wc_str());
Status = egSaveFile(&selfOem.getConfigDir(), L"ACPI\\origin\\DSDT-or.aml", (UINT8*)(UINTN)FadtPointer->XDsdt, bufferLen);
}
//native DSDT or loaded we want to apply autoFix to this
// if (gSettings.ACPI.DSDT.FixDsdt) { //fix even with zero mask because we want to know PCIRootUID and count(?)
DBG("Apply DsdtFixMask=0x%08X\n", gSettings.ACPI.DSDT.FixDsdt);
// DBG(" drop _DSM mask=0x%04hX\n", dropDSM);
FixBiosDsdt((UINT8*)(UINTN)FadtPointer->XDsdt, FadtPointer, OSVersion);
if (gSettings.ACPI.DSDT.DebugDSDT) {
for (Index=0; Index < 60; Index++) {
XStringW DsdtPatchedName = SWPrintf("ACPI\\origin\\DSDT-pa%llu.aml", Index);
if(!FileExists(&selfOem.getConfigDir(), DsdtPatchedName)){
Status = egSaveFile(&selfOem.getConfigDir(), DsdtPatchedName.wc_str(), (UINT8*)(UINTN)FadtPointer->XDsdt, bufferLen);
if (!EFI_ERROR(Status)) {
break;
}
}
}
if (EFI_ERROR(Status)) {
DBG("...saving DSDT failed with status=%s\n", efiStrError(Status));
}
}
// handle unusual situations with XSDT and RSDT
PreCleanupRSDT();
PreCleanupXSDT();
// XsdtReplaceSizes array is used to keep track of allocations for the merged tables,
// as those tables may need to be freed if patched later.
XsdtReplaceSizes = (__typeof__(XsdtReplaceSizes))AllocateZeroPool(XsdtTableCount() * sizeof(*XsdtReplaceSizes));
// Load merged ACPI files from ACPI/patched
LoadAllPatchedAML(L"ACPI\\patched"_XSW, AUTOMERGE_PASS1);
// Drop tables
if (GlobalConfig.ACPIDropTables.notEmpty()) {
DbgHeader("ACPIDropTables");
for ( size_t idx = 0 ; idx < GlobalConfig.ACPIDropTables.length() ; ++idx ) {
ACPI_DROP_TABLE& DropTable = GlobalConfig.ACPIDropTables[idx];
if (DropTable.MenuItem.BValue) {
//DBG("Attempting to drop \"%4.4a\" (%8.8X) \"%8.8a\" (%16.16lX) L=%d\n", &(DropTable.Signature), DropTable.Signature, &(DropTable.TableId), DropTable.TableId, DropTable.Length);
DropTableFromXSDT(DropTable.Signature, DropTable.TableId, DropTable.Length);
DropTableFromRSDT(DropTable.Signature, DropTable.TableId, DropTable.Length);
}
}
}
if (GlobalConfig.DropSSDT) {
DbgHeader("DropSSDT");
//special case if we set into menu drop all SSDT
DropTableFromXSDT(EFI_ACPI_4_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE, 0, 0);
DropTableFromRSDT(EFI_ACPI_4_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE, 0, 0);
}
//It's time to fix headers of all remaining ACPI tables.
// The bug reported by TheRacerMaster and https://alextjam.es/debugging-appleacpiplatform/
// Workaround proposed by cecekpawon, revised by Slice
PatchAllTables();
// Load add-on ACPI files from ACPI/patched
LoadAllPatchedAML(L"ACPI\\patched"_XSW, AUTOMERGE_PASS2);
if (XsdtReplaceSizes) {
FreePool(XsdtReplaceSizes);
XsdtReplaceSizes = NULL;
}
//Slice - this is a time to patch MADT table.
// DBG("Fool proof: size of APIC NMI = %d\n", sizeof(EFI_ACPI_2_0_LOCAL_APIC_NMI_STRUCTURE));
// DBG("----------- size of APIC DESC = %d\n", sizeof(EFI_ACPI_2_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER));
// DBG("----------- size of APIC PROC = %d\n", sizeof(EFI_ACPI_2_0_PROCESSOR_LOCAL_APIC_STRUCTURE));
ApicCPUNum = 0;
// 2. For absent NMI subtable
xf = ScanXSDT(APIC_SIGN, 0);
if (xf) {
ApicTable = (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)(*xf);
// ApicLen = ApicTable->Length;
ProcLocalApic = (EFI_ACPI_2_0_PROCESSOR_LOCAL_APIC_STRUCTURE *)(UINTN)(*xf + sizeof(EFI_ACPI_2_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER));
while ((ProcLocalApic->Type == EFI_ACPI_4_0_PROCESSOR_LOCAL_APIC) && (ProcLocalApic->Length == 8)) {
if (ProcLocalApic->Flags & EFI_ACPI_4_0_LOCAL_APIC_ENABLED) {
ApicCPUNum++;
}
ProcLocalApic++;
if (ApicCPUNum > 16) {
DBG("Out of control with CPU numbers\n");
break;
}
}
//fool proof
if ((ApicCPUNum == 0) || (ApicCPUNum > 16)) {
ApicCPUNum = gCPUStructure.Threads;
}
DBG("ApicCPUNum=%llu\n", ApicCPUNum);
//reallocate table
if (gSettings.ACPI.PatchNMI) {
BufferPtr = EFI_SYSTEM_TABLE_MAX_ADDRESS;
Status=gBS->AllocatePages(AllocateMaxAddress, EfiACPIReclaimMemory, 1, &BufferPtr);
if(!EFI_ERROR(Status)) {
//save old table and drop it from XSDT
CopyMem((void*)(UINTN)BufferPtr, ApicTable, ApicTable->Length);
DropTableFromXSDT(APIC_SIGN, 0, 0);
ApicTable = (EFI_ACPI_DESCRIPTION_HEADER*)(UINTN)BufferPtr;
ApicTable->Revision = EFI_ACPI_4_0_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION;
CopyMem(&ApicTable->OemId, oemID, 6);
CopyMem(&ApicTable->OemTableId, oemTableID, 8);
ApicTable->OemRevision = 0x00000001;
CopyMem(&ApicTable->CreatorId, creatorID, 4);
SubTable = (UINT8*)((UINTN)BufferPtr + sizeof(EFI_ACPI_2_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER));
Index = 0;
while (*SubTable != EFI_ACPI_4_0_LOCAL_APIC_NMI) {
DBG("Found subtable in MADT: type=%d\n", *SubTable);
if (*SubTable == EFI_ACPI_4_0_PROCESSOR_LOCAL_APIC) {
ProcLocalApic = (EFI_ACPI_2_0_PROCESSOR_LOCAL_APIC_STRUCTURE *)SubTable;
// macOS assumes that the first processor from DSDT is always enabled, without checking MADT table
// here we're trying to assign first IDs found in DSDT to enabled processors in MADT, such that macOS assumption to be true
if (ProcLocalApic->Flags & EFI_ACPI_4_0_LOCAL_APIC_ENABLED) {
if (ProcLocalApic->AcpiProcessorId != acpi_cpu_processor_id[Index]) {
DBG("AcpiProcessorId changed: 0x%02hhX to 0x%02hhX\n", ProcLocalApic->AcpiProcessorId, acpi_cpu_processor_id[Index]);
ProcLocalApic->AcpiProcessorId = acpi_cpu_processor_id[Index];
} else {
DBG("AcpiProcessorId: 0x%02hhX\n", ProcLocalApic->AcpiProcessorId);
}
Index++;
}
}
bufferLen = (UINTN)SubTable[1];
SubTable += bufferLen;
if (((UINTN)SubTable - (UINTN)BufferPtr) >= ApicTable->Length) {
break;
}
}
if (*SubTable == EFI_ACPI_4_0_LOCAL_APIC_NMI) {
DBG("LocalApicNMI is already present, no patch needed\n");
} else {
LocalApicNMI = (EFI_ACPI_2_0_LOCAL_APIC_NMI_STRUCTURE*)((UINTN)ApicTable + ApicTable->Length);
for (Index = 0; Index < ApicCPUNum; Index++) {
LocalApicNMI->Type = EFI_ACPI_4_0_LOCAL_APIC_NMI;
LocalApicNMI->Length = sizeof(EFI_ACPI_4_0_LOCAL_APIC_NMI_STRUCTURE);
LocalApicNMI->AcpiProcessorId = acpi_cpu_processor_id[Index];
LocalApicNMI->Flags = 5;
LocalApicNMI->LocalApicLint = 1;
LocalApicNMI++;
ApicTable->Length += sizeof(EFI_ACPI_4_0_LOCAL_APIC_NMI_STRUCTURE);
}
DBG("ApicTable new Length=%d\n", ApicTable->Length);
// insert corrected MADT
}
Status = InsertTable(ApicTable, ApicTable->Length);
if (!EFI_ERROR(Status)) {
DBG("New APIC table successfully inserted\n");
}
/*
Status = egSaveFile(&self.getSelfRootDir(), PatchedAPIC, (UINT8 *)ApicTable, ApicTable->Length);
if (EFI_ERROR(Status)) {
Status = egSaveFile(NULL, PatchedAPIC, (UINT8 *)ApicTable, ApicTable->Length);
}
if (!EFI_ERROR(Status)) {
DBG("Patched APIC table saved into efi/clover/acpi/origin/APIC-p.aml \n");
}
*/
}
}
}
else {
DBG("No APIC table Found !!!\n");
}
if (gCPUStructure.Threads >= gCPUStructure.Cores) {
ApicCPUNum = gCPUStructure.Threads;
} else {
ApicCPUNum = gCPUStructure.Cores;
}
// }
/*
At this moment we have CPU numbers from DSDT - acpi_cpu_num
and from CPU characteristics gCPUStructure
Also we had the number from APIC table ApicCPUNum
What to choose?
Since rev745 I will return to acpi_cpu_count global variable
*/
if (acpi_cpu_count) {
ApicCPUNum = acpi_cpu_count;
}
if (gSettings.ACPI.SSDT.Generate.GeneratePStates || gSettings.ACPI.SSDT.Generate.GeneratePluginType) {
Status = EFI_NOT_FOUND;
Ssdt = generate_pss_ssdt(ApicCPUNum);
if (Ssdt) {
Status = InsertTable(Ssdt, Ssdt->Length);
}
if(EFI_ERROR(Status)){
DBG("GeneratePStates failed: Status=%s\n", efiStrError(Status));
}
}
if (gSettings.ACPI.SSDT.Generate.GenerateCStates) {
Status = EFI_NOT_FOUND;
Ssdt = generate_cst_ssdt(FadtPointer, ApicCPUNum);
if (Ssdt) {
Status = InsertTable(Ssdt, Ssdt->Length);
}
if(EFI_ERROR(Status)){
DBG("GenerateCStates failed Status=%s\n", efiStrError(Status));
}
}
// remove NULL entries from RSDT and XSDT
PostCleanupRSDT();
PostCleanupXSDT();
if (NeedUpdate) {
gBS->InstallConfigurationTable(&gEfiAcpiTableGuid, RsdPointer);
gBS->InstallConfigurationTable(&gEfiAcpi10TableGuid, RsdPointer);
}
//free regions?
while (gRegions) {
tmpRegion = gRegions->next;
FreePool(gRegions);
gRegions = tmpRegion;
}
return EFI_SUCCESS;
}
/**
* Searches for TableName in PathPatched dirs and loads it
* to Buffer if found. Buffer is allocated here and should be released
* by caller.
*/
EFI_STATUS LoadAcpiTable (
CONST CHAR16 *PathPatched,
CONST CHAR16 *TableName,
UINT8 **Buffer,
UINTN *BufferLen
)
{
EFI_STATUS Status;
Status = EFI_NOT_FOUND;
// checking \EFI\ACPI\patched dir
XStringW TmpStr = SWPrintf("%ls\\%ls", PathPatched, TableName);
if (FileExists(&self.getCloverDir(), TmpStr)) {
DBG("found %ls\n", TmpStr.wc_str());
Status = egLoadFile(&self.getCloverDir(), TmpStr.wc_str(), Buffer, BufferLen);
}
return Status;
}
/**
* Searches for DSDT in AcpiOemPath or PathPatched dirs and inserts it
* to FadtPointer if found.
*/
EFI_STATUS LoadAndInjectDSDT(CONST CHAR16 *PathPatched,
EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *FadtPointer)
{
EFI_STATUS Status;
UINT8 *Buffer = NULL;
UINTN BufferLen = 0;
EFI_PHYSICAL_ADDRESS Dsdt;
// load if exists
Status = LoadAcpiTable(PathPatched, gSettings.ACPI.DSDT.DsdtName.wc_str(), &Buffer, &BufferLen);
if (!EFI_ERROR(Status)) {
// loaded - allocate EfiACPIReclaim
DBG("Loaded DSDT at \\%ls\\%ls\\%ls\n", self.getCloverDirFullPath().wc_str(), PathPatched, gSettings.ACPI.DSDT.DsdtName.wc_str());
Dsdt = EFI_SYSTEM_TABLE_MAX_ADDRESS; //0xFE000000;
Status = gBS->AllocatePages (
AllocateMaxAddress,
EfiACPIReclaimMemory,
EFI_SIZE_TO_PAGES(BufferLen),
&Dsdt
);
if(!EFI_ERROR(Status)) {
// copy DSDT into EfiACPIReclaim block
CopyMem((void*)(UINTN)Dsdt, Buffer, BufferLen);
// update FADT
FadtPointer->Dsdt = (UINT32)Dsdt;
FadtPointer->XDsdt = Dsdt;
FixChecksum(&FadtPointer->Header);
DBG("DSDT at 0x%llX injected to FADT 0x%llx\n", Dsdt, (UINTN)FadtPointer);
}
if(Buffer) FreePool(Buffer);
}
return Status;
}
/**
* Searches for TableName in AcpiOemPath or PathPatched dirs and inserts it
* to Rsdt and/or Xsdt (globals) if found.
*/
EFI_STATUS LoadAndInjectAcpiTable(CONST CHAR16 *PathPatched,
CONST CHAR16 *TableName)
{
EFI_STATUS Status;
UINT8 *Buffer = NULL;
UINTN BufferLen = 0;
EFI_ACPI_DESCRIPTION_HEADER *TableHeader = NULL;
// load if exists
Status = LoadAcpiTable(PathPatched, TableName, &Buffer, &BufferLen);
if(!EFI_ERROR(Status)) {
if (Buffer) {
// if this is SLIC, then remove previous SLIC if it is there
TableHeader = (EFI_ACPI_DESCRIPTION_HEADER*)Buffer;
if (TableHeader->Signature == SLIC_SIGN) {
DropTableFromXSDT(SLIC_SIGN, 0, 0);
DropTableFromRSDT(SLIC_SIGN, 0, 0);
}
}
// loaded - insert it into XSDT/RSDT
Status = InsertTable(Buffer, BufferLen);
if(!EFI_ERROR(Status)) {
DBG("Table %ls inserted.\n", TableName);
// if this was SLIC, then update IDs in XSDT/RSDT
if (TableHeader->Signature == SLIC_SIGN) {
if (Rsdt) {
DBG("SLIC: Rsdt OEMid '%6.6s', TabId '%8.8s'", (CHAR8*)&Rsdt->Header.OemId, (CHAR8*)&Rsdt->Header.OemTableId);
CopyMem(&Rsdt->Header.OemId, &TableHeader->OemId, 6);
Rsdt->Header.OemTableId = TableHeader->OemTableId;
DBG(" to OEMid '%6.6s', TabId '%8.8s'\n", (CHAR8*)&Rsdt->Header.OemId, (CHAR8*)&Rsdt->Header.OemTableId);
}
if (Xsdt) {
DBG("SLIC: Xsdt OEMid '%6.6s', TabId '%8.8s'", (CHAR8*)&Xsdt->Header.OemId, (CHAR8*)&Xsdt->Header.OemTableId);
CopyMem(&Xsdt->Header.OemId, &TableHeader->OemId, 6);
Xsdt->Header.OemTableId = TableHeader->OemTableId;
DBG(" to OEMid '%6.6s', TabId '%8.8s'\n", (CHAR8*)&Xsdt->Header.OemId, (CHAR8*)&Xsdt->Header.OemTableId);
}
}
} else {
DBG("Insert return status %s\n", efiStrError(Status));
}
FreePool(Buffer);
} // if table loaded
return Status;
}
/**
* Patches UEFI ACPI tables with tables found in OsSubdir.
*/
EFI_STATUS PatchACPI_OtherOS(CONST CHAR16* OsSubdir, XBool DropSSDT)
{
EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *RsdPointer;
EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *FadtPointer;
EFI_STATUS Status; // = EFI_SUCCESS;
// UINTN Index;
REFIT_DIR_ITER DirIter;
EFI_FILE_INFO *DirEntry;
//
// Search for RSDP in UEFI SystemTable/ConfigTable (first Acpi 2.0, then 1.0)
//
RsdPointer = NULL;
Status = EfiGetSystemConfigurationTable (&gEfiAcpi20TableGuid, (void **) &RsdPointer);
if (RsdPointer != NULL) {
DBG("OtherOS: Found Acpi 2.0 RSDP 0x%llX\n", (uintptr_t)RsdPointer);
} else {
Status = EfiGetSystemConfigurationTable (&gEfiAcpi10TableGuid, (void **) &RsdPointer);
if (RsdPointer != NULL) {
DBG("Found Acpi 1.0 RSDP 0x%llX\n", (uintptr_t)RsdPointer);
}
}
// if RSDP not found - quit
if (!RsdPointer) {
return Status;
}
//
// Find RSDT and/or XSDT
//
Rsdt = (RSDT_TABLE*)(UINTN)(RsdPointer->RsdtAddress);
if (Rsdt != NULL && Rsdt->Header.Signature != EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {
Rsdt = NULL;
}
DBG("RSDT at 0x%llX\n", (UINTN)Rsdt);
// check for XSDT
Xsdt = NULL;
if (RsdPointer->Revision >=2 && (RsdPointer->XsdtAddress < (UINT64)((UINTN)(-1)))) {
Xsdt = (XSDT_TABLE*)(UINTN)RsdPointer->XsdtAddress;
if (Xsdt != NULL && Xsdt->Header.Signature != EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {
Xsdt = NULL;
}
}
DBG("XSDT at 0x%llX\n", (UINTN)Xsdt);
// if RSDT and XSDT not found - quit
if (Rsdt == NULL && Xsdt == NULL) {
return EFI_UNSUPPORTED;
}
//
// Take FADT (FACP) from XSDT or RSDT (always first entry)
//
FadtPointer = NULL;
if (Xsdt) {
FadtPointer = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE*)(UINTN)(Xsdt->Entry);
} else if (Rsdt) {
FadtPointer = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE*)(UINTN)(Rsdt->Entry);
}
DBG("FADT pointer = 0x%llX\n", (UINTN)FadtPointer);
// if not found - quit
if(FadtPointer == NULL) {
return EFI_NOT_FOUND;
}
//
// Inject/drop tables
//
// prepare dirs that will be searched for custom ACPI tables
XStringW PathPatched;
if ( selfOem.oemDirExists() ) {
PathPatched = SWPrintf("%ls\\ACPI\\%ls", selfOem.getOemPathRelToSelfDir().wc_str(), OsSubdir);
if ( !FileExists(&self.getCloverDir(), PathPatched) ) {
PathPatched.setEmpty();
}
}
if ( PathPatched.isEmpty() ) {
PathPatched = SWPrintf("ACPI\\%ls", OsSubdir);
if (!FileExists(&self.getCloverDir(), PathPatched)) {
DBG("Dir '\\%ls\\%ls' not found. No patching will be done.\n", self.getCloverDirFullPath().wc_str(), PathPatched.wc_str());
return EFI_NOT_FOUND;
}
}
//
// Inject DSDT
//
/* Status = */LoadAndInjectDSDT(PathPatched.wc_str(), FadtPointer);
//
// Drop SSDT if requested. Not until now
//
/*
if (DropSSDT) {
DropTableFromXSDT(EFI_ACPI_4_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE, 0, 0);
DropTableFromRSDT(EFI_ACPI_4_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE, 0, 0);
}
*/
if (GlobalConfig.ACPIDropTables.notEmpty()) {
for ( size_t idx = 0 ; idx < GlobalConfig.ACPIDropTables.length() ; ++idx ) {
ACPI_DROP_TABLE& DropTable = GlobalConfig.ACPIDropTables[idx];
DropTable.MenuItem.ItemType = BoolValue;
}
}
if (GlobalConfig.ACPIDropTables.notEmpty()) {
DbgHeader("ACPIDropTables");
for ( size_t idx = 0 ; idx < GlobalConfig.ACPIDropTables.length() ; ++idx ) {
ACPI_DROP_TABLE& DropTable = GlobalConfig.ACPIDropTables[idx];
// only for tables that have OtherOS true
if (DropTable.OtherOS && DropTable.MenuItem.BValue) {
//DBG("Attempting to drop \"%4.4a\" (%8.8X) \"%8.8a\" (%16.16lX) L=%d\n", &(DropTable.Signature), DropTable.Signature, &(DropTable.TableId), DropTable.TableId, DropTable.Length);
DropTableFromXSDT(DropTable.Signature, DropTable.TableId, DropTable.Length);
DropTableFromRSDT(DropTable.Signature, DropTable.TableId, DropTable.Length);
}
}
}
//
// find and inject other ACPI tables
//
DirIterOpen(&self.getCloverDir(), PathPatched.wc_str(), &DirIter);
while (DirIterNext(&DirIter, 2, L"*.aml", &DirEntry)) {
if (DirEntry->FileName[0] == L'.') {
continue;
}
if (StrStr(DirEntry->FileName, L"DSDT")) {
continue;
}
LoadAndInjectAcpiTable(PathPatched.wc_str(), DirEntry->FileName);
}
/*Status = */DirIterClose(&DirIter);
// remove NULL entries from RSDT and XSDT
PostCleanupRSDT();
PostCleanupXSDT();
return EFI_SUCCESS;
}