feat: FS-E strict filesystem mode - ext4 rw driver, FAT32 big-disk fix, forced re-login
- CODE/dsk/ext4.c: minimal full ext4 read/write (GPT probe, feature gate, extent depth 0/1 rw, block/inode alloc-free, dir insert, write-verify); boot selftest PASS on p2. Unsupported features panic (FS-E2x), zero fallback. - DSK fat32 write: on-demand FAT entry RMW + chunked free-cluster scan (fixes "no free clusters" on 5.3G ESP); rec_len shrink offset (+4) and chunk alignment fixes. USER.CONF write-verify ok, firstInit flip ok. - First-boot: wizard -> persist -> forced login (FS-E06 if skipped/failed) -> desktop. login.elf loaded via system/user/use fallback. - FirstInit network page: Connect now panics NET-E01 (no wlan stack); Save (config only) is the only path forward. - FirstInit/DSK fatal: ohMyLogo lotus fatal screen (NET-E01 / FS-E series). - Boot screen: Logo.png image removed, "DESHAB P01" text centered only; dcp/xj logos regenerated from TEXTURES/. - run_qemu.bat: main GPT image on SATA ide.0 (AHCI provider must see ext4).
This commit is contained in:
@@ -3,6 +3,9 @@
|
|||||||
#include <utsm/idt.h>
|
#include <utsm/idt.h>
|
||||||
#include <utsm/net.h>
|
#include <utsm/net.h>
|
||||||
#include <utsm/mm.h>
|
#include <utsm/mm.h>
|
||||||
|
#include <utsm/dma.h>
|
||||||
|
#include <utsm/drr.h>
|
||||||
|
#include <utsm/utrw.h>
|
||||||
#include <utsm/panic.h>
|
#include <utsm/panic.h>
|
||||||
|
|
||||||
static void dkm_api_info(const char *msg) {
|
static void dkm_api_info(const char *msg) {
|
||||||
@@ -81,13 +84,57 @@ static dkm_kernel_api g_kernel_api = {
|
|||||||
.mm_map_mmio = 0,
|
.mm_map_mmio = 0,
|
||||||
.mm_unmap_mmio = 0,
|
.mm_unmap_mmio = 0,
|
||||||
.register_apic_eoi = 0,
|
.register_apic_eoi = 0,
|
||||||
.sched = 0
|
.sched = 0,
|
||||||
|
.kapi = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
const dkm_kernel_api *dkm_get_kernel_api(void) {
|
const dkm_kernel_api *dkm_get_kernel_api(void) {
|
||||||
return &g_kernel_api;
|
return &g_kernel_api;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
* D3: 按名导出表(kapi)——自由也要有地图
|
||||||
|
* =================================================================== */
|
||||||
|
|
||||||
|
static const dkm_kapi_entry g_kapi_entries[] = {
|
||||||
|
{ "log.info", (void *)dkm_api_info },
|
||||||
|
{ "log.warn", (void *)dkm_api_warn },
|
||||||
|
{ "log.error", (void *)dkm_api_error },
|
||||||
|
{ "log.panic", (void *)dkm_api_panic },
|
||||||
|
{ "dma.alloc_pages", (void *)dma_alloc_pages },
|
||||||
|
{ "irq.register", (void *)irq_register },
|
||||||
|
{ "irq.vector_alloc", (void *)irq_vector_alloc },
|
||||||
|
{ "irq.vector_free", (void *)irq_vector_free },
|
||||||
|
{ "mm.map_mmio", (void *)mm_map_mmio },
|
||||||
|
{ "mm.unmap_mmio", (void *)mm_unmap_mmio },
|
||||||
|
{ "apic.register_eoi", (void *)idt_register_apic_eoi },
|
||||||
|
{ "drr.snapshot_dirty", (void *)drr_ckpt_snapshot_dirty },
|
||||||
|
{ "drr.rollback_pages", (void *)drr_rollback_pages },
|
||||||
|
{ "utrw.debug_dump", (void *)utrw_debug_dump_page },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int kapi_lookup(const char *name, void **out_fn) {
|
||||||
|
if (!name) return -1;
|
||||||
|
for (u32 i = 0; i < sizeof(g_kapi_entries) / sizeof(g_kapi_entries[0]); i++) {
|
||||||
|
const dkm_kapi_entry *e = &g_kapi_entries[i];
|
||||||
|
const char *a = e->name, *b = name;
|
||||||
|
while (*a && *a == *b) { a++; b++; }
|
||||||
|
if (*a == 0 && *b == 0) {
|
||||||
|
if (out_fn) *out_fn = e->fn;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const dkm_kapi_table g_kapi_table = {
|
||||||
|
.magic = DKM_KAPI_MAGIC,
|
||||||
|
.version = DKM_KAPI_VERSION,
|
||||||
|
.count = sizeof(g_kapi_entries) / sizeof(g_kapi_entries[0]),
|
||||||
|
.entries = g_kapi_entries,
|
||||||
|
.lookup = kapi_lookup,
|
||||||
|
};
|
||||||
|
|
||||||
#include "../arch/x86_64/limine.h"
|
#include "../arch/x86_64/limine.h"
|
||||||
extern volatile struct limine_rsdp_request g_rsdp_request;
|
extern volatile struct limine_rsdp_request g_rsdp_request;
|
||||||
extern volatile struct limine_framebuffer_request g_fb_request;
|
extern volatile struct limine_framebuffer_request g_fb_request;
|
||||||
@@ -127,4 +174,6 @@ void dkm_fill_platform_info(void) {
|
|||||||
/* B7 阶段3: 暴露动态向量分配器(MSI/MSI-X 设备驱动使用) */
|
/* B7 阶段3: 暴露动态向量分配器(MSI/MSI-X 设备驱动使用) */
|
||||||
g_kernel_api.irq_vector_alloc = irq_vector_alloc;
|
g_kernel_api.irq_vector_alloc = irq_vector_alloc;
|
||||||
g_kernel_api.irq_vector_free = irq_vector_free;
|
g_kernel_api.irq_vector_free = irq_vector_free;
|
||||||
|
/* D3: 按名导出表挂入 kernel_api(尾部追加,ABI 兼容) */
|
||||||
|
g_kernel_api.kapi = &g_kapi_table;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
dkm/kernel_api.o: dkm\kernel_api.c include\utsm\dkm.h \
|
dkm/kernel_api.o: dkm\kernel_api.c include\utsm\dkm.h \
|
||||||
include\utsm\types.h include\utsm\dma.h include\utsm\block.h \
|
include\utsm\types.h include\utsm\dma.h include\utsm\block.h \
|
||||||
include\utsm\net.h include\utsm\paging.h include\utsm\log.h \
|
include\utsm\net.h include\utsm\paging.h include\utsm\log.h \
|
||||||
include\utsm\idt.h include\utsm\mm.h include\utsm\panic.h \
|
include\utsm\idt.h include\utsm\mm.h include\utsm\drr.h \
|
||||||
|
include\utsm\config.h include\utsm\utrw.h include\utsm\status.h \
|
||||||
|
include\utsm\capability.h include\utsm\panic.h \
|
||||||
dkm\..\arch\x86_64\limine.h
|
dkm\..\arch\x86_64\limine.h
|
||||||
include\utsm\dkm.h:
|
include\utsm\dkm.h:
|
||||||
include\utsm\types.h:
|
include\utsm\types.h:
|
||||||
@@ -12,5 +14,10 @@ include\utsm\paging.h:
|
|||||||
include\utsm\log.h:
|
include\utsm\log.h:
|
||||||
include\utsm\idt.h:
|
include\utsm\idt.h:
|
||||||
include\utsm\mm.h:
|
include\utsm\mm.h:
|
||||||
|
include\utsm\drr.h:
|
||||||
|
include\utsm\config.h:
|
||||||
|
include\utsm\utrw.h:
|
||||||
|
include\utsm\status.h:
|
||||||
|
include\utsm\capability.h:
|
||||||
include\utsm\panic.h:
|
include\utsm\panic.h:
|
||||||
dkm\..\arch\x86_64\limine.h:
|
dkm\..\arch\x86_64\limine.h:
|
||||||
|
|||||||
Binary file not shown.
@@ -33,6 +33,30 @@
|
|||||||
struct dkm_kernel_api;
|
struct dkm_kernel_api;
|
||||||
struct dkm_driver_handle;
|
struct dkm_driver_handle;
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
* D3: 内核 API 按名导出表(kapi)
|
||||||
|
*
|
||||||
|
* "自由也要有地图。" 固定偏移 struct ABI 之外的按名查找层:
|
||||||
|
* 驱动/用户态可枚举内核服务并按名取函数指针。
|
||||||
|
* 通过 kernel_api 尾部追加的 ->kapi 指针访问(ABI 兼容)。
|
||||||
|
* =================================================================== */
|
||||||
|
typedef struct {
|
||||||
|
const char *name; /* 如 "log.info"、"dma.alloc_pages" */
|
||||||
|
void *fn; /* 服务函数指针(签名见对应服务头) */
|
||||||
|
} dkm_kapi_entry;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u32 magic; /* DKM_KAPI_MAGIC */
|
||||||
|
u32 version; /* DKM_KAPI_VERSION */
|
||||||
|
u32 count; /* entries 数量 */
|
||||||
|
const dkm_kapi_entry *entries;
|
||||||
|
/* 按名查找:命中返回 0 并写 out_fn;未命中 -1 */
|
||||||
|
int (*lookup)(const char *name, void **out_fn);
|
||||||
|
} dkm_kapi_table;
|
||||||
|
|
||||||
|
#define DKM_KAPI_MAGIC 0x4b415049u /* "KAPI" */
|
||||||
|
#define DKM_KAPI_VERSION 1u
|
||||||
|
|
||||||
typedef struct dkm_driver_desc {
|
typedef struct dkm_driver_desc {
|
||||||
u32 magic;
|
u32 magic;
|
||||||
u16 abi_version;
|
u16 abi_version;
|
||||||
@@ -133,6 +157,9 @@ typedef struct dkm_kernel_api {
|
|||||||
* 驱动可用:task_create/uptime/sleep/current_task_slot。
|
* 驱动可用:task_create/uptime/sleep/current_task_slot。
|
||||||
* DSK 以 api+0xA8 硬偏移读 block 的约定不受影响(仅尾部追加)。 */
|
* DSK 以 api+0xA8 硬偏移读 block 的约定不受影响(仅尾部追加)。 */
|
||||||
const utsm_sched_api *sched;
|
const utsm_sched_api *sched;
|
||||||
|
/* D3: 按名导出表(尾部追加,ABI 兼容)。驱动/用户态枚举内核服务并
|
||||||
|
* 按名取函数指针;固定偏移 struct 字段不受影响。 */
|
||||||
|
const dkm_kapi_table *kapi;
|
||||||
} dkm_kernel_api;
|
} dkm_kernel_api;
|
||||||
|
|
||||||
typedef enum dkm_driver_state {
|
typedef enum dkm_driver_state {
|
||||||
@@ -160,6 +187,7 @@ typedef struct dkm_builtin_driver {
|
|||||||
void dkm_init(void);
|
void dkm_init(void);
|
||||||
/* D4: 设置当前驱动日志颜色上下文(driver_init 前设置,返回后清 0) */
|
/* D4: 设置当前驱动日志颜色上下文(driver_init 前设置,返回后清 0) */
|
||||||
void dkm_log_set_driver(const char *name);
|
void dkm_log_set_driver(const char *name);
|
||||||
|
|
||||||
int dkm_load_builtin(const dkm_builtin_driver *driver);
|
int dkm_load_builtin(const dkm_builtin_driver *driver);
|
||||||
void dkm_scan_boot_modules(void);
|
void dkm_scan_boot_modules(void);
|
||||||
void dsm_load_by_manifest(void);
|
void dsm_load_by_manifest(void);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Generate panic_logo_data.inc from ohMyLogo.png (root).
|
"""Generate RGBA8888 C array from a PNG (panic screen / login intro logo).
|
||||||
|
|
||||||
- Downscale logo to 384px wide (keep aspect), RGBA8888
|
- Downscale logo to target width (keep aspect), RGBA8888
|
||||||
- Sample the 4 corners (averaged) as the panic-screen background color
|
- Sample the 4 corners (averaged) as background color
|
||||||
Usage: python gen_panic_logo.py <ohMyLogo.png> <panic_logo_data.inc>
|
Usage: python gen_panic_logo.py <src.png> <out.c> [target_width]
|
||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
@@ -16,10 +16,11 @@ TARGET_W = 384
|
|||||||
def main() -> None:
|
def main() -> None:
|
||||||
src = sys.argv[1] if len(sys.argv) > 1 else SRC
|
src = sys.argv[1] if len(sys.argv) > 1 else SRC
|
||||||
dst = sys.argv[2] if len(sys.argv) > 2 else DST
|
dst = sys.argv[2] if len(sys.argv) > 2 else DST
|
||||||
|
target_w = int(sys.argv[3]) if len(sys.argv) > 3 else TARGET_W
|
||||||
im = Image.open(src).convert("RGBA")
|
im = Image.open(src).convert("RGBA")
|
||||||
w0, h0 = im.size
|
w0, h0 = im.size
|
||||||
scale = TARGET_W / w0
|
scale = target_w / w0
|
||||||
w, h = TARGET_W, max(1, round(h0 * scale))
|
w, h = target_w, max(1, round(h0 * scale))
|
||||||
im = im.resize((w, h), Image.LANCZOS)
|
im = im.resize((w, h), Image.LANCZOS)
|
||||||
px = im.load()
|
px = im.load()
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include <utsm/utsm.h>
|
#include <utsm/utsm.h>
|
||||||
#include <utsm/pckc.h>
|
#include <utsm/pckc.h>
|
||||||
#include <utsm/crypto.h>
|
#include <utsm/crypto.h>
|
||||||
|
#include <utsm/dma.h>
|
||||||
|
#include <utsm/dkm.h>
|
||||||
#include <utsm/log.h>
|
#include <utsm/log.h>
|
||||||
#include <utsm/segment.h>
|
#include <utsm/segment.h>
|
||||||
#include <utsm/arena.h>
|
#include <utsm/arena.h>
|
||||||
@@ -190,6 +192,38 @@ int utsm_selftest_run(void) {
|
|||||||
log_info("[UTSM] selftest unsealed segment ok (plain storage)");
|
log_info("[UTSM] selftest unsealed segment ok (plain storage)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==== D3: 按名导出表(kapi)——按名取服务并调用 ==== */
|
||||||
|
{
|
||||||
|
const dkm_kernel_api *api = dkm_get_kernel_api();
|
||||||
|
if (!api->kapi || api->kapi->magic != DKM_KAPI_MAGIC) {
|
||||||
|
log_error("[UTSM] selftest kapi table missing");
|
||||||
|
return UTSM_ERR_INVALID;
|
||||||
|
}
|
||||||
|
void *fn = 0;
|
||||||
|
if (api->kapi->lookup("log.info", &fn) != 0) {
|
||||||
|
log_error("[UTSM] selftest kapi lookup log.info failed");
|
||||||
|
return UTSM_ERR_INVALID;
|
||||||
|
}
|
||||||
|
void (*klog)(const char *) = (void (*)(const char *))fn;
|
||||||
|
klog("[UTSM] selftest kapi: log.info via export table");
|
||||||
|
if (api->kapi->lookup("dma.alloc_pages", &fn) != 0) {
|
||||||
|
log_error("[UTSM] selftest kapi lookup dma failed");
|
||||||
|
return UTSM_ERR_INVALID;
|
||||||
|
}
|
||||||
|
int (*kalloc)(u64, u64, u64, dkm_dma_buffer *) = (int (*)(u64, u64, u64, dkm_dma_buffer *))fn;
|
||||||
|
dkm_dma_buffer kbuf;
|
||||||
|
if (kalloc(1, 4096, 0xFFFFFFFFULL, &kbuf) != 0 || !kbuf.virt) {
|
||||||
|
log_error("[UTSM] selftest kapi dma alloc failed");
|
||||||
|
return UTSM_ERR_INVALID;
|
||||||
|
}
|
||||||
|
if (api->kapi->lookup("no.such.service", &fn) == 0) {
|
||||||
|
log_error("[UTSM] selftest kapi negative lookup failed");
|
||||||
|
return UTSM_ERR_INVALID;
|
||||||
|
}
|
||||||
|
log_hex64("[UTSM] selftest kapi entries=", api->kapi->count);
|
||||||
|
log_info("[UTSM] selftest kapi ok");
|
||||||
|
}
|
||||||
|
|
||||||
/* ==== SCHED-1: O(1) runqueue 语义(合成 TCB,协作式,无 tick) ==== */
|
/* ==== SCHED-1: O(1) runqueue 语义(合成 TCB,协作式,无 tick) ==== */
|
||||||
/* ==== SCHED-1 + SCHED-2: runqueue / block-wake(tick 竞态保护) ==== */
|
/* ==== SCHED-1 + SCHED-2: runqueue / block-wake(tick 竞态保护) ==== */
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ test/utsm_selftest.o: test\utsm_selftest.c include\utsm\utsm.h \
|
|||||||
include\utsm\segment.h include\utsm\dirty.h include\utsm\utrw.h \
|
include\utsm\segment.h include\utsm\dirty.h include\utsm\utrw.h \
|
||||||
include\utsm\vmm.h include\utsm\linux_loader.h include\utsm\ipc_shm.h \
|
include\utsm\vmm.h include\utsm\linux_loader.h include\utsm\ipc_shm.h \
|
||||||
..\utsm-ipc\ipc_proto.h include\utsm\hypercall.h include\utsm\pckc.h \
|
..\utsm-ipc\ipc_proto.h include\utsm\hypercall.h include\utsm\pckc.h \
|
||||||
include\utsm\crypto.h include\utsm\log.h include\utsm\arena.h \
|
include\utsm\crypto.h include\utsm\dma.h include\utsm\dkm.h \
|
||||||
include\utsm\drr.h include\utsm\sched_ext.h include\utsm\sched.h \
|
include\utsm\block.h include\utsm\net.h include\utsm\paging.h \
|
||||||
test\chacha_kat.inc
|
include\utsm\log.h include\utsm\arena.h include\utsm\drr.h \
|
||||||
|
include\utsm\sched_ext.h include\utsm\sched.h test\chacha_kat.inc
|
||||||
include\utsm\utsm.h:
|
include\utsm\utsm.h:
|
||||||
include\utsm\types.h:
|
include\utsm\types.h:
|
||||||
include\utsm\status.h:
|
include\utsm\status.h:
|
||||||
@@ -24,6 +25,11 @@ include\utsm\ipc_shm.h:
|
|||||||
include\utsm\hypercall.h:
|
include\utsm\hypercall.h:
|
||||||
include\utsm\pckc.h:
|
include\utsm\pckc.h:
|
||||||
include\utsm\crypto.h:
|
include\utsm\crypto.h:
|
||||||
|
include\utsm\dma.h:
|
||||||
|
include\utsm\dkm.h:
|
||||||
|
include\utsm\block.h:
|
||||||
|
include\utsm\net.h:
|
||||||
|
include\utsm\paging.h:
|
||||||
include\utsm\log.h:
|
include\utsm\log.h:
|
||||||
include\utsm\arena.h:
|
include\utsm\arena.h:
|
||||||
include\utsm\drr.h:
|
include\utsm\drr.h:
|
||||||
|
|||||||
Binary file not shown.
+703
-703
File diff suppressed because one or more lines are too long
+737
@@ -0,0 +1,737 @@
|
|||||||
|
/* ext4.c — minimal full read-write ext4 driver for DSK (p2 data partition)
|
||||||
|
* 见 ext4.h 头注释。严格错误策略:任何错误 fatal panic,零降级。 */
|
||||||
|
|
||||||
|
#include "ext4.h"
|
||||||
|
|
||||||
|
/* ---------------- IO 回调与日志 ---------------- */
|
||||||
|
static ext4_block_read_fn rd_fn;
|
||||||
|
static ext4_block_write_fn wr_fn;
|
||||||
|
static void (*fatalf)(const char *sym);
|
||||||
|
static void (*logl_fn)(const char *s);
|
||||||
|
static void (*logh_fn)(const char *p, ext4_u64 v);
|
||||||
|
|
||||||
|
/* ---------------- 小端读取 ---------------- */
|
||||||
|
static ext4_u16 r16(const ext4_u8 *p) { return (ext4_u16)(p[0] | (p[1] << 8)); }
|
||||||
|
static ext4_u32 r32(const ext4_u8 *p) {
|
||||||
|
return (ext4_u32)p[0] | ((ext4_u32)p[1] << 8) |
|
||||||
|
((ext4_u32)p[2] << 16) | ((ext4_u32)p[3] << 24);
|
||||||
|
}
|
||||||
|
static ext4_u64 r64(const ext4_u8 *p) { return (ext4_u64)r32(p) | ((ext4_u64)r32(p + 4) << 32); }
|
||||||
|
static void w32(ext4_u8 *p, ext4_u32 v) { p[0]=v&0xFF; p[1]=(v>>8)&0xFF; p[2]=(v>>16)&0xFF; p[3]=(v>>24)&0xFF; }
|
||||||
|
static void w16(ext4_u8 *p, ext4_u16 v) { p[0]=v&0xFF; p[1]=(v>>8)&0xFF; }
|
||||||
|
|
||||||
|
/* ---------------- 状态 ---------------- */
|
||||||
|
static ext4_u64 g_part_lba; /* ext4 分区起始 LBA */
|
||||||
|
static ext4_u32 g_block_size; /* 4096 */
|
||||||
|
static ext4_u32 g_sec_per_block; /* 8 */
|
||||||
|
static ext4_u32 g_inodes_per_group;
|
||||||
|
static ext4_u32 g_blocks_per_group;
|
||||||
|
static ext4_u32 g_inode_size;
|
||||||
|
static ext4_u32 g_first_data_block;
|
||||||
|
static ext4_u32 g_desc_size;
|
||||||
|
static ext4_u32 g_group_count;
|
||||||
|
static ext4_u64 g_blocks_count;
|
||||||
|
static ext4_u8 g_sb[1024]; /* superblock(1024B) */
|
||||||
|
static ext4_u8 g_gdt[16384]; /* 组描述符表缓存(≤512 组 × 32B) */
|
||||||
|
#define EXT4_MAX_FILE (8u * 1024u * 1024u)
|
||||||
|
static ext4_u8 g_fdata[EXT4_MAX_FILE]; /* 读文件静态缓冲 */
|
||||||
|
static ext4_u8 g_blk[4096]; /* 元数据块 scratch(位图/目录/inode 表) */
|
||||||
|
static ext4_u8 g_blk2[4096]; /* 第二 scratch(inode 表与目录并行操作) */
|
||||||
|
|
||||||
|
#define EXT4_SUPER_MAGIC 0xEF53
|
||||||
|
#define EXT4_ROOT_INO 2
|
||||||
|
|
||||||
|
/* incompat feature 位 */
|
||||||
|
#define INCOMPAT_FILETYPE 0x0002u
|
||||||
|
#define INCOMPAT_RECOVER 0x0004u
|
||||||
|
#define INCOMPAT_META_BG 0x0010u
|
||||||
|
#define INCOMPAT_EXTENTS 0x0040u
|
||||||
|
#define INCOMPAT_64BIT 0x0080u
|
||||||
|
#define INCOMPAT_MMP 0x0100u
|
||||||
|
#define INCOMPAT_FLEX_BG 0x0200u
|
||||||
|
#define INCOMPAT_METADATA_CSUM 0x0400u
|
||||||
|
#define INCOMPAT_INLINE_DATA 0x8000u
|
||||||
|
#define INCOMPAT_SUPPORTED (INCOMPAT_FILETYPE | INCOMPAT_EXTENTS | INCOMPAT_FLEX_BG)
|
||||||
|
|
||||||
|
/* inode flags */
|
||||||
|
#define EXT4_NODUMP_FL 0x00000004u
|
||||||
|
#define EXT4_EXTENTS_FL 0x00080000u
|
||||||
|
|
||||||
|
/* ---------------- 底层 IO(失败即 fatal) ---------------- */
|
||||||
|
static void rd_sec(ext4_u64 lba, ext4_u32 cnt, void *buf) {
|
||||||
|
if (rd_fn(0, lba, cnt, buf) != 0) fatalf("FS-E20 EXT4 BLOCK READ FAILED");
|
||||||
|
}
|
||||||
|
static void wr_sec(ext4_u64 lba, ext4_u32 cnt, const void *buf) {
|
||||||
|
if (wr_fn(0, lba, cnt, buf) != 0) fatalf("FS-E21 EXT4 BLOCK WRITE FAILED");
|
||||||
|
}
|
||||||
|
/* 块号 → LBA */
|
||||||
|
static ext4_u64 blk_lba(ext4_u32 blk) { return g_part_lba + (ext4_u64)blk * g_sec_per_block; }
|
||||||
|
static void rd_blk(ext4_u32 blk, void *buf) { rd_sec(blk_lba(blk), g_sec_per_block, buf); }
|
||||||
|
static void wr_blk(ext4_u32 blk, const void *buf) { wr_sec(blk_lba(blk), g_sec_per_block, buf); }
|
||||||
|
|
||||||
|
/* ---------------- GPT 探测 ext4 分区 ---------------- */
|
||||||
|
static void gpt_find_ext4(void) {
|
||||||
|
ext4_u8 gpt[512];
|
||||||
|
rd_sec(1, 1, gpt);
|
||||||
|
if (gpt[0] != 'E' || gpt[1] != 'F' || gpt[2] != 'I' || gpt[3] != ' ')
|
||||||
|
fatalf("FS-E22 GPT SIGNATURE NOT FOUND");
|
||||||
|
ext4_u64 ent_lba = r64(gpt + 0x48);
|
||||||
|
ext4_u32 ent_num = r32(gpt + 0x50);
|
||||||
|
ext4_u32 ent_sz = r32(gpt + 0x54);
|
||||||
|
if (ent_sz < 128 || ent_num == 0 || ent_num > 128)
|
||||||
|
fatalf("FS-E23 GPT ENTRY TABLE INVALID");
|
||||||
|
static ext4_u8 ents[128 * 128];
|
||||||
|
rd_sec(ent_lba, (ent_num * ent_sz + 511) / 512, ents);
|
||||||
|
for (ext4_u32 i = 0; i < ent_num; i++) {
|
||||||
|
const ext4_u8 *e = ents + i * ent_sz;
|
||||||
|
static const ext4_u8 linux_guid[16] = {
|
||||||
|
0xAF,0x3D,0xC6,0x0F, 0x83,0x84, 0x72,0x47,
|
||||||
|
0x8E,0x79, 0x3D,0x69,0xD8,0x47,0x7D,0xE4 };
|
||||||
|
int is_linux = 1;
|
||||||
|
for (int j = 0; j < 16; j++) if (e[j] != linux_guid[j]) { is_linux = 0; break; }
|
||||||
|
if (!is_linux) continue;
|
||||||
|
ext4_u64 start = r64(e + 0x20);
|
||||||
|
if (start == 0) continue;
|
||||||
|
/* 探测 superblock 魔数(offset 1024 处 0xEF53) */
|
||||||
|
ext4_u8 sb_probe[1024];
|
||||||
|
rd_sec(start + 2, 2, sb_probe);
|
||||||
|
if (r16(sb_probe + 0x38) == EXT4_SUPER_MAGIC) {
|
||||||
|
g_part_lba = start;
|
||||||
|
for (int j = 0; j < 1024; j++) g_sb[j] = sb_probe[j];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fatalf("FS-E24 EXT4 PARTITION NOT FOUND IN GPT");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------- 挂载 ---------------- */
|
||||||
|
int ext4_mount(ext4_block_read_fn rd, ext4_block_write_fn wr,
|
||||||
|
void (*fatal)(const char *sym), void (*logl)(const char *s),
|
||||||
|
void (*logh)(const char *p, ext4_u64 v)) {
|
||||||
|
rd_fn = rd; wr_fn = wr; fatalf = fatal; logl_fn = logl; logh_fn = logh;
|
||||||
|
gpt_find_ext4();
|
||||||
|
logh_fn("[EXT4] partition LBA=", g_part_lba);
|
||||||
|
if (r16(g_sb + 0x38) != EXT4_SUPER_MAGIC) fatalf("FS-E25 SUPERBLOCK MAGIC MISMATCH");
|
||||||
|
|
||||||
|
ext4_u32 log_bs = r32(g_sb + 0x18);
|
||||||
|
if (log_bs != 2) fatalf("FS-E26 UNSUPPORTED BLOCKSIZE (need 4096)");
|
||||||
|
g_block_size = 4096; g_sec_per_block = 8;
|
||||||
|
|
||||||
|
g_first_data_block = r32(g_sb + 0x14);
|
||||||
|
g_blocks_per_group = r32(g_sb + 0x20);
|
||||||
|
g_inodes_per_group = r32(g_sb + 0x28);
|
||||||
|
g_inode_size = r16(g_sb + 0x58);
|
||||||
|
g_desc_size = r16(g_sb + 0xFE); /* s_desc_size @ 0xFE(0xFA 是 hash_seed) */
|
||||||
|
if (g_desc_size < 32) g_desc_size = 32;
|
||||||
|
g_blocks_count = r32(g_sb + 0x04);
|
||||||
|
|
||||||
|
ext4_u32 incompat = r32(g_sb + 0x60);
|
||||||
|
if (incompat & INCOMPAT_RECOVER) fatalf("FS-E27 JOURNAL DIRTY (RECOVER SET)");
|
||||||
|
if (incompat & (INCOMPAT_64BIT | INCOMPAT_METADATA_CSUM | INCOMPAT_MMP |
|
||||||
|
INCOMPAT_INLINE_DATA | INCOMPAT_META_BG))
|
||||||
|
fatalf("FS-E28 UNSUPPORTED EXT4 FEATURE");
|
||||||
|
if (incompat & ~INCOMPAT_SUPPORTED) fatalf("FS-E28 UNSUPPORTED EXT4 FEATURE");
|
||||||
|
|
||||||
|
ext4_u16 state = r16(g_sb + 0x3A);
|
||||||
|
if (state != 1) fatalf("FS-E29 FILESYSTEM NOT CLEAN");
|
||||||
|
|
||||||
|
if (g_inode_size != 256) fatalf("FS-E2A UNSUPPORTED INODE SIZE");
|
||||||
|
if (g_first_data_block != 0) fatalf("FS-E2B UNEXPECTED FIRST DATA BLOCK");
|
||||||
|
if (g_blocks_per_group != 32768) fatalf("FS-E2C UNEXPECTED BLOCKS PER GROUP");
|
||||||
|
|
||||||
|
g_group_count = (ext4_u32)((g_blocks_count - g_first_data_block + g_blocks_per_group - 1) / g_blocks_per_group);
|
||||||
|
if (g_group_count == 0) g_group_count = 1;
|
||||||
|
if ((ext4_u64)g_group_count * g_desc_size > sizeof(g_gdt))
|
||||||
|
fatalf("FS-E2D GROUP DESCRIPTOR TABLE OVERFLOW");
|
||||||
|
/* 组描述符表位于 block 1(first_data_block==0 时) */
|
||||||
|
rd_blk(1, g_gdt);
|
||||||
|
logh_fn("[EXT4] groups=", g_group_count);
|
||||||
|
|
||||||
|
/* mount 即写 sb(mnt_count++),验证写通道,失败即 panic */
|
||||||
|
ext4_u16 mnt = r16(g_sb + 0x34);
|
||||||
|
w16(g_sb + 0x34, (ext4_u16)(mnt + 1));
|
||||||
|
{
|
||||||
|
ext4_u8 sbw[1024];
|
||||||
|
for (int i = 0; i < 1024; i++) sbw[i] = g_sb[i];
|
||||||
|
wr_sec(g_part_lba + 2, 2, sbw);
|
||||||
|
ext4_u8 sbr[1024];
|
||||||
|
rd_sec(g_part_lba + 2, 2, sbr);
|
||||||
|
if (r16(sbr + 0x34) != (ext4_u16)(mnt + 1)) fatalf("FS-E2E SB WRITE VERIFY FAILED");
|
||||||
|
}
|
||||||
|
logl_fn("[EXT4] mounted");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------- 组描述符访问 ---------------- */
|
||||||
|
/* 32B 组描述符:0x00 block_bitmap, 0x04 inode_bitmap, 0x08 inode_table,
|
||||||
|
* 0x0C free_blocks_lo(u16), 0x0E free_inodes_lo(u16), 0x10 used_dirs(u16) */
|
||||||
|
static ext4_u32 gd_block_bitmap(ext4_u32 g) { return r32(g_gdt + (ext4_u64)g * g_desc_size + 0x00); }
|
||||||
|
static ext4_u32 gd_inode_bitmap(ext4_u32 g) { return r32(g_gdt + (ext4_u64)g * g_desc_size + 0x04); }
|
||||||
|
static ext4_u32 gd_inode_table(ext4_u32 g) { return r32(g_gdt + (ext4_u64)g * g_desc_size + 0x08); }
|
||||||
|
static ext4_u16 gd_free_blocks(ext4_u32 g) { return r16(g_gdt + (ext4_u64)g * g_desc_size + 0x0C); }
|
||||||
|
static ext4_u16 gd_free_inodes(ext4_u32 g) { return r16(g_gdt + (ext4_u64)g * g_desc_size + 0x0E); }
|
||||||
|
|
||||||
|
static void gd_set_free_blocks(ext4_u32 g, ext4_u16 v) { w16(g_gdt + (ext4_u64)g * g_desc_size + 0x0C, v); }
|
||||||
|
static void gd_set_free_inodes(ext4_u32 g, ext4_u16 v) { w16(g_gdt + (ext4_u64)g * g_desc_size + 0x0E, v); }
|
||||||
|
|
||||||
|
static void gdt_flush(void) { wr_blk(1, g_gdt); }
|
||||||
|
|
||||||
|
/* ---------------- inode 读写 ---------------- */
|
||||||
|
/* inode 结构关键偏移(256B):
|
||||||
|
* 0x00 mode 0x02 uid 0x04 size_lo 0x1A links 0x1C blocks_lo(512B)
|
||||||
|
* 0x20 flags 0x28 i_block[15] 0x6C size_high */
|
||||||
|
static void read_inode(ext4_u32 ino, ext4_u8 *dst) {
|
||||||
|
if (ino < 2) fatalf("FS-E2F BAD INODE NUMBER");
|
||||||
|
ext4_u32 g = (ino - 1) / g_inodes_per_group;
|
||||||
|
ext4_u32 idx = (ino - 1) % g_inodes_per_group;
|
||||||
|
ext4_u32 table = gd_inode_table(g);
|
||||||
|
ext4_u32 per_block = g_block_size / g_inode_size; /* 16 */
|
||||||
|
rd_blk(table + idx / per_block, g_blk);
|
||||||
|
for (ext4_u32 i = 0; i < g_inode_size; i++)
|
||||||
|
dst[i] = g_blk[(ext4_u64)(idx % per_block) * g_inode_size + i];
|
||||||
|
}
|
||||||
|
static void write_inode(ext4_u32 ino, const ext4_u8 *src) {
|
||||||
|
if (ino < 2) fatalf("FS-E2F BAD INODE NUMBER");
|
||||||
|
ext4_u32 g = (ino - 1) / g_inodes_per_group;
|
||||||
|
ext4_u32 idx = (ino - 1) % g_inodes_per_group;
|
||||||
|
ext4_u32 table = gd_inode_table(g);
|
||||||
|
ext4_u32 per_block = g_block_size / g_inode_size;
|
||||||
|
rd_blk(table + idx / per_block, g_blk);
|
||||||
|
for (ext4_u32 i = 0; i < g_inode_size; i++)
|
||||||
|
g_blk[(ext4_u64)(idx % per_block) * g_inode_size + i] = src[i];
|
||||||
|
wr_blk(table + idx / per_block, g_blk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------- 块分配/回收 ---------------- */
|
||||||
|
static void sb_dec_free_blocks(void) {
|
||||||
|
ext4_u32 v = r32(g_sb + 0x0C);
|
||||||
|
if (v == 0) fatalf("FS-E30 FREE BLOCK COUNT UNDERFLOW");
|
||||||
|
w32(g_sb + 0x0C, v - 1);
|
||||||
|
/* sb 稍后统一 flush(写文件末尾),避免多次 1KB IO */
|
||||||
|
}
|
||||||
|
static void sb_inc_free_blocks(void) {
|
||||||
|
w32(g_sb + 0x0C, r32(g_sb + 0x0C) + 1);
|
||||||
|
}
|
||||||
|
static void sb_flush(void) {
|
||||||
|
ext4_u8 sbw[1024];
|
||||||
|
for (int i = 0; i < 1024; i++) sbw[i] = g_sb[i];
|
||||||
|
wr_sec(g_part_lba + 2, 2, sbw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分配 1 个数据块,返回物理块号。立即落盘位图与组描述符。 */
|
||||||
|
static ext4_u32 block_alloc(void) {
|
||||||
|
for (ext4_u32 g = 0; g < g_group_count; g++) {
|
||||||
|
if (gd_free_blocks(g) == 0) continue;
|
||||||
|
rd_blk(gd_block_bitmap(g), g_blk);
|
||||||
|
for (ext4_u32 byte = 0; byte < g_block_size; byte++) {
|
||||||
|
if (g_blk[byte] == 0xFF) continue;
|
||||||
|
for (int bit = 0; bit < 8; bit++) {
|
||||||
|
if (g_blk[byte] & (1u << bit)) continue;
|
||||||
|
ext4_u64 blk = (ext4_u64)g * g_blocks_per_group + (ext4_u64)byte * 8 + bit;
|
||||||
|
if (blk >= g_blocks_count) fatalf("FS-E31 BLOCK ALLOC OUT OF RANGE");
|
||||||
|
g_blk[byte] |= (ext4_u8)(1u << bit);
|
||||||
|
wr_blk(gd_block_bitmap(g), g_blk);
|
||||||
|
gd_set_free_blocks(g, (ext4_u16)(gd_free_blocks(g) - 1));
|
||||||
|
gdt_flush();
|
||||||
|
sb_dec_free_blocks();
|
||||||
|
/* 新块需清零(目录块/文件尾块) */
|
||||||
|
for (ext4_u32 i = 0; i < g_block_size; i++) g_blk2[i] = 0;
|
||||||
|
wr_blk((ext4_u32)blk, g_blk2);
|
||||||
|
return (ext4_u32)blk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fatalf("FS-E32 NO FREE BLOCKS");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 回收 1 个块 */
|
||||||
|
static void block_free(ext4_u32 blk) {
|
||||||
|
ext4_u32 g = (ext4_u32)(blk / g_blocks_per_group);
|
||||||
|
ext4_u32 off = (ext4_u32)(blk % g_blocks_per_group);
|
||||||
|
rd_blk(gd_block_bitmap(g), g_blk);
|
||||||
|
if (!(g_blk[off / 8] & (1u << (off % 8)))) fatalf("FS-E33 DOUBLE FREE DETECTED");
|
||||||
|
g_blk[off / 8] &= (ext4_u8)~(1u << (off % 8));
|
||||||
|
wr_blk(gd_block_bitmap(g), g_blk);
|
||||||
|
gd_set_free_blocks(g, (ext4_u16)(gd_free_blocks(g) + 1));
|
||||||
|
gdt_flush();
|
||||||
|
sb_inc_free_blocks();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------- extent 树 ---------------- */
|
||||||
|
/* 根节点位于 inode i_block(0x28,60B):
|
||||||
|
* header 12B: magic(2) entries(2) max(2) depth(2) gen(4)
|
||||||
|
* depth 0: leaf 项 12B: block(4) len(2) starthi(2) startlo(4)
|
||||||
|
* depth 1: index 项 12B: block(4) leaf_lo(4) leaf_hi(2) unused(2) */
|
||||||
|
#define EXTENT_MAGIC 0xF30A
|
||||||
|
|
||||||
|
/* 遍历叶项回调:map(file_block → phys_block)。depth 0/1 支持。 */
|
||||||
|
static int extent_walk(const ext4_u8 *ino,
|
||||||
|
void (*cb)(void *ud, ext4_u64 file_blk, ext4_u64 phys_blk, ext4_u32 len),
|
||||||
|
void *ud) {
|
||||||
|
ext4_u8 root[60];
|
||||||
|
for (int i = 0; i < 60; i++) root[i] = ino[0x28 + i];
|
||||||
|
if (r16(root + 0) != EXTENT_MAGIC) fatalf("FS-E34 EXTENT MAGIC INVALID");
|
||||||
|
ext4_u16 depth = r16(root + 6);
|
||||||
|
if (depth == 0) {
|
||||||
|
ext4_u16 n = r16(root + 2);
|
||||||
|
const ext4_u8 *e = root + 12;
|
||||||
|
for (ext4_u16 i = 0; i < n; i++, e += 12) {
|
||||||
|
ext4_u64 fb = r32(e);
|
||||||
|
ext4_u32 len = r16(e + 4);
|
||||||
|
if (len == 0) continue;
|
||||||
|
ext4_u64 phys = r32(e + 8) | ((ext4_u64)r16(e + 6) << 32);
|
||||||
|
cb(ud, fb, phys, len);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (depth != 1) fatalf("FS-E35 EXTENT DEPTH > 1 UNSUPPORTED");
|
||||||
|
ext4_u16 n = r16(root + 2);
|
||||||
|
const ext4_u8 *e = root + 12;
|
||||||
|
for (ext4_u16 i = 0; i < n; i++, e += 12) {
|
||||||
|
ext4_u64 leaf = r32(e + 4) | ((ext4_u64)r16(e + 8) << 32);
|
||||||
|
rd_blk((ext4_u32)leaf, g_blk);
|
||||||
|
if (r16(g_blk + 0) != EXTENT_MAGIC) fatalf("FS-E34 EXTENT MAGIC INVALID");
|
||||||
|
ext4_u16 ln = r16(g_blk + 2);
|
||||||
|
if (r16(g_blk + 6) != 0) fatalf("FS-E35 EXTENT DEPTH > 1 UNSUPPORTED");
|
||||||
|
const ext4_u8 *le = g_blk + 12;
|
||||||
|
for (ext4_u16 j = 0; j < ln; j++, le += 12) {
|
||||||
|
ext4_u64 fb = r32(le);
|
||||||
|
ext4_u32 len = r16(le + 4);
|
||||||
|
if (len == 0) continue;
|
||||||
|
ext4_u64 phys = r32(le + 8) | ((ext4_u64)r16(le + 6) << 32);
|
||||||
|
cb(ud, fb, phys, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 回调载体:读文件 */
|
||||||
|
typedef struct { ext4_u8 *dst; ext4_u64 dst_cap; } read_ctx;
|
||||||
|
/* 优化:整段连续读(尾块超界时经 g_blk2 部分拷贝,杜绝缓冲区溢出) */
|
||||||
|
static void cb_read_seq(void *ud, ext4_u64 fb, ext4_u64 phys, ext4_u32 len) {
|
||||||
|
read_ctx *c = (read_ctx *)ud;
|
||||||
|
ext4_u64 start = fb * g_block_size;
|
||||||
|
ext4_u64 bytes = (ext4_u64)len * g_block_size;
|
||||||
|
if (start >= c->dst_cap) return;
|
||||||
|
if (start + bytes <= c->dst_cap) {
|
||||||
|
rd_sec(blk_lba((ext4_u32)phys), (ext4_u32)(len * g_sec_per_block),
|
||||||
|
c->dst + start);
|
||||||
|
} else {
|
||||||
|
for (ext4_u32 i = 0; i < len; i++) {
|
||||||
|
ext4_u64 off = start + (ext4_u64)i * g_block_size;
|
||||||
|
if (off >= c->dst_cap) return;
|
||||||
|
rd_blk((ext4_u32)(phys + i), g_blk2);
|
||||||
|
ext4_u64 rem = c->dst_cap - off;
|
||||||
|
if (rem > g_block_size) rem = g_block_size;
|
||||||
|
for (ext4_u64 b = 0; b < rem; b++) c->dst[off + b] = g_blk2[b];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 回调载体:释放全部块 */
|
||||||
|
static void cb_free(void *ud, ext4_u64 fb, ext4_u64 phys, ext4_u32 len) {
|
||||||
|
(void)ud; (void)fb;
|
||||||
|
for (ext4_u32 i = 0; i < len; i++) block_free((ext4_u32)phys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------- inode 分配 ---------------- */
|
||||||
|
static ext4_u32 inode_alloc(void) {
|
||||||
|
for (ext4_u32 g = 0; g < g_group_count; g++) {
|
||||||
|
if (gd_free_inodes(g) == 0) continue;
|
||||||
|
rd_blk(gd_inode_bitmap(g), g_blk);
|
||||||
|
for (ext4_u32 byte = 0; byte < g_block_size; byte++) {
|
||||||
|
if (g_blk[byte] == 0xFF) continue;
|
||||||
|
for (int bit = 0; bit < 8; bit++) {
|
||||||
|
if (g_blk[byte] & (1u << bit)) continue;
|
||||||
|
ext4_u32 ino = g * g_inodes_per_group + (ext4_u32)byte * 8 + (ext4_u32)bit + 1;
|
||||||
|
if (ino < 11) continue; /* 保留 inode 1..10 */
|
||||||
|
g_blk[byte] |= (ext4_u8)(1u << bit);
|
||||||
|
wr_blk(gd_inode_bitmap(g), g_blk);
|
||||||
|
gd_set_free_inodes(g, (ext4_u16)(gd_free_inodes(g) - 1));
|
||||||
|
gdt_flush();
|
||||||
|
w32(g_sb + 0x10, r32(g_sb + 0x10) - 1); /* free_inodes_count */
|
||||||
|
return ino;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fatalf("FS-E37 NO FREE INODES");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------- 路径解析 ---------------- */
|
||||||
|
static ext4_u32 streq(const char *a, const char *b, ext4_u32 n) {
|
||||||
|
for (ext4_u32 i = 0; i < n; i++) if (a[i] != b[i]) return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 目录块内查找 name。返回 inode nr(0=未找到)。
|
||||||
|
* entry: inode(4) rec_len(2) name_len(1) ftype(1) name */
|
||||||
|
static ext4_u32 dir_block_find(const ext4_u8 *blk, const char *name, ext4_u32 name_len) {
|
||||||
|
ext4_u32 off = 0;
|
||||||
|
while (off + 8 <= g_block_size) {
|
||||||
|
ext4_u32 ino = r32(blk + off);
|
||||||
|
ext4_u16 rec = r16(blk + off + 4);
|
||||||
|
if (rec < 8 || (off + rec) > g_block_size) fatalf("FS-E38 DIR ENTRY CORRUPT");
|
||||||
|
if (ino != 0) {
|
||||||
|
ext4_u8 nl = blk[off + 6];
|
||||||
|
if (nl == name_len && streq((const char *)(blk + off + 8), name, name_len))
|
||||||
|
return ino;
|
||||||
|
}
|
||||||
|
off += rec;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 读 inode 的第 bi 个目录块(bi < ceil(size/blocksize)) */
|
||||||
|
static void dir_read_block(const ext4_u8 *ino, ext4_u32 bi, ext4_u8 *out) {
|
||||||
|
ext4_u8 root[60];
|
||||||
|
for (int i = 0; i < 60; i++) root[i] = ino[0x28 + i];
|
||||||
|
if (r16(root + 0) != EXTENT_MAGIC) fatalf("FS-E34 EXTENT MAGIC INVALID");
|
||||||
|
ext4_u16 depth = r16(root + 6);
|
||||||
|
if (depth != 0) fatalf("FS-E39 DIR TREE DEPTH UNSUPPORTED");
|
||||||
|
ext4_u16 n = r16(root + 2);
|
||||||
|
const ext4_u8 *e = root + 12;
|
||||||
|
for (ext4_u16 i = 0; i < n; i++, e += 12) {
|
||||||
|
ext4_u64 fb = r32(e);
|
||||||
|
ext4_u32 len = r16(e + 4);
|
||||||
|
ext4_u64 phys = r32(e + 8) | ((ext4_u64)r16(e + 6) << 32);
|
||||||
|
if (bi >= fb && bi < fb + len) {
|
||||||
|
rd_blk((ext4_u32)(phys + (bi - fb)), out);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fatalf("FS-E3A DIR BLOCK NOT MAPPED");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 在 dir inode 下查找子项,返回 inode nr(0=不存在) */
|
||||||
|
static ext4_u32 dir_lookup(const ext4_u8 *dir_ino, const char *name, ext4_u32 name_len) {
|
||||||
|
ext4_u64 dsize = r32(dir_ino + 0x04);
|
||||||
|
ext4_u32 nblk = (ext4_u32)((dsize + g_block_size - 1) / g_block_size);
|
||||||
|
for (ext4_u32 bi = 0; bi < nblk; bi++) {
|
||||||
|
dir_read_block(dir_ino, bi, g_blk);
|
||||||
|
ext4_u32 hit = dir_block_find(g_blk, name, name_len);
|
||||||
|
if (hit) return hit;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按路径逐级走到父目录 + 最终名。 */
|
||||||
|
static void resolve_parent(const char *path, ext4_u32 *parent_ino,
|
||||||
|
const char **name, ext4_u32 *name_len) {
|
||||||
|
if (path[0] != '/') fatalf("FS-E3B PATH MUST BE ABSOLUTE");
|
||||||
|
ext4_u32 cur = EXT4_ROOT_INO;
|
||||||
|
const char *p = path + 1;
|
||||||
|
/* 找最终段 */
|
||||||
|
const char *last = p;
|
||||||
|
for (const char *q = p; *q; q++) if (*q == '/') last = q + 1;
|
||||||
|
ext4_u32 last_len = 0;
|
||||||
|
while (last[last_len]) last_len++;
|
||||||
|
if (last_len == 0 || last_len > 255) fatalf("FS-E3C BAD FINAL NAME");
|
||||||
|
/* 逐级遍历前面的段 */
|
||||||
|
while (p < last) {
|
||||||
|
ext4_u32 seg = 0;
|
||||||
|
while (p[seg] && p[seg] != '/') seg++;
|
||||||
|
if (seg == 0) { p++; continue; }
|
||||||
|
ext4_u8 ino_buf[256];
|
||||||
|
read_inode(cur, ino_buf);
|
||||||
|
ext4_u32 next = dir_lookup(ino_buf, p, seg);
|
||||||
|
if (!next) fatalf("FS-E3D PATH COMPONENT NOT FOUND");
|
||||||
|
cur = next;
|
||||||
|
p += seg + 1;
|
||||||
|
}
|
||||||
|
*parent_ino = cur;
|
||||||
|
*name = last;
|
||||||
|
*name_len = last_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------- 读文件 ---------------- */
|
||||||
|
int ext4_read_file(const char *path, ext4_u8 **out, ext4_u32 *size) {
|
||||||
|
ext4_u32 parent; const char *name; ext4_u32 name_len;
|
||||||
|
resolve_parent(path, &parent, &name, &name_len);
|
||||||
|
ext4_u8 dir_ino[256];
|
||||||
|
read_inode(parent, dir_ino);
|
||||||
|
ext4_u32 target = dir_lookup(dir_ino, name, name_len);
|
||||||
|
if (!target) fatalf("FS-E3E FILE NOT FOUND");
|
||||||
|
ext4_u8 ino[256];
|
||||||
|
read_inode(target, ino);
|
||||||
|
ext4_u16 mode = r16(ino + 0x00);
|
||||||
|
if ((mode & 0xF000) != 0x8000) fatalf("FS-E3F NOT A REGULAR FILE");
|
||||||
|
ext4_u64 fsize = r32(ino + 0x04) | ((ext4_u64)r32(ino + 0x6C) << 32);
|
||||||
|
if (fsize > EXT4_MAX_FILE) fatalf("FS-E36 FILE EXCEEDS READ BUFFER");
|
||||||
|
read_ctx rc; rc.dst = g_fdata; rc.dst_cap = fsize;
|
||||||
|
extent_walk(ino, cb_read_seq, &rc);
|
||||||
|
*out = g_fdata;
|
||||||
|
*size = (ext4_u32)fsize;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------- 目录项插入 ---------------- */
|
||||||
|
/* 在 dir inode 的现有块中找空隙插入。返回 0 成功;1 无空隙(需扩块)。 */
|
||||||
|
static int dir_insert_existing(ext4_u8 *dir_ino, const char *name, ext4_u32 name_len, ext4_u32 target) {
|
||||||
|
ext4_u64 dsize = r32(dir_ino + 0x04);
|
||||||
|
ext4_u32 nblk = (ext4_u32)((dsize + g_block_size - 1) / g_block_size);
|
||||||
|
ext4_u32 need = (8 + name_len + 3) & ~3u;
|
||||||
|
for (ext4_u32 bi = 0; bi < nblk; bi++) {
|
||||||
|
dir_read_block(dir_ino, bi, g_blk);
|
||||||
|
ext4_u32 off = 0;
|
||||||
|
while (off + 8 <= g_block_size) {
|
||||||
|
ext4_u32 ino = r32(g_blk + off);
|
||||||
|
ext4_u16 rec = r16(g_blk + off + 4);
|
||||||
|
if (rec < 8 || (off + rec) > g_block_size) fatalf("FS-E38 DIR ENTRY CORRUPT");
|
||||||
|
if (ino == 0 && rec >= need) {
|
||||||
|
/* 空洞直接占用 */
|
||||||
|
w32(g_blk + off, target);
|
||||||
|
g_blk[off + 6] = (ext4_u8)name_len;
|
||||||
|
g_blk[off + 7] = 1; /* REGULAR FILE */
|
||||||
|
for (ext4_u32 i = 0; i < name_len; i++) g_blk[off + 8 + i] = (ext4_u8)name[i];
|
||||||
|
wr_blk_dir:;
|
||||||
|
ext4_u8 root[60];
|
||||||
|
for (int i = 0; i < 60; i++) root[i] = dir_ino[0x28 + i];
|
||||||
|
/* 找 bi 对应物理块写回 */
|
||||||
|
const ext4_u8 *e = root + 12;
|
||||||
|
ext4_u16 n = r16(root + 2);
|
||||||
|
for (ext4_u16 i = 0; i < n; i++, e += 12) {
|
||||||
|
ext4_u64 fb = r32(e);
|
||||||
|
ext4_u32 len = r16(e + 4);
|
||||||
|
ext4_u64 phys = r32(e + 8) | ((ext4_u64)r16(e + 6) << 32);
|
||||||
|
if (bi >= fb && bi < fb + len) { wr_blk((ext4_u32)(phys + (bi - fb)), g_blk); return 0; }
|
||||||
|
}
|
||||||
|
fatalf("FS-E3A DIR BLOCK NOT MAPPED");
|
||||||
|
}
|
||||||
|
if (off + rec == g_block_size) {
|
||||||
|
/* 尾项收缩插入 */
|
||||||
|
ext4_u8 nl = g_blk[off + 6];
|
||||||
|
ext4_u32 min_rec = (8 + nl + 3) & ~3u;
|
||||||
|
if (rec - min_rec >= need) {
|
||||||
|
ext4_u32 new_off = off + min_rec;
|
||||||
|
w16(g_blk + off + 4, (ext4_u16)min_rec); /* rec_len 在 +4 */
|
||||||
|
w32(g_blk + new_off, target);
|
||||||
|
w16(g_blk + new_off + 4, (ext4_u16)(rec - min_rec));
|
||||||
|
g_blk[new_off + 6] = (ext4_u8)name_len;
|
||||||
|
g_blk[new_off + 7] = 1;
|
||||||
|
for (ext4_u32 i = 0; i < name_len; i++) g_blk[new_off + 8 + i] = (ext4_u8)name[i];
|
||||||
|
goto wr_blk_dir;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
off += rec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 追加一个新目录块并插入条目 */
|
||||||
|
static void dir_insert_new_block(ext4_u8 *dir_ino, const char *name, ext4_u32 name_len, ext4_u32 target) {
|
||||||
|
/* 扩展 extent:depth 0 且 <4 项时直接追加;否则 fatal(目录应远小于 4 块) */
|
||||||
|
ext4_u8 root[60];
|
||||||
|
for (int i = 0; i < 60; i++) root[i] = dir_ino[0x28 + i];
|
||||||
|
if (r16(root + 0) != EXTENT_MAGIC) fatalf("FS-E34 EXTENT MAGIC INVALID");
|
||||||
|
if (r16(root + 6) != 0) fatalf("FS-E39 DIR TREE DEPTH UNSUPPORTED");
|
||||||
|
ext4_u16 n = r16(root + 2);
|
||||||
|
ext4_u16 max = r16(root + 4);
|
||||||
|
if (n >= max || n >= 4) fatalf("FS-E40 DIR EXTENT TABLE FULL");
|
||||||
|
ext4_u32 phys = block_alloc();
|
||||||
|
ext4_u64 dsize = r32(dir_ino + 0x04);
|
||||||
|
/* 追加 extent 项:file block = dsize/4096,len 1 */
|
||||||
|
w32(root + 12 + (ext4_u64)n * 12, (ext4_u32)(dsize / g_block_size));
|
||||||
|
w16(root + 12 + (ext4_u64)n * 12 + 4, 1);
|
||||||
|
w16(root + 12 + (ext4_u64)n * 12 + 6, 0);
|
||||||
|
w32(root + 12 + (ext4_u64)n * 12 + 8, phys);
|
||||||
|
w16(root + 12 + (ext4_u64)n * 12 + 10, 0);
|
||||||
|
w16(root + 2, (ext4_u16)(n + 1));
|
||||||
|
for (int i = 0; i < 60; i++) dir_ino[0x28 + i] = root[i];
|
||||||
|
/* 更新 dir 尺寸与块计数 */
|
||||||
|
dsize += g_block_size;
|
||||||
|
w32(dir_ino + 0x04, (ext4_u32)dsize);
|
||||||
|
w32(dir_ino + 0x1C, r32(dir_ino + 0x1C) + g_sec_per_block);
|
||||||
|
/* 构造新目录块 */
|
||||||
|
for (ext4_u32 i = 0; i < g_block_size; i++) g_blk[i] = 0;
|
||||||
|
w32(g_blk + 0, target);
|
||||||
|
w16(g_blk + 4, (ext4_u16)g_block_size);
|
||||||
|
g_blk[6] = (ext4_u8)name_len;
|
||||||
|
g_blk[7] = 1;
|
||||||
|
for (ext4_u32 i = 0; i < name_len; i++) g_blk[8 + i] = (ext4_u8)name[i];
|
||||||
|
/* used_dirs 不变(非目录)。写 inode 由调用方统一完成 */
|
||||||
|
wr_blk(phys, g_blk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------- 写文件(覆写/创建) ---------------- */
|
||||||
|
int ext4_write_file(const char *path, const ext4_u8 *data, ext4_u32 size) {
|
||||||
|
ext4_u32 parent; const char *name; ext4_u32 name_len;
|
||||||
|
resolve_parent(path, &parent, &name, &name_len);
|
||||||
|
ext4_u8 dir_ino[256];
|
||||||
|
read_inode(parent, dir_ino);
|
||||||
|
ext4_u32 target = dir_lookup(dir_ino, name, name_len);
|
||||||
|
int existed = (target != 0);
|
||||||
|
|
||||||
|
ext4_u8 ino[256];
|
||||||
|
if (target) {
|
||||||
|
read_inode(target, ino);
|
||||||
|
ext4_u16 mode = r16(ino + 0x00);
|
||||||
|
if ((mode & 0xF000) != 0x8000) fatalf("FS-E3F NOT A REGULAR FILE");
|
||||||
|
/* 释放旧 extent 叶块(depth 1) */
|
||||||
|
{
|
||||||
|
ext4_u8 root0[60];
|
||||||
|
for (int i = 0; i < 60; i++) root0[i] = ino[0x28 + i];
|
||||||
|
if (r16(root0 + 0) != EXTENT_MAGIC) fatalf("FS-E34 EXTENT MAGIC INVALID");
|
||||||
|
if (r16(root0 + 6) == 1) {
|
||||||
|
ext4_u16 nn = r16(root0 + 2);
|
||||||
|
for (ext4_u16 i = 0; i < nn; i++) {
|
||||||
|
ext4_u64 leaf = r32(root0 + 12 + i * 12 + 4) |
|
||||||
|
((ext4_u64)r16(root0 + 12 + i * 12 + 8) << 32);
|
||||||
|
block_free((ext4_u32)leaf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 释放旧数据块 */
|
||||||
|
extent_walk(ino, cb_free, 0);
|
||||||
|
} else {
|
||||||
|
target = inode_alloc();
|
||||||
|
for (ext4_u32 i = 0; i < g_inode_size; i++) ino[i] = 0;
|
||||||
|
w16(ino + 0x00, 0x8000 | 0644); /* regular, rw-r--r-- */
|
||||||
|
w16(ino + 0x1A, 1); /* links */
|
||||||
|
w32(ino + 0x20, EXT4_EXTENTS_FL);
|
||||||
|
w32(ino + 0x64, 0xDE5AAB1E); /* generation 品牌落款 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 构造 extent 根(depth 0/1) */
|
||||||
|
ext4_u32 nblk = (size + g_block_size - 1) / g_block_size;
|
||||||
|
if ((ext4_u64)nblk * g_block_size > EXT4_MAX_FILE) fatalf("FS-E36 FILE EXCEEDS READ BUFFER");
|
||||||
|
/* 收集分配的块号(最大 8MB/4K = 2048 块) */
|
||||||
|
static ext4_u32 alloc_map[2048];
|
||||||
|
for (ext4_u32 i = 0; i < nblk; i++) alloc_map[i] = block_alloc();
|
||||||
|
/* 写数据块 */
|
||||||
|
for (ext4_u32 i = 0; i < nblk; i++) {
|
||||||
|
for (ext4_u32 b = 0; b < g_block_size; b++) {
|
||||||
|
ext4_u64 off = (ext4_u64)i * g_block_size + b;
|
||||||
|
g_blk[b] = (off < size) ? data[off] : 0;
|
||||||
|
}
|
||||||
|
wr_blk(alloc_map[i], g_blk);
|
||||||
|
}
|
||||||
|
/* 生成 extent 叶项(每 extent ≤ 32768 块,此处 ≤2048) */
|
||||||
|
ext4_u8 root[60];
|
||||||
|
for (int i = 0; i < 60; i++) root[i] = 0;
|
||||||
|
w16(root + 0, EXTENT_MAGIC);
|
||||||
|
w16(root + 4, 4); /* max = 4(根内联) */
|
||||||
|
w16(root + 6, 0); /* depth 0 */
|
||||||
|
ext4_u16 ne = 0;
|
||||||
|
ext4_u32 i = 0;
|
||||||
|
if (nblk > 0) {
|
||||||
|
while (i < nblk) {
|
||||||
|
if (ne >= 4) {
|
||||||
|
/* >4 extents:需要 depth 1。把现有叶项压入一个叶块,继续分配。 */
|
||||||
|
/* 分配叶块,把 4 项写入 */
|
||||||
|
ext4_u32 leaf = block_alloc();
|
||||||
|
ext4_u8 lfb[4096];
|
||||||
|
for (ext4_u32 b = 0; b < g_block_size; b++) lfb[b] = 0;
|
||||||
|
w16(lfb + 0, EXTENT_MAGIC);
|
||||||
|
w16(lfb + 2, ne);
|
||||||
|
w16(lfb + 4, (ext4_u16)((g_block_size - 12) / 12));
|
||||||
|
w16(lfb + 6, 0);
|
||||||
|
for (ext4_u16 j = 0; j < ne; j++) {
|
||||||
|
const ext4_u8 *src = root + 12 + j * 12;
|
||||||
|
ext4_u8 *dst = lfb + 12 + j * 12;
|
||||||
|
for (int k = 0; k < 12; k++) dst[k] = src[k];
|
||||||
|
}
|
||||||
|
wr_blk(leaf, lfb);
|
||||||
|
/* root 变 depth1:保留 index[0] = 旧叶;继续向 alloc_map 后段收集 */
|
||||||
|
/* 这里简化:一次性深度1重建(见下),直接跳出 */
|
||||||
|
static ext4_u32 leaf_blocks[8];
|
||||||
|
static ext4_u16 leaf_counts[8];
|
||||||
|
int nleaf = 0;
|
||||||
|
leaf_blocks[nleaf] = leaf; leaf_counts[nleaf] = ne; nleaf++;
|
||||||
|
/* 继续把剩余块打成叶块,每块 ≤340 项 */
|
||||||
|
ext4_u16 cur_cnt = ne;
|
||||||
|
ext4_u32 cur_start = i; /* 前 4 项 extent 已覆盖 file blocks [0, i) */
|
||||||
|
(void)cur_cnt;
|
||||||
|
while (cur_start < nblk) {
|
||||||
|
ext4_u32 cnt = nblk - cur_start;
|
||||||
|
if (cnt > (g_block_size - 12) / 12) cnt = (g_block_size - 12) / 12;
|
||||||
|
ext4_u32 lf = block_alloc();
|
||||||
|
ext4_u8 lb[4096];
|
||||||
|
for (ext4_u32 b = 0; b < g_block_size; b++) lb[b] = 0;
|
||||||
|
w16(lb + 0, EXTENT_MAGIC);
|
||||||
|
w16(lb + 2, (ext4_u16)cnt);
|
||||||
|
w16(lb + 4, (ext4_u16)((g_block_size - 12) / 12));
|
||||||
|
w16(lb + 6, 0);
|
||||||
|
for (ext4_u32 j = 0; j < cnt; j++) {
|
||||||
|
ext4_u8 *ee = lb + 12 + j * 12;
|
||||||
|
w32(ee, cur_start + j); /* file block */
|
||||||
|
w16(ee + 4, 1); /* len 1 */
|
||||||
|
w16(ee + 6, 0);
|
||||||
|
w32(ee + 8, alloc_map[cur_start + j]);
|
||||||
|
}
|
||||||
|
wr_blk(lf, lb);
|
||||||
|
if (nleaf >= 8) fatalf("FS-E41 TOO MANY EXTENT LEAVES");
|
||||||
|
leaf_blocks[nleaf] = lf; leaf_counts[nleaf] = (ext4_u16)cnt; nleaf++;
|
||||||
|
cur_start += cnt;
|
||||||
|
}
|
||||||
|
/* 重建根为 depth 1 */
|
||||||
|
for (int b = 0; b < 60; b++) root[b] = 0;
|
||||||
|
w16(root + 0, EXTENT_MAGIC);
|
||||||
|
w16(root + 2, (ext4_u16)nleaf);
|
||||||
|
w16(root + 4, 4);
|
||||||
|
w16(root + 6, 1);
|
||||||
|
ext4_u64 fb_cur = 0;
|
||||||
|
for (int l = 0; l < nleaf; l++) {
|
||||||
|
ext4_u8 *ie = root + 12 + l * 12;
|
||||||
|
w32(ie, (ext4_u32)fb_cur);
|
||||||
|
w32(ie + 4, leaf_blocks[l]);
|
||||||
|
w16(ie + 8, 0);
|
||||||
|
fb_cur += leaf_counts[l];
|
||||||
|
}
|
||||||
|
goto root_done;
|
||||||
|
}
|
||||||
|
/* 找连续 run */
|
||||||
|
ext4_u32 run = 1;
|
||||||
|
while (i + run < nblk && run < 32768 &&
|
||||||
|
alloc_map[i + run] == alloc_map[i] + run) run++;
|
||||||
|
ext4_u8 *ee = root + 12 + ne * 12;
|
||||||
|
w32(ee, i);
|
||||||
|
w16(ee + 4, (ext4_u16)run);
|
||||||
|
w16(ee + 6, 0);
|
||||||
|
w32(ee + 8, alloc_map[i]);
|
||||||
|
ne++;
|
||||||
|
i += run;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w16(root + 2, ne);
|
||||||
|
root_done:
|
||||||
|
for (int b = 0; b < 60; b++) ino[0x28 + b] = root[b];
|
||||||
|
w32(ino + 0x04, size); /* size_lo */
|
||||||
|
w32(ino + 0x6C, 0); /* size_high */
|
||||||
|
w32(ino + 0x1C, (ext4_u32)((ext4_u64)nblk * g_sec_per_block)); /* blocks(512B) */
|
||||||
|
w32(ino + 0x10, 0xDE5A0000u); /* mtime 品牌纪年 */
|
||||||
|
write_inode(target, ino);
|
||||||
|
|
||||||
|
/* 新文件:插入目录项(target 为新分配 inode 且原查找时不存在) */
|
||||||
|
if (!existed) {
|
||||||
|
if (dir_insert_existing(dir_ino, name, name_len, target) != 0)
|
||||||
|
dir_insert_new_block(dir_ino, name, name_len, target);
|
||||||
|
write_inode(parent, dir_ino);
|
||||||
|
}
|
||||||
|
|
||||||
|
sb_flush();
|
||||||
|
logh_fn("[EXT4] wrote blocks=", nblk);
|
||||||
|
|
||||||
|
/* 写后回读校验 */
|
||||||
|
ext4_u8 *vdata; ext4_u32 vsize;
|
||||||
|
ext4_read_file(path, &vdata, &vsize);
|
||||||
|
if (vsize != size) fatalf("FS-E42 WRITE VERIFY SIZE MISMATCH");
|
||||||
|
for (ext4_u32 b = 0; b < size; b++)
|
||||||
|
if (vdata[b] != data[b]) fatalf("FS-E42 WRITE VERIFY DATA MISMATCH");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------- 启动自检 ---------------- */
|
||||||
|
void ext4_boot_selftest(void) {
|
||||||
|
/* 读测试:p2 上的 FUCK 配置副本 */
|
||||||
|
ext4_u8 *d; ext4_u32 n;
|
||||||
|
ext4_read_file("/system/deshab64/FUCK", &d, &n);
|
||||||
|
if (n == 0) fatalf("FS-E43 SELFTEST FUCK READ EMPTY");
|
||||||
|
logh_fn("[EXT4] selftest FUCK bytes=", n);
|
||||||
|
/* 写读校验测试 */
|
||||||
|
static const ext4_u8 msg[] = "DESHAB EXT4 RW OK - DEAICUP\n";
|
||||||
|
ext4_write_file("/dsk_ext4_rw.test", msg, sizeof(msg) - 1);
|
||||||
|
logl_fn("[EXT4] selftest PASS");
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/* ext4.h — minimal full read-write ext4 driver for DSK (p2 data partition)
|
||||||
|
*
|
||||||
|
* 严格错误策略:任何错误(挂载失败/feature 不支持/IO 失败/校验不符)
|
||||||
|
* 一律经 fatal(sym) 整屏 panic 停机,错误码体系 FS-E1x / FS-E2x。
|
||||||
|
*
|
||||||
|
* 支持范围(mkfs.ext4 由 pack_system_image.sh 以 -O
|
||||||
|
* ^has_journal,^metadata_csum,^64bit,^resize_inode -b 4096 -I 256 创建):
|
||||||
|
* - 4K blocksize、inode 256B、无 journal、无 metadata_csum、无 64bit
|
||||||
|
* - extent 树(depth 0/1)读写
|
||||||
|
* - 块位图/inode 位图分配与回收
|
||||||
|
* - 线性目录项查找/插入/扩展
|
||||||
|
*/
|
||||||
|
#ifndef DSK_EXT4_H
|
||||||
|
#define DSK_EXT4_H
|
||||||
|
|
||||||
|
typedef unsigned char ext4_u8;
|
||||||
|
typedef unsigned short ext4_u16;
|
||||||
|
typedef unsigned int ext4_u32;
|
||||||
|
typedef unsigned long long ext4_u64;
|
||||||
|
|
||||||
|
typedef int (*ext4_block_read_fn)(unsigned int index, ext4_u64 lba,
|
||||||
|
ext4_u32 count, void *buf);
|
||||||
|
typedef int (*ext4_block_write_fn)(unsigned int index, ext4_u64 lba,
|
||||||
|
ext4_u32 count, const void *buf);
|
||||||
|
|
||||||
|
/* 挂载 p2 ext4 分区(GPT 探测 + superblock/feature 校验)。
|
||||||
|
* 任何失败直接调用 fatal(sym) panic。成功返回 0。 */
|
||||||
|
int ext4_mount(ext4_block_read_fn rd, ext4_block_write_fn wr,
|
||||||
|
void (*fatal)(const char *sym), void (*logl)(const char *s),
|
||||||
|
void (*logh)(const char *p, ext4_u64 v));
|
||||||
|
|
||||||
|
/* 按路径读文件("/system/deshab64/FUCK"),返回内部静态缓冲。
|
||||||
|
* 任何失败 fatal。成功返回 0 并填充 out/size。 */
|
||||||
|
int ext4_read_file(const char *path, ext4_u8 **out, ext4_u32 *size);
|
||||||
|
|
||||||
|
/* 按路径写文件(存在则覆写,不存在则创建)。
|
||||||
|
* 写后自动回读逐字节校验,不符 fatal。成功返回 0。 */
|
||||||
|
int ext4_write_file(const char *path, const ext4_u8 *data, ext4_u32 size);
|
||||||
|
|
||||||
|
/* 启动自检:读 /system/deshab64/FUCK + 写读校验 /dsk_ext4_rw.test。 */
|
||||||
|
void ext4_boot_selftest(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
+229
-149
@@ -5,18 +5,20 @@
|
|||||||
#include "port.h"
|
#include "port.h"
|
||||||
#include "../tools/fat32_lfn.h"
|
#include "../tools/fat32_lfn.h"
|
||||||
#include "../tools/fat32_part.h"
|
#include "../tools/fat32_part.h"
|
||||||
|
#include "ext4.h"
|
||||||
/* Embedded Logo.png RGBA data (auto-generated by png_to_rgba.py) */
|
|
||||||
#include "logo_data.c"
|
|
||||||
|
|
||||||
/* Embedded dcplogo.png / xjlogo.png RGBA data (auto-generated by
|
/* Embedded dcplogo.png / xjlogo.png RGBA data (auto-generated by
|
||||||
* png_to_rgba_prefixed.py; must stay file-local like logo_data.c) */
|
* png_to_rgba_prefixed.py from TEXTURES directory; must stay file-local like
|
||||||
|
* other embedded art) */
|
||||||
#include "dcp_logo_data.c"
|
#include "dcp_logo_data.c"
|
||||||
#include "xj_logo_data.c"
|
#include "xj_logo_data.c"
|
||||||
|
|
||||||
/* Embedded ASCII bitmap font (Consolas 18px grayscale, 11x18 cell) */
|
/* Embedded ASCII bitmap font (Consolas 18px grayscale, 11x18 cell) */
|
||||||
#include "../firstInit/ascii_bitmaps.c"
|
#include "../firstInit/ascii_bitmaps.c"
|
||||||
|
|
||||||
|
/* Embedded ohMyLogo.png RGBA data(莲花 panic 界面用,与 login.elf 同源) */
|
||||||
|
#include "../login/ohmylogo_data.c"
|
||||||
|
|
||||||
#define COM1 0x3F8
|
#define COM1 0x3F8
|
||||||
|
|
||||||
typedef unsigned char u8;
|
typedef unsigned char u8;
|
||||||
@@ -550,10 +552,6 @@ static void dsk_draw_rgba(u32 *fb, const u8 *pix, int w, int h) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dsk_draw_logo(u32 *fb) {
|
|
||||||
dsk_draw_rgba(fb, g_logo_rgba, g_logo_w, g_logo_h);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Splash sequence entry: one image + hold for the given milliseconds.
|
/* Splash sequence entry: one image + hold for the given milliseconds.
|
||||||
* Redraws the gradient background first so successive images replace
|
* Redraws the gradient background first so successive images replace
|
||||||
* each other cleanly. */
|
* each other cleanly. */
|
||||||
@@ -634,6 +632,64 @@ static void dsk_draw_string(u32 *fb, const char *s, i64 x, i64 y, u32 fg, int sp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
* 严格错误策略插桩:FS/调度层任何错误(含未实现路径)不再降级、
|
||||||
|
* 不再静默跳过,必须整屏 fatal 显示错误码后停机。
|
||||||
|
* 错误码体系:FS-E<nn>(文件系统层)。
|
||||||
|
* =================================================================== */
|
||||||
|
static u64 dsk_fat_strlen(const char *s) { u64 n = 0; while (s[n]) n++; return n; }
|
||||||
|
|
||||||
|
/* 莲花 panic 界面:深蓝底 + ohMyLogo 莲花居中 + 错误码 + 停机落款。
|
||||||
|
* 与 login.elf / UTSM F1 panic 同一视觉体系。 */
|
||||||
|
static void dsk_fatal_lotus(u32 *fb, const char *sym) {
|
||||||
|
/* 深蓝底 */
|
||||||
|
for (u64 y = 0; y < g_fb_h; y++) {
|
||||||
|
u32 *line = (u32 *)((u8 *)fb + y * g_fb_p);
|
||||||
|
for (u64 x = 0; x < g_fb_w; x++) line[x] = 0xFF020F22u;
|
||||||
|
}
|
||||||
|
/* 莲花居中(偏上,给错误码留空间) */
|
||||||
|
i64 lx = ((i64)g_fb_w - g_panic_logo_w) / 2;
|
||||||
|
i64 ly = ((i64)g_fb_h - g_panic_logo_h) / 2 - 60;
|
||||||
|
if (ly < 0) ly = 0;
|
||||||
|
for (i64 r = 0; r < g_panic_logo_h; r++) {
|
||||||
|
for (i64 c = 0; c < g_panic_logo_w; c++) {
|
||||||
|
const unsigned char *px4 =
|
||||||
|
g_panic_logo_rgba + ((u64)r * g_panic_logo_w + (u64)c) * 4;
|
||||||
|
u32 a = px4[3];
|
||||||
|
if (a < 8) continue;
|
||||||
|
u32 col = 0xFF000000u | ((u32)px4[0] << 16) | ((u32)px4[1] << 8) | px4[2];
|
||||||
|
if (a < 250) {
|
||||||
|
u32 bg = 0xFF020F22u;
|
||||||
|
u32 na = 256 - a;
|
||||||
|
u32 rr = (((bg >> 16) & 0xFF) * na + ((col >> 16) & 0xFF) * a) >> 8;
|
||||||
|
u32 gg = (((bg >> 8) & 0xFF) * na + ((col >> 8) & 0xFF) * a) >> 8;
|
||||||
|
u32 bb = ((bg & 0xFF) * na + (col & 0xFF) * a) >> 8;
|
||||||
|
col = 0xFF000000u | (rr << 16) | (gg << 8) | bb;
|
||||||
|
}
|
||||||
|
i64 xx = lx + c, yy = ly + r;
|
||||||
|
if (xx < 0 || (u64)xx >= g_fb_w || yy < 0 || (u64)yy >= g_fb_h) continue;
|
||||||
|
*(u32 *)((u8 *)fb + (u64)yy * g_fb_p + (u64)xx * 4) = col;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 错误码 + 停机落款(莲花下方) */
|
||||||
|
i64 ty = ly + g_panic_logo_h + 24;
|
||||||
|
i64 spacing = g_ascii_w + 1;
|
||||||
|
dsk_draw_string(fb, sym,
|
||||||
|
((i64)g_fb_w - (i64)dsk_fat_strlen(sym) * spacing) / 2, ty,
|
||||||
|
0xFFF2D8D8u, (int)spacing);
|
||||||
|
dsk_draw_string(fb, "DESHAB FS FATAL - SYSTEM HALTED",
|
||||||
|
((i64)g_fb_w - 31 * spacing) / 2, ty + g_ascii_h + 16,
|
||||||
|
0xFFB08890u, (int)spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dsk_fatal(const char *sym) {
|
||||||
|
logl(sym);
|
||||||
|
if (g_fb_addr) {
|
||||||
|
dsk_fatal_lotus((u32 *)(u64)g_fb_addr, sym);
|
||||||
|
}
|
||||||
|
for (;;) __asm__("cli; hlt");
|
||||||
|
}
|
||||||
|
|
||||||
static int fat32_find_entry(const u8 *clus, u32 clus_sectors, const char *target, u32 *out_clus, u32 *out_size) {
|
static int fat32_find_entry(const u8 *clus, u32 clus_sectors, const char *target, u32 *out_clus, u32 *out_size) {
|
||||||
const fat32_de *dir = (const fat32_de *)clus;
|
const fat32_de *dir = (const fat32_de *)clus;
|
||||||
for (u32 e=0; e*32 < clus_sectors*512; e++) {
|
for (u32 e=0; e*32 < clus_sectors*512; e++) {
|
||||||
@@ -722,6 +778,51 @@ static int fat32_read_root_file(const char *name11, u8 **out_data, u32 *out_size
|
|||||||
* If the file exists, its content is replaced. If not, a new entry is created.
|
* If the file exists, its content is replaced. If not, a new entry is created.
|
||||||
* Uses g_disk (256-sector buffer) which must already contain the BPB+FAT+root.
|
* Uses g_disk (256-sector buffer) which must already contain the BPB+FAT+root.
|
||||||
* Returns 0 on success. */
|
* Returns 0 on success. */
|
||||||
|
/* BUG-20261002 修复: 按需 FAT 表项读写(支持 FAT 超出 g_disk 256 扇区的大容量 ESP)。
|
||||||
|
* 缓冲内直接改写(随后由步骤 6 回写),缓冲外读单扇区到 g_fat_sec 改写后立即落盘。 */
|
||||||
|
static u32 fat32_fat_read_entry(u32 fat_byte_off, u32 c) {
|
||||||
|
u32 fo = fat_byte_off + c * 4;
|
||||||
|
if (fo + 4 <= sizeof(g_disk)) return r32(g_disk + fo) & 0x0FFFFFFF;
|
||||||
|
u32 sec = fo / 512, off = fo % 512;
|
||||||
|
if (fat32_read_sectors(sec, 1, g_fat_sec) != 0) return 0x0FFFFFF7; /* 读失败视作坏簇 */
|
||||||
|
return r32(g_fat_sec + off) & 0x0FFFFFFF;
|
||||||
|
}
|
||||||
|
static int fat32_fat_write_entry(u32 fat_byte_off, u32 c, u32 val) {
|
||||||
|
u32 fo = fat_byte_off + c * 4;
|
||||||
|
if (fo + 4 <= sizeof(g_disk)) {
|
||||||
|
g_disk[fo] = (u8)(val & 0xFF);
|
||||||
|
g_disk[fo+1] = (u8)((val >> 8) & 0xFF);
|
||||||
|
g_disk[fo+2] = (u8)((val >> 16) & 0xFF);
|
||||||
|
g_disk[fo+3] = (u8)((g_disk[fo+3] & 0xF0) | ((val >> 24) & 0x0F));
|
||||||
|
return fat32_write_sectors(fo / 512, 1, g_disk + (u64)(fo / 512) * 512);
|
||||||
|
}
|
||||||
|
u32 sec = fo / 512, off = fo % 512;
|
||||||
|
if (fat32_read_sectors(sec, 1, g_fat_sec) != 0) return -1;
|
||||||
|
g_fat_sec[off] = (u8)(val & 0xFF);
|
||||||
|
g_fat_sec[off+1] = (u8)((val >> 8) & 0xFF);
|
||||||
|
g_fat_sec[off+2] = (u8)((val >> 16) & 0xFF);
|
||||||
|
g_fat_sec[off+3] = (u8)((g_fat_sec[off+3] & 0xF0) | ((val >> 24) & 0x0F));
|
||||||
|
return fat32_write_sectors(sec, 1, g_fat_sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 块状空闲簇扫描:每次读 8 扇区(1024 表项),fat_byte_off 必须扇区对齐。
|
||||||
|
* chunk k 覆盖表项 [k*1024, (k+1)*1024)。 */
|
||||||
|
static u8 g_fatscan[4096];
|
||||||
|
static u32 fat32_find_free_cluster(u32 fat_byte_off, u32 max_c) {
|
||||||
|
for (u32 k = 0; k * 1024 < max_c; k++) {
|
||||||
|
u32 base = k * 1024;
|
||||||
|
if (fat32_read_sectors(fat_byte_off / 512 + k * 8, 8, g_fatscan) != 0)
|
||||||
|
continue;
|
||||||
|
u32 n = max_c - base; if (n > 1024) n = 1024;
|
||||||
|
for (u32 i = 0; i < n; i++) {
|
||||||
|
u32 c = base + i;
|
||||||
|
if (c < 2) continue;
|
||||||
|
if ((r32(g_fatscan + i * 4) & 0x0FFFFFFF) == 0) return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int fat32_write_root_file(const char *name11, const u8 *data, u32 size) {
|
static int fat32_write_root_file(const char *name11, const u8 *data, u32 size) {
|
||||||
u8 *disk = g_disk;
|
u8 *disk = g_disk;
|
||||||
const fat32_bpb *bpb = (const fat32_bpb *)disk;
|
const fat32_bpb *bpb = (const fat32_bpb *)disk;
|
||||||
@@ -818,54 +919,32 @@ static int fat32_write_root_file(const char *name11, const u8 *data, u32 size) {
|
|||||||
u32 count = 0;
|
u32 count = 0;
|
||||||
while (cur >= 2 && cur < 0x0FFFFFF8 && count < clusters_needed) {
|
while (cur >= 2 && cur < 0x0FFFFFF8 && count < clusters_needed) {
|
||||||
prev_clus = cur;
|
prev_clus = cur;
|
||||||
u32 fo = fat_byte_off + cur * 4;
|
cur = fat32_fat_read_entry(fat_byte_off, cur);
|
||||||
if (fo + 4 > sizeof(g_disk)) break;
|
|
||||||
cur = r32(disk + fo) & 0x0FFFFFFF;
|
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
/* If we need more clusters, allocate them */
|
/* If we need more clusters, allocate them */
|
||||||
while (count < clusters_needed) {
|
while (count < clusters_needed) {
|
||||||
/* Find a free cluster in FAT */
|
u32 newc = fat32_find_free_cluster(fat_byte_off, (fat_sectors * 512) / 4);
|
||||||
u32 newc = 0;
|
|
||||||
for (u32 c = 2; c < (fat_sectors * 512) / 4; c++) {
|
|
||||||
u32 fo = fat_byte_off + c * 4;
|
|
||||||
if (fo + 4 > sizeof(g_disk)) break;
|
|
||||||
if ((r32(disk + fo) & 0x0FFFFFFF) == 0) { newc = c; break; }
|
|
||||||
}
|
|
||||||
if (newc == 0) { logl("[DSK] fat32 write: no free clusters"); return -3; }
|
if (newc == 0) { logl("[DSK] fat32 write: no free clusters"); return -3; }
|
||||||
/* BUG-20260730: Mark newly allocated cluster as EOC immediately to prevent re-allocation */
|
logh("[DSK] fat32 write: alloc clus=", newc);
|
||||||
{
|
/* Mark newly allocated cluster as EOC immediately to prevent re-allocation */
|
||||||
u32 nfo = fat_byte_off + newc * 4;
|
if (fat32_fat_write_entry(fat_byte_off, newc, 0x0FFFFFF8) != 0) return -7;
|
||||||
if (nfo + 4 <= sizeof(g_disk)) {
|
|
||||||
disk[nfo] = 0xF8; disk[nfo+1] = 0xFF; disk[nfo+2] = 0xFF; disk[nfo+3] = (disk[nfo+3] & 0xF0) | 0x0F;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Link previous cluster to new */
|
/* Link previous cluster to new */
|
||||||
u32 pfo = fat_byte_off + prev_clus * 4;
|
if (prev_clus >= 2) {
|
||||||
if (pfo + 4 <= sizeof(g_disk)) {
|
if (fat32_fat_write_entry(fat_byte_off, prev_clus, newc) != 0) return -7;
|
||||||
disk[pfo] = (u8)(newc & 0xFF);
|
|
||||||
disk[pfo+1] = (u8)((newc >> 8) & 0xFF);
|
|
||||||
disk[pfo+2] = (u8)((newc >> 16) & 0xFF);
|
|
||||||
disk[pfo+3] = (disk[pfo+3] & 0xF0) | (u8)((newc >> 24) & 0x0F);
|
|
||||||
}
|
}
|
||||||
prev_clus = newc;
|
prev_clus = newc;
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
/* BUG-20260730: Terminate the chain, saving original next pointer before overwriting with EOC */
|
/* Terminate the chain, saving original next pointer before overwriting with EOC */
|
||||||
if (prev_clus >= 2) {
|
if (prev_clus >= 2) {
|
||||||
u32 fo = fat_byte_off + prev_clus * 4;
|
u32 original_next = fat32_fat_read_entry(fat_byte_off, prev_clus);
|
||||||
u32 original_next = 0;
|
fat32_fat_write_entry(fat_byte_off, prev_clus, 0x0FFFFFF8);
|
||||||
if (fo + 4 <= sizeof(g_disk)) {
|
|
||||||
original_next = r32(disk + fo) & 0x0FFFFFFF;
|
|
||||||
disk[fo] = 0xF8; disk[fo+1] = 0xFF; disk[fo+2] = 0xFF; disk[fo+3] = (disk[fo+3] & 0xF0) | 0x0F;
|
|
||||||
}
|
|
||||||
/* Free excess clusters from the old chain using saved pointer */
|
/* Free excess clusters from the old chain using saved pointer */
|
||||||
u32 next = original_next;
|
u32 next = original_next;
|
||||||
while (next >= 2 && next < 0x0FFFFFF8) {
|
while (next >= 2 && next < 0x0FFFFFF8) {
|
||||||
u32 nfo = fat_byte_off + next * 4;
|
u32 nn = fat32_fat_read_entry(fat_byte_off, next);
|
||||||
u32 nn = 0;
|
fat32_fat_write_entry(fat_byte_off, next, 0);
|
||||||
if (nfo + 4 <= sizeof(g_disk)) nn = r32(disk + nfo) & 0x0FFFFFFF;
|
|
||||||
if (nfo + 4 <= sizeof(g_disk)) { disk[nfo] = 0; disk[nfo+1] = 0; disk[nfo+2] = 0; disk[nfo+3] = disk[nfo+3] & 0xF0; }
|
|
||||||
next = nn;
|
next = nn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -873,40 +952,17 @@ static int fat32_write_root_file(const char *name11, const u8 *data, u32 size) {
|
|||||||
/* Allocate new clusters */
|
/* Allocate new clusters */
|
||||||
if (free_entry < 0) { logl("[DSK] fat32 write: no free dir entry"); return -4; }
|
if (free_entry < 0) { logl("[DSK] fat32 write: no free dir entry"); return -4; }
|
||||||
for (u32 i = 0; i < clusters_needed; i++) {
|
for (u32 i = 0; i < clusters_needed; i++) {
|
||||||
u32 newc = 0;
|
u32 newc = fat32_find_free_cluster(fat_byte_off, (fat_sectors * 512) / 4);
|
||||||
for (u32 c = 2; c < (fat_sectors * 512) / 4; c++) {
|
|
||||||
u32 fo = fat_byte_off + c * 4;
|
|
||||||
if (fo + 4 > sizeof(g_disk)) break;
|
|
||||||
if ((r32(disk + fo) & 0x0FFFFFFF) == 0) { newc = c; break; }
|
|
||||||
}
|
|
||||||
if (newc == 0) { logl("[DSK] fat32 write: no free clusters"); return -3; }
|
if (newc == 0) { logl("[DSK] fat32 write: no free clusters"); return -3; }
|
||||||
/* BUG-20260730: Mark newly allocated cluster as EOC immediately to prevent re-allocation */
|
logh("[DSK] fat32 write: alloc clus=", newc);
|
||||||
{
|
/* Mark newly allocated cluster as EOC immediately to prevent re-allocation */
|
||||||
u32 nfo = fat_byte_off + newc * 4;
|
if (fat32_fat_write_entry(fat_byte_off, newc, 0x0FFFFFF8) != 0) return -7;
|
||||||
if (nfo + 4 <= sizeof(g_disk)) {
|
|
||||||
disk[nfo] = 0xF8; disk[nfo+1] = 0xFF; disk[nfo+2] = 0xFF; disk[nfo+3] = (disk[nfo+3] & 0xF0) | 0x0F;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i == 0) first_clus = newc;
|
if (i == 0) first_clus = newc;
|
||||||
if (prev_clus >= 2) {
|
if (prev_clus >= 2) {
|
||||||
u32 pfo = fat_byte_off + prev_clus * 4;
|
if (fat32_fat_write_entry(fat_byte_off, prev_clus, newc) != 0) return -7;
|
||||||
if (pfo + 4 <= sizeof(g_disk)) {
|
|
||||||
disk[pfo] = (u8)(newc & 0xFF);
|
|
||||||
disk[pfo+1] = (u8)((newc >> 8) & 0xFF);
|
|
||||||
disk[pfo+2] = (u8)((newc >> 16) & 0xFF);
|
|
||||||
disk[pfo+3] = (disk[pfo+3] & 0xF0) | (u8)((newc >> 24) & 0x0F);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
prev_clus = newc;
|
prev_clus = newc;
|
||||||
}
|
}
|
||||||
/* Terminate chain */
|
|
||||||
/* BUG-20260730: Terminate chain preserving high 4 bits */
|
|
||||||
if (prev_clus >= 2) {
|
|
||||||
u32 fo = fat_byte_off + prev_clus * 4;
|
|
||||||
if (fo + 4 <= sizeof(g_disk)) {
|
|
||||||
disk[fo] = 0xF8; disk[fo+1] = 0xFF; disk[fo+2] = 0xFF; disk[fo+3] = (disk[fo+3] & 0xF0) | 0x0F;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 4. Write data to clusters */
|
/* 4. Write data to clusters */
|
||||||
@@ -931,9 +987,7 @@ static int fat32_write_root_file(const char *name11, const u8 *data, u32 size) {
|
|||||||
if (fat32_write_sectors(clba, spc, g_cluster) != 0) return -6;
|
if (fat32_write_sectors(clba, spc, g_cluster) != 0) return -6;
|
||||||
}
|
}
|
||||||
src += chunk; remaining -= chunk; ci++;
|
src += chunk; remaining -= chunk; ci++;
|
||||||
u32 fo = fat_byte_off + cur * 4;
|
cur = fat32_fat_read_entry(fat_byte_off, cur);
|
||||||
if (fo + 4 > sizeof(g_disk)) break;
|
|
||||||
cur = r32(disk + fo) & 0x0FFFFFFF;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 5. Update directory entry */
|
/* 5. Update directory entry */
|
||||||
@@ -1307,18 +1361,11 @@ static int dsk_load_elf(u8 *data, u32 size, void **entry_out) {
|
|||||||
/* Load an ELF module from the FAT32 root by 11-char 8.3 name, parse it into
|
/* Load an ELF module from the FAT32 root by 11-char 8.3 name, parse it into
|
||||||
* the shared image buffer, and jump to its entry with the boot context.
|
* the shared image buffer, and jump to its entry with the boot context.
|
||||||
* Returns 0 if the module ran and returned, non-zero on load failure. */
|
* Returns 0 if the module ran and returned, non-zero on load failure. */
|
||||||
static int dsk_load_and_run(const char name11[12], const dsk_boot_context *ctx) {
|
/* 按名字加载模块 ELF:先根目录,后 SYSTEM/DESHAB64/ 与 SYSTEM/USER/USE/ 回退。 */
|
||||||
DSK_TS_DECL(mod_run);
|
static int dsk_load_module(const char name11[12], u8 **data, u32 *size) {
|
||||||
DSK_TS_BEGIN(mod_run);
|
if (fat32_read_root_file(name11, data, size) == 0) return 0;
|
||||||
DSK_STAT_INC(mod_schedule);
|
|
||||||
u8 *data=0; u32 size=0; void *entry=0;
|
|
||||||
logl("[DSK] load_and_run: ");
|
|
||||||
swrite(name11); swrite("\n");
|
|
||||||
if (fat32_read_root_file(name11, &data, &size) != 0) {
|
|
||||||
/* 依次回退:SYSTEM/DESHAB64/ → SYSTEM/USER/USE/(FirstInit.elf 部署位) */
|
|
||||||
static const char *pfxs[] = { "SYSTEM /DESHAB64 /", "SYSTEM /USER /USE /" };
|
static const char *pfxs[] = { "SYSTEM /DESHAB64 /", "SYSTEM /USER /USE /" };
|
||||||
int loaded = 0;
|
for (int pi = 0; pi < 2; pi++) {
|
||||||
for (int pi = 0; pi < 2 && !loaded; pi++) {
|
|
||||||
char path[40];
|
char path[40];
|
||||||
int i = 0, j;
|
int i = 0, j;
|
||||||
const char *pfx = pfxs[pi];
|
const char *pfx = pfxs[pi];
|
||||||
@@ -1327,16 +1374,21 @@ static int dsk_load_and_run(const char name11[12], const dsk_boot_context *ctx)
|
|||||||
path[i] = 0;
|
path[i] = 0;
|
||||||
logl("[DSK] load_and_run: trying fallback path");
|
logl("[DSK] load_and_run: trying fallback path");
|
||||||
if (fat32_read_sectors(0, 256, g_disk) == 0 &&
|
if (fat32_read_sectors(0, 256, g_disk) == 0 &&
|
||||||
fat32_read_path(path, &data, &size) == 0) {
|
fat32_read_path(path, data, size) == 0) return 0;
|
||||||
loaded = 1;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!loaded) {
|
|
||||||
logl("[DSK] load_and_run: fat32 read failed for ");
|
logl("[DSK] load_and_run: fat32 read failed for ");
|
||||||
swrite(name11); swrite("\n");
|
swrite(name11); swrite("\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
static int dsk_load_and_run(const char name11[12], const dsk_boot_context *ctx) {
|
||||||
|
DSK_TS_DECL(mod_run);
|
||||||
|
DSK_TS_BEGIN(mod_run);
|
||||||
|
DSK_STAT_INC(mod_schedule);
|
||||||
|
u8 *data=0; u32 size=0; void *entry=0;
|
||||||
|
logl("[DSK] load_and_run: ");
|
||||||
|
swrite(name11); swrite("\n");
|
||||||
|
if (dsk_load_module(name11, &data, &size) != 0) return -1;
|
||||||
if (dsk_load_elf(data, size, &entry) != 0) { logl("[DSK] load_and_run: elf load failed for "); swrite(name11); swrite("\n"); return -2; }
|
if (dsk_load_elf(data, size, &entry) != 0) { logl("[DSK] load_and_run: elf load failed for "); swrite(name11); swrite("\n"); return -2; }
|
||||||
logl("[DSK] jumping to module");
|
logl("[DSK] jumping to module");
|
||||||
void(*fn)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))entry;
|
void(*fn)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))entry;
|
||||||
@@ -1664,11 +1716,13 @@ static void dsk_persist_userconf(const dsk_boot_context *ctx) {
|
|||||||
u64 conf_ptr = ctx_mut->reserved[0];
|
u64 conf_ptr = ctx_mut->reserved[0];
|
||||||
u64 conf_sz = ctx_mut->reserved[1];
|
u64 conf_sz = ctx_mut->reserved[1];
|
||||||
if (!conf_ptr || !conf_sz) {
|
if (!conf_ptr || !conf_sz) {
|
||||||
logl("[DSK] no conf buffer from FirstInit, skipping persist");
|
/* 严格错误策略:FirstInit 返回却没留下配置缓冲 = 向导失败,零降级 */
|
||||||
|
dsk_fatal("FS-E05 FIRSTINIT NO CONF BUFFER");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!g_block_write) {
|
if (!g_block_write) {
|
||||||
logl("[DSK] no block_write, cannot persist user.conf");
|
/* 严格错误策略:块层无写通道 = FS 不可写,零降级 */
|
||||||
|
dsk_fatal("FS-E01 NO BLOCK WRITE PROVIDER");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const u8 *conf = (const u8 *)conf_ptr;
|
const u8 *conf = (const u8 *)conf_ptr;
|
||||||
@@ -1679,6 +1733,20 @@ static void dsk_persist_userconf(const dsk_boot_context *ctx) {
|
|||||||
logl("[DSK] writing USER.CONF");
|
logl("[DSK] writing USER.CONF");
|
||||||
int wrc = fat32_write_root_file(cn, conf, (u32)conf_sz);
|
int wrc = fat32_write_root_file(cn, conf, (u32)conf_sz);
|
||||||
logh("[DSK] USER.CONF write rc=", (u64)(i64)wrc);
|
logh("[DSK] USER.CONF write rc=", (u64)(i64)wrc);
|
||||||
|
if (wrc != 0) dsk_fatal("FS-E02 USER.CONF WRITE FAILED");
|
||||||
|
|
||||||
|
/* 1b. Write-verify: 回读 USER.CONF 逐字节比对,确保 FAT32 写路径真的落盘 */
|
||||||
|
{
|
||||||
|
u8 *vdata = 0; u32 vsize = 0;
|
||||||
|
if (fat32_read_root_file(cn, &vdata, &vsize) != 0)
|
||||||
|
dsk_fatal("FS-E04 USER.CONF VERIFY READ FAILED");
|
||||||
|
if (vsize != (u32)conf_sz)
|
||||||
|
dsk_fatal("FS-E04 USER.CONF VERIFY SIZE MISMATCH");
|
||||||
|
for (u32 i = 0; i < (u32)conf_sz; i++) {
|
||||||
|
if (vdata[i] != conf[i]) dsk_fatal("FS-E04 USER.CONF VERIFY DATA MISMATCH");
|
||||||
|
}
|
||||||
|
logl("[DSK] USER.CONF verify ok");
|
||||||
|
}
|
||||||
|
|
||||||
/* 2. Flip firstInit.txt: '0' -> '1',保留第二行 dev_mode 标志 */
|
/* 2. Flip firstInit.txt: '0' -> '1',保留第二行 dev_mode 标志 */
|
||||||
char fn[12]; /* FIRSTINTXT */
|
char fn[12]; /* FIRSTINTXT */
|
||||||
@@ -1694,6 +1762,7 @@ static void dsk_persist_userconf(const dsk_boot_context *ctx) {
|
|||||||
u8 fi_buf[3]; fi_buf[0] = '1'; fi_buf[1] = '\n'; fi_buf[2] = dev_mode_byte;
|
u8 fi_buf[3]; fi_buf[0] = '1'; fi_buf[1] = '\n'; fi_buf[2] = dev_mode_byte;
|
||||||
int frc = fat32_write_root_file(fn, fi_buf, 3);
|
int frc = fat32_write_root_file(fn, fi_buf, 3);
|
||||||
logh("[DSK] firstInit.txt flip rc=", (u64)(i64)frc);
|
logh("[DSK] firstInit.txt flip rc=", (u64)(i64)frc);
|
||||||
|
if (frc != 0) dsk_fatal("FS-E03 FIRSTINIT.TXT WRITE FAILED");
|
||||||
|
|
||||||
/* Clear reserved so login path doesn't see stale pointer */
|
/* Clear reserved so login path doesn't see stale pointer */
|
||||||
ctx_mut->reserved[0] = 0;
|
ctx_mut->reserved[0] = 0;
|
||||||
@@ -1722,6 +1791,53 @@ static void dsk_schedule_netman(const dsk_boot_context *ctx, int load_netman) {
|
|||||||
else logl("[DSK] netman not found");
|
else logl("[DSK] netman not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 调度 login.elf:读取 USER.CONF 传给 login,成功置 reserved[2]=1。
|
||||||
|
* 返回 1 = 登录成功;0 = 失败(调用方决定是否 fatal)。
|
||||||
|
* 首次启动(向导后强制重登)与常规启动共用此路径。 */
|
||||||
|
static int dsk_run_login(const dsk_boot_context *ctx) {
|
||||||
|
DSK_TS_DECL(login_phase);
|
||||||
|
DSK_TS_BEGIN(login_phase);
|
||||||
|
logl("[DSK:SCHED] scheduling login.elf");
|
||||||
|
char ln[12]; /* LOGIN ELF */
|
||||||
|
ln[0]='L';ln[1]='O';ln[2]='G';ln[3]='I';ln[4]='N';ln[5]=' ';ln[6]=' ';ln[7]=' ';ln[8]='E';ln[9]='L';ln[10]='F';
|
||||||
|
u8 *ld=0; u32 ls=0; void *le=0;
|
||||||
|
int login_ok = 0;
|
||||||
|
if (dsk_load_module(ln, &ld, &ls) == 0) {
|
||||||
|
if (dsk_load_elf(ld, ls, &le) == 0) {
|
||||||
|
char cn[12]; /* USER CON */
|
||||||
|
cn[0]='U';cn[1]='S';cn[2]='E';cn[3]='R';cn[4]=' ';cn[5]=' ';cn[6]=' ';cn[7]=' ';cn[8]='C';cn[9]='O';cn[10]='N';
|
||||||
|
u8 *conf_data=0; u32 conf_size=0;
|
||||||
|
if (fat32_read_root_file(cn, &conf_data, &conf_size) == 0) {
|
||||||
|
logl("[DSK] USER.CONF found, passing to login");
|
||||||
|
dsk_boot_context *ctx_mut = (dsk_boot_context *)ctx;
|
||||||
|
ctx_mut->reserved[0] = (u64)conf_data;
|
||||||
|
ctx_mut->reserved[1] = (u64)conf_size;
|
||||||
|
ctx_mut->reserved[2] = 0; /* login success flag */
|
||||||
|
|
||||||
|
logl("[DSK] jumping to login");
|
||||||
|
void(*lentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))le;
|
||||||
|
lentry(ctx);
|
||||||
|
logl("[DSK] login returned");
|
||||||
|
|
||||||
|
if (ctx_mut->reserved[2] == 1) {
|
||||||
|
logl("[DSK] login success");
|
||||||
|
login_ok = 1;
|
||||||
|
} else {
|
||||||
|
logl("[DSK] login skipped or failed");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logl("[DSK] USER.CONF not found");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logl("[DSK] login.elf load failed");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logl("[DSK] login.elf not found");
|
||||||
|
}
|
||||||
|
DSK_TS_END(login_phase, "login phase");
|
||||||
|
return login_ok;
|
||||||
|
}
|
||||||
|
|
||||||
/* Draw the spinning loader (comet-tail arc) on the gradient background,
|
/* Draw the spinning loader (comet-tail arc) on the gradient background,
|
||||||
* then run the firstInit check and dispatch system component loading.
|
* then run the firstInit check and dispatch system component loading.
|
||||||
* FUCK config controls: show_logo, skip_login, mouse_init, netman, dev_mode. */
|
* FUCK config controls: show_logo, skip_login, mouse_init, netman, dev_mode. */
|
||||||
@@ -1743,6 +1859,10 @@ static void dsk_spinner_and_check(const void *boot_ctx_ptr) {
|
|||||||
g_fb_addr=ctx->framebuffer_address; g_fb_w=ctx->framebuffer_width;
|
g_fb_addr=ctx->framebuffer_address; g_fb_w=ctx->framebuffer_width;
|
||||||
g_fb_h=ctx->framebuffer_height; g_fb_p=ctx->framebuffer_pitch;
|
g_fb_h=ctx->framebuffer_height; g_fb_p=ctx->framebuffer_pitch;
|
||||||
if (!g_fb_addr) { logl("[DSK] FATAL: framebuffer address is NULL"); for(;;)__asm__("hlt"); }
|
if (!g_fb_addr) { logl("[DSK] FATAL: framebuffer address is NULL"); for(;;)__asm__("hlt"); }
|
||||||
|
|
||||||
|
/* ext4 p2 数据分区挂载 + 启动读写自检(任何失败 fatal panic,FS-E2x/FS-E3x) */
|
||||||
|
ext4_mount(g_block_read, g_block_write, dsk_fatal, logl, logh);
|
||||||
|
ext4_boot_selftest();
|
||||||
u32 *sfb=(u32*)(u64)g_fb_addr;
|
u32 *sfb=(u32*)(u64)g_fb_addr;
|
||||||
logh("[DSK] block_read ptr=", (u64)g_block_read);
|
logh("[DSK] block_read ptr=", (u64)g_block_read);
|
||||||
|
|
||||||
@@ -1809,36 +1929,28 @@ static void dsk_spinner_and_check(const void *boot_ctx_ptr) {
|
|||||||
else logl("[DSK] compat: PE DLL search disabled");
|
else logl("[DSK] compat: PE DLL search disabled");
|
||||||
milestone_write(3, "FUCK config loaded");
|
milestone_write(3, "FUCK config loaded");
|
||||||
|
|
||||||
/* Gradient background + centered Logo.png (5 seconds) */
|
/* Gradient background + centered "DESHAB P01" text(Logo.png 图像已移除) */
|
||||||
dsk_fill_gradient_rect(sfb, 0, 0, (i64)g_fb_w, (i64)g_fb_h);
|
dsk_fill_gradient_rect(sfb, 0, 0, (i64)g_fb_w, (i64)g_fb_h);
|
||||||
|
|
||||||
if (show_logo) {
|
if (show_logo) {
|
||||||
/* Draw Logo.png centered with alpha blending on gradient background */
|
/* "DESHAB P01" 文字垂直居中(不再绘制 Logo 图像) */
|
||||||
dsk_draw_logo(sfb);
|
|
||||||
|
|
||||||
/* Draw "DESHAB P01" text centered below the logo */
|
|
||||||
{
|
{
|
||||||
i64 logo_cx = (i64)g_fb_w / 2;
|
|
||||||
i64 logo_cy = ((i64)g_fb_h - g_logo_h) / 2;
|
|
||||||
const char *ver_str = "DESHAB P01";
|
const char *ver_str = "DESHAB P01";
|
||||||
int len = 10; /* strlen("DESHAB P01") */
|
int len = 10; /* strlen("DESHAB P01") */
|
||||||
int spacing = g_ascii_w + 1; /* 11+1 = 12px per char */
|
int spacing = g_ascii_w + 1; /* 11+1 = 12px per char */
|
||||||
i64 text_w = (i64)len * spacing;
|
i64 text_w = (i64)len * spacing;
|
||||||
i64 text_x = logo_cx - text_w / 2;
|
i64 text_x = (i64)g_fb_w / 2 - text_w / 2;
|
||||||
i64 text_y = logo_cy + g_logo_h + 12; /* 12px below logo */
|
i64 text_y = (i64)g_fb_h / 2 - g_ascii_h / 2;
|
||||||
/* White text: BGRX = 0xFF_RR_GG_BB = 0xFF_FFFFFF */
|
/* White text: BGRX = 0xFF_RR_GG_BB = 0xFF_FFFFFF */
|
||||||
u32 fg = 0xFFFFFFFFu;
|
u32 fg = 0xFFFFFFFFu;
|
||||||
dsk_draw_string(sfb, ver_str, text_x, text_y, fg, spacing);
|
dsk_draw_string(sfb, ver_str, text_x, text_y, fg, spacing);
|
||||||
}
|
}
|
||||||
|
|
||||||
logh("[DSK] Logo.png displayed (", (u64)g_logo_w);
|
/* Hold text for 5 seconds */
|
||||||
logh("[DSK] x", (u64)g_logo_h);
|
|
||||||
|
|
||||||
/* Hold Logo for 5 seconds */
|
|
||||||
u64 logo_start = rdtsc_dsk();
|
u64 logo_start = rdtsc_dsk();
|
||||||
u64 logo_budget = g_tsc_per_ms * 5000; /* 5000 ms = 5 seconds */
|
u64 logo_budget = g_tsc_per_ms * 5000; /* 5000 ms = 5 seconds */
|
||||||
while (rdtsc_dsk() - logo_start < logo_budget) __asm__("pause");
|
while (rdtsc_dsk() - logo_start < logo_budget) __asm__("pause");
|
||||||
logl("[DSK] Logo 5s done, proceeding");
|
logl("[DSK] boot text 5s done, proceeding");
|
||||||
|
|
||||||
/* Follow-up splash: dcplogo.png 3s, then OpenXJ380 xjlogo.png 3s */
|
/* Follow-up splash: dcplogo.png 3s, then OpenXJ380 xjlogo.png 3s */
|
||||||
dsk_splash(sfb, g_dcp_logo_rgba, g_dcp_logo_w, g_dcp_logo_h,
|
dsk_splash(sfb, g_dcp_logo_rgba, g_dcp_logo_w, g_dcp_logo_h,
|
||||||
@@ -1917,47 +2029,7 @@ static void dsk_spinner_and_check(const void *boot_ctx_ptr) {
|
|||||||
if (skip_login || skip_firstinit) {
|
if (skip_login || skip_firstinit) {
|
||||||
logl("[DSK:SCHED] login skipped by FUCK config");
|
logl("[DSK:SCHED] login skipped by FUCK config");
|
||||||
} else {
|
} else {
|
||||||
DSK_TS_DECL(login_phase);
|
dsk_run_login(ctx);
|
||||||
DSK_TS_BEGIN(login_phase);
|
|
||||||
logl("[DSK:SCHED] scheduling login.elf");
|
|
||||||
char ln[12]; /* LOGIN ELF */
|
|
||||||
ln[0]='L';ln[1]='O';ln[2]='G';ln[3]='I';ln[4]='N';ln[5]=' ';ln[6]=' ';ln[7]=' ';ln[8]='E';ln[9]='L';ln[10]='F';
|
|
||||||
u8 *ld=0; u32 ls=0; void *le=0;
|
|
||||||
int login_ok = 0;
|
|
||||||
if (fat32_read_root_file(ln, &ld, &ls) == 0) {
|
|
||||||
if (dsk_load_elf(ld, ls, &le) == 0) {
|
|
||||||
char cn[12]; /* USER CON */
|
|
||||||
cn[0]='U';cn[1]='S';cn[2]='E';cn[3]='R';cn[4]=' ';cn[5]=' ';cn[6]=' ';cn[7]=' ';cn[8]='C';cn[9]='O';cn[10]='N';
|
|
||||||
u8 *conf_data=0; u32 conf_size=0;
|
|
||||||
if (fat32_read_root_file(cn, &conf_data, &conf_size) == 0) {
|
|
||||||
logl("[DSK] USER.CONF found, passing to login");
|
|
||||||
dsk_boot_context *ctx_mut = (dsk_boot_context *)ctx;
|
|
||||||
ctx_mut->reserved[0] = (u64)conf_data;
|
|
||||||
ctx_mut->reserved[1] = (u64)conf_size;
|
|
||||||
ctx_mut->reserved[2] = 0; /* login success flag */
|
|
||||||
|
|
||||||
logl("[DSK] jumping to login");
|
|
||||||
void(*lentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))le;
|
|
||||||
lentry(ctx);
|
|
||||||
logl("[DSK] login returned");
|
|
||||||
|
|
||||||
if (ctx_mut->reserved[2] == 1) {
|
|
||||||
logl("[DSK] login success");
|
|
||||||
login_ok = 1;
|
|
||||||
} else {
|
|
||||||
logl("[DSK] login skipped or failed");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logl("[DSK] USER.CONF not found, skipping login");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logl("[DSK] login.elf load failed, skipping login");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logl("[DSK] login.elf not found, skipping login");
|
|
||||||
}
|
|
||||||
(void)login_ok;
|
|
||||||
DSK_TS_END(login_phase, "login phase");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* default_shell=cmd → 直接进 CMD.ELF(PE/EXE 兼容层前端),
|
/* default_shell=cmd → 直接进 CMD.ELF(PE/EXE 兼容层前端),
|
||||||
@@ -2009,9 +2081,8 @@ static void dsk_spinner_and_check(const void *boot_ctx_ptr) {
|
|||||||
en[6]='~';en[7]='1';en[8]='E';en[9]='L';en[10]='F';en[11]=0;
|
en[6]='~';en[7]='1';en[8]='E';en[9]='L';en[10]='F';en[11]=0;
|
||||||
logl("[DSK] loading FirstInit");
|
logl("[DSK] loading FirstInit");
|
||||||
if (dsk_load_and_run(en, ctx) != 0) {
|
if (dsk_load_and_run(en, ctx) != 0) {
|
||||||
logl("[DSK] FirstInit not found, loading desktop directly");
|
/* 严格错误策略:首次启动却没有 FirstInit.elf = 未实现路径,零降级 */
|
||||||
milestone_write(6, "desktop loading");
|
dsk_fatal("FS-E07 FIRSTINIT.ELF NOT FOUND");
|
||||||
dsk_choose_and_run_session(ctx, sfb, &dsk_cfg, dsk_cfg_ok);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logl("[DSK] FirstInit returned, persisting user.conf");
|
logl("[DSK] FirstInit returned, persisting user.conf");
|
||||||
@@ -2020,6 +2091,15 @@ static void dsk_spinner_and_check(const void *boot_ctx_ptr) {
|
|||||||
logh("[DSK:STAT] fat32_write_count=", (u64)fat32_write_count);
|
logh("[DSK:STAT] fat32_write_count=", (u64)fat32_write_count);
|
||||||
logh("[DSK:STAT] elf_load_count=", (u64)elf_load_count);
|
logh("[DSK:STAT] elf_load_count=", (u64)elf_load_count);
|
||||||
logh("[DSK:STAT] mod_schedule_count=", (u64)mod_schedule_count);
|
logh("[DSK:STAT] mod_schedule_count=", (u64)mod_schedule_count);
|
||||||
|
|
||||||
|
/* 向导完成后强制重新登录:进桌面前必须再走一次 login.elf。
|
||||||
|
* 零降级策略:登录被跳过/失败(如 Esc 跳过)不允许静默进桌面。 */
|
||||||
|
if (skip_login) {
|
||||||
|
logl("[DSK:SCHED] first-boot login skipped by FUCK config");
|
||||||
|
} else if (!dsk_run_login(ctx)) {
|
||||||
|
dsk_fatal("FS-E06 LOGIN REQUIRED AFTER FIRSTINIT");
|
||||||
|
}
|
||||||
|
|
||||||
logl("[DSK] loading desktop");
|
logl("[DSK] loading desktop");
|
||||||
milestone_write(6, "desktop loading");
|
milestone_write(6, "desktop loading");
|
||||||
dsk_choose_and_run_session(ctx, sfb, &dsk_cfg, dsk_cfg_ok);
|
dsk_choose_and_run_session(ctx, sfb, &dsk_cfg, dsk_cfg_ok);
|
||||||
|
|||||||
Binary file not shown.
+183
-284
File diff suppressed because one or more lines are too long
@@ -231,6 +231,51 @@ static void fb_bitmap_alpha(u32 *fb, const u8 *data, i64 w, i64 h, i64 x, i64 y,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- 严格错误策略:NET-E 系列 lotus fatal(零降级) ----
|
||||||
|
* 内嵌 ohMyLogo 莲花(与 login.elf / DSK 同源),任何未实现/失败路径
|
||||||
|
* 整屏显示错误码后停机。 */
|
||||||
|
#include "../login/ohmylogo_data.c"
|
||||||
|
|
||||||
|
static u64 fi_fat_strlen(const char *s) { u64 n = 0; while (s[n]) n++; return n; }
|
||||||
|
|
||||||
|
static void fill_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 color);
|
||||||
|
static void firstinit_fatal(const char *sym) {
|
||||||
|
logl(sym);
|
||||||
|
if (fb_a) {
|
||||||
|
u32 *fb = (u32 *)(u64)fb_a;
|
||||||
|
fill_rect(fb, 0, 0, (i64)fb_w, (i64)fb_h, 0xFF020F22u);
|
||||||
|
i64 lx = ((i64)fb_w - g_panic_logo_w) / 2;
|
||||||
|
i64 ly = ((i64)fb_h - g_panic_logo_h) / 2 - 60;
|
||||||
|
if (ly < 0) ly = 0;
|
||||||
|
for (i64 r = 0; r < g_panic_logo_h; r++) {
|
||||||
|
for (i64 c = 0; c < g_panic_logo_w; c++) {
|
||||||
|
const unsigned char *px4 =
|
||||||
|
g_panic_logo_rgba + ((u64)r * g_panic_logo_w + (u64)c) * 4;
|
||||||
|
u32 a = px4[3];
|
||||||
|
if (a < 8) continue;
|
||||||
|
u32 col = 0xFF000000u | ((u32)px4[0] << 16) | ((u32)px4[1] << 8) | px4[2];
|
||||||
|
if (a < 250) {
|
||||||
|
u32 bg = 0xFF020F22u;
|
||||||
|
u32 na = 256 - a;
|
||||||
|
u32 rr = (((bg >> 16) & 0xFF) * na + ((col >> 16) & 0xFF) * a) >> 8;
|
||||||
|
u32 gg = (((bg >> 8) & 0xFF) * na + ((col >> 8) & 0xFF) * a) >> 8;
|
||||||
|
u32 bb = ((bg & 0xFF) * na + (col & 0xFF) * a) >> 8;
|
||||||
|
col = 0xFF000000u | (rr << 16) | (gg << 8) | bb;
|
||||||
|
}
|
||||||
|
i64 xx = lx + c, yy = ly + r;
|
||||||
|
if (xx < 0 || (u64)xx >= fb_w || yy < 0 || (u64)yy >= fb_h) continue;
|
||||||
|
*(u32 *)((u8 *)fb + (u64)yy * fb_p + (u64)xx * 4) = col;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i64 ty = ly + g_panic_logo_h + 24;
|
||||||
|
fb_text(fb, sym, ((i64)fb_w - (i64)fi_fat_strlen(sym) * ASCII_STEP) / 2,
|
||||||
|
ty, 0xFFF2D8D8u, 0);
|
||||||
|
fb_text(fb, "DESHAB NET FATAL - SYSTEM HALTED",
|
||||||
|
((i64)fb_w - 32 * ASCII_STEP) / 2, ty + ASCII_H + 16, 0xFFB08890u, 0);
|
||||||
|
}
|
||||||
|
for (;;) __asm__("cli; hlt");
|
||||||
|
}
|
||||||
|
|
||||||
static void fill_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 color) {
|
static void fill_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 color) {
|
||||||
for (i64 r=0; r<h; r++) {
|
for (i64 r=0; r<h; r++) {
|
||||||
i64 yy = y + r;
|
i64 yy = y + r;
|
||||||
@@ -1201,6 +1246,8 @@ static void read_network_page(u32 *fb, i64 card_x, i64 card_y, setup_net *net, i
|
|||||||
}
|
}
|
||||||
/* 连接按钮 */
|
/* 连接按钮 */
|
||||||
if (*mx >= card_x + 680 && *mx < card_x + 860 && *my >= card_y + 310 && *my < card_y + 354) {
|
if (*mx >= card_x + 680 && *mx < card_x + 860 && *my >= card_y + 310 && *my < card_y + 354) {
|
||||||
|
/* 严格错误策略:无 wlan 驱动栈 —— 零降级 panic */
|
||||||
|
firstinit_fatal("NET-E01 WLAN CONNECT NOT IMPLEMENTED");
|
||||||
net->connected = 1;
|
net->connected = 1;
|
||||||
logl("[FirstInit] network connect requested");
|
logl("[FirstInit] network connect requested");
|
||||||
redraw_network_card(fb, card_x, card_y, net, NET_ACT_CONNECT, *mx, *my, bg, fg);
|
redraw_network_card(fb, card_x, card_y, net, NET_ACT_CONNECT, *mx, *my, bg, fg);
|
||||||
@@ -1246,6 +1293,8 @@ static void read_network_page(u32 *fb, i64 card_x, i64 card_y, setup_net *net, i
|
|||||||
for (int i=0;i<30;i++) delay_frame();
|
for (int i=0;i<30;i++) delay_frame();
|
||||||
return;
|
return;
|
||||||
} else if (active == NET_ACT_CONNECT) {
|
} else if (active == NET_ACT_CONNECT) {
|
||||||
|
/* 严格错误策略:无 wlan 驱动栈,"连接"是未实现路径 —— 零降级 panic */
|
||||||
|
firstinit_fatal("NET-E01 WLAN CONNECT NOT IMPLEMENTED");
|
||||||
net->connected = 1;
|
net->connected = 1;
|
||||||
logl("[FirstInit] network connect requested");
|
logl("[FirstInit] network connect requested");
|
||||||
redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg);
|
redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg);
|
||||||
|
|||||||
Binary file not shown.
@@ -174,7 +174,11 @@ if [[ "$esp_bytes" -ge $((4096 * 1024 * 1024)) ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
mkfs.vfat -F 32 -n DESHABBOOT -s "$fat_cluster_sectors" "$p1" >/dev/null
|
mkfs.vfat -F 32 -n DESHABBOOT -s "$fat_cluster_sectors" "$p1" >/dev/null
|
||||||
mkfs.ext4 -F -q -L DESHAB -U "$EXT4_UUID" -m 1 "$p2"
|
# DSK 内嵌 ext4 读写驱动支持范围:4K block / 256B inode / extent 树,
|
||||||
|
# 不支持 journal、metadata_csum、64bit(遇到即 panic FS-E28)——
|
||||||
|
# 因此这里必须显式关闭这三个 feature。
|
||||||
|
mkfs.ext4 -F -q -L DESHAB -U "$EXT4_UUID" -m 1 \
|
||||||
|
-b 4096 -I 256 -O ^has_journal,^metadata_csum,^64bit,^resize_inode "$p2"
|
||||||
echo "[pack] filesystems:"
|
echo "[pack] filesystems:"
|
||||||
blkid "$p1" "$p2" || true
|
blkid "$p1" "$p2" || true
|
||||||
|
|
||||||
|
|||||||
+704
-294
File diff suppressed because one or more lines are too long
+5
-7
@@ -143,17 +143,15 @@ if exist "%USB_IMG%" (
|
|||||||
)
|
)
|
||||||
|
|
||||||
REM ===== Boot disk attach =====
|
REM ===== Boot disk attach =====
|
||||||
REM Main disk must use explicit virtio-blk-pci + bootindex=1: with plain
|
REM Main disk must use explicit ide-hd + bootindex=1: the DSK/AHCI block
|
||||||
REM "-drive if=virtio", OVMF only auto-creates a boot entry when virtio-blk
|
REM provider only registers the first ready SATA port, so the GPT image
|
||||||
REM lands on PCI slot 0x3 (no other PCI devices). Adding NVMe/USB/e1000 moves
|
REM (p1 FAT32 ESP + p2 ext4) has to live on SATA for DSK to see ext4 at all.
|
||||||
REM it to 0x4/0x5 and OVMF boot enumeration silently drops the ESP, falling
|
|
||||||
REM back to EFI Internal Shell even though BOOTX64.EFI is present and valid.
|
|
||||||
REM bootindex=1 injects the fw_cfg bootorder entry so OVMF always picks it.
|
REM bootindex=1 injects the fw_cfg bootorder entry so OVMF always picks it.
|
||||||
set "OSDISK_OPTS=-drive if=none,id=osdisk,format=raw,file="%IMG%" -device virtio-blk-pci,drive=osdisk,bootindex=1"
|
set "OSDISK_OPTS=-drive if=none,id=osdisk,format=raw,file="%IMG%" -device ide-hd,drive=osdisk,bus=ide.0,bootindex=1"
|
||||||
|
|
||||||
if exist "%SATA_IMG%" (
|
if exist "%SATA_IMG%" (
|
||||||
echo [qemu] SATA: %SATA_IMG%
|
echo [qemu] SATA: %SATA_IMG%
|
||||||
"%QEMU%" -accel %QEMU_ACCEL% -machine q35 -m 12G -cpu %QEMU_CPU% %SERIAL_ARGS% -drive if=pflash,format=raw,readonly=on,file="%OVMF%" %OSDISK_OPTS% -drive id=sata0,format=raw,file="%SATA_IMG%",if=none -device ide-hd,drive=sata0,bus=ide.0 %NVME_OPTS% %USB_OPTS% -netdev user,id=net0 -device e1000,netdev=net0,mac=52:54:00:12:34:56 -boot menu=on
|
"%QEMU%" -accel %QEMU_ACCEL% -machine q35 -m 12G -cpu %QEMU_CPU% %SERIAL_ARGS% -drive if=pflash,format=raw,readonly=on,file="%OVMF%" %OSDISK_OPTS% -drive id=sata0,format=raw,file="%SATA_IMG%",if=none -device ide-hd,drive=sata0,bus=ide.1 %NVME_OPTS% %USB_OPTS% -netdev user,id=net0 -device e1000,netdev=net0,mac=52:54:00:12:34:56 -boot menu=on
|
||||||
) else (
|
) else (
|
||||||
echo [qemu] SATA image not found, booting without block device
|
echo [qemu] SATA image not found, booting without block device
|
||||||
"%QEMU%" -accel %QEMU_ACCEL% -machine q35 -m 12G -cpu %QEMU_CPU% %SERIAL_ARGS% -drive if=pflash,format=raw,readonly=on,file="%OVMF%" %OSDISK_OPTS% %NVME_OPTS% %USB_OPTS% -netdev user,id=net0 -device e1000,netdev=net0 -boot menu=on
|
"%QEMU%" -accel %QEMU_ACCEL% -machine q35 -m 12G -cpu %QEMU_CPU% %SERIAL_ARGS% -drive if=pflash,format=raw,readonly=on,file="%OVMF%" %OSDISK_OPTS% %NVME_OPTS% %USB_OPTS% -netdev user,id=net0 -device e1000,netdev=net0 -boot menu=on
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 1.3 MiB |
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 2.9 MiB After Width: | Height: | Size: 2.9 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
@@ -76,6 +76,20 @@
|
|||||||
|
|
||||||
## 文档体系
|
## 文档体系
|
||||||
|
|
||||||
|
### FS-E 系列:文件系统读写严格模式(2026-10-02)
|
||||||
|
|
||||||
|
| 项 | 状态 | 说明 | 证据 |
|
||||||
|
|----|------|------|------|
|
||||||
|
| ext4 读写驱动(CODE/dsk/ext4.c) | ✅ | GPT 定位 p2 + feature 校验 + extent 树(depth0/1)读写 + 块/inode 分配回收 + 目录项插入 + 写后回读校验;启动自检 PASS | `.build_tmp/qemu_run.log`([EXT4] selftest PASS) |
|
||||||
|
| FAT32 大盘写修复 | ✅ | 按需 FAT 表项 RMW(fat32_fat_read/write_entry)+ 块状空闲簇扫描(fat32_find_free_cluster);修复 rec_len +4 收缩偏移与 chunk 对齐两处错位 | 同上(USER.CONF verify ok → firstInit.txt flip ok) |
|
||||||
|
| FS-E01..E07 / FS-E2x / FS-E3x 插桩 | ✅ | 写失败/校验不符/feature 不支持/路径缺失 → 莲花 panic 零降级 | `.build_tmp/qemu_run.log`(FS-E02/E06/E22 复现) |
|
||||||
|
| 向导后强制重登 | ✅ | first-boot 分支 FirstInit→persist→login;登录跳过/失败 → FS-E06 panic | 同上(login success → desktop ready) |
|
||||||
|
| NET-E01 网络页 Connect panic | ✅ | 无 wlan 栈,Connect 即 panic;Save(仅存配置) 为唯一前进路径 | 同上(NET-E01 复现,fb pmemsave 确认莲花屏) |
|
||||||
|
| 主镜像改挂 SATA(run_qemu.bat) | ✅ | DSK/AHCI 块层必须可见 GPT 盘才能访问 p2 ext4;边车退居 ide.1 | run_qemu.bat |
|
||||||
|
| 开机画面去 Logo | ✅ | 移除 Logo.png 图像,仅保留居中 "DESHAB P01" 文字;dcp/xj logo 改由 TEXTURES/ 重新生成 | build 日志 |
|
||||||
|
|
||||||
|
## 文档体系
|
||||||
|
|
||||||
| 文档 | 状态 | 说明 |
|
| 文档 | 状态 | 说明 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| [STATUS.md](./STATUS.md) | ✅ | 单一状态源(本文件) |
|
| [STATUS.md](./STATUS.md) | ✅ | 单一状态源(本文件) |
|
||||||
|
|||||||
Reference in New Issue
Block a user