# Original source: # https://github.com/corpnewt/SSDTTime/blob/7b3fb78112bf320a1bc6a7e50dddb2b375cb70b0/SSDTTime.py from Scripts.datasets import chipset_data from Scripts.datasets import cpu_data from Scripts.datasets import pci_data from Scripts import smbios from Scripts import dsdt from Scripts import run from Scripts import utils import os import binascii import re import tempfile import shutil import sys class ACPIGuru: def __init__(self): self.acpi = dsdt.DSDT() self.smbios = smbios.SMBIOS() self.run = run.Run().run self.utils = utils.Utils() self.dsdt = None self.lpc_bus_device = None self.osi_strings = { "Windows 2000": "Windows 2000", "Windows XP": "Windows 2001", "Windows XP SP1": "Windows 2001 SP1", "Windows Server 2003": "Windows 2001.1", "Windows XP SP2": "Windows 2001 SP2", "Windows Server 2003 SP1": "Windows 2001.1 SP1", "Windows Vista": "Windows 2006", "Windows Vista SP1": "Windows 2006 SP1", "Windows Server 2008": "Windows 2006.1", "Windows 7, Win Server 2008 R2": "Windows 2009", "Windows 8, Win Server 2012": "Windows 2012", "Windows 8.1": "Windows 2013", "Windows 10": "Windows 2015", "Windows 10, version 1607": "Windows 2016", "Windows 10, version 1703": "Windows 2017", "Windows 10, version 1709": "Windows 2017.2", "Windows 10, version 1803": "Windows 2018", "Windows 10, version 1809": "Windows 2018.2", "Windows 10, version 1903": "Windows 2019", "Windows 10, version 2004": "Windows 2020", "Windows 11": "Windows 2021", "Windows 11, version 22H2": "Windows 2022" } self.pre_patches = ( { "PrePatch":"GPP7 duplicate _PRW methods", "Comment" :"GPP7._PRW to XPRW to fix Gigabyte's Mistake", "Find" :"3708584847500A021406535245470214065350525701085F505257", "Replace" :"3708584847500A0214065352454702140653505257010858505257" }, { "PrePatch":"GPP7 duplicate UP00 devices", "Comment" :"GPP7.UP00 to UPXX to fix Gigabyte's Mistake", "Find" :"1047052F035F53425F50434930475050375B82450455503030", "Replace" :"1047052F035F53425F50434930475050375B82450455505858" }, { "PrePatch":"GPP6 duplicate _PRW methods", "Comment" :"GPP6._PRW to XPRW to fix ASRock's Mistake", "Find" :"47505036085F4144520C04000200140F5F505257", "Replace" :"47505036085F4144520C04000200140F58505257" }, { "PrePatch":"GPP1 duplicate PTXH devices", "Comment" :"GPP1.PTXH to XTXH to fix MSI's Mistake", "Find" :"50545848085F41445200140F", "Replace" :"58545848085F41445200140F" } ) self.target_irqs = [0, 2, 8, 11] self.illegal_names = ("XHC1", "EHC1", "EHC2", "PXSX") self.result = { "Add": [], "Delete": [], "Patch": [] } def get_unique_name(self,name,target_folder,name_append="-Patched"): # Get a new file name in the Results folder so we don't override the original name = os.path.basename(name) ext = "" if not "." in name else name.split(".")[-1] if ext: name = name[:-len(ext)-1] if name_append: name = name+str(name_append) check_name = ".".join((name,ext)) if ext else name if not os.path.exists(os.path.join(target_folder,check_name)): return check_name # We need a unique name num = 1 while True: check_name = "{}-{}".format(name,num) if ext: check_name += "."+ext if not os.path.exists(os.path.join(target_folder,check_name)): return check_name num += 1 # Increment our counter def get_unique_device(self, path, base_name, starting_number=0, used_names = []): # Appends a hex number until a unique device is found while True: hex_num = hex(starting_number).replace("0x","").upper() name = base_name[:-1*len(hex_num)]+hex_num if not len(self.acpi.get_device_paths("."+name)) and not name in used_names: return (name,starting_number) starting_number += 1 def sorted_nicely(self, l): convert = lambda text: int(text) if text.isdigit() else text alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key.lower()) ] return sorted(l, key = alphanum_key) def read_dsdt(self, path): if not path: return tables = [] trouble_dsdt = None fixed = False temp = None prior_tables = self.acpi.acpi_tables # Retain in case of failure # Clear any existing tables so we load anew self.acpi.acpi_tables = {} if os.path.isdir(path): print("Gathering valid tables from {}...\n".format(os.path.basename(path))) for t in self.sorted_nicely(os.listdir(path)): if self.acpi.table_is_valid(path,t): print(" - {}".format(t)) tables.append(t) if not tables: # Check if there's an ACPI directory within the passed # directory - this may indicate SysReport was dropped if os.path.isdir(os.path.join(path,"ACPI")): # Rerun this function with that updated path return self.read_dsdt(os.path.join(path,"ACPI")) print(" - No valid .aml files were found!") print("") self.utils.request_input() # Restore any prior tables self.acpi.acpi_tables = prior_tables return print("") # We got at least one file - let's look for the DSDT specifically # and try to load that as-is. If it doesn't load, we'll have to # manage everything with temp folders dsdt_list = [x for x in tables if self.acpi._table_signature(path,x) == "DSDT"] if len(dsdt_list) > 1: print("Multiple files with DSDT signature passed:") for d in self.sorted_nicely(dsdt_list): print(" - {}".format(d)) print("\nOnly one is allowed at a time. Please remove one of the above and try again.") print("") self.utils.request_input() # Restore any prior tables self.acpi.acpi_tables = prior_tables return # Get the DSDT, if any dsdt = dsdt_list[0] if len(dsdt_list) else None if dsdt: # Try to load it and see if it causes problems print("Disassembling {} to verify if pre-patches are needed...".format(dsdt)) if not self.acpi.load(os.path.join(path,dsdt))[0]: trouble_dsdt = dsdt else: print("\nDisassembled successfully!\n") elif os.path.isfile(path): #print("Loading {}...".format(os.path.basename(path))) if self.acpi.load(path)[0]: #print("\nDone.") # If it loads fine - just return the path # to the parent directory return os.path.dirname(path) if not self.acpi._table_signature(path) == "DSDT": # Not a DSDT, we aren't applying pre-patches print("\n{} could not be disassembled!".format(os.path.basename(path))) print("") self.utils.request_input() # Restore any prior tables self.acpi.acpi_tables = prior_tables return # It didn't load - set it as the trouble file trouble_dsdt = os.path.basename(path) # Put the table in the tables list, and adjust # the path to represent the parent dir tables.append(os.path.basename(path)) path = os.path.dirname(path) else: print("Passed file/folder does not exist!") print("") self.utils.request_input() # Restore any prior tables self.acpi.acpi_tables = prior_tables return # If we got here - check if we have a trouble_dsdt. if trouble_dsdt: # We need to move our ACPI files to a temp folder # then try patching the DSDT there temp = tempfile.mkdtemp() for table in tables: shutil.copy( os.path.join(path,table), temp ) # Get a reference to the new trouble file trouble_path = os.path.join(temp,trouble_dsdt) # Now we try patching it print("Checking available pre-patches...") print("Loading {} into memory...".format(trouble_dsdt)) with open(trouble_path,"rb") as f: d = f.read() res = self.acpi.check_output(self.output) target_name = self.get_unique_name(trouble_dsdt,res,name_append="-Patched") patches = [] print("Iterating patches...\n") for p in self.pre_patches: if not all(x in p for x in ("PrePatch","Comment","Find","Replace")): continue print(" - {}".format(p["PrePatch"])) find = binascii.unhexlify(p["Find"]) if d.count(find) == 1: self.result.get("Patch").append(p) # Retain the patch repl = binascii.unhexlify(p["Replace"]) print(" --> Located - applying...") d = d.replace(find,repl) # Replace it in memory with open(trouble_path,"wb") as f: f.write(d) # Write the updated file # Attempt to load again if self.acpi.load(trouble_path)[0]: fixed = True # We got it to load - let's write the patches print("\nDisassembled successfully!\n") #self.make_plist(None, None, patches) # Save to the local file with open(os.path.join(res,target_name),"wb") as f: f.write(d) #print("\n!! Patches applied to modified file in Results folder:\n {}".format(target_name)) #self.patch_warn() break if not fixed: print("\n{} could not be disassembled!".format(trouble_dsdt)) print("") self.utils.request_input() if temp: shutil.rmtree(temp,ignore_errors=True) # Restore any prior tables self.acpi.acpi_tables = prior_tables return # Let's load the rest of the tables if len(tables) > 1: print("Loading valid tables in {}...".format(path)) loaded_tables,failed = self.acpi.load(temp or path) if not loaded_tables or failed: print("\nFailed to load tables in {}{}\n".format( os.path.dirname(path) if os.path.isfile(path) else path, ":" if failed else "" )) for t in self.sorted_nicely(failed): print(" - {}".format(t)) # Restore any prior tables if not loaded_tables: self.acpi.acpi_tables = prior_tables else: if len(tables) > 1: print("") # Newline for readability print("Done.") # If we had to patch the DSDT, or if not all tables loaded, # make sure we get interaction from the user to continue if trouble_dsdt or not loaded_tables or failed: print("") self.utils.request_input() if temp: shutil.rmtree(temp,ignore_errors=True) return path def get_sta_var(self,var="STAS",device=None,dev_hid="ACPI000E",dev_name="AWAC",log_locate=True,table=None): # Helper to check for a device, check for (and qualify) an _STA method, # and look for a specific variable in the _STA scope # # Returns a dict with device info - only "valid" parameter is # guaranteed. has_var = False patches = [] root = None if device: dev_list = self.acpi.get_device_paths(device,table=table) if not len(dev_list): #if log_locate: print(" - Could not locate {}".format(device)) return {"value":False} else: #if log_locate: print("Locating {} ({}) devices...".format(dev_hid,dev_name)) dev_list = self.acpi.get_device_paths_with_hid(dev_hid,table=table) if not len(dev_list): #if log_locate: print(" - Could not locate any {} devices".format(dev_hid)) return {"valid":False} dev = dev_list[0] #if log_locate: print(" - Found {}".format(dev[0])) root = dev[0].split(".")[0] #print(" --> Verifying _STA...") # Check Method first - then Name sta_type = "MethodObj" sta = self.acpi.get_method_paths(dev[0]+"._STA",table=table) xsta = self.acpi.get_method_paths(dev[0]+".XSTA",table=table) if not sta and not xsta: # Check for names sta_type = "IntObj" sta = self.acpi.get_name_paths(dev[0]+"._STA",table=table) xsta = self.acpi.get_name_paths(dev[0]+".XSTA",table=table) if xsta and not sta: #print(" --> _STA already renamed to XSTA! Skipping other checks...") #print(" Please disable _STA to XSTA renames for this device, reboot, and try again.") #print("") return {"valid":False,"break":True,"device":dev,"dev_name":dev_name,"dev_hid":dev_hid,"sta_type":sta_type} if sta: if var: scope = "\n".join(self.acpi.get_scope(sta[0][1],strip_comments=True,table=table)) has_var = var in scope #print(" --> {} {} variable".format("Has" if has_var else "Does NOT have",var)) else: pass #print(" --> No _STA method/name found") # Let's find out of we need a unique patch for _STA -> XSTA if sta and not has_var: #print(" --> Generating _STA to XSTA rename") sta_index = self.acpi.find_next_hex(sta[0][1],table=table)[1] #print(" ----> Found at index {}".format(sta_index)) sta_hex = "5F535441" # _STA xsta_hex = "58535441" # XSTA padl,padr = self.acpi.get_shortest_unique_pad(sta_hex,sta_index,table=table) patches.append({"Comment":"{} _STA to XSTA Rename".format(dev_name),"Find":padl+sta_hex+padr,"Replace":padl+xsta_hex+padr}) return {"valid":True,"has_var":has_var,"sta":sta,"patches":patches,"device":dev,"dev_name":dev_name,"dev_hid":dev_hid,"root":root,"sta_type":sta_type} def get_address_from_line(self, line, split_by="_ADR, ", table=None): if table is None: table = self.acpi.get_dsdt_or_only() try: return int(table["lines"][line].split(split_by)[1].split(")")[0].replace("Zero","0x0").replace("One","0x1"),16) except: return None def check_acpi_directory(self, acpi_directory): if acpi_directory: if not os.path.isdir(acpi_directory): self.utils.mkdirs(acpi_directory) return acpi_directory else: return acpi_directory return None def write_ssdt(self, ssdt_name, ssdt_content, compile=True): dsl_path = os.path.join(self.acpi_directory, ssdt_name + ".dsl") aml_path = os.path.join(self.acpi_directory, ssdt_name + ".aml") with open(dsl_path,"w") as f: f.write(ssdt_content) if not compile: return False output = self.run({ "args":[self.acpi.iasl, dsl_path] }) if output[-1] != 0: return False else: os.remove(dsl_path) return os.path.exists(aml_path) def apply_acpi_patches(self): for acpi_patch in self.result.get("Patch"): acpi_patch["Base"] = acpi_patch.get("Base", "") acpi_patch["BaseSkip"] = acpi_patch.get("BaseSkip", 0) acpi_patch["Count"] = acpi_patch.get("Count", 0) acpi_patch["Enabled"] = True acpi_patch["Find"] = self.utils.hex_to_bytes(acpi_patch["Find"]) acpi_patch["Limit"] = acpi_patch.get("Limit", 0) acpi_patch["Mask"] = self.utils.hex_to_bytes(acpi_patch.get("Mask", "")) acpi_patch["OemTableId"] = self.utils.hex_to_bytes(acpi_patch.get("OemTableId", "")) acpi_patch["Replace"] = self.utils.hex_to_bytes(acpi_patch["Replace"]) acpi_patch["ReplaceMask"] = self.utils.hex_to_bytes(acpi_patch.get("ReplaceMask", "")) acpi_patch["Skip"] = acpi_patch.get("Skip", 0) acpi_patch["TableLength"] = acpi_patch.get("TableLength", 0) acpi_patch["TableSignature"] = self.utils.hex_to_bytes(acpi_patch.get("TableSignature", "")) self.result["Patch"] = sorted(self.result["Patch"], key=lambda x: x["Comment"]) def get_low_pin_count_bus_device(self): for lpc_bus_name in ("LPCB", "LPC0", "LPC", "SBRG", "PX40"): try: self.lpc_bus_device = self.acpi.get_device_paths(lpc_bus_name)[0][0] break except: pass def add_intel_management_engine(self, cpu_codename, intel_mei): comment = "Creates a fake IMEI device to ensure Intel iGPUs acceleration functions properly" ssdt_name = "SSDT-IMEI" ssdt_content = """ DefinitionBlock ("", "SSDT", 2, "ZPSS", "IMEI", 0x00000000) { External (_SB_.PCI0, DeviceObj) Scope (_SB.PCI0) { Device (IMEI) { Name (_ADR, 0x00160000) // _ADR: Address Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } } } } }""" if intel_mei: if "Sandy Bridge" in cpu_codename and intel_mei.get("Device ID") in "8086-1E3A" or "Ivy Bridge" in cpu_codename and intel_mei.get("Device ID") in "8086-1C3A": imei_device = self.acpi.get_device_paths_with_hid("0x00160000", self.dsdt) if not imei_device: self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" }) def add_memory_controller_device(self, cpu_manufacturer, smbios): if "Intel" not in cpu_manufacturer or "MacPro" in smbios: return comment = "Add a Memory Controller Hub Controller device to fix AppleSMBus" ssdt_name = "SSDT-MCHC" ssdt_content = """ DefinitionBlock ("", "SSDT", 2, "ZPSS", "MCHC", 0) { External ([[PCIName]], DeviceObj) Scope ([[PCIName]]) { Device (MCHC) { Name (_ADR, Zero) Method (_STA, 0, NotSerialized) { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } } } } }""" mchc_device = self.acpi.get_device_paths("MCHC", self.dsdt) if mchc_device: return pci_bus_device = ".".join(self.lpc_bus_device.split(".")[:2]) ssdt_content = ssdt_content.replace("[[PCIName]]", pci_bus_device) self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" }) def add_system_management_bus_device(self, cpu_manufacturer, cpu_codename): if "Intel" not in cpu_manufacturer: return try: smbus_device_name = self.acpi.get_device_paths_with_hid("0x001F0003" if self.utils.contains_any(cpu_data.IntelCPUGenerations, cpu_codename, end=4) else "0x001F0004", self.dsdt)[0][0].split(".")[-1] except: smbus_device_name = "SBUS" pci_bus_device = ".".join(self.lpc_bus_device.split(".")[:2]) smbus_device_path = "{}.{}".format(pci_bus_device, smbus_device_name) comment = "Add a System Management Bus device to fix AppleSMBus issues" ssdt_name = "SSDT-{}".format(smbus_device_name) ssdt_content = """ DefinitionBlock ("", "SSDT", 2, "ZPSS", "[[SMBUSName]]", 0) { External ([[SMBUSDevice]], DeviceObj) Scope ([[SMBUSDevice]]) { Device (BUS0) { Name (_CID, "smbus") Name (_ADR, Zero) Method (_STA, 0, NotSerialized) { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } } } } }""".replace("[[SMBUSName]]", smbus_device_name).replace("[[SMBUSDevice]]", smbus_device_path) self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" }) def add_usb_power_properties(self, smbios): comment = "Creates an USBX device to inject USB power properties" ssdt_name = "SSDT-USBX" ssdt_content = """ DefinitionBlock ("", "SSDT", 2, "ZPSS", "USBX", 0x00001000) { Scope (\\_SB) { Device (USBX) { Name (_ADR, Zero) // _ADR: Address Method (_DSM, 4, NotSerialized) // _DSM: Device-Specific Method { If (LNot (Arg2)) { Return (Buffer () { 0x03 }) } Return (Package () {[[USBX_PROPS]] }) } Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } } } } }""" usb_power_properties = None if self.utils.contains_any(["MacPro7,1", "iMacPro1,1", "iMac20,", "iMac19,", "iMac18,", "iMac17,", "iMac16,"], smbios): usb_power_properties = { "kUSBSleepPowerSupply":"0x13EC", "kUSBSleepPortCurrentLimit":"0x0834", "kUSBWakePowerSupply":"0x13EC", "kUSBWakePortCurrentLimit":"0x0834" } elif "MacMini8,1" in smbios: usb_power_properties = { "kUSBSleepPowerSupply":"0x0C80", "kUSBSleepPortCurrentLimit":"0x0834", "kUSBWakePowerSupply":"0x0C80", "kUSBWakePortCurrentLimit":"0x0834" } elif self.utils.contains_any(["MacBookPro16,", "MacBookPro15,", "MacBookPro14,", "MacBookPro13,", "MacBookAir9,1"], smbios): usb_power_properties = { "kUSBSleepPortCurrentLimit":"0x0BB8", "kUSBWakePortCurrentLimit":"0x0BB8" } elif "MacBook9,1" in smbios: usb_power_properties = { "kUSBSleepPowerSupply":"0x05DC", "kUSBSleepPortCurrentLimit":"0x05DC", "kUSBWakePowerSupply":"0x05DC", "kUSBWakePortCurrentLimit":"0x05DC" } if usb_power_properties: ssdt_content = ssdt_content.replace("[[USBX_PROPS]]", "".join("\n \"{}\",\n {}{}".format(key, usb_power_properties[key], "," if index < len(usb_power_properties) else "") for index, key in enumerate(usb_power_properties, start=1)) ) self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" }) def ambient_light_sensor(self, motherboard_name, platform, integrated_gpu): if "Laptop" not in platform or not integrated_gpu or "SURFACE" in motherboard_name: return ssdt_name = "SSDT-ALS0" ssdt_content = """ // Resource: https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/AcpiSamples/Source/SSDT-ALS0.dsl /* * Starting with macOS 10.15 Ambient Light Sensor presence is required for backlight functioning. * Here we create an Ambient Light Sensor ACPI Device, which can be used by SMCLightSensor kext * to report either dummy (when no device is present) or valid values through SMC interface. */ DefinitionBlock ("", "SSDT", 2, "ZPSS", "ALS0", 0x00000000) { Scope (_SB) { Device (ALS0) { Name (_HID, "ACPI0008" /* Ambient Light Sensor Device */) // _HID: Hardware ID Name (_CID, "smc-als") // _CID: Compatible ID Name (_ALI, 0x012C) // _ALI: Ambient Light Illuminance Name (_ALR, Package (0x01) // _ALR: Ambient Light Response { Package (0x02) { 0x64, 0x012C } }) Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } } } } }""" try: als_device = self.acpi.get_device_paths_with_hid("ACPI0008", self.dsdt)[0][0] except: als_device = None if als_device: als_device_name = als_device.split(".")[-1] if "." not in als_device: als_device_name = als_device_name[1:] sta = self.get_sta_var(var=None, device=None, dev_hid="ACPI0008", dev_name=als_device_name, table=self.dsdt) if sta.get("patches"): self.result["Patch"].extend(sta.get("patches", [])) ssdt_name = "SSDT-{}".format(als_device_name) ssdt_content = """ DefinitionBlock ("", "SSDT", 2, "ZPSS", "[[ALSName]]", 0x00000000) { External ([[ALSDevice]], DeviceObj) External ([[ALSDevice]].XSTA, [[STAType]]) Scope ([[ALSDevice]]) { Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0) } Else { Return ([[XSTA]]) } } } }""".replace("[[ALSName]]", als_device_name) \ .replace("[[ALSDevice]]", als_device) \ .replace("[[STAType]]", sta.get("sta_type","MethodObj")) \ .replace("[[XSTA]]", "{}.XSTA{}".format(als_device," ()" if sta.get("sta_type","MethodObj") == "MethodObj" else "") if sta else "0x0F") comment = "{} Ambient Light Sensor device for storing the current brightness/auto-brightness level".format("Fake" if not als_device else "Enable") self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" }) def disable_rhub_devices(self): comment = "Disable RHUB/HUBN/URTH devices and rename PXSX, XHC1, EHC1, and EHC2 devices" ssdt_name = "SSDT-USB-Reset" ssdt_content = """ DefinitionBlock ("", "SSDT", 2, "ZPSS", "UsbReset", 0x00001000) { """ rhub_devices = self.acpi.get_device_paths("RHUB", self.dsdt) rhub_devices.extend(self.acpi.get_device_paths("HUBN", self.dsdt)) rhub_devices.extend(self.acpi.get_device_paths("URTH", self.dsdt)) if not len(rhub_devices): return tasks = [] used_names = [] xhc_num = 2 ehc_num = 1 for rhub_device in rhub_devices: task = { "device": rhub_device[0] } name = rhub_device[0].split(".")[-2] if name in self.illegal_names or name in used_names: task["device"] = ".".join(task["device"].split(".")[:-1]) task["parent"] = ".".join(task["device"].split(".")[:-1]) if name.startswith("EHC"): task["rename"],ehc_num = self.get_unique_device(task["parent"],"EH01",ehc_num,used_names) ehc_num += 1 # Increment the name number else: task["rename"],xhc_num = self.get_unique_device(task["parent"],"XHCI",xhc_num,used_names) xhc_num += 1 # Increment the name number used_names.append(task["rename"]) else: used_names.append(name) sta_method = self.acpi.get_method_paths(task["device"]+"._STA", self.dsdt) if len(sta_method): sta_index = self.acpi.find_next_hex(sta_method[0][1], self.dsdt)[1] sta_hex = "5F535441" xsta_hex = "58535441" padl,padr = self.acpi.get_shortest_unique_pad(sta_hex, sta_index, self.dsdt) self.result.get("Patch").append({ "Comment": "{} _STA to XSTA Rename".format(task["device"].split(".")[-1]), "Find": padl+sta_hex+padr, "Replace": padl+xsta_hex+padr }) scope_adr = self.acpi.get_name_paths(task["device"]+"._ADR", self.dsdt) task["address"] = self.dsdt.get("lines")[scope_adr[0][1]].strip() if len(scope_adr) else "Name (_ADR, Zero) // _ADR: Address" tasks.append(task) parents = sorted(list(set([task["parent"] for task in tasks if task.get("parent", None)]))) for parent in parents: ssdt_content += " External ({}, DeviceObj)\n".format(parent) for task in tasks: ssdt_content += " External ({}, DeviceObj)\n".format(task["device"]) for task in tasks: if task.get("rename", None): ssdt_content += """ Scope ([[device]]) { Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (Zero) } Else { Return (0x0F) } } } Scope ([[parent]]) { Device ([[new_device]]) { [[address]] Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } } } } """.replace("[[device]]", task["device"]) \ .replace("[[parent]]", task["parent"]) \ .replace("[[address]]", task.get("address", "Name (_ADR, Zero) // _ADR: Address")) \ .replace("[[new_device]]", task["rename"]) else: ssdt_content += """ Scope ([[device]]) { Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (Zero) } Else { Return (0x0F) } } } """.replace("[[device]]", task["device"]) ssdt_content += "\n}" self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" }) def disable_unsupported_device(self, unsupported_devices): for device_name, device_props in unsupported_devices.items(): if not "Storage" in device_name: continue comment = "Disable {}".format(device_name.split(": ")[-1]) ssdt_name = None if "Storage" in device_name: ssdt_name = "SSDT-Disable_NVMe" ssdt_content = """ // Replace all "_SB.PCI0.RP09.PXSX" with the ACPI path of your SSD NVMe DefinitionBlock ("", "SSDT", 2, "ZPSS", "DNVMe", 0x00000000) { External (_SB.PCI0.RP09.PXSX, DeviceObj) Method (_SB.PCI0.RP09.PXSX._DSM, 4, NotSerialized) // _DSM: Device-Specific Method { If (_OSI ("Darwin")) { If (!Arg2) { Return (Buffer (One) { 0x03 // . }) } Return (Package (0x02) { "class-code", Buffer (0x04) { 0xFF, 0x08, 0x01, 0x00 // .... } }) } } } """ if ssdt_name: self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content, compile=False), "Path": ssdt_name + ".aml" }) def enable_backlight_controls(self, platform, cpu_codename, integrated_gpu): if "Laptop" not in platform or not integrated_gpu: return uid_value = 19 if self.utils.contains_any(cpu_data.IntelCPUGenerations, cpu_codename, end=2): uid_value = 14 elif self.utils.contains_any(cpu_data.IntelCPUGenerations, cpu_codename, start=2, end=4): uid_value = 15 elif self.utils.contains_any(cpu_data.IntelCPUGenerations, cpu_codename, start=4, end=7): uid_value = 16 igpu = "" if uid_value == 14: # Try to gather our iGPU device paths = self.acpi.get_path_of_type(obj_type="Name", obj="_ADR", table=self.dsdt) for path in paths: adr = self.get_address_from_line(path[1], self.dsdt) if adr == 0x00020000: igpu = path[0][:-5] break if not igpu: # Try matching by name pci_roots = self.acpi.get_device_paths_with_hid("PNP0A08", self.dsdt) pci_roots += self.acpi.get_device_paths_with_hid("PNP0A03", self.dsdt) pci_roots += self.acpi.get_device_paths_with_hid("ACPI0016", self.dsdt) external = [] for line in self.dsdt.get("lines"): if not line.strip().startswith("External ("): continue # We don't need it try: path = line.split("(")[1].split(", ")[0] # Prepend the backslash and ensure trailing underscores are stripped. path = "\\"+".".join([x.rstrip("_").replace("\\","") for x in path.split(".")]) external.append(path) except: pass for root in pci_roots: for name in ("IGPU","_VID","VID0","VID1","GFX0","VGA","_VGA"): test_path = "{}.{}".format(root[0],name) device = self.acpi.get_device_paths(test_path, self.dsdt) if device: device = device[0][0] # Unpack to the path else: # Walk the external paths and see if it's declared elsewhere? # We're not patching anything directly - just getting a pathing # reference, so it's fine to not have the surrounding code. device = next((x for x in external if test_path == x),None) if not device: continue # Not found :( # Got a device - see if it has an _ADR, and skip if so - as it was wrong in the prior loop if self.acpi.get_path_of_type(obj_type="Name",obj=device+"._ADR", table=self.dsdt): continue # At this point - we got a hit igpu = device if "PNLF" in self.dsdt.get("table"): self.result.get("Patch").append({ "Comment": "PNLF to XNLF Rename", "Find": "504E4C46", "Replace": "584E4C46" }) nbcf = binascii.unhexlify("084E42434600") if nbcf in self.dsdt.get("raw"): self.result.get("Patch").append({ "Comment": "NBCF Zero to One for BrightnessKeys.kext", "Find": "084E42434600", "Replace": "084E42434601" }) comment = "Defines a PNLF device to enable backlight controls on laptops" ssdt_name = "SSDT-PNLF" ssdt_content = """ DefinitionBlock ("", "SSDT", 2, "ZPSS", "PNLF", 0x00000000) {""" if igpu: ssdt_content += """\n External ([[igpu_path]], DeviceObj)\n""" ssdt_content += """ Device (PNLF) { Name (_HID, EisaId ("APP0002")) // _HID: Hardware ID Name (_CID, "backlight") // _CID: Compatible ID Name (_UID, [[uid_value]]) // _UID: Unique ID Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0B) } Else { Return (Zero) } }""" if igpu: ssdt_content += """ Method (_INI, 0, Serialized) { If (LAnd (_OSI ("Darwin"), CondRefOf ([[igpu_path]]))) { OperationRegion ([[igpu_path]].RMP3, PCI_Config, Zero, 0x14) Field ([[igpu_path]].RMP3, AnyAcc, NoLock, Preserve) { Offset (0x02), GDID,16, Offset (0x10), BAR1,32, } // IGPU PWM backlight register descriptions: // LEV2 not currently used // LEVL level of backlight in Sandy/Ivy // P0BL counter, when zero is vertical blank // GRAN see description below in INI1 method // LEVW should be initialized to 0xC0000000 // LEVX PWMMax except FBTYPE_HSWPLUS combo of max/level (Sandy/Ivy stored in MSW) // LEVD level of backlight for Coffeelake // PCHL not currently used OperationRegion (RMB1, SystemMemory, BAR1 & ~0xF, 0xe1184) Field(RMB1, AnyAcc, Lock, Preserve) { Offset (0x48250), LEV2, 32, LEVL, 32, Offset (0x70040), P0BL, 32, Offset (0xc2000), GRAN, 32, Offset (0xc8250), LEVW, 32, LEVX, 32, LEVD, 32, Offset (0xe1180), PCHL, 32, } // Now fixup the backlight PWM depending on the framebuffer type // At this point: // Local4 is RMCF.BLKT value (unused here), if specified (default is 1) // Local0 is device-id for IGPU // Local2 is LMAX, if specified (Ones means based on device-id) // Local3 is framebuffer type // Adjustment required when using WhateverGreen.kext Local0 = GDID Local2 = Ones Local3 = 0 // check Sandy/Ivy // #define FBTYPE_SANDYIVY 1 If (LOr (LEqual (1, Local3), LNotEqual (Match (Package() { // Sandy HD3000 0x010b, 0x0102, 0x0106, 0x1106, 0x1601, 0x0116, 0x0126, 0x0112, 0x0122, // Ivy 0x0152, 0x0156, 0x0162, 0x0166, 0x016a, // Arrandale 0x0046, 0x0042, }, MEQ, Local0, MTR, 0, 0), Ones))) { if (LEqual (Local2, Ones)) { // #define SANDYIVY_PWMMAX 0x710 Store (0x710, Local2) } // change/scale only if different than current... Store (LEVX >> 16, Local1) If (LNot (Local1)) { Store (Local2, Local1) } If (LNotEqual (Local2, Local1)) { // set new backlight PWMMax but retain current backlight level by scaling Store ((LEVL * Local2) / Local1, Local0) Store (Local2 << 16, Local3) If (LGreater (Local2, Local1)) { // PWMMax is getting larger... store new PWMMax first Store (Local3, LEVX) Store (Local0, LEVL) } Else { // otherwise, store new brightness level, followed by new PWMMax Store (Local0, LEVL) Store (Local3, LEVX) } } } } }""" ssdt_content += """ } }""" # Perform the replacements ssdt_content = ssdt_content.replace("[[uid_value]]", str(uid_value)).replace("[[igpu_path]]",igpu) self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" }) def enable_cpu_power_management(self, cpu_codename): if self.utils.contains_any(cpu_data.IntelCPUGenerations, cpu_codename, end=2): return comment = "Sets plugin-type to 1 on the first Processor object to enable CPU power management" ssdt_name = "SSDT-PLUG" try: cpu_name = self.acpi.get_processor_paths()[0][0] except: cpu_name = None if cpu_name: ssdt_content = """ // Resource: https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/AcpiSamples/SSDT-PLUG.dsl DefinitionBlock ("", "SSDT", 2, "ZPSS", "CpuPlug", 0x00003000) { External ([[CPUName]], ProcessorObj) Scope ([[CPUName]]) { If (_OSI ("Darwin")) { Method (_DSM, 4, NotSerialized) // _DSM: Device-Specific Method { If (LNot (Arg2)) { Return (Buffer (One) { 0x03 }) } Return (Package (0x02) { "plugin-type", One }) } } } }""".replace("[[CPUName]]", cpu_name) else: comment = "Redefines modern CPU Devices as legacy Processor objects and sets plugin-type to 1 on the first to enable CPU power management" ssdt_name += "-ALT" procs = self.acpi.get_device_paths_with_hid("ACPI0007", self.dsdt) if not procs: return parent = procs[0][0].split(".")[0] proc_list = [] for proc in procs: uid = self.acpi.get_path_of_type(obj_type="Name", obj=proc[0]+"._UID", table=self.dsdt) if not uid: continue # Let's get the actual _UID value try: _uid = self.dsdt.get("lines")[uid[0][1]].split("_UID, ")[1].split(")")[0] proc_list.append((proc[0],_uid)) except: pass if not proc_list: return ssdt_content = """ // Resource: https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/AcpiSamples/Source/SSDT-PLUG-ALT.dsl DefinitionBlock ("", "SSDT", 2, "ZPSS", "CpuPlugA", 0x00003000) { External ([[parent]], DeviceObj) Scope ([[parent]]) {""".replace("[[parent]]",parent) # Walk the processor objects, and add them to the SSDT for i,proc_uid in enumerate(proc_list): proc,uid = proc_uid adr = hex(i)[2:].upper() name = "CP00"[:-len(adr)]+adr ssdt_content += """ Processor ([[name]], [[uid]], 0x00000510, 0x06) { // [[proc]] Name (_HID, "ACPI0007" /* Processor Device */) // _HID: Hardware ID Name (_UID, [[uid]]) Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } }""".replace("[[name]]",name).replace("[[uid]]",uid).replace("[[proc]]",proc) if i == 0: # Got the first, add plugin-type as well ssdt_content += """ Method (_DSM, 4, NotSerialized) { If (LNot (Arg2)) { Return (Buffer (One) { 0x03 }) } Return (Package (0x02) { "plugin-type", One }) }""" # Close up the SSDT ssdt_content += """ }""" ssdt_content += """ } }""" self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" }) def enable_gpio_device(self, platform, cpu_manufacturer, touchpad_communication): if "Laptop" not in platform or "Intel" not in cpu_manufacturer or not "I2C" in touchpad_communication: return try: gpio_device = self.acpi.get_device_paths("GPI0", self.dsdt)[0][0] or self.acpi.get_device_paths("GPIO", self.dsdt)[0][0] except: gpio_device = None if not gpio_device: return sta = self.get_sta_var(var=None, device=gpio_device, dev_hid=None, dev_name=gpio_device.split(".")[-1], table=self.dsdt) if sta.get("patches"): self.result["Patch"].extend(sta.get("patches", [])) comment = "Enable GPIO device for a I2C TouchPads to function properly" ssdt_name = "SSDT-GPI0" ssdt_content = """ DefinitionBlock ("", "SSDT", 2, "ZPSS", "GPI0", 0x00000000) { External ([[GPI0Path]], DeviceObj) External ([[GPI0Path]].XSTA, [[STAType]]) Scope ([[GPI0Path]]) { Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return ([[XSTA]]) } } } }""".replace("[[GPI0Path]]", gpio_device) \ .replace("[[STAType]]", sta.get("sta_type","MethodObj")) \ .replace("[[XSTA]]", "{}.XSTA{}".format(gpio_device," ()" if sta.get("sta_type","MethodObj") == "MethodObj" else "") if sta else "0x0F") self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" }) def enable_nvram_support(self, motherboard_chipset): if not self.utils.contains_any(chipset_data.IntelChipsets, motherboard_chipset, start=85, end=97) or not self.lpc_bus_device: return comment = "Add a PMCR device to enable NVRAM support for 300-series mainboards" ssdt_name = "SSDT-PMC" ssdt_content = """ // Resource: https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/AcpiSamples/Source/SSDT-PMC.dsl /* * Intel 300-series PMC support for macOS * * Starting from Z390 chipsets PMC (D31:F2) is only available through MMIO. * Since there is no standard device for PMC in ACPI, Apple introduced its * own naming "APP9876" to access this device from AppleIntelPCHPMC driver. * To avoid confusion we disable this device for all other operating systems, * as they normally use another non-standard device with "PNP0C02" HID and * "PCHRESV" UID. * * On certain implementations, including APTIO V, PMC initialisation is * required for NVRAM access. Otherwise it will freeze in SMM mode. * The reason for this is rather unclear. Note, that PMC and SPI are * located in separate memory regions and PCHRESV maps both, yet only * PMC region is used by AppleIntelPCHPMC: * 0xFE000000~0xFE00FFFF - PMC MBAR * 0xFE010000~0xFE010FFF - SPI BAR0 * 0xFE020000~0xFE035FFF - SerialIo BAR in ACPI mode * * PMC device has nothing to do to LPC bus, but is added to its scope for * faster initialisation. If we add it to PCI0, where it normally exists, * it will start in the end of PCI configuration, which is too late for * NVRAM support. */ DefinitionBlock ("", "SSDT", 2, "ACDT", "PMCR", 0x00001000) { External ([[LPCPath]], DeviceObj) Scope ([[LPCPath]]) { Device (PMCR) { Name (_HID, EisaId ("APP9876")) // _HID: Hardware ID Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0B) } Else { Return (Zero) } } Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { Memory32Fixed (ReadWrite, 0xFE000000, // Address Base 0x00010000, // Address Length ) }) } } }""".replace("[[LPCPath]]", self.lpc_bus_device) self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" }) def fake_embedded_controller(self, platform): comment = "Add a fake EC to ensure macOS compatibility" ssdt_name = "SSDT-EC" laptop = "Laptop" in platform def sta_needs_patching(sta): if not isinstance(sta,dict) or not sta.get("sta"): return False # Check if we have an IntObj or MethodObj # _STA, and scrape for values if possible. if sta.get("sta_type") == "IntObj": # We got an int - see if it's force-enabled try: sta_scope = self.dsdt.get("lines")[sta["sta"][0][1]] if not "Name (_STA, 0x0F)" in sta_scope: return True except Exception as e: print(e) return True elif sta.get("sta_type") == "MethodObj": # We got a method - if we have more than one # "Return (", or not a single "Return (0x0F)", # then we need to patch this out and replace try: sta_scope = "\n".join(self.acpi.get_scope(sta["sta"][0][1],strip_comments=True,table=self.dsdt)) if sta_scope.count("Return (") > 1 or not "Return (0x0F)" in sta_scope: # More than one return, or our return isn't force-enabled return True except Exception as e: return True # If we got here - it's not a recognized type, or # it was fullly qualified and doesn't need patching return False rename = False named_ec = False ec_to_patch = [] ec_to_enable = [] ec_sta = {} ec_enable_sta = {} patches = [] ec_located = False ec_list = self.acpi.get_device_paths_with_hid("PNP0C09",table=self.dsdt) if len(ec_list): self.lpc_bus_device = ".".join(ec_list[0][0].split(".")[:-1]) for x in ec_list: device = orig_device = x[0] #print(" --> {}".format(device)) if device.split(".")[-1] == "EC": named_ec = True if not laptop: # Only rename if we're trying to replace it #print(" ----> PNP0C09 (EC) called EC. Renaming") device = ".".join(device.split(".")[:-1]+["EC0"]) rename = True scope = "\n".join(self.acpi.get_scope(x[1],strip_comments=True,table=self.dsdt)) # We need to check for _HID, _CRS, and _GPE if all(y in scope for y in ["_HID","_CRS","_GPE"]): #print(" ----> Valid PNP0C09 (EC) Device") ec_located = True sta = self.get_sta_var( var=None, device=orig_device, dev_hid="PNP0C09", dev_name=orig_device.split(".")[-1], log_locate=False, table=self.dsdt ) if not laptop: ec_to_patch.append(device) # Only unconditionally override _STA methods # if not building for a laptop if sta.get("patches"): patches.extend(sta.get("patches",[])) ec_sta[device] = sta elif sta.get("patches"): if sta_needs_patching(sta): # Retain the info as we need to override it ec_to_enable.append(device) ec_enable_sta[device] = sta # Disable the patches by default and add to the list for patch in sta.get("patches",[]): patch["Enabled"] = False patch["Disabled"] = True patches.append(patch) if not ec_located: pass #print(" - No valid PNP0C09 (EC) devices found - only needs a Fake EC device") if laptop and named_ec and not patches: #print(" ----> Named EC device located - no fake needed.") #print("") #self.utils.request_input("Press [enter] to return to main menu...") return if not self.lpc_bus_device: #self.utils.request_input("Press [enter] to return to main menu...") return if rename == True: patches.insert(0,{ "Comment":"EC to EC0{}".format("" if not ec_sta else " - must come before any EC _STA to XSTA renames!"), "Find":"45435f5f", "Replace":"4543305f" }) comment += " - Needs EC to EC0 {}".format( "and EC _STA to XSTA renames" if ec_sta else "rename" ) elif ec_sta: comment += " - Needs EC _STA to XSTA renames" #oc = {"Comment":comment,"Enabled":True,"Path":"SSDT-EC.aml"} #self.make_plist(oc, "SSDT-EC.aml", patches, replace=True) #print("Creating SSDT-EC...") ssdt_content = """ DefinitionBlock ("", "SSDT", 2, "CORP ", "SsdtEC", 0x00001000) { External ([[LPCName]], DeviceObj) """.replace("[[LPCName]]",self.lpc_bus_device) for x in ec_to_patch: ssdt_content += " External ({}, DeviceObj)\n".format(x) if x in ec_sta: ssdt_content += " External ({}.XSTA, {})\n".format(x,ec_sta[x].get("sta_type","MethodObj")) # Walk the ECs to enable for x in ec_to_enable: ssdt_content += " External ({}, DeviceObj)\n".format(x) if x in ec_enable_sta: # Add the _STA and XSTA refs as the patch may not be enabled ssdt_content += " External ({0}._STA, {1})\n External ({0}.XSTA, {1})\n".format(x,ec_enable_sta[x].get("sta_type","MethodObj")) # Walk them again and add the _STAs for x in ec_to_patch: ssdt_content += """ Scope ([[ECName]]) { Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0) } Else { Return ([[XSTA]]) } } } """.replace("[[LPCName]]",self.lpc_bus_device).replace("[[ECName]]",x) \ .replace("[[XSTA]]","{}.XSTA{}".format(x," ()" if ec_sta[x].get("sta_type","MethodObj")=="MethodObj" else "") if x in ec_sta else "0x0F") # Walk them yet again - and force enable as needed for x in ec_to_enable: ssdt_content += """ If (LAnd (CondRefOf ([[ECName]].XSTA), LNot (CondRefOf ([[ECName]]._STA)))) { Scope ([[ECName]]) { Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return ([[XSTA]]) } } } } """.replace("[[LPCName]]", self.lpc_bus_device).replace("[[ECName]]",x) \ .replace("[[XSTA]]","{}.XSTA{}".format(x," ()" if ec_enable_sta[x].get("sta_type","MethodObj")=="MethodObj" else "") if x in ec_enable_sta else "Zero") # Create the faked EC if not laptop or not named_ec: ssdt_content += """ Scope ([[LPCName]]) { Device (EC) { Name (_HID, "ACID0001") // _HID: Hardware ID Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } } } }""".replace("[[LPCName]]", self.lpc_bus_device) # Close the SSDT scope ssdt_content += """ }""" if self.write_ssdt(ssdt_name, ssdt_content): self.result.get("Patch").extend(patches) self.result["Add"].append({ "Comment": comment, "Enabled": True, "Path": ssdt_name + ".aml" }) def fix_hp_005_post_error(self, motherboard_name): if "HP" not in motherboard_name: return if binascii.unhexlify("4701700070000108") in self.dsdt.get("raw"): self.result.get("Patch").append({ "Comment": "Fix HP Real-Time Clock Power Loss (005) Post Error", "Find": "4701700070000108", "Replace": "4701700070000102" }) def add_null_ethernet_device(self, network): for network_name, network_props in network.items(): if self.utils.contains_any(pci_data.NetworkIDs, network_props.get("Device ID"), start=108, end=219): return random_mac_address = self.smbios.generate_random_mac() mac_address_byte = ", ".join([f'0x{random_mac_address[i:i+2]}' for i in range(0, len(random_mac_address), 2)]) comment = "Creates a Null Ethernet to allow macOS system access to iServices" ssdt_name = "SSDT-RMNE" ssdt_content = """ // Resource: https://github.com/RehabMan/OS-X-Null-Ethernet/blob/master/SSDT-RMNE.dsl /* ssdt.dsl -- SSDT injector for NullEthernet * * Copyright (c) 2014 RehabMan * All rights reserved. * * 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 2 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. * */ // Use this SSDT as an alternative to patching your DSDT... DefinitionBlock("", "SSDT", 2, "ZPSS", "RMNE", 0x00001000) { Device (RMNE) { Name (_ADR, Zero) // The NullEthernet kext matches on this HID Name (_HID, "NULE0000") // This is the MAC address returned by the kext. Modify if necessary. Name (MAC, Buffer() { [[MACAddress]] }) Method (_DSM, 4, NotSerialized) { If (LEqual (Arg2, Zero)) { Return (Buffer() { 0x03 } ) } Return (Package() { "built-in", Buffer() { 0x00 }, "IOName", "ethernet", "name", Buffer() { "ethernet" }, "model", Buffer() { "RM-NullEthernet-1001" }, "device_type", Buffer() { "ethernet" }, }) } Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } } } }""".replace("[[MACAddress]]", mac_address_byte) self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" }) def is_intel_hedt_cpu(self, cpu_codename): return "-E" in cpu_codename and not self.utils.contains_any(cpu_data.IntelCPUGenerations, cpu_codename, end=4) is None or \ ("-X" in cpu_codename or "-W" in cpu_codename) and not self.utils.contains_any(cpu_data.IntelCPUGenerations, cpu_codename, start=4, end=6) is None def list_irqs(self): # Walks the DSDT keeping track of the current device and # saving the IRQNoFlags if found devices = {} current_device = None current_hid = None irq = False last_irq = False irq_index = 0 for index,line in enumerate(self.acpi.get_dsdt_or_only()["lines"]): if self.acpi.is_hex(line): # Skip all hex lines continue if irq: # Get the values num = line.split("{")[1].split("}")[0].replace(" ","") num = "#" if not len(num) else num if current_device in devices: if last_irq: # In a row devices[current_device]["irq"] += ":"+num else: # Skipped at least one line irq_index = self.acpi.find_next_hex(index)[1] devices[current_device]["irq"] += "-"+str(irq_index)+"|"+num else: irq_index = self.acpi.find_next_hex(index)[1] devices[current_device] = {"irq":str(irq_index)+"|"+num} irq = False last_irq = True elif "Device (" in line: # Check if we retain the _HID here if current_device and current_device in devices and current_hid: # Save it devices[current_device]["hid"] = current_hid last_irq = False current_hid = None try: current_device = line.split("(")[1].split(")")[0] except: current_device = None continue elif "_HID, " in line and current_device: try: current_hid = line.split('"')[1] except: pass elif "IRQNoFlags" in line and current_device: # Next line has our interrupts irq = True # Check if just a filler line elif len(line.replace("{","").replace("}","").replace("(","").replace(")","").replace(" ","").split("//")[0]): # Reset last IRQ as it's not in a row last_irq = False # Retain the final _HID if needed if current_device and current_device in devices and current_hid: devices[current_device]["hid"] = current_hid return devices def get_irq_choice(self, irqs): names_and_hids = [ "PIC", "IPIC", "TMR", "TIMR", "RTC", "RTC0", "RTC1", "PNPC0000", "PNP0100", "PNP0B00" ] defaults = [x for x in irqs if x.upper() in names_and_hids or irqs[x].get("hid","").upper() in names_and_hids] d = {} for x in defaults: d[x] = self.target_irqs return d def get_hex_from_irqs(self, irq, rem_irq = None): # We need to search for a few different types: # # 22 XX XX 22 XX XX 22 XX XX (multiples on different lines) # 22 XX XX (summed multiples in the same bracket - {0,8,11}) # 22 XX XX (single IRQNoFlags entry) # # Can end with 79 [00] (end of method), 86 09 (middle of method) or 47 01 (unknown) lines = [] remd = [] for a in irq.split("-"): index,i = a.split("|") # Get the index index = int(index) find = self.get_int_for_line(i) repl = [0]*len(find) # Now we need to verify if we're patching *all* IRQs, or just some specifics if rem_irq: repl = [x for x in find] matched = [] for x in rem_irq: # Get the int rem = self.convert_irq_to_int(x) repl1 = [y&(rem^0xFFFF) if y >= rem else y for y in repl] if repl1 != repl: # Changes were made remd.append(x) repl = [y for y in repl1] # Get the hex d = { "irq":i, "find": "".join(["22"+self.acpi.get_hex_from_int(x) for x in find]), "repl": "".join(["22"+self.acpi.get_hex_from_int(x) for x in repl]), "remd": remd, "index": index } d["changed"] = not (d["find"]==d["repl"]) lines.append(d) return lines def get_int_for_line(self, irq): irq_list = [] for i in irq.split(":"): irq_list.append(self.same_line_irq(i)) return irq_list def convert_irq_to_int(self, irq): b = "0"*(16-irq)+"1"+"0"*(irq) return int(b,2) def same_line_irq(self, irq): # We sum the IRQ values and return the int total = 0 for i in irq.split(","): if i == "#": continue # Null value try: i=int(i) except: continue # Not an int if i > 15 or i < 0: continue # Out of range total = total | self.convert_irq_to_int(i) return total def fix_irq_conflicts(self, platform, cpu_codename): if "Laptop" not in platform or self.utils.contains_any(cpu_data.IntelCPUGenerations, cpu_codename, end=4) is None: return hpets = self.acpi.get_device_paths_with_hid("PNP0103") hpet_fake = not hpets hpet_sta = False sta = None if hpets: name = hpets[0][0] sta = self.get_sta_var(var=None,dev_hid="PNP0103",dev_name="HPET",log_locate=False) if sta.get("patches"): hpet_sta = True self.result.get("Patch").extend(sta.get("patches",[])) hpet = self.acpi.get_method_paths(name+"._CRS") or self.acpi.get_name_paths(name+"._CRS") if not hpet: return crs_index = self.acpi.find_next_hex(hpet[0][1])[1] mem_base = mem_length = primed = None for line in self.acpi.get_scope(hpets[0][1],strip_comments=True): if "Memory32Fixed (" in line: primed = True continue if not primed: continue elif ")" in line: # Reached the end of the scope break # We're primed, and not at the end - let's try to get the base and length try: val = line.strip().split(",")[0].replace("Zero","0x0").replace("One","0x1") check = int(val,16) except: break # Set them in order if mem_base is None: mem_base = val else: mem_length = val break # Leave after we get both values # Check if we found the values got_mem = mem_base and mem_length if not got_mem: mem_base = "0xFED00000" mem_length = "0x00000400" crs = "5F435253" xcrs = "58435253" padl,padr = self.acpi.get_shortest_unique_pad(crs, crs_index) self.result.get("Patch").append({"Comment":"{} _CRS to XCRS Rename".format(name.split(".")[-1].lstrip("\\")),"Find":padl+crs+padr,"Replace":padl+xcrs+padr}) else: ec_list = self.acpi.get_device_paths_with_hid("PNP0C09") name = None if len(ec_list): name = ".".join(ec_list[0][0].split(".")[:-1]) if name == None: for x in ("LPCB", "LPC0", "LPC", "SBRG", "PX40"): try: name = self.acpi.get_device_paths(x)[0][0] break except: pass if not name: return devs = self.list_irqs() target_irqs = self.get_irq_choice(devs) if target_irqs is None: return # Bailed, going to the main menu # Let's apply patches as we go saved_dsdt = self.dsdt.get("raw") unique_patches = {} generic_patches = [] for dev in devs: if not dev in target_irqs: continue irq_patches = self.get_hex_from_irqs(devs[dev]["irq"],target_irqs[dev]) i = [x for x in irq_patches if x["changed"]] for a,t in enumerate(i): if not t["changed"]: # Nothing patched - skip continue # Try our endings here - 7900, 8609, and 4701 - also allow for up to 8 chars of pad (thanks MSI) matches = re.findall("("+t["find"]+"(.{0,8})(7900|4701|8609))",self.acpi.get_hex_starting_at(t["index"])[0]) if not len(matches): continue if len(matches) > 1: # Found too many matches! # Add them all as find/replace entries for x in matches: generic_patches.append({ "remd":",".join([str(y) for y in set(t["remd"])]), "orig":t["find"], "find":t["find"]+"".join(x[1:]), "repl":t["repl"]+"".join(x[1:]) }) continue ending = "".join(matches[0][1:]) padl,padr = self.acpi.get_shortest_unique_pad(t["find"]+ending, t["index"]) t_patch = padl+t["find"]+ending+padr r_patch = padl+t["repl"]+ending+padr if not dev in unique_patches: unique_patches[dev] = [] unique_patches[dev].append({ "dev":dev, "remd":",".join([str(y) for y in set(t["remd"])]), "orig":t["find"], "find":t_patch, "repl":r_patch }) # Walk the unique patches if any if len(unique_patches): for x in unique_patches: for i,p in enumerate(unique_patches[x]): patch_name = "{} IRQ {} Patch".format(x, p["remd"]) if len(unique_patches[x]) > 1: patch_name += " - {} of {}".format(i+1, len(unique_patches[x])) self.result.get("Patch").append({ "Comment": patch_name, "Find": p["find"], "Replace": p["repl"] }) # Walk the generic patches if any if len(generic_patches): generic_set = [] # Make sure we don't repeat find values for x in generic_patches: if x in generic_set: continue generic_set.append(x) for i,x in enumerate(generic_set): patch_name = "Generic IRQ Patch {} of {} - {} - {}".format(i+1,len(generic_set),x["remd"],x["orig"]) self.result.get("Patch").append({ "Comment": patch_name, "Find": x["find"], "Replace": x["repl"], "Enabled": False }) # Restore the original DSDT in memory self.dsdt["raw"] = saved_dsdt comment = "HPET Device Fake" if hpet_fake else "{} _CRS (Needs _CRS to XCRS Rename)".format(name.split(".")[-1].lstrip("\\")) ssdt_name = "SSDT-HPET" if hpet_fake: ssdt_content = """// Fake HPET device // DefinitionBlock ("", "SSDT", 2, "CORP", "HPET", 0x00000000) { External ([[name]], DeviceObj) Scope ([[name]]) { Device (HPET) { Name (_HID, EisaId ("PNP0103") /* HPET System Timer */) // _HID: Hardware ID Name (_CID, EisaId ("PNP0C01") /* System Board */) // _CID: Compatible ID Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } } Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { IRQNoFlags () {0,8,11} Memory32Fixed (ReadWrite, 0xFED00000, // Address Base 0x00000400, // Address Length ) }) } } }""".replace("[[name]]",name) else: ssdt_content = """// // Supplementary HPET _CRS from Goldfish64 // Requires the HPET's _CRS to XCRS rename // DefinitionBlock ("", "SSDT", 2, "CORP", "HPET", 0x00000000) { External ([[name]], DeviceObj) External ([[name]].XCRS, [[type]]) Scope ([[name]]) { Name (BUFX, ResourceTemplate () { IRQNoFlags () {0,8,11} Memory32Fixed (ReadWrite, // [[mem]] [[mem_base]], // Address Base [[mem_length]], // Address Length ) }) Method (_CRS, 0, Serialized) // _CRS: Current Resource Settings { // Return our buffer if booting macOS or the XCRS method // no longer exists for some reason If (LOr (_OSI ("Darwin"), LNot(CondRefOf ([[name]].XCRS)))) { Return (BUFX) } // Not macOS and XCRS exists - return its result Return ([[name]].XCRS[[method]]) }""" \ .replace("[[name]]",name) \ .replace("[[type]]","MethodObj" if hpet[0][-1] == "Method" else "BuffObj") \ .replace("[[mem]]","Base/Length pulled from DSDT" if got_mem else "Default Base/Length - verify with your DSDT!") \ .replace("[[mem_base]]",mem_base) \ .replace("[[mem_length]]",mem_length) \ .replace("[[method]]"," ()" if hpet[0][-1]=="Method" else "") if hpet_sta: # Inject our external reference to the renamed XSTA method ssdt_parts = [] external = False for line in ssdt_content.split("\n"): if "External (" in line: external = True elif external: ssdt_parts.append(" External ({}.XSTA, {})".format(name,sta["sta_type"])) external = False ssdt_parts.append(line) ssdt_content = "\n".join(ssdt_parts) # Add our method ssdt_content += """ Method (_STA, 0, NotSerialized) // _STA: Status { // Return 0x0F if booting macOS or the XSTA method // no longer exists for some reason If (LOr (_OSI ("Darwin"), LNot (CondRefOf ([[name]].XSTA)))) { Return (0x0F) } // Not macOS and XSTA exists - return its result Return ([[name]].XSTA[[called]]) }""".replace("[[name]]",name).replace("[[called]]"," ()" if sta["sta_type"]=="MethodObj" else "") ssdt_content += """ } }""" self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" }) def fix_system_clock_awac(self, motherboard_chipset): if not self.utils.contains_any(chipset_data.IntelChipsets, motherboard_chipset, start=85): return rtc_range_needed = False rtc_crs_type = None crs_lines = [] lpc_name = None awac_dict = self.get_sta_var(var="STAS",dev_hid="ACPI000E",dev_name="AWAC") rtc_dict = self.get_sta_var(var="STAS",dev_hid="PNP0B00",dev_name="RTC") # At this point - we should have any info about our AWAC and RTC devices # we need. Let's see if we need an RTC fake - then build the SSDT. if not rtc_dict.get("valid"): #print(" - Fake needed!") lpc_name = self.get_lpc_name() if lpc_name is None: self.utils.request_input("Press [enter] to return to main menu...") return else: # Let's check if our RTC device has a _CRS variable - and if so, let's look for any skipped ranges #print(" --> Checking for _CRS...") rtc_crs = self.acpi.get_method_paths(rtc_dict["device"][0]+"._CRS") or self.acpi.get_name_paths(rtc_dict["device"][0]+"._CRS") if rtc_crs: #print(" ----> {}".format(rtc_crs[0][0])) rtc_crs_type = "MethodObj" if rtc_crs[0][-1] == "Method" else "BuffObj" # Only check for the range if it's a buffobj if rtc_crs_type.lower() == "buffobj": last_adr = last_len = last_ind = None crs_scope = self.acpi.get_scope(rtc_crs[0][1]) # Let's try and clean up the scope - it's often a jumbled mess pad_len = len(crs_scope[0])-len(crs_scope[0].lstrip()) pad = crs_scope[0][:pad_len] fixed_scope = [] for line in crs_scope: if line.startswith(pad): # Got a full line - strip the pad, and save it fixed_scope.append(line[pad_len:]) else: # Likely a part of the prior line fixed_scope[-1] = fixed_scope[-1]+line for i,line in enumerate(fixed_scope): if "Name (_CRS, " in line: # Rename _CRS to BUFX for later - and strip any comments to avoid confusion line = line.replace("Name (_CRS, ","Name (BUFX, ").split(" //")[0] if "IO (Decode16," in line: # We have our start - get the the next line, and 4th line try: curr_adr = int(fixed_scope[i+1].strip().split(",")[0],16) curr_len = int(fixed_scope[i+4].strip().split(",")[0],16) curr_ind = i+4 # Save the value we may pad except: # Bad values? Bail... #print(" ----> Failed to gather values - could not verify RTC range.") rtc_range_needed = False break if last_adr is not None: # Compare our range values adjust = curr_adr - (last_adr + last_len) if adjust: # We need to increment the previous length by our adjust value rtc_range_needed = True #print(" ----> Adjusting IO range {} length to {}".format(self.hexy(last_adr,pad_to=4),self.hexy(last_len+adjust,pad_to=2))) try: hex_find,hex_repl = self.hexy(last_len,pad_to=2),self.hexy(last_len+adjust,pad_to=2) crs_lines[last_ind] = crs_lines[last_ind].replace(hex_find,hex_repl) except: #print(" ----> Failed to adjust values - could not verify RTC range.") rtc_range_needed = False break # Save our last values last_adr,last_len,last_ind = curr_adr,curr_len,curr_ind crs_lines.append(line) if rtc_range_needed: # We need to generate a rename for _CRS -> XCRS # print(" --> Generating _CRS to XCRS rename...") crs_index = self.acpi.find_next_hex(rtc_crs[0][1])[1] #print(" ----> Found at index {}".format(crs_index)) crs_hex = "5F435253" # _CRS xcrs_hex = "58435253" # XCRS padl,padr = self.acpi.get_shortest_unique_pad(crs_hex, crs_index) patches = rtc_dict.get("patches",[]) patches.append({"Comment":"{} _CRS to XCRS Rename".format(rtc_dict["dev_name"]),"Find":padl+crs_hex+padr,"Replace":padl+xcrs_hex+padr}) rtc_dict["patches"] = patches rtc_dict["crs"] = True else: pass #print(" ----> Not found") # Let's see if we even need an SSDT # Not required if AWAC is not present; RTC is present, doesn't have an STAS var, and doesn't have an _STA method, and no range fixes are needed if not awac_dict.get("valid") and rtc_dict.get("valid") and not rtc_dict.get("has_var") and not rtc_dict.get("sta") and not rtc_range_needed: #print("") #print("Valid PNP0B00 (RTC) device located and qualified, and no ACPI000E (AWAC) devices found.") #print("No patching or SSDT needed.") #print("") #self.utils.request_input("Press [enter] to return to main menu...") return comment = "Incompatible AWAC Fix" if awac_dict.get("valid") else "RTC Fake" if not rtc_dict.get("valid") else "RTC Range Fix" if rtc_range_needed else "RTC Enable Fix" suffix = [] for x in (awac_dict,rtc_dict): if not x.get("valid"): continue val = "" if x.get("sta") and not x.get("has_var"): val = "{} _STA to XSTA".format(x["dev_name"]) if x.get("crs"): val += "{} _CRS to XCRS".format(" and " if val else x["dev_name"]) if val: suffix.append(val) if suffix: comment += " - Requires {} Rename".format(", ".join(suffix)) # At this point - we need to do the following: # 1. Change STAS if needed # 2. Setup _STA with _OSI and call XSTA if needed # 3. Fake RTC if needed ssdt_name = "SSDT-RTCAWAC" ssdt_content = """// // Original sources from Acidanthera: // - https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/AcpiSamples/SSDT-AWAC.dsl // - https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/AcpiSamples/SSDT-RTC0.dsl // // DefinitionBlock ("", "SSDT", 2, "ZPSS", "RTCAWAC", 0x00000000) { """ if any(x.get("has_var") for x in (awac_dict,rtc_dict)): ssdt_content += """ External (STAS, IntObj) Scope (\\) { Method (_INI, 0, NotSerialized) // _INI: Initialize { If (_OSI ("Darwin")) { Store (One, STAS) } } } """ for x in (awac_dict,rtc_dict): if not x.get("valid") or x.get("has_var") or not x.get("device"): continue # Device was found, and it doesn't have the STAS var - check if we # have an _STA (which would be renamed) macos,original = ("Zero","0x0F") if x.get("dev_hid") == "ACPI000E" else ("0x0F","Zero") if x.get("sta"): ssdt_content += """ External ([[DevPath]], DeviceObj) External ([[DevPath]].XSTA, [[sta_type]]) Scope ([[DevPath]]) { Name (ZSTA, [[Original]]) Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return ([[macOS]]) } // Default to [[Original]] - but return the result of the renamed XSTA if possible If (CondRefOf ([[DevPath]].XSTA)) { Store ([[DevPath]].XSTA[[called]], ZSTA) } Return (ZSTA) } } """.replace("[[DevPath]]",x["device"][0]).replace("[[Original]]",original).replace("[[macOS]]",macos).replace("[[sta_type]]",x["sta_type"]).replace("[[called]]"," ()" if x["sta_type"]=="MethodObj" else "") elif x.get("dev_hid") == "ACPI000E": # AWAC device with no STAS, and no _STA - let's just add one ssdt_content += """ External ([[DevPath]], DeviceObj) Scope ([[DevPath]]) { Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (Zero) } Else { Return (0x0F) } } } """.replace("[[DevPath]]",x["device"][0]) # Check if we need to setup an RTC range correction if rtc_range_needed and rtc_crs_type.lower() == "buffobj" and crs_lines and rtc_dict.get("valid"): ssdt_content += """ External ([[DevPath]], DeviceObj) External ([[DevPath]].XCRS, [[type]]) Scope ([[DevPath]]) { // Adjusted and renamed _CRS buffer ripped from DSDT with corrected range [[NewCRS]] // End of adjusted _CRS and renamed buffer // Create a new _CRS method that returns the result of the renamed XCRS Method (_CRS, 0, Serialized) // _CRS: Current Resource Settings { If (LOr (_OSI ("Darwin"), LNot (CondRefOf ([[DevPath]].XCRS)))) { // Return our buffer if booting macOS or the XCRS method // no longer exists for some reason Return (BUFX) } // Not macOS and XCRS exists - return its result Return ([[DevPath]].XCRS[[method]]) } } """.replace("[[DevPath]]",rtc_dict["device"][0]) \ .replace("[[type]]",rtc_crs_type) \ .replace("[[method]]"," ()" if rtc_crs_type == "Method" else "") \ .replace("[[NewCRS]]","\n".join([(" "*8)+x for x in crs_lines])) # Check if we do not have an RTC device at all if not rtc_dict.get("valid") and lpc_name: ssdt_content += """ External ([[LPCName]], DeviceObj) // (from opcode) Scope ([[LPCName]]) { Device (RTC0) { Name (_HID, EisaId ("PNP0B00")) // _HID: Hardware ID Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { IO (Decode16, 0x0070, // Range Minimum 0x0070, // Range Maximum 0x01, // Alignment 0x08, // Length ) IRQNoFlags () {8} }) Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (0) } } } } """.replace("[[LPCName]]",lpc_name) ssdt_content += "}" if self.write_ssdt(ssdt_name, ssdt_content): self.result.get("Patch").extend(rtc_dict["patches"]) self.result["Add"].append({ "Comment": comment, "Enabled": True, "Path": ssdt_name + ".aml" }) def fix_system_clock_hedt(self, motherboard_chipset, macos_version): if macos_version < (20, 0, 0) or not self.utils.contains_any(["X99", "X299"], motherboard_chipset): return if not self.lpc_bus_device: return awac_device = self.acpi.get_device_paths_with_hid("ACPI000E") rtc_device_name = self.acpi.get_device_paths_with_hid("PNP0B00")[0][0].split(".")[-1] comment = "Creates a new RTC device to resolve PCI Configuration issues in macOS Big Sur 11+" ssdt_name = "SSDT-RTC0-RANGE" ssdt_content = """ // Resource: https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/AcpiSamples/Source/SSDT-RTC0-RANGE.dsl /* * On certain motherboards(mainly Asus X299 boards), not all ports are * mapped in the RTC device. For the majority of the time, users will not notice * this issue though in extreme circumstances macOS may halt in early booting. * Most prominently seen around the PCI Configuration stage with macOS 11 Big Sur. * * To resolve this, we'll want to create a new RTC device(PNP0B00) with the correct * range. * * Note that due to AWAC systems having an _STA method already defined, attempting * to set another _STA method in your RTC device will conflict. To resolve this, * SSDT-AWAC should be removed and instead opt for this SSDT instead. */ DefinitionBlock ("", "SSDT", 2, "ZPSS", "RtcRange", 0x00000000) { External ([[LPCPath]], DeviceObj)""" if not awac_device: ssdt_content += """ External ([[LPCPath]].[[RTCName]], DeviceObj)""" ssdt_content += """ Scope ([[LPCPath]]) {""" if not awac_device: ssdt_content += """ Scope ([[RTCName]]) { Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (Zero) } Else { Return (0x0F) } } }""".replace("[[RTCName]]", rtc_device_name) ssdt_content += """ Device (RTC0) { /* * Change the below _CSR range to match your hardware. * * For this example, we'll use the Asus Strix X299-E Gaming's ACPI, and show how to correct it. * Within the original RTC device, we see that sections 0x70 through 0x77 are mapped: * * Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings * { * IO (Decode16, * 0x0070, // Range Minimum 1 * 0x0070, // Range Maximum 1 * 0x01, // Alignment 1 * 0x02, // Length 1 * ) * IO (Decode16, * 0x0074, // Range Minimum 2 * 0x0074, // Range Maximum 2 * 0x01, // Alignment 2 * 0x04, // Length 2 * ) * IRQNoFlags () * {8} * }) * * Though Asus seems to have forgotten to map sections 0x72 and 0x73 in the first bank, so * we'll want to expand the range to include them under Length 1. * Note that not all boards will be the same, verify with your ACPI tables for both the range and * missing regions. */ Name (_HID, EisaId ("PNP0B00")) // _HID: Hardware ID Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { IO (Decode16, 0x0070, // Range Minimum 1 0x0070, // Range Maximum 1 0x01, // Alignment 1 0x04, // Length 1 (Expanded to include 0x72 and 0x73) ) IO (Decode16, 0x0074, // Range Minimum 2 0x0074, // Range Maximum 2 0x01, // Alignment 2 0x04, // Length 2 ) IRQNoFlags () {8} }) Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } } } } }""" ssdt_content = ssdt_content.replace("[[LPCPath]]", self.lpc_bus_device).replace("[[RTCName]]", rtc_device_name) self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" }) def instant_wake_fix(self): comment = "Fix sleep state values in _PRW methods to prevent immediate wake in macOS" ssdt_name = "SSDT-PRW" uswe_object = "9355535745" wole_object = "93574F4C45" gprw_method = "4750525702" uprw_method = "5550525702" xprw_method = "5850525702" if binascii.unhexlify(gprw_method) in self.dsdt.get("raw"): self.result.get("Patch").append({ "Comment": "GPRW to XPRW Rename", "Find": gprw_method, "Replace": xprw_method }) if binascii.unhexlify(uprw_method) in self.dsdt.get("raw"): self.result.get("Patch").append({ "Comment": "UPRW to XPRW Rename", "Find": uprw_method, "Replace": xprw_method }) if not binascii.unhexlify(uswe_object) in self.dsdt.get("raw"): uswe_object = None if not binascii.unhexlify(wole_object) in self.dsdt.get("raw"): wole_object = None ssdt_content = """ // Resource: https://github.com/5T33Z0/OC-Little-Translated/blob/main/04_Fixing_Sleep_and_Wake_Issues/060D_Instant_Wake_Fix/README.md DefinitionBlock ("", "SSDT", 2, "ZPSS", "_PRW", 0x00000000) {""" if binascii.unhexlify(gprw_method[2:]) in self.dsdt.get("raw"): ssdt_content += """\n External(XPRW, MethodObj)""" if uswe_object: ssdt_content += "\n External (USWE, FieldUnitObj)" if wole_object: ssdt_content += "\n External (WOLE, FieldUnitObj)" if uswe_object or wole_object: ssdt_content += """\n Scope (\\) { If (_OSI ("Darwin")) {""" if uswe_object: ssdt_content += "\n USWE = Zero" if wole_object: ssdt_content += "\n WOLE = Zero" ssdt_content += """ } }""" if binascii.unhexlify(gprw_method) in self.dsdt.get("raw"): ssdt_content += """ Method (GPRW, 2, NotSerialized) { If (_OSI ("Darwin")) { If ((0x6D == Arg0)) { Return (Package () { 0x6D, Zero }) } If ((0x0D == Arg0)) { Return (Package () { 0x0D, Zero }) } } Return (XPRW (Arg0, Arg1)) }""" if binascii.unhexlify(uprw_method) in self.dsdt.get("raw"): ssdt_content += """ Method (UPRW, 2, NotSerialized) { If (_OSI ("Darwin")) { If ((0x6D == Arg0)) { Return (Package () { 0x6D, Zero }) } If ((0x0D == Arg0)) { Return (Package () { 0x0D, Zero }) } } Return (XPRW (Arg0, Arg1)) }""" ssdt_content += "\n}" self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" }) def fix_uncore_bridge(self, motherboard_chipset, macos_version): if macos_version < (20, 0, 0) or not self.utils.contains_any(["X79", "C602", "Patsburg", "C612", "X99", "Wellsburg"], motherboard_chipset): return unc0_device = self.acpi.get_device_paths("UNC0") if not unc0_device: return comment = "Disables unused uncore bridges to prevent kenel panic in macOS Big Sur 11+" ssdt_name = "SSDT-UNC" ssdt_content = """ // Resource: https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/AcpiSamples/Source/SSDT-UNC.dsl /* * Discovered on X99-series. * These platforms have uncore PCI bridges for 4 CPU sockets * present in ACPI despite having none physically. * * Under normal conditions these are disabled depending on * CPU presence in the socket via Processor Bit Mask (PRBM), * but on X99 this code is unused or broken as such bridges * simply do not exist. We fix that by writing 0 to PRBM. * * Doing so is important as starting with macOS 11 IOPCIFamily * will crash as soon as it sees non-existent PCI bridges. */ DefinitionBlock ("", "SSDT", 2, "ZPSS", "UNC", 0x00000000) { External (_SB.UNC0, DeviceObj) External (PRBM, IntObj) Scope (_SB.UNC0) { Method (_INI, 0, NotSerialized) { // In most cases this patch does benefit all operating systems, // yet on select pre-Windows 10 it may cause issues. // Remove If (_OSI ("Darwin")) in case you have none. If (_OSI ("Darwin")) { PRBM = 0 } } } }""" self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" }) def operating_system_patch(self, platform): if not "Laptop" in platform: return comment = "Spoofs the operating system to Windows, enabling devices locked behind non-Windows systems on macOS" ssdt_name = "SSDT-XOSI" ssdt_content = """ // Resource: https://github.com/dortania/Getting-Started-With-ACPI/blob/master/extra-files/decompiled/SSDT-XOSI.dsl DefinitionBlock ("", "SSDT", 2, "ZPSS", "XOSI", 0x00001000) { Method (XOSI, 1, NotSerialized) { // Based off of: // https://docs.microsoft.com/en-us/windows-hardware/drivers/acpi/winacpi-osi#_osi-strings-for-windows-operating-systems // Add OSes from the below list as needed, most only check up to Windows 2015 // but check what your DSDT looks for Store (Package () { [[OSIStrings]] }, Local0) If (_OSI ("Darwin")) { Return (LNotEqual (Match (Local0, MEQ, Arg0, MTR, Zero, Zero), Ones)) } Else { Return (_OSI (Arg0)) } } }""".replace("[[OSIStrings]]", "\n,".join([" \"{}\"".format(osi_string) for target_os, osi_string in self.osi_strings.items() if osi_string in self.dsdt.get("table")])) osid = self.acpi.get_method_paths("OSID") if osid: self.result.get("Patch").append({ "Comment": "OSID to XSID rename - must come before _OSI to XOSI rename!", "Find": "4F534944", "Replace": "58534944" }) osif = self.acpi.get_method_paths("OSIF") if osif: self.result.get("Patch").append({ "Comment": "OSIF to XSIF rename - must come before _OSI to XOSI rename!", "Find": "4F534946", "Replace": "58534946" }) self.result.get("Patch").append({ "Comment": "_OSI to XOSI rename - requires SSDT-XOSI.aml", "Find": "5F4F5349", "Replace": "584F5349" }) self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" }) def surface_laptop_special_patch(self, motherboard_name, platform): if "Surface".upper() not in motherboard_name or "Laptop" not in platform: return comment = "Special Patch for all Surface Pro / Book / Laptop hardwares" ssdt_name = "SSDT-SURFACE" ssdt_content = """ DefinitionBlock ("", "SSDT", 2, "ZPSS", "Surface", 0x00001000) { External (_SB_.PCI0, DeviceObj) External (GPRW, MethodObj) // 2 Arguments If (_OSI ("Darwin")) { Scope (_SB) { Device (ALS0) { Name (_HID, "ACPI0008" /* Ambient Light Sensor Device */) // _HID: Hardware ID Name (_CID, "smc-als") // _CID: Compatible ID Name (_ALI, 0x012C) // _ALI: Ambient Light Illuminance Name (_ALR, Package (0x05) // _ALR: Ambient Light Response { Package (0x02) { 0x46, Zero }, Package (0x02) { 0x49, 0x0A }, Package (0x02) { 0x55, 0x50 }, Package (0x02) { 0x64, 0x012C }, Package (0x02) { 0x96, 0x03E8 } }) Method (XALI, 1, Serialized) { _ALI = Arg0 } } Device (ADP0) { Name (_HID, "ACPI0003" /* Power Source Device */) // _HID: Hardware ID Name (SPSR, Zero) Method (_PRW, 0, NotSerialized) // _PRW: Power Resources for Wake { Return (GPRW (0x6D, 0x04)) } Method (_STA, 0, NotSerialized) // _STA: Status { Return (0x0F) } Method (XPSR, 1, Serialized) { If ((Arg0 == Zero)) { SPSR = Zero } ElseIf ((Arg0 == One)) { SPSR = One } Notify (ADP0, 0x80) // Status Change } Method (_PSR, 0, Serialized) // _PSR: Power Source { Return (SPSR) /* \\_SB_.ADP0.SPSR */ } Method (_PCL, 0, NotSerialized) // _PCL: Power Consumer List { Return (\\_SB) } } Device (BAT0) { Name (_HID, EisaId ("PNP0C0A") /* Control Method Battery */) // _HID: Hardware ID Name (_UID, Zero) // _UID: Unique ID Name (_PCL, Package (0x01) // _PCL: Power Consumer List { _SB }) Method (_STA, 0, NotSerialized) // _STA: Status { Return (0x1F) } } } Scope (_SB.PCI0) { Device (IPTS) { Name (_ADR, 0x00160004) // _ADR: Address } } } }""" self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" }) def battery_status_check(self, platform): if "Laptop" not in platform: return if not self.dsdt: self.result["Battery Status Patch Needed"] = True return search_start_idx = 0 while "EmbeddedControl" in self.dsdt.get("table")[search_start_idx:]: emb_ctrl_start_idx = self.dsdt.get("table").index("EmbeddedControl", search_start_idx) emb_ctrl_end_idx = emb_ctrl_start_idx + self.dsdt.get("table")[emb_ctrl_start_idx:].index("}") emb_ctrl_block = self.dsdt.get("table")[emb_ctrl_start_idx:emb_ctrl_end_idx] for line in emb_ctrl_block.splitlines(): if ", " in line and ", 8" not in line: self.result["Battery Status Patch Needed"] = True return search_start_idx = emb_ctrl_end_idx return def select_dsdt(self): results_path = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "Results") apcidump_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "acpidump.exe") while True: self.utils.head("Select ACPI Tables") print("") if sys.platform == "win32": print("To manually dump ACPI tables, open Command Prompt and enter the following commands in sequence:") print("") print(" > cd \"{}\"".format(results_path.replace("/", "\\"))) print(" > mkdir ACPITables") print(" > cd ACPITables") print(" > \"{}\" -b".format(apcidump_path.replace("/", "\\"))) print(" > rename *.dat *.aml") print("") print("The ACPI tables will now be available in\n \"{}\\ACPITables\"".format(results_path.replace("/", "\\"))) print("") if sys.platform.startswith("linux") or sys.platform == "win32": print("P. Dump ACPI Tables") print("Q. Quit") print(" ") menu = self.utils.request_input("Please drag and drop ACPI Tables folder here: ") if menu.lower() == "p" and (sys.platform.startswith("linux") or sys.platform == "win32"): return self.read_dsdt( self.acpi.dump_tables(os.path.join(results_path, "ACPITables")) ) elif menu.lower() == "q": self.utils.exit_program() path = self.utils.normalize_path(menu) if not path: continue return self.read_dsdt(path) def add_dtgp_method(self): comment = "Enables macOS to read and merge device properties from ACPI and determine if a specific device should temporarily receive power" ssdt_name = "SSDT-DTGP" ssdt_content = """ // Resource: https://github.com/5T33Z0/OC-Little-Translated/blob/main/01_Adding_missing_Devices_and_enabling_Features/Method_DTGP/SSDT-DTGP.dsl DefinitionBlock ("", "SSDT", 2, "ZPSS", "DTGP", 0x00001000) { Method (DTGP, 5, NotSerialized) { If ((Arg0 == ToUUID ("a0b5b7c6-1318-441c-b0c9-fe695eaf949b") /* Unknown UUID */)) { If ((Arg1 == One)) { If ((Arg2 == Zero)) { Arg4 = Buffer (One) { 0x03 // . } Return (One) } If ((Arg2 == One)) { Return (One) } } } Arg4 = Buffer (One) { 0x00 // . } Return (Zero) } } """ self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" }) def spoof_discrete_gpu(self, discrete_gpu): device_id = discrete_gpu.get("Device ID") if not device_id in pci_data.SpoofGPUIDs: return self.add_dtgp_method() new_device_id_le = self.utils.to_little_endian_hex(pci_data.SpoofGPUIDs.get(device_id).split("-")[-1]) device_id_byte = ", ".join([f'0x{new_device_id_le[i:i+2]}' for i in range(0, len(new_device_id_le), 2)]) comment = "Spoof GPU ID to enable graphics acceleration in macOS" ssdt_name = "SSDT-GPU-SPOOF" ssdt_content = """ // Resource: https://github.com/dortania/Getting-Started-With-ACPI/blob/master/extra-files/decompiled/SSDT-GPU-SPOOF.dsl // Replace all "_SB.PCI0.PEG0.PEGP" with the ACPI path of your discrete GPU DefinitionBlock ("", "SSDT", 2, "ZPSS", "GPUSPOOF", 0x00001000) { External (_SB.PCI0.PEG0.PEGP, DeviceObj) External (DTGP, MethodObj) Scope (_SB.PCI0.PEG0.PEGP) { if (_OSI ("Darwin")) { Method (_DSM, 4, NotSerialized) // _DSM: Device-Specific Method { Local0 = Package (0x04) { "device-id", Buffer (0x04) { [[DeviceID]] }, "model", Buffer () { "[[model]]" } } DTGP (Arg0, Arg1, Arg2, Arg3, RefOf (Local0)) Return (Local0) } } } }""".replace("[[DeviceID]]", device_id_byte).replace("[[model]]", discrete_gpu.get("GPU Name")) self.result["Add"].append({ "Comment": comment, "Enabled": self.write_ssdt(ssdt_name, ssdt_content, compile=False), "Path": ssdt_name + ".aml" }) def dropping_the_table(self, signature=None, oemtableid=None): table_data = self.acpi.get_table_with_signature(signature) or self.acpi.get_table_with_id(oemtableid) if not table_data: return self.result["Delete"].append({ "All": True, "Comment": "Delete {}".format(signature or oemtableid), "Enabled": True, "OemTableId": self.utils.hex_to_bytes(self.utils.string_to_hex(table_data.get("id"))), "TableLength": 0, "TableSignature": self.utils.hex_to_bytes(self.utils.string_to_hex(table_data.get("signature"))) }) def fix_apic_processor_id(self, cpu_codename): self.apic = self.acpi.get_table_with_signature("APIC") processors = [line for line in self.dsdt.get("lines") if line.strip().startswith("Processor")] if not self.is_intel_hedt_cpu(cpu_codename) or not self.apic or not processors: return processor_index = -1 for index, line in enumerate(self.apic.get("lines")): if "Subtable Type" in line and "[Processor Local APIC]" in line: processor_index += 1 apic_processor_id = self.apic["lines"][index + 2][-2:] try: dsdt_processor_id = processors[processor_index].split("0x")[1].split(",")[0] except: return if processor_index == 0 and apic_processor_id == dsdt_processor_id: break self.apic["lines"][index + 2] = self.apic["lines"][index + 2][:-2] + dsdt_processor_id if processor_index != -1: if self.write_ssdt("APIC", "\n".join(self.apic.get("lines"))): self.result["Add"].append({ "Comment": "Avoid kernel panic by pointing the first CPU entry to an active CPU on HEDT systems", "Enabled": True, "Path": "APIC.aml" }) self.dropping_the_table("APIC") def initialize_patches(self, motherboard_name, motherboard_chipset, platform, cpu_manufacturer, cpu_codename, integrated_gpu, discrete_gpu, network, touchpad_communication, smbios, intel_mei, unsupported_devices, macos_version, acpi_directory): self.acpi_directory = self.check_acpi_directory(acpi_directory) if self.select_dsdt(): self.dsdt = self.acpi.get_dsdt_or_only() self.get_low_pin_count_bus_device() self.add_intel_management_engine(cpu_codename, intel_mei) self.add_memory_controller_device(cpu_manufacturer, smbios) self.add_null_ethernet_device(network) self.add_system_management_bus_device(cpu_manufacturer, cpu_codename) self.add_usb_power_properties(smbios) self.ambient_light_sensor(motherboard_name, platform, integrated_gpu) self.battery_status_check(platform) self.disable_rhub_devices() self.disable_unsupported_device(unsupported_devices) if not self.is_intel_hedt_cpu(cpu_codename) and self.utils.contains_any(cpu_data.IntelCPUGenerations, cpu_codename, end=2): self.dropping_the_table(oemtableid="CpuPm") self.dropping_the_table(oemtableid="Cpu0Ist") self.enable_backlight_controls(platform, cpu_codename, integrated_gpu) self.enable_cpu_power_management(cpu_codename) self.enable_gpio_device(platform, cpu_manufacturer, touchpad_communication) self.enable_nvram_support(motherboard_chipset) self.fake_embedded_controller(platform) self.fix_apic_processor_id(cpu_codename) self.fix_hp_005_post_error(motherboard_name) self.fix_irq_conflicts(platform, cpu_codename) self.fix_system_clock_awac(motherboard_chipset) self.fix_system_clock_hedt(motherboard_chipset, macos_version) self.fix_uncore_bridge(motherboard_chipset, macos_version) self.instant_wake_fix() self.operating_system_patch(platform) self.spoof_discrete_gpu(discrete_gpu) self.surface_laptop_special_patch(motherboard_name, platform) self.result["Add"] = sorted(self.result["Add"], key=lambda x: x["Path"]) self.apply_acpi_patches() return self.result