The Pedigree Project 0.1
PciSriov.h
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#ifndef PEDIGREE_PCI_SRIOV_H
3#define PEDIGREE_PCI_SRIOV_H
4
5#include "pedigree/kernel/processor/types.h"
6
7namespace PciSriov {
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;
13
14enum class Result { Success, Invalid, IoError, RestoreFailed, RollbackFailed };
15
16struct State {
17 uint16_t control = 0;
18 uint16_t initialVfs = 0;
19 uint16_t totalVfs = 0;
20 uint16_t numVfs = 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] = {};
27
28 bool enabled() const {
29 return control & 1U;
30 }
31};
32
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);
35
36struct Bar {
37 uint64_t base = 0;
38 uint64_t perVfBytes = 0;
39 uint64_t apertureBytes = 0;
40 bool io = false;
41 bool wide = false;
42 bool prefetchable = false;
43};
44
45struct Geometry {
46 Bar bars[6] = {};
47};
48
49struct Plan {
50 uint8_t pfBus = 0;
51 uint8_t pfDevice = 0;
52 uint8_t pfFunction = 0;
53 uint16_t numVfs = 0;
54 uint16_t firstVfOffset = 0;
55 uint16_t vfStride = 0;
56 uint16_t firstRid = 0;
57 uint16_t lastRid = 0;
58 uint32_t originalPageSize = 0;
59};
60
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);
65}
66
67template <class Config>
68bool read(Config& config, uint16_t offset, uint16_t next, State& result) {
69 if (!validOffset(offset, next)) {
70 return false;
71 }
72 State state;
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)) {
82 return false;
83 }
84 for (unsigned i = 0; i < 6; ++i) {
85 if (!config.read32(offset + 0x24 + 4 * i, state.bars[i])) {
86 return false;
87 }
88 }
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))) {
94 return false;
95 }
96 result = state;
97 return true;
98}
99
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)) {
106 return false;
107 }
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,
113 firstFunction) ||
114 !vfRoutingId(pfBus, pfDevice, pfFunction, requested, count - 1, lastBus, lastDevice,
115 lastFunction)) {
116 return false;
117 }
118 result = Plan{pfBus,
119 pfDevice,
120 pfFunction,
121 count,
122 state.firstVfOffset,
123 state.vfStride,
124 uint16_t((uint16_t(firstBus) << 8) | (firstDevice << 3) | firstFunction),
125 uint16_t((uint16_t(lastBus) << 8) | (lastDevice << 3) | lastFunction),
126 state.systemPageSize};
127 return true;
128}
129
130template <class Config>
131bool writeVerified16(Config& config, uint16_t offset, uint16_t value) {
132 uint16_t actual = 0;
133 return config.write16(offset, value) && config.read16(offset, actual) && actual == value;
134}
135
136template <class Config>
137bool writeVerified32(Config& config, uint16_t offset, uint32_t value) {
138 uint32_t actual = 0;
139 return config.write32(offset, value) && config.read32(offset, actual) && actual == value;
140}
141
143template <class Config>
144Result probeBars(Config& config, uint16_t offset, uint16_t next, const State& state,
145 Geometry& result) {
146 if (!validOffset(offset, next) || !state.totalVfs || (state.control & VfControl)) {
147 return Result::Invalid;
148 }
149 uint16_t control = 0;
150 uint32_t original[6] = {}, masks[6] = {};
151 if (!config.read16(offset + 0x08, control)) {
152 return Result::IoError;
153 }
154 if (control != state.control || (control & VfControl)) {
155 return Result::Invalid;
156 }
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;
160 }
161 }
162
163 bool probed = true;
164 for (unsigned i = 0; i < 6; ++i) {
165 if (!config.write32(offset + 0x24 + 4 * i, 0xffffffffU)) {
166 probed = false;
167 break;
168 }
169 }
170 if (probed) {
171 for (unsigned i = 0; i < 6; ++i) {
172 if (!config.read32(offset + 0x24 + 4 * i, masks[i])) {
173 probed = false;
174 break;
175 }
176 }
177 }
178 bool restored = true;
179 for (unsigned i = 0; i < 6; ++i) {
180 if (!config.write32(offset + 0x24 + 4 * i, original[i])) {
181 restored = false;
182 }
183 }
184 for (unsigned i = 0; i < 6; ++i) {
185 uint32_t actual = 0;
186 if (!config.read32(offset + 0x24 + 4 * i, actual) || actual != original[i]) {
187 restored = false;
188 }
189 }
190 if (!restored) {
191 return writeVerified16(config, offset + 0x08, control & ~VfControl) ? Result::RestoreFailed
192 : Result::RollbackFailed;
193 }
194 if (!probed) {
195 return Result::IoError;
196 }
197
198 Geometry geometry;
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) {
203 continue;
204 }
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;
210 }
211 const bool wide = !io && type == 2;
212 if (wide && i == 5) {
213 return Result::Invalid;
214 }
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;
223 }
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;
228 }
229 geometry.bars[i] = Bar{base, bytes, aperture, io, wide, !io && bool(low & 8U)};
230 if (wide) {
231 ++i;
232 }
233 }
234 result = geometry;
235 return Result::Success;
236}
237
239template <class Config>
240Result enable(Config& config, uint16_t offset, uint16_t next, const Plan& request) {
241 State state;
242 if (!read(config, offset, next, state)) {
243 return Result::IoError;
244 }
245 Plan expected;
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;
251 }
252
253 if (writeVerified32(config, offset + 0x20, PageSize4K) &&
254 writeVerified16(config, offset + 0x10, request.numVfs)) {
255 State staged;
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)) {
259 State enabled;
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;
264 }
265 }
266 }
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;
272}
273
275template <class Config>
276Result disable(Config& config, uint16_t offset, uint16_t next) {
277 State state;
278 if (!read(config, offset, next, state)) {
279 return Result::RollbackFailed;
280 }
281 if (!writeVerified16(config, offset + 0x08, state.control & ~VfControl)) {
282 return Result::RollbackFailed;
283 }
284 return writeVerified16(config, offset + 0x10, 0) ? Result::Success : Result::IoError;
285}
286
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)) {
291 return false;
292 }
293 const uint32_t rid = (uint32_t(pfBus) << 8) + (uint32_t(pfDevice) << 3) + pfFunction +
294 state.firstVfOffset + uint32_t(index) * state.vfStride;
295 if (rid > 0xffffU) {
296 return false;
297 }
298 bus = rid >> 8;
299 device = (rid >> 3) & 31U;
300 function = rid & 7U;
301 return true;
302}
303} // namespace PciSriov
304
305#endif