[feat] FASTER READ!!!
This commit is contained in:
@@ -7,28 +7,52 @@ x86_64 UEFI hobby OS. C17 boot → C++17 kernel, ELF64 → PE32+ via objcopy.
|
||||
| Command | What |
|
||||
|---------|------|
|
||||
| `make` | Build `build/BOOTX64.EFI` + `build/Kernel.elf` |
|
||||
| `make run` | QEMU + OVMF, serial 1 → `serial.log`, serial 2 → stdio, paused for GDB (`:1234`) |
|
||||
| `make vdir` | Stage both binaries into `vdir/EFI/BOOT/` for manual boot |
|
||||
| `make clean` | **Always before commit** — untracked build artifacts leak outside `build/` |
|
||||
| `make disk` | Build FAT32 disk image (128 MiB) only — no QEMU |
|
||||
| `make run` | `make disk` + QEMU/OVMF. Serial 1 → `serial.log` (gitignored), paused for GDB (`:1234`) |
|
||||
| `make vdir` | Stage `BOOTX64.EFI` + `Kernel.elf` + `resources/` into `vdir/EFI/BOOT/` for manual boot |
|
||||
| `make clean` | wipes `build/`, `vdir/`, and `serial.log` |
|
||||
|
||||
**Prereqs for `make run`:** `qemu-system-x86_64`, OVMF at `/usr/share/ovmf/OVMF.fd`, `mtools` (`mformat`/`mmd`/`mcopy`). QEMU starts paused — connect GDB to `:1234` to proceed. Kernel debug output goes to `serial.log`.
|
||||
|
||||
## Architecture
|
||||
|
||||
- Two-stage boot: `boot.c` (UEFI app) loads `Kernel.elf` from FAT volume, parses ELF PHDRs, jumps to entry
|
||||
- `kernel/entry.cpp` — `_start` saves `SystemTable`, calls `kernel_main()`
|
||||
- `kernel/main.cpp` — `extern "C" kernel_main()`: init GOP → serial → PMM → heap → idle (`hlt`)
|
||||
- `kernel/serial.cpp` — UEFI serial protocol wrapper (write/read/hex)
|
||||
- `kernel/memory/pmm.cpp` — bitmap + free-list physical page allocator
|
||||
- `kernel/memory/heap.cpp` — kmalloc/kfree/kcalloc/krealloc (first-fit w/ coalescing)
|
||||
- `graphics/` — GOP framebuffer, `fonts/` — Hankaku 8×16 font via `pf_print()`
|
||||
- `efi/` — bundled gnu-efi sources: `gnuefi/` (crt0, lds, reloc), `lib/`, `inc/`
|
||||
- Kernel linked at `0x100000` (`kernel/kernel.ld`)
|
||||
`boot.c` is legacy UEFI glue — loads `Kernel.elf` from the FAT volume, parses ELF PHDRs, jumps to entry. Don't refactor unless touching the boot stage. Bundled gnu-efi sources live in `efi/`.
|
||||
|
||||
**Kernel** lives in `kernel/`. Boot calls `_start` (`kernel/entry.cpp:8`), which saves `EFI_SYSTEM_TABLE*` in global `ST`, then `extern "C" kernel_main()` (`kernel/main.cpp:73`) runs:
|
||||
|
||||
1. `init_gop()` → `gfx_init(GOP)` framebuffer (`graphics/context.cpp`)
|
||||
2. `init_serial()` → locate SERIAL_IO protocol
|
||||
3. `pmm_init()` — bitmap + free-list page allocator (`kernel/memory/pmm.cpp`)
|
||||
4. `init_heap()` — kmalloc/kfree/kcalloc/krealloc, first-fit w/ coalescing (`kernel/memory/heap.cpp`)
|
||||
5. `fs_init()` / `fs_list()` — reads FAT volume via UEFI (`kernel/fs.cpp`)
|
||||
6. `gdt_init()` → `idt_init()` → `pic_init()` → `pit_init()` (`kernel/interrupt/`)
|
||||
7. `ASM("sti")` — interrupts on
|
||||
8. `layer_init()` + `layer_create(...)` for desktop + windows (`kernel/graphics/layer.cpp`)
|
||||
9. `task_create("compositor", layer_compositor_task)`
|
||||
10. `scheduler_run()` — never returns. Preemptive, 100 Hz PIT, 5-tick time slice (`kernel/scheduler/`)
|
||||
|
||||
PIT IRQ 0 dispatches to `pit_irq_handler` → `scheduler_tick` via the IRQ handler in `kernel/main.cpp:56`.
|
||||
|
||||
## `include/common.h` — use these, not raw equivalents
|
||||
|
||||
Always `#include <common.h>` (it pulls in `<types.h>` + `<string_utils.h>` and defines the `ASM` macro):
|
||||
|
||||
| Provided | Use instead of |
|
||||
|----------|----------------|
|
||||
| `SSINT8/16/32/64`, `SUINT8/16/32/64` | raw `int*_t` / `uint*_t` |
|
||||
| `String` (`const char*`), `WString` (`const uint16_t*`) | raw `char*` / `CHAR16*` |
|
||||
| `str_len`, `str_cmp`, `str_eq`, `str_copy` | hand-rolled loops on `char*` |
|
||||
| `wstr_len`, `wstr_cmp`, `wstr_eq`, `wstr_copy`, `wstr_to_ascii` | hand-rolled loops on `CHAR16*` |
|
||||
| `mem_set`, `mem_copy` | hand-rolled byte loops |
|
||||
| `ASM(...)` | raw `asm volatile(...)` — e.g. `ASM("cli")`, `ASM("hlt")`, `ASM("sti")` |
|
||||
|
||||
All `string_utils.h` helpers are `static inline` — header-only, no link issues. New kernel code should reach for these before adding anything new to `string_utils.h`.
|
||||
|
||||
## Conventions
|
||||
|
||||
- Commits: `[type] message` with types `feat`, `fix`, `chore`, `docs` (git log)
|
||||
- `#define ASM asm volatile` in `include/common.h`
|
||||
- C17 (`boot.c`), C++17 (`kernel/`) — `-ffreestanding -fno-stack-protector -fshort-wchar -mno-red-zone`
|
||||
- `uefi_call_wrapper` required for all UEFI protocol calls (omission → page-fault)
|
||||
- `kernel_main` must be `extern "C"` (C→C++ linkage)
|
||||
- QEMU starts paused: connect GDB to `:1234` to proceed
|
||||
- No tests, no CI, no lint config
|
||||
- **Commits:** `[type] message` — types: `feat`, `fix`, `chore`, `docs`, `refactor` (see `git log`).
|
||||
- **Languages:** C17 (`boot.c`), C++17 (`kernel/`) — `-ffreestanding -fno-stack-protector -fshort-wchar -mno-red-zone -fcf-protection=none`. Kernel adds `-DGNU_EFI_USE_MS_ABI -fno-pic`.
|
||||
- **`kernel_main` must be `extern "C"`** — `boot.c` (C) calls into C++ (`kernel/entry.cpp:6`).
|
||||
- **All UEFI protocol calls must use `uefi_call_wrapper`** — calling the member fn directly page-faults in long mode. See `kernel/main.cpp:23-24` and throughout.
|
||||
- **Kernel linked at `0x100000`** (`kernel/kernel.ld`); `.bss` start/end symbols exposed as `__bss_start` / `__bss_end`.
|
||||
- **No tests, no CI, no lint config** — `make` is the only verification step. Boot by hand in QEMU to confirm kernel output in `serial.log`.
|
||||
|
||||
@@ -5,3 +5,4 @@
|
||||
EFI_STATUS fs_init();
|
||||
void fs_list();
|
||||
EFI_STATUS fs_read(WString Path, void **Buffer, UINTN *Size);
|
||||
EFI_STATUS fs_write(WString Path, const void *Data, UINTN Size, UINTN Offset);
|
||||
|
||||
+289
-22
@@ -144,6 +144,13 @@ static EFI_STATUS blk_read(struct block_dev *dev, UINT64 LBA,
|
||||
Sectors * dev->BlockSize, Buf);
|
||||
}
|
||||
|
||||
static EFI_STATUS blk_write(struct block_dev *dev, UINT64 LBA,
|
||||
UINTN Sectors, const void *Buf) {
|
||||
return uefi_call_wrapper(dev->Bio->WriteBlocks, 5,
|
||||
dev->Bio, dev->Bio->Media->MediaId, LBA,
|
||||
Sectors * dev->BlockSize, (void*)Buf);
|
||||
}
|
||||
|
||||
// ---- Partition detection ----
|
||||
|
||||
static EFI_STATUS find_gpt_partition(struct block_dev *dev, UINT64 *StartLBA) {
|
||||
@@ -435,10 +442,10 @@ static void sfn_to_name(const UINT8 *E, CHAR16 *out, UINTN out_size) {
|
||||
// ---- Directory reading ----
|
||||
|
||||
typedef void (*dir_cb)(void *Ctx, const CHAR16 *Name, UINT8 Attr,
|
||||
UINT32 Size, UINT32 FirstClus);
|
||||
UINT32 Size, UINT32 FirstClus, UINTN EntryOff);
|
||||
|
||||
// Returns TRUE when end-of-directory reached
|
||||
static BOOLEAN process_sector(struct fat_fs *fs, UINT8 *Buf,
|
||||
static BOOLEAN process_sector(struct fat_fs *fs, UINT8 *Buf, UINTN SectBase,
|
||||
struct lfn_state *lfn,
|
||||
dir_cb Callback, void *Ctx) {
|
||||
for (UINTN off = 0; off < fs->BytsPerSec; off += 32) {
|
||||
@@ -475,7 +482,7 @@ static BOOLEAN process_sector(struct fat_fs *fs, UINT8 *Buf,
|
||||
else
|
||||
FirstClus = *(const UINT16*)(E + 26);
|
||||
|
||||
Callback(Ctx, Name, Attr, Size, FirstClus);
|
||||
Callback(Ctx, Name, Attr, Size, FirstClus, SectBase + off);
|
||||
lfn_reset(lfn);
|
||||
}
|
||||
return FALSE;
|
||||
@@ -483,7 +490,8 @@ static BOOLEAN process_sector(struct fat_fs *fs, UINT8 *Buf,
|
||||
|
||||
static void read_directory(struct fat_fs *fs, UINT32 Cluster,
|
||||
dir_cb Callback, void *Ctx) {
|
||||
UINT8 *Buf = (UINT8*)kmalloc(fs->BytsPerSec);
|
||||
UINTN ClusBytes = fs->ClusSize;
|
||||
UINT8 *Buf = (UINT8*)kmalloc(ClusBytes);
|
||||
if (!Buf) return;
|
||||
|
||||
struct lfn_state lfn;
|
||||
@@ -493,10 +501,12 @@ static void read_directory(struct fat_fs *fs, UINT32 Cluster,
|
||||
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)))
|
||||
if (EFI_ERROR(blk_read(fs->Dev, BaseLBA, fs->SecPerClus, Buf)))
|
||||
goto done;
|
||||
if (process_sector(fs, Buf, &lfn, Callback, Ctx))
|
||||
for (UINTN s = 0; s < fs->SecPerClus; s++) {
|
||||
if (process_sector(fs, Buf + s * fs->BytsPerSec,
|
||||
s * fs->BytsPerSec,
|
||||
&lfn, Callback, Ctx))
|
||||
goto done;
|
||||
}
|
||||
Clus = fat_next(fs, Clus);
|
||||
@@ -505,19 +515,25 @@ static void read_directory(struct fat_fs *fs, UINT32 Cluster,
|
||||
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)))
|
||||
if (EFI_ERROR(blk_read(fs->Dev, RootLBA + s, 1, Buf + s * fs->BytsPerSec)))
|
||||
goto done;
|
||||
if (process_sector(fs, Buf, &lfn, Callback, Ctx))
|
||||
}
|
||||
for (UINTN s = 0; s < RootSecs; s++) {
|
||||
if (process_sector(fs, Buf + s * fs->BytsPerSec,
|
||||
s * fs->BytsPerSec,
|
||||
&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)))
|
||||
if (EFI_ERROR(blk_read(fs->Dev, BaseLBA, fs->SecPerClus, Buf)))
|
||||
goto done;
|
||||
if (process_sector(fs, Buf, &lfn, Callback, Ctx))
|
||||
for (UINTN s = 0; s < fs->SecPerClus; s++) {
|
||||
if (process_sector(fs, Buf + s * fs->BytsPerSec,
|
||||
s * fs->BytsPerSec,
|
||||
&lfn, Callback, Ctx))
|
||||
goto done;
|
||||
}
|
||||
Clus = fat_next(fs, Clus);
|
||||
@@ -545,7 +561,7 @@ static void name_to_ascii(const CHAR16 *Name, char *Ascii, UINTN ascii_sz) {
|
||||
}
|
||||
|
||||
static void list_callback(void *ctx, const CHAR16 *Name, UINT8 Attr,
|
||||
UINT32 Size, UINT32 FirstClus) {
|
||||
UINT32 Size, UINT32 FirstClus, UINTN EntryOff) {
|
||||
struct list_ctx *lc = (struct list_ctx*)ctx;
|
||||
for (SSINT32 i = 0; i < lc->depth; i++) serial_write(" ");
|
||||
|
||||
@@ -641,7 +657,7 @@ static BOOLEAN name_match(const CHAR16 *a, const CHAR16 *b) {
|
||||
}
|
||||
|
||||
static void find_callback(void *ctx, const CHAR16 *Name, UINT8 Attr,
|
||||
UINT32 Size, UINT32 FirstClus) {
|
||||
UINT32 Size, UINT32 FirstClus, UINTN EntryOff) {
|
||||
struct find_ctx *fc = (struct find_ctx*)ctx;
|
||||
if (fc->Found) return;
|
||||
if (name_match(fc->Target, Name)) {
|
||||
@@ -684,16 +700,24 @@ EFI_STATUS fs_read(WString Path, void **Buffer, UINTN *Size) {
|
||||
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,
|
||||
// Find longest contiguous run starting at Clus
|
||||
UINT32 RunStart = Clus;
|
||||
UINT32 RunEnd = Clus;
|
||||
UINT32 Next = fat_next(&g_fs, Clus);
|
||||
while (Next == RunEnd + 1 && Next >= 2 && Next < 0x0FFFFFF8) {
|
||||
RunEnd = Next;
|
||||
Next = fat_next(&g_fs, RunEnd);
|
||||
}
|
||||
UINTN RunClus = (UINTN)(RunEnd - RunStart + 1);
|
||||
UINT64 LBA = clus_to_lba(&g_fs, RunStart);
|
||||
UINTN Want = RunClus * ClusBytes;
|
||||
if (Want > FileSz - Offset) Want = FileSz - Offset;
|
||||
UINTN NSecs = (Want + g_fs.BytsPerSec - 1) / g_fs.BytsPerSec;
|
||||
EFI_STATUS st = blk_read(g_fs.Dev, LBA, NSecs,
|
||||
(UINT8*)Buf + Offset);
|
||||
if (EFI_ERROR(st)) { kfree(Buf); return st; }
|
||||
Offset += Part;
|
||||
}
|
||||
Clus = fat_next(&g_fs, Clus);
|
||||
Offset += Want;
|
||||
Clus = Next;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -708,3 +732,246 @@ EFI_STATUS fs_read(WString Path, void **Buffer, UINTN *Size) {
|
||||
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
// ---- FAT allocation ----
|
||||
|
||||
static BOOLEAN fat_entry_free(struct fat_fs *fs, UINT32 Clus) {
|
||||
if (Clus >= fs->FATEntries) return FALSE;
|
||||
if (fs->IsFAT32) return (fs->FAT32[Clus] & 0x0FFFFFFF) == 0;
|
||||
return fs->FAT16[Clus] == 0;
|
||||
}
|
||||
|
||||
static void fat_entry_set(struct fat_fs *fs, UINT32 Clus, UINT32 Val) {
|
||||
if (Clus >= fs->FATEntries) return;
|
||||
if (fs->IsFAT32) fs->FAT32[Clus] = Val;
|
||||
else fs->FAT16[Clus] = (UINT16)Val;
|
||||
}
|
||||
|
||||
// Allocate `Count` free clusters and link them into a chain.
|
||||
// Returns the first cluster of the new chain; the last entry is marked EOF.
|
||||
static EFI_STATUS fat_alloc_chain(struct fat_fs *fs, UINT32 Count, UINT32 *OutFirst) {
|
||||
UINT32 Found = 0;
|
||||
UINT32 First = 0, Prev = 0;
|
||||
UINT32 EOC = fs->IsFAT32 ? 0x0FFFFFFF : 0xFFFF;
|
||||
|
||||
for (UINT32 i = 2; i < fs->FATEntries && Found < Count; i++) {
|
||||
if (!fat_entry_free(fs, i)) continue;
|
||||
if (Found == 0) First = i;
|
||||
else fat_entry_set(fs, Prev, i);
|
||||
Found++;
|
||||
Prev = i;
|
||||
}
|
||||
if (Found < Count) return EFI_OUT_OF_RESOURCES;
|
||||
fat_entry_set(fs, Prev, EOC);
|
||||
*OutFirst = First;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
// Write the in-memory FAT cache back to all FAT copies on disk.
|
||||
static EFI_STATUS fat_flush(struct fat_fs *fs) {
|
||||
for (UINTN f = 0; f < fs->NumFATs; f++) {
|
||||
EFI_STATUS st = blk_write(fs->Dev,
|
||||
fs->PartLBA + fs->RsvdSecCnt + f * fs->FATSz,
|
||||
fs->FATSz, fs->FatBuf);
|
||||
if (EFI_ERROR(st)) return st;
|
||||
}
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
// ---- File writing ----
|
||||
|
||||
struct find_write_ctx {
|
||||
const CHAR16 *Target;
|
||||
BOOLEAN Found;
|
||||
UINT32 Cluster;
|
||||
UINT32 Size;
|
||||
UINT8 Attr;
|
||||
UINTN DirOffset;
|
||||
};
|
||||
|
||||
static void find_write_callback(void *ctx, const CHAR16 *Name, UINT8 Attr,
|
||||
UINT32 Size, UINT32 FirstClus, UINTN EntryOff) {
|
||||
struct find_write_ctx *fc = (struct find_write_ctx*)ctx;
|
||||
if (fc->Found) return;
|
||||
if (name_match(fc->Target, Name)) {
|
||||
fc->Found = TRUE;
|
||||
fc->Cluster = FirstClus;
|
||||
fc->Size = Size;
|
||||
fc->Attr = Attr;
|
||||
fc->DirOffset = EntryOff;
|
||||
}
|
||||
}
|
||||
|
||||
// Update a dirent's size + first-cluster fields in-place inside `DirBuf`
|
||||
// at the given byte offset. For FAT32 we also patch the high cluster word.
|
||||
static void dirent_update_size(void *DirBuf, UINTN EntryOff,
|
||||
UINT32 NewSize, UINT32 FirstClus,
|
||||
BOOLEAN PatchFirstClus, BOOLEAN IsFAT32) {
|
||||
UINT8 *E = (UINT8*)DirBuf + EntryOff;
|
||||
*(UINT32*)(E + 28) = NewSize;
|
||||
if (PatchFirstClus) {
|
||||
if (IsFAT32) {
|
||||
*(UINT16*)(E + 20) = (UINT16)(FirstClus >> 16);
|
||||
*(UINT16*)(E + 26) = (UINT16)(FirstClus & 0xFFFF);
|
||||
} else {
|
||||
*(UINT16*)(E + 26) = (UINT16)(FirstClus & 0xFFFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write [Data, Data+Size) into the file at Path, starting at byte Offset.
|
||||
// Semantics: existing file only (no create); Offset must be <= current size.
|
||||
// New file size = Offset + Size. Trailing clusters beyond the new size are
|
||||
// left allocated (lost) — no truncate support.
|
||||
EFI_STATUS fs_write(WString Path, const void *Data, UINTN Size, UINTN Offset) {
|
||||
if (!g_fs_inited) return EFI_NOT_READY;
|
||||
if (Data == NULL && Size > 0) return EFI_INVALID_PARAMETER;
|
||||
|
||||
const CHAR16 *p = Path;
|
||||
while (*p == L'\\') p++;
|
||||
|
||||
UINT32 CurClus = g_fs.IsFAT32 ? g_fs.RootClus : 0;
|
||||
|
||||
CHAR16 Comp[256];
|
||||
BOOLEAN Resolved = FALSE;
|
||||
UINT32 FileFirst = 0, FileSize = 0, FileAttr = 0;
|
||||
UINTN FileDirOff = 0;
|
||||
|
||||
while (*p) {
|
||||
UINTN ci = 0;
|
||||
while (*p && *p != L'\\' && ci < 255) Comp[ci++] = *p++;
|
||||
Comp[ci] = 0;
|
||||
while (*p == L'\\') p++;
|
||||
|
||||
struct find_write_ctx fc;
|
||||
fc.Target = Comp;
|
||||
fc.Found = FALSE;
|
||||
read_directory(&g_fs, CurClus, find_write_callback, &fc);
|
||||
if (!fc.Found) return EFI_NOT_FOUND;
|
||||
|
||||
if (*p == 0) {
|
||||
if (fc.Attr & 0x10) return EFI_INVALID_PARAMETER;
|
||||
Resolved = TRUE;
|
||||
FileFirst = fc.Cluster;
|
||||
FileSize = fc.Size;
|
||||
FileAttr = fc.Attr;
|
||||
FileDirOff = fc.DirOffset;
|
||||
} else {
|
||||
if (!(fc.Attr & 0x10)) return EFI_NOT_FOUND;
|
||||
CurClus = fc.Cluster;
|
||||
}
|
||||
}
|
||||
if (!Resolved) return EFI_NOT_FOUND;
|
||||
if (Offset > FileSize) return EFI_INVALID_PARAMETER;
|
||||
|
||||
UINT32 NewSize = (UINT32)(Offset + Size);
|
||||
UINT32 OldClusCount = (FileSize + g_fs.ClusSize - 1) / g_fs.ClusSize;
|
||||
UINT32 NewClusCount = (NewSize + g_fs.ClusSize - 1) / g_fs.ClusSize;
|
||||
UINT32 AddClusCount = NewClusCount - OldClusCount;
|
||||
BOOLEAN FileWasEmpty = (FileSize == 0);
|
||||
|
||||
// --- Extend cluster chain if we need more clusters ---
|
||||
if (AddClusCount > 0) {
|
||||
UINT32 NewFirst;
|
||||
EFI_STATUS st = fat_alloc_chain(&g_fs, AddClusCount, &NewFirst);
|
||||
if (EFI_ERROR(st)) return st;
|
||||
|
||||
if (FileWasEmpty) {
|
||||
FileFirst = NewFirst;
|
||||
} else {
|
||||
// Walk to the last cluster of the existing chain
|
||||
UINT32 Cur = FileFirst;
|
||||
UINT32 Next;
|
||||
while (TRUE) {
|
||||
Next = fat_next(&g_fs, Cur);
|
||||
if (Next >= 0x0FFFFFF8) break;
|
||||
Cur = Next;
|
||||
}
|
||||
// Cur is the last cluster; link to new chain
|
||||
fat_entry_set(&g_fs, Cur, NewFirst);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Write data clusters ---
|
||||
if (Size > 0) {
|
||||
// Walk to the cluster containing byte Offset
|
||||
UINT32 Clus = FileFirst;
|
||||
UINTN SkipBytes = Offset;
|
||||
while (SkipBytes >= g_fs.ClusSize) {
|
||||
Clus = fat_next(&g_fs, Clus);
|
||||
SkipBytes -= g_fs.ClusSize;
|
||||
}
|
||||
|
||||
void *ClusBuf = kmalloc(g_fs.ClusSize);
|
||||
if (!ClusBuf) return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
const UINT8 *Src = (const UINT8*)Data;
|
||||
UINTN Remaining = Size;
|
||||
UINTN InClusOff = SkipBytes;
|
||||
|
||||
while (Remaining > 0) {
|
||||
UINT64 LBA = clus_to_lba(&g_fs, Clus);
|
||||
UINTN SpaceInClus = g_fs.ClusSize - InClusOff;
|
||||
UINTN Chunk = Remaining < SpaceInClus ? Remaining : SpaceInClus;
|
||||
|
||||
if (InClusOff > 0 || Chunk < g_fs.ClusSize) {
|
||||
EFI_STATUS rst = blk_read(g_fs.Dev, LBA, g_fs.SecPerClus, ClusBuf);
|
||||
if (EFI_ERROR(rst)) { kfree(ClusBuf); return rst; }
|
||||
mem_copy((UINT8*)ClusBuf + InClusOff, Src, Chunk);
|
||||
EFI_STATUS wst = blk_write(g_fs.Dev, LBA, g_fs.SecPerClus, ClusBuf);
|
||||
if (EFI_ERROR(wst)) { kfree(ClusBuf); return wst; }
|
||||
} else {
|
||||
EFI_STATUS wst = blk_write(g_fs.Dev, LBA, g_fs.SecPerClus, Src);
|
||||
if (EFI_ERROR(wst)) { kfree(ClusBuf); return wst; }
|
||||
}
|
||||
|
||||
Src += Chunk;
|
||||
Remaining -= Chunk;
|
||||
InClusOff = 0;
|
||||
if (Remaining > 0) {
|
||||
Clus = fat_next(&g_fs, Clus);
|
||||
}
|
||||
}
|
||||
|
||||
kfree(ClusBuf);
|
||||
}
|
||||
|
||||
// --- Update dirent (size, and first cluster if file was empty) ---
|
||||
{
|
||||
void *DirBuf;
|
||||
UINT64 DirLBA;
|
||||
UINTN DirNSecs;
|
||||
UINTN DirBufBytes;
|
||||
|
||||
if (g_fs.IsFAT32) {
|
||||
DirLBA = clus_to_lba(&g_fs, CurClus);
|
||||
DirNSecs = g_fs.SecPerClus;
|
||||
DirBufBytes = g_fs.ClusSize;
|
||||
} else if (CurClus == 0) {
|
||||
UINT32 RootSecs = ((g_fs.RootEntCnt * 32) + g_fs.BytsPerSec - 1) / g_fs.BytsPerSec;
|
||||
DirLBA = g_fs.PartLBA + g_fs.RsvdSecCnt + g_fs.NumFATs * g_fs.FATSz;
|
||||
DirNSecs = RootSecs;
|
||||
DirBufBytes = RootSecs * g_fs.BytsPerSec;
|
||||
} else {
|
||||
DirLBA = clus_to_lba(&g_fs, CurClus);
|
||||
DirNSecs = g_fs.SecPerClus;
|
||||
DirBufBytes = g_fs.ClusSize;
|
||||
}
|
||||
|
||||
DirBuf = kmalloc(DirBufBytes);
|
||||
if (!DirBuf) return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
EFI_STATUS rst = blk_read(g_fs.Dev, DirLBA, DirNSecs, DirBuf);
|
||||
if (EFI_ERROR(rst)) { kfree(DirBuf); return rst; }
|
||||
|
||||
dirent_update_size(DirBuf, FileDirOff, NewSize, FileFirst,
|
||||
FileWasEmpty, g_fs.IsFAT32);
|
||||
|
||||
EFI_STATUS wst = blk_write(g_fs.Dev, DirLBA, DirNSecs, DirBuf);
|
||||
kfree(DirBuf);
|
||||
if (EFI_ERROR(wst)) return wst;
|
||||
}
|
||||
|
||||
// --- Flush FAT ---
|
||||
return fat_flush(&g_fs);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user