Files
OpenCore-Legacy-Patcher/resources/sys_patch/sys_patch_helpers.py
2023-04-17 10:37:36 -06:00

270 lines
10 KiB
Python

# Additional support functions for sys_patch.py
# Copyright (C) 2020-2023, Dhinak G, Mykola Grymalyuk
import plistlib
import os
import logging
import subprocess
from typing import Union
from pathlib import Path
from datetime import datetime
from data import os_data
from resources import bplist, constants, generate_smbios, utilities
class SysPatchHelpers:
"""
Library of helper functions for sys_patch.py and related libraries
"""
def __init__(self, global_constants: constants.Constants):
self.constants: constants.Constants = global_constants
def snb_board_id_patch(self, source_files_path: str):
"""
Patch AppleIntelSNBGraphicsFB.kext to support unsupported Board IDs
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
Parameters:
source_files_path (str): Path to the source files
"""
source_files_path = str(source_files_path)
if self.constants.computer.reported_board_id in self.constants.sandy_board_id_stock:
return
logging.info(f"- Found unsupported Board ID {self.constants.computer.reported_board_id}, performing AppleIntelSNBGraphicsFB bin patching")
board_to_patch = generate_smbios.determine_best_board_id_for_sandy(self.constants.computer.reported_board_id, self.constants.computer.gpus)
logging.info(f"- Replacing {board_to_patch} with {self.constants.computer.reported_board_id}")
board_to_patch_hex = bytes.fromhex(board_to_patch.encode('utf-8').hex())
reported_board_hex = bytes.fromhex(self.constants.computer.reported_board_id.encode('utf-8').hex())
if len(board_to_patch_hex) > len(reported_board_hex):
# Pad the reported Board ID with zeros to match the length of the board to patch
reported_board_hex = reported_board_hex + bytes(len(board_to_patch_hex) - len(reported_board_hex))
elif len(board_to_patch_hex) < len(reported_board_hex):
logging.info(f"- Error: Board ID {self.constants.computer.reported_board_id} is longer than {board_to_patch}")
raise Exception("Host's Board ID is longer than the kext's Board ID, cannot patch!!!")
path = source_files_path + "/10.13.6/System/Library/Extensions/AppleIntelSNBGraphicsFB.kext/Contents/MacOS/AppleIntelSNBGraphicsFB"
if not Path(path).exists():
logging.info(f"- Error: Could not find {path}")
raise Exception("Failed to find AppleIntelSNBGraphicsFB.kext, cannot patch!!!")
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)
def generate_patchset_plist(self, patchset: dict, file_name: str, kdk_used: Path):
"""
Generate patchset file for user reference
Parameters:
patchset (dict): Dictionary of patchset, see sys_patch_detect.py and sys_patch_dict.py
file_name (str): Name of the file to write to
kdk_used (Path): Path to the KDK used, if any
Returns:
bool: True if successful, False if not
"""
source_path = f"{self.constants.payload_path}"
source_path_file = f"{source_path}/{file_name}"
kdk_string = "Not applicable"
if kdk_used:
kdk_string = kdk_used
data = {
"OpenCore Legacy Patcher": f"v{self.constants.patcher_version}",
"PatcherSupportPkg": f"v{self.constants.patcher_support_pkg_version}",
"Time Patched": f"{datetime.now().strftime('%B %d, %Y @ %H:%M:%S')}",
"Commit URL": f"{self.constants.commit_info[2]}",
"Kernel Debug Kit Used": f"{kdk_string}",
"OS Version": f"{self.constants.detected_os}.{self.constants.detected_os_minor} ({self.constants.detected_os_build})",
}
data.update(patchset)
if Path(source_path_file).exists():
os.remove(source_path_file)
# Need to write to a safe location
plistlib.dump(data, Path(source_path_file).open("wb"), sort_keys=False)
if Path(source_path_file).exists():
return True
return False
def disable_window_server_caching(self):
"""
Disable WindowServer's asset caching
On legacy GCN GPUs, the WindowServer cache generated creates
corrupted Opaque shaders.
To work-around this, we disable WindowServer caching
And force macOS into properly generating the Opaque shaders
"""
if self.constants.detected_os < os_data.os_data.ventura:
return
logging.info("- Disabling WindowServer Caching")
# Invoke via 'bash -c' to resolve pathing
utilities.elevated(["bash", "-c", "rm -rf /private/var/folders/*/*/*/WindowServer/com.apple.WindowServer"])
# Disable writing to WindowServer folder
utilities.elevated(["bash", "-c", "chflags uchg /private/var/folders/*/*/*/WindowServer"])
# Reference:
# To reverse write lock:
# 'chflags nouchg /private/var/folders/*/*/*/WindowServer'
def remove_news_widgets(self):
"""
Remove News Widgets from Notification Centre
On Ivy Bridge and Haswell iGPUs, RenderBox will crash the News Widgets in
Notification Centre. To ensure users can access Notifications normally,
we manually remove all News Widgets
"""
if self.constants.detected_os < os_data.os_data.ventura:
return
logging.info("- Parsing Notification Centre Widgets")
file_path = "~/Library/Containers/com.apple.notificationcenterui/Data/Library/Preferences/com.apple.notificationcenterui.plist"
file_path = Path(file_path).expanduser()
if not file_path.exists():
logging.info(" - Defaults file not found, skipping")
return
did_find = False
with open(file_path, "rb") as f:
data = plistlib.load(f)
if "widgets" not in data:
return
if "instances" not in data["widgets"]:
return
for widget in list(data["widgets"]["instances"]):
widget_data = bplist.BPListReader(widget).parse()
for entry in widget_data:
if 'widget' not in entry:
continue
sub_data = bplist.BPListReader(widget_data[entry]).parse()
for sub_entry in sub_data:
if not '$object' in sub_entry:
continue
if not b'com.apple.news' in sub_data[sub_entry][2]:
continue
logging.info(f" - Found News Widget to remove: {sub_data[sub_entry][2].decode('ascii')}")
data["widgets"]["instances"].remove(widget)
did_find = True
if did_find:
with open(file_path, "wb") as f:
plistlib.dump(data, f, sort_keys=False)
subprocess.run(["killall", "NotificationCenter"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
def install_rsr_repair_binary(self):
"""
Installs RSRRepair
RSRRepair is a utility that will sync the SysKC and BootKC in the event of a panic
With macOS 13.2, Apple implemented the Rapid Security Response System
However Apple added a half baked snapshot reversion system if seal was broken,
which forgets to handle Preboot BootKC syncing.
Thus this application will try to re-sync the BootKC with SysKC in the event of a panic
Reference: https://github.com/dortania/OpenCore-Legacy-Patcher/issues/1019
This is a (hopefully) temporary work-around, however likely to stay.
RSRRepair has the added bonus of fixing desynced KCs from 'bless', so useful in Big Sur+
Source: https://github.com/flagersgit/RSRRepair
"""
if self.constants.detected_os < os_data.os_data.big_sur:
return
logging.info("- Installing Kernel Collection syncing utility")
result = utilities.elevated([self.constants.rsrrepair_userspace_path, "--install"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if result.returncode != 0:
logging.info(f" - Failed to install RSRRepair: {result.stdout.decode()}")
def patch_gpu_compiler_libraries(self, mount_point: Union[str, Path]):
"""
Fix GPUCompiler.framework's libraries to resolve linking issues
On 13.3 with 3802 GPUs, OCLP will downgrade GPUCompiler to resolve
graphics support. However the binary hardcodes the library names,
and thus we need to adjust the libraries to match (31001.669)
Important portions of the library will be downgraded to 31001.669,
and the remaining bins will be copied over (via CoW to reduce waste)
Primary folders to merge:
- 31001.XXX: (current OS version)
- include:
- module.modulemap
- opencl-c.h
- lib (entire directory)
Parameters:
mount_point: The mount point of the target volume
"""
if self.constants.detected_os < os_data.os_data.ventura:
return
if self.constants.detected_os == os_data.os_data.ventura:
if self.constants.detected_os_minor < 4:
return
LIBRARY_DIR = f"{mount_point}/System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/31001/Libraries/lib/clang"
GPU_VERSION = "31001.669"
DEST_DIR = f"{LIBRARY_DIR}/{GPU_VERSION}"
if not Path(DEST_DIR).exists():
return
for file in Path(LIBRARY_DIR).iterdir():
if file.is_file():
continue
if file.name == GPU_VERSION:
continue
# Partial match as each OS can increment the version
if not file.name.startswith("31001."):
continue
logging.info(f"- Merging GPUCompiler.framework libraries to match binary")
src_dir = f"{LIBRARY_DIR}/{file.name}"
if not Path(f"{DEST_DIR}/lib").exists():
utilities.process_status(utilities.elevated(["cp", "-cR", f"{src_dir}/lib", f"{DEST_DIR}/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
break