- CODE/dsk/ext4.c: minimal full ext4 read/write (GPT probe, feature gate, extent depth 0/1 rw, block/inode alloc-free, dir insert, write-verify); boot selftest PASS on p2. Unsupported features panic (FS-E2x), zero fallback. - DSK fat32 write: on-demand FAT entry RMW + chunked free-cluster scan (fixes "no free clusters" on 5.3G ESP); rec_len shrink offset (+4) and chunk alignment fixes. USER.CONF write-verify ok, firstInit flip ok. - First-boot: wizard -> persist -> forced login (FS-E06 if skipped/failed) -> desktop. login.elf loaded via system/user/use fallback. - FirstInit network page: Connect now panics NET-E01 (no wlan stack); Save (config only) is the only path forward. - FirstInit/DSK fatal: ohMyLogo lotus fatal screen (NET-E01 / FS-E series). - Boot screen: Logo.png image removed, "DESHAB P01" text centered only; dcp/xj logos regenerated from TEXTURES/. - run_qemu.bat: main GPT image on SATA ide.0 (AHCI provider must see ext4).
303 lines
12 KiB
C
303 lines
12 KiB
C
#include <utsm/utsm.h>
|
||
#include <utsm/pckc.h>
|
||
#include <utsm/crypto.h>
|
||
#include <utsm/dma.h>
|
||
#include <utsm/dkm.h>
|
||
#include <utsm/log.h>
|
||
#include <utsm/segment.h>
|
||
#include <utsm/arena.h>
|
||
#include <utsm/drr.h>
|
||
#include <utsm/sched_ext.h>
|
||
|
||
int memcmp(const void *a, const void *b, usize len);
|
||
usize strlen(const char *s);
|
||
|
||
/* selftest 与 LAPIC/PIT tick 竞态:tick 在别的任务上唤醒 sleeper 时
|
||
* 会向同一 runqueue 入队,破坏 pick_next 的确定性断言(偶发
|
||
* block/wake FAIL → arch_halt_forever)。临界区内关中断。 */
|
||
static u64 selftest_irq_save(void) {
|
||
u64 flags;
|
||
__asm__ volatile("pushfq; popq %0; cli" : "=r"(flags) :: "memory");
|
||
return flags;
|
||
}
|
||
|
||
static void selftest_irq_restore(u64 flags) {
|
||
__asm__ volatile("pushq %0; popfq" :: "r"(flags) : "memory");
|
||
}
|
||
|
||
int utsm_selftest_run(void) {
|
||
log_info("[UTSM] selftest enter");
|
||
|
||
/* utsm_process_context is ~8KB (256 capabilities × 32 bytes).
|
||
* Allocate from arena instead of stack to avoid kernel stack overflow. */
|
||
utsm_process_context *process = (utsm_process_context *)kmem_alloc(sizeof(utsm_process_context));
|
||
if (!process) {
|
||
log_error("[UTSM] selftest: failed to allocate process context");
|
||
return UTSM_ERR_NO_MEMORY;
|
||
}
|
||
utsm_process_create_test(process, 1);
|
||
|
||
utsm_capability cap;
|
||
int status = utsm_create_segment(process, UTSM_PAGE_SIZE, UTSM_SEG_F_READ | UTSM_SEG_F_WRITE | UTSM_SEG_F_STRONG_RECOVERY, &cap);
|
||
if (status != UTSM_OK) {
|
||
log_error("[UTSM] selftest create segment failed");
|
||
return status;
|
||
}
|
||
|
||
const char *message = "deshab utsm sealed memory selftest";
|
||
u64 len = strlen(message) + 1;
|
||
status = utsm_write(cap, 128, message, len);
|
||
if (status != UTSM_OK) {
|
||
log_error("[UTSM] selftest write failed");
|
||
return status;
|
||
}
|
||
log_info("[UTSM] selftest write ok");
|
||
|
||
utsm_segment_desc *desc = utsm_get_segment(cap.segment_slot);
|
||
if (!desc) {
|
||
return UTSM_ERR_INVALID;
|
||
}
|
||
if (memcmp(desc->cipher_base + 128, message, len) == 0) {
|
||
log_error("[UTSM] raw cipher equals plaintext");
|
||
return UTSM_ERR_INVALID;
|
||
}
|
||
log_info("[UTSM] raw cipher check ok");
|
||
|
||
char readback[64];
|
||
status = utsm_read(cap, 128, readback, len);
|
||
if (status != UTSM_OK) {
|
||
log_error("[UTSM] selftest read failed");
|
||
return status;
|
||
}
|
||
if (memcmp(readback, message, len) != 0) {
|
||
log_error("[UTSM] selftest read mismatch");
|
||
return UTSM_ERR_INVALID;
|
||
}
|
||
log_info("[UTSM] selftest read ok");
|
||
|
||
/* ==== U5: chacha20 KAT(RFC 8439 §2.4.2 官方向量)====
|
||
* 历史注:FUCK 注释曾提"并行会话 chacha20 KAT 间歇 FAIL"——该测试
|
||
* 从未合并进本树(幽灵缺陷)。本 KAT 为确定性官方向量,不依赖
|
||
* 硬件/时钟/随机数,PASS/FAIL 完全可复现。期望值由
|
||
* test/gen_chacha_kat.py 生成,并经 pycryptodome 独立对拍。 */
|
||
{
|
||
#include "chacha_kat.inc"
|
||
u8 ks[64];
|
||
utsm_chacha20_block(kat_key, kat_nonce, kat_counter, ks);
|
||
if (memcmp(ks, kat_expect, 64) != 0) {
|
||
log_error("[UTSM] selftest chacha20 KAT FAIL (RFC 8439 §2.4.2)");
|
||
return UTSM_ERR_INVALID;
|
||
}
|
||
log_info("[UTSM] selftest chacha20 KAT ok (RFC 8439 §2.4.2)");
|
||
|
||
/* page MAC 确定性:同输入同 MAC、跨页不同 MAC */
|
||
const u64 *rk = drr_get_root_key();
|
||
u64 m1 = utsm_page_mac(rk, 0, 0, 0, desc->cipher_base);
|
||
u64 m2 = utsm_page_mac(rk, 0, 0, 0, desc->cipher_base);
|
||
u64 m3 = utsm_page_mac(rk, 0, 1, 0, desc->cipher_base);
|
||
if (m1 != m2 || m1 == m3) {
|
||
log_error("[UTSM] selftest page MAC determinism FAIL");
|
||
return UTSM_ERR_INVALID;
|
||
}
|
||
log_info("[UTSM] selftest page MAC determinism ok");
|
||
}
|
||
|
||
/* ==== U4: 密文页 dump —— 串口输出密文/明文 hex 对照 ==== */
|
||
{
|
||
int dst = utrw_debug_dump_page(cap.segment_slot, 0, 128);
|
||
if (dst != UTSM_OK) {
|
||
log_error("[UTSM] selftest cipher dump failed");
|
||
return dst;
|
||
}
|
||
}
|
||
|
||
/* ==== U2: Slow Path 真分支(EPOCH 刷新 / KEY_MISS 重派生 / POISONED 拒绝) ==== */
|
||
{
|
||
/* 1) EPOCH 过期:伪造 stale cap(epoch=desc->key_epoch+1),slow path 应
|
||
* 返回 OK 并回传刷新后的 cap,重试 read 应成功且内容一致 */
|
||
utsm_capability stale = cap;
|
||
stale.epoch = (u32)desc->key_epoch + 1;
|
||
utsm_capability refreshed = stale;
|
||
status = utsm_slow_path(stale, UTSM_RIGHT_READ, 128, len,
|
||
UTSM_ERR_EPOCH, &refreshed);
|
||
if (status != UTSM_OK) {
|
||
log_error("[UTSM] selftest slow EPOCH recover failed");
|
||
return status;
|
||
}
|
||
char slow_rb[64];
|
||
status = utsm_read(refreshed, 128, slow_rb, len);
|
||
if (status != UTSM_OK || memcmp(slow_rb, message, len) != 0) {
|
||
log_error("[UTSM] selftest slow EPOCH retry read failed");
|
||
return (status == UTSM_OK) ? UTSM_ERR_INVALID : status;
|
||
}
|
||
log_info("[UTSM] selftest slow EPOCH ok");
|
||
|
||
/* 2) KEY_MISS:清掉该段 PCKC 缓存,slow path 重派生应返回 OK */
|
||
utsm_pckc_invalidate_slot(cap.segment_slot);
|
||
status = utsm_slow_path(refreshed, UTSM_RIGHT_READ, 128, len,
|
||
UTSM_ERR_KEY_MISS, 0);
|
||
if (status != UTSM_OK) {
|
||
log_error("[UTSM] selftest slow KEY_MISS recover failed");
|
||
return status;
|
||
}
|
||
status = utsm_read(refreshed, 128, slow_rb, len);
|
||
if (status != UTSM_OK || memcmp(slow_rb, message, len) != 0) {
|
||
log_error("[UTSM] selftest slow KEY_MISS retry read failed");
|
||
return (status == UTSM_OK) ? UTSM_ERR_INVALID : status;
|
||
}
|
||
log_info("[UTSM] selftest slow KEY_MISS ok");
|
||
|
||
/* 3) POISONED:不可恢复,原样返回;DRR fault 留痕不重启
|
||
* (selftest 场景 drr_report_fault 只计数,无 reboot 语义) */
|
||
status = utsm_slow_path(refreshed, UTSM_RIGHT_READ, 128, len,
|
||
UTSM_ERR_POISONED, 0);
|
||
if (status != UTSM_ERR_POISONED) {
|
||
log_error("[UTSM] selftest slow POISONED passthrough failed");
|
||
return UTSM_ERR_INVALID;
|
||
}
|
||
log_info("[UTSM] selftest slow POISONED ok");
|
||
}
|
||
|
||
/* ==== U3: @sealed 可选标记 —— unsealed 段明文直存(0 加解密开销) ==== */
|
||
{
|
||
utsm_capability ucap;
|
||
status = utsm_create_segment(process, UTSM_PAGE_SIZE,
|
||
UTSM_SEG_F_READ | UTSM_SEG_F_WRITE |
|
||
UTSM_SEG_F_UNSEALED,
|
||
&ucap);
|
||
if (status != UTSM_OK) {
|
||
log_error("[UTSM] selftest unsealed segment create failed");
|
||
return status;
|
||
}
|
||
const char *upl = "unsealed plain text 00";
|
||
u64 ulen = strlen(upl) + 1;
|
||
status = utsm_write(ucap, 128, upl, ulen);
|
||
if (status != UTSM_OK) {
|
||
log_error("[UTSM] selftest unsealed write failed");
|
||
return status;
|
||
}
|
||
utsm_segment_desc *udesc = utsm_get_segment(ucap.segment_slot);
|
||
if (!udesc) return UTSM_ERR_INVALID;
|
||
/* 明文直存:存储区内容必须与明文完全一致(0 开销的可观察证据) */
|
||
if (memcmp(udesc->cipher_base + 128, upl, ulen) != 0) {
|
||
log_error("[UTSM] selftest unsealed store not plaintext");
|
||
return UTSM_ERR_INVALID;
|
||
}
|
||
char urb[64];
|
||
status = utsm_read(ucap, 128, urb, ulen);
|
||
if (status != UTSM_OK || memcmp(urb, upl, ulen) != 0) {
|
||
log_error("[UTSM] selftest unsealed read failed");
|
||
return (status == UTSM_OK) ? UTSM_ERR_INVALID : status;
|
||
}
|
||
log_info("[UTSM] selftest unsealed segment ok (plain storage)");
|
||
}
|
||
|
||
/* ==== D3: 按名导出表(kapi)——按名取服务并调用 ==== */
|
||
{
|
||
const dkm_kernel_api *api = dkm_get_kernel_api();
|
||
if (!api->kapi || api->kapi->magic != DKM_KAPI_MAGIC) {
|
||
log_error("[UTSM] selftest kapi table missing");
|
||
return UTSM_ERR_INVALID;
|
||
}
|
||
void *fn = 0;
|
||
if (api->kapi->lookup("log.info", &fn) != 0) {
|
||
log_error("[UTSM] selftest kapi lookup log.info failed");
|
||
return UTSM_ERR_INVALID;
|
||
}
|
||
void (*klog)(const char *) = (void (*)(const char *))fn;
|
||
klog("[UTSM] selftest kapi: log.info via export table");
|
||
if (api->kapi->lookup("dma.alloc_pages", &fn) != 0) {
|
||
log_error("[UTSM] selftest kapi lookup dma failed");
|
||
return UTSM_ERR_INVALID;
|
||
}
|
||
int (*kalloc)(u64, u64, u64, dkm_dma_buffer *) = (int (*)(u64, u64, u64, dkm_dma_buffer *))fn;
|
||
dkm_dma_buffer kbuf;
|
||
if (kalloc(1, 4096, 0xFFFFFFFFULL, &kbuf) != 0 || !kbuf.virt) {
|
||
log_error("[UTSM] selftest kapi dma alloc failed");
|
||
return UTSM_ERR_INVALID;
|
||
}
|
||
if (api->kapi->lookup("no.such.service", &fn) == 0) {
|
||
log_error("[UTSM] selftest kapi negative lookup failed");
|
||
return UTSM_ERR_INVALID;
|
||
}
|
||
log_hex64("[UTSM] selftest kapi entries=", api->kapi->count);
|
||
log_info("[UTSM] selftest kapi ok");
|
||
}
|
||
|
||
/* ==== SCHED-1: O(1) runqueue 语义(合成 TCB,协作式,无 tick) ==== */
|
||
/* ==== SCHED-1 + SCHED-2: runqueue / block-wake(tick 竞态保护) ==== */
|
||
{
|
||
u64 st_flags = selftest_irq_save();
|
||
int rq_rc = utsm_sched_selftest_runqueue();
|
||
int bw_rc = (rq_rc == 0) ? utsm_sched_selftest_blockwake() : 0;
|
||
selftest_irq_restore(st_flags);
|
||
if (rq_rc != 0) {
|
||
log_error("[SCHED] selftest: runqueue O(1) FAIL");
|
||
return UTSM_ERR_INVALID;
|
||
}
|
||
log_info("[SCHED] selftest: runqueue O(1) PASS");
|
||
if (bw_rc != 0) {
|
||
log_error("[SCHED] selftest: block/wake FAIL");
|
||
return UTSM_ERR_INVALID;
|
||
}
|
||
log_info("[SCHED] selftest: block/wake PASS");
|
||
}
|
||
|
||
/* ==== DRR-1: 真实脏页快照 + 页级回滚 ====
|
||
* 写 P1 → 快照(捕获 P1 密文页)→ 写 P2 覆盖 → 回滚 → 读回 P1 */
|
||
{
|
||
const char *p1 = "P1-ROLLBACK-STATE-01";
|
||
const char *p2 = "P2-OVERWRITE-STATE-0";
|
||
u64 plen = strlen(p1) + 1;
|
||
status = utsm_write(cap, 512, p1, plen);
|
||
if (status != UTSM_OK) {
|
||
log_error("[DRR] selftest P1 write failed");
|
||
return status;
|
||
}
|
||
int snap = drr_ckpt_snapshot_dirty();
|
||
log_hex64("[DRR] ckpt snapshot pages=", (u64)(i64)snap);
|
||
if (snap < 1) {
|
||
log_error("[DRR] selftest snapshot failed");
|
||
return UTSM_ERR_INVALID;
|
||
}
|
||
drr_checkpoint_meta meta;
|
||
if (drr_checkpoint_recover(&meta) != 0 || meta.type != DRR_CKPT_DIRTY_PAGE ||
|
||
meta.dirty_page_count < 1) {
|
||
log_error("[DRR] selftest meta verify failed");
|
||
return UTSM_ERR_INVALID;
|
||
}
|
||
status = utsm_write(cap, 512, p2, plen);
|
||
if (status != UTSM_OK) {
|
||
log_error("[DRR] selftest P2 write failed");
|
||
return status;
|
||
}
|
||
int rb = drr_rollback_pages();
|
||
log_hex64("[DRR] rollback pages=", (u64)(i64)rb);
|
||
if (rb < 1) {
|
||
log_error("[DRR] selftest rollback failed");
|
||
return UTSM_ERR_INVALID;
|
||
}
|
||
status = utsm_read(cap, 512, readback, plen);
|
||
if (status != UTSM_OK || memcmp(readback, p1, plen) != 0) {
|
||
log_error("[DRR] rollback verify FAIL");
|
||
return UTSM_ERR_INVALID;
|
||
}
|
||
log_info("[DRR] rollback verify ok");
|
||
}
|
||
|
||
/* ==== DRR-2: 看门狗判定(dry-run:reboot 关闭,仅验证日志路径) ==== */
|
||
{
|
||
drr_set_reboot_enabled(0);
|
||
drr_watchdog_register(0xFFFFFEu, 2);
|
||
drr_heartbeat_tick();
|
||
drr_heartbeat_tick();
|
||
drr_heartbeat_tick(); /* 3 ticks > timeout 2 → 触发 */
|
||
drr_watchdog_check(); /* 期望 [DRR] FAULT: watchdog timeout */
|
||
drr_watchdog_unregister(0xFFFFFEu);
|
||
drr_set_reboot_enabled(1);
|
||
log_info("[DRR] watchdog selftest done");
|
||
}
|
||
|
||
return UTSM_OK;
|
||
}
|