From a91da672160f30a31cd81fc4825857bd33e9f534 Mon Sep 17 00:00:00 2001 From: Liav A Date: Tue, 8 Feb 2022 19:28:08 +0200 Subject: [PATCH] Utilities/lspci: Iterate over /sys/bus/pci instead of reading /proc/pci This opens many opportunities to add more data printed in lspci in a flexible manner - so instead of reading an ever-expanding JSON encoded file, we can add more features and let the utility read the directory entries from sysfs. This also allows not only filtering data on devices but to easily filter non-wanted devices when printing the output. --- Base/usr/share/man/man8/lspci.md | 2 +- Userland/Utilities/lspci.cpp | 85 +++++++++++++++++++++++++------- 2 files changed, 69 insertions(+), 18 deletions(-) diff --git a/Base/usr/share/man/man8/lspci.md b/Base/usr/share/man/man8/lspci.md index 97dced288b..3237cc5e70 100644 --- a/Base/usr/share/man/man8/lspci.md +++ b/Base/usr/share/man/man8/lspci.md @@ -15,7 +15,7 @@ and devices connected to them. It shows a brief list of devices. ## Files -* `/proc/pci` - source of the PCI devices list. +* `/sys/bus/pci` - source of the PCI devices list. * `/res/pci.ids` - a database of PCI identifiers used to match available devices to their vendor, device and class names. ## Examples diff --git a/Userland/Utilities/lspci.cpp b/Userland/Utilities/lspci.cpp index cd7ad88c5d..921c0bdbf7 100644 --- a/Userland/Utilities/lspci.cpp +++ b/Userland/Utilities/lspci.cpp @@ -4,10 +4,13 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include +#include #include +#include #include #include #include @@ -18,11 +21,25 @@ static bool flag_show_numerical = false; static const char* format_numerical = "{:04x}:{:02x}:{:02x}.{} {}: {}:{} (rev {:02x})"; static const char* format_textual = "{:04x}:{:02x}:{:02x}.{} {}: {} {} (rev {:02x})"; +static u32 read_hex_string_from_bytebuffer(ByteBuffer const& buf) +{ + return AK::StringUtils::convert_to_uint_from_hex(String(buf.slice(2, buf.size() - 2).bytes())).release_value(); +} + +static u32 convert_sysfs_value_to_uint(String const& value) +{ + if (auto result = AK::StringUtils::convert_to_uint_from_hex(value); result.has_value()) + return result.release_value(); + if (auto result = AK::StringUtils::convert_to_uint(value); result.has_value()) + return result.release_value(); + VERIFY_NOT_REACHED(); +} + ErrorOr serenity_main(Main::Arguments arguments) { TRY(Core::System::pledge("stdio rpath")); TRY(Core::System::unveil("/res/pci.ids", "r")); - TRY(Core::System::unveil("/proc/pci", "r")); + TRY(Core::System::unveil("/sys/bus/pci", "r")); TRY(Core::System::unveil(nullptr, nullptr)); Core::ArgsParser args_parser; @@ -41,23 +58,57 @@ ErrorOr serenity_main(Main::Arguments arguments) } } - auto proc_pci = TRY(Core::File::open("/proc/pci", Core::OpenMode::ReadOnly)); + Core::DirIterator di("/sys/bus/pci/", Core::DirIterator::SkipParentAndBaseDir); + if (di.has_error()) { + warnln("Failed to open /sys/bus/pci - {}", di.error()); + return 1; + } - TRY(Core::System::pledge("stdio")); + TRY(Core::System::pledge("stdio rpath")); - auto file_contents = proc_pci->read_all(); - auto json = TRY(JsonValue::from_string(file_contents)); - json.as_array().for_each([db, format](auto& value) { - auto& dev = value.as_object(); - auto domain = dev.get("domain").to_u32(); - auto bus = dev.get("bus").to_u32(); - auto device = dev.get("device").to_u32(); - auto function = dev.get("function").to_u32(); - auto vendor_id = dev.get("vendor_id").to_u32(); - auto device_id = dev.get("device_id").to_u32(); - auto revision_id = dev.get("revision_id").to_u32(); - auto class_id = dev.get("class").to_u32(); - auto subclass_id = dev.get("subclass").to_u32(); + while (di.has_next()) { + auto dir = di.next_path(); + auto domain_bus_device_parts = dir.split(':'); + VERIFY(domain_bus_device_parts.size() == 3); + auto domain = convert_sysfs_value_to_uint(domain_bus_device_parts[0]); + auto bus = convert_sysfs_value_to_uint(domain_bus_device_parts[1]); + auto device = convert_sysfs_value_to_uint(domain_bus_device_parts[2].split('.')[0]); + + auto function_parts = dir.split('.'); + VERIFY(function_parts.size() == 2); + auto function = convert_sysfs_value_to_uint(function_parts[1]); + + auto vendor_id_file = Core::File::construct(String::formatted("/sys/bus/pci/{}/vendor", dir)); + if (!vendor_id_file->open(Core::OpenMode::ReadOnly)) { + dbgln("Error: Could not open {}: {}", vendor_id_file->name(), vendor_id_file->error_string()); + continue; + } + auto device_id_file = Core::File::construct(String::formatted("/sys/bus/pci/{}/device_id", dir)); + if (!device_id_file->open(Core::OpenMode::ReadOnly)) { + dbgln("Error: Could not open {}: {}", device_id_file->name(), device_id_file->error_string()); + continue; + } + auto class_id_file = Core::File::construct(String::formatted("/sys/bus/pci/{}/class", dir)); + if (!class_id_file->open(Core::OpenMode::ReadOnly)) { + dbgln("Error: Could not open {}: {}", class_id_file->name(), class_id_file->error_string()); + continue; + } + auto subclass_id_file = Core::File::construct(String::formatted("/sys/bus/pci/{}/subclass", dir)); + if (!subclass_id_file->open(Core::OpenMode::ReadOnly)) { + dbgln("Error: Could not open {}: {}", subclass_id_file->name(), subclass_id_file->error_string()); + continue; + } + auto revision_id_file = Core::File::construct(String::formatted("/sys/bus/pci/{}/revision", dir)); + if (!revision_id_file->open(Core::OpenMode::ReadOnly)) { + dbgln("Error: Could not open {}: {}", revision_id_file->name(), revision_id_file->error_string()); + continue; + } + + u32 vendor_id = read_hex_string_from_bytebuffer(vendor_id_file->read_all()); + u32 device_id = read_hex_string_from_bytebuffer(device_id_file->read_all()); + u32 revision_id = read_hex_string_from_bytebuffer(revision_id_file->read_all()); + u32 class_id = read_hex_string_from_bytebuffer(class_id_file->read_all()); + u32 subclass_id = read_hex_string_from_bytebuffer(subclass_id_file->read_all()); String vendor_name; String device_name; @@ -77,7 +128,7 @@ ErrorOr serenity_main(Main::Arguments arguments) class_name = String::formatted("{:02x}{:02x}", class_id, subclass_id); outln(format, domain, bus, device, function, class_name, vendor_name, device_name, revision_id); - }); + } return 0; }