mirror of
https://github.com/HorlogeSkynet/archey4
synced 2025-04-18 16:00:14 +02:00
parent
e11cbfc7a9
commit
26af17534d
@ -7,6 +7,7 @@ and this project (partially) adheres to [Semantic Versioning](https://semver.org
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- `GPU` support for Raspberry Pi
|
||||
- `Model` support for Raspberry Pi 5+
|
||||
- `none` logo style to completely hide distribution logo
|
||||
- AppArmor confinement profile (included in Debian and AUR packages)
|
||||
|
@ -60,6 +60,7 @@ profile archey4 /usr/{,local/}bin/archey{,4} {
|
||||
|
||||
# [GPU] entry
|
||||
/{,usr/}bin/lspci PUx,
|
||||
@{sys}/kernel/debug/dri/[0-9]*/{name,v3d_ident} r,
|
||||
|
||||
# [Hostname] entry
|
||||
/etc/hostname r,
|
||||
|
@ -1,13 +1,29 @@
|
||||
"""GPU information detection class"""
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
from pathlib import Path
|
||||
from shlex import split
|
||||
from subprocess import DEVNULL, CalledProcessError, check_output
|
||||
from typing import List
|
||||
|
||||
from archey.entry import Entry
|
||||
|
||||
LINUX_DRI_DEBUGFS_PATH = Path("/sys/kernel/debug/dri")
|
||||
|
||||
# See <https://unix.stackexchange.com/a/725968>
|
||||
LINUX_DRM_DEV_MAJOR = 226
|
||||
LINUX_MAX_DRM_MINOR_PRIMARY = 63
|
||||
|
||||
# From <https://www.lambda-v.com/texts/programming/gpu/gpu_raspi.html#id_sec_vcdisasm_examples>
|
||||
V3D_REV_TO_VC_VERSION = {
|
||||
"1": "VideoCore IV", # Raspberry Pi 1 -> 3
|
||||
"4.2": "VideoCore VI", # Raspberry Pi 4
|
||||
"7.1": "VideoCore VII", # Raspberry Pi 5
|
||||
}
|
||||
|
||||
|
||||
class GPU(Entry):
|
||||
"""Relies on `lspci` or `pciconf` to retrieve graphical device(s) information"""
|
||||
@ -18,7 +34,7 @@ class GPU(Entry):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
if platform.system() == "Linux":
|
||||
self.value = self._parse_lspci_output()
|
||||
self.value = self._parse_lspci_output() + self._videocore_chipsets()
|
||||
else:
|
||||
# Darwin or any other BSD-based system.
|
||||
self.value = self._parse_system_profiler() or self._parse_pciconf_output()
|
||||
@ -49,6 +65,54 @@ class GPU(Entry):
|
||||
|
||||
return gpus_list
|
||||
|
||||
def _videocore_chipsets(self) -> List[str]:
|
||||
"""
|
||||
Browse /dev/dri/card* to look for devices managed by vc4/v3d driver. From there, infer
|
||||
VideoCore chipset version from v3d driver revision (through kernel debugfs).
|
||||
|
||||
See <https://unix.stackexchange.com/a/393324>.
|
||||
"""
|
||||
videocore_chipsets = []
|
||||
|
||||
for device in Path("/dev/dri").glob("card*"):
|
||||
# safety checks to make sure DRI cards are primary interface node devices
|
||||
# Python < 3.10, can't use `follow_symlinks=False` with `stat`
|
||||
st_rdev = device.stat().st_rdev
|
||||
if (
|
||||
os.major(st_rdev) != LINUX_DRM_DEV_MAJOR
|
||||
or os.minor(st_rdev) > LINUX_MAX_DRM_MINOR_PRIMARY
|
||||
):
|
||||
continue
|
||||
|
||||
with contextlib.suppress(OSError):
|
||||
# assert device card driver is vc4 or v3d
|
||||
if (LINUX_DRI_DEBUGFS_PATH / str(os.minor(st_rdev)) / "name").read_text().split()[
|
||||
0
|
||||
] not in ("vc4", "v3d"):
|
||||
continue
|
||||
|
||||
# retrieve driver (major.minor SemVer segments) revision from v3d_ident file
|
||||
# Note : there seems to be multiple "Revision" fields for v3d driver (1 global + 1
|
||||
# per core). `re.search` will return the first match.
|
||||
v3d_revision = re.search(
|
||||
r"Revision:\s*(\d(?:\.\d)?)",
|
||||
(LINUX_DRI_DEBUGFS_PATH / str(os.minor(st_rdev)) / "v3d_ident").read_text(),
|
||||
)
|
||||
if v3d_revision is None:
|
||||
self._logger.warning("could not find v3d driver revision for %s", device)
|
||||
continue
|
||||
|
||||
try:
|
||||
videocore_chipsets.append(V3D_REV_TO_VC_VERSION[v3d_revision.group(1)])
|
||||
except KeyError:
|
||||
self._logger.warning(
|
||||
"could not infer VideoCore version from %s driver version %s",
|
||||
device,
|
||||
v3d_revision.group(1),
|
||||
)
|
||||
|
||||
return videocore_chipsets
|
||||
|
||||
@staticmethod
|
||||
def _parse_system_profiler() -> List[str]:
|
||||
"""Based on `system_profiler` output, return a list of video chipsets model names"""
|
||||
|
@ -1,6 +1,9 @@
|
||||
"""Test module for Archey's GPU detection module"""
|
||||
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, call, patch
|
||||
|
||||
from archey.configuration import DEFAULT_CONFIG
|
||||
@ -56,6 +59,100 @@ XX:YY.H "Non-Volatile memory controller" "Sandisk Corp" "SanDisk Ultra 3D / WD B
|
||||
self.assertListEqual(GPU._parse_lspci_output(), ["GPU-Manufacturer GPU-MODEL-NAME"])
|
||||
# pylint: enable=protected-access
|
||||
|
||||
@unittest.skipUnless(
|
||||
sys.platform.startswith("linux"),
|
||||
"major/minor device types differ between UNIX implementations",
|
||||
)
|
||||
@patch("archey.entries.gpu.Path")
|
||||
def test_videocore_chipsets(self, path_mock):
|
||||
"""Check `_videocore_chipsets` behavior"""
|
||||
gpu_instance_mock = HelperMethods.entry_mock(GPU)
|
||||
|
||||
# Prepare mocks for `Path("...").glob("...")` and device major/minor checks.
|
||||
device_dri_card_path_mock = MagicMock()
|
||||
device_dri_card_path_mock.stat.return_value = MagicMock(
|
||||
st_rdev=57856 # device type: 226, 0
|
||||
)
|
||||
path_mock.return_value.glob.return_value = [device_dri_card_path_mock]
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
# create a fake debugfs kernel tree
|
||||
with tempfile.TemporaryDirectory() as temp_dir, patch(
|
||||
"archey.entries.gpu.LINUX_DRI_DEBUGFS_PATH", Path(temp_dir)
|
||||
):
|
||||
debugfs_dri_0 = Path(temp_dir) / "0"
|
||||
debugfs_dri_0.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# There is a /dev/dri/card0, but its driver information are (currently) missing
|
||||
self.assertListEmpty(gpu_instance_mock._videocore_chipsets(gpu_instance_mock))
|
||||
|
||||
# VideoCore IV (Raspberry Pi 1 -> 3)
|
||||
(debugfs_dri_0 / "name").write_text(
|
||||
"vc4 dev=XXXX:YY:ZZ.T master=pci:XXXX:YY:ZZ.T unique=XXXX:YY:ZZ.T\n"
|
||||
)
|
||||
(debugfs_dri_0 / "v3d_ident").write_text(
|
||||
"""\
|
||||
Revision: 1
|
||||
Slices: 3
|
||||
TMUs: 6
|
||||
QPUs: 12
|
||||
Semaphores: 16
|
||||
"""
|
||||
)
|
||||
self.assertListEqual(
|
||||
gpu_instance_mock._videocore_chipsets(gpu_instance_mock), ["VideoCore IV"]
|
||||
)
|
||||
|
||||
# VideoCore VI (Raspberry Pi 4)
|
||||
(debugfs_dri_0 / "name").write_text(
|
||||
"v3d dev=XXXX:YY:ZZ.T master=pci:XXXX:YY:ZZ.T unique=XXXX:YY:ZZ.T\n"
|
||||
)
|
||||
(debugfs_dri_0 / "v3d_ident").write_text(
|
||||
"""\
|
||||
Revision: 4.2.14.0
|
||||
MMU: yes
|
||||
TFU: yes
|
||||
TSY: yes
|
||||
MSO: yes
|
||||
L3C: no (0kb)
|
||||
Core 0:
|
||||
Revision: 4.2
|
||||
Slices: 2
|
||||
TMUs: 2
|
||||
QPUs: 8
|
||||
Semaphores: 0
|
||||
BCG int: 0
|
||||
Override TMU: 0
|
||||
"""
|
||||
)
|
||||
self.assertListEqual(
|
||||
gpu_instance_mock._videocore_chipsets(gpu_instance_mock), ["VideoCore VI"]
|
||||
)
|
||||
|
||||
# VideoCore VII (Raspberry Pi 5)
|
||||
(debugfs_dri_0 / "name").write_text(
|
||||
"v3d dev=XXXX:YY:ZZ.T master=pci:XXXX:YY:ZZ.T unique=XXXX:YY:ZZ.T\n"
|
||||
)
|
||||
(debugfs_dri_0 / "v3d_ident").write_text(
|
||||
"""\
|
||||
Revision: 7.1.7.0
|
||||
MMU: yes
|
||||
TFU: no
|
||||
MSO: yes
|
||||
L3C: no (0kb)
|
||||
Core 0:
|
||||
Revision: 7.1
|
||||
Slices: 4
|
||||
TMUs: 4
|
||||
QPUs: 16
|
||||
Semaphores: 0
|
||||
"""
|
||||
)
|
||||
self.assertListEqual(
|
||||
gpu_instance_mock._videocore_chipsets(gpu_instance_mock), ["VideoCore VII"]
|
||||
)
|
||||
|
||||
@patch(
|
||||
"archey.entries.gpu.check_output",
|
||||
side_effect=[
|
||||
|
Loading…
x
Reference in New Issue
Block a user