# Original source: https://github.com/corpnewt/SSDTTime/blob/44aadf01b7fe75cb4a3eab5590e7b6c458265c6f/SSDTTime.py from Scripts.datasets import acpi_patch_data 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 from Scripts.custom_dialogs import show_checklist_dialog import os import binascii import re import tempfile import shutil import sys import plistlib class ACPIGuru: def __init__(self, dsdt_instance=None, smbios_instance=None, run_instance=None, utils_instance=None): self.acpi = dsdt_instance if dsdt_instance else dsdt.DSDT() self.smbios = smbios_instance if smbios_instance else smbios.SMBIOS() self.run = run_instance.run if run_instance else run.Run().run self.utils = utils_instance if utils_instance else utils.Utils() self.patches = acpi_patch_data.patches self.hardware_report = None self.disabled_devices = None self.acpi_directory = None self.smbios_model = None 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.dsdt_patches = [] 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_acpi_tables(self, path): if not path: return self.utils.log_message("[ACPI GURU] Loading ACPI Table(s) from {}".format(path), level="INFO") 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): self.utils.log_message("[ACPI GURU] Gathering valid tables from {}".format(os.path.basename(path)), level="INFO") for t in self.sorted_nicely(os.listdir(path)): if not "Patched" in t and self.acpi.table_is_valid(path,t): self.utils.log_message("[ACPI GURU] Found valid table: {}".format(t), level="INFO") 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_acpi_tables(os.path.join(path,"ACPI")) self.utils.log_message("[ACPI GURU] No valid .aml files were found!", level="ERROR") #self.u.grab("Press [enter] to return...") # Restore any prior tables self.acpi.acpi_tables = prior_tables return self.utils.log_message("[ACPI GURU] Found at least one valid table", level="INFO") # 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: self.utils.log_message("[ACPI GURU] Multiple files with DSDT signature passed:", level="ERROR") for d in self.sorted_nicely(dsdt_list): self.utils.log_message("[ACPI GURU] Found DSDT file: {}".format(d), level="INFO") self.utils.log_message("[ACPI GURU] Only one is allowed at a time. Please remove one of the above and try again.", level="ERROR") #self.u.grab("Press [enter] to return...") # 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 self.utils.log_message("[ACPI GURU] Disassembling {} to verify if pre-patches are needed...".format(dsdt), level="INFO") if not self.acpi.load(os.path.join(path,dsdt))[0]: trouble_dsdt = dsdt else: self.utils.log_message("[ACPI GURU] Disassembled successfully!", level="INFO") elif not "Patched" in path and os.path.isfile(path): self.utils.log_message("[ACPI GURU] Loading {}...".format(os.path.basename(path)), level="INFO") if self.acpi.load(path)[0]: self.utils.log_message("[ACPI GURU] Done.", level="INFO") # 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 self.utils.log_message("[ACPI GURU] {} could not be disassembled!".format(os.path.basename(path)), level="ERROR") #self.u.grab("Press [enter] to return...") # 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: self.utils.log_message("[ACPI GURU] Passed file/folder does not exist!", level="ERROR") #self.u.grab("Press [enter] to return...") # 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 self.utils.log_message("[ACPI GURU] Checking available pre-patches...", level="INFO") self.utils.log_message("[ACPI GURU] Loading {} into memory...".format(trouble_dsdt), level="INFO") with open(trouble_path,"rb") as f: d = f.read() res = self.acpi.check_output(path) target_name = self.get_unique_name(trouble_dsdt,res,name_append="-Patched") self.dsdt_patches = [] self.utils.log_message("[ACPI GURU] Iterating patches...", level="INFO") for p in self.pre_patches: if not all(x in p for x in ("PrePatch","Comment","Find","Replace")): continue self.utils.log_message("[ACPI GURU] Found pre-patch: {}".format(p["PrePatch"]), level="INFO") find = binascii.unhexlify(p["Find"]) if d.count(find) == 1: self.dsdt_patches.append(p) # Retain the patch repl = binascii.unhexlify(p["Replace"]) self.utils.log_message("[ACPI GURU] Located pre-patch - applying...", level="INFO") 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 self.utils.log_message("[ACPI GURU] Disassembled successfully!", level="INFO") #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: self.utils.log_message("[ACPI GURU] {} could not be disassembled!".format(trouble_dsdt), level="ERROR") #self.u.grab("Press [enter] to return...") 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: self.utils.log_message("[ACPI GURU] Loading valid tables in {}...".format(path), level="INFO") loaded_tables,failed = self.acpi.load(temp or path) if not loaded_tables or failed: self.utils.log_message("[ACPI GURU] Failed 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): self.utils.log_message("[ACPI GURU] Failed to load table: {}".format(t), level="ERROR") # Restore any prior tables if not loaded_tables: self.acpi.acpi_tables = prior_tables else: #if len(tables) > 1: # print("") # Newline for readability self.utils.log_message("[ACPI GURU] Done.", level="INFO") # 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: pass #self.u.grab("Press [enter] to return...") #self.utils.request_input() if temp: shutil.rmtree(temp,ignore_errors=True) self.dsdt = self.acpi.get_dsdt_or_only() return path def _ensure_dsdt(self, allow_any=False): # Helper to check conditions for when we have valid tables return self.dsdt and ((allow_any and self.acpi.acpi_tables) or (not allow_any and self.acpi.get_dsdt_or_only())) def ensure_dsdt(self, allow_any=False): if self._ensure_dsdt(allow_any=allow_any): # Got it already return True # Need to prompt #self.select_acpi_tables() self.dsdt = self.acpi.get_dsdt_or_only() if self._ensure_dsdt(allow_any=allow_any): return True return False def get_sta_var(self,var="STAS",device=None,dev_hid="ACPI000E",dev_name="AWAC",log_locate=False,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: #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_lpc_name(self,log=False,skip_ec=False,skip_common_names=False): # Intel devices appear to use _ADR, 0x001F0000 # AMD devices appear to use _ADR, 0x00140003 if log: print("Locating LPC(B)/SBRG...") for table_name in self.sorted_nicely(list(self.acpi.acpi_tables)): table = self.acpi.acpi_tables[table_name] # The LPCB device will always be the parent of the PNP0C09 device # if found if not skip_ec: ec_list = self.acpi.get_device_paths_with_hid("PNP0C09",table=table) if len(ec_list): lpc_name = ".".join(ec_list[0][0].split(".")[:-1]) if log: print(" - Found {} in {}".format(lpc_name,table_name)) return lpc_name # Maybe try common names if we haven't found it yet if not skip_common_names: for x in ("LPCB", "LPC0", "LPC", "SBRG", "PX40"): try: lpc_name = self.acpi.get_device_paths(x,table=table)[0][0] if log: print(" - Found {} in {}".format(lpc_name,table_name)) return lpc_name except: pass # Finally check by address - some Intel tables have devices at # 0x00140003 paths = self.acpi.get_path_of_type(obj_type="Name",obj="_ADR",table=table) for path in paths: adr = self.get_address_from_line(path[1],table=table) if adr in (0x001F0000, 0x00140003): # Get the path minus ._ADR lpc_name = path[0][:-5] # Make sure the LPCB device does not have an _HID lpc_hid = lpc_name+"._HID" if any(x[0]==lpc_hid for x in table.get("paths",[])): continue if log: print(" - Found {} in {}".format(lpc_name,table_name)) return lpc_name if log: print(" - Could not locate LPC(B)! Aborting!") print("") return None # Didn't find it 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 enable_cpu_power_management(self): #if not self.ensure_dsdt(allow_any=True): # return #self.u.head("Plugin Type") #print("") #print("Determining CPU name scheme...") for table_name in self.sorted_nicely(list(self.acpi.acpi_tables)): ssdt_name = "SSDT-PLUG" table = self.acpi.acpi_tables[table_name] if not table.get("signature") in (b"DSDT",b"SSDT"): continue # We're not checking data tables #print(" Checking {}...".format(table_name)) try: cpu_name = self.acpi.get_processor_paths(table=table)[0][0] except: cpu_name = None if cpu_name: #print(" - Found Processor: {}".format(cpu_name)) #oc = {"Comment":"Sets plugin-type to 1 on first Processor object","Enabled":True,"Path":ssdt_name+".aml"} #print("Creating SSDT-PLUG...") ssdt = """// // Based on the sample found at 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: ssdt_name += "-ALT" #print(" - No Processor objects found...") procs = self.acpi.get_device_paths_with_hid(hid="ACPI0007",table=table) if not procs: #print(" - No ACPI0007 devices found...") continue #print(" - Located {:,} ACPI0007 device{}".format( # len(procs), "" if len(procs)==1 else "s" #)) parent = procs[0][0].split(".")[0] #print(" - Got parent at {}, iterating...".format(parent)) proc_list = [] for proc in procs: #print(" - Checking {}...".format(proc[0].split(".")[-1])) uid = self.acpi.get_path_of_type(obj_type="Name",obj=proc[0]+"._UID",table=table) if not uid: #print(" --> Not found! Skipping...") continue # Let's get the actual _UID value try: _uid = table["lines"][uid[0][1]].split("_UID, ")[1].split(")")[0] #print(" --> _UID: {}".format(_uid)) proc_list.append((proc[0],_uid)) except: pass #print(" --> Not found! Skipping...") if not proc_list: continue #print("Iterating {:,} valid processor device{}...".format(len(proc_list),"" if len(proc_list)==1 else "s")) ssdt = """// // Based on the sample found at 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) # Ensure our name scheme won't conflict schemes = ("C000","CP00","P000","PR00","CX00","PX00") # 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 = None for s in schemes: name_check = s[:-len(adr)]+adr check_path = "{}.{}".format(parent,name_check) if self.acpi.get_path_of_type(obj_type="Device",obj=check_path,table=table): continue # Already defined - skip # If we got here - we found an unused name name = name_check break if not name: #print(" - Could not find an available name scheme! Aborting.") #print("") #self.u.grab("Press [enter] to return to main menu...") return ssdt+=""" 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 += """ Method (_DSM, 4, NotSerialized) { If (LNot (Arg2)) { Return (Buffer (One) { 0x03 }) } Return (Package (0x02) { "plugin-type", One }) }""" # Close up the SSDT ssdt += """ }""" ssdt += """ } }""" # oc = {"Comment":"Redefines modern CPU Devices as legacy Processor objects and sets plugin-type to 1 on the first","Enabled":True,"Path":ssdt_name+".aml"} #self.make_plist(oc, ssdt_name+".aml", ()) #self.write_ssdt(ssdt_name,ssdt) #print("") #print("Done.") #self.patch_warn() #self.u.grab("Press [enter] to return...") #return # If we got here - we reached the end #print("No valid processor devices found!") #print("") #self.u.grab("Press [enter] to return...") #return return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt), "Path": ssdt_name + ".aml" } ] } 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.dsdt["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): hpets = self.acpi.get_device_paths_with_hid("PNP0103") hpet_fake = not hpets hpet_sta = False sta = None patches = [] 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 patches.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) patches.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])) patches.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"]) patches.append({ "Comment": patch_name, "Find": x["find"], "Replace": x["repl"], "Enabled": False }) # Restore the original DSDT in memory self.dsdt["raw"] = saved_dsdt ssdt_name = "SSDT-HPET" if hpet_fake: ssdt_content = """// Fake HPET device // DefinitionBlock ("", "SSDT", 2, "ZPSS", "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, "ZPSS", "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 += """ } }""" return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" } ], "Patch": patches } def fix_system_clock_awac(self): #if not self.ensure_dsdt(): # return #self.u.head("SSDT RTCAWAC") #print("") 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.u.grab("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": #print(" --> _CRS is a Buffer - checking RTC range...") 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: # 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.u.grab("Press [enter] to return to main menu...") return 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 #oc = {"Comment":comment,"Enabled":True,"Path":"SSDT-RTCAWAC.aml"} #self.make_plist(oc, "SSDT-RTCAWAC.aml", awac_dict.get("patches",[])+rtc_dict.get("patches",[]), replace=True) #print("Creating SSDT-RTCAWAC...") ssdt_name = "SSDT-RTCAWAC" ssdt = """// // 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 // // Uses the ZPSS name to denote where this was created for troubleshooting purposes. // DefinitionBlock ("", "SSDT", 2, "ZPSS", "RTCAWAC", 0x00000000) { """ if any(x.get("has_var") for x in (awac_dict,rtc_dict)): ssdt += """ 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 += """ 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 += """ 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 += """ 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 += """ 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 += "}" #self.write_ssdt("SSDT-RTCAWAC",ssdt) #print("") #print("Done.") # See if we just generated a failsafe - and encourage manual checking # Would require only an RTC device (no AWAC) that has an _STA with no STAS var #if rtc_dict.get("valid") and not awac_dict.get("valid") and rtc_dict.get("sta") and not rtc_dict.get("has_var") and not rtc_range_needed: # print("\n {}!! NOTE !!{} Only RTC (no AWAC) detected with an _STA method and no STAS".format(self.yel,self.rst)) # print(" variable! Patch(es) and SSDT-RTCAWAC created as a failsafe,") # print(" but verify you need them by checking the RTC._STA conditions!") #self.patch_warn() #self.u.grab("Press [enter] to return...") if self.write_ssdt(ssdt_name, ssdt): return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt), "Path": ssdt_name + ".aml" } ], "Patch": awac_dict.get("patches",[])+rtc_dict.get("patches",[]) } def fake_embedded_controller(self): ssdt_name = "SSDT-EC" laptop = "Laptop" in self.hardware_report.get("Motherboard").get("Platform") #if not self.ensure_dsdt(): # return #self.u.head("Fake EC") #print("") #print("Locating PNP0C09 (EC) devices...") # Set up a helper method to determine # if an _STA needs patching based on # the type and returns. 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 = table["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=table)) 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 = [] lpc_name = None ec_located = False for table_name in self.sorted_nicely(list(self.acpi.acpi_tables)): table = self.acpi.acpi_tables[table_name] ec_list = self.acpi.get_device_paths_with_hid("PNP0C09",table=table) if len(ec_list): lpc_name = ".".join(ec_list[0][0].split(".")[:-1]) #print(" - Got {:,} in {}".format(len(ec_list),table_name)) #print(" - Validating...") 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=table)) # 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=table ) 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) #else: # print(" --> _STA properly enabled - skipping rename") #else: # print(" ----> NOT Valid PNP0C09 (EC) Device") #if not ec_located: #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.u.grab("Press [enter] to return to main menu...") return if lpc_name is None: lpc_name = self.get_lpc_name(skip_ec=True,skip_common_names=True) if lpc_name is None: #self.u.grab("Press [enter] to return to main menu...") return #comment = "Faked Embedded Controller" 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 = """ DefinitionBlock ("", "SSDT", 2, "ZPSS", "EC", 0x00001000) { External ([[LPCName]], DeviceObj) """.replace("[[LPCName]]",lpc_name) for x in ec_to_patch: ssdt += " External ({}, DeviceObj)\n".format(x) if x in ec_sta: ssdt += " External ({}.XSTA, {})\n".format(x,ec_sta[x].get("sta_type","MethodObj")) # Walk the ECs to enable for x in ec_to_enable: ssdt += " 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 += " 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 += """ Scope ([[ECName]]) { Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0) } Else { Return ([[XSTA]]) } } } """.replace("[[LPCName]]",lpc_name).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 += """ 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]]",lpc_name).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 += """ 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]]",lpc_name) # Close the SSDT scope ssdt += """ }""" #self.write_ssdt("SSDT-EC",ssdt) #print("") #print("Done.") #self.patch_warn() #self.u.grab("Press [enter] to return...") return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt), "Path": ssdt_name + ".aml" } ], "Patch": patches } def get_data(self, data, pad_to=0): if sys.version_info >= (3, 0): if not isinstance(data,bytes): data = data.encode() return data+b"\x00"*(max(pad_to-len(data),0)) else: return plistlib.Data(data+b"\x00"*(max(pad_to-len(data),0))) 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") if not os.path.exists(self.acpi_directory): os.makedirs(self.acpi_directory) 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, acpi_patches): acpi_patches = [ { "Base": acpi_patch.get("Base", ""), "BaseSkip": acpi_patch.get("BaseSkip", 0), "Comment": acpi_patch.get("Comment", ""), "Count": acpi_patch.get("Count", 0), "Enabled": True, "Find": self.utils.hex_to_bytes(acpi_patch["Find"]), "Limit": acpi_patch.get("Limit", 0), "Mask": self.utils.hex_to_bytes(acpi_patch.get("Mask", "")), "OemTableId": self.utils.hex_to_bytes(acpi_patch.get("OemTableId", "")), "Replace": self.utils.hex_to_bytes(acpi_patch["Replace"]), "ReplaceMask": self.utils.hex_to_bytes(acpi_patch.get("ReplaceMask", "")), "Skip": acpi_patch.get("Skip", 0), "TableLength": acpi_patch.get("TableLength", 0), "TableSignature": self.utils.hex_to_bytes(acpi_patch.get("TableSignature", "")), } for acpi_patch in acpi_patches ] return sorted(acpi_patches, key=lambda x: x["Comment"]) def add_intel_management_engine(self): 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) } } } } }""" imei_device = self.acpi.get_device_paths_with_hid("0x00160000", self.dsdt) if not imei_device: return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" } ] } def add_memory_controller_device(self): if not self.lpc_bus_device: return 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) return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" } ] } def add_system_management_bus_device(self): if not self.lpc_bus_device: return try: smbus_device_name = self.acpi.get_device_paths_with_hid("0x001F0003" if self.hardware_report.get("CPU").get("Codename") in cpu_data.IntelCPUGenerations[50:] 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) 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) return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" } ] } def add_usb_power_properties(self): 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,"], self.smbios_model): usb_power_properties = { "kUSBSleepPowerSupply":"0x13EC", "kUSBSleepPortCurrentLimit":"0x0834", "kUSBWakePowerSupply":"0x13EC", "kUSBWakePortCurrentLimit":"0x0834" } elif "MacMini8,1" in self.smbios_model: usb_power_properties = { "kUSBSleepPowerSupply":"0x0C80", "kUSBSleepPortCurrentLimit":"0x0834", "kUSBWakePowerSupply":"0x0C80", "kUSBWakePortCurrentLimit":"0x0834" } elif self.utils.contains_any(["MacBookPro16,", "MacBookPro15,", "MacBookPro14,", "MacBookPro13,", "MacBookAir9,1"], self.smbios_model): usb_power_properties = { "kUSBSleepPortCurrentLimit":"0x0BB8", "kUSBWakePortCurrentLimit":"0x0BB8" } elif "MacBook9,1" in self.smbios_model: 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]) for key in usb_power_properties)) return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" } ] } def ambient_light_sensor(self): 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 patches = [] 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) patches.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 (0x0F) } 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") return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" } ], "Patch": patches } def findall_power_resource_blocks(self, table_lines): power_resource_blocks = [] i = 0 while i < len(table_lines): line = table_lines[i].strip() if line.startswith("PowerResource"): start_index = i open_brackets = 1 i += 1 while i < len(table_lines) and open_brackets > 0: if '{' in table_lines[i]: open_brackets += table_lines[i].count('{') if '}' in table_lines[i]: open_brackets -= table_lines[i].count('}') i += 1 end_index = i - 1 power_resource_blocks.append((start_index, end_index)) else: i += 1 return power_resource_blocks def is_method_in_power_resource(self, method, table_lines): power_resource_blocks = self.findall_power_resource_blocks(table_lines) for start, end in power_resource_blocks: if start <= method[1] <= end: return True return False def disable_unsupported_device(self): results = { "Add": [] } for device_name, device_props in self.disabled_devices.items(): if not device_props.get("Bus Type", "PCI") == "PCI" or not device_props.get("ACPI Path"): continue ssdt_name = None if "GPU" in device_name and device_props.get("Device Type") != "Integrated GPU": ssdt_name = "SSDT-Disable_GPU_{}".format(device_props.get("ACPI Path").split(".")[2]) target_device = device_props.get("ACPI Path") off_method_found = ps3_method_found = False for table_name, table_data in self.acpi.acpi_tables.items(): off_methods = self.acpi.get_method_paths("_OFF", table_data) ps3_methods = self.acpi.get_method_paths("_PS3", table_data) off_method_found = off_method_found or any(method[0].startswith(target_device) and not self.is_method_in_power_resource(method, table_data.get("lines")) for method in off_methods) ps3_method_found = ps3_method_found or any(method[0].startswith(target_device) for method in ps3_methods) if not off_method_found and not ps3_method_found: continue if off_method_found: ps3_method_found = False device_props["Disabled"] = True ssdt_content = """ DefinitionBlock ("", "SSDT", 2, "ZPSS", "DGPU", 0x00000000) {""" if off_method_found: ssdt_content += """ External ([[DevicePath]]._OFF, MethodObj) External ([[DevicePath]]._ON_, MethodObj)""" if ps3_method_found: ssdt_content += """ External ([[DevicePath]]._PS0, MethodObj) External ([[DevicePath]]._PS3, MethodObj) External ([[DevicePath]]._DSM, MethodObj) """ ssdt_content += """ Device (DGPU) { Name (_HID, "DGPU1000") Method (_INI, 0, NotSerialized) { _OFF () } Method (_STA, 0, NotSerialized) { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } } Method (_ON, 0, NotSerialized) { """ if off_method_found: ssdt_content += """ [[DevicePath]]._ON () """ if ps3_method_found: ssdt_content += """ [[DevicePath]]._PS0 () """ ssdt_content += """ } Method (_OFF, 0, NotSerialized) { """ if off_method_found: ssdt_content += """ [[DevicePath]]._OFF () """ if ps3_method_found: ssdt_content += """ [[DevicePath]]._DSM (ToUUID ("a486d8f8-0bda-471b-a72b-6042a6b5bee0") /* Unknown UUID */, 0x0100, 0x1A, Buffer (0x04) { 0x01, 0x00, 0x00, 0x03 // .... }) [[DevicePath]]._PS3 () """ ssdt_content += """\n }\n }\n}""" elif "Network" in device_name and device_props.get("Bus Type") == "PCI": ssdt_name = "SSDT-Disable_Network_{}".format(device_props.get("ACPI Path").split(".")[2]) ssdt_content = """ DefinitionBlock ("", "SSDT", 2, "ZPSS", "DNET", 0x00000000) { External ([[DevicePath]], DeviceObj) Method ([[DevicePath]]._DSM, 4, NotSerialized) // _DSM: Device-Specific Method { If ((!Arg2 || (_OSI ("Darwin") == Zero))) { Return (Buffer (One) { 0x03 // . }) } Return (Package (0x0A) { "name", Buffer (0x09) { "#network" }, "IOName", "#display", "class-code", Buffer (0x04) { 0xFF, 0xFF, 0xFF, 0xFF // .... }, "vendor-id", Buffer (0x04) { 0xFF, 0xFF, 0x00, 0x00 // .... }, "device-id", Buffer (0x04) { 0xFF, 0xFF, 0x00, 0x00 // .... } }) } } """ elif "Storage" in device_name: ssdt_name = "SSDT-Disable_NVMe_{}".format(device_props.get("ACPI Path").split(".")[-2]) ssdt_content = """ DefinitionBlock ("", "SSDT", 2, "ZPSS", "DNVMe", 0x00000000) { External ([[DevicePath]], DeviceObj) Method ([[DevicePath]]._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: ssdt_content = ssdt_content.replace("[[DevicePath]]", device_props.get("ACPI Path")) results["Add"].append( { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" } ) return results def enable_backlight_controls(self): patches = [] integrated_gpu = list(self.hardware_report.get("GPU").items())[-1][-1] uid_value = 19 if integrated_gpu.get("Codename") in ("Iron Lake", "Sandy Bridge", "Ivy Bridge"): uid_value = 14 elif integrated_gpu.get("Codename") in ("Haswell", "Broadwell"): uid_value = 15 elif integrated_gpu.get("Codename") in ("Skylake", "Kaby Lake"): uid_value = 16 if "PNLF" in self.dsdt.get("table"): patches.append({ "Comment": "PNLF to XNLF Rename", "Find": "504E4C46", "Replace": "584E4C46" }) for table_name in self.sorted_nicely(list(self.acpi.acpi_tables)): table = self.acpi.acpi_tables[table_name] if binascii.unhexlify("084E4243460A00") in table.get("raw"): patches.append({ "Comment": "NBCF 0x00 to 0x01", "Find": "084E4243460A00", "Replace": "084E4243460A01" }) break elif binascii.unhexlify("084E42434600") in table.get("raw"): patches.append({ "Comment": "NBCF Zero to One", "Find": "084E42434600", "Replace": "084E42434601" }) break ssdt_name = "SSDT-PNLF" ssdt_content = """ DefinitionBlock ("", "SSDT", 2, "ZPSS", "PNLF", 0x00000000) {""" if integrated_gpu.get("ACPI Path"): ssdt_content += """\n External ([[DevicePath]], DeviceObj)\n Device ([[DevicePath]].PNLF)""" else: ssdt_content += """\n Device (PNLF)""" ssdt_content += """ { 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 integrated_gpu.get("ACPI Path") and uid_value == 14: ssdt_content += """ Method (_INI, 0, Serialized) { If (_OSI ("Darwin")) { OperationRegion ([[DevicePath]].RMP3, PCI_Config, Zero, 0x14) Field ([[DevicePath]].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 += """ } }""" ssdt_content = ssdt_content.replace("[[uid_value]]", str(uid_value)) if integrated_gpu.get("ACPI Path"): ssdt_content = ssdt_content.replace("[[DevicePath]]", integrated_gpu.get("ACPI Path")) return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" } ], "Patch": patches } def enable_gpio_device(self): 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: return sta = self.get_sta_var(var=None, device=gpio_device, dev_hid=None, dev_name=gpio_device.split(".")[-1], table=self.dsdt) 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") return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" } ], "Patch": sta.get("patches", []) } def enable_nvram_support(self): if not self.lpc_bus_device: return 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) return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" } ], } def remove_conditional_scope(self): return { "Patch": [ { "Comment": "Remove conditional ACPI scope declaration", "Find": "A000000092935043484100", "Replace": "A3A3A3A3A3A3A3A3A3A3A3", "Mask": "FF000000FFFFFFFFFFFFFF", "Count": 1, "TableSignature": "44534454" } ] } def fix_hp_005_post_error(self): if binascii.unhexlify("4701700070000108") in self.dsdt.get("raw"): return { "Patch": [ { "Comment": "Fix HP Real-Time Clock Power Loss (005) Post Error", "Find": "4701700070000108", "Replace": "4701700070000102" } ] } def add_null_ethernet_device(self): 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)]) 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) return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" } ] } def is_intel_hedt_cpu(self, processor_name, cpu_codename): if cpu_codename in cpu_data.IntelCPUGenerations[45:66]: return cpu_codename.endswith(("-X", "-P", "-W", "-E", "-EP", "-EX")) if cpu_codename in cpu_data.IntelCPUGenerations[66:]: return "Xeon" in processor_name return False def fix_system_clock_hedt(self): awac_device = self.acpi.get_device_paths_with_hid("ACPI000E", self.dsdt) try: rtc_device = self.acpi.get_device_paths_with_hid("PNP0B00", self.dsdt)[0][0] if rtc_device.endswith("RTC"): rtc_device += "_" except: if not self.lpc_bus_device: return rtc_device = self.lpc_bus_device + ".RTC0" new_rtc_device = ".".join(rtc_device.split(".")[:-1] + [self.get_unique_device(rtc_device, rtc_device.split(".")[-1])[0]]) patches = [] ssdt_name = "SSDT-RTC0-RANGE" ssdt_content = """ DefinitionBlock ("", "SSDT", 2, "ZPSS", "RtcRange", 0x00000000) {""" if not awac_device: sta = self.get_sta_var(var=None, device=rtc_device, dev_hid=None, dev_name=rtc_device.split(".")[-1], table=self.dsdt) patches.extend(sta.get("patches", [])) ssdt_content += """ External ([[device_path]], DeviceObj) Scope ([[device_path]]) { Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (Zero) } Else { Return (0x0F) } } }""".replace("[[device_path]]", rtc_device) ssdt_content += """ External ([[parent_path]], DeviceObj) Device ([[device_path]]) { Name (_HID, EisaId ("PNP0B00") /* AT Real-Time Clock */) // _HID: Hardware ID Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { IO (Decode16, 0x0070, // Range Minimum 0x0070, // Range Maximum 0x01, // Alignment 0x04, // Length ) IO (Decode16, 0x0074, // Range Minimum 0x0074, // Range Maximum 0x01, // Alignment 0x04, // Length ) IRQNoFlags () {8} }) Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } } } }""".replace("[[parent_path]]", ".".join(rtc_device.split(".")[:-1])).replace("[[device_path]]", new_rtc_device) return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" } ], "Patch": patches } def instant_wake_fix(self): ssdt_name = "SSDT-PRW" uswe_object = "9355535745" wole_object = "93574F4C45" gprw_method = "4750525702" uprw_method = "5550525702" xprw_method = "5850525702" patches = [] if binascii.unhexlify(gprw_method) in self.dsdt.get("raw"): patches.append({ "Comment": "GPRW to XPRW Rename", "Find": gprw_method, "Replace": xprw_method }) else: gprw_method = None if binascii.unhexlify(uprw_method) in self.dsdt.get("raw"): patches.append({ "Comment": "UPRW to XPRW Rename", "Find": uprw_method, "Replace": xprw_method }) else: uprw_method = None 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 gprw_method or uprw_method: 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 gprw_method: 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 uprw_method: 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}" if gprw_method or uprw_method or uswe_object or wole_object: return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" } ], "Patch": patches } def fix_uncore_bridge(self): unc0_device = self.acpi.get_device_paths("UNC0", self.dsdt) if not unc0_device: return 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 } } } }""" return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" } ] } def operating_system_patch(self): 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")])) patches = [] osid = self.acpi.get_method_paths("OSID", self.dsdt) if osid: patches.append({ "Comment": "OSID to XSID rename - must come before _OSI to XOSI rename!", "Find": "4F534944", "Replace": "58534944" }) osif = self.acpi.get_method_paths("OSIF", self.dsdt) if osif: patches.append({ "Comment": "OSIF to XSIF rename - must come before _OSI to XOSI rename!", "Find": "4F534946", "Replace": "58534946" }) patches.append({ "Comment": "_OSI to XOSI rename - requires SSDT-XOSI.aml", "Find": "5F4F5349", "Replace": "584F5349" }) return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" } ], "Patch": patches } def surface_laptop_special_patch(self): 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 } } } } """ return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" } ] } def find_line_start(self, text, index): current_idx = index while current_idx > 0: if text[current_idx] == '\n': return current_idx + 1 current_idx -= 1 return 0 def extract_line(self, text, index): start_idx = self.find_line_start(text, index) end_idx = text.index("\n", start_idx) + 1 if "\n" in text[start_idx:] else len(text) return text[start_idx:end_idx].strip(), start_idx, end_idx def extract_block_content(self, text, start_idx): try: block_start = text.index("{", start_idx) brace_count = 1 pos = block_start + 1 while brace_count > 0 and pos < len(text): if text[pos] == '{': brace_count += 1 elif text[pos] == '}': brace_count -= 1 pos += 1 if brace_count == 0: return text[block_start:pos] except ValueError as e: pass return "" def parse_field_line(self, line): try: if "//" in line: line = line.split("//")[0].strip() parts = line.split(",") if len(parts) >= 2: field_name = parts[0].strip() size_part = parts[1].strip() try: field_size = int(size_part) except ValueError: return None, None return field_name, field_size except (ValueError, IndexError) as e: pass return None, None def process_embedded_control_region(self, table, start_idx): try: embed_control_idx = table.index("EmbeddedControl", start_idx) line, start_line_idx, end_line_idx = self.extract_line(table, embed_control_idx) region_name = line.split("(")[1].split(",")[0].strip() return region_name, end_line_idx except (ValueError, IndexError) as e: return None, start_idx + 1 def process_field_definition(self, table, region_name, start_idx): fields = [] try: field_pattern = f"Field ({region_name}" if field_pattern not in table[start_idx:]: return fields, len(table) field_start_idx = table.index(field_pattern, start_idx) field_line, field_start_line_idx, field_end_line_idx = self.extract_line(table, field_start_idx) field_block = self.extract_block_content(table, field_end_line_idx) for line in field_block.splitlines(): line = line.strip() if not line or line in ["{", "}"]: continue field_name, field_size = self.parse_field_line(line) if field_name and field_size is not None: field_info = { "name": field_name, "size": field_size, } fields.append(field_info) return fields, field_end_line_idx except (ValueError, IndexError) as e: return fields, start_idx + 1 def battery_status_patch(self): if not self.dsdt: return False search_start_idx = 0 all_fields = [] while "EmbeddedControl" in self.dsdt.get("table")[search_start_idx:]: region_name, search_start_idx = self.process_embedded_control_region(self.dsdt.get("table"), search_start_idx) if not region_name: continue current_idx = search_start_idx region_fields = [] while True: fields, next_idx = self.process_field_definition(self.dsdt.get("table"), region_name, current_idx) if not fields or next_idx <= current_idx: break region_fields.extend(fields) current_idx = next_idx if f"Field ({region_name}" not in self.dsdt.get("table")[current_idx:]: break all_fields.extend(region_fields) return any(f["size"] > 8 for f in all_fields) 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 return { "All": True, "Comment": "Delete {}".format((signature or oemtableid).rstrip(b"\x00").decode()), "Enabled": True, "OemTableId": self.utils.hex_to_bytes(binascii.hexlify(table_data.get("id")).decode()), "TableLength": table_data.get("length"), "TableSignature": self.utils.hex_to_bytes(binascii.hexlify(table_data.get("signature")).decode()) } def fix_apic_processor_id(self): self.apic = self.acpi.get_table_with_signature("APIC") new_apic = "" if not self.apic: return for table_name in self.sorted_nicely(list(self.acpi.acpi_tables)): table = self.acpi.acpi_tables[table_name] processors = self.acpi.get_processor_paths(table=table) if not processors: continue processor_index = -1 apic_length = len(self.apic.get("lines")) skip_unknown_subtable = False for index in range(apic_length): line = self.apic.get("lines")[index] if "Unknown" in line: skip_unknown_subtable = not skip_unknown_subtable continue if skip_unknown_subtable: continue if "Subtable Type" in line and "[Processor Local APIC]" in line: processor_index += 1 apic_processor_id = self.apic["lines"][index + 2][-2:] try: processor_id = table.get("lines")[processors[processor_index][1]].split(", ")[1][2:] except: return if processor_index == 0 and apic_processor_id == processor_id: return self.apic["lines"][index + 2] = self.apic["lines"][index + 2][:-2] + processor_id new_apic += line + "\n" if processor_index != -1: return { "Add": [ { "Comment": "APIC.aml", "Enabled": self.write_ssdt("APIC", new_apic), "Path": "APIC.aml" } ], "Delete": [ self.dropping_the_table("APIC") ] } def disable_usb_hub_devices(self): ssdt_name = "SSDT-USB-Reset" patches = [] ssdt_content = """ DefinitionBlock ("", "SSDT", 2, "ZPSS", "UsbReset", 0x00001000) {""" rhub_devices = self.acpi.get_device_paths("RHUB") rhub_devices.extend(self.acpi.get_device_paths("HUBN")) rhub_devices.extend(self.acpi.get_device_paths("URTH")) if not rhub_devices: return for device in rhub_devices: device_path = device[0] sta = self.get_sta_var(var=None, device=device_path, dev_hid=None, dev_name=device_path.split(".")[-1], table=self.dsdt) patches.extend(sta.get("patches", [])) ssdt_content += """ External ([[device_path]], DeviceObj) Scope ([[device_path]]) { Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (Zero) } Else { Return (0x0F) } } } """.replace("[[device_path]]", device_path) ssdt_content += "\n}" return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" } ], "Patch": patches } def return_thermal_zone(self): ssdt_name = "SSDT-WMIS" ssdt_content = """ // Resource: https://github.com/zhen-zen/YogaSMC/blob/master/YogaSMC/SSDTSample/SSDT-WMIS.dsl /* * Sample SSDT to fix sensor return * * Certain models forget to return result from ThermalZone: * * Method (WQBI, 1, NotSerialized) * { * \\_TZ.WQBI (Arg0) * } * * So we have to patch it for correct reporting. * Rename Method (WQBI, 1, N) to XQBI * (ThermalZone one usually has Serialized type) * * Find: 57514249 01 // WQBI * Repl: 58514249 01 // XQBI * * MethodFlags := * bit 0-2: ArgCount (0-7) * bit 3: SerializeFlag * 0 NotSerialized * 1 Serialized */ DefinitionBlock ("", "SSDT", 2, "ZPSS", "WMIS", 0x00000000) { External (_TZ.WQBI, MethodObj) // Method in ThermalZone Method (_SB.WMIS.WQBI, 1, NotSerialized) { Return (\\_TZ.WQBI (Arg0)) } } """ for table_name in self.sorted_nicely(list(self.acpi.acpi_tables)): table = self.acpi.acpi_tables[table_name] wqbi_method = self.acpi.get_method_paths("WQBI", table=table) if not wqbi_method: continue return { "Add": [ { "Comment": ssdt_name + ".aml", "Enabled": self.write_ssdt(ssdt_name, ssdt_content), "Path": ssdt_name + ".aml" } ], "Patch": [ { "Comment": "WQBI to XQBI Rename", "Find": "5751424901", "Replace": "5851424901" } ] } def drop_cpu_tables(self): cpu_tables = ["CpuPm", "Cpu0Ist"] deletes = [] for table_name in cpu_tables: padded_table_id = self.get_data(table_name, pad_to=8) table_entry = self.dropping_the_table(oemtableid=padded_table_id) if table_entry: deletes.append(table_entry) return { "Delete": deletes } def get_patch_index(self, name): for index, patch in enumerate(self.patches): if patch.name == name: return index return None def select_acpi_patches(self, hardware_report, disabled_devices): self.utils.log_message("[ACPI GURU] Selecting ACPI patches...", level="INFO") selected_patches = [] if "Laptop" in hardware_report.get("Motherboard").get("Platform") and \ "Integrated GPU" in list(hardware_report.get("GPU").items())[-1][-1].get("Device Type") and \ not "SURFACE" in hardware_report.get("Motherboard").get("Name"): selected_patches.append("ALS") selected_patches.append("PNLF") if self.is_intel_hedt_cpu(hardware_report.get("CPU").get("Processor Name"), hardware_report.get("CPU").get("Codename")): selected_patches.append("APIC") for device_name, device_info in disabled_devices.items(): if "PCI" in device_info.get("Bus Type", "PCI"): selected_patches.append("Disable Devices") selected_patches.append("FakeEC") if "HP " in hardware_report.get("Motherboard").get("Name"): selected_patches.append("CMOS") if hardware_report.get("Motherboard").get("Chipset") in chipset_data.IntelChipsets[-7:]: selected_patches.append("RCSP") if "Laptop" in hardware_report.get("Motherboard").get("Platform") and hardware_report.get("CPU").get("Codename") in cpu_data.IntelCPUGenerations[50:]: selected_patches.append("FixHPET") for device_name, device_info in hardware_report.get("System Devices", {}).items(): device_id = device_info.get("Device ID") if not device_id in ("8086-1C3A", "8086-1E3A"): continue if "Sandy Bridge" in hardware_report.get("CPU").get("Codename") and device_id in "8086-1E3A" or \ "Ivy Bridge" in hardware_report.get("CPU").get("Codename") and device_id in "8086-1C3A": selected_patches.append("IMEI") if hardware_report.get("Motherboard").get("Chipset") in chipset_data.IntelChipsets[100:112]: selected_patches.append("PMC") if "Sandy Bridge" in hardware_report.get("CPU").get("Codename") or "Ivy Bridge" in hardware_report.get("CPU").get("Codename"): selected_patches.append("PM (Legacy)") else: selected_patches.append("PLUG") if all(network_props.get("Bus Type") == "USB" for network_props in hardware_report.get("Network", {}).values()): selected_patches.append("RMNE") if hardware_report.get("Motherboard").get("Chipset") in chipset_data.IntelChipsets[62:64] + chipset_data.IntelChipsets[90:100]: selected_patches.append("RTC0") if "AMD" in hardware_report.get("CPU").get("Manufacturer") or hardware_report.get("CPU").get("Codename") in cpu_data.IntelCPUGenerations[:40]: selected_patches.append("RTCAWAC") if "Intel" in hardware_report.get("CPU").get("Manufacturer"): selected_patches.append("BUS0") if "SURFACE" in hardware_report.get("Motherboard").get("Name"): selected_patches.append("Surface Patch") else: if "Intel" in hardware_report.get("CPU").get("Manufacturer"): for device_name, device_info in hardware_report.get("Input", {}).items(): if "I2C" in device_info.get("Device Type", "None"): selected_patches.append("GPI0") if hardware_report.get("Motherboard").get("Chipset") in chipset_data.IntelChipsets[27:28] + chipset_data.IntelChipsets[62:64]: selected_patches.append("UNC") if "AMD" in hardware_report.get("CPU").get("Manufacturer") or hardware_report.get("Motherboard").get("Chipset") in chipset_data.IntelChipsets[112:]: selected_patches.append("USB Reset") selected_patches.append("USBX") if "Laptop" in hardware_report.get("Motherboard").get("Platform"): selected_patches.append("BATP") selected_patches.append("XOSI") for device_name, device_info in hardware_report.get("System Devices", {}).items(): if device_info.get("Bus Type") == "ACPI" and device_info.get("Device") in pci_data.YogaHIDs: selected_patches.append("WMIS") self.utils.log_message("[ACPI GURU] Selected patches: {}".format(", ".join(selected_patches)), level="INFO") for patch in self.patches: patch.checked = patch.name in selected_patches def customize_patch_selection(self): items = [] checked_indices = [] for i, patch in enumerate(self.patches): label = f"{patch.name} - {patch.description}" items.append(label) if patch.checked: checked_indices.append(i) result = show_checklist_dialog("Configure ACPI Patches", "Select ACPI patches you want to apply:", items, checked_indices) if result is not None: for i, patch in enumerate(self.patches): patch.checked = i in result