mirror of
https://github.com/dortania/OpenCore-Legacy-Patcher.git
synced 2026-04-22 19:10:15 +10:00
Rework Kernel Cache management
This commit is contained in:
@@ -17,15 +17,14 @@ from .. import constants
|
|||||||
|
|
||||||
from ..wx_gui import gui_entry
|
from ..wx_gui import gui_entry
|
||||||
from ..efi_builder import build
|
from ..efi_builder import build
|
||||||
|
from ..sys_patch import sys_patch
|
||||||
|
from ..sys_patch.auto_patcher import StartAutomaticPatching
|
||||||
|
|
||||||
from ..datasets import (
|
from ..datasets import (
|
||||||
model_array,
|
model_array,
|
||||||
os_data
|
os_data
|
||||||
)
|
)
|
||||||
from ..sys_patch import (
|
|
||||||
sys_patch,
|
|
||||||
sys_patch_auto
|
|
||||||
)
|
|
||||||
from . import (
|
from . import (
|
||||||
utilities,
|
utilities,
|
||||||
defaults,
|
defaults,
|
||||||
@@ -118,7 +117,7 @@ class arguments:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
logging.info("Set Auto patching")
|
logging.info("Set Auto patching")
|
||||||
sys_patch_auto.AutomaticSysPatch(self.constants).start_auto_patch()
|
StartAutomaticPatching(self.constants).start_auto_patch()
|
||||||
|
|
||||||
|
|
||||||
def _prepare_for_update_handler(self) -> None:
|
def _prepare_for_update_handler(self) -> None:
|
||||||
|
|||||||
17
opencore_legacy_patcher/sys_patch/auto_patcher/__init__.py
Normal file
17
opencore_legacy_patcher/sys_patch/auto_patcher/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
"""
|
||||||
|
auto_patcher: Automatic system volume patching after updates, etc.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
>>> # Installing launch services
|
||||||
|
>>> from auto_patcher import InstallAutomaticPatchingServices
|
||||||
|
>>> InstallAutomaticPatchingServices(self.constants).install_auto_patcher_launch_agent()
|
||||||
|
|
||||||
|
|
||||||
|
>>> # When patching the system volume (ex. launch service)
|
||||||
|
>>> from auto_patcher import StartAutomaticPatching
|
||||||
|
>>> StartAutomaticPatching(self.constants).start_auto_patch()
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .install import InstallAutomaticPatchingServices
|
||||||
|
from .start import StartAutomaticPatching
|
||||||
116
opencore_legacy_patcher/sys_patch/auto_patcher/install.py
Normal file
116
opencore_legacy_patcher/sys_patch/auto_patcher/install.py
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
"""
|
||||||
|
install.py: Install the auto patcher launch services
|
||||||
|
"""
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import logging
|
||||||
|
import plistlib
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from ... import constants
|
||||||
|
|
||||||
|
from ...volume import generate_copy_arguments
|
||||||
|
|
||||||
|
from ...support import (
|
||||||
|
utilities,
|
||||||
|
subprocess_wrapper
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class InstallAutomaticPatchingServices:
|
||||||
|
"""
|
||||||
|
Install the auto patcher launch services
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, global_constants: constants.Constants):
|
||||||
|
self.constants: constants.Constants = global_constants
|
||||||
|
|
||||||
|
|
||||||
|
def install_auto_patcher_launch_agent(self, kdk_caching_needed: bool = False):
|
||||||
|
"""
|
||||||
|
Install patcher launch services
|
||||||
|
|
||||||
|
See start_auto_patch() comments for more info
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.constants.launcher_script is not None:
|
||||||
|
logging.info("- Skipping Auto Patcher Launch Agent, not supported when running from source")
|
||||||
|
return
|
||||||
|
|
||||||
|
services = {
|
||||||
|
self.constants.auto_patch_launch_agent_path: "/Library/LaunchAgents/com.dortania.opencore-legacy-patcher.auto-patch.plist",
|
||||||
|
self.constants.update_launch_daemon_path: "/Library/LaunchDaemons/com.dortania.opencore-legacy-patcher.macos-update.plist",
|
||||||
|
**({ self.constants.rsr_monitor_launch_daemon_path: "/Library/LaunchDaemons/com.dortania.opencore-legacy-patcher.rsr-monitor.plist" } if self._create_rsr_monitor_daemon() else {}),
|
||||||
|
**({ self.constants.kdk_launch_daemon_path: "/Library/LaunchDaemons/com.dortania.opencore-legacy-patcher.os-caching.plist" } if kdk_caching_needed is True else {} ),
|
||||||
|
}
|
||||||
|
|
||||||
|
for service in services:
|
||||||
|
name = Path(service).name
|
||||||
|
logging.info(f"- Installing {name}")
|
||||||
|
if Path(services[service]).exists():
|
||||||
|
if hashlib.sha256(open(service, "rb").read()).hexdigest() == hashlib.sha256(open(services[service], "rb").read()).hexdigest():
|
||||||
|
logging.info(f" - {name} checksums match, skipping")
|
||||||
|
continue
|
||||||
|
logging.info(f" - Existing service found, removing")
|
||||||
|
subprocess_wrapper.run_as_root_and_verify(["/bin/rm", services[service]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
# Create parent directories
|
||||||
|
if not Path(services[service]).parent.exists():
|
||||||
|
logging.info(f" - Creating {Path(services[service]).parent} directory")
|
||||||
|
subprocess_wrapper.run_as_root_and_verify(["/bin/mkdir", "-p", Path(services[service]).parent], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
subprocess_wrapper.run_as_root_and_verify(generate_copy_arguments(service, services[service]), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
|
# Set the permissions on the service
|
||||||
|
subprocess_wrapper.run_as_root_and_verify(["/bin/chmod", "644", services[service]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
subprocess_wrapper.run_as_root_and_verify(["/usr/sbin/chown", "root:wheel", services[service]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
|
|
||||||
|
def _create_rsr_monitor_daemon(self) -> bool:
|
||||||
|
# Get kext list in /Library/Extensions that have the 'GPUCompanionBundles' property
|
||||||
|
# This is used to determine if we need to run the RSRMonitor
|
||||||
|
logging.info("- Checking if RSRMonitor is needed")
|
||||||
|
|
||||||
|
cryptex_path = f"/System/Volumes/Preboot/{utilities.get_preboot_uuid()}/cryptex1/current/OS.dmg"
|
||||||
|
if not Path(cryptex_path).exists():
|
||||||
|
logging.info("- No OS.dmg, skipping RSRMonitor")
|
||||||
|
return False
|
||||||
|
|
||||||
|
kexts = []
|
||||||
|
for kext in Path("/Library/Extensions").glob("*.kext"):
|
||||||
|
if not Path(f"{kext}/Contents/Info.plist").exists():
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
kext_plist = plistlib.load(open(f"{kext}/Contents/Info.plist", "rb"))
|
||||||
|
except Exception as e:
|
||||||
|
logging.info(f" - Failed to load plist for {kext.name}: {e}")
|
||||||
|
continue
|
||||||
|
if "GPUCompanionBundles" not in kext_plist:
|
||||||
|
continue
|
||||||
|
logging.info(f" - Found kext with GPUCompanionBundles: {kext.name}")
|
||||||
|
kexts.append(kext.name)
|
||||||
|
|
||||||
|
# If we have no kexts, we don't need to run the RSRMonitor
|
||||||
|
if not kexts:
|
||||||
|
logging.info("- No kexts found with GPUCompanionBundles, skipping RSRMonitor")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Load the RSRMonitor plist
|
||||||
|
rsr_monitor_plist = plistlib.load(open(self.constants.rsr_monitor_launch_daemon_path, "rb"))
|
||||||
|
|
||||||
|
arguments = ["/bin/rm", "-Rfv"]
|
||||||
|
arguments += [f"/Library/Extensions/{kext}" for kext in kexts]
|
||||||
|
|
||||||
|
# Add the arguments to the RSRMonitor plist
|
||||||
|
rsr_monitor_plist["ProgramArguments"] = arguments
|
||||||
|
|
||||||
|
# Next add monitoring for '/System/Volumes/Preboot/{UUID}/cryptex1/OS.dmg'
|
||||||
|
logging.info(f" - Adding monitor: {cryptex_path}")
|
||||||
|
rsr_monitor_plist["WatchPaths"] = [
|
||||||
|
cryptex_path,
|
||||||
|
]
|
||||||
|
|
||||||
|
# Write the RSRMonitor plist
|
||||||
|
plistlib.dump(rsr_monitor_plist, Path(self.constants.rsr_monitor_launch_daemon_path).open("wb"))
|
||||||
|
|
||||||
|
return True
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
sys_patch_auto.py: Library of functions for launch services, including automatic patching
|
start.py: Start automatic patching of host
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import wx
|
import wx
|
||||||
import wx.html2
|
import wx.html2
|
||||||
|
|
||||||
import hashlib
|
|
||||||
import logging
|
import logging
|
||||||
import plistlib
|
import plistlib
|
||||||
import requests
|
import requests
|
||||||
@@ -13,31 +12,27 @@ import markdown2
|
|||||||
import subprocess
|
import subprocess
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
|
||||||
from pathlib import Path
|
from .. import sys_patch_detect
|
||||||
|
|
||||||
from . import sys_patch_detect
|
from ... import constants
|
||||||
|
|
||||||
from .. import constants
|
from ...datasets import css_data
|
||||||
|
|
||||||
from ..datasets import css_data
|
from ...wx_gui import (
|
||||||
from ..volume import generate_copy_arguments
|
|
||||||
|
|
||||||
from ..wx_gui import (
|
|
||||||
gui_entry,
|
gui_entry,
|
||||||
gui_support
|
gui_support
|
||||||
)
|
)
|
||||||
from ..support import (
|
from ...support import (
|
||||||
utilities,
|
utilities,
|
||||||
updates,
|
updates,
|
||||||
global_settings,
|
global_settings,
|
||||||
network_handler,
|
network_handler,
|
||||||
subprocess_wrapper
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AutomaticSysPatch:
|
class StartAutomaticPatching:
|
||||||
"""
|
"""
|
||||||
Library of functions for launch agent, including automatic patching
|
Start automatic patching of host
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, global_constants: constants.Constants):
|
def __init__(self, global_constants: constants.Constants):
|
||||||
@@ -318,91 +313,3 @@ Please check the Github page for more information about this release."""
|
|||||||
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
logging.info("- Unable to determine if boot disk is removable, skipping prompt")
|
logging.info("- Unable to determine if boot disk is removable, skipping prompt")
|
||||||
|
|
||||||
|
|
||||||
def install_auto_patcher_launch_agent(self, kdk_caching_needed: bool = False):
|
|
||||||
"""
|
|
||||||
Install patcher launch services
|
|
||||||
|
|
||||||
See start_auto_patch() comments for more info
|
|
||||||
"""
|
|
||||||
|
|
||||||
if self.constants.launcher_script is not None:
|
|
||||||
logging.info("- Skipping Auto Patcher Launch Agent, not supported when running from source")
|
|
||||||
return
|
|
||||||
|
|
||||||
services = {
|
|
||||||
self.constants.auto_patch_launch_agent_path: "/Library/LaunchAgents/com.dortania.opencore-legacy-patcher.auto-patch.plist",
|
|
||||||
self.constants.update_launch_daemon_path: "/Library/LaunchDaemons/com.dortania.opencore-legacy-patcher.macos-update.plist",
|
|
||||||
**({ self.constants.rsr_monitor_launch_daemon_path: "/Library/LaunchDaemons/com.dortania.opencore-legacy-patcher.rsr-monitor.plist" } if self._create_rsr_monitor_daemon() else {}),
|
|
||||||
**({ self.constants.kdk_launch_daemon_path: "/Library/LaunchDaemons/com.dortania.opencore-legacy-patcher.os-caching.plist" } if kdk_caching_needed is True else {} ),
|
|
||||||
}
|
|
||||||
|
|
||||||
for service in services:
|
|
||||||
name = Path(service).name
|
|
||||||
logging.info(f"- Installing {name}")
|
|
||||||
if Path(services[service]).exists():
|
|
||||||
if hashlib.sha256(open(service, "rb").read()).hexdigest() == hashlib.sha256(open(services[service], "rb").read()).hexdigest():
|
|
||||||
logging.info(f" - {name} checksums match, skipping")
|
|
||||||
continue
|
|
||||||
logging.info(f" - Existing service found, removing")
|
|
||||||
subprocess_wrapper.run_as_root_and_verify(["/bin/rm", services[service]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
||||||
# Create parent directories
|
|
||||||
if not Path(services[service]).parent.exists():
|
|
||||||
logging.info(f" - Creating {Path(services[service]).parent} directory")
|
|
||||||
subprocess_wrapper.run_as_root_and_verify(["/bin/mkdir", "-p", Path(services[service]).parent], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
||||||
subprocess_wrapper.run_as_root_and_verify(generate_copy_arguments(service, services[service]), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
||||||
|
|
||||||
# Set the permissions on the service
|
|
||||||
subprocess_wrapper.run_as_root_and_verify(["/bin/chmod", "644", services[service]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
||||||
subprocess_wrapper.run_as_root_and_verify(["/usr/sbin/chown", "root:wheel", services[service]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
||||||
|
|
||||||
|
|
||||||
def _create_rsr_monitor_daemon(self) -> bool:
|
|
||||||
# Get kext list in /Library/Extensions that have the 'GPUCompanionBundles' property
|
|
||||||
# This is used to determine if we need to run the RSRMonitor
|
|
||||||
logging.info("- Checking if RSRMonitor is needed")
|
|
||||||
|
|
||||||
cryptex_path = f"/System/Volumes/Preboot/{utilities.get_preboot_uuid()}/cryptex1/current/OS.dmg"
|
|
||||||
if not Path(cryptex_path).exists():
|
|
||||||
logging.info("- No OS.dmg, skipping RSRMonitor")
|
|
||||||
return False
|
|
||||||
|
|
||||||
kexts = []
|
|
||||||
for kext in Path("/Library/Extensions").glob("*.kext"):
|
|
||||||
if not Path(f"{kext}/Contents/Info.plist").exists():
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
kext_plist = plistlib.load(open(f"{kext}/Contents/Info.plist", "rb"))
|
|
||||||
except Exception as e:
|
|
||||||
logging.info(f" - Failed to load plist for {kext.name}: {e}")
|
|
||||||
continue
|
|
||||||
if "GPUCompanionBundles" not in kext_plist:
|
|
||||||
continue
|
|
||||||
logging.info(f" - Found kext with GPUCompanionBundles: {kext.name}")
|
|
||||||
kexts.append(kext.name)
|
|
||||||
|
|
||||||
# If we have no kexts, we don't need to run the RSRMonitor
|
|
||||||
if not kexts:
|
|
||||||
logging.info("- No kexts found with GPUCompanionBundles, skipping RSRMonitor")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Load the RSRMonitor plist
|
|
||||||
rsr_monitor_plist = plistlib.load(open(self.constants.rsr_monitor_launch_daemon_path, "rb"))
|
|
||||||
|
|
||||||
arguments = ["/bin/rm", "-Rfv"]
|
|
||||||
arguments += [f"/Library/Extensions/{kext}" for kext in kexts]
|
|
||||||
|
|
||||||
# Add the arguments to the RSRMonitor plist
|
|
||||||
rsr_monitor_plist["ProgramArguments"] = arguments
|
|
||||||
|
|
||||||
# Next add monitoring for '/System/Volumes/Preboot/{UUID}/cryptex1/OS.dmg'
|
|
||||||
logging.info(f" - Adding monitor: {cryptex_path}")
|
|
||||||
rsr_monitor_plist["WatchPaths"] = [
|
|
||||||
cryptex_path,
|
|
||||||
]
|
|
||||||
|
|
||||||
# Write the RSRMonitor plist
|
|
||||||
plistlib.dump(rsr_monitor_plist, Path(self.constants.rsr_monitor_launch_daemon_path).open("wb"))
|
|
||||||
|
|
||||||
return True
|
|
||||||
11
opencore_legacy_patcher/sys_patch/kernelcache/__init__.py
Normal file
11
opencore_legacy_patcher/sys_patch/kernelcache/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
"""
|
||||||
|
kernelcache: Library for rebuilding macOS kernelcache files.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
>>> from kernelcache import RebuildKernelCache
|
||||||
|
>>> RebuildKernelCache(os_version, mount_location, auxiliary_cache, auxiliary_cache_only).rebuild()
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .rebuild import RebuildKernelCache
|
||||||
|
from .kernel_collection.support import KernelCacheSupport
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
"""
|
||||||
|
cache.py: Base class for kernel cache management
|
||||||
|
"""
|
||||||
|
|
||||||
|
class BaseKernelCache:
|
||||||
|
|
||||||
|
def rebuild(self) -> None:
|
||||||
|
raise NotImplementedError("To be implemented in subclass")
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
"""
|
||||||
|
auxiliary.py: Auxiliary Kernel Collection management
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from ..base.cache import BaseKernelCache
|
||||||
|
from ....support import subprocess_wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class AuxiliaryKernelCollection(BaseKernelCache):
|
||||||
|
|
||||||
|
def __init__(self, mount_location: str) -> None:
|
||||||
|
self.mount_location = mount_location
|
||||||
|
|
||||||
|
|
||||||
|
def _kmutil_arguments(self) -> list[str]:
|
||||||
|
args = ["/usr/bin/kmutil", "create", "--allow-missing-kdk"]
|
||||||
|
|
||||||
|
args.append("--new")
|
||||||
|
args.append("aux")
|
||||||
|
|
||||||
|
args.append("--boot-path")
|
||||||
|
args.append(f"{self.mount_location}/System/Library/KernelCollections/BootKernelExtensions.kc")
|
||||||
|
|
||||||
|
args.append("--system-path")
|
||||||
|
args.append(f"{self.mount_location}/System/Library/KernelCollections/SystemKernelExtensions.kc")
|
||||||
|
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def _force_auxiliary_usage(self) -> bool:
|
||||||
|
"""
|
||||||
|
Force the auxiliary kernel collection to be used.
|
||||||
|
|
||||||
|
This is required as Apple doesn't offer a public way
|
||||||
|
to rebuild the auxiliary kernel collection. Instead deleting
|
||||||
|
necessary files and directories will force the newly built
|
||||||
|
collection to be used.
|
||||||
|
"""
|
||||||
|
|
||||||
|
print("- Forcing Auxiliary Kernel Collection usage")
|
||||||
|
result = subprocess_wrapper.run_as_root(["/usr/bin/killall", "syspolicyd", "kernelmanagerd"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
if result.returncode != 0:
|
||||||
|
logging.info("- Unable to kill syspolicyd and kernelmanagerd")
|
||||||
|
subprocess_wrapper.log(result)
|
||||||
|
return False
|
||||||
|
|
||||||
|
for file in ["KextPolicy", "KextPolicy-shm", "KextPolicy-wal"]:
|
||||||
|
result = subprocess_wrapper.run_as_root(["/bin/rm", f"/private/var/db/SystemPolicyConfiguration/{file}"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
if result.returncode != 0:
|
||||||
|
logging.info(f"- Unable to remove {file}")
|
||||||
|
subprocess_wrapper.log(result)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def rebuild(self) -> None:
|
||||||
|
logging.info("- Building new Auxiliary Kernel Collection")
|
||||||
|
result = subprocess_wrapper.run_as_root(self._kmutil_arguments(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
if result.returncode != 0:
|
||||||
|
logging.info("- Unable to build Auxiliary Kernel Collection")
|
||||||
|
subprocess_wrapper.log(result)
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self._force_auxiliary_usage() is False:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
"""
|
||||||
|
boot_system.py: Boot and System Kernel Collection management
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from ..base.cache import BaseKernelCache
|
||||||
|
from ....support import subprocess_wrapper
|
||||||
|
from ....datasets import os_data
|
||||||
|
|
||||||
|
|
||||||
|
class BootSystemKernelCollections(BaseKernelCache):
|
||||||
|
|
||||||
|
def __init__(self, mount_location: str, detected_os: int, auxiliary_kc: bool) -> None:
|
||||||
|
self.mount_location = mount_location
|
||||||
|
self.detected_os = detected_os
|
||||||
|
self.auxiliary_kc = auxiliary_kc
|
||||||
|
|
||||||
|
|
||||||
|
def _kmutil_arguments(self) -> list[str]:
|
||||||
|
"""
|
||||||
|
Generate kmutil arguments for creating or updating
|
||||||
|
the boot, system and auxiliary kernel collections
|
||||||
|
"""
|
||||||
|
|
||||||
|
args = ["/usr/bin/kmutil"]
|
||||||
|
|
||||||
|
if self.detected_os >= os_data.os_data.ventura:
|
||||||
|
args.append("create")
|
||||||
|
args.append("--allow-missing-kdk")
|
||||||
|
else:
|
||||||
|
args.append("install")
|
||||||
|
|
||||||
|
args.append("--volume-root")
|
||||||
|
args.append(self.mount_location)
|
||||||
|
|
||||||
|
args.append("--update-all")
|
||||||
|
|
||||||
|
args.append("--variant-suffix")
|
||||||
|
args.append("release")
|
||||||
|
|
||||||
|
if self.auxiliary_kc is True:
|
||||||
|
# Following arguments are supposed to skip kext consent
|
||||||
|
# prompts when creating auxiliary KCs with SIP disabled
|
||||||
|
args.append("--no-authentication")
|
||||||
|
args.append("--no-authorization")
|
||||||
|
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def rebuild(self) -> bool:
|
||||||
|
logging.info(f"- Rebuilding {'Boot and System' if self.auxiliary_kc is False else 'Boot, System and Auxiliary'} Kernel Collections")
|
||||||
|
if self.auxiliary_kc is True:
|
||||||
|
logging.info(" (You will get a prompt by System Preferences, ignore for now)")
|
||||||
|
|
||||||
|
result = subprocess_wrapper.run_as_root(self._kmutil_arguments(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
if result.returncode != 0:
|
||||||
|
subprocess_wrapper.log(result)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
"""
|
||||||
|
support.py: Kernel Cache support functions
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import plistlib
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from ....datasets import os_data
|
||||||
|
from ....support import subprocess_wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class KernelCacheSupport:
|
||||||
|
|
||||||
|
def __init__(self, mount_location_data: str, detected_os: int, skip_root_kmutil_requirement: bool) -> None:
|
||||||
|
self.mount_location_data = mount_location_data
|
||||||
|
self.detected_os = detected_os
|
||||||
|
self.skip_root_kmutil_requirement = skip_root_kmutil_requirement
|
||||||
|
|
||||||
|
|
||||||
|
def check_kexts_needs_authentication(self, kext_name: str) -> bool:
|
||||||
|
"""
|
||||||
|
Verify whether the user needs to authenticate in System Preferences
|
||||||
|
Sets 'needs_to_open_preferences' to True if the kext is not in the AuxKC
|
||||||
|
|
||||||
|
Logic:
|
||||||
|
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/KernelExtensionManagement/AuxKC/CurrentAuxKC/com.apple.kcgen.instructions.plist")
|
||||||
|
if aux_cache_path.exists():
|
||||||
|
aux_cache_data = plistlib.load((aux_cache_path).open("rb"))
|
||||||
|
for kext in aux_cache_data["kextsToBuild"]:
|
||||||
|
if "bundlePathMainOS" in aux_cache_data["kextsToBuild"][kext]:
|
||||||
|
if aux_cache_data["kextsToBuild"][kext]["bundlePathMainOS"] == f"/Library/Extensions/{kext_name}":
|
||||||
|
return False
|
||||||
|
except PermissionError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
logging.info(f" - {kext_name} requires authentication in System Preferences")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def add_auxkc_support(self, install_file: str, source_folder_path: str, install_patch_directory: str, destination_folder_path: str) -> str:
|
||||||
|
"""
|
||||||
|
Patch provided Kext to support Auxiliary Kernel Collection
|
||||||
|
|
||||||
|
Logic:
|
||||||
|
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'
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
install_file (str): Kext file name
|
||||||
|
source_folder_path (str): Source folder path
|
||||||
|
install_patch_directory (str): Patch directory
|
||||||
|
destination_folder_path (str): Destination folder path
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Updated destination folder path
|
||||||
|
"""
|
||||||
|
|
||||||
|
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.detected_os < os_data.os_data.ventura:
|
||||||
|
return destination_folder_path
|
||||||
|
|
||||||
|
updated_install_location = str(self.mount_location_data) + "/Library/Extensions"
|
||||||
|
|
||||||
|
logging.info(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"))
|
||||||
|
|
||||||
|
return updated_install_location
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def clean_auxiliary_kc(self) -> None:
|
||||||
|
"""
|
||||||
|
Clean the Auxiliary Kernel Collection
|
||||||
|
|
||||||
|
Logic:
|
||||||
|
When reverting root volume patches, the AuxKC will still retain the UUID
|
||||||
|
it was built against. Thus when Boot/SysKC are reverted, Aux will break
|
||||||
|
To resolve this, delete all installed kexts in /L*/E* and rebuild the AuxKC
|
||||||
|
We can verify our binaries based off the OpenCore-Legacy-Patcher.plist file
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.detected_os < os_data.os_data.big_sur:
|
||||||
|
return
|
||||||
|
|
||||||
|
logging.info("- Cleaning Auxiliary Kernel Collection")
|
||||||
|
oclp_path = "/System/Library/CoreServices/OpenCore-Legacy-Patcher.plist"
|
||||||
|
if Path(oclp_path).exists():
|
||||||
|
oclp_plist_data = plistlib.load(Path(oclp_path).open("rb"))
|
||||||
|
for key in oclp_plist_data:
|
||||||
|
if isinstance(oclp_plist_data[key], (bool, int)):
|
||||||
|
continue
|
||||||
|
for install_type in ["Install", "Install Non-Root"]:
|
||||||
|
if install_type not in oclp_plist_data[key]:
|
||||||
|
continue
|
||||||
|
for location in oclp_plist_data[key][install_type]:
|
||||||
|
if not location.endswith("Extensions"):
|
||||||
|
continue
|
||||||
|
for file in oclp_plist_data[key][install_type][location]:
|
||||||
|
if not file.endswith(".kext"):
|
||||||
|
continue
|
||||||
|
if not Path(f"/Library/Extensions/{file}").exists():
|
||||||
|
continue
|
||||||
|
logging.info(f" - Removing {file}")
|
||||||
|
subprocess_wrapper.run_as_root(["/bin/rm", "-Rf", f"/Library/Extensions/{file}"])
|
||||||
|
|
||||||
|
# Handle situations where users migrated from older OSes with a lot of garbage in /L*/E*
|
||||||
|
# ex. Nvidia Web Drivers, NetUSB, dosdude1's patches, etc.
|
||||||
|
# Move if file's age is older than October 2021 (year before Ventura)
|
||||||
|
if self.detected_os < os_data.os_data.ventura:
|
||||||
|
return
|
||||||
|
|
||||||
|
relocation_path = "/Library/Relocated Extensions"
|
||||||
|
if not Path(relocation_path).exists():
|
||||||
|
subprocess_wrapper.run_as_root(["/bin/mkdir", relocation_path])
|
||||||
|
|
||||||
|
for file in Path("/Library/Extensions").glob("*.kext"):
|
||||||
|
try:
|
||||||
|
if datetime.fromtimestamp(file.stat().st_mtime) < datetime(2021, 10, 1):
|
||||||
|
logging.info(f" - Relocating {file.name} kext to {relocation_path}")
|
||||||
|
if Path(relocation_path) / Path(file.name).exists():
|
||||||
|
subprocess_wrapper.run_as_root(["/bin/rm", "-Rf", relocation_path / Path(file.name)])
|
||||||
|
subprocess_wrapper.run_as_root(["/bin/mv", file, relocation_path])
|
||||||
|
except:
|
||||||
|
# Some users have the most cursed /L*/E* folders
|
||||||
|
# ex. Symlinks pointing to symlinks pointing to dead files
|
||||||
|
pass
|
||||||
32
opencore_legacy_patcher/sys_patch/kernelcache/mkext/mkext.py
Normal file
32
opencore_legacy_patcher/sys_patch/kernelcache/mkext/mkext.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
"""
|
||||||
|
mkext.py: MKext cache management
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from ..base.cache import BaseKernelCache
|
||||||
|
|
||||||
|
from ....support import subprocess_wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class MKext(BaseKernelCache):
|
||||||
|
|
||||||
|
def __init__(self, mount_location: str) -> None:
|
||||||
|
self.mount_location = mount_location
|
||||||
|
|
||||||
|
|
||||||
|
def _mkext_arguments(self) -> list[str]:
|
||||||
|
args = ["/usr/bin/touch", f"{self.mount_location}/System/Library/Extensions"]
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def rebuild(self) -> None:
|
||||||
|
logging.info("- Rebuilding MKext cache")
|
||||||
|
result = subprocess_wrapper.run_as_root(self._mkext_arguments(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
subprocess_wrapper.log(result)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
"""
|
||||||
|
prelinked.py: Prelinked Kernel cache management
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from ..base.cache import BaseKernelCache
|
||||||
|
from ....support import subprocess_wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class PrelinkedKernel(BaseKernelCache):
|
||||||
|
|
||||||
|
def __init__(self, mount_location: str) -> None:
|
||||||
|
self.mount_location = mount_location
|
||||||
|
|
||||||
|
|
||||||
|
def _kextcache_arguments(self) -> list[str]:
|
||||||
|
args = ["/usr/sbin/kextcache", "-invalidate", f"{self.mount_location}/"]
|
||||||
|
return args
|
||||||
|
|
||||||
|
def _update_preboot_kernel_cache(self) -> bool:
|
||||||
|
"""
|
||||||
|
Ensure Preboot volume's kernel cache is updated
|
||||||
|
"""
|
||||||
|
if not Path("/usr/sbin/kcditto").exists():
|
||||||
|
return
|
||||||
|
|
||||||
|
logging.info("- Syncing Kernel Cache to Preboot")
|
||||||
|
subprocess_wrapper.run_as_root_and_verify(["/usr/sbin/kcditto"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
|
|
||||||
|
def rebuild(self) -> None:
|
||||||
|
logging.info("- Rebuilding Prelinked Kernel")
|
||||||
|
result = subprocess_wrapper.run_as_root(self._kextcache_arguments(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
|
# kextcache notes:
|
||||||
|
# - kextcache always returns 0, even if it fails
|
||||||
|
# - Check the output for 'KernelCache ID' to see if the cache was successfully rebuilt
|
||||||
|
if "KernelCache ID" not in result.stdout.decode():
|
||||||
|
subprocess_wrapper.log(result)
|
||||||
|
return False
|
||||||
|
|
||||||
|
self._update_preboot_kernel_cache()
|
||||||
|
|
||||||
|
return True
|
||||||
51
opencore_legacy_patcher/sys_patch/kernelcache/rebuild.py
Normal file
51
opencore_legacy_patcher/sys_patch/kernelcache/rebuild.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
"""
|
||||||
|
rebuild.py: Manage kernel cache rebuilding regardless of macOS version
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .base.cache import BaseKernelCache
|
||||||
|
from ...datasets import os_data
|
||||||
|
|
||||||
|
|
||||||
|
class RebuildKernelCache:
|
||||||
|
"""
|
||||||
|
RebuildKernelCache: Rebuild the kernel cache
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- os_version: macOS version
|
||||||
|
- mount_location: Path to the mounted volume
|
||||||
|
- auxiliary_cache: Whether to create auxiliary kernel cache (Big Sur and later)
|
||||||
|
- auxiliary_cache_only: Whether to only create auxiliary kernel cache (Ventura and later)
|
||||||
|
"""
|
||||||
|
def __init__(self, os_version: os_data.os_data, mount_location: str, auxiliary_cache: bool, auxiliary_cache_only: bool) -> None:
|
||||||
|
self.os_version = os_version
|
||||||
|
self.mount_location = mount_location
|
||||||
|
self.auxiliary_cache = auxiliary_cache
|
||||||
|
self.auxiliary_cache_only = auxiliary_cache_only
|
||||||
|
|
||||||
|
|
||||||
|
def _rebuild_method(self) -> BaseKernelCache:
|
||||||
|
"""
|
||||||
|
Determine the correct method to rebuild the kernel cache
|
||||||
|
"""
|
||||||
|
if self.os_version >= os_data.os_data.big_sur:
|
||||||
|
if self.os_version >= os_data.os_data.ventura:
|
||||||
|
if self.auxiliary_cache_only:
|
||||||
|
from .kernel_collection.auxiliary import AuxiliaryKernelCollection
|
||||||
|
return AuxiliaryKernelCollection(self.mount_location)
|
||||||
|
|
||||||
|
from .kernel_collection.boot_system import BootSystemKernelCollections
|
||||||
|
return BootSystemKernelCollections(self.mount_location, self.os_version, self.auxiliary_cache)
|
||||||
|
|
||||||
|
if os_data.os_data.catalina >= self.os_version >= os_data.os_data.lion:
|
||||||
|
from .prelinked.prelinked import PrelinkedKernel
|
||||||
|
return PrelinkedKernel(self.mount_location)
|
||||||
|
|
||||||
|
from .mkext.mkext import MKext
|
||||||
|
return MKext(self.mount_location)
|
||||||
|
|
||||||
|
|
||||||
|
def rebuild(self) -> bool:
|
||||||
|
"""
|
||||||
|
Rebuild the kernel cache
|
||||||
|
"""
|
||||||
|
return self._rebuild_method().rebuild()
|
||||||
@@ -55,11 +55,12 @@ from ..support import (
|
|||||||
)
|
)
|
||||||
from . import (
|
from . import (
|
||||||
sys_patch_detect,
|
sys_patch_detect,
|
||||||
sys_patch_auto,
|
|
||||||
sys_patch_helpers,
|
sys_patch_helpers,
|
||||||
sys_patch_generate,
|
sys_patch_generate,
|
||||||
sys_patch_mount
|
sys_patch_mount,
|
||||||
|
kernelcache
|
||||||
)
|
)
|
||||||
|
from .auto_patcher import InstallAutomaticPatchingServices
|
||||||
|
|
||||||
|
|
||||||
class PatchSysVolume:
|
class PatchSysVolume:
|
||||||
@@ -270,7 +271,13 @@ class PatchSysVolume:
|
|||||||
|
|
||||||
self._clean_skylight_plugins()
|
self._clean_skylight_plugins()
|
||||||
self._delete_nonmetal_enforcement()
|
self._delete_nonmetal_enforcement()
|
||||||
self._clean_auxiliary_kc()
|
|
||||||
|
kernelcache.KernelCacheSupport(
|
||||||
|
mount_location_data=self.mount_location_data,
|
||||||
|
detected_os=self.constants.detected_os,
|
||||||
|
skip_root_kmutil_requirement=self.skip_root_kmutil_requirement
|
||||||
|
).clean_auxiliary_kc()
|
||||||
|
|
||||||
self.constants.root_patcher_succeeded = True
|
self.constants.root_patcher_succeeded = True
|
||||||
logging.info("- Unpatching complete")
|
logging.info("- Unpatching complete")
|
||||||
logging.info("\nPlease reboot the machine for patches to take effect")
|
logging.info("\nPlease reboot the machine for patches to take effect")
|
||||||
@@ -315,93 +322,12 @@ class PatchSysVolume:
|
|||||||
bool: True if successful, False if not
|
bool: True if successful, False if not
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logging.info("- Rebuilding Kernel Cache (This may take some time)")
|
return kernelcache.RebuildKernelCache(
|
||||||
if self.constants.detected_os > os_data.os_data.catalina:
|
os_version=self.constants.detected_os,
|
||||||
# Base Arguments
|
mount_location=self.mount_location,
|
||||||
args = ["/usr/bin/kmutil", "install"]
|
auxiliary_cache=self.needs_kmutil_exemptions,
|
||||||
|
auxiliary_cache_only=self.skip_root_kmutil_requirement
|
||||||
if self.skip_root_kmutil_requirement is True:
|
).rebuild()
|
||||||
# Only rebuild the Auxiliary Kernel Collection
|
|
||||||
args.append("--new")
|
|
||||||
args.append("aux")
|
|
||||||
|
|
||||||
args.append("--boot-path")
|
|
||||||
args.append(f"{self.mount_location}/System/Library/KernelCollections/BootKernelExtensions.kc")
|
|
||||||
|
|
||||||
args.append("--system-path")
|
|
||||||
args.append(f"{self.mount_location}/System/Library/KernelCollections/SystemKernelExtensions.kc")
|
|
||||||
else:
|
|
||||||
# Rebuild Boot, System and Auxiliary Kernel Collections
|
|
||||||
args.append("--volume-root")
|
|
||||||
args.append(self.mount_location)
|
|
||||||
|
|
||||||
# Build Boot, Sys and Aux KC
|
|
||||||
args.append("--update-all")
|
|
||||||
|
|
||||||
# If multiple kernels found, only build release KCs
|
|
||||||
args.append("--variant-suffix")
|
|
||||||
args.append("release")
|
|
||||||
|
|
||||||
if self.constants.detected_os >= os_data.os_data.ventura:
|
|
||||||
# With Ventura, we're required to provide a KDK in some form
|
|
||||||
# to rebuild the Kernel Cache
|
|
||||||
#
|
|
||||||
# However since we already merged the KDK onto root with 'ditto',
|
|
||||||
# We can add '--allow-missing-kdk' to skip parsing the KDK
|
|
||||||
#
|
|
||||||
# This allows us to only delete/overwrite kexts inside of
|
|
||||||
# /System/Library/Extensions and not the entire KDK
|
|
||||||
args.append("--allow-missing-kdk")
|
|
||||||
|
|
||||||
# 'install' and '--update-all' cannot be used together in Ventura.
|
|
||||||
# kmutil will request the usage of 'create' instead:
|
|
||||||
# Warning: kmutil install's usage of --update-all is deprecated.
|
|
||||||
# Use kmutil create --update-install instead'
|
|
||||||
args[1] = "create"
|
|
||||||
|
|
||||||
if self.needs_kmutil_exemptions is True:
|
|
||||||
# When installing to '/Library/Extensions', following args skip kext consent
|
|
||||||
# prompt in System Preferences when SIP's disabled
|
|
||||||
logging.info(" (You will get a prompt by System Preferences, ignore for now)")
|
|
||||||
args.append("--no-authentication")
|
|
||||||
args.append("--no-authorization")
|
|
||||||
else:
|
|
||||||
args = ["/usr/sbin/kextcache", "-i", f"{self.mount_location}/"]
|
|
||||||
|
|
||||||
result = subprocess_wrapper.run_as_root(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
||||||
|
|
||||||
# kextcache notes:
|
|
||||||
# - kextcache always returns 0, even if it fails
|
|
||||||
# - Check the output for 'KernelCache ID' to see if the cache was successfully rebuilt
|
|
||||||
# kmutil notes:
|
|
||||||
# - will return 71 on failure to build KCs
|
|
||||||
# - will return 31 on 'No binaries or codeless kexts were provided'
|
|
||||||
# - will return -10 if the volume is missing (ie. unmounted by another process)
|
|
||||||
if result.returncode != 0 or (self.constants.detected_os < os_data.os_data.catalina and "KernelCache ID" not in result.stdout.decode()):
|
|
||||||
logging.info("- Unable to build new kernel cache")
|
|
||||||
subprocess_wrapper.log(result)
|
|
||||||
logging.info("")
|
|
||||||
logging.info("\nPlease reboot the machine to avoid potential issues rerunning the patcher")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if self.skip_root_kmutil_requirement is True:
|
|
||||||
# Force rebuild the Auxiliary KC
|
|
||||||
result = subprocess_wrapper.run_as_root(["/usr/bin/killall", "syspolicyd", "kernelmanagerd"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
||||||
if result.returncode != 0:
|
|
||||||
logging.info("- Unable to remove kernel extension policy files")
|
|
||||||
subprocess_wrapper.log(result)
|
|
||||||
logging.info("")
|
|
||||||
logging.info("\nPlease reboot the machine to avoid potential issues rerunning the patcher")
|
|
||||||
return False
|
|
||||||
|
|
||||||
for file in ["KextPolicy", "KextPolicy-shm", "KextPolicy-wal"]:
|
|
||||||
self._remove_file("/private/var/db/SystemPolicyConfiguration/", file)
|
|
||||||
else:
|
|
||||||
# Install RSRHelper utility to handle desynced KCs
|
|
||||||
sys_patch_helpers.SysPatchHelpers(self.constants).install_rsr_repair_binary()
|
|
||||||
|
|
||||||
logging.info("- Successfully built new kernel cache")
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def _create_new_apfs_snapshot(self) -> bool:
|
def _create_new_apfs_snapshot(self) -> bool:
|
||||||
@@ -464,61 +390,6 @@ class PatchSysVolume:
|
|||||||
subprocess_wrapper.run_as_root(["/usr/bin/defaults", "delete", "/Library/Preferences/com.apple.CoreDisplay", arg])
|
subprocess_wrapper.run_as_root(["/usr/bin/defaults", "delete", "/Library/Preferences/com.apple.CoreDisplay", arg])
|
||||||
|
|
||||||
|
|
||||||
def _clean_auxiliary_kc(self) -> None:
|
|
||||||
"""
|
|
||||||
Clean the Auxiliary Kernel Collection
|
|
||||||
|
|
||||||
Logic:
|
|
||||||
When reverting root volume patches, the AuxKC will still retain the UUID
|
|
||||||
it was built against. Thus when Boot/SysKC are reverted, Aux will break
|
|
||||||
To resolve this, delete all installed kexts in /L*/E* and rebuild the AuxKC
|
|
||||||
We can verify our binaries based off the OpenCore-Legacy-Patcher.plist file
|
|
||||||
"""
|
|
||||||
|
|
||||||
if self.constants.detected_os < os_data.os_data.big_sur:
|
|
||||||
return
|
|
||||||
|
|
||||||
logging.info("- Cleaning Auxiliary Kernel Collection")
|
|
||||||
oclp_path = "/System/Library/CoreServices/OpenCore-Legacy-Patcher.plist"
|
|
||||||
if Path(oclp_path).exists():
|
|
||||||
oclp_plist_data = plistlib.load(Path(oclp_path).open("rb"))
|
|
||||||
for key in oclp_plist_data:
|
|
||||||
if isinstance(oclp_plist_data[key], (bool, int)):
|
|
||||||
continue
|
|
||||||
for install_type in ["Install", "Install Non-Root"]:
|
|
||||||
if install_type not in oclp_plist_data[key]:
|
|
||||||
continue
|
|
||||||
for location in oclp_plist_data[key][install_type]:
|
|
||||||
if not location.endswith("Extensions"):
|
|
||||||
continue
|
|
||||||
for file in oclp_plist_data[key][install_type][location]:
|
|
||||||
if not file.endswith(".kext"):
|
|
||||||
continue
|
|
||||||
self._remove_file("/Library/Extensions", file)
|
|
||||||
|
|
||||||
# Handle situations where users migrated from older OSes with a lot of garbage in /L*/E*
|
|
||||||
# ex. Nvidia Web Drivers, NetUSB, dosdude1's patches, etc.
|
|
||||||
# Move if file's age is older than October 2021 (year before Ventura)
|
|
||||||
if self.constants.detected_os < os_data.os_data.ventura:
|
|
||||||
return
|
|
||||||
|
|
||||||
relocation_path = "/Library/Relocated Extensions"
|
|
||||||
if not Path(relocation_path).exists():
|
|
||||||
subprocess_wrapper.run_as_root(["/bin/mkdir", relocation_path])
|
|
||||||
|
|
||||||
for file in Path("/Library/Extensions").glob("*.kext"):
|
|
||||||
try:
|
|
||||||
if datetime.fromtimestamp(file.stat().st_mtime) < datetime(2021, 10, 1):
|
|
||||||
logging.info(f" - Relocating {file.name} kext to {relocation_path}")
|
|
||||||
if Path(relocation_path) / Path(file.name).exists():
|
|
||||||
subprocess_wrapper.run_as_root(["/bin/rm", "-Rf", relocation_path / Path(file.name)])
|
|
||||||
subprocess_wrapper.run_as_root(["/bin/mv", file, relocation_path])
|
|
||||||
except:
|
|
||||||
# Some users have the most cursed /L*/E* folders
|
|
||||||
# ex. Symlinks pointing to symlinks pointing to dead files
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def _write_patchset(self, patchset: dict) -> None:
|
def _write_patchset(self, patchset: dict) -> None:
|
||||||
"""
|
"""
|
||||||
Write patchset information to Root Volume
|
Write patchset information to Root Volume
|
||||||
@@ -537,93 +408,6 @@ class PatchSysVolume:
|
|||||||
subprocess_wrapper.run_as_root_and_verify(generate_copy_arguments(f"{self.constants.payload_path}/{file_name}", destination_path), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
subprocess_wrapper.run_as_root_and_verify(generate_copy_arguments(f"{self.constants.payload_path}/{file_name}", destination_path), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
|
|
||||||
def _add_auxkc_support(self, install_file: str, source_folder_path: str, install_patch_directory: str, destination_folder_path: str) -> str:
|
|
||||||
"""
|
|
||||||
Patch provided Kext to support Auxiliary Kernel Collection
|
|
||||||
|
|
||||||
Logic:
|
|
||||||
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'
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
install_file (str): Kext file name
|
|
||||||
source_folder_path (str): Source folder path
|
|
||||||
install_patch_directory (str): Patch directory
|
|
||||||
destination_folder_path (str): Destination folder path
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: Updated destination folder path
|
|
||||||
"""
|
|
||||||
|
|
||||||
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"
|
|
||||||
|
|
||||||
logging.info(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"))
|
|
||||||
|
|
||||||
self._check_kexts_needs_authentication(install_file)
|
|
||||||
|
|
||||||
return updated_install_location
|
|
||||||
|
|
||||||
|
|
||||||
def _check_kexts_needs_authentication(self, kext_name: str):
|
|
||||||
"""
|
|
||||||
Verify whether the user needs to authenticate in System Preferences
|
|
||||||
Sets 'needs_to_open_preferences' to True if the kext is not in the AuxKC
|
|
||||||
|
|
||||||
Logic:
|
|
||||||
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>'
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
kext_name (str): Name of the kext to check
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
aux_cache_path = Path(self.mount_location_data) / Path("/private/var/db/KernelExtensionManagement/AuxKC/CurrentAuxKC/com.apple.kcgen.instructions.plist")
|
|
||||||
if aux_cache_path.exists():
|
|
||||||
aux_cache_data = plistlib.load((aux_cache_path).open("rb"))
|
|
||||||
for kext in aux_cache_data["kextsToBuild"]:
|
|
||||||
if "bundlePathMainOS" in aux_cache_data["kextsToBuild"][kext]:
|
|
||||||
if aux_cache_data["kextsToBuild"][kext]["bundlePathMainOS"] == f"/Library/Extensions/{kext_name}":
|
|
||||||
return
|
|
||||||
except PermissionError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
logging.info(f" - {kext_name} requires authentication in System Preferences")
|
|
||||||
self.constants.needs_to_open_preferences = True # Notify in GUI to open System Preferences
|
|
||||||
|
|
||||||
|
|
||||||
def _patch_root_vol(self):
|
def _patch_root_vol(self):
|
||||||
"""
|
"""
|
||||||
Patch root volume
|
Patch root volume
|
||||||
@@ -639,7 +423,7 @@ class PatchSysVolume:
|
|||||||
needs_daemon = False
|
needs_daemon = False
|
||||||
if self.constants.detected_os >= os_data.os_data.ventura and self.skip_root_kmutil_requirement is False:
|
if self.constants.detected_os >= os_data.os_data.ventura and self.skip_root_kmutil_requirement is False:
|
||||||
needs_daemon = True
|
needs_daemon = True
|
||||||
sys_patch_auto.AutomaticSysPatch(self.constants).install_auto_patcher_launch_agent(kdk_caching_needed=needs_daemon)
|
InstallAutomaticPatchingServices(self.constants).install_auto_patcher_launch_agent(kdk_caching_needed=needs_daemon)
|
||||||
|
|
||||||
self._rebuild_root_volume()
|
self._rebuild_root_volume()
|
||||||
|
|
||||||
@@ -652,6 +436,12 @@ class PatchSysVolume:
|
|||||||
required_patches (dict): Patchset to execute (generated by sys_patch_generate.GenerateRootPatchSets)
|
required_patches (dict): Patchset to execute (generated by sys_patch_generate.GenerateRootPatchSets)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
kc_support_obj = kernelcache.KernelCacheSupport(
|
||||||
|
mount_location_data=self.mount_location_data,
|
||||||
|
detected_os=self.constants.detected_os,
|
||||||
|
skip_root_kmutil_requirement=self.skip_root_kmutil_requirement
|
||||||
|
)
|
||||||
|
|
||||||
source_files_path = str(self.constants.payload_local_binaries_root_path)
|
source_files_path = str(self.constants.payload_local_binaries_root_path)
|
||||||
self._preflight_checks(required_patches, source_files_path)
|
self._preflight_checks(required_patches, source_files_path)
|
||||||
for patch in required_patches:
|
for patch in required_patches:
|
||||||
@@ -669,7 +459,9 @@ class PatchSysVolume:
|
|||||||
|
|
||||||
|
|
||||||
for method_install in ["Install", "Install Non-Root"]:
|
for method_install in ["Install", "Install Non-Root"]:
|
||||||
if method_install in required_patches[patch]:
|
if method_install not in required_patches[patch]:
|
||||||
|
continue
|
||||||
|
|
||||||
for install_patch_directory in list(required_patches[patch][method_install]):
|
for install_patch_directory in list(required_patches[patch][method_install]):
|
||||||
logging.info(f"- Handling Installs in: {install_patch_directory}")
|
logging.info(f"- Handling Installs in: {install_patch_directory}")
|
||||||
for install_file in list(required_patches[patch][method_install][install_patch_directory]):
|
for install_file in list(required_patches[patch][method_install][install_patch_directory]):
|
||||||
@@ -679,10 +471,15 @@ class PatchSysVolume:
|
|||||||
else:
|
else:
|
||||||
if install_patch_directory == "/Library/Extensions":
|
if install_patch_directory == "/Library/Extensions":
|
||||||
self.needs_kmutil_exemptions = True
|
self.needs_kmutil_exemptions = True
|
||||||
self._check_kexts_needs_authentication(install_file)
|
if kc_support_obj.check_kexts_needs_authentication(install_file) is True:
|
||||||
|
self.constants.needs_to_open_preferences = True
|
||||||
|
|
||||||
destination_folder_path = str(self.mount_location_data) + install_patch_directory
|
destination_folder_path = str(self.mount_location_data) + install_patch_directory
|
||||||
|
|
||||||
updated_destination_folder_path = self._add_auxkc_support(install_file, source_folder_path, install_patch_directory, destination_folder_path)
|
updated_destination_folder_path = kc_support_obj.add_auxkc_support(install_file, source_folder_path, install_patch_directory, destination_folder_path)
|
||||||
|
|
||||||
|
if kc_support_obj.check_kexts_needs_authentication(install_file) is True:
|
||||||
|
self.constants.needs_to_open_preferences = True
|
||||||
|
|
||||||
if destination_folder_path != updated_destination_folder_path:
|
if destination_folder_path != updated_destination_folder_path:
|
||||||
# Update required_patches to reflect the new destination folder path
|
# Update required_patches to reflect the new destination folder path
|
||||||
@@ -729,7 +526,11 @@ class PatchSysVolume:
|
|||||||
# Make sure non-Metal Enforcement preferences are not present
|
# Make sure non-Metal Enforcement preferences are not present
|
||||||
self._delete_nonmetal_enforcement()
|
self._delete_nonmetal_enforcement()
|
||||||
# Make sure we clean old kexts in /L*/E* that are not in the patchset
|
# Make sure we clean old kexts in /L*/E* that are not in the patchset
|
||||||
self._clean_auxiliary_kc()
|
kernelcache.KernelCacheSupport(
|
||||||
|
mount_location_data=self.mount_location_data,
|
||||||
|
detected_os=self.constants.detected_os,
|
||||||
|
skip_root_kmutil_requirement=self.skip_root_kmutil_requirement
|
||||||
|
).clean_auxiliary_kc()
|
||||||
|
|
||||||
# Make sure SNB kexts are compatible with the host
|
# Make sure SNB kexts are compatible with the host
|
||||||
if "Intel Sandy Bridge" in required_patches:
|
if "Intel Sandy Bridge" in required_patches:
|
||||||
|
|||||||
Reference in New Issue
Block a user