from Scripts import dsdt, plist, reveal, run, utils import getpass, os, tempfile, shutil, plistlib, sys, binascii, zipfile, re, string, json, textwrap class SSDT: def __init__(self, **kwargs): self.u = utils.Utils("SSDT Time") self.r = run.Run() self.re = reveal.Reveal() try: self.d = dsdt.DSDT() except Exception as e: print("Something went wrong :( - Aborting!\n - {}".format(e)) exit(1) self.w = 80 self.h = 24 self.red = "\u001b[41;1m" self.yel = "\u001b[43;1m" self.grn = "\u001b[42;1m" self.blu = "\u001b[46;1m" self.rst = "\u001b[0m" self.copy_as_path = self.u.check_admin() if os.name=="nt" else False if 2/3==0: # ANSI escapes don't seem to work properly with python 2.x self.red = self.yel = self.grn = self.blu = self.rst = "" if os.name == "nt": if 2/3!=0: os.system("color") # Allow ANSI color escapes. self.w = 120 self.h = 30 self.iasl_legacy = False self.resize_window = True # Set up match mode approach: # 0 = Any table id, any length # 1 = Any table id, match length # 2 = Match table id, match length # 3 = Match NORMALIZED table id, match length self.match_mode = 0 self.match_dict = { 0:"{}Least Strict{}".format(self.red,self.rst), 1:"{}Length Only{}".format(self.yel,self.rst), 2:"{}Table Ids and Length{}".format(self.grn,self.rst), 3:"{}Table Ids and Length (NormalizeHeaders){}".format(self.blu,self.rst) } self.dsdt = None self.settings = os.path.join(os.path.dirname(os.path.realpath(__file__)),"Scripts","settings.json") if os.path.exists(self.settings): self.load_settings() self.output = "Results" self.target_irqs = [0,2,8,11] self.illegal_names = ("XHC1","EHC1","EHC2","PXSX") # _OSI Strings found here: https://learn.microsoft.com/en-us/windows-hardware/drivers/acpi/winacpi-osi 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" } ) def save_settings(self): settings = { "legacy_compiler": self.iasl_legacy, "resize_window": self.resize_window, "match_mode": self.match_mode } try: json.dump(settings,open(self.settings,"w"),indent=2) except: return def load_settings(self): try: settings = json.load(open(self.settings)) if self.d.iasl_legacy: # Only load the legacy compiler setting if we can self.iasl_legacy = settings.get("legacy_compiler",False) self.resize_window = settings.get("resize_window",True) self.match_mode = settings.get("match_mode",0) except: return 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 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 load_dsdt(self, path): if not path: return self.u.head("Loading ACPI Table(s)") print("") tables = [] trouble_dsdt = None fixed = False temp = None prior_tables = self.d.acpi_tables # Retain in case of failure # Clear any existing tables so we load anew self.d.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.d.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.load_dsdt(os.path.join(path,"ACPI")) print(" - No valid .aml files were found!") print("") self.u.grab("Press [enter] to return...") # Restore any prior tables self.d.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.d._table_signature(path,x) == b"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 all but one of the above and try") print("again.") print("") self.u.grab("Press [enter] to return...") # Restore any prior tables self.d.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.d.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.d.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.d._table_signature(path) == b"DSDT": # Not a DSDT, we aren't applying pre-patches print("\n{} could not be disassembled!".format(os.path.basename(path))) print("") self.u.grab("Press [enter] to return...") # Restore any prior tables self.d.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.u.grab("Press [enter] to return...") # Restore any prior tables self.d.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.d.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: patches.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 loaded_table = self.d.load(trouble_path)[0] if loaded_table: try: table = loaded_table[list(loaded_table)[0]] except: pass 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.u.grab("Press [enter] to return...") if temp: shutil.rmtree(temp,ignore_errors=True) # Restore any prior tables self.d.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.d.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.d.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.u.grab("Press [enter] to continue...") if temp: shutil.rmtree(temp,ignore_errors=True) return path def select_dsdt(self, single_table=False): while True: self.u.head("Select ACPI Table{}".format("" if single_table else "s")) print(" ") if self.copy_as_path: print("NOTE: Currently running as admin on Windows - drag and drop may not work.") print(" Shift + right-click in Explorer and select 'Copy as path' then paste here instead.") print("") if sys.platform.startswith("linux") or sys.platform == "win32": print("P. Dump the current system's ACPI tables") print("M. Main") print("Q. Quit") print(" ") if single_table: print("NOTE: The function requesting this table expects either a single table, or one") print(" with the DSDT signature. If neither condition is met, you will be") print(" returned to the main menu.") print("") dsdt = self.u.grab("Please drag and drop an ACPI table or folder of tables here: ") if dsdt.lower() == "p" and (sys.platform.startswith("linux") or sys.platform == "win32"): output_folder = os.path.join(os.path.dirname(os.path.realpath(__file__)),self.output) acpi_name = self.get_unique_name("OEM",output_folder,name_append="") return self.load_dsdt( self.d.dump_tables(os.path.join(output_folder,acpi_name)) ) elif dsdt.lower() == "m": return self.dsdt elif dsdt.lower() == "q": self.u.custom_quit() out = self.u.check_path(dsdt) if not out: continue # Got a DSDT, try to load it return self.load_dsdt(out) 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.d.acpi_tables) or (not allow_any and self.d.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.dsdt = self.select_dsdt(single_table=not allow_any) if self._ensure_dsdt(allow_any=allow_any): return True return False def write_ssdt(self, ssdt_name, ssdt): res = self.d.check_output(self.output) dsl_path = os.path.join(res,ssdt_name+".dsl") aml_path = os.path.join(res,ssdt_name+".aml") iasl_path = self.d.iasl_legacy if self.iasl_legacy else self.d.iasl with open(dsl_path,"w") as f: f.write(ssdt) print("Compiling...{}".format(" {}!! Using Legacy Compiler !!{}".format(self.yel,self.rst) if self.iasl_legacy else "")) out = self.r.run({"args":[iasl_path, dsl_path]}) if out[2] != 0: print(" - {}".format(out[1])) self.re.reveal(dsl_path,True) return False else: self.re.reveal(aml_path,True) return True def ensure_path(self, plist_data, path_list, final_type = list): if not path_list: return plist_data last = plist_data for index,path in enumerate(path_list): if not path in last: if index >= len(path_list)-1: last[path] = final_type() else: last[path] = {} last = last[path] return plist_data def make_plist(self, oc_acpi, cl_acpi, patches, drops=[], replace=False): # if not len(patches): return # No patches to add - bail repeat = False print("Building patches_OC and patches_Clover plists...") output = self.d.check_output(self.output) oc_plist = {} cl_plist = {} # Check for the plists if os.path.isfile(os.path.join(output,"patches_OC.plist")): e = os.path.join(output,"patches_OC.plist") with open(e,"rb") as f: oc_plist = plist.load(f) if os.path.isfile(os.path.join(output,"patches_Clover.plist")): e = os.path.join(output,"patches_Clover.plist") with open(e,"rb") as f: cl_plist = plist.load(f) # Ensure all the pathing is where it needs to be if oc_acpi: oc_plist = self.ensure_path(oc_plist,("ACPI","Add")) if cl_acpi: cl_plist = self.ensure_path(cl_plist,("ACPI","SortedOrder")) if patches: oc_plist = self.ensure_path(oc_plist,("ACPI","Patch")) cl_plist = self.ensure_path(cl_plist,("ACPI","DSDT","Patches")) if drops: oc_plist = self.ensure_path(oc_plist,("ACPI","Delete")) cl_plist = self.ensure_path(cl_plist,("ACPI","DropTables")) # Add the .aml references if replace: # Remove any conflicting entries if oc_acpi: oc_plist["ACPI"]["Add"] = [x for x in oc_plist["ACPI"]["Add"] if oc_acpi["Path"] != x["Path"]] if cl_acpi: cl_plist["ACPI"]["SortedOrder"] = [x for x in cl_plist["ACPI"]["SortedOrder"] if cl_acpi != x] if oc_acpi: # Make sure we have something if any(oc_acpi["Path"] == x["Path"] for x in oc_plist["ACPI"]["Add"]): print(" -> Add \"{}\" already in OC plist!".format(oc_acpi["Path"])) else: oc_plist["ACPI"]["Add"].append(oc_acpi) if cl_acpi: # Make sure we have something if cl_acpi in cl_plist["ACPI"]["SortedOrder"]: print(" -> \"{}\" already in Clover plist!".format(cl_acpi)) else: cl_plist["ACPI"]["SortedOrder"].append(cl_acpi) # Iterate the patches for p in patches: ocp = self.get_oc_patch(p) cp = self.get_clover_patch(p) if replace: # Remove any conflicting entries oc_plist["ACPI"]["Patch"] = [x for x in oc_plist["ACPI"]["Patch"] if ocp["Find"] != x["Find"] and ocp["Replace"] != x["Replace"]] cl_plist["ACPI"]["DSDT"]["Patches"] = [x for x in cl_plist["ACPI"]["DSDT"]["Patches"] if cp["Find"] != x["Find"] and cp["Replace"] != x["Replace"]] if any(x["Find"] == ocp["Find"] and x["Replace"] == ocp["Replace"] for x in oc_plist["ACPI"]["Patch"]): print(" -> Patch \"{}\" already in OC plist!".format(p["Comment"])) else: print(" -> Adding Patch \"{}\" to OC plist!".format(p["Comment"])) oc_plist["ACPI"]["Patch"].append(ocp) if any(x["Find"] == cp["Find"] and x["Replace"] == cp["Replace"] for x in cl_plist["ACPI"]["DSDT"]["Patches"]): print(" -> Patch \"{}\" already in Clover plist!".format(p["Comment"])) else: print(" -> Adding Patch \"{}\" to Clover plist!".format(p["Comment"])) cl_plist["ACPI"]["DSDT"]["Patches"].append(cp) # Iterate any dropped tables for d in drops: ocd = self.get_oc_drop(d) cd = self.get_clover_drop(d) if replace: oc_plist["ACPI"]["Delete"] = [x for x in oc_plist["ACPI"]["Delete"] if ocd["TableSignature"] != x["TableSignature"] and ocd["OemTableId"] != x["OemTableId"]] cl_plist["ACPI"]["DropTables"] = [x for x in cl_plist["ACPI"]["DropTables"] if cd.get("Signature") != x.get("Signature") and cd.get("TableId") != x.get("TableId")] if any(x["TableSignature"] == ocd["TableSignature"] and x["OemTableId"] == ocd["OemTableId"] for x in oc_plist["ACPI"]["Delete"]): print(" -> \"{}\" already in OC plist!".format(d["Comment"])) else: print(" -> Adding \"{}\" to OC plist!".format(d["Comment"])) oc_plist["ACPI"]["Delete"].append(ocd) name_parts = [] for x in ("Signature","TableId"): if not cd.get(x): continue n = cd[x] if 2/3!=0 and not isinstance(n,str): try: n = n.decode() except: continue name_parts.append(n.replace("?"," ").strip()) name = " - ".join(name_parts) if any(x.get("Signature") == cd.get("Signature") and x.get("TableId") == cd.get("TableId") for x in cl_plist["ACPI"]["DropTables"]): print(" -> \"{}\" already in Clover plist!".format(name or "Unknown Dropped Table")) else: cl_plist["ACPI"]["DropTables"].append(cd) print(" -> Adding \"{}\" to Clover plist!".format(name or "Unknown Dropped Table")) # Write the plists if we have something to write if oc_plist: with open(os.path.join(output,"patches_OC.plist"),"wb") as f: plist.dump(oc_plist,f) if cl_plist: with open(os.path.join(output,"patches_Clover.plist"),"wb") as f: plist.dump(cl_plist,f) def patch_warn(self): # Warn users to ensure they merge the patches_XX.plist contents with their config.plist print("\n{}!! WARNING !!{} Make sure you merge the contents of patches_[OC/Clover].plist".format(self.red,self.rst)) print(" with your config.plist!\n") def get_lpc_name(self,log=True,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.d.acpi_tables)): table = self.d.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.d.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.d.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.d.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 sta_needs_patching(self, sta, table): # A helper method to determine if an _STA # needs patching to enable based on the # type and returns. 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: 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.d.get_scope(sta["sta"][0][1],strip_comments=True,table=table)) if sta_scope.count("Return (") > 1 or not "Return (0x0F)" in sta_scope: print("Multiple returns or not Return (0x0F)") # 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 def fake_ec(self, laptop = False): if not self.ensure_dsdt(): return self.u.head("Fake EC") print("") print("Locating PNP0C09 (EC) devices...") 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.d.acpi_tables)): table = self.d.acpi_tables[table_name] ec_list = self.d.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.d.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 self.sta_needs_patching(sta, table=table): # 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 laptop: comment += " (Laptop)" 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, "CORP ", "SsdtEC", 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...") def plugin_type(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.d.acpi_tables)): ssdt_name = "SSDT-PLUG" table = self.d.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.d.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, "CORP", "CpuPlug", 0x00003000) { External ([[CPUName]], ProcessorObj) Scope ([[CPUName]]) { Method (_DSM, 4, NotSerialized) // _DSM: Device-Specific Method { If (_OSI ("Darwin")) { If (LNot (Arg2)) { Return (Buffer (One) { 0x03 }) } Return (Package (0x02) { "plugin-type", One }) } Else { Return (Buffer (One) { Zero }) } } } }""".replace("[[CPUName]]",cpu_name) else: ssdt_name += "-ALT" print(" - No Processor objects found...") procs = self.d.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.d.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: 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, "CORP", "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.d.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 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.d.get_dsdt_or_only()["lines"]): if self.d.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.d.find_next_hex(index)[1] devices[current_device]["irq"] += "-"+str(irq_index)+"|"+num else: irq_index = self.d.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_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.d.get_hex_from_int(x) for x in find]), "repl": "".join(["22"+self.d.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 get_all_irqs(self, irq): irq_list = set() for a in irq.split("-"): i = a.split("|")[1] for x in i.split(":"): for y in x.split(","): if y == "#": continue irq_list.add(int(y)) return sorted(list(irq_list)) def get_data(self, data, pad_to=0): if sys.version_info >= (3, 0) and not isinstance(data,bytes): data = data.encode() return plist.wrap_data(data+b"\x00"*(max(pad_to-len(data),0))) def _get_table_id(self, table, id_name, mode=None): if mode is None: mode = self.match_mode if table is None: # No table found - return 0s as a failsafe mode = 0 # 0 = Any table id, any length # 1 = Any table id, match length # 2 = Match table id, match length # 3 = Match NORMALIZED table id, match length zero = self.d.get_hex_bytes("00" * (8 if id_name == "id" else 4)) if mode == 2: return table.get(id_name,zero) elif mode == 3: return table.get(id_name+"_ascii",table.get(id_name,zero)) else: # 0/1 match any table id return zero def _get_table_length(self, table, mode=None): if mode is None: mode = self.match_mode if table is None or mode not in (1,2,3): # No table found, or we're zeroing the # length - just return 0 return 0 # If mode is not 0, we return the table # length return table.get("length",0) def get_clover_patch(self, patch): return { "Comment": patch["Comment"], "Disabled": patch.get("Disabled",False), "Find": self.get_data(self.d.get_hex_bytes(patch["Find"])), "Replace": self.get_data(self.d.get_hex_bytes(patch["Replace"])) } def get_oc_patch(self, patch): table = patch.get("Table",self.d.get_dsdt_or_only()) if not isinstance(table,dict): table = {} return { "Base": patch.get("Base",""), "BaseSkip": patch.get("BaseSkip",0), "Comment": patch.get("Comment",""), "Count": patch.get("Count",0), "Enabled": patch.get("Enabled",True), "Find": self.get_data(self.d.get_hex_bytes(patch["Find"])), "Limit": patch.get("Limit",0), "Mask": self.get_data(patch.get("Mask",b"")), "OemTableId": self.get_data(patch.get("TableId",self._get_table_id(table,"id")),pad_to=8), "Replace": self.get_data(self.d.get_hex_bytes(patch["Replace"])), "ReplaceMask": self.get_data(patch.get("ReplaceMask",b"")), "Skip": patch.get("Skip",0), "TableLength": patch.get("Length",self._get_table_length(table)), "TableSignature": self.get_data(patch.get("Signature",self._get_table_id(table,"signature")),pad_to=4) } def get_oc_drop(self, drop): table = drop.get("Table") # Cannot accept None for a table to drop table = table or self.d.get_dsdt_or_only() assert table oc = { "All": drop.get("All",False), "Comment": drop.get("Comment",""), "Enabled": drop.get("Enabled",True), "OemTableId": self.get_data(drop.get("TableId",self._get_table_id(table,"id")),pad_to=8), "TableLength": drop.get("Length",self._get_table_length(table)), "TableSignature": self.get_data(drop.get("Signature",self._get_table_id(table,"signature")),pad_to=4) } # Ensure at least one of TableLength, OemTableId, or TableSignature is non-zero def _int(val): return val if isinstance(val,int) else sum([int(x) for x in val]) if sum(_int(oc[x]) for x in ("TableLength","OemTableId","TableSignature")) == 0: raise Exception("TableLength, OemTableId, and TableSignature cannot all be zeroes.") return oc def get_clover_drop(self, drop): table = drop.get("Table") # Cannot accept None for a table to drop table = table or self.d.get_dsdt_or_only() leng = self._get_table_length(table) d = { # Strip null chars and then decode to strings "Signature": table["signature"].rstrip(b"\x00").decode(), "TableId": table["id"].rstrip(b"\x00").decode(), } # Only add the length if we have a valid value for it length = drop.get("Length",leng) if length: d["Length"] = length return d def get_irq_choice(self, irqs): if not irqs or not isinstance(irqs,dict): # No IRQNoFlags entries located - or irqs isn't # a dict - just return the same value we would if # the user chose N. None return {} hid_pad = max((len(irqs[x].get("hid","")) for x in 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] while True: lines = [""] lines.append("Current Legacy IRQs:") lines.append("") if not len(irqs): lines.append(" - None Found") for x in irqs: if not hid_pad: lines.append(" {} {}: {}".format( "*" if x.upper() in names_and_hids else " ", x.rjust(4," "), self.get_all_irqs(irqs[x]["irq"]) )) else: lines.append(" {} {} {}: {}".format( "*" if x.upper() in names_and_hids or irqs[x].get("hid","").upper() in names_and_hids else " ", x.rjust(4," "), ("- "+irqs[x].get("hid","").rjust(hid_pad," ")) if irqs[x].get("hid") else "".rjust(hid_pad+2," "), self.get_all_irqs(irqs[x]["irq"]) )) lines.append("") lines.append("C. Only Conflicting IRQs from Legacy Devices ({} from * devices)".format(",".join([str(x) for x in self.target_irqs]) if len(self.target_irqs) else "None")) lines.append("O. Only Conflicting IRQs ({})".format(",".join([str(x) for x in self.target_irqs]) if len(self.target_irqs) else "None")) lines.append("L. Legacy IRQs (from * devices)") lines.append("N. None") lines.append("") lines.append("M. Main Menu") lines.append("Q. Quit") lines.append("") lines.append("* Indicates a typically troublesome device") lines.append("You can also type your own list of Devices and IRQs") lines.append("The format is DEV1:IRQ1,IRQ2 DEV2:IRQ3,IRQ4") lines.append("You can omit the IRQ# to remove all from that device (DEV1: DEV2:1,2,3)") lines.append("For example, to remove IRQ 0 from RTC, all from IPIC, and 8 and 11 from TMR:\n") lines.append("RTC:0 IPIC: TMR:8,11") lines.append("") max_line = max(lines,key=len) if self.resize_window: self.u.resize(max(len(max_line),self.w), max(len(lines)+5,self.h)) self.u.head("Select IRQs To Nullify") print("\n".join(lines)) menu = self.u.grab("Please select an option (default is C): ") if not len(menu): menu = "c" if menu.lower() == "m": return None elif menu.lower() == "q": if self.resize_window: self.u.resize(self.w,self.h) self.u.custom_quit() d = {} if menu.lower() == "n": pass # Don't populate d at all elif menu.lower() == "o": for x in irqs: d[x] = self.target_irqs elif menu.lower() == "l": for x in defaults: d[x] = [] elif menu.lower() == "c": for x in defaults: d[x] = self.target_irqs else: # User supplied for i in menu.split(" "): if not len(i): continue try: name,val = i.split(":") val = [int(x) for x in val.split(",") if len(x)] except Exception as e: # Incorrectly formatted print("!! Incorrect Custom IRQ List Format !!\n - {}".format(e)) d = None break d[name.upper()] = val if d is None: continue if self.resize_window: self.u.resize(self.w,self.h) return d def fix_hpet(self): if not self.ensure_dsdt(): return self.u.head("Fix HPET") print("") print("Locating PNP0103 (HPET) devices...") hpets = self.d.get_device_paths_with_hid("PNP0103") hpet_fake = not hpets patches = [] hpet_sta = False sta = None if hpets: name = hpets[0][0] print(" - Located at {}".format(name)) # Let's locate any _STA methods sta = self.get_sta_var(var=None,dev_hid="PNP0103",dev_name="HPET") if sta.get("patches"): hpet_sta = True patches.extend(sta.get("patches",[])) print("Locating HPET's _CRS Method/Name...") hpet = self.d.get_method_paths(name+"._CRS") or self.d.get_name_paths(name+"._CRS") if not hpet: print(" - Could not locate {}._CRS! Aborting!".format(name)) # Check for XCRS to see if the rename is already applied if self.d.get_method_paths(name+".XCRS") or self.d.get_name_paths(name+".XCRS"): print(" --> Appears to already be named XCRS!") print("") self.u.grab("Press [enter] to return to main menu...") return print(" - Located at {}._CRS".format(name)) crs_index = self.d.find_next_hex(hpet[0][1])[1] print(" - Found at index {}".format(crs_index)) print(" - Type: {}".format(hpet[0][-1])) # Let's find the Memory32Fixed portion within HPET's _CRS method print(" - Checking for Memory32Fixed...") mem_access = mem_base = mem_length = primed = None for line in self.d.get_scope(hpets[0][1],strip_comments=True): if "Memory32Fixed (" in line: try: mem_access = line.split("(")[1].split(",")[0] except: print(" --> Could not determine memory access type!") break 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: # Couldn't convert to an int - likely using vars, fall back to defaults print(" --> Could not convert Base or Length to Integer!") 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_access and mem_base and mem_length if got_mem: print(" --> Got {} {} -> {}".format(mem_access,mem_base,mem_length)) else: mem_access = "ReadWrite" mem_base = "0xFED00000" mem_length = "0x00000400" print(" --> Not located!") print(" --> Using defaults {} -> {}".format(mem_base,mem_length)) crs = "5F435253" xcrs = "58435253" padl,padr = self.d.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: print(" - None located!") name = self.get_lpc_name(skip_ec=True,skip_common_names=True) if name is None: self.u.grab("Press [enter] to return to main menu...") return devs = self.list_irqs() target_irqs = self.get_irq_choice(devs) if target_irqs is None: return # Bailed, going to the main menu self.u.head("Creating IRQ Patches") print("") if sta and sta.get("patches"): print(" - {} _STA to XSTA Rename:".format(sta["dev_name"])) print(" Find: {}".format(sta["patches"][0]["Find"])) print(" Replace: {}".format(sta["patches"][0]["Replace"])) print("") if not hpet_fake: print(" - {} _CRS to XCRS Rename:".format(name.split(".")[-1].lstrip("\\"))) print(" Find: {}".format(padl+crs+padr)) print(" Replace: {}".format(padl+xcrs+padr)) print("") print("Checking IRQs...") print("") if not devs: print(" - Nothing to patch!") print("") # Let's apply patches as we go saved_dsdt = self.d.get_dsdt_or_only()["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.d.get_hex_starting_at(t["index"])[0]) if not len(matches): print("Missing IRQ Patch ending for {} ({})! Skipping...".format(dev,t["find"])) 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.d.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"] }) print(" - {}".format(patch_name)) print(" Find: {}".format(p["find"])) print(" Replace: {}".format(p["repl"])) print("") # 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) print("The following may not be unique and are disabled by default!") print("") 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"], "Disabled":True, "Enabled":False }) print(" - {}".format(patch_name)) print(" Find: {}".format(x["find"])) print(" Replace: {}".format(x["repl"])) print("") # Restore the original DSDT in memory self.d.get_dsdt_or_only()["raw"] = saved_dsdt oc = { "Comment":"HPET Device Fake" if hpet_fake else "{} _CRS (Needs _CRS to XCRS Rename)".format(name.split(".")[-1].lstrip("\\")), "Enabled":True, "Path":"SSDT-HPET.aml" } self.make_plist(oc, "SSDT-HPET.aml", patches) print("Creating SSDT-HPET...") if hpet_fake: ssdt = """// 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 { // Only choose 0, and 8 to mimic a real mac's DSDT. // You may optionally want to add 11 and/or 12 here if // it does not work as expected - though those may have // other side effects (broken trackpad or otherwise). IRQNoFlags () {0,8} Memory32Fixed (ReadWrite, // Access Type 0xFED00000, // Address Base 0x00000400, // Address Length ) }) } } }""".replace("[[name]]",name) else: ssdt = """// // Supplementary HPET _CRS from Goldfish64 // Requires at least 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 () { // Only choose 0, and 8 to mimic a real mac's DSDT. // You may optionally want to add 11 and/or 12 here if // it does not work as expected - though those may have // other side effects (broken trackpad or otherwise). IRQNoFlags () {0,8} // [[mem]] Memory32Fixed ([[mem_access]], // Access Type [[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]]","AccessType/Base/Length pulled from DSDT" if got_mem else "Default AccessType/Base/Length - verify with your DSDT!") \ .replace("[[mem_access]]",mem_access) \ .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.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 = "\n".join(ssdt_parts) # Add our method ssdt += """ 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 += """ } }""" self.write_ssdt("SSDT-HPET",ssdt) print("") print("Done.") self.patch_warn() self.u.grab("Press [enter] to return...") def ssdt_pmc(self): if not self.ensure_dsdt(): return self.u.head("SSDT PMC") print("") lpc_name = self.get_lpc_name() if lpc_name is None: self.u.grab("Press [enter] to return to main menu...") return oc = {"Comment":"PMCR for native 300-series NVRAM","Enabled":True,"Path":"SSDT-PMC.aml"} self.make_plist(oc, "SSDT-PMC.aml", ()) print("Creating SSDT-PMC...") ssdt = """// // SSDT-PMC source from Acidanthera // Original found here: https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/AcpiSamples/SSDT-PMC.dsl // // Uses the CORP name to denote where this was created for troubleshooting purposes. // DefinitionBlock ("", "SSDT", 2, "CORP", "PMCR", 0x00001000) { External ([[LPCName]], DeviceObj) Scope ([[LPCName]]) { 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("[[LPCName]]",lpc_name) self.write_ssdt("SSDT-PMC",ssdt) print("") print("Done.") self.patch_warn() self.u.grab("Press [enter] to return...") 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. table = table or self.d.get_dsdt_or_only() has_var = False patches = [] root = None if device: dev_list = self.d.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.d.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.d.get_method_paths(dev[0]+"._STA",table=table) xsta = self.d.get_method_paths(dev[0]+".XSTA",table=table) if not sta and not xsta: # Check for names sta_type = "IntObj" sta = self.d.get_name_paths(dev[0]+"._STA",table=table) xsta = self.d.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.d.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.d.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.d.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, "Table":table }) 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 ssdt_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.d.get_method_paths(rtc_dict["device"][0]+"._CRS") or self.d.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 not rtc_crs_type.lower() == "buffobj": print(" --> _CRS is a Method - cannot verify RTC range!") else: print(" --> _CRS is a Buffer - checking RTC range...") last_adr = last_len = last_ind = None crs_scope = self.d.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.d.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.d.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 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 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 = """// // 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 CORP name to denote where this was created for troubleshooting purposes. // DefinitionBlock ("", "SSDT", 2, "CORP", "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...") def get_unique_device(self, parent_path, base_name, starting_number=0, used_names=[]): # Appends a hex number until a unique device is found while True: if starting_number < 0: # Try the original name first name = base_name # Ensure the starting number will be 0 next loop starting_number = -1 else: # Append the number to the name hex_num = hex(starting_number).replace("0x","").upper() name = base_name[:-1*len(hex_num)]+hex_num # Check if the name exists if not len(self.d.get_device_paths(parent_path.rstrip(".")+"."+name)) and not name in used_names: return (name,starting_number) # Increment the starting number starting_number += 1 def ssdt_rhub(self): if not self.ensure_dsdt(): return self.u.head("USB Reset") print("") print("Gathering RHUB/HUBN/URTH devices...") rhubs = self.d.get_device_paths("RHUB") rhubs.extend(self.d.get_device_paths("HUBN")) rhubs.extend(self.d.get_device_paths("URTH")) if not len(rhubs): print(" - None found! Aborting...") print("") self.u.grab("Press [enter] to return to main menu...") return print(" - Found {:,}".format(len(rhubs))) # Gather some info patches = [] tasks = [] used_names = [] xhc_num = 2 ehc_num = 1 for x in rhubs: task = {"device":x[0]} print(" --> {}".format(".".join(x[0].split(".")[:-1]))) name = x[0].split(".")[-2] if name in self.illegal_names or name in used_names: print(" ----> Needs rename!") # Get the new name, and the path to the device and its parent 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", starting_number=ehc_num, used_names=used_names ) ehc_num += 1 # Increment the name number else: task["rename"],xhc_num = self.get_unique_device( task["parent"], "XHCI", starting_number=xhc_num, used_names=used_names ) xhc_num += 1 # Increment the name number used_names.append(task["rename"]) else: used_names.append(name) sta_method = self.d.get_method_paths(task["device"]+"._STA") # Let's find out of we need a unique patch for _STA -> XSTA if len(sta_method): print(" ----> Generating _STA to XSTA patch") sta_index = self.d.find_next_hex(sta_method[0][1])[1] print(" ------> Found at index {}".format(sta_index)) sta_hex = "5F535441" xsta_hex = "58535441" padl,padr = self.d.get_shortest_unique_pad(sta_hex, sta_index) patches.append({ "Comment":"{} _STA to XSTA Rename".format(task["device"].split(".")[-1]), "Find":padl+sta_hex+padr, "Replace":padl+xsta_hex+padr }) # Let's try to get the _ADR scope_adr = self.d.get_name_paths(task["device"]+"._ADR") task["address"] = self.d.get_dsdt_or_only()["lines"][scope_adr[0][1]].strip() if len(scope_adr) else "Name (_ADR, Zero) // _ADR: Address" tasks.append(task) oc = {"Comment":"SSDT to disable USB RHUB/HUBN/URTH and rename devices","Enabled":True,"Path":"SSDT-USB-Reset.aml"} self.make_plist(oc, "SSDT-USB-Reset.aml", patches) ssdt = """// // SSDT to disable RHUB/HUBN/URTH devices and rename PXSX, XHC1, EHC1, and EHC2 devices // DefinitionBlock ("", "SSDT", 2, "CORP", "UsbReset", 0x00001000) { """ # Iterate the USB controllers and add external references # Gather the parents first - ensure they're unique, and put them in order parents = sorted(list(set([x["parent"] for x in tasks if x.get("parent",None)]))) for x in parents: ssdt += " External ({}, DeviceObj)\n".format(x) for x in tasks: ssdt += " External ({}, DeviceObj)\n".format(x["device"]) # Let's walk them again and disable RHUBs and rename for x in tasks: if x.get("rename",None): # Disable the old controller ssdt += """ 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]]",x["device"]).replace("[[parent]]",x["parent"]).replace("[[address]]",x.get("address","Name (_ADR, Zero) // _ADR: Address")).replace("[[new_device]]",x["rename"]) else: # Only disabling the RHUB ssdt += """ Scope ([[device]]) { Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (Zero) } Else { Return (0x0F) } } } """.replace("[[device]]",x["device"]) ssdt += "\n}" self.write_ssdt("SSDT-USB-Reset",ssdt) print("") print("Done.") self.patch_warn() self.u.grab("Press [enter] to return...") return def ssdt_usbx(self): usbx_props = { "kUSBSleepPowerSupply":"0x13EC", "kUSBSleepPortCurrentLimit":"0x0834", "kUSBWakePowerSupply":"0x13EC", "kUSBWakePortCurrentLimit":"0x0834" } while True: self.u.head("USBX Device") print("") print("Current USBX Device Properties To Use:") print("") if usbx_props: for i,x in enumerate(usbx_props,start=1): print("{}. {} -> {}".format(i,x,usbx_props[x])) else: print(" - No properties set") print("") print("B. Build SSDT-USBX") print("A. Remove All") print("M. Return to Menu") print("Q. Quit") print("") print("Remove a property by typing its key or number (ie kUSBSleepPowerSupply)") print("Add/Edit a property using this format key:value (ie kUSBWakePowerSupply:0x13EC)") print("Values must be a 16-bit hexadecimal integer") print("") menu = self.u.grab("Please enter your selection (default is B): ") if not menu: menu = "b" if menu.lower() == "m": return elif menu.lower() == "q": self.u.custom_quit() elif menu.lower() == "a": usbx_props = {} elif menu.lower() == "b" and usbx_props: break elif ":" in menu: try: key,value = menu.split(":") if key.isnumeric(): # Assume they want to update a number key = list(usbx_props)[int(key)-1] else: # Assume we're adding a new one - make sure it's just alpha chars key = "".join([x for x in key if x.isalpha()]) value = self.hexy(int(value,16),pad_to=4) assert len(value) == 6 # Ensure it's no larger than 16-bits usbx_props[key] = value except: pass elif menu.isnumeric(): # Assume it's a value to remove try: usbx_props.pop(list(usbx_props)[int(menu)-1],None) except: pass else: # Assume it's a value we're trying to remove usbx_props.pop(menu,None) # Now build! self.u.head("USBX Device") print("") print("Creating generic SSDT-USBX...") oc = {"Comment":"Generic USBX device for USB power properties","Enabled":True,"Path":"SSDT-USBX.aml"} self.make_plist(oc, "SSDT-USBX.aml", []) ssdt = """// Generic USBX Device with power properties injected // Edited from: // https://github.com/dortania/OpenCore-Post-Install/blob/master/extra-files/SSDT-USBX.aml DefinitionBlock ("", "SSDT", 2, "CORP", "SsdtUsbx", 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 () {""" for i,key in enumerate(usbx_props,start=1): ssdt += "\n \"{}\",".format(key) ssdt += "\n {}".format(usbx_props[key]) if i < len(usbx_props): ssdt += "," ssdt += """ }) } Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } } } } }""" self.write_ssdt("SSDT-USBX",ssdt) print("") print("Done.") self.patch_warn() self.u.grab("Press [enter] to return...") return def ssdt_xosi(self): if not self.ensure_dsdt(): return # Let's see what, if any, the highest version contained in the DSDT is highest_osi = None for x in self.osi_strings: if self.osi_strings[x] in self.d.get_dsdt_or_only()["table"]: highest_osi = x while True: lines = [""] pad = len(str(len(self.osi_strings))) for i,x in enumerate(self.osi_strings,start=1): lines.append("{}. {} ({})".format(str(i).rjust(pad),x,self.osi_strings[x])) if highest_osi: lines.append("") lines.append("A. Auto-Detected ({} - {})".format(highest_osi,self.osi_strings[highest_osi])) lines.append("") lines.append("M. Main") lines.append("Q. Quit") lines.append("") if self.resize_window: self.u.resize(self.w, max(len(lines)+4,self.h)) self.u.head("XOSI") print("\n".join(lines)) menu = self.u.grab("Please select the latest Windows version for SSDT-XOSI{}: ".format( " (default is A)" if highest_osi else "" )) if not len(menu): menu = "a" # Use the default if we passed nothing if menu.lower() == "m": return if menu.lower() == "q": if self.resize_window: self.u.resize(self.w,self.h) self.u.custom_quit() if menu.lower() == "a" and highest_osi: target_string = highest_osi break # Make sure we got a number - and it's within our range try: target_string = list(self.osi_strings)[int(menu)-1] except: continue # Got a valid option - break out and create the SSDT break if self.resize_window: self.u.resize(self.w,self.h) self.u.head("XOSI") print("") print("Creating SSDT-XOSI with support through {}...".format(target_string)) ssdt = """DefinitionBlock ("", "SSDT", 2, "CORP", "XOSI", 0x00001000) { Method (XOSI, 1, NotSerialized) { // Edited from: // https://github.com/dortania/Getting-Started-With-ACPI/blob/master/extra-files/decompiled/SSDT-XOSI.dsl // 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 () { """ # Iterate our OS versions, and stop once we've added the last supported for i,x in enumerate(self.osi_strings,start=1): osi_string = self.osi_strings[x] ssdt += " \"{}\"".format(osi_string) if x == target_string or i==len(self.osi_strings): # Last one - bail ssdt += " // "+x break ssdt += ", // "+x+"\n" # Add a comma and newline for the next value ssdt +=""" }, Local0) If (_OSI ("Darwin")) { Return (LNotEqual (Match (Local0, MEQ, Arg0, MTR, Zero, Zero), Ones)) } Else { Return (_OSI (Arg0)) } } }""" patches = [] print("Checking for OSID Method...") osid = self.d.get_method_paths("OSID") if osid: print(" - Located {} Method at offset {}".format(osid[0][0],osid[0][1])) print(" - Creating OSID to XSID rename...") patches.append({ "Comment":"OSID to XSID rename - must come before _OSI to XOSI rename!", "Find":"4F534944", "Replace":"58534944", "Table":None # Apply to all tables }) else: print(" - Not found, no OSID to XSID rename needed") print("Creating _OSI to XOSI rename...") patches.append({ "Comment":"_OSI to XOSI rename - requires SSDT-XOSI.aml", "Find":"5F4F5349", "Replace":"584F5349", "Table":None # Apply to all tables }) self.write_ssdt("SSDT-XOSI",ssdt) oc = {"Comment":"_OSI override to return true through {} - requires _OSI to XOSI rename".format(target_string),"Enabled":True,"Path":"SSDT-XOSI.aml"} self.make_plist(oc, "SSDT-XOSI.aml", patches, replace=True) print("") print("Done.") self.patch_warn() self.u.grab("Press [enter] to return...") return def get_address_from_line(self, line, split_by="_ADR, ", table=None): if table is None: table = self.d.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 hexy(self,integer,pad_to=0): return "0x"+hex(integer)[2:].upper().rjust(pad_to,"0") def get_bridge_devices(self, path): # Takes a Pci(x,x)/Pci(x,x) style path, and returns named bridges and addresses adrs = re.split(r"#|\/",path.lower().replace("pciroot(","").replace("pci(","").replace(")","")) # Walk the addresses and create our bridge objects bridges = [] for bridge in adrs: if not len(bridge): continue # Skip empty entries if not "," in bridge: return # Uh... we don't want to bridge the PciRoot - something's wrong. try: adr1,adr2 = [int(x,16) for x in bridge.split(",")] # Join the addresses as a 32-bit int adr_int = (adr1 << 16) + adr2 bridges.append(adr_int) except: return [] # Failed :( return bridges def sanitize_device_path(self, device_path): # Walk the device_path, gather the addresses, and rebuild it if not device_path.lower().startswith("pciroot("): # Not a device path - bail return # Strip out PciRoot() and Pci() - then split by separators adrs = re.split(r"#|\/",device_path.lower().replace("pciroot(","").replace("pci(","").replace(")","")) new_path = [] for i,adr in enumerate(adrs): if i == 0: # Check for roots if "," in adr: return # Broken try: new_path.append("PciRoot({})".format(self.hexy(int(adr,16)))) except: return # Broken again :( else: if "," in adr: # Not Windows formatted try: adr1,adr2 = [int(x,16) for x in adr.split(",")] except: return # REEEEEEEEEE else: try: adr = int(adr,16) adr2,adr1 = adr & 0xFF, adr >> 8 & 0xFF except: return # AAAUUUGGGHHHHHHHH # Should have adr1 and adr2 - let's add them new_path.append("Pci({},{})".format(self.hexy(adr1),self.hexy(adr2))) return "/".join(new_path) def get_longest_match(self, device_dict, match_path, adj=False, exclusions_list=None): matches = self.get_all_matches(device_dict,match_path,adj=adj,exclusions_list=exclusions_list) if not matches: return return sorted(matches,key=lambda x:x[-1],reverse=True)[0] def get_all_matches(self, device_dict, match_path, adj=False, exclusions_list=None): matched = None exact = False key = "adj_path" if adj else "path" matches = [] for d in device_dict: try: if any(d.startswith(x) for x in exclusions_list): # Skip excluded paths, and all child elements under # those paths. continue except: pass device = device_dict[d].get(key) if not device: continue if match_path.lower().startswith(device.lower()): matches.append((d,device_dict[d],device.lower()==match_path.lower(),len(device))) return matches def get_device_path(self): paths = {} acpi_exclusions = [] while True: self.u.head("Input Device Path") print("") print("Current Paths:") # Retain order to prevent any odd drifting sorted_paths = self.sorted_nicely(paths) paths_length = len(sorted_paths) if not paths: print(" - None") else: for i,x in enumerate(sorted_paths,start=1): if paths[x]: print("{}. {} {}".format( str(i).rjust(2),x,paths[x] )) else: print("{}. {}".format(str(i).rjust(2),x)) if acpi_exclusions: print("") print("ACPI Devices Excluded:") for i,x in enumerate(self.sorted_nicely(acpi_exclusions),start=1): print("{}. {}".format(str(i+paths_length).rjust(2),x)) print("") print("A valid device path will have one of the following formats,") print("optionally followed by a 4-digit device name:") print("") print("macOS: PciRoot(0x0)/Pci(0x0,0x0)/Pci(0x0,0x0)") print("Windows: PCIROOT(0)#PCI(0000)#PCI(0000)") print("") if paths: print("A. Accept Paths and Continue") print("C. Clear All Device Paths") if acpi_exclusions: print("X. Clear All ACPI Exclusions") print("M. Main") print("Q. Quit") print("") print("Enter the number next to a device/ACPI path above to remove it.") print("Enter an ACPI path to exclude it from the checks.") print("Drag and drop a config.plist to extract device paths from within.") print("") if self.copy_as_path: print("NOTE: Currently running as admin on Windows - drag and drop may not work.") print(" Shift + right-click in Explorer and select 'Copy as path' then paste here instead.") print("") path = self.u.grab("Please enter the device path needing bridges:\n\n") if path.lower() == "m": return elif path.lower() == "q": self.u.custom_quit() elif path.lower() == "a" and paths: return (paths, acpi_exclusions) elif path.lower() == "c": paths = {} continue elif path.lower() == "x" and acpi_exclusions: acpi_exclusions = [] continue # Check if it's a number first try: path_int = int(path) if path_int <= paths_length: # Removing device path del paths[sorted_paths[path_int-1]] else: # Removing ACPI path del acpi_exclusions[path_int-paths_length-1] continue except: pass # Check if it's an ACPI path acpi_dev = None if len(path) >= 3 and not " " in path: # Make sure we send at least 3 chars before # looking for ACPI paths acpi_dev = self.sanitize_acpi_path(path) if acpi_dev: # Got an ACPI path, make sure it exists acpi_path = ".".join(acpi_dev) # Search the tables for any matches - only look for actual # Device () definitions though, not Scope () sets as well. matched_devices = [] for table_name in self.sorted_nicely(list(self.d.acpi_tables)): table = self.d.acpi_tables[table_name] matched_devices += [(x,table_name) for x in self.d.get_device_paths(obj=acpi_path,table=table)] if matched_devices: # We have at least one matched device if len(matched_devices) > 1: # We have multiples - prompt for which to add bail = False while True: self.u.head("Multiple Matches") print("") print("There are {:,} matches for {}:".format(len(matched_devices),acpi_path)) print("") for i,m in enumerate(matched_devices,start=1): print("{}. {} ({})".format(str(i).rjust(2),m[0][0],m[1])) print("") print("M. Device Path Menu") print("Q. Quit") print("") d = self.u.grab("Please select the ACPI path to exclude: ") if d.lower() == "m": bail = True break elif d.lower() == "q": self.u.custom_quit() try: d = int(d) assert 0 < d <= len(matched_devices) except: continue # Got one - select it matched_devices = [matched_devices[d-1]] break if bail: # We wanted to return to the menu continue # We got a single matched device - add only its path # to the list. if not matched_devices[0][0][0] in acpi_exclusions: acpi_exclusions.append(matched_devices[0][0][0]) continue # Check if we got a file path file_path = self.u.check_path(path) if file_path and file_path.lower().endswith(".plist") and os.path.isfile(file_path): # Try loading it file_name = os.path.basename(file_path) self.u.head("Processing {}".format(file_name)) print("") print("Loading {}...".format(file_name)) try: with open(file_path,"rb") as f: passed_plist = plist.load(f) except Exception as e: print(" - Failed to open: {}".format(e)) print("") self.u.grab("Press [enter] to return...") continue print("Verifying root node type...") if not isinstance(passed_plist,dict): print(" - Invalid type - must be dictionary") print("") self.u.grab("Press [enter] to return...") continue print("Gathering device paths...") dp_keys = None try: # OpenCore pathing dp_keys = passed_plist.get("DeviceProperties",{}).get("Add",{}) except: pass if not dp_keys: try: # Clover pathing dp_keys = passed_plist.get("Devices",{}).get("Properties") except: pass if not dp_keys or not isinstance(dp_keys,dict): print(" - No device paths located.") print("") self.u.grab("Press [enter] to return...") continue print("Iterating {:,} device paths...".format(len(dp_keys))) any_failed = False for d in dp_keys: print(" - {}".format(d)) d_path = self.sanitize_device_path(d) if not d_path: print(" --> Invalid device path - skipping") any_failed = True continue if d_path in paths: print(" --> Already exists in device path list - skipping") continue # Add it paths[d_path] = None if any_failed: print("") self.u.grab("Press [enter] to return...") continue # Extract the path and device # if specified path_dev = path.split() dev = None if len(path_dev) == 1: path = path_dev[0] elif len(path_dev) == 2: path,dev = path_dev else: continue # Incorrect formatting # Make sure the device is valid if dev: dev = dev.replace("_","")[:4] if not dev.isalnum(): continue dev = dev.upper().ljust(4,"0") path = self.sanitize_device_path(path) if not path: continue # Add our path at the end paths[path] = dev def get_device_paths(self): print("Gathering ACPI devices...") device_dict = {} pci_root_paths = [] orphaned_devices = [] sanitized_paths = [] for table_name in self.sorted_nicely(list(self.d.acpi_tables)): table = self.d.acpi_tables[table_name] # Let's gather our roots - and any other paths that and in _ADR pci_roots = self.d.get_device_paths_with_id(_id="PNP0A08",table=table) pci_roots += self.d.get_device_paths_with_id(_id="PNP0A03",table=table) pci_roots += self.d.get_device_paths_with_id(_id="ACPI0016",table=table) paths = self.d.get_path_of_type(obj_type="Name",obj="_ADR",table=table) # Let's create our dictionary device paths - starting with the roots for path in pci_roots: if path[0] in device_dict: continue # Already have it device_uid = self.d.get_name_paths(obj=path[0]+"._UID",table=table) if device_uid and len(device_uid)==1: adr = self.get_address_from_line(device_uid[0][1],split_by="_UID, ",table=table) else: # Assume 0 adr = 0 device_dict[path[0]] = {"path":"PciRoot({})".format(self.hexy(adr))} pci_root_paths.append(device_dict[path[0]]) # First - let's create a new list of tuples with the ._ADR stripped # The goal here is to ensure pathing is listed in the proper order. sanitized_paths.extend([( x[0][0:-5], # The path minus the ._ADR/._UID x[1], # The line number x[2], # The type of the match (Name, Device, Method, etc) self.get_address_from_line(x[1],table=table) # Address ) for x in paths]) print("Generating device paths...") def check_path(path,device_dict): # Returns a bool depending on the checks # True = added, already added, ignore # False = orphaned adr = path[3] # Retain the address adr_overflow = False # Let's bitshift to get both addresses try: adr1,adr2 = adr >> 16 & 0xFFFF, adr & 0xFFFF radr1,radr2 = adr1,adr2 # Save placeholders in case we overflow if adr1 > 0xFF: # Overflowed adr_overflow = True radr1 = 0 if adr2 > 0xFF: # Overflowed adr_overflow = True radr2 = 0 except: return True # Bad address? # Let's check if our path already exists if path[0] in device_dict: return True # Skip # Doesn't exist - let's see if the parent path does? parent = ".".join(path[0].split(".")[:-1]) parent_device = device_dict.get(parent) if not parent_device or not parent_device.get("path"): # No parent either - let's keep track of the device # as an orphan - and check it at the end return False # Our parent path exists - let's copy its device_path, and append our addressing device_path = parent_device["path"] device_path += "/Pci({},{})".format(self.hexy(adr1),self.hexy(adr2)) device_dict[path[0]] = {"path":device_path} # Check if either we, or our parent has an adr overflow if adr_overflow or parent_device.get("adr_overflow"): device_dict[path[0]]["adr_overflow"] = True parent_path = parent_device.get("adj_path",parent_device["path"]) device_dict[path[0]]["adj_path"] = parent_path + "/Pci({},{})".format(self.hexy(radr1),self.hexy(radr2)) if adr_overflow: # It was us, not a parent dev_overflow = device_dict[path[0]].get("dev_overflow",[]) dev_overflow.append(path[0]) device_dict[path[0]]["dev_overflow"] = dev_overflow return True for path in sorted(sanitized_paths): if not check_path(path,device_dict): orphaned_devices.append(path) if orphaned_devices: print("Rechecking orphaned devices...") while True: removed = [] for path in orphaned_devices: if check_path(path,device_dict): removed.append(path) if not removed: break for r in removed: try: orphaned_devices.remove(r) except ValueError: pass return (device_dict,pci_root_paths) def print_unmatched(self, unmatched=None, pci_root_paths=None): print("") if unmatched: print("{}!! WARNING !!{} No matches were found for the following paths:".format(self.yel,self.rst)) print("\n".join([" {}".format(x) for x in sorted(unmatched)])) else: print("{}!! WARNING !!{} No matches found!".format(self.yel,self.rst)) if pci_root_paths: print("\n{}!! WARNING !!{} Device paths must start with one of the following PciRoot()".format(self.yel,self.rst)) print(" options to match the current ACPI tables:") print("\n".join([" {}".format(x.get("path",x)) for x in sorted(pci_root_paths)])) def print_address_overflow(self, addr_overflow): print("") print("{}!! WARNING !!{} There are _ADR overflows in the device path!".format(self.red,self.rst)) print(" The following devices may need adjustments for bridges to work:") # Ensure they're all unique, and we sort them as we print for d in sorted(list(set(addr_overflow))): print(" {}".format(d)) def print_failed_bridges(self, failed_bridges): print("\n{}!! WARNING !!{} The following bridges failed to resolve:".format(self.yel,self.rst)) print("\n".join([" {}".format(x) for x in sorted(failed_bridges)])) def pci_bridge(self): if not self.ensure_dsdt(): return path_dict = self.get_device_path() if not path_dict: return # Break out the paths from any acpi exclusions path_dict,acpi_exclusions = path_dict self.u.head("Building Bridges") print("") device_dict,pci_root_paths = self.get_device_paths() matches = [] unmatched = [] print("Matching device paths...") for p in sorted(path_dict): print(" - {}".format(p)) match = self.get_longest_match(device_dict,p,exclusions_list=acpi_exclusions) if not match: print(" --> No match found!") unmatched.append(p) else: # We got a match - check if we need to list bridges needed if match[2]: print(" --> Matched {} - no bridge needed".format(match[0])) else: b = p[match[-1]+1:].count("/")+1 print(" --> Matched {} - {:,} bridge{} needed".format( match[0], b, "" if b==1 else "s" )) matches.append((p,match)) # See if we have any addresses that overflow over_match = self.get_longest_match(device_dict,p,adj=True) if over_match: print("\n{}!! WARNING !!{} Also matched the following devices whose addresses overflow which".format(self.yel,self.rst)) print(" may prevent bridges and DeviceProperties from working correctly:") print("\n".join([" {}".format(x) for x in sorted(over_match[1].get("dev_overflow",[over_match[0]]))])) if not matches: self.print_unmatched(unmatched=unmatched,pci_root_paths=pci_root_paths) print("") print("No matches found!") print("") self.u.grab("Press [enter] to return...") return # Check for, and warn about address overflows addr_overflow = [] for test_path,match in matches: if match[1].get("adr_overflow"): # Get all matches and list the devices whose addresses overflow over_flow = self.get_all_matches(device_dict,match[1]["path"]) for d in over_flow: if d[1].get("dev_overflow"): addr_overflow.extend(d[1]["dev_overflow"]) # Make sure we have something to display - at least if all(match[1][2] for match in matches): if unmatched: self.print_unmatched(unmatched=unmatched,pci_root_paths=pci_root_paths) if addr_overflow: self.print_address_overflow(addr_overflow) print("") print("No bridges needed!") print("") self.u.grab("Press [enter] to return...") return starting_at = 0 print("") print("Resolving bridges...") bridge_match = {} bridge_list = [] failed_bridges = [] external_refs = [] for test_path,match in matches: if match[2]: continue # Skip full matches remain = test_path[match[-1]+1:] print(" - {}".format(remain)) bridges = self.get_bridge_devices(remain) if not bridges: print(" --> Could not resolve!") failed_bridges.append(test_path) else: # Join the elements separated by a space to # make parsing easier path = match[0] for i,b in enumerate(bridges,start=1): path += " " + str(b) if not path in bridge_list: bridge_list.append(path) # Retain the final path for comments later if i == len(bridges): bridge_match[path] = test_path # Retain the ACPI path for the SSDT if not match[0] in external_refs: external_refs.append(match[0]) # Make sure we have something in order to continue if not bridge_list: if failed_bridges: self.print_failed_bridges(failed_bridges) if unmatched: self.print_unmatched(unmatched=unmatched,pci_root_paths=pci_root_paths) if addr_overflow: self.print_address_overflow(addr_overflow) print("") print("Something went wrong resolving bridges!") print("") self.u.grab("Press [enter] to return...") return print("") print("Creating SSDT-Bridge...") # First - we need to define our header and external references ssdt = """// Source and info from: // https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/AcpiSamples/Source/SSDT-BRG0.dsl DefinitionBlock ("", "SSDT", 2, "CORP", "PCIBRG", 0x00000000) { /* * Start copying here if you're adding this info to an existing SSDT-Bridge! */ """ for acpi in external_refs: # Walk our external refs and define them at the top ssdt += " External ({}, DeviceObj)\n".format(acpi) ssdt += "\n" def close_brackets(ssdt,depth,iterations=1,pad=" "): # Helper to close brackets based on depth and # iteration count while iterations > 0: ssdt += (pad*depth)+"}\n" iterations -= 1 depth -= 1 return ssdt # Walk the bridge list and define elements as we go last_path = [] pad = " " acpi = None bridge_names = {} acpi_paths = {} # Sorting ensures our hierarchy should remain intact # which vastly simplifies the work we have to do for element in sorted(bridge_list): # Split our element into path components comp = element.split() _acpi = comp[0] # Find our longest match with the last path checked _match = 0 for i in range(min(len(comp),len(last_path))): if comp[i] != last_path[i]: break _match += 1 # Close any open brackets that weren't matched if last_path: ssdt = close_brackets(ssdt,len(last_path),len(last_path)-_match) # Retain the last path last_path = comp if _acpi != acpi: # Set a new scope if we found a different # ACPI path acpi = _acpi ssdt += pad+"Scope ({})\n".format(acpi) ssdt += pad+"{\n" curr_depth = len(comp) if curr_depth == 0: continue # top level.. somehow? Skip. # Got a new device - pad and define parent_path = " ".join(comp[:-1]) # Get our bridge name by keeping track of # the number of bridges per parent path if not parent_path in bridge_names: bridge_names[parent_path] = [] # Generate a name from the bridge number parent_acpi = acpi_paths.get(parent_path,_acpi) brg_basename = path_dict.get(bridge_match.get(element)) name,num = self.get_unique_device( parent_acpi, brg_basename or "BRG0", starting_number=-1, # Try the original name first used_names=bridge_names[parent_path] ) # Add our path to the dict to increment the next count bridge_names[parent_path].append(name) # Set our acpi path for any child elements acpi_paths[element] = parent_acpi+"."+name # Get our padding p = pad*(curr_depth) # If this is a final bridge - add note about customization if element in bridge_match: if brg_basename: # We got a custom bridge name if brg_basename != name: # It was overridden - make a note ssdt += p+"// User-provided name '{}' supplied, incremented for uniqueness\n".format(brg_basename) else: # It was used as provided ssdt += p+"// User-provided name '{}' supplied\n".format(brg_basename) else: # Just leave a note about potentially customizing the name ssdt += p+"// Customize the following device name if needed, eg. GFX0\n" # Set up our device definition ssdt += p+"Device ({})\n".format(name) ssdt += p+"{\n" # Increase our padding p += pad if element in bridge_match: # Add our comment ssdt += "{0}// Target Device Path:\n{0}// {1}\n".format( p, bridge_match[element] ) # Format the address: 0 = Zero, 1 = One, # others are padded to 8 hex digits if # > 0xFFFF adr_int = int(comp[-1]) adr = { 0:"Zero", 1:"One" }.get( adr_int, "0x"+hex(adr_int).upper()[2:].rjust( 8 if adr_int > 0xFFFF else 0, "0" ) ) ssdt += "{}Name (_ADR, {})\n".format(p,adr) # We finished parsing - clean up after ourselves if last_path: last_depth = len(last_path) # Close any missing elements ssdt = close_brackets(ssdt,last_depth,last_depth) # Close the final bracket ssdt += """ /* * End copying here if you're adding this info to an existing SSDT-Bridge! */ } """ self.write_ssdt("SSDT-Bridge",ssdt) oc = {"Comment":"Defines missing PCI bridges for property injection","Enabled":True,"Path":"SSDT-Bridge.aml"} self.make_plist(oc, "SSDT-Bridge.aml", ()) print("") print("Done.") if failed_bridges: self.print_failed_bridges(failed_bridges) if unmatched: self.print_unmatched(unmatched=unmatched,pci_root_paths=pci_root_paths) if addr_overflow: self.print_address_overflow(addr_overflow) self.patch_warn() self.u.grab("Press [enter] to return...") return def sanitize_acpi_path(self, path): # Takes an ACPI path either using ACPI()#ACPI() or # PATH.PATH notation and breaks it into a list of # elements without the leading backslash, and without # the trailing underscore pads path = path.replace("ACPI(","").replace(")","").replace("#",".").replace("\\","") new_path = [] valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" for element in path.split("."): element = element.rstrip("_").upper() if len(element) > 4 or not all(x in valid for x in element): # Invalid element, return None return None new_path.append(element) return new_path def compare_acpi_paths(self, path, path_list): path_check = self.sanitize_acpi_path(path) if not path_check: return False # Invalid path if not len(path_list) == len(path_check): return False # Same length - check the elements return all((path_list[i] == path_check[i] for i in range(len(path_list)))) def get_acpi_path(self): while True: self.u.head("Input ACPI Path") print("") print("A valid ACPI path will have one of the following formats:") print("") print("macOS: \\_SB.PCI0.XHC.RHUB") print("Windows: ACPI(_SB_)#ACPI(PCI0)#ACPI(XHC_)#ACPI(RHUB)") print("") print("M. Main") print("Q. Quit") print(" ") path = self.u.grab("Please enter the ACPI path:\n\n") if path.lower() == "m": return if path.lower() == "q": self.u.custom_quit() path = self.sanitize_acpi_path(path) if not path: continue return path def print_acpi_path(self, path): # Takes a list of ACPI path elements, and formats them # into a single path. Will ensure the first element starts # with \ return ".".join([("\\" if i==0 else"")+x.lstrip("\\").rstrip("_") for i,x in enumerate(path)]) def acpi_device_path(self): if not self.ensure_dsdt(): return test_path = self.get_acpi_path() if not test_path: return print_path = self.print_acpi_path(test_path) self.u.head("ACPI -> Device Path") print("") device_dict,_ = self.get_device_paths() print("Matching against {}".format(print_path)) p = next( (x for x in device_dict if self.compare_acpi_paths(x,test_path)), None ) if not p: print(" - Not found!") print("") self.u.grab("Press [enter] to return...") return print(" - Matched: {}".format(device_dict[p]["path"])) if device_dict[p].get("adr_overflow"): # Get all matches and list the devices whose addresses overflow over_flow = self.get_all_matches(device_dict,device_dict[p]["path"]) devs = [] for d in over_flow: if d[1].get("dev_overflow"): devs.extend(d[1]["dev_overflow"]) # Make sure we have something to display - at least if devs: print("\n{}!! WARNING !!{} There are _ADR overflows in the device path!".format(self.red,self.rst)) print(" - The following devices may affect property injection:") # Ensure they're all unique, and we sort them as we print for d in sorted(list(set(devs))): print(" --> {}".format(d)) print("") self.u.grab("Press [enter] to return...") return def ssdt_pnlf(self): if not self.ensure_dsdt(allow_any=True): return # Let's get our _UID while True: self.u.head("Select _UID for PNLF") print("") print("_UID | Supported Platform(s) | PWMMax") print("-----------------------------------------------") print(" 14 | Arrandale, Sandy/Ivy Bridge | 0x0710") print(" 15 | Haswell/Broadwell | 0x0AD9") print(" 16 | Skylake/Kaby Lake, some Haswell | 0x056C") print(" 17 | Custom LMAX | 0x07A1") print(" 18 | Custom LMAX | 0x1499") print(" 19 | CoffeeLake and newer (or AMD) | 0xFFFF") print(" 99 | Other (requires custom applbkl-name/applbkl-data dev props)") print("") print("The _UID tells WhateverGreen what backlight data to use.") print("More info can be found in WEG's kern_weg.cpp here under appleBacklightData") print("") print("M. Main Menu") print("Q. Quit") print("") menu = self.u.grab("Please select the target _UID value: ") if menu.lower() == "m": return elif menu.lower() == "q": self.u.custom_quit() try: uid = int(menu) except: continue if not uid in (14,15,16,17,18,19): while True: self.u.head("Custom _UID for PNLF") print("") print("{} is a custom _UID which may require customization to setup,".format(uid)) print("or not have support at all.") print("") print("M. Return to Main Menu") print("Q. Quit") print("") menu = self.u.grab("Are you sure you want to use it? (y/n): ") if menu.lower() == "q": self.u.custom_quit() elif menu.lower() == "m": return if not menu.lower() in ("y","n"): continue break if menu.lower() == "n": continue break get_igpu = False igpu = "" guessed = manual = False if uid == 14: while True: self.u.head("Arrandale/SNB/IVB _UID") print("") print("Some machines using _UID 14 have problems with max brightness or") print("other issues. In order to fix these - the iGPU device path must") print("be discovered and some GPU registers need to be set.") print("") print("{}!! WARNING !!{} It is recommended to try WITHOUT this first!!".format(self.yel,self.rst)) print("") print("M. Return to Main Menu") print("Q. Quit") print("") gpu_reg = self.u.grab("Would you like to include GPU register code? (y/n): ") if gpu_reg.lower() == "q": self.u.custom_quit() elif gpu_reg.lower() == "m": return elif gpu_reg.lower() == "y": get_igpu = True break elif gpu_reg.lower() == "n": break # Leave the loop self.u.head("Generating PNLF") print("") print("Creating SSDT-PNLF...") print(" - _UID: {}".format(uid)) # Check if we are building the SSDT with a _UID of 14 if get_igpu: print(" - Setting PWMMax calculations") print("Looking for iGPU device at 0x00020000...") for table_name in self.sorted_nicely(list(self.d.acpi_tables)): table = self.d.acpi_tables[table_name] print(" Checking {}...".format(table_name)) # Try to gather our iGPU device paths = self.d.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 == 0x00020000: igpu = path[0][:-5] print(" - Found at {}".format(igpu)) break if igpu: break # Leave the table search loop if not igpu: # Try matching by name print("Not found by address!") print("Searching common iGPU names...") for table_name in self.sorted_nicely(list(self.d.acpi_tables)): table = self.d.acpi_tables[table_name] print(" Checking {}...".format(table_name)) pci_roots = self.d.get_device_paths_with_id(_id="PNP0A08",table=table) pci_roots += self.d.get_device_paths_with_id(_id="PNP0A03",table=table) pci_roots += self.d.get_device_paths_with_id(_id="ACPI0016",table=table) external = [] for line in table["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.d.get_device_paths(test_path,table=table) 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.d.get_path_of_type(obj_type="Name",obj=device+"._ADR",table=table): continue # At this point - we got a hit igpu = device guessed = True print(" - Found likely iGPU device at {}".format(igpu)) if igpu: break # Leave the table search loop if get_igpu and (not igpu or guessed): # We need to prompt the user based on what we have if igpu: while True: self.u.head("iGPU Path") print("") print("Found likely iGPU at {}".format(igpu)) print("") print("M. Return to Main Menu") print("Q. Quit") print("") manual_igpu = self.u.grab("Would you like to use this path? (y/n): ") if manual_igpu.lower() == "q": self.u.custom_quit() elif manual_igpu.lower() == "m": return elif manual_igpu.lower() == "y": break elif manual_igpu.lower() == "n": igpu = "" break # Leave the loop if not igpu: while True: self.u.head("Custom iGPU Path") print("") if not guessed: print("No valid iGPU path was found in the passed ACPI table(s).\n") print("Please type the iGPU ACPI path to use. Each path element is limited") print("to 4 alphanumeric characters (starting with a letter or underscore),") print("and separated by spaces.") print("") print("e.g. _SB_.PCI0.GFX0") print("") print("M. Return to Main Menu") print("Q. Quit") print("") manual_igpu = self.u.grab("Please type the iGPU path to use: ") if manual_igpu.lower() == "q": self.u.custom_quit() elif manual_igpu.lower() == "m": return else: # Maybe got a path - qualify it parts = manual_igpu.lstrip("\\").upper().split(".") # Make sure it's between 1 and 4 chars long, and doesn't start with a number valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" nostart = "0123456789" if any(not 0> 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 += """ } }""" # Perform the replacements ssdt = ssdt.replace("[[uid_value]]",self.hexy(uid)).replace("[[uid_dec]]",str(uid)).replace("[[igpu_path]]",igpu) self.write_ssdt("SSDT-PNLF",ssdt) oc = { "Comment":"Defines PNLF device with a _UID of {} for backlight control{}".format( uid, " - requires PNLF to XNLF rename" if any("XNLF" in p["Comment"] for p in patches) else "" ), "Enabled":True, "Path":"SSDT-PNLF.aml" } self.make_plist(oc, "SSDT-PNLF.aml", patches, replace=True) if igpu: if guessed: print("\n{}!! WARNING !!{} iGPU path was guessed to be {}\n !!VERIFY BEFORE USING!!".format(self.red,self.rst,igpu)) if manual: print("\n{}!! WARNING !!{} iGPU path was manually set to {}\n !!VERIFY BEFORE USING!!".format(self.red,self.rst,igpu)) if has_nbcf_old or has_nbcf_new: print("\n{}!! WARNING !!{} NBCF patch was generated - VERIFY BEFORE ENABLING!!".format(self.red,self.rst)) print("") print("Done.") self.patch_warn() self.u.grab("Press [enter] to return...") return def fix_dmar(self): dmar = next((table for table in self.d.acpi_tables.values() if table.get("signature") == b"DMAR"),None) if not dmar: d = None while True: self.u.head("Select DMAR Table") print(" ") if self.copy_as_path: print("NOTE: Currently running as admin on Windows - drag and drop may not work.") print(" Shift + right-click in Explorer and select 'Copy as path' then paste here instead.") print("") print("M. Main") print("Q. Quit") print(" ") dmar = self.u.grab("Please drag and drop a DMAR table here: ") if dmar.lower() == "m": return if dmar.lower() == "q": self.u.custom_quit() out = self.u.check_path(dmar) if not out: continue self.u.head("Loading DMAR Table") print("") print("Loading {}...".format(os.path.basename(out))) if d is None: d = dsdt.DSDT() # Initialize a new instance just for this # Got a DMAR table, try to load it d.load(out) dmar = d.get_table_with_signature("DMAR") if not dmar: continue break self.u.head("Patching DMAR") print("") print("Verifying signature...") reserved = got_sig = False new_dmar = ["// DMAR table with Reserved Memory Regions stripped\n"] region_count = 0 for line in dmar.get("lines",[]): if 'Signature : "DMAR"' in line: got_sig = True print("Checking for Reserved Memory Regions...") if not got_sig: continue # Skip until we find the signature # If we find a reserved memory region, toggle our indicator if "Subtable Type : 0001 [Reserved Memory Region]" in line: region_count += 1 reserved = True # Check for a non-reserved memory region subtable type elif "Subtable Type : " in line: reserved = False elif 'Oem ID : "' in line: # Got the OEM - replace with CORP line = line.split('"')[0] + '"CORP"' elif 'Oem Table ID : "' in line: # Got the OEM Table ID - replace with DMAR line = line.split('"')[0] + '"DMAR"' # Only append if we're not in a reserved memory region if not reserved: # Ensure any digits in Reserved : XX fields are 0s if "Reserved : " in line: res,value = line.split(" : ") new_val = "" for i,char in enumerate(value): if not char in " 0123456789ABCDEF": # Hit something else - dump the rest as-is into the val new_val += value[i:] break elif char not in ("0"," "): # Ensure we 0 out all non-0, non-space values char = "0" # Append the character new_val += char line = "{} : {}".format(res,new_val) new_dmar.append(line) if not got_sig: print(" - Not found, does not appear to be a valid DMAR table.") print("") self.u.grab("Press [enter] to return...") return # Give the user some feedback if not region_count: # None found print("No Reserved Memory Regions found - DMAR does not need patching.") print("") self.u.grab("Press [enter] to return to main menu...") return # We removed some regions print("Located {:,} Reserved Memory Region{} - generating new table...".format(region_count,"" if region_count==1 else "s")) self.write_ssdt("DMAR","\n".join(new_dmar).strip()) oc = { "Comment":"Replacement DMAR table with Reserved Memory Regions stripped - requires DMAR table be dropped", "Enabled":True, "Path":"DMAR.aml" } drop = ({ "Comment":"Drop DMAR Table", "Table":dmar, "Signature":dmar.get("signature",b"DMAR") },) self.make_plist(oc, "DMAR.aml", (), drops=drop) print("") print("Done.") self.patch_warn() self.u.grab("Press [enter] to return...") return def get_dev_at_adr(self,target_adr=0x001F0004,exclude_names=("XHC",)): # Helper to walk tables looking for device + parent at a # provided address for table_name in self.sorted_nicely(list(self.d.acpi_tables)): table = self.d.acpi_tables[table_name] paths = self.d.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) # Check by address # - Intel tables seem to have it at 0x001F0004 # - AMD tables seem to have it at 0x00140000 # Though this matches Intel chipset USB 3 controllers # so we'll need to also check names and such. if adr == target_adr: # Ensure our path minus ._ADR is not top level, that we # didn't match any devices with "XHC" in their name, and # then return the path + parent path + table name path_parts = path[0].split(".")[:-1] if len(path_parts) > 1: # Make sure we account for any excluded names if exclude_names is None or not \ any(x.lower() in path_parts[-1].lower() for x in exclude_names): _path = ".".join(path_parts) _parent = ".".join(path_parts[:-1]) return (_path,_parent,table_name) def smbus(self): if not self.ensure_dsdt(): return self.u.head("SMBus") print("") print("Gathering potential bus devices...") bus_path = bus_parent = None # It seems modern Intel uses 0x001F0004 for the SBus # Legacy Intel uses 0x001F0003 though - which lines up # with modern Intel HDEF/HDAS devices. # AMD uses 0x00140000 - which lines up with Intel XHCI # controllers. We'll have to try to narrow down which # is valid. # # Get our devices at the potential addresses dev_1F4 = self.get_dev_at_adr(0x001F0004) dev_1F3 = self.get_dev_at_adr(0x001F0003,exclude_names=("AZAL","HDEF","HDAS")) dev_1B = self.get_dev_at_adr(0x001B0000) dev_14 = self.get_dev_at_adr(0x00140000) # Initialize our bus_check var bus_check = adr = None # Iterate our checks in order if dev_1F4 and dev_1F3: # We got the newer Intel approach bus_check = dev_1F4 adr = 0x001F0004 elif dev_1F3 and dev_1B: # We got the older Intel approach bus_check = dev_1F3 adr = 0x001F0003 elif dev_1F4: # *Likely* newer Intel approach bus_check = dev_1F4 adr = 0x001F0004 elif dev_1F3: # *Likely* older Intel approach bus_check = dev_1F3 adr = 0x001F0003 elif dev_14: # Neither of the Intel approaches, # *likely* AMD bus_check = dev_14 adr = 0x00140000 if not bus_check: # Never found it - report the error and bail print(" - Could not locate a valid bus device! Aborting.") print("") self.u.grab("Press [enter] to return to main menu...") return # Break out our vars bus_path,bus_parent,table_name = bus_check print(" - Located {} (0x{}) in {}".format( bus_path, hex(adr)[2:].upper().rjust(8,"0"), table_name) ) print("Creating SSDT-SBUS-MCHC...") # At this point - we have both paths, let's build our table ssdt = """/* * SMBus compatibility table. * Original from: https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/AcpiSamples/Source/SSDT-SBUS-MCHC.dsl */ DefinitionBlock ("", "SSDT", 2, "CORP", "SBUSMCHC", 0x00000000) { External ([[bus_parent]], DeviceObj) External ([[bus_parent]].MCHC, DeviceObj) External ([[bus_path]], DeviceObj) // Only create MCHC if it doesn't already exist If (LNot (CondRefOf ([[bus_parent]].MCHC))) { Scope ([[bus_parent]]) { Device (MCHC) { Name (_ADR, Zero) // _ADR: Address Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } } } } } Device ([[bus_path]].BUS0) { Name (_CID, "smbus") // _CID: Compatible ID Name (_ADR, Zero) // _ADR: Address /* * Uncomment replacing 0x57 with your own value which might be found * in SMBus section of Intel datasheet for your motherboard. * * The "diagsvault" is the diagnostic vault where messages are stored. * It's located at address 87 (0x57) on the SMBus controller. * While "diagsvault" may refer to diags, a hardware diagnosis program via EFI for Macs * that communicates with the SMBus controller, the effect is really unknown for hacks. * Uncomment this with caution. */ /** Device (DVL0) { Name (_ADR, 0x57) // _ADR: Address Name (_CID, "diagsvault") // _CID: Compatible ID Method (_DSM, 4, NotSerialized) // _DSM: Device-Specific Method { If (!Arg2) { Return (Buffer (One) { 0x57 // W }) } Return (Package (0x02) { "address", 0x57 }) } } **/ Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { Return (0x0F) } Else { Return (Zero) } } } }""".replace("[[bus_parent]]",bus_parent).replace("[[bus_path]]",bus_path) oc = { "Comment":"Defines an MCHC and BUS0 device for SMBus compatibility", "Enabled":True, "Path":"SSDT-SBUS-MCHC.aml" } self.write_ssdt("SSDT-SBUS-MCHC",ssdt) self.make_plist(oc, "SSDT-SBUS-MCHC.aml", ()) print("") print("Done.") self.patch_warn() self.u.grab("Press [enter] to return...") return def ambient_light_sensor(self): if not self.ensure_dsdt(): return self.u.head("Ambient Light Sensor") print("") print("Locating ACPI0008 (ALS) devices...") for table_name in self.sorted_nicely(list(self.d.acpi_tables)): table = self.d.acpi_tables[table_name] print(" Checking {}...".format(table_name)) # Try to find any ambient light sensor devices in the # current table als = self.d.get_device_paths_with_hid("ACPI0008",table=table) if als: print(" - Found at {}".format(als[0][0])) print(" --> No fake needed!") # Check for an _STA var, and override it if need be sta = self.get_sta_var( var=None, device=als[0][0], dev_hid="ACPI0008", dev_name=als[0][0].split(".")[-1], log_locate=False, table=table ) if sta.get("patches"): if self.sta_needs_patching(sta, table=table): # We need to write a quick SSDT to force enable our # light sensor print("Creating SSDT-ALS0...") ssdt = """ DefinitionBlock ("", "SSDT", 2, "CORP", "ALS0", 0x00000000) { External ([[als0_path]], DeviceObj) External ([[als0_path]]._STA, [[sta_type]]) External ([[als0_path]].XSTA, [[sta_type]]) If (LAnd (CondRefOf ([[als0_path]].XSTA), LNot (CondRefOf ([[als0_path]]._STA)))) { Scope ([[als0_path]]) { // Override our original Light Sensor _STA method Method (_STA, 0, NotSerialized) // _STA: Status { If (_OSI ("Darwin")) { // Explicitly enable the Light Sensor in macOS Return (0x0F) } Else { // Call the original, now renamed _STA method // if we're not booting macOS Return ([[XSTA]]) } } } } }""".replace("[[als0_path]]",als[0][0]) \ .replace("[[sta_type]]",sta.get("sta_type","MethodObj")) \ .replace("[[XSTA]]","{}.XSTA{}".format(als[0][0]," ()" if sta.get("sta_type","MethodObj")=="MethodObj" else "")) oc = { "Comment":"Enables {} in macOS - requires _STA to XSTA rename".format(sta["dev_name"]), "Enabled":True, "Path":"SSDT-ALS0.aml" } self.write_ssdt("SSDT-ALS0",ssdt) self.make_plist(oc,"SSDT-ALS0.aml",sta.get("patches",[])) print("") print("Done.") self.patch_warn() self.u.grab("Press [enter] to return...") return else: print(" --> _STA properly enabled - no patching needed!") else: print(" --> Not found - no patching needed!") print("") self.u.grab("Press [enter] to return to main menu...") return # If we got here - we didn't find any print("No ACPI0008 (ALS) devices found - fake needed...") print("Creating SSDT-ALS0...") ssdt = """// // Original source from: // https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/AcpiSamples/Source/SSDT-ALS0.dsl // DefinitionBlock ("", "SSDT", 2, "CORP", "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) } } } } }""" oc = { "Comment":"Faked Ambient Light Sensor", "Enabled":True, "Path":"SSDT-ALS0.aml" } self.write_ssdt("SSDT-ALS0",ssdt) self.make_plist(oc,"SSDT-ALS0.aml",()) print("") print("Done.") self.patch_warn() self.u.grab("Press [enter] to return...") return def imei_bridge(self): if not self.ensure_dsdt(): return def fake_note(): print("\n{}NOTE:{} Ensure you fake the IMEI device-id as applicable in DeviceProperties:".format(self.yel,self.rst)) print("") print(" If you have a Sandy Bridge CPU with a 7-series chipset:") print(" - device-id | Data | <3A1C0000>") print("") print(" If you have an Ivy Bridge CPU with a 6-series chipset:") print(" - device-id | Data | <3A1E0000>") print("") def print_line(line,lines=[]): print(line) lines.append(line) return lines self.u.head("IMEI Bridge") lines = print_line("") lines = print_line("Locating IMEI devices at address 0x00160000...",lines) imei = self.get_dev_at_adr(0x00160000) if imei: print(" - Located at {}".format( imei[0] )) print(" --> No bridge needed!") fake_note() self.u.grab("Press [enter] to return to main menu...") return # We didn't find it lines = print_line(" - Not located - bridge needed",lines) lines = print_line("Checking for parent device...") lines = print_line(" - Locating iGPU device at address 0x00020000...",lines) parent = None igpu = self.get_dev_at_adr(0x00020000) if not igpu: lines = print_line(" --> Not located!",lines) lines = print_line(" - Attempting to locate PCI Roots...",lines) pci_roots = [] for table_name in self.sorted_nicely(list(self.d.acpi_tables)): table = self.d.acpi_tables[table_name] pci_roots = self.d.get_device_paths_with_id(_id="PNP0A08",table=table) pci_roots += self.d.get_device_paths_with_id(_id="PNP0A03",table=table) pci_roots += self.d.get_device_paths_with_id(_id="ACPI0016",table=table) if pci_roots: break # Bail on the first match if not pci_roots: print(" --> None found! Cannot continue.") print("") self.u.grab("Press [enter] to reeturn to main menu...") return parent = pci_roots[0][0] lines = print_line(" --> Located at {}".format(parent),lines) else: lines = print_line(" --> Located at {}".format(igpu[0]),lines) parent = ".".join(igpu[0].split(".")[:-1]) lines = print_line(" --> Using parent: {}".format(parent),lines) lines = print_line("Gathering device-id approach...") # Ask the user what approach they're using approach = None while True: self.u.head("Fake Device-ID") print("") print("Select your current CPU and chipset configuration:") print("") print("1. Sandy Bridge CPU with 7-series chipset") print("2. Ivy Bridge CPU with 6-series chipset") print("3. Do not fake device-id in SSDT (requires DeviceProperties)") print("") print("M. Main Menu") print("Q. Quit") print("") m = self.u.grab("Please select an option: ") if not m: continue if m.lower() == "m": return if m.lower() == "q": self.u.custom_quit() if m not in ("1","2","3"): continue approach = {"1":1,"2":2}.get(m) break # Restore the lines up to this point self.u.head("IMEI Bridge") print("\n".join(lines)) if approach is None: print(" - Only building bridge, must fake using DeviceProperties!") elif approach == 1: print(" - Faking IMEI as 6-series to match Sandy Bridge CPU") else: print(" - Faking IMEI as 7-series to match Ivy Bridge CPU") print("Creating SSDT-IMEI...") ssdt = """// // Original source from: // https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/AcpiSamples/Source/SSDT-IMEI.dsl // DefinitionBlock ("", "SSDT", 2, "CORP", "IMEI", 0x00000000) { External ([[parent]], DeviceObj) Scope ([[parent]]) { Device (IMEI) { Name (_ADR, 0x00160000) // _ADR: Address""".replace("[[parent]]",parent) if approach is not None: # We're providing the fake in-line ssdt += """ Method (_DSM, 4, NotSerialized) { If (LEqual (Arg2, Zero)) { Return (Buffer (One) { 0x03 }) } Return (Package (0x02) { "device-id", Buffer (0x04) { 0x3A, 0x1[[fake]], 0x00, 0x00 } }) }""".replace("[[fake]]","C" if approach == 1 else "E") ssdt += """ } } } """ oc = { "Comment":"IMEI Bridge to Allow Faking for Exotic CPU+Mobo Configurations", "Enabled":True, "Path":"SSDT-IMEI.aml" } self.write_ssdt("SSDT-IMEI",ssdt) self.make_plist(oc,"SSDT-IMEI.aml",()) if approach is None: fake_note() else: print("") print("Done.") self.patch_warn() self.u.grab("Press [enter] to return...") return def pick_match_mode(self): while True: self.u.head("Select OpenCore Match Mode") print("") print("1. {}:".format(self.match_dict[0])) print(" - Signature/Table ID Matching: {} ANY {}".format(self.red,self.rst)) print(" - Table Length Matching: {} ANY {}".format(self.red,self.rst)) print("2. {}:".format(self.match_dict[1])) print(" - Signature/Table ID Matching: {} ANY {}".format(self.red,self.rst)) print(" - Table Length Matching: {}STRICT{}".format(self.grn,self.rst)) print("3. {}:".format(self.match_dict[2])) print(" !! Requires NormalizeHeaders quirk is {}DISABLED{} in config.plist !!".format(self.red,self.rst)) print(" - Signature/Table ID Matching: {}STRICT{}".format(self.grn,self.rst)) print(" - Table Length Matching: {}STRICT{}".format(self.grn,self.rst)) print("4. {}:".format(self.match_dict[3])) print(" !! Requires NormalizeHeaders quirk is {}ENABLED{} in config.plist !!".format(self.grn,self.rst)) print(" - Signature/Table ID Matching: {}STRICT{}".format(self.grn,self.rst)) print(" - Table Length Matching: {}STRICT{}".format(self.grn,self.rst)) print("") print("Current Match Mode: {}".format(self.match_dict.get(self.match_mode,list(self.match_dict)[0]))) print("M. Return to Menu") print("Q. Quit") print("") menu = self.u.grab("Please select an option: ") if not len(menu): continue elif menu.lower() == "m": return elif menu.lower() == "q": self.u.custom_quit() elif menu in ("1","2","3","4"): self.match_mode = int(menu)-1 return def main(self): cwd = os.getcwd() lines=[""] if self.dsdt and self.d.acpi_tables: lines.append("Currently Loaded Tables ({:,}):".format(len(self.d.acpi_tables))) lines.append("") lines.extend([" "+x for x in textwrap.wrap( " ".join(self.sorted_nicely(list(self.d.acpi_tables))), width=70, # Limit the width to 80 for aesthetics break_on_hyphens=False )]) lines.extend([ "", "Loaded From: {}".format(self.dsdt) ]) else: lines.append("Currently Loaded Tables: None") lines.append("") lines.append("1. FixHPET - Patch Out IRQ Conflicts") lines.append("2. FakeEC - OS-Aware Fake EC") lines.append("3. FakeEC Laptop - Only Builds Fake EC - Leaves Existing Untouched") lines.append("4. USBX - Power properties for USB on SKL and newer SMBIOS") lines.append("5. PluginType - Redefines CPU Objects as Processor and sets plugin-type = 1") lines.append("6. PMC - Enables Native NVRAM on True 300-Series Boards") lines.append("7. RTCAWAC - Context-Aware AWAC Disable and RTC Enable/Fake/Range Fix") lines.append("8. USB Reset - Reset USB controllers to allow hardware mapping") lines.append("9. PCI Bridge - Create missing PCI bridges for passed device path") lines.append("0. PNLF - Sets up a PNLF device for laptop backlight control") lines.append("A. XOSI - _OSI rename and patch to return true for a range of Windows") lines.append(" versions - also checks for OSID") lines.append("B. Fix DMAR - Remove Reserved Memory Regions from the DMAR table") lines.append("C. SMBus - Defines an MCHC and BUS0 device for SMBus compatibility") lines.append("E. ACPI > Device - Searches the loaded tables for the passed ACPI path and") lines.append(" prints the corresponding Device Path") lines.append("F. ALS0 - Defines a fake Ambient Light Sensor") lines.append("G. IMEI Bridge - Defines IMEI - only needed on SNB+7-series or IVB+6-series") lines.append("") if sys.platform.startswith("linux") or sys.platform == "win32": lines.append("P. Dump the current system's ACPI tables") if self.d.iasl_legacy: lines.append("L. Use Legacy Compiler for macOS 10.6 and prior: {}".format("{}!! Enabled !!{}".format(self.yel,self.rst) if self.iasl_legacy else "Disabled")) lines.append("D. Select ACPI table or folder containing tables") lines.append("M. OpenCore Match Mode: {}".format( self.match_dict.get(self.match_mode,list(self.match_dict)[0]) )) lines.append("R. {} Window Resizing".format("Enable" if not self.resize_window else "Disable")) lines.append("Q. Quit") lines.append("") if self.resize_window: self.u.resize(self.w,max(self.h,len(lines)+4)) self.u.head() print("\n".join(lines)) menu = self.u.grab("Please make a selection: ") if not len(menu): return if self.resize_window: self.u.resize(self.w,self.h) if menu.lower() == "q": self.u.custom_quit() if menu.lower() == "d": self.dsdt = self.select_dsdt() return if menu == "1": self.fix_hpet() elif menu == "2": self.fake_ec() elif menu == "3": self.fake_ec(True) elif menu == "4": self.ssdt_usbx() elif menu == "5": self.plugin_type() elif menu == "6": self.ssdt_pmc() elif menu == "7": self.ssdt_awac() elif menu == "8": self.ssdt_rhub() elif menu == "9": self.pci_bridge() elif menu == "0": self.ssdt_pnlf() elif menu.lower() == "a": self.ssdt_xosi() elif menu.lower() == "b": self.fix_dmar() elif menu.lower() == "c": self.smbus() elif menu.lower() == "e": self.acpi_device_path() elif menu.lower() == "f": self.ambient_light_sensor() elif menu.lower() == "g": self.imei_bridge() elif menu.lower() == "p" and (sys.platform.startswith("linux") or sys.platform == "win32"): output_folder = os.path.join(os.path.dirname(os.path.realpath(__file__)),self.output) acpi_name = self.get_unique_name("OEM",output_folder,name_append="") self.dsdt = self.load_dsdt( self.d.dump_tables(os.path.join(output_folder,acpi_name)) ) elif menu.lower() == "l" and self.d.iasl_legacy: self.iasl_legacy = not self.iasl_legacy self.save_settings() elif menu.lower() == "m": self.pick_match_mode() self.save_settings() elif menu.lower() == "r": self.resize_window ^= True self.save_settings() return if __name__ == '__main__': if 2/3 == 0: input = raw_input s = SSDT() while True: try: s.main() except Exception as e: print("An error occurred: {}".format(e)) input("Press [enter] to continue...")