The Pedigree Project 0.1
PciExtendedCapabilities.h
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#ifndef PEDIGREE_PCI_EXTENDED_CAPABILITIES_H
3#define PEDIGREE_PCI_EXTENDED_CAPABILITIES_H
4
5#include "pedigree/kernel/processor/types.h"
6
7namespace PciExtendedCapabilities {
8struct Capability {
9 uint16_t id = 0;
10 uint8_t version = 0;
11 uint16_t offset = 0;
12 uint16_t next = 0;
13};
14
15enum class ScanResult { Complete, Stopped, Unavailable, Malformed };
16enum class FindResult { Found, Absent, Unavailable, Malformed };
17
18template <class Config, class Visitor>
19ScanResult walk(Config& config, Visitor visitor) {
20 // One bit for each DWORD of extended configuration space. A link may move
21 // backward, so an iteration limit alone would revisit capabilities.
22 uint32_t visited[30] = {};
23 uint16_t offset = 0x100;
24 for (;;) {
25 const unsigned slot = (offset - 0x100) / 4;
26 const uint32_t mask = 1U << (slot % 32);
27 if (visited[slot / 32] & mask) {
28 return ScanResult::Malformed;
29 }
30 visited[slot / 32] |= mask;
31
32 uint32_t header = 0;
33 if (!config.read32(offset, header)) {
34 return offset == 0x100 ? ScanResult::Unavailable : ScanResult::Malformed;
35 }
36 if (header == 0 || header == 0xffffffffU) {
37 if (offset != 0x100) {
38 return ScanResult::Malformed;
39 }
40 return header == 0 ? ScanResult::Complete : ScanResult::Unavailable;
41 }
42
43 Capability cap{uint16_t(header), uint8_t((header >> 16) & 0xfU), offset,
44 uint16_t(header >> 20)};
45 if (!cap.id || cap.id == 0xffffU || !cap.version ||
46 (cap.next && (cap.next < 0x100 || cap.next > 0xffc || (cap.next & 3U)))) {
47 return ScanResult::Malformed;
48 }
49 if (!visitor(cap)) {
50 return ScanResult::Stopped;
51 }
52 if (!cap.next) {
53 return ScanResult::Complete;
54 }
55 offset = cap.next;
56 }
57}
58
59template <class Config>
60FindResult find(Config& config, uint16_t id, Capability& result) {
61 Capability found;
62 bool present = false;
63 const ScanResult status = walk(config, [&](const Capability& cap) {
64 if (!present && cap.id == id) {
65 found = cap;
66 present = true;
67 }
68 return true;
69 });
70 if (status == ScanResult::Unavailable) {
71 return FindResult::Unavailable;
72 }
73 if (status != ScanResult::Complete) {
74 return FindResult::Malformed;
75 }
76 if (!present) {
77 return FindResult::Absent;
78 }
79 result = found;
80 return FindResult::Found;
81}
82} // namespace PciExtendedCapabilities
83
84#endif