FUCKKKK
This commit is contained in:
@@ -114,7 +114,9 @@ static void do_fetch(void) {
|
||||
|
||||
br_set_status("Receiving...");
|
||||
redraw_all_imm();
|
||||
int n = ns_tcp_recv(0, 0, 5000);
|
||||
/* buf=0 不拷贝(直接读全局 ns_tcp_rx),cap 必须给缓冲容量,
|
||||
* 否则 n=min(rx_len,0) 恒为 0,误判为超时。 */
|
||||
int n = ns_tcp_recv(0, NS_TCP_RX_CAP, 5000);
|
||||
ns_tcp_close();
|
||||
|
||||
if (n < 0) { br_set_status("Connection reset"); return; }
|
||||
@@ -277,6 +279,7 @@ void dsk_entry(const da_boot_context *ctx) {
|
||||
}
|
||||
|
||||
da_init(&g_ac, ctx);
|
||||
ns_apply_lease_from_ctx((u64)(const void *)ctx); /* netman 租约覆盖默认 slirp IP */
|
||||
da_cursor_init(&g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
|
||||
da_mouse_init();
|
||||
|
||||
|
||||
@@ -213,7 +213,9 @@ static void do_http_get(const char *url) {
|
||||
/* 收至 FIN */
|
||||
term_puts_color(" -> 等待响应...\n", DA_TEXT_DIM);
|
||||
redraw_all();
|
||||
int n = ns_tcp_recv(0, 0, 5000);
|
||||
/* buf=0 不拷贝(直接读全局 ns_tcp_rx),cap 必须给缓冲容量,
|
||||
* 否则 n=min(rx_len,0) 恒为 0,误判为超时。 */
|
||||
int n = ns_tcp_recv(0, NS_TCP_RX_CAP, 5000);
|
||||
ns_tcp_close();
|
||||
if (n < 0) {
|
||||
term_puts_color(" [错误] 连接被重置\n", DA_ERROR);
|
||||
@@ -319,6 +321,7 @@ void dsk_entry(const da_boot_context *ctx) {
|
||||
}
|
||||
|
||||
da_init(&g_ac, ctx);
|
||||
ns_apply_lease_from_ctx((u64)(const void *)ctx); /* netman 租约覆盖默认 slirp IP */
|
||||
term_init_layout();
|
||||
term_clear();
|
||||
|
||||
|
||||
+451
-16
@@ -21,6 +21,8 @@
|
||||
#ifndef DESHAB_FAT32_IO_H
|
||||
#define DESHAB_FAT32_IO_H
|
||||
|
||||
#include "fat32_lfn.h"
|
||||
|
||||
/* ---- block 设备函数类型(与 shell/DSK 一致:index 参数固定 0) ---- */
|
||||
typedef int (*f32_block_read_fn)(u32 index, u64 lba, u32 count, void *buf);
|
||||
typedef int (*f32_block_write_fn)(u32 index, u64 lba, u32 count, const void *buf);
|
||||
@@ -381,6 +383,217 @@ static inline int f32_write_root_file(const char *name11, const u8 *data, u32 si
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 在指定目录簇中写入文件(存在则替换,不存在则新建)。
|
||||
* 泛化自 f32_write_root_file:目录可跨簇链,目录项回写到其所在簇。
|
||||
* dir_clus : 目标目录首簇(0 = 根目录)
|
||||
* name11 : 11 字符 8.3 名
|
||||
* 返回 0 成功;负值失败(-4 = 目录链无空闲槽,不扩展目录簇链)。
|
||||
* 注意:仅 8.3 短名匹配/创建,不生成 LFN 项。 */
|
||||
static inline int f32_write_file_in_dir(u32 dir_clus, const char *name11, const u8 *data, u32 size) {
|
||||
if (f32_disk_load() != 0) return -1;
|
||||
if (!f32_blk_write) return -10;
|
||||
/* 写入前重新加载 BPB+FAT,保证与盘上一致 */
|
||||
if (f32_read_sectors(0, 256, f32_disk) != 0) return -2;
|
||||
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
||||
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 (dir_clus == 0) dir_clus = bpb->root_clus;
|
||||
|
||||
/* 第一遍:沿目录簇链查找现有项与空闲槽,记录其所在簇与索引 */
|
||||
u32 found_clus = 0, existing_clus = 0;
|
||||
int found = 0;
|
||||
u32 found_idx = 0, free_idx = 0;
|
||||
u32 free_slot_clus = 0;
|
||||
int have_free = 0;
|
||||
u32 dclus = dir_clus;
|
||||
while (dclus >= 2 && dclus < 0x0FFFFFF8 && !found) {
|
||||
u32 dlba = data_lba + (dclus - 2) * spc;
|
||||
const u8 *db;
|
||||
if ((u64)dlba * 512 + cluster_bytes <= 256ULL * 512)
|
||||
db = f32_disk + (u64)dlba * 512;
|
||||
else {
|
||||
if (f32_read_sectors(dlba, spc, f32_cluster) != 0) return -11;
|
||||
db = f32_cluster;
|
||||
}
|
||||
const f32_dirent *dir = (const f32_dirent *)db;
|
||||
u32 max_entries = cluster_bytes / 32;
|
||||
for (u32 e = 0; e < max_entries; e++) {
|
||||
if (dir[e].name[0] == 0) {
|
||||
if (!have_free) { have_free = 1; free_slot_clus = dclus; free_idx = e; }
|
||||
goto scan_done; /* 0x00 = 目录结尾 */
|
||||
}
|
||||
if ((u8)dir[e].name[0] == 0xE5) {
|
||||
if (!have_free) { have_free = 1; free_slot_clus = dclus; free_idx = e; }
|
||||
continue;
|
||||
}
|
||||
if (dir[e].attr == 0x0F) continue;
|
||||
if (dir[e].attr & 0x08) continue;
|
||||
if (f32_neq11(dir[e].name, name11)) {
|
||||
existing_clus = (u32)f32_r16((const u8*)&dir[e].clow) | ((u32)f32_r16((const u8*)&dir[e].chigh) << 16);
|
||||
found = 1; found_idx = e; found_clus = dclus;
|
||||
if (!have_free) { have_free = 1; free_slot_clus = dclus; free_idx = e; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) break;
|
||||
u32 fo = fat_byte_off + dclus * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
dclus = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
}
|
||||
scan_done:
|
||||
; /* C89: 标签后必须是语句 */
|
||||
|
||||
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) {
|
||||
first_clus = existing_clus;
|
||||
u32 cur = existing_clus;
|
||||
u32 count = 0;
|
||||
while (cur >= 2 && cur < 0x0FFFFFF8 && count < clusters_needed) {
|
||||
prev_clus = cur;
|
||||
u32 fo = fat_byte_off + cur * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
cur = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
count++;
|
||||
}
|
||||
while (count < clusters_needed) {
|
||||
u32 newc = 0;
|
||||
for (u32 c = 2; c < (fat_sectors * 512) / 4; c++) {
|
||||
u32 fo = fat_byte_off + c * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
if ((f32_r32(f32_disk + fo) & 0x0FFFFFFF) == 0) { newc = c; break; }
|
||||
}
|
||||
if (newc == 0) return -3;
|
||||
u32 pfo = fat_byte_off + prev_clus * 4;
|
||||
if (pfo + 4 <= sizeof(f32_disk)) {
|
||||
f32_disk[pfo] = (u8)(newc & 0xFF);
|
||||
f32_disk[pfo+1] = (u8)((newc >> 8) & 0xFF);
|
||||
f32_disk[pfo+2] = (u8)((newc >> 16) & 0xFF);
|
||||
f32_disk[pfo+3] = (u8)((newc >> 24) & 0x0F);
|
||||
}
|
||||
prev_clus = newc;
|
||||
count++;
|
||||
}
|
||||
if (prev_clus >= 2) {
|
||||
u32 fo = fat_byte_off + prev_clus * 4;
|
||||
if (fo + 4 <= sizeof(f32_disk)) {
|
||||
f32_disk[fo] = 0xF8; f32_disk[fo+1] = 0xFF; f32_disk[fo+2] = 0xFF; f32_disk[fo+3] = 0x0F;
|
||||
}
|
||||
}
|
||||
/* 释放多余旧簇 */
|
||||
u32 next = 0;
|
||||
u32 fo = fat_byte_off + prev_clus * 4;
|
||||
if (fo + 4 <= sizeof(f32_disk)) next = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
while (next >= 2 && next < 0x0FFFFFF8) {
|
||||
u32 nfo = fat_byte_off + next * 4;
|
||||
u32 nn = 0;
|
||||
if (nfo + 4 <= sizeof(f32_disk)) nn = f32_r32(f32_disk + nfo) & 0x0FFFFFFF;
|
||||
f32_disk[nfo] = 0; f32_disk[nfo+1] = 0; f32_disk[nfo+2] = 0; f32_disk[nfo+3] = 0;
|
||||
next = nn;
|
||||
}
|
||||
} else {
|
||||
if (!have_free) return -4; /* 目录链满,不扩展 */
|
||||
for (u32 i = 0; i < clusters_needed; i++) {
|
||||
u32 newc = 0;
|
||||
for (u32 c = 2; c < (fat_sectors * 512) / 4; c++) {
|
||||
u32 fo = fat_byte_off + c * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
if ((f32_r32(f32_disk + fo) & 0x0FFFFFFF) == 0) { newc = c; break; }
|
||||
}
|
||||
if (newc == 0) return -3;
|
||||
if (i == 0) first_clus = newc;
|
||||
if (prev_clus >= 2) {
|
||||
u32 pfo = fat_byte_off + prev_clus * 4;
|
||||
f32_disk[pfo] = (u8)(newc & 0xFF);
|
||||
f32_disk[pfo+1] = (u8)((newc >> 8) & 0xFF);
|
||||
f32_disk[pfo+2] = (u8)((newc >> 16) & 0xFF);
|
||||
f32_disk[pfo+3] = (u8)((newc >> 24) & 0x0F);
|
||||
}
|
||||
prev_clus = newc;
|
||||
}
|
||||
if (prev_clus >= 2) {
|
||||
u32 fo = fat_byte_off + prev_clus * 4;
|
||||
f32_disk[fo] = 0xF8; f32_disk[fo+1] = 0xFF; f32_disk[fo+2] = 0xFF; f32_disk[fo+3] = 0x0F;
|
||||
}
|
||||
}
|
||||
|
||||
/* 写数据到簇(窗口内直接改 f32_disk,窗口外 RMW) */
|
||||
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;
|
||||
if ((u64)clba * 512 + cluster_bytes <= 256ULL * 512) {
|
||||
u8 *dst = f32_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 {
|
||||
if (f32_read_sectors(clba, spc, f32_cluster) != 0) return -5;
|
||||
for (u32 b = 0; b < chunk; b++) f32_cluster[b] = src[b];
|
||||
for (u32 b = chunk; b < cluster_bytes; b++) f32_cluster[b] = 0;
|
||||
if (f32_write_sectors(clba, spc, f32_cluster) != 0) return -6;
|
||||
}
|
||||
src += chunk; remaining -= chunk; ci++;
|
||||
u32 fo = fat_byte_off + cur * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
cur = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
}
|
||||
|
||||
/* 更新目录项(所在簇可能不在窗口内,需 RMW) */
|
||||
u32 entry_clus = found ? found_clus : free_slot_clus;
|
||||
u32 entry_idx = found ? found_idx : free_idx;
|
||||
u32 elba = data_lba + (entry_clus - 2) * spc;
|
||||
u8 *ebuf;
|
||||
int ebuf_windowed = ((u64)elba * 512 + cluster_bytes <= 256ULL * 512);
|
||||
if (ebuf_windowed) ebuf = f32_disk + (u64)elba * 512;
|
||||
else {
|
||||
if (f32_read_sectors(elba, spc, f32_cluster) != 0) return -12;
|
||||
ebuf = f32_cluster;
|
||||
}
|
||||
f32_dirent *e = &((f32_dirent *)ebuf)[entry_idx];
|
||||
if (!found) {
|
||||
for (int i = 0; i < 11; i++) e->name[i] = name11[i];
|
||||
e->attr = 0x20; e->ntr = 0; e->ctenth = 0; e->ctime = 0; e->cdate = 0; e->adate = 0;
|
||||
e->wtime = 0; e->wdate = 0;
|
||||
}
|
||||
e->chigh = (u16)((first_clus >> 16) & 0xFFFF);
|
||||
e->clow = (u16)(first_clus & 0xFFFF);
|
||||
e->fsize = size;
|
||||
|
||||
/* 回写 FAT(两份)+ 目录项簇 + 窗口内数据簇 */
|
||||
for (u32 f = 0; f < bpb->fc; f++) {
|
||||
u32 flba = fat_lba + f * fat_sectors;
|
||||
if (f32_write_sectors(flba, fat_sectors, f32_disk + (u64)flba * 512) != 0) return -7;
|
||||
}
|
||||
if (ebuf_windowed) {
|
||||
if (f32_write_sectors(elba, spc, f32_disk + (u64)elba * 512) != 0) return -8;
|
||||
} else {
|
||||
if (f32_write_sectors(elba, spc, f32_cluster) != 0) return -8;
|
||||
}
|
||||
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 (f32_write_sectors(clba, spc, f32_disk + (u64)clba * 512) != 0) return -9;
|
||||
}
|
||||
u32 fo = fat_byte_off + cur * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
cur = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
ci++;
|
||||
}
|
||||
f32_disk_loaded = 0; /* 下次操作重新加载,避免使用脏缓存 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 删除根目录下指定 8.3 名文件:清空簇链 + 标记目录项为 0xE5。
|
||||
* 返回 0 成功,-1 未找到,其他负值失败。 */
|
||||
static inline int f32_delete_root_file(const char *name11) {
|
||||
@@ -474,16 +687,18 @@ done:
|
||||
|
||||
/* 目录条目(供 fileman 等工具使用) */
|
||||
typedef struct {
|
||||
char name[13]; /* 8.3 显示名 */
|
||||
u32 clus; /* 首簇号 */
|
||||
u32 size; /* 文件大小(目录为 0) */
|
||||
char name[13]; /* 8.3 显示名 */
|
||||
u32 clus; /* 首簇号 */
|
||||
u32 size; /* 文件大小(目录为 0) */
|
||||
u8 attr;
|
||||
u8 is_dir;
|
||||
u8 has_lfn; /* 1=long_name 字段有效 */
|
||||
char long_name[260]; /* LFN 长文件名(ASCII) */
|
||||
} f32_entry;
|
||||
|
||||
typedef int (*f32_list_entry_cb)(const f32_entry *e, void *user_data);
|
||||
|
||||
/* 列举指定目录簇的内容。
|
||||
/* 列举指定目录簇的内容(含 LFN 长文件名)。
|
||||
* dir_clus = 0 表示根目录(自动从 BPB 取 root_clus)。
|
||||
* 回调返回非 0 立即停止。返回 0 成功,负值失败。 */
|
||||
static inline int f32_list_dir(u32 dir_clus, f32_list_entry_cb emit, void *user_data) {
|
||||
@@ -493,6 +708,9 @@ static inline int f32_list_dir(u32 dir_clus, f32_list_entry_cb emit, void *user_
|
||||
u32 fat_byte_off = bpb->rsvd * 512;
|
||||
u32 spc = bpb->spc;
|
||||
u32 clus = (dir_clus == 0) ? bpb->root_clus : dir_clus;
|
||||
/* LFN 缓冲必须在簇循环外声明:跨簇 LFN 链需要持续存活 */
|
||||
fat32_lfn_buf lfn;
|
||||
fat32_lfn_init(&lfn);
|
||||
while (clus >= 2 && clus < 0x0FFFFFF8) {
|
||||
u32 lba = data_lba + (clus - 2) * spc;
|
||||
const u8 *cb;
|
||||
@@ -502,22 +720,38 @@ static inline int f32_list_dir(u32 dir_clus, f32_list_entry_cb emit, void *user_
|
||||
if (f32_read_sectors(lba, 8, f32_cluster) != 0) return -4;
|
||||
cb = f32_cluster;
|
||||
}
|
||||
const f32_dirent *dir = (const f32_dirent *)cb;
|
||||
for (u32 e = 0; e * 32 < spc * 512; e++) {
|
||||
if (dir[e].name[0] == 0) goto done;
|
||||
if ((u8)dir[e].name[0] == 0xE5) continue;
|
||||
if (dir[e].attr == 0x0F) continue;
|
||||
if (dir[e].attr & 0x08) continue;
|
||||
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) goto done;
|
||||
if ((u8)entry[0] == 0xE5) {
|
||||
/* 已删除项:清空 LFN 缓冲防止残留污染 */
|
||||
fat32_lfn_init(&lfn);
|
||||
continue;
|
||||
}
|
||||
const f32_dirent *de = (const f32_dirent *)entry;
|
||||
int is_short = fat32_lfn_process(&lfn, entry);
|
||||
if (!is_short) continue; /* LFN 片段,等待短名项 */
|
||||
if (de->attr & 0x08) continue; /* 卷标 */
|
||||
/* 跳过 "." 和 ".." 条目 */
|
||||
if (dir[e].name[0] == '.' && (dir[e].name[1] == ' ' || dir[e].name[1] == '.'))
|
||||
if (de->name[0] == '.' && (de->name[1] == ' ' || de->name[1] == '.'))
|
||||
continue;
|
||||
f32_entry fe;
|
||||
f32_name_from_83(dir[e].name, fe.name);
|
||||
fe.clus = (u32)f32_r16((const u8*)&dir[e].clow) | ((u32)f32_r16((const u8*)&dir[e].chigh) << 16);
|
||||
fe.size = dir[e].fsize;
|
||||
fe.attr = dir[e].attr;
|
||||
fe.is_dir = (dir[e].attr & 0x10) ? 1 : 0;
|
||||
f32_name_from_83(de->name, fe.name);
|
||||
fe.clus = (u32)f32_r16((const u8*)&de->clow) | ((u32)f32_r16((const u8*)&de->chigh) << 16);
|
||||
fe.size = de->fsize;
|
||||
fe.attr = de->attr;
|
||||
fe.is_dir = (de->attr & 0x10) ? 1 : 0;
|
||||
fe.has_lfn = 0;
|
||||
fe.long_name[0] = 0;
|
||||
if (lfn.valid) {
|
||||
int ln = fat32_lfn_to_ascii(&lfn, fe.long_name, sizeof(fe.long_name));
|
||||
if (ln >= 0) fe.has_lfn = 1;
|
||||
}
|
||||
if (emit(&fe, user_data) != 0) goto done;
|
||||
/* 短名项处理完毕,重置 LFN 缓冲等待下一条链 */
|
||||
fat32_lfn_init(&lfn);
|
||||
}
|
||||
u32 fo = fat_byte_off + clus * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
@@ -559,4 +793,205 @@ static inline int f32_read_file_by_clus(u32 clus, u32 size, u8 *out_buf, u32 buf
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- LFN 长文件名支持(Phase 1 SYSTEM/lib 兼容层) ---- */
|
||||
|
||||
/* 在指定目录簇中按长文件名查找文件/子目录。
|
||||
* dir_clus : 起始目录簇(0=根目录)
|
||||
* long_name : 目标长文件名(ASCII,大小写不敏感);为 NULL 时仅按 8.3 短名匹配
|
||||
* is_dir : 1 只匹配子目录,0 只匹配文件
|
||||
* out_clus : 输出首簇号
|
||||
* out_size : 输出文件大小
|
||||
* 返回 0 成功,-1 未找到 / 错误。
|
||||
* LFN 缓冲跨簇遍历持续存活,支持跨簇边界的 LFN 链。 */
|
||||
static inline int f32_find_in_dir_lfn(u32 dir_clus, const char *long_name,
|
||||
int is_dir, u32 *out_clus, u32 *out_size) {
|
||||
if (f32_disk_load() != 0) return -1;
|
||||
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
||||
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
|
||||
u32 fat_byte_off = bpb->rsvd * 512;
|
||||
u32 spc = bpb->spc;
|
||||
u32 clus = (dir_clus == 0) ? bpb->root_clus : dir_clus;
|
||||
|
||||
/* LFN 缓冲必须在簇循环外声明:跨簇 LFN 链需要持续存活 */
|
||||
fat32_lfn_buf lfn;
|
||||
fat32_lfn_init(&lfn);
|
||||
|
||||
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 = f32_disk + (u64)lba * 512;
|
||||
else {
|
||||
if (f32_read_sectors(lba, 8, f32_cluster) != 0) return -4;
|
||||
cb = f32_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) goto not_found;
|
||||
if ((u8)entry[0] == 0xE5) {
|
||||
fat32_lfn_init(&lfn);
|
||||
continue;
|
||||
}
|
||||
const f32_dirent *de = (const f32_dirent *)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;
|
||||
}
|
||||
/* 先尝试 LFN 长名匹配,再回退 8.3 匹配 */
|
||||
int matched = 0;
|
||||
if (lfn.valid && long_name) {
|
||||
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 && long_name) {
|
||||
/* 用短名构造 8.3 显示串与目标比较(大小写不敏感) */
|
||||
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 = (u32)f32_r16((const u8 *)&de->clow) |
|
||||
((u32)f32_r16((const u8 *)&de->chigh) << 16);
|
||||
*out_size = de->fsize;
|
||||
return 0;
|
||||
}
|
||||
fat32_lfn_init(&lfn);
|
||||
}
|
||||
u32 fo = fat_byte_off + clus * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
clus = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
}
|
||||
not_found:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 按 '/' 分隔的路径定位最终目录簇。
|
||||
* path : 路径(如 "lib" 或 "system/deshab64"),每段为长文件名
|
||||
* out_clus : 输出目录簇号
|
||||
* 返回 0 成功,-1 失败。空路径返回根目录簇。 */
|
||||
static inline int f32_find_path_dir_lfn(const char *path, u32 *out_clus) {
|
||||
if (!path || !out_clus) return -1;
|
||||
if (f32_disk_load() != 0) return -1;
|
||||
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
||||
u32 cur = bpb->root_clus;
|
||||
|
||||
u32 pos = 0;
|
||||
u32 path_len = 0;
|
||||
while (path[path_len]) path_len++;
|
||||
|
||||
/* 空路径或仅 "/" → 根目录 */
|
||||
if (path_len == 0 || (path_len == 1 && path[0] == '/')) {
|
||||
*out_clus = cur;
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (pos < path_len) {
|
||||
/* 跳过前导 '/' */
|
||||
while (pos < path_len && path[pos] == '/') pos++;
|
||||
if (pos >= path_len) break;
|
||||
/* 提取路径组件(到下一个 '/' 或结尾) */
|
||||
char comp[260];
|
||||
u32 ci = 0;
|
||||
while (pos < path_len && path[pos] != '/' && ci + 1 < sizeof(comp)) {
|
||||
comp[ci++] = path[pos++];
|
||||
}
|
||||
comp[ci] = 0;
|
||||
if (ci == 0) continue;
|
||||
|
||||
u32 sub_clus = 0, sub_size = 0;
|
||||
if (f32_find_in_dir_lfn(cur, comp, 1, &sub_clus, &sub_size) != 0) return -1;
|
||||
cur = sub_clus;
|
||||
}
|
||||
*out_clus = cur;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 按长路径读取文件到 f32_data 缓冲。
|
||||
* path : '/' 分隔的长文件名路径(如 "lib/libtest.so")
|
||||
* out_data : 返回 f32_data 内的指针
|
||||
* out_size : 返回文件大小
|
||||
* 返回 0 成功,负值失败。 */
|
||||
static inline int f32_read_path_lfn(const char *path, u8 **out_data, u32 *out_size) {
|
||||
if (!path || !out_data || !out_size) return -1;
|
||||
if (f32_disk_load() != 0) return -1;
|
||||
|
||||
/* 分离目录部分与文件名 */
|
||||
u32 path_len = 0;
|
||||
while (path[path_len]) path_len++;
|
||||
u32 last_slash = 0xFFFFFFFFu;
|
||||
for (u32 i = 0; i < path_len; i++) {
|
||||
if (path[i] == '/') last_slash = i;
|
||||
}
|
||||
const char *fname = (last_slash == 0xFFFFFFFFu) ? path : path + last_slash + 1;
|
||||
|
||||
/* 定位父目录簇 */
|
||||
u32 dir_clus = 0;
|
||||
if (last_slash == 0xFFFFFFFFu) {
|
||||
/* 无 '/' → 根目录 */
|
||||
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
||||
dir_clus = bpb->root_clus;
|
||||
} else {
|
||||
char dirpath[260];
|
||||
u32 dn = last_slash;
|
||||
if (dn >= sizeof(dirpath)) dn = sizeof(dirpath) - 1;
|
||||
for (u32 i = 0; i < dn; i++) dirpath[i] = path[i];
|
||||
dirpath[dn] = 0;
|
||||
if (f32_find_path_dir_lfn(dirpath, &dir_clus) != 0) return -2;
|
||||
}
|
||||
|
||||
/* 在父目录中查找文件 */
|
||||
u32 file_clus = 0, file_size = 0;
|
||||
if (f32_find_in_dir_lfn(dir_clus, fname, 0, &file_clus, &file_size) != 0) return -3;
|
||||
if (file_size > sizeof(f32_data)) return -4;
|
||||
|
||||
/* 读取文件数据 */
|
||||
if (f32_read_file_by_clus(file_clus, file_size, f32_data, sizeof(f32_data)) != 0) return -5;
|
||||
*out_data = f32_data;
|
||||
*out_size = file_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 按长路径写入文件(存在则替换,不存在则在父目录中新建 8.3 项)。
|
||||
* path : '/' 分隔路径(如 "system/deshab64/FUCK"),文件名段必须可转 8.3
|
||||
* 返回 0 成功,负值失败(-6 = 文件名不是合法 8.3)。 */
|
||||
static inline int f32_write_path_lfn(const char *path, const u8 *data, u32 size) {
|
||||
if (!path || !data) return -1;
|
||||
if (f32_disk_load() != 0) return -1;
|
||||
|
||||
u32 path_len = 0;
|
||||
while (path[path_len]) path_len++;
|
||||
u32 last_slash = 0xFFFFFFFFu;
|
||||
for (u32 i = 0; i < path_len; i++) {
|
||||
if (path[i] == '/') last_slash = i;
|
||||
}
|
||||
const char *fname = (last_slash == 0xFFFFFFFFu) ? path : path + last_slash + 1;
|
||||
|
||||
u32 dir_clus = 0;
|
||||
if (last_slash == 0xFFFFFFFFu) {
|
||||
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
||||
dir_clus = bpb->root_clus;
|
||||
} else {
|
||||
char dirpath[260];
|
||||
u32 dn = last_slash;
|
||||
if (dn >= sizeof(dirpath)) dn = sizeof(dirpath) - 1;
|
||||
for (u32 i = 0; i < dn; i++) dirpath[i] = path[i];
|
||||
dirpath[dn] = 0;
|
||||
if (f32_find_path_dir_lfn(dirpath, &dir_clus) != 0) return -2;
|
||||
}
|
||||
|
||||
char name11[11];
|
||||
if (f32_name_to_83(fname, name11) != 0) return -6;
|
||||
return f32_write_file_in_dir(dir_clus, name11, data, size);
|
||||
}
|
||||
|
||||
#endif /* DESHAB_FAT32_IO_H */
|
||||
|
||||
+37
-9
@@ -28,6 +28,34 @@
|
||||
#define NS_DNS_IP NS_IP4(10,0,2,3)
|
||||
#define NS_NETMASK NS_IP4(255,255,255,0)
|
||||
|
||||
/* 当前生效 IP 配置:默认 slirp 硬编码;netman 租约经 ns_apply_lease 覆盖。
|
||||
* 租约约定:dsk_boot_context.reserved[7] 存放指向 net_lease_info(dsk.h)的
|
||||
* 指针,结构由 DSK 常驻持有,netman 在 DHCP/静态配置成功后写入。 */
|
||||
static u32 ns_guest_ip = NS_GUEST_IP;
|
||||
static u32 ns_gateway_ip = NS_GATEWAY_IP;
|
||||
static u32 ns_dns_ip = NS_DNS_IP;
|
||||
|
||||
static inline void ns_apply_lease(u32 ip, u32 gw, u32 dns) {
|
||||
if (ip) ns_guest_ip = ip;
|
||||
if (gw) ns_gateway_ip = gw;
|
||||
if (dns) ns_dns_ip = dns;
|
||||
}
|
||||
|
||||
/* 从 boot context 读取 netman 租约(存在且 valid 则应用)。
|
||||
* ctx_u64 为 dsk_boot_context 指针值。reserved[8] 位于偏移 160
|
||||
* (第 20 个 u64 起),reserved[7] = 第 27 个 u64,存 net_lease_info 指针:
|
||||
* +0 magic("NETL" 0x4E45544C) +4 flags(bit0 valid) +8 ip +12 gw +16 dns */
|
||||
static inline void ns_apply_lease_from_ctx(u64 ctx_u64) {
|
||||
if (!ctx_u64) return;
|
||||
const u64 *ctx = (const u64 *)ctx_u64;
|
||||
u64 lease_ptr = ctx[27]; /* reserved[7] */
|
||||
if (!lease_ptr) return;
|
||||
const u32 *li = (const u32 *)lease_ptr;
|
||||
if (li[0] != 0x4E45544Cu) return; /* NET_LEASE_MAGIC */
|
||||
if (!(li[1] & 1u)) return; /* NET_LEASE_F_VALID */
|
||||
ns_apply_lease(li[2], li[3], li[4]);
|
||||
}
|
||||
|
||||
#define NS_ETH_ARP 0x0806
|
||||
#define NS_ETH_IPV4 0x0800
|
||||
#define NS_IP_ICMP 1
|
||||
@@ -272,10 +300,10 @@ static inline void ns_arp_handle(const u8 *pkt, u32 len) {
|
||||
u16 oper = ns_r16(pkt + 6);
|
||||
u32 spa = ns_r32(pkt + 14);
|
||||
u32 tpa = ns_r32(pkt + 24);
|
||||
if (oper == 1 && tpa == NS_GUEST_IP) {
|
||||
if (oper == 1 && tpa == ns_guest_ip) {
|
||||
/* 请求我们的 IP → 回复 */
|
||||
u8 reply[28];
|
||||
ns_arp_build(reply, 2, ns_mac, NS_GUEST_IP, pkt + 8, spa);
|
||||
ns_arp_build(reply, 2, ns_mac, ns_guest_ip, pkt + 8, spa);
|
||||
ns_send_eth(pkt + 8, NS_ETH_ARP, reply, 28);
|
||||
} else if (oper == 2) {
|
||||
ns_arp_cache_put(spa, pkt + 8);
|
||||
@@ -314,7 +342,7 @@ static inline int ns_arp_resolve(u32 ip, u8 out_mac[6]) {
|
||||
static const u8 zero_mac[6] = {0,0,0,0,0,0};
|
||||
for (int attempt = 0; attempt < 3; attempt++) {
|
||||
u8 req[28];
|
||||
ns_arp_build(req, 1, ns_mac, NS_GUEST_IP, zero_mac, ip);
|
||||
ns_arp_build(req, 1, ns_mac, ns_guest_ip, zero_mac, ip);
|
||||
ns_arp_reply_valid = 0;
|
||||
if (ns_send_eth(bcast, NS_ETH_ARP, req, 28) != 0) continue;
|
||||
u64 deadline = ns_rdtsc() + ns_tsc_per_ms * 1000;
|
||||
@@ -341,7 +369,7 @@ static inline int ns_arp_resolve(u32 ip, u8 out_mac[6]) {
|
||||
/* 目标 IP 的下一跳 MAC(同子网直连,否则走网关)。返回 0 成功。 */
|
||||
static inline int ns_route_mac(u32 dst_ip, u8 out_mac[6]) {
|
||||
u32 next = dst_ip;
|
||||
if ((dst_ip & NS_NETMASK) != (NS_GUEST_IP & NS_NETMASK)) next = NS_GATEWAY_IP;
|
||||
if ((dst_ip & NS_NETMASK) != (ns_guest_ip & NS_NETMASK)) next = ns_gateway_ip;
|
||||
return ns_arp_resolve(next, out_mac);
|
||||
}
|
||||
|
||||
@@ -367,7 +395,7 @@ static inline int ns_ping(u32 dst_ip, u16 seq, u32 timeout_ms, u32 *out_rtt_ms,
|
||||
if (ns_route_mac(dst_ip, dst_mac) != 0) return -1;
|
||||
|
||||
u8 pkt[20 + 8 + 32];
|
||||
ns_ipv4_build(pkt, NS_IP_ICMP, NS_GUEST_IP, dst_ip, 8 + 32);
|
||||
ns_ipv4_build(pkt, NS_IP_ICMP, ns_guest_ip, dst_ip, 8 + 32);
|
||||
u8 *icmp = pkt + 20;
|
||||
icmp[0] = 8; /* echo request */
|
||||
icmp[1] = 0;
|
||||
@@ -415,7 +443,7 @@ static inline int ns_dns_resolve(const char *host, u32 *out_ip) {
|
||||
if (ns_parse_ip(host, out_ip) == 0) return 0;
|
||||
|
||||
u8 dst_mac[6];
|
||||
if (ns_route_mac(NS_DNS_IP, dst_mac) != 0) return -1;
|
||||
if (ns_route_mac(ns_dns_ip, dst_mac) != 0) return -1;
|
||||
|
||||
/* 构造查询:header + qname + qtype/qclass */
|
||||
u8 q[512];
|
||||
@@ -443,7 +471,7 @@ static inline int ns_dns_resolve(const char *host, u32 *out_ip) {
|
||||
u32 qlen = p;
|
||||
|
||||
u8 pkt[20 + 8 + 512];
|
||||
ns_ipv4_build(pkt, NS_IP_UDP, NS_GUEST_IP, NS_DNS_IP, 8 + qlen);
|
||||
ns_ipv4_build(pkt, NS_IP_UDP, ns_guest_ip, ns_dns_ip, 8 + qlen);
|
||||
u8 *udp = pkt + 20;
|
||||
ns_w16(udp + 0, 49153);
|
||||
ns_w16(udp + 2, 53);
|
||||
@@ -540,7 +568,7 @@ static inline int ns_tcp_emit(u8 flags, const u8 *data, u32 len, u32 seq) {
|
||||
u8 pkt[20 + 20 + 1460];
|
||||
if (len > 1460) return -1;
|
||||
u32 tcp_len = 20 + len;
|
||||
ns_ipv4_build(pkt, NS_IP_TCP, NS_GUEST_IP, ns_tc.dst_ip, tcp_len);
|
||||
ns_ipv4_build(pkt, NS_IP_TCP, ns_guest_ip, ns_tc.dst_ip, tcp_len);
|
||||
u8 *t = pkt + 20;
|
||||
ns_w16(t + 0, ns_tc.src_port);
|
||||
ns_w16(t + 2, ns_tc.dst_port);
|
||||
@@ -554,7 +582,7 @@ static inline int ns_tcp_emit(u8 flags, const u8 *data, u32 len, u32 seq) {
|
||||
if (len) ns_memcpy(t + 20, data, len);
|
||||
/* 伪头部校验和 */
|
||||
u8 ph[12];
|
||||
ns_w32(ph + 0, NS_GUEST_IP);
|
||||
ns_w32(ph + 0, ns_guest_ip);
|
||||
ns_w32(ph + 4, ns_tc.dst_ip);
|
||||
ph[8] = 0; ph[9] = NS_IP_TCP;
|
||||
ns_w16(ph + 10, (u16)tcp_len);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/* realdll64.c — PE32+ 真实 DLL 链验证程序
|
||||
*
|
||||
* 用途: 验证 Deshab PE 兼容层 pe_dll_manager 对 SYSTEM/lib 真实 DLL 的
|
||||
* 加载与解析(含转发导出跟随与 DllMain ATTACH)。
|
||||
*
|
||||
* 验证路径:
|
||||
* A) 静态导入 msvcrt.dll 中非 shim 函数(strcat/strstr/atoi):
|
||||
* shim 表未命中 → pe_dll_resolve → 真实 msvcrt.dll
|
||||
* (Win10 msvcrt 把这些函数转发到 ucrtbase,验证转发导出跟随)
|
||||
* B) LoadLibraryA("ucrtbase.dll") + GetProcAddress:
|
||||
* 未知 DLL → pe_dll_load 真实加载,句柄=镜像基址,
|
||||
* GetProcAddress 按基址解析 strlen/memcpy 并调用验证。
|
||||
*
|
||||
* 输出: 全部经 shim printf(串口)。锚点格式 "[REALDLL] ..."。
|
||||
*
|
||||
* 编译 (MinGW-w64):
|
||||
* x86_64-w64-mingw32-gcc -fno-builtin -O1 -o realdll64.exe realdll64.c \
|
||||
* -nostartfiles -Wl,--entry,main -Wl,--image-base,0x140000000
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* 显式声明:这些函数不在 shim 表内,强制走真实 DLL 解析。
|
||||
* -fno-builtin 阻止 gcc 内联展开,保证产生 IAT 导入调用。 */
|
||||
__declspec(dllimport) char * __cdecl strcat(char *dst, const char *src);
|
||||
__declspec(dllimport) char * __cdecl strstr(const char *hay, const char *needle);
|
||||
__declspec(dllimport) int __cdecl atoi(const char *s);
|
||||
|
||||
typedef unsigned long long (__cdecl *strlen_fn)(const char *);
|
||||
typedef void *(__cdecl *memcpy_fn)(void *, const void *, unsigned long long);
|
||||
|
||||
/* MinGW ABI 对 main 强制插入 __main 调用(CRT 静态构造注册)。
|
||||
* freestanding(-nostdlib)无 CRT,提供空 stub。 */
|
||||
void __main(void) {}
|
||||
|
||||
static int g_fails = 0;
|
||||
|
||||
static void check(const char *name, int ok) {
|
||||
printf("[REALDLL] %s: %s\n", name, ok ? "OK" : "FAIL");
|
||||
if (!ok) g_fails++;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("[REALDLL] begin\n");
|
||||
|
||||
/* ---- A1: strcat(真实 DLL,Win10 转发到 ucrtbase) ---- */
|
||||
char buf[64];
|
||||
buf[0] = '\0';
|
||||
strcat(buf, "abc");
|
||||
strcat(buf, "def");
|
||||
check("A1 strcat", buf[0] == 'a' && buf[1] == 'b' && buf[2] == 'c' &&
|
||||
buf[3] == 'd' && buf[4] == 'e' && buf[5] == 'f' &&
|
||||
buf[6] == '\0');
|
||||
|
||||
/* ---- A2: strstr(真实 DLL) ---- */
|
||||
const char *hay = "hello deshab world";
|
||||
char *hit = strstr(hay, "deshab");
|
||||
check("A2 strstr", hit == hay + 6);
|
||||
|
||||
/* ---- A3: atoi(真实 DLL) ---- */
|
||||
check("A3 atoi", atoi("12345") == 12345 && atoi("-42") == -42);
|
||||
|
||||
/* ---- B1: LoadLibraryA 真实 DLL ---- */
|
||||
HMODULE ucrt = LoadLibraryA("ucrtbase.dll");
|
||||
check("B1 LoadLibraryA(ucrtbase.dll)", ucrt != NULL);
|
||||
|
||||
if (ucrt) {
|
||||
/* ---- B2: GetProcAddress strlen + 调用 ---- */
|
||||
strlen_fn p_strlen = (strlen_fn)GetProcAddress(ucrt, "strlen");
|
||||
check("B2 GetProcAddress(strlen)", p_strlen != NULL);
|
||||
if (p_strlen) {
|
||||
check("B2 strlen call", p_strlen("hello world") == 11);
|
||||
}
|
||||
|
||||
/* ---- B3: GetProcAddress memcpy + 调用 ---- */
|
||||
memcpy_fn p_memcpy = (memcpy_fn)GetProcAddress(ucrt, "memcpy");
|
||||
check("B3 GetProcAddress(memcpy)", p_memcpy != NULL);
|
||||
if (p_memcpy) {
|
||||
char dst[16];
|
||||
for (int i = 0; i < 16; i++) dst[i] = 0;
|
||||
p_memcpy(dst, "0123456789", 10);
|
||||
check("B3 memcpy call", dst[0] == '0' && dst[9] == '9' && dst[10] == 0);
|
||||
}
|
||||
}
|
||||
|
||||
printf("[REALDLL] end fails=%d\n", g_fails);
|
||||
ExitProcess(g_fails);
|
||||
return g_fails;
|
||||
}
|
||||
Binary file not shown.
@@ -294,6 +294,7 @@ void dsk_entry(const da_boot_context *ctx) {
|
||||
}
|
||||
|
||||
da_init(&g_ac, ctx);
|
||||
ns_apply_lease_from_ctx((u64)(const void *)ctx); /* netman 租约覆盖默认 slirp IP */
|
||||
term_init_layout();
|
||||
term_clear();
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
CC := clang
|
||||
LD := ld.lld
|
||||
|
||||
ROOT := ../../..
|
||||
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/settings.elf
|
||||
|
||||
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I.. -I../../UTSM/include -O2
|
||||
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
|
||||
|
||||
C_SOURCES := main.c
|
||||
OBJECTS := $(C_SOURCES:.c=.o)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(OUT)
|
||||
|
||||
$(OUT): $(OBJECTS) linker.ld
|
||||
@mkdir -p "$(ROOT)/SYSTEM/system/deshab64/tools"
|
||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
|
||||
@@ -0,0 +1,27 @@
|
||||
ENTRY(dsk_entry)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FLAGS(5);
|
||||
rodata PT_LOAD FLAGS(4);
|
||||
data PT_LOAD FLAGS(6);
|
||||
dynamic PT_DYNAMIC FLAGS(4);
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x1000;
|
||||
.text : ALIGN(4K) { *(.text*) } :text
|
||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
||||
.data : ALIGN(4K) {
|
||||
*(.data*)
|
||||
*(.data.rel.ro*)
|
||||
*(COMMON)
|
||||
} :data
|
||||
.rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data
|
||||
.dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic
|
||||
/* .bss 分离为独立 NOBITS 段,避免 BSS 占文件空间(见 DSK linker.ld)。 */
|
||||
.bss : ALIGN(4K) {
|
||||
*(.bss*)
|
||||
} :data
|
||||
}
|
||||
@@ -0,0 +1,1173 @@
|
||||
/* Deshab Settings - 系统设置工具
|
||||
*
|
||||
* 内核页 (KERNEL): 查看/编辑 FUCK 内核配置(boot/drivers/dsk/debug/utsm 常用键),
|
||||
* 行保留方式序列化(注释与未暴露键不受影响),写回
|
||||
* /system/deshab64/FUCK(缺失时回退根目录 FUCK)。
|
||||
* 网络页 (NETWORK): 查看/编辑网络配置,全量重写根目录 NETCONF.CNF(netman 读取)。
|
||||
* 设备页 (DEVICES): 实时枚举内核 netdev 注册表,选择连接设备,
|
||||
* WiFi 扫描结果查看与 SSID 选择(选择写入 NETCONF.CNF)。
|
||||
*
|
||||
* 快捷键: F1 内核页 F2 网络页 F3 设备页 Tab 切页 F5 保存 F6 重载 Esc 退出
|
||||
* Up/Down 选择字段 Space/Enter/Left/Right 切换布尔/枚举/整数
|
||||
* 文本字段选中后直接键入编辑,Backspace 删除
|
||||
* 设备页: S 扫描WiFi Enter 选择设备/SSID
|
||||
* 鼠标: 点击 tab / 字段行 / 按钮
|
||||
*
|
||||
* 注意: 配置修改在下次启动后生效(FUCK 由 Limine boot module 加载,
|
||||
* NETCONF.CNF 由 netman.elf 在启动时读取)。
|
||||
*/
|
||||
|
||||
#include "../../firstInit/ascii_bitmaps.c"
|
||||
#include "../desktop_app.h"
|
||||
#include "../fat32_io.h"
|
||||
|
||||
/* ---- Net API 类型(与 dkm_net_api 布局一致,偏移访问) ---- */
|
||||
typedef u32 (*net_device_count_fn)(void);
|
||||
typedef int (*net_device_info_fn)(u32 index, void *out);
|
||||
typedef int (*net_scan_start_fn)(u32 index);
|
||||
typedef int (*net_scan_count_fn)(u32 index);
|
||||
typedef int (*net_scan_result_fn)(u32 index, u32 n, void *out);
|
||||
typedef int (*net_is_wireless_fn)(u32 index);
|
||||
|
||||
#define NM_NET_F_LINK_UP (1u << 0)
|
||||
#define NM_NET_F_TX_READY (1u << 1)
|
||||
#define NM_NET_F_RX_READY (1u << 2)
|
||||
#define NM_NET_F_WIRELESS (1u << 3)
|
||||
|
||||
#define NM_SEC_OPEN 0u
|
||||
#define NM_SEC_WEP 1u
|
||||
#define NM_SEC_WPA 2u
|
||||
#define NM_SEC_WPA2 3u
|
||||
#define NM_SEC_WPA3 4u
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
u8 mac[6];
|
||||
u32 flags;
|
||||
} nm_device_info;
|
||||
|
||||
typedef struct {
|
||||
char ssid[33];
|
||||
u8 bssid[6];
|
||||
u8 channel;
|
||||
signed char rssi;
|
||||
u8 security;
|
||||
} nm_scan_result;
|
||||
|
||||
/* ---- 字段模型 ---- */
|
||||
typedef enum { FT_BOOL = 0, FT_INT, FT_TEXT, FT_CHOICE } sftype;
|
||||
|
||||
typedef struct {
|
||||
const char *section; /* INI section */
|
||||
const char *key; /* 键名(同时作显示名) */
|
||||
sftype type;
|
||||
const char *choices; /* FT_CHOICE: "|" 分隔,如 "dhcp|static" */
|
||||
int min, max; /* FT_INT 范围 */
|
||||
char value[48];
|
||||
int dirty;
|
||||
} sfield;
|
||||
|
||||
/* ---- 内核页字段(FUCK) ---- */
|
||||
static sfield g_kfields[] = {
|
||||
/* boot */
|
||||
{ "boot", "dev_mode", FT_BOOL, 0, 0, 0, "1", 0 },
|
||||
{ "boot", "selftest", FT_BOOL, 0, 0, 0, "1", 0 },
|
||||
{ "boot", "vmm", FT_BOOL, 0, 0, 0, "1", 0 },
|
||||
{ "boot", "linux_guest", FT_BOOL, 0, 0, 0, "1", 0 },
|
||||
/* drivers */
|
||||
{ "drivers", "e1000", FT_BOOL, 0, 0, 0, "1", 0 },
|
||||
{ "drivers", "virtio_net", FT_BOOL, 0, 0, 0, "1", 0 },
|
||||
{ "drivers", "nvme", FT_BOOL, 0, 0, 0, "1", 0 },
|
||||
{ "drivers", "ahci", FT_BOOL, 0, 0, 0, "1", 0 },
|
||||
{ "drivers", "apic_route", FT_BOOL, 0, 0, 0, "1", 0 },
|
||||
{ "drivers", "apic_route_legacy", FT_BOOL, 0, 0, 0, "1", 0 },
|
||||
{ "drivers", "nvme_msi", FT_BOOL, 0, 0, 0, "1", 0 },
|
||||
{ "drivers", "ath9k", FT_BOOL, 0, 0, 0, "0", 0 },
|
||||
/* dsk */
|
||||
{ "dsk", "show_logo", FT_BOOL, 0, 0, 0, "1", 0 },
|
||||
{ "dsk", "skip_login", FT_BOOL, 0, 0, 0, "1", 0 },
|
||||
{ "dsk", "mouse_init", FT_BOOL, 0, 0, 0, "1", 0 },
|
||||
{ "dsk", "netman", FT_BOOL, 0, 0, 0, "1", 0 },
|
||||
{ "dsk", "default_shell", FT_TEXT, 0, 0, 0, "cmd", 0 },
|
||||
/* debug */
|
||||
{ "debug", "enabled", FT_BOOL, 0, 0, 0, "1", 0 },
|
||||
{ "debug", "log_level", FT_INT, 0, 0, 5, "1", 0 },
|
||||
{ "debug", "net_trace", FT_BOOL, 0, 0, 0, "0", 0 },
|
||||
/* utsm 算法参数 */
|
||||
{ "utsm", "max_segments", FT_TEXT, 0, 0, 0, "1024", 0 },
|
||||
{ "utsm", "max_capabilities", FT_TEXT, 0, 0, 0, "256", 0 },
|
||||
{ "utsm", "max_pckc_keys", FT_TEXT, 0, 0, 0, "8", 0 },
|
||||
{ "utsm", "dirty_shard_pages", FT_TEXT, 0, 0, 0, "1024", 0 },
|
||||
{ "utsm", "arena_size_mb", FT_TEXT, 0, 0, 0, "64", 0 },
|
||||
{ "utsm", "drr_emergency_pool_kb", FT_TEXT, 0, 0, 0, "64", 0 },
|
||||
{ "utsm", "drr_recovery_log_size", FT_TEXT, 0, 0, 0, "4096", 0 },
|
||||
{ "utsm", "drr_heartbeat_interval", FT_TEXT, 0, 0, 0, "0", 0 },
|
||||
};
|
||||
#define KFIELD_COUNT (sizeof(g_kfields)/sizeof(g_kfields[0]))
|
||||
|
||||
/* ---- 网络页字段(NETCONF.CNF) ---- */
|
||||
static sfield g_nfields[] = {
|
||||
{ "network", "mode", FT_CHOICE, "dhcp|static|disabled", 0, 0, "dhcp", 0 },
|
||||
{ "network", "device", FT_TEXT, 0, 0, 0, "auto", 0 },
|
||||
{ "network", "ip", FT_TEXT, 0, 0, 0, "dhcp", 0 },
|
||||
{ "network", "gateway", FT_TEXT, 0, 0, 0, "auto", 0 },
|
||||
{ "network", "dns", FT_TEXT, 0, 0, 0, "auto", 0 },
|
||||
{ "network", "name", FT_TEXT, 0, 0, 0, "", 0 },
|
||||
{ "network", "password", FT_TEXT, 0, 0, 0, "", 0 },
|
||||
};
|
||||
#define NFIELD_COUNT (sizeof(g_nfields)/sizeof(g_nfields[0]))
|
||||
#define NF_DEVICE 1 /* g_nfields 中 device 字段索引 */
|
||||
#define NF_NAME 5 /* g_nfields 中 name 字段索引 */
|
||||
|
||||
/* ---- 全局状态 ---- */
|
||||
static da_app_context g_ac;
|
||||
static da_cursor g_cursor;
|
||||
static da_mouse g_mouse;
|
||||
|
||||
static int g_page = 0; /* 0=kernel 1=network 2=devices */
|
||||
static int g_sel = 0; /* 当前页选中字段 */
|
||||
static int g_scroll = 0; /* 当前页滚动偏移 */
|
||||
static int g_sel_saved[3] = { 0, 0, 0 };
|
||||
static int g_scroll_saved[3] = { 0, 0, 0 };
|
||||
|
||||
static char g_status[96]; /* 状态栏消息 */
|
||||
static u32 g_status_color = DA_SUCCESS;
|
||||
static int g_fuck_loaded = 0; /* FUCK 原文是否读到 */
|
||||
static int g_net_loaded = 0;
|
||||
|
||||
/* 配置文本缓冲 */
|
||||
static char g_fuck_text[12288]; /* FUCK 原文 */
|
||||
static u32 g_fuck_len = 0;
|
||||
static char g_fuck_out[16384]; /* FUCK 序列化输出 */
|
||||
static char g_conf_text[4096]; /* NETCONF 原文 */
|
||||
static char g_conf_out[2048]; /* NETCONF 序列化输出 */
|
||||
|
||||
/* ---- Net API 函数指针 ---- */
|
||||
static net_device_count_fn g_net_device_count;
|
||||
static net_device_info_fn g_net_device_info;
|
||||
static net_scan_start_fn g_net_scan_start;
|
||||
static net_scan_count_fn g_net_scan_count;
|
||||
static net_scan_result_fn g_net_scan_result;
|
||||
static net_is_wireless_fn g_net_is_wireless;
|
||||
|
||||
/* ---- 设备页状态 ---- */
|
||||
#define DEV_MAX 8
|
||||
#define SCAN_MAX 32
|
||||
|
||||
static nm_device_info g_devs[DEV_MAX];
|
||||
static int g_dev_count = 0;
|
||||
static nm_scan_result g_scans[SCAN_MAX];
|
||||
static int g_scan_count = 0;
|
||||
static int g_scan_dev_idx = -1; /* 上次扫描的设备索引 */
|
||||
static int g_net_avail = 0; /* net API 是否可用 */
|
||||
|
||||
/* 设备页 item 布局:
|
||||
* 0..g_dev_count-1 : 设备行
|
||||
* g_dev_count : 分隔线 (不可选)
|
||||
* g_dev_count+1..+g_scan_count : 扫描结果行
|
||||
* g_dev_count+g_scan_count+1 : SCAN 按钮
|
||||
* g_dev_count+g_scan_count+2 : SELECT 按钮
|
||||
*/
|
||||
#define DEV_ITEM_SCAN_BTN (-1)
|
||||
#define DEV_ITEM_SEL_BTN (-2)
|
||||
|
||||
/* ---- 小工具 ---- */
|
||||
static int s_strlen(const char *s) { int n = 0; while (s[n]) n++; return n; }
|
||||
static void s_strcpy(char *d, const char *s, int cap) {
|
||||
int i = 0;
|
||||
for (; i < cap - 1 && s[i]; i++) d[i] = s[i];
|
||||
d[i] = 0;
|
||||
}
|
||||
static int s_streq(const char *a, const char *b) {
|
||||
while (*a && *b) { if (*a != *b) return 0; a++; b++; }
|
||||
return *a == *b;
|
||||
}
|
||||
static int s_streqn(const char *a, const char *b, int n) {
|
||||
for (int i = 0; i < n; i++) { if (a[i] != b[i]) return 0; }
|
||||
return 1;
|
||||
}
|
||||
static void set_status(const char *msg, u32 color) {
|
||||
s_strcpy(g_status, msg, sizeof(g_status));
|
||||
g_status_color = color;
|
||||
}
|
||||
|
||||
/* int -> 字符串 */
|
||||
static int s_itoa(int v, char *out) {
|
||||
if (v == 0) { out[0] = '0'; out[1] = 0; return 1; }
|
||||
int neg = 0;
|
||||
if (v < 0) { neg = 1; v = -v; }
|
||||
char tmp[12]; int tl = 0;
|
||||
while (v > 0) { tmp[tl++] = (char)('0' + v % 10); v /= 10; }
|
||||
int p = 0;
|
||||
if (neg) out[p++] = '-';
|
||||
while (tl > 0) out[p++] = tmp[--tl];
|
||||
out[p] = 0;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* MAC 地址格式化: "XX:XX:XX:XX:XX:XX" */
|
||||
static void fmt_mac(const u8 mac[6], char *out) {
|
||||
static const char h[] = "0123456789ABCDEF";
|
||||
int p = 0;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (i) out[p++] = ':';
|
||||
out[p++] = h[(mac[i] >> 4) & 0xF];
|
||||
out[p++] = h[mac[i] & 0xF];
|
||||
}
|
||||
out[p] = 0;
|
||||
}
|
||||
|
||||
/* 安全类型名称 */
|
||||
static const char *sec_name(u8 sec) {
|
||||
switch (sec) {
|
||||
case NM_SEC_OPEN: return "Open";
|
||||
case NM_SEC_WEP: return "WEP";
|
||||
case NM_SEC_WPA: return "WPA";
|
||||
case NM_SEC_WPA2: return "WPA2";
|
||||
case NM_SEC_WPA3: return "WPA3";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- INI 解析(填充字段值) ---- */
|
||||
static void ini_fill_fields(const char *text, sfield *fields, int count) {
|
||||
char cur_sec[32]; cur_sec[0] = 0;
|
||||
const char *p = text;
|
||||
while (*p) {
|
||||
/* 取一行 */
|
||||
const char *ls = p;
|
||||
while (*p && *p != '\n') p++;
|
||||
int len = (int)(p - ls);
|
||||
if (*p == '\n') p++;
|
||||
/* 去尾部 \r */
|
||||
while (len > 0 && ls[len-1] == '\r') len--;
|
||||
/* 跳过前导空白 */
|
||||
int i = 0;
|
||||
while (i < len && (ls[i] == ' ' || ls[i] == '\t')) i++;
|
||||
if (i >= len) continue;
|
||||
if (ls[i] == '#' || ls[i] == ';') continue;
|
||||
if (ls[i] == '[') {
|
||||
int si = 0;
|
||||
i++;
|
||||
while (i < len && ls[i] != ']' && si < 31) cur_sec[si++] = ls[i++];
|
||||
cur_sec[si] = 0;
|
||||
continue;
|
||||
}
|
||||
/* key=value */
|
||||
int eq = -1;
|
||||
for (int j = i; j < len; j++) if (ls[j] == '=') { eq = j; break; }
|
||||
if (eq < 0) continue;
|
||||
int klen = eq - i;
|
||||
/* key 去尾部空白 */
|
||||
while (klen > 0 && (ls[i+klen-1] == ' ' || ls[i+klen-1] == '\t')) klen--;
|
||||
int vs = eq + 1;
|
||||
while (vs < len && (ls[vs] == ' ' || ls[vs] == '\t')) vs++;
|
||||
int vlen = len - vs;
|
||||
for (int f = 0; f < count; f++) {
|
||||
int skl = s_strlen(fields[f].section);
|
||||
int fkl = s_strlen(fields[f].key);
|
||||
if (skl != s_strlen(cur_sec) || !s_streqn(cur_sec, fields[f].section, skl)) continue;
|
||||
if (fkl != klen || !s_streqn(ls + i, fields[f].key, fkl)) continue;
|
||||
int n = vlen < 47 ? vlen : 47;
|
||||
for (int k = 0; k < n; k++) fields[f].value[k] = ls[vs + k];
|
||||
fields[f].value[n] = 0;
|
||||
fields[f].dirty = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- FUCK 序列化(行保留:替换已暴露键的值,缺失键追加到末尾) ---- */
|
||||
static u32 fuck_serialize(void) {
|
||||
char *out = g_fuck_out;
|
||||
u32 olen = 0;
|
||||
u8 appended[KFIELD_COUNT];
|
||||
for (int i = 0; i < (int)KFIELD_COUNT; i++) appended[i] = 0;
|
||||
|
||||
char cur_sec[32]; cur_sec[0] = 0;
|
||||
const char *p = g_fuck_text;
|
||||
const char *end = g_fuck_text + g_fuck_len;
|
||||
while (p < end) {
|
||||
const char *ls = p;
|
||||
while (p < end && *p != '\n') p++;
|
||||
int len = (int)(p - ls);
|
||||
if (p < end && *p == '\n') p++;
|
||||
int wlen = len; /* 去 \r 用于解析 */
|
||||
while (wlen > 0 && ls[wlen-1] == '\r') wlen--;
|
||||
|
||||
int replaced = 0;
|
||||
int i = 0;
|
||||
while (i < wlen && (ls[i] == ' ' || ls[i] == '\t')) i++;
|
||||
if (i < wlen && ls[i] == '[') {
|
||||
int si = 0; i++;
|
||||
while (i < wlen && ls[i] != ']' && si < 31) cur_sec[si++] = ls[i++];
|
||||
cur_sec[si] = 0;
|
||||
} else if (i < wlen && ls[i] != '#' && ls[i] != ';') {
|
||||
int eq = -1;
|
||||
for (int j = i; j < wlen; j++) if (ls[j] == '=') { eq = j; break; }
|
||||
if (eq >= 0) {
|
||||
int klen = eq - i;
|
||||
while (klen > 0 && (ls[i+klen-1] == ' ' || ls[i+klen-1] == '\t')) klen--;
|
||||
for (int f = 0; f < (int)KFIELD_COUNT; f++) {
|
||||
int skl = s_strlen(g_kfields[f].section);
|
||||
int fkl = s_strlen(g_kfields[f].key);
|
||||
if (skl != s_strlen(cur_sec) || !s_streqn(cur_sec, g_kfields[f].section, skl)) continue;
|
||||
if (fkl != klen || !s_streqn(ls + i, g_kfields[f].key, fkl)) continue;
|
||||
/* 替换为 key=value */
|
||||
int vl = s_strlen(g_kfields[f].value);
|
||||
if (olen + (u32)fkl + 1 + (u32)vl + 1 < sizeof(g_fuck_out)) {
|
||||
for (int k = 0; k < fkl; k++) out[olen++] = g_kfields[f].key[k];
|
||||
out[olen++] = '=';
|
||||
for (int k = 0; k < vl; k++) out[olen++] = g_kfields[f].value[k];
|
||||
out[olen++] = '\n';
|
||||
}
|
||||
appended[f] = 1;
|
||||
replaced = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!replaced) {
|
||||
/* 原样保留(含换行) */
|
||||
int raw = (int)(p - ls);
|
||||
if (olen + (u32)raw < sizeof(g_fuck_out)) {
|
||||
for (int k = 0; k < raw; k++) out[olen++] = ls[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 未在原文出现的 dirty 字段 -> 末尾追加(ini_get 首匹配生效,缺失键无冲突) */
|
||||
for (int f = 0; f < (int)KFIELD_COUNT; f++) {
|
||||
if (appended[f] || !g_kfields[f].dirty) continue;
|
||||
out[olen++] = '\n';
|
||||
out[olen++] = '[';
|
||||
for (const char *s = g_kfields[f].section; *s; ) out[olen++] = *s++;
|
||||
out[olen++] = ']'; out[olen++] = '\n';
|
||||
for (const char *s = g_kfields[f].key; *s; ) out[olen++] = *s++;
|
||||
out[olen++] = '=';
|
||||
for (const char *s = g_kfields[f].value; *s; ) out[olen++] = *s++;
|
||||
out[olen++] = '\n';
|
||||
}
|
||||
return olen;
|
||||
}
|
||||
|
||||
/* ---- NETCONF 序列化(全量重写,格式与 netman 解析器一致) ---- */
|
||||
static u32 conf_serialize(void) {
|
||||
static const char *order[] = { "mode", "device", "name", "password", "ip", "gateway", "dns" };
|
||||
u32 olen = 0;
|
||||
char *out = g_conf_out;
|
||||
for (int o = 0; o < 7; o++) {
|
||||
const char *k = order[o];
|
||||
const char *v = "";
|
||||
for (int f = 0; f < (int)NFIELD_COUNT; f++)
|
||||
if (s_streq(g_nfields[f].key, k)) { v = g_nfields[f].value; break; }
|
||||
const char *pfx = "network.";
|
||||
for (const char *s = pfx; *s; ) out[olen++] = *s++;
|
||||
for (const char *s = k; *s; ) out[olen++] = *s++;
|
||||
out[olen++] = '=';
|
||||
for (const char *s = v; *s; ) out[olen++] = *s++;
|
||||
out[olen++] = '\n';
|
||||
}
|
||||
return olen;
|
||||
}
|
||||
|
||||
/* ---- 磁盘加载 / 保存 ---- */
|
||||
static void load_configs(void) {
|
||||
u8 *data = 0; u32 size = 0;
|
||||
g_fuck_loaded = 0;
|
||||
if (f32_read_path_lfn("system/deshab64/FUCK", &data, &size) == 0 ||
|
||||
f32_read_path_lfn("FUCK", &data, &size) == 0) {
|
||||
if (size >= sizeof(g_fuck_text)) size = sizeof(g_fuck_text) - 1;
|
||||
for (u32 i = 0; i < size; i++) g_fuck_text[i] = (char)data[i];
|
||||
g_fuck_text[size] = 0;
|
||||
g_fuck_len = size;
|
||||
g_fuck_loaded = 1;
|
||||
ini_fill_fields(g_fuck_text, g_kfields, (int)KFIELD_COUNT);
|
||||
} else {
|
||||
g_fuck_len = 0;
|
||||
g_fuck_text[0] = 0;
|
||||
}
|
||||
|
||||
g_net_loaded = 0;
|
||||
if (f32_read_root_file("NETCONF CNF", &data, &size) == 0) {
|
||||
if (size >= sizeof(g_conf_text)) size = sizeof(g_conf_text) - 1;
|
||||
for (u32 i = 0; i < size; i++) g_conf_text[i] = (char)data[i];
|
||||
g_conf_text[size] = 0;
|
||||
g_net_loaded = 1;
|
||||
ini_fill_fields(g_conf_text, g_nfields, (int)NFIELD_COUNT);
|
||||
} else {
|
||||
g_conf_text[0] = 0;
|
||||
}
|
||||
|
||||
if (g_fuck_loaded && g_net_loaded)
|
||||
set_status("Loaded FUCK + NETCONF.CNF", DA_SUCCESS);
|
||||
else if (g_fuck_loaded)
|
||||
set_status("Loaded FUCK (NETCONF.CNF missing, defaults shown)", DA_WARNING);
|
||||
else
|
||||
set_status("Config files not found, defaults shown", DA_WARNING);
|
||||
}
|
||||
|
||||
static void save_configs(void) {
|
||||
int ok_k = 0, ok_n = 0;
|
||||
if (g_page == 0) {
|
||||
u32 olen = fuck_serialize();
|
||||
if (f32_write_path_lfn("system/deshab64/FUCK", (const u8 *)g_fuck_out, olen) == 0 ||
|
||||
f32_write_path_lfn("FUCK", (const u8 *)g_fuck_out, olen) == 0) {
|
||||
ok_k = 1;
|
||||
for (int f = 0; f < (int)KFIELD_COUNT; f++) g_kfields[f].dirty = 0;
|
||||
/* 以新内容作为后续编辑基准 */
|
||||
if (olen < sizeof(g_fuck_text)) {
|
||||
for (u32 i = 0; i < olen; i++) g_fuck_text[i] = g_fuck_out[i];
|
||||
g_fuck_len = olen;
|
||||
g_fuck_text[olen] = 0;
|
||||
g_fuck_loaded = 1;
|
||||
}
|
||||
}
|
||||
set_status(ok_k ? "FUCK saved (reboot to apply)" : "FUCK save FAILED",
|
||||
ok_k ? DA_SUCCESS : DA_ERROR);
|
||||
} else {
|
||||
/* page 1 (NETWORK) 和 page 2 (DEVICES) 都保存 NETCONF.CNF */
|
||||
u32 olen = conf_serialize();
|
||||
if (f32_write_root_file("NETCONF CNF", (const u8 *)g_conf_out, olen) == 0) {
|
||||
ok_n = 1;
|
||||
for (int f = 0; f < (int)NFIELD_COUNT; f++) g_nfields[f].dirty = 0;
|
||||
}
|
||||
set_status(ok_n ? "NETCONF.CNF saved (reboot to apply)" : "NETCONF save FAILED",
|
||||
ok_n ? DA_SUCCESS : DA_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 字段操作 ---- */
|
||||
static sfield *cur_fields(void) { return g_page == 0 ? g_kfields : g_nfields; }
|
||||
static int cur_count(void) {
|
||||
if (g_page == 2) {
|
||||
/* 设备页: 设备数 + 扫描结果数 + 2 按钮 */
|
||||
return g_dev_count + g_scan_count + 2;
|
||||
}
|
||||
return g_page == 0 ? (int)KFIELD_COUNT : (int)NFIELD_COUNT;
|
||||
}
|
||||
|
||||
static void field_cycle(sfield *f, int dir) {
|
||||
if (f->type == FT_BOOL) {
|
||||
f->value[0] = (f->value[0] == '1') ? '0' : '1';
|
||||
f->value[1] = 0;
|
||||
} else if (f->type == FT_INT) {
|
||||
int v = 0;
|
||||
for (const char *s = f->value; *s >= '0' && *s <= '9'; s++) v = v * 10 + (*s - '0');
|
||||
v += dir;
|
||||
if (v > f->max) v = f->min;
|
||||
if (v < f->min) v = f->max;
|
||||
int n = 0;
|
||||
if (v == 0) f->value[n++] = '0';
|
||||
char tmp[12]; int tl = 0;
|
||||
while (v > 0) { tmp[tl++] = (char)('0' + v % 10); v /= 10; }
|
||||
while (tl > 0) f->value[n++] = tmp[--tl];
|
||||
f->value[n] = 0;
|
||||
} else if (f->type == FT_CHOICE) {
|
||||
/* 在 "a|b|c" 中找当前项,切到下一项/上一项 */
|
||||
char opts[8][16]; int oc = 0;
|
||||
const char *s = f->choices;
|
||||
while (*s && oc < 8) {
|
||||
int l = 0;
|
||||
while (*s && *s != '|' && l < 15) opts[oc][l++] = *s++;
|
||||
opts[oc][l] = 0; oc++;
|
||||
if (*s == '|') s++;
|
||||
}
|
||||
int cur = -1;
|
||||
for (int i = 0; i < oc; i++) if (s_streq(f->value, opts[i])) { cur = i; break; }
|
||||
cur += dir;
|
||||
if (cur >= oc) cur = 0;
|
||||
if (cur < 0) cur = oc - 1;
|
||||
if (oc > 0) s_strcpy(f->value, opts[cur < 0 ? 0 : cur], sizeof(f->value));
|
||||
}
|
||||
f->dirty = 1;
|
||||
}
|
||||
|
||||
/* ---- Net API 初始化 ---- */
|
||||
static void init_net_api(const da_boot_context *ctx) {
|
||||
g_net_device_count = 0;
|
||||
g_net_device_info = 0;
|
||||
g_net_scan_start = 0;
|
||||
g_net_scan_count = 0;
|
||||
g_net_scan_result = 0;
|
||||
g_net_is_wireless = 0;
|
||||
g_net_avail = 0;
|
||||
|
||||
u64 api = ctx->dkm_kernel_api;
|
||||
if (!api) return;
|
||||
u64 net_api = *(u64 *)(api + 0x48);
|
||||
if (!net_api) return;
|
||||
|
||||
g_net_device_count = (net_device_count_fn)*(u64 *)(net_api + 8);
|
||||
g_net_device_info = (net_device_info_fn)*(u64 *)(net_api + 16);
|
||||
g_net_scan_start = (net_scan_start_fn)*(u64 *)(net_api + 40);
|
||||
g_net_scan_count = (net_scan_count_fn)*(u64 *)(net_api + 48);
|
||||
g_net_scan_result = (net_scan_result_fn)*(u64 *)(net_api + 56);
|
||||
g_net_is_wireless = (net_is_wireless_fn)*(u64 *)(net_api + 64);
|
||||
g_net_avail = 1;
|
||||
}
|
||||
|
||||
/* ---- 设备枚举 ---- */
|
||||
static void query_devices(void) {
|
||||
g_dev_count = 0;
|
||||
if (!g_net_device_count || !g_net_device_info) return;
|
||||
u32 count = g_net_device_count();
|
||||
if (count > DEV_MAX) count = DEV_MAX;
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
nm_device_info info;
|
||||
da_memset(&info, 0, sizeof(info));
|
||||
if (g_net_device_info(i, &info) != 0) continue;
|
||||
/* 复制到本地 */
|
||||
g_devs[g_dev_count] = info;
|
||||
g_dev_count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- WiFi 扫描 ---- */
|
||||
static void trigger_scan(void) {
|
||||
if (!g_net_avail || !g_net_is_wireless || !g_net_scan_start) {
|
||||
set_status("Net API or WiFi scan not available", DA_ERROR);
|
||||
return;
|
||||
}
|
||||
if (g_sel < 0 || g_sel >= g_dev_count) {
|
||||
/* 如果当前不在设备行,尝试用上次选中的设备 */
|
||||
if (g_dev_count == 0) { set_status("No devices", DA_ERROR); return; }
|
||||
}
|
||||
/* 找到选中的无线设备 */
|
||||
int dev_idx = g_sel;
|
||||
if (dev_idx >= g_dev_count) dev_idx = 0;
|
||||
int wl = g_net_is_wireless(dev_idx);
|
||||
if (!wl) {
|
||||
/* 搜索第一个无线设备 */
|
||||
int found = -1;
|
||||
for (int i = 0; i < g_dev_count; i++) {
|
||||
if (g_net_is_wireless(i)) { found = i; break; }
|
||||
}
|
||||
if (found < 0) { set_status("No wireless device found", DA_WARNING); return; }
|
||||
dev_idx = found;
|
||||
}
|
||||
|
||||
set_status("Scanning WiFi...", DA_ACCENT_LIGHT);
|
||||
int rc = g_net_scan_start(dev_idx);
|
||||
if (rc != 0) {
|
||||
set_status("WiFi scan failed", DA_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
g_scan_count = 0;
|
||||
if (g_net_scan_count) {
|
||||
int sc = g_net_scan_count(dev_idx);
|
||||
if (sc > SCAN_MAX) sc = SCAN_MAX;
|
||||
for (int i = 0; i < sc; i++) {
|
||||
if (g_net_scan_result(dev_idx, i, &g_scans[g_scan_count]) == 0)
|
||||
g_scan_count++;
|
||||
}
|
||||
}
|
||||
g_scan_dev_idx = dev_idx;
|
||||
|
||||
char msg[64];
|
||||
s_strcpy(msg, "Scan found ", sizeof(msg));
|
||||
char nb[12]; s_itoa(g_scan_count, nb);
|
||||
int ml = s_strlen(msg);
|
||||
for (int i = 0; nb[i] && ml < 60; i++) msg[ml++] = nb[i];
|
||||
const char *suf = " networks";
|
||||
for (const char *s = suf; *s && ml < 62; ) msg[ml++] = *s++;
|
||||
msg[ml] = 0;
|
||||
set_status(msg, g_scan_count > 0 ? DA_SUCCESS : DA_WARNING);
|
||||
}
|
||||
|
||||
/* ---- 设备选择(写入 NETCONF.CNF device 字段) ---- */
|
||||
static void select_device(int idx) {
|
||||
if (idx < 0 || idx >= g_dev_count) return;
|
||||
if (!g_devs[idx].name) return;
|
||||
s_strcpy(g_nfields[NF_DEVICE].value, g_devs[idx].name, sizeof(g_nfields[NF_DEVICE].value));
|
||||
g_nfields[NF_DEVICE].dirty = 1;
|
||||
char msg[80];
|
||||
s_strcpy(msg, "Device set to ", sizeof(msg));
|
||||
int ml = s_strlen(msg);
|
||||
const char *nm = g_devs[idx].name;
|
||||
for (const char *s = nm; *s && ml < 78; ) msg[ml++] = *s++;
|
||||
const char *suf = " (F5 to save)";
|
||||
for (const char *s = suf; *s && ml < 79; ) msg[ml++] = *s++;
|
||||
msg[ml] = 0;
|
||||
set_status(msg, DA_SUCCESS);
|
||||
}
|
||||
|
||||
/* ---- SSID 选择(写入 NETCONF.CNF name 字段) ---- */
|
||||
static void select_ssid(int idx) {
|
||||
if (idx < 0 || idx >= g_scan_count) return;
|
||||
s_strcpy(g_nfields[NF_NAME].value, g_scans[idx].ssid, sizeof(g_nfields[NF_NAME].value));
|
||||
g_nfields[NF_NAME].dirty = 1;
|
||||
char msg[80];
|
||||
s_strcpy(msg, "SSID set to ", sizeof(msg));
|
||||
int ml = s_strlen(msg);
|
||||
for (const char *s = g_scans[idx].ssid; *s && ml < 78; ) msg[ml++] = *s++;
|
||||
const char *suf = " (F5 to save)";
|
||||
for (const char *s = suf; *s && ml < 79; ) msg[ml++] = *s++;
|
||||
msg[ml] = 0;
|
||||
set_status(msg, DA_SUCCESS);
|
||||
}
|
||||
|
||||
/* ---- 布局 ---- */
|
||||
#define TAB_Y (DA_TITLEBAR_H + 4)
|
||||
#define TAB_H 26
|
||||
#define TAB_W 130
|
||||
#define FIELD_Y0 (TAB_Y + TAB_H + 10)
|
||||
#define ROW_H 24
|
||||
#define LABEL_X 20
|
||||
#define VALUE_X 320
|
||||
#define VALUE_W 300
|
||||
#define VALUE_H 20
|
||||
#define BTN_Y_OFF 44 /* 底部按钮距状态栏高度 */
|
||||
#define BTN_W 110
|
||||
#define BTN_H 26
|
||||
|
||||
static int visible_rows(void) {
|
||||
int h = (int)g_ac.fb_h - FIELD_Y0 - BTN_Y_OFF - DA_STATUSBAR_H - 8;
|
||||
int r = h / ROW_H;
|
||||
return r < 1 ? 1 : r;
|
||||
}
|
||||
|
||||
static void ensure_visible(void) {
|
||||
int vr = visible_rows();
|
||||
if (g_sel < g_scroll) g_scroll = g_sel;
|
||||
if (g_sel >= g_scroll + vr) g_scroll = g_sel - vr + 1;
|
||||
}
|
||||
|
||||
/* ---- 绘制 ---- */
|
||||
static void draw_tab(int idx, const char *label, int x) {
|
||||
int active = (g_page == idx);
|
||||
u32 bg = active ? DA_BG_TERTIARY : DA_BG_SECONDARY;
|
||||
u32 fg = active ? DA_ACCENT_LIGHT : DA_TEXT_DIM;
|
||||
da_fill_rounded_rect(&g_ac, x, TAB_Y, TAB_W - 8, TAB_H, bg, 6);
|
||||
da_rect_outline(&g_ac, x, TAB_Y, TAB_W - 8, TAB_H, active ? DA_BORDER_FOCUS : DA_BORDER, 6);
|
||||
int lx = x + 14;
|
||||
da_draw_string(&g_ac, label, lx, TAB_Y + 4, fg, bg, 12);
|
||||
}
|
||||
|
||||
static void draw_field_row(int fi, int row_y) {
|
||||
sfield *f = &cur_fields()[fi];
|
||||
int selected = (fi == g_sel);
|
||||
i64 fw = (i64)g_ac.fb_w - 40;
|
||||
if (selected)
|
||||
da_fill_rect(&g_ac, LABEL_X - 4, row_y - 2, fw, ROW_H, DA_BG_SECONDARY);
|
||||
|
||||
/* 标签: section/key */
|
||||
char label[64];
|
||||
int lp = 0;
|
||||
for (const char *s = f->section; *s && lp < 60; ) label[lp++] = *s++;
|
||||
label[lp++] = '/';
|
||||
for (const char *s = f->key; *s && lp < 62; ) label[lp++] = *s++;
|
||||
label[lp] = 0;
|
||||
da_draw_string(&g_ac, label, LABEL_X, row_y + 1, selected ? DA_TEXT_PRIMARY : DA_TEXT_DIM,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12);
|
||||
|
||||
/* 值框 */
|
||||
u32 vbg = selected ? DA_BG_TERTIARY : DA_BG_SECONDARY;
|
||||
u32 vborder = selected ? DA_BORDER_FOCUS : DA_BORDER;
|
||||
da_fill_rect(&g_ac, VALUE_X, row_y, VALUE_W, VALUE_H, vbg);
|
||||
da_rect_outline(&g_ac, VALUE_X, row_y, VALUE_W, VALUE_H, vborder, 2);
|
||||
|
||||
char disp[56];
|
||||
int dp = 0;
|
||||
if (f->type == FT_BOOL || f->type == FT_CHOICE || f->type == FT_INT) {
|
||||
disp[dp++] = '<'; disp[dp++] = ' ';
|
||||
for (const char *s = f->value; *s && dp < 52; ) disp[dp++] = *s++;
|
||||
disp[dp++] = ' '; disp[dp++] = '>';
|
||||
} else {
|
||||
for (const char *s = f->value; *s && dp < 52; ) disp[dp++] = *s++;
|
||||
}
|
||||
disp[dp] = 0;
|
||||
u32 vfg;
|
||||
if (f->type == FT_BOOL)
|
||||
vfg = (f->value[0] == '1') ? DA_SUCCESS : DA_ERROR;
|
||||
else
|
||||
vfg = DA_ACCENT_LIGHT;
|
||||
da_draw_string(&g_ac, disp, VALUE_X + 8, row_y + 1, vfg, vbg, 12);
|
||||
|
||||
/* 文本字段编辑光标 */
|
||||
if (selected && f->type == FT_TEXT) {
|
||||
int vl = s_strlen(f->value);
|
||||
i64 cx = VALUE_X + 8 + (i64)vl * 12;
|
||||
if (cx < VALUE_X + VALUE_W - 12)
|
||||
da_fill_rect(&g_ac, cx, row_y + VALUE_H - 4, 11, 2, DA_ACCENT);
|
||||
}
|
||||
/* dirty 标记 */
|
||||
if (f->dirty)
|
||||
da_draw_char(&g_ac, '*', VALUE_X + VALUE_W + 6, row_y + 1, DA_WARNING,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY);
|
||||
}
|
||||
|
||||
/* ---- 设备页绘制 ---- */
|
||||
static int dev_item_count(void) {
|
||||
return g_dev_count + g_scan_count + 2;
|
||||
}
|
||||
|
||||
/* 返回设备页第 sel 项的类型和索引 */
|
||||
static void dev_item_info(int sel, int *type, int *idx) {
|
||||
if (sel < g_dev_count) { *type = 0; *idx = sel; return; } /* 设备 */
|
||||
int scan_base = g_dev_count + 1; /* +1 跳过分隔线 */
|
||||
if (sel >= scan_base && sel < scan_base + g_scan_count) { *type = 1; *idx = sel - scan_base; return; } /* 扫描 */
|
||||
int btn_base = scan_base + g_scan_count;
|
||||
if (sel == btn_base) { *type = 2; *idx = 0; return; } /* SCAN */
|
||||
if (sel == btn_base + 1) { *type = 3; *idx = 0; return; } /* SELECT */
|
||||
*type = -1; *idx = -1;
|
||||
}
|
||||
|
||||
static void draw_device_row(int idx, int row_y, int selected) {
|
||||
nm_device_info *d = &g_devs[idx];
|
||||
if (selected)
|
||||
da_fill_rect(&g_ac, LABEL_X - 4, row_y - 2, (i64)g_ac.fb_w - 40, ROW_H, DA_BG_SECONDARY);
|
||||
|
||||
/* 索引 */
|
||||
char idx_s[8]; s_itoa(idx, idx_s);
|
||||
da_draw_string(&g_ac, "[", LABEL_X, row_y + 1,
|
||||
selected ? DA_TEXT_PRIMARY : DA_TEXT_DIM,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12);
|
||||
da_draw_string(&g_ac, idx_s, LABEL_X + 12, row_y + 1,
|
||||
selected ? DA_TEXT_PRIMARY : DA_TEXT_DIM,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12);
|
||||
da_draw_string(&g_ac, "]", LABEL_X + 24, row_y + 1,
|
||||
selected ? DA_TEXT_PRIMARY : DA_TEXT_DIM,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12);
|
||||
|
||||
/* 名称 */
|
||||
int nx = LABEL_X + 40;
|
||||
const char *nm = d->name ? d->name : "<noname>";
|
||||
u32 name_fg = DA_ACCENT_LIGHT;
|
||||
if (d->flags & NM_NET_F_WIRELESS) name_fg = DA_WARNING;
|
||||
da_draw_string(&g_ac, nm, nx, row_y + 1, name_fg,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12);
|
||||
|
||||
/* MAC */
|
||||
char mac_s[20]; fmt_mac(d->mac, mac_s);
|
||||
int mac_x = LABEL_X + 200;
|
||||
da_draw_string(&g_ac, mac_s, mac_x, row_y + 1, DA_TEXT_DIM,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12);
|
||||
|
||||
/* 状态标志 */
|
||||
int sx = mac_x + 180;
|
||||
if (d->flags & NM_NET_F_LINK_UP)
|
||||
da_draw_string(&g_ac, "LINK", sx, row_y + 1, DA_SUCCESS,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12);
|
||||
else
|
||||
da_draw_string(&g_ac, "down", sx, row_y + 1, DA_ERROR,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12);
|
||||
sx += 60;
|
||||
if (d->flags & NM_NET_F_TX_READY)
|
||||
da_draw_string(&g_ac, "TX", sx, row_y + 1, DA_SUCCESS,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12);
|
||||
sx += 30;
|
||||
if (d->flags & NM_NET_F_RX_READY)
|
||||
da_draw_string(&g_ac, "RX", sx, row_y + 1, DA_SUCCESS,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12);
|
||||
sx += 30;
|
||||
if (d->flags & NM_NET_F_WIRELESS)
|
||||
da_draw_string(&g_ac, "WiFi", sx, row_y + 1, DA_WARNING,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12);
|
||||
|
||||
/* 当前选中设备标记 */
|
||||
if (d->name && s_streq(g_nfields[NF_DEVICE].value, d->name))
|
||||
da_draw_char(&g_ac, '*', (i64)g_ac.fb_w - 30, row_y + 1, DA_SUCCESS,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY);
|
||||
}
|
||||
|
||||
static void draw_scan_row(int idx, int row_y, int selected) {
|
||||
nm_scan_result *r = &g_scans[idx];
|
||||
if (selected)
|
||||
da_fill_rect(&g_ac, LABEL_X - 4, row_y - 2, (i64)g_ac.fb_w - 40, ROW_H, DA_BG_SECONDARY);
|
||||
|
||||
/* SSID */
|
||||
const char *ssid = r->ssid[0] ? r->ssid : "<hidden>";
|
||||
da_draw_string(&g_ac, ssid, LABEL_X, row_y + 1,
|
||||
selected ? DA_TEXT_PRIMARY : DA_ACCENT_LIGHT,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12);
|
||||
|
||||
/* RSSI */
|
||||
char rssi_s[12];
|
||||
rssi_s[0] = 0;
|
||||
s_itoa(r->rssi, rssi_s);
|
||||
int rx = LABEL_X + 260;
|
||||
da_draw_string(&g_ac, rssi_s, rx, row_y + 1,
|
||||
r->rssi > -50 ? DA_SUCCESS : (r->rssi > -70 ? DA_WARNING : DA_ERROR),
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12);
|
||||
da_draw_string(&g_ac, "dBm", rx + 40, row_y + 1, DA_TEXT_DIM,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12);
|
||||
|
||||
/* 信道 */
|
||||
char ch_s[8]; s_itoa(r->channel, ch_s);
|
||||
da_draw_string(&g_ac, "ch", rx + 90, row_y + 1, DA_TEXT_DIM,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12);
|
||||
da_draw_string(&g_ac, ch_s, rx + 110, row_y + 1, DA_TEXT_DIM,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12);
|
||||
|
||||
/* 加密 */
|
||||
da_draw_string(&g_ac, sec_name(r->security), rx + 140, row_y + 1,
|
||||
r->security == NM_SEC_OPEN ? DA_SUCCESS : DA_WARNING,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY, 12);
|
||||
|
||||
/* 当前选中 SSID 标记 */
|
||||
if (r->ssid[0] && s_streq(g_nfields[NF_NAME].value, r->ssid))
|
||||
da_draw_char(&g_ac, '*', (i64)g_ac.fb_w - 30, row_y + 1, DA_SUCCESS,
|
||||
selected ? DA_BG_SECONDARY : DA_BG_PRIMARY);
|
||||
}
|
||||
|
||||
static void draw_device_button(int x, int y, const char *label, int selected) {
|
||||
u32 bg = selected ? DA_BG_TERTIARY : DA_BG_SECONDARY;
|
||||
u32 border = selected ? DA_BORDER_FOCUS : DA_BORDER;
|
||||
da_fill_rounded_rect(&g_ac, x, y, BTN_W, BTN_H, bg, 6);
|
||||
da_rect_outline(&g_ac, x, y, BTN_W, BTN_H, border, 6);
|
||||
int ll = s_strlen(label);
|
||||
da_draw_string(&g_ac, label, x + (BTN_W - ll * 12) / 2, y + 4,
|
||||
DA_ACCENT_LIGHT, bg, 12);
|
||||
}
|
||||
|
||||
static void draw_device_page(void) {
|
||||
int vr = visible_rows();
|
||||
int count = dev_item_count();
|
||||
|
||||
/* 列表 */
|
||||
for (int r = 0; r < vr; r++) {
|
||||
int item = g_scroll + r;
|
||||
if (item >= count) break;
|
||||
int row_y = FIELD_Y0 + r * ROW_H;
|
||||
|
||||
int type, idx;
|
||||
dev_item_info(item, &type, &idx);
|
||||
|
||||
if (type == 0) {
|
||||
draw_device_row(idx, row_y, item == g_sel);
|
||||
} else if (type == 1) {
|
||||
draw_scan_row(idx, row_y, item == g_sel);
|
||||
} else if (type == 2) {
|
||||
draw_device_button(LABEL_X, row_y, "SCAN (S)", item == g_sel);
|
||||
} else if (type == 3) {
|
||||
draw_device_button(LABEL_X + BTN_W + 12, row_y, "SELECT", item == g_sel);
|
||||
}
|
||||
}
|
||||
|
||||
/* 分隔线: 设备列表和扫描结果之间 */
|
||||
int sep_item = g_dev_count; /* 分隔线位置 */
|
||||
int sep_row = sep_item - g_scroll;
|
||||
if (sep_row >= 0 && sep_row < vr) {
|
||||
int sy = FIELD_Y0 + sep_row * ROW_H + ROW_H / 2;
|
||||
da_fill_rect(&g_ac, LABEL_X, sy, (i64)g_ac.fb_w - 40, 1, DA_BORDER);
|
||||
const char *hdr = (g_scan_count > 0) ? "--- WiFi Scan Results ---" : "--- WiFi Scan (press S) ---";
|
||||
int hx = LABEL_X + 20;
|
||||
da_draw_string(&g_ac, hdr, hx, sy - 8, DA_TEXT_DIM, DA_BG_PRIMARY, 12);
|
||||
}
|
||||
|
||||
/* 滚动指示 */
|
||||
if (count > vr) {
|
||||
char info[32];
|
||||
int ip = 0;
|
||||
int a = g_scroll + 1, b = g_scroll + vr > count ? count : g_scroll + vr;
|
||||
char nb[12]; int nl;
|
||||
nl = 0; while (a) { nb[nl++] = (char)('0' + a % 10); a /= 10; }
|
||||
while (nl > 0) info[ip++] = nb[--nl];
|
||||
info[ip++] = '-';
|
||||
nl = 0; if (b == 0) nb[nl++] = '0'; while (b) { nb[nl++] = (char)('0' + b % 10); b /= 10; }
|
||||
while (nl > 0) info[ip++] = nb[--nl];
|
||||
info[ip++] = '/';
|
||||
nl = 0; { int c2 = count; if (c2 == 0) nb[nl++] = '0'; while (c2) { nb[nl++] = (char)('0' + c2 % 10); c2 /= 10; } }
|
||||
while (nl > 0) info[ip++] = nb[--nl];
|
||||
info[ip] = 0;
|
||||
da_draw_string(&g_ac, info, (i64)g_ac.fb_w - 120, TAB_Y + 6, DA_TEXT_DIM, DA_BG_PRIMARY, 12);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_button(int x, int y, const char *label, int hot) {
|
||||
u32 bg = hot ? DA_BG_TERTIARY : DA_BG_SECONDARY;
|
||||
da_fill_rounded_rect(&g_ac, x, y, BTN_W, BTN_H, bg, 6);
|
||||
da_rect_outline(&g_ac, x, y, BTN_W, BTN_H, DA_BORDER_FOCUS, 6);
|
||||
int ll = s_strlen(label);
|
||||
da_draw_string(&g_ac, label, x + (BTN_W - ll * 12) / 2, y + 4, DA_ACCENT_LIGHT, bg, 12);
|
||||
}
|
||||
|
||||
static void redraw_all(void) {
|
||||
da_cursor_restore(&g_ac, &g_cursor);
|
||||
da_fill_bg(&g_ac, DA_BG_PRIMARY);
|
||||
da_draw_titlebar(&g_ac, "Settings", (i64)g_ac.fb_w);
|
||||
|
||||
draw_tab(0, "KERNEL", 16);
|
||||
draw_tab(1, "NETWORK", 16 + TAB_W);
|
||||
draw_tab(2, "DEVICES", 16 + 2 * TAB_W);
|
||||
|
||||
if (g_page < 2) {
|
||||
/* 字段列表 */
|
||||
int vr = visible_rows();
|
||||
int count = cur_count();
|
||||
for (int r = 0; r < vr; r++) {
|
||||
int fi = g_scroll + r;
|
||||
if (fi >= count) break;
|
||||
draw_field_row(fi, FIELD_Y0 + r * ROW_H);
|
||||
}
|
||||
/* 滚动指示 */
|
||||
if (count > vr) {
|
||||
char info[32];
|
||||
int ip = 0;
|
||||
int a = g_scroll + 1, b = g_scroll + vr > count ? count : g_scroll + vr;
|
||||
char nb[12]; int nl;
|
||||
nl = 0; while (a) { nb[nl++] = (char)('0' + a % 10); a /= 10; }
|
||||
while (nl > 0) info[ip++] = nb[--nl];
|
||||
info[ip++] = '-';
|
||||
nl = 0; if (b == 0) nb[nl++] = '0'; while (b) { nb[nl++] = (char)('0' + b % 10); b /= 10; }
|
||||
while (nl > 0) info[ip++] = nb[--nl];
|
||||
info[ip++] = '/';
|
||||
nl = 0; { int c2 = count; if (c2 == 0) nb[nl++] = '0'; while (c2) { nb[nl++] = (char)('0' + c2 % 10); c2 /= 10; } }
|
||||
while (nl > 0) info[ip++] = nb[--nl];
|
||||
info[ip] = 0;
|
||||
da_draw_string(&g_ac, info, (i64)g_ac.fb_w - 120, TAB_Y + 6, DA_TEXT_DIM, DA_BG_PRIMARY, 12);
|
||||
}
|
||||
} else {
|
||||
/* 设备页 */
|
||||
if (!g_net_avail) {
|
||||
da_draw_string(&g_ac, "Net API not available (no kernel network support)",
|
||||
LABEL_X, FIELD_Y0, DA_WARNING, DA_BG_PRIMARY, 12);
|
||||
} else if (g_dev_count == 0) {
|
||||
da_draw_string(&g_ac, "No network devices found",
|
||||
LABEL_X, FIELD_Y0, DA_WARNING, DA_BG_PRIMARY, 12);
|
||||
} else {
|
||||
draw_device_page();
|
||||
}
|
||||
}
|
||||
|
||||
/* 底部按钮 + 状态消息 */
|
||||
int by = (int)g_ac.fb_h - DA_STATUSBAR_H - BTN_Y_OFF + 8;
|
||||
draw_button(20, by, "SAVE (F5)", 0);
|
||||
draw_button(20 + BTN_W + 12, by, "RELOAD", 0);
|
||||
if (g_status[0])
|
||||
da_draw_string(&g_ac, g_status, 20 + 2 * (BTN_W + 12) + 8, by + 4,
|
||||
g_status_color, DA_BG_PRIMARY, 12);
|
||||
|
||||
/* 状态栏 */
|
||||
const char *hint;
|
||||
if (g_page == 0)
|
||||
hint = "F1 Kernel | F2 Network | F3 Devices | F5 Save | F6 Reload | Esc Quit | reboot to apply";
|
||||
else if (g_page == 1)
|
||||
hint = "F1 Kernel | F2 Network | F3 Devices | F5 Save | F6 Reload | Esc Quit | netman reads at boot";
|
||||
else
|
||||
hint = "F1 Kernel | F2 Network | F3 Devices | S Scan | Enter Select | F5 Save | Esc Quit";
|
||||
da_draw_statusbar(&g_ac, hint, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
|
||||
|
||||
da_cursor_save(&g_ac, &g_cursor);
|
||||
da_cursor_draw(&g_ac, &g_cursor, DA_CURSOR_COLOR);
|
||||
}
|
||||
|
||||
/* ---- 页面切换 ---- */
|
||||
static void switch_page(int p) {
|
||||
if (p == g_page) return;
|
||||
g_sel_saved[g_page] = g_sel;
|
||||
g_scroll_saved[g_page] = g_scroll;
|
||||
g_page = p;
|
||||
g_sel = g_sel_saved[p];
|
||||
g_scroll = g_scroll_saved[p];
|
||||
ensure_visible();
|
||||
}
|
||||
|
||||
/* ---- 鼠标点击 ---- */
|
||||
static void handle_click(int mx, int my) {
|
||||
/* tab 区 */
|
||||
if (my >= TAB_Y && my < TAB_Y + TAB_H) {
|
||||
if (mx >= 16 && mx < 16 + TAB_W - 8) { switch_page(0); return; }
|
||||
if (mx >= 16 + TAB_W && mx < 16 + 2 * TAB_W - 8) { switch_page(1); return; }
|
||||
if (mx >= 16 + 2 * TAB_W && mx < 16 + 3 * TAB_W - 8) { switch_page(2); return; }
|
||||
}
|
||||
|
||||
if (g_page < 2) {
|
||||
/* 字段区 */
|
||||
if (my >= FIELD_Y0) {
|
||||
int row = (my - FIELD_Y0) / ROW_H;
|
||||
int fi = g_scroll + row;
|
||||
if (fi >= 0 && fi < cur_count() && row < visible_rows()) {
|
||||
g_sel = fi;
|
||||
sfield *f = &cur_fields()[fi];
|
||||
if (mx >= VALUE_X && mx < VALUE_X + VALUE_W && f->type != FT_TEXT)
|
||||
field_cycle(f, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* 设备页 */
|
||||
if (my >= FIELD_Y0) {
|
||||
int row = (my - FIELD_Y0) / ROW_H;
|
||||
int item = g_scroll + row;
|
||||
if (item >= 0 && item < dev_item_count() && row < visible_rows()) {
|
||||
g_sel = item;
|
||||
int type, idx;
|
||||
dev_item_info(item, &type, &idx);
|
||||
if (type == 0) {
|
||||
select_device(idx);
|
||||
} else if (type == 1) {
|
||||
select_ssid(idx);
|
||||
} else if (type == 2) {
|
||||
trigger_scan();
|
||||
} else if (type == 3) {
|
||||
/* SELECT 按钮: 选择当前选中设备 */
|
||||
if (g_sel < g_dev_count) select_device(g_sel);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 按钮区 */
|
||||
int by = (int)g_ac.fb_h - DA_STATUSBAR_H - BTN_Y_OFF + 8;
|
||||
if (my >= by && my < by + BTN_H) {
|
||||
if (mx >= 20 && mx < 20 + BTN_W) { save_configs(); return; }
|
||||
if (mx >= 20 + BTN_W + 12 && mx < 20 + 2 * BTN_W + 12) {
|
||||
load_configs();
|
||||
if (g_page == 2) query_devices();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 主入口 ---- */
|
||||
__attribute__((visibility("default")))
|
||||
void dsk_entry(const da_boot_context *ctx) {
|
||||
__asm__ volatile("cli");
|
||||
da_slog("settings", "boot");
|
||||
|
||||
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
|
||||
da_slog("settings", "bad context");
|
||||
for (;;) __asm__("hlt");
|
||||
}
|
||||
|
||||
da_init(&g_ac, ctx);
|
||||
if (!g_ac.block_read || !g_ac.block_write) {
|
||||
da_slog("settings", "block api missing");
|
||||
for (;;) __asm__("hlt");
|
||||
}
|
||||
f32_init((f32_block_read_fn)g_ac.block_read, (f32_block_write_fn)g_ac.block_write);
|
||||
|
||||
/* 初始化 Net API */
|
||||
init_net_api(ctx);
|
||||
if (g_net_avail) query_devices();
|
||||
|
||||
g_status[0] = 0;
|
||||
load_configs();
|
||||
|
||||
da_mouse_init();
|
||||
g_mouse.idx = 0; g_mouse.has_pkt = 0;
|
||||
da_cursor_init(&g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
|
||||
|
||||
int shift = 0, e0 = 0;
|
||||
int prev_btn = 0;
|
||||
redraw_all();
|
||||
|
||||
for (;;) {
|
||||
/* 鼠标 */
|
||||
if (da_mouse_poll(&g_mouse, &g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h)) {
|
||||
if (g_cursor.btn && !prev_btn)
|
||||
handle_click(g_cursor.mx, g_cursor.my);
|
||||
prev_btn = g_cursor.btn;
|
||||
redraw_all();
|
||||
continue;
|
||||
}
|
||||
/* 键盘 */
|
||||
u8 st = inb(0x64);
|
||||
if (!(st & 1)) { __asm__("pause"); continue; }
|
||||
if (st & 0x20) continue; /* AUX 数据留给鼠标路径 */
|
||||
u8 sc = inb(0x60);
|
||||
if (sc == 0xE0) { e0 = 1; continue; }
|
||||
if (sc == 0x2A || sc == 0x36) { shift = 1; continue; }
|
||||
if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; }
|
||||
if (sc & 0x80) { e0 = 0; continue; }
|
||||
|
||||
if (sc == 0x01 && !e0) { /* Esc */
|
||||
da_slog("settings", "exit");
|
||||
da_cursor_restore(&g_ac, &g_cursor);
|
||||
return;
|
||||
}
|
||||
if (!e0 && sc == 0x3B) { switch_page(0); redraw_all(); continue; } /* F1 */
|
||||
if (!e0 && sc == 0x3C) { switch_page(1); redraw_all(); continue; } /* F2 */
|
||||
if (!e0 && sc == 0x3D) { switch_page(2); redraw_all(); continue; } /* F3 */
|
||||
if (!e0 && sc == 0x0F) { switch_page((g_page + 1) % 3); redraw_all(); continue; } /* Tab */
|
||||
if (!e0 && sc == 0x3F) { save_configs(); redraw_all(); continue; } /* F5 */
|
||||
if (!e0 && sc == 0x40) { load_configs(); if (g_page == 2) query_devices(); redraw_all(); continue; } /* F6 */
|
||||
|
||||
int count = cur_count();
|
||||
if (e0) {
|
||||
if (sc == 0x48) { /* Up */
|
||||
if (g_sel > 0) g_sel--;
|
||||
ensure_visible();
|
||||
} else if (sc == 0x50) { /* Down */
|
||||
if (g_sel < count - 1) g_sel++;
|
||||
ensure_visible();
|
||||
} else if (sc == 0x4B) { /* Left */
|
||||
if (g_page < 2) {
|
||||
sfield *f = &cur_fields()[g_sel];
|
||||
if (f->type != FT_TEXT) field_cycle(f, -1);
|
||||
}
|
||||
} else if (sc == 0x4D) { /* Right */
|
||||
if (g_page < 2) {
|
||||
sfield *f = &cur_fields()[g_sel];
|
||||
if (f->type != FT_TEXT) field_cycle(f, 1);
|
||||
}
|
||||
} else if (sc == 0x47) { g_sel = 0; ensure_visible(); } /* Home */
|
||||
else if (sc == 0x4F) { g_sel = count - 1; ensure_visible(); } /* End */
|
||||
else if (sc == 0x49) { /* PgUp */
|
||||
g_sel -= visible_rows();
|
||||
if (g_sel < 0) g_sel = 0;
|
||||
ensure_visible();
|
||||
} else if (sc == 0x51) { /* PgDn */
|
||||
g_sel += visible_rows();
|
||||
if (g_sel > count - 1) g_sel = count - 1;
|
||||
ensure_visible();
|
||||
}
|
||||
e0 = 0;
|
||||
redraw_all();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 设备页特殊键 */
|
||||
if (g_page == 2) {
|
||||
if (sc == 0x1C || sc == 0x39) { /* Enter / Space */
|
||||
int type, idx;
|
||||
dev_item_info(g_sel, &type, &idx);
|
||||
if (type == 0) select_device(idx);
|
||||
else if (type == 1) select_ssid(idx);
|
||||
else if (type == 2) trigger_scan();
|
||||
else if (type == 3) {
|
||||
if (g_sel < g_dev_count) select_device(g_sel);
|
||||
}
|
||||
redraw_all();
|
||||
continue;
|
||||
}
|
||||
/* 'S' 键扫描 */
|
||||
if (sc == 0x1F) { /* S */
|
||||
trigger_scan();
|
||||
redraw_all();
|
||||
continue;
|
||||
}
|
||||
/* 其他键在设备页不处理 */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 字段页键盘输入 */
|
||||
sfield *f = &cur_fields()[g_sel];
|
||||
if (sc == 0x1C || sc == 0x39) { /* Enter / Space */
|
||||
if (f->type != FT_TEXT) field_cycle(f, 1);
|
||||
redraw_all();
|
||||
continue;
|
||||
}
|
||||
if (sc == 0x0E) { /* Backspace */
|
||||
if (f->type == FT_TEXT) {
|
||||
int l = s_strlen(f->value);
|
||||
if (l > 0) { f->value[l-1] = 0; f->dirty = 1; }
|
||||
}
|
||||
redraw_all();
|
||||
continue;
|
||||
}
|
||||
char c = da_scan_to_ascii(sc, shift);
|
||||
if (c && c >= 32 && c <= 126 && f->type == FT_TEXT) {
|
||||
int l = s_strlen(f->value);
|
||||
if (l < 46) {
|
||||
f->value[l] = c;
|
||||
f->value[l+1] = 0;
|
||||
f->dirty = 1;
|
||||
}
|
||||
redraw_all();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
CC := clang
|
||||
LD := ld.lld
|
||||
|
||||
ROOT := ../../..
|
||||
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/sysinfo.elf
|
||||
|
||||
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I../../sdk/include -O2
|
||||
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
|
||||
|
||||
C_SOURCES := main.c
|
||||
OBJECTS := $(C_SOURCES:.c=.o)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(OUT)
|
||||
|
||||
$(OUT): $(OBJECTS) linker.ld
|
||||
@mkdir -p "$(dir $(OUT))"
|
||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
|
||||
@@ -0,0 +1,26 @@
|
||||
ENTRY(dsk_entry)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FLAGS(5);
|
||||
rodata PT_LOAD FLAGS(4);
|
||||
data PT_LOAD FLAGS(6);
|
||||
dynamic PT_DYNAMIC FLAGS(4);
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x1000;
|
||||
.text : ALIGN(4K) { *(.text*) } :text
|
||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
||||
.data : ALIGN(4K) {
|
||||
*(.data*)
|
||||
*(.data.rel.ro*)
|
||||
*(COMMON)
|
||||
} :data
|
||||
.rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data
|
||||
.dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic
|
||||
.bss : ALIGN(4K) {
|
||||
*(.bss*)
|
||||
} :data
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/* sysinfo - Deshab 系统信息查看工具
|
||||
*
|
||||
* 展示 SDK 完整能力链:
|
||||
* boot_context 解析 -> kernel_api 探测 -> block/net 设备枚举 -> 绘制 + 输入
|
||||
*
|
||||
* 构建: cd CODE/tools/sysinfo ; make
|
||||
* 输出: SYSTEM/system/deshab64/tools/sysinfo.elf
|
||||
*/
|
||||
#include "../../firstInit/ascii_bitmaps.c"
|
||||
#include "../../sdk/include/deshab/deshab.h"
|
||||
#include "../../sdk/include/deshab/font.h"
|
||||
|
||||
static dsb_app_context g_ac;
|
||||
|
||||
/* ---- 简易格式化(SDK 无 sprintf) ---- */
|
||||
static char *fmt_u64_hex(u64 v, char *buf) {
|
||||
static const char hx[] = "0123456789ABCDEF";
|
||||
buf[0] = '0'; buf[1] = 'x';
|
||||
for (int i = 0; i < 16; i++)
|
||||
buf[2 + i] = hx[(v >> ((15 - i) * 4)) & 0xF];
|
||||
buf[18] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *fmt_u64_dec(u64 v, char *buf) {
|
||||
char tmp[24]; int n = 0;
|
||||
if (!v) { buf[0] = '0'; buf[1] = 0; return buf; }
|
||||
while (v && n < 23) { tmp[n++] = (char)('0' + v % 10); v /= 10; }
|
||||
for (int i = 0; i < n; i++) buf[i] = tmp[n - 1 - i];
|
||||
buf[n] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* ---- 信息行绘制 ---- */
|
||||
#define INFO_X 12
|
||||
#define INFO_Y_START (DSB_TITLEBAR_H + 8)
|
||||
#define LINE_H 20
|
||||
#define COL_LABEL_W 200
|
||||
|
||||
static int g_line = 0;
|
||||
|
||||
static void info_line(const char *label, const char *value, u32 val_color) {
|
||||
i64 y = INFO_Y_START + (i64)g_line * LINE_H;
|
||||
dsb_draw_string(&g_ac, label, INFO_X, y, DSB_TEXT_DIM, DSB_BG_PRIMARY, 12);
|
||||
dsb_draw_string(&g_ac, value, INFO_X + COL_LABEL_W, y, val_color, DSB_BG_PRIMARY, 12);
|
||||
g_line++;
|
||||
}
|
||||
|
||||
static void info_section(const char *title) {
|
||||
i64 y = INFO_Y_START + (i64)g_line * LINE_H;
|
||||
dsb_draw_string(&g_ac, title, INFO_X, y, DSB_ACCENT_LIGHT, DSB_BG_PRIMARY, 12);
|
||||
dsb_fill_rect(&g_ac, INFO_X, y + 16, (i64)g_ac.fb_w - INFO_X * 2, 1, DSB_BORDER);
|
||||
g_line += 2;
|
||||
}
|
||||
|
||||
static void info_ptr(const char *label, const void *p) {
|
||||
info_line(label, p ? "present" : "NULL", p ? DSB_SUCCESS : DSB_ERROR);
|
||||
}
|
||||
|
||||
void dsk_entry(const dsk_boot_context *ctx) {
|
||||
if (ctx->magic != DSK_BOOT_MAGIC) return;
|
||||
dsb_app_init(&g_ac, ctx);
|
||||
|
||||
/* 清屏 + 标题栏 + 状态栏 */
|
||||
dsb_fill_bg(&g_ac, DSB_BG_PRIMARY);
|
||||
dsb_draw_titlebar(&g_ac, "SysInfo", (i64)g_ac.fb_w);
|
||||
dsb_draw_statusbar(&g_ac, "Esc to exit", (i64)g_ac.fb_w, (i64)g_ac.fb_h);
|
||||
|
||||
char buf[32];
|
||||
|
||||
/* ---- DSK Boot Context ---- */
|
||||
info_section("== DSK Boot Context ==");
|
||||
info_line("magic", fmt_u64_hex(ctx->magic, buf), DSB_TEXT_PRIMARY);
|
||||
info_line("abi_version", fmt_u64_dec(ctx->abi_version, buf), DSB_TEXT_PRIMARY);
|
||||
info_line("flags", fmt_u64_hex(ctx->flags, buf), DSB_TEXT_PRIMARY);
|
||||
|
||||
/* ---- Framebuffer ---- */
|
||||
info_section("== Framebuffer ==");
|
||||
info_line("width", fmt_u64_dec(ctx->framebuffer_width, buf), DSB_TEXT_PRIMARY);
|
||||
info_line("height", fmt_u64_dec(ctx->framebuffer_height, buf), DSB_TEXT_PRIMARY);
|
||||
info_line("pitch", fmt_u64_dec(ctx->framebuffer_pitch, buf), DSB_TEXT_PRIMARY);
|
||||
info_line("bpp", fmt_u64_dec(ctx->framebuffer_bpp, buf), DSB_TEXT_PRIMARY);
|
||||
info_line("fb_addr", fmt_u64_hex(ctx->framebuffer_address, buf), DSB_TEXT_PRIMARY);
|
||||
|
||||
/* ---- Memory Map ---- */
|
||||
info_section("== Memory Map ==");
|
||||
info_line("entry_count", fmt_u64_dec(ctx->memory_map_count, buf), DSB_TEXT_PRIMARY);
|
||||
info_line("entry_size", fmt_u64_dec(ctx->memory_map_entry_size, buf), DSB_TEXT_PRIMARY);
|
||||
info_line("hhdm_offset", fmt_u64_hex(ctx->hhdm_offset, buf), DSB_TEXT_PRIMARY);
|
||||
|
||||
/* ---- Kernel API ---- */
|
||||
info_section("== Kernel API ==");
|
||||
const dkm_kernel_api *api = g_ac.api;
|
||||
if (!api) {
|
||||
info_line("kernel_api", "NULL", DSB_ERROR);
|
||||
} else {
|
||||
info_line("version", fmt_u64_dec(api->version, buf), DSB_TEXT_PRIMARY);
|
||||
info_line("feature_bits", fmt_u64_hex(api->feature_bits, buf), DSB_TEXT_PRIMARY);
|
||||
info_line("rsdp_addr", fmt_u64_hex((u64)api->rsdp_address, buf), DSB_TEXT_PRIMARY);
|
||||
info_ptr("log_api", api->log);
|
||||
info_ptr("dma_api", api->dma);
|
||||
info_ptr("net_api", api->net);
|
||||
info_ptr("block_api", api->block);
|
||||
info_ptr("mmio_api", api->mmio);
|
||||
info_ptr("irq_register", (const void *)api->irq_register);
|
||||
info_ptr("mm_map_mmio", (const void *)api->mm_map_mmio);
|
||||
info_ptr("register_apic_eoi", (const void *)api->register_apic_eoi);
|
||||
info_ptr("irq_vector_alloc", (const void *)api->irq_vector_alloc);
|
||||
|
||||
/* ---- Block 设备探测 ---- */
|
||||
if (api->block && api->block->device_count) {
|
||||
info_section("== Block Devices ==");
|
||||
u32 dev_count = api->block->device_count();
|
||||
info_line("device_count", fmt_u64_dec(dev_count, buf), DSB_TEXT_PRIMARY);
|
||||
for (u32 i = 0; i < dev_count && i < 4; i++) {
|
||||
char label[8];
|
||||
label[0] = '['; label[1] = '0' + (char)i; label[2] = ']'; label[3] = 0;
|
||||
const char *name = api->block->device_name ? api->block->device_name(i) : "?";
|
||||
u64 sz = api->block->sector_size ? api->block->sector_size(i) : 0;
|
||||
/* 显示: "name sector_size" */
|
||||
info_line(label, name ? name : "?", DSB_ACCENT_LIGHT);
|
||||
char sz_label[16];
|
||||
sz_label[0] = ' '; sz_label[1] = 's'; sz_label[2] = 'z'; sz_label[3] = '=';
|
||||
/* 拼接 size 到 sz_label... 简化: 单独行显示 */
|
||||
info_line(" sector_size", fmt_u64_dec(sz, buf), DSB_TEXT_DIM);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Net 设备探测 ---- */
|
||||
if (api->net && api->net->device_count) {
|
||||
info_section("== Net Devices ==");
|
||||
u32 dev_count = api->net->device_count();
|
||||
info_line("device_count", fmt_u64_dec(dev_count, buf), DSB_TEXT_PRIMARY);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- SDK Block 便捷层 ---- */
|
||||
info_section("== SDK Block Layer ==");
|
||||
info_ptr("block.read", (const void *)g_ac.block.read);
|
||||
info_ptr("block.write", (const void *)g_ac.block.write);
|
||||
|
||||
/* 等待 Esc 退出 */
|
||||
for (;;) {
|
||||
u8 st = dsb_inb(0x64);
|
||||
if (st & 1) {
|
||||
u8 sc = dsb_inb(0x60);
|
||||
if (sc == 1) break; /* Esc make code */
|
||||
}
|
||||
__asm__ volatile("pause");
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user