1#![no_std]
2#![no_main]
3
4use core::panic::PanicInfo;
5use strat9_syscall::call;
6
7const PAGE_SIZE: usize = 4096;
8
9fn write_fd(fd: usize, msg: &str) {
11 let _ = call::write(fd, msg.as_bytes());
12}
13
14fn log(msg: &str) {
16 write_fd(1, msg);
17}
18
19fn log_err(msg: &str) {
21 write_fd(2, msg);
22}
23
24fn log_u64(mut value: u64) {
26 let mut buf = [0u8; 21];
27 if value == 0 {
28 log("0");
29 return;
30 }
31 let mut i = buf.len();
32 while value > 0 {
33 i -= 1;
34 buf[i] = b'0' + (value % 10) as u8;
35 value /= 10;
36 }
37 let s = unsafe { core::str::from_utf8_unchecked(&buf[i..]) };
38 log(s);
39}
40
41fn log_hex_u64(mut value: u64) {
43 let mut buf = [0u8; 16];
44 for i in (0..16).rev() {
45 let nibble = (value & 0xF) as u8;
46 buf[i] = if nibble < 10 {
47 b'0' + nibble
48 } else {
49 b'a' + (nibble - 10)
50 };
51 value >>= 4;
52 }
53 log("0x");
54 let s = unsafe { core::str::from_utf8_unchecked(&buf) };
55 log(s);
56}
57
58fn log_line(label: &str, value: u64) {
60 log(label);
61 log_u64(value);
62 log("\n");
63}
64
65fn section(title: &str) {
67 log("\n============================================================\n");
68 log("[test_mem] ");
69 log(title);
70 log("\n============================================================\n");
71}
72
73fn query_brk() -> usize {
75 match call::brk(0) {
76 Ok(v) => v,
77 Err(e) => {
78 log_err("[test_mem] brk(0) failed: ");
79 log_err(e.name());
80 log_err("\n");
81 call::exit(101);
82 }
83 }
84}
85
86fn set_brk(new_brk: usize, ctx: &str) -> usize {
88 match call::brk(new_brk) {
89 Ok(v) => v,
90 Err(e) => {
91 log_err("[test_mem] brk(set) failed in ");
92 log_err(ctx);
93 log_err(": ");
94 log_err(e.name());
95 log_err("\n");
96 call::exit(102);
97 }
98 }
99}
100
101fn fill_and_verify_pages(base: usize, pages: usize, seed: u8, tag: &str) {
103 log("[test_mem] fill_and_verify ");
104 log(tag);
105 log(" base=");
106 log_hex_u64(base as u64);
107 log(" pages=");
108 log_u64(pages as u64);
109 log(" seed=");
110 log_hex_u64(seed as u64);
111 log("\n");
112
113 let total = pages * PAGE_SIZE;
114 let buf = unsafe { core::slice::from_raw_parts_mut(base as *mut u8, total) };
115 for p in 0..pages {
116 let page_off = p * PAGE_SIZE;
117 let a = seed.wrapping_add((p as u8).wrapping_mul(3));
118 let b = seed.wrapping_add((p as u8).wrapping_mul(7)).wrapping_add(1);
119 let c = seed
120 .wrapping_add((p as u8).wrapping_mul(11))
121 .wrapping_add(2);
122
123 buf[page_off] = a;
124 buf[page_off + 17] = b;
125 buf[page_off + PAGE_SIZE - 1] = c;
126 }
127
128 for p in 0..pages {
129 let page_off = p * PAGE_SIZE;
130 let a = seed.wrapping_add((p as u8).wrapping_mul(3));
131 let b = seed.wrapping_add((p as u8).wrapping_mul(7)).wrapping_add(1);
132 let c = seed
133 .wrapping_add((p as u8).wrapping_mul(11))
134 .wrapping_add(2);
135
136 if buf[page_off] != a || buf[page_off + 17] != b || buf[page_off + PAGE_SIZE - 1] != c {
137 log_err("[test_mem] verify failed at page ");
138 log_u64(p as u64);
139 log_err(" in ");
140 log_err(tag);
141 log_err("\n");
142 call::exit(103);
143 }
144 }
145
146 log("[test_mem] verify OK for ");
147 log(tag);
148 log("\n");
149}
150
151fn single_roundtrip(case_name: &str, pages: usize, seed: u8) {
153 section(case_name);
154 let base = query_brk();
155 let target = base + pages * PAGE_SIZE;
156
157 log("[test_mem] base brk = ");
158 log_hex_u64(base as u64);
159 log("\n[test_mem] target brk = ");
160 log_hex_u64(target as u64);
161 log("\n[test_mem] grow pages = ");
162 log_u64(pages as u64);
163 log("\n");
164
165 let after_grow = set_brk(target, case_name);
166 log("[test_mem] after grow = ");
167 log_hex_u64(after_grow as u64);
168 log("\n");
169 if after_grow < target {
170 log_err("[test_mem] ERROR: brk grow returned too small value\n");
171 call::exit(104);
172 }
173
174 fill_and_verify_pages(base, pages, seed, case_name);
175
176 let after_shrink = set_brk(base, case_name);
177 log("[test_mem] after shrink = ");
178 log_hex_u64(after_shrink as u64);
179 log("\n");
180 if after_shrink > base {
181 log_err("[test_mem] ERROR: brk shrink did not return near base\n");
182 call::exit(105);
183 }
184}
185
186fn saw_tooth_stress() {
188 section("Saw-tooth stress (split/coalesce pressure)");
189 let base = query_brk();
190 log("[test_mem] initial base = ");
191 log_hex_u64(base as u64);
192 log("\n");
193
194 let mut pages = 1usize;
195 for step in 0..18usize {
196 let target = base + pages * PAGE_SIZE;
197 log("[test_mem] step ");
198 log_u64(step as u64);
199 log(": grow to ");
200 log_u64(pages as u64);
201 log(" pages -> ");
202 log_hex_u64(target as u64);
203 log("\n");
204 let _ = set_brk(target, "saw_tooth_grow");
205 fill_and_verify_pages(base, pages, (0x40u8).wrapping_add(step as u8), "saw_tooth");
206
207 let back_pages = pages / 2;
208 let back_target = base + back_pages * PAGE_SIZE;
209 log("[test_mem] step ");
210 log_u64(step as u64);
211 log(": shrink to ");
212 log_u64(back_pages as u64);
213 log(" pages -> ");
214 log_hex_u64(back_target as u64);
215 log("\n");
216 let _ = set_brk(back_target, "saw_tooth_shrink");
217
218 pages = if pages >= 64 { 1 } else { pages * 2 };
219 let _ = call::sched_yield();
220 }
221
222 let final_brk = set_brk(base, "saw_tooth_final");
223 log("[test_mem] final brk = ");
224 log_hex_u64(final_brk as u64);
225 log("\n");
226}
227
228fn churn_many_small_ops() {
230 section("Many small grow/shrink cycles");
231 let base = query_brk();
232 for i in 0..80usize {
233 let pages = 1 + (i % 7);
234 let target = base + pages * PAGE_SIZE;
235 let _ = set_brk(target, "churn_grow");
236 fill_and_verify_pages(base, pages, (0x90u8).wrapping_add(i as u8), "churn");
237 let _ = set_brk(base, "churn_shrink");
238 if i % 10 == 0 {
239 log("[test_mem] churn checkpoint i=");
240 log_u64(i as u64);
241 log("\n");
242 }
243 }
244}
245
246#[panic_handler]
247fn panic(_info: &PanicInfo) -> ! {
249 log_err("[test_mem] PANIC\n");
250 call::exit(200)
251}
252
253#[no_mangle]
254pub extern "C" fn _start() -> ! {
256 section("Strat9 userspace memory stress test (very verbose)");
257 log("[test_mem] objectif: valider grow/shrink BRK + accès page par page\n");
258 log("[test_mem] ce test est volontairement verbeux\n");
259
260 let pid = call::getpid().unwrap_or(0);
261 let tid = call::gettid().unwrap_or(0);
262 log("[test_mem] pid=");
263 log_u64(pid as u64);
264 log(" tid=");
265 log_u64(tid as u64);
266 log("\n");
267
268 single_roundtrip("Roundtrip 1 page", 1, 0x11);
269 single_roundtrip("Roundtrip 2 pages", 2, 0x22);
270 single_roundtrip("Roundtrip 4 pages", 4, 0x33);
271 single_roundtrip("Roundtrip 8 pages", 8, 0x44);
272 single_roundtrip("Roundtrip 16 pages", 16, 0x55);
273 single_roundtrip("Roundtrip 32 pages", 32, 0x66);
274
275 saw_tooth_stress();
276 churn_many_small_ops();
277
278 section("Completed successfully");
279 log_line("[test_mem] exit code = ", 0);
280 call::exit(0)
281}