From 45693c96b50caec757d697e6502b1461e8320fa3 Mon Sep 17 00:00:00 2001 From: pyao12 Date: Fri, 29 May 2026 19:59:41 +0800 Subject: [PATCH] [feat] Simple Filesystem --- Makefile | 9 +- include/fs.h | 6 + kernel/fs.cpp | 709 +++++++++++++++++++++++++++++++++++++++++ kernel/main.cpp | 10 + kernel/memory/heap.cpp | 2 +- kernel/memory/pmm.cpp | 10 + 6 files changed, 741 insertions(+), 5 deletions(-) create mode 100644 include/fs.h create mode 100644 kernel/fs.cpp diff --git a/Makefile b/Makefile index f667d03..c60b38c 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -Iinclude -Iefi/inc -ffreestanding -fno-stack-protector -fno-stack-check -fshort-wchar -mno-red-zone -std=c17 -Wwrite-strings -CXXFLAGS = -Iinclude -Iefi/inc -ffreestanding -fno-stack-protector -fno-stack-check -fshort-wchar -mno-red-zone -maccumulate-outgoing-args -std=c++17 -Wwrite-strings +CFLAGS = -Iinclude -Iefi/inc -ffreestanding -fno-stack-protector -fno-stack-check -fshort-wchar -mno-red-zone -std=c17 -Wwrite-strings -fcf-protection=none +CXXFLAGS = -Iinclude -Iefi/inc -ffreestanding -fno-stack-protector -fno-stack-check -fshort-wchar -mno-red-zone -maccumulate-outgoing-args -std=c++17 -Wwrite-strings -fcf-protection=none LDFLAGS = -shared -Bsymbolic -Tefi/gnuefi/elf_x86_64_efi.lds LDLIBS = --no-undefined @@ -10,11 +10,12 @@ EFI_CFLAGS = -Iefi/inc -Iefi/inc/x86_64 -Iefi/inc/protocol \ -ffreestanding -fno-stack-protector -fno-stack-check \ -fshort-wchar -mno-red-zone -maccumulate-outgoing-args \ -fPIC -fno-strict-aliasing -Wall -Wextra -Wstrict-prototypes \ - -DGNU_EFI_USE_MS_ABI -DCONFIG_x86_64 -std=c11 -O2 -g + -DGNU_EFI_USE_MS_ABI -DCONFIG_x86_64 -std=c11 -O2 -g \ + -fcf-protection=none BOOT_OBJ = build/boot.o -KERNEL_CPP = kernel/entry.cpp kernel/main.cpp kernel/serial.cpp \ +KERNEL_CPP = kernel/entry.cpp kernel/main.cpp kernel/serial.cpp kernel/fs.cpp \ kernel/memory/heap.cpp kernel/memory/pmm.cpp \ graphics/context.cpp graphics/draw.cpp \ fonts/pixel_font.cpp diff --git a/include/fs.h b/include/fs.h new file mode 100644 index 0000000..c7df69f --- /dev/null +++ b/include/fs.h @@ -0,0 +1,6 @@ +#pragma once +#include + +EFI_STATUS fs_init(); +void fs_list(); +EFI_STATUS fs_read(const CHAR16 *Path, void **Buffer, UINTN *Size); diff --git a/kernel/fs.cpp b/kernel/fs.cpp new file mode 100644 index 0000000..87eaed1 --- /dev/null +++ b/kernel/fs.cpp @@ -0,0 +1,709 @@ +#include +#include +#include +#include + +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 (int 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 (int 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 (int 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 (int 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; +}; + +static void lfn_reset(struct lfn_state *lfn) { + lfn->count = 0; +} + +static void lfn_add(struct lfn_state *lfn, const UINT8 *E) { + if (lfn->count >= LFN_MAX_FRAGS) return; + UINTN pos = 0; + for (int i = 0; i < 5 && pos < LFN_FRAG_SIZE; i++) { + CHAR16 c = *(const UINT16*)(E + 1 + i * 2); + if (c == 0x0000 || c == 0xFFFF) { lfn->frags[lfn->count][pos] = 0; return; } + lfn->frags[lfn->count][pos++] = c; + } + for (int i = 0; i < 6 && pos < LFN_FRAG_SIZE; i++) { + CHAR16 c = *(const UINT16*)(E + 14 + i * 2); + if (c == 0x0000 || c == 0xFFFF) { lfn->frags[lfn->count][pos] = 0; return; } + lfn->frags[lfn->count][pos++] = c; + } + for (int i = 0; i < 2 && pos < LFN_FRAG_SIZE; i++) { + CHAR16 c = *(const UINT16*)(E + 28 + i * 2); + if (c == 0x0000 || c == 0xFFFF) { lfn->frags[lfn->count][pos] = 0; return; } + 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 (int 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 (int 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 == 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) == E[13]) { + 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; + int depth; +}; + +static void name_to_ascii(const CHAR16 *Name, char *Ascii, UINTN ascii_sz) { + UINTN i = 0; + for (; Name[i] && i < ascii_sz - 1; i++) { + if (Name[i] >= 0x20 && Name[i] <= 0x7E) + Ascii[i] = (char)Name[i]; + else + Ascii[i] = '?'; + } + Ascii[i] = 0; +} + +static void list_callback(void *ctx, const CHAR16 *Name, UINT8 Attr, + UINT32 Size, UINT32 FirstClus) { + struct list_ctx *lc = (struct list_ctx*)ctx; + for (int 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) { + UINTN i = 0; + for (; a[i] && b[i]; i++) + if (a[i] != b[i]) return FALSE; + return a[i] == 0 && b[i] == 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(const CHAR16 *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; +} diff --git a/kernel/main.cpp b/kernel/main.cpp index 7241513..afac7a3 100644 --- a/kernel/main.cpp +++ b/kernel/main.cpp @@ -6,6 +6,7 @@ #include #include #include +#include extern EFI_SYSTEM_TABLE *ST; @@ -94,6 +95,15 @@ extern "C" void kernel_main() { serial_write("Sylva: memory init done.\n"); + serial_write("Sylva: FS init...\n"); + EFI_STATUS fs_st = fs_init(); + if (EFI_ERROR(fs_st)) { + serial_write("Sylva: FS init FAILED!\n"); + } else { + serial_write("Sylva: root directory listing:\n"); + fs_list(); + } + pf_print("Welcome to Sylva OS!\n"); serial_write(" Kernel prepared well, start loop.\n"); diff --git a/kernel/memory/heap.cpp b/kernel/memory/heap.cpp index 8226a14..5053c7c 100644 --- a/kernel/memory/heap.cpp +++ b/kernel/memory/heap.cpp @@ -112,7 +112,7 @@ void* kmalloc(UINTN size) { split->size = block_sz - alloc_size; // Insert split into free list split->next = block->next; - block->size = alloc_size; + block->size = alloc_size | 1; *prev = split; } else { // Use the whole block diff --git a/kernel/memory/pmm.cpp b/kernel/memory/pmm.cpp index 9483b03..85ae72a 100644 --- a/kernel/memory/pmm.cpp +++ b/kernel/memory/pmm.cpp @@ -133,6 +133,15 @@ EFI_STATUS pmm_init() { bitmap_set((UINTN)p); } + // Reserve low memory (first 4 MB) — UEFI firmware may use it during BS calls + UINT64 low_reserve_pages = 0x400; + for (UINT64 p = 0; p < low_reserve_pages && p < g_pmm.total_pages; p++) { + if (!bitmap_test((UINTN)p)) { + bitmap_set((UINTN)p); + g_pmm.free_pages--; + } + } + // Build free list by linking free pages g_pmm.free_list_head = NULL; void* prev = NULL; @@ -145,6 +154,7 @@ EFI_STATUS pmm_init() { for (UINT64 p = start_page; p < end_page; p++) { if (p >= bm_start_page && p < bm_end_page) continue; + if (p < low_reserve_pages) continue; void* page = (void*)(UINTN)(p * PAGE_SIZE); if (prev) { *(void**)prev = page;