2#ifndef PEDIGREE_PCI_FUNCTION_STATE_H
3#define PEDIGREE_PCI_FUNCTION_STATE_H
4#include "pedigree/kernel/compiler.h"
5#include "pedigree/kernel/processor/types.h"
7namespace PciFunctionState {
10 uint32_t bars[6] = {};
12 uint8_t interruptLine = 0, interruptPin = 0;
13 uint8_t pm = 0, msi = 0, msix = 0;
17 uint8_t bar = 0, pbaBar = 0;
18 uint32_t offset = 0, pbaOffset = 0;
22template <
class Config>
23bool writeVerified16(Config& config, uint16_t offset, uint16_t value) {
25 return config.write16(offset, value) && config.read16(offset, actual) && actual == value;
28template <
class Config>
29bool writeVerified32(Config& config, uint16_t offset, uint32_t value) {
31 return config.write32(offset, value) && config.read32(offset, actual) && actual == value;
36template <
class Config>
37bool inspect(Config& config, State& result,
bool requireLegacyInterrupt =
true,
38 bool virtualFunction =
false) {
45 !(virtualFunction &&
identity == 0xffffffffU)) ||
46 !config.read8(14, header) || (header & 0x7f) > (virtualFunction ? 0 : 1) ||
47 !config.read16(4, state.command) || !config.read16(6, status) ||
48 !config.read8(0x3c, state.interruptLine) || !config.read8(0x3d, state.interruptPin)) {
51 state.barCount = (header & 0x7f) == 1 ? 2 : 6;
52 if (state.interruptPin > 4 || (requireLegacyInterrupt && !state.interruptPin)) {
57 if (requireLegacyInterrupt &&
58 (state.interruptLine >= 16 || !(0xdef8U & (1U << state.interruptLine)))) {
61 for (
unsigned i = 0; i < state.barCount; ++i) {
62 if (!config.read32(0x10 + 4 * i, state.bars[i])) {
67 if ((status & 0x10) && !config.read8(0x34, cap))
69 uint64_t occupied = 0;
71 if (cap < 0x40 || (cap & 3))
73 uint8_t type = 0, next = 0;
75 if (!config.read8(cap, type) || !config.read8(cap + 1, next) ||
76 !config.read16(cap + 2, control))
84 }
else if (type == 5) {
88 bytes = (control & 0x80) ? 14 : 10;
91 }
else if (type == 0x11) {
97 if (cap + bytes > 256)
99 for (
unsigned slot = cap / 4; slot < (cap + bytes + 3) / 4; ++slot) {
100 if (occupied & (1ULL << slot))
102 occupied |= 1ULL << slot;
106 if (!config.read16(cap + 4, pmcsr) || (pmcsr & 3))
111 if (!requireLegacyInterrupt && !state.interruptPin && !state.msi && !state.msix)
117template <
class Config>
118bool msixTable(Config& config,
const State& state, MsixTable& result) {
119 if (!state.msix || (state.barCount != 2 && state.barCount != 6)) {
122 uint16_t control = 0;
123 uint32_t table = 0, pba = 0;
124 if (!config.read16(state.msix + 2, control) || !config.read32(state.msix + 4, table) ||
125 !config.read32(state.msix + 8, pba) || (table & 7U) >= state.barCount ||
126 (pba & 7U) >= state.barCount) {
129 const uint16_t vectors = (control & 0x7ffU) + 1;
130 const uint8_t bar = table & 7U, pbaBar = pba & 7U;
131 const uint64_t tableEnd = uint64_t(table & ~7U) + uint64_t(vectors) * 16;
132 const uint64_t pbaEnd = uint64_t(pba & ~7U) + uint64_t((vectors + 63) / 64) * 8;
133 if (tableEnd > 0x100000000ULL || pbaEnd > 0x100000000ULL ||
134 (bar == pbaBar && (table & ~7U) < pbaEnd && (pba & ~7U) < tableEnd))
136 result = {bar, pbaBar, table & ~7U, pba & ~7U, vectors};
140template <
class Config>
141bool enableMsi(Config& config,
const State& state, uint64_t address, uint16_t data) {
142 if (!state.msi || (address & 3U))
144 uint16_t control = 0, command = 0, oldData = 0, msixControl = 0;
145 uint32_t oldLow = 0, oldHigh = 0, mask = 0;
146 if (!config.read16(state.msi + 2, control) || !config.read16(4, command) ||
147 !config.read32(state.msi + 4, oldLow) || (control & 1U) ||
148 (state.msix && (!config.read16(state.msix + 2, msixControl) || (msixControl & 0x8000U))))
150 const bool wide = control & 0x80U;
151 if (!wide && (address >> 32))
153 const uint16_t dataOffset = state.msi + (wide ? 12 : 8);
154 const uint16_t maskOffset = state.msi + (wide ? 16 : 12);
155 if ((wide && !config.read32(state.msi + 8, oldHigh)) || !config.read16(dataOffset, oldData) ||
156 ((control & 0x100U) && !config.read32(maskOffset, mask)))
159 const bool programmed =
160 writeVerified32(config, state.msi + 4, uint32_t(address)) &&
161 (!wide || writeVerified32(config, state.msi + 8, uint32_t(address >> 32))) &&
162 writeVerified16(config, dataOffset, data) &&
163 (!(control & 0x100U) || writeVerified32(config, maskOffset, mask & ~1U)) &&
164 writeVerified16(config, 4, command | 0x400U) &&
165 writeVerified16(config, state.msi + 2, (control & ~0x70U) | 1U);
170 config.write16(state.msi + 2, control & ~1U);
171 config.write32(state.msi + 4, oldLow);
173 config.write32(state.msi + 8, oldHigh);
174 config.write16(dataOffset, oldData);
175 if (control & 0x100U)
176 config.write32(maskOffset, mask);
177 config.write16(4, command);
181template <
class Config>
182bool disableMsi(Config& config,
const State& state) {
185 uint16_t control = 0;
186 if (!config.read16(state.msi + 2, control))
191 if (control & 0x100U) {
193 const uint16_t offset = state.msi + ((control & 0x80U) ? 16 : 12);
194 masked = config.read32(offset, mask) && writeVerified32(config, offset, mask | 1U);
196 const bool disabled = writeVerified16(config, state.msi + 2, control & ~1U);
197 return masked && disabled;
200template <
class Config,
class Io>
201bool enableMsixVectors(Config& config,
const State& state, Io& table, uint32_t offset,
202 uint64_t address,
const uint32_t* data,
size_t count,
203 const uint64_t* addresses =
nullptr) {
204 constexpr size_t MaxVectors = 64;
205 if (!state.msix || !data || !count || count > MaxVectors ||
206 uint64_t(offset) + count * 16 > table.size())
208 for (
size_t i = 0; i < count; ++i) {
209 if ((addresses ? addresses[i] : address) & 3U) {
213 uint16_t control = 0, command = 0, msiControl = 0;
214 if (!config.read16(state.msix + 2, control) || !config.read16(4, command) ||
215 (control & 0x8000U) || count > (control & 0x7ffU) + 1 ||
216 (state.msi && (!config.read16(state.msi + 2, msiControl) || (msiControl & 1U))))
218 uint32_t previous[MaxVectors][4];
219 for (
size_t i = 0; i < count; ++i) {
220 const uint32_t entry = offset + i * 16;
221 for (
size_t word = 0; word < 4; ++word)
222 previous[i][word] = table.read32(entry + word * 4);
224 if (!writeVerified16(config, state.msix + 2, control | 0x4000U)) {
225 config.write16(state.msix + 2, control);
228 bool programmed =
true;
229 for (
size_t i = 0; i < count; ++i) {
230 const uint32_t entry = offset + i * 16;
231 table.write32(previous[i][3] | 1U, entry + 12);
232 const uint64_t destination = addresses ? addresses[i] : address;
233 table.write32(uint32_t(destination), entry);
234 table.write32(uint32_t(destination >> 32), entry + 4);
235 table.write32(data[i], entry + 8);
238 for (
size_t i = 0; i < count; ++i) {
239 const uint32_t entry = offset + i * 16;
240 const uint64_t destination = addresses ? addresses[i] : address;
241 programmed = programmed && (table.read32(entry + 12) & 1U) &&
242 table.read32(entry) == uint32_t(destination) &&
243 table.read32(entry + 4) == uint32_t(destination >> 32) &&
244 table.read32(entry + 8) == data[i];
247 programmed = writeVerified16(config, 4, command | 0x400U) &&
248 writeVerified16(config, state.msix + 2, control | 0xc000U);
250 for (
size_t i = 0; i < count; ++i)
251 table.write32(previous[i][3] & ~1U, offset + i * 16 + 12);
253 for (
size_t i = 0; i < count; ++i)
254 programmed = programmed && !(table.read32(offset + i * 16 + 12) & 1U);
255 if (programmed && writeVerified16(config, state.msix + 2, (control | 0x8000U) & ~0x4000U))
260 if (!writeVerified16(config, state.msix + 2, (control | 0x4000U) & ~0x8000U))
262 for (
size_t i = 0; i < count; ++i) {
263 const uint32_t entry = offset + i * 16;
264 table.write32(previous[i][3] | 1U, entry + 12);
265 for (
size_t word = 0; word < 3; ++word)
266 table.write32(previous[i][word], entry + word * 4);
267 table.write32(previous[i][3], entry + 12);
270 config.write16(state.msix + 2, control);
271 config.write16(4, command);
275template <
class Config,
class Io>
276bool enableMsix(Config& config,
const State& state, Io& table, uint32_t offset, uint64_t address,
278 return enableMsixVectors(config, state, table, offset, address, &data, 1);
281template <
class Config,
class Io>
282bool setMsixVectorMask(Config& config,
const State& state, Io& table, uint32_t offset,
size_t index,
284 uint16_t control = 0;
285 if (!state.msix || !config.read16(state.msix + 2, control) || index >= (control & 0x7ffU) + 1 ||
286 uint64_t(offset) + (index + 1) * 16 > table.size() || (!masked && !(control & 0x8000U)))
288 const uint32_t entryControl = table.read32(offset + index * 16 + 12);
289 const uint32_t desired = masked ? entryControl | 1U : entryControl & ~1U;
290 table.write32(desired, offset + index * 16 + 12);
292 return table.read32(offset + index * 16 + 12) == desired;
295template <
class Config,
class Io>
296bool disableMsix(Config& config,
const State& state, Io& table, uint32_t offset) {
297 if (!state.msix || uint64_t(offset) + 16 > table.size())
299 uint16_t control = 0;
300 if (!config.read16(state.msix + 2, control))
302 if (!writeVerified16(config, state.msix + 2, control | 0x4000U))
304 const uint32_t entryControl = table.read32(offset + 12);
305 table.write32(entryControl | 1U, offset + 12);
307 return (table.read32(offset + 12) & 1U) &&
308 writeVerified16(config, state.msix + 2, (control | 0x4000U) & ~0x8000U);
311template <
class Config>
312bool disableMessageInterrupts(Config& config,
const State& state) {
313 const uint8_t capabilities[] = {state.msi, state.msix};
314 for (uint8_t cap : capabilities) {
317 uint16_t control = 0, actual = 0;
318 if (!config.read16(cap + 2, control))
320 const uint16_t desired = cap == state.msi ? control & ~1U : (control | 0x4000U) & ~0x8000U;
321 if (!config.write16(cap + 2, desired) || !config.read16(cap + 2, actual) || actual != desired)
327template <
class Config>
328bool resourcesUnchanged(Config& config,
const State& state,
bool virtualFunction =
false) {
329 if (state.barCount != 2 && state.barCount != 6) {
336 !(virtualFunction &&
identity == 0xffffffffU)) ||
337 !config.read8(14, header) || (header & 0x7f) > (virtualFunction ? 0 : 1) ||
338 state.barCount != ((header & 0x7f) == 1 ? 2 : 6)) {
341 for (
unsigned i = 0; i < state.barCount; ++i) {
343 if (!config.read32(0x10 + 4 * i, bar) || bar != state.bars[i])
346 uint8_t line = 0, pin = 0;
347 return config.read8(0x3c, line) && config.read8(0x3d, pin) && line == state.interruptLine &&
348 pin == state.interruptPin;