Refactor GPU detection logic and remove unused PCI ID data
All checks were successful
PikaOS Package Build & Release (amd64-v3) / build (push) Successful in 50s

- Removed the `pci.ids` file as it is no longer needed for GPU detection.
- Simplified GPU detection functions to streamline vendor identification and model name retrieval.
- Enhanced memory reporting and improved overall structure of GPU information.
- Updated version in changelog to reflect these changes.
This commit is contained in:
ferreo 2024-11-30 23:32:29 +00:00
parent a2c372b101
commit 9db38b0c4d
6 changed files with 183 additions and 39425 deletions

View File

@ -1 +1 @@
1
2

View File

@ -4,12 +4,6 @@ pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const pci_ids_path = "data/pci.ids";
// Create the options module
const options = b.addOptions();
options.addOption([]const u8, "pci_ids_path", pci_ids_path);
const exe = b.addExecutable(.{
.name = "pikafetch",
.root_source_file = .{ .cwd_relative = "src/main.zig" },
@ -17,9 +11,6 @@ pub fn build(b: *std.Build) void {
.optimize = optimize,
});
// Add options to the executable
exe.root_module.addOptions("build_options", options);
exe.linkLibC();
b.installArtifact(exe);
}

View File

@ -1,4 +1,4 @@
pikafetch (0.1.0-101pika6) pika; urgency=medium
pikafetch (0.1.0-101pika7) pika; urgency=medium
* Initial release.

View File

@ -18,7 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Notes:
The contents of src/data/pci.ids is licensed under GNU General Public License (version 2 or higher)
SOFTWARE.

File diff suppressed because it is too large Load Diff

View File

