Update GPU detection logic
All checks were successful
PikaOS Package Build & Release (amd64-v3) / build (push) Successful in 49s

- Enhanced GPU detection functions to support multiple GPU vendors (NVIDIA, AMD, Intel) and improved memory reporting.
This commit is contained in:
ferreo 2024-11-30 19:44:28 +00:00
parent 946c64d5ab
commit f118503903
3 changed files with 274 additions and 119 deletions

View File

@ -1 +1 @@
2 3

View File

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

View File

@ -1,7 +1,62 @@
const std = @import("std"); const std = @import("std");
const mem = std.mem;
const fs = std.fs;
const ArrayList = std.ArrayList;
const allocPrint = std.fmt.allocPrint;
pub fn getGPUInfo(allocator: std.mem.Allocator) ![][]const u8 { const VendorId = struct {
var gpus = std.ArrayList([]const u8).init(allocator); 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 GPUInfo = struct {
vendor: []const u8,
model: []const u8,
driver: ?[]const u8,
memory: ?u64,
pub fn format(self: GPUInfo, allocator: mem.Allocator) ![]const u8 {
if (self.memory) |memm| {
if (self.driver) |drv| {
return allocPrint(allocator, "{s} {s} ({s}, {d}MB)", .{
self.vendor, self.model, drv, memm,
});
} else {
return allocPrint(allocator, "{s} {s} ({d}MB)", .{
self.vendor, self.model, memm,
});
}
} else {
if (self.driver) |drv| {
return allocPrint(allocator, "{s} {s} ({s})", .{
self.vendor, self.model, drv,
});
} else {
return allocPrint(allocator, "{s} {s}", .{
self.vendor, self.model,
});
}
}
}
};
pub fn getGPUInfo(allocator: mem.Allocator) ![][]const u8 {
var gpus = ArrayList([]const u8).init(allocator);
errdefer { errdefer {
for (gpus.items) |gpu| { for (gpus.items) |gpu| {
allocator.free(gpu); allocator.free(gpu);
@ -9,115 +64,55 @@ pub fn getGPUInfo(allocator: std.mem.Allocator) ![][]const u8 {
gpus.deinit(); gpus.deinit();
} }
var card_num: u32 = 0; var dir = fs.openDirAbsolute("/sys/class/drm", .{ .iterate = true }) catch {
while (true) : (card_num += 1) { const unknown = try allocator.dupe(u8, "Unknown GPU");
const card_path = try std.fmt.allocPrint(allocator, "/sys/class/drm/card{d}/device", .{card_num}); try gpus.append(unknown);
defer allocator.free(card_path); return gpus.toOwnedSlice();
};
defer dir.close();
const vendor_file = std.fs.openFileAbsolute(try std.fmt.allocPrint(allocator, "{s}/vendor", .{card_path}), .{}) catch |err| switch (err) { var iter = dir.iterate();
error.FileNotFound => break, // No more cards found while (try iter.next()) |entry| {
else => return err, 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(); defer vendor_file.close();
const device_file = std.fs.openFileAbsolute(try std.fmt.allocPrint(allocator, "{s}/device", .{card_path}), .{}) catch |err| switch (err) {
error.FileNotFound => continue,
else => return err,
};
defer device_file.close();
var vendor_buf: [32]u8 = undefined; var vendor_buf: [32]u8 = undefined;
var device_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_bytes = try vendor_file.readAll(&vendor_buf); const vendor_id = std.fmt.parseInt(u32, vendor_str, 16) catch continue;
const device_bytes = try device_file.readAll(&device_buf);
const vendor_str = std.mem.trim(u8, vendor_buf[0..vendor_bytes], "\n\r \t0x"); switch (vendor_id) {
const device_str = std.mem.trim(u8, device_buf[0..device_bytes], "\n\r \t0x"); VendorId.NVIDIA, VendorId.NVIDIA_2, VendorId.NVIDIA_3 => {
if (try detectNvidiaGPU(allocator, entry.name)) |gpu| {
const vendor_id = try std.fmt.parseInt(u32, vendor_str, 16); try gpus.append(gpu);
const device_id = try std.fmt.parseInt(u32, device_str, 16); continue;
const modalias_file = std.fs.openFileAbsolute(try std.fmt.allocPrint(allocator, "/sys/class/drm/card{d}/device/modalias", .{card_num}), .{}) catch |err| switch (err) {
error.FileNotFound => null,
else => return err,
};
defer if (modalias_file) |f| f.close();
const name_file = std.fs.openFileAbsolute(try std.fmt.allocPrint(allocator, "/sys/class/drm/card{d}/device/product", .{card_num}), .{}) catch |err| switch (err) {
error.FileNotFound => null,
else => return err,
};
defer if (name_file) |f| f.close();
const model_file = std.fs.openFileAbsolute(try std.fmt.allocPrint(allocator, "/sys/class/drm/card{d}/device/model", .{card_num}), .{}) catch |err| switch (err) {
error.FileNotFound => null,
else => return err,
};
defer if (model_file) |f| f.close();
const subsystem_file = std.fs.openFileAbsolute(try std.fmt.allocPrint(allocator, "/sys/class/drm/card{d}/device/subsystem_device", .{card_num}), .{}) catch |err| switch (err) {
error.FileNotFound => null,
else => return err,
};
defer if (subsystem_file) |f| f.close();
var name_buf: [256]u8 = undefined;
var gpu_name: []const u8 = "";
if (name_file) |f| {
const name_bytes = try f.readAll(&name_buf);
const name = std.mem.trim(u8, name_buf[0..name_bytes], "\n\r \t");
if (name.len > 0) {
gpu_name = name;
}
}
if (gpu_name.len == 0) {
if (model_file) |f| {
const name_bytes = try f.readAll(&name_buf);
const name = std.mem.trim(u8, name_buf[0..name_bytes], "\n\r \t");
if (name.len > 0) {
gpu_name = name;
} }
} },
VendorId.AMD, VendorId.AMD_2 => {
if (try detectAMDGPU(allocator, entry.name)) |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 => {},
} }
// Declare and initialize the driver variable if (try detectGenericGPU(allocator, entry.name, vendor_id)) |gpu| {
var dir_handle = std.fs.openDirAbsolute(card_path, .{}) catch null; try gpus.append(gpu);
const driver = if (dir_handle) |*dir| blk: { }
defer dir.close();
var path_buf: [256]u8 = undefined;
if (dir.readLink("driver", &path_buf)) |path| {
break :blk std.fs.path.basename(path);
} else |_| {
break :blk null;
}
} else null;
const gpu_info = if (findPCIName(vendor_id, device_id)) |model|
try std.fmt.allocPrint(allocator, "{s} {s}", .{
switch (vendor_id) {
0x1002 => "AMD",
0x10de => "NVIDIA",
0x8086 => "Intel",
else => "Unknown",
},
model,
})
else
try std.fmt.allocPrint(allocator, "{s} GPU (0x{x:0>4}){s}", .{
switch (vendor_id) {
0x1002 => "AMD",
0x10de => "NVIDIA",
0x8086 => "Intel",
else => "Unknown",
},
device_id,
if (driver) |d| try std.fmt.allocPrint(allocator, ", {s}", .{d}) else "",
});
try gpus.append(gpu_info);
} }
if (gpus.items.len == 0) { if (gpus.items.len == 0) {
@ -128,33 +123,193 @@ pub fn getGPUInfo(allocator: std.mem.Allocator) ![][]const u8 {
return gpus.toOwnedSlice(); return gpus.toOwnedSlice();
} }
const pci_ids = @embedFile("../../data/pci.ids"); fn detectNvidiaGPU(allocator: mem.Allocator, card_name: []const u8) !?[]const u8 {
const proc_path = try allocPrint(allocator, "/proc/driver/nvidia/gpus/{s}/information", .{card_name});
defer allocator.free(proc_path);
const info_file = fs.openFileAbsolute(proc_path, .{}) catch return null;
defer info_file.close();
var buf: [1024]u8 = undefined;
const bytes_read = info_file.readAll(&buf) catch return null;
const content = buf[0..bytes_read];
var model_name: ?[]const u8 = null;
var vbios_version: ?[]const u8 = null;
var memory_size: ?u64 = null;
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 (model_name) |model| {
const gpu_info = GPUInfo{
.vendor = "NVIDIA",
.model = model,
.driver = vbios_version,
.memory = memory_size,
};
const formatted = try gpu_info.format(allocator);
return formatted;
}
return null;
}
fn detectAMDGPU(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 = "AMD",
.model = product_name,
.driver = driver,
.memory = null,
};
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 { fn findPCIName(vendor_id: u32, device_id: u32) ?[]const u8 {
var lines = std.mem.splitSequence(u8, pci_ids, "\n"); const pci_data = @embedFile("../../data/pci.ids");
var lines = mem.split(u8, pci_data, "\n");
var current_vendor: ?u32 = null; var current_vendor: ?u32 = null;
while (lines.next()) |line| { while (lines.next()) |line| {
if (line.len == 0 or line[0] == '#') continue; const trimmed = mem.trim(u8, line, " \t\r");
if (trimmed.len == 0 or trimmed[0] == '#') continue;
if (line[0] != '\t') { // Check if this is a vendor line (no tabs at start)
if (line.len < 4) continue; if (trimmed[0] != '\t') {
const vid = std.fmt.parseInt(u32, line[0..4], 16) catch continue; // Parse vendor ID
current_vendor = vid; 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; continue;
} }
if (current_vendor) |vid| { // If we found our vendor, look for the device
if (vid != vendor_id) continue; if (current_vendor) |vendor| {
if (vendor != vendor_id) continue;
if (line[0] == '\t' and line[1] != '\t') { // Device lines have one tab
if (line.len < 5) continue; if (trimmed[0] != '\t' or trimmed.len < 2) continue;
const did = std.fmt.parseInt(u32, std.mem.trim(u8, line[1..5], " "), 16) catch continue; const device_line = mem.trim(u8, trimmed[1..], " \t");
if (did == device_id) {
return std.mem.trim(u8, line[6..], " \t"); 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; 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}),
.driver = null,
.memory = null,
};
const formatted = try gpu_info.format(allocator);
return formatted;
}