711 lines
21 KiB
C++
711 lines
21 KiB
C++
#include <fs.h>
|
|
#include <serial.h>
|
|
#include <memory/heap.h>
|
|
#include <string_utils.h>
|
|
|
|
extern EFI_SYSTEM_TABLE *ST;
|
|
|
|
#pragma pack(push, 1)
|
|
|
|
struct BPB {
|
|
UINT8 jmpBoot[3];
|
|
UINT8 OEMName[8];
|
|
UINT16 BytsPerSec;
|
|
UINT8 SecPerClus;
|
|
UINT16 RsvdSecCnt;
|
|
UINT8 NumFATs;
|
|
UINT16 RootEntCnt;
|
|
UINT16 TotSec16;
|
|
UINT8 Media;
|
|
UINT16 FATSz16;
|
|
UINT16 SecPerTrk;
|
|
UINT16 NumHeads;
|
|
UINT32 HiddSec;
|
|
UINT32 TotSec32;
|
|
UINT32 FATSz32;
|
|
UINT16 ExtFlags;
|
|
UINT16 FSVer;
|
|
UINT32 RootClus;
|
|
UINT16 FSInfo;
|
|
UINT16 BkBootSec;
|
|
UINT8 Reserved[12];
|
|
UINT8 DrvNum;
|
|
UINT8 Reserved1;
|
|
UINT8 BootSig;
|
|
UINT32 VolID;
|
|
UINT8 VolLab[11];
|
|
UINT8 FilSysType[8];
|
|
};
|
|
|
|
struct GPTHeader {
|
|
UINT64 Signature;
|
|
UINT32 Revision;
|
|
UINT32 HeaderSize;
|
|
UINT32 HeaderCRC32;
|
|
UINT32 Reserved;
|
|
UINT64 MyLBA;
|
|
UINT64 AlternateLBA;
|
|
UINT64 FirstUsableLBA;
|
|
UINT64 LastUsableLBA;
|
|
EFI_GUID DiskGUID;
|
|
UINT64 PartitionEntryLBA;
|
|
UINT32 NumPartitionEntries;
|
|
UINT32 SizeOfPartitionEntry;
|
|
UINT32 PartitionEntryArrayCRC32;
|
|
};
|
|
|
|
struct GPTEntry {
|
|
EFI_GUID PartitionTypeGUID;
|
|
EFI_GUID UniquePartitionGUID;
|
|
UINT64 StartingLBA;
|
|
UINT64 EndingLBA;
|
|
UINT64 Attributes;
|
|
CHAR16 PartitionName[36];
|
|
};
|
|
|
|
struct MBRPart {
|
|
UINT8 Status;
|
|
UINT8 CHSStart[3];
|
|
UINT8 Type;
|
|
UINT8 CHSEnd[3];
|
|
UINT32 LBABegin;
|
|
UINT32 NumSectors;
|
|
};
|
|
|
|
#pragma pack(pop)
|
|
|
|
#define GPT_SIGNATURE_VAL 0x5452415020494645ULL
|
|
#define MBR_SIGNATURE 0xAA55
|
|
|
|
#define MBR_TYPE_FAT12 0x01
|
|
#define MBR_TYPE_FAT16 0x04
|
|
#define MBR_TYPE_FAT16B 0x06
|
|
#define MBR_TYPE_FAT32 0x0B
|
|
#define MBR_TYPE_FAT32LBA 0x0C
|
|
#define MBR_TYPE_FAT16LBA 0x0E
|
|
#define MBR_TYPE_ESP 0xEF
|
|
|
|
// ---- GUID helpers ----
|
|
|
|
static BOOLEAN guid_is_zero(EFI_GUID *g) {
|
|
return g->Data1 == 0 && g->Data2 == 0 && g->Data3 == 0
|
|
&& g->Data4[0] == 0 && g->Data4[1] == 0
|
|
&& g->Data4[2] == 0 && g->Data4[3] == 0
|
|
&& g->Data4[4] == 0 && g->Data4[5] == 0
|
|
&& g->Data4[6] == 0 && g->Data4[7] == 0;
|
|
}
|
|
|
|
static BOOLEAN guid_eq(EFI_GUID *a, EFI_GUID *b) {
|
|
return a->Data1 == b->Data1 && a->Data2 == b->Data2
|
|
&& a->Data3 == b->Data3
|
|
&& a->Data4[0] == b->Data4[0] && a->Data4[1] == b->Data4[1]
|
|
&& a->Data4[2] == b->Data4[2] && a->Data4[3] == b->Data4[3]
|
|
&& a->Data4[4] == b->Data4[4] && a->Data4[5] == b->Data4[5]
|
|
&& a->Data4[6] == b->Data4[6] && a->Data4[7] == b->Data4[7];
|
|
}
|
|
|
|
// ---- Block I/O ----
|
|
|
|
struct block_dev {
|
|
EFI_BLOCK_IO_PROTOCOL *Bio;
|
|
UINT32 BlockSize;
|
|
};
|
|
|
|
static EFI_STATUS blk_init(struct block_dev *dev) {
|
|
EFI_GUID g = EFI_BLOCK_IO_PROTOCOL_GUID;
|
|
EFI_HANDLE *Handles = NULL;
|
|
UINTN NoHandles = 0;
|
|
EFI_STATUS st = uefi_call_wrapper(ST->BootServices->LocateHandleBuffer, 5,
|
|
ByProtocol, &g, NULL, &NoHandles, &Handles);
|
|
if (EFI_ERROR(st) || NoHandles == 0) return EFI_NOT_FOUND;
|
|
|
|
EFI_BLOCK_IO_PROTOCOL *Bio = NULL;
|
|
for (UINTN i = 0; i < NoHandles; i++) {
|
|
EFI_BLOCK_IO_PROTOCOL *b = NULL;
|
|
st = uefi_call_wrapper(ST->BootServices->HandleProtocol, 3,
|
|
Handles[i], &g, (void**)&b);
|
|
if (EFI_ERROR(st)) continue;
|
|
if (b->Media->MediaPresent) { Bio = b; break; }
|
|
if (!Bio) Bio = b;
|
|
}
|
|
|
|
uefi_call_wrapper(ST->BootServices->FreePool, 1, Handles);
|
|
if (!Bio) return EFI_NOT_FOUND;
|
|
|
|
dev->Bio = Bio;
|
|
dev->BlockSize = Bio->Media->BlockSize;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static EFI_STATUS blk_read(struct block_dev *dev, UINT64 LBA,
|
|
UINTN Sectors, void *Buf) {
|
|
return uefi_call_wrapper(dev->Bio->ReadBlocks, 5,
|
|
dev->Bio, dev->Bio->Media->MediaId, LBA,
|
|
Sectors * dev->BlockSize, Buf);
|
|
}
|
|
|
|
// ---- Partition detection ----
|
|
|
|
static EFI_STATUS find_gpt_partition(struct block_dev *dev, UINT64 *StartLBA) {
|
|
UINT8 *Buf = (UINT8*)kmalloc(dev->BlockSize);
|
|
if (!Buf) return EFI_OUT_OF_RESOURCES;
|
|
|
|
EFI_STATUS st = blk_read(dev, 1, 1, Buf);
|
|
if (EFI_ERROR(st)) { kfree(Buf); return st; }
|
|
|
|
struct GPTHeader *Hdr = (struct GPTHeader*)Buf;
|
|
if (Hdr->Signature != GPT_SIGNATURE_VAL) { kfree(Buf); return EFI_NOT_FOUND; }
|
|
|
|
// Read partition entries
|
|
UINTN EntrySz = Hdr->SizeOfPartitionEntry;
|
|
UINTN NumEnt = Hdr->NumPartitionEntries;
|
|
UINTN Total = EntrySz * NumEnt;
|
|
UINTN Secs = (Total + dev->BlockSize - 1) / dev->BlockSize;
|
|
|
|
UINT8 *EntBuf = (UINT8*)kmalloc(Secs * dev->BlockSize);
|
|
if (!EntBuf) { kfree(Buf); return EFI_OUT_OF_RESOURCES; }
|
|
|
|
st = blk_read(dev, Hdr->PartitionEntryLBA, Secs, EntBuf);
|
|
if (EFI_ERROR(st)) { kfree(Buf); kfree(EntBuf); return st; }
|
|
|
|
// EFI System Partition GUID
|
|
EFI_GUID esp = { 0xC12A7328, 0xF81F, 0x11D2,
|
|
{0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B} };
|
|
// Basic data partition GUID (FAT)
|
|
EFI_GUID basic = { 0xEBD0A0A2, 0xB9E5, 0x4433,
|
|
{0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7} };
|
|
|
|
BOOLEAN found = FALSE;
|
|
for (UINTN i = 0; i < NumEnt; i++) {
|
|
struct GPTEntry *E = (struct GPTEntry*)(EntBuf + i * EntrySz);
|
|
if (guid_is_zero(&E->PartitionTypeGUID)) continue;
|
|
if (guid_eq(&E->PartitionTypeGUID, &esp) ||
|
|
guid_eq(&E->PartitionTypeGUID, &basic)) {
|
|
*StartLBA = E->StartingLBA;
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
// Fallback: first non-empty partition
|
|
for (UINTN i = 0; i < NumEnt; i++) {
|
|
struct GPTEntry *E = (struct GPTEntry*)(EntBuf + i * EntrySz);
|
|
if (!guid_is_zero(&E->PartitionTypeGUID)) {
|
|
*StartLBA = E->StartingLBA;
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
kfree(Buf);
|
|
kfree(EntBuf);
|
|
return found ? EFI_SUCCESS : EFI_NOT_FOUND;
|
|
}
|
|
|
|
static EFI_STATUS find_mbr_partition(struct block_dev *dev, UINT64 *StartLBA) {
|
|
UINT8 *Buf = (UINT8*)kmalloc(dev->BlockSize);
|
|
if (!Buf) return EFI_OUT_OF_RESOURCES;
|
|
|
|
EFI_STATUS st = blk_read(dev, 0, 1, Buf);
|
|
if (EFI_ERROR(st)) { kfree(Buf); return st; }
|
|
|
|
if (*(UINT16*)(Buf + 510) != MBR_SIGNATURE) { kfree(Buf); return EFI_NOT_FOUND; }
|
|
|
|
struct MBRPart *Parts = (struct MBRPart*)(Buf + 446);
|
|
// Verify at least one non-zero partition entry exists
|
|
BOOLEAN has_part = FALSE;
|
|
for (SSINT32 i = 0; i < 4; i++) {
|
|
if (Parts[i].Type != 0x00 && Parts[i].Type != 0xEE) {
|
|
has_part = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!has_part) { kfree(Buf); return EFI_NOT_FOUND; }
|
|
|
|
for (SSINT32 i = 0; i < 4; i++) {
|
|
UINT8 t = Parts[i].Type;
|
|
if (t == 0x00 || t == 0xEE) continue;
|
|
if (t == MBR_TYPE_FAT12 || t == MBR_TYPE_FAT16 ||
|
|
t == MBR_TYPE_FAT16B || t == MBR_TYPE_FAT32 ||
|
|
t == MBR_TYPE_FAT32LBA || t == MBR_TYPE_FAT16LBA ||
|
|
t == MBR_TYPE_ESP) {
|
|
*StartLBA = Parts[i].LBABegin;
|
|
kfree(Buf);
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
// Fallback: first non-empty partition
|
|
for (SSINT32 i = 0; i < 4; i++) {
|
|
if (Parts[i].Type != 0x00) {
|
|
*StartLBA = Parts[i].LBABegin;
|
|
kfree(Buf);
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
kfree(Buf);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
// ---- FAT driver ----
|
|
|
|
struct fat_fs {
|
|
struct block_dev *Dev;
|
|
UINT64 PartLBA;
|
|
UINT16 BytsPerSec;
|
|
UINT8 SecPerClus;
|
|
UINT16 RsvdSecCnt;
|
|
UINT8 NumFATs;
|
|
UINT32 FATSz;
|
|
UINT32 RootClus;
|
|
UINT32 TotClus;
|
|
UINT16 RootEntCnt;
|
|
BOOLEAN IsFAT32;
|
|
UINT32 ClusSize;
|
|
|
|
void *FatBuf; // cached FAT
|
|
UINT32 FATEntries;
|
|
UINT32 *FAT32;
|
|
UINT16 *FAT16;
|
|
};
|
|
|
|
static UINT64 clus_to_lba(struct fat_fs *fs, UINT32 Clus) {
|
|
UINT32 RootSecs = fs->IsFAT32 ? 0
|
|
: ((fs->RootEntCnt * 32) + fs->BytsPerSec - 1) / fs->BytsPerSec;
|
|
UINT32 FirstDataSec = fs->RsvdSecCnt + fs->NumFATs * fs->FATSz + RootSecs;
|
|
return fs->PartLBA + FirstDataSec + (UINT64)(Clus - 2) * fs->SecPerClus;
|
|
}
|
|
|
|
static UINT32 fat_next(struct fat_fs *fs, UINT32 Clus) {
|
|
if (fs->IsFAT32) {
|
|
if (Clus >= fs->FATEntries) return 0x0FFFFFF8;
|
|
UINT32 v = fs->FAT32[Clus] & 0x0FFFFFFF;
|
|
if (v == 0x00000000) return 0;
|
|
if (v >= 0x0FFFFFF8) return 0x0FFFFFF8;
|
|
return v;
|
|
} else {
|
|
if (Clus >= fs->FATEntries) return 0xFFF8;
|
|
UINT16 v = fs->FAT16[Clus];
|
|
if (v == 0x0000) return 0;
|
|
if (v >= 0xFFF8) return 0x0FFFFFF8;
|
|
return v;
|
|
}
|
|
}
|
|
|
|
static EFI_STATUS fat_init(struct fat_fs *fs, struct block_dev *dev, UINT64 PartLBA) {
|
|
fs->Dev = dev;
|
|
fs->PartLBA = PartLBA;
|
|
|
|
UINT8 *Buf = (UINT8*)kmalloc(dev->BlockSize);
|
|
if (!Buf) return EFI_OUT_OF_RESOURCES;
|
|
|
|
EFI_STATUS st = blk_read(dev, PartLBA, 1, Buf);
|
|
if (EFI_ERROR(st)) { kfree(Buf); return st; }
|
|
|
|
if (*(UINT16*)(Buf + 510) != MBR_SIGNATURE) { kfree(Buf); return EFI_UNSUPPORTED; }
|
|
|
|
struct BPB *bpb = (struct BPB*)Buf;
|
|
fs->BytsPerSec = bpb->BytsPerSec;
|
|
fs->SecPerClus = bpb->SecPerClus;
|
|
fs->RsvdSecCnt = bpb->RsvdSecCnt;
|
|
fs->NumFATs = bpb->NumFATs;
|
|
fs->RootEntCnt = bpb->RootEntCnt;
|
|
|
|
UINT32 TotSec = bpb->TotSec16 ? bpb->TotSec16 : bpb->TotSec32;
|
|
UINT32 RootSecs = ((fs->RootEntCnt * 32) + fs->BytsPerSec - 1) / fs->BytsPerSec;
|
|
UINT32 FATSz = bpb->FATSz16 ? bpb->FATSz16 : bpb->FATSz32;
|
|
fs->FATSz = FATSz;
|
|
|
|
UINT32 FirstDataSec = fs->RsvdSecCnt + fs->NumFATs * FATSz + RootSecs;
|
|
fs->TotClus = (TotSec - FirstDataSec) / fs->SecPerClus;
|
|
|
|
if (fs->TotClus < 4085) {
|
|
serial_write("Sylva: FS: FAT12 unsupported\n");
|
|
kfree(Buf);
|
|
return EFI_UNSUPPORTED;
|
|
} else if (fs->TotClus < 65525) {
|
|
fs->IsFAT32 = FALSE;
|
|
fs->FATEntries = FATSz * fs->BytsPerSec / 2;
|
|
} else {
|
|
fs->IsFAT32 = TRUE;
|
|
fs->RootClus = bpb->RootClus;
|
|
fs->FATEntries = FATSz * fs->BytsPerSec / 4;
|
|
}
|
|
|
|
fs->ClusSize = fs->BytsPerSec * fs->SecPerClus;
|
|
|
|
// Cache FAT
|
|
UINTN FATBytes = FATSz * fs->BytsPerSec;
|
|
fs->FatBuf = kmalloc(FATBytes);
|
|
if (!fs->FatBuf) { kfree(Buf); return EFI_OUT_OF_RESOURCES; }
|
|
|
|
st = blk_read(dev, PartLBA + fs->RsvdSecCnt, FATSz, fs->FatBuf);
|
|
if (EFI_ERROR(st)) { kfree(Buf); kfree(fs->FatBuf); return st; }
|
|
|
|
if (fs->IsFAT32) {
|
|
fs->FAT32 = (UINT32*)fs->FatBuf;
|
|
fs->FAT16 = NULL;
|
|
} else {
|
|
fs->FAT16 = (UINT16*)fs->FatBuf;
|
|
fs->FAT32 = NULL;
|
|
}
|
|
|
|
kfree(Buf);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
// ---- LFN helpers ----
|
|
|
|
static UINT8 lfn_checksum(const UINT8 *SFN) {
|
|
UINT8 sum = 0;
|
|
for (SSINT32 i = 0; i < 11; i++)
|
|
sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1) + SFN[i];
|
|
return sum;
|
|
}
|
|
|
|
#define LFN_MAX_FRAGS 20
|
|
#define LFN_FRAG_SIZE 13
|
|
|
|
struct lfn_state {
|
|
CHAR16 frags[LFN_MAX_FRAGS][LFN_FRAG_SIZE + 1];
|
|
UINTN count;
|
|
UINT8 checksum;
|
|
};
|
|
|
|
static void lfn_reset(struct lfn_state *lfn) {
|
|
lfn->count = 0;
|
|
lfn->checksum = 0;
|
|
}
|
|
|
|
static void lfn_add(struct lfn_state *lfn, const UINT8 *E) {
|
|
if (lfn->count >= LFN_MAX_FRAGS) return;
|
|
lfn->checksum = E[13];
|
|
UINTN pos = 0;
|
|
BOOLEAN done = FALSE;
|
|
for (SSINT32 i = 0; i < 5 && pos < LFN_FRAG_SIZE && !done; i++) {
|
|
CHAR16 c = *(const UINT16*)(E + 1 + i * 2);
|
|
if (c == 0x0000 || c == 0xFFFF) { done = TRUE; break; }
|
|
lfn->frags[lfn->count][pos++] = c;
|
|
}
|
|
for (SSINT32 i = 0; i < 6 && pos < LFN_FRAG_SIZE && !done; i++) {
|
|
CHAR16 c = *(const UINT16*)(E + 14 + i * 2);
|
|
if (c == 0x0000 || c == 0xFFFF) { done = TRUE; break; }
|
|
lfn->frags[lfn->count][pos++] = c;
|
|
}
|
|
for (SSINT32 i = 0; i < 2 && pos < LFN_FRAG_SIZE && !done; i++) {
|
|
CHAR16 c = *(const UINT16*)(E + 28 + i * 2);
|
|
if (c == 0x0000 || c == 0xFFFF) { done = TRUE; break; }
|
|
lfn->frags[lfn->count][pos++] = c;
|
|
}
|
|
lfn->frags[lfn->count][pos] = 0;
|
|
lfn->count++;
|
|
}
|
|
|
|
// Fragments arrive in reverse order (seq=N first, seq=1 last).
|
|
// Concatenate from last to first to get correct name order.
|
|
static void lfn_build(struct lfn_state *lfn, CHAR16 *out, UINTN out_size) {
|
|
UINTN pos = 0;
|
|
for (UINTN i = lfn->count; i > 0; i--) {
|
|
for (UINTN j = 0; lfn->frags[i - 1][j] && pos < out_size - 1; j++)
|
|
out[pos++] = lfn->frags[i - 1][j];
|
|
}
|
|
out[pos] = 0;
|
|
}
|
|
|
|
static void sfn_to_name(const UINT8 *E, CHAR16 *out, UINTN out_size) {
|
|
UINTN pos = 0;
|
|
for (SSINT32 i = 0; i < 8 && pos < out_size - 1; i++)
|
|
if (E[i] != ' ') out[pos++] = E[i];
|
|
UINTN ext_start = pos;
|
|
BOOLEAN has_ext = FALSE;
|
|
for (SSINT32 i = 8; i < 11 && pos < out_size - 1; i++) {
|
|
if (E[i] != ' ') {
|
|
if (!has_ext) { out[pos++] = '.'; has_ext = TRUE; ext_start = pos; }
|
|
out[pos++] = E[i];
|
|
}
|
|
}
|
|
// If extension is empty but we added a dot, remove it
|
|
if (has_ext && pos == ext_start) pos--;
|
|
out[pos] = 0;
|
|
}
|
|
|
|
// ---- Directory reading ----
|
|
|
|
typedef void (*dir_cb)(void *Ctx, const CHAR16 *Name, UINT8 Attr,
|
|
UINT32 Size, UINT32 FirstClus);
|
|
|
|
// Returns TRUE when end-of-directory reached
|
|
static BOOLEAN process_sector(struct fat_fs *fs, UINT8 *Buf,
|
|
struct lfn_state *lfn,
|
|
dir_cb Callback, void *Ctx) {
|
|
for (UINTN off = 0; off < fs->BytsPerSec; off += 32) {
|
|
UINT8 *E = Buf + off;
|
|
|
|
if (E[0] == 0x00) return TRUE;
|
|
if (E[0] == 0xE5) { lfn_reset(lfn); continue; }
|
|
|
|
UINT8 Attr = E[11];
|
|
|
|
if ((Attr & 0x3F) == 0x0F) {
|
|
if (E[0] & 0x40) lfn_reset(lfn);
|
|
lfn_add(lfn, E);
|
|
continue;
|
|
}
|
|
|
|
// SFN entry
|
|
CHAR16 Name[256];
|
|
BOOLEAN use_lfn = FALSE;
|
|
if (lfn->count > 0 && lfn_checksum(E) == lfn->checksum) {
|
|
lfn_build(lfn, Name, 256);
|
|
use_lfn = TRUE;
|
|
}
|
|
if (!use_lfn) sfn_to_name(E, Name, 256);
|
|
|
|
// Skip . and ..
|
|
if (E[0] == 0x2E) { lfn_reset(lfn); continue; }
|
|
|
|
UINT32 Size = *(const UINT32*)(E + 28);
|
|
UINT32 FirstClus;
|
|
if (fs->IsFAT32)
|
|
FirstClus = ((UINT32)*(const UINT16*)(E + 20) << 16)
|
|
| *(const UINT16*)(E + 26);
|
|
else
|
|
FirstClus = *(const UINT16*)(E + 26);
|
|
|
|
Callback(Ctx, Name, Attr, Size, FirstClus);
|
|
lfn_reset(lfn);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static void read_directory(struct fat_fs *fs, UINT32 Cluster,
|
|
dir_cb Callback, void *Ctx) {
|
|
UINT8 *Buf = (UINT8*)kmalloc(fs->BytsPerSec);
|
|
if (!Buf) return;
|
|
|
|
struct lfn_state lfn;
|
|
lfn_reset(&lfn);
|
|
|
|
if (fs->IsFAT32) {
|
|
UINT32 Clus = Cluster;
|
|
while (Clus >= 2 && Clus < 0x0FFFFFF8) {
|
|
UINT64 BaseLBA = clus_to_lba(fs, Clus);
|
|
for (UINTN s = 0; s < fs->SecPerClus; s++) {
|
|
if (EFI_ERROR(blk_read(fs->Dev, BaseLBA + s, 1, Buf)))
|
|
goto done;
|
|
if (process_sector(fs, Buf, &lfn, Callback, Ctx))
|
|
goto done;
|
|
}
|
|
Clus = fat_next(fs, Clus);
|
|
}
|
|
} else if (Cluster == 0) {
|
|
UINT32 RootSecs = ((fs->RootEntCnt * 32) + fs->BytsPerSec - 1) / fs->BytsPerSec;
|
|
UINT64 RootLBA = fs->PartLBA + fs->RsvdSecCnt + fs->NumFATs * fs->FATSz;
|
|
for (UINTN s = 0; s < RootSecs; s++) {
|
|
if (EFI_ERROR(blk_read(fs->Dev, RootLBA + s, 1, Buf)))
|
|
goto done;
|
|
if (process_sector(fs, Buf, &lfn, Callback, Ctx))
|
|
goto done;
|
|
}
|
|
} else {
|
|
UINT32 Clus = Cluster;
|
|
while (Clus >= 2 && Clus < 0x0FFFFFF8) {
|
|
UINT64 BaseLBA = clus_to_lba(fs, Clus);
|
|
for (UINTN s = 0; s < fs->SecPerClus; s++) {
|
|
if (EFI_ERROR(blk_read(fs->Dev, BaseLBA + s, 1, Buf)))
|
|
goto done;
|
|
if (process_sector(fs, Buf, &lfn, Callback, Ctx))
|
|
goto done;
|
|
}
|
|
Clus = fat_next(fs, Clus);
|
|
}
|
|
}
|
|
|
|
done:
|
|
kfree(Buf);
|
|
}
|
|
|
|
// ---- Public API ----
|
|
|
|
static struct fat_fs g_fs;
|
|
static BOOLEAN g_fs_inited = FALSE;
|
|
|
|
// Recursive listing support
|
|
|
|
struct list_ctx {
|
|
struct fat_fs *fs;
|
|
SSINT32 depth;
|
|
};
|
|
|
|
static void name_to_ascii(const CHAR16 *Name, char *Ascii, UINTN ascii_sz) {
|
|
wstr_to_ascii(Ascii, (WString)Name, ascii_sz);
|
|
}
|
|
|
|
static void list_callback(void *ctx, const CHAR16 *Name, UINT8 Attr,
|
|
UINT32 Size, UINT32 FirstClus) {
|
|
struct list_ctx *lc = (struct list_ctx*)ctx;
|
|
for (SSINT32 i = 0; i < lc->depth; i++) serial_write(" ");
|
|
|
|
serial_write(Attr & 0x10 ? "[DIR] " : "[FILE] ");
|
|
|
|
char ascii[256];
|
|
name_to_ascii(Name, ascii, sizeof(ascii));
|
|
serial_write(ascii);
|
|
|
|
if (!(Attr & 0x10)) {
|
|
serial_write(" (");
|
|
serial_write_hex(Size);
|
|
serial_write(" bytes)");
|
|
}
|
|
serial_write("\n");
|
|
|
|
if ((Attr & 0x10) && lc->depth < 8) {
|
|
struct list_ctx sub = { lc->fs, lc->depth + 1 };
|
|
read_directory(lc->fs, FirstClus, list_callback, &sub);
|
|
}
|
|
}
|
|
|
|
EFI_STATUS fs_init() {
|
|
serial_write("Sylva: FS: init block...\n");
|
|
static struct block_dev dev;
|
|
EFI_STATUS st = blk_init(&dev);
|
|
if (EFI_ERROR(st)) {
|
|
serial_write("Sylva: FS: no block device!\n");
|
|
return st;
|
|
}
|
|
|
|
serial_write("Sylva: FS: block size = ");
|
|
serial_write_hex(dev.BlockSize);
|
|
serial_write("\n");
|
|
|
|
UINT64 PartLBA = 0;
|
|
st = find_gpt_partition(&dev, &PartLBA);
|
|
if (EFI_ERROR(st)) {
|
|
serial_write("Sylva: FS: no GPT, trying MBR...\n");
|
|
st = find_mbr_partition(&dev, &PartLBA);
|
|
}
|
|
if (EFI_ERROR(st)) {
|
|
serial_write("Sylva: FS: no partition table, assuming super-floppy...\n");
|
|
PartLBA = 0;
|
|
}
|
|
|
|
serial_write("Sylva: FS: partition LBA = ");
|
|
serial_write_hex(PartLBA);
|
|
serial_write("\n");
|
|
|
|
st = fat_init(&g_fs, &dev, PartLBA);
|
|
if (EFI_ERROR(st)) {
|
|
serial_write("Sylva: FS: FAT init failed!\n");
|
|
return st;
|
|
}
|
|
|
|
serial_write("Sylva: FS: FAT");
|
|
serial_write(g_fs.IsFAT32 ? "32" : "16");
|
|
serial_write(" ready, cluster size = ");
|
|
serial_write_hex(g_fs.ClusSize);
|
|
serial_write("\n");
|
|
|
|
g_fs_inited = TRUE;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
void fs_list() {
|
|
if (!g_fs_inited) { serial_write("FS not initialized\n"); return; }
|
|
UINT32 Root = g_fs.IsFAT32 ? g_fs.RootClus : 0;
|
|
struct list_ctx lc = { &g_fs, 0 };
|
|
read_directory(&g_fs, Root, list_callback, &lc);
|
|
}
|
|
|
|
// ---- File reading ----
|
|
|
|
struct find_ctx {
|
|
const CHAR16 *Target;
|
|
BOOLEAN Found;
|
|
UINT32 Cluster;
|
|
UINT32 Size;
|
|
UINT8 Attr;
|
|
};
|
|
|
|
static BOOLEAN name_match(const CHAR16 *a, const CHAR16 *b) {
|
|
while (*a && *b) {
|
|
CHAR16 ca = *a, cb = *b;
|
|
if (ca >= L'A' && ca <= L'Z') ca += 32;
|
|
if (cb >= L'A' && cb <= L'Z') cb += 32;
|
|
if (ca != cb) return FALSE;
|
|
a++; b++;
|
|
}
|
|
return *a == 0 && *b == 0;
|
|
}
|
|
|
|
static void find_callback(void *ctx, const CHAR16 *Name, UINT8 Attr,
|
|
UINT32 Size, UINT32 FirstClus) {
|
|
struct find_ctx *fc = (struct find_ctx*)ctx;
|
|
if (fc->Found) return;
|
|
if (name_match(fc->Target, Name)) {
|
|
fc->Found = TRUE;
|
|
fc->Cluster = FirstClus;
|
|
fc->Size = Size;
|
|
fc->Attr = Attr;
|
|
}
|
|
}
|
|
|
|
EFI_STATUS fs_read(WString Path, void **Buffer, UINTN *Size) {
|
|
if (!g_fs_inited) return EFI_NOT_READY;
|
|
|
|
const CHAR16 *p = Path;
|
|
while (*p == L'\\') p++;
|
|
|
|
UINT32 CurClus = g_fs.IsFAT32 ? g_fs.RootClus : 0;
|
|
|
|
CHAR16 Comp[256];
|
|
while (*p) {
|
|
UINTN ci = 0;
|
|
while (*p && *p != L'\\' && ci < 255) Comp[ci++] = *p++;
|
|
Comp[ci] = 0;
|
|
while (*p == L'\\') p++;
|
|
|
|
struct find_ctx fc;
|
|
fc.Target = Comp;
|
|
fc.Found = FALSE;
|
|
read_directory(&g_fs, CurClus, find_callback, &fc);
|
|
if (!fc.Found) return EFI_NOT_FOUND;
|
|
|
|
if (*p == 0 && !(fc.Attr & 0x10)) {
|
|
UINT32 FileSz = fc.Size;
|
|
void *Buf = kmalloc(FileSz ? FileSz : 1);
|
|
if (!Buf) return EFI_OUT_OF_RESOURCES;
|
|
|
|
if (FileSz > 0) {
|
|
UINT32 Clus = fc.Cluster;
|
|
UINTN Offset = 0;
|
|
UINTN ClusBytes = g_fs.ClusSize;
|
|
|
|
while (Clus >= 2 && Clus < 0x0FFFFFF8 && Offset < FileSz) {
|
|
UINT64 LBA = clus_to_lba(&g_fs, Clus);
|
|
for (UINTN s = 0; s < g_fs.SecPerClus && Offset < FileSz; s++) {
|
|
UINTN Part = g_fs.BytsPerSec;
|
|
if (Part > FileSz - Offset) Part = FileSz - Offset;
|
|
EFI_STATUS st = blk_read(g_fs.Dev, LBA + s, 1,
|
|
(UINT8*)Buf + Offset);
|
|
if (EFI_ERROR(st)) { kfree(Buf); return st; }
|
|
Offset += Part;
|
|
}
|
|
Clus = fat_next(&g_fs, Clus);
|
|
}
|
|
}
|
|
|
|
*Buffer = Buf;
|
|
*Size = FileSz;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
if (!(fc.Attr & 0x10)) return EFI_NOT_FOUND;
|
|
CurClus = fc.Cluster;
|
|
}
|
|
|
|
return EFI_NOT_FOUND;
|
|
}
|