Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sys_patch: Allow KDK-less root patching on Intel iGPUs and Nvidia Kepler #1007

Merged
merged 2 commits into from
Aug 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion data/sys_patch_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def SystemPatchDictionary(os_major, os_minor, non_metal_os_support):
},
},

"Metal 1 Common": {
"Metal 3802 Common": {
"Display Name": "",
"OS Support": {
"Minimum OS Support": {
Expand Down
6 changes: 6 additions & 0 deletions resources/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ def generate_base_data(self):
self.constants.unpack_thread.start()
self.constants.commit_info = commit_info.commit_info(self.constants.launcher_binary).generate_commit_info()

# Now that we have commit info, update nightly link
if self.constants.commit_info[0] != "Running from source":
branch = self.constants.commit_info[0]
branch = branch.replace("refs/heads/", "")
self.constants.installer_pkg_url_nightly = self.constants.installer_pkg_url_nightly.replace("main", branch)

defaults.generate_defaults.probe(self.computer.real_model, True, self.constants)

if utilities.check_cli_args() is not None:
Expand Down
97 changes: 84 additions & 13 deletions resources/sys_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
# This is because Apple removed on-disk binaries (ref: https://github.com/dortania/OpenCore-Legacy-Patcher/issues/998)
# 'sudo ditto /Library/Developer/KDKs/<KDK Version>/System /System/Volumes/Update/mnt1/System'

import plistlib
import shutil
import subprocess
from pathlib import Path
Expand Down Expand Up @@ -60,6 +61,8 @@ def __init__(self, model, versions, hardware_details=None):
self.hardware_details = hardware_details
self.init_pathing(custom_root_mount_path=None, custom_data_mount_path=None)

self.skip_root_kmutil_requirement = self.hardware_details["Settings: Supports Auxiliary Cache"]

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():
Expand Down Expand Up @@ -106,6 +109,8 @@ def mount_root_vol(self):
return False

def merge_kdk_with_root(self):
if self.skip_root_kmutil_requirement is True:
return
if self.constants.detected_os < os_data.os_data.ventura:
return
if (Path(self.mount_location) / Path("System/Library/Extensions/System.kext/PlugIns/Libkern.kext/Libkern")).exists():
Expand Down Expand Up @@ -149,8 +154,23 @@ def unpatch_root_vol(self):
print("\nPlease reboot the machine for patches to take effect")

def rebuild_snapshot(self):
print("- Rebuilding Kernel Cache (This may take some time)")
if self.rebuild_kernel_collection() is True:
self.update_preboot_kernel_cache()
self.rebuild_dyld_shared_cache()
if self.create_new_apfs_snapshot() is True:
print("- Patching complete")
print("\nPlease reboot the machine for patches to take effect")
if self.needs_kmutil_exemptions is True:
print("Note: Apple will require you to open System Preferences -> Security to allow the new kernel extensions to be loaded")
self.constants.root_patcher_succeeded = True
if self.constants.gui_mode is False:
input("\nPress [ENTER] to continue")

def rebuild_kernel_collection(self):
if self.skip_root_kmutil_requirement is True:
return True

print("- Rebuilding Kernel Cache (This may take some time)")
if self.constants.detected_os > os_data.os_data.catalina:
args = [
"kmutil",
Expand Down Expand Up @@ -206,18 +226,10 @@ def rebuild_snapshot(self):
print("\nPlease reboot the machine to avoid potential issues rerunning the patcher")
if self.constants.gui_mode is False:
input("Press [ENTER] to continue")
else:
print("- Successfully built new kernel cache")
self.update_preboot_kernel_cache()
self.rebuild_dyld_shared_cache()
if self.create_new_apfs_snapshot() is True:
print("- Patching complete")
print("\nPlease reboot the machine for patches to take effect")
if self.needs_kmutil_exemptions is True:
print("Note: Apple will require you to open System Preferences -> Security to allow the new kernel extensions to be loaded")
self.constants.root_patcher_succeeded = True
if self.constants.gui_mode is False:
input("\nPress [ENTER] to continue")
return False

print("- Successfully built new kernel cache")
return True

def create_new_apfs_snapshot(self):
if self.root_supports_snapshot is True:
Expand Down Expand Up @@ -279,6 +291,64 @@ def write_patchset(self, patchset):
utilities.process_status(utilities.elevated(["rm", destination_path_file], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
utilities.process_status(utilities.elevated(["cp", f"{self.constants.payload_path}/{file_name}", destination_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))

def add_auxkc_support(self, install_file, source_folder_path, install_patch_directory, destination_folder_path):
# In macOS Ventura, KDKs are required to build new Boot and System KCs
# However for some patch sets, we're able to use the Auxiliary KCs with '/Library/Extensions'

# kernelmanagerd determines which kext is installed by their 'OSBundleRequired' entry
# If a kext is labeled as 'OSBundleRequired: Root' or 'OSBundleRequired: Safe Boot',
# kernelmanagerd will require the kext to be installed in the Boot/SysKC

# Additionally, kexts starting with 'com.apple.' are not natively allowed to be installed
# in the AuxKC. So we need to explicitly set our 'OSBundleRequired' to 'Auxiliary'

if self.skip_root_kmutil_requirement is False:
return destination_folder_path
if not install_file.endswith(".kext"):
return destination_folder_path
if install_patch_directory != "/System/Library/Extensions":
return destination_folder_path
if self.constants.detected_os < os_data.os_data.ventura:
return destination_folder_path

updated_install_location = str(self.mount_location_data) + "/Library/Extensions"

print(f"- Adding AuxKC support to {install_file}")
plist_path = Path(Path(source_folder_path) / Path(install_file) / Path("Contents/Info.plist"))
plist_data = plistlib.load((plist_path).open("rb"))

# Check if we need to update the 'OSBundleRequired' entry
if not plist_data["CFBundleIdentifier"].startswith("com.apple."):
return updated_install_location
if "OSBundleRequired" in plist_data:
if plist_data["OSBundleRequired"] == "Auxiliary":
return updated_install_location

plist_data["OSBundleRequired"] = "Auxiliary"
plistlib.dump(plist_data, plist_path.open("wb"))

# Verify whether the user needs to authenticate in System Preferences
# Specifically under 'private/var/db/KernelManagement/AuxKC/CurrentAuxKC/com.apple.kcgen.instructions.plist'
# ["kextsToBuild"][i]:
# ["bundlePathMainOS"] = /Library/Extensions/Test.kext
# ["cdHash"] = Bundle's CDHash (random on ad-hoc signed, static on dev signed)
# ["teamID"] = Team ID (blank on ad-hoc signed)
# To grab the CDHash of a kext, run 'codesign -dvvv <kext_path>'
try:
aux_cache_path = Path(self.mount_location_data) / Path("private/var/db/KernelManagement/AuxKC/CurrentAuxKC/com.apple.kcgen.instructions.plist")
if Path(aux_cache_path).exists():
aux_cache_data = plistlib.load((aux_cache_path).open("rb"))
for kext in aux_cache_data["kextsToBuild"]:
if "bundlePathMainOS" in kext:
if kext["bundlePathMainOS"] == f"/Library/Extensions/{install_file}":
return updated_install_location
except PermissionError:
pass

self.constants.needs_to_open_preferences = True

return updated_install_location

def patch_root_vol(self):
print(f"- Running patches for {self.model}")
if self.patch_set_dictionary != {}:
Expand Down Expand Up @@ -316,6 +386,7 @@ def execute_patchset(self, required_patches):
if install_patch_directory == "/Library/Extensions":
self.needs_kmutil_exemptions = True
destination_folder_path = str(self.mount_location_data) + install_patch_directory
destination_folder_path = self.add_auxkc_support(install_file, source_folder_path, install_patch_directory, destination_folder_path)
self.install_new_file(source_folder_path, destination_folder_path, install_file)

if "Processes" in required_patches[patch]:
Expand Down
27 changes: 18 additions & 9 deletions resources/sys_patch_detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def __init__(self, model, versions):
self.amfi_must_disable = False
self.supports_metal = False
self.needs_nv_web_checks = False
self.requires_root_kc = False

# Validation Checks
self.sip_enabled = False
Expand All @@ -63,6 +64,7 @@ def detect_gpus(self):
self.nvidia_tesla = True
self.amfi_must_disable = True
self.legacy_keyboard_backlight = self.check_legacy_keyboard_backlight()
self.requires_root_kc = True
elif gpu.arch == device_probe.NVIDIA.Archs.Kepler and self.constants.force_nv_web is False:
if self.constants.detected_os > os_data.os_data.big_sur:
# Kepler drivers were dropped with Beta 7
Expand All @@ -86,14 +88,17 @@ def detect_gpus(self):
self.nvidia_web = True
self.amfi_must_disable = True
self.needs_nv_web_checks = True
self.requires_root_kc = True
elif gpu.arch == device_probe.AMD.Archs.TeraScale_1:
if self.constants.detected_os > non_metal_os:
self.amd_ts1 = True
self.amfi_must_disable = True
self.requires_root_kc = True
elif gpu.arch == device_probe.AMD.Archs.TeraScale_2:
if self.constants.detected_os > non_metal_os:
self.amd_ts2 = True
self.amfi_must_disable = True
self.requires_root_kc = True
elif gpu.arch in [
device_probe.AMD.Archs.Legacy_GCN_7000,
device_probe.AMD.Archs.Legacy_GCN_8000,
Expand All @@ -105,16 +110,19 @@ def detect_gpus(self):
continue
self.legacy_gcn = True
self.supports_metal = True
self.requires_root_kc = True
elif gpu.arch == device_probe.Intel.Archs.Iron_Lake:
if self.constants.detected_os > non_metal_os:
self.iron_gpu = True
self.amfi_must_disable = True
self.legacy_keyboard_backlight = self.check_legacy_keyboard_backlight()
self.requires_root_kc = True
elif gpu.arch == device_probe.Intel.Archs.Sandy_Bridge:
if self.constants.detected_os > non_metal_os:
self.sandy_gpu = True
self.amfi_must_disable = True
self.legacy_keyboard_backlight = self.check_legacy_keyboard_backlight()
self.requires_root_kc = True
elif gpu.arch == device_probe.Intel.Archs.Ivy_Bridge:
if self.constants.detected_os > os_data.os_data.big_sur:
self.ivy_gpu = True
Expand Down Expand Up @@ -287,6 +295,7 @@ def detect_patch_set(self):
"Miscellaneous: Legacy GMUX": self.legacy_gmux,
"Miscellaneous: Legacy Keyboard Backlight": self.legacy_keyboard_backlight,
"Settings: Requires AMFI exemption": self.amfi_must_disable,
"Settings: Supports Auxiliary Cache": not self.requires_root_kc,
"Validation: Patching Possible": self.verify_patch_allowed(),
f"Validation: SIP is enabled (Required: {self.check_sip()[2]} or higher)": self.sip_enabled,
f"Validation: Currently Booted SIP: ({hex(utilities.csr_dump())})": self.sip_enabled,
Expand All @@ -309,7 +318,8 @@ def verify_patch_allowed(self, print_errors=False):
sip_value = sip_dict[1]

self.sip_enabled, self.sbm_enabled, self.amfi_enabled, self.fv_enabled, self.dosdude_patched = utilities.patching_status(sip, self.constants.detected_os)
self.missing_kdk = not self.check_kdk()
if self.requires_root_kc is True:
self.missing_kdk = not self.check_kdk()

if self.nvidia_web is True:
self.missing_nv_web_nvram = not self.check_nv_web_nvram()
Expand Down Expand Up @@ -360,10 +370,9 @@ def verify_patch_allowed(self, print_errors=False):
print("\nCannot patch! WhateverGreen.kext missing")
print("Please ensure WhateverGreen.kext is installed")

if self.amfi_must_disable is True:
if self.missing_kdk is True:
print("\nCannot patch! Kernel Debug Kit missing")
print(f"Please ensure the correct KDK is installed (required: {self.constants.detected_os_build})")
if self.missing_kdk is True:
print("\nCannot patch! Kernel Debug Kit missing")
print(f"Please ensure the correct KDK is installed (required: {self.constants.detected_os_build})")

if any(
[
Expand Down Expand Up @@ -399,17 +408,17 @@ def generate_patchset(self, hardware_details):
required_patches.update({"Intel Sandy Bridge": all_hardware_patchset["Graphics"]["Intel Sandy Bridge"]})
if hardware_details["Graphics: Intel Ivy Bridge"] is True:
required_patches.update({"Metal Common": all_hardware_patchset["Graphics"]["Metal Common"]})
required_patches.update({"Metal 1 Common": all_hardware_patchset["Graphics"]["Metal 1 Common"]})
required_patches.update({"Metal 3802 Common": all_hardware_patchset["Graphics"]["Metal 3802 Common"]})
required_patches.update({"Catalina GVA": all_hardware_patchset["Graphics"]["Catalina GVA"]})
required_patches.update({"Intel Ivy Bridge": all_hardware_patchset["Graphics"]["Intel Ivy Bridge"]})
if hardware_details["Graphics: Intel Haswell"] is True:
required_patches.update({"Metal Common": all_hardware_patchset["Graphics"]["Metal Common"]})
required_patches.update({"Metal 1 Common": all_hardware_patchset["Graphics"]["Metal 1 Common"]})
required_patches.update({"Metal 3802 Common": all_hardware_patchset["Graphics"]["Metal 3802 Common"]})
required_patches.update({"Monterey GVA": all_hardware_patchset["Graphics"]["Monterey GVA"]})
required_patches.update({"Intel Haswell": all_hardware_patchset["Graphics"]["Intel Haswell"]})
if hardware_details["Graphics: Intel Broadwell"] is True:
required_patches.update({"Metal Common": all_hardware_patchset["Graphics"]["Metal Common"]})
required_patches.update({"Metal 1 Common": all_hardware_patchset["Graphics"]["Metal 1 Common"]})
required_patches.update({"Metal 3802 Common": all_hardware_patchset["Graphics"]["Metal 3802 Common"]})
required_patches.update({"Monterey GVA": all_hardware_patchset["Graphics"]["Monterey GVA"]})
required_patches.update({"Intel Broadwell": all_hardware_patchset["Graphics"]["Intel Broadwell"]})
if hardware_details["Graphics: Intel Skylake"] is True:
Expand All @@ -427,7 +436,7 @@ def generate_patchset(self, hardware_details):
required_patches.update({"Non-Metal Enforcement": all_hardware_patchset["Graphics"]["Non-Metal Enforcement"]})
if hardware_details["Graphics: Nvidia Kepler"] is True:
required_patches.update({"Metal Common": all_hardware_patchset["Graphics"]["Metal Common"]})
required_patches.update({"Metal 1 Common": all_hardware_patchset["Graphics"]["Metal 1 Common"]})
required_patches.update({"Metal 3802 Common": all_hardware_patchset["Graphics"]["Metal 3802 Common"]})
required_patches.update({"Catalina GVA": all_hardware_patchset["Graphics"]["Catalina GVA"]})
required_patches.update({"Nvidia Kepler": all_hardware_patchset["Graphics"]["Nvidia Kepler"]})
for gpu in self.constants.computer.gpus:
Expand Down