- 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).
2119 lines
88 KiB
C
2119 lines
88 KiB
C
/* DSK Main Kernel — boot entry, FAT32 firstInit check, ELF loader */
|
||
|
||
#include "../UTSM/include/utsm/dsk.h"
|
||
#include "../UTSM/include/utsm/linux_compat.h"
|
||
#include "port.h"
|
||
#include "../tools/fat32_lfn.h"
|
||
#include "../tools/fat32_part.h"
|
||
#include "ext4.h"
|
||
|
||
/* Embedded dcplogo.png / xjlogo.png RGBA data (auto-generated by
|
||
* png_to_rgba_prefixed.py from TEXTURES directory; must stay file-local like
|
||
* other embedded art) */
|
||
#include "dcp_logo_data.c"
|
||
#include "xj_logo_data.c"
|
||
|
||
/* Embedded ASCII bitmap font (Consolas 18px grayscale, 11x18 cell) */
|
||
#include "../firstInit/ascii_bitmaps.c"
|
||
|
||
/* Embedded ohMyLogo.png RGBA data(莲花 panic 界面用,与 login.elf 同源) */
|
||
#include "../login/ohmylogo_data.c"
|
||
|
||
#define COM1 0x3F8
|
||
|
||
typedef unsigned char u8;
|
||
typedef unsigned short u16;
|
||
typedef unsigned int u32;
|
||
typedef unsigned long long u64;
|
||
typedef long long i64;
|
||
|
||
typedef int (*block_read_fn)(u32 index, u64 lba, u32 count, void *buf);
|
||
typedef int (*block_write_fn)(u32 index, u64 lba, u32 count, const void *buf);
|
||
|
||
/* freestanding 无 stddef.h,本地提供 offsetof */
|
||
#ifndef offsetof
|
||
#define offsetof(type, member) __builtin_offsetof(type, member)
|
||
#endif
|
||
|
||
/* ---- BUG-021 修复: kernel_api 镜像结构体 + 编译期偏移校验 ----
|
||
* DSK 独立编译不含 dkm_kernel_api 定义,为避免硬偏移脆蠕性,
|
||
* 在此定义镜像结构体并用 _Static_assert 校验 block 字段偏移
|
||
* 与 dkm.h 中 dkm_kernel_api.block (0xA8) 一致。 */
|
||
typedef struct dsk_block_api_mirror {
|
||
void *pad0; void *pad1; /* register_device, device_count */
|
||
block_read_fn read;
|
||
block_write_fn write;
|
||
void *pad2; void *pad3; void *pad4; /* sector_size, device_name, set_write_fn */
|
||
u64 (*sector_count)(u32 index); /* 0x38: tail-append, 老内核该槽为 NULL */
|
||
} dsk_block_api_mirror;
|
||
|
||
typedef struct dsk_kernel_api_mirror {
|
||
u32 version; u32 size; u64 feature_bits;
|
||
const void *log; const void *mem; const void *utsm;
|
||
const void *irq; const void *pci; const void *dma;
|
||
const void *vfs; const void *net; const void *timer; const void *drr;
|
||
const void *rsdp_address; const void *fb_address;
|
||
u64 fb_width; u64 fb_height; u64 fb_pitch; u16 fb_bpp; u16 _pad_bpp;
|
||
const void *boot_modules_response;
|
||
int (*irq_register)(u8 irq, void *handler);
|
||
u64 hhdm_offset;
|
||
const dsk_block_api_mirror *block; /* 必须在偏移 0xA8 */
|
||
const void *mmio;
|
||
void *(*mm_map_mmio)(u64 phys, u64 size);
|
||
void (*mm_unmap_mmio)(void *virt, u64 size);
|
||
} dsk_kernel_api_mirror;
|
||
_Static_assert(offsetof(dsk_kernel_api_mirror, block) == 0xA8,
|
||
"dsk_kernel_api_mirror.block offset must be 0xA8 (matching dkm_kernel_api)");
|
||
_Static_assert(offsetof(dsk_block_api_mirror, read) == 16,
|
||
"dsk_block_api_mirror.read offset must be 16 (matching dkm_block_api)");
|
||
_Static_assert(offsetof(dsk_block_api_mirror, sector_count) == 0x38,
|
||
"dsk_block_api_mirror.sector_count offset must be 0x38 (matching dkm_block_api tail-append)");
|
||
|
||
static void sputc(char c) { for(unsigned i=0;i<100000;i++){if(inb(COM1+5)&0x20)break;} outb(COM1,(u8)c); }
|
||
static void swrite(const char *s) { while(*s){if(*s=='\n')sputc('\r');sputc(*s++);} }
|
||
static void logl(const char *s) { swrite(s); swrite("\n"); }
|
||
static void logh(const char *p, u64 v) { static const char h[]="0123456789abcdef"; char b[19]; int i=0; b[i++]='0';b[i++]='x';
|
||
for(int j=15;j>=0;j--)b[i++]=h[(v>>(j*4))&0xf];b[i]=0; swrite(p);swrite(b);swrite("\n"); }
|
||
|
||
/* ---- DSK 插桩: 轻量级 TSC 计时 (forward-declared, rdtsc_dsk defined later) ---- */
|
||
static u64 rdtsc_dsk(void);
|
||
#define DSK_TS_DECL(name) static u64 name##_ts = 0
|
||
#define DSK_TS_BEGIN(name) do { name##_ts = rdtsc_dsk(); } while(0)
|
||
#define DSK_TS_END(name, label) do { \
|
||
if (g_tsc_per_ms) { \
|
||
u64 _d = rdtsc_dsk() - name##_ts; \
|
||
u64 _ms = _d / g_tsc_per_ms; \
|
||
logl("[DSK:TS] " label); logh(" elapsed_ms=", _ms); \
|
||
} \
|
||
} while(0)
|
||
|
||
/* ---- DSK 插桩: 操作计数器 ---- */
|
||
#define DSK_STAT_DECL(name) static u32 name##_count = 0
|
||
#define DSK_STAT_INC(name) do { name##_count++; } while(0)
|
||
|
||
static void *memset_dsk(void *d, int c, u64 n) { u8 *p=(u8*)d; while(n--)*p++=(u8)c; return d; }
|
||
static void *memcpy_dsk(void *d, const void *s, u64 n) { u8 *dd=(u8*)d; const u8 *ss=(const u8*)s; while(n--)*dd++=*ss++; return d; }
|
||
|
||
typedef struct __attribute__((packed)) {
|
||
u8 jmp[3]; char oem[8]; u16 bps; u8 spc; u16 rsvd; u8 fc; u16 root_ent;
|
||
u16 ts16; u8 media; u16 spf16; u16 spt; u16 heads; u32 hidden; u32 ts32;
|
||
u32 spf; u16 flags; u16 ver; u32 root_clus; u16 fsi; u16 bkboot;
|
||
u8 res[12]; u8 drv; u8 ntfl; u8 sig; u32 ser; char lbl[11]; char typ[8];
|
||
u8 code[420]; u16 boot_sig;
|
||
} fat32_bpb;
|
||
|
||
typedef struct __attribute__((packed)) {
|
||
char name[11]; u8 attr; u8 ntr; u8 ctenth;
|
||
u16 ctime; u16 cdate; u16 adate; u16 chigh;
|
||
u16 wtime; u16 wdate; u16 clow; u32 fsize;
|
||
} fat32_de;
|
||
|
||
static u32 r32(const u8 *p) { return (u32)p[0]|((u32)p[1]<<8)|((u32)p[2]<<16)|((u32)p[3]<<24); }
|
||
static u16 r16(const u8 *p) { return (u16)p[0]|((u16)p[1]<<8); }
|
||
static int neq11(const char *a, const char *b) { for(int i=0;i<11;i++) if(a[i]!=b[i]) return 0; return 1; }
|
||
|
||
static u64 g_fb_addr, g_fb_w, g_fb_h, g_fb_p;
|
||
static block_read_fn g_block_read;
|
||
static block_write_fn g_block_write;
|
||
static u32 g_fat_lba_off = 0;
|
||
static u64 g_tsc_per_ms = 0;
|
||
static u8 g_disk[131072];
|
||
static u8 g_cluster[FAT32_MAX_CLUSTER_BYTES];
|
||
static u8 g_fdata[10485760]; /* 10MB file data buffer (与 UTSM 端一致, 支持 SYSTEM/lib 大型 DLL) */
|
||
/* BUG-019 修复: FAT 按需扇区读取缓冲区(仅 512 字节) */
|
||
static u8 g_fat_sec[512];
|
||
static u8 g_milestone_buf[FAT32_MAX_CLUSTER_BYTES]; /* milestone write cluster buffer */
|
||
/* 网络租约缓冲:DSK 常驻持有,调度 netman 前经 reserved[7] 暴露指针,
|
||
* netman 写入 DHCP/静态配置,后续模块(shell/desktop/工具)经同一指针读取。 */
|
||
static net_lease_info g_net_lease;
|
||
|
||
DSK_STAT_DECL(fat32_read);
|
||
DSK_STAT_DECL(fat32_write);
|
||
DSK_STAT_DECL(elf_load);
|
||
DSK_STAT_DECL(mod_schedule);
|
||
|
||
static int fat32_read_sectors(u32 lba, u32 count, u8 *out) {
|
||
DSK_STAT_INC(fat32_read);
|
||
return g_block_read ? g_block_read(0, (u64)lba + g_fat_lba_off, count, out) : -1;
|
||
}
|
||
static int fat32_write_sectors(u32 lba, u32 count, const u8 *buf) {
|
||
DSK_STAT_INC(fat32_write);
|
||
return g_block_write ? g_block_write(0, (u64)lba + g_fat_lba_off, count, buf) : -1;
|
||
}
|
||
|
||
static void fat32_probe_esp(void) {
|
||
g_fat_lba_off = (u32)fat32_part_probe(g_block_read, 0);
|
||
logh("[DSK] FAT32 volume LBA=", (u64)g_fat_lba_off);
|
||
}
|
||
|
||
/* BUG-019 修复: 按需读取 FAT 扇区获取下一个簇号。
|
||
* 如果 FAT 条目在预读缓冲区内直接读取;
|
||
* 否则只读取包含该条目的单个 FAT 扇区(512 字节)。 */
|
||
static u32 fat32_get_next_cluster(u32 fat_byte_off, u32 clus, u32 fat_rsvd_sec) {
|
||
u32 ent_off = fat_byte_off + clus * 4;
|
||
/* 情况 1: 在预读缓冲区内 */
|
||
if (ent_off + 4 <= sizeof(g_disk)) {
|
||
return r32(g_disk + ent_off) & 0x0FFFFFFF;
|
||
}
|
||
/* 情况 2: 超出预读缓冲区——按需读取单扇区 */
|
||
u32 fat_byte_in_fat = clus * 4;
|
||
u32 sec_idx = fat_byte_in_fat / 512;
|
||
u32 off_in_sec = fat_byte_in_fat % 512;
|
||
u32 sec_lba = fat_rsvd_sec + sec_idx;
|
||
if (fat32_read_sectors(sec_lba, 1, g_fat_sec) != 0) {
|
||
logl("[DSK] FAT32: on-demand FAT sector read failed");
|
||
logh("[DSK] FAT32: sec_lba=", (u64)sec_lba);
|
||
return 0x0FFFFFFF;
|
||
}
|
||
return r32(g_fat_sec + off_in_sec) & 0x0FFFFFFF;
|
||
}
|
||
|
||
/* ---- DSK 内嵌 INI 解析器 ---- */
|
||
typedef struct { char section[32]; char key[32]; char value[64]; } dsk_ini_entry;
|
||
typedef struct { dsk_ini_entry entries[64]; u32 count; } dsk_ini_config;
|
||
|
||
static int dsk_ini_streq(const char *a, const char *b) {
|
||
if(!a||!b) return 0;
|
||
while(*a&&*b){ if(*a!=*b) return 0; a++; b++; }
|
||
return *a==*b;
|
||
}
|
||
|
||
static int dsk_ini_atoi(const char *s) {
|
||
int neg=0, v=0;
|
||
if(!s) return 0;
|
||
while(*s==' '||*s=='\t') s++;
|
||
if(*s=='-'){neg=1;s++;}
|
||
while(*s>='0'&&*s<='9'){v=v*10+(*s-'0');s++;}
|
||
return neg?-v:v;
|
||
}
|
||
|
||
static int dsk_ini_parse_bool(const char *s) {
|
||
if(!s) return -1;
|
||
if(s[0]=='1') return 1;
|
||
if(s[0]=='0') return 0;
|
||
if(dsk_ini_streq(s,"true")) return 1;
|
||
if(dsk_ini_streq(s,"false")) return 0;
|
||
return -1;
|
||
}
|
||
|
||
static int dsk_ini_parse(const char *text, u32 size, dsk_ini_config *cfg) {
|
||
if(!text||!cfg) return -1;
|
||
cfg->count = 0;
|
||
char cur_sec[32]; for(int i=0;i<32;i++) cur_sec[i]=0;
|
||
u32 pos = 0;
|
||
while(pos < size) {
|
||
while(pos<size&&(text[pos]==' '||text[pos]=='\t')) pos++;
|
||
if(pos>=size) break;
|
||
char c = text[pos];
|
||
if(c=='\n'){pos++;continue;}
|
||
if(c=='#'||c==';'){while(pos<size&&text[pos]!='\n')pos++;continue;}
|
||
if(c=='['){
|
||
pos++;
|
||
u32 si=0;
|
||
while(pos<size&&text[pos]!=']'&&text[pos]!='\n'&&si+1<32) cur_sec[si++]=text[pos++];
|
||
cur_sec[si]=0;
|
||
while(pos<size&&text[pos]!='\n') pos++;
|
||
continue;
|
||
}
|
||
if(cfg->count>=64) break;
|
||
dsk_ini_entry *e = &cfg->entries[cfg->count];
|
||
u32 ki=0;
|
||
while(pos<size&&text[pos]!='='&&text[pos]!='\n'&&!(text[pos]==' '||text[pos]=='\t')&&ki+1<32) e->key[ki++]=text[pos++];
|
||
e->key[ki]=0;
|
||
while(pos<size&&(text[pos]==' '||text[pos]=='\t')&&text[pos]!='\n') pos++;
|
||
if(pos>=size||text[pos]!='='){while(pos<size&&text[pos]!='\n')pos++;continue;}
|
||
pos++;
|
||
while(pos<size&&(text[pos]==' '||text[pos]=='\t')&&text[pos]!='\n') pos++;
|
||
u32 vi=0;
|
||
while(pos<size&&text[pos]!='\n'&&vi+1<64) e->value[vi++]=text[pos++];
|
||
while(vi>0&&(e->value[vi-1]==' '||e->value[vi-1]=='\t')) vi--;
|
||
e->value[vi]=0;
|
||
for(int i=0;i<32;i++) e->section[i]=cur_sec[i];
|
||
cfg->count++;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static const char *dsk_ini_get(const dsk_ini_config *cfg, const char *sec, const char *key) {
|
||
if(!cfg||!sec||!key) return 0;
|
||
for(u32 i=0;i<cfg->count;i++)
|
||
if(dsk_ini_streq(cfg->entries[i].section,sec)&&dsk_ini_streq(cfg->entries[i].key,key))
|
||
return cfg->entries[i].value;
|
||
return 0;
|
||
}
|
||
|
||
__attribute__((unused))
|
||
static int dsk_ini_get_int(const dsk_ini_config *cfg, const char *sec, const char *key, int def) {
|
||
const char *v=dsk_ini_get(cfg,sec,key);
|
||
if(!v||v[0]==0) return def;
|
||
return dsk_ini_atoi(v);
|
||
}
|
||
|
||
static int dsk_ini_get_bool(const dsk_ini_config *cfg, const char *sec, const char *key, int def) {
|
||
const char *v=dsk_ini_get(cfg,sec,key);
|
||
if(!v||v[0]==0) return def;
|
||
int b=dsk_ini_parse_bool(v);
|
||
return (b>=0)?b:def;
|
||
}
|
||
|
||
/* ---- DSK FAT32 子目录遍历 ---- */
|
||
|
||
/* 在指定起始 cluster 的目录中查找 name11(8.3)或 long_name(LFN 长名)。
|
||
* long_name 为 NULL 时仅按 8.3 匹配(向后兼容)。
|
||
* is_dir=1 只匹配子目录,is_dir=0 只匹配文件。
|
||
* 注意:需先调用 fat32_read_sectors(0,256,g_disk) 确保 BPB 缓冲可用。
|
||
* LFN 缓冲跨簇遍历持续存活,支持跨簇边界的 LFN 链。 */
|
||
static int fat32_find_in_dir(u32 start_clus, const char *name11, int is_dir,
|
||
u32 *out_clus, u32 *out_size, const char *long_name) {
|
||
const fat32_bpb *bpb = (const fat32_bpb *)g_disk;
|
||
if(bpb->boot_sig!=0xAA55||bpb->bps!=512) return -2;
|
||
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
|
||
u32 fat_byte_off = bpb->rsvd * 512;
|
||
u32 spc = bpb->spc;
|
||
if (spc == 0 || (u64)spc * 512u > sizeof(g_cluster)) return -2;
|
||
|
||
/* LFN 缓冲必须在簇循环外声明:跨簇 LFN 链需要持续存活 */
|
||
fat32_lfn_buf lfn;
|
||
fat32_lfn_init(&lfn);
|
||
|
||
u32 clus = start_clus;
|
||
while(clus>=2 && clus<0x0FFFFFF8) {
|
||
u32 lba = data_lba + (clus-2)*spc;
|
||
const u8 *cb;
|
||
if((u64)lba*512+(u64)spc*512<=256ULL*512)
|
||
cb = g_disk + (u64)lba*512;
|
||
else {
|
||
if(fat32_read_sectors(lba,spc,g_cluster)!=0) return -3;
|
||
cb = g_cluster;
|
||
}
|
||
const u8 *entries = (const u8 *)cb;
|
||
u32 entry_count = (spc*512)/32;
|
||
for(u32 e=0; e<entry_count; e++) {
|
||
const u8 *entry = entries + e*32;
|
||
if(entry[0]==0) break;
|
||
if((u8)entry[0]==0xE5) { fat32_lfn_init(&lfn); continue; }
|
||
const fat32_de *de = (const fat32_de *)entry;
|
||
int is_short = fat32_lfn_process(&lfn, entry);
|
||
if(!is_short) continue; /* LFN 片段 */
|
||
if(de->attr&0x08) { fat32_lfn_init(&lfn); continue; } /* 卷标 */
|
||
int entry_is_dir = (de->attr&0x10)?1:0;
|
||
if(entry_is_dir!=is_dir) { fat32_lfn_init(&lfn); continue; }
|
||
/* 匹配检查:先 8.3 短名,再 LFN 长名 */
|
||
int matched = 0;
|
||
if(name11 && neq11(de->name, name11)) {
|
||
matched = 1;
|
||
} else if(long_name) {
|
||
if(lfn.valid) {
|
||
char ascii[FAT32_LFN_MAX];
|
||
int n = fat32_lfn_to_ascii(&lfn, ascii, sizeof(ascii));
|
||
if(n>=0 && fat32_lfn_streq_ci(ascii, long_name)) matched = 1;
|
||
}
|
||
if(!matched) {
|
||
char short_disp[13];
|
||
fat32_lfn_short_to_str((const u8 *)de->name, short_disp);
|
||
if(fat32_lfn_streq_ci(short_disp, long_name)) matched = 1;
|
||
}
|
||
}
|
||
if(matched) {
|
||
*out_clus = r16((const u8*)&de->clow)|((u32)r16((const u8*)&de->chigh)<<16);
|
||
*out_size = de->fsize;
|
||
return 0;
|
||
}
|
||
fat32_lfn_init(&lfn);
|
||
}
|
||
/* BUG-019: 使用按需 FAT 读取 */
|
||
clus = fat32_get_next_cluster(fat_byte_off, clus, bpb->rsvd);
|
||
}
|
||
return -4;
|
||
}
|
||
|
||
/* 按 '/' 分隔的路径读取文件。支持 8.3 路径与长名路径两种格式。
|
||
* 需先调用 fat32_read_sectors(0,256,g_disk) 确保 BPB 可用。 */
|
||
static int fat32_read_path(const char *path, u8 **out_data, u32 *out_size) {
|
||
DSK_TS_DECL(fat32_path_read);
|
||
DSK_TS_BEGIN(fat32_path_read);
|
||
const fat32_bpb *bpb = (const fat32_bpb *)g_disk;
|
||
if(bpb->boot_sig!=0xAA55||bpb->bps!=512) { logl("[DSK] fat32_read_path: bad BPB"); return -2; }
|
||
u32 fat_byte_off = bpb->rsvd*512;
|
||
u32 data_lba = bpb->rsvd + (u32)bpb->fc*bpb->spf;
|
||
u32 spc = bpb->spc;
|
||
|
||
u32 pos=0, path_len=0;
|
||
while(path[path_len]) path_len++;
|
||
u32 cur_clus = bpb->root_clus;
|
||
|
||
while(pos<path_len) {
|
||
char comp[12]; u32 ci=0;
|
||
char orig[260]; u32 oi=0;
|
||
while(pos<path_len&&path[pos]!='/') {
|
||
if(ci<11) comp[ci++]=path[pos];
|
||
if(oi+1<sizeof(orig)) orig[oi++]=path[pos];
|
||
pos++;
|
||
}
|
||
while(ci<11) comp[ci++]=' ';
|
||
comp[11]=0;
|
||
orig[oi]=0;
|
||
if(pos<path_len&&path[pos]=='/') pos++;
|
||
int is_last = (pos>=path_len);
|
||
|
||
if(is_last) {
|
||
u32 found_clus=0, found_size=0;
|
||
if(fat32_find_in_dir(cur_clus,comp,0,&found_clus,&found_size,
|
||
(oi>0 && oi!=11)?orig:0)!=0) { logl("[DSK] fat32_read_path: file not found: "); swrite(path); swrite("\n"); return -4; }
|
||
if(found_size>sizeof(g_fdata)) { logh("[DSK] fat32_read_path: file too large size=", (u64)found_size); return -5; }
|
||
u8 *dst=g_fdata; u32 remaining=found_size; u32 fc=found_clus;
|
||
while(fc>=2&&fc<0x0FFFFFF8&&remaining>0) {
|
||
u32 fc_lba=data_lba+(fc-2)*spc;
|
||
u32 fc_bytes=spc*512;
|
||
if(fc_bytes>remaining) fc_bytes=remaining;
|
||
const u8 *fb;
|
||
if((u64)fc_lba*512+(u64)spc*512<=256ULL*512)
|
||
fb=g_disk+(u64)fc_lba*512;
|
||
else {
|
||
if(fat32_read_sectors(fc_lba,spc,g_cluster)!=0) { logh("[DSK] fat32_read_path: data cluster read failed lba=", (u64)fc_lba); return -6; }
|
||
fb=g_cluster;
|
||
}
|
||
for(u32 b=0;b<fc_bytes;b++) dst[b]=fb[b];
|
||
dst+=fc_bytes; remaining-=fc_bytes;
|
||
/* BUG-019: 使用按需 FAT 读取 */
|
||
fc = fat32_get_next_cluster(fat_byte_off, fc, bpb->rsvd);
|
||
}
|
||
*out_data=g_fdata; *out_size=found_size;
|
||
DSK_TS_END(fat32_path_read, "fat32_read_path");
|
||
return 0;
|
||
} else {
|
||
u32 sub_clus=0, sub_size=0;
|
||
if(fat32_find_in_dir(cur_clus,comp,1,&sub_clus,&sub_size,
|
||
(oi>0 && oi!=11)?orig:0)!=0) { logl("[DSK] fat32_read_path: dir not found in path: "); swrite(path); swrite("\n"); return -7; }
|
||
cur_clus=sub_clus;
|
||
}
|
||
}
|
||
DSK_TS_END(fat32_path_read, "fat32_read_path (failed)");
|
||
return -8;
|
||
}
|
||
|
||
/* Navigate a '/'-separated path (8.3 or LFN) to find a directory cluster.
|
||
* Returns the cluster of the final directory, or 0 on failure.
|
||
* Requires g_disk to contain a valid BPB. */
|
||
static u32 fat32_find_dir_cluster(const char *path) {
|
||
const fat32_bpb *bpb = (const fat32_bpb *)g_disk;
|
||
if (bpb->boot_sig != 0xAA55 || bpb->bps != 512) return 0;
|
||
u32 pos = 0, path_len = 0;
|
||
while (path[path_len]) path_len++;
|
||
u32 cur_clus = bpb->root_clus;
|
||
while (pos < path_len) {
|
||
char comp[12]; u32 ci = 0;
|
||
char orig[260]; u32 oi = 0;
|
||
while (pos < path_len && path[pos] != '/') {
|
||
if (ci < 11) comp[ci++] = path[pos];
|
||
if (oi + 1 < sizeof(orig)) orig[oi++] = path[pos];
|
||
pos++;
|
||
}
|
||
while (ci < 11) comp[ci++] = ' ';
|
||
comp[11] = 0;
|
||
orig[oi] = 0;
|
||
if (pos < path_len && path[pos] == '/') pos++;
|
||
u32 sub_clus = 0, sub_size = 0;
|
||
if (fat32_find_in_dir(cur_clus, comp, 1, &sub_clus, &sub_size,
|
||
(oi > 0 && oi != 11) ? orig : 0) != 0) return 0;
|
||
cur_clus = sub_clus;
|
||
}
|
||
return cur_clus;
|
||
}
|
||
|
||
#define EI_MAG0 0x7f
|
||
#define ELFCLASS64 2
|
||
#define ELFDATA2LSB 1
|
||
#define EM_X86_64 62
|
||
#define PT_LOAD 1
|
||
#define PT_DYNAMIC 2
|
||
#define DT_RELA 7
|
||
#define DT_RELASZ 8
|
||
#define DT_RELAENT 9
|
||
#define R_X86_64_RELATIVE 8
|
||
typedef struct { u8 ident[16]; u16 type,machine; u32 ver; u64 entry,phoff,shoff; u32 flags; u16 ehsize,phentsize,phnum,shentsize,shnum,shstrndx; } elf64_ehdr;
|
||
typedef struct { u32 type,flags; u64 offset,vaddr,paddr,filesz,memsz,align; } elf64_phdr;
|
||
typedef struct { i64 tag; u64 val; } elf64_dyn;
|
||
typedef struct { u64 offset; u64 info; i64 addend; } elf64_rela;
|
||
|
||
#define BG_TOP 0xFFC8E0F0u
|
||
#define BG_BOTTOM 0xFF49306Fu
|
||
#define BG_MID 0xFF8888B0u
|
||
|
||
/* ---- TSC-based timing ---- */
|
||
static u64 rdtsc_dsk(void) {
|
||
u32 lo, hi;
|
||
__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
|
||
return ((u64)hi << 32) | lo;
|
||
}
|
||
|
||
static void tsc_calibrate_dsk(void) {
|
||
outb(0x43, 0x30); /* ch0, lo+hi, mode 0, binary */
|
||
outb(0x40, 0x7c); /* 11932 low = ~10ms */
|
||
outb(0x40, 0x2e); /* 11932 high */
|
||
u64 tsc_start = rdtsc_dsk();
|
||
u16 prev = 0; u64 loops = 0;
|
||
for (;;) {
|
||
outb(0x43, 0x00);
|
||
u16 cur = (u16)inb(0x40) | ((u16)inb(0x40) << 8);
|
||
if (cur > prev && loops > 10) break;
|
||
prev = cur; loops++;
|
||
}
|
||
u64 tsc_end = rdtsc_dsk();
|
||
g_tsc_per_ms = (tsc_end - tsc_start) / 10;
|
||
logh("[DSK] tsc_per_ms=", g_tsc_per_ms);
|
||
}
|
||
|
||
static u32 blend_color(u32 c1, u32 c2, u32 a) {
|
||
u32 na = 256 - a;
|
||
u32 r = ((c1 & 0xFF) * na + (c2 & 0xFF) * a) >> 8;
|
||
u32 g = (((c1 >> 8) & 0xFF) * na + ((c2 >> 8) & 0xFF) * a) >> 8;
|
||
u32 b = (((c1 >> 16) & 0xFF) * na + ((c2 >> 16) & 0xFF) * a) >> 8;
|
||
return 0xFF000000 | (b << 16) | (g << 8) | r;
|
||
}
|
||
|
||
static u32 dsk_bg_at_y(i64 y) {
|
||
if (g_fb_h <= 1) return BG_TOP;
|
||
if (y < 0) y = 0;
|
||
if ((u64)y >= g_fb_h) y = (i64)g_fb_h - 1;
|
||
return blend_color(BG_TOP, BG_BOTTOM, (u32)(((u64)y * 255ULL) / (g_fb_h - 1)));
|
||
}
|
||
|
||
static void dsk_fill_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 color) {
|
||
for (i64 r = 0; r < h; r++) {
|
||
i64 yy = y + r;
|
||
if (yy < 0 || (u64)yy >= g_fb_h) continue;
|
||
u32 *line = (u32 *)((u8 *)fb + (u64)yy * g_fb_p);
|
||
for (i64 c = 0; c < w; c++) {
|
||
i64 xx = x + c;
|
||
if (xx < 0 || (u64)xx >= g_fb_w) continue;
|
||
line[(u64)xx] = color;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void dsk_fill_gradient_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h) {
|
||
for (i64 r = 0; r < h; r++) {
|
||
i64 yy = y + r;
|
||
if (yy < 0 || (u64)yy >= g_fb_h) continue;
|
||
u32 color = dsk_bg_at_y(yy);
|
||
u32 *line = (u32 *)((u8 *)fb + (u64)yy * g_fb_p);
|
||
for (i64 c = 0; c < w; c++) {
|
||
i64 xx = x + c;
|
||
if (xx < 0 || (u64)xx >= g_fb_w) continue;
|
||
line[(u64)xx] = color;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* ---- Spinner animation (removed — replaced by static Logo.png display) ---- */
|
||
|
||
/* ---- Logo rendering: center RGBA image with alpha blending on gradient bg ----
|
||
* g_logo_rgba / g_logo_w / g_logo_h are from logo_data.c (included above).
|
||
* Source pixel format: RGBA8888 (R at byte 0, G byte 1, B byte 2, A byte 3).
|
||
* Framebuffer format: UEFI BGRX — u32 = 0xAARRGGBB
|
||
* bits 0-7 = B, bits 8-15 = G, bits 16-23 = R, bits 24-31 = A/X
|
||
*/
|
||
static void dsk_draw_rgba(u32 *fb, const u8 *pix, int w, int h) {
|
||
/* Center the image on screen */
|
||
i64 ox = ((i64)g_fb_w - w) / 2;
|
||
i64 oy = ((i64)g_fb_h - h) / 2;
|
||
|
||
for (int ly = 0; ly < h; ly++) {
|
||
i64 fy = oy + ly;
|
||
if (fy < 0 || (u64)fy >= g_fb_h) continue;
|
||
u32 *line = (u32 *)((u8 *)fb + (u64)fy * g_fb_p);
|
||
|
||
for (int lx = 0; lx < w; lx++) {
|
||
i64 fx = ox + lx;
|
||
if (fx < 0 || (u64)fx >= g_fb_w) continue;
|
||
|
||
/* Read RGBA pixel from embedded data */
|
||
int idx = (ly * w + lx) * 4;
|
||
u8 r = pix[idx + 0];
|
||
u8 g = pix[idx + 1];
|
||
u8 b = pix[idx + 2];
|
||
u8 a = pix[idx + 3];
|
||
|
||
if (a == 0) continue; /* fully transparent: skip */
|
||
if (a == 255) {
|
||
/* fully opaque: BGRX = 0xFF000000 | (R<<16) | (G<<8) | B */
|
||
line[(u64)fx] = 0xFF000000u | ((u32)r << 16) | ((u32)g << 8) | b;
|
||
} else {
|
||
/* alpha blend with background (BGRX channel order) */
|
||
u32 bg = line[(u64)fx];
|
||
u32 na = 256 - a;
|
||
u32 ob = (((bg & 0xFF) * na + (u32)b * a) >> 8) & 0xFF;
|
||
u32 og = ((((bg >> 8) & 0xFF) * na + (u32)g * a) >> 8) & 0xFF;
|
||
u32 or = ((((bg >> 16)& 0xFF) * na + (u32)r * a) >> 8) & 0xFF;
|
||
line[(u64)fx] = 0xFF000000u | (or << 16) | (og << 8) | ob;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Splash sequence entry: one image + hold for the given milliseconds.
|
||
* Redraws the gradient background first so successive images replace
|
||
* each other cleanly. */
|
||
static void dsk_splash(u32 *fb, const u8 *pix, int w, int h, u64 hold_ms,
|
||
const char *name) {
|
||
dsk_fill_gradient_rect(fb, 0, 0, (i64)g_fb_w, (i64)g_fb_h);
|
||
dsk_draw_rgba(fb, pix, w, h);
|
||
logl("[DSK] splash: showing");
|
||
logl(name);
|
||
u64 start = rdtsc_dsk();
|
||
u64 budget = g_tsc_per_ms * hold_ms;
|
||
while (rdtsc_dsk() - start < budget) __asm__("pause");
|
||
}
|
||
|
||
/* ---- Text rendering: ASCII bitmap font on framebuffer ----
|
||
* Uses g_ascii[95][11*18] from ascii_bitmaps.c (Consolas 18px).
|
||
* Framebuffer format: BGRX — u32 = 0xAARRGGBB.
|
||
*/
|
||
static void dsk_draw_char(u32 *fb, char ch, i64 x, i64 y, u32 fg) {
|
||
u32 idx = (u32)(ch - ' ');
|
||
if (idx > 94) idx = 0;
|
||
const u8 *glyph = g_ascii[idx];
|
||
for (int r = 0; r < g_ascii_h; r++) {
|
||
i64 yy = y + r;
|
||
if (yy < 0 || (u64)yy >= g_fb_h) continue;
|
||
u32 *line = (u32 *)((u8 *)fb + (u64)yy * g_fb_p);
|
||
for (int c = 0; c < g_ascii_w; c++) {
|
||
u8 a = glyph[r * g_ascii_w + c];
|
||
if (a == 0) continue;
|
||
i64 xx = x + c;
|
||
if (xx < 0 || (u64)xx >= g_fb_w) continue;
|
||
if (a == 255) {
|
||
line[(u64)xx] = fg;
|
||
} else {
|
||
/* alpha blend with background */
|
||
u32 bg = line[(u64)xx];
|
||
u32 na = 256 - a;
|
||
u32 ob = (((bg & 0xFF) * na + ((fg & 0xFF) ) * a) >> 8) & 0xFF;
|
||
u32 og = ((((bg >> 8) & 0xFF) * na + (((fg >> 8) & 0xFF)) * a) >> 8) & 0xFF;
|
||
u32 or = ((((bg >> 16)& 0xFF) * na + (((fg >> 16)& 0xFF)) * a) >> 8) & 0xFF;
|
||
line[(u64)xx] = 0xFF000000u | (or << 16) | (og << 8) | ob;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static void dsk_draw_char_scaled(u32 *fb, char ch, i64 x, i64 y, u32 fg, int scale) {
|
||
u32 idx = (u32)(ch - ' ');
|
||
int r, c, sy, sx;
|
||
if (idx > 94) idx = 0;
|
||
if (scale < 1) scale = 1;
|
||
const u8 *glyph = g_ascii[idx];
|
||
for (r = 0; r < g_ascii_h; r++) {
|
||
for (c = 0; c < g_ascii_w; c++) {
|
||
u8 a = glyph[r * g_ascii_w + c];
|
||
if (a == 0) continue;
|
||
for (sy = 0; sy < scale; sy++) {
|
||
i64 yy = y + (i64)r * scale + sy;
|
||
u32 *line;
|
||
if (yy < 0 || (u64)yy >= g_fb_h) continue;
|
||
line = (u32 *)((u8 *)fb + (u64)yy * g_fb_p);
|
||
for (sx = 0; sx < scale; sx++) {
|
||
i64 xx = x + (i64)c * scale + sx;
|
||
if (xx < 0 || (u64)xx >= g_fb_w) continue;
|
||
line[(u64)xx] = fg;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static void dsk_draw_string(u32 *fb, const char *s, i64 x, i64 y, u32 fg, int spacing) {
|
||
i64 cx = x;
|
||
while (*s) {
|
||
dsk_draw_char(fb, *s, cx, y, fg);
|
||
cx += spacing;
|
||
s++;
|
||
}
|
||
}
|
||
|
||
/* ===================================================================
|
||
* 严格错误策略插桩: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) {
|
||
const fat32_de *dir = (const fat32_de *)clus;
|
||
for (u32 e=0; e*32 < clus_sectors*512; e++) {
|
||
if (dir[e].name[0]==0) break;
|
||
if ((u8)dir[e].name[0]==0xE5) continue;
|
||
if (dir[e].attr==0x0F) continue;
|
||
if (dir[e].attr & 0x08) continue;
|
||
if (neq11(dir[e].name, target)) {
|
||
*out_clus = r16((const u8*)&dir[e].clow) | ((u32)r16((const u8*)&dir[e].chigh) << 16);
|
||
*out_size = dir[e].fsize;
|
||
return 0;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/* find a single file in root directory by 11-char 8.3 name (avoids PIE pointer reloc) */
|
||
static int fat32_read_root_file(const char *name11, u8 **out_data, u32 *out_size) {
|
||
u8 *disk = g_disk;
|
||
DSK_TS_DECL(fat32_root_read);
|
||
DSK_TS_BEGIN(fat32_root_read);
|
||
logl("[DSK] fat32 reading 256 sectors from LBA 0");
|
||
int st = fat32_read_sectors(0, 256, disk);
|
||
logh("[DSK] fat32 read_sectors rc=", (u64)(i64)st);
|
||
if (st != 0) { logl("[DSK] fat32_read_root_file: BPB read failed"); return -1; }
|
||
const fat32_bpb *bpb = (const fat32_bpb *)disk;
|
||
if (bpb->boot_sig != 0xAA55 || bpb->bps != 512) { logl("[DSK] fat32_read_root_file: bad BPB sig/bps"); return -2; }
|
||
if (bpb->spf == 0 || bpb->root_clus < 2) { logh("[DSK] fat32_read_root_file: bad spf/root_clus spf=", (u64)bpb->spf); return -3; }
|
||
|
||
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
|
||
u32 fat_byte_off = bpb->rsvd * 512;
|
||
u32 spc = bpb->spc;
|
||
logh("[DSK] fat32 data_lba=", data_lba);
|
||
logh("[DSK] fat32 spc=", (u64)spc);
|
||
if (spc == 0 || (u64)spc * 512u > sizeof(g_cluster)) {
|
||
logl("[DSK] fat32 cluster larger than g_cluster");
|
||
return -3;
|
||
}
|
||
|
||
/* walk root directory cluster chain */
|
||
u32 clus = bpb->root_clus;
|
||
u32 found_clus = 0, found_size = 0;
|
||
int found = 0;
|
||
while (clus >= 2 && clus < 0x0FFFFFF8 && !found) {
|
||
u32 lba = data_lba + (clus - 2) * spc;
|
||
const u8 *cb;
|
||
if ((u64)lba * 512 + (u64)spc * 512 <= 256ULL * 512)
|
||
cb = disk + (u64)lba * 512;
|
||
else {
|
||
if (fat32_read_sectors(lba, spc, g_cluster) != 0) { logh("[DSK] fat32_read_root_file: dir cluster read failed lba=", (u64)lba); return -4; }
|
||
cb = g_cluster;
|
||
}
|
||
if (fat32_find_entry(cb, spc, name11, &found_clus, &found_size) == 0) { found = 1; break; }
|
||
/* BUG-019: 使用按需 FAT 读取 */
|
||
clus = fat32_get_next_cluster(fat_byte_off, clus, bpb->rsvd);
|
||
}
|
||
if (!found) { logl("[DSK] fat32 entry not found: "); swrite(name11); swrite("\n"); return -5; }
|
||
logh("[DSK] fat32 found_clus=", found_clus);
|
||
logh("[DSK] fat32 found_size=", found_size);
|
||
|
||
if (found_size > sizeof(g_fdata)) { logh("[DSK] fat32_read_root_file: file too large size=", (u64)found_size); return -6; }
|
||
u8 *dst = g_fdata; u32 remaining = found_size; u32 fc = found_clus;
|
||
while (fc >= 2 && fc < 0x0FFFFFF8 && remaining > 0) {
|
||
u32 fc_lba = data_lba + (fc - 2) * spc;
|
||
u32 fc_bytes = spc * 512;
|
||
if (fc_bytes > remaining) fc_bytes = remaining;
|
||
const u8 *fb;
|
||
if ((u64)fc_lba * 512 + (u64)spc * 512 <= 256ULL * 512)
|
||
fb = disk + (u64)fc_lba * 512;
|
||
else {
|
||
if (fat32_read_sectors(fc_lba, spc, g_cluster) != 0) { logh("[DSK] fat32_read_root_file: data cluster read failed lba=", (u64)fc_lba); return -7; }
|
||
fb = g_cluster;
|
||
}
|
||
for (u32 b = 0; b < fc_bytes; b++) dst[b] = fb[b];
|
||
dst += fc_bytes; remaining -= fc_bytes;
|
||
/* BUG-019: 使用按需 FAT 读取 */
|
||
fc = fat32_get_next_cluster(fat_byte_off, fc, bpb->rsvd);
|
||
}
|
||
*out_data = g_fdata; *out_size = found_size;
|
||
DSK_TS_END(fat32_root_read, "fat32_read_root_file");
|
||
logl("[DSK] fat32 file read done");
|
||
return 0;
|
||
}
|
||
|
||
/* Write a file to the FAT32 root directory by 11-char 8.3 name.
|
||
* 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.
|
||
* 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) {
|
||
u8 *disk = g_disk;
|
||
const fat32_bpb *bpb = (const fat32_bpb *)disk;
|
||
if (bpb->boot_sig != 0xAA55 || bpb->bps != 512) return -1;
|
||
u32 spc = bpb->spc;
|
||
u32 fat_lba = bpb->rsvd;
|
||
u32 fat_sectors = bpb->spf;
|
||
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
|
||
u32 fat_byte_off = bpb->rsvd * 512;
|
||
u32 root_clus = bpb->root_clus;
|
||
u32 cluster_bytes = spc * 512;
|
||
|
||
/* 1. Re-read BPB+FAT to ensure g_disk is fresh */
|
||
if (fat32_read_sectors(0, 256, disk) != 0) return -2;
|
||
|
||
/* 2. Find file entry in root directory — scan ALL clusters in FAT chain */
|
||
u32 cur_clus = root_clus;
|
||
int free_entry = -1;
|
||
u32 free_clus = 0; /* cluster number where free entry was found */
|
||
u32 existing_clus = 0;
|
||
int found = 0;
|
||
u32 found_clus = 0; /* cluster number where existing file was found */
|
||
while (cur_clus >= 2 && cur_clus < 0x0FFFFFF8) {
|
||
u32 clus_lba = data_lba + (cur_clus - 2) * spc;
|
||
int in_buf = ((u64)clus_lba * 512 + cluster_bytes <= 256ULL * 512);
|
||
u8 *buf;
|
||
if (in_buf) {
|
||
buf = disk + (u64)clus_lba * 512;
|
||
} else {
|
||
if (fat32_read_sectors(clus_lba, spc, g_milestone_buf) != 0) {
|
||
logl("[DSK] fat32 write: root dir cluster read failed");
|
||
break;
|
||
}
|
||
buf = g_milestone_buf;
|
||
}
|
||
u32 me = cluster_bytes / 32;
|
||
fat32_de *d = (fat32_de *)buf;
|
||
int cluster_done = 0;
|
||
for (u32 e = 0; e < me; e++) {
|
||
if (d[e].name[0] == 0) {
|
||
if (free_entry < 0) { free_entry = (int)e; free_clus = cur_clus; }
|
||
cluster_done = 1; break;
|
||
}
|
||
if ((u8)d[e].name[0] == 0xE5) {
|
||
if (free_entry < 0) { free_entry = (int)e; free_clus = cur_clus; }
|
||
continue;
|
||
}
|
||
if (d[e].attr == 0x0F) continue;
|
||
if (d[e].attr & 0x08) continue;
|
||
if (neq11(d[e].name, name11)) {
|
||
existing_clus = r16((const u8*)&d[e].clow) | ((u32)r16((const u8*)&d[e].chigh) << 16);
|
||
found = 1; found_clus = cur_clus;
|
||
cluster_done = 1; break;
|
||
}
|
||
}
|
||
if (cluster_done) break;
|
||
cur_clus = fat32_get_next_cluster(fat_byte_off, cur_clus, bpb->rsvd);
|
||
}
|
||
|
||
/* Re-read the active cluster (the one we'll modify) into root_buf */
|
||
u32 active_clus = found ? found_clus : free_clus;
|
||
u32 root_lba;
|
||
int root_in_buffer;
|
||
u8 *root_buf;
|
||
fat32_de *dir;
|
||
u32 max_entries = cluster_bytes / 32;
|
||
if (active_clus >= 2) {
|
||
root_lba = data_lba + (active_clus - 2) * spc;
|
||
root_in_buffer = ((u64)root_lba * 512 + cluster_bytes <= 256ULL * 512);
|
||
if (root_in_buffer) {
|
||
root_buf = disk + (u64)root_lba * 512;
|
||
} else {
|
||
if (fat32_read_sectors(root_lba, spc, g_milestone_buf) != 0) {
|
||
logl("[DSK] fat32 write: root dir re-read failed");
|
||
return -10;
|
||
}
|
||
root_buf = g_milestone_buf;
|
||
}
|
||
dir = (fat32_de *)root_buf;
|
||
} else {
|
||
root_lba = 0; root_in_buffer = 0; root_buf = 0; dir = 0;
|
||
}
|
||
|
||
/* 3. Allocate or reuse clusters */
|
||
u32 bytes_needed = size > 0 ? size : 1;
|
||
u32 clusters_needed = (bytes_needed + cluster_bytes - 1) / cluster_bytes;
|
||
u32 first_clus = 0;
|
||
u32 prev_clus = 0;
|
||
|
||
if (found && existing_clus >= 2) {
|
||
/* Reuse existing chain, extend or shrink as needed */
|
||
first_clus = existing_clus;
|
||
u32 cur = existing_clus;
|
||
u32 count = 0;
|
||
while (cur >= 2 && cur < 0x0FFFFFF8 && count < clusters_needed) {
|
||
prev_clus = cur;
|
||
cur = fat32_fat_read_entry(fat_byte_off, cur);
|
||
count++;
|
||
}
|
||
/* If we need more clusters, allocate them */
|
||
while (count < clusters_needed) {
|
||
u32 newc = fat32_find_free_cluster(fat_byte_off, (fat_sectors * 512) / 4);
|
||
if (newc == 0) { logl("[DSK] fat32 write: no free clusters"); return -3; }
|
||
logh("[DSK] fat32 write: alloc clus=", newc);
|
||
/* Mark newly allocated cluster as EOC immediately to prevent re-allocation */
|
||
if (fat32_fat_write_entry(fat_byte_off, newc, 0x0FFFFFF8) != 0) return -7;
|
||
/* Link previous cluster to new */
|
||
if (prev_clus >= 2) {
|
||
if (fat32_fat_write_entry(fat_byte_off, prev_clus, newc) != 0) return -7;
|
||
}
|
||
prev_clus = newc;
|
||
count++;
|
||
}
|
||
/* Terminate the chain, saving original next pointer before overwriting with EOC */
|
||
if (prev_clus >= 2) {
|
||
u32 original_next = fat32_fat_read_entry(fat_byte_off, prev_clus);
|
||
fat32_fat_write_entry(fat_byte_off, prev_clus, 0x0FFFFFF8);
|
||
/* Free excess clusters from the old chain using saved pointer */
|
||
u32 next = original_next;
|
||
while (next >= 2 && next < 0x0FFFFFF8) {
|
||
u32 nn = fat32_fat_read_entry(fat_byte_off, next);
|
||
fat32_fat_write_entry(fat_byte_off, next, 0);
|
||
next = nn;
|
||
}
|
||
}
|
||
} else {
|
||
/* Allocate new clusters */
|
||
if (free_entry < 0) { logl("[DSK] fat32 write: no free dir entry"); return -4; }
|
||
for (u32 i = 0; i < clusters_needed; i++) {
|
||
u32 newc = fat32_find_free_cluster(fat_byte_off, (fat_sectors * 512) / 4);
|
||
if (newc == 0) { logl("[DSK] fat32 write: no free clusters"); return -3; }
|
||
logh("[DSK] fat32 write: alloc clus=", newc);
|
||
/* Mark newly allocated cluster as EOC immediately to prevent re-allocation */
|
||
if (fat32_fat_write_entry(fat_byte_off, newc, 0x0FFFFFF8) != 0) return -7;
|
||
if (i == 0) first_clus = newc;
|
||
if (prev_clus >= 2) {
|
||
if (fat32_fat_write_entry(fat_byte_off, prev_clus, newc) != 0) return -7;
|
||
}
|
||
prev_clus = newc;
|
||
}
|
||
}
|
||
|
||
/* 4. Write data to clusters */
|
||
u32 remaining = size;
|
||
const u8 *src = data;
|
||
u32 cur = first_clus;
|
||
u32 ci = 0;
|
||
while (cur >= 2 && cur < 0x0FFFFFF8 && ci < clusters_needed) {
|
||
u32 clba = data_lba + (cur - 2) * spc;
|
||
u32 chunk = remaining < cluster_bytes ? remaining : cluster_bytes;
|
||
/* Read cluster into g_cluster, overlay data, write back */
|
||
if ((u64)clba * 512 + cluster_bytes <= 256ULL * 512) {
|
||
/* In g_disk buffer — copy data directly */
|
||
u8 *dst = disk + (u64)clba * 512;
|
||
for (u32 b = 0; b < chunk; b++) dst[b] = src[b];
|
||
for (u32 b = chunk; b < cluster_bytes; b++) dst[b] = 0;
|
||
} else {
|
||
/* Out of buffer — use sector write */
|
||
if (fat32_read_sectors(clba, spc, g_cluster) != 0) return -5;
|
||
for (u32 b = 0; b < chunk; b++) g_cluster[b] = src[b];
|
||
for (u32 b = chunk; b < cluster_bytes; b++) g_cluster[b] = 0;
|
||
if (fat32_write_sectors(clba, spc, g_cluster) != 0) return -6;
|
||
}
|
||
src += chunk; remaining -= chunk; ci++;
|
||
cur = fat32_fat_read_entry(fat_byte_off, cur);
|
||
}
|
||
|
||
/* 5. Update directory entry */
|
||
if (!found) {
|
||
fat32_de *e = &dir[free_entry];
|
||
for (int i = 0; i < 11; i++) e->name[i] = name11[i];
|
||
e->attr = 0x20; /* archive */
|
||
e->ntr = 0; e->ctenth = 0; e->ctime = 0; e->cdate = 0;
|
||
e->adate = 0; e->chigh = (u16)((first_clus >> 16) & 0xFFFF);
|
||
e->wtime = 0; e->wdate = 0; e->clow = (u16)(first_clus & 0xFFFF);
|
||
e->fsize = size;
|
||
} else {
|
||
/* 文件已存在:只更新匹配到的条目(不触碰 free_entry!) */
|
||
for (u32 e2 = 0; e2 < max_entries; e2++) {
|
||
if (neq11(dir[e2].name, name11)) {
|
||
dir[e2].chigh = (u16)((first_clus >> 16) & 0xFFFF);
|
||
dir[e2].clow = (u16)(first_clus & 0xFFFF);
|
||
dir[e2].fsize = size;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 6. Write FAT + root directory + data sectors back to disk */
|
||
/* BUG-20260730: Only write back FAT sectors within g_disk buffer bounds */
|
||
{
|
||
u32 max_fat_sectors_in_buffer = (256 - fat_lba) / fat_sectors;
|
||
if (max_fat_sectors_in_buffer > bpb->fc) max_fat_sectors_in_buffer = bpb->fc;
|
||
for (u32 f = 0; f < max_fat_sectors_in_buffer; f++) {
|
||
u32 flba = fat_lba + f * fat_sectors;
|
||
u32 sectors_to_write = fat_sectors;
|
||
if (flba + sectors_to_write > 256) sectors_to_write = 256 - flba;
|
||
if (sectors_to_write > 0 && flba < 256) {
|
||
if (fat32_write_sectors(flba, sectors_to_write, disk + (u64)flba * 512) != 0) return -7;
|
||
}
|
||
}
|
||
}
|
||
/* Write root directory cluster that was modified */
|
||
{
|
||
if (root_in_buffer) {
|
||
if (fat32_write_sectors(root_lba, spc, disk + (u64)root_lba * 512) != 0) return -8;
|
||
} else {
|
||
if (fat32_write_sectors(root_lba, spc, g_milestone_buf) != 0) {
|
||
logl("[DSK] fat32 write: root dir write failed (bounce buffer)");
|
||
return -8;
|
||
}
|
||
}
|
||
}
|
||
/* Write data clusters that were in the g_disk buffer */
|
||
cur = first_clus;
|
||
ci = 0;
|
||
while (cur >= 2 && cur < 0x0FFFFFF8 && ci < clusters_needed) {
|
||
u32 clba = data_lba + (cur - 2) * spc;
|
||
if ((u64)clba * 512 + cluster_bytes <= 256ULL * 512) {
|
||
if (fat32_write_sectors(clba, spc, disk + (u64)clba * 512) != 0) return -9;
|
||
}
|
||
u32 fo = fat_byte_off + cur * 4;
|
||
if (fo + 4 > sizeof(g_disk)) break;
|
||
cur = r32(disk + fo) & 0x0FFFFFFF;
|
||
ci++;
|
||
}
|
||
|
||
logl("[DSK] fat32 write: success");
|
||
return 0;
|
||
}
|
||
|
||
/* Write a file into a directory identified by dir_clus, by 11-char 8.3 name.
|
||
* If the file exists, its content is replaced. If not, a new entry is created.
|
||
* Content must fit in one cluster (optimized for small marker files).
|
||
* Uses g_disk (BPB+FAT) and g_cluster (dir) and g_milestone_buf (data).
|
||
* Returns 0 on success. */
|
||
static int fat32_write_file_in_dir(u32 dir_clus, const char *name11, const u8 *data, u32 size) {
|
||
u8 *disk = g_disk;
|
||
const fat32_bpb *bpb = (const fat32_bpb *)disk;
|
||
if (bpb->boot_sig != 0xAA55 || bpb->bps != 512) return -1;
|
||
u32 spc = bpb->spc;
|
||
u32 fat_lba = bpb->rsvd;
|
||
u32 fat_sectors = bpb->spf;
|
||
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
|
||
u32 fat_byte_off = bpb->rsvd * 512;
|
||
u32 cluster_bytes = spc * 512;
|
||
if (size > cluster_bytes) return -1; /* content must fit in one cluster */
|
||
|
||
/* 1. Re-read BPB+FAT to ensure g_disk is fresh */
|
||
if (fat32_read_sectors(0, 256, disk) != 0) return -2;
|
||
|
||
/* 2. Read directory cluster */
|
||
u32 dir_lba = data_lba + (dir_clus - 2) * spc;
|
||
if (fat32_read_sectors(dir_lba, spc, g_cluster) != 0) return -3;
|
||
|
||
/* 3. Find existing entry or free slot */
|
||
u32 max_entries = cluster_bytes / 32;
|
||
fat32_de *dir = (fat32_de *)g_cluster;
|
||
int free_entry = -1;
|
||
u32 existing_clus = 0;
|
||
int found = 0;
|
||
for (u32 e = 0; e < max_entries; e++) {
|
||
if (dir[e].name[0] == 0) { if (free_entry < 0) free_entry = (int)e; break; }
|
||
if ((u8)dir[e].name[0] == 0xE5) { if (free_entry < 0) free_entry = (int)e; continue; }
|
||
if (dir[e].attr == 0x0F) continue;
|
||
if (dir[e].attr & 0x08) continue;
|
||
if (neq11(dir[e].name, name11)) {
|
||
existing_clus = r16((const u8*)&dir[e].clow) | ((u32)r16((const u8*)&dir[e].chigh) << 16);
|
||
found = 1;
|
||
break;
|
||
}
|
||
}
|
||
if (!found && free_entry < 0) return -4;
|
||
|
||
/* 4. Allocate or reuse data cluster */
|
||
u32 file_clus = 0;
|
||
if (found && existing_clus >= 2) {
|
||
file_clus = existing_clus;
|
||
} else {
|
||
/* Find free cluster in FAT */
|
||
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) { file_clus = c; break; }
|
||
}
|
||
if (file_clus == 0) return -5;
|
||
/* BUG-20260730: Mark as end-of-chain in FAT, preserving high 4 bits */
|
||
u32 fo = fat_byte_off + file_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;
|
||
}
|
||
}
|
||
|
||
/* 5. Write file data to cluster */
|
||
u32 file_lba = data_lba + (file_clus - 2) * spc;
|
||
for (u32 b = 0; b < size; b++) g_milestone_buf[b] = data[b];
|
||
for (u32 b = size; b < cluster_bytes; b++) g_milestone_buf[b] = 0;
|
||
if (fat32_write_sectors(file_lba, spc, g_milestone_buf) != 0) return -6;
|
||
|
||
/* 6. Update directory entry */
|
||
if (found) {
|
||
for (u32 e = 0; e < max_entries; e++) {
|
||
if (neq11(dir[e].name, name11)) {
|
||
dir[e].chigh = (u16)((file_clus >> 16) & 0xFFFF);
|
||
dir[e].clow = (u16)(file_clus & 0xFFFF);
|
||
dir[e].fsize = size;
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
fat32_de *e = &dir[free_entry];
|
||
for (int i = 0; i < 11; i++) e->name[i] = name11[i];
|
||
e->attr = 0x20; /* archive */
|
||
e->ntr = 0; e->ctenth = 0; e->ctime = 0; e->cdate = 0;
|
||
e->adate = 0; e->chigh = (u16)((file_clus >> 16) & 0xFFFF);
|
||
e->wtime = 0; e->wdate = 0; e->clow = (u16)(file_clus & 0xFFFF);
|
||
e->fsize = size;
|
||
}
|
||
|
||
/* 7. Write directory cluster back */
|
||
if (fat32_write_sectors(dir_lba, spc, g_cluster) != 0) return -7;
|
||
|
||
/* BUG-20260730: Only write back FAT sectors within g_disk buffer bounds */
|
||
{
|
||
u32 max_fat_sectors_in_buffer = (256 - fat_lba) / fat_sectors;
|
||
if (max_fat_sectors_in_buffer > bpb->fc) max_fat_sectors_in_buffer = bpb->fc;
|
||
for (u32 f = 0; f < max_fat_sectors_in_buffer; f++) {
|
||
u32 flba = fat_lba + f * fat_sectors;
|
||
u32 sectors_to_write = fat_sectors;
|
||
if (flba + sectors_to_write > 256) sectors_to_write = 256 - flba;
|
||
if (sectors_to_write > 0 && flba < 256) {
|
||
if (fat32_write_sectors(flba, sectors_to_write, disk + (u64)flba * 512) != 0) return -8;
|
||
}
|
||
}
|
||
}
|
||
|
||
logl("[DSK] fat32 write_in_dir: success");
|
||
return 0;
|
||
}
|
||
|
||
/* ---- DSK 启动链里程碑标记系统 ----
|
||
* 实机测试崩溃定位:每个启动阶段成功后写标记文件到 FAT32。
|
||
* 命名:BOOT01.TXT ~ BOOT07.TXT(8.3 短名)。
|
||
* 目标目录:DESHAB64/DEV/(不存在时回退根目录)。
|
||
* 写入失败静默忽略,不影响启动流程。
|
||
*
|
||
* 阶段定义:
|
||
* BOOT_01: FAT32 文件系统就绪
|
||
* BOOT_02: block_read/block_write API 就绪
|
||
* BOOT_03: FUCK 配置加载完成
|
||
* BOOT_04: firstInit 检查完成
|
||
* BOOT_05: dev_mode shell 加载完成
|
||
* BOOT_06: desktop.elf 加载完成
|
||
* BOOT_07: 网络管理器加载完成 */
|
||
static void milestone_write(int num, const char *desc) {
|
||
if (!g_block_write) return;
|
||
if (num < 1 || num > 9) return;
|
||
/* 构造 8.3 短名: "BOOTnn TXT" (FAT 8.3 format, 11 chars) */
|
||
char name11[12];
|
||
name11[0]='B'; name11[1]='O'; name11[2]='O'; name11[3]='T';
|
||
name11[4]=(char)('0' + (num / 10));
|
||
name11[5]=(char)('0' + (num % 10));
|
||
name11[6]=' '; name11[7]=' ';
|
||
name11[8]='T'; name11[9]='X'; name11[10]='T';
|
||
/* 构造内容: "OK\n" + desc + "\n" */
|
||
/* BUG-20260730: Enlarge buffer — "OK\n" + stage desc + " tsc=0x" + 16-hex + " ms=" + digits + "\n" can exceed 64 */
|
||
u8 buf[160];
|
||
u32 len = 0;
|
||
buf[len++]='O'; buf[len++]='K'; buf[len++]='\n';
|
||
while (*desc && len < 150) buf[len++] = (u8)*desc++;
|
||
buf[len++] = '\n';
|
||
/* 确保 BPB 在 g_disk 中可用 */
|
||
if (fat32_read_sectors(0, 256, g_disk) != 0) return;
|
||
/* 尝试写入 DESHAB64/DEV/ 目录,不存在则回退根目录 */
|
||
u32 dev_clus = fat32_find_dir_cluster("SYSTEM /DESHAB64 /DEV ");
|
||
int rc;
|
||
if (dev_clus >= 2) {
|
||
rc = fat32_write_file_in_dir(dev_clus, name11, buf, len);
|
||
} else {
|
||
rc = fat32_write_root_file(name11, buf, len);
|
||
}
|
||
if (rc == 0) {
|
||
swrite("[DSK] milestone BOOT_0");
|
||
sputc('0' + num);
|
||
logl(dev_clus >= 2 ? " written (DEV)" : " written (root)");
|
||
}
|
||
}
|
||
|
||
static int dsk_check_firstinit(int *is_first, int *dev_mode) {
|
||
DSK_TS_DECL(firstinit_check);
|
||
DSK_TS_BEGIN(firstinit_check);
|
||
char name[12]; /* "FIRSTINTXT" on stack — no PIE reloc */
|
||
name[0]='F';name[1]='I';name[2]='R';name[3]='S';name[4]='T';
|
||
name[5]='I';name[6]='N';name[7]='T';name[8]='X';name[9]='T';name[10]=0;
|
||
u8 *data = 0; u32 size = 0;
|
||
*dev_mode = 0;
|
||
logl("[DSK] fat32_read_path start");
|
||
int rc = fat32_read_root_file(name, &data, &size);
|
||
if (rc != 0) {
|
||
logl("[DSK] firstInit.txt not in FAT32 root, trying system/user/use");
|
||
if (fat32_read_sectors(0, 256, g_disk) == 0) {
|
||
rc = fat32_read_path("system/user/use/firstInit.txt", &data, &size);
|
||
if (rc != 0)
|
||
rc = fat32_read_path("user/use/firstInit.txt", &data, &size);
|
||
}
|
||
}
|
||
logh("[DSK] fat32_read_path rc=", (u64)(i64)rc);
|
||
if (rc != 0) { logl("[DSK] firstInit.txt not found, assuming first boot"); *is_first = 1; DSK_TS_END(firstinit_check, "dsk_check_firstinit (not found)"); return 0; }
|
||
if (data[0] == '0') { logl("[DSK] firstInit=0"); *is_first = 1; }
|
||
else { logl("[DSK] firstInit != 0"); *is_first = 0; }
|
||
/* 解析第二行 dev_mode 标志(firstInit.txt 格式: "0|1\n0|1") */
|
||
u32 i = 0;
|
||
while (i < size && data[i] != '\n') i++;
|
||
if (i < size) {
|
||
i++; /* 跳过换行 */
|
||
if (i < size && data[i] == '1') {
|
||
*dev_mode = 1;
|
||
logl("[DSK] dev_mode=1 (developer auto-test)");
|
||
}
|
||
}
|
||
DSK_TS_END(firstinit_check, "dsk_check_firstinit");
|
||
return 0;
|
||
}
|
||
|
||
static int dsk_load_elf(u8 *data, u32 size, void **entry_out) {
|
||
DSK_TS_DECL(elf_load);
|
||
DSK_TS_BEGIN(elf_load);
|
||
DSK_STAT_INC(elf_load);
|
||
const elf64_ehdr *eh = (const elf64_ehdr *)data;
|
||
if (size < sizeof(elf64_ehdr)) { logl("[DSK] ELF: data too small"); return -1; }
|
||
if (eh->ident[0] != EI_MAG0 || eh->ident[4] != ELFCLASS64) { logl("[DSK] ELF: bad magic/class"); return -1; }
|
||
if (eh->machine != EM_X86_64) { logl("[DSK] ELF: wrong machine"); return -2; }
|
||
/* P0 修复: 验证程序头在数据范围内 */
|
||
if (eh->phoff == 0 || eh->phnum == 0) { logl("[DSK] ELF: no program headers"); return -3; }
|
||
if ((u64)eh->phoff + (u64)eh->phnum * (u64)eh->phentsize > (u64)size) {
|
||
logl("[DSK] ELF: program headers beyond data bounds");
|
||
return -4;
|
||
}
|
||
u64 min_vaddr = ~0ULL, max_vaddr = 0;
|
||
u32 lc = 0;
|
||
for (u16 i = 0; i < eh->phnum; i++) {
|
||
const elf64_phdr *ph = (const elf64_phdr *)(data + eh->phoff + (u64)i * eh->phentsize);
|
||
if (ph->type != PT_LOAD) continue;
|
||
if (ph->filesz > ph->memsz) { logl("[DSK] ELF: filesz > memsz"); return -5; }
|
||
/* P0 修复: 验证 PT_LOAD 段在数据范围内 */
|
||
if ((u64)ph->offset + (u64)ph->filesz > (u64)size) {
|
||
logl("[DSK] ELF: PT_LOAD beyond data bounds");
|
||
logh("[DSK] ELF: offset=", ph->offset);
|
||
logh("[DSK] ELF: filesz=", ph->filesz);
|
||
return -6;
|
||
}
|
||
if (ph->vaddr < min_vaddr) min_vaddr = ph->vaddr;
|
||
if (ph->vaddr + ph->memsz > max_vaddr) max_vaddr = ph->vaddr + ph->memsz;
|
||
lc++;
|
||
}
|
||
if (!lc || min_vaddr == ~0ULL) { logl("[DSK] ELF: no PT_LOAD segments"); return -7; }
|
||
u64 isize = max_vaddr - min_vaddr;
|
||
isize = (isize + 0xFFF) & ~0xFFFULL;
|
||
logh("[DSK] elf total image size=", isize);
|
||
/* 加载缓冲:需容纳 desktop.elf(~1.67MB memsz span,含 Linux 窗口槽+16KB 缓冲 BSS)。2MB 留余量。 */
|
||
static u8 ibuf[8388608]; /* 8MB — cmd.elf 含 4MB PE 文件缓冲 BSS */
|
||
u8 *image = ibuf;
|
||
if (isize > 8388608) { logl("[DSK] ELF: image too large for 8MB ibuf"); logh("[DSK] ELF isize=", isize); return -8; }
|
||
memset_dsk(image, 0, isize);
|
||
for (u16 i = 0; i < eh->phnum; i++) {
|
||
const elf64_phdr *ph = (const elf64_phdr *)(data + eh->phoff + (u64)i * eh->phentsize);
|
||
if (ph->type != PT_LOAD) continue;
|
||
u64 off = ph->vaddr - min_vaddr;
|
||
logh("[DSK] PT_LOAD vaddr=", ph->vaddr);
|
||
logh("[DSK] PT_LOAD filesz=", ph->filesz);
|
||
logh("[DSK] PT_LOAD memsz=", ph->memsz);
|
||
/* off + filesz 不会越界,已在上面的循环中验证 */
|
||
memcpy_dsk(image + off, data + ph->offset, ph->filesz);
|
||
}
|
||
/* P0 修复: 验证 entry 在映像范围内 */
|
||
if (eh->entry < min_vaddr || eh->entry >= max_vaddr) {
|
||
logl("[DSK] ELF: entry point out of bounds");
|
||
logh("[DSK] ELF: entry=", eh->entry);
|
||
logh("[DSK] ELF: min_vaddr=", min_vaddr);
|
||
logh("[DSK] ELF: max_vaddr=", max_vaddr);
|
||
return -9;
|
||
}
|
||
*entry_out = image + (eh->entry - min_vaddr);
|
||
|
||
/* apply R_X86_64_RELATIVE relocations from PT_DYNAMIC (PIE) */
|
||
u64 load_bias = (u64)image - min_vaddr;
|
||
for (u16 i = 0; i < eh->phnum; i++) {
|
||
const elf64_phdr *ph = (const elf64_phdr *)(data + eh->phoff + (u64)i * eh->phentsize);
|
||
if (ph->type != PT_DYNAMIC) continue;
|
||
/* P0 修复: 验证 PT_DYNAMIC vaddr 在映像范围内 */
|
||
if (ph->vaddr < min_vaddr || ph->vaddr + ph->filesz > max_vaddr) {
|
||
logl("[DSK] ELF: PT_DYNAMIC out of image bounds");
|
||
continue;
|
||
}
|
||
const elf64_dyn *dyn = (const elf64_dyn *)(image + (ph->vaddr - min_vaddr));
|
||
u64 rela_off = 0, rela_sz = 0, rela_ent = 24;
|
||
for (; dyn->tag != 0; dyn++) {
|
||
if (dyn->tag == DT_RELA) rela_off = dyn->val;
|
||
else if (dyn->tag == DT_RELASZ) rela_sz = dyn->val;
|
||
else if (dyn->tag == DT_RELAENT) rela_ent = dyn->val;
|
||
}
|
||
if (rela_off && rela_sz) {
|
||
/* P0 修复: 边界检查 rela_off 和 rela_off+rela_sz 必须在 [min_vaddr, min_vaddr+isize) 内 */
|
||
if (rela_off < min_vaddr || rela_off >= min_vaddr + isize) {
|
||
logl("[DSK] ELF: rela_off out of bounds, skipping reloc");
|
||
logh("[DSK] ELF: rela_off=", rela_off);
|
||
logh("[DSK] ELF: min_vaddr=", min_vaddr);
|
||
logh("[DSK] ELF: isize=", isize);
|
||
continue;
|
||
}
|
||
if (rela_off + rela_sz > min_vaddr + isize) {
|
||
logl("[DSK] ELF: rela table extends beyond image, clamping");
|
||
rela_sz = (min_vaddr + isize - rela_off) / rela_ent * rela_ent;
|
||
}
|
||
u64 n = rela_sz / rela_ent;
|
||
logh("[DSK] elf relocs=", n);
|
||
for (u64 r = 0; r < n; r++) {
|
||
const elf64_rela *rel = (const elf64_rela *)(image + (rela_off - min_vaddr) + r * rela_ent);
|
||
u32 rtype = (u32)(rel->info & 0xFFFFFFFF);
|
||
if (rtype == R_X86_64_RELATIVE) {
|
||
/* P0 修复: 验证 rel->offset 在映像范围内,防止无符号下溢 */
|
||
if (rel->offset < min_vaddr || rel->offset >= min_vaddr + isize) continue;
|
||
u64 *slot = (u64 *)(image + (rel->offset - min_vaddr));
|
||
*slot = load_bias + (u64)rel->addend;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
logh("[DSK] elf image base=", (u64)image);
|
||
logh("[DSK] elf entry=", (u64)*entry_out);
|
||
DSK_TS_END(elf_load, "dsk_load_elf");
|
||
return 0;
|
||
}
|
||
|
||
/* 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.
|
||
* Returns 0 if the module ran and returned, non-zero on load failure. */
|
||
/* 按名字加载模块 ELF:先根目录,后 SYSTEM/DESHAB64/ 与 SYSTEM/USER/USE/ 回退。 */
|
||
static int dsk_load_module(const char name11[12], u8 **data, u32 *size) {
|
||
if (fat32_read_root_file(name11, data, size) == 0) return 0;
|
||
static const char *pfxs[] = { "SYSTEM /DESHAB64 /", "SYSTEM /USER /USE /" };
|
||
for (int pi = 0; pi < 2; pi++) {
|
||
char path[40];
|
||
int i = 0, j;
|
||
const char *pfx = pfxs[pi];
|
||
while (pfx[i]) { path[i] = pfx[i]; i++; }
|
||
for (j = 0; j < 11; j++) path[i++] = name11[j];
|
||
path[i] = 0;
|
||
logl("[DSK] load_and_run: trying fallback path");
|
||
if (fat32_read_sectors(0, 256, g_disk) == 0 &&
|
||
fat32_read_path(path, data, size) == 0) return 0;
|
||
}
|
||
logl("[DSK] load_and_run: fat32 read failed for ");
|
||
swrite(name11); swrite("\n");
|
||
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; }
|
||
logl("[DSK] jumping to module");
|
||
void(*fn)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))entry;
|
||
fn(ctx);
|
||
DSK_TS_END(mod_run, "dsk_load_and_run");
|
||
return 0;
|
||
}
|
||
|
||
/* Load desktop.elf and jump to it. Never returns on success. */
|
||
static void dsk_run_desktop(const dsk_boot_context *ctx) {
|
||
char dn[12]; /* DESKTOP ELF */
|
||
dn[0]='D';dn[1]='E';dn[2]='S';dn[3]='K';dn[4]='T';dn[5]='O';dn[6]='P';dn[7]=' ';dn[8]='E';dn[9]='L';dn[10]='F';
|
||
if (dsk_load_and_run(dn, ctx) != 0) logl("[DSK] desktop load failed");
|
||
for(;;)__asm__("hlt");
|
||
}
|
||
|
||
#define DSK_SESSION_NATIVE 0
|
||
#define DSK_SESSION_KDE 1
|
||
#define DSK_SESSION_ASK (-1)
|
||
|
||
static int dsk_poll_scancode(void) {
|
||
u8 st = inb(0x64);
|
||
if (!(st & 1)) return -1;
|
||
if (st & 0x20) { (void)inb(0x60); return -1; }
|
||
return (int)inb(0x60);
|
||
}
|
||
|
||
static int dsk_poll_serial(void) {
|
||
if (!(inb(COM1 + 5) & 1)) return -1;
|
||
return (int)inb(COM1);
|
||
}
|
||
|
||
static int dsk_session_from_cfg(const dsk_ini_config *cfg, int cfg_ok) {
|
||
const char *s = cfg_ok ? dsk_ini_get(cfg, "dsk", "session") : 0;
|
||
if (s && dsk_ini_streq(s, "kde")) return DSK_SESSION_KDE;
|
||
if (s && dsk_ini_streq(s, "ask")) return DSK_SESSION_ASK;
|
||
return DSK_SESSION_NATIVE;
|
||
}
|
||
|
||
static int dsk_fuck_from_modules(const dsk_boot_context *ctx, u8 **out, u32 *out_size) {
|
||
typedef struct {
|
||
u64 revision;
|
||
void *address;
|
||
u64 size;
|
||
char *path;
|
||
char *cmdline;
|
||
} dsk_limine_file;
|
||
typedef struct {
|
||
u64 revision;
|
||
u64 module_count;
|
||
dsk_limine_file **modules;
|
||
} dsk_limine_mods;
|
||
const dsk_limine_mods *rsp = (const dsk_limine_mods *)ctx->boot_modules_response;
|
||
if (!rsp || !rsp->modules) return -1;
|
||
const char *tag = "fuck:config";
|
||
for (u64 i = 0; i < rsp->module_count; i++) {
|
||
dsk_limine_file *f = rsp->modules[i];
|
||
if (!f || !f->cmdline || !f->address || f->size == 0) continue;
|
||
const char *c = f->cmdline;
|
||
int match = 1;
|
||
for (int j = 0; tag[j]; j++) {
|
||
if (c[j] != tag[j]) { match = 0; break; }
|
||
}
|
||
if (!match || c[11] != 0) continue;
|
||
if (f->size > sizeof(g_fdata)) return -2;
|
||
memcpy_dsk(g_fdata, f->address, (u64)f->size);
|
||
*out = g_fdata;
|
||
*out_size = (u32)f->size;
|
||
return 0;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/* argv_blob is only 96 bytes. Keep guest commands tiny. */
|
||
static int dsk_sh_status(const linux_compat_service *lxc, const char *cmd) {
|
||
const char *argv[4];
|
||
u64 ec = 1;
|
||
if (!lxc || !lxc->exec || !cmd) return -1;
|
||
argv[0] = "sh";
|
||
argv[1] = "-c";
|
||
argv[2] = cmd;
|
||
argv[3] = 0;
|
||
int rc = lxc->exec("/bin/sh", 3, argv, 0, 0, 0, &ec);
|
||
if (rc != 0) return rc;
|
||
return (int)ec;
|
||
}
|
||
|
||
static void dsk_blit_scanout(u32 *fb, const struct linux_compat_scanout_info *si) {
|
||
u32 sw, sh, stride, copy_w, row;
|
||
i64 ox, oy;
|
||
const u8 *src;
|
||
if (!fb || !si || !si->enabled || !si->host_vaddr) return;
|
||
sw = si->width;
|
||
sh = si->height;
|
||
stride = si->stride;
|
||
if (!sw || !sh || stride < sw * 4) return;
|
||
ox = ((i64)g_fb_w - (i64)sw) / 2;
|
||
oy = ((i64)g_fb_h - (i64)sh) / 2;
|
||
if (ox < 0) ox = 0;
|
||
if (oy < 0) oy = 0;
|
||
src = (const u8 *)si->host_vaddr;
|
||
for (row = 0; row < sh; row++) {
|
||
if ((u64)(oy + (i64)row) >= g_fb_h) break;
|
||
copy_w = sw;
|
||
if ((u64)ox + copy_w > g_fb_w) copy_w = (u32)(g_fb_w - (u64)ox);
|
||
memcpy_dsk((u8 *)fb + (u64)(oy + (i64)row) * g_fb_p + (u64)ox * 4,
|
||
src + (u64)row * stride,
|
||
(u64)copy_w * 4);
|
||
}
|
||
}
|
||
|
||
static int dsk_kde_host_loop(u32 *fb, const linux_compat_service *lxc) {
|
||
int scanout_ok = 0;
|
||
u64 start, budget;
|
||
logl("[DSK] KDE host loop");
|
||
dsk_fill_gradient_rect(fb, 0, 0, (i64)g_fb_w, (i64)g_fb_h);
|
||
dsk_draw_string(fb, "Starting KDE ...", 32, 32, 0xFFFFFFFFu, g_ascii_w + 1);
|
||
start = rdtsc_dsk();
|
||
budget = g_tsc_per_ms ? g_tsc_per_ms * 90000ULL : 0;
|
||
(void)dsk_sh_status(lxc, "rm -f /tmp/deshab-native /tmp/deshab-native-app");
|
||
{
|
||
int native_chk = 0;
|
||
for (;;) {
|
||
struct linux_compat_scanout_info si;
|
||
int sc = dsk_poll_scancode();
|
||
if (sc == 0x01 || sc == 0x02) {
|
||
logl("[DSK] KDE host loop aborted");
|
||
return -1;
|
||
}
|
||
if (lxc->run_slice) {
|
||
int sl;
|
||
for (sl = 0; sl < 4; sl++)
|
||
(void)lxc->run_slice();
|
||
} else {
|
||
(void)dsk_sh_status(lxc, ":");
|
||
}
|
||
native_chk++;
|
||
if ((native_chk & 31) == 0 &&
|
||
dsk_sh_status(lxc, "test -f /tmp/deshab-native") == 0) {
|
||
logl("[DSK] KDE host loop: native switch");
|
||
return 0;
|
||
}
|
||
if (lxc->gpu_get_scanout_info &&
|
||
lxc->gpu_get_scanout_info(&si) == 0 &&
|
||
si.enabled && si.host_vaddr) {
|
||
if (!scanout_ok) {
|
||
logh("[DSK] KDE scanout w=", si.width);
|
||
logh("[DSK] KDE scanout h=", si.height);
|
||
scanout_ok = 1;
|
||
}
|
||
dsk_blit_scanout(fb, &si);
|
||
} else if (!scanout_ok && budget && rdtsc_dsk() - start > budget) {
|
||
logl("[DSK] KDE scanout timeout");
|
||
return -1;
|
||
}
|
||
__asm__("pause");
|
||
}
|
||
}
|
||
}
|
||
|
||
static int dsk_try_kde(const dsk_boot_context *ctx, u32 *fb) {
|
||
const linux_compat_service *lxc =
|
||
(const linux_compat_service *)ctx->reserved[5];
|
||
int dri, persist, script;
|
||
const char *path;
|
||
const char *argv[2];
|
||
int rc;
|
||
if (!lxc || lxc->magic != LINUX_COMPAT_MAGIC ||
|
||
!lxc->is_available || !lxc->exec_async) {
|
||
logl("[DSK] KDE: linux_compat unavailable");
|
||
return -1;
|
||
}
|
||
if (!lxc->is_available()) {
|
||
logl("[DSK] KDE: Linux guest not parked");
|
||
return -1;
|
||
}
|
||
dri = dsk_sh_status(lxc, "test -e /dev/dri/card0");
|
||
persist = dsk_sh_status(lxc, "test -d /mnt/persist/usr");
|
||
script = dsk_sh_status(lxc, "test -x /usr/local/bin/deshab-kde-session");
|
||
logh("[DSK] KDE preflight dri=", (u64)(i64)dri);
|
||
logh("[DSK] KDE preflight persist=", (u64)(i64)persist);
|
||
logh("[DSK] KDE preflight script=", (u64)(i64)script);
|
||
if (dri != 0 || script != 0) {
|
||
logl("[DSK] KDE preflight missing dri/script");
|
||
return -1;
|
||
}
|
||
path = "/usr/local/bin/deshab-kde-session";
|
||
argv[0] = path;
|
||
argv[1] = 0;
|
||
rc = lxc->exec_async(path, 1, argv);
|
||
logh("[DSK] KDE exec_async rc=", (u64)(i64)rc);
|
||
if (rc != 0) return rc;
|
||
logl("[DSK] KDE spawned");
|
||
return dsk_kde_host_loop(fb, lxc);
|
||
}
|
||
|
||
static void dsk_chooser_paint(u32 *fb, int left) {
|
||
i64 cw = 620, ch = 300;
|
||
i64 cx = ((i64)g_fb_w - cw) / 2;
|
||
i64 cy = ((i64)g_fb_h - ch) / 2;
|
||
int spacing = g_ascii_w + 1;
|
||
u32 fg = 0xFFFFFFFFu;
|
||
u32 dim = 0xFF8A96A0u;
|
||
u32 hot = 0xFFFFD24Au;
|
||
i64 tx;
|
||
int n;
|
||
char big;
|
||
if (left < 1) left = 1;
|
||
if (left > 8) left = 8;
|
||
dsk_fill_gradient_rect(fb, 0, 0, (i64)g_fb_w, (i64)g_fb_h);
|
||
dsk_fill_rect(fb, cx, cy, cw, ch, 0xFF2B3A4A);
|
||
dsk_fill_rect(fb, cx + 4, cy + 4, cw - 8, ch - 8, 0xFF1A2733);
|
||
dsk_draw_string(fb, "Choose desktop", cx + 32, cy + 24, fg, spacing);
|
||
dsk_draw_string(fb, "1 Native Deshab", cx + 32, cy + 64, fg, spacing);
|
||
dsk_draw_string(fb, "2 Guest KDE / Wayland", cx + 32, cy + 92, fg, spacing);
|
||
big = (char)('0' + left);
|
||
dsk_draw_char_scaled(fb, big, cx + cw - 32 - g_ascii_w * 3, cy + 28, hot, 3);
|
||
tx = cx + 48;
|
||
for (n = 8; n >= 1; n--) {
|
||
char d = (char)('0' + n);
|
||
u32 col = (n == left) ? hot : (n > left ? 0xFF3A4550u : dim);
|
||
dsk_draw_char_scaled(fb, d, tx, cy + 140, col, (n == left) ? 2 : 1);
|
||
tx += (n == left) ? (g_ascii_w * 2 + 16) : (g_ascii_w + 14);
|
||
}
|
||
dsk_draw_string(fb, "timeout -> native", cx + 32, cy + 240, dim, spacing);
|
||
}
|
||
|
||
static int dsk_chooser_read_key(void) {
|
||
int sc = dsk_poll_scancode();
|
||
int ch;
|
||
if (sc >= 0 && sc < 0x80) {
|
||
if (sc == 0x02 || sc == 0x4F) return DSK_SESSION_NATIVE;
|
||
if (sc == 0x03 || sc == 0x50) return DSK_SESSION_KDE;
|
||
}
|
||
ch = dsk_poll_serial();
|
||
if (ch == '1') {
|
||
logl("[DSK] session from serial=1");
|
||
return DSK_SESSION_NATIVE;
|
||
}
|
||
if (ch == '2') {
|
||
logl("[DSK] session from serial=2");
|
||
return DSK_SESSION_KDE;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static int dsk_session_chooser(u32 *fb) {
|
||
int left = 8;
|
||
int last_left = -1;
|
||
dsk_chooser_paint(fb, left);
|
||
if (!g_tsc_per_ms) {
|
||
u64 spin;
|
||
logl("[DSK] chooser: no TSC, spinning 8s");
|
||
for (left = 8; left >= 1; left--) {
|
||
if (left != last_left) {
|
||
dsk_chooser_paint(fb, left);
|
||
last_left = left;
|
||
logh("[DSK] chooser left=", (u64)left);
|
||
}
|
||
for (spin = 0; spin < 40000000ULL; spin++) {
|
||
int choice = dsk_chooser_read_key();
|
||
if (choice) return choice;
|
||
__asm__("pause");
|
||
}
|
||
}
|
||
logl("[DSK] session chooser timed out -> native");
|
||
return DSK_SESSION_NATIVE;
|
||
}
|
||
{
|
||
u64 start = rdtsc_dsk();
|
||
u64 one_s = g_tsc_per_ms * 1000ULL;
|
||
u64 budget = one_s * 8ULL;
|
||
while (rdtsc_dsk() - start < budget) {
|
||
u64 elapsed = rdtsc_dsk() - start;
|
||
int now = 8 - (int)(elapsed / one_s);
|
||
int choice;
|
||
if (now < 1) now = 1;
|
||
if (now != last_left) {
|
||
last_left = now;
|
||
dsk_chooser_paint(fb, now);
|
||
logh("[DSK] chooser left=", (u64)now);
|
||
}
|
||
choice = dsk_chooser_read_key();
|
||
if (choice) return choice;
|
||
__asm__("pause");
|
||
}
|
||
}
|
||
logl("[DSK] session chooser timed out -> native");
|
||
return DSK_SESSION_NATIVE;
|
||
}
|
||
|
||
static void dsk_choose_and_run_session(const dsk_boot_context *ctx, u32 *fb,
|
||
const dsk_ini_config *cfg, int cfg_ok) {
|
||
int choice = dsk_session_from_cfg(cfg, cfg_ok);
|
||
if (choice == DSK_SESSION_ASK) {
|
||
logl("[DSK] session=ask, waiting for 1/2");
|
||
choice = dsk_session_chooser(fb);
|
||
}
|
||
if (choice == DSK_SESSION_KDE) {
|
||
logl("[DSK] session=kde");
|
||
if (dsk_try_kde(ctx, fb) != 0) {
|
||
logl("[DSK] KDE failed, falling back to native desktop");
|
||
dsk_fill_gradient_rect(fb, 0, 0, (i64)g_fb_w, (i64)g_fb_h);
|
||
dsk_draw_string(fb, "KDE failed, starting native desktop",
|
||
32, 32, 0xFFFFD24Au, g_ascii_w + 1);
|
||
if (g_tsc_per_ms) {
|
||
u64 t0 = rdtsc_dsk();
|
||
while (rdtsc_dsk() - t0 < g_tsc_per_ms * 1500ULL)
|
||
__asm__("pause");
|
||
}
|
||
} else {
|
||
logl("[DSK] KDE session ended");
|
||
}
|
||
} else {
|
||
logl("[DSK] session=native");
|
||
}
|
||
dsk_run_desktop(ctx);
|
||
}
|
||
|
||
/* After FirstInit returns, persist the encrypted user.conf buffer (left in
|
||
* ctx->reserved[0/1]) to the FAT32 root as USER.CONF, then flip firstInit.txt
|
||
* from '0' to '1' so subsequent boots skip the wizard and go to login. */
|
||
static void dsk_persist_userconf(const dsk_boot_context *ctx) {
|
||
dsk_boot_context *ctx_mut = (dsk_boot_context *)ctx;
|
||
u64 conf_ptr = ctx_mut->reserved[0];
|
||
u64 conf_sz = ctx_mut->reserved[1];
|
||
if (!conf_ptr || !conf_sz) {
|
||
/* 严格错误策略:FirstInit 返回却没留下配置缓冲 = 向导失败,零降级 */
|
||
dsk_fatal("FS-E05 FIRSTINIT NO CONF BUFFER");
|
||
return;
|
||
}
|
||
if (!g_block_write) {
|
||
/* 严格错误策略:块层无写通道 = FS 不可写,零降级 */
|
||
dsk_fatal("FS-E01 NO BLOCK WRITE PROVIDER");
|
||
return;
|
||
}
|
||
const u8 *conf = (const u8 *)conf_ptr;
|
||
|
||
/* 1. Write USER.CONF (encrypted buffer as-is; login decrypts with password) */
|
||
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';
|
||
logl("[DSK] writing USER.CONF");
|
||
int wrc = fat32_write_root_file(cn, conf, (u32)conf_sz);
|
||
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 标志 */
|
||
char fn[12]; /* FIRSTINTXT */
|
||
fn[0]='F';fn[1]='I';fn[2]='R';fn[3]='S';fn[4]='T';fn[5]='I';fn[6]='N';fn[7]='T';fn[8]='X';fn[9]='T';fn[10]=0;
|
||
/* 读取当前 firstInit.txt 以保留 dev_mode(第二行),格式 "0|1\n0|1" */
|
||
u8 dev_mode_byte = '0';
|
||
u8 *fi_data = 0; u32 fi_size = 0;
|
||
if (fat32_read_root_file(fn, &fi_data, &fi_size) == 0) {
|
||
u32 i = 0;
|
||
while (i < fi_size && fi_data[i] != '\n') i++;
|
||
if (i < fi_size) { i++; if (i < fi_size) dev_mode_byte = fi_data[i]; }
|
||
}
|
||
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);
|
||
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 */
|
||
ctx_mut->reserved[0] = 0;
|
||
ctx_mut->reserved[1] = 0;
|
||
}
|
||
|
||
/* 调度 netman.elf:先初始化租约缓冲并经 reserved[7] 暴露给 netman/后续工具。
|
||
* 每次启动(含 is_first=0)都应调用,保证联网配置在进桌面前就绪。 */
|
||
static void dsk_schedule_netman(const dsk_boot_context *ctx, int load_netman) {
|
||
dsk_boot_context *ctx_mut = (dsk_boot_context *)ctx;
|
||
g_net_lease.magic = NET_LEASE_MAGIC;
|
||
g_net_lease.flags = 0;
|
||
g_net_lease.ip = 0; g_net_lease.gateway = 0;
|
||
g_net_lease.dns = 0; g_net_lease.server = 0;
|
||
g_net_lease.reserved[0] = 0; g_net_lease.reserved[1] = 0;
|
||
ctx_mut->reserved[7] = (u64)&g_net_lease;
|
||
if (!load_netman) {
|
||
logl("[DSK] netman skipped by FUCK config");
|
||
return;
|
||
}
|
||
logl("[DSK:SCHED] scheduling netman.elf");
|
||
char nn[12]; /* NETMAN ELF */
|
||
nn[0]='N';nn[1]='E';nn[2]='T';nn[3]='M';nn[4]='A';nn[5]='N';nn[6]=' ';nn[7]=' ';nn[8]='E';nn[9]='L';nn[10]='F';
|
||
logl("[DSK] loading netman");
|
||
if (dsk_load_and_run(nn, ctx) == 0) logl("[DSK] netman returned");
|
||
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,
|
||
* then run the firstInit check and dispatch system component loading.
|
||
* FUCK config controls: show_logo, skip_login, mouse_init, netman, dev_mode. */
|
||
static void dsk_spinner_and_check(const void *boot_ctx_ptr) {
|
||
const dsk_boot_context *ctx = (const dsk_boot_context *)boot_ctx_ptr;
|
||
{ u64 api_addr = ctx->dkm_kernel_api;
|
||
logh("[DSK] kernel_api addr=", api_addr);
|
||
if (!api_addr) { logl("[DSK] FATAL: kernel_api is NULL"); for(;;)__asm__("hlt"); }
|
||
const dsk_kernel_api_mirror *api = (const dsk_kernel_api_mirror *)api_addr;
|
||
const dsk_block_api_mirror *blk = api->block;
|
||
logh("[DSK] block_api addr=", (u64)(u64)blk);
|
||
g_block_read = blk ? blk->read : 0;
|
||
g_block_write = blk ? blk->write : 0;
|
||
logh("[DSK] block_read fn=", (u64)g_block_read);
|
||
logh("[DSK] block_write fn=", (u64)g_block_write);
|
||
fat32_probe_esp(); }
|
||
milestone_write(2, "block API ready");
|
||
|
||
g_fb_addr=ctx->framebuffer_address; g_fb_w=ctx->framebuffer_width;
|
||
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"); }
|
||
|
||
/* 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;
|
||
logh("[DSK] block_read ptr=", (u64)g_block_read);
|
||
|
||
tsc_calibrate_dsk();
|
||
|
||
/* === 读取 FUCK 配置 === */
|
||
DSK_TS_DECL(fuck_config);
|
||
DSK_TS_BEGIN(fuck_config);
|
||
dsk_ini_config dsk_cfg;
|
||
int dsk_cfg_ok = 0;
|
||
/* Prefer the Limine fuck:config module (SYSTEM/ on ext4 after GPT split).
|
||
* FAT32 on the SATA test disk is only a fallback. */
|
||
{
|
||
u8 *fuck_data = 0;
|
||
u32 fuck_size = 0;
|
||
int mrc = dsk_fuck_from_modules(ctx, &fuck_data, &fuck_size);
|
||
logh("[DSK] FUCK boot-module rc=", (u64)(i64)mrc);
|
||
if (mrc == 0 && fuck_size > 0 &&
|
||
dsk_ini_parse((const char *)fuck_data, fuck_size, &dsk_cfg) == 0) {
|
||
dsk_cfg_ok = 1;
|
||
logl("[DSK] FUCK config loaded from boot module");
|
||
}
|
||
}
|
||
if (!dsk_cfg_ok && g_block_read) {
|
||
logl("[DSK] reading BPB for FUCK config");
|
||
int bpb_rc = fat32_read_sectors(0, 256, g_disk);
|
||
if (bpb_rc == 0) {
|
||
milestone_write(1, "FAT32 ready");
|
||
u8 *fuck_data=0; u32 fuck_size=0;
|
||
int rc = fat32_read_path("SYSTEM /DESHAB64 /FUCK ", &fuck_data, &fuck_size);
|
||
logh("[DSK] fat32_read_path FUCK rc=", (u64)(i64)rc);
|
||
if (rc != 0) {
|
||
/* 扁平布局回退:SATA 测试盘等场景下 FUCK 直接放根目录 */
|
||
rc = fat32_read_path("FUCK ", &fuck_data, &fuck_size);
|
||
logh("[DSK] fat32_read_path root FUCK rc=", (u64)(i64)rc);
|
||
}
|
||
if (rc == 0 && fuck_size > 0) {
|
||
if (dsk_ini_parse((const char *)fuck_data, fuck_size, &dsk_cfg) == 0) {
|
||
dsk_cfg_ok = 1;
|
||
logl("[DSK] FUCK config loaded");
|
||
} else {
|
||
logl("[DSK] FUCK INI parse failed");
|
||
}
|
||
} else {
|
||
logl("[DSK] FUCK file not found or empty");
|
||
}
|
||
} else {
|
||
logl("[DSK] BPB read failed, cannot load FUCK");
|
||
}
|
||
}
|
||
DSK_TS_END(fuck_config, "FUCK config load");
|
||
|
||
/* === 应用 DSK 配置 === */
|
||
int show_logo = dsk_cfg_ok ? dsk_ini_get_bool(&dsk_cfg, "dsk", "show_logo", 1) : 1;
|
||
int skip_login = dsk_cfg_ok ? dsk_ini_get_bool(&dsk_cfg, "dsk", "skip_login", 0) : 0;
|
||
/* F5: skip_firstinit=1 → 跳过首次启动向导与登录,直达 desktop */
|
||
int skip_firstinit = dsk_cfg_ok ? dsk_ini_get_bool(&dsk_cfg, "dsk", "skip_firstinit", 0) : 0;
|
||
int load_mouse = dsk_cfg_ok ? dsk_ini_get_bool(&dsk_cfg, "dsk", "mouse_init", 1) : 1;
|
||
int load_netman = dsk_cfg_ok ? dsk_ini_get_bool(&dsk_cfg, "dsk", "netman", 1) : 1;
|
||
int dev_mode = dsk_cfg_ok ? dsk_ini_get_bool(&dsk_cfg, "boot", "dev_mode", 0) : 0;
|
||
/* [compat] 段: DSK 侧只读不写,仅记录 PE DLL 搜索开关(实际加载在 UTSM 侧) */
|
||
int pe_dll_search = dsk_cfg_ok ? dsk_ini_get_bool(&dsk_cfg, "compat", "pe_dll_search", 1) : 1;
|
||
if (pe_dll_search) logl("[DSK] compat: PE DLL search enabled (UTSM side)");
|
||
else logl("[DSK] compat: PE DLL search disabled");
|
||
milestone_write(3, "FUCK config loaded");
|
||
|
||
/* Gradient background + centered "DESHAB P01" text(Logo.png 图像已移除) */
|
||
dsk_fill_gradient_rect(sfb, 0, 0, (i64)g_fb_w, (i64)g_fb_h);
|
||
|
||
if (show_logo) {
|
||
/* "DESHAB P01" 文字垂直居中(不再绘制 Logo 图像) */
|
||
{
|
||
const char *ver_str = "DESHAB P01";
|
||
int len = 10; /* strlen("DESHAB P01") */
|
||
int spacing = g_ascii_w + 1; /* 11+1 = 12px per char */
|
||
i64 text_w = (i64)len * spacing;
|
||
i64 text_x = (i64)g_fb_w / 2 - text_w / 2;
|
||
i64 text_y = (i64)g_fb_h / 2 - g_ascii_h / 2;
|
||
/* White text: BGRX = 0xFF_RR_GG_BB = 0xFF_FFFFFF */
|
||
u32 fg = 0xFFFFFFFFu;
|
||
dsk_draw_string(sfb, ver_str, text_x, text_y, fg, spacing);
|
||
}
|
||
|
||
/* Hold text for 5 seconds */
|
||
u64 logo_start = rdtsc_dsk();
|
||
u64 logo_budget = g_tsc_per_ms * 5000; /* 5000 ms = 5 seconds */
|
||
while (rdtsc_dsk() - logo_start < logo_budget) __asm__("pause");
|
||
logl("[DSK] boot text 5s done, proceeding");
|
||
|
||
/* 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,
|
||
3000, "[DSK] splash: dcplogo.png (3s)");
|
||
dsk_splash(sfb, g_xj_logo_rgba, g_xj_logo_w, g_xj_logo_h,
|
||
3000, "[DSK] splash: xjlogo.png (3s)");
|
||
logl("[DSK] splash sequence done");
|
||
} else {
|
||
logl("[DSK] logo skipped by FUCK config");
|
||
}
|
||
|
||
if (!g_block_read) {
|
||
logl("[DSK] no block device, halting");
|
||
for(;;)__asm__("hlt");
|
||
}
|
||
|
||
logl("[DSK] checking firstInit...");
|
||
DSK_TS_DECL(firstinit_phase);
|
||
DSK_TS_BEGIN(firstinit_phase);
|
||
int is_first = 0, firstinit_dev_mode = 0;
|
||
if (dsk_check_firstinit(&is_first, &firstinit_dev_mode) != 0) {
|
||
logl("[DSK] firstInit check failed, halting");
|
||
for(;;)__asm__("hlt");
|
||
}
|
||
DSK_TS_END(firstinit_phase, "firstInit check phase");
|
||
milestone_write(4, "firstInit check done");
|
||
|
||
/* session=ask|kde: packed SYSTEM/FUCK selected a desktop path.
|
||
* Do not trap on FirstInit or the SATA firstInit.txt dev_mode shell. */
|
||
{
|
||
int sess = dsk_session_from_cfg(&dsk_cfg, dsk_cfg_ok);
|
||
if (sess == DSK_SESSION_ASK || sess == DSK_SESSION_KDE) {
|
||
if (is_first || firstinit_dev_mode) {
|
||
logl("[DSK] session chooser armed, skipping first-boot wizard/dev shell");
|
||
}
|
||
is_first = 0;
|
||
firstinit_dev_mode = 0;
|
||
}
|
||
}
|
||
|
||
/* 合并 FUCK dev_mode 和 firstInit.txt dev_mode */
|
||
if (firstinit_dev_mode) dev_mode = 1;
|
||
|
||
/* F5: skip_firstinit=1 → 直达 desktop(跳过向导与登录)。
|
||
* 优先级最高:即便 firstInit.txt 为 0(首次)也视作已完成。 */
|
||
if (skip_firstinit) {
|
||
logl("[DSK] skip_firstinit=1: direct to desktop (wizard+login skipped)");
|
||
is_first = 0;
|
||
firstinit_dev_mode = 0;
|
||
}
|
||
|
||
/* 开发者模式:加载 shell.elf 自动测试命令序列(cp/mv/echo/ls/cat/rm) */
|
||
if (dev_mode) {
|
||
logl("[DSK:SCHED] decision: dev_mode -> shell.elf");
|
||
/* dev_mode 同样需要网络就绪:先调度 netman(DHCP/静态 + ARP/DNS 自验),
|
||
* 租约写入 g_net_lease 供 shell 内 ping/curl/browser 经 reserved[7] 取用。 */
|
||
dsk_schedule_netman(ctx, load_netman);
|
||
char sn[12]; /* SHELL ELF */
|
||
sn[0]='S';sn[1]='H';sn[2]='E';sn[3]='L';sn[4]='L';sn[5]=' ';sn[6]=' ';sn[7]=' ';sn[8]='E';sn[9]='L';sn[10]='F';
|
||
dsk_boot_context *ctx_mut = (dsk_boot_context *)ctx;
|
||
ctx_mut->reserved[3] = 1; /* dev mode flag for shell */
|
||
if (dsk_load_and_run(sn, ctx) == 0) logl("[DSK] shell auto-test returned");
|
||
else logl("[DSK] shell.elf not found, skipping dev test");
|
||
ctx_mut->reserved[3] = 0;
|
||
milestone_write(5, "dev shell loaded");
|
||
}
|
||
|
||
if (!is_first) {
|
||
logl("[DSK:SCHED] decision: is_first=0 -> netman -> login then desktop");
|
||
|
||
/* netman.elf — 每次启动都调度,进桌面前获取 IP(可通过 FUCK netman=0 跳过) */
|
||
dsk_schedule_netman(ctx, load_netman);
|
||
milestone_write(7, "netman loaded");
|
||
|
||
/* login — 可通过 skip_login=1 跳过;skip_firstinit=1 时一并跳过(F5) */
|
||
if (skip_login || skip_firstinit) {
|
||
logl("[DSK:SCHED] login skipped by FUCK config");
|
||
} else {
|
||
dsk_run_login(ctx);
|
||
}
|
||
|
||
/* default_shell=cmd → 直接进 CMD.ELF(PE/EXE 兼容层前端),
|
||
* cmd 退出后回落桌面。 */
|
||
const char *def_shell = dsk_cfg_ok ? dsk_ini_get(&dsk_cfg, "dsk", "default_shell") : 0;
|
||
if (def_shell && dsk_ini_streq(def_shell, "cmd")) {
|
||
logl("[DSK:SCHED] default_shell=cmd -> cmd.elf");
|
||
char cn[12]; /* CMD ELF */
|
||
cn[0]='C';cn[1]='M';cn[2]='D';cn[3]=' ';cn[4]=' ';cn[5]=' ';cn[6]=' ';cn[7]=' ';cn[8]='E';cn[9]='L';cn[10]='F';
|
||
if (dsk_load_and_run(cn, ctx) == 0) logl("[DSK] cmd.elf returned, falling back to desktop");
|
||
else logl("[DSK] cmd.elf load failed, falling back to desktop");
|
||
}
|
||
|
||
logl("[DSK:SCHED] loading desktop");
|
||
logh("[DSK:STAT] fat32_read_count=", (u64)fat32_read_count);
|
||
logh("[DSK:STAT] fat32_write_count=", (u64)fat32_write_count);
|
||
logh("[DSK:STAT] elf_load_count=", (u64)elf_load_count);
|
||
logh("[DSK:STAT] mod_schedule_count=", (u64)mod_schedule_count);
|
||
milestone_write(6, "desktop loading");
|
||
dsk_choose_and_run_session(ctx, sfb, &dsk_cfg, dsk_cfg_ok);
|
||
return;
|
||
}
|
||
|
||
/* First boot: run system initializers, then FirstInit, then desktop */
|
||
logl("[DSK:SCHED] decision: is_first=1 -> mouseInit -> netman -> FirstInit -> desktop");
|
||
|
||
/* mouseInit.elf — 可通过 FUCK mouse_init=0 跳过 */
|
||
if (load_mouse) {
|
||
logl("[DSK:SCHED] scheduling mouseInit.elf");
|
||
char mn[12]; /* MOUSE ELF */
|
||
mn[0]='M';mn[1]='O';mn[2]='U';mn[3]='S';mn[4]='E';mn[5]=' ';mn[6]=' ';mn[7]=' ';mn[8]='E';mn[9]='L';mn[10]='F';
|
||
logl("[DSK] loading mouseInit");
|
||
if (dsk_load_and_run(mn, ctx) == 0) logl("[DSK] mouseInit returned");
|
||
else logl("[DSK] mouseInit not found");
|
||
} else {
|
||
logl("[DSK] mouseInit skipped by FUCK config");
|
||
}
|
||
|
||
/* netman.elf — 可通过 FUCK netman=0 跳过 */
|
||
dsk_schedule_netman(ctx, load_netman);
|
||
milestone_write(7, "netman loaded");
|
||
|
||
/* FirstInit.elf — user setup wizard; returns to DSK when done */
|
||
{ logl("[DSK:SCHED] scheduling FirstInit.elf");
|
||
/* "FirstInit.elf" 9 字符长名 → FAT32 8.3 别名 FIRSTI~1.ELF
|
||
* (mtools 打包时生成;mtools 量产命名,首个同名前缀稳定为 ~1) */
|
||
char en[12]; /* FIRSTI~1ELF */
|
||
en[0]='F';en[1]='I';en[2]='R';en[3]='S';en[4]='T';en[5]='I';
|
||
en[6]='~';en[7]='1';en[8]='E';en[9]='L';en[10]='F';en[11]=0;
|
||
logl("[DSK] loading FirstInit");
|
||
if (dsk_load_and_run(en, ctx) != 0) {
|
||
/* 严格错误策略:首次启动却没有 FirstInit.elf = 未实现路径,零降级 */
|
||
dsk_fatal("FS-E07 FIRSTINIT.ELF NOT FOUND");
|
||
return;
|
||
}
|
||
logl("[DSK] FirstInit returned, persisting user.conf");
|
||
dsk_persist_userconf(ctx);
|
||
logh("[DSK:STAT] fat32_read_count=", (u64)fat32_read_count);
|
||
logh("[DSK:STAT] fat32_write_count=", (u64)fat32_write_count);
|
||
logh("[DSK:STAT] elf_load_count=", (u64)elf_load_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");
|
||
milestone_write(6, "desktop loading");
|
||
dsk_choose_and_run_session(ctx, sfb, &dsk_cfg, dsk_cfg_ok);
|
||
}
|
||
}
|
||
|
||
__attribute__((noreturn, visibility("default")))
|
||
void dsk_entry(const dsk_boot_context *ctx) {
|
||
__asm__ volatile("cli"); /* disable IRQs — ps2kbd/IRQ1 & mouse/IRQ12 handlers crash during boot */
|
||
logl("[DSK] boot");
|
||
if(!ctx||ctx->magic!=DSK_BOOT_MAGIC){logl("[DSK] bad context");for(;;)__asm__("hlt");}
|
||
logl("[DSK] context ok");
|
||
dsk_spinner_and_check(ctx);
|
||
logl("[DSK] SELFTEST PASS");
|
||
for(;;)__asm__("hlt");
|
||
}
|