Serenity Operating System
1/*
2 * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Format.h>
8#include <Kernel/Bus/PCI/Access.h>
9#include <Kernel/Bus/PCI/Controller/HostController.h>
10#include <Kernel/Bus/PCI/Definitions.h>
11#include <Kernel/Sections.h>
12
13namespace Kernel::PCI {
14
15HostController::HostController(PCI::Domain const& domain)
16 : m_domain(domain)
17 , m_enumerated_buses(Bitmap::create(256, false).release_value_but_fixme_should_propagate_errors())
18{
19}
20
21UNMAP_AFTER_INIT Optional<u8> HostController::get_capabilities_pointer_for_function(BusNumber bus, DeviceNumber device, FunctionNumber function)
22{
23 if (read16_field(bus, device, function, PCI::RegisterOffset::STATUS) & (1 << 4)) {
24 return read8_field(bus, device, function, PCI::RegisterOffset::CAPABILITIES_POINTER);
25 }
26 return {};
27}
28
29UNMAP_AFTER_INIT Vector<Capability> HostController::get_capabilities_for_function(BusNumber bus, DeviceNumber device, FunctionNumber function)
30{
31 auto capabilities_pointer = get_capabilities_pointer_for_function(bus, device, function);
32 if (!capabilities_pointer.has_value()) {
33 return {};
34 }
35 Vector<Capability> capabilities;
36 auto capability_pointer = capabilities_pointer.value();
37 while (capability_pointer != 0) {
38 u16 capability_header = read16_field(bus, device, function, capability_pointer);
39 u8 capability_id = capability_header & 0xff;
40
41 // FIXME: Don't attach a PCI address to a capability object
42 capabilities.append({ Address(domain_number(), bus.value(), device.value(), function.value()), capability_id, capability_pointer });
43 capability_pointer = capability_header >> 8;
44 }
45 return capabilities;
46}
47
48u8 HostController::read8_field(BusNumber bus, DeviceNumber device, FunctionNumber function, PCI::RegisterOffset field)
49{
50 return read8_field(bus, device, function, to_underlying(field));
51}
52u16 HostController::read16_field(BusNumber bus, DeviceNumber device, FunctionNumber function, PCI::RegisterOffset field)
53{
54 return read16_field(bus, device, function, to_underlying(field));
55}
56
57UNMAP_AFTER_INIT void HostController::enumerate_functions(Function<IterationDecision(EnumerableDeviceIdentifier)> const& callback, BusNumber bus, DeviceNumber device, FunctionNumber function, bool recursive_search_into_bridges)
58{
59 dbgln_if(PCI_DEBUG, "PCI: Enumerating function, bus={}, device={}, function={}", bus, device, function);
60 Address address(domain_number(), bus.value(), device.value(), function.value());
61 auto pci_class = (read8_field(bus, device, function, PCI::RegisterOffset::CLASS) << 8u) | read8_field(bus, device, function, PCI::RegisterOffset::SUBCLASS);
62
63 HardwareID id = { read16_field(bus, device, function, PCI::RegisterOffset::VENDOR_ID), read16_field(bus, device, function, PCI::RegisterOffset::DEVICE_ID) };
64 ClassCode class_code = read8_field(bus, device, function, PCI::RegisterOffset::CLASS);
65 SubclassCode subclass_code = read8_field(bus, device, function, PCI::RegisterOffset::SUBCLASS);
66 ProgrammingInterface prog_if = read8_field(bus, device, function, PCI::RegisterOffset::PROG_IF);
67 RevisionID revision_id = read8_field(bus, device, function, PCI::RegisterOffset::REVISION_ID);
68 SubsystemID subsystem_id = read16_field(bus, device, function, PCI::RegisterOffset::SUBSYSTEM_ID);
69 SubsystemVendorID subsystem_vendor_id = read16_field(bus, device, function, PCI::RegisterOffset::SUBSYSTEM_VENDOR_ID);
70 InterruptLine interrupt_line = read8_field(bus, device, function, PCI::RegisterOffset::INTERRUPT_LINE);
71 InterruptPin interrupt_pin = read8_field(bus, device, function, PCI::RegisterOffset::INTERRUPT_PIN);
72 auto capabilities = get_capabilities_for_function(bus, device, function);
73 callback(EnumerableDeviceIdentifier { address, id, revision_id, class_code, subclass_code, prog_if, subsystem_id, subsystem_vendor_id, interrupt_line, interrupt_pin, capabilities });
74
75 if (pci_class == (to_underlying(PCI::ClassID::Bridge) << 8 | to_underlying(PCI::Bridge::SubclassID::PCI_TO_PCI))
76 && recursive_search_into_bridges
77 && (!m_enumerated_buses.get(read8_field(bus, device, function, PCI::RegisterOffset::SECONDARY_BUS)))) {
78 u8 secondary_bus = read8_field(bus, device, function, PCI::RegisterOffset::SECONDARY_BUS);
79 dbgln_if(PCI_DEBUG, "PCI: Found secondary bus: {}", secondary_bus);
80 VERIFY(secondary_bus != bus);
81 m_enumerated_buses.set(secondary_bus, true);
82 enumerate_bus(callback, secondary_bus, recursive_search_into_bridges);
83 }
84}
85
86UNMAP_AFTER_INIT void HostController::enumerate_device(Function<IterationDecision(EnumerableDeviceIdentifier)> const& callback, BusNumber bus, DeviceNumber device, bool recursive_search_into_bridges)
87{
88 dbgln_if(PCI_DEBUG, "PCI: Enumerating device in bus={}, device={}", bus, device);
89 if (read16_field(bus, device, 0, PCI::RegisterOffset::VENDOR_ID) == PCI::none_value)
90 return;
91 enumerate_functions(callback, bus, device, 0, recursive_search_into_bridges);
92 if (!(read8_field(bus, device, 0, PCI::RegisterOffset::HEADER_TYPE) & 0x80))
93 return;
94 for (u8 function = 1; function < 8; ++function) {
95 if (read16_field(bus, device, function, PCI::RegisterOffset::VENDOR_ID) != PCI::none_value)
96 enumerate_functions(callback, bus, device, function, recursive_search_into_bridges);
97 }
98}
99
100UNMAP_AFTER_INIT void HostController::enumerate_bus(Function<IterationDecision(EnumerableDeviceIdentifier)> const& callback, BusNumber bus, bool recursive_search_into_bridges)
101{
102 dbgln_if(PCI_DEBUG, "PCI: Enumerating bus {}", bus);
103 for (u8 device = 0; device < 32; ++device)
104 enumerate_device(callback, bus, device, recursive_search_into_bridges);
105}
106
107UNMAP_AFTER_INIT void HostController::enumerate_attached_devices(Function<IterationDecision(EnumerableDeviceIdentifier)> callback)
108{
109 VERIFY(Access::the().access_lock().is_locked());
110 VERIFY(Access::the().scan_lock().is_locked());
111 // First scan bus 0. Find any device on that bus, and if it's a PCI-to-PCI
112 // bridge, recursively scan it too.
113 m_enumerated_buses.set(m_domain.start_bus(), true);
114 enumerate_bus(callback, m_domain.start_bus(), true);
115
116 // Handle Multiple PCI host bridges on bus 0, device 0, functions 1-7 (function 0
117 // is the main host bridge).
118 // If we happen to miss some PCI buses because they are not reachable through
119 // recursive PCI-to-PCI bridges starting from bus 0, we might find them here.
120 if ((read8_field(0, 0, 0, PCI::RegisterOffset::HEADER_TYPE) & 0x80) != 0) {
121 for (int bus_as_function_number = 1; bus_as_function_number < 8; ++bus_as_function_number) {
122 if (read16_field(0, 0, bus_as_function_number, PCI::RegisterOffset::VENDOR_ID) == PCI::none_value)
123 continue;
124 if (read16_field(0, 0, bus_as_function_number, PCI::RegisterOffset::CLASS) != 0x6)
125 continue;
126 if (Checked<u8>::addition_would_overflow(m_domain.start_bus(), bus_as_function_number))
127 break;
128 if (m_enumerated_buses.get(m_domain.start_bus() + bus_as_function_number))
129 continue;
130 enumerate_bus(callback, m_domain.start_bus() + bus_as_function_number, false);
131 m_enumerated_buses.set(m_domain.start_bus() + bus_as_function_number, true);
132 }
133 }
134}
135
136}