2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/Log.h"
4#include "pedigree/kernel/machine/Device.h"
5#include "pedigree/kernel/machine/Pci.h"
6#include "pedigree/kernel/machine/PciFirmware.h"
7#include "pedigree/kernel/machine/PciVirtualFunctions.h"
8#include "pedigree/kernel/panic.h"
9#include "pedigree/kernel/process/Mutex.h"
10#include "pedigree/kernel/time/Time.h"
11#include "pedigree/kernel/utilities/new"
20 bool read16(uint16_t offset, uint16_t& value) {
21 return PciBus::instance().readConfig16(device, offset, value);
23 bool read32(uint16_t offset, uint32_t& value) {
24 return PciBus::instance().readConfig32(device, offset, value);
26 bool write16(uint16_t offset, uint16_t value) {
27 return PciBus::instance().writeConfig16(device, offset, value);
29 bool write32(uint16_t offset, uint32_t value) {
30 return PciBus::instance().writeConfig32(device, offset, value);
35 const Time::Timestamp start = Time::getTicks();
36 constexpr Time::Timestamp delay = 100 * Time::Multiplier::Millisecond;
38 const Time::Timestamp elapsed = Time::getTicks() - start;
39 if (elapsed >= delay) {
42 (void)Time::delay(delay - elapsed);
47 auto* bytes =
reinterpret_cast<uint8_t*
>(&header);
48 for (
size_t offset = 0; offset <
sizeof(header); offset += 4) {
50 if (!PciBus::instance().readConfig32(device, offset, value)) {
53 for (
unsigned byte = 0;
byte < 4; ++byte) {
54 bytes[offset + byte] = value >> (
byte * 8);
60bool busReachable(
Device* pf, uint8_t bus) {
61 auto& pci = PciBus::instance();
62 uint8_t first = 0, last = 0;
64 if (!pci.busRange(first, last) || bus < first || bus > last || !root ||
65 PciFirmware::rootForBus(bus) != root) {
68 for (
Device* parent = pf->
getParent(); parent; parent = parent->getParent()) {
69 if (parent->getPciClassCode() != 6 || parent->getPciSubclassCode() != 4 ||
70 (parent->getPciConfigHeader().header_type & 0x7f) != 1) {
74 if (!pci.readConfig32(parent, 0x18, buses) || uint8_t(buses) != parent->getPciBusPosition() ||
75 bus < uint8_t(buses >> 8) || bus > uint8_t(buses >> 16)) {
83PciVirtualFunctions::PciVirtualFunctions(
Device* pf) : m_Pf(pf) {}
85PciVirtualFunctions::~PciVirtualFunctions() {
87 panic(
"SR-IOV: VF group destroyed with active users");
91bool PciVirtualFunctions::enable(
size_t requested) {
93 auto& pci = PciBus::instance();
94 if (!m_Pf || m_Count || m_PlannedCount || !requested || requested > MaxFunctions ||
101 uint16_t command = 0;
105 if (!pci.readConfig16(m_Pf, 4, command) || (command & 4U) ||
106 pci.findExtendedCapability(m_Pf, PciSriov::CapabilityId, capability) !=
107 PciExtendedCapabilities::FindResult::Found ||
108 !PciSriov::read(config, capability.offset, capability.next, state) ||
114 m_Offset = capability.offset;
115 m_NextCapability = capability.next;
116 state.numVfs = requested;
117 for (
size_t i = 0; i < requested; ++i) {
118 uint8_t bus = 0, device = 0, function = 0;
119 if (!PciSriov::vfRoutingId(plan.pfBus, plan.pfDevice, plan.pfFunction, state, i, bus, device,
121 !busReachable(m_Pf, bus)) {
130 m_Functions[m_PlannedCount++] = vf;
137 if (!pci.readConfig32(vf, 0,
identity) || !pci.readConfig32(vf, 0xc, header) ||
138 identity != 0xffffffffU || header != 0xffffffffU) {
147 if (!PciSriov::writeVerified32(config, m_Offset + 0x20, PciSriov::PageSize4K) ||
148 !PciSriov::read(config, m_Offset, m_NextCapability, state) ||
149 !PciSriov::plan(plan.pfBus, plan.pfDevice, plan.pfFunction, state, requested, selected) ||
150 selected.firstRid != plan.firstRid || selected.lastRid != plan.lastRid ||
151 selected.firstVfOffset != plan.firstVfOffset || selected.vfStride != plan.vfStride ||
152 state.totalVfs != m_Original.totalVfs || state.vfDeviceId != m_Original.vfDeviceId ||
153 PciSriov::probeBars(config, m_Offset, m_NextCapability, state, m_Bars) !=
154 PciSriov::Result::Success) {
159 for (
size_t i = 0; i < 6; ++i) {
160 auto& bar = m_Bars.bars[i];
161 if (!bar.perVfBytes) {
168 uint64_t base = bar.base;
169 if (!(base && PciResources::reserve(m_Pf, base, bar.apertureBytes, bar.prefetchable)) &&
170 !PciResources::allocate(m_Pf, bar.apertureBytes, bar.perVfBytes, bar.prefetchable,
171 bar.wide ? ~uint64_t{0} : 0xffffffffU, base)) {
172 WARNING(
"SR-IOV: no firmware aperture for VF BAR " <<
Dec << i);
176 m_Reserved[i] =
true;
178 const uint32_t low = uint32_t(base) | (m_Original.bars[i] & 15U);
179 if (!PciSriov::writeVerified32(config, m_Offset + 0x24 + 4 * i, low) ||
181 !PciSriov::writeVerified32(config, m_Offset + 0x28 + 4 * i, uint32_t(base >> 32)))) {
186 m_EnableAttempted =
true;
187 if (PciSriov::enable(config, m_Offset, m_NextCapability, plan) != PciSriov::Result::Success) {
193 for (
size_t i = 0; i < requested; ++i) {
194 Device* vf = m_Functions[i];
196 if (!readHeader(vf, header) || (header.header_type & 0x7f) ||
197 !((header.vendor == 0xffff && header.device == 0xffff) ||
198 (header.vendor == m_Pf->
getPciVendorId() && header.device == m_Original.vfDeviceId))) {
199 WARNING(
"SR-IOV: VF " <<
Dec << i <<
" configuration unavailable after enable");
204 uint16_t vfCommand = 0;
205 if (!pci.updateCommand(vf, 4U, 0) || !pci.readConfig16(vf, 4, vfCommand) || (vfCommand & 4U)) {
211 header.device = m_Original.vfDeviceId;
212 header.command = vfCommand;
213 vf->
setPciIdentifiers(header.class_code, header.subclass, header.vendor, header.device,
217 for (
size_t bar = 0; bar < 6; ++bar) {
218 const auto& aperture = m_Bars.bars[bar];
219 if (!aperture.perVfBytes) {
222 const uint64_t base = aperture.base + i * aperture.perVfBytes;
224 if (aperture.perVfBytes > ~
size_t{0} ||
225 !pci.translateAddress(base, aperture.perVfBytes,
false, cpu) || cpu > ~uintptr_t{0}) {
229 char name[] = {
'b',
'a',
'r',
static_cast<char>(
'0' + bar), 0};
231 static_cast<size_t>(aperture.perVfBytes),
false);
239 if (!pci.attachIsolatedDma(vf) || !pci.hasDmaIsolation(vf)) {
251Device* PciVirtualFunctions::function(
size_t index)
const {
253 return index < m_Count ? m_Functions[index] :
nullptr;
256size_t PciVirtualFunctions::count()
const {
261bool PciVirtualFunctions::disable() {
263 auto& pci = PciBus::instance();
264 for (
size_t i = 0; i < m_Count; ++i) {
265 Device* vf = m_Functions[i];
266 uint16_t command = 0;
267 if (vf->
getNumChildren() || !pci.readConfig16(vf, 4, command) || (command & 4U) ||
268 (pci.hasDmaIsolation(vf) && !pci.isolatedDmaIdle(vf)) ||
269 (pci.hasDmaRemapping(vf) && !pci.hasDmaIsolation(vf))) {
277void PciVirtualFunctions::reclaim() {
278 auto& pci = PciBus::instance();
281 bool quiescent =
true;
282 for (
size_t i = 0; i < m_PlannedCount; ++i) {
283 if (m_Present[i] && !pci.updateCommand(m_Functions[i], 4U, 0)) {
287 uint16_t control = 0;
288 if (!config.read16(m_Offset + 8, control) ||
289 !PciSriov::writeVerified16(config, m_Offset + 8, control & ~PciSriov::VfControl) ||
290 !PciSriov::writeVerified16(config, m_Offset + 0x10, 0)) {
291 panic(
"SR-IOV: cannot disable VF decoding");
293 if (m_EnableAttempted) {
297 panic(
"SR-IOV: cannot verify VF DMA quiescence");
300 for (
size_t i = 0; i < m_PlannedCount; ++i) {
301 Device* vf = m_Functions[i];
302 if (vf->
getNumChildren() || (pci.hasDmaIsolation(vf) && !pci.detachDisabledIsolatedDma(vf)) ||
303 pci.hasDmaRemapping(vf)) {
304 panic(
"SR-IOV: VF still owns DMA resources");
307 m_Functions[i] =
nullptr;
308 m_Present[i] =
false;
311 for (
size_t i = 0; i < 6; ++i) {
312 if (!PciSriov::writeVerified32(config, m_Offset + 0x24 + 4 * i, m_Original.bars[i])) {
313 panic(
"SR-IOV: cannot restore VF BARs");
316 if (!PciSriov::writeVerified32(config, m_Offset + 0x20, m_Original.systemPageSize) ||
317 !PciSriov::writeVerified16(config, m_Offset + 0x10, m_Original.numVfs) ||
318 !PciSriov::writeVerified16(config, m_Offset + 8, m_Original.control)) {
319 panic(
"SR-IOV: cannot restore PF state");
322 for (
size_t i = 0; i < 6; ++i) {
324 !PciResources::release(m_Pf, m_Bars.bars[i].base, m_Bars.bars[i].apertureBytes)) {
325 panic(
"SR-IOV: lost VF BAR reservation");
327 m_Reserved[i] =
false;
332 m_EnableAttempted =
false;
334 m_NextCapability = 0;
339bool PciVirtualFunctions::bar(
Device* vf, uint8_t index, uint64_t& pciBase, uint64_t& bytes) {
341 auto& pci = PciBus::instance();
342 Device* pf = vf ? vf->getPhysicalFunction() :
nullptr;
346 if (!pf || index >= 6 ||
347 pci.findExtendedCapability(pf, PciSriov::CapabilityId, capability) !=
348 PciExtendedCapabilities::FindResult::Found ||
349 !PciSriov::read(config, capability.offset, capability.next, state) ||
350 (state.control & PciSriov::VfControl) != PciSriov::VfControl ||
351 state.systemPageSize != PciSriov::PageSize4K ||
352 vf->getVirtualFunctionIndex() >= state.numVfs) {
355 uint8_t bus = 0, device = 0, function = 0;
363 for (
unsigned i = 0; i < index; ++i) {
364 if (!(state.bars[i] & 1U) && (state.bars[i] & 6U) == 4) {
370 const uint32_t low = state.bars[index];
371 const unsigned type = (low >> 1) & 3U;
372 const bool wide = type == 2;
373 if ((low & 1U) || (type != 0 && !wide) || (wide && index == 5)) {
377 if (!pci.readConfig32(vf, 0x10 + 4 * index, raw) || raw ||
378 (wide && (!pci.readConfig32(vf, 0x14 + 4 * index, raw) || raw))) {
381 char name[] = {
'b',
'a',
'r',
static_cast<char>(
'0' + index), 0};
384 if (candidate && candidate->m_Name == name && !candidate->m_IsIoSpace) {
391 const uint64_t size = address ? address->m_Size : 0;
392 const uint64_t base = (uint64_t(wide ? state.bars[index + 1] : 0) << 32) | (low & ~15U);
393 const uint64_t maximum = wide ? ~uint64_t{0} : 0xffffffffU;
394 if (!base || !size || (size & (size - 1)) || (base & (size - 1)) ||
395 size > ~uint64_t{0} / state.totalVfs || size * state.totalVfs - 1 > maximum - base) {
398 const uint64_t slice = base + vf->getVirtualFunctionIndex() * size;
400 if (!pci.translateAddress(slice, size,
false, cpu) || cpu != address->m_Address) {
uint16_t getPciVendorId()
uint32_t getPciFunctionNumber()
uint32_t getPciDevicePosition()
void setPciConfigHeader(const PciBus::ConfigSpace &space)
virtual Vector< Address * > & addresses()
void setPciIdentifiers(uint8_t classCode, uint8_t subclassCode, uint16_t vendorId, uint16_t deviceId, uint8_t progIf)
virtual void setSpecificType(String str)
virtual const String & getSpecificType()
void setVirtualFunction(Device *physicalFunction, size_t index)
Device * getParent() const
uint32_t getPciBusPosition()
void setParent(Device *p)
void setPciPosition(uint32_t bus, uint32_t device, uint32_t func)
void EXPORTED_PUBLIC panic(const char *msg) NORETURN