redo boot around an explicit system volume and fixed kernel/HAL images, redo volumes and filesystem paths around capabilities instead of drive numbers and kernel CWD state, add a capability/security model with derived and revocable handles and security contexts, add port IPC and rssrv to manage the userspace resource namespace and publish the system volume, move process, dll and path loading to userspace and add process builder syscalls, init now supervises rssrv and the session process, redo the syscall ABI around request structs and four arguments, add a volume manager and proper partition arrival/removal handling so FAT and NTFS mount dynamically, clean up the HAL/DDK boundary so drivers don't depend on HAL internals, save fpu state properly now, the serial driver doesn't hang on a missing port, fix AHCI startup races, fix timeout accounting, fix shared mmap writeback, more smoke tests
This commit is contained in:
1 parent
4add6789ba
commit
f882d339fb
232 files changed
+20934
-8898
No files matched your search
+10
-18
@@ -611,15 +611,6 @@ __declspec(dllexport) KSTATUS PciEnableMsi(DEVICE_OBJECT* pdo, uint8_t irqSlot)
|
||||
if (!cap)
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
|
||||
uint32_t bspLapic = gDdk->Platform->GetBspLapicId();
|
||||
uint8_t x2Apic = gDdk->Platform->IsX2ApicEnabled();
|
||||
if (!x2Apic && bspLapic >= 256u)
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
if (x2Apic && bspLapic >= 256u) {
|
||||
// Extended x2APIC MSI destinations need interrupt-remapping awareness.
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
uint8_t bus = ext->Info.Address.Bus;
|
||||
uint8_t dev = ext->Info.Address.Device;
|
||||
uint8_t fn = ext->Info.Address.Function;
|
||||
@@ -627,18 +618,19 @@ __declspec(dllexport) KSTATUS PciEnableMsi(DEVICE_OBJECT* pdo, uint8_t irqSlot)
|
||||
uint16_t control = PciRawRead16(bus, dev, fn, (uint16_t)(cap + 2u));
|
||||
bool is64 = (control & PCI_MSI_CTRL_64BIT) != 0;
|
||||
bool pvm = (control & PCI_MSI_CTRL_PVM) != 0;
|
||||
uint8_t vector = (uint8_t)(PCI_IRQ_VECTOR_BASE + irqSlot);
|
||||
uint32_t address = PCI_MSI_ADDRESS_BASE | ((bspLapic & 0xFFu) << 12);
|
||||
uint16_t data = vector;
|
||||
NTL_DDK_MSI_MESSAGE message;
|
||||
KSTATUS messageStatus = gDdk->Interrupt->ComposeMsiMessage(irqSlot, &message);
|
||||
if (!KSUCCESS(messageStatus))
|
||||
return messageStatus;
|
||||
|
||||
PciRawWrite32(bus, dev, fn, (uint16_t)(cap + 4u), address);
|
||||
PciRawWrite32(bus, dev, fn, (uint16_t)(cap + 4u), (uint32_t)message.Address);
|
||||
if (is64) {
|
||||
PciRawWrite32(bus, dev, fn, (uint16_t)(cap + 8u), 0);
|
||||
PciRawWrite16(bus, dev, fn, (uint16_t)(cap + 12u), data);
|
||||
PciRawWrite32(bus, dev, fn, (uint16_t)(cap + 8u), (uint32_t)(message.Address >> 32));
|
||||
PciRawWrite16(bus, dev, fn, (uint16_t)(cap + 12u), (uint16_t)message.Data);
|
||||
if (pvm)
|
||||
PciRawWrite32(bus, dev, fn, (uint16_t)(cap + 16u), 0);
|
||||
} else {
|
||||
PciRawWrite16(bus, dev, fn, (uint16_t)(cap + 8u), data);
|
||||
PciRawWrite16(bus, dev, fn, (uint16_t)(cap + 8u), (uint16_t)message.Data);
|
||||
if (pvm)
|
||||
PciRawWrite32(bus, dev, fn, (uint16_t)(cap + 12u), 0);
|
||||
}
|
||||
@@ -651,8 +643,8 @@ __declspec(dllexport) KSTATUS PciEnableMsi(DEVICE_OBJECT* pdo, uint8_t irqSlot)
|
||||
cmd |= PCI_COMMAND_INTX_DISABLE;
|
||||
PciRawWrite16(bus, dev, fn, PCI_REG_COMMAND_STATUS, cmd);
|
||||
|
||||
gDdk->Debug->Print("pci: MSI enabled %02x:%02x.%u cap=0x%02x vector=0x%02x irqslot=%u dest=%u %s\n",
|
||||
bus, dev, fn, cap, vector, irqSlot, bspLapic, is64 ? "64-bit" : "32-bit");
|
||||
gDdk->Debug->Print("pci: MSI enabled %02x:%02x.%u cap=0x%02x vector=0x%02x irqslot=%u %s\n",
|
||||
bus, dev, fn, cap, (unsigned)(message.Data & 0xFFu), irqSlot, is64 ? "64-bit" : "32-bit");
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
#define PCI_MSI_CTRL_64BIT 0x0080u
|
||||
#define PCI_MSI_CTRL_PVM 0x0100u
|
||||
|
||||
#define PCI_MSI_ADDRESS_BASE 0xFEE00000u
|
||||
#define PCI_IRQ_VECTOR_BASE 0x20u
|
||||
|
||||
typedef struct PCI_PDO_EXTENSION {
|
||||
|
||||
+352
-562
File diff suppressed because it is too large.
Load diff
+23
-21
@@ -21,25 +21,27 @@
|
||||
|
||||
typedef struct FAT_VOL_S FAT_VOL;
|
||||
|
||||
KSTATUS FatOpen(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx);
|
||||
KSTATUS FatFlush(IO_FILESYSTEM *fs, void *fileCtx);
|
||||
void FatClose(IO_FILESYSTEM *fs, void *fileCtx);
|
||||
KSTATUS FatRead(IO_FILESYSTEM *fs, void *fileCtx, uint64_t offset, void *buf, size_t size, size_t *bytesRead);
|
||||
KSTATUS FatWrite(IO_FILESYSTEM *fs, void *fileCtx, uint64_t offset, const void *buf, size_t size, size_t *bytesWritten);
|
||||
KSTATUS FatReadAsync(IO_FILESYSTEM *fs, void *fileCtx, uint64_t offset, void *buf, size_t size, IO_FS_ASYNC_COMPLETE complete, void *completeContext);
|
||||
KSTATUS FatWriteAsync(IO_FILESYSTEM *fs, void *fileCtx, uint64_t offset, const void *buf, size_t size, IO_FS_ASYNC_COMPLETE complete, void *completeContext);
|
||||
KSTATUS FatCreate(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx);
|
||||
KSTATUS FatCreateDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW path);
|
||||
KSTATUS FatRemoveDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW path);
|
||||
KSTATUS FatDelete(IO_FILESYSTEM *fs, SYS_STRING_VIEW path);
|
||||
KSTATUS FatTruncate(IO_FILESYSTEM *fs, void *fileCtx, uint64_t newSize, uint32_t fileFlags);
|
||||
uint64_t FatGetSize(IO_FILESYSTEM *fs, void *fileCtx);
|
||||
KSTATUS FatQueryInfo(IO_FILESYSTEM *fs, void *fileCtx, SYS_FILE_STAT *out);
|
||||
KSTATUS FatStat(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, SYS_FILE_STAT *out);
|
||||
KSTATUS FatQueryIdentity(IO_FILESYSTEM *fs, void *fileCtx, IO_FILE_IDENTITY *out);
|
||||
KSTATUS FatOpenDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW dirPath, void **dirCtx);
|
||||
KSTATUS FatReadDir(IO_FILESYSTEM *fs, void *dirCtx, KDIR_ENTRY *out);
|
||||
void FatCloseDir(IO_FILESYSTEM *fs, void *dirCtx);
|
||||
void FatQueryStats(IO_FILESYSTEM *fs, IO_FILESYSTEM_STATS *out);
|
||||
KSTATUS FatOpen(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path, void** fileCtx);
|
||||
KSTATUS FatFlush(IO_VOLUME_PROVIDER* fs, void* fileCtx);
|
||||
void FatClose(IO_VOLUME_PROVIDER* fs, void* fileCtx);
|
||||
KSTATUS FatRead(IO_VOLUME_PROVIDER* fs, void* fileCtx, uint64_t offset, void* buf, size_t size, size_t* bytesRead);
|
||||
KSTATUS FatWrite(IO_VOLUME_PROVIDER* fs, void* fileCtx, uint64_t offset, const void* buf, size_t size, size_t* bytesWritten);
|
||||
KSTATUS FatReadAsync(IO_VOLUME_PROVIDER* fs, void* fileCtx, uint64_t offset, void* buf, size_t size, IO_FS_ASYNC_COMPLETE complete, void* completeContext);
|
||||
KSTATUS FatWriteAsync(IO_VOLUME_PROVIDER* fs, void* fileCtx, uint64_t offset, const void* buf, size_t size, IO_FS_ASYNC_COMPLETE complete, void* completeContext);
|
||||
KSTATUS FatCreate(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path, void** fileCtx);
|
||||
KSTATUS FatCreateDir(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path);
|
||||
KSTATUS FatRemoveDir(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path);
|
||||
KSTATUS FatDelete(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path);
|
||||
KSTATUS FatTruncate(IO_VOLUME_PROVIDER* fs, void* fileCtx, uint64_t newSize, uint32_t fileFlags);
|
||||
uint64_t FatGetSize(IO_VOLUME_PROVIDER* fs, void* fileCtx);
|
||||
KSTATUS FatQueryInfo(IO_VOLUME_PROVIDER* fs, void* fileCtx, SYS_FILE_STAT* out);
|
||||
KSTATUS FatStat(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path, SYS_FILE_STAT* out);
|
||||
KSTATUS FatQueryIdentity(IO_VOLUME_PROVIDER* fs, void* fileCtx, IO_FILE_IDENTITY* out);
|
||||
KSTATUS FatOpenDir(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW dirPath, void** dirCtx);
|
||||
KSTATUS FatReadDir(IO_VOLUME_PROVIDER* fs, void* dirCtx, KDIR_ENTRY* out);
|
||||
void FatCloseDir(IO_VOLUME_PROVIDER* fs, void* dirCtx);
|
||||
void FatQueryStats(IO_VOLUME_PROVIDER* fs, IO_FILESYSTEM_STATS* out);
|
||||
|
||||
void FatInitVtable(IO_FILESYSTEM* fs, FAT_VOL* vol);
|
||||
void FatInitProvider(IO_VOLUME_PROVIDER* provider, FAT_VOL* vol);
|
||||
void FatVolumeDead(IO_VOLUME_PROVIDER* provider, IO_VOLUME_REMOVAL_REASON reason);
|
||||
void FatDestroyVolumeProvider(IO_VOLUME_PROVIDER* provider);
|
||||
@@ -63,39 +63,6 @@ typedef struct {
|
||||
uint16_t Name3[2];
|
||||
} LFN_ENTRY;
|
||||
|
||||
typedef struct {
|
||||
uint32_t Data1;
|
||||
uint16_t Data2;
|
||||
uint16_t Data3;
|
||||
uint8_t Data4[8];
|
||||
} EFI_GUID;
|
||||
|
||||
typedef struct {
|
||||
char Signature[8];
|
||||
uint32_t Revision;
|
||||
uint32_t HeaderSize;
|
||||
uint32_t HeaderCRC32;
|
||||
uint32_t Reserved;
|
||||
uint64_t MyLBA;
|
||||
uint64_t AlternateLBA;
|
||||
uint64_t FirstUsableLBA;
|
||||
uint64_t LastUsableLBA;
|
||||
EFI_GUID DiskGUID;
|
||||
uint64_t PartitionEntryLBA;
|
||||
uint32_t NumberOfPartitionEntries;
|
||||
uint32_t SizeOfPartitionEntry;
|
||||
uint32_t PartitionEntryArrayCRC32;
|
||||
} GPT_HEADER;
|
||||
|
||||
typedef struct {
|
||||
EFI_GUID PartitionTypeGUID;
|
||||
EFI_GUID UniquePartitionGUID;
|
||||
uint64_t StartingLBA;
|
||||
uint64_t EndingLBA;
|
||||
uint64_t Attributes;
|
||||
uint16_t PartitionName[36];
|
||||
} GPT_ENTRY;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#define ATTR_VOLUME_ID 0x08
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
#include "fat.h"
|
||||
|
||||
// Initializes vtable.
|
||||
void FatInitVtable(IO_FILESYSTEM* fs, FAT_VOL* vol) {
|
||||
// Initializes the FAT volume provider dispatch.
|
||||
void FatInitProvider(IO_VOLUME_PROVIDER* fs, FAT_VOL* vol) {
|
||||
memset(fs, 0, sizeof(*fs));
|
||||
fs->Open = FatOpen;
|
||||
fs->Read = FatRead;
|
||||
@@ -21,9 +21,11 @@ void FatInitVtable(IO_FILESYSTEM* fs, FAT_VOL* vol) {
|
||||
fs->QueryIdentity = FatQueryIdentity;
|
||||
fs->Flush = FatFlush;
|
||||
fs->Close = FatClose;
|
||||
fs->DriverCtx = vol;
|
||||
fs->Context = vol;
|
||||
fs->OpenDir = FatOpenDir;
|
||||
fs->ReadDir = FatReadDir;
|
||||
fs->CloseDir = FatCloseDir;
|
||||
fs->QueryStats = FatQueryStats;
|
||||
fs->VolumeDead = FatVolumeDead;
|
||||
fs->Destroy = FatDestroyVolumeProvider;
|
||||
}
|
||||
+195
-298
@@ -1,6 +1,6 @@
|
||||
// Native read-only NTFS filesystem driver for NTLite.
|
||||
// Portions are based on ntfs-efi by Mark Harmstone (2023), GPLv2-or-later.
|
||||
// The port removes UEFI protocol/header dependencies and targets NTLite's IO_FILESYSTEM ABI.
|
||||
// The port removes UEFI protocol/header dependencies and targets NTLite's IO_VOLUME_PROVIDER ABI.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
@@ -13,9 +13,8 @@
|
||||
#include <ntlite/ddk/debug.h>
|
||||
#include <ntlite/ddk/list.h>
|
||||
#include <ntlite/ddk/sync.h>
|
||||
#include <ntlite/ddk/mutex.h>
|
||||
|
||||
#define NTFS_MAX_VOLUMES 8u
|
||||
#define NTFS_MAX_PARTITIONS_PER_DISK 32u
|
||||
#define NTFS_SECTOR_SIZE 512u
|
||||
#define NTFS_FS_NAME "NTFS "
|
||||
#define NTFS_UPCASE_TABLE_CHARS 0x10000u
|
||||
@@ -203,48 +202,6 @@ typedef struct NTFS_FILE_NAME_ATTR {
|
||||
uint16_t FileName[1];
|
||||
} NTFS_FILE_NAME_ATTR;
|
||||
|
||||
typedef struct NTFS_GPT_GUID {
|
||||
uint32_t Data1;
|
||||
uint16_t Data2;
|
||||
uint16_t Data3;
|
||||
uint8_t Data4[8];
|
||||
} NTFS_GPT_GUID;
|
||||
|
||||
typedef struct NTFS_GPT_HEADER {
|
||||
char Signature[8];
|
||||
uint32_t Revision;
|
||||
uint32_t HeaderSize;
|
||||
uint32_t HeaderCRC32;
|
||||
uint32_t Reserved;
|
||||
uint64_t MyLBA;
|
||||
uint64_t AlternateLBA;
|
||||
uint64_t FirstUsableLBA;
|
||||
uint64_t LastUsableLBA;
|
||||
NTFS_GPT_GUID DiskGUID;
|
||||
uint64_t PartitionEntryLBA;
|
||||
uint32_t NumberOfPartitionEntries;
|
||||
uint32_t SizeOfPartitionEntry;
|
||||
uint32_t PartitionEntryArrayCRC32;
|
||||
} NTFS_GPT_HEADER;
|
||||
|
||||
typedef struct NTFS_GPT_ENTRY {
|
||||
NTFS_GPT_GUID PartitionTypeGUID;
|
||||
NTFS_GPT_GUID UniquePartitionGUID;
|
||||
uint64_t StartingLBA;
|
||||
uint64_t EndingLBA;
|
||||
uint64_t Attributes;
|
||||
uint16_t PartitionName[36];
|
||||
} NTFS_GPT_ENTRY;
|
||||
|
||||
typedef struct NTFS_MBR_PARTITION {
|
||||
uint8_t BootIndicator;
|
||||
uint8_t StartingChs[3];
|
||||
uint8_t OsType;
|
||||
uint8_t EndingChs[3];
|
||||
uint32_t StartingLba;
|
||||
uint32_t SizeInLba;
|
||||
} NTFS_MBR_PARTITION;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef struct NTFS_RUN {
|
||||
@@ -257,7 +214,6 @@ typedef struct NTFS_RUN {
|
||||
|
||||
typedef struct NTFS_VOLUME {
|
||||
uint8_t DriveIndex;
|
||||
uint8_t VolumeIndex;
|
||||
uint64_t PartitionLba;
|
||||
uint64_t PartitionSize;
|
||||
uint64_t SerialNumber;
|
||||
@@ -269,6 +225,8 @@ typedef struct NTFS_VOLUME {
|
||||
uint64_t FileRecordSize;
|
||||
LIST_ENTRY MftRuns;
|
||||
uint16_t* Upcase;
|
||||
IO_VOLUME* Volume; // Active mount-list reference.
|
||||
LIST_ENTRY MountLink;
|
||||
} NTFS_VOLUME;
|
||||
|
||||
typedef struct NTFS_FILE {
|
||||
@@ -306,11 +264,6 @@ typedef struct NTFS_DIR_CTX {
|
||||
uint32_t Position;
|
||||
} NTFS_DIR_CTX;
|
||||
|
||||
typedef struct NTFS_PARTITION_CANDIDATE {
|
||||
uint64_t StartLba;
|
||||
uint64_t SectorCount;
|
||||
} NTFS_PARTITION_CANDIDATE;
|
||||
|
||||
typedef struct NTFS_ATTR_VIEW {
|
||||
const NTFS_ATTRIBUTE_RECORD_HEADER* Attr;
|
||||
const uint8_t* ResidentData;
|
||||
@@ -332,13 +285,11 @@ typedef struct NTFS_COLLECT_CTX {
|
||||
bool HideNtfsMetadata;
|
||||
} NTFS_COLLECT_CTX;
|
||||
|
||||
static NTFS_VOLUME gVolumes[NTFS_MAX_VOLUMES];
|
||||
static IO_FILESYSTEM gFilesystems[NTFS_MAX_VOLUMES];
|
||||
static bool gVolumeMounted[NTFS_MAX_VOLUMES];
|
||||
static bool gVolumeBusy[NTFS_MAX_VOLUMES];
|
||||
static bool gDiskScanned[NTFS_MAX_VOLUMES];
|
||||
static SPINLOCK gNtfsMountLock = SPINLOCK_INIT;
|
||||
static LIST_ENTRY gNtfsMounts;
|
||||
static uint32_t gMountedCount;
|
||||
static MUTEX gNtfsMountMutex;
|
||||
static SPINLOCK gNtfsMountMutexInitLock = SPINLOCK_INIT;
|
||||
static int gNtfsMountMutexReady;
|
||||
|
||||
static uint64_t NtfsAlignUp(uint64_t value, uint64_t alignment) {
|
||||
if (alignment == 0) return value;
|
||||
@@ -937,7 +888,6 @@ static void NtfsFillStat(NTFS_FILE* file, SYS_FILE_STAT* out) {
|
||||
out->Attributes = NtfsSysAttrsFromNtfs(file->Attributes, file->IsDirectory);
|
||||
out->LinkCount = 1;
|
||||
out->FileSize = file->Size;
|
||||
out->VolumeId = file->Vol->SerialNumber;
|
||||
out->FileId = file->Inode;
|
||||
NtfsWinTimeToSystemTime(file->StandardInfo.CreationTime, &out->CreationTime);
|
||||
NtfsWinTimeToSystemTime(file->StandardInfo.LastAccessTime, &out->LastAccessTime);
|
||||
@@ -1139,9 +1089,9 @@ static bool NtfsCollectEntryCallback(const NTFS_FILE_NAME_ATTR* fn, uint64_t ino
|
||||
return true;
|
||||
}
|
||||
|
||||
static KSTATUS NtfsOpen(IO_FILESYSTEM* fs, SYS_STRING_VIEW path, void** fileCtx) {
|
||||
static KSTATUS NtfsOpen(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path, void** fileCtx) {
|
||||
if (!fs || !fileCtx || !SysSvIsValid(path)) return NTLSTATUS_INVALID_PARAMETER;
|
||||
NTFS_VOLUME* vol = (NTFS_VOLUME*)fs->DriverCtx;
|
||||
NTFS_VOLUME* vol = (NTFS_VOLUME*)fs->Context;
|
||||
if (!vol) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
uint64_t inode;
|
||||
@@ -1156,7 +1106,7 @@ static KSTATUS NtfsOpen(IO_FILESYSTEM* fs, SYS_STRING_VIEW path, void** fileCtx)
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void NtfsClose(IO_FILESYSTEM* fs, void* fileCtx) {
|
||||
static void NtfsClose(IO_VOLUME_PROVIDER* fs, void* fileCtx) {
|
||||
(void)fs;
|
||||
NTFS_FILE* file = (NTFS_FILE*)fileCtx;
|
||||
if (!file) return;
|
||||
@@ -1164,46 +1114,46 @@ static void NtfsClose(IO_FILESYSTEM* fs, void* fileCtx) {
|
||||
MmFreePool(file);
|
||||
}
|
||||
|
||||
static KSTATUS NtfsRead(IO_FILESYSTEM* fs, void* fileCtx, uint64_t offset, void* buf, size_t size, size_t* bytesRead) {
|
||||
static KSTATUS NtfsRead(IO_VOLUME_PROVIDER* fs, void* fileCtx, uint64_t offset, void* buf, size_t size, size_t* bytesRead) {
|
||||
(void)fs;
|
||||
return NtfsReadFileData((NTFS_FILE*)fileCtx, offset, buf, size, bytesRead);
|
||||
}
|
||||
|
||||
static KSTATUS NtfsWrite(IO_FILESYSTEM* fs, void* fileCtx, uint64_t offset, const void* buf, size_t size, size_t* bytesWritten) {
|
||||
static KSTATUS NtfsWrite(IO_VOLUME_PROVIDER* fs, void* fileCtx, uint64_t offset, const void* buf, size_t size, size_t* bytesWritten) {
|
||||
(void)fs; (void)fileCtx; (void)offset; (void)buf; (void)size;
|
||||
if (bytesWritten) *bytesWritten = 0;
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
static KSTATUS NtfsCreate(IO_FILESYSTEM* fs, SYS_STRING_VIEW path, void** fileCtx) {
|
||||
static KSTATUS NtfsCreate(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path, void** fileCtx) {
|
||||
(void)fs; (void)path; (void)fileCtx;
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
static KSTATUS NtfsDelete(IO_FILESYSTEM* fs, SYS_STRING_VIEW path) {
|
||||
static KSTATUS NtfsDelete(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path) {
|
||||
(void)fs; (void)path;
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
static KSTATUS NtfsTruncate(IO_FILESYSTEM* fs, void* fileCtx, uint64_t newSize, uint32_t fileFlags) {
|
||||
static KSTATUS NtfsTruncate(IO_VOLUME_PROVIDER* fs, void* fileCtx, uint64_t newSize, uint32_t fileFlags) {
|
||||
(void)fs; (void)fileCtx; (void)newSize; (void)fileFlags;
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
static uint64_t NtfsGetSize(IO_FILESYSTEM* fs, void* fileCtx) {
|
||||
static uint64_t NtfsGetSize(IO_VOLUME_PROVIDER* fs, void* fileCtx) {
|
||||
(void)fs;
|
||||
NTFS_FILE* file = (NTFS_FILE*)fileCtx;
|
||||
return file ? file->Size : 0;
|
||||
}
|
||||
|
||||
static KSTATUS NtfsQueryInfo(IO_FILESYSTEM* fs, void* fileCtx, SYS_FILE_STAT* out) {
|
||||
static KSTATUS NtfsQueryInfo(IO_VOLUME_PROVIDER* fs, void* fileCtx, SYS_FILE_STAT* out) {
|
||||
(void)fs;
|
||||
if (!fileCtx || !out) return NTLSTATUS_INVALID_PARAMETER;
|
||||
NtfsFillStat((NTFS_FILE*)fileCtx, out);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static KSTATUS NtfsStat(IO_FILESYSTEM* fs, SYS_STRING_VIEW path, SYS_FILE_STAT* out) {
|
||||
static KSTATUS NtfsStat(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path, SYS_FILE_STAT* out) {
|
||||
if (!fs || !out || !SysSvIsValid(path)) return NTLSTATUS_INVALID_PARAMETER;
|
||||
void* ctx = NULL;
|
||||
KSTATUS s = NtfsOpen(fs, path, &ctx);
|
||||
@@ -1213,27 +1163,27 @@ static KSTATUS NtfsStat(IO_FILESYSTEM* fs, SYS_STRING_VIEW path, SYS_FILE_STAT*
|
||||
return s;
|
||||
}
|
||||
|
||||
static KSTATUS NtfsQueryIdentity(IO_FILESYSTEM* fs, void* fileCtx, IO_FILE_IDENTITY* out) {
|
||||
static KSTATUS NtfsQueryIdentity(IO_VOLUME_PROVIDER* fs, void* fileCtx, IO_FILE_IDENTITY* out) {
|
||||
(void)fs;
|
||||
if (!fileCtx || !out) return NTLSTATUS_INVALID_PARAMETER;
|
||||
NTFS_FILE* file = (NTFS_FILE*)fileCtx;
|
||||
out->VolumeId = file->Vol->SerialNumber;
|
||||
out->FileId = file->Inode;
|
||||
out->Generation = (uint64_t)file->StandardInfo.LastWriteTime ^ ((uint64_t)file->StandardInfo.ChangeTime << 1u);
|
||||
out->Size = file->Size;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static KSTATUS NtfsOpenDir(IO_FILESYSTEM* fs, SYS_STRING_VIEW dirPath, void** dirCtx) {
|
||||
static KSTATUS NtfsOpenDir(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW dirPath, void** dirCtx) {
|
||||
if (!fs || !dirCtx || !SysSvIsValid(dirPath)) return NTLSTATUS_INVALID_PARAMETER;
|
||||
NTFS_VOLUME* vol = (NTFS_VOLUME*)fs->DriverCtx;
|
||||
NTFS_VOLUME* vol = (NTFS_VOLUME*)fs->Context;
|
||||
if (!vol) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
uint64_t inode;
|
||||
KSTATUS s = NtfsLookupPath(vol, dirPath, &inode);
|
||||
if (!KSUCCESS(s)) {
|
||||
NtfsTrace("NTFS: opendir: lookup failed drive=%u pathLen=%llu st=0x%x\n",
|
||||
vol->VolumeIndex, (unsigned long long)dirPath.Length, s);
|
||||
NtfsTrace("NTFS: opendir: lookup failed disk=%u lba=%llu pathLen=%llu st=0x%x\n",
|
||||
vol->DriveIndex, (unsigned long long)vol->PartitionLba,
|
||||
(unsigned long long)dirPath.Length, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1295,7 +1245,7 @@ static KSTATUS NtfsOpenDir(IO_FILESYSTEM* fs, SYS_STRING_VIEW dirPath, void** di
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static KSTATUS NtfsReadDir(IO_FILESYSTEM* fs, void* dirCtx, KDIR_ENTRY* out) {
|
||||
static KSTATUS NtfsReadDir(IO_VOLUME_PROVIDER* fs, void* dirCtx, KDIR_ENTRY* out) {
|
||||
(void)fs;
|
||||
if (!dirCtx || !out) return NTLSTATUS_INVALID_PARAMETER;
|
||||
NTFS_DIR_CTX* ctx = (NTFS_DIR_CTX*)dirCtx;
|
||||
@@ -1311,7 +1261,7 @@ static KSTATUS NtfsReadDir(IO_FILESYSTEM* fs, void* dirCtx, KDIR_ENTRY* out) {
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void NtfsCloseDir(IO_FILESYSTEM* fs, void* dirCtx) {
|
||||
static void NtfsCloseDir(IO_VOLUME_PROVIDER* fs, void* dirCtx) {
|
||||
(void)fs;
|
||||
NTFS_DIR_CTX* ctx = (NTFS_DIR_CTX*)dirCtx;
|
||||
if (!ctx) return;
|
||||
@@ -1319,12 +1269,15 @@ static void NtfsCloseDir(IO_FILESYSTEM* fs, void* dirCtx) {
|
||||
MmFreePool(ctx);
|
||||
}
|
||||
|
||||
static void NtfsQueryStats(IO_FILESYSTEM* fs, IO_FILESYSTEM_STATS* out) {
|
||||
static void NtfsQueryStats(IO_VOLUME_PROVIDER* fs, IO_FILESYSTEM_STATS* out) {
|
||||
(void)fs;
|
||||
if (out) memset(out, 0, sizeof(*out));
|
||||
}
|
||||
|
||||
static void NtfsInitVtable(IO_FILESYSTEM* fs, NTFS_VOLUME* vol) {
|
||||
static void NtfsVolumeDead(IO_VOLUME_PROVIDER* provider, IO_VOLUME_REMOVAL_REASON reason);
|
||||
static void NtfsDestroyVolumeProvider(IO_VOLUME_PROVIDER* provider);
|
||||
|
||||
static void NtfsInitProvider(IO_VOLUME_PROVIDER* fs, NTFS_VOLUME* vol) {
|
||||
memset(fs, 0, sizeof(*fs));
|
||||
fs->Open = NtfsOpen;
|
||||
fs->Read = NtfsRead;
|
||||
@@ -1338,15 +1291,12 @@ static void NtfsInitVtable(IO_FILESYSTEM* fs, NTFS_VOLUME* vol) {
|
||||
fs->QueryIdentity = NtfsQueryIdentity;
|
||||
fs->Close = NtfsClose;
|
||||
fs->QueryStats = NtfsQueryStats;
|
||||
fs->DriverCtx = vol;
|
||||
fs->Context = vol;
|
||||
fs->OpenDir = NtfsOpenDir;
|
||||
fs->ReadDir = NtfsReadDir;
|
||||
fs->CloseDir = NtfsCloseDir;
|
||||
}
|
||||
|
||||
static bool NtfsGuidIsZero(const NTFS_GPT_GUID* guid) {
|
||||
static const NTFS_GPT_GUID zeroGuid = {0};
|
||||
return memcmp(guid, &zeroGuid, sizeof(*guid)) == 0;
|
||||
fs->VolumeDead = NtfsVolumeDead;
|
||||
fs->Destroy = NtfsDestroyVolumeProvider;
|
||||
}
|
||||
|
||||
static bool NtfsIsValidBootSector(const NTFS_BOOT_SECTOR* bs) {
|
||||
@@ -1425,6 +1375,22 @@ static void NtfsCleanupVolume(NTFS_VOLUME* vol) {
|
||||
}
|
||||
}
|
||||
|
||||
// Provider death blocks new I/O in the I/O core; NTFS state is retained for safe close teardown.
|
||||
static void NtfsVolumeDead(IO_VOLUME_PROVIDER* provider, IO_VOLUME_REMOVAL_REASON reason) {
|
||||
(void)provider;
|
||||
(void)reason;
|
||||
}
|
||||
|
||||
// Releases one dynamically allocated NTFS mount after every volume reference has drained.
|
||||
static void NtfsDestroyVolumeProvider(IO_VOLUME_PROVIDER* provider) {
|
||||
if (!provider || !provider->Context)
|
||||
return;
|
||||
NTFS_VOLUME* volume = (NTFS_VOLUME*)provider->Context;
|
||||
NtfsCleanupVolume(volume);
|
||||
memset(volume, 0, sizeof(*volume));
|
||||
MmFreePool(volume);
|
||||
}
|
||||
|
||||
static void NtfsTryLoadUpcase(NTFS_VOLUME* vol) {
|
||||
NTFS_FILE upcase;
|
||||
if (!KSUCCESS(NtfsLoadFile(vol, NTFS_UPCASE_INODE, &upcase))) return;
|
||||
@@ -1443,6 +1409,7 @@ static KSTATUS NtfsProbeAndPrepareVolume(NTFS_VOLUME* vol, uint8_t disk, uint64_
|
||||
vol->PartitionSize = sectors;
|
||||
vol->BytesPerSector = NTFS_SECTOR_SIZE;
|
||||
NtfsInitRunList(&vol->MftRuns);
|
||||
LIST_INIT(&vol->MountLink);
|
||||
|
||||
uint8_t sector[NTFS_SECTOR_SIZE];
|
||||
memset(sector, 0, sizeof(sector));
|
||||
@@ -1472,252 +1439,182 @@ static KSTATUS NtfsProbeAndPrepareVolume(NTFS_VOLUME* vol, uint8_t disk, uint64_
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static uint32_t NtfsEnumerateDiskPartitions(uint8_t disk, NTFS_PARTITION_CANDIDATE* parts, uint32_t maxParts, bool* mediaReadable) {
|
||||
if (mediaReadable) *mediaReadable = false;
|
||||
if (!parts || maxParts == 0) return 0;
|
||||
uint8_t sector[NTFS_SECTOR_SIZE];
|
||||
NTFS_VOLUME raw;
|
||||
memset(&raw, 0, sizeof(raw));
|
||||
raw.DriveIndex = disk;
|
||||
raw.PartitionLba = 0;
|
||||
raw.PartitionSize = IoDiskSectorCount(disk);
|
||||
raw.BytesPerSector = NTFS_SECTOR_SIZE;
|
||||
NtfsInitRunList(&raw.MftRuns);
|
||||
// Initializes the dynamic NTFS mount-list mutex without allocating kernel objects.
|
||||
static void NtfsMountEnsureMutex(void) {
|
||||
if (gNtfsMountMutexReady)
|
||||
return;
|
||||
|
||||
if (!KSUCCESS(NtfsReadSectors(&raw, 0, 1, sector))) return 0;
|
||||
if (mediaReadable) *mediaReadable = true;
|
||||
|
||||
uint32_t count = 0;
|
||||
NTFS_BOOT_SECTOR* vbr = (NTFS_BOOT_SECTOR*)sector;
|
||||
if (NtfsIsValidBootSector(vbr)) {
|
||||
parts[count].StartLba = 0;
|
||||
parts[count].SectorCount = vbr->TotalSectors;
|
||||
count++;
|
||||
if (count >= maxParts) return count;
|
||||
uintptr_t flags = KeAcquireSpinlock(&gNtfsMountMutexInitLock);
|
||||
if (!gNtfsMountMutexReady) {
|
||||
memset(&gNtfsMountMutex, 0, sizeof(gNtfsMountMutex));
|
||||
ObInitializeObjectHeader(&gNtfsMountMutex.Header, ObjectTypeMutex);
|
||||
gNtfsMountMutex.Lock = (SPINLOCK)SPINLOCK_INIT;
|
||||
gNtfsMountMutex.Owner = (THREAD*)0;
|
||||
LIST_INIT(&gNtfsMountMutex.WaitList);
|
||||
LIST_INIT(&gNtfsMountMutex.OwnerLink);
|
||||
gNtfsMountMutex.DonationClass = KeSchedulingNormal;
|
||||
gNtfsMountMutex.DonationPriority = PriorityLow;
|
||||
gNtfsMountMutex.HasDonation = 0;
|
||||
gNtfsMountMutex.Abandoned = 0;
|
||||
gNtfsMountMutexReady = 1;
|
||||
}
|
||||
|
||||
if (sector[510] != 0x55u || sector[511] != 0xaau) return count;
|
||||
|
||||
NTFS_MBR_PARTITION* mbr = (NTFS_MBR_PARTITION*)(sector + 446u);
|
||||
bool protectiveMbr = false;
|
||||
for (uint32_t i = 0; i < 4u; i++) if (mbr[i].OsType == 0xeeu) protectiveMbr = true;
|
||||
|
||||
if (protectiveMbr) {
|
||||
uint8_t gptSector[NTFS_SECTOR_SIZE];
|
||||
if (!KSUCCESS(NtfsReadSectors(&raw, 1, 1, gptSector))) return count;
|
||||
NTFS_GPT_HEADER* hdr = (NTFS_GPT_HEADER*)gptSector;
|
||||
if (memcmp(hdr->Signature, "EFI PART", 8) != 0 || hdr->SizeOfPartitionEntry < sizeof(NTFS_GPT_ENTRY)) return count;
|
||||
uint32_t entries = hdr->NumberOfPartitionEntries;
|
||||
if (entries > 256u) entries = 256u;
|
||||
uint32_t entrySize = hdr->SizeOfPartitionEntry;
|
||||
uint8_t entrySector[NTFS_SECTOR_SIZE];
|
||||
for (uint32_t i = 0; i < entries && count < maxParts; i++) {
|
||||
uint64_t byteOff = (uint64_t)i * entrySize;
|
||||
uint64_t lba = hdr->PartitionEntryLBA + byteOff / NTFS_SECTOR_SIZE;
|
||||
uint32_t inSector = (uint32_t)(byteOff % NTFS_SECTOR_SIZE);
|
||||
if (inSector + sizeof(NTFS_GPT_ENTRY) > NTFS_SECTOR_SIZE) continue;
|
||||
if (!KSUCCESS(NtfsReadSectors(&raw, lba, 1, entrySector))) break;
|
||||
NTFS_GPT_ENTRY* e = (NTFS_GPT_ENTRY*)(entrySector + inSector);
|
||||
if (NtfsGuidIsZero(&e->PartitionTypeGUID)) continue;
|
||||
if (e->EndingLBA < e->StartingLBA) continue;
|
||||
parts[count].StartLba = e->StartingLBA;
|
||||
parts[count].SectorCount = e->EndingLBA - e->StartingLBA + 1ull;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < 4u && count < maxParts; i++) {
|
||||
if (mbr[i].OsType == 0 || mbr[i].SizeInLba == 0) continue;
|
||||
parts[count].StartLba = mbr[i].StartingLba;
|
||||
parts[count].SectorCount = mbr[i].SizeInLba;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
KeReleaseSpinlock(&gNtfsMountMutexInitLock, flags);
|
||||
}
|
||||
|
||||
static int NtfsReserveFreeVolumeIndex(void) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&gNtfsMountLock);
|
||||
for (uint8_t i = 0; i < NTFS_MAX_VOLUMES; i++) {
|
||||
if (!gVolumeMounted[i] && !gVolumeBusy[i] && !IoFilesystemPresent(i)) {
|
||||
gVolumeBusy[i] = true;
|
||||
KeReleaseSpinlock(&gNtfsMountLock, flags);
|
||||
return (int)i;
|
||||
// Returns one active NTFS mount for an exact partition extent while the mount mutex is held.
|
||||
static NTFS_VOLUME* NtfsFindMountLocked(const IO_PARTITION_INFO* partition) {
|
||||
if (!partition)
|
||||
return (NTFS_VOLUME*)0;
|
||||
for (LIST_ENTRY* entry = gNtfsMounts.Fwd; entry != &gNtfsMounts; entry = entry->Fwd) {
|
||||
NTFS_VOLUME* volume = CONTAINING_RECORD(entry, NTFS_VOLUME, MountLink);
|
||||
if (volume->DriveIndex == partition->DiskIndex &&
|
||||
volume->PartitionLba == partition->StartLba) {
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
KeReleaseSpinlock(&gNtfsMountLock, flags);
|
||||
return -1;
|
||||
return (NTFS_VOLUME*)0;
|
||||
}
|
||||
|
||||
static void NtfsReleaseReservedVolumeIndex(uint8_t volumeIndex) {
|
||||
if (volumeIndex >= NTFS_MAX_VOLUMES) return;
|
||||
uintptr_t flags = KeAcquireSpinlock(&gNtfsMountLock);
|
||||
gVolumeBusy[volumeIndex] = false;
|
||||
KeReleaseSpinlock(&gNtfsMountLock, flags);
|
||||
}
|
||||
// Probes one I/O-core partition extent and mounts it only when its VBR is valid NTFS.
|
||||
static bool NtfsTryMountPartition(const IO_PARTITION_INFO* partition) {
|
||||
if (!partition || partition->SectorCount == 0 || !IoDiskDevicePresent(partition->DiskIndex))
|
||||
return false;
|
||||
|
||||
static bool NtfsBeginDiskScan(uint8_t disk) {
|
||||
if (disk >= NTFS_MAX_VOLUMES) return false;
|
||||
uintptr_t flags = KeAcquireSpinlock(&gNtfsMountLock);
|
||||
if (gDiskScanned[disk]) {
|
||||
KeReleaseSpinlock(&gNtfsMountLock, flags);
|
||||
NtfsMountEnsureMutex();
|
||||
if (!KE_MUTEX_ACQUIRE_SUCCEEDED(KeAcquireMutex(&gNtfsMountMutex, KE_WAIT_INFINITE)))
|
||||
return false;
|
||||
|
||||
if (NtfsFindMountLocked(partition)) {
|
||||
KeReleaseMutex(&gNtfsMountMutex);
|
||||
return false;
|
||||
}
|
||||
gDiskScanned[disk] = true;
|
||||
KeReleaseSpinlock(&gNtfsMountLock, flags);
|
||||
|
||||
NTFS_VOLUME* mount = (NTFS_VOLUME*)MmAllocatePool(sizeof(NTFS_VOLUME));
|
||||
if (!mount) {
|
||||
KeReleaseMutex(&gNtfsMountMutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
KSTATUS status = NtfsProbeAndPrepareVolume(mount,
|
||||
partition->DiskIndex,
|
||||
partition->StartLba,
|
||||
partition->SectorCount);
|
||||
if (!KSUCCESS(status)) {
|
||||
NtfsCleanupVolume(mount);
|
||||
MmFreePool(mount);
|
||||
KeReleaseMutex(&gNtfsMountMutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
IO_VOLUME_PROVIDER provider;
|
||||
NtfsInitProvider(&provider, mount);
|
||||
IO_VOLUME_SOURCE params;
|
||||
params.Type = IoVolumeSourcePartition;
|
||||
params.DiskIndex = partition->DiskIndex;
|
||||
memset(params.Reserved, 0, sizeof(params.Reserved));
|
||||
params.StartLba = partition->StartLba;
|
||||
params.SectorCount = mount->PartitionSize;
|
||||
|
||||
IO_VOLUME* volume = (IO_VOLUME*)0;
|
||||
status = IoCreateVolume(&provider, ¶ms, &volume);
|
||||
if (!KSUCCESS(status)) {
|
||||
if (volume) {
|
||||
(void)IoRemoveVolume(volume, IoVolumeUnmounted);
|
||||
IoReleaseVolume(volume);
|
||||
} else {
|
||||
NtfsCleanupVolume(mount);
|
||||
MmFreePool(mount);
|
||||
}
|
||||
DbgPrint("NTFS: disk=%u lba=%llu mount failed status=0x%x\n",
|
||||
partition->DiskIndex, (unsigned long long)partition->StartLba, status);
|
||||
KeReleaseMutex(&gNtfsMountMutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retain the IoCreateVolume caller reference while this provider remains on the active mount list.
|
||||
mount->Volume = volume;
|
||||
ListInsertTail(&gNtfsMounts, &mount->MountLink);
|
||||
gMountedCount++;
|
||||
uint32_t mountedCount = gMountedCount;
|
||||
uint32_t instanceId = IoGetVolumeInstanceId(volume);
|
||||
uint64_t recordSize = mount->FileRecordSize;
|
||||
|
||||
DbgPrint("NTFS: disk=%u lba=%llu mounted volume=%u (mft_record=%llu); total=%u\n",
|
||||
partition->DiskIndex, (unsigned long long)partition->StartLba,
|
||||
instanceId, (unsigned long long)recordSize, mountedCount);
|
||||
KeReleaseMutex(&gNtfsMountMutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void NtfsEndDiskScan(uint8_t disk, bool completed) {
|
||||
if (disk >= NTFS_MAX_VOLUMES) return;
|
||||
if (completed) return;
|
||||
uintptr_t flags = KeAcquireSpinlock(&gNtfsMountLock);
|
||||
gDiskScanned[disk] = false;
|
||||
KeReleaseSpinlock(&gNtfsMountLock, flags);
|
||||
// Unmounts one exact dynamic NTFS provider instance when its backing partition disappears.
|
||||
static void NtfsUnmountPartition(const IO_PARTITION_INFO* partition, IO_PARTITION_CHANGE change) {
|
||||
if (!partition)
|
||||
return;
|
||||
|
||||
NtfsMountEnsureMutex();
|
||||
if (!KE_MUTEX_ACQUIRE_SUCCEEDED(KeAcquireMutex(&gNtfsMountMutex, KE_WAIT_INFINITE)))
|
||||
return;
|
||||
|
||||
NTFS_VOLUME* mount = NtfsFindMountLocked(partition);
|
||||
if (!mount) {
|
||||
KeReleaseMutex(&gNtfsMountMutex);
|
||||
return;
|
||||
}
|
||||
|
||||
IO_VOLUME* volume = mount->Volume;
|
||||
uint32_t instanceId = IoGetVolumeInstanceId(volume);
|
||||
ListRemove(&mount->MountLink);
|
||||
mount->Volume = (IO_VOLUME*)0;
|
||||
if (gMountedCount)
|
||||
gMountedCount--;
|
||||
uint32_t mountedCount = gMountedCount;
|
||||
KeReleaseMutex(&gNtfsMountMutex);
|
||||
|
||||
(void)IoRemoveVolume(volume, IoVolumeBackingRemoved);
|
||||
IoReleaseVolume(volume); // Drop the active mount-list reference.
|
||||
DbgPrint("NTFS: disk=%u lba=%llu %s; unmounted volume=%u; total=%u\n",
|
||||
partition->DiskIndex, (unsigned long long)partition->StartLba,
|
||||
change == IoPartitionSurpriseRemoved ? "surprise removed" : "removed",
|
||||
instanceId, mountedCount);
|
||||
}
|
||||
|
||||
static uint32_t NtfsTryMountDisk(uint8_t disk) {
|
||||
if (disk >= NTFS_MAX_VOLUMES) return 0;
|
||||
if (!IoDiskDevicePresent(disk)) return 0;
|
||||
if (!NtfsBeginDiskScan(disk)) return 0;
|
||||
|
||||
bool scanCompleted = false;
|
||||
bool mediaReadable = false;
|
||||
NTFS_PARTITION_CANDIDATE parts[NTFS_MAX_PARTITIONS_PER_DISK];
|
||||
uint32_t partCount = NtfsEnumerateDiskPartitions(disk, parts, NTFS_MAX_PARTITIONS_PER_DISK, &mediaReadable);
|
||||
if (!mediaReadable) {
|
||||
DbgPrint("NTFS: disk %u: media probe deferred; sector 0 unreadable\n", disk);
|
||||
NtfsEndDiskScan(disk, false);
|
||||
return 0;
|
||||
}
|
||||
scanCompleted = true;
|
||||
|
||||
uint32_t mounted = 0;
|
||||
for (uint32_t p = 0; p < partCount; p++) {
|
||||
int freeVolume = NtfsReserveFreeVolumeIndex();
|
||||
if (freeVolume < 0) break;
|
||||
uint8_t volumeIndex = (uint8_t)freeVolume;
|
||||
KSTATUS s = NtfsProbeAndPrepareVolume(&gVolumes[volumeIndex], disk, parts[p].StartLba, parts[p].SectorCount);
|
||||
if (!KSUCCESS(s)) {
|
||||
NtfsCleanupVolume(&gVolumes[volumeIndex]);
|
||||
memset(&gVolumes[volumeIndex], 0, sizeof(gVolumes[volumeIndex]));
|
||||
NtfsReleaseReservedVolumeIndex(volumeIndex);
|
||||
continue;
|
||||
}
|
||||
gVolumes[volumeIndex].VolumeIndex = volumeIndex;
|
||||
|
||||
NtfsInitVtable(&gFilesystems[volumeIndex], &gVolumes[volumeIndex]);
|
||||
IO_VOLUME_PARAMS params;
|
||||
params.DiskIndex = disk;
|
||||
params.PartitionLba = parts[p].StartLba;
|
||||
params.PartitionSize = gVolumes[volumeIndex].PartitionSize;
|
||||
s = IoRegisterFilesystem(volumeIndex, &gFilesystems[volumeIndex], ¶ms);
|
||||
if (KSUCCESS(s)) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&gNtfsMountLock);
|
||||
gVolumeMounted[volumeIndex] = true;
|
||||
gVolumeBusy[volumeIndex] = false;
|
||||
gMountedCount++;
|
||||
KeReleaseSpinlock(&gNtfsMountLock, flags);
|
||||
mounted++;
|
||||
DbgPrint("NTFS: disk %u part %u mounted as drive %u (lba=%llu mft_record=%llu).\n",
|
||||
disk, p, volumeIndex,
|
||||
(unsigned long long)parts[p].StartLba,
|
||||
(unsigned long long)gVolumes[volumeIndex].FileRecordSize);
|
||||
} else {
|
||||
NtfsCleanupVolume(&gVolumes[volumeIndex]);
|
||||
memset(&gVolumes[volumeIndex], 0, sizeof(gVolumes[volumeIndex]));
|
||||
memset(&gFilesystems[volumeIndex], 0, sizeof(gFilesystems[volumeIndex]));
|
||||
NtfsReleaseReservedVolumeIndex(volumeIndex);
|
||||
DbgPrint("NTFS: disk %u part %u mount failed status=0x%x.\n", disk, p, s);
|
||||
}
|
||||
}
|
||||
NtfsEndDiskScan(disk, scanCompleted);
|
||||
if (partCount && !mounted && gMountedCount >= NTFS_MAX_VOLUMES)
|
||||
DbgPrint("NTFS: disk %u: volume table full\n", disk);
|
||||
return mounted;
|
||||
}
|
||||
|
||||
static void NtfsUnmountDisk(uint8_t diskIndex, IO_DEVICE_CHANGE change) {
|
||||
uint32_t removed = 0;
|
||||
for (uint8_t i = 0; i < NTFS_MAX_VOLUMES; i++) {
|
||||
if (!gVolumeMounted[i] || gVolumes[i].DriveIndex != diskIndex) continue;
|
||||
(void)IoUnregisterFilesystem(i);
|
||||
NtfsCleanupVolume(&gVolumes[i]);
|
||||
memset(&gVolumes[i], 0, sizeof(gVolumes[i]));
|
||||
memset(&gFilesystems[i], 0, sizeof(gFilesystems[i]));
|
||||
uintptr_t flags = KeAcquireSpinlock(&gNtfsMountLock);
|
||||
gVolumeMounted[i] = false;
|
||||
gVolumeBusy[i] = false;
|
||||
if (gMountedCount) gMountedCount--;
|
||||
KeReleaseSpinlock(&gNtfsMountLock, flags);
|
||||
removed++;
|
||||
}
|
||||
if (diskIndex < NTFS_MAX_VOLUMES) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&gNtfsMountLock);
|
||||
gDiskScanned[diskIndex] = false;
|
||||
KeReleaseSpinlock(&gNtfsMountLock, flags);
|
||||
}
|
||||
if (removed) {
|
||||
DbgPrint("NTFS: disk %u %s; unmounted %u volume(s); total=%u\n",
|
||||
diskIndex,
|
||||
change == IoDeviceSurpriseRemoved ? "surprise removed" : "removed",
|
||||
removed,
|
||||
gMountedCount);
|
||||
}
|
||||
}
|
||||
|
||||
static void NtfsDiskArrival(uint8_t driveIndex, DEVICE_OBJECT* device, void* context) {
|
||||
(void)device;
|
||||
(void)context;
|
||||
uint32_t mounted = NtfsTryMountDisk(driveIndex);
|
||||
if (mounted) DbgPrint("NTFS: hotplug scan mounted %u new volume(s); total=%u\n", mounted, gMountedCount);
|
||||
}
|
||||
|
||||
static void NtfsDiskChange(uint8_t driveIndex, DEVICE_OBJECT* device, IO_DEVICE_CHANGE change, void* context) {
|
||||
(void)device;
|
||||
// Receives immutable partition extents from the I/O core; NTFS never parses partition tables.
|
||||
static void NtfsPartitionChange(const IO_PARTITION_INFO* partition,
|
||||
IO_PARTITION_CHANGE change,
|
||||
void* context) {
|
||||
(void)context;
|
||||
switch (change) {
|
||||
case IoDeviceArrived:
|
||||
NtfsDiskArrival(driveIndex, device, context);
|
||||
case IoPartitionArrived:
|
||||
(void)NtfsTryMountPartition(partition);
|
||||
break;
|
||||
case IoDeviceSurpriseRemoved:
|
||||
case IoDeviceRemoved:
|
||||
NtfsUnmountDisk(driveIndex, change);
|
||||
case IoPartitionSurpriseRemoved:
|
||||
case IoPartitionRemoved:
|
||||
NtfsUnmountPartition(partition, change);
|
||||
break;
|
||||
case IoDeviceQueryRemove:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Initializes NTFS and subscribes to the authoritative kernel partition stream.
|
||||
KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
DbgPrint("NTFS: Init\n");
|
||||
if (!ddk) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (!ddk)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
(void)init;
|
||||
memset(gVolumes, 0, sizeof(gVolumes));
|
||||
memset(gFilesystems, 0, sizeof(gFilesystems));
|
||||
memset(gVolumeMounted, 0, sizeof(gVolumeMounted));
|
||||
memset(gVolumeBusy, 0, sizeof(gVolumeBusy));
|
||||
memset(gDiskScanned, 0, sizeof(gDiskScanned));
|
||||
|
||||
LIST_INIT(&gNtfsMounts);
|
||||
gMountedCount = 0;
|
||||
NtfsMountEnsureMutex();
|
||||
|
||||
KSTATUS cbStatus = IoRegisterDiskChangeCallback(NtfsDiskChange, (void*)0);
|
||||
bool callbackRegistered = KSUCCESS(cbStatus);
|
||||
if (!callbackRegistered)
|
||||
DbgPrint("NTFS: disk-change callback registration failed st=0x%x\n", cbStatus);
|
||||
|
||||
KSTATUS enumStatus = IoEnumerateDiskDevices(NtfsDiskArrival, (void*)0);
|
||||
if (!KSUCCESS(enumStatus))
|
||||
DbgPrint("NTFS: initial disk enumeration failed st=0x%x\n", enumStatus);
|
||||
|
||||
if (!gMountedCount && !callbackRegistered) {
|
||||
DbgPrint("NTFS: no mountable NTFS volumes found and disk-change callback unavailable.\n");
|
||||
return cbStatus;
|
||||
KSTATUS status = IoRegisterPartitionChangeCallback(NtfsPartitionChange, NULL);
|
||||
if (!KSUCCESS(status)) {
|
||||
DbgPrint("NTFS: partition callback registration failed status=0x%x\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!gMountedCount)
|
||||
DbgPrint("NTFS: no mountable NTFS volumes found; driver resident for disk changes.\n");
|
||||
|
||||
DbgPrint("NTFS: no mountable NTFS partitions found; driver resident for partition changes.\n");
|
||||
DbgPrint("NTFS: Init complete; mounted %u volume(s).\n", gMountedCount);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
#define SERIAL_REG_LCR 3u
|
||||
#define SERIAL_REG_MCR 4u
|
||||
#define SERIAL_REG_LSR 5u
|
||||
#define SERIAL_REG_SCR 7u
|
||||
|
||||
#define SERIAL_LSR_DATA_READY 0x01u
|
||||
#define SERIAL_LSR_TX_EMPTY 0x20u
|
||||
@@ -37,6 +38,7 @@
|
||||
#define SERIAL_FCR_TRIGGER_14 0xC0u
|
||||
|
||||
#define SERIAL_RX_QUEUE_SIZE 1024u
|
||||
#define SERIAL_RX_DRAIN_LIMIT 256u
|
||||
#define SERIAL_RX_QUEUE_MASK (SERIAL_RX_QUEUE_SIZE - 1u)
|
||||
|
||||
typedef enum SERIAL_ESCAPE_STATE {
|
||||
@@ -65,6 +67,19 @@ static void SerialWrite(uint16_t reg, uint8_t value) {
|
||||
__outbyte((unsigned short)(SERIAL_COM1_BASE + reg), value);
|
||||
}
|
||||
|
||||
// Probes the 16550 scratch register so an absent COM1 device is rejected before any receive loop is entered.
|
||||
static bool SerialProbeCom1(void) {
|
||||
uint8_t original = SerialRead(SERIAL_REG_SCR);
|
||||
|
||||
SerialWrite(SERIAL_REG_SCR, 0x5Au);
|
||||
uint8_t first = SerialRead(SERIAL_REG_SCR);
|
||||
SerialWrite(SERIAL_REG_SCR, 0xA5u);
|
||||
uint8_t second = SerialRead(SERIAL_REG_SCR);
|
||||
SerialWrite(SERIAL_REG_SCR, original);
|
||||
|
||||
return first == 0x5Au && second == 0xA5u;
|
||||
}
|
||||
|
||||
// Submits one input key transition.
|
||||
static void SerialSubmitTransition(INPUT_KEY key, bool pressed) {
|
||||
INPUT_KEY_EVENT event;
|
||||
@@ -301,8 +316,8 @@ static void SerialIsr(void) {
|
||||
}
|
||||
}
|
||||
|
||||
// Initializes COM1 as an 8N1 115200 baud input device.
|
||||
static void SerialInitializeCom1(void) {
|
||||
// Configures COM1 as an 8N1 115200 baud input device while leaving receive interrupts disabled.
|
||||
static KSTATUS SerialConfigureCom1(void) {
|
||||
SerialWrite(SERIAL_REG_IER, 0x00u);
|
||||
SerialWrite(SERIAL_REG_LCR, SERIAL_LCR_DLAB);
|
||||
SerialWrite(SERIAL_REG_DATA, 0x01u);
|
||||
@@ -312,10 +327,16 @@ static void SerialInitializeCom1(void) {
|
||||
SERIAL_FCR_CLEAR_TX | SERIAL_FCR_TRIGGER_14);
|
||||
SerialWrite(SERIAL_REG_MCR, SERIAL_MCR_DTR | SERIAL_MCR_RTS | SERIAL_MCR_OUT2);
|
||||
|
||||
while (SerialRead(SERIAL_REG_LSR) & SERIAL_LSR_DATA_READY)
|
||||
for (uint32_t i = 0; i < SERIAL_RX_DRAIN_LIMIT; i++) {
|
||||
uint8_t lsr = SerialRead(SERIAL_REG_LSR);
|
||||
if (lsr == 0xFFu)
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
if ((lsr & SERIAL_LSR_DATA_READY) == 0)
|
||||
return NTLSTATUS_SUCCESS;
|
||||
(void)SerialRead(SERIAL_REG_DATA);
|
||||
}
|
||||
|
||||
SerialWrite(SERIAL_REG_IER, SERIAL_IER_RX_READY);
|
||||
return NTLSTATUS_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
// DriverEntry registers COM1 as an input source.
|
||||
@@ -323,8 +344,19 @@ KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
(void)ddk;
|
||||
(void)init;
|
||||
|
||||
if (!SerialProbeCom1()) {
|
||||
DbgPrint("serial: COM1 not present; input driver disabled\n");
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
KSTATUS st = SerialConfigureCom1();
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("serial: COM1 did not become idle during initialization, status=0x%08X\n", st);
|
||||
return st;
|
||||
}
|
||||
|
||||
uint32_t deviceId = 0;
|
||||
KSTATUS st = KeInputRegisterDevice("serial-com1", 0, &deviceId);
|
||||
st = KeInputRegisterDevice("serial-com1", 0, &deviceId);
|
||||
if (!KSUCCESS(st))
|
||||
return st;
|
||||
|
||||
@@ -336,8 +368,8 @@ KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) {
|
||||
gSerialRxTail = 0;
|
||||
gSerialRxDropped = 0;
|
||||
|
||||
SerialInitializeCom1();
|
||||
KeIrqRegister(SERIAL_COM1_IRQ, SerialIsr);
|
||||
SerialWrite(SERIAL_REG_IER, SERIAL_IER_RX_READY);
|
||||
|
||||
DbgPrint("serial: initialized COM1 input device=%u irq=%u\n", deviceId, SERIAL_COM1_IRQ);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
|
||||
@@ -349,10 +349,10 @@ sys_thread_t sys_thread_new(const char* name, lwip_thread_fn thread, void* arg,
|
||||
}
|
||||
|
||||
sys_prot_t sys_arch_protect(void) {
|
||||
return (sys_prot_t)HalDisableInterrupts();
|
||||
return (sys_prot_t)KeDisableInterrupts();
|
||||
}
|
||||
|
||||
// Restores the interrupt state saved by sys_arch_protect.
|
||||
void sys_arch_unprotect(sys_prot_t pval) {
|
||||
HalRestoreInterrupts((uintptr_t)pval);
|
||||
KeRestoreInterrupts((uintptr_t)pval);
|
||||
}
|
||||
@@ -29,28 +29,28 @@ static KSTATUS AhciStartController(AHCI_CONTROLLER* ctl);
|
||||
static void AhciUnregisterController(AHCI_CONTROLLER* ctl);
|
||||
|
||||
// Accessors for port structure memory at known CPU and device-address offsets.
|
||||
static inline HBA_CMD_HEADER *cmd_list(AHCI_DRIVE *drv) {
|
||||
return (HBA_CMD_HEADER *)((uint8_t *)drv->structVirt + PORT_OFF_CMDLIST);
|
||||
static inline HBA_CMD_HEADER* cmd_list(AHCI_DRIVE* drv) {
|
||||
return (HBA_CMD_HEADER*)((uint8_t*)drv->structVirt + PORT_OFF_CMDLIST);
|
||||
}
|
||||
|
||||
// Returns the command-list device address.
|
||||
static inline uint64_t cmd_list_device_address(AHCI_DRIVE *drv) {
|
||||
static inline uint64_t cmd_list_device_address(AHCI_DRIVE* drv) {
|
||||
return drv->structDeviceAddress + PORT_OFF_CMDLIST;
|
||||
}
|
||||
|
||||
// Returns the received-FIS device address.
|
||||
static inline uint64_t fis_device_address(AHCI_DRIVE *drv) {
|
||||
static inline uint64_t fis_device_address(AHCI_DRIVE* drv) {
|
||||
return drv->structDeviceAddress + PORT_OFF_FIS;
|
||||
}
|
||||
|
||||
static inline HBA_CMD_TBL *cmd_tbl(AHCI_DRIVE *drv, int slot) {
|
||||
return (HBA_CMD_TBL *)((uint8_t *)drv->structVirt
|
||||
static inline HBA_CMD_TBL* cmd_tbl(AHCI_DRIVE* drv, int slot) {
|
||||
return (HBA_CMD_TBL*)((uint8_t*)drv->structVirt
|
||||
+ PORT_OFF_CMDTBLS
|
||||
+ (uint32_t)slot * PORT_CMDTBL_SIZE);
|
||||
}
|
||||
|
||||
// Returns one command-table device address.
|
||||
static inline uint64_t cmd_tbl_device_address(AHCI_DRIVE *drv, int slot) {
|
||||
static inline uint64_t cmd_tbl_device_address(AHCI_DRIVE* drv, int slot) {
|
||||
return drv->structDeviceAddress + PORT_OFF_CMDTBLS
|
||||
+ (uint64_t)slot * PORT_CMDTBL_SIZE;
|
||||
}
|
||||
@@ -118,7 +118,7 @@ static void AhciReleaseDriveDma(AHCI_DRIVE* drv) {
|
||||
}
|
||||
|
||||
// Allocates and programs one port's command, FIS, and bounce buffers.
|
||||
static KSTATUS port_init_memory(AHCI_DRIVE *drv) {
|
||||
static KSTATUS port_init_memory(AHCI_DRIVE* drv) {
|
||||
if (!drv || !drv->controller || !drv->controller->dmaAdapter)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
@@ -142,7 +142,7 @@ static KSTATUS port_init_memory(AHCI_DRIVE *drv) {
|
||||
drv->bounceVirt = gAhciDdk->Dma->GetCommonBufferAddress(drv->bounceDma);
|
||||
drv->bounceDeviceAddress = gAhciDdk->Dma->GetCommonBufferDeviceAddress(drv->bounceDma);
|
||||
|
||||
HBA_PORT *port = drv->port;
|
||||
HBA_PORT* port = drv->port;
|
||||
port_stop(port);
|
||||
|
||||
port->clb = (uint32_t)(cmd_list_device_address(drv) & 0xFFFFFFFF);
|
||||
@@ -150,7 +150,7 @@ static KSTATUS port_init_memory(AHCI_DRIVE *drv) {
|
||||
port->fb = (uint32_t)(fis_device_address(drv) & 0xFFFFFFFF);
|
||||
port->fbu = (uint32_t)(fis_device_address(drv) >> 32);
|
||||
|
||||
HBA_CMD_HEADER *hdrs = cmd_list(drv);
|
||||
HBA_CMD_HEADER* hdrs = cmd_list(drv);
|
||||
for (int i = 0; i < NUM_CMD_SLOTS; i++) {
|
||||
hdrs[i].prdtl = PORT_PRDT_COUNT;
|
||||
uint64_t table = cmd_tbl_device_address(drv, i);
|
||||
@@ -168,8 +168,8 @@ static KSTATUS port_init_memory(AHCI_DRIVE *drv) {
|
||||
// before the scheduler and interrupt handler are live).
|
||||
// Returns NTLSTATUS_SUCCESS, NTLSTATUS_DEVICE_ERROR, or NTLSTATUS_TIMEOUT.
|
||||
|
||||
static KSTATUS port_issue_cmd_poll(AHCI_DRIVE *drv) {
|
||||
HBA_PORT *port = drv->port;
|
||||
static KSTATUS port_issue_cmd_poll(AHCI_DRIVE* drv) {
|
||||
HBA_PORT* port = drv->port;
|
||||
|
||||
// Clear pending interrupts before issuing.
|
||||
port->is = (uint32_t)-1;
|
||||
@@ -214,11 +214,11 @@ static KSTATUS port_issue_cmd_poll(AHCI_DRIVE *drv) {
|
||||
}
|
||||
|
||||
// Recovers a port after a command that cannot be trusted to have retired.
|
||||
static void AhciRecoverPort(AHCI_DRIVE *drv) {
|
||||
static void AhciRecoverPort(AHCI_DRIVE* drv) {
|
||||
if (!drv || !drv->port)
|
||||
return;
|
||||
|
||||
HBA_PORT *port = drv->port;
|
||||
HBA_PORT* port = drv->port;
|
||||
uint32_t savedIe = port->ie;
|
||||
|
||||
// Keep reset-generated FIS traffic from satisfying a command waiter.
|
||||
@@ -240,13 +240,13 @@ static void AhciRecoverPort(AHCI_DRIVE *drv) {
|
||||
// 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,
|
||||
void *systemArgument1, void *systemArgument2) {
|
||||
static void AhciCompletionDpc(KDPC* dpc, void* context,
|
||||
void* systemArgument1, void* systemArgument2) {
|
||||
(void)dpc;
|
||||
(void)systemArgument1;
|
||||
(void)systemArgument2;
|
||||
|
||||
AHCI_DRIVE *drv = (AHCI_DRIVE *)context;
|
||||
AHCI_DRIVE* drv = (AHCI_DRIVE*)context;
|
||||
if (drv && drv->completionEvent)
|
||||
KeSignalEvent(drv->completionEvent);
|
||||
}
|
||||
@@ -267,7 +267,7 @@ static void AhciIsr(void) {
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < ctl->driveCount; i++) {
|
||||
AHCI_DRIVE *drv = &ctl->drives[i];
|
||||
AHCI_DRIVE* drv = &ctl->drives[i];
|
||||
uint32_t portBit = (1u << drv->portNo);
|
||||
if (!(pending & portBit)) continue;
|
||||
|
||||
@@ -286,7 +286,7 @@ static void AhciIsr(void) {
|
||||
// command completion. Only wake the worker for D2H completion or
|
||||
// an error that terminates the command.
|
||||
if (portIs & AHCI_PORT_TERMINAL_MASK)
|
||||
KeInsertQueueDpc(&drv->completionDpc, (void *)0, (void *)0);
|
||||
KeInsertQueueDpc(&drv->completionDpc, (void*)0, (void*)0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -295,11 +295,11 @@ static void AhciIsr(void) {
|
||||
// Caller has already built slot 0 via build_rw_cmd / port_identify.
|
||||
// Blocks the current thread until the ISR signals completionEvent.
|
||||
|
||||
static KSTATUS port_issue_cmd(AHCI_DRIVE *drv) {
|
||||
static KSTATUS port_issue_cmd(AHCI_DRIVE* drv) {
|
||||
if (drv->controller && drv->controller->pollingMode)
|
||||
return port_issue_cmd_poll(drv);
|
||||
|
||||
HBA_PORT *port = drv->port;
|
||||
HBA_PORT* port = drv->port;
|
||||
|
||||
// A serialized slot must be idle before reuse. A stale active bit means a
|
||||
// prior command escaped normal completion and must be recovered first.
|
||||
@@ -330,10 +330,10 @@ static KSTATUS port_issue_cmd(AHCI_DRIVE *drv) {
|
||||
|
||||
// Issue slot 0, then block with a finite deadline.
|
||||
port->ci = (1u << 0);
|
||||
EVENT *events[1] = { drv->completionEvent };
|
||||
EVENT* events[1] = { drv->completionEvent };
|
||||
KSTATUS waitStatus = KeWaitForMultipleEvents(events, 1, 0,
|
||||
AHCI_COMMAND_TIMEOUT_MS,
|
||||
(uint32_t *)0);
|
||||
(uint32_t*)0);
|
||||
|
||||
// Merge any live status that was not consumed by the ISR. This lets a
|
||||
// missed IRQ still report the hardware error that accompanied it.
|
||||
@@ -378,10 +378,10 @@ static KSTATUS port_issue_cmd(AHCI_DRIVE *drv) {
|
||||
|
||||
// Build a standard DMA read or write command in slot 0.
|
||||
|
||||
static void build_rw_cmd(AHCI_DRIVE *drv, uint64_t lba, uint32_t sectors,
|
||||
static void build_rw_cmd(AHCI_DRIVE* drv, uint64_t lba, uint32_t sectors,
|
||||
uint64_t bufferDeviceAddress, bool write) {
|
||||
HBA_CMD_HEADER *hdr = cmd_list(drv); // slot 0
|
||||
HBA_CMD_TBL *tbl = cmd_tbl(drv, 0);
|
||||
HBA_CMD_HEADER* hdr = cmd_list(drv); // slot 0
|
||||
HBA_CMD_TBL* tbl = cmd_tbl(drv, 0);
|
||||
|
||||
memset(tbl, 0, PORT_CMDTBL_SIZE);
|
||||
|
||||
@@ -411,7 +411,7 @@ static void build_rw_cmd(AHCI_DRIVE *drv, uint64_t lba, uint32_t sectors,
|
||||
}
|
||||
|
||||
// Build H2D FIS.
|
||||
FIS_REG_H2D *fis = (FIS_REG_H2D *)tbl->cfis;
|
||||
FIS_REG_H2D* fis = (FIS_REG_H2D*)tbl->cfis;
|
||||
memset(fis, 0, sizeof(*fis));
|
||||
fis->fis_type = FIS_TYPE_REG_H2D;
|
||||
fis->c = 1;
|
||||
@@ -456,12 +456,12 @@ static KSTATUS do_flush(AHCI_DRIVE* drv) {
|
||||
|
||||
// IDENTIFY command - fills a 512-byte buffer and returns it.
|
||||
|
||||
static KSTATUS port_identify(AHCI_DRIVE *drv) {
|
||||
static KSTATUS port_identify(AHCI_DRIVE* drv) {
|
||||
// Reuse bounce buffer for IDENTIFY data (512 bytes).
|
||||
memset(drv->bounceVirt, 0, 512);
|
||||
|
||||
HBA_CMD_HEADER *hdr = cmd_list(drv);
|
||||
HBA_CMD_TBL *tbl = cmd_tbl(drv, 0);
|
||||
HBA_CMD_HEADER* hdr = cmd_list(drv);
|
||||
HBA_CMD_TBL* tbl = cmd_tbl(drv, 0);
|
||||
|
||||
memset(tbl, 0, PORT_CMDTBL_SIZE);
|
||||
|
||||
@@ -475,7 +475,7 @@ static KSTATUS port_identify(AHCI_DRIVE *drv) {
|
||||
tbl->prdt_entry[0].dbc = 512 - 1;
|
||||
tbl->prdt_entry[0].i = 1;
|
||||
|
||||
FIS_REG_H2D *fis = (FIS_REG_H2D *)tbl->cfis;
|
||||
FIS_REG_H2D* fis = (FIS_REG_H2D*)tbl->cfis;
|
||||
memset(fis, 0, sizeof(*fis));
|
||||
fis->fis_type = FIS_TYPE_REG_H2D;
|
||||
fis->c = 1;
|
||||
@@ -487,7 +487,7 @@ static KSTATUS port_identify(AHCI_DRIVE *drv) {
|
||||
if (!KSUCCESS(st)) return st;
|
||||
|
||||
// Parse sector count from IDENTIFY data.
|
||||
const uint16_t *id = (const uint16_t *)drv->bounceVirt;
|
||||
const uint16_t* id = (const uint16_t*)drv->bounceVirt;
|
||||
|
||||
if (id[ATA_IDENT_W83] & (1u << 10)) {
|
||||
// 48-bit LBA supported.
|
||||
@@ -505,9 +505,9 @@ static KSTATUS port_identify(AHCI_DRIVE *drv) {
|
||||
}
|
||||
|
||||
// Core read logic.
|
||||
static KSTATUS do_read(AHCI_DRIVE *drv, uint64_t lba,
|
||||
uint32_t count, void *buf) {
|
||||
uint8_t *out = (uint8_t *)buf;
|
||||
static KSTATUS do_read(AHCI_DRIVE* drv, uint64_t lba,
|
||||
uint32_t count, void* buf) {
|
||||
uint8_t* out = (uint8_t*)buf;
|
||||
|
||||
while (count > 0) {
|
||||
uint32_t batch = (count > BOUNCE_SECTORS) ? BOUNCE_SECTORS : count;
|
||||
@@ -525,9 +525,9 @@ static KSTATUS do_read(AHCI_DRIVE *drv, uint64_t lba,
|
||||
}
|
||||
|
||||
// Core write logic.
|
||||
static KSTATUS do_write(AHCI_DRIVE *drv, uint64_t lba,
|
||||
uint32_t count, const void *buf) {
|
||||
const uint8_t *in = (const uint8_t *)buf;
|
||||
static KSTATUS do_write(AHCI_DRIVE* drv, uint64_t lba,
|
||||
uint32_t count, const void* buf) {
|
||||
const uint8_t* in = (const uint8_t*)buf;
|
||||
|
||||
while (count > 0) {
|
||||
uint32_t batch = (count > BOUNCE_SECTORS) ? BOUNCE_SECTORS : count;
|
||||
@@ -571,9 +571,9 @@ static KSTATUS AhciGetIrpReadWrite(IRP* irp, bool* outWrite, uint64_t* outLba,
|
||||
// Accepted IRPs are queued to the per-drive worker and completed later with
|
||||
// IoCompleteRequest(). Rejected IRPs are returned synchronously and are not
|
||||
// completed.
|
||||
static KSTATUS AhciDispatchReadWrite(DEVICE_OBJECT *device, IRP *irp) {
|
||||
static KSTATUS AhciDispatchReadWrite(DEVICE_OBJECT* device, IRP* irp) {
|
||||
if (!device || !irp) return NTLSTATUS_INVALID_PARAMETER;
|
||||
AHCI_DRIVE *drv = (AHCI_DRIVE *)device->DeviceExtension;
|
||||
AHCI_DRIVE* drv = (AHCI_DRIVE*)device->DeviceExtension;
|
||||
if (!drv) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
KSTATUS st = IoAcquireRemoveLock(&drv->removeLock, irp);
|
||||
@@ -762,7 +762,7 @@ static KSTATUS AhciDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
|
||||
// record slotIrp[slot] = irp, and issue via port->sact + port->ci.
|
||||
// 5. Return immediately so the ISR can drive all completions.
|
||||
static void AhciWorker(uintptr_t arg) {
|
||||
AHCI_DRIVE *drv = (AHCI_DRIVE *)arg;
|
||||
AHCI_DRIVE* drv = (AHCI_DRIVE*)arg;
|
||||
|
||||
while (drv && drv->workerRunning) {
|
||||
// Block until AHCI dispatch signals a new IRP.
|
||||
@@ -770,7 +770,7 @@ static void AhciWorker(uintptr_t arg) {
|
||||
if (!drv->workerRunning)
|
||||
break;
|
||||
|
||||
IRP *irp = IrpQueueDequeue(&drv->irpQueue);
|
||||
IRP* irp = IrpQueueDequeue(&drv->irpQueue);
|
||||
if (!irp)
|
||||
continue;
|
||||
|
||||
@@ -954,11 +954,17 @@ static KSTATUS AhciStartController(AHCI_CONTROLLER* ctl) {
|
||||
return st;
|
||||
}
|
||||
|
||||
// IoRegisterDiskDevice publishes arrival synchronously, and partition/filesystem
|
||||
// consumers may issue reads from that callback. Install the controller ISR before
|
||||
// any disk is published so an interrupt-driven drive can complete those reads.
|
||||
if (!ctl->pollingMode)
|
||||
KeIrqRegister(ctl->irq, AhciIsr);
|
||||
|
||||
uint32_t pi = ctl->hba->pi;
|
||||
for (uint32_t portno = 0; portno < 32 && ctl->driveCount < MAX_AHCI_DRIVES; portno++) {
|
||||
if (!(pi & (1u << portno))) continue;
|
||||
|
||||
HBA_PORT *port = &ctl->hba->ports[portno];
|
||||
HBA_PORT* port = &ctl->hba->ports[portno];
|
||||
|
||||
// Phase 1: physical link check.
|
||||
if (!port_link_up(port)) {
|
||||
@@ -969,7 +975,7 @@ static KSTATUS AhciStartController(AHCI_CONTROLLER* ctl) {
|
||||
DbgPrint("AHCI: port %u: link up, initializing...\n", portno);
|
||||
|
||||
int driveIdx = ctl->driveCount;
|
||||
AHCI_DRIVE *drv = &ctl->drives[driveIdx];
|
||||
AHCI_DRIVE* drv = &ctl->drives[driveIdx];
|
||||
memset(drv, 0, sizeof(*drv));
|
||||
drv->magic = AHCI_DRIVE_MAGIC;
|
||||
drv->controller = ctl;
|
||||
@@ -1013,7 +1019,7 @@ static KSTATUS AhciStartController(AHCI_CONTROLLER* ctl) {
|
||||
|
||||
// Phase 5b: create the IRP queue semaphore and initialise the queue.
|
||||
// The semaphore starts at 0; each submitted IRP increments it by one.
|
||||
SEMAPHORE *irpSem = (SEMAPHORE *)0;
|
||||
SEMAPHORE* irpSem = (SEMAPHORE*)0;
|
||||
st = KeSemaphoreCreate(0, 0x7FFFFFFF, &irpSem);
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("AHCI: port %u: semaphore create failed (0x%x)\n", portno, st);
|
||||
@@ -1031,7 +1037,7 @@ static KSTATUS AhciStartController(AHCI_CONTROLLER* ctl) {
|
||||
}
|
||||
drv->removeLockReady = true;
|
||||
|
||||
// Phase 6: IDENTIFY (polled - scheduler not yet running).
|
||||
// Phase 6: IDENTIFY remains polled until the disk runtime path is ready.
|
||||
st = port_identify(drv);
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("AHCI: port %u: IDENTIFY failed (0x%x)\n", portno, st);
|
||||
@@ -1045,12 +1051,8 @@ static KSTATUS AhciStartController(AHCI_CONTROLLER* ctl) {
|
||||
(unsigned long long)drv->sectorCount,
|
||||
(unsigned long long)(drv->sectorCount / 2048));
|
||||
|
||||
// Phase 7: enable per-port interrupts.
|
||||
// Clear any stale IS first so we don't get a spurious signal immediately.
|
||||
port->is = (uint32_t)-1;
|
||||
port->ie = AHCI_PORT_TERMINAL_MASK;
|
||||
|
||||
// Phase 8: create and register a disk DEVICE_OBJECT.
|
||||
// Phase 7: create the disk DEVICE_OBJECT, but do not publish it until the
|
||||
// asynchronous runtime path is fully operational.
|
||||
st = IoCreateDevice(&g_driver, 0, &drv->deviceObject);
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("AHCI: drive %d: IoCreateDevice failed (0x%x)\n", g_nextDiskIndex, st);
|
||||
@@ -1063,24 +1065,9 @@ static KSTATUS AhciStartController(AHCI_CONTROLLER* ctl) {
|
||||
drv->deviceObject->u.Disk.SectorSize = 512;
|
||||
drv->deviceObject->AlignmentRequirement = 512;
|
||||
|
||||
uint8_t diskIndex = g_nextDiskIndex;
|
||||
st = IoRegisterDiskDevice(diskIndex, drv->deviceObject);
|
||||
if (KSUCCESS(st)) {
|
||||
DbgPrint("AHCI: drive %u registered\n", diskIndex);
|
||||
ctl->driveCount++;
|
||||
g_nextDiskIndex++;
|
||||
} else {
|
||||
DbgPrint("AHCI: IoRegisterDiskDevice(%u) failed (0x%x)\n", diskIndex, st);
|
||||
IoDeleteDevice(drv->deviceObject);
|
||||
drv->deviceObject = (DEVICE_OBJECT*)0;
|
||||
AhciReleaseDriveRuntime(drv);
|
||||
AhciReleaseDriveDma(drv);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Phase 9: spawn the per-drive IRP worker thread.
|
||||
// The worker owns do_read / do_write exclusively once the scheduler
|
||||
// is live; it is the only thread that may call port_issue_cmd().
|
||||
// Phase 8: start the per-drive IRP worker before publishing the disk.
|
||||
// IoRegisterDiskDevice synchronously notifies partition consumers, which may
|
||||
// immediately submit reads that must have a live worker and completion path.
|
||||
st = KeEventCreate(EventManualReset, 0, &drv->workerFinished);
|
||||
if (KSUCCESS(st)) {
|
||||
drv->workerRunning = true;
|
||||
@@ -1092,9 +1079,34 @@ static KSTATUS AhciStartController(AHCI_CONTROLLER* ctl) {
|
||||
}
|
||||
if (!KSUCCESS(st)) {
|
||||
drv->workerRunning = false;
|
||||
DbgPrint("AHCI: drive %u: worker creation failed (0x%x)\n", diskIndex, st);
|
||||
(void)IoUnregisterDiskDevice(diskIndex);
|
||||
DbgPrint("AHCI: drive %u: worker creation failed (0x%x)\n", g_nextDiskIndex, st);
|
||||
IoDeleteDevice(drv->deviceObject);
|
||||
drv->deviceObject = (DEVICE_OBJECT*)0;
|
||||
AhciReleaseDriveRuntime(drv);
|
||||
AhciReleaseDriveDma(drv);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make the initialized drive visible to the controller ISR, then arm its
|
||||
// interrupt source. No consumer can submit I/O until publication below.
|
||||
ctl->driveCount++;
|
||||
port->is = (uint32_t)-1;
|
||||
port->ie = AHCI_PORT_TERMINAL_MASK;
|
||||
|
||||
// Phase 9: publish only after reads and writes can complete synchronously.
|
||||
uint8_t diskIndex = g_nextDiskIndex;
|
||||
st = IoRegisterDiskDevice(diskIndex, drv->deviceObject);
|
||||
if (KSUCCESS(st)) {
|
||||
DbgPrint("AHCI: drive %u registered\n", diskIndex);
|
||||
g_nextDiskIndex++;
|
||||
} else {
|
||||
DbgPrint("AHCI: IoRegisterDiskDevice(%u) failed (0x%x)\n", diskIndex, st);
|
||||
port->ie = 0;
|
||||
ctl->driveCount--;
|
||||
IoDeleteDevice(drv->deviceObject);
|
||||
drv->deviceObject = (DEVICE_OBJECT*)0;
|
||||
AhciReleaseDriveRuntime(drv);
|
||||
AhciReleaseDriveDma(drv);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -1106,7 +1118,6 @@ static KSTATUS AhciStartController(AHCI_CONTROLLER* ctl) {
|
||||
ctl->pciInfo.Address.Function);
|
||||
|
||||
if (!ctl->pollingMode) {
|
||||
KeIrqRegister(ctl->irq, AhciIsr);
|
||||
DbgPrint("AHCI: ISR registered on IRQ %u (%s); controller drives=%d\n",
|
||||
(unsigned)ctl->irq, ctl->usingMsi ? "MSI" : "INTx", ctl->driveCount);
|
||||
} else {
|
||||
|
||||
@@ -317,7 +317,7 @@ typedef struct AHCI_CONTROLLER AHCI_CONTROLLER;
|
||||
typedef struct {
|
||||
uint32_t magic; // AHCI_DRIVE_MAGIC
|
||||
AHCI_CONTROLLER* controller; // owning AHCI controller FDO extension
|
||||
HBA_PORT *port; // MMIO port registers (virtual)
|
||||
HBA_PORT* port; // MMIO port registers (virtual)
|
||||
uint64_t sectorCount; // Total LBA sectors on device
|
||||
uint8_t portNo; // Physical port index (for ISR dispatch)
|
||||
|
||||
@@ -333,7 +333,7 @@ typedef struct {
|
||||
|
||||
// Signaled by the completion DPC when the port raises an interrupt.
|
||||
// Auto-reset: one Wait consumes one completion.
|
||||
EVENT *completionEvent;
|
||||
EVENT* completionEvent;
|
||||
|
||||
// Last IS snapshot captured by the ISR (for error checking post-wait).
|
||||
volatile uint32_t lastIs;
|
||||
@@ -389,10 +389,10 @@ extern const NTL_DDK* gAhciDdk;
|
||||
extern DRIVER_OBJECT g_driver;
|
||||
|
||||
KSTATUS ahci_map_mmio(uint64_t physBase, uint64_t length);
|
||||
void port_stop(HBA_PORT *port);
|
||||
void port_start(HBA_PORT *port);
|
||||
KSTATUS hba_reset(HBA_MEM *hba);
|
||||
bool port_link_up(HBA_PORT *port);
|
||||
bool port_is_sata_disk(HBA_PORT *port);
|
||||
void port_stop(HBA_PORT* port);
|
||||
void port_start(HBA_PORT* port);
|
||||
KSTATUS hba_reset(HBA_MEM* hba);
|
||||
bool port_link_up(HBA_PORT* port);
|
||||
bool port_is_sata_disk(HBA_PORT* port);
|
||||
void ahci_delay_ms(uint32_t ms);
|
||||
KSTATUS port_comreset(AHCI_DRIVE *drv);
|
||||
KSTATUS port_comreset(AHCI_DRIVE* drv);
|
||||
@@ -26,7 +26,7 @@ KSTATUS ahci_map_mmio(uint64_t physBase, uint64_t length) {
|
||||
}
|
||||
|
||||
// Implements the port stop operation.
|
||||
void port_stop(HBA_PORT *port) {
|
||||
void port_stop(HBA_PORT* port) {
|
||||
// Clear ST (bit 0).
|
||||
port->cmd &= ~PORT_CMD_ST;
|
||||
// Wait for CR (bit 15) to clear (command list DMA stops).
|
||||
@@ -42,7 +42,7 @@ void port_stop(HBA_PORT *port) {
|
||||
}
|
||||
|
||||
// Implements the port start operation.
|
||||
void port_start(HBA_PORT *port) {
|
||||
void port_start(HBA_PORT* port) {
|
||||
// Wait until CR is clear before setting ST.
|
||||
for (int i = 0; i < POLL_TIMEOUT; i++) {
|
||||
if (!(port->cmd & PORT_CMD_CR)) break;
|
||||
@@ -52,7 +52,7 @@ void port_start(HBA_PORT *port) {
|
||||
}
|
||||
|
||||
// Implements the hba reset operation.
|
||||
KSTATUS hba_reset(HBA_MEM *hba) {
|
||||
KSTATUS hba_reset(HBA_MEM* hba) {
|
||||
// BIOS/OS handoff (spec §10.6.3).
|
||||
if (hba->cap2 & CAP2_BOH) {
|
||||
hba->bohc |= BOHC_OOS;
|
||||
@@ -86,14 +86,14 @@ KSTATUS hba_reset(HBA_MEM *hba) {
|
||||
}
|
||||
|
||||
// Implements the port link up operation.
|
||||
bool port_link_up(HBA_PORT *port) {
|
||||
bool port_link_up(HBA_PORT* port) {
|
||||
uint32_t det = port->ssts & 0x0F;
|
||||
uint32_t ipm = (port->ssts >> 8) & 0x0F;
|
||||
return (det == PORT_DET_PRESENT && ipm == PORT_IPM_ACTIVE);
|
||||
}
|
||||
|
||||
// Implements the port is sata disk operation.
|
||||
bool port_is_sata_disk(HBA_PORT *port) {
|
||||
bool port_is_sata_disk(HBA_PORT* port) {
|
||||
return port->sig == SATA_SIG_ATA;
|
||||
}
|
||||
|
||||
@@ -104,8 +104,8 @@ void ahci_delay_ms(uint32_t ms) {
|
||||
}
|
||||
|
||||
// Implements the port comreset operation.
|
||||
KSTATUS port_comreset(AHCI_DRIVE *drv) {
|
||||
HBA_PORT *port = drv->port;
|
||||
KSTATUS port_comreset(AHCI_DRIVE* drv) {
|
||||
HBA_PORT* port = drv->port;
|
||||
|
||||
port->cmd &= ~PORT_CMD_ST;
|
||||
for (int i = 0; i < POLL_TIMEOUT; i++)
|
||||
|
||||
Reference in new issue
Block a user