The Pedigree Project 0.1
Runtime.cc
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#include "pedigree/kernel/Log.h"
3#include "pedigree/kernel/machine/Device.h"
4#include "pedigree/kernel/machine/PciDrivers.h"
5#include "pedigree/kernel/machine/PciSriov.h"
6#include "pedigree/kernel/panic.h"
7#include "pedigree/kernel/utilities/new"
8
9#include "Enumeration.h"
10#include "ProbeBars.h"
11#include "Resources.h"
12
13namespace {
14bool readHeader(Device* device, PciBus::ConfigSpace& header) {
15 auto* bytes = reinterpret_cast<uint8_t*>(&header);
16 for (uint16_t offset = 0; offset < sizeof(header); offset += 4) {
17 uint32_t value = 0;
18 if (!PciBus::instance().readConfig32(device, offset, value)) {
19 return false;
20 }
21 for (unsigned byte = 0; byte < 4; ++byte) {
22 bytes[offset + byte] = value >> (byte * 8);
23 }
24 }
25 return true;
26}
27
28bool writeBar(Device* device, size_t index, uint32_t value) {
29 uint32_t actual = 0;
30 auto& pci = PciBus::instance();
31 return pci.writeConfig32(device, 0x10 + 4 * index, value) &&
32 pci.readConfig32(device, 0x10 + 4 * index, actual) && actual == value;
33}
34
35Device* slotBus(Device* port, uint8_t& number) {
36 uint32_t buses = 0;
37 if (!port || port->getPciClassCode() != 6 || port->getPciSubclassCode() != 4 ||
38 !PciBus::instance().readConfig32(port, 0x18, buses) ||
39 uint8_t(buses) != port->getPciBusPosition() || uint8_t(buses >> 8) <= uint8_t(buses) ||
40 uint8_t(buses >> 16) < uint8_t(buses >> 8)) {
41 return nullptr;
42 }
43 number = buses >> 8;
44 Device* result = nullptr;
46 for (size_t i = 0; i < port->getNumChildren(); ++i) {
47 Device* child = port->getChild(i);
48 if (child->getSpecificType() == "pci") {
49 if (result) {
50 return nullptr;
51 }
52 result = child;
53 }
54 }
55 return result;
56}
57
58bool noVirtualFunctions(Device* device) {
60 auto& pci = PciBus::instance();
61 const auto found = pci.findExtendedCapability(device, PciSriov::CapabilityId, capability);
62 if (found == PciExtendedCapabilities::FindResult::Absent) {
63 return true;
64 }
65 uint16_t control = 0;
66 return found == PciExtendedCapabilities::FindResult::Found &&
67 pci.readConfig16(device, capability.offset + 8, control) &&
68 !(control & PciSriov::VfControl);
69}
70
71Device* leaf(Device* bus) {
73 if (!bus || bus->getNumChildren() != 1) {
74 return nullptr;
75 }
76 Device* device = bus->getChild(0);
77 const auto header = device->getPciConfigHeader();
78 return !header.header_type && !device->getPciDevicePosition() &&
79 !device->getPciFunctionNumber() && !device->getPhysicalFunction()
80 ? device
81 : nullptr;
82}
83} // namespace
84
85namespace PciEnumeration {
86bool probeSlot(Device* port) {
87 uint8_t number = 0;
88 Device* bus = slotBus(port, number);
89 if (!bus) {
90 return false;
91 }
92 if (bus->getNumChildren()) {
93 return leaf(bus) != nullptr;
94 }
95 auto& pci = PciBus::instance();
96 auto* device = new Device;
97 device->setPciPosition(number, 0, 0);
98 device->setParent(bus);
99 PciBus::ConfigSpace original{};
100 if (!readHeader(device, original) || !original.vendor || original.vendor == 0xffff ||
101 original.header_type || (original.command & 7U) || (original.rom_base_address & 1U) ||
102 !noVirtualFunctions(device)) {
103 delete device;
104 return false;
105 }
106 device->setPciIdentifiers(original.class_code, original.subclass, original.vendor,
107 original.device, original.progif);
108 PciBus::ConfigSpace assigned = original;
109 bool registered = false;
110 auto rollback = [&]() {
111 if (!pci.updateCommand(device, 7U, 0)) {
112 panic("PCI hotplug: cannot stop decoding during probe rollback");
113 }
114 for (size_t i = 0; i < 6; ++i) {
115 if (!writeBar(device, i, original.bar[i])) {
116 panic("PCI hotplug: cannot restore BARs during probe rollback");
117 }
118 }
119 if (registered && !PciResources::removeFunction(device)) {
120 panic("PCI hotplug: cannot reclaim failed probe resources");
121 }
122 delete device;
123 return false;
124 };
125 // A newly inserted endpoint has no driver. Normalize stale, disabled BARs
126 // before reserving space; firmware will not allocate this insertion for us.
127 for (size_t i = 0; i < 6; ++i) {
128 const uint32_t low = original.bar[i];
129 assigned.bar[i] = low & ((low & 1U) ? 3U : 15U);
130 if (!writeBar(device, i, assigned.bar[i])) {
131 return rollback();
132 }
133 if (!(low & 1U) && (low & 6U) == 4) {
134 if (++i == 6) {
135 return rollback();
136 }
137 assigned.bar[i] = 0;
138 if (!writeBar(device, i, 0)) {
139 return rollback();
140 }
141 }
142 }
143 device->setPciConfigHeader(assigned);
144 if (!PciResources::addFunction(device)) {
145 return rollback();
146 }
147 registered = true;
148 const auto probe = PciBar::probe(pci, device, assigned);
149 if (probe.result != PciBar::ProbeResult::Success) {
150 return rollback();
151 }
152 for (size_t i = 0; i < 6; ++i) {
153 const uint32_t low = assigned.bar[i];
154 const bool wide = !(low & 1U) && (low & 6U) == 4;
155 if ((low & 6U) && !wide) {
156 return rollback();
157 }
158 uint64_t mask = probe.masks[i] & ~uint32_t{15};
159 if (wide) {
160 if (i == 5) {
161 return rollback();
162 }
163 mask |= uint64_t(probe.masks[i + 1]) << 32;
164 }
165 if (!mask) {
166 if (wide) {
167 ++i;
168 }
169 continue;
170 }
171 if (low & 1U) {
172 return rollback();
173 }
174 const uint64_t width = wide ? ~uint64_t{0} : 0xffffffffU;
175 const uint64_t bytes = ((~mask) & width) + 1;
176 uint64_t base = 0, cpu = 0;
177 if (!bytes || (bytes & (bytes - 1)) || bytes > ~size_t{0} ||
178 !PciResources::allocate(device, bytes, bytes, low & 8U, width, base) ||
179 !pci.translateAddress(base, bytes, false, cpu) || cpu > ~uintptr_t{0}) {
180 return rollback();
181 }
182 assigned.bar[i] = uint32_t(base) | low;
183 if (!writeBar(device, i, assigned.bar[i])) {
184 return rollback();
185 }
186 char name[] = {'b', 'a', 'r', static_cast<char>('0' + i), 0};
187 device->addresses().pushBack(new Device::Address(String(name), cpu, bytes, false));
188 if (wide) {
189 assigned.bar[++i] = base >> 32;
190 if (!writeBar(device, i, assigned.bar[i])) {
191 return rollback();
192 }
193 }
194 }
195 device->setPciConfigHeader(assigned);
196 const uint32_t route = pci.interruptRoute(number, 0, 0, assigned.interrupt_pin);
197 device->setInterruptNumber(route ? route : assigned.interrupt_line);
198 {
200 bus->addChild(device);
201 }
202 const bool bound = PciDrivers::attach(device);
203 NOTICE("PCI hotplug: inserted " << Dec << unsigned(number) << ":0.0 driver bound=" << bound);
204 return true;
205}
206
207bool prepareRemoveSlot(Device* port) {
208 uint8_t number = 0;
209 Device* device = leaf(slotBus(port, number));
210 return device && noVirtualFunctions(device) && PciDrivers::prepareRemove(device);
211}
212
213void cancelRemoveSlot(Device* port) {
214 uint8_t number = 0;
215 if (Device* device = leaf(slotBus(port, number))) {
216 PciDrivers::cancelRemove(device);
217 }
218}
219
220bool removeSlot(Device* port) {
221 uint8_t number = 0;
222 Device* bus = slotBus(port, number);
223 Device* device = leaf(bus);
224 if (!device || !PciDrivers::remove(device)) {
225 return false;
226 }
227 auto& pci = PciBus::instance();
228 if (!pci.updateCommand(device, 7U, 0) || !PciResources::removeFunction(device)) {
229 panic("PCI hotplug: cannot retire endpoint resources");
230 }
231 {
233 bus->removeChild(device);
234 device->setParent(nullptr);
235 }
236 delete device;
237 NOTICE("PCI hotplug: removed " << Dec << unsigned(number) << ":0.0");
238 return true;
239}
240} // namespace PciEnumeration
size_t getNumChildren()
Definition Device.cc:143
Device * getChild(size_t n)
Definition Device.cc:139
uint8_t getPciClassCode()
Definition Device.h:216
void addChild(Device *pDevice)
Definition Device.cc:135
virtual const String & getSpecificType()
Definition Device.h:183
uint8_t getPciSubclassCode()
Definition Device.h:220
uint32_t getPciBusPosition()
Definition Device.h:236
void removeChild(size_t n)
Definition Device.cc:147
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118
@ Dec
Definition Log.h:126