i think it works
This commit is contained in:
commit
0e1479b168
167 files changed
+27008
No files matched your search
@@ -0,0 +1,159 @@
|
||||
// ahci.c - AHCI SATA driver (basic PRDT, 1 slot, port 0)
|
||||
#include <io_driver.h>
|
||||
#include <hal_api.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ke_debug.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
|
||||
// AHCI generic host control registers (BAR5 offsets).
|
||||
#define AHCI_GHC 0x04 // Global Host Control
|
||||
#define AHCI_PI 0x0C // Ports Implemented
|
||||
#define AHCI_PORT_BASE(p) (0x100 + (p) * 0x80)
|
||||
|
||||
// Port registers (relative to port base).
|
||||
#define P_CLB 0x00 // Command List Base Address
|
||||
#define P_FB 0x08 // FIS Base Address
|
||||
#define P_IS 0x10 // Interrupt Status
|
||||
#define P_CMD 0x18 // Command and Status
|
||||
#define P_TFD 0x20 // Task File Data
|
||||
#define P_SIG 0x24 // Signature
|
||||
#define P_SSTS 0x28 // SATA Status
|
||||
#define P_CI 0x38 // Command Issue
|
||||
|
||||
#define CMD_ST (1 << 0)
|
||||
#define CMD_FRE (1 << 4)
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct { uint8_t cfl:5,a:1,w:1,p:1; uint8_t pmport:4,r:1,b:1,c:1,rsvd:1; uint16_t prdtl;
|
||||
uint32_t prdbc; uint64_t ctba; uint32_t rsvd2[4]; } HBA_CMD_HEADER;
|
||||
typedef struct { uint64_t dba; uint32_t rsvd; uint32_t dbc:22, rsvd2:9, i:1; } HBA_PRDT;
|
||||
typedef struct { uint8_t cfis[64]; uint8_t acmd[16]; uint8_t rsvd[48]; HBA_PRDT prdt[1]; } HBA_CMD_TABLE;
|
||||
#pragma pack(pop)
|
||||
|
||||
static const HAL_API* gHal;
|
||||
static volatile uint32_t* gAhci = (volatile uint32_t *)0;
|
||||
static HBA_CMD_HEADER* gCmdList = (HBA_CMD_HEADER *)0;
|
||||
static HBA_CMD_TABLE* gCmdTable = (HBA_CMD_TABLE *)0;
|
||||
static uint8_t* gFisBuf = (uint8_t *)0;
|
||||
static uint8_t gPort = 0;
|
||||
static uint64_t gSectors = 0;
|
||||
|
||||
static inline uint32_t PRd(uint32_t off) {
|
||||
return *(volatile uint32_t *)((uint8_t *)gAhci + off);
|
||||
}
|
||||
static inline void PWr(uint32_t off, uint32_t v) {
|
||||
*(volatile uint32_t *)((uint8_t *)gAhci + off) = v;
|
||||
}
|
||||
static inline uint32_t PortRd(uint8_t p, uint32_t off) {
|
||||
return PRd(AHCI_PORT_BASE(p) + off);
|
||||
}
|
||||
static inline void PortWr(uint8_t p, uint32_t off, uint32_t v) {
|
||||
PWr(AHCI_PORT_BASE(p) + off, v);
|
||||
}
|
||||
|
||||
static void AhciIssueCmd(uint8_t fis[20], void* buf, uint32_t bytes, int write) {
|
||||
// Fill FIS in command table.
|
||||
memcpy(gCmdTable->cfis, fis, 20);
|
||||
gCmdTable->prdt[0].dba = MmGetPhysicalAddress((uintptr_t)buf);
|
||||
gCmdTable->prdt[0].dbc = bytes - 1;
|
||||
gCmdTable->prdt[0].i = 1;
|
||||
|
||||
gCmdList[0].cfl = 5; // FIS length in DWORDs
|
||||
gCmdList[0].w = write ? 1 : 0;
|
||||
gCmdList[0].prdtl = 1;
|
||||
gCmdList[0].prdbc = 0;
|
||||
gCmdList[0].ctba = MmGetPhysicalAddress((uintptr_t)gCmdTable);
|
||||
|
||||
PortWr(gPort, P_CI, 1);
|
||||
while (PortRd(gPort, P_CI) & 1) _mm_pause();
|
||||
}
|
||||
|
||||
static KSTATUS AhciRead(uint64_t lba, uint32_t count, void* buf) {
|
||||
uint8_t fis[20] = {0};
|
||||
fis[0] = 0x27; fis[1] = 0x80; // H2D, C=1
|
||||
fis[2] = 0x25; // READ DMA EXT
|
||||
fis[4] = (uint8_t)lba;
|
||||
fis[5] = (uint8_t)(lba >> 8);
|
||||
fis[6] = (uint8_t)(lba >> 16);
|
||||
fis[7] = 0x40; // LBA mode
|
||||
fis[8] = (uint8_t)(lba >> 24);
|
||||
fis[9] = (uint8_t)(lba >> 32);
|
||||
fis[10]= (uint8_t)(lba >> 40);
|
||||
fis[12]= (uint8_t)count;
|
||||
fis[13]= (uint8_t)(count >> 8);
|
||||
AhciIssueCmd(fis, buf, count * 512, 0);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static KSTATUS AhciWrite(uint64_t lba, uint32_t count, const void* buf) {
|
||||
uint8_t fis[20] = {0};
|
||||
fis[0] = 0x27; fis[1] = 0x80;
|
||||
fis[2] = 0x35; // WRITE DMA EXT
|
||||
fis[4] = (uint8_t)lba;
|
||||
fis[5] = (uint8_t)(lba >> 8);
|
||||
fis[6] = (uint8_t)(lba >> 16);
|
||||
fis[7] = 0x40;
|
||||
fis[8] = (uint8_t)(lba >> 24);
|
||||
fis[9] = (uint8_t)(lba >> 32);
|
||||
fis[10]= (uint8_t)(lba >> 40);
|
||||
fis[12]= (uint8_t)count;
|
||||
fis[13]= (uint8_t)(count >> 8);
|
||||
AhciIssueCmd(fis, (void *)buf, count * 512, 1);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
KSTATUS DriverEntry(const HAL_API* hal) {
|
||||
DbgPrint("AHCI: Init\n");
|
||||
gHal = hal;
|
||||
uint8_t bus, dev, fn;
|
||||
if (!hal->Pci.FindClass(0x01, 0x06, &bus, &dev, &fn))
|
||||
return STATUS_NOT_FOUND; // no AHCI controller
|
||||
|
||||
// BAR5 = AHCI base address.
|
||||
uint32_t bar5 = hal->Pci.ConfigRead32(bus, dev, fn, 0x24) & ~0xFU;
|
||||
if (!bar5) return STATUS_NOT_FOUND;
|
||||
|
||||
gAhci = (volatile uint32_t *)(gHal->PhysToVirt(bar5));
|
||||
|
||||
// Enable AHCI mode.
|
||||
PWr(AHCI_GHC, PRd(AHCI_GHC) | (1 << 31));
|
||||
|
||||
// Find a port with a connected device.
|
||||
uint32_t pi = PRd(AHCI_PI);
|
||||
for (int p = 0; p < 32; p++) {
|
||||
if (!(pi & (1 << p))) continue;
|
||||
uint32_t ssts = PortRd((uint8_t)p, P_SSTS);
|
||||
if ((ssts & 0xF) != 3) continue; // DET: device present + PHY
|
||||
if (PortRd((uint8_t)p, P_SIG) != 0x00000101) continue; // SATA
|
||||
gPort = (uint8_t)p;
|
||||
goto found;
|
||||
}
|
||||
return STATUS_NOT_FOUND;
|
||||
found:
|
||||
// Allocate command list (1 header), FIS buffer, command table.
|
||||
gCmdList = (HBA_CMD_HEADER *)MmAllocatePool(4096);
|
||||
gFisBuf = (uint8_t *)MmAllocatePool(4096);
|
||||
gCmdTable = (HBA_CMD_TABLE *)MmAllocatePool(4096);
|
||||
if (!gCmdList || !gFisBuf || !gCmdTable) return STATUS_NO_MEMORY;
|
||||
memset(gCmdList, 0, 4096);
|
||||
memset(gFisBuf, 0, 4096);
|
||||
memset(gCmdTable,0, 4096);
|
||||
|
||||
PortWr(gPort, P_CLB, (uint32_t)(MmGetPhysicalAddress((uintptr_t)gCmdList)));
|
||||
PortWr(gPort, P_FB, (uint32_t)(MmGetPhysicalAddress((uintptr_t)gFisBuf)));
|
||||
PortWr(gPort, P_CMD, PortRd(gPort, P_CMD) | CMD_FRE | CMD_ST);
|
||||
|
||||
// Identify to get sector count.
|
||||
uint8_t fis[20] = {0};
|
||||
fis[0] = 0x27; fis[1] = 0x80; fis[2] = 0xEC;
|
||||
uint16_t ident[256];
|
||||
memset(ident, 0, sizeof(ident));
|
||||
AhciIssueCmd(fis, ident, 512, 0);
|
||||
gSectors = *(uint64_t *)(&ident[100]);
|
||||
if (!gSectors) gSectors = ((uint32_t)ident[61] << 16) | ident[60];
|
||||
|
||||
DISK_DRIVER drv = { AhciRead, AhciWrite, gSectors };
|
||||
return IoRegisterDisk(1, &drv); // drive 1; drive 0 = IDE
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
[boot]
|
||||
ide.sys
|
||||
fat32.sys
|
||||
|
||||
[runtime]
|
||||
usb.sys
|
||||
@@ -0,0 +1,591 @@
|
||||
// fat32.c - FAT32 filesystem driver
|
||||
// Registers itself via IoRegisterFilesystem after finding a FAT32 partition.
|
||||
// Supports read-only access; write support is not implemented.
|
||||
#include <io_driver.h>
|
||||
#include <hal_api.h>
|
||||
#include <io_file.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ke_debug.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// On-disk structures
|
||||
// ---------------------------------------------------------------------------
|
||||
#pragma pack(push, 1)
|
||||
|
||||
typedef struct {
|
||||
uint8_t Jump[3];
|
||||
char OemId[8];
|
||||
uint16_t BytesPerSector;
|
||||
uint8_t SectorsPerCluster;
|
||||
uint16_t ReservedSectors;
|
||||
uint8_t NumFats;
|
||||
uint16_t RootEntryCount; // 0 for FAT32
|
||||
uint16_t TotalSectors16;
|
||||
uint8_t MediaType;
|
||||
uint16_t FatSize16; // 0 for FAT32
|
||||
uint16_t SectorsPerTrack;
|
||||
uint16_t NumHeads;
|
||||
uint32_t HiddenSectors;
|
||||
uint32_t TotalSectors32;
|
||||
// FAT32 extended BPB
|
||||
uint32_t FatSize32;
|
||||
uint16_t ExtFlags;
|
||||
uint16_t FsVersion;
|
||||
uint32_t RootCluster;
|
||||
uint16_t FsInfoSector;
|
||||
uint16_t BackupBootSector;
|
||||
uint8_t Reserved[12];
|
||||
uint8_t DriveNumber;
|
||||
uint8_t Reserved1;
|
||||
uint8_t BootSignature;
|
||||
uint32_t VolumeId;
|
||||
char VolumeLabel[11];
|
||||
char FsType[8];
|
||||
} BPB;
|
||||
|
||||
typedef struct {
|
||||
char Name[8];
|
||||
char Ext[3];
|
||||
uint8_t Attr;
|
||||
uint8_t Reserved;
|
||||
uint8_t CrtTimeTenth;
|
||||
uint16_t CrtTime;
|
||||
uint16_t CrtDate;
|
||||
uint16_t LastAccDate;
|
||||
uint16_t FirstClusterHi;
|
||||
uint16_t WrtTime;
|
||||
uint16_t WrtDate;
|
||||
uint16_t FirstClusterLo;
|
||||
uint32_t FileSize;
|
||||
} DIRENT;
|
||||
|
||||
typedef struct {
|
||||
uint8_t Order;
|
||||
uint16_t Name1[5];
|
||||
uint8_t Attr; // 0x0F
|
||||
uint8_t Type;
|
||||
uint8_t Checksum;
|
||||
uint16_t Name2[6];
|
||||
uint16_t FirstCluster;
|
||||
uint16_t Name3[2];
|
||||
} LFN_ENTRY;
|
||||
|
||||
// GPT Structures
|
||||
typedef struct {
|
||||
uint32_t Data1;
|
||||
uint16_t Data2;
|
||||
uint16_t Data3;
|
||||
uint8_t Data4[8];
|
||||
} EFI_GUID;
|
||||
|
||||
typedef struct {
|
||||
char Signature[8]; // "EFI PART"
|
||||
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_DIRECTORY 0x10
|
||||
#define ATTR_LFN 0x0F
|
||||
#define FAT32_EOC 0x0FFFFFF8
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Driver state
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef struct {
|
||||
uint8_t DriveIndex;
|
||||
uint32_t PartitionLba; // LBA of the partition's first sector
|
||||
uint32_t BytesPerSector;
|
||||
uint32_t SectorsPerCluster;
|
||||
uint32_t BytesPerCluster;
|
||||
uint32_t ReservedSectors;
|
||||
uint32_t NumFats;
|
||||
uint32_t FatSize; // in sectors
|
||||
uint32_t RootCluster;
|
||||
uint32_t DataStart; // LBA of cluster 2
|
||||
const HAL_API* Hal;
|
||||
} FAT32_VOL;
|
||||
|
||||
typedef struct {
|
||||
uint32_t StartCluster;
|
||||
uint32_t FileSize;
|
||||
FAT32_VOL* Vol;
|
||||
} FAT32_FILE;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
static KSTATUS ReadSectors(FAT32_VOL* v, uint64_t lba,
|
||||
uint32_t count, void* buf) {
|
||||
if (!v || !v->Hal || !v->Hal->Disk.ReadSectors) {
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
return v->Hal->Disk.ReadSectors(v->DriveIndex, lba, count, buf);
|
||||
}
|
||||
|
||||
static uint64_t ClusterToLba(FAT32_VOL* v, uint32_t cluster) {
|
||||
return v->DataStart + (uint64_t)(cluster - 2) * v->SectorsPerCluster;
|
||||
}
|
||||
|
||||
// Read the FAT entry for a given cluster. Returns 0 on error.
|
||||
static uint32_t FatEntry(FAT32_VOL* v, uint32_t cluster) {
|
||||
uint32_t fatOffset = cluster * 4;
|
||||
uint32_t sector = v->PartitionLba + v->ReservedSectors
|
||||
+ fatOffset / v->BytesPerSector;
|
||||
uint32_t offInSec = fatOffset % v->BytesPerSector;
|
||||
|
||||
uint8_t* sec = (uint8_t *)MmAllocatePool(v->BytesPerSector);
|
||||
if (!sec) return 0;
|
||||
if (!KSUCCESS(ReadSectors(v, sector, 1, sec))) {
|
||||
DbgPrint("fat32: FatEntry: ReadSectors failed!\n");
|
||||
}
|
||||
uint32_t val = *(uint32_t *)(sec + offInSec) & 0x0FFFFFFF;
|
||||
MmFreePool(sec);
|
||||
return val;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 8.3 name matching
|
||||
// ---------------------------------------------------------------------------
|
||||
static int MatchName83(const char* dirName, const char* dirExt,
|
||||
const char* want) {
|
||||
char buf[13];
|
||||
int n = 0;
|
||||
for (int i = 0; i < 8 && dirName[i] != ' '; i++) {
|
||||
buf[n++] = (char)(dirName[i] >= 'A' && dirName[i] <= 'Z'
|
||||
? dirName[i] + 32 : dirName[i]);
|
||||
}
|
||||
if (dirExt[0] != ' ') {
|
||||
buf[n++] = '.';
|
||||
for (int i = 0; i < 3 && dirExt[i] != ' '; i++) {
|
||||
buf[n++] = (char)(dirExt[i] >= 'A' && dirExt[i] <= 'Z'
|
||||
? dirExt[i] + 32 : dirExt[i]);
|
||||
}
|
||||
}
|
||||
buf[n] = '\0';
|
||||
|
||||
const char* p = buf;
|
||||
while (*want && *p) {
|
||||
char a = *p++, b = *want++;
|
||||
if (a >= 'A' && a <= 'Z') a += 32;
|
||||
if (b >= 'A' && b <= 'Z') b += 32;
|
||||
if (a != b) return 0;
|
||||
}
|
||||
return* want == '\0' && *p == '\0';
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// LFN name assembly
|
||||
// ---------------------------------------------------------------------------
|
||||
#define MAX_LFN 256
|
||||
|
||||
static void LfnAppend(char* lfn, const LFN_ENTRY* e) {
|
||||
int seq = (e->Order & 0x3F) - 1;
|
||||
if (seq < 0 || seq >= (MAX_LFN / 13)) return;
|
||||
|
||||
int pos = seq * 13;
|
||||
uint16_t chars[13];
|
||||
int idx = 0;
|
||||
|
||||
for (int i = 0; i < 5; i++) chars[idx++] = e->Name1[i];
|
||||
for (int i = 0; i < 6; i++) chars[idx++] = e->Name2[i];
|
||||
for (int i = 0; i < 2; i++) chars[idx++] = e->Name3[i];
|
||||
|
||||
for (int i = 0; i < 13 && pos < MAX_LFN - 1; i++, pos++) {
|
||||
uint16_t c = chars[i];
|
||||
if (c == 0x0000 || c == 0xFFFF) { lfn[pos] = '\0'; break; }
|
||||
lfn[pos] = (char)(c & 0x7F);
|
||||
}
|
||||
}
|
||||
|
||||
static int LfnMatch(const char* lfn, const char* want) {
|
||||
while (*lfn && *want) {
|
||||
char a = *lfn++, b = *want++;
|
||||
if (a >= 'A' && a <= 'Z') a += 32;
|
||||
if (b >= 'A' && b <= 'Z') b += 32;
|
||||
if (a != b) return 0;
|
||||
}
|
||||
return* lfn == '\0' && *want == '\0';
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Directory search
|
||||
// ---------------------------------------------------------------------------
|
||||
static int SearchDir(FAT32_VOL* v, uint32_t dirCluster,
|
||||
const char* name,
|
||||
uint32_t* outCluster, uint32_t* outSize,
|
||||
uint8_t* outAttr) {
|
||||
uint8_t* clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
|
||||
if (!clusterBuf) return 0;
|
||||
|
||||
char lfn[MAX_LFN];
|
||||
int hasLfn = 0;
|
||||
memset(lfn, 0, sizeof(lfn));
|
||||
|
||||
uint32_t cluster = dirCluster;
|
||||
while (cluster < FAT32_EOC) {
|
||||
if (!KSUCCESS(ReadSectors(v, ClusterToLba(v, cluster),
|
||||
v->SectorsPerCluster, clusterBuf))) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t entries = v->BytesPerCluster / sizeof(DIRENT);
|
||||
DIRENT* dir = (DIRENT *)clusterBuf;
|
||||
|
||||
for (uint32_t i = 0; i < entries; i++) {
|
||||
if (dir[i].Name[0] == 0x00) goto done; // end of directory
|
||||
if ((uint8_t)dir[i].Name[0] == 0xE5) { // deleted
|
||||
hasLfn = 0; memset(lfn, 0, sizeof(lfn));
|
||||
continue;
|
||||
}
|
||||
if (dir[i].Attr == ATTR_LFN) {
|
||||
LfnAppend(lfn, (LFN_ENTRY *)&dir[i]);
|
||||
hasLfn = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
int match = 0;
|
||||
if (hasLfn && lfn[0]) {
|
||||
match = LfnMatch(lfn, name);
|
||||
}
|
||||
if (!match) {
|
||||
match = MatchName83(dir[i].Name, dir[i].Ext, name);
|
||||
}
|
||||
|
||||
hasLfn = 0; memset(lfn, 0, sizeof(lfn));
|
||||
|
||||
if (match) {
|
||||
*outCluster = ((uint32_t)dir[i].FirstClusterHi << 16) | dir[i].FirstClusterLo;
|
||||
*outSize = dir[i].FileSize;
|
||||
*outAttr = dir[i].Attr;
|
||||
MmFreePool(clusterBuf);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
cluster = FatEntry(v, cluster);
|
||||
}
|
||||
done:
|
||||
MmFreePool(clusterBuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Path traversal
|
||||
// ---------------------------------------------------------------------------
|
||||
static KSTATUS Traverse(FAT32_VOL* v, const char* path,
|
||||
uint32_t* outCluster, uint32_t* outSize) {
|
||||
uint32_t cluster = v->RootCluster;
|
||||
uint32_t size = 0;
|
||||
uint8_t attr = ATTR_DIRECTORY;
|
||||
|
||||
char component[256];
|
||||
const char* p = path;
|
||||
|
||||
while (*p) {
|
||||
while (*p == '\\' || *p == '/') p++;
|
||||
if (!*p) break;
|
||||
|
||||
int len = 0;
|
||||
while (*p && *p != '\\' && *p != '/' && len < 255)
|
||||
component[len++] = *p++;
|
||||
component[len] = '\0';
|
||||
if (!len) break;
|
||||
|
||||
if (!(attr & ATTR_DIRECTORY)) return STATUS_NOT_FOUND;
|
||||
|
||||
uint32_t childCluster, childSize;
|
||||
uint8_t childAttr;
|
||||
if (!SearchDir(v, cluster, component, &childCluster, &childSize, &childAttr))
|
||||
return STATUS_NOT_FOUND;
|
||||
|
||||
cluster = childCluster;
|
||||
size = childSize;
|
||||
attr = childAttr;
|
||||
}
|
||||
|
||||
*outCluster = cluster;
|
||||
*outSize = size;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IO_FILESYSTEM vtable implementations
|
||||
// ---------------------------------------------------------------------------
|
||||
static KSTATUS Fat32Open(IO_FILESYSTEM* fs, const char* path, void **fileCtx) {
|
||||
FAT32_VOL* v = (FAT32_VOL *)fs->DriverCtx;
|
||||
uint32_t cluster, size;
|
||||
KSTATUS s = Traverse(v, path, &cluster, &size);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
FAT32_FILE* f = (FAT32_FILE *)MmAllocatePool(sizeof(FAT32_FILE));
|
||||
if (!f) return STATUS_NO_MEMORY;
|
||||
f->StartCluster = cluster;
|
||||
f->FileSize = size;
|
||||
f->Vol = v;
|
||||
|
||||
*fileCtx = f;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static KSTATUS Fat32Read(IO_FILESYSTEM* fs, void* fileCtx,
|
||||
uint64_t offset, void* buf,
|
||||
size_t size, size_t* bytesRead) {
|
||||
(void)fs;
|
||||
FAT32_FILE* f = (FAT32_FILE *)fileCtx;
|
||||
FAT32_VOL* v = f->Vol;
|
||||
|
||||
if (offset >= f->FileSize) { *bytesRead = 0; return STATUS_SUCCESS; }
|
||||
if (offset + size > f->FileSize) size = (size_t)(f->FileSize - offset);
|
||||
|
||||
uint8_t* clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
|
||||
if (!clusterBuf) return STATUS_NO_MEMORY;
|
||||
|
||||
size_t totalRead = 0;
|
||||
uint8_t* out = (uint8_t *)buf;
|
||||
|
||||
uint32_t skipClusters = (uint32_t)(offset / v->BytesPerCluster);
|
||||
uint32_t offsetInCluster = (uint32_t)(offset % v->BytesPerCluster);
|
||||
uint32_t cluster = f->StartCluster;
|
||||
|
||||
for (uint32_t i = 0; i < skipClusters && cluster < FAT32_EOC; i++)
|
||||
cluster = FatEntry(v, cluster);
|
||||
|
||||
while (size > 0 && cluster < FAT32_EOC) {
|
||||
if (!KSUCCESS(ReadSectors(v, ClusterToLba(v, cluster),
|
||||
v->SectorsPerCluster, clusterBuf))) {
|
||||
break;
|
||||
}
|
||||
|
||||
size_t avail = v->BytesPerCluster - offsetInCluster;
|
||||
size_t copy = avail < size ? avail : size;
|
||||
|
||||
memcpy(out, clusterBuf + offsetInCluster, copy);
|
||||
out += copy;
|
||||
totalRead += copy;
|
||||
size -= copy;
|
||||
offsetInCluster = 0;
|
||||
|
||||
cluster = FatEntry(v, cluster);
|
||||
}
|
||||
|
||||
MmFreePool(clusterBuf);
|
||||
*bytesRead = totalRead;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static uint64_t Fat32GetSize(IO_FILESYSTEM* fs, void* fileCtx) {
|
||||
(void)fs;
|
||||
return ((FAT32_FILE *)fileCtx)->FileSize;
|
||||
}
|
||||
|
||||
static void Fat32Close(IO_FILESYSTEM* fs, void* fileCtx) {
|
||||
(void)fs;
|
||||
MmFreePool(fileCtx);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Partition scan - Supports MBR and GPT
|
||||
// ---------------------------------------------------------------------------
|
||||
static const EFI_GUID GUID_EFI_SYSTEM = {
|
||||
0xC12A7328, 0xF81F, 0x11D2, {0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B}
|
||||
};
|
||||
static const EFI_GUID GUID_MS_BASIC_DATA = {
|
||||
0xEBD0A0A2, 0xB9E5, 0x4433, {0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7}
|
||||
};
|
||||
static const EFI_GUID GUID_LINUX_BASIC_DATA = {
|
||||
0x0FC63DAF, 0x8483, 0x4772, {0x8E, 0x79, 0x3D, 0x69, 0xD8, 0x47, 0x7D, 0xE4}
|
||||
};
|
||||
|
||||
// Validates whether the VBR actually contains a FAT32 BPB
|
||||
static int IsFat32Vbr(FAT32_VOL* v, uint32_t lba) {
|
||||
uint8_t sector[512];
|
||||
if (!KSUCCESS(ReadSectors(v, lba, 1, sector))) return 0;
|
||||
|
||||
if (sector[510] != 0x55 || sector[511] != 0xAA) return 0;
|
||||
|
||||
BPB* bpb = (BPB *)sector;
|
||||
if (bpb->FatSize16 != 0 || bpb->RootEntryCount != 0) return 0;
|
||||
if (bpb->FatSize32 == 0) return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint32_t FindFat32Partition(FAT32_VOL* v) {
|
||||
uint8_t mbr[512];
|
||||
|
||||
KSTATUS s = ReadSectors(v, 0, 1, mbr);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("FAT32: [DEBUG] ReadSectors for MBR failed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mbr[510] != 0x55 || mbr[511] != 0xAA) {
|
||||
DbgPrint("FAT32: [DEBUG] Invalid signature at LBA 0.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check for GPT Protective MBR (Type 0xEE)
|
||||
int isGpt = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (mbr[0x1BE + i * 16 + 4] == 0xEE) {
|
||||
isGpt = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isGpt) {
|
||||
DbgPrint("FAT32: [DEBUG] Protective MBR found. Parsing GPT...\n");
|
||||
uint8_t gptSector[512];
|
||||
if (!KSUCCESS(ReadSectors(v, 1, 1, gptSector))) return 0;
|
||||
|
||||
GPT_HEADER* gpt = (GPT_HEADER *)gptSector;
|
||||
if (memcmp(gpt->Signature, "EFI PART", 8) != 0) {
|
||||
DbgPrint("FAT32: [DEBUG] Invalid GPT signature.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t entryLBA = gpt->PartitionEntryLBA;
|
||||
uint32_t numEntries = gpt->NumberOfPartitionEntries;
|
||||
uint32_t entrySize = gpt->SizeOfPartitionEntry;
|
||||
|
||||
if (entrySize < sizeof(GPT_ENTRY)) return 0;
|
||||
|
||||
uint32_t entriesPerSector = 512 / entrySize;
|
||||
uint8_t entrySector[512];
|
||||
|
||||
for (uint32_t i = 0; i < numEntries; i++) {
|
||||
if (i % entriesPerSector == 0) {
|
||||
if (!KSUCCESS(ReadSectors(v, entryLBA + (i / entriesPerSector), 1, entrySector))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
GPT_ENTRY* entry = (GPT_ENTRY *)(entrySector + (i % entriesPerSector) * entrySize);
|
||||
|
||||
static const EFI_GUID emptyGuid = {0};
|
||||
if (memcmp(&entry->PartitionTypeGUID, &emptyGuid, sizeof(EFI_GUID)) == 0) continue;
|
||||
|
||||
// Microsoft Basic Data maps to FAT, NTFS, and exFAT. VBR validation checks true FAT32 nature.
|
||||
if (memcmp(&entry->PartitionTypeGUID, &GUID_EFI_SYSTEM, sizeof(EFI_GUID)) == 0 ||
|
||||
memcmp(&entry->PartitionTypeGUID, &GUID_MS_BASIC_DATA, sizeof(EFI_GUID)) == 0 ||
|
||||
memcmp(&entry->PartitionTypeGUID, &GUID_LINUX_BASIC_DATA, sizeof(EFI_GUID)) == 0) {
|
||||
|
||||
uint32_t lba = (uint32_t)entry->StartingLBA;
|
||||
if (IsFat32Vbr(v, lba)) {
|
||||
DbgPrint("FAT32: [DEBUG] Found FAT32 partition via GPT at LBA %u\n", lba);
|
||||
return lba;
|
||||
}
|
||||
}
|
||||
}
|
||||
DbgPrint("FAT32: [DEBUG] No FAT32 partition found in GPT.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Fallback: MBR Parsing
|
||||
for (int i = 0; i < 4; i++) {
|
||||
uint8_t* entry = mbr + 0x1BE + i * 16;
|
||||
uint8_t type = entry[4];
|
||||
|
||||
if (type == 0x0B || type == 0x0C) {
|
||||
uint32_t lba = *(uint32_t *)(entry + 8);
|
||||
if (IsFat32Vbr(v, lba)) {
|
||||
DbgPrint("FAT32: [DEBUG] Found FAT32 partition via MBR at LBA %u\n", lba);
|
||||
return lba;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DbgPrint("FAT32: [DEBUG] No FAT32 partition found in MBR.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DriverEntry
|
||||
// ---------------------------------------------------------------------------
|
||||
static FAT32_VOL gVol;
|
||||
static IO_FILESYSTEM gFs;
|
||||
|
||||
KSTATUS DriverEntry(const HAL_API* hal) {
|
||||
DbgPrint("FAT32: Init\n");
|
||||
if (!hal) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
gVol.Hal = hal;
|
||||
gVol.DriveIndex = 0;
|
||||
|
||||
uint32_t partLba = FindFat32Partition(&gVol);
|
||||
if (partLba == 0) {
|
||||
DbgPrint("FAT32: [DEBUG] Could not locate a valid FAT32 volume.\n");
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
gVol.PartitionLba = partLba;
|
||||
|
||||
uint8_t sector[512];
|
||||
if (!KSUCCESS(ReadSectors(&gVol, partLba, 1, sector))) {
|
||||
DbgPrint("FAT32: [DEBUG] ReadSectors FAILED!\n");
|
||||
return STATUS_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
BPB* bpb = (BPB *)sector;
|
||||
|
||||
// IsFat32Vbr guarantees these pass, but retained for absolute sanity checking.
|
||||
if (bpb->FatSize16 != 0 || bpb->RootEntryCount != 0) {
|
||||
DbgPrint("FAT32: [DEBUG] Validation failed - Not a FAT32 volume.\n");
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
gVol.BytesPerSector = bpb->BytesPerSector;
|
||||
gVol.SectorsPerCluster = bpb->SectorsPerCluster;
|
||||
gVol.BytesPerCluster = bpb->BytesPerSector * bpb->SectorsPerCluster;
|
||||
gVol.ReservedSectors = bpb->ReservedSectors;
|
||||
gVol.NumFats = bpb->NumFats;
|
||||
gVol.FatSize = bpb->FatSize32;
|
||||
gVol.RootCluster = bpb->RootCluster;
|
||||
gVol.DataStart = partLba
|
||||
+ bpb->ReservedSectors
|
||||
+ bpb->NumFats * bpb->FatSize32;
|
||||
|
||||
gFs.Open = Fat32Open;
|
||||
gFs.Read = Fat32Read;
|
||||
gFs.GetSize = Fat32GetSize;
|
||||
gFs.Close = Fat32Close;
|
||||
gFs.DriverCtx = &gVol;
|
||||
|
||||
// Populate IO_VOLUME_PARAMS
|
||||
IO_VOLUME_PARAMS volParams;
|
||||
volParams.DiskIndex = gVol.DriveIndex;
|
||||
volParams.PartitionLba = partLba;
|
||||
// FAT32 sizes are stored in TotalSectors32 if TotalSectors16 is 0
|
||||
volParams.PartitionSize = (bpb->TotalSectors16 != 0) ? bpb->TotalSectors16 : bpb->TotalSectors32;
|
||||
|
||||
// Pass the new volParams structure
|
||||
KSTATUS status = IoRegisterFilesystem(gVol.DriveIndex, &gFs, &volParams);
|
||||
DbgPrint("FAT32: [DEBUG] pIoRegisterFilesystem returned: %d\n", status);
|
||||
|
||||
DbgPrint("FAT32: Init complete.\n");
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
// ide.c - PIO-mode IDE driver (legacy 28-bit LBA)
|
||||
#include <io_driver.h>
|
||||
#include <hal_api.h>
|
||||
#include <ke_debug.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
|
||||
// Primary channel I/O ports.
|
||||
#define IDE_DATA 0x1F0
|
||||
#define IDE_ERR 0x1F1
|
||||
#define IDE_SECCNT 0x1F2
|
||||
#define IDE_LBA0 0x1F3
|
||||
#define IDE_LBA1 0x1F4
|
||||
#define IDE_LBA2 0x1F5
|
||||
#define IDE_DRIVE 0x1F6 // 0xE0=master 0xF0=slave
|
||||
#define IDE_STATUS 0x1F7
|
||||
#define IDE_CMD 0x1F7
|
||||
#define IDE_ALT 0x3F6
|
||||
|
||||
#define IDE_BUSY 0x80
|
||||
#define IDE_READY 0x40
|
||||
#define IDE_ERR_F 0x01
|
||||
#define IDE_DRQ 0x08
|
||||
|
||||
#define CMD_READ 0x20
|
||||
#define CMD_WRITE 0x30
|
||||
#define CMD_IDENT 0xEC
|
||||
|
||||
static const HAL_API* gHal;
|
||||
static DISK_DRIVER gDrv;
|
||||
static uint64_t gSectorCount = 0;
|
||||
|
||||
static void IdeWait(void) {
|
||||
while (__inbyte(IDE_STATUS) & IDE_BUSY) _mm_pause();
|
||||
}
|
||||
|
||||
static KSTATUS IdeWaitData(void) {
|
||||
// Read the Alternate Status port 4 times to waste 400ns
|
||||
for (int i = 0; i < 4; i++) {
|
||||
__inbyte(IDE_ALT);
|
||||
}
|
||||
|
||||
// Wait until the drive is not busy AND data is requested (DRQ)
|
||||
while (1) {
|
||||
uint8_t st = __inbyte(IDE_STATUS);
|
||||
if (st & IDE_ERR_F) return STATUS_DEVICE_ERROR;
|
||||
if (!(st & IDE_BUSY) && (st & IDE_DRQ)) return STATUS_SUCCESS;
|
||||
_mm_pause();
|
||||
}
|
||||
}
|
||||
|
||||
static KSTATUS IdeRead(uint64_t lba, uint32_t count, void* buf) {
|
||||
if (lba + count > gSectorCount) return STATUS_INVALID_PARAMETER;
|
||||
uint16_t* p = (uint16_t *)buf;
|
||||
|
||||
for (uint32_t i = 0; i < count; i++, lba++) {
|
||||
IdeWait(); // Wait for drive to be ready for commands
|
||||
__outbyte(IDE_DRIVE, 0xE0 | ((lba >> 24) & 0xF));
|
||||
__outbyte(IDE_SECCNT, 1);
|
||||
__outbyte(IDE_LBA0, (uint8_t)lba);
|
||||
__outbyte(IDE_LBA1, (uint8_t)(lba >> 8));
|
||||
__outbyte(IDE_LBA2, (uint8_t)(lba >> 16));
|
||||
__outbyte(IDE_CMD, CMD_READ);
|
||||
|
||||
// Use the delay/DRQ wait
|
||||
if (IdeWaitData() != STATUS_SUCCESS) return STATUS_DEVICE_ERROR;
|
||||
|
||||
for (int w = 0; w < 256; w++) *p++ = __inword(IDE_DATA);
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static KSTATUS IdeWrite(uint64_t lba, uint32_t count, const void* buf) {
|
||||
if (lba + count > gSectorCount) return STATUS_INVALID_PARAMETER;
|
||||
const uint16_t* p = (const uint16_t *)buf;
|
||||
|
||||
for (uint32_t i = 0; i < count; i++, lba++) {
|
||||
IdeWait();
|
||||
__outbyte(IDE_DRIVE, 0xE0 | ((lba >> 24) & 0xF));
|
||||
__outbyte(IDE_SECCNT, 1);
|
||||
__outbyte(IDE_LBA0, (uint8_t)lba);
|
||||
__outbyte(IDE_LBA1, (uint8_t)(lba >> 8));
|
||||
__outbyte(IDE_LBA2, (uint8_t)(lba >> 16));
|
||||
__outbyte(IDE_CMD, CMD_WRITE);
|
||||
IdeWait();
|
||||
for (int w = 0; w < 256; w++) __outword(IDE_DATA, *p++);
|
||||
__outbyte(IDE_ALT, 0); // flush cache
|
||||
if (IdeWaitData() != STATUS_SUCCESS) return STATUS_DEVICE_ERROR;
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
KSTATUS DriverEntry(const HAL_API* hal) {
|
||||
DbgPrint("IDE: Init\n");
|
||||
gHal = hal;
|
||||
uint8_t status;
|
||||
int timeout;
|
||||
|
||||
// 1. Select Primary Master Drive (0xA0)
|
||||
__outbyte(IDE_DRIVE, 0xA0);
|
||||
|
||||
// Waste 400ns to let the drive selection settle on the bus
|
||||
for(int i=0; i<4; i++) __inbyte(IDE_ALT);
|
||||
|
||||
// 2. Zero out the LBA and Sector Count registers
|
||||
__outbyte(IDE_SECCNT, 0);
|
||||
__outbyte(IDE_LBA0, 0);
|
||||
__outbyte(IDE_LBA1, 0);
|
||||
__outbyte(IDE_LBA2, 0);
|
||||
|
||||
// 3. Send the IDENTIFY command
|
||||
DbgPrint("IDE: Sending CMD_IDENT (0xEC)...\n");
|
||||
__outbyte(IDE_CMD, CMD_IDENT);
|
||||
|
||||
// 4. Read status immediately. If it's 0, the drive doesn't exist at all.
|
||||
status = __inbyte(IDE_STATUS);
|
||||
// DbgPrint("IDE: Initial status after IDENTIFY: 0x%02X\n", status);
|
||||
if (status == 0) {
|
||||
DbgPrint("IDE ERROR: Drive does not exist (Status 0).\n");
|
||||
return STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
// 5. Wait for BSY (Busy) to clear, WITH A TIMEOUT
|
||||
timeout = 100000;
|
||||
while (__inbyte(IDE_STATUS) & IDE_BUSY) {
|
||||
if (--timeout == 0) {
|
||||
DbgPrint("IDE ERROR: Timeout waiting for BSY to clear!\n");
|
||||
return STATUS_DEVICE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Check for ATAPI (CD-ROM) or SATA signatures.
|
||||
// If these registers aren't 0 after BSY clears, it's NOT a standard ATA disk!
|
||||
uint8_t lba1 = __inbyte(IDE_LBA1);
|
||||
uint8_t lba2 = __inbyte(IDE_LBA2);
|
||||
if (lba1 != 0 || lba2 != 0) {
|
||||
DbgPrint("IDE ERROR: Not an ATA drive! Signatures: LBA1=0x%02X, LBA2=0x%02X\n", lba1, lba2);
|
||||
// (ATAPI CD-ROMs usually show LBA1=0x14, LBA2=0xEB)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// 7. Wait for DRQ (Data Request) or ERR (Error)
|
||||
timeout = 100000;
|
||||
while (1) {
|
||||
status = __inbyte(IDE_STATUS);
|
||||
if (status & IDE_ERR_F) {
|
||||
DbgPrint("IDE ERROR: Drive returned an error! Status: 0x%02X\n", status);
|
||||
return STATUS_DEVICE_ERROR;
|
||||
}
|
||||
if (status & IDE_DRQ) {
|
||||
break; // Data is ready!
|
||||
}
|
||||
if (--timeout == 0) {
|
||||
DbgPrint("IDE ERROR: Timeout waiting for DRQ! Status: 0x%02X\n", status);
|
||||
return STATUS_DEVICE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Success! Read the 256 words (512 bytes) of IDENTIFY data
|
||||
// DbgPrint("IDE: DRQ set! Reading 256 words...\n");
|
||||
uint16_t ident[256];
|
||||
for (int i = 0; i < 256; i++) {
|
||||
ident[i] = __inword(IDE_DATA);
|
||||
}
|
||||
|
||||
// 9. Extract and print the sector count (Words 60 and 61 for 28-bit LBA)
|
||||
gSectorCount = ((uint32_t)ident[61] << 16) | ident[60];
|
||||
DbgPrint("IDE: SUCCESS: Disk found! Sector count: %u\n", (uint32_t)gSectorCount);
|
||||
|
||||
if (gSectorCount == 0) return STATUS_NOT_FOUND;
|
||||
|
||||
// Register driver globally (from the previous fix!)
|
||||
gDrv.ReadSectors = IdeRead;
|
||||
gDrv.WriteSectors = IdeWrite;
|
||||
gDrv.SectorCount = gSectorCount;
|
||||
|
||||
return IoRegisterDisk(0, &gDrv);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#include <hal_api.h>
|
||||
#include <ke_status.h>
|
||||
#include <ke_debug.h>
|
||||
|
||||
KSTATUS DriverEntry(const HAL_API* hal) {
|
||||
(void)hal;
|
||||
DbgPrint("Test runtime driver\n");
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
Reference in new issue
Block a user