code wiki / _hdl_build / _sched_gate.nx
_sched_gate.nx source
↩ module page · 114 lines · 6514 B
1// _sched_gate.nx -- gate for the 2-task PREEMPTIVE SCHEDULER slice.
2// NO mocks: runs the REAL nx_sched_emit, RUNS the image on the SOVEREIGN rv64 emu, and asserts a
3// STRUCTURAL property honest for a preemptive phenomenon: the serial has boot 'S' + BOTH task
4// markers 'A' and 'B' in MANY alternating runs (>=8 A<->B transitions = the scheduler preempted +
5// context-switched repeatedly) + a clean finisher halt. Then TAMPER: zero the mie.MTIE-enable byte
6// -> no timer -> no preemption -> task A loops alone -> 'B' never appears, transitions collapse ->
7// the gate MUST go RED (proves the SWITCHING is really timer-preemption, not a rubber stamp).
8// Evidence -> knowledge/status/sched.log (SCHEDGATE row). license_tier: ORIGINAL
9import "nx_syscalls.nx"
10
11func g_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
12func g_fp(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 }
13func g_fn(fd: i64, v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(fd,bb,k); return 0 }
14
15func g_run1(prog: *u8, arg1: *u8, outpath: *u8) -> i64 {
16 let pid: i64 = sys_fork()
17 if pid == 0 {
18 if outpath != (0 as *u8) { let ofd: i64 = sys_openat_wr(outpath, 0x1a4); if ofd >= 0 { sys_dup3(ofd, 1, 0); sys_dup3(ofd, 2, 0) } }
19 else { let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4); if dn >= 0 { sys_dup3(dn, 1, 0) } }
20 let argv: *i64 = sys_mmap(32) as *i64
21 argv[0] = prog as i64
22 if arg1 != (0 as *u8) { argv[1] = arg1 as i64; argv[2] = 0 } else { argv[1] = 0 }
23 let envp: *i64 = sys_mmap(16) as *i64
24 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0
25 sys_execve(prog, argv, envp)
26 sys_exit(127)
27 }
28 let st: *i64 = sys_mmap(16) as *i64
29 sys_wait4(pid, st, 0)
30 return st[0]
31}
32
33func g_read(path: *u8, buf: *u8, cap: i64) -> i64 {
34 let fd: i64 = sys_openat_rd(path)
35 if fd < 0 { return 0 }
36 var n: i64 = 0
37 var go: i64 = 1
38 while go == 1 { let r: i64 = sys_read(fd, (buf as i64 + n) as *u8, cap - 1 - n); if r <= 0 { go = 0 } else { n = n + r } if n >= cap - 1 { go = 0 } }
39 sys_close(fd)
40 return n
41}
42
43func g_has(buf: *u8, n: i64, ch: i64) -> i64 { var i: i64=0; while i<n { if buf[i]==(ch as u8) { return 1 } i=i+1 } return 0 }
44
45// count A<->B transitions (adjacent chars both in {A,B} and differing).
46func g_transitions(buf: *u8, n: i64) -> i64 {
47 var c: i64 = 0
48 var i: i64 = 0
49 while i + 1 < n {
50 let a: i64 = buf[i] as i64
51 let b: i64 = buf[i+1] as i64
52 var aok: i64 = 0; if a == 65 { aok = 1 } if a == 66 { aok = 1 }
53 var bok: i64 = 0; if b == 65 { bok = 1 } if b == 66 { bok = 1 }
54 if aok == 1 { if bok == 1 { if a != b { c = c + 1 } } }
55 i = i + 1
56 }
57 return c
58}
59
60func main() -> i64 {
61 let binpath: *u8 = "runtime/_hdl_build/_sched_virt.bin" as *u8
62 let tamper_bin: *u8 = "/tmp/_schedgate_tamper.bin" as *u8
63 let sov_serial: *u8 = "/tmp/_schedgate_sov.txt" as *u8
64 let sov_tamper: *u8 = "/tmp/_schedgate_tamper.txt" as *u8
65 g_p("=== preemptive-scheduler gate (2-task round-robin: SOVEREIGN rv64 emu + tamper) ===\n" as *u8)
66 let lfd: i64 = sys_openat_append("knowledge/status/sched.log" as *u8, 0x1a4)
67
68 let est: i64 = g_run1("/tmp/nx_sched_emit.sov.elf" as *u8, 0 as *u8, 0 as *u8)
69 if est != 0 { g_p("SCHEDGATE verdict=RED reason=emit-failed\n" as *u8); if lfd>=0 { g_fp(lfd,"SCHEDGATE verdict=RED reason=emit-failed\n" as *u8); sys_close(lfd) } sys_exit(1); return 1 }
70
71 // PRIMARY: sovereign emu runs the scheduler.
72 let sst: i64 = g_run1("_offc/nx_boot_run_sov.elf" as *u8, binpath, sov_serial)
73 let sbuf: *u8 = sys_mmap(131072)
74 let sbn: i64 = g_read(sov_serial, sbuf, 131072)
75 let has_s: i64 = g_has(sbuf, sbn, 83)
76 let has_a: i64 = g_has(sbuf, sbn, 65)
77 let has_b: i64 = g_has(sbuf, sbn, 66)
78 let trans: i64 = g_transitions(sbuf, sbn)
79 var halt_ok: i64 = 0
80 var p2: i64 = 0
81 while p2 + 21 <= sbn { var k: i64=0; var hit: i64=1; let pat: *u8="BOOTSOV verdict=GREEN" as *u8; while k<21 { if sbuf[p2+k]!=pat[k] { hit=0; k=21 } else { k=k+1 } } if hit==1 { halt_ok=1; p2=sbn } else { p2=p2+1 } }
82 var sov_ok: i64 = 0
83 if sst == 0 { if has_s == 1 { if has_a == 1 { if has_b == 1 { if trans >= 8 { if halt_ok == 1 { sov_ok = 1 } } } } } }
84
85 // TAMPER: zero mie.MTIE-enable imm (addi t1,x0,0x80 at byte 48 -> imm byte 51).
86 let ibuf: *u8 = sys_mmap(8192)
87 let ibn: i64 = g_read(binpath, ibuf, 8192)
88 ibuf[48 + 3] = 0 as u8
89 let tfd: i64 = sys_openat_wr(tamper_bin, 0x1a4)
90 if tfd >= 0 { sys_write(tfd, ibuf, ibn); sys_close(tfd) }
91 let tst: i64 = g_run1("_offc/nx_boot_run_sov.elf" as *u8, tamper_bin, sov_tamper)
92 let tbuf: *u8 = sys_mmap(131072)
93 let tbn: i64 = g_read(sov_tamper, tbuf, 131072)
94 let t_trans: i64 = g_transitions(tbuf, tbn)
95 var tamper_bites: i64 = 0
96 if t_trans < 4 { tamper_bites = 1 } // no preemption -> task B never scheduled -> A<->B switching collapses (real run >=8; note 'B' alone is unreliable -- it appears in the runner's "BOOTSOV" line)
97
98 g_p(" sovereign_emu=" as *u8)
99 if sov_ok == 1 { g_p("GREEN" as *u8) } else { g_p("RED" as *u8) }
100 g_p("(switches=" as *u8); g_fn(1, trans); g_p(") tamper_bites=" as *u8)
101 if tamper_bites == 1 { g_p("yes\n" as *u8) } else { g_p("no\n" as *u8) }
102
103 var pass: i64 = 0
104 if sov_ok == 1 { if tamper_bites == 1 { pass = 1 } }
105 if pass == 1 {
106 g_p("SCHEDGATE verdict=GREEN (sovereign rv64 emu: 2-task preemptive round-robin; repeated context switches; tamper REJECTED)\n" as *u8)
107 if lfd >= 0 { g_fp(lfd, "SCHEDGATE verdict=GREEN runtime=sovereign-emu sched=preemptive-round-robin tasks=2 switches=" as *u8); g_fn(lfd, trans); g_fp(lfd, " mechanism=timer-preempt-csrrw-mepc-swap tamper=rejected epoch=" as *u8); g_fn(lfd, sys_now_realtime_sec()); g_fp(lfd, "\n" as *u8); sys_close(lfd) }
108 sys_exit(0); return 0
109 }
110 g_p("SCHEDGATE verdict=RED (sov_ok/tamper not both green)\n" as *u8)
111 if lfd >= 0 { g_fp(lfd, "SCHEDGATE verdict=RED sov_ok=" as *u8); g_fn(lfd, sov_ok); g_fp(lfd, " switches=" as *u8); g_fn(lfd, trans); g_fp(lfd, " tamper_bites=" as *u8); g_fn(lfd, tamper_bites); g_fp(lfd, "\n" as *u8); sys_close(lfd) }
112 sys_exit(1)
113 return 1
114}