@ -3,26 +3,8 @@ const mem = std.mem;
const fs = std.fs;
const ArrayList = std.ArrayList;
const allocPrint = std.fmt.allocPrint;
const VendorId = struct {
const APPLE = 0x106b;
const AMD = 0x1002;
const AMD_2 = 0x1022;
const INTEL = 0x8086;
const INTEL_2 = 0x8087;
const INTEL_3 = 0x03e7;
const NVIDIA = 0x10de;
const NVIDIA_2 = 0x12d2;
const NVIDIA_3 = 0x0955;
const MTHREADS = 0x1ed5;
const QUALCOMM = 0x5143;
const MTK = 0x14c3;
const VMWARE = 0x15ad;
const REDHAT = 0x1af4;
const PARALLEL = 0x1ab8;
const MICROSOFT = 0x1414;
const ORACLE = 0x108e;
};
const vector = @import("std").simd;
const builtin = @import("builtin");
const GPUInfo = struct {
vendor: []const u8,
@ -76,41 +58,7 @@ pub fn getGPUInfo(allocator: mem.Allocator) ![][]const u8 {
if (!mem.startsWith(u8, entry.name, "card")) continue;
if (mem.indexOf(u8, entry.name, "-")) |_| continue;
const vendor_path = try allocPrint(allocator, "/sys/class/drm/{s}/device/vendor", .{entry.name});
defer allocator.free(vendor_path);
const vendor_file = fs.openFileAbsolute(vendor_path, .{}) catch continue;
defer vendor_file.close();
var vendor_buf: [32]u8 = undefined;
const vendor_len = vendor_file.readAll(&vendor_buf) catch continue;
const vendor_str = mem.trim(u8, vendor_buf[0..vendor_len], "\n\r \t0x");
const vendor_id = std.fmt.parseInt(u32, vendor_str, 16) catch continue;
switch (vendor_id) {
VendorId.NVIDIA, VendorId.NVIDIA_2, VendorId.NVIDIA_3 => {
if (try detectNvidiaGPU(allocator, entry.name)) |gpu| {
try gpus.append(gpu);
continue;
}
},
VendorId.AMD, VendorId.AMD_2 => {
if (try detectAMDGPU(allocator, entry.name, vendor_id)) |gpu| {
try gpus.append(gpu);
continue;
}
},
VendorId.INTEL, VendorId.INTEL_2, VendorId.INTEL_3 => {
if (try detectIntelGPU(allocator, entry.name)) |gpu| {
try gpus.append(gpu);
continue;
}
},
else => {},
}
if (try detectGenericGPU(allocator, entry.name, vendor_id)) |gpu| {
if (try detectGPU(allocator, entry.name)) |gpu| {
try gpus.append(gpu);
}
}
@ -123,8 +71,21 @@ pub fn getGPUInfo(allocator: mem.Allocator) ![][]const u8 {
return gpus.toOwnedSlice();
}
fn detectNvidiaGPU(allocator: mem.Allocator, card_name: []const u8) !?[]const u8 {
// First try to get device ID for PCI lookup
fn detectGPU(allocator: mem.Allocator, card_name: []const u8) !?[]const u8 {
if (try queryHwdb(allocator, card_name)) |gpu| {
return gpu;
}
const vendor_path = try allocPrint(allocator, "/sys/class/drm/{s}/device/vendor", .{card_name});
defer allocator.free(vendor_path);
const vendor_file = fs.openFileAbsolute(vendor_path, .{}) catch return null;
defer vendor_file.close();
var vendor_buf: [32]u8 = undefined;
const vendor_len = vendor_file.readAll(&vendor_buf) catch return null;
const vendor_str = mem.trim(u8, vendor_buf[0..vendor_len], "\n\r \t0x");
const device_path = try allocPrint(allocator, "/sys/class/drm/{s}/device/device", .{card_name});
defer allocator.free(device_path);
@ -135,257 +96,174 @@ fn detectNvidiaGPU(allocator: mem.Allocator, card_name: []const u8) !?[]const u8
const device_len = device_file.readAll(&device_buf) catch return null;
const device_str = mem.trim(u8, device_buf[0..device_len], "\n\r \t0x");
const device_id = std.fmt.parseInt(u32, device_str, 16) catch return null;
// Try /proc/driver/nvidia first
const proc_path = try allocPrint(allocator, "/proc/driver/nvidia/gpus/{s}/information", .{card_name});
defer allocator.free(proc_path);
var model_name: ?[]const u8 = null;
var vbios_version: ?[]const u8 = null;
var memory_size: ?u64 = null;
// Try to read from nvidia proc
const info_file = fs.openFileAbsolute(proc_path, .{}) catch null;
if (info_file) |file| {
defer file.close();
var buf: [1024]u8 = undefined;
const bytes_read = file.readAll(&buf) catch 0;
if (bytes_read > 0) {
const content = buf[0..bytes_read];
var lines = mem.split(u8, content, "\n");
while (lines.next()) |line| {
if (mem.indexOf(u8, line, "Model:")) |_| {
const start = mem.indexOf(u8, line, ":").? + 1;
model_name = mem.trim(u8, line[start..], " \t\r\n");
} else if (mem.indexOf(u8, line, "Video BIOS:")) |_| {
const start = mem.indexOf(u8, line, ":").? + 1;
vbios_version = mem.trim(u8, line[start..], " \t\r\n");
} else if (mem.indexOf(u8, line, "Video Memory:")) |_| {
const start = mem.indexOf(u8, line, ":").? + 1;
const mem_str = mem.trim(u8, line[start..], " \t\r\nMB");
memory_size = std.fmt.parseInt(u64, mem_str, 10) catch null;
}
}
}
}
// If we couldn't get model name from nvidia proc, try product name
if (model_name == null) {
const product_path = try allocPrint(allocator, "/sys/class/drm/{s}/device/product_name", .{card_name});
defer allocator.free(product_path);
const product_file = fs.openFileAbsolute(product_path, .{}) catch null;
if (product_file) |file| {
defer file.close();
var product_buf: [256]u8 = undefined;
const len = file.readAll(&product_buf) catch 0;
if (len > 0) {
model_name = mem.trim(u8, product_buf[0..len], "\n\r \t");
}
}
}
// If still no model name, try PCI lookup
if (model_name == null) {
model_name = if (findPCIName(VendorId.NVIDIA, device_id)) |name|
name
else
try allocPrint(allocator, "NVIDIA GPU (0x{x:0>4})", .{device_id});
}
// Get driver info if not already found from nvidia proc
if (vbios_version == null) {
const driver_path = try allocPrint(allocator, "/sys/class/drm/{s}/device/driver", .{card_name});
defer allocator.free(driver_path);
vbios_version = blk: {
var dir_handle = fs.openDirAbsolute(driver_path, .{}) catch break :blk null;
defer dir_handle.close();
var path_buf: [256]u8 = undefined;
const path = dir_handle.readLink("driver", &path_buf) catch break :blk null;
break :blk std.fs.path.basename(path);
};
}
const model = try allocPrint(allocator, "(vendor: 0x{s}, device: 0x{s})", .{ vendor_str, device_str });
errdefer allocator.free(model);
const gpu_info = GPUInfo{
.vendor = "NVIDIA",
.model = model_name.?, // Safe because we always set it above
.driver = vbios_version,
.memory = memory_size,
};
const formatted = try gpu_info.format(allocator);
return formatted;
}
fn detectAMDGPU(allocator: mem.Allocator, card_name: []const u8, vendor_id: u32) !?[]const u8 {
// Get device ID
const device_path = try allocPrint(allocator, "/sys/class/drm/{s}/device/device", .{card_name});
defer allocator.free(device_path);
const device_file = fs.openFileAbsolute(device_path, .{}) catch return null;
defer device_file.close();
var device_buf: [32]u8 = undefined;
const device_len = device_file.readAll(&device_buf) catch return null;
const device_str = mem.trim(u8, device_buf[0..device_len], "\n\r \t0x");
const device_id = std.fmt.parseInt(u32, device_str, 16) catch return null;
// Get model name from PCI database
const model_name = if (findPCIName(vendor_id, device_id)) |name|
name
else
try allocPrint(allocator, "GPU (0x{x:0>4})", .{device_id});
// Get driver info
const driver = blk: {
const driver_path = try allocPrint(allocator, "/sys/class/drm/{s}/device/driver", .{card_name});
defer allocator.free(driver_path);
var dir_handle = fs.openDirAbsolute(driver_path, .{}) catch break :blk null;
defer dir_handle.close();
var path_buf: [256]u8 = undefined;
const path = dir_handle.readLink("driver", &path_buf) catch break :blk null;
break :blk std.fs.path.basename(path);
};
// Get VRAM info
var memory: ?u64 = null;
const vram_path = try allocPrint(allocator, "/sys/class/drm/{s}/device/mem_info_vram_total", .{card_name});
defer allocator.free(vram_path);
if (fs.openFileAbsolute(vram_path, .{})) |vram_file| {
defer vram_file.close();
var vram_buf: [32]u8 = undefined;
if (vram_file.readAll(&vram_buf)) |len| {
const vram_str = mem.trim(u8, vram_buf[0..len], "\n\r \t");
if (std.fmt.parseInt(u64, vram_str, 10)) |vram_bytes| {
memory = vram_bytes / (1024 * 1024); // Convert to MB
} else |_| {}
} else |_| {}
} else |_| {}
const gpu_info = GPUInfo{
.vendor = "AMD",
.model = model_name,
.driver = driver,
.memory = memory,
};
const formatted = try gpu_info.format(allocator);
return formatted;
}
fn detectIntelGPU(allocator: mem.Allocator, card_name: []const u8) !?[]const u8 {
const product_path = try allocPrint(allocator, "/sys/class/drm/{s}/device/product_name", .{card_name});
defer allocator.free(product_path);
var product_name: []const u8 = undefined;
if (fs.openFileAbsolute(product_path, .{})) |product_file| {
defer product_file.close();
var product_buf: [256]u8 = undefined;
const product_len = product_file.readAll(&product_buf) catch return null;
product_name = mem.trim(u8, product_buf[0..product_len], "\n\r \t");
} else |_| {
return null;
}
const driver_path = try allocPrint(allocator, "/sys/class/drm/{s}/device/driver", .{card_name});
defer allocator.free(driver_path);
const driver = blk: {
var dir_handle = fs.openDirAbsolute(driver_path, .{}) catch break :blk null;
defer dir_handle.close();
var path_buf: [256]u8 = undefined;
const path = dir_handle.readLink("driver", &path_buf) catch break :blk null;
break :blk std.fs.path.basename(path);
};
const gpu_info = GPUInfo{
.vendor = "Intel",
.model = product_name,
.driver = driver,
.memory = null,
};
const formatted = try gpu_info.format(allocator);
return formatted;
}
fn findPCIName(vendor_id: u32, device_id: u32) ?[]const u8 {
const pci_data = @embedFile("../../data/pci.ids");
var lines = mem.split(u8, pci_data, "\n");
var current_vendor: ?u32 = null;
while (lines.next()) |line| {
const trimmed = mem.trim(u8, line, " \r\n");
if (trimmed.len == 0 or trimmed[0] == '#') continue;
// Count leading tabs
const tab_count = for (trimmed, 0..) |c, i| {
if (c != '\t') break @as(usize, @intCast(i));
} else trimmed.len;
// No tabs = vendor line
if (tab_count == 0) {
const space_idx = mem.indexOf(u8, trimmed, " ") orelse continue;
const vendor_str = trimmed[0..space_idx];
current_vendor = std.fmt.parseInt(u32, vendor_str, 16) catch continue;
continue;
}
// One tab = device line
if (tab_count == 1 and current_vendor != null and current_vendor.? == vendor_id) {
const device_line = trimmed[tab_count..];
const space_idx = mem.indexOf(u8, device_line, " ") orelse continue;
const device_str = device_line[0..space_idx];
const found_device = std.fmt.parseInt(u32, device_str, 16) catch continue;
if (found_device == device_id) {
return device_line[space_idx + 1 ..];
}
}
}
return null;
}
fn detectGenericGPU(allocator: mem.Allocator, card_name: []const u8, vendor_id: u32) !?[]const u8 {
const device_path = try allocPrint(allocator, "/sys/class/drm/{s}/device/device", .{card_name});
defer allocator.free(device_path);
const device_file = fs.openFileAbsolute(device_path, .{}) catch return null;
defer device_file.close();
var device_buf: [32]u8 = undefined;
const device_len = device_file.readAll(&device_buf) catch return null;
const device_str = mem.trim(u8, device_buf[0..device_len], "\n\r \t0x");
const device_id = std.fmt.parseInt(u32, device_str, 16) catch return null;
const vendor_name = switch (vendor_id) {
VendorId.AMD, VendorId.AMD_2 => "AMD",
VendorId.INTEL, VendorId.INTEL_2, VendorId.INTEL_3 => "Intel",
VendorId.NVIDIA, VendorId.NVIDIA_2, VendorId.NVIDIA_3 => "NVIDIA",
else => "Unknown",
};
const gpu_info = GPUInfo{
.vendor = vendor_name,
.model = if (findPCIName(vendor_id, device_id)) |name|
name
else
try allocPrint(allocator, "GPU (0x{x:0>4})", .{device_id}),
.vendor = "GPU",
.model = model,
.driver = null,
.memory = null,
};
const formatted = try gpu_info.format(allocator);
allocator.free(model);
return formatted;
}
fn getShortVendorName(vendor: []const u8) []const u8 {
if (mem.startsWith(u8, vendor, "Advanced Micro Devices")) return "AMD";
if (mem.startsWith(u8, vendor, "AMD")) return "AMD";
if (mem.startsWith(u8, vendor, "NVIDIA Corporation")) return "NVIDIA";
if (mem.startsWith(u8, vendor, "Intel Corporation")) return "Intel";
return vendor;
}
fn simdStartsWith(haystack: []const u8, needle: []const u8) bool {
if (haystack.len < needle.len) return false;
const Vec = @Vector(32, u8);
var i: usize = 0;
while (i + 32 <= needle.len) : (i += 32) {
const hay = @as(Vec, haystack[i .. i + 32][0..32].*);
const ned = @as(Vec, needle[i .. i + 32][0..32].*);
const mask = hay == ned;
if (!@reduce(.And, mask)) return false;
}
const Vec8 = @Vector(8, u8);
while (i + 8 <= needle.len) : (i += 8) {
const hay = @as(Vec8, haystack[i .. i + 8][0..8].*);
const ned = @as(Vec8, needle[i .. i + 8][0..8].*);
const mask = hay == ned;
if (!@reduce(.And, mask)) return false;
}
while (i < needle.len) : (i += 1) {
if (haystack[i] != needle[i]) return false;
}
return true;
}
fn cleanModelName(model: []const u8) []const u8 {
var start: usize = 0;
while (start < model.len and (model[start] == ' ' or std.ascii.isDigit(model[start]))) : (start += 1) {}
const trimmed = model[start..];
if (mem.startsWith(u8, trimmed, "AMD")) return trimmed["AMD".len..];
if (mem.startsWith(u8, trimmed, "NVIDIA")) return trimmed["NVIDIA".len..];
if (mem.startsWith(u8, trimmed, "Intel")) return trimmed["Intel".len..];
if (mem.indexOf(u8, trimmed, "[")) |bracket_start| {
if (mem.indexOf(u8, trimmed[bracket_start..], "]")) |bracket_end| {
return trimmed[bracket_start + 1 .. bracket_start + bracket_end];
}
}
return trimmed;
}
fn queryHwdb(allocator: mem.Allocator, card_name: []const u8) !?[]const u8 {
const modalias_path = try allocPrint(allocator, "/sys/class/drm/{s}/device/modalias", .{card_name});
defer allocator.free(modalias_path);
const modalias_file = fs.openFileAbsolute(modalias_path, .{}) catch return null;
defer modalias_file.close();
var modalias_buf: [256]u8 = undefined;
const modalias = modalias_file.readAll(&modalias_buf) catch return null;
const modalias_str = mem.trimRight(u8, modalias_buf[0..modalias], "\n");
if (!mem.startsWith(u8, modalias_str, "pci:")) return null;
var vendor_id: ?[]const u8 = null;
var device_id: ?[]const u8 = null;
var modalias_idx: usize = 4;
while (modalias_idx < modalias_str.len) : (modalias_idx += 1) {
if (modalias_str[modalias_idx] == 'v' and modalias_idx + 9 <= modalias_str.len) {
vendor_id = modalias_str[modalias_idx + 1 .. modalias_idx + 9];
var j = modalias_idx + 9;
while (j < modalias_str.len) : (j += 1) {
if (modalias_str[j] == 'd' and j + 9 <= modalias_str.len) {
device_id = modalias_str[j + 1 .. j + 9];
break;
}
}
break;
}
}
if (vendor_id == null or device_id == null) return null;
const hwdb_file = fs.openFileAbsolute("/usr/lib/udev/hwdb.d/20-pci-vendor-model.hwdb", .{}) catch return null;
defer hwdb_file.close();
const stat = try hwdb_file.stat();
const mapped = try std.posix.mmap(
null,
@as(usize, @intCast(stat.size)),
std.posix.PROT.READ,
.{ .TYPE = std.os.linux.MAP_TYPE.PRIVATE },
hwdb_file.handle,
0,
);
defer std.posix.munmap(mapped);
var vendor_name: ?[]const u8 = null;
var model_name: ?[]const u8 = null;
const vendor_pattern = try allocPrint(allocator, "pci:v{s}*", .{vendor_id.?});
defer allocator.free(vendor_pattern);
const device_pattern = try allocPrint(allocator, "pci:v{s}d{s}*", .{ vendor_id.?, device_id.? });
defer allocator.free(device_pattern);
var it = mem.split(u8, mapped, "\n");
while (it.next()) |line| {
const trimmed = mem.trim(u8, line, " \t\r\n");
if (trimmed.len == 0 or simdStartsWith(trimmed, "#")) continue;
if (vendor_name == null and simdStartsWith(trimmed, vendor_pattern)) {
if (it.next()) |next_line| {
const next_trimmed = mem.trim(u8, next_line, " \t\r\n");
if (simdStartsWith(next_trimmed, "ID_VENDOR_FROM_DATABASE=")) {
const raw_vendor = next_trimmed["ID_VENDOR_FROM_DATABASE=".len..];
vendor_name = try allocator.dupe(u8, raw_vendor);
}
}
} else if (model_name == null and simdStartsWith(trimmed, device_pattern)) {
if (it.next()) |next_line| {
const next_trimmed = mem.trim(u8, next_line, " \t\r\n");
if (simdStartsWith(next_trimmed, "ID_MODEL_FROM_DATABASE=")) {
const raw_model = next_trimmed["ID_MODEL_FROM_DATABASE=".len..];
model_name = try allocator.dupe(u8, raw_model);
}
}
}
if (vendor_name != null and model_name != null) break;
}
if (vendor_name != null and model_name != null) {
const gpu_info = GPUInfo{
.vendor = try allocator.dupe(u8, getShortVendorName(vendor_name.?)),
.model = try allocator.dupe(u8, cleanModelName(model_name.?)),
.driver = null,
.memory = null,
};
const formatted = try gpu_info.format(allocator);
allocator.free(gpu_info.vendor);
allocator.free(gpu_info.model);
if (vendor_name != null) allocator.free(vendor_name.?);
if (model_name != null) allocator.free(model_name.?);
return formatted;
}
if (vendor_name != null) allocator.free(vendor_name.?);
if (model_name != null) allocator.free(model_name.?);
return null;
}