add ntfs.sys standalone driver target, change default usb vhd layout formatting to ntfs, implement Fat32CreateDir for directory creation support in the fat32 file system driver, add a dedicated HAL serial output endpoint for COM1 writes in hal_serial.c and subscribe serial to HAL text streams, introduce waitable kernel timers with KeTimerCreate, KeTimerSet, and KeTimerCancel alongside spinlock-protected global timer list initialization and scheduler tick tracking, implement SyscallWriteFileAsync system call for asynchronous non-blocking file writes, refactor NetSocketConnect and NetSocketSendTo to use generic SYS_SOCKET_ADDRESS_K structures instead of being hardcoded to IPv4, update gethostbyname and DNS name resolution to support IPv6 parsing via ResolveNameFamily, update tcpserve.exe with -4 and -6 command-line flags to accept and display IPv6 addresses, append size and version headers to all kernel telemetry and statistics structures including SYS_PMM_STATS, SYS_CPU_STATS, and SYS_SCHEDULER_STATS, update winmm timeGetTime to look up high-resolution hardware frequencies via SysGetTickFrequency, integrate 14 native smoke test applications.

This commit is contained in:
maksw committed 2026-06-26 15:18:48 +02:00
1 parent 2c4a68a75f
commit ed3d065abd
156 files changed
+10509 -2643

No files matched your search

