#include #include #include #include #include #include #include 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"); /* ==== 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"); } /* ==== 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; }