WIP device probe refactor

This commit is contained in:
Dhinak G
2021-06-04 14:14:06 -04:00
parent 4b187a045a
commit 4747c2619f
4 changed files with 908 additions and 490 deletions

View File

@@ -275,14 +275,14 @@ class BuildOpenCore:
elif not self.constants.custom_model and wifi_vendor:
if wifi_vendor == self.constants.pci_broadcom:
# This works around OCLP spoofing the Wifi card and therefore unable to actually detect the correct device
if wifi_device in PCIIDArray.broadcom_ids().BCM4360Wifi and wifi_ioname not in ["pci14e4,4353", "pci14e4,4331"]:
if wifi_device in PCIIDArray.broadcom_ids().AirPortBrcmNIC and wifi_ioname not in ["pci14e4,4353", "pci14e4,4331"]:
self.enable_kext("AirportBrcmFixup.kext", self.constants.airportbcrmfixup_version, self.constants.airportbcrmfixup_path)
elif wifi_ioname in ["pci14e4,4353", "pci14e4,4331"] or wifi_device in PCIIDArray.broadcom_ids().BCM94331Wifi:
elif wifi_ioname in ["pci14e4,4353", "pci14e4,4331"] or wifi_device in PCIIDArray.broadcom_ids().AirPortBrcm4360:
wifi_fake_id(self)
elif wifi_device in PCIIDArray.broadcom_ids().BCM94322Wifi:
elif wifi_device in PCIIDArray.broadcom_ids().AirPortBrcm4331:
self.enable_kext("IO80211Mojave.kext", self.constants.io80211mojave_version, self.constants.io80211mojave_path)
self.get_kext_by_bundle_path("IO80211Mojave.kext/Contents/PlugIns/AirPortBrcm4331.kext")["Enabled"] = True
elif wifi_device in PCIIDArray.broadcom_ids().BCM94328Wifi:
elif wifi_device in PCIIDArray.broadcom_ids().AppleAirPortBrcm43224:
self.enable_kext("corecaptureElCap.kext", self.constants.corecaptureelcap_version, self.constants.corecaptureelcap_path)
self.enable_kext("IO80211ElCap.kext", self.constants.io80211elcap_version, self.constants.io80211elcap_path)
self.get_kext_by_bundle_path("IO80211ElCap.kext/Contents/PlugIns/AppleAirPortBrcm43224.kext")["Enabled"] = True

File diff suppressed because it is too large Load Diff

337
Resources/device_probe.py Normal file
View File

@@ -0,0 +1,337 @@
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(" "),
)

75
Resources/ioreg.py Normal file
View File

@@ -0,0 +1,75 @@
from dataclasses import dataclass
import plistlib
import subprocess
from typing import Any
@dataclass
class IORegistryEntry:
name: str
entry_class: str
properties: dict
location: str
children: list
parent: Any
class IOReg:
def __init__(self):
self.ioreg = plistlib.loads(subprocess.run("ioreg -a -l".split(), stdout=subprocess.PIPE).stdout.strip())
self.tree = self.recurse(self.ioreg, None)
def recurse(self, entry, parent):
converted = IORegistryEntry(
entry["IORegistryEntryName"],
entry["IOObjectClass"],
{
i: v
for i, v in entry.items()
if i
not in [
"IOServiceBusyState",
"IOServiceBusyTime",
"IOServiceState",
"IORegistryEntryLocation",
"IORegistryEntryName",
"IORegistryEntryID",
"IOObjectClass",
"IORegistryEntryChildren",
"IOObjectRetainCount",
]
},
entry.get("IORegistryEntryLocation"),
[],
parent,
)
for i in entry.get("IORegistryEntryChildren", []):
converted.children.append(self.recurse(i, converted))
return converted
def find(self, root=None, **kwargs):
if not root:
root = self.tree
if not kwargs:
return
conditions = []
if "name" in kwargs:
conditions.append(kwargs["name"] == root.name)
if "entry_class" in kwargs:
conditions.append(kwargs["entry_class"] == root.entry_class)
if "key" in kwargs:
conditions.append(kwargs["key"] in root.properties)
if "property" in kwargs:
conditions.append(kwargs["property"][0] in root.properties and root.properties[kwargs["property"][0]] == kwargs["property"][1])
if all(conditions):
yield root
for i in root.children:
for j in self.find(i, **kwargs):
yield j