The Pedigree Project 0.1
PciFunctionState.h
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#ifndef PEDIGREE_PCI_FUNCTION_STATE_H
3#define PEDIGREE_PCI_FUNCTION_STATE_H
4#include "pedigree/kernel/processor/types.h"
5
6namespace PciFunctionState {
7struct State {
8 uint16_t command = 0;
9 uint32_t bars[6] = {};
10 uint8_t interruptLine = 0, interruptPin = 0;
11 uint8_t pm = 0, msi = 0, msix = 0;
12};
13
14template <class Config>
15bool inspect(Config& config, State& result) {
16 State state;
17 uint32_t identity = 0;
18 uint16_t status = 0;
19 uint8_t header = 0;
20 if (!config.read32(0, identity) || (identity & 0xffff) == 0xffff || !(identity & 0xffff) ||
21 !config.read8(14, header) || (header & 0x7f) != 0 || !config.read16(4, state.command) ||
22 !config.read16(6, status) || !config.read8(0x3c, state.interruptLine) ||
23 !config.read8(0x3d, state.interruptPin))
24 return false;
25 // This driver profile uses the programmable PCI inputs of the legacy PIC.
26 // A valid Interrupt Line is only a routing claim, not proof of delivery.
27 if (state.interruptPin < 1 || state.interruptPin > 4 || state.interruptLine >= 16 ||
28 !(0xdef8U & (1U << state.interruptLine)))
29 return false;
30 for (unsigned i = 0; i < 6; ++i)
31 if (!config.read32(0x10 + 4 * i, state.bars[i]))
32 return false;
33 uint8_t cap = 0;
34 if ((status & 0x10) && !config.read8(0x34, cap))
35 return false;
36 uint64_t occupied = 0;
37 while (cap) {
38 if (cap < 0x40 || (cap & 3))
39 return false;
40 uint8_t type = 0, next = 0;
41 uint16_t control = 0;
42 if (!config.read8(cap, type) || !config.read8(cap + 1, next) ||
43 !config.read16(cap + 2, control))
44 return false;
45 unsigned bytes = 4;
46 if (type == 1) {
47 if (state.pm)
48 return false;
49 state.pm = cap;
50 bytes = 8;
51 } else if (type == 5) {
52 if (state.msi)
53 return false;
54 state.msi = cap;
55 bytes = (control & 0x80) ? 14 : 10;
56 if (control & 0x100)
57 bytes += 10;
58 } else if (type == 0x11) {
59 if (state.msix)
60 return false;
61 state.msix = cap;
62 bytes = 12;
63 }
64 if (cap + bytes > 256)
65 return false;
66 for (unsigned slot = cap / 4; slot < (cap + bytes + 3) / 4; ++slot) {
67 if (occupied & (1ULL << slot))
68 return false;
69 occupied |= 1ULL << slot;
70 }
71 if (type == 1) {
72 uint16_t pmcsr = 0;
73 if (!config.read16(cap + 4, pmcsr) || (pmcsr & 3))
74 return false;
75 }
76 cap = next;
77 }
78 result = state;
79 return true;
80}
81
82template <class Config>
83bool disableMessageInterrupts(Config& config, const State& state) {
84 const uint8_t capabilities[] = {state.msi, state.msix};
85 for (uint8_t cap : capabilities) {
86 if (!cap)
87 continue;
88 uint16_t control = 0, actual = 0;
89 if (!config.read16(cap + 2, control))
90 return false;
91 const uint16_t desired = cap == state.msi ? control & ~1U : (control | 0x4000U) & ~0x8000U;
92 if (!config.write16(cap + 2, desired) || !config.read16(cap + 2, actual) || actual != desired)
93 return false;
94 }
95 return true;
96}
97
98template <class Config>
99bool resourcesUnchanged(Config& config, const State& state) {
100 for (unsigned i = 0; i < 6; ++i) {
101 uint32_t bar = 0;
102 if (!config.read32(0x10 + 4 * i, bar) || bar != state.bars[i])
103 return false;
104 }
105 uint8_t line = 0, pin = 0;
106 return config.read8(0x3c, line) && config.read8(0x3d, pin) && line == state.interruptLine &&
107 pin == state.interruptPin;
108}
109} // namespace PciFunctionState
110#endif