mirror of
https://github.com/dortania/OpenCore-Legacy-Patcher.git
synced 2026-04-20 18:54:30 +10:00
338 lines
12 KiB
Python
338 lines
12 KiB
Python
import binascii
|
|
import enum
|
|
import itertools
|
|
from dataclasses import dataclass, field
|
|
import plistlib
|
|
import subprocess
|
|
from typing import Any, ClassVar, Optional, Type
|
|
|
|
from Resources import Utilities, ioreg, PCIIDArray
|
|
|
|
|
|
@dataclass
|
|
class GPU:
|
|
arch: enum.Enum = field(init=False) # The architecture, see subclasses.
|
|
|
|
def __post_init__(self):
|
|
self.detect_arch()
|
|
|
|
def detect_arch(self):
|
|
raise NotImplementedError
|
|
|
|
|
|
@dataclass
|
|
class WirelessCard:
|
|
CLASS_CODE: ClassVar[int] = 0x028000 # 00800200 hexswapped
|
|
model: enum.Enum = field(init=False)
|
|
|
|
def __post_init__(self):
|
|
self.detect_model()
|
|
|
|
def detect_model(self):
|
|
raise NotImplementedError
|
|
|
|
|
|
@dataclass
|
|
class PCIDevice:
|
|
VENDOR_ID: ClassVar[int] # Default vendor id, for subclasses.
|
|
|
|
vendor_id: int # The vendor ID of this PCI device
|
|
device_id: int # The device ID of this PCI device
|
|
class_code: int # The class code of this PCI device
|
|
|
|
# ioregistryentry: Optional[ioreg.IORegistryEntry] = None
|
|
name: Optional[str] = None
|
|
pci_path: Optional[str] = None
|
|
|
|
# def __getstate__(self):
|
|
# state = self.__dict__.copy()
|
|
# state.pop("ioregistryentry")
|
|
# return state
|
|
|
|
@classmethod
|
|
def from_ioregistry(cls, entry: ioreg.IORegistryEntry):
|
|
device = cls(
|
|
int.from_bytes(entry.properties["vendor-id"][:4], byteorder="little"),
|
|
int.from_bytes(entry.properties["device-id"][:4], byteorder="little"),
|
|
int.from_bytes(entry.properties["class-code"][:6], byteorder="little"),
|
|
)
|
|
if "model" in entry.properties:
|
|
device.name = entry.properties["model"].strip(b"\0").decode()
|
|
device.populate_pci_path(entry)
|
|
return device
|
|
|
|
# @staticmethod
|
|
# def vendor_detect_old(device):
|
|
# for i in [NVIDIA, AMD]:
|
|
# if i.detect(device):
|
|
# return i
|
|
# return None
|
|
|
|
def vendor_detect(self, *, inherits: ClassVar[Any] = None, classes: list = None):
|
|
for i in classes or PCIDevice.__subclasses__():
|
|
if issubclass(i, inherits or object) and i.detect(self):
|
|
return i
|
|
return None
|
|
|
|
@classmethod
|
|
def detect(cls, device):
|
|
return device.vendor_id == cls.VENDOR_ID and ((device.class_code == cls.CLASS_CODE) if getattr(cls, "CLASS_CODE", None) else True) # type: ignore # pylint: disable=no-member
|
|
|
|
# def acpi_path(self):
|
|
# # Eventually
|
|
# raise NotImplementedError
|
|
|
|
def populate_pci_path(self, entry: ioreg.IORegistryEntry):
|
|
# Eventually
|
|
# Trash, but who really cares?
|
|
paths = []
|
|
while entry:
|
|
if entry.entry_class == "IOPCIDevice":
|
|
location = [hex(int(i, 16)) for i in entry.location.split(",") + ["0"]]
|
|
paths.append(f"Pci({location[0]},{location[1]})")
|
|
elif entry.entry_class == "IOACPIPlatformDevice":
|
|
paths.append(f"PciRoot({hex(int(entry.properties.get('_UID', 0)))})")
|
|
break
|
|
entry = entry.parent
|
|
self.pci_path = "/".join(reversed(paths))
|
|
|
|
|
|
@dataclass
|
|
class NVIDIA(GPU, PCIDevice):
|
|
VENDOR_ID: ClassVar[int] = 0x10DE
|
|
|
|
class Archs(enum.Enum):
|
|
# pylint: disable=invalid-name
|
|
Fermi = "Fermi"
|
|
Tesla = "Tesla"
|
|
Kepler = "Kepler"
|
|
Unknown = "Unknown"
|
|
|
|
arch: Archs = field(init=False)
|
|
|
|
def detect_arch(self):
|
|
# G80/G80GL
|
|
if self.device_id in PCIIDArray.nvidia_ids.tesla_ids:
|
|
self.arch = NVIDIA.Archs.Tesla
|
|
elif self.device_id in PCIIDArray.nvidia_ids.fermi_ids:
|
|
self.arch = NVIDIA.Archs.Fermi
|
|
elif self.device_id in PCIIDArray.nvidia_ids.kepler_ids:
|
|
self.arch = NVIDIA.Archs.Kepler
|
|
else:
|
|
self.arch = NVIDIA.Archs.Unknown
|
|
|
|
|
|
@dataclass
|
|
class AMD(GPU, PCIDevice):
|
|
VENDOR_ID: ClassVar[int] = 0x1002
|
|
|
|
class Archs(enum.Enum):
|
|
# pylint: disable=invalid-name
|
|
Legacy_GCN = "Legacy GCN"
|
|
TeraScale_1 = "TeraScale 1"
|
|
TeraScale_2 = "TeraScale 2"
|
|
Polaris = "Polaris"
|
|
Vega = "Vega"
|
|
Navi = "Navi"
|
|
Unknown = "Unknown"
|
|
|
|
arch: Archs = field(init=False)
|
|
|
|
def detect_arch(self):
|
|
if self.device_id in PCIIDArray.amd_ids.legacy_gcn_ids:
|
|
self.arch = AMD.Archs.Legacy_GCN
|
|
elif self.device_id in PCIIDArray.amd_ids.terascale_1_ids:
|
|
self.arch = AMD.Archs.TeraScale_1
|
|
elif self.device_id in PCIIDArray.amd_ids.terascale_2_ids:
|
|
self.arch = AMD.Archs.TeraScale_2
|
|
elif self.device_id in PCIIDArray.amd_ids.polaris_ids:
|
|
self.arch = AMD.Archs.Polaris
|
|
elif self.device_id in PCIIDArray.amd_ids.vega_ids:
|
|
self.arch = AMD.Archs.Vega
|
|
elif self.device_id in PCIIDArray.amd_ids.navi_ids:
|
|
self.arch = AMD.Archs.Navi
|
|
else:
|
|
self.arch = AMD.Archs.Unknown
|
|
|
|
|
|
@dataclass
|
|
class Intel(GPU, PCIDevice):
|
|
VENDOR_ID: ClassVar[int] = 0x8086
|
|
|
|
class Archs(enum.Enum):
|
|
# pylint: disable=invalid-name
|
|
Iron_Lake = "Iron Lake"
|
|
Sandy_Bridge = "Sandy Bridge"
|
|
Ivy_Bridge = "Ivy Bridge"
|
|
Unknown = "Unknown"
|
|
|
|
arch: Archs = field(init=False)
|
|
|
|
def detect_arch(self):
|
|
if self.device_id in PCIIDArray.intel_ids.iron_ids:
|
|
self.arch = Intel.Archs.Iron_Lake
|
|
elif self.device_id in PCIIDArray.intel_ids.sandy_ids:
|
|
self.arch = Intel.Archs.Sandy_Bridge
|
|
elif self.device_id in PCIIDArray.intel_ids.ivy_ids:
|
|
self.arch = Intel.Archs.Ivy_Bridge
|
|
else:
|
|
self.arch = Intel.Archs.Unknown
|
|
|
|
|
|
@dataclass
|
|
class Broadcom(WirelessCard, PCIDevice):
|
|
VENDOR_ID: ClassVar[int] = 0x14E4
|
|
|
|
class Models(enum.Enum):
|
|
# pylint: disable=invalid-name
|
|
AirportBrcmNIC = "AirportBrcmNIC supported"
|
|
AirPortBrcm4360 = "AirPortBrcm4360 supported"
|
|
AirPortBrcm4331 = "AirPortBrcm4331 supported"
|
|
AirPortBrcm43224 = "AppleAirPortBrcm43224 supported"
|
|
Unknown = "Unknown"
|
|
|
|
model: Models = field(init=False)
|
|
|
|
def detect_model(self):
|
|
if self.device_id in PCIIDArray.broadcom_ids.AirPortBrcmNIC:
|
|
self.model = Broadcom.Models.AirportBrcmNIC
|
|
elif self.device_id in PCIIDArray.broadcom_ids.AirPortBrcm4360:
|
|
self.model = Broadcom.Models.AirPortBrcm4360
|
|
elif self.device_id in PCIIDArray.broadcom_ids.AirPortBrcm4331:
|
|
self.model = Broadcom.Models.AirPortBrcm4331
|
|
elif self.device_id in PCIIDArray.broadcom_ids.AppleAirPortBrcm43224:
|
|
self.model = Broadcom.Models.AirPortBrcm43224
|
|
else:
|
|
self.model = Broadcom.Models.Unknown
|
|
|
|
|
|
@dataclass
|
|
class Atheros(WirelessCard, PCIDevice):
|
|
VENDOR_ID: ClassVar[int] = 0x168C
|
|
|
|
class Models(enum.Enum):
|
|
# pylint: disable=invalid-name
|
|
# Well there's only one model but
|
|
AirPortAtheros40 = "AirPortAtheros40 supported"
|
|
Unknown = "Unknown"
|
|
|
|
model: Models = field(init=False)
|
|
|
|
def detect_model(self):
|
|
if self.device_id in PCIIDArray.atheros_ids.AtherosWifi:
|
|
self.model = Atheros.Models.AirPortAtheros40
|
|
else:
|
|
self.model = Atheros.Models.Unknown
|
|
|
|
|
|
@dataclass
|
|
class CPU:
|
|
name: str
|
|
flags: list[str]
|
|
|
|
|
|
@dataclass
|
|
class Computer:
|
|
opencore_model: Optional[str] = None
|
|
opencore_board_id: Optional[str] = None
|
|
reported_model: Optional[str] = None
|
|
reported_board_id: Optional[str] = None
|
|
gpus: list[GPU] = field(default_factory=list)
|
|
igpu: Optional[GPU] = None
|
|
dgpu: Optional[GPU] = None
|
|
wifi: Optional[PCIDevice] = None
|
|
cpu: Optional[CPU] = None
|
|
oclp: Optional[str] = None
|
|
ioregistry: Optional[ioreg.IOReg] = None
|
|
|
|
@staticmethod
|
|
def probe():
|
|
computer = Computer()
|
|
computer.ioregistry = ioreg.IOReg()
|
|
computer.gpu_probe()
|
|
computer.dgpu_probe()
|
|
computer.igpu_probe()
|
|
computer.wifi_probe()
|
|
computer.smbios_probe()
|
|
computer.cpu_probe()
|
|
return computer
|
|
|
|
def gpu_probe(self):
|
|
devices = itertools.chain(self.ioregistry.find(property=("class-code", binascii.a2b_hex("00000300"))), self.ioregistry.find(property=("class-code", binascii.a2b_hex("00800300"))))
|
|
|
|
for device in devices:
|
|
vendor: Type[GPU] = PCIDevice.from_ioregistry(device).vendor_detect(inherits=GPU) # type: ignore
|
|
if vendor:
|
|
self.gpus.append(vendor.from_ioregistry(device)) # type: ignore
|
|
|
|
def dgpu_probe(self):
|
|
# result = subprocess.run("ioreg -r -n GFX0 -a".split(), stdout=subprocess.PIPE).stdout.strip()
|
|
result = list(self.ioregistry.find(name="GFX0"))
|
|
|
|
if not result:
|
|
# No devices
|
|
return
|
|
|
|
# device = plistlib.loads(result)[0]
|
|
device = result[0]
|
|
vendor: Type[GPU] = PCIDevice.from_ioregistry(device).vendor_detect(inherits=GPU) # type: ignore
|
|
if vendor:
|
|
self.dgpu = vendor.from_ioregistry(device) # type: ignore
|
|
|
|
def igpu_probe(self):
|
|
# result = subprocess.run("ioreg -r -n IGPU -a".split(), stdout=subprocess.PIPE).stdout.strip()
|
|
result = list(self.ioregistry.find(name="IGPU"))
|
|
if not result:
|
|
# No devices
|
|
return
|
|
|
|
# device = plistlib.loads(result)[0]
|
|
device = result[0]
|
|
vendor: Type[GPU] = PCIDevice.from_ioregistry(device).vendor_detect(inherits=GPU) # type: ignore
|
|
if vendor:
|
|
self.igpu = vendor.from_ioregistry(device) # type: ignore
|
|
|
|
def wifi_probe(self):
|
|
# result = subprocess.run("ioreg -r -c IOPCIDevice -a -d2".split(), stdout=subprocess.PIPE).stdout.strip()
|
|
devices = self.ioregistry.find(property=("class-code", binascii.a2b_hex(Utilities.hexswap(hex(WirelessCard.CLASS_CODE)[2:].zfill(8)))))
|
|
# if not result:
|
|
# # No devices
|
|
# print("A")
|
|
# return
|
|
|
|
# devices = plistlib.loads(result)
|
|
# devices = [i for i in devices if i["class-code"] == binascii.a2b_hex("00800200")]
|
|
|
|
# if not devices:
|
|
# # No devices
|
|
# print("B")
|
|
# return
|
|
|
|
for device in devices:
|
|
vendor: Type[WirelessCard] = PCIDevice.from_ioregistry(device).vendor_detect(inherits=WirelessCard) # type: ignore
|
|
if vendor:
|
|
self.wifi = vendor.from_ioregistry(device) # type: ignore
|
|
break
|
|
|
|
def smbios_probe(self):
|
|
opencore_model = subprocess.run("nvram -x 4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102:oem-product".split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.strip()
|
|
if opencore_model:
|
|
self.opencore_model = plistlib.loads(opencore_model)["4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102:oem-product"].decode()
|
|
self.reported_model = next(self.ioregistry.find(entry_class="IOPlatformExpertDevice")).properties["model"].strip(b"\0").decode()
|
|
|
|
opencore_board = subprocess.run("nvram -x 4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102:oem-board".split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.strip()
|
|
if opencore_board:
|
|
self.opencore_board_id = plistlib.loads(opencore_board)["4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102:oem-board"].decode()
|
|
entry = next(self.ioregistry.find(entry_class="IOPlatformExpertDevice"))
|
|
self.reported_board_id = entry.properties.get("board-id", entry.properties.get("target-type", b"")).strip(b"\0").decode()
|
|
|
|
oclp_version = subprocess.run("nvram -x 4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102:OCLP-Version".split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.strip()
|
|
if oclp_version:
|
|
self.oclp = plistlib.loads(oclp_version)["4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102:OCLP-Version"].strip(b"\0").decode()
|
|
|
|
def cpu_probe(self):
|
|
self.cpu = CPU(
|
|
subprocess.run("sysctl machdep.cpu.brand_string".split(), stdout=subprocess.PIPE).stdout.decode().partition(": ")[2].strip(),
|
|
subprocess.run("sysctl machdep.cpu.features".split(), stdout=subprocess.PIPE).stdout.decode().partition(": ")[2].strip().split(" "),
|
|
)
|