2#ifndef PEDIGREE_PCI_SRIOV_H
3#define PEDIGREE_PCI_SRIOV_H
5#include "pedigree/kernel/processor/types.h"
8constexpr uint16_t CapabilityId = 0x0010;
9constexpr uint16_t VfEnable = 1U;
10constexpr uint16_t VfMemoryEnable = 8U;
11constexpr uint32_t PageSize4K = 1U;
12constexpr uint16_t VfControl = VfEnable | VfMemoryEnable;
14enum class Result { Success, Invalid, IoError, RestoreFailed, RollbackFailed };
18 uint16_t initialVfs = 0;
19 uint16_t totalVfs = 0;
21 uint16_t firstVfOffset = 0;
22 uint16_t vfStride = 0;
23 uint16_t vfDeviceId = 0;
24 uint32_t supportedPageSizes = 0;
25 uint32_t systemPageSize = 0;
26 uint32_t bars[6] = {};
28 bool enabled()
const {
33inline bool vfRoutingId(uint8_t pfBus, uint8_t pfDevice, uint8_t pfFunction,
const State& state,
34 uint16_t index, uint8_t& bus, uint8_t& device, uint8_t& function);
38 uint64_t perVfBytes = 0;
39 uint64_t apertureBytes = 0;
42 bool prefetchable =
false;
52 uint8_t pfFunction = 0;
54 uint16_t firstVfOffset = 0;
55 uint16_t vfStride = 0;
56 uint16_t firstRid = 0;
58 uint32_t originalPageSize = 0;
61inline bool validOffset(uint16_t offset, uint16_t next) {
62 constexpr uint16_t RequiredBytes = 0x3c;
63 return offset >= 0x100 && !(offset & 3U) && offset <= 0x1000 - RequiredBytes &&
64 !(next && next >= offset && next < offset + RequiredBytes);
67template <
class Config>
68bool read(Config& config, uint16_t offset, uint16_t next, State& result) {
69 if (!validOffset(offset, next)) {
73 if (!config.read16(offset + 0x08, state.control) ||
74 !config.read16(offset + 0x0c, state.initialVfs) ||
75 !config.read16(offset + 0x0e, state.totalVfs) ||
76 !config.read16(offset + 0x10, state.numVfs) ||
77 !config.read16(offset + 0x14, state.firstVfOffset) ||
78 !config.read16(offset + 0x16, state.vfStride) ||
79 !config.read16(offset + 0x1a, state.vfDeviceId) ||
80 !config.read32(offset + 0x1c, state.supportedPageSizes) ||
81 !config.read32(offset + 0x20, state.systemPageSize)) {
84 for (
unsigned i = 0; i < 6; ++i) {
85 if (!config.read32(offset + 0x24 + 4 * i, state.bars[i])) {
89 if (!state.totalVfs || state.initialVfs > state.totalVfs || state.numVfs > state.totalVfs ||
90 !state.supportedPageSizes ||
91 (state.systemPageSize && (state.systemPageSize & (state.systemPageSize - 1) ||
92 !(state.systemPageSize & state.supportedPageSizes))) ||
93 (state.enabled() && (!state.numVfs || !state.systemPageSize))) {
101inline bool plan(uint8_t pfBus, uint8_t pfDevice, uint8_t pfFunction,
const State& state,
102 uint16_t count, Plan& result) {
103 if (pfDevice >= 32 || pfFunction >= 8 || (state.control & VfControl) || state.numVfs || !count ||
104 count > state.initialVfs || count > state.totalVfs ||
105 !(state.supportedPageSizes & PageSize4K)) {
108 State requested = state;
109 requested.numVfs = count;
110 uint8_t firstBus = 0, firstDevice = 0, firstFunction = 0;
111 uint8_t lastBus = 0, lastDevice = 0, lastFunction = 0;
112 if (!vfRoutingId(pfBus, pfDevice, pfFunction, requested, 0, firstBus, firstDevice,
114 !vfRoutingId(pfBus, pfDevice, pfFunction, requested, count - 1, lastBus, lastDevice,
124 uint16_t((uint16_t(firstBus) << 8) | (firstDevice << 3) | firstFunction),
125 uint16_t((uint16_t(lastBus) << 8) | (lastDevice << 3) | lastFunction),
126 state.systemPageSize};
130template <
class Config>
131bool writeVerified16(Config& config, uint16_t offset, uint16_t value) {
133 return config.write16(offset, value) && config.read16(offset, actual) && actual == value;
136template <
class Config>
137bool writeVerified32(Config& config, uint16_t offset, uint32_t value) {
139 return config.write32(offset, value) && config.read32(offset, actual) && actual == value;
143template <
class Config>
144Result probeBars(Config& config, uint16_t offset, uint16_t next,
const State& state,
146 if (!validOffset(offset, next) || !state.totalVfs || (state.control & VfControl)) {
147 return Result::Invalid;
149 uint16_t control = 0;
150 uint32_t original[6] = {}, masks[6] = {};
151 if (!config.read16(offset + 0x08, control)) {
152 return Result::IoError;
154 if (control != state.control || (control & VfControl)) {
155 return Result::Invalid;
157 for (
unsigned i = 0; i < 6; ++i) {
158 if (!config.read32(offset + 0x24 + 4 * i, original[i]) || original[i] != state.bars[i]) {
159 return Result::Invalid;
164 for (
unsigned i = 0; i < 6; ++i) {
165 if (!config.write32(offset + 0x24 + 4 * i, 0xffffffffU)) {
171 for (
unsigned i = 0; i < 6; ++i) {
172 if (!config.read32(offset + 0x24 + 4 * i, masks[i])) {
178 bool restored =
true;
179 for (
unsigned i = 0; i < 6; ++i) {
180 if (!config.write32(offset + 0x24 + 4 * i, original[i])) {
184 for (
unsigned i = 0; i < 6; ++i) {
186 if (!config.read32(offset + 0x24 + 4 * i, actual) || actual != original[i]) {
191 return writeVerified16(config, offset + 0x08, control & ~VfControl) ? Result::RestoreFailed
192 : Result::RollbackFailed;
195 return Result::IoError;
199 for (
unsigned i = 0; i < 6; ++i) {
200 const uint32_t low = original[i];
201 const uint32_t maskLow = masks[i];
202 if (!low && !maskLow) {
205 const bool io = low & 1U;
206 const unsigned type = (low >> 1) & 3U;
207 if ((io && ((low ^ maskLow) & 3U)) ||
208 (!io && (((low ^ maskLow) & 15U) || type == 1 || type == 3))) {
209 return Result::Invalid;
211 const bool wide = !io && type == 2;
212 if (wide && i == 5) {
213 return Result::Invalid;
215 const uint64_t base = wide ? ((uint64_t(original[i + 1]) << 32) | (low & ~15U))
216 : uint64_t(low & (io ? ~3U : ~15U));
217 const uint64_t mask = wide ? ((uint64_t(masks[i + 1]) << 32) | (maskLow & ~15U))
218 : uint64_t(maskLow & (io ? ~3U : ~15U));
219 const uint64_t bytes = wide ? ~mask + 1 : uint64_t(~uint32_t(mask)) + 1;
220 if (!bytes || (bytes & (bytes - 1)) || bytes > ~uint64_t{0} / state.totalVfs ||
221 (base && (base & (bytes - 1)))) {
222 return Result::Invalid;
224 const uint64_t aperture = bytes * state.totalVfs;
225 const uint64_t limit = io ? 0xffffU : wide ? ~uint64_t{0} : 0xffffffffU;
226 if (base > limit || aperture - 1 > limit - base) {
227 return Result::Invalid;
229 geometry.bars[i] = Bar{base, bytes, aperture, io, wide, !io && bool(low & 8U)};
235 return Result::Success;
239template <
class Config>
240Result enable(Config& config, uint16_t offset, uint16_t next,
const Plan& request) {
242 if (!read(config, offset, next, state)) {
243 return Result::IoError;
246 if (!plan(request.pfBus, request.pfDevice, request.pfFunction, state, request.numVfs, expected) ||
247 expected.firstRid != request.firstRid || expected.lastRid != request.lastRid ||
248 expected.firstVfOffset != request.firstVfOffset || expected.vfStride != request.vfStride ||
249 expected.originalPageSize != request.originalPageSize) {
250 return Result::Invalid;
253 if (writeVerified32(config, offset + 0x20, PageSize4K) &&
254 writeVerified16(config, offset + 0x10, request.numVfs)) {
256 if (read(config, offset, next, staged) && staged.firstVfOffset == request.firstVfOffset &&
257 staged.vfStride == request.vfStride && !(staged.control & VfControl) &&
258 writeVerified16(config, offset + 0x08, state.control | VfControl)) {
260 if (read(config, offset, next, enabled) && enabled.enabled() &&
261 (enabled.control & VfMemoryEnable) && enabled.numVfs == request.numVfs &&
262 enabled.systemPageSize == PageSize4K) {
263 return Result::Success;
267 const bool controlRestored = writeVerified16(config, offset + 0x08, state.control & ~VfControl);
268 const bool countRestored = writeVerified16(config, offset + 0x10, 0);
269 const bool pageRestored = writeVerified32(config, offset + 0x20, state.systemPageSize);
270 return controlRestored && countRestored && pageRestored ? Result::IoError
271 : Result::RollbackFailed;
275template <
class Config>
276Result disable(Config& config, uint16_t offset, uint16_t next) {
278 if (!read(config, offset, next, state)) {
279 return Result::RollbackFailed;
281 if (!writeVerified16(config, offset + 0x08, state.control & ~VfControl)) {
282 return Result::RollbackFailed;
284 return writeVerified16(config, offset + 0x10, 0) ? Result::Success : Result::IoError;
287inline bool vfRoutingId(uint8_t pfBus, uint8_t pfDevice, uint8_t pfFunction,
const State& state,
288 uint16_t index, uint8_t& bus, uint8_t& device, uint8_t& function) {
289 if (pfDevice >= 32 || pfFunction >= 8 || index >= state.numVfs || !state.firstVfOffset ||
290 (state.numVfs > 1 && !state.vfStride)) {
293 const uint32_t rid = (uint32_t(pfBus) << 8) + (uint32_t(pfDevice) << 3) + pfFunction +
294 state.firstVfOffset + uint32_t(index) * state.vfStride;
299 device = (rid >> 3) & 31U;