Files
Deshab-OS/CODE/dsk/main.c
T
Linehu 583262f5f0 feat: SKEL skeleton instrumentation - designed-but-unimplemented paths panic
- hypercall.c: 5 Phase 1.3 stubs -> HCALL-E01..E05 panic (no NOSYS return)
- drr_stub.c: snapshot alloc fail -> DRR-E01; new skeletons DRR-E02 root key
  seal, DRR-E03 task kill crypto erase, DRR-E04/E05 segment/system rollback,
  DRR-E06/E07 driver recovery ops dispatch
- sched.c: enable fail -> SCH-E02 panic (no silent demo skip)
- dkm/kernel_api.c: D1 hot-unload DKM-E01, D2 bare code loader DKM-E02,
  SIPI AP wake DKM-E03 skeletons
- virtio_net tx/rx stubs -> VNET-E01/E02; ath9k tx stub -> ATH-E01;
  xhci port reset -> XHC-E01; nvme MSI/MSI-X fail -> NVME-E01..E03 (no
  silent polling downgrade); e1000 MAC fallback -> E1K-E01
- pe_shim unimpl API -> PE-E01; PE ordinal import -> PE-E02
- kernel/main.c: lib sync / vscode install fail -> LNX-E01/E02
- NEW brand/brand.c (E1 boot anim BRAND-E01, E2 time egg BRAND-E02) and
  pkg/pkg.c (pacman skeleton PKG-E01..03), wired into UTSM MAKEFILE
- Deliberate design-internal fallbacks kept and documented (VMX skip,
  bzImage skip, KDE fallback, boot-module fallback, apic->PIC route)
2026-10-03 00:30:54 +08:00

2120 lines
88 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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) {
/* 严格错误策略:重定位表越界 = 镜像损坏,零降级 panic */
logh("[DSK] ELF: rela_off=", rela_off);
logh("[DSK] ELF: min_vaddr=", min_vaddr);
logh("[DSK] ELF: isize=", isize);
dsk_fatal("ELF-E01 RELA OUT OF BOUNDS");
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");
}