summaryrefslogtreecommitdiff
path: root/Userland/Utilities/lspci.cpp
blob: b26a03f4a9c20981929866d4a49f6f30bb46b3c6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/*
 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
 * Copyright (c) 2022, the SerenityOS developers.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <AK/DeprecatedString.h>
#include <AK/Hex.h>
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <AK/StringUtils.h>
#include <AK/StringView.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/DirIterator.h>
#include <LibCore/File.h>
#include <LibCore/System.h>
#include <LibMain/Main.h>
#include <LibPCIDB/Database.h>

static bool flag_show_numerical = false;
static bool flag_verbose = false;

static constexpr StringView format_numerical = "{:04x}:{:02x}:{:02x}.{} {}: {}:{} (rev {:02x})"sv;
static constexpr StringView format_textual = "{:04x}:{:02x}:{:02x}.{} {}: {} {} (rev {:02x})"sv;
static constexpr StringView format_region = "\tBAR {}: {} region @ {:#x}"sv;

static u32 read_hex_string_from_bytebuffer(ByteBuffer const& buf)
{
    // FIXME: Propagate errors.
    return AK::StringUtils::convert_to_uint_from_hex(
        DeprecatedString(MUST(buf.slice(2, buf.size() - 2)).bytes()))
        .release_value();
}

static u32 convert_sysfs_value_to_uint(DeprecatedString 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<int> serenity_main(Main::Arguments arguments)
{
    TRY(Core::System::pledge("stdio rpath"));

    Core::ArgsParser args_parser;
    args_parser.set_general_help("List PCI devices.");
    args_parser.add_option(flag_show_numerical, "Show numerical IDs", "numerical", 'n');
    args_parser.add_option(flag_verbose, "Show verbose info on devices", "verbose", 'v');
    args_parser.parse(arguments);

    if (!flag_show_numerical)
        TRY(Core::System::unveil("/res/pci.ids", "r"));
    TRY(Core::System::unveil("/sys/bus/pci", "r"));
    TRY(Core::System::unveil(nullptr, nullptr));

    auto const format = flag_show_numerical ? format_numerical : format_textual;

    RefPtr<PCIDB::Database> db;
    if (!flag_show_numerical) {
        db = PCIDB::Database::open();
        if (!db) {
            warnln("Couldn't open PCI ID database");
            flag_show_numerical = true;
        }
    }

    Core::DirIterator di("/sys/bus/pci/", Core::DirIterator::SkipParentAndBaseDir);
    if (di.has_error()) {
        auto error = di.error();
        warnln("Failed to open /sys/bus/pci - {}", error);
        return error;
    }

    TRY(Core::System::pledge("stdio rpath"));

    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_filename = DeprecatedString::formatted("/sys/bus/pci/{}/vendor", dir);
        auto vendor_id_file = Core::File::open(vendor_id_filename, Core::File::OpenMode::Read);
        if (vendor_id_file.is_error()) {
            dbgln("Error: Could not open {}: {}", vendor_id_filename, vendor_id_file.error());
            continue;
        }
        auto device_id_filename = DeprecatedString::formatted("/sys/bus/pci/{}/device_id", dir);
        auto device_id_file = Core::File::open(device_id_filename, Core::File::OpenMode::Read);
        if (device_id_file.is_error()) {
            dbgln("Error: Could not open {}: {}", device_id_filename, device_id_file.error());
            continue;
        }
        auto class_id_filename = DeprecatedString::formatted("/sys/bus/pci/{}/class", dir);
        auto class_id_file = Core::File::open(class_id_filename, Core::File::OpenMode::Read);
        if (class_id_file.is_error()) {
            dbgln("Error: Could not open {}: {}", class_id_filename, class_id_file.error());
            continue;
        }
        auto subclass_id_filename = DeprecatedString::formatted("/sys/bus/pci/{}/subclass", dir);
        auto subclass_id_file = Core::File::open(subclass_id_filename, Core::File::OpenMode::Read);
        if (subclass_id_file.is_error()) {
            dbgln("Error: Could not open {}: {}", subclass_id_filename, subclass_id_file.error());
            continue;
        }
        auto revision_id_filename = DeprecatedString::formatted("/sys/bus/pci/{}/revision", dir);
        auto revision_id_file = Core::File::open(revision_id_filename, Core::File::OpenMode::Read);
        if (revision_id_file.is_error()) {
            dbgln("Error: Could not open {}: {}", revision_id_filename, revision_id_file.error());
            continue;
        }

        auto vendor_id_contents = vendor_id_file.value()->read_until_eof();
        if (vendor_id_contents.is_error()) {
            dbgln("Error: Could not read {}: {}", vendor_id_filename, vendor_id_contents.error());
            continue;
        }
        u32 vendor_id = read_hex_string_from_bytebuffer(vendor_id_contents.value());

        auto device_id_contents = device_id_file.value()->read_until_eof();
        if (device_id_contents.is_error()) {
            dbgln("Error: Could not read {}: {}", device_id_filename, device_id_contents.error());
            continue;
        }
        u32 device_id = read_hex_string_from_bytebuffer(device_id_contents.value());

        auto revision_id_contents = revision_id_file.value()->read_until_eof();
        if (revision_id_contents.is_error()) {
            dbgln("Error: Could not read {}: {}", revision_id_filename, revision_id_contents.error());
            continue;
        }
        u32 revision_id = read_hex_string_from_bytebuffer(revision_id_contents.value());

        auto class_id_contents = class_id_file.value()->read_until_eof();
        if (class_id_contents.is_error()) {
            dbgln("Error: Could not read {}: {}", class_id_filename, class_id_contents.error());
            continue;
        }
        u32 class_id = read_hex_string_from_bytebuffer(class_id_contents.value());

        auto subclass_id_contents = subclass_id_file.value()->read_until_eof();
        if (subclass_id_contents.is_error()) {
            dbgln("Error: Could not read {}: {}", subclass_id_filename, subclass_id_contents.error());
            continue;
        }
        u32 subclass_id = read_hex_string_from_bytebuffer(subclass_id_contents.value());

        DeprecatedString vendor_name;
        DeprecatedString device_name;
        DeprecatedString class_name;

        if (db) {
            vendor_name = db->get_vendor(vendor_id);
            device_name = db->get_device(vendor_id, device_id);
            class_name = db->get_class(class_id);
        }

        if (vendor_name.is_empty())
            vendor_name = DeprecatedString::formatted("{:04x}", vendor_id);
        if (device_name.is_empty())
            device_name = DeprecatedString::formatted("{:04x}", device_id);
        if (class_name.is_empty())
            class_name = DeprecatedString::formatted("{:02x}{:02x}", class_id, subclass_id);

        outln(format, domain, bus, device, function, class_name, vendor_name, device_name, revision_id);

        if (!flag_verbose)
            continue;
        for (size_t bar_index = 0; bar_index <= 5; bar_index++) {
            auto bar_value_filename = DeprecatedString::formatted("/sys/bus/pci/{}/bar{}", dir, bar_index);
            auto bar_value_file = Core::File::open(bar_value_filename, Core::File::OpenMode::Read);
            if (bar_value_file.is_error()) {
                dbgln("Error: Could not open {}: {}", bar_value_filename, bar_value_file.error());
                continue;
            }

            auto bar_value_contents = bar_value_file.value()->read_until_eof();
            if (bar_value_contents.is_error()) {
                dbgln("Error: Could not read {}: {}", bar_value_filename, bar_value_contents.error());
                continue;
            }

            u32 bar_value = read_hex_string_from_bytebuffer(bar_value_contents.value());
            if (bar_value == 0)
                continue;
            bool memory_region = ((bar_value & 1) == 0);
            outln(format_region, bar_index, memory_region ? "Memory" : "IO", bar_value);
        }
    }

    return 0;
}