sys_patch.py: Add additional preflight checks

This commit is contained in:
Mykola Grymalyuk
2022-05-06 11:04:41 -06:00
parent 0c5017a16a
commit b39f2a1d5d
6 changed files with 181 additions and 54 deletions

1
.gitignore vendored
View File

@@ -26,3 +26,4 @@ __pycache__/
/payloads/seed.plist
/payloads/AutoPkg-Assets.pkg
/payloads/AutoPkg-Assets.pkg.zip
/payloads/Universal-Binaries

View File

@@ -6,6 +6,11 @@
- Add Macmini8,1 FeatureUnlock support
- Drops CPU check, supports all machines
- Refactor Root Patching System
- Adds preflight checks validating patch set data and presence
- Adds dynamic Sandy Bridge Board ID patching
- Allows for unrestricted SMBIOS usage with `AppleIntelSNBGraphicsFB`
- Adds OpenCL downgrade for TeraScale 2
- Resolves VNC support (credit IronApple#2711)
- Add `OpenCore-Legacy-Patcher.plist` for applied patch info
- Located under `/System/Library/CoreServices`
- Lists patch sets applied including files installed and removed

View File

@@ -34,4 +34,14 @@ class os_conversion:
if kernel >= os_data.big_sur:
return str((kernel - 9))
else:
return str((f"10.{kernel - 4}"))
return str((f"10.{kernel - 4}"))
def is_os_newer(source_major, source_minor, target_major, target_minor):
# Check if OS version 1 is newer than OS version 2
if source_major < target_major:
return True
elif source_major == target_major:
if source_minor < target_minor:
return True
else:
return False

View File

@@ -13,10 +13,14 @@
from data import os_data
def SystemPatchDictionary(os_major):
def SystemPatchDictionary(os_major, os_minor):
sys_patch_dict = {
"Graphics": {
"Non-Metal Common": {
"OS Support": {
"OS Major": os_data.os_data.big_sur,
"OS Minor": 0
},
"Install": {
"/System/Library/Extensions": {
"IOSurface.kext": "10.15.7",
@@ -68,6 +72,10 @@ def SystemPatchDictionary(os_major):
},
},
"Metal Common": {
"OS Support": {
"OS Major": os_data.os_data.monterey,
"OS Minor": 0
},
"Install": {
"/System/Library/Frameworks": {
"OpenCL.framework": "11.6",
@@ -81,6 +89,10 @@ def SystemPatchDictionary(os_major):
},
"Legacy GVA": {
"OS Support": {
"OS Major": os_data.os_data.big_sur,
"OS Minor": 0
},
"Install": {
"/System/Library/PrivateFrameworks": {
"AppleGVA.framework": "10.13.6",
@@ -90,6 +102,10 @@ def SystemPatchDictionary(os_major):
},
"Nvidia Tesla": {
"OS Support": {
"OS Major": os_data.os_data.mojave,
"OS Minor": 0
},
"Install": {
"/System/Library/Extensions": {
"GeForceGA.bundle": "10.13.6",
@@ -98,11 +114,17 @@ def SystemPatchDictionary(os_major):
"GeForceTeslaVADriver.bundle": "10.13.6",
"NVDANV50HalTesla.kext": "10.13.6",
"NVDAResmanTesla.kext": "10.13.6",
**({ "NVDAStartup.kext": "12.0 Beta 6" } if os_major >= os_data.os_data.monterey else {})
# Apple dropped NVDAStartup in 12.0 Beta 7 (XNU 21.1)
**({ "NVDAStartup.kext": "12.0 Beta 6" } if os_data.os_conversion.is_os_newer(os_data.os_data.monterey, 1, os_major, os_minor) else {})
},
},
},
"Nvidia Kepler": {
"OS Support": {
# 12.0 beta 7 (XNU 21.1)
"OS Major": os_data.os_data.monterey,
"OS Minor": 1
},
"Install": {
"/System/Library/Extensions": {
"GeForceAIRPlugin.bundle": "11.0 Beta 3",
@@ -118,6 +140,10 @@ def SystemPatchDictionary(os_major):
},
},
"Nvidia Web Drivers": {
"OS Support": {
"OS Major": os_data.os_data.mojave,
"OS Minor": 0
},
"Install": {
"/System/Library/Extensions": {
"NVDAStartupWeb.kext": "10.13.6",
@@ -141,6 +167,10 @@ def SystemPatchDictionary(os_major):
},
},
"AMD Non-Metal Common": {
"OS Support": {
"OS Major": os_data.os_data.mojave,
"OS Minor": 0
},
"Install": {
"/System/Library/Extensions": {
"AMDFramebuffer.kext": "10.13.6",
@@ -162,6 +192,10 @@ def SystemPatchDictionary(os_major):
},
"AMD TeraScale 1": {
"OS Support": {
"OS Major": os_data.os_data.mojave,
"OS Minor": 0
},
"Install": {
"/System/Library/Extensions": {
"AMD2400Controller.kext": "10.13.6",
@@ -175,8 +209,25 @@ def SystemPatchDictionary(os_major):
"ATIRadeonX2000VADriver.bundle": "10.13.6",
},
},
"Remove": {
"/System/Library/Extensions": [
# Following removals are a work around for 0.4.3 and older root patches
# Previously TS1 and TS2 patch sets were shared, now they're split off
# Due to this, updating to 0.4.4 or newer can break kmutil linking
"AMD5000Controller.kext",
"AMD6000Controller.kext",
"AMDRadeonVADriver.bundle",
"AMDRadeonVADriver2.bundle",
"AMDRadeonX3000.kext",
"AMDRadeonX3000GLDriver.bundle",
],
},
},
"AMD TeraScale 2": {
"OS Support": {
"OS Major": os_data.os_data.mojave,
"OS Minor": 0
},
"Install": {
"/System/Library/Extensions": {
"AMD5000Controller.kext": "10.13.6",
@@ -204,6 +255,10 @@ def SystemPatchDictionary(os_major):
},
},
"Intel Ironlake": {
"OS Support": {
"OS Major": os_data.os_data.mojave,
"OS Minor": 0
},
"Install": {
"/System/Library/Extensions": {
"AppleIntelHDGraphics.kext": "10.13.6",
@@ -215,6 +270,10 @@ def SystemPatchDictionary(os_major):
},
},
"Intel Sandy Bridge": {
"OS Support": {
"OS Major": os_data.os_data.mojave,
"OS Minor": 0
},
"Install": {
"/System/Library/Extensions": {
"AppleIntelHD3000Graphics.kext": "10.13.6",
@@ -227,6 +286,10 @@ def SystemPatchDictionary(os_major):
},
},
"Intel Ivy Bridge": {
"OS Support": {
"OS Major": os_data.os_data.monterey,
"OS Minor": 0
},
"Processes": {
"defaults write com.apple.coremedia hardwareVideoDecoder -string enable": False,
},
@@ -245,6 +308,10 @@ def SystemPatchDictionary(os_major):
},
"Audio": {
"Legacy Realtek": {
"OS Support": {
"OS Major": os_data.os_data.sierra,
"OS Minor": 0
},
# For iMac7,1 and iMac8,1 units with legacy Realtek HD Audio
"Install": {
"/System/Library/Extensions": {
@@ -264,6 +331,10 @@ def SystemPatchDictionary(os_major):
},
# For Mac Pros with non-UGA/GOP GPUs
"Legacy Non-GOP": {
"OS Support": {
"OS Major": os_data.os_data.mojave,
"OS Minor": 0
},
"Install": {
"/System/Library/Extensions": {
"AppleHDA.kext": "10.13.6",
@@ -273,6 +344,10 @@ def SystemPatchDictionary(os_major):
},
"Networking": {
"Legacy WiFi": {
"OS Support": {
"OS Major": os_data.os_data.monterey,
"OS Minor": 0
},
"Install": {
"/usr/libexec": {
"airportd": "11.5.2",
@@ -291,6 +366,10 @@ def SystemPatchDictionary(os_major):
},
"Brightness": {
"Legacy Brightness": {
"OS Support": {
"OS Major": os_data.os_data.high_sierra,
"OS Minor": 0
},
"Install": {
"/System/Library/Extensions": {
"AppleBacklight.kext": "10.12.6",
@@ -309,6 +388,10 @@ def SystemPatchDictionary(os_major):
},
"Miscellaneous": {
"Legacy GMUX": {
"OS Support": {
"OS Major": os_data.os_data.high_sierra,
"OS Minor": 0
},
"Install": {
"/System/Library/Extensions/AppleGraphicsControl.kext/Contents/PlugIns": {
"AppleMuxControl.kext": "10.12.6",
@@ -325,6 +408,10 @@ def SystemPatchDictionary(os_major):
},
},
"Legacy Keyboard Backlight": {
"OS Support": {
"OS Major": os_data.os_data.big_sur,
"OS Minor": 0
},
"Processes": {
"defaults write /Library/Preferences/.GlobalPreferences.plist Moraea_BacklightHack -bool true": True,
},

View File

@@ -49,6 +49,11 @@ class PatchSysVolume:
hardware_details = sys_patch_detect.detect_root_patch(self.computer.real_model, self.constants).detect_patch_set()
self.hardware_details = hardware_details
self.init_pathing(custom_root_mount_path=None, custom_data_mount_path=None)
def __del__(self):
# Ensures that each time we're patching, we're using a clean repository
if Path(self.constants.payload_local_binaries_root_path).exists():
shutil.rmtree(self.constants.payload_local_binaries_root_path)
def init_pathing(self, custom_root_mount_path=None, custom_data_mount_path=None):
if custom_root_mount_path and custom_data_mount_path:
@@ -63,42 +68,32 @@ class PatchSysVolume:
self.mount_location_data = ""
self.mount_extensions = f"{self.mount_location}/System/Library/Extensions"
self.mount_application_support = f"{self.mount_location_data}/Library/Application Support"
def find_mount_root_vol(self, patch):
def mount_root_vol(self):
# Returns boolean if Root Volume is available
self.root_mount_path = utilities.get_disk_path()
if self.root_mount_path.startswith("disk"):
print(f"- Found Root Volume at: {self.root_mount_path}")
if Path(self.mount_extensions).exists():
print("- Root Volume is already mounted")
if patch is True:
self.patch_root_vol()
return True
else:
self.unpatch_root_vol()
return True
return True
else:
if self.constants.detected_os > os_data.os_data.catalina and self.root_supports_snapshot is True:
if self.root_supports_snapshot is True:
print("- Mounting APFS Snapshot as writable")
result = utilities.elevated(["mount", "-o", "nobrowse", "-t", "apfs", f"/dev/{self.root_mount_path}", self.mount_location], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if result.returncode == 0:
print(f"- Mounted APFS Snapshot as writable at: {self.mount_location}")
if Path(self.mount_extensions).exists():
print("- Successfully mounted the Root Volume")
if patch is True:
self.patch_root_vol()
return True
if Path(self.mount_extensions).exists():
print("- Successfully mounted the Root Volume")
return True
else:
print("- Root Volume appears to have unmounted unexpectedly")
else:
self.unpatch_root_vol()
return True
else:
print("- Failed to mount the Root Volume")
print("- Recommend rebooting the machine and trying to patch again")
if self.constants.gui_mode is False:
input("- Press [ENTER] to exit: ")
else:
print("- Could not find root volume")
if self.constants.gui_mode is False:
input("- Press [ENTER] to exit: ")
print("- Unable to mount APFS Snapshot as writable")
print("Reason for mount failure:")
print(result.stdout.decode().strip())
return False
def unpatch_root_vol(self):
if self.constants.detected_os > os_data.os_data.catalina and self.root_supports_snapshot is True:
@@ -248,30 +243,18 @@ class PatchSysVolume:
# Make sure SNB kexts are compatible with the host
if "Intel Sandy Bridge" in required_patches:
if self.computer.reported_board_id not in self.constants.sandy_board_id_stock:
print(f"- Found unspported Board ID {self.computer.reported_board_id}, performing AppleIntelSNBGraphicsFB bin patching")
board_to_patch = generate_smbios.determine_best_board_id_for_sandy(self.computer.reported_board_id, self.computer.gpus)
print(f"- Replacing {board_to_patch} with {self.computer.reported_board_id}")
board_to_patch_hex = bytes.fromhex(board_to_patch.encode('utf-8').hex())
reported_board_hex = bytes.fromhex(self.computer.reported_board_id.encode('utf-8').hex())
if len(board_to_patch_hex) != len(reported_board_hex):
print(f"- Error: Board ID {self.computer.reported_board_id} is not the same length as {board_to_patch}")
raise Exception("Host's Board ID is not the same length as the kext's Board ID, cannot patch!!!")
else:
path = source_files_path + "10.13.6/System/Library/Extensions/AppleIntelSNBGraphicsFB.kext/Contents/MacOS/AppleIntelSNBGraphicsFB"
if Path(path).exists():
with open(path, 'rb') as f:
data = f.read()
data = data.replace(board_to_patch_hex, reported_board_hex)
with open(path, 'wb') as f:
f.write(data)
else:
raise Exception("Failed to find AppleIntelSNBGraphicsFB.kext, cannot patch!!!")
self.snb_board_id_patch(source_files_path)
# Check all the files are present
for patch in required_patches:
# Check if the patch sets support the current OS
if required_patches[patch]["OS Support"]["OS Major"] > self.constants.detected_os:
print(f"Patch set OS Major check: {required_patches[patch]['OS Support']['OS Major']} < {self.constants.detected_os}")
raise Exception("This patchset is not supported on this version of macOS!")
elif required_patches[patch]["OS Support"]["OS Minor"] > self.constants.detected_os_minor:
print(f"Patch set OS Minor check: {required_patches[patch]['OS Support']['OS Minor']} < {self.constants.detected_os_minor}")
raise Exception("This patchset is not supported on this version of macOS!")
# Check if all files are present
for method_type in ["Install", "Install Non-Root"]:
if method_type in required_patches[patch]:
for install_patch_directory in required_patches[patch][method_type]:
@@ -331,6 +314,35 @@ class PatchSysVolume:
utilities.process_status(utilities.elevated(chown_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
def snb_board_id_patch(self, source_files_path):
# AppleIntelSNBGraphicsFB hard codes the supported Board IDs for Sandy Bridge iGPUs
# Because of this, the kext errors out on unsupported systems
# This function simply patches in a supported Board ID, using 'determine_best_board_id_for_sandy()'
# to supplement the ideal Board ID
if self.computer.reported_board_id not in self.constants.sandy_board_id_stock:
print(f"- Found unspported Board ID {self.computer.reported_board_id}, performing AppleIntelSNBGraphicsFB bin patching")
board_to_patch = generate_smbios.determine_best_board_id_for_sandy(self.computer.reported_board_id, self.computer.gpus)
print(f"- Replacing {board_to_patch} with {self.computer.reported_board_id}")
board_to_patch_hex = bytes.fromhex(board_to_patch.encode('utf-8').hex())
reported_board_hex = bytes.fromhex(self.computer.reported_board_id.encode('utf-8').hex())
if len(board_to_patch_hex) != len(reported_board_hex):
print(f"- Error: Board ID {self.computer.reported_board_id} is not the same length as {board_to_patch}")
raise Exception("Host's Board ID is not the same length as the kext's Board ID, cannot patch!!!")
else:
path = source_files_path + "/10.13.6/System/Library/Extensions/AppleIntelSNBGraphicsFB.kext/Contents/MacOS/AppleIntelSNBGraphicsFB"
if Path(path).exists():
with open(path, 'rb') as f:
data = f.read()
data = data.replace(board_to_patch_hex, reported_board_hex)
with open(path, 'wb') as f:
f.write(data)
else:
print(f"- Error: Could not find {path}")
raise Exception("Failed to find AppleIntelSNBGraphicsFB.kext, cannot patch!!!")
def check_files(self):
if Path(self.constants.payload_local_binaries_root_path).exists():
print("- Found local Apple Binaries")
@@ -389,7 +401,14 @@ class PatchSysVolume:
if sys_patch_detect.detect_root_patch(self.computer.real_model, self.constants).verify_patch_allowed(print_errors=not self.constants.wxpython_variant) is True:
print("- Patcher is capable of patching")
if self.check_files():
self.find_mount_root_vol(True)
if self.mount_root_vol() is True:
self.patch_root_vol()
if self.constants.gui_mode is False:
input("\nPress [ENTER] to return to the main menu")
else:
print("- Recommend rebooting the machine and trying to patch again")
if self.constants.gui_mode is False:
input("- Press [ENTER] to exit: ")
elif self.constants.gui_mode is False:
input("\nPress [ENTER] to return to the main menu: ")
@@ -399,8 +418,13 @@ class PatchSysVolume:
def start_unpatch(self):
print("- Starting Unpatch Process")
if sys_patch_detect.detect_root_patch(self.computer.real_model, self.constants).verify_patch_allowed(print_errors=True) is True:
self.find_mount_root_vol(False)
if self.constants.gui_mode is False:
input("\nPress [ENTER] to return to the main menu")
if self.mount_root_vol() is True:
self.unpatch_root_vol()
if self.constants.gui_mode is False:
input("\nPress [ENTER] to return to the main menu")
else:
print("- Recommend rebooting the machine and trying to patch again")
if self.constants.gui_mode is False:
input("- Press [ENTER] to exit: ")
elif self.constants.gui_mode is False:
input("\nPress [ENTER] to return to the main menu")

View File

@@ -220,7 +220,7 @@ class detect_root_patch:
return True
def generate_patchset(self, hardware_details):
all_hardware_patchset = sys_patch_dict.SystemPatchDictionary(self.constants.detected_os)
all_hardware_patchset = sys_patch_dict.SystemPatchDictionary(self.constants.detected_os, self.constants.detected_os_minor)
required_patches = {}
utilities.cls()
print("- The following patches will be applied:")