The Pedigree Project 0.1
PciConfigAccess.h
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#ifndef PEDIGREE_PCI_CONFIG_ACCESS_H
3#define PEDIGREE_PCI_CONFIG_ACCESS_H
4#include "pedigree/kernel/processor/types.h"
5
6// Mechanism 1 exposes only the first 256 bytes. Keep validation ahead of any
7// port access, including accesses made by the legacy dword-index interface.
8template <class Io, class Lock>
10 public:
11 PciConfigAccess(Io& io, Lock& lock) : m_Io(io), m_Lock(lock) {}
12 static bool valid(uint8_t device, uint8_t function, uint16_t offset, uint8_t width) {
13 return device < 32 && function < 8 && (width == 1 || width == 2 || width == 4) &&
14 offset <= 256 - width && !(offset & (width - 1));
15 }
16 bool read(uint8_t bus, uint8_t device, uint8_t function, uint16_t offset, uint8_t width,
17 uint32_t& value) {
18 if (!valid(device, function, offset, width))
19 return false;
20 Guard guard(m_Lock);
21 select(bus, device, function, offset);
22 value = readData(offset, width);
23 return true;
24 }
25 bool write(uint8_t bus, uint8_t device, uint8_t function, uint16_t offset, uint8_t width,
26 uint32_t value) {
27 if (!valid(device, function, offset, width))
28 return false;
29 Guard guard(m_Lock);
30 select(bus, device, function, offset);
31 writeData(offset, width, value);
32 return true;
33 }
34 bool updateCommand(uint8_t bus, uint8_t device, uint8_t function, uint16_t clearBits,
35 uint16_t setBits) {
36 if (!valid(device, function, 4, 2))
37 return false;
38 Guard guard(m_Lock);
39 select(bus, device, function, 4);
40 const uint16_t desired = (m_Io.read16(4) & ~clearBits) | setBits;
41 m_Io.write16(desired, 4);
42 return m_Io.read16(4) == desired;
43 }
44
45 private:
46 struct Guard {
47 Lock& lock;
48 explicit Guard(Lock& value) : lock(value) {
49 lock.acquire();
50 }
51 ~Guard() {
52 lock.release();
53 }
54 };
55 void select(uint8_t bus, uint8_t device, uint8_t function, uint16_t offset) {
56 m_Io.write32(0x80000000U | (uint32_t{bus} << 16) | (uint32_t{device} << 11) |
57 (uint32_t{function} << 8) | (offset & ~3U),
58 0);
59 }
60 uint32_t readData(uint16_t offset, uint8_t width) {
61 const uint8_t port = 4 + (offset & 3);
62 if (width == 1)
63 return m_Io.read8(port);
64 if (width == 2)
65 return m_Io.read16(port);
66 return m_Io.read32(port);
67 }
68 void writeData(uint16_t offset, uint8_t width, uint32_t value) {
69 const uint8_t port = 4 + (offset & 3);
70 if (width == 1)
71 m_Io.write8(value, port);
72 else if (width == 2)
73 m_Io.write16(value, port);
74 else
75 m_Io.write32(value, port);
76 }
77 Io& m_Io;
78 Lock& m_Lock;
79};
80#endif