fix: kernel concurrency, interrupt context, and user VA safety

This commit is contained in:
2026-08-01 15:43:38 +02:00
parent f222bf1f04
commit b1c55073c7
71 changed files with 2654 additions and 1092 deletions
+78 -14
View File
@@ -10,6 +10,7 @@
#include <Memory/HHDM.hpp>
#include <Memory/Paging.hpp>
#include <Io/IoPort.hpp>
#include <CppLib/Spinlock.hpp>
using namespace Kt;
@@ -19,6 +20,14 @@ namespace Pci {
static constexpr uint16_t ConfigAddressPort = 0xCF8;
static constexpr uint16_t ConfigDataPort = 0xCFC;
// CF8/CFC is one machine-wide address/data latch, not a per-CPU interface.
// Keep the address write and matching data access indivisible: otherwise a
// second CPU can replace CF8 between them and make us read or write an
// unrelated device's register. Besides bogus probing, a crossed runtime
// command/MSI write can disable interrupts or bus mastering underneath an
// active GPU, USB controller, NIC, or HDA controller.
static kcp::Spinlock g_legacyConfigLock;
// PCI config space register offsets
static constexpr uint16_t RegVendorId = 0x00;
static constexpr uint16_t RegDeviceId = 0x02;
@@ -163,11 +172,26 @@ namespace Pci {
| (offset & 0xFC);
}
uint32_t LegacyRead32(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset) {
static uint32_t LegacyRead32Unlocked(uint8_t bus, uint8_t device,
uint8_t function, uint8_t offset) {
Io::Out32(LegacyBuildAddress(bus, device, function, offset), ConfigAddressPort);
return Io::In32(ConfigDataPort);
}
static void LegacyWrite32Unlocked(uint8_t bus, uint8_t device,
uint8_t function, uint8_t offset,
uint32_t value) {
Io::Out32(LegacyBuildAddress(bus, device, function, offset), ConfigAddressPort);
Io::Out32(value, ConfigDataPort);
}
uint32_t LegacyRead32(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset) {
g_legacyConfigLock.Acquire();
uint32_t value = LegacyRead32Unlocked(bus, device, function, offset);
g_legacyConfigLock.Release();
return value;
}
uint16_t LegacyRead16(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset) {
uint32_t val = LegacyRead32(bus, device, function, offset & 0xFC);
return (uint16_t)(val >> ((offset & 2) * 8));
@@ -179,30 +203,29 @@ namespace Pci {
}
void LegacyWrite32(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value) {
Io::Out32(LegacyBuildAddress(bus, device, function, offset), ConfigAddressPort);
Io::Out32(value, ConfigDataPort);
g_legacyConfigLock.Acquire();
LegacyWrite32Unlocked(bus, device, function, offset, value);
g_legacyConfigLock.Release();
}
void LegacyWrite16(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint16_t value) {
uint32_t addr = LegacyBuildAddress(bus, device, function, offset & 0xFC);
Io::Out32(addr, ConfigAddressPort);
uint32_t tmp = Io::In32(ConfigDataPort);
g_legacyConfigLock.Acquire();
uint32_t tmp = LegacyRead32Unlocked(bus, device, function, offset & 0xFC);
int shift = (offset & 2) * 8;
tmp &= ~(0xFFFF << shift);
tmp |= ((uint32_t)value << shift);
Io::Out32(addr, ConfigAddressPort);
Io::Out32(tmp, ConfigDataPort);
LegacyWrite32Unlocked(bus, device, function, offset & 0xFC, tmp);
g_legacyConfigLock.Release();
}
void LegacyWrite8(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint8_t value) {
uint32_t addr = LegacyBuildAddress(bus, device, function, offset & 0xFC);
Io::Out32(addr, ConfigAddressPort);
uint32_t tmp = Io::In32(ConfigDataPort);
g_legacyConfigLock.Acquire();
uint32_t tmp = LegacyRead32Unlocked(bus, device, function, offset & 0xFC);
int shift = (offset & 3) * 8;
tmp &= ~(0xFF << shift);
tmp |= ((uint32_t)value << shift);
Io::Out32(addr, ConfigAddressPort);
Io::Out32(tmp, ConfigDataPort);
LegacyWrite32Unlocked(bus, device, function, offset & 0xFC, tmp);
g_legacyConfigLock.Release();
}
// -------------------------------------------------------------------------
@@ -237,8 +260,17 @@ namespace Pci {
// Read Capabilities Pointer (offset 0x34), mask to dword-aligned
uint8_t offset = ReadConfig8(bus, device, function, 0x34) & 0xFC;
// Walk the linked list (cap_id @ +0, next_ptr @ +1)
// Walk the conventional 256-byte capability list (cap_id @ +0,
// next_ptr @ +1). Firmware owns these pointers, so reject offsets
// below the capability area and cycles instead of allowing a corrupt
// list to spin forever during device initialization or resume.
uint64_t visited = 0;
while (offset != 0) {
if (offset < 0x40) return 0;
uint64_t bit = 1ULL << (offset >> 2);
if (visited & bit) return 0;
visited |= bit;
uint8_t id = ReadConfig8(bus, device, function, offset);
if (id == capId) {
return offset;
@@ -249,6 +281,38 @@ namespace Pci {
return 0;
}
bool DisableInterruptDelivery(uint8_t bus, uint8_t device, uint8_t function) {
uint16_t command = LegacyRead16(bus, device, function,
(uint8_t)PCI_REG_COMMAND);
if (!(command & PCI_CMD_INTX_DISABLE)) {
LegacyWrite16(bus, device, function, (uint8_t)PCI_REG_COMMAND,
command | PCI_CMD_INTX_DISABLE);
}
bool disabledMessageDelivery = false;
uint8_t msi = FindCapability(bus, device, function, PCI_CAP_MSI);
if (msi != 0) {
uint16_t control = LegacyRead16(bus, device, function, msi + 2);
if (control & 1) {
LegacyWrite16(bus, device, function, msi + 2, control & ~1u);
disabledMessageDelivery = true;
}
}
uint8_t msix = FindCapability(bus, device, function, PCI_CAP_MSIX);
if (msix != 0) {
uint16_t control = LegacyRead16(bus, device, function, msix + 2);
if (control & (1u << 15)) {
// Function-mask before clearing MSI-X Enable so no vector can
// escape during the ownership transition.
uint16_t disabled = (control | (1u << 14)) & ~(1u << 15);
LegacyWrite16(bus, device, function, msix + 2, disabled);
disabledMessageDelivery = true;
}
}
return disabledMessageDelivery;
}
// -------------------------------------------------------------------------
// PCI class code names
// -------------------------------------------------------------------------
+6 -1
View File
@@ -61,7 +61,8 @@ namespace Pci {
void LegacyWrite32(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value);
// PCI capability IDs
constexpr uint8_t PCI_CAP_MSI = 0x05;
constexpr uint8_t PCI_CAP_MSI = 0x05;
constexpr uint8_t PCI_CAP_MSIX = 0x11;
// Walk the PCI capability linked list for a given device.
// Returns the config-space offset of the capability, or 0 if not found.
@@ -96,6 +97,10 @@ namespace Pci {
// Enable memory space access and bus mastering in PCI command register.
void EnableBusMaster(uint8_t bus, uint8_t device, uint8_t function);
// Disable legacy INTx plus any enabled MSI/MSI-X capability. Returns true
// when message-signalled delivery had been enabled and was turned off.
bool DisableInterruptDelivery(uint8_t bus, uint8_t device, uint8_t function);
// =========================================================================
// PCI driver matching
// =========================================================================