index : archinstall32 | |
Archlinux32 installer | gitolite user |
summaryrefslogtreecommitdiff |
-rw-r--r-- | archinstall/lib/hardware.py | 496 |
diff --git a/archinstall/lib/hardware.py b/archinstall/lib/hardware.py index 8400d338..c8001c19 100644 --- a/archinstall/lib/hardware.py +++ b/archinstall/lib/hardware.py @@ -1,192 +1,318 @@ import os -import logging -from functools import partial +from enum import Enum +from functools import cached_property from pathlib import Path -from typing import Iterator, Optional, Union +from typing import Optional, Dict, List, TYPE_CHECKING, Any +from .exceptions import SysCallError from .general import SysCommand from .networking import list_interfaces, enrich_iface_types -from .exceptions import SysCallError -from .output import log - -__packages__ = [ - "mesa", - "xf86-video-amdgpu", - "xf86-video-ati", - "xf86-video-nouveau", - "xf86-video-vmware", - "xf86-video-intel", - "xf86-video-qxl", - "libva-mesa-driver", - "libva-intel-driver", - "vulkan-radeon", - "vulkan-intel", -] - -AVAILABLE_GFX_DRIVERS = { - # Sub-dicts are layer-2 options to be selected - # and lists are a list of packages to be installed - "All open-source (default)": [ - "mesa", - "xf86-video-amdgpu", - "xf86-video-ati", - "xf86-video-nouveau", - "xf86-video-vmware", - "xf86-video-intel", - "xf86-video-qxl", - "libva-mesa-driver", - "libva-intel-driver", - "vulkan-radeon", - "vulkan-intel", - ], - "AMD / ATI (open-source)": [ - "mesa", - "xf86-video-amdgpu", - "xf86-video-ati", - "libva-mesa-driver", - "vulkan-radeon", - ], - "Intel (open-source, modern)": [ - "mesa", - "libva-intel-driver", - "vulkan-intel", - ], - "Intel (open-source, old)": [ - "mesa", - "xf86-video-intel" - ], - "Nvidia (open-source nouveau driver)": [ - "mesa", - "xf86-video-nouveau", - "libva-mesa-driver" - ], - "VMware / VirtualBox / QXL (open-source)": ["mesa", "xf86-video-vmware", "xf86-video-qxl"], -} - -CPUINFO = Path("/proc/cpuinfo") -MEMINFO = Path("/proc/meminfo") - - -def cpuinfo() -> Iterator[dict[str, str]]: - """Yields information about the CPUs of the system.""" - cpu = {} - - with CPUINFO.open() as file: - for line in file: - if not (line := line.strip()): - yield cpu - cpu = {} - continue - - key, value = line.split(":", maxsplit=1) - cpu[key.strip()] = value.strip() - - -def meminfo(key: Optional[str] = None) -> Union[dict[str, int], Optional[int]]: - """Returns a dict with memory info if called with no args - or the value of the given key of said dict. - """ - with MEMINFO.open() as file: - mem_info = { - (columns := line.strip().split())[0].rstrip(':'): int(columns[1]) - for line in file - } - - if key is None: - return mem_info - - return mem_info.get(key) - - -def has_wifi() -> bool: - return 'WIRELESS' in enrich_iface_types(list_interfaces().values()).values() - - -def has_cpu_vendor(vendor_id: str) -> bool: - return any(cpu.get("vendor_id") == vendor_id for cpu in cpuinfo()) - - -has_amd_cpu = partial(has_cpu_vendor, "AuthenticAMD") - - -has_intel_cpu = partial(has_cpu_vendor, "GenuineIntel") - - -def has_uefi() -> bool: - return os.path.isdir('/sys/firmware/efi') - - -def graphics_devices() -> dict: - cards = {} - for line in SysCommand("lspci"): - if b' VGA ' in line or b' 3D ' in line: - _, identifier = line.split(b': ', 1) - cards[identifier.strip().decode('UTF-8')] = line - return cards - - -def has_nvidia_graphics() -> bool: - return any('nvidia' in x.lower() for x in graphics_devices()) - - -def has_amd_graphics() -> bool: - return any('amd' in x.lower() for x in graphics_devices()) - - -def has_intel_graphics() -> bool: - return any('intel' in x.lower() for x in graphics_devices()) +from .output import debug +from .utils.util import format_cols + +if TYPE_CHECKING: + _: Any + + +class CpuVendor(Enum): + AuthenticAMD = 'amd' + GenuineIntel = 'intel' + _Unknown = 'unknown' + + @classmethod + def get_vendor(cls, name: str) -> 'CpuVendor': + if vendor := getattr(cls, name, None): + return vendor + else: + debug(f"Unknown CPU vendor '{name}' detected.") + return cls._Unknown + + def _has_microcode(self) -> bool: + match self: + case CpuVendor.AuthenticAMD | CpuVendor.GenuineIntel: + return True + case _: + return False + + def get_ucode(self) -> Optional[Path]: + if self._has_microcode(): + return Path(self.value + '-ucode.img') + return None + + +class GfxPackage(Enum): + Dkms = 'dkms' + IntelMediaDriver = 'intel-media-driver' + LibvaIntelDriver = 'libva-intel-driver' + LibvaMesaDriver = 'libva-mesa-driver' + Mesa = "mesa" + NvidiaDkms = 'nvidia-dkms' + NvidiaOpen = 'nvidia-open' + NvidiaOpenDkms = 'nvidia-open-dkms' + VulkanIntel = 'vulkan-intel' + VulkanRadeon = 'vulkan-radeon' + Xf86VideoAmdgpu = "xf86-video-amdgpu" + Xf86VideoAti = "xf86-video-ati" + Xf86VideoNouveau = 'xf86-video-nouveau' + Xf86VideoVmware = 'xf86-video-vmware' + XorgServer = 'xorg-server' + XorgXinit = 'xorg-xinit' + + +class GfxDriver(Enum): + AllOpenSource = 'All open-source' + AmdOpenSource = 'AMD / ATI (open-source)' + IntelOpenSource = 'Intel (open-source)' + NvidiaOpenKernel = 'Nvidia (open kernel module for newer GPUs, Turing+)' + NvidiaOpenSource = 'Nvidia (open-source nouveau driver)' + NvidiaProprietary = 'Nvidia (proprietary)' + VMOpenSource = 'VMware / VirtualBox (open-source)' + + def is_nvidia(self) -> bool: + match self: + case GfxDriver.NvidiaProprietary | \ + GfxDriver.NvidiaOpenSource | \ + GfxDriver.NvidiaOpenKernel: + return True + case _: + return False + + def packages_text(self) -> str: + text = str(_('Installed packages')) + ':\n' + pkg_names = [p.value for p in self.gfx_packages()] + text += format_cols(sorted(pkg_names)) + return text + + def gfx_packages(self) -> List[GfxPackage]: + packages = [GfxPackage.XorgServer, GfxPackage.XorgXinit] + + match self: + case GfxDriver.AllOpenSource: + packages += [ + GfxPackage.Mesa, + GfxPackage.Xf86VideoAmdgpu, + GfxPackage.Xf86VideoAti, + GfxPackage.Xf86VideoNouveau, + GfxPackage.Xf86VideoVmware, + GfxPackage.LibvaMesaDriver, + GfxPackage.LibvaIntelDriver, + GfxPackage.IntelMediaDriver, + GfxPackage.VulkanRadeon, + GfxPackage.VulkanIntel + ] + case GfxDriver.AmdOpenSource: + packages += [ + GfxPackage.Mesa, + GfxPackage.Xf86VideoAmdgpu, + GfxPackage.Xf86VideoAti, + GfxPackage.LibvaMesaDriver, + GfxPackage.VulkanRadeon + ] + case GfxDriver.IntelOpenSource: + packages += [ + GfxPackage.Mesa, + GfxPackage.LibvaIntelDriver, + GfxPackage.IntelMediaDriver, + GfxPackage.VulkanIntel + ] + case GfxDriver.NvidiaOpenKernel: + packages += [ + GfxPackage.NvidiaOpen, + GfxPackage.Dkms, + GfxPackage.NvidiaOpenDkms + ] + case GfxDriver.NvidiaOpenSource: + packages += [ + GfxPackage.Mesa, + GfxPackage.Xf86VideoNouveau, + GfxPackage.LibvaMesaDriver + ] + case GfxDriver.NvidiaProprietary: + packages += [ + GfxPackage.NvidiaDkms, + GfxPackage.Dkms, + ] + case GfxDriver.VMOpenSource: + packages += [ + GfxPackage.Mesa, + GfxPackage.Xf86VideoVmware + ] + + return packages + +class _SysInfo: + def __init__(self): + pass + + @cached_property + def cpu_info(self) -> Dict[str, str]: + """ + Returns system cpu information + """ + cpu_info_path = Path("/proc/cpuinfo") + cpu: Dict[str, str] = {} + + with cpu_info_path.open() as file: + for line in file: + if line := line.strip(): + key, value = line.split(":", maxsplit=1) + cpu[key.strip()] = value.strip() + + return cpu + + @cached_property + def mem_info(self) -> Dict[str, int]: + """ + Returns system memory information + """ + mem_info_path = Path("/proc/meminfo") + mem_info: Dict[str, int] = {} + + with mem_info_path.open() as file: + for line in file: + key, value = line.strip().split(':') + num = value.split()[0] + mem_info[key] = int(num) + return mem_info -def cpu_vendor() -> Optional[str]: - for cpu in cpuinfo(): - return cpu.get("vendor_id") - - return None - - -def cpu_model() -> Optional[str]: - for cpu in cpuinfo(): - return cpu.get("model name") - - return None - - -def sys_vendor() -> Optional[str]: - with open(f"/sys/devices/virtual/dmi/id/sys_vendor") as vendor: - return vendor.read().strip() - - -def product_name() -> Optional[str]: - with open(f"/sys/devices/virtual/dmi/id/product_name") as product: - return product.read().strip() - - -def mem_available() -> Optional[int]: - return meminfo('MemAvailable') - - -def mem_free() -> Optional[int]: - return meminfo('MemFree') - - -def mem_total() -> Optional[int]: - return meminfo('MemTotal') - - -def virtualization() -> Optional[str]: - try: - return str(SysCommand("systemd-detect-virt")).strip('\r\n') - except SysCallError as error: - log(f"Could not detect virtual system: {error}", level=logging.DEBUG) - - return None - - -def is_vm() -> bool: - try: - return b"none" not in b"".join(SysCommand("systemd-detect-virt")).lower() - except SysCallError as error: - log(f"System is not running in a VM: {error}", level=logging.DEBUG) - return None - -# TODO: Add more identifiers + def mem_info_by_key(self, key: str) -> int: + return self.mem_info[key] + + @cached_property + def loaded_modules(self) -> List[str]: + """ + Returns loaded kernel modules + """ + modules_path = Path('/proc/modules') + modules: List[str] = [] + + with modules_path.open() as file: + for line in file: + module = line.split(maxsplit=1)[0] + modules.append(module) + + return modules + + +_sys_info = _SysInfo() + + +class SysInfo: + @staticmethod + def has_wifi() -> bool: + ifaces = list(list_interfaces().values()) + return 'WIRELESS' in enrich_iface_types(ifaces).values() + + @staticmethod + def has_uefi() -> bool: + return os.path.isdir('/sys/firmware/efi') + + @staticmethod + def _graphics_devices() -> Dict[str, str]: + cards: Dict[str, str] = {} + for line in SysCommand("lspci"): + if b' VGA ' in line or b' 3D ' in line: + _, identifier = line.split(b': ', 1) + cards[identifier.strip().decode('UTF-8')] = str(line) + return cards + + @staticmethod + def has_nvidia_graphics() -> bool: + return any('nvidia' in x.lower() for x in SysInfo._graphics_devices()) + + @staticmethod + def has_amd_graphics() -> bool: + return any('amd' in x.lower() for x in SysInfo._graphics_devices()) + + @staticmethod + def has_intel_graphics() -> bool: + return any('intel' in x.lower() for x in SysInfo._graphics_devices()) + + @staticmethod + def cpu_vendor() -> Optional[CpuVendor]: + if vendor := _sys_info.cpu_info.get('vendor_id'): + return CpuVendor.get_vendor(vendor) + return None + + @staticmethod + def cpu_model() -> Optional[str]: + return _sys_info.cpu_info.get('model name', None) + + @staticmethod + def sys_vendor() -> str: + with open(f"/sys/devices/virtual/dmi/id/sys_vendor") as vendor: + return vendor.read().strip() + + @staticmethod + def product_name() -> str: + with open(f"/sys/devices/virtual/dmi/id/product_name") as product: + return product.read().strip() + + @staticmethod + def mem_available() -> int: + return _sys_info.mem_info_by_key('MemAvailable') + + @staticmethod + def mem_free() -> int: + return _sys_info.mem_info_by_key('MemFree') + + @staticmethod + def mem_total() -> int: + return _sys_info.mem_info_by_key('MemTotal') + + @staticmethod + def virtualization() -> Optional[str]: + try: + return str(SysCommand("systemd-detect-virt")).strip('\r\n') + except SysCallError as err: + debug(f"Could not detect virtual system: {err}") + + return None + + @staticmethod + def is_vm() -> bool: + try: + result = SysCommand("systemd-detect-virt") + return b"none" not in b"".join(result).lower() + except SysCallError as err: + debug(f"System is not running in a VM: {err}") + + return False + + @staticmethod + def requires_sof_fw() -> bool: + return 'snd_sof' in _sys_info.loaded_modules + + @staticmethod + def requires_alsa_fw() -> bool: + modules = ( + 'snd_asihpi', + 'snd_cs46xx', + 'snd_darla20', + 'snd_darla24', + 'snd_echo3g', + 'snd_emu10k1', + 'snd_gina20', + 'snd_gina24', + 'snd_hda_codec_ca0132', + 'snd_hdsp', + 'snd_indigo', + 'snd_indigodj', + 'snd_indigodjx', + 'snd_indigoio', + 'snd_indigoiox', + 'snd_layla20', + 'snd_layla24', + 'snd_mia', + 'snd_mixart', + 'snd_mona', + 'snd_pcxhr', + 'snd_vx_lib' + ) + + for loaded_module in _sys_info.loaded_modules: + if loaded_module in modules: + return True + + return False |