add system time, clean up pci into pci.sys, clean up syscall names, fix USERAPC1 typo

This commit is contained in:
maksw committed 2026-06-11 18:11:59 +02:00
1 parent c1a291c4c9
commit e41bf6e2c0
64 files changed
+4514 -2220

No files matched your search

+603
View File
@@ -0,0 +1,603 @@
// pci.c - PCI bus driver, PDO enumerator, and exported PCI DDI
#include "pci.h"
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <common/printf.h>
#include <ntlite/ddi/hal_api.h>
#include <ntlite/ddi/io_driver.h>
#include <kernel/mm_virt.h>
#include <kernel/ke_debug.h>
#include <kernel/ke_irp.h>
#include <kernel/hal/hal_apic.h>
int _fltused; // MSVC floating-point helper anchor required by printf.obj
static const HAL_API* gHal;
static DRIVER_OBJECT gPciDriver;
static uint32_t gPciDeviceCount;
static bool PciIsPdo(DEVICE_OBJECT* pdo, PCI_PDO_EXTENSION** outExt) {
if (!pdo || pdo->Type != DeviceObjectPhysical || pdo->u.Pdo.BusType != IoBusPci)
return false;
PCI_PDO_EXTENSION* ext = (PCI_PDO_EXTENSION*)pdo->DeviceExtension;
if (!ext) return false;
if (outExt) *outExt = ext;
return true;
}
static uint32_t PciRawRead32(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t offset) {
if (!gHal || offset > 0xFCu) return 0xFFFFFFFFu;
return gHal->Pci.Read32(0, bus, dev, fn, offset & 0xFCu);
}
static void PciRawWrite32(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t offset, uint32_t value) {
if (!gHal || offset > 0xFCu) return;
gHal->Pci.Write32(0, bus, dev, fn, offset & 0xFCu, value);
}
static uint8_t PciRawRead8(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t offset) {
uint32_t v = PciRawRead32(bus, dev, fn, offset);
return (uint8_t)(v >> ((offset & 3u) * 8u));
}
static uint16_t PciRawRead16(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t offset) {
uint32_t v = PciRawRead32(bus, dev, fn, offset);
return (uint16_t)(v >> ((offset & 2u) * 8u));
}
static void PciRawWrite8(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t offset, uint8_t value) {
uint16_t aligned = offset & ~3u;
uint32_t shift = (offset & 3u) * 8u;
uint32_t old = PciRawRead32(bus, dev, fn, aligned);
old &= ~(0xFFu << shift);
old |= ((uint32_t)value << shift);
PciRawWrite32(bus, dev, fn, aligned, old);
}
static void PciRawWrite16(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t offset, uint16_t value) {
uint16_t aligned = offset & ~3u;
uint32_t shift = (offset & 2u) * 8u;
uint32_t old = PciRawRead32(bus, dev, fn, aligned);
old &= ~(0xFFFFu << shift);
old |= ((uint32_t)value << shift);
PciRawWrite32(bus, dev, fn, aligned, old);
}
static uint64_t PciBarSizeFromMask(uint64_t mask, uint64_t baseMask) {
uint64_t sizeMask = mask & baseMask;
if (!sizeMask || sizeMask == baseMask)
return 0;
return (~sizeMask + 1u) & baseMask;
}
static uint8_t PciHeaderBarCount(uint8_t headerType) {
switch (headerType & PCI_HEADER_TYPE_MASK) {
case 0x00: return 6;
case 0x01: return 2;
case 0x02: return 1;
default: return 0;
}
}
static void PciDecodeBars(PCI_PDO_EXTENSION* ext) {
uint8_t barCount = PciHeaderBarCount(ext->Info.HeaderType);
for (uint8_t i = 0; i < PCI_MAX_BARS; i++) {
ext->Bars[i].Type = PciBarUnused;
ext->Bars[i].Index = i;
}
// Size BARs with I/O and memory decoding disabled so all-ones probes do
// not accidentally alias a live resource. Restore policy immediately;
// PciEnableDevice() is the only exported path that changes command bits.
uint16_t savedCommand = PciRawRead16(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
PCI_REG_COMMAND_STATUS);
uint16_t disabledCommand = (uint16_t)(savedCommand &
(uint16_t)~(PCI_COMMAND_IO_SPACE | PCI_COMMAND_MEMORY_SPACE));
if (disabledCommand != savedCommand) {
PciRawWrite16(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
PCI_REG_COMMAND_STATUS,
disabledCommand);
}
for (uint8_t i = 0; i < barCount && i < PCI_MAX_BARS; i++) {
uint16_t reg = (uint16_t)(PCI_REG_BAR0 + i * 4u);
uint32_t original = PciRawRead32(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
reg);
if (original == 0 || original == 0xFFFFFFFFu)
continue;
PCI_BAR* bar = &ext->Bars[i];
bar->Index = i;
bar->Length = 0;
bar->Prefetchable = false;
if (original & 0x1u) {
PciRawWrite32(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
reg,
0xFFFFFFFFu);
uint32_t maskLo = PciRawRead32(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
reg);
PciRawWrite32(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
reg,
original);
bar->Type = PciBarIo;
bar->Base = (uint64_t)(original & ~0x3u);
bar->Length = PciBarSizeFromMask((uint64_t)maskLo, 0xFFFFFFFCull);
continue;
}
uint32_t memType = (original >> 1) & 0x3u;
bar->Prefetchable = (original & 0x8u) != 0;
if (memType == 0x2u && i + 1u < barCount && i + 1u < PCI_MAX_BARS) {
uint16_t regHi = (uint16_t)(reg + 4u);
uint32_t originalHi = PciRawRead32(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
regHi);
PciRawWrite32(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
reg,
0xFFFFFFFFu);
PciRawWrite32(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
regHi,
0xFFFFFFFFu);
uint32_t maskLo = PciRawRead32(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
reg);
uint32_t maskHi = PciRawRead32(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
regHi);
PciRawWrite32(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
reg,
original);
PciRawWrite32(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
regHi,
originalHi);
bar->Type = PciBarMemory64;
bar->Base = ((uint64_t)originalHi << 32) | (uint64_t)(original & ~0xFu);
uint64_t mask = ((uint64_t)maskHi << 32) | (uint64_t)(maskLo & ~0xFu);
bar->Length = PciBarSizeFromMask(mask, 0xFFFFFFFFFFFFFFF0ull);
i++;
ext->Bars[i].Type = PciBarUnused;
ext->Bars[i].Index = i;
} else {
PciRawWrite32(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
reg,
0xFFFFFFFFu);
uint32_t maskLo = PciRawRead32(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
reg);
PciRawWrite32(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
reg,
original);
bar->Type = PciBarMemory32;
bar->Base = (uint64_t)(original & ~0xFu);
bar->Length = PciBarSizeFromMask((uint64_t)maskLo, 0xFFFFFFF0ull);
}
}
if (disabledCommand != savedCommand) {
PciRawWrite16(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
PCI_REG_COMMAND_STATUS,
savedCommand);
}
}
static uint8_t PciFindCapabilityOffset(PCI_PDO_EXTENSION* ext) {
uint16_t status = (uint16_t)(PciRawRead32(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
PCI_REG_COMMAND_STATUS) >> 16);
if ((status & PCI_STATUS_CAP_LIST) == 0)
return 0;
uint8_t cap = PciRawRead8(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
PCI_REG_CAP_PTR) & 0xFCu;
return cap;
}
static void PciMakeIds(const PCI_DEVICE_INFO* info, IO_DEVICE_ID* ids, char storage[][IO_MAX_DEVICE_ID_CHARS], size_t* outCount) {
size_t count = 0;
snprintf(storage[count], IO_MAX_DEVICE_ID_CHARS,
"PCI\\VEN_%04X&DEV_%04X&REV_%02X",
info->VendorId, info->DeviceId, info->RevisionId);
ids[count].BusType = IoBusPci;
ids[count].Id = SysSvFromCstr(storage[count]);
count++;
snprintf(storage[count], IO_MAX_DEVICE_ID_CHARS,
"PCI\\VEN_%04X&DEV_%04X",
info->VendorId, info->DeviceId);
ids[count].BusType = IoBusPci;
ids[count].Id = SysSvFromCstr(storage[count]);
count++;
snprintf(storage[count], IO_MAX_DEVICE_ID_CHARS,
"PCI\\CC_%02X%02X%02X",
info->ClassCode, info->Subclass, info->ProgIf);
ids[count].BusType = IoBusPci;
ids[count].Id = SysSvFromCstr(storage[count]);
count++;
snprintf(storage[count], IO_MAX_DEVICE_ID_CHARS,
"PCI\\CC_%02X%02X",
info->ClassCode, info->Subclass);
ids[count].BusType = IoBusPci;
ids[count].Id = SysSvFromCstr(storage[count]);
count++;
snprintf(storage[count], IO_MAX_DEVICE_ID_CHARS,
"PCI\\CC_%02X",
info->ClassCode);
ids[count].BusType = IoBusPci;
ids[count].Id = SysSvFromCstr(storage[count]);
count++;
*outCount = count;
}
static KSTATUS PciCreatePdo(uint8_t bus, uint8_t dev, uint8_t fn) {
uint32_t id = PciRawRead32(bus, dev, fn, PCI_REG_VENDOR_DEVICE);
uint16_t vendor = (uint16_t)(id & 0xFFFFu);
if (vendor == PCI_VENDOR_INVALID)
return STATUS_NOT_FOUND;
uint32_t classReg = PciRawRead32(bus, dev, fn, PCI_REG_REVISION_CLASS);
uint32_t hdrReg = PciRawRead32(bus, dev, fn, PCI_REG_HEADER_TYPE);
uint32_t irqReg = PciRawRead32(bus, dev, fn, PCI_REG_INTERRUPT_LINE_PIN);
uint32_t subsysReg = PciRawRead32(bus, dev, fn, PCI_REG_SUBSYSTEM);
PCI_DEVICE_INFO info;
memset(&info, 0, sizeof(info));
info.Address.Segment = 0;
info.Address.Bus = bus;
info.Address.Device = dev;
info.Address.Function = fn;
info.VendorId = vendor;
info.DeviceId = (uint16_t)(id >> 16);
info.SubsystemVendorId = (uint16_t)(subsysReg & 0xFFFFu);
info.SubsystemId = (uint16_t)(subsysReg >> 16);
info.RevisionId = (uint8_t)(classReg & 0xFFu);
info.ProgIf = (uint8_t)((classReg >> 8) & 0xFFu);
info.Subclass = (uint8_t)((classReg >> 16) & 0xFFu);
info.ClassCode = (uint8_t)((classReg >> 24) & 0xFFu);
info.HeaderType = (uint8_t)((hdrReg >> 16) & 0xFFu);
info.InterruptLine = (uint8_t)(irqReg & 0xFFu);
info.InterruptPin = (uint8_t)((irqReg >> 8) & 0xFFu);
IO_DEVICE_ID ids[5];
char idStorage[5][IO_MAX_DEVICE_ID_CHARS];
size_t idCount = 0;
PciMakeIds(&info, ids, idStorage, &idCount);
DEVICE_OBJECT* pdo = (DEVICE_OBJECT*)0;
KSTATUS st = IoCreatePhysicalDevice(&gPciDriver,
(DEVICE_OBJECT*)0,
ids,
idCount,
sizeof(PCI_PDO_EXTENSION),
&pdo);
if (!KSUCCESS(st))
return st;
PCI_PDO_EXTENSION* ext = (PCI_PDO_EXTENSION*)pdo->DeviceExtension;
memset(ext, 0, sizeof(*ext));
ext->Info = info;
PciDecodeBars(ext);
ext->CapabilityOffset = PciFindCapabilityOffset(ext);
pdo->u.Pdo.DeviceContext = ext;
char name[IO_MAX_DEVICE_NAME_CHARS];
snprintf(name, sizeof(name), "pci-%04X:%02X:%02X.%u",
info.Address.Segment, info.Address.Bus, info.Address.Device, info.Address.Function);
IoSetDeviceName(pdo, SysSvFromCstr(name));
st = IoRegisterPhysicalDevice(pdo);
if (!KSUCCESS(st)) {
IoDeleteDevice(pdo);
return st;
}
gPciDeviceCount++;
DbgPrint("pci: %v ven=%04x dev=%04x class=%02x%02x%02x irq=%u\n",
pdo->Name,
info.VendorId,
info.DeviceId,
info.ClassCode,
info.Subclass,
info.ProgIf,
info.InterruptLine);
return STATUS_SUCCESS;
}
static void PciEnumerateBus(void) {
for (uint32_t bus = 0; bus < 256u; bus++) {
for (uint32_t dev = 0; dev < 32u; dev++) {
uint32_t id0 = PciRawRead32((uint8_t)bus, (uint8_t)dev, 0, PCI_REG_VENDOR_DEVICE);
if ((uint16_t)(id0 & 0xFFFFu) == PCI_VENDOR_INVALID)
continue;
uint8_t headerType = PciRawRead8((uint8_t)bus, (uint8_t)dev, 0, PCI_REG_HEADER_TYPE + 2u);
uint8_t fnLimit = (headerType & PCI_HEADER_TYPE_MULTI) ? 8u : 1u;
for (uint8_t fn = 0; fn < fnLimit; fn++) {
KSTATUS st = PciCreatePdo((uint8_t)bus, (uint8_t)dev, fn);
if (!KSUCCESS(st) && st != STATUS_NOT_FOUND) {
DbgPrint("pci: failed to create PDO for %02x:%02x.%u st=0x%x\n",
bus, dev, fn, st);
}
}
}
}
}
static KSTATUS PciDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
if (!device || !irp) return STATUS_INVALID_PARAMETER;
IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp);
if (!ios || ios->MajorFunction != IRP_MJ_PNP) return STATUS_INVALID_PARAMETER;
switch ((IO_PNP_MINOR_FUNCTION)ios->MinorFunction) {
case IRP_MN_START_DEVICE:
device->PnpState = DevicePnpStarted;
return STATUS_SUCCESS;
case IRP_MN_QUERY_REMOVE_DEVICE:
case IRP_MN_CANCEL_REMOVE_DEVICE:
case IRP_MN_SURPRISE_REMOVAL:
case IRP_MN_REMOVE_DEVICE:
return STATUS_NOT_SUPPORTED;
default:
return STATUS_NOT_SUPPORTED;
}
}
__declspec(dllexport) uint32_t PciDriverAbiVersion(void) {
return 1u;
}
__declspec(dllexport) KSTATUS PciGetDeviceInfo(DEVICE_OBJECT* pdo, PCI_DEVICE_INFO* outInfo) {
PCI_PDO_EXTENSION* ext;
if (!outInfo || !PciIsPdo(pdo, &ext)) return STATUS_INVALID_PARAMETER;
*outInfo = ext->Info;
return STATUS_SUCCESS;
}
__declspec(dllexport) KSTATUS PciReadConfig8(DEVICE_OBJECT* pdo, uint16_t offset, uint8_t* outValue) {
PCI_PDO_EXTENSION* ext;
if (!outValue || offset > 0xFFu || !PciIsPdo(pdo, &ext)) return STATUS_INVALID_PARAMETER;
*outValue = PciRawRead8(ext->Info.Address.Bus, ext->Info.Address.Device, ext->Info.Address.Function, offset);
return STATUS_SUCCESS;
}
__declspec(dllexport) KSTATUS PciReadConfig16(DEVICE_OBJECT* pdo, uint16_t offset, uint16_t* outValue) {
PCI_PDO_EXTENSION* ext;
if (!outValue || offset > 0xFEu || !PciIsPdo(pdo, &ext)) return STATUS_INVALID_PARAMETER;
*outValue = PciRawRead16(ext->Info.Address.Bus, ext->Info.Address.Device, ext->Info.Address.Function, offset);
return STATUS_SUCCESS;
}
__declspec(dllexport) KSTATUS PciReadConfig32(DEVICE_OBJECT* pdo, uint16_t offset, uint32_t* outValue) {
PCI_PDO_EXTENSION* ext;
if (!outValue || offset > 0xFCu || !PciIsPdo(pdo, &ext)) return STATUS_INVALID_PARAMETER;
*outValue = PciRawRead32(ext->Info.Address.Bus, ext->Info.Address.Device, ext->Info.Address.Function, offset);
return STATUS_SUCCESS;
}
__declspec(dllexport) KSTATUS PciWriteConfig8(DEVICE_OBJECT* pdo, uint16_t offset, uint8_t value) {
PCI_PDO_EXTENSION* ext;
if (offset > 0xFFu || !PciIsPdo(pdo, &ext)) return STATUS_INVALID_PARAMETER;
PciRawWrite8(ext->Info.Address.Bus, ext->Info.Address.Device, ext->Info.Address.Function, offset, value);
return STATUS_SUCCESS;
}
__declspec(dllexport) KSTATUS PciWriteConfig16(DEVICE_OBJECT* pdo, uint16_t offset, uint16_t value) {
PCI_PDO_EXTENSION* ext;
if (offset > 0xFEu || !PciIsPdo(pdo, &ext)) return STATUS_INVALID_PARAMETER;
PciRawWrite16(ext->Info.Address.Bus, ext->Info.Address.Device, ext->Info.Address.Function, offset, value);
return STATUS_SUCCESS;
}
__declspec(dllexport) KSTATUS PciWriteConfig32(DEVICE_OBJECT* pdo, uint16_t offset, uint32_t value) {
PCI_PDO_EXTENSION* ext;
if (offset > 0xFCu || !PciIsPdo(pdo, &ext)) return STATUS_INVALID_PARAMETER;
PciRawWrite32(ext->Info.Address.Bus, ext->Info.Address.Device, ext->Info.Address.Function, offset, value);
return STATUS_SUCCESS;
}
__declspec(dllexport) KSTATUS PciGetBar(DEVICE_OBJECT* pdo, uint8_t index, PCI_BAR* outBar) {
PCI_PDO_EXTENSION* ext;
if (!outBar || index >= PCI_MAX_BARS || !PciIsPdo(pdo, &ext)) return STATUS_INVALID_PARAMETER;
if (ext->Bars[index].Type == PciBarUnused) return STATUS_NOT_FOUND;
*outBar = ext->Bars[index];
return STATUS_SUCCESS;
}
__declspec(dllexport) KSTATUS PciMapBar(DEVICE_OBJECT* pdo, uint8_t index, uint64_t pageFlags, void** outVirtual, uint64_t* outLength) {
PCI_BAR bar;
if (!outVirtual) return STATUS_INVALID_PARAMETER;
KSTATUS st = PciGetBar(pdo, index, &bar);
if (!KSUCCESS(st)) return st;
if (bar.Type != PciBarMemory32 && bar.Type != PciBarMemory64)
return STATUS_NOT_SUPPORTED;
if (!bar.Length)
return STATUS_DEVICE_ERROR;
uint64_t flags = pageFlags ? pageFlags : (PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE);
uint64_t pageBase = bar.Base & ~(uint64_t)(PAGE_SIZE - 1u);
uint64_t pageEnd = (bar.Base + bar.Length + PAGE_SIZE - 1u) & ~(uint64_t)(PAGE_SIZE - 1u);
for (uint64_t phys = pageBase; phys < pageEnd; phys += PAGE_SIZE) {
uintptr_t virt = (uintptr_t)gHal->PhysToVirt(phys);
st = MmMapPage(virt, phys, flags, MmMapFailIfPresent);
if (st == STATUS_ALREADY_EXISTS)
st = MmSetPageProtect(virt, PAGE_SIZE, flags);
if (!KSUCCESS(st))
return st;
}
*outVirtual = (void*)((uintptr_t)gHal->PhysToVirt(pageBase) + (uintptr_t)(bar.Base - pageBase));
if (outLength) *outLength = bar.Length;
return STATUS_SUCCESS;
}
__declspec(dllexport) KSTATUS PciEnableDevice(DEVICE_OBJECT* pdo, uint32_t flags) {
PCI_PDO_EXTENSION* ext;
if (!PciIsPdo(pdo, &ext)) return STATUS_INVALID_PARAMETER;
uint16_t cmd = PciRawRead16(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
PCI_REG_COMMAND_STATUS);
if (flags & PCI_ENABLE_IO) cmd |= PCI_COMMAND_IO_SPACE;
if (flags & PCI_ENABLE_MEMORY) cmd |= PCI_COMMAND_MEMORY_SPACE;
if (flags & PCI_ENABLE_BUSMASTER) cmd |= PCI_COMMAND_BUS_MASTER;
if (flags & PCI_ENABLE_INTX) cmd &= (uint16_t)~PCI_COMMAND_INTX_DISABLE;
if (flags & PCI_DISABLE_INTX) cmd |= PCI_COMMAND_INTX_DISABLE;
PciRawWrite16(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
PCI_REG_COMMAND_STATUS,
cmd);
return STATUS_SUCCESS;
}
__declspec(dllexport) KSTATUS PciGetInterrupt(DEVICE_OBJECT* pdo, uint8_t* outIrq) {
PCI_PDO_EXTENSION* ext;
if (!outIrq || !PciIsPdo(pdo, &ext)) return STATUS_INVALID_PARAMETER;
if (ext->Info.InterruptLine == 0xFFu) return STATUS_NOT_FOUND;
*outIrq = ext->Info.InterruptLine;
return STATUS_SUCCESS;
}
static uint8_t PciFindCapability(PCI_PDO_EXTENSION* ext, uint8_t capId) {
if (!ext || !ext->CapabilityOffset)
return 0;
uint8_t cap = ext->CapabilityOffset & 0xFCu;
for (uint32_t guard = 0; cap && guard < 48u; guard++) {
uint32_t hdr = PciRawRead32(ext->Info.Address.Bus,
ext->Info.Address.Device,
ext->Info.Address.Function,
cap);
uint8_t id = (uint8_t)(hdr & 0xFFu);
uint8_t next = (uint8_t)((hdr >> 8) & 0xFCu);
if (id == capId)
return cap;
cap = next;
}
return 0;
}
__declspec(dllexport) KSTATUS PciEnableMsi(DEVICE_OBJECT* pdo, uint8_t irqSlot) {
PCI_PDO_EXTENSION* ext;
if (!PciIsPdo(pdo, &ext))
return STATUS_INVALID_PARAMETER;
if (irqSlot >= 24u)
return STATUS_INVALID_PARAMETER;
uint8_t cap = PciFindCapability(ext, PCI_CAP_ID_MSI);
if (!cap)
return STATUS_NOT_SUPPORTED;
uint32_t bspLapic = HalGetBspLapicId();
if (!HalApicUsingX2Apic() && bspLapic >= 256u)
return STATUS_NOT_SUPPORTED;
if (HalApicUsingX2Apic() && bspLapic >= 256u) {
// Extended x2APIC MSI destinations need interrupt-remapping awareness.
return STATUS_NOT_SUPPORTED;
}
uint8_t bus = ext->Info.Address.Bus;
uint8_t dev = ext->Info.Address.Device;
uint8_t fn = ext->Info.Address.Function;
uint16_t control = PciRawRead16(bus, dev, fn, (uint16_t)(cap + 2u));
bool is64 = (control & PCI_MSI_CTRL_64BIT) != 0;
bool pvm = (control & PCI_MSI_CTRL_PVM) != 0;
uint8_t vector = (uint8_t)(PCI_IRQ_VECTOR_BASE + irqSlot);
uint32_t address = PCI_MSI_ADDRESS_BASE | ((bspLapic & 0xFFu) << 12);
uint16_t data = vector;
PciRawWrite32(bus, dev, fn, (uint16_t)(cap + 4u), address);
if (is64) {
PciRawWrite32(bus, dev, fn, (uint16_t)(cap + 8u), 0);
PciRawWrite16(bus, dev, fn, (uint16_t)(cap + 12u), data);
if (pvm)
PciRawWrite32(bus, dev, fn, (uint16_t)(cap + 16u), 0);
} else {
PciRawWrite16(bus, dev, fn, (uint16_t)(cap + 8u), data);
if (pvm)
PciRawWrite32(bus, dev, fn, (uint16_t)(cap + 12u), 0);
}
control &= (uint16_t)~PCI_MSI_CTRL_MME_MASK;
control |= PCI_MSI_CTRL_ENABLE;
PciRawWrite16(bus, dev, fn, (uint16_t)(cap + 2u), control);
uint16_t cmd = PciRawRead16(bus, dev, fn, PCI_REG_COMMAND_STATUS);
cmd |= PCI_COMMAND_INTX_DISABLE;
PciRawWrite16(bus, dev, fn, PCI_REG_COMMAND_STATUS, cmd);
DbgPrint("pci: MSI enabled %02x:%02x.%u cap=0x%02x vector=0x%02x irqslot=%u dest=%u %s\n",
bus, dev, fn, cap, vector, irqSlot, bspLapic, is64 ? "64-bit" : "32-bit");
return STATUS_SUCCESS;
}
KSTATUS DriverEntry(const HAL_API* hal) {
if (!hal) return STATUS_INVALID_PARAMETER;
gHal = hal;
gPciDeviceCount = 0;
IoInitializeDriverObject(&gPciDriver, SYS_SV_LITERAL("pci"));
gPciDriver.MajorFunction[IRP_MJ_PNP] = PciDispatchPnp;
KSTATUS st = IoRegisterDriverObject(&gPciDriver);
if (!KSUCCESS(st)) return st;
DbgPrint("pci: DriverEntry ABI=%u\n", PciDriverAbiVersion());
PciEnumerateBus();
DbgPrint("pci: enumeration complete, devices=%u\n", gPciDeviceCount);
return STATUS_SUCCESS;
}
+45
View File
@@ -0,0 +1,45 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <ntlite/ddi/pci.h>
#define PCI_MAX_BARS 6u
#define PCI_VENDOR_INVALID 0xFFFFu
#define PCI_REG_VENDOR_DEVICE 0x00u
#define PCI_REG_COMMAND_STATUS 0x04u
#define PCI_REG_REVISION_CLASS 0x08u
#define PCI_REG_HEADER_TYPE 0x0Cu
#define PCI_REG_BAR0 0x10u
#define PCI_REG_SUBSYSTEM 0x2Cu
#define PCI_REG_CAP_PTR 0x34u
#define PCI_REG_INTERRUPT_LINE_PIN 0x3Cu
#define PCI_COMMAND_IO_SPACE 0x0001u
#define PCI_COMMAND_MEMORY_SPACE 0x0002u
#define PCI_COMMAND_BUS_MASTER 0x0004u
#define PCI_COMMAND_INTX_DISABLE 0x0400u
#define PCI_HEADER_TYPE_MASK 0x7Fu
#define PCI_HEADER_TYPE_MULTI 0x80u
#define PCI_STATUS_CAP_LIST 0x0010u
#define PCI_CAP_ID_MSI 0x05u
#define PCI_CAP_ID_MSIX 0x11u
#define PCI_MSI_CTRL_ENABLE 0x0001u
#define PCI_MSI_CTRL_MME_MASK 0x0070u
#define PCI_MSI_CTRL_64BIT 0x0080u
#define PCI_MSI_CTRL_PVM 0x0100u
#define PCI_MSI_ADDRESS_BASE 0xFEE00000u
#define PCI_IRQ_VECTOR_BASE 0x20u
typedef struct PCI_PDO_EXTENSION {
PCI_DEVICE_INFO Info;
PCI_BAR Bars[PCI_MAX_BARS];
uint8_t CapabilityOffset;
} PCI_PDO_EXTENSION;
+28 -60
View File
@@ -1,8 +1,17 @@
// usb.c - USB bus driver entry point and PCI controller discovery.
// usb.c - USB bus driver entry point and host-controller driver registration.
#include "usb.h"
XHCI_CONTROLLER gXhci;
bool gHaveXhci = false;
DRIVER_OBJECT gXhciDriver;
const HAL_API* gUsbHal;
static const IO_DEVICE_ID gXhciIds[] = {
{ IoBusPci, { "PCI\\CC_0C0330", sizeof("PCI\\CC_0C0330") - 1 } },
};
static const IO_DRIVER_ID_TABLE gXhciIdTable = {
gXhciIds,
sizeof(gXhciIds) / sizeof(gXhciIds[0]),
};
const char* UsbControllerName(uint8_t progIf) {
switch (progIf) {
@@ -14,66 +23,19 @@ const char* UsbControllerName(uint8_t progIf) {
}
}
void UsbDiscoverControllers(const HAL_API* hal) {
uint32_t found = 0;
uint32_t xhciOk = 0;
for (uint16_t bus = 0; bus < 256; bus++) {
for (uint8_t dev = 0; dev < 32; dev++) {
for (uint8_t fn = 0; fn < 8; fn++) {
uint32_t vd = hal->Pci.Read32((uint8_t)bus, dev, fn, PCI_REG_VENDOR_DEVICE);
if ((vd & 0xFFFFu) == PCI_VENDOR_INVALID)
continue;
uint32_t classReg = hal->Pci.Read32((uint8_t)bus, dev, fn, PCI_REG_CLASS);
uint8_t classCode = (uint8_t)(classReg >> 24);
uint8_t subclass = (uint8_t)(classReg >> 16);
uint8_t progIf = (uint8_t)(classReg >> 8);
if (classCode != PCI_CLASS_SERIAL_BUS || subclass != PCI_SUBCLASS_USB)
continue;
USB_BAR bar0;
bool hasBar0 = UsbReadBar(hal, (uint8_t)bus, dev, fn, 0, &bar0);
uint32_t rawBar0 = hal->Pci.Read32((uint8_t)bus, dev, fn, PCI_REG_BAR0);
uint32_t rawBar1 = hal->Pci.Read32((uint8_t)bus, dev, fn, PCI_REG_BAR0 + 4u);
uint8_t irq = hal->Pci.GetIrqLine((uint8_t)bus, dev, fn);
DbgPrint("usb: found %s controller at %u:%u.%u vendor=%04x device=%04x irq=%u bar0raw=%08x bar1raw=%08x",
UsbControllerName(progIf),
bus, dev, fn,
vd & 0xFFFFu, vd >> 16,
irq, rawBar0, rawBar1);
if (hasBar0 && bar0.Kind == UsbBarMmio) {
DbgPrint(" mmio=0x%016llX\n", (unsigned long long)bar0.Base);
} else if (hasBar0 && bar0.Kind == UsbBarIo) {
DbgPrint(" io=0x%016llX\n", (unsigned long long)bar0.Base);
} else {
DbgPrint(" no-bar0\n");
}
if (progIf == PCI_PROGIF_XHCI) {
KSTATUS st = XhciInitialise(hal, (uint8_t)bus, dev, fn,
(uint16_t)(vd & 0xFFFFu),
(uint16_t)(vd >> 16));
if (KSUCCESS(st) || st == STATUS_ALREADY_EXISTS) xhciOk++;
else DbgPrint("usb: xHCI init failed st=0x%x\n", st);
}
found++;
}
}
}
if (!found)
DbgPrint("usb: no PCI USB controllers discovered\n");
else
DbgPrint("usb: discovery complete; controllers=%u xhci_initialised=%u\n", found, xhciOk);
static KSTATUS UsbInitializeXhciDriver(void) {
memset(&gXhciDriver, 0, sizeof(gXhciDriver));
IoInitializeDriverObject(&gXhciDriver, SYS_SV_LITERAL("xhci"));
gXhciDriver.IdTable = &gXhciIdTable;
gXhciDriver.DriverAddDevice = XhciAddDevice;
gXhciDriver.MajorFunction[IRP_MJ_PNP] = XhciDispatchPnp;
return IoRegisterDriverObject(&gXhciDriver);
}
KSTATUS DriverEntry(const HAL_API* hal) {
if (!hal) return STATUS_INVALID_PARAMETER;
DbgPrint("usb: discovery starting\n");
DbgPrint("usb: driver loading\n");
gUsbHal = hal;
KSTATUS st = UsbInitializeBusModel();
if (!KSUCCESS(st)) {
@@ -105,6 +67,12 @@ KSTATUS DriverEntry(const HAL_API* hal) {
return st;
}
UsbDiscoverControllers(hal);
st = UsbInitializeXhciDriver();
if (!KSUCCESS(st)) {
DbgPrint("usb: xHCI driver initialisation failed st=0x%x\n", st);
return st;
}
DbgPrint("usb: driver initialized\n");
return STATUS_SUCCESS;
}
+14 -31
View File
@@ -10,6 +10,7 @@
#include <ntlite/ddi/hal_api.h>
#include <ntlite/ddi/input_device.h>
#include <ntlite/ddi/io_driver.h>
#include <ntlite/ddi/pci.h>
#include <ntlite/status.h>
#include <kernel/ke_debug.h>
#include <kernel/ke_irq.h>
@@ -32,17 +33,9 @@ static __inline void UsbTrace(const char* fmt, ...) { (void)fmt; }
#endif
#define PCI_VENDOR_INVALID 0xFFFFu
#define PCI_CLASS_SERIAL_BUS 0x0Cu
#define PCI_SUBCLASS_USB 0x03u
#define PCI_PROGIF_XHCI 0x30u
#define PCI_REG_VENDOR_DEVICE 0x00u
#define PCI_REG_COMMAND 0x04u
#define PCI_REG_CLASS 0x08u
#define PCI_REG_BAR0 0x10u
#define PCI_COMMAND_MEMORY (1u << 1)
#define PCI_COMMAND_MASTER (1u << 2)
#define PCI_COMMAND_INTX_DISABLE (1u << 10)
#define XHCI_CAP_CAPLENGTH 0x00u
#define XHCI_CAP_HCSPARAMS1 0x04u
@@ -212,7 +205,6 @@ static __inline void UsbTrace(const char* fmt, ...) { (void)fmt; }
#define XHCI_RING_TRBS 256u
#define XHCI_EVENT_TRBS 256u
#define XHCI_DCBAA_ENTRIES 256u
#define XHCI_MMIO_FLOOR_BYTES 0x10000u
#define XHCI_MAX_SCRATCHPADS 1024u
#define XHCI_MAX_TRACKED_SLOTS 64u
#define XHCI_MAX_ROOT_PORTS 255u
@@ -347,19 +339,6 @@ typedef struct USB_TRACKED_INTERFACE {
USB_ENDPOINT_DESCRIPTOR Endpoints[USB_MAX_TRACKED_ENDPOINTS];
} USB_TRACKED_INTERFACE;
typedef enum USB_BAR_KIND {
UsbBarNone,
UsbBarIo,
UsbBarMmio
} USB_BAR_KIND;
typedef struct USB_BAR {
USB_BAR_KIND Kind;
uint64_t Base;
uint8_t Index;
} USB_BAR;
typedef enum USB_DEVICE_STATE {
UsbDeviceAttached = 0,
UsbDevicePowered,
@@ -422,7 +401,7 @@ struct USB_HCD {
const USB_HCD_OPERATIONS* Operations;
void* ControllerContext;
DRIVER_OBJECT* DriverObject;
DEVICE_OBJECT* ControllerPdo;
DEVICE_OBJECT* ControllerDevice;
DEVICE_OBJECT* RootHubPdo;
void* RootHubContext;
uint8_t ControllerIndex;
@@ -488,10 +467,9 @@ struct USB_URB {
};
typedef enum USB_PDO_KIND {
UsbPdoController = 1,
UsbPdoRootHub = 1,
UsbPdoDevice = 2,
UsbPdoInterface = 3,
UsbPdoRootHub = 4,
} USB_PDO_KIND;
typedef struct USB_PDO_EXTENSION {
@@ -779,7 +757,13 @@ typedef struct XHCI_WAIT_TARGET {
} XHCI_WAIT_TARGET;
typedef struct XHCI_CONTROLLER {
uint32_t Magic;
const HAL_API* Hal;
DEVICE_OBJECT* Fdo;
DEVICE_OBJECT* Pdo;
DEVICE_OBJECT* LowerDevice;
PCI_DEVICE_INFO PciInfo;
PCI_BAR MmioBar;
uint8_t Bus;
uint8_t Dev;
uint8_t Fn;
@@ -845,13 +829,13 @@ typedef struct XHCI_CONTROLLER {
} XHCI_CONTROLLER;
extern XHCI_CONTROLLER gXhci;
extern bool gHaveXhci;
extern DRIVER_OBJECT gUsbStorageDriver;
extern DRIVER_OBJECT gUsbBusDriver;
extern DRIVER_OBJECT gUsbCompositeDriver;
extern DRIVER_OBJECT gUsbHidDriver;
extern DRIVER_OBJECT gUsbHubDriver;
extern DRIVER_OBJECT gXhciDriver;
extern const HAL_API* gUsbHal;
KSTATUS UsbInitializeBusModel(void);
KSTATUS UsbInitializeCompositeDriver(void);
@@ -865,7 +849,6 @@ void UsbInitializeHcd(USB_HCD* hcd,
void* controllerContext,
uint8_t controllerIndex,
uint8_t maxRootPorts);
KSTATUS UsbCreateControllerPdo(USB_HCD* hcd, uint16_t vendor, uint16_t device);
KSTATUS UsbCreateRootHubPdo(USB_HCD* hcd);
KSTATUS UsbRemoveDeviceObjectChildren(DEVICE_OBJECT* parent, bool surprise);
KSTATUS UsbRemovePdoSubtree(DEVICE_OBJECT* pdo, bool surprise);
@@ -899,8 +882,6 @@ void XhciWrite64(volatile uint8_t* base, uint32_t reg, uint64_t value);
void UsbDelayUs(const HAL_API* hal, uint32_t us);
bool UsbWaitSet32(const HAL_API* hal, volatile uint8_t* base, uint32_t reg, uint32_t mask, uint32_t timeoutUs);
bool UsbWaitClear32(const HAL_API* hal, volatile uint8_t* base, uint32_t reg, uint32_t mask, uint32_t timeoutUs);
bool UsbReadBar(const HAL_API* hal, uint8_t bus, uint8_t dev, uint8_t fn, uint8_t index, USB_BAR* out);
KSTATUS UsbMapMmio(const HAL_API* hal, uint64_t physBase, uint32_t bytes, volatile uint8_t** outMmio);
void* UsbDmaAllocPages(XHCI_CONTROLLER* xhci, size_t pages, PHYSICAL_ADDRESS* outPhys);
uint16_t XhciScratchpadCount(uint32_t hcs2);
uint32_t XhciTrbType(const XHCI_TRB* trb);
@@ -1014,4 +995,6 @@ KSTATUS UsbHubScheduleRootScan(USB_HCD* hcd);
void UsbHubQueueRootPortChange(USB_HCD* hcd, uint8_t port);
void UsbHubQueueExternalPortChange(USB_HUB_EXTENSION* hub, uint8_t port);
extern const USB_HCD_OPERATIONS gXhciHcdOperations;
KSTATUS XhciInitialise(const HAL_API* hal, uint8_t bus, uint8_t dev, uint8_t fn, uint16_t vendor, uint16_t device);
KSTATUS XhciStartController(XHCI_CONTROLLER* xhci);
KSTATUS XhciAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject);
KSTATUS XhciDispatchPnp(DEVICE_OBJECT* device, IRP* irp);
+2 -44
View File
@@ -177,16 +177,6 @@ static void UsbFormatComposite(char* buf, size_t cap) {
UsbIdAppendLiteral(buf, cap, &pos, "USB\\COMPOSITE");
}
static void UsbFormatPciXhci(char* buf, size_t cap, uint16_t vendor, uint16_t device) {
size_t pos = 0;
if (cap) buf[0] = '\0';
UsbIdAppendLiteral(buf, cap, &pos, "PCI\\VEN_");
UsbIdAppendHex(buf, cap, &pos, vendor, 4);
UsbIdAppendLiteral(buf, cap, &pos, "&DEV_");
UsbIdAppendHex(buf, cap, &pos, device, 4);
UsbIdAppendLiteral(buf, cap, &pos, "&CC_0C0330");
}
static void UsbFormatRootHub(char* buf, size_t cap) {
size_t pos = 0;
if (cap) buf[0] = '\0';
@@ -273,38 +263,6 @@ void UsbInitializeHcd(USB_HCD* hcd,
hcd->MaxRootPorts = maxRootPorts;
}
KSTATUS UsbCreateControllerPdo(USB_HCD* hcd, uint16_t vendor, uint16_t device) {
if (!hcd) return STATUS_INVALID_PARAMETER;
if (hcd->ControllerPdo) return STATUS_ALREADY_EXISTS;
IO_DEVICE_ID ids[2];
char idbuf[2][USB_MAX_DEVICE_ID_TEXT];
UsbFormatPciXhci(idbuf[0], USB_MAX_DEVICE_ID_TEXT, vendor, device);
{
size_t pos = 0;
idbuf[1][0] = '\0';
UsbIdAppendLiteral(idbuf[1], USB_MAX_DEVICE_ID_TEXT, &pos, "PCI\\CC_0C0330");
}
ids[0] = (IO_DEVICE_ID){ IoBusPci, UsbIdFromBuffer(idbuf[0]) };
ids[1] = (IO_DEVICE_ID){ IoBusPci, UsbIdFromBuffer(idbuf[1]) };
DEVICE_OBJECT* pdo = (DEVICE_OBJECT*)0;
KSTATUS st = IoCreatePhysicalDevice(&gUsbBusDriver, (DEVICE_OBJECT*)0, ids, 2, sizeof(USB_PDO_EXTENSION), &pdo);
if (!KSUCCESS(st)) return st;
(void)IoSetDeviceName(pdo, SYS_SV_LITERAL("xhci-controller"));
USB_PDO_EXTENSION* ext = (USB_PDO_EXTENSION*)pdo->DeviceExtension;
ext->Kind = UsbPdoController;
ext->Hcd = hcd;
hcd->ControllerPdo = pdo;
st = IoRegisterPhysicalDevice(pdo);
if (!KSUCCESS(st) && st != STATUS_ALREADY_EXISTS)
return st;
return STATUS_SUCCESS;
}
KSTATUS UsbCreateRootHubPdo(USB_HCD* hcd) {
if (!hcd) return STATUS_INVALID_PARAMETER;
if (hcd->RootHubPdo) return STATUS_ALREADY_EXISTS;
@@ -318,7 +276,7 @@ KSTATUS UsbCreateRootHubPdo(USB_HCD* hcd) {
DEVICE_OBJECT* pdo = (DEVICE_OBJECT*)0;
KSTATUS st = IoCreatePhysicalDevice(&gUsbBusDriver,
hcd->ControllerPdo,
hcd->ControllerDevice,
ids,
2,
sizeof(USB_PDO_EXTENSION),
@@ -376,7 +334,7 @@ KSTATUS UsbCreateDevicePdo(USB_HCD* hcd, XHCI_DEVICE* xdev, DEVICE_OBJECT* paren
DEVICE_OBJECT* pdo = (DEVICE_OBJECT*)0;
KSTATUS st = IoCreatePhysicalDevice(&gUsbBusDriver,
parent ? parent : (hcd->RootHubPdo ? hcd->RootHubPdo : hcd->ControllerPdo),
parent ? parent : (hcd->RootHubPdo ? hcd->RootHubPdo : hcd->ControllerDevice),
ids,
idCount,
sizeof(USB_PDO_EXTENSION),
+1 -1
View File
@@ -242,7 +242,7 @@ KSTATUS XhciDiscoverAndSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
if (!dev->UsbPdo) {
st = UsbCreateDevicePdo(&xhci->Hcd, dev,
dev->ParentDeviceObject ? dev->ParentDeviceObject :
(xhci->Hcd.RootHubPdo ? xhci->Hcd.RootHubPdo : xhci->Hcd.ControllerPdo));
(xhci->Hcd.RootHubPdo ? xhci->Hcd.RootHubPdo : xhci->Hcd.ControllerDevice));
if (!KSUCCESS(st)) {
DbgPrint("usb: create device PDO slot=%u failed st=0x%x\n", dev->SlotId, st);
return st;
+183 -136
View File
@@ -74,62 +74,6 @@ bool UsbWaitClear32(const HAL_API* hal, volatile uint8_t* base, uint32_t reg,
}
bool UsbReadBar(const HAL_API* hal, uint8_t bus, uint8_t dev, uint8_t fn,
uint8_t index, USB_BAR* out) {
uint8_t reg = (uint8_t)(PCI_REG_BAR0 + index * 4u);
uint32_t lo = hal->Pci.Read32(bus, dev, fn, reg);
out->Kind = UsbBarNone;
out->Base = 0;
out->Index = index;
if (lo == 0 || lo == 0xFFFFFFFFu)
return false;
if (lo & 1u) {
out->Kind = UsbBarIo;
out->Base = (uint64_t)(lo & ~0x3u);
return out->Base != 0;
}
uint64_t base = (uint64_t)(lo & ~0xFu);
uint32_t type = (lo >> 1) & 0x3u;
if (type == 0x2u && index < 5) {
uint32_t hi = hal->Pci.Read32(bus, dev, fn, (uint8_t)(reg + 4u));
base |= ((uint64_t)hi << 32);
}
out->Kind = UsbBarMmio;
out->Base = base;
return base != 0;
}
KSTATUS UsbMapMmio(const HAL_API* hal, uint64_t physBase,
uint32_t bytes, volatile uint8_t** outMmio) {
uint64_t pageBase = physBase & PAGE_MASK;
uint64_t pageOff = physBase - pageBase;
uint32_t total = UsbAlignUp32((uint32_t)pageOff + bytes, (uint32_t)PAGE_SIZE);
uint32_t pages = total / (uint32_t)PAGE_SIZE;
for (uint32_t i = 0; i < pages; i++) {
PHYSICAL_ADDRESS phys = pageBase + (uint64_t)i * PAGE_SIZE;
uintptr_t virt = (uintptr_t)hal->PhysToVirt(phys);
KSTATUS st = MmMapPage(virt, phys, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE, MmMapFailIfPresent);
if (st == STATUS_ALREADY_EXISTS)
st = MmSetPageProtect(virt, PAGE_SIZE, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE);
if (!KSUCCESS(st)) {
DbgPrint("usb: xHCI MmMapPage failed phys=0x%016llX st=0x%x\n",
(unsigned long long)phys, st);
return st;
}
}
*outMmio = (volatile uint8_t*)((uintptr_t)hal->PhysToVirt(pageBase) + pageOff);
return STATUS_SUCCESS;
}
void* UsbDmaAllocPages(XHCI_CONTROLLER* xhci, size_t pages,
PHYSICAL_ADDRESS* outPhys) {
if (!outPhys || pages == 0)
@@ -617,19 +561,39 @@ void XhciEventDpcRoutine(KDPC* dpc, void* context,
}
#define XHCI_CONTROLLER_MAGIC 0x58484349u /* XHCI */
#define XHCI_MAX_CONTROLLERS 8u
static XHCI_CONTROLLER* gXhciControllers[XHCI_MAX_CONTROLLERS];
static uint32_t gXhciControllerCount;
static KSTATUS XhciRegisterController(XHCI_CONTROLLER* xhci) {
if (!xhci) return STATUS_INVALID_PARAMETER;
for (uint32_t i = 0; i < gXhciControllerCount; i++) {
if (gXhciControllers[i] == xhci)
return STATUS_SUCCESS;
}
if (gXhciControllerCount >= XHCI_MAX_CONTROLLERS)
return STATUS_NO_MEMORY;
gXhciControllers[gXhciControllerCount++] = xhci;
return STATUS_SUCCESS;
}
void XhciIsr(void) {
XHCI_CONTROLLER* xhci = &gXhci;
if (!xhci->Running || !xhci->Op || !xhci->Runtime)
return;
for (uint32_t i = 0; i < gXhciControllerCount; i++) {
XHCI_CONTROLLER* xhci = gXhciControllers[i];
if (!xhci || !xhci->Running || !xhci->Op || !xhci->Runtime)
continue;
uint32_t status = XhciRead32(xhci->Op, XHCI_OP_USBSTS);
if (!(status & (XHCI_USBSTS_EINT | XHCI_USBSTS_PCD | XHCI_USBSTS_HSE | XHCI_USBSTS_HCE)))
return;
uint32_t status = XhciRead32(xhci->Op, XHCI_OP_USBSTS);
if (!(status & (XHCI_USBSTS_EINT | XHCI_USBSTS_PCD | XHCI_USBSTS_HSE | XHCI_USBSTS_HCE)))
continue;
xhci->InterruptCount++;
XhciWrite32(xhci->Op, XHCI_OP_USBSTS, status & XHCI_USBSTS_CLEAR_MASK);
XhciAckInterrupter0(xhci);
KeInsertQueueDpc(&xhci->EventDpc, (void*)0, (void*)0);
xhci->InterruptCount++;
XhciWrite32(xhci->Op, XHCI_OP_USBSTS, status & XHCI_USBSTS_CLEAR_MASK);
XhciAckInterrupter0(xhci);
KeInsertQueueDpc(&xhci->EventDpc, (void*)0, (void*)0);
}
}
@@ -1872,65 +1836,67 @@ const USB_HCD_OPERATIONS gXhciHcdOperations = {
XhciHcdAllocateDma,
};
KSTATUS XhciInitialise(const HAL_API* hal,
uint8_t bus, uint8_t dev, uint8_t fn,
uint16_t vendor, uint16_t device) {
if (gHaveXhci) {
DbgPrint("usb: additional xHCI controller at %u:%u.%u ignored for now\n",
bus, dev, fn);
return STATUS_ALREADY_EXISTS;
KSTATUS XhciStartController(XHCI_CONTROLLER* xhci) {
if (!xhci || xhci->Magic != XHCI_CONTROLLER_MAGIC || !xhci->Pdo || !xhci->Fdo)
return STATUS_INVALID_PARAMETER;
if (xhci->Running)
return STATUS_SUCCESS;
KSTATUS st = PciGetDeviceInfo(xhci->Pdo, &xhci->PciInfo);
if (!KSUCCESS(st)) {
DbgPrint("usb: xHCI PciGetDeviceInfo failed st=0x%x\n", st);
return st;
}
USB_BAR bar;
if (!UsbReadBar(hal, bus, dev, fn, 0, &bar) || bar.Kind != UsbBarMmio) {
DbgPrint("usb: xHCI controller has no usable MMIO BAR0\n");
xhci->Bus = xhci->PciInfo.Address.Bus;
xhci->Dev = xhci->PciInfo.Address.Device;
xhci->Fn = xhci->PciInfo.Address.Function;
st = PciGetBar(xhci->Pdo, 0, &xhci->MmioBar);
if (!KSUCCESS(st)) {
DbgPrint("usb: xHCI PCI %02x:%02x.%x BAR0 missing st=0x%x\n",
xhci->Bus, xhci->Dev, xhci->Fn, st);
return st;
}
if (xhci->MmioBar.Type != PciBarMemory32 && xhci->MmioBar.Type != PciBarMemory64) {
DbgPrint("usb: xHCI PCI %02x:%02x.%x BAR0 is not MMIO type=%u\n",
xhci->Bus, xhci->Dev, xhci->Fn, xhci->MmioBar.Type);
return STATUS_DEVICE_ERROR;
}
st = PciGetInterrupt(xhci->Pdo, &xhci->Irq);
if (!KSUCCESS(st)) {
DbgPrint("usb: xHCI PciGetInterrupt failed st=0x%x\n", st);
return st;
}
if (xhci->Irq == 0xFFu) {
DbgPrint("usb: xHCI PCI %02x:%02x.%x has no INTx IRQ; MSI is not in pci.sys yet\n",
xhci->Bus, xhci->Dev, xhci->Fn);
return STATUS_NOT_SUPPORTED;
}
memset(&gXhci, 0, sizeof(gXhci));
XHCI_CONTROLLER* xhci = &gXhci;
KeInitializeDpc(&xhci->EventDpc, XhciEventDpcRoutine, xhci);
xhci->Hal = hal;
xhci->Bus = bus;
xhci->Dev = dev;
xhci->Fn = fn;
xhci->Irq = hal->Pci.GetIrqLine(bus, dev, fn);
xhci->MmioPhys = bar.Base;
uint32_t pciCmd = hal->Pci.Read32(bus, dev, fn, PCI_REG_COMMAND);
pciCmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
hal->Pci.Write32(bus, dev, fn, PCI_REG_COMMAND, pciCmd);
if (xhci->Irq == 0xFFu) {
uint8_t msiIrq = 0xFFu;
KSTATUS irqSt = KeIrqAllocateMsiVector(&msiIrq);
if (!KSUCCESS(irqSt)) {
DbgPrint("usb: xHCI has no PCI INTx IRQ line and MSI vector allocation failed st=0x%x\n",
irqSt);
return irqSt;
}
if (!hal->Pci.EnableMsi) {
DbgPrint("usb: xHCI has no PCI INTx IRQ line and HAL MSI support is unavailable\n");
return STATUS_NOT_SUPPORTED;
}
irqSt = hal->Pci.EnableMsi(bus, dev, fn, msiIrq);
if (!KSUCCESS(irqSt)) {
DbgPrint("usb: xHCI has no PCI INTx IRQ line and MSI enable failed st=0x%x\n",
irqSt);
return irqSt;
}
xhci->Irq = msiIrq;
xhci->UsingMsi = true;
DbgPrint("usb: xHCI using MSI irqslot=%u vector=0x%02x\n",
xhci->Irq, 0x20u + xhci->Irq);
} else {
pciCmd = hal->Pci.Read32(bus, dev, fn, PCI_REG_COMMAND);
pciCmd &= ~PCI_COMMAND_INTX_DISABLE;
hal->Pci.Write32(bus, dev, fn, PCI_REG_COMMAND, pciCmd);
st = PciEnableDevice(xhci->Pdo,
PCI_ENABLE_MEMORY |
PCI_ENABLE_BUSMASTER |
PCI_ENABLE_INTX);
if (!KSUCCESS(st)) {
DbgPrint("usb: xHCI PciEnableDevice failed st=0x%x\n", st);
return st;
}
KSTATUS st = UsbMapMmio(hal, xhci->MmioPhys, PAGE_SIZE, &xhci->Mmio);
if (!KSUCCESS(st)) return st;
xhci->MmioPhys = xhci->MmioBar.Base;
uint64_t mappedLength = 0;
st = PciMapBar(xhci->Pdo,
0,
PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE,
(void**)&xhci->Mmio,
&mappedLength);
if (!KSUCCESS(st)) {
DbgPrint("usb: xHCI PciMapBar(BAR0) failed st=0x%x\n", st);
return st;
}
xhci->MmioBytes = (mappedLength > 0xFFFFFFFFull) ? 0xFFFFFFFFu : (uint32_t)mappedLength;
xhci->Cap = xhci->Mmio;
uint32_t cap0 = XhciRead32(xhci->Cap, XHCI_CAP_CAPLENGTH);
@@ -1954,32 +1920,29 @@ KSTATUS XhciInitialise(const HAL_API* hal,
return STATUS_DEVICE_ERROR;
}
UsbInitializeHcd(&xhci->Hcd, &gXhciHcdOperations, xhci, 0, xhci->MaxPorts);
KSTATUS hcdPdoStatus = UsbCreateControllerPdo(&xhci->Hcd, vendor, device);
if (!KSUCCESS(hcdPdoStatus)) {
DbgPrint("usb: xHCI controller PDO creation failed st=0x%x\n", hcdPdoStatus);
return hcdPdoStatus;
}
uint32_t portEnd = xhci->CapLength + XHCI_OP_PORT_BASE +
(uint32_t)xhci->MaxPorts * XHCI_OP_PORT_STRIDE;
uint32_t needBytes = portEnd;
if (xhci->DbOff + 0x100u > needBytes) needBytes = xhci->DbOff + 0x100u;
if (xhci->RtsOff + 0x1000u > needBytes) needBytes = xhci->RtsOff + 0x1000u;
if (needBytes < XHCI_MMIO_FLOOR_BYTES) needBytes = XHCI_MMIO_FLOOR_BYTES;
needBytes = UsbAlignUp32(needBytes, (uint32_t)PAGE_SIZE);
if (xhci->MmioBytes < needBytes) {
DbgPrint("usb: xHCI BAR0 too small length=0x%x need=0x%x dboff=0x%x rtsoff=0x%x ports=%u\n",
xhci->MmioBytes, needBytes, xhci->DbOff, xhci->RtsOff, xhci->MaxPorts);
return STATUS_DEVICE_ERROR;
}
st = UsbMapMmio(hal, xhci->MmioPhys, needBytes, &xhci->Mmio);
if (!KSUCCESS(st)) return st;
xhci->MmioBytes = needBytes;
xhci->Cap = xhci->Mmio;
xhci->Op = xhci->Mmio + xhci->CapLength;
xhci->Runtime = xhci->Mmio + xhci->RtsOff;
xhci->Doorbells = xhci->Mmio + xhci->DbOff;
DbgPrint("usb: xHCI init %u:%u.%u vendor=%04x device=%04x irq=%u%s mmio=0x%016llX caplen=%u version=%04x\n",
bus, dev, fn, vendor, device, xhci->Irq,
xhci->UsingMsi ? "(msi)" : "",
UsbInitializeHcd(&xhci->Hcd, &gXhciHcdOperations, xhci, 0, xhci->MaxPorts);
xhci->Hcd.ControllerDevice = xhci->Fdo;
DbgPrint("usb: xHCI init %02x:%02x.%x vendor=%04x device=%04x irq=%u mmio=0x%016llX caplen=%u version=%04x\n",
xhci->Bus, xhci->Dev, xhci->Fn,
xhci->PciInfo.VendorId, xhci->PciInfo.DeviceId,
xhci->Irq,
(unsigned long long)xhci->MmioPhys, xhci->CapLength, xhci->Version);
DbgPrint("usb: xHCI caps slots=%u intrs=%u ports=%u ctx=%u scratchpads=%u hcs2=%08x hcc1=%08x dboff=0x%x rtsoff=0x%x pagesize=%08x\n",
xhci->MaxSlots, xhci->MaxIntrs, xhci->MaxPorts, xhci->ContextBytes,
@@ -2021,12 +1984,18 @@ KSTATUS XhciInitialise(const HAL_API* hal,
return st;
}
st = XhciRegisterController(xhci);
if (!KSUCCESS(st)) {
DbgPrint("usb: xHCI controller registry full st=0x%x\n", st);
return st;
}
KeIrqRegister(xhci->Irq, XhciIsr);
XhciEnableInterrupts(xhci);
xhci->InterruptsEnabled = true;
xhci->Fdo->PnpState = DevicePnpStarted;
KeInsertQueueDpc(&xhci->EventDpc, (void*)0, (void*)0);
DbgPrint("usb: xHCI ISR registered on IRQ %u%s; event ring handled by DPC\n",
xhci->Irq, xhci->UsingMsi ? "(msi)" : "");
DbgPrint("usb: xHCI ISR registered on IRQ %u; event ring handled by DPC\n", xhci->Irq);
st = UsbHubScheduleRootScan(&xhci->Hcd);
if (!KSUCCESS(st))
@@ -2034,8 +2003,86 @@ KSTATUS XhciInitialise(const HAL_API* hal,
else
DbgPrint("usb: root hub initial scan queued asynchronously\n");
gHaveXhci = true;
DbgPrint("usb: xHCI host controller initialised; HID boot input active=%u mass-storage will arrive through hotplug\n",
xhci->HidDeviceCount);
return STATUS_SUCCESS;
}
KSTATUS XhciDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
if (!device || !irp || !device->DeviceExtension)
return STATUS_INVALID_PARAMETER;
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)device->DeviceExtension;
if (xhci->Magic != XHCI_CONTROLLER_MAGIC)
return STATUS_INVALID_PARAMETER;
IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp);
if (!ios) return STATUS_INVALID_PARAMETER;
switch ((IO_PNP_MINOR_FUNCTION)ios->MinorFunction) {
case IRP_MN_START_DEVICE:
return XhciStartController(xhci);
case IRP_MN_QUERY_REMOVE_DEVICE:
device->PnpState = DevicePnpRemovePending;
return STATUS_SUCCESS;
case IRP_MN_CANCEL_REMOVE_DEVICE:
device->PnpState = xhci->Running ? DevicePnpStarted : DevicePnpNotStarted;
return STATUS_SUCCESS;
case IRP_MN_SURPRISE_REMOVAL:
device->PnpState = DevicePnpSurpriseRemoved;
xhci->Running = false;
xhci->InterruptsEnabled = false;
if (xhci->Op) {
uint32_t cmd = XhciRead32(xhci->Op, XHCI_OP_USBCMD);
XhciWrite32(xhci->Op, XHCI_OP_USBCMD, cmd & ~XHCI_USBCMD_RS);
}
if (xhci->Hcd.RootHubPdo)
(void)UsbRemovePdoSubtree(xhci->Hcd.RootHubPdo, true);
return STATUS_SUCCESS;
case IRP_MN_REMOVE_DEVICE:
device->PnpState = DevicePnpDeleted;
xhci->Running = false;
xhci->InterruptsEnabled = false;
if (xhci->Hcd.RootHubPdo)
(void)UsbRemovePdoSubtree(xhci->Hcd.RootHubPdo, true);
IoDetachDevice(device);
IoDeleteDevice(device);
return STATUS_SUCCESS;
default:
return IoForwardIrpToLower(device, irp);
}
}
KSTATUS XhciAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject) {
if (!driver || !physicalDeviceObject || physicalDeviceObject->Type != DeviceObjectPhysical)
return STATUS_INVALID_PARAMETER;
DEVICE_OBJECT* fdo = (DEVICE_OBJECT*)0;
KSTATUS st = IoCreateDevice(driver, sizeof(XHCI_CONTROLLER), &fdo);
if (!KSUCCESS(st))
return st;
(void)IoSetDeviceName(fdo, SYS_SV_LITERAL("xhci-controller"));
XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)fdo->DeviceExtension;
memset(xhci, 0, sizeof(*xhci));
xhci->Magic = XHCI_CONTROLLER_MAGIC;
xhci->Hal = gUsbHal;
xhci->Fdo = fdo;
xhci->Pdo = physicalDeviceObject;
xhci->Irq = 0xFFu;
KeInitializeDpc(&xhci->EventDpc, XhciEventDpcRoutine, xhci);
xhci->LowerDevice = IoAttachDeviceToDeviceStack(fdo, physicalDeviceObject);
if (!xhci->LowerDevice) {
IoDeleteDevice(fdo);
return STATUS_FAILURE;
}
DbgPrint("usb: xHCI FDO attached to %v\n", physicalDeviceObject->Name);
return STATUS_SUCCESS;
}
+3 -1
View File
@@ -1,10 +1,12 @@
[boot]
pci.sys
ps2.sys
ide.sys
ahci.sys
ata.sys
usb.sys
fat32.sys
[runtime]
lwip.sys
rtl8139.sys
rtl816x.sys
+274 -28
View File
@@ -101,6 +101,8 @@ typedef struct {
#define ATTR_DIRECTORY 0x10
#define ATTR_ARCHIVE 0x20
#define ATTR_LFN 0x0F
#define FAT_NTRES_BASE_LOWER 0x08u
#define FAT_NTRES_EXT_LOWER 0x10u
#define FAT32_EOC 0x0FFFFFF8u
#define FAT32_EOC_MARK 0x0FFFFFFFu
#define FAT32_FREE 0x00000000u
@@ -343,34 +345,186 @@ static void FreeClusterChain(FAT32_VOL *v, uint32_t start) {
}
// ---------------------------------------------------------------------------
// DIRENT update helpers
// FAT timestamp and DIRENT metadata helpers
// ---------------------------------------------------------------------------
static KSTATUS DirentUpdateSize(FAT32_VOL *v, uint64_t lba,
uint32_t offset, uint32_t size) {
typedef struct FAT32_ENCODED_TIME {
uint16_t Date;
uint16_t Time;
uint8_t CreateTimeTenth;
} FAT32_ENCODED_TIME;
typedef struct FAT32_DIRENT_UPDATE {
uint32_t Flags;
uint32_t Size;
uint32_t FirstCluster;
FAT32_ENCODED_TIME Timestamp;
} FAT32_DIRENT_UPDATE;
#define FAT32_DIRENT_UPDATE_SIZE 0x00000001u
#define FAT32_DIRENT_UPDATE_FIRST_CLUSTER 0x00000002u
#define FAT32_DIRENT_UPDATE_CREATE_TIME 0x00000004u
#define FAT32_DIRENT_UPDATE_WRITE_TIME 0x00000008u
#define FAT32_DIRENT_UPDATE_ACCESS_DATE 0x00000010u
static int FatIsLeapYear(uint32_t year) {
return ((year % 4u) == 0u && (year % 100u) != 0u) || ((year % 400u) == 0u);
}
static uint32_t FatDaysInMonth(uint32_t year, uint32_t month) {
static const uint8_t days[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
if (month < 1u || month > 12u) return 31u;
if (month == 2u && FatIsLeapYear(year)) return 29u;
return days[month - 1u];
}
static uint64_t FatCalendarToEpochSeconds(uint32_t year, uint32_t month, uint32_t day,
uint32_t hour, uint32_t minute, uint32_t second) {
uint64_t days = 0;
for (uint32_t y = 1970u; y < year; y++)
days += FatIsLeapYear(y) ? 366u : 365u;
for (uint32_t m = 1u; m < month; m++)
days += FatDaysInMonth(year, m);
days += day - 1u;
return (((days * 24u) + hour) * 60u + minute) * 60u + second;
}
static uint8_t FatDayOfWeek(uint32_t year, uint32_t month, uint32_t day) {
uint64_t epoch = FatCalendarToEpochSeconds(year, month, day, 0, 0, 0);
return (uint8_t)(((epoch / 86400u) + 4u) % 7u);
}
static uint16_t FatEncodeDate(const SYS_SYSTEM_TIME* t) {
uint32_t year = t && t->Year ? t->Year : 1980u;
uint32_t month = t && t->Month ? t->Month : 1u;
uint32_t day = t && t->Day ? t->Day : 1u;
if (year < 1980u) year = 1980u;
if (year > 2107u) year = 2107u;
if (month < 1u) month = 1u;
if (month > 12u) month = 12u;
uint32_t dim = FatDaysInMonth(year, month);
if (day < 1u) day = 1u;
if (day > dim) day = dim;
return (uint16_t)(((year - 1980u) << 9) | (month << 5) | day);
}
static uint16_t FatEncodeTime(const SYS_SYSTEM_TIME* t) {
uint32_t hour = t ? t->Hour : 0u;
uint32_t minute = t ? t->Minute : 0u;
uint32_t second = t ? t->Second : 0u;
if (hour > 23u) hour = 23u;
if (minute > 59u) minute = 59u;
if (second > 59u) second = 59u;
return (uint16_t)((hour << 11) | (minute << 5) | (second / 2u));
}
static uint8_t FatEncodeCreateTimeTenth(const SYS_SYSTEM_TIME* t) {
uint32_t second = t ? t->Second : 0u;
uint32_t ms = t ? t->Millisecond : 0u;
if (second > 59u) second = 59u;
if (ms > 999u) ms = 999u;
uint32_t v = ((second & 1u) * 100u) + (ms / 10u);
if (v > 199u) v = 199u;
return (uint8_t)v;
}
static void FatQueryEncodedTime(FAT32_ENCODED_TIME* out) {
SYS_SYSTEM_TIME now;
memset(&now, 0, sizeof(now));
if (KeQuerySystemTime(&now) != STATUS_SUCCESS) {
now.Year = 1980;
now.Month = 1;
now.Day = 1;
}
out->Date = FatEncodeDate(&now);
out->Time = FatEncodeTime(&now);
out->CreateTimeTenth = FatEncodeCreateTimeTenth(&now);
}
static void FatDecodeDateTime(uint16_t date, uint16_t time, uint8_t createTenth,
SYS_SYSTEM_TIME* out) {
memset(out, 0, sizeof(*out));
uint32_t year = 1980u + ((date >> 9) & 0x7Fu);
uint32_t month = (date >> 5) & 0x0Fu;
uint32_t day = date & 0x1Fu;
uint32_t hour = (time >> 11) & 0x1Fu;
uint32_t minute = (time >> 5) & 0x3Fu;
uint32_t second = (time & 0x1Fu) * 2u;
if (date == 0 || month < 1u || month > 12u) { year = 1980u; month = 1u; day = 1u; }
uint32_t dim = FatDaysInMonth(year, month);
if (day < 1u) day = 1u;
if (day > dim) day = dim;
if (hour > 23u) hour = 23u;
if (minute > 59u) minute = 59u;
if (second > 58u) second = 58u;
if (createTenth >= 100u && second < 59u) second++;
out->Year = (uint16_t)year;
out->Month = (uint8_t)month;
out->Day = (uint8_t)day;
out->Hour = (uint8_t)hour;
out->Minute = (uint8_t)minute;
out->Second = (uint8_t)second;
out->Millisecond = (uint16_t)((createTenth % 100u) * 10u);
out->DayOfWeek = FatDayOfWeek(year, month, day);
out->Flags = SYS_TIME_FLAG_VALID;
out->EpochSeconds = FatCalendarToEpochSeconds(year, month, day, hour, minute, second);
}
static void FatDecodeAccessDate(uint16_t date, SYS_SYSTEM_TIME* out) {
FatDecodeDateTime(date, 0, 0, out);
}
static KSTATUS DirentUpdateMetadata(FAT32_VOL *v, uint64_t lba,
uint32_t offset,
const FAT32_DIRENT_UPDATE* update) {
if (!v || !update || !update->Flags) return STATUS_INVALID_PARAMETER;
uint8_t *sec = (uint8_t *)MmAllocatePool(v->BytesPerSector);
if (!sec) return STATUS_NO_MEMORY;
KSTATUS s = ReadSectors(v, lba, 1, sec);
if (KSUCCESS(s)) {
((DIRENT *)(sec + offset))->FileSize = size;
DIRENT *d = (DIRENT *)(sec + offset);
if (update->Flags & FAT32_DIRENT_UPDATE_SIZE)
d->FileSize = update->Size;
if (update->Flags & FAT32_DIRENT_UPDATE_FIRST_CLUSTER) {
d->FirstClusterHi = (uint16_t)(update->FirstCluster >> 16);
d->FirstClusterLo = (uint16_t)(update->FirstCluster & 0xFFFF);
}
if (update->Flags & FAT32_DIRENT_UPDATE_CREATE_TIME) {
d->CrtTimeTenth = update->Timestamp.CreateTimeTenth;
d->CrtTime = update->Timestamp.Time;
d->CrtDate = update->Timestamp.Date;
}
if (update->Flags & FAT32_DIRENT_UPDATE_WRITE_TIME) {
d->WrtTime = update->Timestamp.Time;
d->WrtDate = update->Timestamp.Date;
}
if (update->Flags & FAT32_DIRENT_UPDATE_ACCESS_DATE)
d->LastAccDate = update->Timestamp.Date;
s = WriteSectors(v, lba, 1, sec);
}
MmFreePool(sec);
return s;
}
static KSTATUS DirentUpdateSize(FAT32_VOL *v, uint64_t lba,
uint32_t offset, uint32_t size) {
FAT32_DIRENT_UPDATE u;
memset(&u, 0, sizeof(u));
FatQueryEncodedTime(&u.Timestamp);
u.Flags = FAT32_DIRENT_UPDATE_SIZE | FAT32_DIRENT_UPDATE_WRITE_TIME;
u.Size = size;
return DirentUpdateMetadata(v, lba, offset, &u);
}
static KSTATUS DirentUpdateFirstCluster(FAT32_VOL *v, uint64_t lba,
uint32_t offset, uint32_t cluster) {
uint8_t *sec = (uint8_t *)MmAllocatePool(v->BytesPerSector);
if (!sec) return STATUS_NO_MEMORY;
KSTATUS s = ReadSectors(v, lba, 1, sec);
if (KSUCCESS(s)) {
DIRENT *d = (DIRENT *)(sec + offset);
d->FirstClusterHi = (uint16_t)(cluster >> 16);
d->FirstClusterLo = (uint16_t)(cluster & 0xFFFF);
s = WriteSectors(v, lba, 1, sec);
}
MmFreePool(sec);
return s;
FAT32_DIRENT_UPDATE u;
memset(&u, 0, sizeof(u));
u.Flags = FAT32_DIRENT_UPDATE_FIRST_CLUSTER;
u.FirstCluster = cluster;
return DirentUpdateMetadata(v, lba, offset, &u);
}
// ---------------------------------------------------------------------------
@@ -467,14 +621,67 @@ static int FatNameIsStructurallyShort83V(SYS_STRING_VIEW name) {
return baseLen >= 1 && baseLen <= 8 && extLen <= 3;
}
// Creation uses LFN when needed for structure, or when lowercase would be lost.
static int FatNameNeedsLfnForCreateV(SYS_STRING_VIEW name) {
if (!FatNameIsStructurallyShort83V(name)) return 1;
for (size_t i = 0; i < name.Length; i++)
static int FatComponentHasLowerV(SYS_STRING_VIEW name, size_t start, size_t end) {
for (size_t i = start; i < end; i++)
if (FatIsLowerChar(name.Data[i])) return 1;
return 0;
}
static int FatComponentHasUpperV(SYS_STRING_VIEW name, size_t start, size_t end) {
for (size_t i = start; i < end; i++)
if (name.Data[i] >= 'A' && name.Data[i] <= 'Z') return 1;
return 0;
}
static void FatSplitShortNameV(SYS_STRING_VIEW name, size_t* baseEnd,
size_t* extStart, size_t* extEnd) {
size_t dot = (size_t)-1;
for (size_t i = 0; i < name.Length; i++)
if (name.Data[i] == '.') dot = i;
if (dot == (size_t)-1) {
*baseEnd = name.Length;
*extStart = name.Length;
*extEnd = name.Length;
} else {
*baseEnd = dot;
*extStart = dot + 1u;
*extEnd = name.Length;
}
}
// Creation uses LFN only when the spelling cannot be represented by a short
// entry, or when mixed-case 8.3 spelling would be lost. Plain lowercase
// 8.3 names are represented with FAT's NT case bits, not VFAT LFN entries.
static int FatNameNeedsLfnForCreateV(SYS_STRING_VIEW name) {
if (!FatNameIsStructurallyShort83V(name)) return 1;
size_t baseEnd, extStart, extEnd;
FatSplitShortNameV(name, &baseEnd, &extStart, &extEnd);
int baseLower = FatComponentHasLowerV(name, 0, baseEnd);
int baseUpper = FatComponentHasUpperV(name, 0, baseEnd);
int extLower = FatComponentHasLowerV(name, extStart, extEnd);
int extUpper = FatComponentHasUpperV(name, extStart, extEnd);
if ((baseLower && baseUpper) || (extLower && extUpper)) return 1;
return 0;
}
static uint8_t FatShortNameCaseFlagsV(SYS_STRING_VIEW name) {
if (!FatNameIsStructurallyShort83V(name)) return 0;
size_t baseEnd, extStart, extEnd;
FatSplitShortNameV(name, &baseEnd, &extStart, &extEnd);
uint8_t flags = 0;
if (FatComponentHasLowerV(name, 0, baseEnd) &&
!FatComponentHasUpperV(name, 0, baseEnd))
flags |= FAT_NTRES_BASE_LOWER;
if (extStart < extEnd &&
FatComponentHasLowerV(name, extStart, extEnd) &&
!FatComponentHasUpperV(name, extStart, extEnd))
flags |= FAT_NTRES_EXT_LOWER;
return flags;
}
static uint8_t LfnChecksum(const char name8[8], const char ext3[3]) {
uint8_t sum = 0;
for (int i = 0; i < 11; i++) {
@@ -1218,9 +1425,17 @@ KSTATUS Fat32Write(IO_FILESYSTEM *fs, void *fileCtx,
MmFreePool(runBuf);
uint64_t newEnd = offset + written;
if (newEnd > f->FileSize) {
f->FileSize = (uint32_t)newEnd;
DirentUpdateSize(v, f->DirentLba, f->DirentOffset, f->FileSize);
if (written > 0) {
FAT32_DIRENT_UPDATE u;
memset(&u, 0, sizeof(u));
FatQueryEncodedTime(&u.Timestamp);
u.Flags = FAT32_DIRENT_UPDATE_WRITE_TIME;
if (newEnd > f->FileSize) {
f->FileSize = (uint32_t)newEnd;
u.Flags |= FAT32_DIRENT_UPDATE_SIZE;
u.Size = f->FileSize;
}
DirentUpdateMetadata(v, f->DirentLba, f->DirentOffset, &u);
}
*bytesWritten = written;
@@ -1316,6 +1531,17 @@ KSTATUS Fat32Create(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
memcpy(d->Name, shortName, 8);
memcpy(d->Ext, shortExt, 3);
d->Attr = ATTR_ARCHIVE;
d->Reserved = FatShortNameCaseFlagsV(filenameView);
{
FAT32_ENCODED_TIME now;
FatQueryEncodedTime(&now);
d->CrtTimeTenth = now.CreateTimeTenth;
d->CrtTime = now.Time;
d->CrtDate = now.Date;
d->LastAccDate = now.Date;
d->WrtTime = now.Time;
d->WrtDate = now.Date;
}
s = WriteSectors(v, clusterLba, v->SectorsPerCluster, clusterBuf);
MmFreePool(clusterBuf);
@@ -1340,7 +1566,13 @@ KSTATUS Fat32Truncate(IO_FILESYSTEM *fs, void *fileCtx, uint64_t newSize) {
(void)fs;
FAT32_FILE *f = (FAT32_FILE *)fileCtx;
FAT32_VOL *v = f->Vol;
if ((uint32_t)newSize == f->FileSize) return STATUS_SUCCESS;
if ((uint32_t)newSize == f->FileSize) {
FAT32_DIRENT_UPDATE u;
memset(&u, 0, sizeof(u));
FatQueryEncodedTime(&u.Timestamp);
u.Flags = FAT32_DIRENT_UPDATE_WRITE_TIME;
return DirentUpdateMetadata(v, f->DirentLba, f->DirentOffset, &u);
}
if (newSize == 0) {
if (f->StartCluster >= 2) {
@@ -1495,6 +1727,8 @@ KSTATUS Fat32ReadDir(IO_FILESYSTEM *fs, void *dirCtx, KDIR_ENTRY *out) {
if (e->Attr == ATTR_LFN) { LfnAppend(ctx->LfnBuf, (LFN_ENTRY *)e); ctx->HasLfn = 1; continue; }
if (e->Attr & 0x08 && !(e->Attr & ATTR_DIRECTORY)) { ctx->HasLfn = 0; memset(ctx->LfnBuf, 0, sizeof(ctx->LfnBuf)); continue; }
memset(out, 0, sizeof(*out));
if (ctx->HasLfn && ctx->LfnBuf[0]) {
size_t len = strlen(ctx->LfnBuf);
if (len >= KDIR_NAME_MAX) len = KDIR_NAME_MAX - 1;
@@ -1513,7 +1747,11 @@ KSTATUS Fat32ReadDir(IO_FILESYSTEM *fs, void *dirCtx, KDIR_ENTRY *out) {
memcpy(out->Name, name83, len); out->Name[len] = '\0';
}
out->Attr = e->Attr; out->FileSize = e->FileSize;
out->Attr = e->Attr;
out->FileSize = e->FileSize;
FatDecodeDateTime(e->CrtDate, e->CrtTime, e->CrtTimeTenth, &out->CreateTime);
FatDecodeAccessDate(e->LastAccDate, &out->LastAccessTime);
FatDecodeDateTime(e->WrtDate, e->WrtTime, 0, &out->LastWriteTime);
ctx->HasLfn = 0; memset(ctx->LfnBuf, 0, sizeof(ctx->LfnBuf));
return STATUS_SUCCESS;
}
@@ -1993,10 +2231,18 @@ static void Fat32AsyncWriteIrpComplete(IRP* irp, void* completeCtx) {
static void Fat32AsyncWriteSubmitNext(FAT32_ASYNC_RW* ctx) {
if (ctx->Remaining == 0) {
uint64_t newEnd = ctx->Offset + ctx->Done;
if (newEnd > ctx->File->FileSize) {
ctx->File->FileSize = (uint32_t)newEnd;
DirentUpdateSize(ctx->Vol, ctx->File->DirentLba,
ctx->File->DirentOffset, ctx->File->FileSize);
if (ctx->Done > 0) {
FAT32_DIRENT_UPDATE u;
memset(&u, 0, sizeof(u));
FatQueryEncodedTime(&u.Timestamp);
u.Flags = FAT32_DIRENT_UPDATE_WRITE_TIME;
if (newEnd > ctx->File->FileSize) {
ctx->File->FileSize = (uint32_t)newEnd;
u.Flags |= FAT32_DIRENT_UPDATE_SIZE;
u.Size = ctx->File->FileSize;
}
DirentUpdateMetadata(ctx->Vol, ctx->File->DirentLba,
ctx->File->DirentOffset, &u);
}
Fat32AsyncFinish(ctx, STATUS_SUCCESS, ctx->Done);
return;
+1
View File
@@ -14,6 +14,7 @@
#include <kernel/ke_semaphore.h>
#include <kernel/ke_thread.h>
#include <kernel/ke_sched.h>
#include <kernel/ke_time.h>
typedef struct FAT32_VOL_S FAT32_VOL;
+276 -50
View File
@@ -2,8 +2,19 @@
#include "rtl8139.h"
const HAL_API* gHal;
RTL8139_DEVICE gDevice;
bool gDevicePresent = false;
static DRIVER_OBJECT gRtl8139Driver;
static RTL8139_DEVICE* gRtl8139Devices[RTL8139_MAX_DEVICES];
static uint32_t gRtl8139DeviceCount;
static SPINLOCK gRtl8139RegistryLock = SPINLOCK_INIT;
static const IO_DEVICE_ID gRtl8139Ids[] = {
{ IoBusPci, { "PCI\\VEN_10EC&DEV_8139", sizeof("PCI\\VEN_10EC&DEV_8139") - 1 } },
};
static const IO_DRIVER_ID_TABLE gRtl8139IdTable = {
gRtl8139Ids,
sizeof(gRtl8139Ids) / sizeof(gRtl8139Ids[0]),
};
static __forceinline void Rtl8139MemoryBarrier(void) {
_ReadWriteBarrier();
@@ -201,19 +212,68 @@ static void Rtl8139DpcRoutine(KDPC* dpc, void* context, void* systemArgument1, v
DbgPrint("RTL8139: interrupt error bits=0x%04x\n", status);
}
static KSTATUS Rtl8139RegisterDevice(RTL8139_DEVICE* r) {
if (!r) return STATUS_INVALID_PARAMETER;
uintptr_t flags = KeAcquireSpinlock(&gRtl8139RegistryLock);
for (uint32_t i = 0; i < gRtl8139DeviceCount; i++) {
if (gRtl8139Devices[i] == r) {
KeReleaseSpinlock(&gRtl8139RegistryLock, flags);
return STATUS_SUCCESS;
}
}
if (gRtl8139DeviceCount >= RTL8139_MAX_DEVICES) {
KeReleaseSpinlock(&gRtl8139RegistryLock, flags);
return STATUS_NO_MEMORY;
}
gRtl8139Devices[gRtl8139DeviceCount++] = r;
KeReleaseSpinlock(&gRtl8139RegistryLock, flags);
return STATUS_SUCCESS;
}
static void Rtl8139UnregisterDevice(RTL8139_DEVICE* r) {
if (!r) return;
uintptr_t flags = KeAcquireSpinlock(&gRtl8139RegistryLock);
for (uint32_t i = 0; i < gRtl8139DeviceCount; i++) {
if (gRtl8139Devices[i] == r) {
gRtl8139Devices[i] = gRtl8139Devices[gRtl8139DeviceCount - 1u];
gRtl8139Devices[gRtl8139DeviceCount - 1u] = (RTL8139_DEVICE*)0;
gRtl8139DeviceCount--;
break;
}
}
KeReleaseSpinlock(&gRtl8139RegistryLock, flags);
}
static void Rtl8139Isr(void) {
if (!gDevicePresent)
return;
RTL8139_DEVICE* snapshot[RTL8139_MAX_DEVICES];
uint32_t count = 0;
RTL8139_DEVICE* r = &gDevice;
uint16_t status = Rtl8139Read16(r, RTL8139_REG_ISR);
status &= RTL8139_INTR_ENABLES;
if (!status || status == 0xFFFFu)
return;
uintptr_t flags = KeAcquireSpinlock(&gRtl8139RegistryLock);
count = gRtl8139DeviceCount;
if (count > RTL8139_MAX_DEVICES)
count = RTL8139_MAX_DEVICES;
for (uint32_t i = 0; i < count; i++)
snapshot[i] = gRtl8139Devices[i];
KeReleaseSpinlock(&gRtl8139RegistryLock, flags);
Rtl8139Write16(r, RTL8139_REG_ISR, status);
r->PendingIsr |= status;
KeInsertQueueDpc(&r->Dpc, (void*)0, (void*)0);
for (uint32_t i = 0; i < count; i++) {
RTL8139_DEVICE* r = snapshot[i];
if (!r || !r->Started || r->Removed || r->SurpriseRemoved)
continue;
uint16_t status = Rtl8139Read16(r, RTL8139_REG_ISR);
status &= RTL8139_INTR_ENABLES;
if (!status || status == 0xFFFFu)
continue;
Rtl8139Write16(r, RTL8139_REG_ISR, status);
r->PendingIsr |= status;
KeInsertQueueDpc(&r->Dpc, (void*)0, (void*)0);
}
}
static KSTATUS Rtl8139SendFrame(NET_DEVICE* device, const void* frame, size_t length) {
@@ -252,76 +312,242 @@ static KSTATUS Rtl8139SendFrame(NET_DEVICE* device, const void* frame, size_t le
return STATUS_SUCCESS;
}
static KSTATUS Rtl8139Initialize(const HAL_API* hal) {
gHal = hal;
memset(&gDevice, 0, sizeof(gDevice));
gDevice.TxLock = (SPINLOCK)SPINLOCK_INIT;
static KSTATUS Rtl8139SelectRegisterBar(RTL8139_DEVICE* r) {
if (!r || !r->Pdo)
return STATUS_INVALID_PARAMETER;
if (!gHal->Pci.FindDevice(RTL8139_VENDOR_REALTEK, RTL8139_DEVICE_ID,
&gDevice.Bus, &gDevice.Dev, &gDevice.Fn)) {
DbgPrint("RTL8139: no PCI device 10ec:8139 found; skipping\n");
PCI_BAR best;
PCI_BAR fallback;
memset(&best, 0, sizeof(best));
memset(&fallback, 0, sizeof(fallback));
best.Type = PciBarUnused;
fallback.Type = PciBarUnused;
for (uint8_t i = 0; i < 6; i++) {
PCI_BAR bar;
KSTATUS st = PciGetBar(r->Pdo, i, &bar);
if (!KSUCCESS(st))
continue;
if (bar.Type == PciBarMemory32 || bar.Type == PciBarMemory64) {
best = bar;
break;
}
if (bar.Type == PciBarIo && fallback.Type == PciBarUnused)
fallback = bar;
}
if (best.Type == PciBarUnused)
best = fallback;
if (best.Type == PciBarMemory32 || best.Type == PciBarMemory64) {
KSTATUS st = PciMapBar(r->Pdo,
best.Index,
PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE,
(void**)&r->Mmio,
&r->RegisterBytes);
if (!KSUCCESS(st))
return st;
r->RegisterBar = best;
r->UseMmio = true;
DbgPrint("RTL8139: BAR%u MMIO base=0x%016llX len=0x%llx virt=0x%016llX\n",
best.Index,
(unsigned long long)best.Base,
(unsigned long long)r->RegisterBytes,
r->Mmio);
return STATUS_SUCCESS;
}
DbgPrint("RTL8139: found Realtek PCI device 10ec:8139 at %u:%u.%u\n",
gDevice.Bus, gDevice.Dev, gDevice.Fn);
if (best.Type == PciBarIo) {
if (best.Base > 0xFFFFu)
return STATUS_NOT_SUPPORTED;
KSTATUS st = Rtl8139SelectRegisterBar(&gDevice);
r->RegisterBar = best;
r->RegisterBytes = best.Length;
r->IoBase = (uint16_t)best.Base;
r->UseMmio = false;
DbgPrint("RTL8139: BAR%u I/O base=0x%04x len=0x%llx\n",
best.Index,
r->IoBase,
(unsigned long long)r->RegisterBytes);
return STATUS_SUCCESS;
}
return STATUS_NOT_FOUND;
}
static KSTATUS Rtl8139StartDevice(RTL8139_DEVICE* r) {
if (!r || r->Magic != RTL8139_DEVICE_MAGIC || !r->Pdo || !r->Fdo)
return STATUS_INVALID_PARAMETER;
if (r->Started)
return STATUS_SUCCESS;
KSTATUS st = PciGetDeviceInfo(r->Pdo, &r->PciInfo);
if (!KSUCCESS(st)) {
DbgPrint("RTL8139: PciGetDeviceInfo failed: 0x%x\n", st);
return st;
}
DbgPrint("RTL8139: controller at PCI %02x:%02x.%x vendor=%04x device=%04x\n",
r->PciInfo.Address.Bus,
r->PciInfo.Address.Device,
r->PciInfo.Address.Function,
r->PciInfo.VendorId,
r->PciInfo.DeviceId);
st = Rtl8139SelectRegisterBar(r);
if (!KSUCCESS(st)) {
DbgPrint("RTL8139: no usable register BAR (0x%x)\n", st);
return st;
}
Rtl8139EnablePci(&gDevice);
gDevice.Irq = hal->Pci.GetIrqLine(gDevice.Bus, gDevice.Dev, gDevice.Fn);
if (gDevice.Irq == 0xFFu) {
DbgPrint("RTL8139: no legacy PCI IRQ line wired\n");
return STATUS_NOT_SUPPORTED;
st = PciEnableDevice(r->Pdo,
PCI_ENABLE_BUSMASTER |
PCI_ENABLE_INTX |
(r->UseMmio ? PCI_ENABLE_MEMORY : PCI_ENABLE_IO));
if (!KSUCCESS(st)) {
DbgPrint("RTL8139: PciEnableDevice failed: 0x%x\n", st);
return st;
}
// Wake, reset, then read the station address.
Rtl8139Write8(&gDevice, RTL8139_REG_CONFIG1, 0x00);
st = Rtl8139Reset(&gDevice);
st = PciGetInterrupt(r->Pdo, &r->Irq);
if (!KSUCCESS(st) || r->Irq == 0xFFu) {
DbgPrint("RTL8139: no legacy PCI IRQ line wired\n");
return KSUCCESS(st) ? STATUS_NOT_SUPPORTED : st;
}
Rtl8139Write8(r, RTL8139_REG_CONFIG1, 0x00);
st = Rtl8139Reset(r);
if (!KSUCCESS(st)) {
DbgPrint("RTL8139: reset timed out\n");
return st;
}
Rtl8139ReadMac(&gDevice);
Rtl8139ReadMac(r);
DbgPrint("RTL8139: MAC %02x:%02x:%02x:%02x:%02x:%02x irq=%u\n",
gDevice.NetDevice.HardwareAddress[0], gDevice.NetDevice.HardwareAddress[1],
gDevice.NetDevice.HardwareAddress[2], gDevice.NetDevice.HardwareAddress[3],
gDevice.NetDevice.HardwareAddress[4], gDevice.NetDevice.HardwareAddress[5],
gDevice.Irq);
r->NetDevice.HardwareAddress[0], r->NetDevice.HardwareAddress[1],
r->NetDevice.HardwareAddress[2], r->NetDevice.HardwareAddress[3],
r->NetDevice.HardwareAddress[4], r->NetDevice.HardwareAddress[5],
r->Irq);
st = Rtl8139AllocateDma(&gDevice);
st = Rtl8139AllocateDma(r);
if (!KSUCCESS(st))
return st;
KeInitializeDpc(&gDevice.Dpc, Rtl8139DpcRoutine, &gDevice);
KeInitializeDpc(&r->Dpc, Rtl8139DpcRoutine, r);
gDevice.NetDevice.Mtu = NET_MTU_ETHERNET;
gDevice.NetDevice.DriverContext = &gDevice;
gDevice.NetDevice.SendFrame = Rtl8139SendFrame;
r->NetDevice.Mtu = NET_MTU_ETHERNET;
r->NetDevice.DriverContext = r;
r->NetDevice.SendFrame = Rtl8139SendFrame;
Rtl8139ProgramHardware(&gDevice);
gDevicePresent = true;
KeIrqRegister(gDevice.Irq, Rtl8139Isr);
st = NetRegisterDevice(&gDevice.NetDevice);
st = Rtl8139RegisterDevice(r);
if (!KSUCCESS(st))
return st;
DbgPrint("RTL8139: initialized; RX/TX enabled on IRQ %u\n", gDevice.Irq);
KeIrqRegister(r->Irq, Rtl8139Isr);
r->Started = true;
Rtl8139ProgramHardware(r);
r->Fdo->PnpState = DevicePnpStarted;
st = NetRegisterDevice(&r->NetDevice);
if (!KSUCCESS(st)) {
Rtl8139Write16(r, RTL8139_REG_IMR, 0);
r->Started = false;
Rtl8139UnregisterDevice(r);
return st;
}
DbgPrint("RTL8139: initialized; RX/TX enabled on IRQ %u\n", r->Irq);
return STATUS_SUCCESS;
}
KSTATUS Rtl8139DispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
if (!device || !irp || !device->DeviceExtension)
return STATUS_INVALID_PARAMETER;
RTL8139_DEVICE* r = (RTL8139_DEVICE*)device->DeviceExtension;
if (r->Magic != RTL8139_DEVICE_MAGIC)
return STATUS_INVALID_PARAMETER;
IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp);
if (!ios || ios->MajorFunction != IRP_MJ_PNP)
return STATUS_INVALID_PARAMETER;
switch ((IO_PNP_MINOR_FUNCTION)ios->MinorFunction) {
case IRP_MN_START_DEVICE:
return Rtl8139StartDevice(r);
case IRP_MN_QUERY_REMOVE_DEVICE:
case IRP_MN_CANCEL_REMOVE_DEVICE:
return STATUS_SUCCESS;
case IRP_MN_SURPRISE_REMOVAL:
r->SurpriseRemoved = true;
device->PnpState = DevicePnpSurpriseRemoved;
if (r->Started)
Rtl8139Write16(r, RTL8139_REG_IMR, 0);
return STATUS_SUCCESS;
case IRP_MN_REMOVE_DEVICE:
r->Removed = true;
device->PnpState = DevicePnpDeleted;
if (r->Started)
Rtl8139Write16(r, RTL8139_REG_IMR, 0);
Rtl8139UnregisterDevice(r);
IoDetachDevice(device);
IoDeleteDevice(device);
return STATUS_SUCCESS;
default:
return IoForwardIrpToLower(device, irp);
}
}
KSTATUS Rtl8139AddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject) {
if (!driver || !physicalDeviceObject || physicalDeviceObject->Type != DeviceObjectPhysical)
return STATUS_INVALID_PARAMETER;
DEVICE_OBJECT* fdo = (DEVICE_OBJECT*)0;
KSTATUS st = IoCreateDevice(driver, sizeof(RTL8139_DEVICE), &fdo);
if (!KSUCCESS(st))
return st;
(void)IoSetDeviceName(fdo, SYS_SV_LITERAL("rtl8139"));
RTL8139_DEVICE* r = (RTL8139_DEVICE*)fdo->DeviceExtension;
memset(r, 0, sizeof(*r));
r->Magic = RTL8139_DEVICE_MAGIC;
r->Fdo = fdo;
r->Pdo = physicalDeviceObject;
r->Irq = 0xFFu;
r->TxLock = (SPINLOCK)SPINLOCK_INIT;
r->LowerDevice = IoAttachDeviceToDeviceStack(fdo, physicalDeviceObject);
if (!r->LowerDevice) {
IoDeleteDevice(fdo);
return STATUS_FAILURE;
}
DbgPrint("RTL8139: FDO attached to %v\n", physicalDeviceObject->Name);
return STATUS_SUCCESS;
}
KSTATUS DriverEntry(const HAL_API* hal) {
if (!hal)
return STATUS_INVALID_PARAMETER;
DbgPrint("RTL8139: driver loading...\n");
KSTATUS st = Rtl8139Initialize(hal);
gHal = hal;
gRtl8139DeviceCount = 0;
memset(&gRtl8139Driver, 0, sizeof(gRtl8139Driver));
IoInitializeDriverObject(&gRtl8139Driver, SYS_SV_LITERAL("rtl8139"));
gRtl8139Driver.IdTable = &gRtl8139IdTable;
gRtl8139Driver.DriverAddDevice = Rtl8139AddDevice;
gRtl8139Driver.MajorFunction[IRP_MJ_PNP] = Rtl8139DispatchPnp;
KSTATUS st = IoRegisterDriverObject(&gRtl8139Driver);
if (!KSUCCESS(st))
DbgPrint("RTL8139: init failed: 0x%x\n", st);
DbgPrint("RTL8139: IoRegisterDriverObject failed: 0x%x\n", st);
return st;
}
+20 -29
View File
@@ -8,6 +8,8 @@
#include <intrin.h>
#include <ntlite/ddi/hal_api.h>
#include <ntlite/ddi/io_driver.h>
#include <ntlite/ddi/pci.h>
#include <ntlite/status.h>
#include <kernel/ke_debug.h>
#include <kernel/ke_irq.h>
@@ -20,12 +22,7 @@
#define RTL8139_VENDOR_REALTEK 0x10ECu
#define RTL8139_DEVICE_ID 0x8139u
#define PCI_REG_COMMAND 0x04u
#define PCI_REG_BAR0 0x10u
#define PCI_COMMAND_IO (1u << 0)
#define PCI_COMMAND_MEMORY (1u << 1)
#define PCI_COMMAND_MASTER (1u << 2)
#define PCI_COMMAND_INTX_DISABLE (1u << 10)
#define RTL8139_DEVICE_MAGIC 0x81390001u
#define RTL8139_REG_IDR0 0x00u
#define RTL8139_REG_MAR0 0x08u
@@ -91,30 +88,29 @@
#define RTL8139_RX_ALLOC_SIZE (RTL8139_RX_RING_SIZE + RTL8139_RX_PAD + RTL8139_RX_GUARD)
#define RTL8139_RX_PAGES ((RTL8139_RX_ALLOC_SIZE + 4095u) / 4096u)
#define RTL8139_TX_PAGES (((RTL8139_TX_SLOTS * RTL8139_TX_BUF_SIZE) + 4095u) / 4096u)
#define RTL8139_MMIO_PAGES 1u
#define RTL8139_RX_WIRE_MAX (NET_ETHERNET_MAX_FRAME + 4u)
#define RTL8139_RX_WIRE_MAX (NET_ETHERNET_MAX_FRAME + 4u)
#define RTL8139_REG_TSD(slot) (RTL8139_REG_TSD0 + ((slot) * 4u))
#define RTL8139_REG_TSAD(slot) (RTL8139_REG_TSAD0 + ((slot) * 4u))
typedef enum RTL8139_BAR_KIND {
Rtl8139BarNone = 0,
Rtl8139BarIo,
Rtl8139BarMmio,
} RTL8139_BAR_KIND;
typedef struct RTL8139_BAR {
RTL8139_BAR_KIND Kind;
uint64_t Base;
uint8_t Index;
} RTL8139_BAR;
#define RTL8139_MAX_DEVICES 8u
typedef struct RTL8139_DEVICE {
uint8_t Bus;
uint8_t Dev;
uint8_t Fn;
uint32_t Magic;
DEVICE_OBJECT* Fdo;
DEVICE_OBJECT* Pdo;
DEVICE_OBJECT* LowerDevice;
PCI_DEVICE_INFO PciInfo;
PCI_BAR RegisterBar;
uint64_t RegisterBytes;
uint8_t Irq;
bool Started;
bool Removed;
bool SurpriseRemoved;
bool UseMmio;
volatile uint8_t* Mmio;
uint16_t IoBase;
@@ -134,12 +130,7 @@ typedef struct RTL8139_DEVICE {
NET_DEVICE NetDevice;
} RTL8139_DEVICE;
extern const HAL_API* gHal;
extern RTL8139_DEVICE gDevice;
extern bool gDevicePresent;
bool Rtl8139ReadBar(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t index, RTL8139_BAR* out);
KSTATUS Rtl8139MapMmio(uint64_t physBase, volatile uint8_t** outMmio);
KSTATUS Rtl8139SelectRegisterBar(RTL8139_DEVICE* r);
void Rtl8139EnablePci(RTL8139_DEVICE* r);
KSTATUS Rtl8139AddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject);
KSTATUS Rtl8139DispatchPnp(DEVICE_OBJECT* device, IRP* irp);
+2 -112
View File
@@ -1,112 +1,2 @@
// rtl8139_pci.c - PCI BAR/MMIO front-end for rtl8139.
#include "rtl8139.h"
bool Rtl8139ReadBar(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t index, RTL8139_BAR* out) {
uint8_t reg = (uint8_t)(PCI_REG_BAR0 + index * 4u);
uint32_t lo = gHal->Pci.Read32(bus, dev, fn, reg);
out->Kind = Rtl8139BarNone;
out->Base = 0;
out->Index = index;
if (lo == 0 || lo == 0xFFFFFFFFu)
return false;
if (lo & 1u) {
uint32_t base = lo & ~0x3u;
if (!base) return false;
out->Kind = Rtl8139BarIo;
out->Base = base;
return true;
}
uint64_t base = (uint64_t)(lo & ~0xFu);
uint32_t type = (lo >> 1) & 0x3u;
if (type == 0x2u && index < 5) {
uint32_t hi = gHal->Pci.Read32(bus, dev, fn, (uint8_t)(reg + 4u));
base |= ((uint64_t)hi << 32);
}
if (!base) return false;
out->Kind = Rtl8139BarMmio;
out->Base = base;
return true;
}
KSTATUS Rtl8139MapMmio(uint64_t physBase, volatile uint8_t** outMmio) {
uint64_t pageBase = physBase & PAGE_MASK;
uint64_t pageOff = physBase - pageBase;
for (uint32_t i = 0; i < RTL8139_MMIO_PAGES; i++) {
PHYSICAL_ADDRESS phys = pageBase + (uint64_t)i * PAGE_SIZE;
uintptr_t virt = (uintptr_t)gHal->PhysToVirt(phys);
KSTATUS st = MmMapPage(virt, phys, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE, MmMapFailIfPresent);
if (st == STATUS_ALREADY_EXISTS)
st = MmSetPageProtect(virt, PAGE_SIZE, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE);
if (!KSUCCESS(st))
return st;
}
*outMmio = (volatile uint8_t*)((uintptr_t)gHal->PhysToVirt(pageBase) + pageOff);
return STATUS_SUCCESS;
}
KSTATUS Rtl8139SelectRegisterBar(RTL8139_DEVICE* r) {
RTL8139_BAR best = { Rtl8139BarNone, 0, 0 };
RTL8139_BAR fallback = { Rtl8139BarNone, 0, 0 };
for (uint8_t i = 0; i < 6; i++) {
RTL8139_BAR bar;
if (!Rtl8139ReadBar(r->Bus, r->Dev, r->Fn, i, &bar))
continue;
if (bar.Kind == Rtl8139BarMmio) {
best = bar;
break;
}
if (bar.Kind == Rtl8139BarIo && fallback.Kind == Rtl8139BarNone)
fallback = bar;
if (bar.Kind == Rtl8139BarMmio) {
uint32_t lo = gHal->Pci.Read32(r->Bus, r->Dev, r->Fn, (uint8_t)(PCI_REG_BAR0 + i * 4u));
if (((lo >> 1) & 0x3u) == 0x2u)
i++;
}
}
if (best.Kind == Rtl8139BarNone)
best = fallback;
if (best.Kind == Rtl8139BarMmio) {
KSTATUS st = Rtl8139MapMmio(best.Base, &r->Mmio);
if (!KSUCCESS(st))
return st;
r->UseMmio = true;
DbgPrint("RTL8139: BAR%u MMIO base=0x%016llX virt=0x%016llX\n",
best.Index, (unsigned long long)best.Base, r->Mmio);
return STATUS_SUCCESS;
}
if (best.Kind == Rtl8139BarIo) {
if (best.Base > 0xFFFFu)
return STATUS_NOT_SUPPORTED;
r->IoBase = (uint16_t)best.Base;
r->UseMmio = false;
DbgPrint("RTL8139: BAR%u I/O base=0x%04x\n", best.Index, r->IoBase);
return STATUS_SUCCESS;
}
return STATUS_NOT_FOUND;
}
void Rtl8139EnablePci(RTL8139_DEVICE* r) {
uint32_t cmd = gHal->Pci.Read32(r->Bus, r->Dev, r->Fn, PCI_REG_COMMAND);
cmd |= PCI_COMMAND_MASTER;
if (r->UseMmio) cmd |= PCI_COMMAND_MEMORY;
else cmd |= PCI_COMMAND_IO;
cmd &= ~PCI_COMMAND_INTX_DISABLE;
gHal->Pci.Write32(r->Bus, r->Dev, r->Fn, PCI_REG_COMMAND, cmd);
}
// rtl8139_pci.c intentionally empty.
// PCI enumeration, BAR decoding, mapping, and command-register policy now live in pci.sys.
+283 -64
View File
@@ -1,9 +1,21 @@
// rtl816x.c - packet I/O, interrupt, and DriverEntry path.
// rtl816x.c - rtl8168/rtl8169 packet I/O, interrupt, and PCI function-driver path.
#include "rtl816x.h"
const HAL_API* gHal;
RTL816X_DEVICE gDevice;
bool gDevicePresent = false;
static DRIVER_OBJECT gRtl816xDriver;
static RTL816X_DEVICE* gRtl816xDevices[RTL816X_MAX_DEVICES];
static uint32_t gRtl816xDeviceCount;
static SPINLOCK gRtl816xRegistryLock = SPINLOCK_INIT;
static const IO_DEVICE_ID gRtl816xIds[] = {
{ IoBusPci, { "PCI\\VEN_10EC&DEV_8168", sizeof("PCI\\VEN_10EC&DEV_8168") - 1 } },
{ IoBusPci, { "PCI\\VEN_10EC&DEV_8169", sizeof("PCI\\VEN_10EC&DEV_8169") - 1 } },
};
static const IO_DRIVER_ID_TABLE gRtl816xIdTable = {
gRtl816xIds,
sizeof(gRtl816xIds) / sizeof(gRtl816xIds[0]),
};
static __forceinline void RtlMemoryBarrier(void) {
_ReadWriteBarrier();
@@ -44,11 +56,6 @@ static void RtlWrite32(RTL816X_DEVICE* r, uint32_t reg, uint32_t value) {
else __outdword((unsigned short)(r->IoBase + reg), value);
}
static KSTATUS RtlReset(RTL816X_DEVICE* r) {
RtlWrite8(r, RTL_REG_CHIP_CMD, RTL_CMD_RESET);
for (uint32_t i = 0; i < 1000; i++) {
@@ -116,14 +123,11 @@ static void RtlInitDescriptors(RTL816X_DEVICE* r) {
}
static void RtlProgramHardware(RTL816X_DEVICE* r) {
// Unlock the 9346-style config register window while programming.
RtlWrite8(r, RTL_REG_CFG9346, 0xC0);
// Disable interrupts while rings are being installed.
RtlWrite16(r, RTL_REG_INTR_MASK, 0);
RtlWrite16(r, RTL_REG_INTR_STAT, 0xFFFFu);
// DMA ring base addresses.
RtlWrite32(r, RTL_REG_TNPDS_LO, (uint32_t)r->TxRingPhys);
RtlWrite32(r, RTL_REG_TNPDS_HI, (uint32_t)(r->TxRingPhys >> 32));
RtlWrite32(r, RTL_REG_THPDS_LO, (uint32_t)r->TxRingPhys);
@@ -131,7 +135,6 @@ static void RtlProgramHardware(RTL816X_DEVICE* r) {
RtlWrite32(r, RTL_REG_RDSAR_LO, (uint32_t)r->RxRingPhys);
RtlWrite32(r, RTL_REG_RDSAR_HI, (uint32_t)(r->RxRingPhys >> 32));
// Plain packet mode. Keep C+ features mostly disabled for bring-up.
RtlWrite16(r, RTL_REG_CPLUS_CMD, RtlRead16(r, RTL_REG_CPLUS_CMD) & ~(uint16_t)0x00E0u);
RtlWrite16(r, RTL_REG_RX_MAXSIZE, RTL_BUF_SIZE);
@@ -141,10 +144,8 @@ static void RtlProgramHardware(RTL816X_DEVICE* r) {
RtlMemoryBarrier();
// Enable receiver and transmitter.
RtlWrite8(r, RTL_REG_CHIP_CMD, RTL_CMD_RX_ENABLE | RTL_CMD_TX_ENABLE);
// Lock config space again and enable selected interrupts.
RtlWrite8(r, RTL_REG_CFG9346, 0x00);
RtlWrite16(r, RTL_REG_INTR_STAT, 0xFFFFu);
RtlWrite16(r, RTL_REG_INTR_MASK, RTL_INTR_ENABLES);
@@ -164,8 +165,6 @@ static void RtlHandleRx(RTL816X_DEVICE* r) {
len >= NET_ETHERNET_HEADER_LENGTH && len <= RTL_RX_WIRE_MAX;
if (good) {
// Realtek reports the Ethernet FCS in the descriptor length on many
// chips. Remove it when present so upper layers see a normal frame.
if (len > 4)
len -= 4;
if (len > NET_ETHERNET_MAX_FRAME) {
@@ -195,6 +194,9 @@ static void RtlDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void*
(void)systemArgument2;
RTL816X_DEVICE* r = (RTL816X_DEVICE*)context;
if (!r || r->Removed || r->SurpriseRemoved)
return;
uint16_t status = (uint16_t)r->PendingIsr;
r->PendingIsr = 0;
@@ -210,18 +212,68 @@ static void RtlDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void*
DbgPrint("RTL816X: interrupt status error bits=0x%04x\n", status);
}
static KSTATUS RtlRegisterDevice(RTL816X_DEVICE* r) {
if (!r) return STATUS_INVALID_PARAMETER;
uintptr_t flags = KeAcquireSpinlock(&gRtl816xRegistryLock);
for (uint32_t i = 0; i < gRtl816xDeviceCount; i++) {
if (gRtl816xDevices[i] == r) {
KeReleaseSpinlock(&gRtl816xRegistryLock, flags);
return STATUS_SUCCESS;
}
}
if (gRtl816xDeviceCount >= RTL816X_MAX_DEVICES) {
KeReleaseSpinlock(&gRtl816xRegistryLock, flags);
return STATUS_NO_MEMORY;
}
gRtl816xDevices[gRtl816xDeviceCount++] = r;
KeReleaseSpinlock(&gRtl816xRegistryLock, flags);
return STATUS_SUCCESS;
}
static void RtlUnregisterDevice(RTL816X_DEVICE* r) {
if (!r) return;
uintptr_t flags = KeAcquireSpinlock(&gRtl816xRegistryLock);
for (uint32_t i = 0; i < gRtl816xDeviceCount; i++) {
if (gRtl816xDevices[i] == r) {
gRtl816xDevices[i] = gRtl816xDevices[gRtl816xDeviceCount - 1u];
gRtl816xDevices[gRtl816xDeviceCount - 1u] = (RTL816X_DEVICE*)0;
gRtl816xDeviceCount--;
break;
}
}
KeReleaseSpinlock(&gRtl816xRegistryLock, flags);
}
static void RtlIsr(void) {
if (!gDevicePresent)
return;
RTL816X_DEVICE* snapshot[RTL816X_MAX_DEVICES];
uint32_t count = 0;
RTL816X_DEVICE* r = &gDevice;
uint16_t status = RtlRead16(r, RTL_REG_INTR_STAT);
if (!status || status == 0xFFFFu)
return;
uintptr_t flags = KeAcquireSpinlock(&gRtl816xRegistryLock);
count = gRtl816xDeviceCount;
if (count > RTL816X_MAX_DEVICES)
count = RTL816X_MAX_DEVICES;
for (uint32_t i = 0; i < count; i++)
snapshot[i] = gRtl816xDevices[i];
KeReleaseSpinlock(&gRtl816xRegistryLock, flags);
RtlWrite16(r, RTL_REG_INTR_STAT, status);
r->PendingIsr |= status;
KeInsertQueueDpc(&r->Dpc, (void*)0, (void*)0);
for (uint32_t i = 0; i < count; i++) {
RTL816X_DEVICE* r = snapshot[i];
if (!r || !r->Started || r->Removed || r->SurpriseRemoved)
continue;
uint16_t status = RtlRead16(r, RTL_REG_INTR_STAT);
status &= RTL_INTR_ENABLES;
if (!status || status == 0xFFFFu)
continue;
RtlWrite16(r, RTL_REG_INTR_STAT, status);
r->PendingIsr |= status;
KeInsertQueueDpc(&r->Dpc, (void*)0, (void*)0);
}
}
static KSTATUS RtlSendFrame(NET_DEVICE* device, const void* frame, size_t length) {
@@ -233,8 +285,8 @@ static KSTATUS RtlSendFrame(NET_DEVICE* device, const void* frame, size_t length
return STATUS_INVALID_PARAMETER;
RTL816X_DEVICE* r = (RTL816X_DEVICE*)device->DriverContext;
if (!r)
return STATUS_INVALID_PARAMETER;
if (!r || !r->Started || r->Removed || r->SurpriseRemoved)
return STATUS_OBJECT_DEAD;
uintptr_t flags = KeAcquireSpinlock(&r->TxLock);
uint32_t index = r->TxIndex;
@@ -263,75 +315,242 @@ static KSTATUS RtlSendFrame(NET_DEVICE* device, const void* frame, size_t length
return STATUS_SUCCESS;
}
static KSTATUS RtlInitialize(const HAL_API* hal) {
gHal = hal;
memset(&gDevice, 0, sizeof(gDevice));
gDevice.TxLock = (SPINLOCK)SPINLOCK_INIT;
static KSTATUS RtlSelectRegisterBar(RTL816X_DEVICE* r) {
if (!r || !r->Pdo)
return STATUS_INVALID_PARAMETER;
uint16_t deviceId;
if (!RtlFindDevice(&gDevice.Bus, &gDevice.Dev, &gDevice.Fn, &deviceId)) {
DbgPrint("RTL816X: no RTL8168/RTL8169 PCI device found; skipping\n");
PCI_BAR best;
PCI_BAR fallback;
memset(&best, 0, sizeof(best));
memset(&fallback, 0, sizeof(fallback));
best.Type = PciBarUnused;
fallback.Type = PciBarUnused;
for (uint8_t i = 0; i < 6; i++) {
PCI_BAR bar;
KSTATUS st = PciGetBar(r->Pdo, i, &bar);
if (!KSUCCESS(st))
continue;
if (bar.Type == PciBarMemory32 || bar.Type == PciBarMemory64) {
best = bar;
break;
}
if (bar.Type == PciBarIo && fallback.Type == PciBarUnused)
fallback = bar;
}
if (best.Type == PciBarUnused)
best = fallback;
if (best.Type == PciBarMemory32 || best.Type == PciBarMemory64) {
KSTATUS st = PciMapBar(r->Pdo,
best.Index,
PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE,
(void**)&r->Mmio,
&r->RegisterBytes);
if (!KSUCCESS(st))
return st;
r->RegisterBar = best;
r->UseMmio = true;
DbgPrint("RTL816X: BAR%u MMIO base=0x%016llX len=0x%llx virt=0x%016llX\n",
best.Index,
(unsigned long long)best.Base,
(unsigned long long)r->RegisterBytes,
r->Mmio);
return STATUS_SUCCESS;
}
gDevice.DeviceId = deviceId;
DbgPrint("RTL816X: found Realtek PCI device 10ec:%04x at %u:%u.%u\n",
deviceId, gDevice.Bus, gDevice.Dev, gDevice.Fn);
if (best.Type == PciBarIo) {
if (best.Base > 0xFFFFu)
return STATUS_NOT_SUPPORTED;
KSTATUS st = RtlSelectRegisterBar(&gDevice);
r->RegisterBar = best;
r->RegisterBytes = best.Length;
r->IoBase = (uint16_t)best.Base;
r->UseMmio = false;
DbgPrint("RTL816X: BAR%u I/O base=0x%04x len=0x%llx\n",
best.Index,
r->IoBase,
(unsigned long long)r->RegisterBytes);
return STATUS_SUCCESS;
}
return STATUS_NOT_FOUND;
}
static KSTATUS RtlStartDevice(RTL816X_DEVICE* r) {
if (!r || r->Magic != RTL816X_DEVICE_MAGIC || !r->Pdo || !r->Fdo)
return STATUS_INVALID_PARAMETER;
if (r->Started)
return STATUS_SUCCESS;
KSTATUS st = PciGetDeviceInfo(r->Pdo, &r->PciInfo);
if (!KSUCCESS(st)) {
DbgPrint("RTL816X: PciGetDeviceInfo failed: 0x%x\n", st);
return st;
}
DbgPrint("RTL816X: controller at PCI %02x:%02x.%x vendor=%04x device=%04x\n",
r->PciInfo.Address.Bus,
r->PciInfo.Address.Device,
r->PciInfo.Address.Function,
r->PciInfo.VendorId,
r->PciInfo.DeviceId);
st = RtlSelectRegisterBar(r);
if (!KSUCCESS(st)) {
DbgPrint("RTL816X: no usable register BAR (0x%x)\n", st);
return st;
}
RtlEnablePci(&gDevice);
gDevice.Irq = hal->Pci.GetIrqLine(gDevice.Bus, gDevice.Dev, gDevice.Fn);
if (gDevice.Irq == 0xFFu) {
DbgPrint("RTL816X: no legacy PCI IRQ line wired\n");
return STATUS_NOT_SUPPORTED;
st = PciEnableDevice(r->Pdo,
PCI_ENABLE_BUSMASTER |
PCI_ENABLE_INTX |
(r->UseMmio ? PCI_ENABLE_MEMORY : PCI_ENABLE_IO));
if (!KSUCCESS(st)) {
DbgPrint("RTL816X: PciEnableDevice failed: 0x%x\n", st);
return st;
}
st = RtlReset(&gDevice);
st = PciGetInterrupt(r->Pdo, &r->Irq);
if (!KSUCCESS(st) || r->Irq == 0xFFu) {
DbgPrint("RTL816X: no legacy PCI IRQ line wired\n");
return KSUCCESS(st) ? STATUS_NOT_SUPPORTED : st;
}
st = RtlReset(r);
if (!KSUCCESS(st)) {
DbgPrint("RTL816X: reset timed out\n");
return st;
}
RtlReadMac(&gDevice);
RtlReadMac(r);
DbgPrint("RTL816X: MAC %02x:%02x:%02x:%02x:%02x:%02x irq=%u\n",
gDevice.NetDevice.HardwareAddress[0], gDevice.NetDevice.HardwareAddress[1],
gDevice.NetDevice.HardwareAddress[2], gDevice.NetDevice.HardwareAddress[3],
gDevice.NetDevice.HardwareAddress[4], gDevice.NetDevice.HardwareAddress[5],
gDevice.Irq);
r->NetDevice.HardwareAddress[0], r->NetDevice.HardwareAddress[1],
r->NetDevice.HardwareAddress[2], r->NetDevice.HardwareAddress[3],
r->NetDevice.HardwareAddress[4], r->NetDevice.HardwareAddress[5],
r->Irq);
st = RtlAllocateDma(&gDevice);
st = RtlAllocateDma(r);
if (!KSUCCESS(st))
return st;
RtlInitDescriptors(&gDevice);
KeInitializeDpc(&gDevice.Dpc, RtlDpcRoutine, &gDevice);
RtlInitDescriptors(r);
KeInitializeDpc(&r->Dpc, RtlDpcRoutine, r);
gDevice.NetDevice.Mtu = NET_MTU_ETHERNET;
gDevice.NetDevice.DriverContext = &gDevice;
gDevice.NetDevice.SendFrame = RtlSendFrame;
RtlProgramHardware(&gDevice);
r->NetDevice.Mtu = NET_MTU_ETHERNET;
r->NetDevice.DriverContext = r;
r->NetDevice.SendFrame = RtlSendFrame;
gDevicePresent = true;
KeIrqRegister(gDevice.Irq, RtlIsr);
st = NetRegisterDevice(&gDevice.NetDevice);
st = RtlRegisterDevice(r);
if (!KSUCCESS(st))
return st;
DbgPrint("RTL816X: initialized; RX/TX enabled on IRQ %u\n", gDevice.Irq);
KeIrqRegister(r->Irq, RtlIsr);
r->Started = true;
RtlProgramHardware(r);
r->Fdo->PnpState = DevicePnpStarted;
st = NetRegisterDevice(&r->NetDevice);
if (!KSUCCESS(st)) {
RtlWrite16(r, RTL_REG_INTR_MASK, 0);
r->Started = false;
RtlUnregisterDevice(r);
return st;
}
DbgPrint("RTL816X: initialized; RX/TX enabled on IRQ %u\n", r->Irq);
return STATUS_SUCCESS;
}
KSTATUS Rtl816xDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
if (!device || !irp || !device->DeviceExtension)
return STATUS_INVALID_PARAMETER;
RTL816X_DEVICE* r = (RTL816X_DEVICE*)device->DeviceExtension;
if (r->Magic != RTL816X_DEVICE_MAGIC)
return STATUS_INVALID_PARAMETER;
IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp);
if (!ios || ios->MajorFunction != IRP_MJ_PNP)
return STATUS_INVALID_PARAMETER;
switch ((IO_PNP_MINOR_FUNCTION)ios->MinorFunction) {
case IRP_MN_START_DEVICE:
return RtlStartDevice(r);
case IRP_MN_QUERY_REMOVE_DEVICE:
case IRP_MN_CANCEL_REMOVE_DEVICE:
return STATUS_SUCCESS;
case IRP_MN_SURPRISE_REMOVAL:
r->SurpriseRemoved = true;
device->PnpState = DevicePnpSurpriseRemoved;
if (r->Started)
RtlWrite16(r, RTL_REG_INTR_MASK, 0);
return STATUS_SUCCESS;
case IRP_MN_REMOVE_DEVICE:
r->Removed = true;
device->PnpState = DevicePnpDeleted;
if (r->Started)
RtlWrite16(r, RTL_REG_INTR_MASK, 0);
RtlUnregisterDevice(r);
IoDetachDevice(device);
IoDeleteDevice(device);
return STATUS_SUCCESS;
default:
return IoForwardIrpToLower(device, irp);
}
}
KSTATUS Rtl816xAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject) {
if (!driver || !physicalDeviceObject || physicalDeviceObject->Type != DeviceObjectPhysical)
return STATUS_INVALID_PARAMETER;
DEVICE_OBJECT* fdo = (DEVICE_OBJECT*)0;
KSTATUS st = IoCreateDevice(driver, sizeof(RTL816X_DEVICE), &fdo);
if (!KSUCCESS(st))
return st;
(void)IoSetDeviceName(fdo, SYS_SV_LITERAL("rtl816x"));
RTL816X_DEVICE* r = (RTL816X_DEVICE*)fdo->DeviceExtension;
memset(r, 0, sizeof(*r));
r->Magic = RTL816X_DEVICE_MAGIC;
r->Fdo = fdo;
r->Pdo = physicalDeviceObject;
r->Irq = 0xFFu;
r->TxLock = (SPINLOCK)SPINLOCK_INIT;
r->LowerDevice = IoAttachDeviceToDeviceStack(fdo, physicalDeviceObject);
if (!r->LowerDevice) {
IoDeleteDevice(fdo);
return STATUS_FAILURE;
}
DbgPrint("RTL816X: FDO attached to %v\n", physicalDeviceObject->Name);
return STATUS_SUCCESS;
}
KSTATUS DriverEntry(const HAL_API* hal) {
if (!hal)
return STATUS_INVALID_PARAMETER;
DbgPrint("RTL816X: driver loading...\n");
KSTATUS st = RtlInitialize(hal);
gHal = hal;
gRtl816xDeviceCount = 0;
memset(&gRtl816xDriver, 0, sizeof(gRtl816xDriver));
IoInitializeDriverObject(&gRtl816xDriver, SYS_SV_LITERAL("rtl816x"));
gRtl816xDriver.IdTable = &gRtl816xIdTable;
gRtl816xDriver.DriverAddDevice = Rtl816xAddDevice;
gRtl816xDriver.MajorFunction[IRP_MJ_PNP] = Rtl816xDispatchPnp;
KSTATUS st = IoRegisterDriverObject(&gRtl816xDriver);
if (!KSUCCESS(st))
DbgPrint("RTL816X: init failed: 0x%x\n", st);
DbgPrint("RTL816X: IoRegisterDriverObject failed: 0x%x\n", st);
return st;
}
+20 -33
View File
@@ -1,4 +1,4 @@
// rtl816x.h - shared rtl816x driver internals.
// rtl816x.h - shared rtl8168/rtl8169 driver internals.
#pragma once
#include <stdint.h>
@@ -8,6 +8,8 @@
#include <intrin.h>
#include <ntlite/ddi/hal_api.h>
#include <ntlite/ddi/io_driver.h>
#include <ntlite/ddi/pci.h>
#include <ntlite/status.h>
#include <kernel/ke_debug.h>
#include <kernel/ke_irq.h>
@@ -21,13 +23,8 @@
#define RTL_DEVICE_8168 0x8168u
#define RTL_DEVICE_8169 0x8169u
#define PCI_REG_COMMAND 0x04u
#define PCI_REG_BAR0 0x10u
#define PCI_REG_IRQ 0x3Cu
#define PCI_COMMAND_IO (1u << 0)
#define PCI_COMMAND_MEMORY (1u << 1)
#define PCI_COMMAND_MASTER (1u << 2)
#define PCI_COMMAND_INTX_DISABLE (1u << 10)
#define RTL816X_DEVICE_MAGIC 0x81690001u
#define RTL816X_MAX_DEVICES 8u
#define RTL_REG_MAC0 0x00u
#define RTL_REG_MAR0 0x08u
@@ -99,7 +96,6 @@
#define RTL_RING_PAGES 1u
#define RTL_RXBUF_PAGES ((RTL_RX_COUNT * RTL_BUF_SIZE + 4095u) / 4096u)
#define RTL_TXBUF_PAGES ((RTL_TX_COUNT * RTL_BUF_SIZE + 4095u) / 4096u)
#define RTL_MMIO_PAGES 1u
#pragma pack(push, 1)
typedef struct RTL_DESC {
@@ -110,24 +106,21 @@ typedef struct RTL_DESC {
} RTL_DESC;
#pragma pack(pop)
typedef enum RTL_BAR_KIND {
RtlBarNone = 0,
RtlBarIo,
RtlBarMmio,
} RTL_BAR_KIND;
typedef struct RTL_BAR {
RTL_BAR_KIND Kind;
uint64_t Base;
uint8_t Index;
} RTL_BAR;
typedef struct RTL816X_DEVICE {
uint8_t Bus;
uint8_t Dev;
uint8_t Fn;
uint32_t Magic;
DEVICE_OBJECT* Fdo;
DEVICE_OBJECT* Pdo;
DEVICE_OBJECT* LowerDevice;
PCI_DEVICE_INFO PciInfo;
PCI_BAR RegisterBar;
uint64_t RegisterBytes;
uint8_t Irq;
uint16_t DeviceId;
bool Started;
bool Removed;
bool SurpriseRemoved;
bool UseMmio;
volatile uint8_t* Mmio;
@@ -152,13 +145,7 @@ typedef struct RTL816X_DEVICE {
NET_DEVICE NetDevice;
} RTL816X_DEVICE;
extern const HAL_API* gHal;
extern RTL816X_DEVICE gDevice;
extern bool gDevicePresent;
bool RtlFindDevice(uint8_t* outBus, uint8_t* outDev, uint8_t* outFn, uint16_t* outDeviceId);
bool RtlReadBar(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t index, RTL_BAR* out);
KSTATUS RtlMapMmio(uint64_t physBase, volatile uint8_t** outMmio);
KSTATUS RtlSelectRegisterBar(RTL816X_DEVICE* r);
void RtlEnablePci(RTL816X_DEVICE* r);
KSTATUS Rtl816xAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject);
KSTATUS Rtl816xDispatchPnp(DEVICE_OBJECT* device, IRP* irp);
-126
View File
@@ -1,126 +0,0 @@
// rtl816x_pci.c - PCI BAR/MMIO front-end for rtl816x.
#include "rtl816x.h"
bool RtlFindDevice(uint8_t* outBus, uint8_t* outDev, uint8_t* outFn, uint16_t* outDeviceId) {
if (gHal->Pci.FindDevice(RTL_VENDOR_REALTEK, RTL_DEVICE_8168, outBus, outDev, outFn)) {
*outDeviceId = RTL_DEVICE_8168;
return true;
}
if (gHal->Pci.FindDevice(RTL_VENDOR_REALTEK, RTL_DEVICE_8169, outBus, outDev, outFn)) {
*outDeviceId = RTL_DEVICE_8169;
return true;
}
return false;
}
bool RtlReadBar(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t index, RTL_BAR* out) {
uint8_t reg = (uint8_t)(PCI_REG_BAR0 + index * 4u);
uint32_t lo = gHal->Pci.Read32(bus, dev, fn, reg);
out->Kind = RtlBarNone;
out->Base = 0;
out->Index = index;
if (lo == 0 || lo == 0xFFFFFFFFu)
return false;
if (lo & 1u) {
uint32_t base = lo & ~0x3u;
if (!base) return false;
out->Kind = RtlBarIo;
out->Base = base;
return true;
}
uint64_t base = (uint64_t)(lo & ~0xFu);
uint32_t type = (lo >> 1) & 0x3u;
if (type == 0x2u && index < 5) {
uint32_t hi = gHal->Pci.Read32(bus, dev, fn, (uint8_t)(reg + 4u));
base |= ((uint64_t)hi << 32);
}
if (!base) return false;
out->Kind = RtlBarMmio;
out->Base = base;
return true;
}
KSTATUS RtlMapMmio(uint64_t physBase, volatile uint8_t** outMmio) {
uint64_t pageBase = physBase & PAGE_MASK;
uint64_t pageOff = physBase - pageBase;
for (uint32_t i = 0; i < RTL_MMIO_PAGES; i++) {
PHYSICAL_ADDRESS phys = pageBase + (uint64_t)i * PAGE_SIZE;
uintptr_t virt = (uintptr_t)gHal->PhysToVirt(phys);
KSTATUS st = MmMapPage(virt, phys, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE, MmMapFailIfPresent);
if (st == STATUS_ALREADY_EXISTS)
st = MmSetPageProtect(virt, PAGE_SIZE, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE);
if (!KSUCCESS(st))
return st;
}
*outMmio = (volatile uint8_t*)((uintptr_t)gHal->PhysToVirt(pageBase) + pageOff);
return STATUS_SUCCESS;
}
KSTATUS RtlSelectRegisterBar(RTL816X_DEVICE* r) {
RTL_BAR best = { RtlBarNone, 0, 0 };
RTL_BAR fallback = { RtlBarNone, 0, 0 };
for (uint8_t i = 0; i < 6; i++) {
RTL_BAR bar;
if (!RtlReadBar(r->Bus, r->Dev, r->Fn, i, &bar))
continue;
if (bar.Kind == RtlBarMmio) {
best = bar;
break;
}
if (bar.Kind == RtlBarIo && fallback.Kind == RtlBarNone)
fallback = bar;
// Skip the high dword of 64-bit memory BARs.
if (bar.Kind == RtlBarMmio) {
uint32_t lo = gHal->Pci.Read32(r->Bus, r->Dev, r->Fn, (uint8_t)(PCI_REG_BAR0 + i * 4u));
if (((lo >> 1) & 0x3u) == 0x2u)
i++;
}
}
if (best.Kind == RtlBarNone)
best = fallback;
if (best.Kind == RtlBarMmio) {
KSTATUS st = RtlMapMmio(best.Base, &r->Mmio);
if (!KSUCCESS(st))
return st;
r->UseMmio = true;
DbgPrint("RTL816X: BAR%u MMIO base=0x%016llX virt=0x%016llX\n",
best.Index, (unsigned long long)best.Base, r->Mmio);
return STATUS_SUCCESS;
}
if (best.Kind == RtlBarIo) {
if (best.Base > 0xFFFFu)
return STATUS_NOT_SUPPORTED;
r->IoBase = (uint16_t)best.Base;
r->UseMmio = false;
DbgPrint("RTL816X: BAR%u I/O base=0x%04x\n", best.Index, r->IoBase);
return STATUS_SUCCESS;
}
return STATUS_NOT_FOUND;
}
void RtlEnablePci(RTL816X_DEVICE* r) {
uint32_t cmd = gHal->Pci.Read32(r->Bus, r->Dev, r->Fn, PCI_REG_COMMAND);
cmd |= PCI_COMMAND_MASTER;
if (r->UseMmio) cmd |= PCI_COMMAND_MEMORY;
else cmd |= PCI_COMMAND_IO;
cmd &= ~PCI_COMMAND_INTX_DISABLE;
gHal->Pci.Write32(r->Bus, r->Dev, r->Fn, PCI_REG_COMMAND, cmd);
}
+246 -104
View File
@@ -2,17 +2,26 @@
#include "ahci.h"
const HAL_API *g_hal;
HBA_MEM *g_hba;
uint8_t g_irq;
DRIVER_OBJECT g_driver;
AHCI_DRIVE g_drives[MAX_AHCI_DRIVES];
int g_driveCount;
static AHCI_CONTROLLER* g_controllers[MAX_AHCI_CONTROLLERS];
static int g_controllerCount;
static uint8_t g_nextDiskIndex;
static const IO_DEVICE_ID g_ahciIds[] = {
{ IoBusPci, { "PCI\\CC_010601", sizeof("PCI\\CC_010601") - 1 } },
};
static const IO_DRIVER_ID_TABLE g_ahciIdTable = {
g_ahciIds,
sizeof(g_ahciIds) / sizeof(g_ahciIds[0]),
};
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
static KSTATUS AhciStartController(AHCI_CONTROLLER* ctl);
// Accessors for port structure memory at known offsets from structVirt/Phys
static inline HBA_CMD_HEADER *cmd_list(AHCI_DRIVE *drv) {
@@ -166,29 +175,34 @@ static void AhciCompletionDpc(KDPC *dpc, void *context,
// ---------------------------------------------------------------------------
static void AhciIsr(void) {
if (!g_hba) return;
for (int c = 0; c < g_controllerCount; c++) {
AHCI_CONTROLLER* ctl = g_controllers[c];
if (!ctl || !ctl->hba || ctl->removed)
continue;
// GHC.IS tells us which ports are pending.
uint32_t pending = g_hba->is;
if (!pending) return;
// GHC.IS tells us which ports are pending for this HBA.
uint32_t pending = ctl->hba->is;
if (!pending)
continue;
for (int i = 0; i < g_driveCount; i++) {
AHCI_DRIVE *drv = &g_drives[i];
uint32_t portBit = (1u << drv->portNo);
if (!(pending & portBit)) continue;
for (int i = 0; i < ctl->driveCount; i++) {
AHCI_DRIVE *drv = &ctl->drives[i];
uint32_t portBit = (1u << drv->portNo);
if (!(pending & portBit)) continue;
// Capture and clear port IS before queueing the bottom half. The
// waiter reads lastIs to check for errors without racing the next
// command. OR in case hardware reports multiple causes before the
// DPC releases the worker.
uint32_t portIs = drv->port->is;
drv->port->is = portIs; // W1C - write back to clear
drv->lastIs |= portIs;
// Capture and clear port IS before queueing the bottom half. The
// waiter reads lastIs to check for errors without racing the next
// command. OR in case hardware reports multiple causes before the
// DPC releases the worker.
uint32_t portIs = drv->port->is;
drv->port->is = portIs; // W1C - write back to clear
drv->lastIs |= portIs;
// Clear the HBA-level bit.
g_hba->is = portBit;
// Clear the HBA-level bit.
ctl->hba->is = portBit;
KeInsertQueueDpc(&drv->completionDpc, (void *)0, (void *)0);
KeInsertQueueDpc(&drv->completionDpc, (void *)0, (void *)0);
}
}
}
@@ -441,7 +455,7 @@ static void AhciCancelReleaseRemoveLock(IRP* irp, void* context) {
IoReleaseRemoveLock(&drv->removeLock, irp);
}
static KSTATUS AhciDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
static KSTATUS AhciDispatchDrivePnp(DEVICE_OBJECT* device, IRP* irp) {
if (!device || !irp) return STATUS_INVALID_PARAMETER;
AHCI_DRIVE* drv = (AHCI_DRIVE*)device->DeviceExtension;
if (!drv) return STATUS_INVALID_PARAMETER;
@@ -478,6 +492,71 @@ static KSTATUS AhciDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
}
}
static KSTATUS AhciDispatchControllerPnp(DEVICE_OBJECT* device, IRP* irp) {
if (!device || !irp) return STATUS_INVALID_PARAMETER;
AHCI_CONTROLLER* ctl = (AHCI_CONTROLLER*)device->DeviceExtension;
if (!ctl || ctl->magic != AHCI_CONTROLLER_MAGIC)
return STATUS_INVALID_PARAMETER;
IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp);
if (!ios || ios->MajorFunction != IRP_MJ_PNP)
return STATUS_INVALID_PARAMETER;
switch ((IO_PNP_MINOR_FUNCTION)ios->MinorFunction) {
case IRP_MN_START_DEVICE:
if (ctl->started) {
device->PnpState = DevicePnpStarted;
return STATUS_SUCCESS;
}
return AhciStartController(ctl);
case IRP_MN_QUERY_REMOVE_DEVICE:
device->PnpState = DevicePnpRemovePending;
return STATUS_SUCCESS;
case IRP_MN_CANCEL_REMOVE_DEVICE:
device->PnpState = DevicePnpStarted;
return STATUS_SUCCESS;
case IRP_MN_SURPRISE_REMOVAL:
ctl->surpriseRemoved = true;
device->PnpState = DevicePnpSurpriseRemoved;
for (int i = 0; i < ctl->driveCount; i++) {
AHCI_DRIVE* drv = &ctl->drives[i];
drv->surpriseRemoved = true;
IrpQueueCancelAllEx(&drv->irpQueue, STATUS_OBJECT_DEAD, AhciCancelReleaseRemoveLock, drv);
}
return STATUS_SUCCESS;
case IRP_MN_REMOVE_DEVICE:
ctl->removed = true;
device->PnpState = DevicePnpDeleted;
for (int i = 0; i < ctl->driveCount; i++) {
AHCI_DRIVE* drv = &ctl->drives[i];
drv->removed = true;
IrpQueueCancelAllEx(&drv->irpQueue, STATUS_OBJECT_DEAD, AhciCancelReleaseRemoveLock, drv);
}
IoDetachDevice(device);
IoDeleteDevice(device);
return STATUS_SUCCESS;
default:
return STATUS_NOT_SUPPORTED;
}
}
static KSTATUS AhciDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
if (!device || !device->DeviceExtension)
return STATUS_INVALID_PARAMETER;
uint32_t magic = *(uint32_t*)device->DeviceExtension;
if (magic == AHCI_CONTROLLER_MAGIC)
return AhciDispatchControllerPnp(device, irp);
if (magic == AHCI_DRIVE_MAGIC)
return AhciDispatchDrivePnp(device, irp);
return STATUS_INVALID_PARAMETER;
}
// AhciWorker - kernel thread, one per drive.
// Dequeues IRPs and executes them one at a time using the existing
// single-slot interrupt-driven path. Completion fires the IRP's event
@@ -548,80 +627,101 @@ static void AhciWorker(uintptr_t arg) {
#define PORT_IE_ERRORS (PORT_IS_TFES | PORT_IS_HBFS | PORT_IS_HBDS | PORT_IS_IFS)
#define PORT_IE_ALL (PORT_IE_NORMAL | PORT_IE_ERRORS)
KSTATUS AhciInitialize(const HAL_API *hal) {
g_hal = hal;
g_driveCount = 0;
memset(&g_driver, 0, sizeof(g_driver));
IoInitializeDriverObject(&g_driver, SYS_SV_LITERAL("ahci"));
g_driver.MajorFunction[IRP_MJ_READ] = AhciDispatchReadWrite;
g_driver.MajorFunction[IRP_MJ_WRITE] = AhciDispatchReadWrite;
g_driver.MajorFunction[IRP_MJ_PNP] = AhciDispatchPnp;
// -----------------------------------------------------------------------
// Locate AHCI controller via PCI class scan
// -----------------------------------------------------------------------
uint8_t pciBus, pciDev, pciFn;
if (!hal->Pci.FindClass(PCI_AHCI_CLASS, PCI_AHCI_SUBCLASS,
&pciBus, &pciDev, &pciFn)) {
DbgPrint("AHCI: no AHCI controller found\n");
return STATUS_NOT_FOUND;
static KSTATUS AhciRegisterController(AHCI_CONTROLLER* ctl) {
if (!ctl) return STATUS_INVALID_PARAMETER;
for (int i = 0; i < g_controllerCount; i++) {
if (g_controllers[i] == ctl)
return STATUS_SUCCESS;
}
DbgPrint("AHCI: controller at PCI %02x:%02x.%x\n", pciBus, pciDev, pciFn);
if (g_controllerCount >= MAX_AHCI_CONTROLLERS)
return STATUS_NO_MEMORY;
g_controllers[g_controllerCount++] = ctl;
return STATUS_SUCCESS;
}
// -----------------------------------------------------------------------
// Enable Bus Master + Memory Space in PCI command register
// -----------------------------------------------------------------------
uint32_t cmd = hal->Pci.Read32(pciBus, pciDev, pciFn, PCI_CMD_REG);
cmd |= (PCI_CMD_BUSMASTER | PCI_CMD_MEMSPACE); cmd &= ~(1 << 10); cmd |= (1 << 2);
hal->Pci.Write32(pciBus, pciDev, pciFn, PCI_CMD_REG, cmd);
static KSTATUS AhciStartController(AHCI_CONTROLLER* ctl) {
if (!ctl || ctl->magic != AHCI_CONTROLLER_MAGIC || !ctl->pdo || !ctl->fdo)
return STATUS_INVALID_PARAMETER;
// -----------------------------------------------------------------------
// Read PCI IRQ line
// -----------------------------------------------------------------------
g_irq = hal->Pci.GetIrqLine(pciBus, pciDev, pciFn);
if (g_irq == 0xFF) {
if (ctl->started)
return STATUS_SUCCESS;
KSTATUS st = PciGetDeviceInfo(ctl->pdo, &ctl->pciInfo);
if (!KSUCCESS(st)) {
DbgPrint("AHCI: PciGetDeviceInfo failed: 0x%x\n", st);
return st;
}
DbgPrint("AHCI: controller at PCI %02x:%02x.%x vendor=%04x device=%04x\n",
ctl->pciInfo.Address.Bus,
ctl->pciInfo.Address.Device,
ctl->pciInfo.Address.Function,
ctl->pciInfo.VendorId,
ctl->pciInfo.DeviceId);
st = PciGetInterrupt(ctl->pdo, &ctl->irq);
if (!KSUCCESS(st) || ctl->irq == 0xFF) {
DbgPrint("AHCI: no PCI IRQ wired - cannot use interrupt-driven mode\n");
return STATUS_NOT_SUPPORTED;
return KSUCCESS(st) ? STATUS_NOT_SUPPORTED : st;
}
DbgPrint("AHCI: PCI IRQ line = %u\n", (unsigned)g_irq);
DbgPrint("AHCI: PCI IRQ line = %u\n", (unsigned)ctl->irq);
// -----------------------------------------------------------------------
// Read ABAR (BAR5) and map as uncacheable
// -----------------------------------------------------------------------
uint32_t bar5 = hal->Pci.Read32(pciBus, pciDev, pciFn, PCI_BAR5_REG);
uint64_t abarPhys = (uint64_t)(bar5 & ~0xFFFu);
KSTATUS st = ahci_map_mmio(abarPhys);
if (!KSUCCESS(st)) return st;
st = PciGetBar(ctl->pdo, AHCI_PCI_BAR_ABAR, &ctl->abar);
if (!KSUCCESS(st)) {
DbgPrint("AHCI: BAR5/ABAR not present (0x%x)\n", st);
return st;
}
if (ctl->abar.Type != PciBarMemory32 && ctl->abar.Type != PciBarMemory64) {
DbgPrint("AHCI: BAR5/ABAR is not MMIO (type=%u)\n", ctl->abar.Type);
return STATUS_DEVICE_ERROR;
}
g_hba = (HBA_MEM *)hal->PhysToVirt(abarPhys);
DbgPrint("AHCI: ABAR phys=0x%016llX version=0x%08x\n",
(unsigned long long)abarPhys, g_hba->vs);
st = PciMapBar(ctl->pdo,
AHCI_PCI_BAR_ABAR,
PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE,
(void**)&ctl->hba,
&ctl->abarLength);
if (!KSUCCESS(st)) {
DbgPrint("AHCI: PciMapBar(BAR5) failed: 0x%x\n", st);
return st;
}
// -----------------------------------------------------------------------
// HBA reset + BIOS handoff (also enables GHC.IE)
// -----------------------------------------------------------------------
st = hba_reset(g_hba);
if (!KSUCCESS(st)) return st;
st = PciEnableDevice(ctl->pdo,
PCI_ENABLE_MEMORY |
PCI_ENABLE_BUSMASTER |
PCI_ENABLE_INTX);
if (!KSUCCESS(st)) {
DbgPrint("AHCI: PciEnableDevice failed: 0x%x\n", st);
return st;
}
// -----------------------------------------------------------------------
// Log capability summary
// -----------------------------------------------------------------------
uint32_t cap = g_hba->cap;
DbgPrint("AHCI: ABAR phys=0x%016llX len=0x%llx version=0x%08x\n",
(unsigned long long)ctl->abar.Base,
(unsigned long long)ctl->abarLength,
ctl->hba->vs);
st = hba_reset(ctl->hba);
if (!KSUCCESS(st))
return st;
uint32_t cap = ctl->hba->cap;
uint32_t nPorts = (cap & 0x1F) + 1;
uint32_t nSlots = ((cap >> 8) & 0x1F) + 1;
bool s64a = !!(cap & (1u << 31));
DbgPrint("AHCI: cap=0x%08x ports=%u slots=%u 64bit=%d\n",
cap, nPorts, nSlots, (int)s64a);
// -----------------------------------------------------------------------
// Enumerate implemented ports
// -----------------------------------------------------------------------
uint32_t pi = g_hba->pi;
for (uint32_t portno = 0; portno < 32 && g_driveCount < MAX_AHCI_DRIVES; portno++) {
st = AhciRegisterController(ctl);
if (!KSUCCESS(st)) {
DbgPrint("AHCI: controller registry full (0x%x)\n", st);
return st;
}
uint32_t pi = ctl->hba->pi;
for (uint32_t portno = 0; portno < 32 && ctl->driveCount < MAX_AHCI_DRIVES; portno++) {
if (!(pi & (1u << portno))) continue;
HBA_PORT *port = &g_hba->ports[portno];
HBA_PORT *port = &ctl->hba->ports[portno];
// Phase 1: physical link check
if (!port_link_up(port)) {
@@ -631,9 +731,11 @@ KSTATUS AhciInitialize(const HAL_API *hal) {
DbgPrint("AHCI: port %u: link up, initializing...\n", portno);
int driveIdx = g_driveCount;
AHCI_DRIVE *drv = &g_drives[driveIdx];
int driveIdx = ctl->driveCount;
AHCI_DRIVE *drv = &ctl->drives[driveIdx];
memset(drv, 0, sizeof(*drv));
drv->magic = AHCI_DRIVE_MAGIC;
drv->controller = ctl;
drv->port = port;
drv->portNo = (uint8_t)portno;
@@ -689,7 +791,7 @@ KSTATUS AhciInitialize(const HAL_API *hal) {
}
DbgPrint("AHCI: drive %d: %llu sectors (%llu MB)\n",
driveIdx,
g_nextDiskIndex,
(unsigned long long)drv->sectorCount,
(unsigned long long)(drv->sectorCount / 2048));
@@ -701,7 +803,7 @@ KSTATUS AhciInitialize(const HAL_API *hal) {
// Phase 8: create and register a disk DEVICE_OBJECT.
st = IoCreateDevice(&g_driver, 0, &drv->deviceObject);
if (!KSUCCESS(st)) {
DbgPrint("AHCI: drive %d: IoCreateDevice failed (0x%x)\n", driveIdx, st);
DbgPrint("AHCI: drive %d: IoCreateDevice failed (0x%x)\n", g_nextDiskIndex, st);
continue;
}
drv->deviceObject->DeviceExtension = drv;
@@ -709,12 +811,14 @@ KSTATUS AhciInitialize(const HAL_API *hal) {
drv->deviceObject->u.Disk.SectorSize = 512;
drv->deviceObject->AlignmentRequirement = 512;
st = IoRegisterDiskDevice((uint8_t)driveIdx, drv->deviceObject);
uint8_t diskIndex = g_nextDiskIndex;
st = IoRegisterDiskDevice(diskIndex, drv->deviceObject);
if (KSUCCESS(st)) {
DbgPrint("AHCI: drive %d registered\n", driveIdx);
g_driveCount++;
DbgPrint("AHCI: drive %u registered\n", diskIndex);
ctl->driveCount++;
g_nextDiskIndex++;
} else {
DbgPrint("AHCI: IoRegisterDiskDevice(%d) failed (0x%x)\n", driveIdx, st);
DbgPrint("AHCI: IoRegisterDiskDevice(%u) failed (0x%x)\n", diskIndex, st);
IoDeleteDevice(drv->deviceObject);
drv->deviceObject = (DEVICE_OBJECT*)0;
continue;
@@ -726,33 +830,71 @@ KSTATUS AhciInitialize(const HAL_API *hal) {
st = KeCreateKernelThread((uintptr_t)AhciWorker, (uintptr_t)drv,
PriorityHigh, &drv->workerThread);
if (!KSUCCESS(st)) {
DbgPrint("AHCI: drive %d: worker thread create failed (0x%x)\n",
driveIdx, st);
DbgPrint("AHCI: drive %u: worker thread create failed (0x%x)\n",
diskIndex, st);
// Fatal for this device: all disk access is IRP-based now, so
// leave the device registered but clearly fail during testing.
}
}
if (g_driveCount == 0) {
DbgPrint("AHCI: no drives found\n");
return STATUS_NOT_FOUND;
if (ctl->driveCount == 0)
DbgPrint("AHCI: no drives found on PCI %02x:%02x.%x\n",
ctl->pciInfo.Address.Bus,
ctl->pciInfo.Address.Device,
ctl->pciInfo.Address.Function);
KeIrqRegister(ctl->irq, AhciIsr);
ctl->started = true;
ctl->fdo->PnpState = DevicePnpStarted;
DbgPrint("AHCI: ISR registered on IRQ %u; controller drives=%d\n",
(unsigned)ctl->irq, ctl->driveCount);
return STATUS_SUCCESS;
}
static KSTATUS AhciAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject) {
if (!driver || !physicalDeviceObject || physicalDeviceObject->Type != DeviceObjectPhysical)
return STATUS_INVALID_PARAMETER;
DEVICE_OBJECT* fdo = (DEVICE_OBJECT*)0;
KSTATUS st = IoCreateDevice(driver, sizeof(AHCI_CONTROLLER), &fdo);
if (!KSUCCESS(st))
return st;
AHCI_CONTROLLER* ctl = (AHCI_CONTROLLER*)fdo->DeviceExtension;
memset(ctl, 0, sizeof(*ctl));
ctl->magic = AHCI_CONTROLLER_MAGIC;
ctl->fdo = fdo;
ctl->pdo = physicalDeviceObject;
ctl->irq = 0xFF;
(void)IoSetDeviceName(fdo, SYS_SV_LITERAL("ahci-controller"));
ctl->lowerDevice = IoAttachDeviceToDeviceStack(fdo, physicalDeviceObject);
if (!ctl->lowerDevice) {
IoDeleteDevice(fdo);
return STATUS_FAILURE;
}
// -----------------------------------------------------------------------
// Register the ISR. All ports on this HBA share one PCI IRQ line.
// Do this after all drives are initialised so the ISR sees valid state.
// -----------------------------------------------------------------------
KeIrqRegister(g_irq, AhciIsr);
DbgPrint("AHCI: ISR registered on IRQ %u; initialised %d drive(s)\n",
(unsigned)g_irq, g_driveCount);
DbgPrint("AHCI: FDO attached to %v\n", physicalDeviceObject->Name);
return STATUS_SUCCESS;
}
KSTATUS DriverEntry(const HAL_API *hal) {
DbgPrint("AHCI: driver loading...\n");
KSTATUS st = AhciInitialize(hal);
g_hal = hal;
g_controllerCount = 0;
g_nextDiskIndex = 0;
memset(&g_driver, 0, sizeof(g_driver));
IoInitializeDriverObject(&g_driver, SYS_SV_LITERAL("ahci"));
g_driver.IdTable = &g_ahciIdTable;
g_driver.DriverAddDevice = AhciAddDevice;
g_driver.MajorFunction[IRP_MJ_READ] = AhciDispatchReadWrite;
g_driver.MajorFunction[IRP_MJ_WRITE] = AhciDispatchReadWrite;
g_driver.MajorFunction[IRP_MJ_PNP] = AhciDispatchPnp;
KSTATUS st = IoRegisterDriverObject(&g_driver);
if (!KSUCCESS(st))
DbgPrint("AHCI: init failed: 0x%x\n", st);
DbgPrint("AHCI: IoRegisterDriverObject failed: 0x%x\n", st);
return st;
}
+31 -12
View File
@@ -5,6 +5,7 @@
#include <stdbool.h>
#include <string.h>
#include <ntlite/ddi/io_driver.h>
#include <ntlite/ddi/pci.h>
#include <kernel/mm_phys.h>
#include <kernel/mm_virt.h>
#include <kernel/ke_debug.h>
@@ -223,13 +224,12 @@ typedef volatile struct {
// Constants
// ---------------------------------------------------------------------------
// PCI
#define PCI_AHCI_CLASS 0x01
#define PCI_AHCI_SUBCLASS 0x06
#define PCI_CMD_REG 0x04
#define PCI_BAR5_REG 0x24
#define PCI_CMD_BUSMASTER (1 << 2)
#define PCI_CMD_MEMSPACE (1 << 1)
// PCI/AHCI binding. PCI enumeration, BAR decoding, interrupt line
// discovery, and command-register policy are owned by pci.sys.
#define AHCI_PCI_BAR_ABAR 5u
#define AHCI_CONTROLLER_MAGIC 0x41484349u // 'AHCI'
#define AHCI_DRIVE_MAGIC 0x41484452u // 'AHDR'
#define MAX_AHCI_CONTROLLERS 8
// GHC register bits
#define GHC_AE (1u << 31) // AHCI Enable
@@ -314,8 +314,12 @@ typedef volatile struct {
// We map 8 pages (32KB) without probing the BAR size.
#define AHCI_MMIO_PAGES 8
typedef struct AHCI_CONTROLLER AHCI_CONTROLLER;
// Per-drive context
typedef struct {
uint32_t magic; // AHCI_DRIVE_MAGIC
AHCI_CONTROLLER* controller; // owning AHCI controller FDO extension
HBA_PORT *port; // MMIO port registers (virtual)
uint64_t sectorCount; // Total LBA sectors on device
uint8_t portNo; // Physical port index (for ISR dispatch)
@@ -358,15 +362,30 @@ typedef struct {
// volatile uint32_t prevSact; // previous port->sact snapshot
} AHCI_DRIVE;
struct AHCI_CONTROLLER {
uint32_t magic; // AHCI_CONTROLLER_MAGIC
DEVICE_OBJECT* fdo;
DEVICE_OBJECT* pdo;
DEVICE_OBJECT* lowerDevice;
PCI_DEVICE_INFO pciInfo;
PCI_BAR abar;
HBA_MEM* hba;
uint64_t abarLength;
uint8_t irq;
AHCI_DRIVE drives[MAX_AHCI_DRIVES];
int driveCount;
bool started;
bool surpriseRemoved;
bool removed;
};
extern const HAL_API *g_hal;
extern HBA_MEM *g_hba;
extern uint8_t g_irq;
extern DRIVER_OBJECT g_driver;
extern AHCI_DRIVE g_drives[MAX_AHCI_DRIVES];
extern int g_driveCount;
KSTATUS ahci_map_mmio(uint64_t physBase);
KSTATUS ahci_map_mmio(uint64_t physBase, uint64_t length);
void port_stop(HBA_PORT *port);
void port_start(HBA_PORT *port);
KSTATUS hba_reset(HBA_MEM *hba);
+8 -4
View File
@@ -1,9 +1,13 @@
// ahci_port.c - AHCI MMIO mapping, HBA reset, and port link bring-up helpers.
#include "ahci.h"
KSTATUS ahci_map_mmio(uint64_t physBase) {
for (int i = 0; i < AHCI_MMIO_PAGES; i++) {
uint64_t phys = physBase + (uint64_t)i * 4096;
KSTATUS ahci_map_mmio(uint64_t physBase, uint64_t length) {
if (length == 0)
length = AHCI_MMIO_PAGES * 4096ull;
uint64_t pageBase = physBase & ~(uint64_t)(PAGE_SIZE - 1u);
uint64_t pageEnd = (physBase + length + PAGE_SIZE - 1u) & ~(uint64_t)(PAGE_SIZE - 1u);
int pageIndex = 0;
for (uint64_t phys = pageBase; phys < pageEnd; phys += PAGE_SIZE, pageIndex++) {
uintptr_t virt = (uintptr_t)g_hal->PhysToVirt(phys);
uint64_t flags = PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE;
KSTATUS st = MmMapPage(virt, phys, flags, MmMapFailIfPresent);
@@ -15,7 +19,7 @@ KSTATUS ahci_map_mmio(uint64_t physBase) {
}
if (!KSUCCESS(st)) {
DbgPrint("AHCI: MmMapPage failed for page %d (phys=0x%016llX, st=0x%x)\n",
i, (unsigned long long)phys, st);
pageIndex, (unsigned long long)phys, st);
return st;
}
}
+686
View File
@@ -0,0 +1,686 @@
// ata.c - PCI IDE / ATA disk driver.
#include "ata.h"
const HAL_API* gAtaHal;
static DRIVER_OBJECT gAtaDriver;
static ATA_CONTROLLER* gAtaControllers[ATA_MAX_CONTROLLERS];
static uint32_t gAtaControllerCount;
static const IO_DEVICE_ID gAtaIds[] = {
{ IoBusPci, { "PCI\\CC_0101", sizeof("PCI\\CC_0101") - 1 } },
};
static const IO_DRIVER_ID_TABLE gAtaIdTable = {
gAtaIds,
sizeof(gAtaIds) / sizeof(gAtaIds[0]),
};
static uint8_t AtaIn8(ATA_CHANNEL* ch, uint8_t reg) {
return __inbyte((unsigned short)(ch->CommandBase + reg));
}
static uint16_t AtaIn16(ATA_CHANNEL* ch, uint8_t reg) {
return __inword((unsigned short)(ch->CommandBase + reg));
}
static void AtaOut8(ATA_CHANNEL* ch, uint8_t reg, uint8_t value) {
__outbyte((unsigned short)(ch->CommandBase + reg), value);
}
static void AtaOut16(ATA_CHANNEL* ch, uint8_t reg, uint16_t value) {
__outword((unsigned short)(ch->CommandBase + reg), value);
}
static uint8_t AtaAltStatus(ATA_CHANNEL* ch) {
return __inbyte((unsigned short)(ch->ControlBase + ATA_REG_ALT_STATUS));
}
static void AtaDeviceControl(ATA_CHANNEL* ch, uint8_t value) {
__outbyte((unsigned short)(ch->ControlBase + ATA_REG_DEVICE_CONTROL), value);
}
static void AtaDelay400ns(ATA_CHANNEL* ch) {
(void)AtaAltStatus(ch);
(void)AtaAltStatus(ch);
(void)AtaAltStatus(ch);
(void)AtaAltStatus(ch);
}
static KSTATUS AtaWaitNotBusy(ATA_CHANNEL* ch) {
for (uint32_t i = 0; i < ATA_POLL_LIMIT; i++) {
if (!(AtaIn8(ch, ATA_REG_STATUS) & ATA_STATUS_BSY))
return STATUS_SUCCESS;
KeYield();
}
return STATUS_TIMEOUT;
}
static KSTATUS AtaWaitDrq(ATA_CHANNEL* ch) {
for (uint32_t i = 0; i < ATA_POLL_LIMIT; i++) {
uint8_t st = AtaIn8(ch, ATA_REG_STATUS);
if (st & ATA_STATUS_ERR)
return STATUS_DEVICE_ERROR;
if (st & ATA_STATUS_DF)
return STATUS_DEVICE_ERROR;
if (!(st & ATA_STATUS_BSY) && (st & ATA_STATUS_DRQ))
return STATUS_SUCCESS;
KeYield();
}
return STATUS_TIMEOUT;
}
static void AtaSelectDrive(ATA_DEVICE* dev) {
ATA_CHANNEL* ch = dev->Channel;
AtaOut8(ch, ATA_REG_DRIVE_HEAD, (uint8_t)(0xA0u | (dev->Drive ? 0x10u : 0u)));
AtaDelay400ns(ch);
}
static KSTATUS AtaIdentify(ATA_DEVICE* dev) {
ATA_CHANNEL* ch = dev->Channel;
uint16_t ident[256];
memset(ident, 0, sizeof(ident));
KSTATUS st = KeWaitSemaphore(ch->Lock);
if (!KSUCCESS(st))
return st;
AtaSelectDrive(dev);
AtaOut8(ch, ATA_REG_SECTOR_COUNT, 0);
AtaOut8(ch, ATA_REG_LBA0, 0);
AtaOut8(ch, ATA_REG_LBA1, 0);
AtaOut8(ch, ATA_REG_LBA2, 0);
AtaOut8(ch, ATA_REG_COMMAND, ATA_CMD_IDENTIFY_DEVICE);
AtaDelay400ns(ch);
uint8_t status = AtaIn8(ch, ATA_REG_STATUS);
if (status == 0) {
KeSignalSemaphore(ch->Lock);
return STATUS_NOT_FOUND;
}
st = AtaWaitNotBusy(ch);
if (!KSUCCESS(st)) {
KeSignalSemaphore(ch->Lock);
return st;
}
if (AtaIn8(ch, ATA_REG_LBA1) != 0 || AtaIn8(ch, ATA_REG_LBA2) != 0) {
KeSignalSemaphore(ch->Lock);
return STATUS_NOT_SUPPORTED; // ATAPI or non-ATA signature.
}
st = AtaWaitDrq(ch);
if (!KSUCCESS(st)) {
KeSignalSemaphore(ch->Lock);
return st;
}
for (uint32_t i = 0; i < 256; i++)
ident[i] = AtaIn16(ch, ATA_REG_DATA);
KeSignalSemaphore(ch->Lock);
if (!(ident[ATA_IDENT_CAPABILITIES] & ATA_IDENT_LBA_SUPPORTED))
return STATUS_NOT_SUPPORTED;
dev->SupportsLba48 = (ident[ATA_IDENT_COMMAND_SETS] & ATA_IDENT_LBA48_SUPPORTED) != 0;
if (dev->SupportsLba48) {
uint64_t sectors = 0;
sectors |= (uint64_t)ident[100];
sectors |= (uint64_t)ident[101] << 16;
sectors |= (uint64_t)ident[102] << 32;
sectors |= (uint64_t)ident[103] << 48;
dev->SectorCount = sectors;
} else {
dev->SectorCount = ((uint32_t)ident[ATA_IDENT_LBA28_SECTORS_HI] << 16) |
ident[ATA_IDENT_LBA28_SECTORS_LO];
}
return dev->SectorCount ? STATUS_SUCCESS : STATUS_NOT_FOUND;
}
static KSTATUS AtaProgramLba(ATA_DEVICE* dev, uint64_t lba, uint16_t count, bool write) {
ATA_CHANNEL* ch = dev->Channel;
if (dev->SupportsLba48) {
AtaOut8(ch, ATA_REG_DRIVE_HEAD, (uint8_t)(0x40u | (dev->Drive ? 0x10u : 0u)));
AtaOut8(ch, ATA_REG_SECTOR_COUNT, (uint8_t)(count >> 8));
AtaOut8(ch, ATA_REG_LBA0, (uint8_t)(lba >> 24));
AtaOut8(ch, ATA_REG_LBA1, (uint8_t)(lba >> 32));
AtaOut8(ch, ATA_REG_LBA2, (uint8_t)(lba >> 40));
AtaOut8(ch, ATA_REG_SECTOR_COUNT, (uint8_t)count);
AtaOut8(ch, ATA_REG_LBA0, (uint8_t)lba);
AtaOut8(ch, ATA_REG_LBA1, (uint8_t)(lba >> 8));
AtaOut8(ch, ATA_REG_LBA2, (uint8_t)(lba >> 16));
AtaOut8(ch, ATA_REG_COMMAND, write ? ATA_CMD_WRITE_SECTORS_EXT : ATA_CMD_READ_SECTORS_EXT);
return STATUS_SUCCESS;
}
if (lba > 0x0FFFFFFFULL || count > 256)
return STATUS_INVALID_PARAMETER;
AtaOut8(ch, ATA_REG_DRIVE_HEAD, (uint8_t)(0xE0u | (dev->Drive ? 0x10u : 0u) | ((lba >> 24) & 0x0Fu)));
AtaOut8(ch, ATA_REG_SECTOR_COUNT, (uint8_t)count);
AtaOut8(ch, ATA_REG_LBA0, (uint8_t)lba);
AtaOut8(ch, ATA_REG_LBA1, (uint8_t)(lba >> 8));
AtaOut8(ch, ATA_REG_LBA2, (uint8_t)(lba >> 16));
AtaOut8(ch, ATA_REG_COMMAND, write ? ATA_CMD_WRITE_SECTORS : ATA_CMD_READ_SECTORS);
return STATUS_SUCCESS;
}
static KSTATUS AtaReadPio(ATA_DEVICE* dev, uint64_t lba, uint32_t count, void* buffer) {
if (!dev || !buffer)
return STATUS_INVALID_PARAMETER;
if (count == 0)
return STATUS_SUCCESS;
if (lba + count > dev->SectorCount)
return STATUS_INVALID_PARAMETER;
ATA_CHANNEL* ch = dev->Channel;
uint16_t* out = (uint16_t*)buffer;
KSTATUS st = KeWaitSemaphore(ch->Lock);
if (!KSUCCESS(st))
return st;
while (count > 0) {
uint16_t batch = (count > 256u) ? 256u : (uint16_t)count;
uint16_t encodedCount = (!dev->SupportsLba48 && batch == 256u) ? 0u : batch;
AtaSelectDrive(dev);
st = AtaWaitNotBusy(ch);
if (!KSUCCESS(st)) break;
st = AtaProgramLba(dev, lba, encodedCount, false);
if (!KSUCCESS(st)) break;
for (uint32_t s = 0; s < batch; s++) {
st = AtaWaitDrq(ch);
if (!KSUCCESS(st)) break;
for (uint32_t w = 0; w < ATA_SECTOR_SIZE / 2u; w++)
*out++ = AtaIn16(ch, ATA_REG_DATA);
}
if (!KSUCCESS(st)) break;
lba += batch;
count -= batch;
}
KeSignalSemaphore(ch->Lock);
return st;
}
static KSTATUS AtaFlush(ATA_DEVICE* dev) {
ATA_CHANNEL* ch = dev->Channel;
KSTATUS st = AtaWaitNotBusy(ch);
if (!KSUCCESS(st))
return st;
AtaOut8(ch, ATA_REG_COMMAND, dev->SupportsLba48 ? ATA_CMD_CACHE_FLUSH_EXT : ATA_CMD_CACHE_FLUSH);
return AtaWaitNotBusy(ch);
}
static KSTATUS AtaWritePio(ATA_DEVICE* dev, uint64_t lba, uint32_t count, const void* buffer) {
if (!dev || !buffer)
return STATUS_INVALID_PARAMETER;
if (count == 0)
return STATUS_SUCCESS;
if (lba + count > dev->SectorCount)
return STATUS_INVALID_PARAMETER;
ATA_CHANNEL* ch = dev->Channel;
const uint16_t* in = (const uint16_t*)buffer;
KSTATUS st = KeWaitSemaphore(ch->Lock);
if (!KSUCCESS(st))
return st;
while (count > 0) {
uint16_t batch = (count > 256u) ? 256u : (uint16_t)count;
uint16_t encodedCount = (!dev->SupportsLba48 && batch == 256u) ? 0u : batch;
AtaSelectDrive(dev);
st = AtaWaitNotBusy(ch);
if (!KSUCCESS(st)) break;
st = AtaProgramLba(dev, lba, encodedCount, true);
if (!KSUCCESS(st)) break;
for (uint32_t s = 0; s < batch; s++) {
st = AtaWaitDrq(ch);
if (!KSUCCESS(st)) break;
for (uint32_t w = 0; w < ATA_SECTOR_SIZE / 2u; w++)
AtaOut16(ch, ATA_REG_DATA, *in++);
}
if (!KSUCCESS(st)) break;
st = AtaFlush(dev);
if (!KSUCCESS(st)) break;
lba += batch;
count -= batch;
}
KeSignalSemaphore(ch->Lock);
return st;
}
static KSTATUS AtaGetIrpReadWrite(IRP* irp, bool* outWrite, uint64_t* outLba,
uint32_t* outSectors, void** outBuffer) {
if (!irp || !outWrite || !outLba || !outSectors || !outBuffer)
return STATUS_INVALID_PARAMETER;
IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp);
if (!ios) return STATUS_INVALID_PARAMETER;
if (ios->MajorFunction != IRP_MJ_READ && ios->MajorFunction != IRP_MJ_WRITE)
return STATUS_INVALID_PARAMETER;
if ((ios->Parameters.ReadWrite.ByteOffset & 511ull) ||
(ios->Parameters.ReadWrite.Length & 511u))
return STATUS_INVALID_PARAMETER;
if (!irp->UserBuffer || ios->Parameters.ReadWrite.Length == 0)
return STATUS_INVALID_PARAMETER;
*outWrite = (ios->MajorFunction == IRP_MJ_WRITE);
*outLba = ios->Parameters.ReadWrite.ByteOffset >> 9;
*outSectors = ios->Parameters.ReadWrite.Length >> 9;
*outBuffer = irp->UserBuffer;
return STATUS_SUCCESS;
}
static void AtaWorker(uintptr_t arg) {
ATA_DEVICE* dev = (ATA_DEVICE*)arg;
if (!dev)
return;
while (true) {
KeWaitSemaphore(dev->IrpQueue.Semaphore);
IRP* irp = IrpQueueDequeue(&dev->IrpQueue);
if (!irp)
continue;
bool write = false;
uint64_t lba = 0;
uint32_t sectors = 0;
void* buffer = (void*)0;
KSTATUS st = AtaGetIrpReadWrite(irp, &write, &lba, &sectors, &buffer);
if (KSUCCESS(st)) {
if (lba + sectors > dev->SectorCount)
st = STATUS_INVALID_PARAMETER;
else
st = write ? AtaWritePio(dev, lba, sectors, buffer)
: AtaReadPio(dev, lba, sectors, buffer);
}
IoReleaseRemoveLock(&dev->RemoveLock, irp);
IoCompleteRequest(irp, st, KSUCCESS(st) ? ((size_t)sectors * ATA_SECTOR_SIZE) : 0);
}
}
KSTATUS AtaDispatchReadWrite(DEVICE_OBJECT* device, IRP* irp) {
if (!device || !irp || !device->DeviceExtension)
return STATUS_INVALID_PARAMETER;
ATA_DEVICE* dev = (ATA_DEVICE*)device->DeviceExtension;
if (dev->Magic != ATA_DEVICE_MAGIC || !dev->Present)
return STATUS_INVALID_PARAMETER;
KSTATUS st = IoAcquireRemoveLock(&dev->RemoveLock, irp);
if (!KSUCCESS(st))
return st;
bool write = false;
uint64_t lba = 0;
uint32_t sectors = 0;
void* buffer = (void*)0;
st = AtaGetIrpReadWrite(irp, &write, &lba, &sectors, &buffer);
(void)write;
(void)buffer;
if (!KSUCCESS(st) || lba + sectors > dev->SectorCount) {
IoReleaseRemoveLock(&dev->RemoveLock, irp);
return KSUCCESS(st) ? STATUS_INVALID_PARAMETER : st;
}
irp->Status = STATUS_FAILURE;
irp->Information = 0;
IrpQueueEnqueue(&dev->IrpQueue, irp);
return STATUS_PENDING;
}
static uint8_t AtaFindFreeDiskIndex(void) {
for (uint8_t i = 0; i < 8; i++) {
if (!IoDiskDevicePresent(i))
return i;
}
return 0xFFu;
}
static uint16_t AtaBarIoBase(ATA_CONTROLLER* ctl, uint8_t barIndex, uint16_t fallback) {
PCI_BAR bar;
KSTATUS st = PciGetBar(ctl->Pdo, barIndex, &bar);
if (KSUCCESS(st) && bar.Type == PciBarIo && bar.Base && bar.Base <= 0xFFFFu)
return (uint16_t)bar.Base;
return fallback;
}
static KSTATUS AtaInitChannel(ATA_CONTROLLER* ctl, uint8_t index) {
ATA_CHANNEL* ch = &ctl->Channels[index];
memset(ch, 0, sizeof(*ch));
ch->Controller = ctl;
ch->Index = index;
if (index == 0) {
ch->CommandBase = AtaBarIoBase(ctl, 0, ATA_PRIMARY_LEGACY_CMD_BASE);
ch->ControlBase = AtaBarIoBase(ctl, 1, ATA_PRIMARY_LEGACY_CTL_BASE);
ch->Irq = ATA_PRIMARY_LEGACY_IRQ;
} else {
ch->CommandBase = AtaBarIoBase(ctl, 2, ATA_SECONDARY_LEGACY_CMD_BASE);
ch->ControlBase = AtaBarIoBase(ctl, 3, ATA_SECONDARY_LEGACY_CTL_BASE);
ch->Irq = ATA_SECONDARY_LEGACY_IRQ;
}
if (ctl->PciInfo.ProgIf & (index == 0 ? 0x01u : 0x04u)) {
uint8_t irq = 0xFFu;
if (KSUCCESS(PciGetInterrupt(ctl->Pdo, &irq)) && irq != 0xFFu)
ch->Irq = irq;
ch->NativeMode = true;
}
KSTATUS st = KeSemaphoreCreate(1, 1, &ch->Lock);
if (!KSUCCESS(st))
return st;
AtaDeviceControl(ch, 0x02u); // nIEN: polled I/O for now.
DbgPrint("ATA: channel %u cmd=0x%04x ctl=0x%04x irq=%u mode=%s\n",
index,
ch->CommandBase,
ch->ControlBase,
ch->Irq,
ch->NativeMode ? "native" : "compat");
return STATUS_SUCCESS;
}
static KSTATUS AtaRegisterDisk(ATA_DEVICE* dev) {
DEVICE_OBJECT* disk = (DEVICE_OBJECT*)0;
KSTATUS st = IoCreateDevice(&gAtaDriver, 0, &disk);
if (!KSUCCESS(st))
return st;
disk->DeviceExtension = dev;
disk->u.Disk.SectorCount = dev->SectorCount;
disk->u.Disk.SectorSize = ATA_SECTOR_SIZE;
disk->AlignmentRequirement = ATA_SECTOR_SIZE;
(void)IoSetDeviceName(disk, SYS_SV_LITERAL("ata-disk"));
uint8_t index = AtaFindFreeDiskIndex();
if (index == 0xFFu) {
IoDeleteDevice(disk);
return STATUS_NO_MEMORY;
}
st = IoRegisterDiskDevice(index, disk);
if (!KSUCCESS(st)) {
IoDeleteDevice(disk);
return st;
}
dev->DeviceObject = disk;
dev->DiskIndex = index;
DbgPrint("ATA: drive %u registered channel=%u %s sectors=%llu (%llu MB)\n",
index,
dev->Channel->Index,
dev->Drive ? "slave" : "master",
(unsigned long long)dev->SectorCount,
(unsigned long long)(dev->SectorCount / 2048));
return STATUS_SUCCESS;
}
static KSTATUS AtaProbeDrive(ATA_CONTROLLER* ctl, ATA_CHANNEL* ch, uint8_t drive) {
ATA_DEVICE* dev = &ch->Devices[drive];
memset(dev, 0, sizeof(*dev));
dev->Magic = ATA_DEVICE_MAGIC;
dev->Controller = ctl;
dev->Channel = ch;
dev->Drive = drive;
dev->DiskIndex = 0xFFu;
KSTATUS st = AtaIdentify(dev);
if (!KSUCCESS(st))
return st;
st = IoInitializeRemoveLock(&dev->RemoveLock);
if (!KSUCCESS(st))
return st;
SEMAPHORE* sem = (SEMAPHORE*)0;
st = KeSemaphoreCreate(0, 0x7FFFFFFF, &sem);
if (!KSUCCESS(st))
return st;
IrpQueueInit(&dev->IrpQueue, sem);
dev->Present = true;
st = AtaRegisterDisk(dev);
if (!KSUCCESS(st)) {
dev->Present = false;
return st;
}
st = KeCreateKernelThread((uintptr_t)AtaWorker, (uintptr_t)dev,
PriorityHigh, &dev->WorkerThread);
if (!KSUCCESS(st))
DbgPrint("ATA: drive %u worker thread create failed (0x%x)\n", dev->DiskIndex, st);
return STATUS_SUCCESS;
}
static KSTATUS AtaRegisterController(ATA_CONTROLLER* ctl) {
if (!ctl) return STATUS_INVALID_PARAMETER;
for (uint32_t i = 0; i < gAtaControllerCount; i++) {
if (gAtaControllers[i] == ctl)
return STATUS_SUCCESS;
}
if (gAtaControllerCount >= ATA_MAX_CONTROLLERS)
return STATUS_NO_MEMORY;
gAtaControllers[gAtaControllerCount++] = ctl;
return STATUS_SUCCESS;
}
static KSTATUS AtaStartController(ATA_CONTROLLER* ctl) {
if (!ctl || ctl->Magic != ATA_CONTROLLER_MAGIC || !ctl->Pdo || !ctl->Fdo)
return STATUS_INVALID_PARAMETER;
if (ctl->Started)
return STATUS_SUCCESS;
KSTATUS st = PciGetDeviceInfo(ctl->Pdo, &ctl->PciInfo);
if (!KSUCCESS(st)) {
DbgPrint("ATA: PciGetDeviceInfo failed: 0x%x\n", st);
return st;
}
DbgPrint("ATA: controller at PCI %02x:%02x.%x vendor=%04x device=%04x progif=%02x\n",
ctl->PciInfo.Address.Bus,
ctl->PciInfo.Address.Device,
ctl->PciInfo.Address.Function,
ctl->PciInfo.VendorId,
ctl->PciInfo.DeviceId,
ctl->PciInfo.ProgIf);
st = PciEnableDevice(ctl->Pdo, PCI_ENABLE_IO | PCI_ENABLE_INTX);
if (!KSUCCESS(st)) {
DbgPrint("ATA: PciEnableDevice failed: 0x%x\n", st);
return st;
}
st = AtaRegisterController(ctl);
if (!KSUCCESS(st))
return st;
for (uint8_t chIndex = 0; chIndex < ATA_CHANNEL_COUNT; chIndex++) {
ATA_CHANNEL* ch = &ctl->Channels[chIndex];
st = AtaInitChannel(ctl, chIndex);
if (!KSUCCESS(st)) {
DbgPrint("ATA: channel %u init failed (0x%x)\n", chIndex, st);
continue;
}
for (uint8_t drive = 0; drive < ATA_DRIVES_PER_CHANNEL; drive++) {
st = AtaProbeDrive(ctl, ch, drive);
if (KSUCCESS(st))
ctl->DeviceCount++;
}
}
if (ctl->DeviceCount == 0)
DbgPrint("ATA: no ATA disks found on PCI %02x:%02x.%x\n",
ctl->PciInfo.Address.Bus,
ctl->PciInfo.Address.Device,
ctl->PciInfo.Address.Function);
ctl->Started = true;
ctl->Fdo->PnpState = DevicePnpStarted;
return STATUS_SUCCESS;
}
static void AtaCancelReleaseRemoveLock(IRP* irp, void* context) {
ATA_DEVICE* dev = (ATA_DEVICE*)context;
if (dev)
IoReleaseRemoveLock(&dev->RemoveLock, irp);
}
static void AtaRemoveControllerDevices(ATA_CONTROLLER* ctl, IO_DEVICE_CHANGE change) {
if (!ctl) return;
for (uint8_t c = 0; c < ATA_CHANNEL_COUNT; c++) {
ATA_CHANNEL* ch = &ctl->Channels[c];
for (uint8_t d = 0; d < ATA_DRIVES_PER_CHANNEL; d++) {
ATA_DEVICE* dev = &ch->Devices[d];
if (!dev->Present)
continue;
IrpQueueCancelAllEx(&dev->IrpQueue, STATUS_OBJECT_DEAD, AtaCancelReleaseRemoveLock, dev);
if (dev->DiskIndex != 0xFFu) {
if (change == IoDeviceSurpriseRemoved)
IoNotifyDiskSurpriseRemoved(dev->DiskIndex);
else
IoUnregisterDiskDevice(dev->DiskIndex);
dev->DiskIndex = 0xFFu;
}
dev->Present = false;
}
}
}
KSTATUS AtaDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
if (!device || !irp || !device->DeviceExtension)
return STATUS_INVALID_PARAMETER;
ATA_CONTROLLER* ctl = (ATA_CONTROLLER*)device->DeviceExtension;
if (ctl->Magic == ATA_CONTROLLER_MAGIC) {
IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp);
if (!ios || ios->MajorFunction != IRP_MJ_PNP)
return STATUS_INVALID_PARAMETER;
switch ((IO_PNP_MINOR_FUNCTION)ios->MinorFunction) {
case IRP_MN_START_DEVICE:
return AtaStartController(ctl);
case IRP_MN_QUERY_REMOVE_DEVICE:
case IRP_MN_CANCEL_REMOVE_DEVICE:
return STATUS_SUCCESS;
case IRP_MN_SURPRISE_REMOVAL:
ctl->SurpriseRemoved = true;
device->PnpState = DevicePnpSurpriseRemoved;
AtaRemoveControllerDevices(ctl, IoDeviceSurpriseRemoved);
return STATUS_SUCCESS;
case IRP_MN_REMOVE_DEVICE:
ctl->Removed = true;
device->PnpState = DevicePnpDeleted;
AtaRemoveControllerDevices(ctl, IoDeviceRemoved);
IoDetachDevice(device);
IoDeleteDevice(device);
return STATUS_SUCCESS;
default:
return IoForwardIrpToLower(device, irp);
}
}
ATA_DEVICE* dev = (ATA_DEVICE*)device->DeviceExtension;
if (dev->Magic == ATA_DEVICE_MAGIC) {
IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp);
if (!ios || ios->MajorFunction != IRP_MJ_PNP)
return STATUS_INVALID_PARAMETER;
switch ((IO_PNP_MINOR_FUNCTION)ios->MinorFunction) {
case IRP_MN_START_DEVICE:
device->PnpState = DevicePnpStarted;
return STATUS_SUCCESS;
case IRP_MN_QUERY_REMOVE_DEVICE:
case IRP_MN_CANCEL_REMOVE_DEVICE:
return STATUS_SUCCESS;
case IRP_MN_SURPRISE_REMOVAL:
device->PnpState = DevicePnpSurpriseRemoved;
dev->Present = false;
IrpQueueCancelAllEx(&dev->IrpQueue, STATUS_OBJECT_DEAD, AtaCancelReleaseRemoveLock, dev);
return STATUS_SUCCESS;
case IRP_MN_REMOVE_DEVICE:
device->PnpState = DevicePnpDeleted;
dev->Present = false;
IrpQueueCancelAllEx(&dev->IrpQueue, STATUS_OBJECT_DEAD, AtaCancelReleaseRemoveLock, dev);
IoReleaseRemoveLockAndWait(&dev->RemoveLock, irp);
IoDeleteDevice(device);
dev->DeviceObject = (DEVICE_OBJECT*)0;
return STATUS_SUCCESS;
default:
return STATUS_NOT_SUPPORTED;
}
}
return STATUS_INVALID_PARAMETER;
}
KSTATUS AtaAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject) {
if (!driver || !physicalDeviceObject || physicalDeviceObject->Type != DeviceObjectPhysical)
return STATUS_INVALID_PARAMETER;
DEVICE_OBJECT* fdo = (DEVICE_OBJECT*)0;
KSTATUS st = IoCreateDevice(driver, sizeof(ATA_CONTROLLER), &fdo);
if (!KSUCCESS(st))
return st;
(void)IoSetDeviceName(fdo, SYS_SV_LITERAL("ata-controller"));
ATA_CONTROLLER* ctl = (ATA_CONTROLLER*)fdo->DeviceExtension;
memset(ctl, 0, sizeof(*ctl));
ctl->Magic = ATA_CONTROLLER_MAGIC;
ctl->Fdo = fdo;
ctl->Pdo = physicalDeviceObject;
ctl->LowerDevice = IoAttachDeviceToDeviceStack(fdo, physicalDeviceObject);
if (!ctl->LowerDevice) {
IoDeleteDevice(fdo);
return STATUS_FAILURE;
}
DbgPrint("ATA: FDO attached to %v\n", physicalDeviceObject->Name);
return STATUS_SUCCESS;
}
KSTATUS DriverEntry(const HAL_API* hal) {
if (!hal)
return STATUS_INVALID_PARAMETER;
DbgPrint("ATA: driver loading...\n");
gAtaHal = hal;
gAtaControllerCount = 0;
memset(&gAtaDriver, 0, sizeof(gAtaDriver));
IoInitializeDriverObject(&gAtaDriver, SYS_SV_LITERAL("ata"));
gAtaDriver.IdTable = &gAtaIdTable;
gAtaDriver.DriverAddDevice = AtaAddDevice;
gAtaDriver.MajorFunction[IRP_MJ_READ] = AtaDispatchReadWrite;
gAtaDriver.MajorFunction[IRP_MJ_WRITE] = AtaDispatchReadWrite;
gAtaDriver.MajorFunction[IRP_MJ_PNP] = AtaDispatchPnp;
KSTATUS st = IoRegisterDriverObject(&gAtaDriver);
if (!KSUCCESS(st))
DbgPrint("ATA: IoRegisterDriverObject failed: 0x%x\n", st);
return st;
}
+130
View File
@@ -0,0 +1,130 @@
// ata.h - PCI IDE / ATA driver internals.
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <intrin.h>
#include <ntlite/ddi/hal_api.h>
#include <ntlite/ddi/io_driver.h>
#include <ntlite/ddi/pci.h>
#include <ntlite/status.h>
#include <kernel/ke_debug.h>
#include <kernel/ke_irp.h>
#include <kernel/ke_sched.h>
#include <kernel/ke_semaphore.h>
#include <kernel/ke_thread.h>
#include <kernel/mm_phys.h>
#include <kernel/mm_virt.h>
#define ATA_CONTROLLER_MAGIC 0x41544143u // ATAC
#define ATA_DEVICE_MAGIC 0x41544144u // ATAD
#define ATA_MAX_CONTROLLERS 8u
#define ATA_CHANNEL_COUNT 2u
#define ATA_DRIVES_PER_CHANNEL 2u
#define ATA_MAX_DEVICES_PER_CTL (ATA_CHANNEL_COUNT * ATA_DRIVES_PER_CHANNEL)
#define ATA_PRIMARY_LEGACY_CMD_BASE 0x1F0u
#define ATA_PRIMARY_LEGACY_CTL_BASE 0x3F6u
#define ATA_PRIMARY_LEGACY_IRQ 14u
#define ATA_SECONDARY_LEGACY_CMD_BASE 0x170u
#define ATA_SECONDARY_LEGACY_CTL_BASE 0x376u
#define ATA_SECONDARY_LEGACY_IRQ 15u
#define ATA_REG_DATA 0x00u
#define ATA_REG_ERROR 0x01u
#define ATA_REG_FEATURES 0x01u
#define ATA_REG_SECTOR_COUNT 0x02u
#define ATA_REG_LBA0 0x03u
#define ATA_REG_LBA1 0x04u
#define ATA_REG_LBA2 0x05u
#define ATA_REG_DRIVE_HEAD 0x06u
#define ATA_REG_STATUS 0x07u
#define ATA_REG_COMMAND 0x07u
#define ATA_REG_ALT_STATUS 0x00u
#define ATA_REG_DEVICE_CONTROL 0x00u
#define ATA_STATUS_ERR 0x01u
#define ATA_STATUS_DRQ 0x08u
#define ATA_STATUS_DF 0x20u
#define ATA_STATUS_DRDY 0x40u
#define ATA_STATUS_BSY 0x80u
#define ATA_CMD_READ_SECTORS 0x20u
#define ATA_CMD_WRITE_SECTORS 0x30u
#define ATA_CMD_CACHE_FLUSH 0xE7u
#define ATA_CMD_IDENTIFY_DEVICE 0xECu
#define ATA_CMD_READ_SECTORS_EXT 0x24u
#define ATA_CMD_WRITE_SECTORS_EXT 0x34u
#define ATA_CMD_CACHE_FLUSH_EXT 0xEAu
#define ATA_IDENT_CAPABILITIES 49u
#define ATA_IDENT_COMMAND_SETS 83u
#define ATA_IDENT_LBA28_SECTORS_LO 60u
#define ATA_IDENT_LBA28_SECTORS_HI 61u
#define ATA_IDENT_LBA48_SECTORS 100u
#define ATA_IDENT_LBA_SUPPORTED (1u << 9)
#define ATA_IDENT_LBA48_SUPPORTED (1u << 10)
#define ATA_SECTOR_SIZE 512u
#define ATA_POLL_LIMIT 100000u
struct ATA_CONTROLLER;
struct ATA_CHANNEL;
typedef struct ATA_DEVICE {
uint32_t Magic;
struct ATA_CONTROLLER* Controller;
struct ATA_CHANNEL* Channel;
DEVICE_OBJECT* DeviceObject;
uint8_t Drive; // 0=master, 1=slave
uint8_t DiskIndex;
bool Present;
bool SupportsLba48;
uint64_t SectorCount;
IRP_QUEUE IrpQueue;
SEMAPHORE* IrpSemaphore;
IO_REMOVE_LOCK RemoveLock;
THREAD* WorkerThread;
} ATA_DEVICE;
typedef struct ATA_CHANNEL {
struct ATA_CONTROLLER* Controller;
uint8_t Index;
uint16_t CommandBase;
uint16_t ControlBase;
uint8_t Irq;
bool NativeMode;
SEMAPHORE* Lock;
ATA_DEVICE Devices[ATA_DRIVES_PER_CHANNEL];
} ATA_CHANNEL;
typedef struct ATA_CONTROLLER {
uint32_t Magic;
DEVICE_OBJECT* Fdo;
DEVICE_OBJECT* Pdo;
DEVICE_OBJECT* LowerDevice;
PCI_DEVICE_INFO PciInfo;
bool Started;
bool Removed;
bool SurpriseRemoved;
ATA_CHANNEL Channels[ATA_CHANNEL_COUNT];
uint8_t DeviceCount;
} ATA_CONTROLLER;
extern const HAL_API* gAtaHal;
KSTATUS AtaAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject);
KSTATUS AtaDispatchPnp(DEVICE_OBJECT* device, IRP* irp);
KSTATUS AtaDispatchReadWrite(DEVICE_OBJECT* device, IRP* irp);
-531
View File
@@ -1,531 +0,0 @@
// ide.c - IDE/ATA disk I/O path and DriverEntry.
#include "ide.h"
// ---------------------------------------------------------------------------
// Module state
// ---------------------------------------------------------------------------
const HAL_API *gHal;
DRIVER_OBJECT gDriver;
DEVICE_OBJECT *gDevice = NULL;
bool gDmaEnabled = false;
uint64_t gSectorCount = 0;
bool gSupportsLba48 = false;
// DMA hardware state
uint16_t gBmBase = 0; // Bus Master I/O base port (BAR4 & ~3)
PRD *gPrdtVirt = NULL; // kernel VA of PRDT page
uint32_t gPrdtPhys = 0; // 32-bit physical address of PRDT
// Async worker synchronisation
IRP_QUEUE gIrpQueue;
SEMAPHORE *gIrpSemaphore = NULL;
EVENT *gDmaEvent = NULL; // auto-reset; DPC signals after each xfer
KDPC gDmaDpc; // bottom half for DMA completion
IO_REMOVE_LOCK gRemoveLock;
bool gSurpriseRemoved = false;
bool gRemoved = false;
// ---------------------------------------------------------------------------
// Bus Master I/O helpers
// ---------------------------------------------------------------------------
static inline uint8_t BmRead8 (uint8_t reg) { return __inbyte (gBmBase + reg); }
static inline void BmWrite8 (uint8_t reg, uint8_t v) { __outbyte (gBmBase + reg, v); }
static inline void BmWrite32(uint8_t reg, uint32_t v) { __outdword(gBmBase + reg, v); }
// ---------------------------------------------------------------------------
// PIO polling helpers (unchanged)
// ---------------------------------------------------------------------------
static void IdeWait(void) {
while (__inbyte(IDE_STATUS) & IDE_BUSY)
KeYield();
}
static KSTATUS IdeWaitData(void) {
for (int i = 0; i < 4; i++) __inbyte(IDE_ALT); // 400 ns delay
while (true) {
uint8_t st = __inbyte(IDE_STATUS);
if (st & IDE_ERR_F) return STATUS_DEVICE_ERROR;
if (!(st & IDE_BUSY) && (st & IDE_DRQ)) return STATUS_SUCCESS;
KeYield();
}
}
// ---------------------------------------------------------------------------
// PIO read/write — sync fallback when DMA is unavailable
// ---------------------------------------------------------------------------
static KSTATUS IdeRead(void *ctx, uint64_t lba, uint32_t count, void *buf) {
(void)ctx;
if (lba + count > gSectorCount) return STATUS_INVALID_PARAMETER;
uint16_t *p = (uint16_t *)buf;
for (uint32_t i = 0; i < count; i++, lba++) {
IdeWait();
if (gSupportsLba48) {
__outbyte(IDE_DRIVE, 0x40);
__outbyte(IDE_SECCNT, 0);
__outbyte(IDE_LBA0, (lba >> 24) & 0xFF);
__outbyte(IDE_LBA1, (lba >> 32) & 0xFF);
__outbyte(IDE_LBA2, (lba >> 40) & 0xFF);
__outbyte(IDE_SECCNT, 1);
__outbyte(IDE_LBA0, (uint8_t)lba);
__outbyte(IDE_LBA1, (uint8_t)(lba >> 8));
__outbyte(IDE_LBA2, (uint8_t)(lba >> 16));
__outbyte(IDE_CMD, CMD_READ_EXT);
} else {
__outbyte(IDE_DRIVE, 0xE0 | ((lba >> 24) & 0xF));
__outbyte(IDE_SECCNT, 1);
__outbyte(IDE_LBA0, (uint8_t)lba);
__outbyte(IDE_LBA1, (uint8_t)(lba >> 8));
__outbyte(IDE_LBA2, (uint8_t)(lba >> 16));
__outbyte(IDE_CMD, 0x20);
}
if (IdeWaitData() != STATUS_SUCCESS) return STATUS_DEVICE_ERROR;
for (int w = 0; w < 256; w++) *p++ = __inword(IDE_DATA);
}
return STATUS_SUCCESS;
}
static KSTATUS IdeWrite(void *ctx, uint64_t lba, uint32_t count, const void *buf) {
(void)ctx;
if (lba + count > gSectorCount) return STATUS_INVALID_PARAMETER;
const uint16_t *p = (const uint16_t *)buf;
for (uint32_t i = 0; i < count; i++, lba++) {
IdeWait();
if (gSupportsLba48) {
__outbyte(IDE_DRIVE, 0x40);
__outbyte(IDE_SECCNT, 0);
__outbyte(IDE_LBA0, (lba >> 24) & 0xFF);
__outbyte(IDE_LBA1, (lba >> 32) & 0xFF);
__outbyte(IDE_LBA2, (lba >> 40) & 0xFF);
__outbyte(IDE_SECCNT, 1);
__outbyte(IDE_LBA0, (uint8_t)lba);
__outbyte(IDE_LBA1, (uint8_t)(lba >> 8));
__outbyte(IDE_LBA2, (uint8_t)(lba >> 16));
__outbyte(IDE_CMD, CMD_WRITE_EXT);
} else {
__outbyte(IDE_DRIVE, 0xE0 | ((lba >> 24) & 0xF));
__outbyte(IDE_SECCNT, 1);
__outbyte(IDE_LBA0, (uint8_t)lba);
__outbyte(IDE_LBA1, (uint8_t)(lba >> 8));
__outbyte(IDE_LBA2, (uint8_t)(lba >> 16));
__outbyte(IDE_CMD, 0x30);
}
IdeWait();
for (int w = 0; w < 256; w++) __outword(IDE_DATA, *p++);
__outbyte(IDE_ALT, 0); // flush write cache
if (IdeWaitData() != STATUS_SUCCESS) return STATUS_DEVICE_ERROR;
}
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// BuildPrdt — fill gPrdtVirt[] for a transfer.
//
// Walks the virtual buffer in page-aligned chunks, looks up each chunk's
// physical address via MmGetPhysicalAddress, and coalesces adjacent physical
// pages into a single PRD entry where possible. This is correct regardless of
// whether the pool gave us physically contiguous pages.
//
// Constraints enforced:
// - Each PRD entry covers at most one 4 KB physical page (never crosses
// a page boundary), so the physical address is valid for the whole chunk.
// - Total byte count per entry is ≤ 64 KB (the PRD limit).
// - Physical address must fit in 32 bits (BM IDE hardware limit).
//
// Returns the number of PRD entries written, or 0 on error.
// ---------------------------------------------------------------------------
static uint32_t BuildPrdt(void *virtBuf, uint32_t byteLen) {
uint32_t nPrd = 0;
uintptr_t va = (uintptr_t)virtBuf;
uint32_t remain = byteLen;
while (remain > 0) {
if (nPrd >= PRDT_MAX_ENTRIES) {
DbgPrint("IDE DMA: transfer too large for PRDT (%u entries)\n", nPrd);
return 0;
}
// Bytes from va to the end of its 4 KB page (never straddles a page).
uint32_t pageOff = (uint32_t)(va & 0xFFF);
uint32_t inPage = 0x1000 - pageOff;
uint32_t chunk = (remain < inPage) ? remain : inPage;
PHYSICAL_ADDRESS phys = MmGetPhysicalAddress(va);
if (phys >> 32) {
// Physical address above 4 GB — BM IDE's 32-bit PRDT can't reach it.
DbgPrint("IDE DMA: buffer page at VA 0x%016llX has phys 0x%016llX > 4 GB\n",
(uint64_t)va, (uint64_t)phys);
return 0;
}
uint32_t phys32 = (uint32_t)phys;
// Try to coalesce with the previous PRD entry if the physical regions
// are adjacent and the combined size stays within the 64 KB limit.
if (nPrd > 0) {
PRD *prev = &gPrdtVirt[nPrd - 1];
uint32_t prevSz = prev->byteCount ? prev->byteCount : 0x10000u;
uint32_t prevEnd = prev->physAddr + prevSz;
uint32_t combined = prevSz + chunk;
if (prevEnd == phys32 && combined <= 0x10000u) {
prev->byteCount = (uint16_t)(combined == 0x10000u ? 0u : combined);
va += chunk;
remain -= chunk;
continue;
}
}
gPrdtVirt[nPrd].physAddr = phys32;
gPrdtVirt[nPrd].byteCount = (uint16_t)(chunk == 0x10000u ? 0u : chunk);
gPrdtVirt[nPrd].flags = 0;
nPrd++;
va += chunk;
remain -= chunk;
}
if (nPrd > 0)
gPrdtVirt[nPrd - 1].flags |= PRD_EOT;
return nPrd;
}
// ---------------------------------------------------------------------------
// IDE DMA completion DPC. The ISR owns hardware acknowledgement; this bottom
// half wakes the worker outside hard IRQ/DIRQL context.
// ---------------------------------------------------------------------------
void IdeDmaCompletionDpc(KDPC *dpc, void *context,
void *systemArgument1, void *systemArgument2) {
(void)dpc;
(void)context;
(void)systemArgument1;
(void)systemArgument2;
if (gDmaEvent)
KeSignalEvent(gDmaEvent);
}
// ---------------------------------------------------------------------------
// IDE ISR (IRQ 14 — primary channel)
//
// Must be fast — runs with interrupts disabled. We only clear the hardware
// interrupt bits here and queue a DPC to wake the worker thread.
// The worker reads the final status and fires IRP completion hooks.
// ---------------------------------------------------------------------------
void IdeIsr(void) {
uint8_t bmSt = BmRead8(BM_STATUS_REG);
if (!(bmSt & BM_STATUS_IRQ))
return; // not our interrupt (spurious or secondary channel)
// Stop the Bus Master engine.
BmWrite8(BM_CMD_REG, 0x00);
// Clear interrupt and error sticky bits (write-1-to-clear).
BmWrite8(BM_STATUS_REG, BM_STATUS_IRQ | BM_STATUS_ERROR);
// Read IDE status and alt-status to fully acknowledge the device interrupt.
(void)__inbyte(IDE_ALT);
(void)__inbyte(IDE_STATUS);
// Wake happens in the DPC. gDmaEvent is auto-reset so exactly one waiter
// is released when the bottom half runs.
KeInsertQueueDpc(&gDmaDpc, (void *)0, (void *)0);
}
// ---------------------------------------------------------------------------
// IdeDmaRw — program the hardware and block until the ISR completes it.
//
// Called exclusively from IdeWorker. Has full control of the IDE/BM
// registers; no other thread touches them while the worker is running.
// Falls back to PIO inline if BuildPrdt fails (address space issue).
// ---------------------------------------------------------------------------
static KSTATUS IdeDmaRw(uint64_t lba, uint32_t count, void *buf, bool write) {
uint32_t byteLen = count * 512u;
uint32_t nPrd = BuildPrdt(buf, byteLen);
if (nPrd == 0) {
// Graceful inline PIO fallback for awkward buffers.
DbgPrint("IDE DMA: BuildPrdt failed — falling back to PIO\n");
return write ? IdeWrite(NULL, lba, count, buf)
: IdeRead (NULL, lba, count, buf);
}
// ---- Program Bus Master -----------------------------------------------
// Load PRDT physical address.
BmWrite32(BM_PRDT_REG, gPrdtPhys);
// Clear any leftover interrupt/error bits from the previous transfer.
BmWrite8(BM_STATUS_REG, BM_STATUS_IRQ | BM_STATUS_ERROR);
// Set direction: BM_CMD_READ = disk→memory; 0 = memory→disk.
// Do NOT set BM_CMD_START yet — spec says program ATA regs first.
BmWrite8(BM_CMD_REG, write ? 0x00 : BM_CMD_READ);
// ---- Program ATA registers -------------------------------------------
IdeWait();
if (gSupportsLba48) {
__outbyte(IDE_DRIVE, 0x40);
__outbyte(IDE_SECCNT, (uint8_t)(count >> 8)); // sector count high byte
__outbyte(IDE_LBA0, (uint8_t)(lba >> 24));
__outbyte(IDE_LBA1, (uint8_t)(lba >> 32));
__outbyte(IDE_LBA2, (uint8_t)(lba >> 40));
__outbyte(IDE_SECCNT, (uint8_t)count); // sector count low byte
__outbyte(IDE_LBA0, (uint8_t)lba);
__outbyte(IDE_LBA1, (uint8_t)(lba >> 8));
__outbyte(IDE_LBA2, (uint8_t)(lba >> 16));
__outbyte(IDE_CMD, write ? CMD_WRITE_DMA_EXT : CMD_READ_DMA_EXT);
} else {
__outbyte(IDE_DRIVE, 0xE0 | ((lba >> 24) & 0xF));
__outbyte(IDE_SECCNT, (uint8_t)count);
__outbyte(IDE_LBA0, (uint8_t)lba);
__outbyte(IDE_LBA1, (uint8_t)(lba >> 8));
__outbyte(IDE_LBA2, (uint8_t)(lba >> 16));
__outbyte(IDE_CMD, write ? CMD_WRITE_DMA : CMD_READ_DMA);
}
// ---- Start DMA --------------------------------------------------------
// Set Start bit. Direction bit is already set from above.
BmWrite8(BM_CMD_REG, (write ? 0x00 : BM_CMD_READ) | BM_CMD_START);
// Block the worker thread until the ISR fires.
KeWaitForEvent(gDmaEvent, 0);
// ---- Read result -------------------------------------------------------
// ISR already stopped the BM engine and cleared BM_STATUS_IRQ.
// We still need to check BM_STATUS_ERROR and IDE_ERR_F.
uint8_t bmSt = BmRead8(BM_STATUS_REG);
uint8_t ideSt = __inbyte(IDE_STATUS);
if (bmSt & BM_STATUS_ERROR) {
DbgPrint("IDE DMA: BM error (bmStatus=0x%02x, ideStatus=0x%02x, ideErr=0x%02x)\n",
bmSt, ideSt, __inbyte(IDE_ERR));
return STATUS_DEVICE_ERROR;
}
if (ideSt & IDE_ERR_F) {
DbgPrint("IDE DMA: ATA error (ideStatus=0x%02x, ideErr=0x%02x)\n",
ideSt, __inbyte(IDE_ERR));
return STATUS_DEVICE_ERROR;
}
return STATUS_SUCCESS;
}
static KSTATUS IdeGetIrpReadWrite(IRP* irp, bool* outWrite, uint64_t* outLba,
uint32_t* outSectors, void** outBuffer) {
if (!irp || !outWrite || !outLba || !outSectors || !outBuffer)
return STATUS_INVALID_PARAMETER;
IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp);
if (!ios) return STATUS_INVALID_PARAMETER;
if (ios->MajorFunction != IRP_MJ_READ && ios->MajorFunction != IRP_MJ_WRITE)
return STATUS_INVALID_PARAMETER;
if ((ios->Parameters.ReadWrite.ByteOffset & 511ull) ||
(ios->Parameters.ReadWrite.Length & 511u))
return STATUS_INVALID_PARAMETER;
if (!irp->UserBuffer || ios->Parameters.ReadWrite.Length == 0)
return STATUS_INVALID_PARAMETER;
*outWrite = (ios->MajorFunction == IRP_MJ_WRITE);
*outLba = ios->Parameters.ReadWrite.ByteOffset >> 9;
*outSectors = ios->Parameters.ReadWrite.Length >> 9;
*outBuffer = irp->UserBuffer;
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// IdeWorker — kernel thread, one per IDE controller.
//
// Blocks on gIrpSemaphore. Each IrpQueueEnqueue increments it by one, so
// the worker wakes once per submitted IRP. Mirrors AhciWorker exactly.
// ---------------------------------------------------------------------------
void IdeWorker(uintptr_t arg) {
(void)arg;
while (true) {
KeWaitSemaphore(gIrpSemaphore);
IRP *irp = IrpQueueDequeue(&gIrpQueue);
if (!irp)
continue;
bool write = false;
uint64_t lba = 0;
uint32_t sectors = 0;
void* buffer = (void*)0;
KSTATUS st = IdeGetIrpReadWrite(irp, &write, &lba, &sectors, &buffer);
if (KSUCCESS(st)) {
if (lba + sectors > gSectorCount)
st = STATUS_INVALID_PARAMETER;
else
st = IdeDmaRw(lba, sectors, buffer, write);
}
IoReleaseRemoveLock(&gRemoveLock, irp);
IoCompleteRequest(irp, st, KSUCCESS(st) ? ((size_t)sectors * 512u) : 0);
}
}
// ---------------------------------------------------------------------------
// IdeDispatchReadWrite — DEVICE_OBJECT dispatch entry for disk IRPs.
//
// If DMA is online, the IRP is queued to the worker and completed later.
// If DMA is unavailable, the IRP is completed inline through the PIO path.
// Either way, accepted IRPs return STATUS_PENDING and complete through
// IoCompleteRequest().
// ---------------------------------------------------------------------------
static KSTATUS IdeDispatchReadWrite(DEVICE_OBJECT *device, IRP *irp) {
(void)device;
KSTATUS st = IoAcquireRemoveLock(&gRemoveLock, irp);
if (!KSUCCESS(st))
return st;
bool write = false;
uint64_t lba = 0;
uint32_t sectors = 0;
void* buffer = (void*)0;
st = IdeGetIrpReadWrite(irp, &write, &lba, &sectors, &buffer);
if (!KSUCCESS(st) || lba + sectors > gSectorCount) {
IoReleaseRemoveLock(&gRemoveLock, irp);
return KSUCCESS(st) ? STATUS_INVALID_PARAMETER : st;
}
irp->Status = STATUS_FAILURE;
irp->Information = 0;
if (gDmaEnabled) {
IrpQueueEnqueue(&gIrpQueue, irp);
return STATUS_PENDING;
}
st = write ? IdeWrite(NULL, lba, sectors, buffer)
: IdeRead (NULL, lba, sectors, buffer);
IoReleaseRemoveLock(&gRemoveLock, irp);
IoCompleteRequest(irp, st, KSUCCESS(st) ? ((size_t)sectors * 512u) : 0);
return STATUS_PENDING;
}
static void IdeCancelReleaseRemoveLock(IRP* irp, void* context) {
(void)context;
IoReleaseRemoveLock(&gRemoveLock, irp);
}
static KSTATUS IdeDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
if (!device || !irp) return STATUS_INVALID_PARAMETER;
IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp);
if (!ios || ios->MajorFunction != IRP_MJ_PNP)
return STATUS_INVALID_PARAMETER;
switch ((IO_PNP_MINOR_FUNCTION)ios->MinorFunction) {
case IRP_MN_START_DEVICE:
device->PnpState = DevicePnpStarted;
return STATUS_SUCCESS;
case IRP_MN_QUERY_REMOVE_DEVICE:
device->PnpState = DevicePnpRemovePending;
return STATUS_SUCCESS;
case IRP_MN_CANCEL_REMOVE_DEVICE:
device->PnpState = DevicePnpStarted;
return STATUS_SUCCESS;
case IRP_MN_SURPRISE_REMOVAL:
gSurpriseRemoved = true;
device->PnpState = DevicePnpSurpriseRemoved;
IrpQueueCancelAllEx(&gIrpQueue, STATUS_OBJECT_DEAD, IdeCancelReleaseRemoveLock, (void*)0);
return STATUS_SUCCESS;
case IRP_MN_REMOVE_DEVICE:
gRemoved = true;
device->PnpState = DevicePnpDeleted;
IrpQueueCancelAllEx(&gIrpQueue, STATUS_OBJECT_DEAD, IdeCancelReleaseRemoveLock, (void*)0);
IoReleaseRemoveLockAndWait(&gRemoveLock, irp);
IoDeleteDevice(device);
gDevice = (DEVICE_OBJECT*)0;
return STATUS_SUCCESS;
default:
return STATUS_NOT_SUPPORTED;
}
}
// ---------------------------------------------------------------------------
// IdeDmaInit — locate the PCI IDE controller, grab BAR4, allocate the PRDT,
// create sync objects, and register IRQ 14.
//
// Returns true on success. On any failure, logs a message and returns false;
// dispatch will complete IRPs inline through PIO.
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// DriverEntry
// ---------------------------------------------------------------------------
KSTATUS DriverEntry(const HAL_API *hal) {
DbgPrint("IDE: Init\n");
gHal = hal;
// ---- IDENTIFY drive (same as before) ----------------------------------
__outbyte(IDE_DRIVE, 0xA0);
for (int i = 0; i < 4; i++) __inbyte(IDE_ALT);
__outbyte(IDE_SECCNT, 0);
__outbyte(IDE_LBA0, 0);
__outbyte(IDE_LBA1, 0);
__outbyte(IDE_LBA2, 0);
__outbyte(IDE_CMD, CMD_IDENT);
if (__inbyte(IDE_STATUS) == 0) {
DbgPrint("IDE: No drive on primary channel\n");
return STATUS_NOT_FOUND;
}
int timeout = 10000;
while (__inbyte(IDE_STATUS) & IDE_BUSY) {
if (--timeout == 0) return STATUS_DEVICE_ERROR;
KeYield();
}
if (__inbyte(IDE_LBA1) != 0 || __inbyte(IDE_LBA2) != 0) {
DbgPrint("IDE: Not a standard ATA drive (ATAPI?)\n");
return STATUS_NOT_SUPPORTED;
}
timeout = 10000;
while (true) {
uint8_t st = __inbyte(IDE_STATUS);
if (st & IDE_ERR_F) return STATUS_DEVICE_ERROR;
if (st & IDE_DRQ) break;
if (--timeout == 0) return STATUS_DEVICE_ERROR;
KeYield();
}
uint16_t ident[256];
for (int i = 0; i < 256; i++) ident[i] = __inword(IDE_DATA);
gSupportsLba48 = (ident[83] & (1 << 10)) != 0;
if (gSupportsLba48) {
gSectorCount = *(uint64_t *)&ident[100];
DbgPrint("IDE: LBA48, %llu sectors\n", gSectorCount);
} else {
gSectorCount = ((uint32_t)ident[61] << 16) | ident[60];
DbgPrint("IDE: LBA28, %u sectors\n", (uint32_t)gSectorCount);
}
if (gSectorCount == 0) return STATUS_NOT_FOUND;
// ---- Register definitive DEVICE_OBJECT --------------------------------
memset(&gDriver, 0, sizeof(gDriver));
IoInitializeDriverObject(&gDriver, SYS_SV_LITERAL("ide"));
gDriver.MajorFunction[IRP_MJ_READ] = IdeDispatchReadWrite;
gDriver.MajorFunction[IRP_MJ_WRITE] = IdeDispatchReadWrite;
gDriver.MajorFunction[IRP_MJ_PNP] = IdeDispatchPnp;
KSTATUS st = IoInitializeRemoveLock(&gRemoveLock);
if (!KSUCCESS(st)) return st;
st = IoCreateDevice(&gDriver, 0, &gDevice);
if (!KSUCCESS(st)) return st;
gDevice->u.Disk.SectorCount = gSectorCount;
gDevice->u.Disk.SectorSize = 512;
gDevice->AlignmentRequirement = 512;
// ---- Try to initialise Bus Master DMA ---------------------------------
gDmaEnabled = IdeDmaInit();
DbgPrint("IDE: Registered (%s)\n", gDmaEnabled ? "DMA" : "PIO fallback");
return IoRegisterDiskDevice(0, gDevice);
}
-102
View File
@@ -1,102 +0,0 @@
// ide.h - shared IDE/ATA driver internals.
#pragma once
#include <stdint.h>
#include <string.h>
#include <intrin.h>
#include <ntlite/ddi/io_driver.h>
#include <ntlite/ddi/hal_api.h>
#include <kernel/ke_debug.h>
#include <kernel/ke_sched.h>
#include <kernel/ke_irp.h>
#include <kernel/ke_event.h>
#include <kernel/ke_semaphore.h>
#include <kernel/ke_thread.h>
#include <kernel/ke_irq.h>
#include <kernel/ke_dpc.h>
#include <kernel/mm_phys.h>
#include <kernel/mm_virt.h>
// ---------------------------------------------------------------------------
// IDE register map (primary channel, base 0x1F0)
// ---------------------------------------------------------------------------
#define IDE_DATA 0x1F0
#define IDE_ERR 0x1F1
#define IDE_SECCNT 0x1F2
#define IDE_LBA0 0x1F3
#define IDE_LBA1 0x1F4
#define IDE_LBA2 0x1F5
#define IDE_DRIVE 0x1F6
#define IDE_STATUS 0x1F7
#define IDE_CMD 0x1F7
#define IDE_ALT 0x3F6
#define IDE_BUSY 0x80
#define IDE_READY 0x40
#define IDE_ERR_F 0x01
#define IDE_DRQ 0x08
// ATA commands
#define CMD_READ_EXT 0x24 // PIO 48-bit read
#define CMD_WRITE_EXT 0x34 // PIO 48-bit write
#define CMD_READ_DMA_EXT 0x25 // DMA 48-bit read
#define CMD_WRITE_DMA_EXT 0x35 // DMA 48-bit write
#define CMD_READ_DMA 0xC8 // DMA 28-bit read
#define CMD_WRITE_DMA 0xCA // DMA 28-bit write
#define CMD_IDENT 0xEC
// IDE primary IRQ
#define IDE_IRQ_PRIMARY 14
// ---------------------------------------------------------------------------
// Bus Master IDE register offsets (relative to gBmBase, primary channel)
// ---------------------------------------------------------------------------
#define BM_CMD_REG 0x00 // command
#define BM_STATUS_REG 0x02 // status
#define BM_PRDT_REG 0x04 // PRDT physical address (32-bit)
// BM_CMD_REG bits
#define BM_CMD_START 0x01 // start DMA transfer
#define BM_CMD_READ 0x08 // 1 = disk→memory (read), 0 = memory→disk (write)
// BM_STATUS_REG bits
#define BM_STATUS_ACTIVE 0x01 // DMA active
#define BM_STATUS_ERROR 0x02 // DMA error (write 1 to clear)
#define BM_STATUS_IRQ 0x04 // interrupt pending (write 1 to clear)
// ---------------------------------------------------------------------------
// Physical Region Descriptor (PRDT entry, 8 bytes)
// The PRDT itself must not cross a 64 KB boundary and must be 4-byte aligned.
// We allocate a full page so both constraints are trivially satisfied.
// ---------------------------------------------------------------------------
#pragma pack(push, 1)
typedef struct {
uint32_t physAddr; // physical base of this memory region
uint16_t byteCount; // byte count; 0 means 64 KB
uint16_t flags; // bit 15: End Of Table
} PRD;
#pragma pack(pop)
#define PRD_EOT 0x8000u
// 512 entries × 8 bytes = 4096 bytes = exactly one page; supports 512×64 KB = 32 MB
#define PRDT_MAX_ENTRIES 512
extern const HAL_API *gHal;
extern DRIVER_OBJECT gDriver;
extern DEVICE_OBJECT *gDevice;
extern bool gDmaEnabled;
extern uint64_t gSectorCount;
extern bool gSupportsLba48;
extern uint16_t gBmBase;
extern PRD *gPrdtVirt;
extern uint32_t gPrdtPhys;
extern IRP_QUEUE gIrpQueue;
extern SEMAPHORE *gIrpSemaphore;
extern EVENT *gDmaEvent;
extern KDPC gDmaDpc;
void IdeDmaCompletionDpc(KDPC *dpc, void *context, void *systemArgument1, void *systemArgument2);
void IdeIsr(void);
void IdeWorker(uintptr_t arg);
bool IdeDmaInit(void);
-87
View File
@@ -1,87 +0,0 @@
// ide_bus.c - Bus-master IDE PCI/DMA initialisation.
#include "ide.h"
bool IdeDmaInit(void) {
// ---- Find PCI IDE controller (class 0x01, subclass 0x01) --------------
uint8_t bus, dev, fn;
if (!gHal->Pci.FindClass(0x01, 0x01, &bus, &dev, &fn)) {
DbgPrint("IDE DMA: No PCI IDE controller found — using PIO\n");
return false;
}
// ---- Read BAR4 (Bus Master base, I/O space) ---------------------------
// PCI config offset 0x20. Bit 0 must be 1 for I/O BAR.
uint32_t bar4 = gHal->Pci.Read32(bus, dev, fn, 0x20);
if (!(bar4 & 0x1)) {
DbgPrint("IDE DMA: BAR4 is a memory BAR (0x%08x) — unexpected, using PIO\n", bar4);
return false;
}
uint16_t bmBase = (uint16_t)(bar4 & 0xFFFC);
if (bmBase == 0) {
DbgPrint("IDE DMA: BAR4 is zero (firmware didn't assign it) — using PIO\n");
return false;
}
DbgPrint("IDE DMA: Bus Master base = 0x%04X (bus %u, dev %u, fn %u)\n",
bmBase, bus, dev, fn);
// ---- Enable Bus Mastering in PCI Command register (bit 2) ------------
uint32_t pciCmd = gHal->Pci.Read32(bus, dev, fn, 0x04);
gHal->Pci.Write32(bus, dev, fn, 0x04, pciCmd | (1u << 2));
// ---- Allocate PRDT page (must be 32-bit physical, 4-byte aligned) ----
PHYSICAL_ADDRESS prdtPhys = MmAllocateContiguous(1);
if (!prdtPhys) {
DbgPrint("IDE DMA: Failed to allocate PRDT page\n");
return false;
}
if (prdtPhys >> 32) {
// MmAllocateContiguous returned memory above 4 GB — BM IDE can't
// address it. This shouldn't happen on normal x86 systems with early
// boot allocations but we check defensively.
DbgPrint("IDE DMA: PRDT page is above 4 GB (0x%016llX) — using PIO\n",
(uint64_t)prdtPhys);
return false;
}
void *prdtVirt = gHal->PhysToVirt(prdtPhys);
DbgPrint("IDE DMA: PRDT at phys 0x%08x, virt 0x%016llX\n", (uint32_t)prdtPhys, prdtVirt);
// ---- Create synchronisation objects ------------------------------------
SEMAPHORE *sem = NULL;
if (!KSUCCESS(KeSemaphoreCreate(0, 0x7FFFFFFF, &sem))) {
DbgPrint("IDE DMA: Failed to create IRP semaphore\n");
return false;
}
EVENT *dmaEv = NULL;
if (!KSUCCESS(KeEventCreate(EventAutoReset, 0, &dmaEv))) {
DbgPrint("IDE DMA: Failed to create DMA completion event\n");
return false;
}
// ---- Commit module state -----------------------------------------------
gBmBase = bmBase;
gPrdtPhys = (uint32_t)prdtPhys;
gPrdtVirt = (PRD *)prdtVirt;
gIrpSemaphore = sem;
gDmaEvent = dmaEv;
KeInitializeDpc(&gDmaDpc, IdeDmaCompletionDpc, (void *)0);
IrpQueueInit(&gIrpQueue, sem);
// ---- Register IRQ 14 --------------------------------------------------
KeIrqRegister(IDE_IRQ_PRIMARY, IdeIsr);
DbgPrint("IDE DMA: IRQ %u registered\n", IDE_IRQ_PRIMARY);
// ---- Spawn worker thread -----------------------------------------------
KSTATUS st = KeCreateKernelThread((uintptr_t)IdeWorker, 0,
PriorityHigh, (THREAD **)NULL);
if (!KSUCCESS(st)) {
DbgPrint("IDE DMA: Worker thread creation failed (0x%x)\n", st);
// Not fatal for init — the PRDT/IRQ are live but async callers will
// never get completions. Better to surface the failure clearly.
return false;
}
DbgPrint("IDE DMA: Init complete\n");
return true;
}