4139 lines
179 KiB
Python
4139 lines
179 KiB
Python
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<len(p)<5 or p[0] in nostart or not all(x in valid for x in p) for p in parts):
|
|
continue
|
|
# Strip trailing underscores
|
|
parts = [p.rstrip("_") for p in parts]
|
|
# Join them with a leading slash
|
|
igpu = "\\"+".".join(parts)
|
|
guessed = False
|
|
manual = True
|
|
break
|
|
self.u.head("Generating PNLF")
|
|
print("")
|
|
print("Creating SSDT-PNLF...")
|
|
print(" - _UID: {}".format(uid))
|
|
print(" - iGPU Path: {}{}".format(
|
|
igpu,
|
|
" (Guessed)" if guessed else " (Manually Entered)" if manual else ""
|
|
))
|
|
patches = []
|
|
# Check all tables for PNLF and generate an XNLF rename if found
|
|
for table_name in self.sorted_nicely(list(self.d.acpi_tables)):
|
|
table = self.d.acpi_tables[table_name]
|
|
if "PNLF" in table["table"]:
|
|
print("PNLF detected in {} - generating rename...".format(table_name))
|
|
patches.append({
|
|
"Comment":"PNLF to XNLF Rename",
|
|
"Find":"504E4C46",
|
|
"Replace":"584E4C46",
|
|
"Table":None # Apply to all tables
|
|
})
|
|
break
|
|
# Checks for Name (NBCF, Zero) or Name (NBCF, 0x00)
|
|
nbcf_old = binascii.unhexlify("084E4243460A00")
|
|
nbcf_new = binascii.unhexlify("084E42434600")
|
|
# Initialize some boolean flags
|
|
has_nbcf_old = has_nbcf_new = False
|
|
for table_name in self.sorted_nicely(list(self.d.acpi_tables)):
|
|
table = self.d.acpi_tables[table_name]
|
|
# Check for NBCF
|
|
if not has_nbcf_old and nbcf_old in table["raw"]:
|
|
print("Name (NBCF, 0x00) detected in {} - generating patch...".format(table_name))
|
|
has_nbcf_old = True
|
|
# Got a hit with the old approach
|
|
patches.append({
|
|
"Comment":"NBCF 0x00 to 0x01 for BrightnessKeys.kext",
|
|
"Find":"084E4243460A00",
|
|
"Replace":"084E4243460A01",
|
|
"Enabled":False,
|
|
"Disabled":True,
|
|
"Table":None # Apply to all tables
|
|
})
|
|
if not has_nbcf_new and nbcf_new in table["raw"]:
|
|
print("Name (NBCF, Zero) detected in {} - generating patch...".format(table_name))
|
|
has_nbcf_new = True
|
|
# Got a hit with the new approach
|
|
patches.append({
|
|
"Comment":"NBCF Zero to One for BrightnessKeys.kext",
|
|
"Find":"084E42434600",
|
|
"Replace":"084E42434601",
|
|
"Enabled":False,
|
|
"Disabled":True,
|
|
"Table":None # Apply to all tables
|
|
})
|
|
if has_nbcf_old and has_nbcf_new:
|
|
break # Nothing else to look for
|
|
ssdt = """//
|
|
// Much of the info pulled from: https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/AcpiSamples/Source/SSDT-PNLF.dsl
|
|
//
|
|
DefinitionBlock ("", "SSDT", 2, "CORP", "PNLF", 0x00000000)
|
|
{"""
|
|
if igpu:
|
|
ssdt += """
|
|
External ([[igpu_path]], DeviceObj)
|
|
"""
|
|
ssdt += """
|
|
Device (PNLF)
|
|
{
|
|
Name (_HID, EisaId ("APP0002")) // _HID: Hardware ID
|
|
Name (_CID, "backlight") // _CID: Compatible ID
|
|
|
|
// _UID | Supported Platform(s) | PWMMax
|
|
// -----------------------------------------------
|
|
// 14 | Arrandale, Sandy/Ivy Bridge | 0x0710
|
|
// 15 | Haswell/Broadwell | 0x0AD9
|
|
// 16 | Skylake/Kaby Lake, some Haswell | 0x056C
|
|
// 17 | Custom LMAX | 0x07A1
|
|
// 18 | Custom LMAX | 0x1499
|
|
// 19 | CoffeeLake and newer (or AMD) | 0xFFFF
|
|
// 99 | Other (requires custom applbkl-name/applbkl-data dev props)
|
|
|
|
Name (_UID, [[uid_value]]) // _UID: Unique ID: [[uid_dec]]
|
|
|
|
Method (_STA, 0, NotSerialized) // _STA: Status
|
|
{
|
|
If (_OSI ("Darwin"))
|
|
{
|
|
Return (0x0B)
|
|
}
|
|
Else
|
|
{
|
|
Return (Zero)
|
|
}
|
|
}"""
|
|
if igpu:
|
|
ssdt += """
|
|
Method (_INI, 0, Serialized)
|
|
{
|
|
If (LAnd (_OSI ("Darwin"), CondRefOf ([[igpu_path]])))
|
|
{
|
|
OperationRegion ([[igpu_path]].RMP3, PCI_Config, Zero, 0x14)
|
|
Field ([[igpu_path]].RMP3, AnyAcc, NoLock, Preserve)
|
|
{
|
|
Offset (0x02), GDID,16,
|
|
Offset (0x10), BAR1,32,
|
|
}
|
|
// IGPU PWM backlight register descriptions:
|
|
// LEV2 not currently used
|
|
// LEVL level of backlight in Sandy/Ivy
|
|
// P0BL counter, when zero is vertical blank
|
|
// GRAN see description below in INI1 method
|
|
// LEVW should be initialized to 0xC0000000
|
|
// LEVX PWMMax except FBTYPE_HSWPLUS combo of max/level (Sandy/Ivy stored in MSW)
|
|
// LEVD level of backlight for Coffeelake
|
|
// PCHL not currently used
|
|
OperationRegion (RMB1, SystemMemory, BAR1 & ~0xF, 0xe1184)
|
|
Field(RMB1, AnyAcc, Lock, Preserve)
|
|
{
|
|
Offset (0x48250),
|
|
LEV2, 32,
|
|
LEVL, 32,
|
|
Offset (0x70040),
|
|
P0BL, 32,
|
|
Offset (0xc2000),
|
|
GRAN, 32,
|
|
Offset (0xc8250),
|
|
LEVW, 32,
|
|
LEVX, 32,
|
|
LEVD, 32,
|
|
Offset (0xe1180),
|
|
PCHL, 32,
|
|
}
|
|
// Now fixup the backlight PWM depending on the framebuffer type
|
|
// At this point:
|
|
// Local4 is RMCF.BLKT value (unused here), if specified (default is 1)
|
|
// Local0 is device-id for IGPU
|
|
// Local2 is LMAX, if specified (Ones means based on device-id)
|
|
// Local3 is framebuffer type
|
|
|
|
// Adjustment required when using WhateverGreen.kext
|
|
Local0 = GDID
|
|
Local2 = Ones
|
|
Local3 = 0
|
|
|
|
// check Sandy/Ivy
|
|
// #define FBTYPE_SANDYIVY 1
|
|
If (LOr (LEqual (1, Local3), LNotEqual (Match (Package()
|
|
{
|
|
// Sandy HD3000
|
|
0x010b, 0x0102,
|
|
0x0106, 0x1106, 0x1601, 0x0116, 0x0126,
|
|
0x0112, 0x0122,
|
|
// Ivy
|
|
0x0152, 0x0156, 0x0162, 0x0166,
|
|
0x016a,
|
|
// Arrandale
|
|
0x0046, 0x0042,
|
|
}, MEQ, Local0, MTR, 0, 0), Ones)))
|
|
{
|
|
if (LEqual (Local2, Ones))
|
|
{
|
|
// #define SANDYIVY_PWMMAX 0x710
|
|
Store (0x710, Local2)
|
|
}
|
|
// change/scale only if different than current...
|
|
Store (LEVX >> 16, Local1)
|
|
If (LNot (Local1))
|
|
{
|
|
Store (Local2, Local1)
|
|
}
|
|
If (LNotEqual (Local2, Local1))
|
|
{
|
|
// set new backlight PWMMax but retain current backlight level by scaling
|
|
Store ((LEVL * Local2) / Local1, Local0)
|
|
Store (Local2 << 16, Local3)
|
|
If (LGreater (Local2, Local1))
|
|
{
|
|
// PWMMax is getting larger... store new PWMMax first
|
|
Store (Local3, LEVX)
|
|
Store (Local0, LEVL)
|
|
}
|
|
Else
|
|
{
|
|
// otherwise, store new brightness level, followed by new PWMMax
|
|
Store (Local0, LEVL)
|
|
Store (Local3, LEVX)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}"""
|
|
ssdt += """
|
|
}
|
|
}"""
|
|
# 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...")
|