+2 -2
View File
@@ -23,7 +23,7 @@ static DRIVER_OBJECT gPciDriver;
static uint32_t gPciDeviceCount;
// PCI MMIO BARs use a private virtual aperture instead of the physical
// direct map. This avoids cacheability aliasing with large direct-map pages
// direct map. This avoids cacheability aliasing with large direct-map pages
// and keeps BAR mappings page-granular.
#define PCI_MMIO_VA_BASE 0xFFFF900000000000ULL
@@ -139,7 +139,7 @@ static void PciDecodeBars(PCI_PDO_EXTENSION* ext) {
}
// Size BARs with I/O and memory decoding disabled so all-ones probes do
// not accidentally alias a live resource. Restore policy immediately;
// 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,
-8
View File
@@ -35,7 +35,6 @@
static __inline void UsbTrace(const char* fmt, ...) { (void)fmt; }
#endif
#define PCI_CLASS_SERIAL_BUS 0x0Cu
#define PCI_SUBCLASS_USB 0x03u
#define PCI_PROGIF_XHCI 0x30u
@@ -116,7 +115,6 @@ static __inline void UsbTrace(const char* fmt, ...) { (void)fmt; }
#define XHCI_TRB_TYPE_ADDRESS_DEVICE 11u
#define XHCI_TRB_TYPE_CONFIGURE_ENDPOINT 12u
#define XHCI_CC_SUCCESS 1u
#define XHCI_CC_SHORT_PACKET 13u
@@ -205,7 +203,6 @@ static __inline void UsbTrace(const char* fmt, ...) { (void)fmt; }
#define SCSI_WRITE_10 0x2Au
#define SCSI_READ_CAPACITY_10 0x25u
#define XHCI_RING_TRBS 256u
#define XHCI_EVENT_TRBS 256u
#define XHCI_DCBAA_ENTRIES 256u
@@ -487,8 +484,6 @@ typedef struct USB_PDO_EXTENSION {
USB_ENDPOINT_DESCRIPTOR Endpoints[USB_MAX_TRACKED_ENDPOINTS];
} USB_PDO_EXTENSION;
typedef enum USB_HUB_KIND {
UsbHubKindRoot = 1,
UsbHubKindExternal = 2,
@@ -591,8 +586,6 @@ typedef struct USB_HID_EXTENSION {
uint32_t InputDeviceId;
} USB_HID_EXTENSION;
typedef enum USB_STORAGE_EXTENSION_KIND {
UsbStorageExtensionInterface = 0x55534946u,
UsbStorageExtensionDisk = 0x5553444Bu,
@@ -838,7 +831,6 @@ typedef struct XHCI_CONTROLLER {
USB_HCD Hcd;
} XHCI_CONTROLLER;
extern DRIVER_OBJECT gUsbStorageDriver;
extern DRIVER_OBJECT gUsbBusDriver;
extern DRIVER_OBJECT gUsbCompositeDriver;
+2 -4
View File
@@ -1,7 +1,7 @@
// USB bus manager. This file owns device addressing, descriptor reads, and child PDO creation.
// This is the structural USB bus layer used by the upcoming composite, hub,
// and hotplug stages. The legacy direct xHCI class path is still present for
// and hotplug stages. The legacy direct xHCI class path is still present for
// boot HID/storage during migration, but newly enumerated xHCI devices now also
// get first-class USB PDOs and can accept internal URB IRPs.
#include "usb.h"
@@ -187,7 +187,6 @@ static void UsbFormatClass(char* buf, size_t cap, uint8_t cls) {
UsbIdAppendHex(buf, cap, &pos, cls, 2);
}
// Implements the format Composite operation.
static void UsbFormatComposite(char* buf, size_t cap) {
size_t pos = 0;
@@ -442,7 +441,6 @@ KSTATUS UsbCreateInterfacePdos(USB_HCD* hcd, XHCI_DEVICE* xdev, DEVICE_OBJECT* p
return STATUS_SUCCESS;
}
// Implements the reference First Child operation.
static DEVICE_OBJECT* UsbReferenceFirstChild(DEVICE_OBJECT* parent) {
if (!parent) return (DEVICE_OBJECT*)0;
@@ -487,7 +485,7 @@ KSTATUS UsbRemovePdoSubtree(DEVICE_OBJECT* pdo, bool surprise) {
return STATUS_INVALID_PARAMETER;
// Child PDOs may be owned either directly by the PDO or by an attached FDO
// such as usb-composite or usb-hub. The attached driver's PnP handler is
// such as usb-composite or usb-hub. The attached driver's PnP handler is
// therefore responsible for removing its own child PDOs before forwarding
// REMOVE_DEVICE to the lower PDO.
(void)UsbRemoveDeviceObjectChildren(pdo, surprise);
+1 -1
View File
@@ -46,7 +46,7 @@ KSTATUS UsbCompositeDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
// * an FDO attached above a USB device PDO, and
// * interface PDOs that it creates for class drivers.
// Forwarded PnP IRPs from HID/storage land on the interface PDO, so the
// PDO must use the generic USB PDO PnP handler. Treating its
// PDO must use the generic USB PDO PnP handler. Treating its
// USB_PDO_EXTENSION as USB_COMPOSITE_EXTENSION corrupts the dispatch path.
if (device->Type == DeviceObjectPhysical)
return UsbDispatchPdoPnp(device, irp);
+2 -8
View File
@@ -25,7 +25,6 @@ KSTATUS XhciSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
return STATUS_SUCCESS;
}
// Returns tracked Interface By Number.
static USB_TRACKED_INTERFACE* UsbFindTrackedInterfaceByNumber(XHCI_DEVICE* dev, uint8_t interfaceNumber) {
if (!dev) return (USB_TRACKED_INTERFACE*)0;
@@ -111,11 +110,11 @@ void XhciLogConfigurationTree(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
}
} else {
// Alternate settings are not independent USB interfaces. Real
// Alternate settings are not independent USB interfaces. Real
// UVC cameras often expose one VideoStreaming interface with
// many alternate settings, and publishing each alternate setting
// as a separate PDO causes duplicate interface devices and can
// exhaust USB_MAX_TRACKED_INTERFACES. Track only alternate
// exhaust USB_MAX_TRACKED_INTERFACES. Track only alternate
// setting zero until a function driver explicitly selects a
// different setting.
@@ -211,8 +210,6 @@ void XhciLogConfigurationTree(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
}
}
KSTATUS XhciDiscoverAndSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
const USB_DEVICE_DESCRIPTOR* devDesc) {
if (devDesc->bNumConfigurations == 0) {
@@ -254,7 +251,6 @@ KSTATUS XhciDiscoverAndSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
return STATUS_SUCCESS;
}
// Implements the rollback Enumerated Slot operation.
static void XhciRollbackEnumeratedSlot(XHCI_CONTROLLER* xhci, uint8_t slotId) {
if (!xhci || slotId == 0 || slotId > XHCI_MAX_TRACKED_SLOTS)
@@ -279,7 +275,6 @@ static void XhciRollbackEnumeratedSlot(XHCI_CONTROLLER* xhci, uint8_t slotId) {
memset(dev, 0, sizeof(*dev));
}
static KSTATUS XhciEnumeratePathEx(XHCI_CONTROLLER* xhci,
uint8_t logicalPort,
uint8_t speed,
@@ -435,7 +430,6 @@ KSTATUS XhciEnumeratePort(XHCI_CONTROLLER* xhci, uint8_t port) {
return XhciEnumeratePortEx(xhci, port, (uint8_t*)0);
}
// Implements the enumerate Root Ports operation.
void XhciEnumerateRootPorts(XHCI_CONTROLLER* xhci) {
uint32_t attempted = 0;
-15
View File
@@ -11,7 +11,6 @@ bool XhciHidKeyInReport(const uint8_t* keys, uint8_t key) {
return false;
}
INPUT_KEY XhciHidModifierKey(uint8_t bit) {
switch (bit) {
case 0: return INPUT_KEY_LEFT_CTRL;
@@ -26,7 +25,6 @@ INPUT_KEY XhciHidModifierKey(uint8_t bit) {
}
}
// Submits hid Key.
void XhciSubmitHidKey(INPUT_KEY key, bool pressed) {
if (key == INPUT_KEY_NONE) return;
@@ -36,7 +34,6 @@ void XhciSubmitHidKey(INPUT_KEY key, bool pressed) {
KeInputSubmitKey(&ev);
}
// Processes boot Keyboard Report.
void XhciProcessBootKeyboardReport(XHCI_DEVICE* dev, const uint8_t* report, uint16_t len) {
if (len < 8) return;
@@ -66,7 +63,6 @@ void XhciProcessBootKeyboardReport(XHCI_DEVICE* dev, const uint8_t* report, uint
memcpy(dev->HidLastKeys, keys, sizeof(dev->HidLastKeys));
}
// Processes boot Mouse Report.
void XhciProcessBootMouseReport(XHCI_DEVICE* dev, const uint8_t* report, uint16_t len) {
if (len < 3) return;
@@ -88,7 +84,6 @@ void XhciProcessBootMouseReport(XHCI_DEVICE* dev, const uint8_t* report, uint16_
dev->HidLastButtons = buttons;
}
// Implements the arm Hid Interrupt In operation.
KSTATUS XhciArmHidInterruptIn(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
if (!dev->HidActive || dev->HidArmed || !dev->HidRing || !dev->HidBuffer)
@@ -117,7 +112,6 @@ KSTATUS XhciArmHidInterruptIn(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
return STATUS_SUCCESS;
}
// Implements the arm All Hid operation.
void XhciArmAllHid(XHCI_CONTROLLER* xhci) {
for (uint8_t slot = 1; slot <= XHCI_MAX_TRACKED_SLOTS; slot++) {
@@ -131,7 +125,6 @@ void XhciArmAllHid(XHCI_CONTROLLER* xhci) {
}
}
static void XhciHandleLegacyHidTransferEvent(XHCI_CONTROLLER* xhci, uint64_t trbPhys,
uint32_t completionCode, uint8_t slotId,
uint8_t endpointId, uint32_t residue) {
@@ -163,7 +156,6 @@ static void XhciHandleLegacyHidTransferEvent(XHCI_CONTROLLER* xhci, uint64_t trb
}
}
// Starts hid Interrupts.
KSTATUS XhciStartHidInterrupts(XHCI_CONTROLLER* xhci) {
if (!xhci || xhci->HidDeviceCount == 0)
@@ -173,7 +165,6 @@ KSTATUS XhciStartHidInterrupts(XHCI_CONTROLLER* xhci) {
return STATUS_SUCCESS;
}
// Updates hid Boot Protocol.
KSTATUS XhciSetHidBootProtocol(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
USB_SETUP_PACKET setup;
@@ -205,7 +196,6 @@ KSTATUS XhciSetHidBootProtocol(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
return STATUS_SUCCESS;
}
// Creates hid Memory.
KSTATUS XhciAllocateHidMemory(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
if (dev->HidRing && dev->HidBuffer) return STATUS_SUCCESS;
@@ -232,7 +222,6 @@ KSTATUS XhciAllocateHidMemory(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
return STATUS_SUCCESS;
}
// Implements the configure Hid Endpoint operation.
KSTATUS XhciConfigureHidEndpoint(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
if (!dev->HidCandidate)
@@ -308,7 +297,6 @@ KSTATUS XhciConfigureHidEndpoint(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
return STATUS_SUCCESS;
}
// PDO/FDO boot HID function driver path.
DRIVER_OBJECT gUsbHidDriver;
@@ -324,7 +312,6 @@ static const IO_DRIVER_ID_TABLE gUsbHidIdTable = {
sizeof(gUsbHidIds) / sizeof(gUsbHidIds[0]),
};
// Implements the hid Is Qemu Absolute Tablet Pdo operation.
static bool UsbHidIsQemuAbsoluteTabletPdo(const USB_PDO_EXTENSION* pdoExt, const USB_ENDPOINT_DESCRIPTOR* ep) {
if (!pdoExt || !pdoExt->Device || !ep)
@@ -358,7 +345,6 @@ static const USB_ENDPOINT_DESCRIPTOR* UsbHidFindInterruptInPdo(const USB_PDO_EXT
return (const USB_ENDPOINT_DESCRIPTOR*)0;
}
// Implements the hid Process Boot Keyboard Report operation.
static void UsbHidProcessBootKeyboardReport(USB_HID_EXTENSION* ext, const uint8_t* report, uint16_t len) {
if (!ext || len < 8) return;
@@ -630,7 +616,6 @@ static KSTATUS UsbHidStartDevice(DEVICE_OBJECT* device, USB_HID_EXTENSION* ext)
return STATUS_SUCCESS;
}
// Implements the hid Dispatch Pnp operation.
KSTATUS UsbHidDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
if (!device || !irp)
+4 -4
View File
@@ -1,6 +1,6 @@
// USB hub driver. This file owns hub probing, port status handling, and child-device enumeration.
// Root hubs and external hubs are represented by the same FDO abstraction. The
// Root hubs and external hubs are represented by the same FDO abstraction. The
// xHCI root hub feeds port-change events directly into this driver, while an
// external hub feeds the same port state machine from its interrupt-IN change
// bitmap.
@@ -77,9 +77,9 @@ static KSTATUS UsbHubEnumerateRootPortWithRetries(USB_HUB_EXTENSION* hub,
// On real Intel laptop controllers some internal full-speed devices
// respond with xHCI USB Transaction Error during Address Device while
// the port is still settling. Retry that boundedly while the port is
// the port is still settling. Retry that boundedly while the port is
// held in ENUMERATING state so queued PSC events cannot start parallel
// enumerations. Descriptor/control timeouts are not retried here; they
// enumerations. Descriptor/control timeouts are not retried here; they
// are usually dead/firmware-owned ports and retrying them stalls boot.
if (last != STATUS_DEVICE_ERROR || attempt == maxAttempts)
@@ -898,7 +898,7 @@ KSTATUS UsbHubDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
if (hub->WorkerSemaphore)
KeSignalSemaphore(hub->WorkerSemaphore);
// Remove downstream PDOs owned by this hub FDO. The explicit
// Remove downstream PDOs owned by this hub FDO. The explicit
// slot arrays are still used as the hardware lookup table; the
// child PDO tree is the authoritative software lifetime tree.
(void)UsbRemoveDeviceObjectChildren(device, true);
+3 -4
View File
@@ -1,8 +1,8 @@
// USB mass-storage driver. This file owns bulk-only transport and storage-device exposure.
// The storage driver now binds as a USB function driver to an interface PDO
// (USB\CLASS_08&SUBCLASS_06&PROT_50). It creates a disk FDO above that
// interface stack and issues BOT transfers through USB URBs. The xHCI HCD
// (USB\CLASS_08&SUBCLASS_06&PROT_50). It creates a disk FDO above that
// interface stack and issues BOT transfers through USB URBs. The xHCI HCD
// still backs the bulk URBs with the existing per-device rings; later stages
// will make endpoint rings fully pipe-owned and asynchronous.
#include "usb.h"
@@ -597,7 +597,7 @@ static KSTATUS UsbStorageStartDevice(DEVICE_OBJECT* device, USB_STORAGE_EXTENSIO
}
// The disk namespace publishes an arrival notification synchronously from
// IoRegisterDiskDevice(). Mark the storage FDO usable before publishing the
// IoRegisterDiskDevice(). Mark the storage FDO usable before publishing the
// disk so filesystem hotplug callbacks can read sector 0 immediately.
xdev->MassActive = true;
storage->Started = true;
@@ -785,7 +785,6 @@ KSTATUS UsbStorageDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
return STATUS_INVALID_PARAMETER;
}
// Implements the storage Add Device operation.
KSTATUS UsbStorageAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject) {
if (!driver || !physicalDeviceObject || physicalDeviceObject->Type != DeviceObjectPhysical)
+8 -87
View File
@@ -7,7 +7,6 @@ uint32_t UsbAlignUp32(uint32_t value, uint32_t align) {
return (value + align - 1u) & ~(align - 1u);
}
// Implements the barrier operation.
void XhciBarrier(void) {
_ReadWriteBarrier();
@@ -15,31 +14,26 @@ void XhciBarrier(void) {
_ReadWriteBarrier();
}
// Implements the read8 operation.
uint8_t XhciRead8(volatile uint8_t* base, uint32_t reg) {
return *(volatile uint8_t*)(base + reg);
}
// Implements the read16 operation.
uint16_t XhciRead16(volatile uint8_t* base, uint32_t reg) {
return *(volatile uint16_t*)(base + reg);
}
// Implements the read32 operation.
uint32_t XhciRead32(volatile uint8_t* base, uint32_t reg) {
return *(volatile uint32_t*)(base + reg);
}
// Implements the write32 operation.
void XhciWrite32(volatile uint8_t* base, uint32_t reg, uint32_t value) {
*(volatile uint32_t*)(base + reg) = value;
}
// Implements the read64 operation.
uint64_t XhciRead64(volatile uint8_t* base, uint32_t reg) {
uint32_t lo = XhciRead32(base, reg);
@@ -47,14 +41,12 @@ uint64_t XhciRead64(volatile uint8_t* base, uint32_t reg) {
return ((uint64_t)hi << 32) | lo;
}
// Implements the write64 operation.
void XhciWrite64(volatile uint8_t* base, uint32_t reg, uint64_t value) {
XhciWrite32(base, reg, (uint32_t)value);
XhciWrite32(base, reg + 4u, (uint32_t)(value >> 32));
}
// Implements the delay Us operation.
void UsbDelayUs(const HAL_API* hal, uint32_t us) {
if (hal && hal->Timer.Stall) hal->Timer.Stall(us);
@@ -63,7 +55,6 @@ 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) {
for (uint32_t i = 0; i < timeoutUs; i++) {
@@ -73,7 +64,6 @@ bool UsbWaitSet32(const HAL_API* hal, volatile uint8_t* base, uint32_t reg,
return false;
}
bool UsbWaitClear32(const HAL_API* hal, volatile uint8_t* base, uint32_t reg,
uint32_t mask, uint32_t timeoutUs) {
for (uint32_t i = 0; i < timeoutUs; i++) {
@@ -83,15 +73,14 @@ bool UsbWaitClear32(const HAL_API* hal, volatile uint8_t* base, uint32_t reg,
return false;
}
void* UsbDmaAllocPages(XHCI_CONTROLLER* xhci, size_t pages,
PHYSICAL_ADDRESS* outPhys) {
if (!outPhys || pages == 0)
return NULL;
// Most xHCI runtime objects are one page. Do not route those through the
// Most xHCI runtime objects are one page. Do not route those through the
// physically-contiguous allocator: on real machines that allocator is a
// scarce DMA primitive, while a single frame is already contiguous. QEMU
// scarce DMA primitive, while a single frame is already contiguous. QEMU
// hid this because it had simple low memory; laptop firmware does not.
PHYSICAL_ADDRESS phys = 0;
if (pages == 1) {
@@ -112,62 +101,52 @@ void* UsbDmaAllocPages(XHCI_CONTROLLER* xhci, size_t pages,
return virt;
}
// Implements the scratchpad Count operation.
uint16_t XhciScratchpadCount(uint32_t hcs2) {
return (uint16_t)((((hcs2 >> 16) & 0x3E0u) | ((hcs2 >> 27) & 0x1Fu)) & 0x3FFu);
}
// Implements the trb Type operation.
uint32_t XhciTrbType(const XHCI_TRB* trb) {
return (trb->Control >> XHCI_TRB_TYPE_SHIFT) & XHCI_TRB_TYPE_MASK;
}
// Implements the completion Code operation.
uint32_t XhciCompletionCode(const XHCI_TRB* trb) {
return (trb->Status >> 24) & 0xFFu;
}
// Implements the event Slot Id operation.
uint8_t XhciEventSlotId(const XHCI_TRB* trb) {
return (uint8_t)(trb->Control >> 24);
}
// Implements the event Endpoint Id operation.
uint8_t XhciEventEndpointId(const XHCI_TRB* trb) {
return (uint8_t)((trb->Control >> 16) & 0x1Fu);
}
// Implements the transfer Residue operation.
uint32_t XhciTransferResidue(const XHCI_TRB* trb) {
return trb->Status & 0xFFFFFFu;
}
// Implements the input Control Context operation.
uint32_t* XhciInputControlContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
(void)xhci;
return (uint32_t*)dev->InputContext;
}
// Implements the input Slot Context operation.
uint32_t* XhciInputSlotContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
return (uint32_t*)(dev->InputContext + xhci->ContextBytes);
}
uint32_t* XhciInputEndpointContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
uint8_t endpointId) {
return (uint32_t*)(dev->InputContext + (uint32_t)(endpointId + 1u) * xhci->ContextBytes);
}
// Implements the ep Max Packet operation.
uint32_t XhciEpMaxPacket(uint8_t speed) {
switch (speed) {
@@ -180,7 +159,6 @@ uint32_t XhciEpMaxPacket(uint8_t speed) {
}
}
// Implements the setup Packet To U64 operation.
uint64_t UsbSetupPacketToU64(const USB_SETUP_PACKET* setup) {
uint64_t v = 0;
@@ -192,7 +170,6 @@ uint64_t UsbSetupPacketToU64(const USB_SETUP_PACKET* setup) {
return v;
}
const char* XhciPortSpeedName(uint32_t speed) {
switch (speed) {
case 1: return "full";
@@ -204,8 +181,6 @@ const char* XhciPortSpeedName(uint32_t speed) {
}
}
// Parses supported Protocols.
void XhciParseSupportedProtocols(XHCI_CONTROLLER* xhci) {
memset(xhci->PortProtocolMajor, 0, sizeof(xhci->PortProtocolMajor));
@@ -253,7 +228,6 @@ void XhciParseSupportedProtocols(XHCI_CONTROLLER* xhci) {
}
}
// Implements the port Protocol Major operation.
uint8_t XhciPortProtocolMajor(XHCI_CONTROLLER* xhci, uint8_t port, uint32_t portsc) {
if (port <= XHCI_MAX_ROOT_PORTS && xhci->PortProtocolMajor[port])
@@ -277,12 +251,10 @@ const char* UsbEndpointTypeName(uint8_t type) {
}
}
const char* UsbDirectionName(uint8_t endpointAddress) {
return (endpointAddress & USB_ENDPOINT_DIR_IN) ? "in" : "out";
}
const char* UsbInterfaceClassName(uint8_t klass, uint8_t subclass, uint8_t proto) {
if (klass == USB_CLASS_HID) {
if (subclass == 1 && proto == 1) return "hid-boot-keyboard";
@@ -297,7 +269,6 @@ const char* UsbInterfaceClassName(uint8_t klass, uint8_t subclass, uint8_t proto
return "class";
}
// Implements the log Port Status operation.
void XhciLogPortStatus(XHCI_CONTROLLER* xhci) {
for (uint8_t port = 1; port <= xhci->MaxPorts; port++) {
@@ -346,7 +317,6 @@ void XhciPowerRootPorts(XHCI_CONTROLLER* xhci) {
}
}
// Implements the ack Interrupter0 operation.
void XhciAckInterrupter0(XHCI_CONTROLLER* xhci) {
if (!xhci->Runtime) return;
@@ -355,7 +325,6 @@ void XhciAckInterrupter0(XHCI_CONTROLLER* xhci) {
XhciWrite32(ir0, 0x00u, (iman & XHCI_IMAN_IE) | XHCI_IMAN_IP);
}
// Updates erdp.
void XhciUpdateErdp(XHCI_CONTROLLER* xhci) {
volatile uint8_t* ir0 = xhci->Runtime + 0x20u;
@@ -363,7 +332,6 @@ void XhciUpdateErdp(XHCI_CONTROLLER* xhci) {
XhciWrite64(ir0, 0x18u, erdp | (1ull << 3)); // EHB clear
}
// Returns command Waiter Locked.
static XHCI_WAIT_TARGET* XhciFindCommandWaiterLocked(XHCI_CONTROLLER* xhci, uint64_t commandPhys) {
for (XHCI_WAIT_TARGET* w = xhci->WaiterList; w; w = w->Next) {
@@ -373,7 +341,6 @@ static XHCI_WAIT_TARGET* XhciFindCommandWaiterLocked(XHCI_CONTROLLER* xhci, uint
return (XHCI_WAIT_TARGET*)0;
}
static XHCI_WAIT_TARGET* XhciFindTransferWaiterLocked(XHCI_CONTROLLER* xhci,
uint64_t transferPhys,
uint8_t slotId,
@@ -389,7 +356,6 @@ static XHCI_WAIT_TARGET* XhciFindTransferWaiterLocked(XHCI_CONTROLLER* xhci,
return (XHCI_WAIT_TARGET*)0;
}
static XHCI_WAIT_TARGET* XhciClaimCommandWaiter(XHCI_CONTROLLER* xhci,
uint64_t commandPhys,
uint32_t completionCode,
@@ -406,7 +372,6 @@ static XHCI_WAIT_TARGET* XhciClaimCommandWaiter(XHCI_CONTROLLER* xhci,
return w;
}
static XHCI_WAIT_TARGET* XhciClaimTransferWaiter(XHCI_CONTROLLER* xhci,
uint64_t transferPhys,
uint8_t slotId,
@@ -427,7 +392,6 @@ static XHCI_WAIT_TARGET* XhciClaimTransferWaiter(XHCI_CONTROLLER* xhci,
return w;
}
// Implements the drain Event Ring operation.
bool XhciDrainEventRing(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
(void)target;
@@ -484,7 +448,6 @@ bool XhciDrainEventRing(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
return processed;
}
// Implements the drain Event Ring Locked operation.
bool XhciDrainEventRingLocked(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
if (!xhci) return false;
@@ -494,7 +457,6 @@ bool XhciDrainEventRingLocked(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
return processed;
}
// Waits for target Complete.
bool XhciWaitTargetComplete(const XHCI_WAIT_TARGET* target) {
if (!target) return false;
@@ -503,7 +465,6 @@ bool XhciWaitTargetComplete(const XHCI_WAIT_TARGET* target) {
return true;
}
// Registers wait Target.
void XhciRegisterWaitTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
if (!xhci || !target || target->Registered)
@@ -516,7 +477,6 @@ void XhciRegisterWaitTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
KeReleaseSpinlock(&xhci->WaitLock, flags);
}
// Implements the unregister Wait Target operation.
void XhciUnregisterWaitTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
if (!xhci || !target || !target->Registered)
@@ -536,7 +496,6 @@ void XhciUnregisterWaitTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target) {
KeReleaseSpinlock(&xhci->WaitLock, flags);
}
KSTATUS XhciWaitForArmedTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target,
uint32_t timeoutUs) {
if (!xhci || !target)
@@ -566,7 +525,6 @@ KSTATUS XhciWaitForArmedTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target,
return STATUS_TIMEOUT;
}
KSTATUS XhciWaitForTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target,
uint32_t timeoutUs) {
if (!xhci || !target)
@@ -575,7 +533,6 @@ KSTATUS XhciWaitForTarget(XHCI_CONTROLLER* xhci, XHCI_WAIT_TARGET* target,
return XhciWaitForArmedTarget(xhci, target, timeoutUs);
}
void XhciEventDpcRoutine(KDPC* dpc, void* context,
void* systemArgument1, void* systemArgument2) {
(void)dpc;
@@ -592,7 +549,6 @@ void XhciEventDpcRoutine(KDPC* dpc, void* context,
}
}
#define XHCI_CONTROLLER_MAGIC 0x58484349u // XHCI
#define XHCI_MAX_CONTROLLERS 8u
@@ -630,7 +586,6 @@ void XhciIsr(void) {
}
}
// Implements the bios Handoff operation.
KSTATUS XhciBiosHandoff(XHCI_CONTROLLER* xhci) {
uint32_t xecp = (xhci->Hcc1 >> 16) & 0xFFFFu;
@@ -664,7 +619,7 @@ KSTATUS XhciBiosHandoff(XHCI_CONTROLLER* xhci) {
}
}
// Clear legacy SMI enables/status. RW1C status bits tolerate this.
// Clear legacy SMI enables/status. RW1C status bits tolerate this.
XhciWrite32(xhci->Mmio, off + 4u, 0xFFFFFFFFu);
return STATUS_SUCCESS;
}
@@ -675,7 +630,6 @@ KSTATUS XhciBiosHandoff(XHCI_CONTROLLER* xhci) {
return STATUS_SUCCESS;
}
// Implements the stop And Reset operation.
KSTATUS XhciStopAndReset(XHCI_CONTROLLER* xhci) {
uint32_t cmd = XhciRead32(xhci->Op, XHCI_OP_USBCMD);
@@ -709,7 +663,6 @@ KSTATUS XhciStopAndReset(XHCI_CONTROLLER* xhci) {
return STATUS_SUCCESS;
}
// Creates scratchpads.
KSTATUS XhciAllocateScratchpads(XHCI_CONTROLLER* xhci) {
if (xhci->ScratchpadCount == 0) return STATUS_SUCCESS;
@@ -744,7 +697,6 @@ KSTATUS XhciAllocateScratchpads(XHCI_CONTROLLER* xhci) {
return STATUS_SUCCESS;
}
// Creates runtime Memory.
KSTATUS XhciAllocateRuntimeMemory(XHCI_CONTROLLER* xhci) {
xhci->Dcbaa = (uint64_t*)UsbDmaAllocPages(xhci, 1, &xhci->DcbaaPhys);
@@ -786,7 +738,6 @@ KSTATUS XhciAllocateRuntimeMemory(XHCI_CONTROLLER* xhci) {
return STATUS_SUCCESS;
}
// Implements the program Runtime operation.
KSTATUS XhciProgramRuntime(XHCI_CONTROLLER* xhci) {
uint8_t slots = xhci->MaxSlots;
@@ -808,13 +759,11 @@ KSTATUS XhciProgramRuntime(XHCI_CONTROLLER* xhci) {
return STATUS_SUCCESS;
}
// Implements the command Trb Phys operation.
uint64_t XhciCommandTrbPhys(XHCI_CONTROLLER* xhci, uint32_t index) {
return xhci->CommandRingPhys + (uint64_t)index * sizeof(XHCI_TRB);
}
static KSTATUS XhciCommandRingAppend(XHCI_CONTROLLER* xhci,
uint64_t parameter,
uint32_t status,
@@ -850,7 +799,6 @@ static KSTATUS XhciCommandRingAppend(XHCI_CONTROLLER* xhci,
return STATUS_SUCCESS;
}
KSTATUS XhciSubmitCommand(XHCI_CONTROLLER* xhci,
uint64_t parameter,
uint32_t status,
@@ -892,7 +840,6 @@ KSTATUS XhciSubmitCommand(XHCI_CONTROLLER* xhci,
return waitStatus;
}
// Submits noop Command.
KSTATUS XhciSubmitNoopCommand(XHCI_CONTROLLER* xhci) {
XHCI_COMMAND_RESULT result;
@@ -913,7 +860,6 @@ KSTATUS XhciSubmitNoopCommand(XHCI_CONTROLLER* xhci) {
return st;
}
// Enables slot.
KSTATUS XhciEnableSlot(XHCI_CONTROLLER* xhci, uint8_t* outSlotId) {
XHCI_COMMAND_RESULT result;
@@ -938,8 +884,6 @@ KSTATUS XhciEnableSlot(XHCI_CONTROLLER* xhci, uint8_t* outSlotId) {
return STATUS_SUCCESS;
}
// Disables slot.
KSTATUS XhciDisableSlot(XHCI_CONTROLLER* xhci, uint8_t slotId) {
if (!xhci || slotId == 0)
@@ -982,7 +926,6 @@ KSTATUS XhciAddressDevice(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
return STATUS_SUCCESS;
}
// Starts the requested operation.
KSTATUS XhciStart(XHCI_CONTROLLER* xhci) {
uint32_t cmd = XhciRead32(xhci->Op, XHCI_OP_USBCMD);
@@ -1000,7 +943,6 @@ KSTATUS XhciStart(XHCI_CONTROLLER* xhci) {
return STATUS_SUCCESS;
}
// Enables interrupts.
void XhciEnableInterrupts(XHCI_CONTROLLER* xhci) {
volatile uint8_t* ir0 = xhci->Runtime + 0x20u;
@@ -1011,13 +953,11 @@ void XhciEnableInterrupts(XHCI_CONTROLLER* xhci) {
XhciWrite32(xhci->Op, XHCI_OP_USBCMD, cmd | XHCI_USBCMD_INTE | XHCI_USBCMD_HSEE);
}
// Implements the port Reg operation.
uint32_t XhciPortReg(uint8_t port) {
return XHCI_OP_PORT_BASE + ((uint32_t)port - 1u) * XHCI_OP_PORT_STRIDE;
}
// Clears port Changes.
void XhciClearPortChanges(XHCI_CONTROLLER* xhci, uint8_t port) {
uint32_t off = XhciPortReg(port);
@@ -1025,16 +965,15 @@ void XhciClearPortChanges(XHCI_CONTROLLER* xhci, uint8_t port) {
uint32_t changes = portsc & XHCI_PORTSC_CHANGE_MASK;
if (changes) {
// PORTSC is a mixed register. The change bits are RW1C, but many
// PORTSC is a mixed register. The change bits are RW1C, but many
// other bits are either RO or have side effects when written as one
// (PED disables the port; PR starts a reset). Preserve only Port
// (PED disables the port; PR starts a reset). Preserve only Port
// Power and write one to the RW1C change bits.
XhciWrite32(xhci->Op, off, (portsc & XHCI_PORTSC_PP) | changes);
}
}
// Implements the reset Port operation.
KSTATUS XhciResetPort(XHCI_CONTROLLER* xhci, uint8_t port, uint8_t* outSpeed) {
uint32_t off = XhciPortReg(port);
@@ -1050,9 +989,9 @@ KSTATUS XhciResetPort(XHCI_CONTROLLER* xhci, uint8_t port, uint8_t* outSpeed) {
if (protocol >= 3u || speed >= 4u) {
// SuperSpeed ports are enabled by link training. Do not issue a
// SuperSpeed ports are enabled by link training. Do not issue a
// USB2-style PORTSC.PR reset; on real Intel controllers that can make
// the link drop during early enumeration. If firmware/controller
// the link drop during early enumeration. If firmware/controller
// already has PR set, just wait for it to settle.
bool enabled = false;
@@ -1118,7 +1057,6 @@ KSTATUS XhciResetPort(XHCI_CONTROLLER* xhci, uint8_t port, uint8_t* outSpeed) {
return STATUS_SUCCESS;
}
// Creates device Memory.
KSTATUS XhciAllocateDeviceMemory(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
dev->InputContext = (uint8_t*)UsbDmaAllocPages(xhci, 1, &dev->InputContextPhys);
@@ -1143,7 +1081,6 @@ KSTATUS XhciAllocateDeviceMemory(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
return STATUS_SUCCESS;
}
// Builds default Control Context.
void XhciBuildDefaultControlContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
memset(dev->InputContext, 0, PAGE_SIZE);
@@ -1187,13 +1124,11 @@ void XhciBuildDefaultControlContext(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) {
XhciBarrier();
}
// Implements the ep0 Trb Phys operation.
uint64_t XhciEp0TrbPhys(XHCI_DEVICE* dev, uint32_t index) {
return dev->Ep0RingPhys + (uint64_t)index * sizeof(XHCI_TRB);
}
KSTATUS XhciEp0AppendTrb(XHCI_DEVICE* dev,
uint64_t parameter,
uint32_t status,
@@ -1229,7 +1164,6 @@ KSTATUS XhciEp0AppendTrb(XHCI_DEVICE* dev,
return STATUS_SUCCESS;
}
KSTATUS XhciControlInTimeout(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
const USB_SETUP_PACKET* setup,
void* buffer,
@@ -1298,7 +1232,6 @@ KSTATUS XhciControlInTimeout(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
return STATUS_DEVICE_ERROR;
}
KSTATUS XhciControlIn(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
const USB_SETUP_PACKET* setup,
void* buffer,
@@ -1308,7 +1241,6 @@ KSTATUS XhciControlIn(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
USB_XHCI_CONTROL_TIMEOUT_US);
}
KSTATUS XhciControlNoData(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
const USB_SETUP_PACKET* setup) {
uint64_t setupValue = UsbSetupPacketToU64(setup);
@@ -1359,7 +1291,6 @@ KSTATUS XhciControlNoData(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
return STATUS_DEVICE_ERROR;
}
KSTATUS XhciGetDeviceDescriptorTimeout(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
USB_DEVICE_DESCRIPTOR* outDesc,
uint32_t timeoutUs) {
@@ -1389,14 +1320,12 @@ KSTATUS XhciGetDeviceDescriptorTimeout(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
return STATUS_SUCCESS;
}
KSTATUS XhciGetDeviceDescriptor(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
USB_DEVICE_DESCRIPTOR* outDesc) {
return XhciGetDeviceDescriptorTimeout(xhci, dev, outDesc,
USB_XHCI_CONTROL_TIMEOUT_US);
}
KSTATUS XhciGetConfigurationDescriptor(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
uint8_t configurationIndex,
USB_CONFIGURATION_DESCRIPTOR* outConfig,
@@ -1456,7 +1385,6 @@ KSTATUS XhciGetConfigurationDescriptor(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
return STATUS_SUCCESS;
}
// Implements the endpoint Id From Address operation.
uint8_t XhciEndpointIdFromAddress(uint8_t endpointAddress) {
uint8_t number = endpointAddress & USB_ENDPOINT_NUMBER_MASK;
@@ -1464,14 +1392,13 @@ uint8_t XhciEndpointIdFromAddress(uint8_t endpointAddress) {
return (uint8_t)(number * 2u + ((endpointAddress & USB_ENDPOINT_DIR_IN) ? 1u : 0u));
}
// Implements the interrupt Interval operation.
uint8_t XhciInterruptInterval(uint8_t speed, uint8_t usbInterval) {
if (usbInterval == 0) usbInterval = 1;
if (speed == 3 || speed == 4 || speed == 5) {
// High-speed and newer interrupt endpoints already encode bInterval as
// an exponent in USB descriptors. xHCI stores the zero-based exponent.
// an exponent in USB descriptors. xHCI stores the zero-based exponent.
if (usbInterval > 16) usbInterval = 16;
return (uint8_t)(usbInterval - 1u);
}
@@ -1488,7 +1415,6 @@ uint8_t XhciInterruptInterval(uint8_t speed, uint8_t usbInterval) {
return interval;
}
KSTATUS XhciRingAppend(XHCI_TRB* ring, PHYSICAL_ADDRESS ringPhys,
uint32_t trbCount, uint32_t* enqueue, uint8_t* cycle,
uint64_t parameter, uint32_t status,
@@ -1523,7 +1449,6 @@ KSTATUS XhciRingAppend(XHCI_TRB* ring, PHYSICAL_ADDRESS ringPhys,
return STATUS_SUCCESS;
}
void XhciInitialiseTransferRing(XHCI_TRB* ring, PHYSICAL_ADDRESS ringPhys,
uint32_t trbCount, uint8_t cycle) {
memset(ring, 0, trbCount * sizeof(XHCI_TRB));
@@ -1536,7 +1461,6 @@ void XhciInitialiseTransferRing(XHCI_TRB* ring, PHYSICAL_ADDRESS ringPhys,
cycle;
}
KSTATUS XhciBulkTransfer(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
uint8_t endpointId,
XHCI_TRB* ring,
@@ -1595,9 +1519,6 @@ KSTATUS XhciBulkTransfer(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev,
return STATUS_SUCCESS;
}
// Implements the configure Pipe Endpoint operation.
KSTATUS XhciConfigurePipeEndpoint(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, USB_PIPE* pipe) {
if (!xhci || !dev || !pipe || !pipe->Open)
+9 -10
View File
@@ -254,10 +254,10 @@ static void VmwSvgaCursorCommit(VMWSVGA_DEVICE* v) {
return;
}
// Legacy QEMU SVGA cursors are frontend/host cursors. Re-defining the
// Legacy QEMU SVGA cursors are frontend/host cursors. Re-defining the
// cursor image from the input path can stall the FIFO/frontend after a
// GTK focus/grab transition, so cursor activity must only update the
// legacy position/visibility registers. The image is defined once when
// legacy position/visibility registers. The image is defined once when
// SetCursorImage runs.
VmwSvgaWriteReg(v, SVGA_REG_CURSOR_ID, 0u);
@@ -315,7 +315,7 @@ static uint32_t VmwSvgaPackMonoCursorDword(const uint8_t* bits,
int y,
uint32_t dwordIndex) {
// SVGA classic cursor scanlines are padded to 32 bits. QEMU stores
// SVGA classic cursor scanlines are padded to 32 bits. QEMU stores
// FIFO dwords in guest-endian order and then interprets the result as
// bytes via cursor_set_mono(), whose first pixel is bit 0x80 of byte 0.
// Therefore byte 0 of the numeric dword must contain pixels 0..7,
@@ -351,7 +351,7 @@ static int VmwSvgaClassicCursorWidth(int width) {
// For widths that are not a multiple of 32 pixels, those two row
// strides differ and every other row is decoded from padding bytes.
// Define the classic cursor with a 32-pixel-aligned visible width and
// leave the extra pixels transparent. The real cursor image remains
// leave the extra pixels transparent. The real cursor image remains
// at the left edge and the hotspot is unchanged.
return (width + 31) & ~31;
@@ -364,11 +364,11 @@ static void VmwSvgaBuildMonoCursorBits(const uint32_t* argbPixels,
uint8_t* andBits,
uint8_t* xorBits) {
// Classic SVGA cursors are AND/XOR masks. The most compatible path is
// Classic SVGA cursors are AND/XOR masks. The most compatible path is
// a 1bpp AND mask plus a 1bpp XOR mask:
// transparent: AND=1 XOR=0
// black: AND=0 XOR=0
// white: AND=0 XOR=1
// black: AND=0 XOR=0
// white: AND=0 XOR=1
// This intentionally discards antialiasing, but avoids QEMU/GTK's
// fragile 32bpp classic host-cursor path.
@@ -509,7 +509,6 @@ static KSTATUS VmwSvgaDefineCursorImage(VMWSVGA_DEVICE* v, int width, int height
return STATUS_SUCCESS;
}
// Implements the align Up32 operation.
static uint32_t VmwSvgaAlignUp32(uint32_t value, uint32_t alignment) {
return (value + alignment - 1u) & ~(alignment - 1u);
@@ -607,9 +606,9 @@ static KSTATUS VmwSvgaInitializeScreenObject(VMWSVGA_DEVICE* v) {
((v->Capabilities & SVGA_CAP_SCREEN_OBJECT_2) != 0);
// Real VMware can advertise SCREEN_OBJECT_2 without also setting the
// original FIFO_CAP_SCREEN_OBJECT bit. The command set is still the
// original FIFO_CAP_SCREEN_OBJECT bit. The command set is still the
// Screen Object command set: DEFINE_SCREEN, DEFINE_GMRFB, and
// BLIT_GMRFB_TO_SCREEN. Treat either capability generation as usable.
// BLIT_GMRFB_TO_SCREEN. Treat either capability generation as usable.
if (!hasScreenObject1 && !hasScreenObject2) {
DbgPrint("VMWSVGA: screen object unavailable: screenObject1=%u screenObject2=%u caps=0x%x fifoCaps=0x%x\n",
+1 -1
View File
@@ -127,7 +127,7 @@
#define SVGA_SCREEN_OBJECT_ID_PRIMARY 0u
#define SVGA_GMRFB_FORMAT_32BPP_24DEPTH ((32u) | (24u << 8))
// The production path is legacy GFB + UPDATE. Screen objects are kept behind
// The production path is legacy GFB + UPDATE. Screen objects are kept behind
// an opt-in switch until the SO/GMRFB path is independently validated on real
// VMware hosts.
+1
View File
@@ -6,6 +6,7 @@ ahci.sys
ata.sys
usb.sys
fat32.sys
ntfs.sys
[runtime]
lwip.sys
+529 -174
View File
@@ -97,6 +97,7 @@ typedef struct {
#pragma pack(pop)
#define ATTR_VOLUME_ID 0x08
#define ATTR_DIRECTORY 0x10
#define ATTR_ARCHIVE 0x20
#define ATTR_LFN 0x0F
@@ -109,10 +110,10 @@ typedef struct {
// FAT32_VOL forward declaration (needed by cache helpers).
typedef struct FAT32_VOL_S FAT32_VOL;
// FAT sector cache (fixes #1 and #5)
// FAT sector cache (fixes #1 and #5)
// 32 direct-mapped slots, indexed by (lba % FAT_CACHE_SLOTS).
// Write-back: WriteFatEntry modifies the in-cache bytes and marks the slot
// dirty. FlushFatCache() propagates dirty slots to every FAT copy at once,
// dirty. FlushFatCache() propagates dirty slots to every FAT copy at once,
// replacing the original per-entry 2x(read+write) pattern with a single
// write per dirty sector per FAT copy.
@@ -155,12 +156,12 @@ typedef struct FAT32_CHAIN_CACHE_ENTRY {
uint32_t NextCluster;
} FAT32_CHAIN_CACHE_ENTRY;
// The FAT sector cache is global and direct-mapped. mmap-backed video can fault
// The FAT sector cache is global and direct-mapped. mmap-backed video can fault
// file pages from multiple Bink worker threads at the same time; without
// serialization one thread can evict/reload a cache slot while another thread is
// still reading a FAT entry from that slot, corrupting cluster-chain traversal.
// Use a sleepable mutex rather than a spinlock because cache misses perform disk
// I/O. The static mutex avoids allocating an object from the paging path.
// I/O. The static mutex avoids allocating an object from the paging path.
static MUTEX gFatCacheMutex;
static SPINLOCK gFatCacheMutexInitLock = SPINLOCK_INIT;
static int gFatCacheMutexReady;
@@ -224,7 +225,7 @@ static KSTATUS FatCacheRead(FAT32_VOL *v, uint64_t lba, uint8_t **out) {
e->lba = lba;
e->valid = true;
e->dirty = false;
*out = e->data;
*out = e->data;
return STATUS_SUCCESS;
}
@@ -327,7 +328,7 @@ static void FlushFatCache(FAT32_VOL *v) {
FatCacheRelease(held);
}
// FAT read / write (through cache).
// FAT read / write (through cache).
static uint32_t FatEntry(FAT32_VOL *v, uint32_t cluster) {
uint32_t fatOffset = cluster * 4;
@@ -368,7 +369,7 @@ static KSTATUS WriteFatEntry(FAT32_VOL *v, uint32_t cluster, uint32_t value) {
return STATUS_SUCCESS;
}
// Cluster allocation (fix #3: next-free hint + two-phase wrap-around scan).
// Cluster allocation (fix #3: next-free hint + two-phase wrap-around scan).
static uint32_t AllocClusterEx(FAT32_VOL *v, int zeroCluster) {
if (v->TotalClusters == 0) return 0;
@@ -385,7 +386,7 @@ static uint32_t AllocClusterEx(FAT32_VOL *v, int zeroCluster) {
v->NextFreeHint = c + 1;
// Normal file growth zeroes newly allocated clusters so old
// disk contents are never exposed. Swap/pagefile creation is
// disk contents are never exposed. Swap/pagefile creation is
// the one exception: every allocated slot is overwritten before
// it can ever be read back, so skipping these writes avoids a
// slow boot-time full-pagefile zero pass.
@@ -404,7 +405,6 @@ static uint32_t AllocClusterEx(FAT32_VOL *v, int zeroCluster) {
return 0;
}
static uint32_t AllocCluster(FAT32_VOL *v) {
return AllocClusterEx(v, 1);
}
@@ -703,9 +703,9 @@ static int FatIsValidShortChar(char c) {
}
}
// True when the user spelling can refer to a normal FAT 8.3 entry. This is
// True when the user spelling can refer to a normal FAT 8.3 entry. This is
// intentionally structural and case-insensitive: "doom1.wad" must still open
// an existing short entry named DOOM1.WAD. Leading-dot names are not 8.3.
// an existing short entry named DOOM1.WAD. Leading-dot names are not 8.3.
static int FatNameIsStructurallyShort83V(SYS_STRING_VIEW name) {
if (!SysSvIsValid(name) || name.Length == 0) return 0;
if (name.Data[0] == '.') return 0;
@@ -757,7 +757,7 @@ static void FatSplitShortNameV(SYS_STRING_VIEW name, size_t* baseEnd,
}
// 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
// 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;
@@ -991,7 +991,7 @@ static KSTATUS DeleteLegacyBrokenDotAliasV(FAT32_VOL *v, uint32_t dirCluster,
for (int j = 0; j < 8; j++) if (dir[i].Name[j] != ' ') emptyBase = 0;
if (!emptyBase) continue;
// Do not free the cluster chain here. The broken entry may have
// Do not free the cluster chain here. The broken entry may have
// aliased existing data; deleting the directory entry is enough to
// hide it from hosts and avoids freeing clusters still owned by a
// valid file.
@@ -1024,7 +1024,6 @@ static void LfnAppend(char *lfn, const LFN_ENTRY *e) {
}
}
// Directory search and view-native path traversal.
static int Fat32IsPathSep(char c) {
return c == '\\' || c == '/';
@@ -1109,7 +1108,7 @@ static void Fat32PathCacheAddDirectoryStats(FAT32_VOL* v, uint64_t clusters, uin
if (!v) return;
uintptr_t flags = KeAcquireSpinlock(&v->PathCacheLock);
v->Stats.DirectorySearches++;
v->Stats.DirectoryClustersRead += clusters;
v->Stats.DirectoryBlocksRead += clusters;
v->Stats.DirectoryEntriesScanned += entries;
KeReleaseSpinlock(&v->PathCacheLock, flags);
}
@@ -1199,15 +1198,6 @@ void Fat32QueryStats(IO_FILESYSTEM* fs, IO_FILESYSTEM_STATS* out) {
KeReleaseSpinlock(&v->PathCacheLock, flags);
}
// Adds FAT-chain traversal counters shared by the read path helpers.
static void Fat32StatsAddChainWalk(FAT32_VOL* v, uint64_t steps) {
if (!v || !steps) return;
uintptr_t flags = KeAcquireSpinlock(&v->PathCacheLock);
v->Stats.FatChainWalkSteps += steps;
KeReleaseSpinlock(&v->PathCacheLock, flags);
}
// Records one data read span and its physically contiguous cluster merges.
static void Fat32StatsAddReadRun(FAT32_VOL* v, uint32_t sectors, uint32_t merges) {
if (!v || !sectors) return;
@@ -1235,12 +1225,6 @@ static void Fat32FileInvalidateChainCache(FAT32_FILE* f) {
memset(f->ChainCache, 0, sizeof(f->ChainCache));
f->ChainCacheClock = 0;
KeReleaseSpinlock(&f->ChainCacheLock, flags);
FAT32_VOL* v = f->Vol;
if (!v) return;
flags = KeAcquireSpinlock(&v->PathCacheLock);
v->Stats.FatChainCacheInvalidations++;
KeReleaseSpinlock(&v->PathCacheLock, flags);
}
// Locates the best cached starting point for a logical cluster index.
@@ -1281,15 +1265,6 @@ static bool Fat32ChainCacheLookupStart(FAT32_FILE* f, uint32_t logicalIndex,
*outCluster = bestCluster;
}
KeReleaseSpinlock(&f->ChainCacheLock, flags);
FAT32_VOL* v = f->Vol;
if (v) {
flags = KeAcquireSpinlock(&v->PathCacheLock);
v->Stats.FatChainCacheLookups++;
if (hit) v->Stats.FatChainCacheHits++;
else v->Stats.FatChainCacheMisses++;
KeReleaseSpinlock(&v->PathCacheLock, flags);
}
return hit;
}
@@ -1309,12 +1284,6 @@ static void Fat32ChainCacheStore(FAT32_FILE* f, uint32_t logicalStart,
e->Length = length;
e->NextCluster = nextCluster;
KeReleaseSpinlock(&f->ChainCacheLock, flags);
FAT32_VOL* v = f->Vol;
if (!v) return;
flags = KeAcquireSpinlock(&v->PathCacheLock);
v->Stats.FatChainCacheStores++;
KeReleaseSpinlock(&v->PathCacheLock, flags);
}
// Resolves a logical file cluster through the cached FAT-chain extent map.
@@ -1331,13 +1300,10 @@ static uint32_t Fat32ResolveFileCluster(FAT32_FILE* f, uint32_t logicalIndex) {
cluster = f->StartCluster;
}
uint64_t steps = 0;
while (currentLogical < logicalIndex && cluster >= 2 && cluster < FAT32_EOC) {
cluster = FatEntry(f->Vol, cluster);
currentLogical++;
steps++;
}
Fat32StatsAddChainWalk(f->Vol, steps);
return cluster;
}
@@ -1355,7 +1321,6 @@ static bool Fat32BuildReadRun(FAT32_FILE* f, uint32_t logicalIndex,
uint32_t runClusters = 1;
uint32_t last = startCluster;
uint32_t next = FatEntry(v, last);
uint64_t steps = 1;
while (next == last + 1 && next < FAT32_EOC &&
runSectors + v->SectorsPerCluster <= maxSectors) {
@@ -1363,10 +1328,8 @@ static bool Fat32BuildReadRun(FAT32_FILE* f, uint32_t logicalIndex,
runClusters++;
runSectors += v->SectorsPerCluster;
next = FatEntry(v, last);
steps++;
}
Fat32StatsAddChainWalk(v, steps);
Fat32ChainCacheStore(f, logicalIndex, startCluster, runClusters, next);
*outRunSectors = runSectors;
*outNextCluster = next;
@@ -1414,8 +1377,8 @@ static int SearchDirV(FAT32_VOL *v, uint32_t dirCluster, SYS_STRING_VIEW name,
if (match) {
*outCluster = ((uint32_t)dir[i].FirstClusterHi << 16) | dir[i].FirstClusterLo;
*outSize = dir[i].FileSize;
*outAttr = dir[i].Attr;
*outSize = dir[i].FileSize;
*outAttr = dir[i].Attr;
if (outDirentLba || outDirentOffset) {
uint64_t eb = (uint64_t)i * sizeof(DIRENT);
if (outDirentLba) *outDirentLba = clusterLba + eb / v->BytesPerSector;
@@ -1453,6 +1416,7 @@ static int Fat32PathNextComponent(SYS_STRING_VIEW path, size_t* cursor,
static KSTATUS Fat32LookupPathV(FAT32_VOL *v, SYS_STRING_VIEW path,
uint32_t *outCluster, uint32_t *outSize,
uint8_t *outAttr,
uint64_t *outDirentLba, uint32_t *outDirentOffset) {
if (!v || !outCluster || !outSize || !SysSvIsValid(path))
return STATUS_INVALID_PARAMETER;
@@ -1461,12 +1425,16 @@ static KSTATUS Fat32LookupPathV(FAT32_VOL *v, SYS_STRING_VIEW path,
uint32_t cluster = v->RootCluster, size = 0;
uint8_t attr = ATTR_DIRECTORY;
uint64_t finalDirentLba = 0;
uint32_t finalDirentOffset = 0;
if (outDirentLba) *outDirentLba = 0;
if (outDirentOffset) *outDirentOffset = 0;
if (Fat32PathCacheLookup(v, path, outCluster, outSize, &attr,
outDirentLba, outDirentOffset))
outDirentLba, outDirentOffset)) {
if (outAttr) *outAttr = attr;
return STATUS_SUCCESS;
}
size_t cursor = 0;
SYS_STRING_VIEW component;
@@ -1482,21 +1450,23 @@ static KSTATUS Fat32LookupPathV(FAT32_VOL *v, SYS_STRING_VIEW path,
return STATUS_NOT_FOUND;
cluster = childCluster; size = childSize; attr = childAttr;
finalDirentLba = childLba;
finalDirentOffset = childOffset;
if (outDirentLba) *outDirentLba = childLba;
if (outDirentOffset) *outDirentOffset = childOffset;
}
*outCluster = cluster;
*outSize = size;
if (outAttr) *outAttr = attr;
Fat32PathCacheStore(v, path, cluster, size, attr,
outDirentLba ? *outDirentLba : 0,
outDirentOffset ? *outDirentOffset : 0);
finalDirentLba, finalDirentOffset);
return STATUS_SUCCESS;
}
static KSTATUS TraverseV(FAT32_VOL *v, SYS_STRING_VIEW path,
uint32_t *outCluster, uint32_t *outSize) {
return Fat32LookupPathV(v, path, outCluster, outSize, NULL, NULL);
return Fat32LookupPathV(v, path, outCluster, outSize, NULL, NULL, NULL);
}
static KSTATUS SplitPathV(SYS_STRING_VIEW path, SYS_STRING_VIEW* parent,
@@ -1546,7 +1516,7 @@ static int FindFreeDirent(FAT32_VOL *v, uint32_t dirCluster,
uint8_t first = (uint8_t)dir[i].Name[0];
if (first == 0xE5 || first == 0x00) {
uint64_t eb = (uint64_t)i * sizeof(DIRENT);
*outLba = clusterLba + eb / v->BytesPerSector;
*outLba = clusterLba + eb / v->BytesPerSector;
*outOffset = (uint32_t)(eb % v->BytesPerSector);
MmFreePool(clusterBuf); return 1;
}
@@ -1564,7 +1534,6 @@ static int FindFreeDirent(FAT32_VOL *v, uint32_t dirCluster,
return 0;
}
// Find a contiguous run of free directory entries within one directory cluster.
// This is required for VFAT LFN creation because N long-name entries must
// immediately precede the final short entry.
@@ -1619,6 +1588,284 @@ static int FindFreeDirentRun(FAT32_VOL *v, uint32_t dirCluster, uint32_t needed,
return 0;
}
// Finds the parent directory and final component for a create or delete path.
static KSTATUS Fat32ResolveParentForMutation(FAT32_VOL *v, SYS_STRING_VIEW path,
uint32_t *parentClusterOut,
SYS_STRING_VIEW *filenameOut) {
if (!v || !parentClusterOut || !filenameOut)
return STATUS_INVALID_PARAMETER;
SYS_STRING_VIEW parentPath;
SYS_STRING_VIEW filenameView;
KSTATUS s = SplitPathV(path, &parentPath, &filenameView);
if (!KSUCCESS(s)) return s;
uint32_t parentCluster = 0;
uint32_t parentSize = 0;
uint8_t parentAttr = 0;
s = Fat32LookupPathV(v, parentPath, &parentCluster, &parentSize,
&parentAttr, NULL, NULL);
if (!KSUCCESS(s)) return s;
if (!(parentAttr & ATTR_DIRECTORY)) return STATUS_NOT_FOUND;
*parentClusterOut = parentCluster;
*filenameOut = filenameView;
return STATUS_SUCCESS;
}
// Creates a FAT directory entry run for a new file or directory.
static KSTATUS Fat32CreateDirent(FAT32_VOL *v, SYS_STRING_VIEW path,
uint8_t attr, uint32_t firstCluster,
uint32_t fileSize,
uint64_t *direntLbaOut,
uint32_t *direntOffsetOut) {
if (!v || !SysSvIsValid(path) || path.Length == 0)
return STATUS_INVALID_PARAMETER;
uint32_t parentCluster = 0;
SYS_STRING_VIEW filenameView;
KSTATUS s = Fat32ResolveParentForMutation(v, path, &parentCluster, &filenameView);
if (!KSUCCESS(s)) return s;
{
uint32_t ec, es;
uint8_t ea;
uint64_t existingLba = 0;
uint32_t existingOffset = 0;
if (SearchDirV(v, parentCluster, filenameView, &ec, &es, &ea,
&existingLba, &existingOffset)) {
return STATUS_OBJECT_NAME_COLLISION;
}
}
int useLfn = FatNameNeedsLfnForCreateV(filenameView);
char shortName[8], shortExt[3];
if (useLfn) {
s = DeleteLegacyBrokenDotAliasV(v, parentCluster, filenameView);
if (!KSUCCESS(s)) return s;
if (!MakeUniqueShortAliasV(v, parentCluster, filenameView, shortName, shortExt))
return STATUS_OBJECT_NAME_COLLISION;
} else {
Make83NameV(filenameView, shortName, shortExt);
}
size_t nameLen = filenameView.Length;
if (nameLen == 0 || nameLen >= MAX_LFN) return STATUS_INVALID_PARAMETER;
uint32_t lfnCount = useLfn ? (uint32_t)((nameLen + 12u) / 13u) : 0;
uint32_t neededEntries = lfnCount + 1u;
uint32_t entryCluster, firstIndex;
if (!FindFreeDirentRun(v, parentCluster, neededEntries, &entryCluster, &firstIndex))
return STATUS_NO_MEMORY;
uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
if (!clusterBuf) return STATUS_NO_MEMORY;
uint64_t clusterLba = ClusterToLba(v, entryCluster);
s = ReadSectors(v, clusterLba, v->SectorsPerCluster, clusterBuf);
if (!KSUCCESS(s)) { MmFreePool(clusterBuf); return s; }
DIRENT *dir = (DIRENT *)clusterBuf;
uint32_t shortIndex = firstIndex + lfnCount;
if (useLfn) {
uint8_t checksum = LfnChecksum(shortName, shortExt);
for (uint32_t slot = 0; slot < lfnCount; slot++) {
uint32_t ordinal = lfnCount - slot;
BuildLfnEntry((LFN_ENTRY *)&dir[firstIndex + slot], filenameView.Data,
nameLen, ordinal, lfnCount, checksum);
}
}
DIRENT *d = &dir[shortIndex];
memset(d, 0, sizeof(DIRENT));
memcpy(d->Name, shortName, 8);
memcpy(d->Ext, shortExt, 3);
d->Attr = attr;
d->Reserved = FatShortNameCaseFlagsV(filenameView);
d->FirstClusterHi = (uint16_t)(firstCluster >> 16);
d->FirstClusterLo = (uint16_t)(firstCluster & 0xFFFFu);
d->FileSize = fileSize;
{
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);
if (!KSUCCESS(s)) return s;
uint64_t shortEntryByte = (uint64_t)shortIndex * sizeof(DIRENT);
uint64_t direntLba = clusterLba + shortEntryByte / v->BytesPerSector;
uint32_t direntOffset = (uint32_t)(shortEntryByte % v->BytesPerSector);
if (direntLbaOut) *direntLbaOut = direntLba;
if (direntOffsetOut) *direntOffsetOut = direntOffset;
Fat32PathCacheInvalidate(v);
return STATUS_SUCCESS;
}
// Initializes one dot entry in a new directory cluster.
static void Fat32InitDotEntry(DIRENT *d, const char *name,
uint32_t firstCluster,
const FAT32_ENCODED_TIME *now) {
memset(d, 0, sizeof(*d));
memset(d->Name, ' ', sizeof(d->Name));
memset(d->Ext, ' ', sizeof(d->Ext));
d->Name[0] = name[0];
if (name[1]) d->Name[1] = name[1];
d->Attr = ATTR_DIRECTORY;
d->FirstClusterHi = (uint16_t)(firstCluster >> 16);
d->FirstClusterLo = (uint16_t)(firstCluster & 0xFFFFu);
d->CrtTimeTenth = now->CreateTimeTenth;
d->CrtTime = now->Time;
d->CrtDate = now->Date;
d->LastAccDate = now->Date;
d->WrtTime = now->Time;
d->WrtDate = now->Date;
}
// Writes the initial contents of a newly allocated FAT directory cluster.
static KSTATUS Fat32InitializeDirectoryCluster(FAT32_VOL *v, uint32_t dirCluster,
uint32_t parentCluster) {
if (!v || dirCluster < 2)
return STATUS_INVALID_PARAMETER;
uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
if (!clusterBuf) return STATUS_NO_MEMORY;
memset(clusterBuf, 0, v->BytesPerCluster);
FAT32_ENCODED_TIME now;
FatQueryEncodedTime(&now);
DIRENT *dir = (DIRENT *)clusterBuf;
Fat32InitDotEntry(&dir[0], ".", dirCluster, &now);
Fat32InitDotEntry(&dir[1], "..", parentCluster, &now);
KSTATUS s = WriteSectors(v, ClusterToLba(v, dirCluster),
v->SectorsPerCluster, clusterBuf);
MmFreePool(clusterBuf);
return s;
}
// Tests whether a FAT directory entry is one of the synthetic dot entries.
static int Fat32IsDotEntry(const DIRENT *d) {
if (!d || !(d->Attr & ATTR_DIRECTORY)) return 0;
if (d->Name[0] != '.') return 0;
if (d->Name[1] == ' ')
return 1;
return d->Name[1] == '.' && d->Name[2] == ' ';
}
// Tests whether a directory contains any real child entries.
static KSTATUS Fat32DirectoryIsEmpty(FAT32_VOL *v, uint32_t dirCluster,
int *emptyOut) {
if (!v || !emptyOut || dirCluster < 2)
return STATUS_INVALID_PARAMETER;
uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
if (!clusterBuf) return STATUS_NO_MEMORY;
uint32_t cluster = dirCluster;
while (cluster >= 2 && cluster < FAT32_EOC) {
KSTATUS s = ReadSectors(v, ClusterToLba(v, cluster),
v->SectorsPerCluster, clusterBuf);
if (!KSUCCESS(s)) { MmFreePool(clusterBuf); return s; }
uint32_t entries = v->BytesPerCluster / sizeof(DIRENT);
DIRENT *dir = (DIRENT *)clusterBuf;
for (uint32_t i = 0; i < entries; i++) {
uint8_t first = (uint8_t)dir[i].Name[0];
if (first == 0x00) { MmFreePool(clusterBuf); *emptyOut = 1; return STATUS_SUCCESS; }
if (first == 0xE5 || dir[i].Attr == ATTR_LFN) continue;
if ((dir[i].Attr & ATTR_VOLUME_ID) && !(dir[i].Attr & ATTR_DIRECTORY)) continue;
if (Fat32IsDotEntry(&dir[i])) continue;
MmFreePool(clusterBuf);
*emptyOut = 0;
return STATUS_SUCCESS;
}
cluster = FatEntry(v, cluster);
}
MmFreePool(clusterBuf);
*emptyOut = 1;
return STATUS_SUCCESS;
}
// Deletes the directory-entry run for one child name from a parent directory.
static KSTATUS Fat32DeleteDirentRunV(FAT32_VOL *v, uint32_t parentCluster,
SYS_STRING_VIEW name) {
if (!v || !Fat32NameViewIsValid(name))
return STATUS_INVALID_PARAMETER;
uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
if (!clusterBuf) return STATUS_NO_MEMORY;
char lfn[MAX_LFN];
int hasLfn = 0;
uint32_t lfnStart = 0;
memset(lfn, 0, sizeof(lfn));
char wantName83[8], wantExt83[3];
int allowShortMatch = FatNameIsStructurallyShort83V(name);
Make83NameV(name, wantName83, wantExt83);
uint32_t cluster = parentCluster;
while (cluster >= 2 && cluster < FAT32_EOC) {
uint64_t clusterLba = ClusterToLba(v, cluster);
KSTATUS s = ReadSectors(v, clusterLba, v->SectorsPerCluster, clusterBuf);
if (!KSUCCESS(s)) { MmFreePool(clusterBuf); return s; }
uint32_t entries = v->BytesPerCluster / sizeof(DIRENT);
DIRENT *dir = (DIRENT *)clusterBuf;
hasLfn = 0;
memset(lfn, 0, sizeof(lfn));
for (uint32_t i = 0; i < entries; i++) {
uint8_t first = (uint8_t)dir[i].Name[0];
if (first == 0x00) { MmFreePool(clusterBuf); return STATUS_NOT_FOUND; }
if (first == 0xE5) { hasLfn = 0; memset(lfn, 0, sizeof(lfn)); continue; }
if (dir[i].Attr == ATTR_LFN) {
if (!hasLfn) lfnStart = i;
LfnAppend(lfn, (LFN_ENTRY *)&dir[i]);
hasLfn = 1;
continue;
}
int match = 0;
if (hasLfn && lfn[0]) match = LfnMatchV(lfn, name);
if (!match && allowShortMatch)
match = MatchName83V(dir[i].Name, dir[i].Ext, name);
if (!match && allowShortMatch)
match = MatchRaw83(dir[i].Name, dir[i].Ext, wantName83, wantExt83);
if (match) {
uint32_t start = hasLfn ? lfnStart : i;
for (uint32_t j = start; j <= i; j++)
dir[j].Name[0] = (char)0xE5;
s = WriteSectors(v, clusterLba, v->SectorsPerCluster, clusterBuf);
MmFreePool(clusterBuf);
if (KSUCCESS(s)) Fat32PathCacheInvalidate(v);
return s;
}
hasLfn = 0;
memset(lfn, 0, sizeof(lfn));
}
cluster = FatEntry(v, cluster);
}
MmFreePool(clusterBuf);
return STATUS_NOT_FOUND;
}
// Open / Close.
KSTATUS Fat32Open(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
if (!fs || !fileCtx || !SysSvIsValid(path) || path.Length == 0)
@@ -1626,7 +1873,7 @@ KSTATUS Fat32Open(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
uint32_t cluster, size; uint64_t direntLba; uint32_t direntOffset;
KSTATUS s = Fat32LookupPathV(v, path, &cluster, &size, &direntLba, &direntOffset);
KSTATUS s = Fat32LookupPathV(v, path, &cluster, &size, NULL, &direntLba, &direntOffset);
if (!KSUCCESS(s)) return s;
FAT32_FILE *f = (FAT32_FILE *)MmAllocatePool(sizeof(FAT32_FILE));
@@ -1646,7 +1893,7 @@ void Fat32Close(IO_FILESYSTEM *fs, void *fileCtx) {
MmFreePool(fileCtx);
}
// Read (fix #4: contiguous-run coalescing)
// Read (fix #4: contiguous-run coalescing)
// Cluster-chain lookup is cached per open file, and data spans are allowed to
// grow beyond the AHCI bounce size because the device layer now splits large
// sector requests for the concrete controller.
@@ -1722,7 +1969,7 @@ KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
if (totalRead != wanted) {
// The directory entry says the file has more data than the cluster chain
// or disk read actually supplied. Clear the caller's remainder so stale
// or disk read actually supplied. Clear the caller's remainder so stale
// memory cannot be interpreted as file contents.
memset(out, 0, wanted - totalRead);
}
@@ -1734,8 +1981,8 @@ KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
return STATUS_SUCCESS;
}
// Write (fix #4: contiguous-run coalescing)
// Same strategy as read. Whole-cluster-aligned runs skip the read-modify-
// Write (fix #4: contiguous-run coalescing)
// Same strategy as read. Whole-cluster-aligned runs skip the read-modify-
// write; partial head/tail clusters still require it.
KSTATUS Fat32Write(IO_FILESYSTEM *fs, void *fileCtx,
@@ -1852,121 +2099,99 @@ KSTATUS Fat32Write(IO_FILESYSTEM *fs, void *fileCtx,
}
// Create.
// Creates the requested object.
// Creates the requested file and fails if the name already exists.
KSTATUS Fat32Create(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
if (!fs || !fileCtx || !SysSvIsValid(path) || path.Length == 0)
return STATUS_INVALID_PARAMETER;
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
SYS_STRING_VIEW parentPath;
SYS_STRING_VIEW filenameView;
KSTATUS s = SplitPathV(path, &parentPath, &filenameView);
uint64_t direntLba = 0;
uint32_t direntOffset = 0;
KSTATUS s = Fat32CreateDirent(v, path, ATTR_ARCHIVE, 0, 0,
&direntLba, &direntOffset);
if (!KSUCCESS(s)) return s;
uint32_t parentCluster, parentSize;
s = TraverseV(v, parentPath, &parentCluster, &parentSize);
if (!KSUCCESS(s)) return s;
{
uint32_t ec, es; uint8_t ea;
uint64_t existingLba = 0; uint32_t existingOffset = 0;
if (SearchDirV(v, parentCluster, filenameView, &ec, &es, &ea,
&existingLba, &existingOffset)) {
if (ea & ATTR_DIRECTORY)
return STATUS_OBJECT_NAME_COLLISION;
FAT32_FILE *f = (FAT32_FILE *)MmAllocatePool(sizeof(FAT32_FILE));
if (!f) return STATUS_NO_MEMORY;
f->StartCluster = ec;
f->FileSize = es;
f->Vol = v;
f->DirentLba = existingLba;
f->DirentOffset = existingOffset;
// Create means overwrite by default for regular files. Reuse the
// existing directory entry, drop the old cluster chain, and return
// an open zero-length file instead of allocating a duplicate name.
KSTATUS ts = Fat32Truncate(fs, f, 0, 0);
if (!KSUCCESS(ts)) { MmFreePool(f); return ts; }
*fileCtx = f;
return STATUS_SUCCESS;
}
}
int useLfn = FatNameNeedsLfnForCreateV(filenameView);
char shortName[8], shortExt[3];
if (useLfn) {
s = DeleteLegacyBrokenDotAliasV(v, parentCluster, filenameView);
if (!KSUCCESS(s)) return s;
if (!MakeUniqueShortAliasV(v, parentCluster, filenameView, shortName, shortExt))
return STATUS_OBJECT_NAME_COLLISION;
} else {
Make83NameV(filenameView, shortName, shortExt);
}
size_t nameLen = filenameView.Length;
if (nameLen == 0 || nameLen >= MAX_LFN) return STATUS_INVALID_PARAMETER;
uint32_t lfnCount = useLfn ? (uint32_t)((nameLen + 12u) / 13u) : 0;
uint32_t neededEntries = lfnCount + 1u;
uint32_t entryCluster, firstIndex;
if (!FindFreeDirentRun(v, parentCluster, neededEntries, &entryCluster, &firstIndex))
return STATUS_NO_MEMORY;
uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
if (!clusterBuf) return STATUS_NO_MEMORY;
uint64_t clusterLba = ClusterToLba(v, entryCluster);
s = ReadSectors(v, clusterLba, v->SectorsPerCluster, clusterBuf);
if (!KSUCCESS(s)) { MmFreePool(clusterBuf); return s; }
DIRENT *dir = (DIRENT *)clusterBuf;
uint32_t shortIndex = firstIndex + lfnCount;
if (useLfn) {
uint8_t checksum = LfnChecksum(shortName, shortExt);
for (uint32_t slot = 0; slot < lfnCount; slot++) {
uint32_t ordinal = lfnCount - slot;
BuildLfnEntry((LFN_ENTRY *)&dir[firstIndex + slot], filenameView.Data,
nameLen, ordinal, lfnCount, checksum);
}
}
DIRENT *d = &dir[shortIndex];
memset(d, 0, sizeof(DIRENT));
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);
if (!KSUCCESS(s)) return s;
uint64_t shortEntryByte = (uint64_t)shortIndex * sizeof(DIRENT);
uint64_t direntLba = clusterLba + shortEntryByte / v->BytesPerSector;
uint32_t direntOffset = (uint32_t)(shortEntryByte % v->BytesPerSector);
FAT32_FILE *f = (FAT32_FILE *)MmAllocatePool(sizeof(FAT32_FILE));
if (!f) return STATUS_NO_MEMORY;
f->StartCluster = 0; f->FileSize = 0; f->Vol = v;
f->DirentLba = direntLba; f->DirentOffset = direntOffset;
f->StartCluster = 0;
f->FileSize = 0;
f->Vol = v;
f->DirentLba = direntLba;
f->DirentOffset = direntOffset;
Fat32FileInitChainCache(f);
Fat32PathCacheInvalidate(v);
*fileCtx = f;
return STATUS_SUCCESS;
}
// Creates a directory and initializes its dot entries.
KSTATUS Fat32CreateDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW path) {
if (!fs || !SysSvIsValid(path) || path.Length == 0)
return STATUS_INVALID_PARAMETER;
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
uint32_t parentCluster = 0;
SYS_STRING_VIEW filenameView;
KSTATUS s = Fat32ResolveParentForMutation(v, path, &parentCluster, &filenameView);
if (!KSUCCESS(s)) return s;
(void)filenameView;
uint32_t dirCluster = AllocCluster(v);
if (!dirCluster) return STATUS_NO_MEMORY;
s = Fat32InitializeDirectoryCluster(v, dirCluster, parentCluster);
if (!KSUCCESS(s)) {
FreeClusterChain(v, dirCluster);
FlushFatCache(v);
return s;
}
s = Fat32CreateDirent(v, path, ATTR_DIRECTORY, dirCluster, 0, NULL, NULL);
if (!KSUCCESS(s)) {
FreeClusterChain(v, dirCluster);
FlushFatCache(v);
return s;
}
FlushFatCache(v);
return STATUS_SUCCESS;
}
// Removes an empty directory.
KSTATUS Fat32RemoveDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW path) {
if (!fs || !SysSvIsValid(path) || path.Length == 0)
return STATUS_INVALID_PARAMETER;
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
uint32_t cluster = 0;
uint32_t size = 0;
uint8_t attr = 0;
uint64_t direntLba = 0;
uint32_t direntOffset = 0;
KSTATUS s = Fat32LookupPathV(v, path, &cluster, &size, &attr,
&direntLba, &direntOffset);
if (!KSUCCESS(s)) return s;
if (!direntLba) return STATUS_ACCESS_DENIED;
if (!(attr & ATTR_DIRECTORY)) return STATUS_ACCESS_DENIED;
int empty = 0;
s = Fat32DirectoryIsEmpty(v, cluster, &empty);
if (!KSUCCESS(s)) return s;
if (!empty) return STATUS_ACCESS_DENIED;
uint32_t parentCluster = 0;
SYS_STRING_VIEW filenameView;
s = Fat32ResolveParentForMutation(v, path, &parentCluster, &filenameView);
if (!KSUCCESS(s)) return s;
s = Fat32DeleteDirentRunV(v, parentCluster, filenameView);
if (!KSUCCESS(s)) return s;
if (cluster >= 2) FreeClusterChain(v, cluster);
FlushFatCache(v);
Fat32PathCacheInvalidate(v);
return STATUS_SUCCESS;
}
// Truncate.
KSTATUS Fat32Truncate(IO_FILESYSTEM *fs, void *fileCtx, uint64_t newSize, uint32_t fileFlags) {
(void)fs;
@@ -2044,7 +2269,7 @@ KSTATUS Fat32Delete(IO_FILESYSTEM *fs, SYS_STRING_VIEW path) {
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
uint32_t cluster, size; uint64_t direntLba; uint32_t direntOffset;
KSTATUS s = Fat32LookupPathV(v, path, &cluster, &size, &direntLba, &direntOffset);
KSTATUS s = Fat32LookupPathV(v, path, &cluster, &size, NULL, &direntLba, &direntOffset);
if (!KSUCCESS(s)) return s;
if (!direntLba) return STATUS_ACCESS_DENIED;
@@ -2066,6 +2291,139 @@ uint64_t Fat32GetSize(IO_FILESYSTEM *fs, void *fileCtx) {
(void)fs; return ((FAT32_FILE *)fileCtx)->FileSize;
}
// Returns a stable volume identity for metadata and cache keys.
static uint64_t Fat32VolumeIdentity(FAT32_VOL *v) {
if (!v) return 0;
return ((uint64_t)v->DriveIndex << 56) ^
((uint64_t)v->PartitionLba << 8) ^
((uint64_t)v->RootCluster << 1) ^
0x4641543332494445ULL;
}
// Converts FAT attributes to the public file type.
static uint32_t Fat32FileTypeFromAttr(uint8_t attr) {
return (attr & ATTR_DIRECTORY) ? SYS_FILE_TYPE_DIRECTORY : SYS_FILE_TYPE_REGULAR;
}
// Fills public metadata from one FAT directory entry.
static void Fat32FillStatFromDirent(FAT32_VOL *v, const DIRENT *d,
uint64_t direntLba, uint32_t direntOffset,
SYS_FILE_STAT *out) {
memset(out, 0, sizeof(*out));
out->Size = sizeof(*out);
out->Type = Fat32FileTypeFromAttr(d->Attr);
out->Attributes = d->Attr;
out->LinkCount = 1;
out->FileSize = d->FileSize;
out->VolumeId = Fat32VolumeIdentity(v);
uint32_t firstCluster = ((uint32_t)d->FirstClusterHi << 16) | d->FirstClusterLo;
out->FileId = direntLba ^
((uint64_t)direntOffset << 32) ^
((uint64_t)firstCluster << 1);
FatDecodeDateTime(d->CrtDate, d->CrtTime, d->CrtTimeTenth, &out->CreationTime);
FatDecodeAccessDate(d->LastAccDate, &out->LastAccessTime);
FatDecodeDateTime(d->WrtDate, d->WrtTime, 0, &out->LastWriteTime);
out->ChangeTime = out->LastWriteTime;
}
// Fills public metadata for the filesystem root.
static void Fat32FillRootStat(FAT32_VOL *v, SYS_FILE_STAT *out) {
memset(out, 0, sizeof(*out));
out->Size = sizeof(*out);
out->Type = SYS_FILE_TYPE_DIRECTORY;
out->Attributes = SYS_FILE_ATTR_DIRECTORY;
out->LinkCount = 1;
out->FileSize = 0;
out->VolumeId = Fat32VolumeIdentity(v);
out->FileId = ((uint64_t)v->RootCluster << 1) ^ 0x524f4f54ULL;
}
// Queries metadata for an open FAT32 file.
KSTATUS Fat32QueryInfo(IO_FILESYSTEM *fs, void *fileCtx, SYS_FILE_STAT *out) {
(void)fs;
if (!fileCtx || !out) return STATUS_INVALID_PARAMETER;
FAT32_FILE *f = (FAT32_FILE *)fileCtx;
FAT32_VOL *v = f->Vol;
if (!v) return STATUS_INVALID_PARAMETER;
if (f->DirentLba == 0) {
if (f->StartCluster == v->RootCluster && f->FileSize == 0) {
Fat32FillRootStat(v, out);
} else {
memset(out, 0, sizeof(*out));
out->Size = sizeof(*out);
out->Type = SYS_FILE_TYPE_REGULAR;
out->Attributes = SYS_FILE_ATTR_ARCHIVE;
out->LinkCount = 1;
out->FileSize = f->FileSize;
out->VolumeId = Fat32VolumeIdentity(v);
out->FileId = ((uint64_t)f->StartCluster << 1);
}
return STATUS_SUCCESS;
}
uint8_t *sec = (uint8_t *)MmAllocatePool(v->BytesPerSector);
if (!sec) return STATUS_NO_MEMORY;
KSTATUS s = ReadSectors(v, f->DirentLba, 1, sec);
if (!KSUCCESS(s)) { MmFreePool(sec); return s; }
DIRENT *d = (DIRENT *)(sec + f->DirentOffset);
if ((uint8_t)d->Name[0] == 0x00 || (uint8_t)d->Name[0] == 0xE5) {
MmFreePool(sec);
return STATUS_NOT_FOUND;
}
Fat32FillStatFromDirent(v, d, f->DirentLba, f->DirentOffset, out);
f->StartCluster = ((uint32_t)d->FirstClusterHi << 16) | d->FirstClusterLo;
f->FileSize = d->FileSize;
MmFreePool(sec);
return STATUS_SUCCESS;
}
// Queries metadata for a FAT32 path.
KSTATUS Fat32Stat(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, SYS_FILE_STAT *out) {
if (!fs || !out || !SysSvIsValid(path)) return STATUS_INVALID_PARAMETER;
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
if (!v) return STATUS_INVALID_PARAMETER;
uint32_t cluster = 0;
uint32_t size = 0;
uint8_t attr = ATTR_DIRECTORY;
uint64_t direntLba = 0;
uint32_t direntOffset = 0;
KSTATUS s = Fat32LookupPathV(v, path, &cluster, &size, &attr, &direntLba, &direntOffset);
if (!KSUCCESS(s)) return s;
(void)cluster;
if (direntLba == 0) {
Fat32FillRootStat(v, out);
return STATUS_SUCCESS;
}
uint8_t *sec = (uint8_t *)MmAllocatePool(v->BytesPerSector);
if (!sec) return STATUS_NO_MEMORY;
s = ReadSectors(v, direntLba, 1, sec);
if (!KSUCCESS(s)) { MmFreePool(sec); return s; }
DIRENT *d = (DIRENT *)(sec + direntOffset);
if ((uint8_t)d->Name[0] == 0x00 || (uint8_t)d->Name[0] == 0xE5) {
MmFreePool(sec);
return STATUS_NOT_FOUND;
}
Fat32FillStatFromDirent(v, d, direntLba, direntOffset, out);
out->Type = Fat32FileTypeFromAttr(attr);
out->FileSize = size;
MmFreePool(sec);
return STATUS_SUCCESS;
}
// Mixes FAT directory metadata into a per-file content generation.
static uint64_t Fat32DirentGeneration(const DIRENT *d) {
if (!d)
@@ -2123,10 +2481,7 @@ KSTATUS Fat32QueryIdentity(IO_FILESYSTEM *fs, void *fileCtx, IO_FILE_IDENTITY *o
MmFreePool(sec);
}
out->VolumeId = ((uint64_t)v->DriveIndex << 56) ^
((uint64_t)v->PartitionLba << 8) ^
((uint64_t)v->RootCluster << 1) ^
0x4641543332494445ULL;
out->VolumeId = Fat32VolumeIdentity(v);
out->FileId = f->DirentLba ^
((uint64_t)f->DirentOffset << 32) ^
((uint64_t)startCluster << 1);
@@ -2135,7 +2490,7 @@ KSTATUS Fat32QueryIdentity(IO_FILESYSTEM *fs, void *fileCtx, IO_FILE_IDENTITY *o
return STATUS_SUCCESS;
}
// Directory enumeration (fix #2: cluster buffer cached in context)
// Directory enumeration (fix #2: cluster buffer cached in context)
// Previously: allocate + ReadSectors + return 1 entry + free, every call.
// Now: buffer lives in FAT32_DIR_CTX; ReadSectors fires only on cluster
// boundaries.
@@ -2418,7 +2773,7 @@ static uint32_t Fat32EnumerateDiskVolumes(uint8_t diskIndex,
uint32_t size = ReadLe32(entry + 12);
if (!start || !size) continue;
// FAT32 CHS/LBA plus EFI System Partition. The VBR probe is the
// FAT32 CHS/LBA plus EFI System Partition. The VBR probe is the
// authority, so accepting 0xEF here lets FAT32 ESPs mount too.
if (type == 0x0B || type == 0x0C || type == 0xEF)
Fat32AddCandidate(&probe, out, &count, maxCount, start, size);
@@ -2928,7 +3283,7 @@ static uint32_t Fat32TryMountDisk(uint8_t disk) {
return 0;
}
// Only remember a completed probe. A transient hotplug/readiness failure
// Only remember a completed probe. A transient hotplug/readiness failure
// must not permanently suppress a later mount attempt for the same disk
// index after a device is unplugged and replugged.
gDiskScanned[disk] = true;
@@ -3029,7 +3384,7 @@ static void Fat32UnmountDisk(uint8_t diskIndex, IO_DEVICE_CHANGE change) {
continue;
// Surprise removal means the backing device may already be gone; do not
// attempt writeback here. Drop cached FAT sectors instead.
// attempt writeback here. Drop cached FAT sectors instead.
FatCacheInvalidate();
(void)IoUnregisterFilesystem(i);
memset(&gVols[i], 0, sizeof(gVols[i]));
+4
View File
@@ -28,9 +28,13 @@ KSTATUS Fat32Write(IO_FILESYSTEM *fs, void *fileCtx, uint64_t offset, const void
KSTATUS Fat32ReadAsync(IO_FILESYSTEM *fs, void *fileCtx, uint64_t offset, void *buf, size_t size, IO_FS_ASYNC_COMPLETE complete, void *completeContext);
KSTATUS Fat32WriteAsync(IO_FILESYSTEM *fs, void *fileCtx, uint64_t offset, const void *buf, size_t size, IO_FS_ASYNC_COMPLETE complete, void *completeContext);
KSTATUS Fat32Create(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx);
KSTATUS Fat32CreateDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW path);
KSTATUS Fat32RemoveDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW path);
KSTATUS Fat32Delete(IO_FILESYSTEM *fs, SYS_STRING_VIEW path);
KSTATUS Fat32Truncate(IO_FILESYSTEM *fs, void *fileCtx, uint64_t newSize, uint32_t fileFlags);
uint64_t Fat32GetSize(IO_FILESYSTEM *fs, void *fileCtx);
KSTATUS Fat32QueryInfo(IO_FILESYSTEM *fs, void *fileCtx, SYS_FILE_STAT *out);
KSTATUS Fat32Stat(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, SYS_FILE_STAT *out);
KSTATUS Fat32QueryIdentity(IO_FILESYSTEM *fs, void *fileCtx, IO_FILE_IDENTITY *out);
KSTATUS Fat32OpenDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW dirPath, void **dirCtx);
KSTATUS Fat32ReadDir(IO_FILESYSTEM *fs, void *dirCtx, KDIR_ENTRY *out);
+4
View File
@@ -12,8 +12,12 @@ void Fat32InitVtable(IO_FILESYSTEM* fs, FAT32_VOL* vol) {
fs->WriteAsync = Fat32WriteAsync;
fs->Create = Fat32Create;
fs->Delete = Fat32Delete;
fs->CreateDir = Fat32CreateDir;
fs->RemoveDir = Fat32RemoveDir;
fs->Truncate = Fat32Truncate;
fs->GetSize = Fat32GetSize;
fs->QueryInfo = Fat32QueryInfo;
fs->Stat = Fat32Stat;
fs->QueryIdentity = Fat32QueryIdentity;
fs->Close = Fat32Close;
fs->DriverCtx = vol;
+339
View File
@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
File diff suppressed because it is too large. Load diff
+43 -7
View File
@@ -2,8 +2,13 @@
#include "ps2.h"
static KDPC gKeyboardDpc;
static KDPC gMouseDpc;
static volatile bool gKeyboardNextExtended;
static INPUT_KEY_EVENT gKeyboardDpcQueue[KEYBOARD_DPC_QUEUE_SIZE];
static volatile int gKeyboardDpcHead;
static volatile int gKeyboardDpcTail;
static volatile uint8_t gKeyboardDown[256];
static volatile uint8_t gMousePacket[3];
static volatile int gMousePacketIndex;
static PS2_MOUSE_PACKET gMouseDpcQueue[MOUSE_DPC_QUEUE_SIZE];
@@ -121,6 +126,40 @@ static INPUT_KEY Ps2Set1ToKey(uint8_t make, bool extended) {
}
}
// Implements the ps2 Keyboard Dpc Routine operation.
static void Ps2KeyboardDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2) {
(void)dpc;
(void)context;
(void)systemArgument1;
(void)systemArgument2;
while (gKeyboardDpcHead != gKeyboardDpcTail) {
INPUT_KEY_EVENT event = gKeyboardDpcQueue[gKeyboardDpcHead];
gKeyboardDpcHead = (gKeyboardDpcHead + 1) & (int)KEYBOARD_DPC_QUEUE_MASK;
KeInputSubmitKey(&event);
}
}
// Implements the ps2 Queue Keyboard Event operation.
static void Ps2QueueKeyboardEvent(INPUT_KEY key, bool pressed) {
if (key >= sizeof(gKeyboardDown))
return;
bool wasDown = gKeyboardDown[key] != 0;
if (pressed == wasDown)
return;
int next = (gKeyboardDpcTail + 1) & (int)KEYBOARD_DPC_QUEUE_MASK;
if (next == gKeyboardDpcHead)
return;
gKeyboardDown[key] = pressed ? 1u : 0u;
gKeyboardDpcQueue[gKeyboardDpcTail].Key = key;
gKeyboardDpcQueue[gKeyboardDpcTail].Pressed = pressed;
gKeyboardDpcTail = next;
KeInsertQueueDpc(&gKeyboardDpc, 0, 0);
}
// Implements the ps2 Keyboard Isr operation.
static void Ps2KeyboardIsr(void) {
uint8_t sc = __inbyte(PS2_DATA);
@@ -137,11 +176,7 @@ static void Ps2KeyboardIsr(void) {
INPUT_KEY key = Ps2Set1ToKey(make, extended);
if (key == INPUT_KEY_NONE) return;
INPUT_KEY_EVENT event = {
.Key = key,
.Pressed = !released,
};
KeInputSubmitKey(&event);
Ps2QueueKeyboardEvent(key, !released);
}
// Implements the ps2 Mouse Dpc Routine operation.
@@ -224,7 +259,7 @@ static void Ps2ControllerEnableInterrupts(void) {
// Implements the ps2 Keyboard Initialize operation.
static void Ps2KeyboardInitialize(void) {
// Enable scanning. If the keyboard is absent, the timeout/ACK failure is
// Enable scanning. If the keyboard is absent, the timeout/ACK failure is
// non-fatal; the IRQ path simply never produces events.
if (!Ps2SendKeyboard(0xF4u))
DbgPrint("ps2: keyboard enable did not ACK\n");
@@ -254,10 +289,11 @@ KSTATUS DriverEntry(const HAL_API* hal) {
KeInputRegisterDevice("ps2-keyboard", 0, &keyboardId);
KeInputRegisterDevice("ps2-mouse", 0, &mouseId);
KeInitializeDpc(&gKeyboardDpc, Ps2KeyboardDpcRoutine, 0);
KeInitializeDpc(&gMouseDpc, Ps2MouseDpcRoutine, 0);
// Keep 8042 IRQ generation disabled while doing polled controller/device
// setup. The old ordering routed IRQ1/IRQ12 first, which let ACK/BAT bytes
// setup. The old ordering routed IRQ1/IRQ12 first, which let ACK/BAT bytes
// race the ISR and get consumed as fake key/mouse input before the polled
// init path could read them.
Ps2ControllerPrepare();
+2
View File
@@ -27,6 +27,8 @@
#define SC1_MAKE_MASK 0x7Fu
#define SC1_EXT_PREFIX 0xE0u
#define KEYBOARD_DPC_QUEUE_SIZE 64u
#define KEYBOARD_DPC_QUEUE_MASK (KEYBOARD_DPC_QUEUE_SIZE - 1u)
#define MOUSE_DPC_QUEUE_SIZE 64u
#define MOUSE_DPC_QUEUE_MASK (MOUSE_DPC_QUEUE_SIZE - 1u)
+43 -1
View File
@@ -9,6 +9,7 @@
#include <ntlite/ddi/hal_api.h>
#include <ntlite/ddi/input_device.h>
#include <kernel/ke_debug.h>
#include <kernel/ke_dpc.h>
#include <kernel/ke_irq.h>
#define SERIAL_COM1_BASE 0x3F8u
@@ -35,6 +36,9 @@
#define SERIAL_FCR_CLEAR_TX 0x04u
#define SERIAL_FCR_TRIGGER_14 0xC0u
#define SERIAL_RX_QUEUE_SIZE 1024u
#define SERIAL_RX_QUEUE_MASK (SERIAL_RX_QUEUE_SIZE - 1u)
typedef enum SERIAL_ESCAPE_STATE {
SerialEscapeNone = 0,
SerialEscapeEsc,
@@ -42,9 +46,14 @@ typedef enum SERIAL_ESCAPE_STATE {
SerialEscapeCsiNumber,
} SERIAL_ESCAPE_STATE;
static KDPC gSerialRxDpc;
static volatile SERIAL_ESCAPE_STATE gSerialEscapeState;
static volatile uint8_t gSerialCsiNumber;
static volatile bool gSerialDropNextLf;
static uint8_t gSerialRxQueue[SERIAL_RX_QUEUE_SIZE];
static volatile uint32_t gSerialRxHead;
static volatile uint32_t gSerialRxTail;
static volatile uint32_t gSerialRxDropped;
// Reads one byte from a COM register.
static uint8_t SerialRead(uint16_t reg) {
@@ -254,12 +263,41 @@ static void SerialReceiveByte(uint8_t ch) {
SerialSubmitPrintable(ch);
}
// Handles queued receive bytes at DISPATCH_LEVEL.
static void SerialRxDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2) {
(void)dpc;
(void)context;
(void)systemArgument1;
(void)systemArgument2;
while (gSerialRxHead != gSerialRxTail) {
uint32_t head = gSerialRxHead;
uint8_t ch = gSerialRxQueue[head];
gSerialRxHead = (head + 1u) & SERIAL_RX_QUEUE_MASK;
SerialReceiveByte(ch);
}
}
// Queues a received byte for bottom-half processing.
static void SerialQueueRxByte(uint8_t ch) {
uint32_t tail = gSerialRxTail;
uint32_t next = (tail + 1u) & SERIAL_RX_QUEUE_MASK;
if (next == gSerialRxHead) {
gSerialRxDropped++;
return;
}
gSerialRxQueue[tail] = ch;
gSerialRxTail = next;
KeInsertQueueDpc(&gSerialRxDpc, 0, 0);
}
// Handles receive interrupts for COM1.
static void SerialIsr(void) {
for (uint32_t i = 0; i < 256u; i++) {
if ((SerialRead(SERIAL_REG_LSR) & SERIAL_LSR_DATA_READY) == 0)
break;
SerialReceiveByte(SerialRead(SERIAL_REG_DATA));
SerialQueueRxByte(SerialRead(SERIAL_REG_DATA));
}
}
@@ -289,9 +327,13 @@ KSTATUS DriverEntry(const HAL_API* hal) {
if (!KSUCCESS(st))
return st;
KeInitializeDpc(&gSerialRxDpc, SerialRxDpcRoutine, 0);
gSerialEscapeState = SerialEscapeNone;
gSerialCsiNumber = 0;
gSerialDropNextLf = false;
gSerialRxHead = 0;
gSerialRxTail = 0;
gSerialRxDropped = 0;
SerialInitializeCom1();
KeIrqRegister(SERIAL_COM1_IRQ, SerialIsr);
@@ -32,12 +32,12 @@ typedef struct sys_mbox* sys_mbox_t;
#define sys_mbox_set_invalid_val(mbox) do { (mbox) = NULL; } while (0)
/*
* lwIP only treats sys_thread_t as an opaque handle. Keep it pointer-sized.
* lwIP only treats sys_thread_t as an opaque handle. Keep it pointer-sized.
*
* Do NOT make this a by-value incomplete struct. MSVC may compile callers
* Do NOT make this a by-value incomplete struct. MSVC may compile callers
* that see only the incomplete type with a hidden struct-return parameter,
* which shifts sys_thread_new(name, thread, arg, ...) so the callee receives
* thread == name and arg == thread. The first tcpip_thread then jumps into
* thread == name and arg == thread. The first tcpip_thread then jumps into
* the literal string "tcpip" as code.
*/
typedef void* sys_thread_t;
+11 -4
View File
@@ -2,9 +2,9 @@
/*
* lwipopts.h - kernel port configuration for lwIP 2.2.1.
*
* This is intentionally a practical first configuration: IPv4, ARP, ICMP,
* UDP, TCP, DHCP, DNS and netconn are enabled; IPv6, PPP,
* SNMP, lwIP's POSIX socket API, and app protocols stay off.
* This is intentionally a practical first configuration: IPv4, IPv6, ARP,
* ICMP, ICMPv6, UDP, TCP, DHCP, DNS and netconn are enabled; PPP, SNMP,
* lwIP's POSIX socket API, and app protocols stay off.
*/
#define NO_SYS 0
@@ -28,10 +28,11 @@
#define MEMP_NUM_SYS_TIMEOUT 32
#define LWIP_IPV4 1
#define LWIP_IPV6 0
#define LWIP_IPV6 1
#define LWIP_ARP 1
#define LWIP_ETHERNET 1
#define LWIP_ICMP 1
#define LWIP_ICMP6 1
#define LWIP_IGMP 0
#define LWIP_RAW 1
#define LWIP_UDP 1
@@ -39,6 +40,10 @@
#define LWIP_DHCP 1
#define LWIP_AUTOIP 0
#define LWIP_DNS 1
#define LWIP_ND6_QUEUEING 1
#define LWIP_IPV6_AUTOCONFIG 1
#define LWIP_IPV6_MLD 1
#define MEMP_NUM_ND6_QUEUE 8
#define TCP_MSS 1460
#define TCP_WND (4 * TCP_MSS)
@@ -81,6 +86,8 @@
#define CHECKSUM_CHECK_IP 1
#define CHECKSUM_CHECK_UDP 1
#define CHECKSUM_CHECK_TCP 1
#define CHECKSUM_GEN_ICMP6 1
#define CHECKSUM_CHECK_ICMP6 1
#define LWIP_TIMERS 1
#define LWIP_TCPIP_TIMEOUT 1
+2 -2
View File
@@ -5,9 +5,9 @@
/*
* Minimal freestanding stdlib shim for lwIP.
*
* lwIP 2.2.1 includes <stdlib.h> in a couple of files only for atoi(). If MSVC
* lwIP 2.2.1 includes <stdlib.h> in a couple of files only for atoi(). If MSVC
* finds the UCRT header, it drags errno.h into the kernel build and then lwIP's
* own errno values collide with UCRT's. Put this header first in lwIP's include
* own errno values collide with UCRT's. Put this header first in lwIP's include
* path so those sources stay freestanding.
*/
static __inline int atoi(const char* s) {
+12
View File
@@ -39,6 +39,18 @@ third_party/lwip/src/core/ipv4/ip4_frag.c
third_party/lwip/src/core/ipv4/ip4.c
third_party/lwip/src/core/ipv4/ip4_addr.c
# IPv6
third_party/lwip/src/core/ipv6/dhcp6.c
third_party/lwip/src/core/ipv6/ethip6.c
third_party/lwip/src/core/ipv6/icmp6.c
third_party/lwip/src/core/ipv6/inet6.c
third_party/lwip/src/core/ipv6/ip6_frag.c
third_party/lwip/src/core/ipv6/ip6.c
third_party/lwip/src/core/ipv6/ip6_addr.c
third_party/lwip/src/core/ipv6/mld6.c
third_party/lwip/src/core/ipv6/nd6.c
# netconn/socket API
third_party/lwip/src/api/api_lib.c
third_party/lwip/src/api/api_msg.c
+214 -60
View File
@@ -22,13 +22,16 @@
#include <lwip/netif.h>
#include <lwip/pbuf.h>
#include <lwip/etharp.h>
#include <lwip/ethip6.h>
#include <lwip/dhcp.h>
#include <lwip/dns.h>
#include <lwip/ip4_addr.h>
#include <lwip/ip6_addr.h>
#include <lwip/ip_addr.h>
#include <lwip/api.h>
#include <lwip/err.h>
#include <lwip/prot/ip.h>
#include <lwip/prot/ip6.h>
#include <netif/ethernet.h>
#define LWIP_MAX_NETIFS 8u
@@ -42,12 +45,18 @@
typedef struct LWIP_NETIF_CONFIG {
uint8_t UseDhcp;
uint8_t HasDns;
uint8_t HasDns4;
uint8_t EnableIpv6;
uint8_t Ipv6Autoconfig;
uint8_t HasStaticIpv6;
uint8_t HasDns6;
char Section[64];
ip4_addr_t Address;
ip4_addr_t Netmask;
ip4_addr_t Gateway;
ip4_addr_t Dns;
ip4_addr_t Dns4;
ip6_addr_t Address6;
ip6_addr_t Dns6;
} LWIP_NETIF_CONFIG;
typedef struct LWIP_BINDING {
@@ -94,19 +103,19 @@ static KSTATUS LwipProviderAttachDevice(NET_DEVICE* device);
static KSTATUS LwipProviderReceiveFrame(NET_DEVICE* device, const void* frame, size_t length);
static KSTATUS LwipProviderSocketCreate(uint32_t domain, uint32_t type, uint32_t protocol, void** outSocket);
static void LwipProviderSocketClose(void* socket);
static KSTATUS LwipProviderBind(void* socket, const SYS_SOCKET_ADDRESS_IPV4_K* address, size_t addressSize);
static KSTATUS LwipProviderBind(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize);
static KSTATUS LwipProviderListen(void* socket, uint32_t backlog);
static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_IPV4_K* remoteOut);
static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_IPV4_K* address, size_t addressSize);
static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut);
static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize);
static KSTATUS LwipProviderSend(void* socket, const void* buffer, size_t length, uint32_t flags, size_t* sentOut);
static KSTATUS LwipProviderReceive(void* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut);
static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t length, uint32_t flags, const SYS_SOCKET_ADDRESS_IPV4_K* remote, size_t remoteSize, size_t* sentOut);
static KSTATUS LwipProviderReceiveFrom(void* socket, void* buffer, size_t length, uint32_t flags, SYS_SOCKET_ADDRESS_IPV4_K* remoteOut, size_t* receivedOut);
static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t length, uint32_t flags, const SYS_SOCKET_ADDRESS_K* remote, size_t remoteSize, size_t* sentOut);
static KSTATUS LwipProviderReceiveFrom(void* socket, void* buffer, size_t length, uint32_t flags, SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut);
static KSTATUS LwipProviderShutdown(void* socket, uint32_t how);
static KSTATUS LwipProviderSetOption(void* socket, uint32_t option, const void* value, size_t valueSize);
static KSTATUS LwipProviderGetOption(void* socket, uint32_t option, void* value, size_t valueSize, size_t* actualSizeOut);
static KSTATUS LwipProviderPoll(void* socket, uint32_t events, uint32_t* returnedEventsOut);
static KSTATUS LwipProviderResolveHostIpv4(const char* name, SYS_SOCKET_ADDRESS_IPV4_K* addressOut);
static KSTATUS LwipProviderResolveHost(const char* name, uint32_t family, SYS_SOCKET_ADDRESS_K* addressOut);
static const NET_STACK_PROVIDER gLwipProvider = {
NET_STACK_PROVIDER_VERSION,
@@ -127,7 +136,7 @@ static const NET_STACK_PROVIDER gLwipProvider = {
LwipProviderSetOption,
LwipProviderGetOption,
LwipProviderPoll,
LwipProviderResolveHostIpv4,
LwipProviderResolveHost,
};
// Implements the err To Status operation.
@@ -137,7 +146,7 @@ static KSTATUS LwipErrToStatus(err_t err) {
case ERR_MEM: return STATUS_NO_MEMORY;
case ERR_BUF: return STATUS_BUFFER_TOO_SMALL;
case ERR_TIMEOUT: return STATUS_TIMEOUT;
case ERR_WOULDBLOCK:return STATUS_TIMEOUT;
case ERR_WOULDBLOCK:return STATUS_WOULD_BLOCK;
case ERR_RTE: return STATUS_NOT_FOUND;
case ERR_VAL: return STATUS_INVALID_PARAMETER;
case ERR_ARG: return STATUS_INVALID_PARAMETER;
@@ -209,6 +218,57 @@ static KSTATUS LwipGetSettingIpv4(const char* section, const char* key,
return STATUS_SUCCESS;
}
static KSTATUS LwipGetSettingIpv6(const char* section, const char* key,
ip6_addr_t* out, int required) {
char value[LWIP_SETTINGS_VALUE_MAX];
KSTATUS st = KeGetSettingString(section, key, value, sizeof(value));
if (st == STATUS_NOT_FOUND && !required)
return STATUS_NOT_FOUND;
if (!KSUCCESS(st))
return st;
if (!ip6addr_aton(value, out)) {
DbgPrint("lwIP: invalid IPv6 setting [%s] %s=%s\n", section, key, value);
return STATUS_INVALID_PARAMETER;
}
return STATUS_SUCCESS;
}
static KSTATUS LwipLoadIpv6Config(const char* section, LWIP_NETIF_CONFIG* cfg) {
char mode[LWIP_SETTINGS_VALUE_MAX];
KSTATUS st = KeGetSettingString(section, "ipv6", mode, sizeof(mode));
if (st == STATUS_NOT_FOUND) {
cfg->EnableIpv6 = 1;
cfg->Ipv6Autoconfig = 1;
} else if (!KSUCCESS(st)) {
return st;
} else if (strcmp(mode, "off") == 0 || strcmp(mode, "disabled") == 0) {
cfg->EnableIpv6 = 0;
} else if (strcmp(mode, "auto") == 0) {
cfg->EnableIpv6 = 1;
cfg->Ipv6Autoconfig = 1;
} else if (strcmp(mode, "static") == 0) {
cfg->EnableIpv6 = 1;
cfg->HasStaticIpv6 = 1;
st = LwipGetSettingIpv6(section, "address6", &cfg->Address6, 1);
if (!KSUCCESS(st))
return st;
} else {
DbgPrint("lwIP: invalid IPv6 mode [%s] ipv6=%s\n", section, mode);
return STATUS_INVALID_PARAMETER;
}
if (!cfg->EnableIpv6)
return STATUS_SUCCESS;
st = LwipGetSettingIpv6(section, "dns6", &cfg->Dns6, 0);
if (KSUCCESS(st))
cfg->HasDns6 = 1;
else if (st != STATUS_NOT_FOUND)
return st;
return STATUS_SUCCESS;
}
// Copies section Name.
static KSTATUS LwipCopySectionName(char* out, size_t outSize, const char* prefix, const char* name) {
if (!out || !outSize || !prefix || !name)
@@ -248,20 +308,20 @@ static KSTATUS LwipLoadConfigFromSection(const char* section, LWIP_NETIF_CONFIG*
if (!KSUCCESS(st)) return st;
st = LwipGetSettingIpv4(section, "gateway", &cfg->Gateway, 1);
if (!KSUCCESS(st)) return st;
st = LwipGetSettingIpv4(section, "dns", &cfg->Dns, 1);
st = LwipGetSettingIpv4(section, "dns", &cfg->Dns4, 1);
if (!KSUCCESS(st)) return st;
cfg->HasDns = 1;
return STATUS_SUCCESS;
cfg->HasDns4 = 1;
return LwipLoadIpv6Config(section, cfg);
}
if (strcmp(mode, "dhcp") == 0) {
cfg->UseDhcp = 1;
st = LwipGetSettingIpv4(section, "dns", &cfg->Dns, 0);
st = LwipGetSettingIpv4(section, "dns", &cfg->Dns4, 0);
if (KSUCCESS(st))
cfg->HasDns = 1;
cfg->HasDns4 = 1;
else if (st != STATUS_NOT_FOUND)
return st;
return STATUS_SUCCESS;
return LwipLoadIpv6Config(section, cfg);
}
DbgPrint("lwIP: invalid network mode [%s] mode=%s\n", section, mode);
@@ -372,18 +432,71 @@ static KSTATUS LwipSocketAllocateWithConn(uint32_t domain, uint32_t type, uint32
return STATUS_SUCCESS;
}
static int LwipSocketAddressValid(const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) {
if (!address || addressSize < sizeof(SYS_SOCKET_ADDRESS_K))
return 0;
if (address->Size < sizeof(SYS_SOCKET_ADDRESS_K))
return 0;
return address->Family == SYS_AF_INET || address->Family == SYS_AF_INET6;
}
static int LwipSocketFamilyMatches(const LWIP_PROVIDER_SOCKET* s, const SYS_SOCKET_ADDRESS_K* address) {
if (!s || !address)
return 0;
if (s->Domain == SYS_AF_INET)
return address->Family == SYS_AF_INET;
if (s->Domain == SYS_AF_INET6)
return address->Family == SYS_AF_INET6;
return 0;
}
// Implements the socket Make Ip operation.
static void LwipSocketMakeIp(const SYS_SOCKET_ADDRESS_IPV4_K* address, ip_addr_t* out) {
IP_ADDR4(out, address->Address[0], address->Address[1], address->Address[2], address->Address[3]);
static KSTATUS LwipSocketMakeIp(const SYS_SOCKET_ADDRESS_K* address, ip_addr_t* out) {
if (!address || !out)
return STATUS_INVALID_PARAMETER;
if (address->Family == SYS_AF_INET) {
IP_ADDR4(out, address->Address[0], address->Address[1], address->Address[2], address->Address[3]);
return STATUS_SUCCESS;
}
if (address->Family == SYS_AF_INET6) {
uint32_t a0 = ((uint32_t)address->Address[0] << 24) | ((uint32_t)address->Address[1] << 16) |
((uint32_t)address->Address[2] << 8) | (uint32_t)address->Address[3];
uint32_t a1 = ((uint32_t)address->Address[4] << 24) | ((uint32_t)address->Address[5] << 16) |
((uint32_t)address->Address[6] << 8) | (uint32_t)address->Address[7];
uint32_t a2 = ((uint32_t)address->Address[8] << 24) | ((uint32_t)address->Address[9] << 16) |
((uint32_t)address->Address[10] << 8) | (uint32_t)address->Address[11];
uint32_t a3 = ((uint32_t)address->Address[12] << 24) | ((uint32_t)address->Address[13] << 16) |
((uint32_t)address->Address[14] << 8) | (uint32_t)address->Address[15];
IP_ADDR6_HOST(out, a0, a1, a2, a3);
ip6_addr_set_zone(ip_2_ip6(out), (u8_t)address->ScopeId);
return STATUS_SUCCESS;
}
return STATUS_NOT_SUPPORTED;
}
// Implements the socket Fill Address operation.
static void LwipSocketFillAddress(SYS_SOCKET_ADDRESS_IPV4_K* out, const ip_addr_t* ip, u16_t port) {
static void LwipSocketFillAddress(SYS_SOCKET_ADDRESS_K* out, const ip_addr_t* ip, u16_t port) {
if (!out)
return;
memset(out, 0, sizeof(*out));
out->Family = SYS_AF_INET;
out->Size = sizeof(*out);
out->Port = port;
if (!ip)
return;
if (IP_IS_V6(ip)) {
out->Family = SYS_AF_INET6;
out->ScopeId = ip6_addr_zone(ip_2_ip6(ip));
for (size_t i = 0; i < 4; ++i) {
uint32_t word = lwip_htonl(ip_2_ip6(ip)->addr[i]);
out->Address[i * 4 + 0] = (uint8_t)(word >> 24);
out->Address[i * 4 + 1] = (uint8_t)(word >> 16);
out->Address[i * 4 + 2] = (uint8_t)(word >> 8);
out->Address[i * 4 + 3] = (uint8_t)word;
}
return;
}
out->Family = SYS_AF_INET;
out->Address[0] = (uint8_t)ip4_addr1(ip_2_ip4(ip));
out->Address[1] = (uint8_t)ip4_addr2(ip_2_ip4(ip));
out->Address[2] = (uint8_t)ip4_addr3(ip_2_ip4(ip));
@@ -428,11 +541,17 @@ static err_t LwipNetifInit(struct netif* netif) {
netif->name[0] = 'e';
netif->name[1] = 'n';
netif->output = etharp_output;
#if LWIP_IPV6
netif->output_ip6 = ethip6_output;
#endif
netif->linkoutput = LwipLowLevelOutput;
netif->mtu = (u16_t)(device->Mtu ? device->Mtu : NET_MTU_ETHERNET);
netif->hwaddr_len = NET_ETHERNET_ADDRESS_LENGTH;
memcpy(netif->hwaddr, device->HardwareAddress, NET_ETHERNET_ADDRESS_LENGTH);
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
#if LWIP_IPV6_MLD
netif->flags |= NETIF_FLAG_MLD6;
#endif
return ERR_OK;
}
@@ -476,12 +595,29 @@ static void LwipAttachCallback(void* arg) {
if (netif_default == NULL)
netif_set_default(n);
if (b->Config.HasDns) {
if (b->Config.HasDns4) {
ip_addr_t dns;
ip_addr_copy_from_ip4(dns, b->Config.Dns);
ip_addr_copy_from_ip4(dns, b->Config.Dns4);
dns_setserver(0, &dns);
}
#if LWIP_IPV6
if (b->Config.EnableIpv6) {
netif_create_ip6_linklocal_address(n, 1);
if (b->Config.Ipv6Autoconfig)
n->ip6_autoconfig_enabled = 1;
if (b->Config.HasStaticIpv6) {
netif_ip6_addr_set(n, 1, &b->Config.Address6);
netif_ip6_addr_set_state(n, 1, IP6_ADDR_PREFERRED);
}
if (b->Config.HasDns6) {
ip_addr_t dns6;
ip_addr_copy_from_ip6(dns6, b->Config.Dns6);
dns_setserver(1, &dns6);
}
}
#endif
if (b->Config.UseDhcp) {
#if LWIP_DHCP
err_t dhcpErr = dhcp_start(n);
@@ -586,11 +722,12 @@ static KSTATUS LwipProviderAttachDevice(NET_DEVICE* device) {
}
gBindings[device->Index] = b;
DbgPrint("lwIP: attached %s as netif %c%c%u using [%s] mode=%s\n",
DbgPrint("lwIP: attached %s as netif %c%c%u using [%s] mode=%s ipv6=%s\n",
device->Name,
b->Netif.name[0], b->Netif.name[1], b->Netif.num,
b->Config.Section,
b->Config.UseDhcp ? "dhcp" : "static");
b->Config.UseDhcp ? "dhcp" : "static",
b->Config.EnableIpv6 ? (b->Config.HasStaticIpv6 ? "static" : "auto") : "off");
return STATUS_SUCCESS;
}
@@ -636,26 +773,27 @@ static KSTATUS LwipProviderSocketCreate(uint32_t domain, uint32_t type, uint32_t
if (!gLwipInitialized)
return STATUS_NOT_SUPPORTED;
if (domain != SYS_AF_INET)
if (domain != SYS_AF_INET && domain != SYS_AF_INET6)
return STATUS_NOT_SUPPORTED;
int ipv6 = domain == SYS_AF_INET6;
enum netconn_type netconnType;
u8_t rawProtocol = 0;
if (type == SYS_SOCK_STREAM) {
if (protocol != 0 && protocol != SYS_IPPROTO_TCP)
return STATUS_NOT_SUPPORTED;
netconnType = NETCONN_TCP;
netconnType = ipv6 ? NETCONN_TCP_IPV6 : NETCONN_TCP;
protocol = SYS_IPPROTO_TCP;
} else if (type == SYS_SOCK_DGRAM) {
if (protocol != 0 && protocol != SYS_IPPROTO_UDP)
return STATUS_NOT_SUPPORTED;
netconnType = NETCONN_UDP;
netconnType = ipv6 ? NETCONN_UDP_IPV6 : NETCONN_UDP;
protocol = SYS_IPPROTO_UDP;
} else if (type == SYS_SOCK_RAW) {
if (protocol != SYS_IPPROTO_ICMP)
if ((!ipv6 && protocol != SYS_IPPROTO_ICMP) || (ipv6 && protocol != SYS_IPPROTO_ICMPV6))
return STATUS_NOT_SUPPORTED;
netconnType = NETCONN_RAW;
rawProtocol = IP_PROTO_ICMP;
netconnType = ipv6 ? NETCONN_RAW_IPV6 : NETCONN_RAW;
rawProtocol = ipv6 ? IP6_NEXTH_ICMP6 : IP_PROTO_ICMP;
} else {
return STATUS_NOT_SUPPORTED;
}
@@ -703,15 +841,15 @@ static void LwipProviderSocketClose(void* socket) {
}
// Implements the provider Bind operation.
static KSTATUS LwipProviderBind(void* socket, const SYS_SOCKET_ADDRESS_IPV4_K* address, size_t addressSize) {
static KSTATUS LwipProviderBind(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) {
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
if (!s || !address)
return STATUS_INVALID_PARAMETER;
if (addressSize < sizeof(SYS_SOCKET_ADDRESS_IPV4_K))
if (!LwipSocketAddressValid(address, addressSize))
return STATUS_BUFFER_TOO_SMALL;
if ((s->Type != SYS_SOCK_STREAM && s->Type != SYS_SOCK_DGRAM) || !s->Conn)
return STATUS_INVALID_PARAMETER;
if (address->Family != SYS_AF_INET)
if (!LwipSocketFamilyMatches(s, address))
return STATUS_NOT_SUPPORTED;
KSTATUS st = LwipSocketLock(s);
@@ -724,7 +862,11 @@ static KSTATUS LwipProviderBind(void* socket, const SYS_SOCKET_ADDRESS_IPV4_K* a
}
ip_addr_t ip;
LwipSocketMakeIp(address, &ip);
st = LwipSocketMakeIp(address, &ip);
if (!KSUCCESS(st)) {
LwipSocketUnlock(s);
return st;
}
err_t err = netconn_bind(s->Conn, &ip, address->Port);
if (err == ERR_OK)
s->Bound = 1;
@@ -763,7 +905,7 @@ static KSTATUS LwipProviderListen(void* socket, uint32_t backlog) {
}
// Implements the provider Accept operation.
static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_IPV4_K* remoteOut) {
static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut) {
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
if (!s || !acceptedOut)
return STATUS_INVALID_PARAMETER;
@@ -807,11 +949,12 @@ static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_A
ip_addr_t peerIp;
u16_t peerPort = 0;
err = netconn_peer(acceptedConn, &peerIp, &peerPort);
if (err == ERR_OK && IP_IS_V4(&peerIp)) {
if (err == ERR_OK) {
LwipSocketFillAddress(remoteOut, &peerIp, peerPort);
} else {
memset(remoteOut, 0, sizeof(*remoteOut));
remoteOut->Family = SYS_AF_INET;
remoteOut->Size = sizeof(*remoteOut);
remoteOut->Family = s->Domain;
}
}
@@ -820,15 +963,15 @@ static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_A
}
// Implements the provider Connect operation.
static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_IPV4_K* address, size_t addressSize) {
static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) {
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
if (!s || !address)
return STATUS_INVALID_PARAMETER;
if (addressSize < sizeof(SYS_SOCKET_ADDRESS_IPV4_K))
if (!LwipSocketAddressValid(address, addressSize))
return STATUS_BUFFER_TOO_SMALL;
if ((s->Type != SYS_SOCK_STREAM && s->Type != SYS_SOCK_DGRAM) || !s->Conn)
return STATUS_INVALID_PARAMETER;
if (address->Family != SYS_AF_INET)
if (!LwipSocketFamilyMatches(s, address))
return STATUS_NOT_SUPPORTED;
KSTATUS st = LwipSocketLock(s);
@@ -845,7 +988,11 @@ static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_IPV4_K
}
ip_addr_t ip;
LwipSocketMakeIp(address, &ip);
st = LwipSocketMakeIp(address, &ip);
if (!KSUCCESS(st)) {
LwipSocketUnlock(s);
return st;
}
err_t err = netconn_connect(s->Conn, &ip, address->Port);
if (err == ERR_OK)
@@ -1007,13 +1154,13 @@ static KSTATUS LwipProviderReceive(void* socket, void* buffer, size_t length, ui
}
static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t length, uint32_t flags,
const SYS_SOCKET_ADDRESS_IPV4_K* remote, size_t remoteSize, size_t* sentOut) {
const SYS_SOCKET_ADDRESS_K* remote, size_t remoteSize, size_t* sentOut) {
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
if (sentOut)
*sentOut = 0;
if (!s || (!buffer && length) || !remote)
return STATUS_INVALID_PARAMETER;
if (remoteSize < sizeof(SYS_SOCKET_ADDRESS_IPV4_K))
if (!LwipSocketAddressValid(remote, remoteSize))
return STATUS_BUFFER_TOO_SMALL;
if (flags != 0)
return STATUS_NOT_SUPPORTED;
@@ -1021,7 +1168,7 @@ static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t lengt
return STATUS_INVALID_PARAMETER;
if (s->Type == SYS_SOCK_RAW && length > LWIP_PROVIDER_SOCKET_RAW_MAX_BYTES)
return STATUS_INVALID_PARAMETER;
if (remote->Family != SYS_AF_INET)
if (!LwipSocketFamilyMatches(s, remote))
return STATUS_NOT_SUPPORTED;
if (length == 0)
return STATUS_SUCCESS;
@@ -1046,8 +1193,11 @@ static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t lengt
err_t err = netbuf_ref(nb, buffer, (u16_t)length);
if (err == ERR_OK) {
ip_addr_t ip;
LwipSocketMakeIp(remote, &ip);
err = netconn_sendto(s->Conn, nb, &ip, remote->Port);
KSTATUS makeStatus = LwipSocketMakeIp(remote, &ip);
if (KSUCCESS(makeStatus))
err = netconn_sendto(s->Conn, nb, &ip, remote->Port);
else
err = ERR_ARG;
}
netbuf_delete(nb);
@@ -1059,7 +1209,7 @@ static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t lengt
}
static KSTATUS LwipProviderReceiveFrom(void* socket, void* buffer, size_t length, uint32_t flags,
SYS_SOCKET_ADDRESS_IPV4_K* remoteOut, size_t* receivedOut) {
SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut) {
LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket;
if (receivedOut)
*receivedOut = 0;
@@ -1114,11 +1264,12 @@ static KSTATUS LwipProviderReceiveFrom(void* socket, void* buffer, size_t length
if (remoteOut) {
const ip_addr_t* from = netbuf_fromaddr(nb);
if (from && IP_IS_V4(from)) {
if (from) {
LwipSocketFillAddress(remoteOut, from, netbuf_fromport(nb));
} else {
memset(remoteOut, 0, sizeof(*remoteOut));
remoteOut->Family = SYS_AF_INET;
remoteOut->Size = sizeof(*remoteOut);
remoteOut->Family = s->Domain;
}
}
@@ -1271,27 +1422,30 @@ static KSTATUS LwipProviderShutdown(void* socket, uint32_t how) {
return LwipErrToStatus(err);
}
// Implements the provider Resolve Host Ipv4 operation.
static KSTATUS LwipProviderResolveHostIpv4(const char* name, SYS_SOCKET_ADDRESS_IPV4_K* addressOut) {
// Implements the provider Resolve Host operation.
static KSTATUS LwipProviderResolveHost(const char* name, uint32_t family, SYS_SOCKET_ADDRESS_K* addressOut) {
if (!name || !addressOut)
return STATUS_INVALID_PARAMETER;
if (!gLwipInitialized)
return STATUS_NOT_SUPPORTED;
u8_t dnsType;
if (family == SYS_AF_INET)
dnsType = NETCONN_DNS_IPV4;
else if (family == SYS_AF_INET6)
dnsType = NETCONN_DNS_IPV6;
else
return STATUS_NOT_SUPPORTED;
ip_addr_t resolved;
err_t err = netconn_gethostbyname_addrtype(name, &resolved, NETCONN_DNS_IPV4);
err_t err = netconn_gethostbyname_addrtype(name, &resolved, dnsType);
if (err != ERR_OK)
return LwipErrToStatus(err);
if (!IP_IS_V4(&resolved))
if ((family == SYS_AF_INET && !IP_IS_V4(&resolved)) ||
(family == SYS_AF_INET6 && !IP_IS_V6(&resolved)))
return STATUS_NOT_FOUND;
memset(addressOut, 0, sizeof(*addressOut));
addressOut->Family = SYS_AF_INET;
addressOut->Port = 0;
addressOut->Address[0] = (uint8_t)ip4_addr1(ip_2_ip4(&resolved));
addressOut->Address[1] = (uint8_t)ip4_addr2(ip_2_ip4(&resolved));
addressOut->Address[2] = (uint8_t)ip4_addr3(ip_2_ip4(&resolved));
addressOut->Address[3] = (uint8_t)ip4_addr4(ip_2_ip4(&resolved));
LwipSocketFillAddress(addressOut, &resolved, 0);
return STATUS_SUCCESS;
}
+5 -12
View File
@@ -5,8 +5,7 @@
#include <stdbool.h>
#include <string.h>
#include <intrin.h>
#pragma intrinsic(_disable)
#pragma intrinsic(_enable)
#include <ntlite/ddi/hal_api.h>
#include <lwip/opt.h>
#include <lwip/sys.h>
#include <lwip/err.h>
@@ -41,7 +40,6 @@ struct sys_mbox {
uint32_t Valid;
};
static uint64_t (*gLwipQueryTicksUs)(void) = 0;
static uint32_t gRandState = 0x4b4e4554u; // 'KNET'
@@ -70,7 +68,7 @@ u32_t sys_jiffies(void) {
return sys_now();
}
// lwIP's core/sys.c provides sys_msleep() when it is not a macro. Do not
// lwIP's core/sys.c provides sys_msleep() when it is not a macro. Do not
// define it here as well, or MSVC links both sys.obj and lwip_sys_arch.obj
// with a duplicate sys_msleep symbol.
@@ -360,15 +358,10 @@ sys_thread_t sys_thread_new(const char* name, lwip_thread_fn thread, void* arg,
}
sys_prot_t sys_arch_protect(void) {
uintptr_t flags = __readeflags();
_disable();
return (sys_prot_t)flags;
return (sys_prot_t)HalDisableInterrupts();
}
// Implements the sys arch unprotect operation.
// Restores the interrupt state saved by sys_arch_protect.
void sys_arch_unprotect(sys_prot_t pval) {
if (((uintptr_t)pval) & 0x200u)
_enable();
else
_disable();
HalRestoreInterrupts((uintptr_t)pval);
}
+1 -5
View File
@@ -63,10 +63,6 @@ static void Rtl8139Write32(RTL8139_DEVICE* r, uint32_t reg, uint32_t value) {
else __outdword((unsigned short)(r->IoBase + reg), value);
}
// Implements the reset operation.
static KSTATUS Rtl8139Reset(RTL8139_DEVICE* r) {
Rtl8139Write8(r, RTL8139_REG_CMD, RTL8139_CMD_RST);
@@ -93,7 +89,7 @@ static KSTATUS Rtl8139AllocateDma(RTL8139_DEVICE* r) {
if (!r->RxPhys || !r->TxPhys)
return STATUS_NO_MEMORY;
// RTL8139 has 32-bit DMA address registers. This is fine for typical QEMU
// RTL8139 has 32-bit DMA address registers. This is fine for typical QEMU
// and low-memory hobby OS configs, but make failure explicit.
if ((r->RxPhys + RTL8139_RX_ALLOC_SIZE - 1) > 0xFFFFFFFFULL ||
(r->TxPhys + RTL8139_TX_SLOTS * RTL8139_TX_BUF_SIZE - 1) > 0xFFFFFFFFULL) {
+13 -19
View File
@@ -49,9 +49,7 @@ static inline uint64_t cmd_tbl_phys(AHCI_DRIVE *drv, int slot) {
+ (uint64_t)slot * PORT_CMDTBL_SIZE;
}
// Port engine start / stop (polling safe).
// Port engine start / stop (polling safe).
// Port memory initialisation.
@@ -148,7 +146,7 @@ static KSTATUS port_issue_cmd_poll(AHCI_DRIVE *drv) {
return STATUS_SUCCESS;
}
// Interrupt bottom half. The ISR owns hardware acknowledgement and status
// Interrupt bottom half. The ISR owns hardware acknowledgement and status
// capture; this DPC wakes the worker thread after the hard IRQ has unwound.
static void AhciCompletionDpc(KDPC *dpc, void *context,
@@ -182,9 +180,9 @@ static void AhciIsr(void) {
uint32_t portBit = (1u << drv->portNo);
if (!(pending & portBit)) continue;
// Capture and clear port IS before queueing the bottom half. The
// 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
// 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
@@ -383,7 +381,6 @@ static KSTATUS do_write(AHCI_DRIVE *drv, uint64_t lba,
// Async IRP dispatch.
static KSTATUS AhciGetIrpReadWrite(IRP* irp, bool* outWrite, uint64_t* outLba,
uint32_t* outSectors, void** outBuffer) {
if (!irp || !outWrite || !outLba || !outSectors || !outBuffer)
@@ -407,7 +404,7 @@ static KSTATUS AhciGetIrpReadWrite(IRP* irp, bool* outWrite, uint64_t* outLba,
// AhciDispatchReadWrite - DEVICE_OBJECT dispatch entry for disk IRPs.
// Accepted IRPs are queued to the per-drive worker and completed later with
// IoCompleteRequest(). Rejected IRPs are returned synchronously and are not
// IoCompleteRequest(). Rejected IRPs are returned synchronously and are not
// completed.
static KSTATUS AhciDispatchReadWrite(DEVICE_OBJECT *device, IRP *irp) {
if (!device || !irp) return STATUS_INVALID_PARAMETER;
@@ -592,26 +589,23 @@ static void AhciWorker(uintptr_t arg) {
// AHCI controller initialisation.
// Before memory init: only check physical link. Signature is not yet valid.
// After COMRESET + FIS receive: check signature for SATA disk.
// Issue a COMRESET on the port, wait for link re-establishment and D2H FIS.
// FRE must already be set (port_init_memory does this) so the HBA can
// capture the signature FIS the device sends after the reset.
// PORT_IE bits we care about:
// DHRS (bit 0) - D2H Register FIS received - normal completion
// PSS (bit 1) - PIO Setup FIS received
// DSS (bit 2) - DMA Setup FIS received
// SDBS (bit 3) - Set Device Bits FIS received
// TFES (bit 30) - Task File Error Status - error
// HBFS (bit 29) - Host Bus Fatal error
// HBDS (bit 28) - Host Bus Data error
// IFS (bit 27) - Interface Fatal error.
// DHRS (bit 0) - D2H Register FIS received - normal completion
// PSS (bit 1) - PIO Setup FIS received
// DSS (bit 2) - DMA Setup FIS received
// SDBS (bit 3) - Set Device Bits FIS received
// TFES (bit 30) - Task File Error Status - error
// HBFS (bit 29) - Host Bus Fatal error
// HBDS (bit 28) - Host Bus Data error
// IFS (bit 27) - Interface Fatal error.
#define PORT_IE_NORMAL (0x0000000Fu)
#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)
+1 -1
View File
@@ -221,7 +221,7 @@ typedef volatile struct {
// Constants.
// PCI/AHCI binding. PCI enumeration, BAR decoding, interrupt line
// 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'
-7
View File
@@ -28,7 +28,6 @@ KSTATUS ahci_map_mmio(uint64_t physBase, uint64_t length) {
return STATUS_SUCCESS;
}
// Implements the port stop operation.
void port_stop(HBA_PORT *port) {
// Clear ST (bit 0).
@@ -45,7 +44,6 @@ void port_stop(HBA_PORT *port) {
}
}
// Implements the port start operation.
void port_start(HBA_PORT *port) {
// Wait until CR is clear before setting ST.
@@ -56,7 +54,6 @@ void port_start(HBA_PORT *port) {
port->cmd |= PORT_CMD_ST;
}
// Implements the hba reset operation.
KSTATUS hba_reset(HBA_MEM *hba) {
// BIOS/OS handoff (spec §10.6.3).
@@ -91,7 +88,6 @@ KSTATUS hba_reset(HBA_MEM *hba) {
return STATUS_SUCCESS;
}
// Implements the port link up operation.
bool port_link_up(HBA_PORT *port) {
uint32_t det = port->ssts & 0x0F;
@@ -99,20 +95,17 @@ bool port_link_up(HBA_PORT *port) {
return (det == PORT_DET_PRESENT && ipm == PORT_IPM_ACTIVE);
}
// Implements the port is sata disk operation.
bool port_is_sata_disk(HBA_PORT *port) {
return port->sig == SATA_SIG_ATA;
}
// Implements the ahci delay ms operation.
void ahci_delay_ms(uint32_t ms) {
for (uint32_t i = 0; i < ms * 1000; i++)
__outbyte(0x80, 0);
}
// Implements the port comreset operation.
KSTATUS port_comreset(AHCI_DRIVE *drv) {
HBA_PORT *port = drv->port;