- slow_path.c: KEY_MISS -> PCKC re-derive (retry OK); EPOCH -> refresh cap epoch via out_cap (retry OK); STALE_CAP permanent; MAC_FAILED/POISONED -> PCKC invalidate + DRR fault report; RETRY passthrough - status.h: add UTSM_ERR_MAC_FAILED (-11); utrw.h: slow path takes cap/rights/offset/len/reason + out_cap - selftest: EPOCH refresh + KEY_MISS re-derive + POISONED passthrough coverage, full SELFTEST PASS in QEMU (.build_tmp/qemu_serial_u2slowpath.log)
198 lines
7.6 KiB
C
198 lines
7.6 KiB
C
#include <utsm/utsm.h>
|
||
#include <utsm/pckc.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");
|
||
|
||
/* ==== 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;
|
||
}
|