/* * Copyright 2008 mackerintel */ #include "libsaio.h" #include "bootstruct.h" #include "acpi.h" #include "efi_tables.h" #include "fake_efi.h" #include "dsdt_patcher.h" #ifndef DEBUG_DSDT #define DEBUG_DSDT 0 #endif #if DEBUG_DSDT==2 #define DBG(x...) {printf(x); sleep(1);} #elif DEBUG_DSDT==1 #define DBG(x...) printf(x) #else #define DBG(x...) #endif /* Gets the ACPI 1.0 RSDP address */ static struct acpi_2_rsdp* getAddressOfAcpiTable() { /* TODO: Before searching the BIOS space we are supposed to search the first 1K of the EBDA */ void *acpi_addr = (void*)ACPI_RANGE_START; for(; acpi_addr <= (void*)ACPI_RANGE_END; acpi_addr += 16) { if(*(uint64_t *)acpi_addr == ACPI_SIGNATURE_UINT64_LE) { uint8_t csum = checksum8(acpi_addr, 20); if(csum == 0) { // Only return the table if it is a true version 1.0 table (Revision 0) if(((struct acpi_2_rsdp*)acpi_addr)->Revision == 0) return acpi_addr; } } } return NULL; } /* Gets the ACPI 2.0 RSDP address */ static struct acpi_2_rsdp* getAddressOfAcpi20Table() { /* TODO: Before searching the BIOS space we are supposed to search the first 1K of the EBDA */ void *acpi_addr = (void*)ACPI_RANGE_START; for(; acpi_addr <= (void*)ACPI_RANGE_END; acpi_addr += 16) { if(*(uint64_t *)acpi_addr == ACPI_SIGNATURE_UINT64_LE) { uint8_t csum = checksum8(acpi_addr, 20); /* Only assume this is a 2.0 or better table if the revision is greater than 0 * NOTE: ACPI 3.0 spec only seems to say that 1.0 tables have revision 1 * and that the current revision is 2.. I am going to assume that rev > 0 is 2.0. */ if(csum == 0 && (((struct acpi_2_rsdp*)acpi_addr)->Revision > 0)) { uint8_t csum2 = checksum8(acpi_addr, sizeof(struct acpi_2_rsdp)); if(csum2 == 0) return acpi_addr; } } } return NULL; } void *loadACPITable (const char *filename) { void *kernelAddr; int fd; char dirspec[512]; // Check booting partition sprintf(dirspec,"%s",filename); fd=open (dirspec,0); if (fd<0) { // Check Extra on booting partition sprintf(dirspec,"/Extra/%s",filename); fd=open (dirspec,0); if (fd<0) { // Fall back to booter partition sprintf(dirspec,"bt(0,0)/Extra/%s",filename); fd=open (dirspec,0); if (fd<0) { verbose("ACPI Table not found: %s\n", filename); return NULL; } } } kernelAddr=(void*)AllocateKernelMemory(file_size (fd)); if (kernelAddr) { read (fd, kernelAddr, file_size (fd)); DBG("File read and stored at: %x\n", kernelAddr); close (fd); return kernelAddr; } close (fd); return NULL; } /* Setup ACPI without replacing DSDT. */ int setupAcpiNoMod() { // addConfigurationTable(&gEfiAcpiTableGuid, getAddressOfAcpiTable(), "ACPI"); // addConfigurationTable(&gEfiAcpi20TableGuid, getAddressOfAcpi20Table(), "ACPI_20"); acpi10_p = (uint32_t)getAddressOfAcpiTable(); acpi20_p = (uint32_t)getAddressOfAcpi20Table(); addConfigurationTable(&gEfiAcpiTableGuid, &acpi10_p, "ACPI"); if(acpi20_p) addConfigurationTable(&gEfiAcpi20TableGuid, &acpi20_p, "ACPI_20"); return 1; } /* Setup ACPI. Replace DSDT if DSDT.aml is found */ int setupAcpi() { int version; void *new_dsdt; const char *dsdt_filename; const char *facp_filename; int len; boolean_t drop_ssdt=NO; boolean_t FixRestart=NO; //DBG("Enter setupACPI\n"); if (!getValueForKey("DSDT", &dsdt_filename, &len, &bootInfo->bootConfig)) dsdt_filename="DSDT.aml"; // Load replacement DSDT new_dsdt=loadACPITable(dsdt_filename); if (!new_dsdt) { return setupAcpiNoMod(); } DBG("New DSDT Loaded in memory\n"); BOOL tmp; drop_ssdt=getBoolForKey("DropSSDT",&tmp, &bootInfo->bootConfig)&&tmp; FixRestart=getBoolForKey("RestartFix",&tmp, &bootInfo->bootConfig)&&tmp; // Do the same procedure for both versions of ACPI for (version=0;version<2;version++) { struct acpi_2_rsdp *rsdp, *rsdp_mod; struct acpi_2_rsdt *rsdt, *rsdt_mod; int rsdplength; // Find original rsdp rsdp=(struct acpi_2_rsdp *)(version?getAddressOfAcpi20Table():getAddressOfAcpiTable()); if (!rsdp) { DBG("No ACPI version %d found. Ignoring\n", version+1); if (version) addConfigurationTable(&gEfiAcpi20TableGuid, NULL, "ACPI_20"); else addConfigurationTable(&gEfiAcpiTableGuid, NULL, "ACPI"); continue; } rsdplength=version?rsdp->Length:20; DBG("RSDP version %d found @%x. Length=%d\n",version+1,rsdp,rsdplength); /* FIXME: no check that memory allocation succeeded * Copy and patch RSDP,RSDT, XSDT and FADT * For more info see ACPI Specification pages 110 and following */ rsdp_mod=(struct acpi_2_rsdp *) AllocateKernelMemory(rsdplength); memcpy(rsdp_mod, rsdp, rsdplength); rsdt=(struct acpi_2_rsdt *)(rsdp->RsdtAddress); DBG("RSDT @%x, Length %d\n",rsdt, rsdt->Length); if (rsdt && (uint32_t)rsdt !=0xffffffff && rsdt->Length<0x10000) { uint32_t *rsdt_entries; int rsdt_entries_num; int dropoffset=0, i; rsdt_mod=(struct acpi_2_rsdt *)AllocateKernelMemory(rsdt->Length); memcpy (rsdt_mod, rsdt, rsdt->Length); rsdp_mod->RsdtAddress=(uint32_t)rsdt_mod; rsdt_entries_num=(rsdt_mod->Length-sizeof(struct acpi_2_rsdt))/4; rsdt_entries=(uint32_t *)(rsdt_mod+1); for (i=0;iLength); if (!fadt || (uint32_t)fadt == 0xffffffff || fadt->Length>0x10000) { printf("FADT incorrect. Not modified\n"); continue; } if (fadt->Length < 0x81 && FixRestart) { fadt_mod=(struct acpi_2_fadt *)AllocateKernelMemory(0x81); memcpy(fadt_mod, fadt, fadt->Length); fadt_mod->Length = 0x81; } else { fadt_mod=(struct acpi_2_fadt *)AllocateKernelMemory(fadt->Length); memcpy(fadt_mod, fadt, fadt->Length); } if (FixRestart) { fadt_mod->Flags|= 0x400; fadt_mod->Reset_SpaceID = 0x01; fadt_mod->Reset_BitWidth = 0x08; fadt_mod->Reset_BitOffset = 0x00; fadt_mod->Reset_AccessWidth = 0x01; fadt_mod->Reset_Address = 0x0cf9; fadt_mod->Reset_Value = 0x06; } // Patch DSDT Address DBG("Old DSDT @%x,%x\n",fadt_mod->DSDT,fadt_mod->X_DSDT); fadt_mod->DSDT=(uint32_t)new_dsdt; if ((uint32_t)(&(fadt_mod->X_DSDT))-(uint32_t)fadt_mod+8<=fadt_mod->Length) fadt_mod->X_DSDT=(uint32_t)new_dsdt; DBG("New DSDT @%x,%x\n",fadt_mod->DSDT,fadt_mod->X_DSDT); // Correct the checksum fadt_mod->Checksum=0; fadt_mod->Checksum=256-checksum8(fadt_mod,fadt_mod->Length); rsdt_entries[i-dropoffset]=(uint32_t)fadt_mod; continue; } } // Correct the checksum of RSDT rsdt_mod->Length-=4*dropoffset; DBG("RSDT Original checksum %d\n", rsdt_mod->Checksum); rsdt_mod->Checksum=0; rsdt_mod->Checksum=256-checksum8(rsdt_mod,rsdt_mod->Length); DBG("RSDT New checksum %d at %x\n", rsdt_mod->Checksum,rsdt_mod); } else { rsdp_mod->RsdtAddress=0; printf("RSDT not found or RSDT incorrect\n"); } if (version) { struct acpi_2_xsdt *xsdt, *xsdt_mod; // FIXME: handle 64-bit address correctly xsdt=(struct acpi_2_xsdt*) ((uint32_t)rsdp->XsdtAddress); DBG("XSDT @%x;%x, Length=%d\n", (uint32_t)(rsdp->XsdtAddress>>32),(uint32_t)rsdp->XsdtAddress, xsdt->Length); if (xsdt && (uint64_t)rsdp->XsdtAddress<0xffffffff && xsdt->Length<0x10000) { uint64_t *xsdt_entries; int xsdt_entries_num, i; int dropoffset=0; xsdt_mod=(struct acpi_2_xsdt*)AllocateKernelMemory(xsdt->Length); memcpy(xsdt_mod, xsdt, xsdt->Length); rsdp_mod->XsdtAddress=(uint32_t)xsdt_mod; xsdt_entries_num=(xsdt_mod->Length-sizeof(struct acpi_2_xsdt))/8; xsdt_entries=(uint64_t *)(xsdt_mod+1); for (i=0;i>32),fadt, fadt->Length); if (!fadt || (uint64_t)xsdt_entries[i] >= 0xffffffff || fadt->Length>0x10000) { printf("FADT incorrect or after 4GB. Dropping XSDT\n"); goto drop_xsdt; } if (fadt->Length < 0x81 && FixRestart) { fadt_mod=(struct acpi_2_fadt *)AllocateKernelMemory(0x81); memcpy(fadt_mod, fadt, fadt->Length); fadt_mod->Length = 0x81; } else { fadt_mod=(struct acpi_2_fadt *)AllocateKernelMemory(fadt->Length); memcpy(fadt_mod, fadt, fadt->Length); } if (FixRestart) { fadt_mod->Flags|= 0x400; fadt_mod->Reset_SpaceID = 0x01; fadt_mod->Reset_BitWidth = 0x08; fadt_mod->Reset_BitOffset = 0x00; fadt_mod->Reset_AccessWidth = 0x01; fadt_mod->Reset_Address = 0x0cf9; fadt_mod->Reset_Value = 0x06; } DBG("Old DSDT @%x,%x\n",fadt_mod->DSDT,fadt_mod->X_DSDT); fadt_mod->DSDT=(uint32_t)new_dsdt; if ((uint32_t)(&(fadt_mod->X_DSDT))-(uint32_t)fadt_mod+8<=fadt_mod->Length) fadt_mod->X_DSDT=(uint32_t)new_dsdt; DBG("New DSDT @%x,%x\n",fadt_mod->DSDT,fadt_mod->X_DSDT); // Correct the checksum fadt_mod->Checksum=0; fadt_mod->Checksum=256-checksum8(fadt_mod,fadt_mod->Length); xsdt_entries[i-dropoffset]=(uint32_t)fadt_mod; continue; } DBG("TABLE %c%c%c%c@%x,",table[0],table[1],table[2],table[3],xsdt_entries[i]); } // Correct the checksum of XSDT xsdt_mod->Length-=8*dropoffset; xsdt_mod->Checksum=0; xsdt_mod->Checksum=256-checksum8(xsdt_mod,xsdt_mod->Length); } else { drop_xsdt: DBG("About to drop XSDT\n"); /*FIXME: Now we just hope that if MacOS doesn't find XSDT it reverts to RSDT. * A Better strategy would be to generate */ rsdp_mod->XsdtAddress=0xffffffffffffffffLL; printf("XSDT not found or XSDT incorrect\n"); } } // Correct the checksum of RSDP DBG("Original checksum %d\n", rsdp_mod->Checksum); rsdp_mod->Checksum=0; rsdp_mod->Checksum=256-checksum8(rsdp_mod,20); DBG("New checksum %d\n", rsdp_mod->Checksum); if (version) { DBG("Original extended checksum %d\n", rsdp_mod->ExtendedChecksum); rsdp_mod->ExtendedChecksum=0; rsdp_mod->ExtendedChecksum=256-checksum8(rsdp_mod,rsdp_mod->Length); DBG("New extended checksum %d\n", rsdp_mod->ExtendedChecksum); } verbose("Patched ACPI version %d DSDT\n", version+1); if (version) { acpi20_p = (uint32_t)rsdp_mod; addConfigurationTable(&gEfiAcpi20TableGuid, &acpi20_p, "ACPI_20"); } else { acpi10_p = (uint32_t)rsdp_mod; addConfigurationTable(&gEfiAcpiTableGuid, &acpi10_p, "ACPI"); } } #if DEBUG_DSDT getc(); #endif return 1; }