Nothing to see here, move along
1#![no_std]
2#![no_main]
3
4use lancer_core::ipc::{MAX_IPC_BYTES, unpack_bytes};
5use lancer_core::packet_ring::{PacketRingReader, PacketRingWriter};
6use lancer_user::path::{
7 build_resolved_line, bytes_eq, extract_filename, first_word, skip_leading_spaces,
8};
9use lancer_user::print;
10use lancer_user::show;
11use lancer_user::syscall;
12
13const INPUT_ENDPOINT: u64 = 1;
14const NETSTACK_RING_BASE_SLOT: u64 = 64;
15const NETSTACK_RING_FRAME_COUNT: u64 = 2;
16
17const SLOT_CHILD_PROC: u64 = 4;
18const SLOT_CHILD_SCHED: u64 = 5;
19const SLOT_CHILD_ENDPOINT: u64 = 6;
20const SLOT_CHILD_NOTIF: u64 = 7;
21
22const TAG_ENDPOINT: u64 = 1;
23const TAG_NOTIFICATION: u64 = 2;
24const TAG_SCHED_CONTEXT: u64 = 5;
25const TAG_FRAME: u64 = 11;
26const RIGHTS_ALL: u64 = 0b1111;
27
28const SLOT_UNTYPED: u64 = 28;
29
30const BOOT_PROCESS_COUNT: u64 = 7;
31
32const NETSOCK_MAP_VADDR: u64 = 0x5000_0000;
33const NETSOCK_RING_HALF: usize = 2048;
34const NETSOCK_SLOT_SIZE: u32 = 64;
35const SLOT_NETSOCK_MEM: u64 = 8;
36
37const SLOT_SHELL_NOTIF: u64 = 9;
38const SLOT_NETSTACK_NOTIF: u64 = 10;
39const VFS_RING_BASE_SLOT: u64 = 128;
40const VFS_RING_FRAME_COUNT_SHELL: u64 = 16;
41const SLOT_VFS_CLIENT_NOTIF: u64 = 12;
42const SLOT_VFS_NOTIF: u64 = 13;
43const SLOT_CHILD_NETSOCK_NOTIF: u64 = 14;
44const SLOT_VFS_PROC: u64 = 15;
45const SLOT_CHILD_PROC_B: u64 = 16;
46const SLOT_CHILD_SCHED_B: u64 = 17;
47const SLOT_CHILD_ENDPOINT_B: u64 = 18;
48const SLOT_NETSOCK_MEM_B: u64 = 19;
49const SLOT_CHILD_NETSOCK_NOTIF_B: u64 = 20;
50const SLOT_TEMP_CLIENT_RING: u64 = 200;
51const SLOT_TEMP_CLIENT_NOTIF: u64 = 216;
52const SLOT_TEMP_CLIENT_RING_B: u64 = 220;
53const SLOT_TEMP_CLIENT_NOTIF_B: u64 = 236;
54
55const VFS_RING_VADDR: u64 = 0x7000_0000;
56const VFS_CLIENT_INIT_VADDR: u64 = 0x4000_0000;
57const NETSTACK_RING_VADDR: u64 = 0x6000_0000;
58const NETSTACK_RING_HALF: usize = 4096;
59const VFS_RING_HALF: usize = 4096;
60const VFS_RING_SLOT_SIZE: u32 = 48;
61const VFS_RING_PAGES: u64 = 16;
62const VFS_CLIENT_RING_BASE_SLOT: u64 = 160;
63const VFS_CLIENT_SLOT_STRIDE: u64 = 32;
64
65const INPUT_RING_VADDR: u64 = 0x3000_0000;
66const INPUT_RING_SIZE: usize = 4096;
67
68const NOTIFY_BIT_CHILD_DEATH: u64 = 0x01;
69
70struct LineBuf {
71 data: [u8; 128],
72 len: usize,
73}
74
75impl LineBuf {
76 const fn new() -> Self {
77 Self {
78 data: [0u8; 128],
79 len: 0,
80 }
81 }
82
83 fn push(&mut self, b: u8) -> bool {
84 match self.len < self.data.len() {
85 true => {
86 self.data[self.len] = b;
87 self.len += 1;
88 true
89 }
90 false => false,
91 }
92 }
93
94 fn pop(&mut self) -> bool {
95 match self.len > 0 {
96 true => {
97 self.len -= 1;
98 true
99 }
100 false => false,
101 }
102 }
103
104 fn clear(&mut self) {
105 self.len = 0;
106 }
107
108 fn as_bytes(&self) -> &[u8] {
109 &self.data[..self.len]
110 }
111}
112
113const MAX_VFS_CLIENTS: usize = 8;
114
115struct VfsClientTracker {
116 free: [bool; MAX_VFS_CLIENTS],
117}
118
119impl VfsClientTracker {
120 const fn new() -> Self {
121 Self {
122 free: [false, true, true, true, true, true, true, true],
123 }
124 }
125
126 fn alloc(&mut self) -> Option<u8> {
127 (1..MAX_VFS_CLIENTS as u8)
128 .find(|&i| self.free[i as usize])
129 .inspect(|&i| {
130 self.free[i as usize] = false;
131 })
132 }
133
134 fn release(&mut self, idx: u8) {
135 if (idx as usize) < MAX_VFS_CLIENTS && idx > 0 {
136 self.free[idx as usize] = true;
137 }
138 }
139}
140
141fn create_vfs_client(client_idx: u8, child_proc_slot: u64, ring_tmp: u64, notif_tmp: u64) -> bool {
142 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_FRAME, 0, ring_tmp, VFS_RING_PAGES);
143 if r < 0 {
144 return false;
145 }
146
147 let map_ok = (0..VFS_RING_PAGES)
148 .all(|i| syscall::frame_map(ring_tmp + i, VFS_CLIENT_INIT_VADDR + i * 4096, 1) >= 0);
149 if !map_ok {
150 (0..VFS_RING_PAGES).for_each(|i| {
151 syscall::frame_unmap(ring_tmp + i, VFS_CLIENT_INIT_VADDR + i * 4096);
152 });
153 (0..VFS_RING_PAGES).for_each(|i| {
154 syscall::cap_revoke(ring_tmp + i);
155 });
156 return false;
157 }
158
159 let base = VFS_CLIENT_INIT_VADDR as *mut u8;
160 let _ = unsafe { PacketRingWriter::init(base, VFS_RING_HALF, VFS_RING_SLOT_SIZE) };
161 let _ = unsafe {
162 PacketRingWriter::init(
163 base.wrapping_add(VFS_RING_HALF),
164 VFS_RING_HALF,
165 VFS_RING_SLOT_SIZE,
166 )
167 };
168
169 (0..VFS_RING_PAGES).for_each(|i| {
170 syscall::frame_unmap(ring_tmp + i, VFS_CLIENT_INIT_VADDR + i * 4096);
171 });
172
173 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_NOTIFICATION, 0, notif_tmp, 1);
174 if r < 0 {
175 (0..VFS_RING_PAGES).for_each(|i| {
176 syscall::cap_revoke(ring_tmp + i);
177 });
178 return false;
179 }
180
181 let child_ring_ok = (0..VFS_RING_PAGES)
182 .all(|i| syscall::cap_grant(ring_tmp + i, child_proc_slot, 11 + i, RIGHTS_ALL) >= 0);
183 if !child_ring_ok {
184 syscall::cap_revoke(notif_tmp);
185 (0..VFS_RING_PAGES).for_each(|i| {
186 syscall::cap_revoke(ring_tmp + i);
187 });
188 return false;
189 }
190
191 let r = syscall::cap_grant(notif_tmp, child_proc_slot, 12 + VFS_RING_PAGES, RIGHTS_ALL);
192 if r < 0 {
193 syscall::cap_revoke(notif_tmp);
194 (0..VFS_RING_PAGES).for_each(|i| {
195 syscall::cap_revoke(ring_tmp + i);
196 });
197 return false;
198 }
199
200 let r = syscall::cap_grant(
201 SLOT_VFS_NOTIF,
202 child_proc_slot,
203 13 + VFS_RING_PAGES,
204 RIGHTS_ALL,
205 );
206 if r < 0 {
207 syscall::cap_revoke(notif_tmp);
208 (0..VFS_RING_PAGES).for_each(|i| {
209 syscall::cap_revoke(ring_tmp + i);
210 });
211 return false;
212 }
213
214 let vfs_ring_base = VFS_CLIENT_RING_BASE_SLOT + (client_idx as u64) * VFS_CLIENT_SLOT_STRIDE;
215 let vfs_notif_slot = vfs_ring_base + VFS_RING_PAGES;
216
217 let vfs_ring_ok = (0..VFS_RING_PAGES).all(|i| {
218 syscall::cap_grant(ring_tmp + i, SLOT_VFS_PROC, vfs_ring_base + i, RIGHTS_ALL) >= 0
219 });
220 if !vfs_ring_ok {
221 syscall::cap_revoke(notif_tmp);
222 (0..VFS_RING_PAGES).for_each(|i| {
223 syscall::cap_revoke(ring_tmp + i);
224 });
225 return false;
226 }
227
228 let r = syscall::cap_grant(notif_tmp, SLOT_VFS_PROC, vfs_notif_slot, RIGHTS_ALL);
229 if r < 0 {
230 syscall::cap_revoke(notif_tmp);
231 (0..VFS_RING_PAGES).for_each(|i| {
232 syscall::cap_revoke(ring_tmp + i);
233 });
234 return false;
235 }
236
237 let mut fs_client = unsafe {
238 lancer_user::fs::FsClient::reattach(
239 VFS_RING_VADDR as usize,
240 SLOT_VFS_NOTIF as u8,
241 SLOT_VFS_CLIENT_NOTIF as u8,
242 )
243 };
244 let reg_result =
245 fs_client.extended_raw(lancer_vfs_proto::EXT_REGISTER_CLIENT, client_idx as u64, 0);
246
247 reg_result.is_ok()
248}
249
250fn destroy_vfs_client(client_idx: u8) {
251 let mut fs_client = unsafe {
252 lancer_user::fs::FsClient::reattach(
253 VFS_RING_VADDR as usize,
254 SLOT_VFS_NOTIF as u8,
255 SLOT_VFS_CLIENT_NOTIF as u8,
256 )
257 };
258 let _ = fs_client.extended_raw(
259 lancer_vfs_proto::EXT_UNREGISTER_CLIENT,
260 client_idx as u64,
261 0,
262 );
263}
264
265fn print_prompt(cwd: &[u8], cwd_len: usize) {
266 let path = &cwd[..cwd_len];
267 print!("lancer:");
268 match cwd_len <= 32 {
269 true => {
270 if let Ok(s) = core::str::from_utf8(path) {
271 print!("{s}");
272 }
273 }
274 false => {
275 let tail = &path[cwd_len - 29..];
276 print!("...");
277 if let Ok(s) = core::str::from_utf8(tail) {
278 print!("{s}");
279 }
280 }
281 }
282 print!("> ");
283}
284
285fn find_module_by_name(name: &[u8]) -> Option<u64> {
286 let mut dummy = [0u8; 0];
287 let total = syscall::module_info(u64::MAX, &mut dummy);
288 if total < 0 {
289 return None;
290 }
291
292 let mut path_buf = [0u8; 128];
293 (BOOT_PROCESS_COUNT..total as u64).find(|&i| {
294 path_buf = [0u8; 128];
295 let path_len = syscall::module_info(i, &mut path_buf);
296 match path_len < 0 {
297 true => false,
298 false => {
299 let used = (path_len as usize).min(path_buf.len());
300 let path = &path_buf[..used];
301 let filename = extract_filename(path);
302 bytes_eq(filename, name)
303 }
304 }
305 })
306}
307
308struct ChildNetsock {
309 tx: PacketRingWriter,
310 rx: PacketRingReader,
311}
312
313fn setup_child_netsock(
314 line: &[u8],
315 has_netring: bool,
316 child_proc_slot: u64,
317 mem_slot: u64,
318 notif_slot: u64,
319) -> Option<ChildNetsock> {
320 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_FRAME, 0, mem_slot, 1);
321 if r < 0 {
322 return None;
323 }
324
325 let r = syscall::frame_map(mem_slot, NETSOCK_MAP_VADDR, 1);
326 if r < 0 {
327 syscall::cap_revoke(mem_slot);
328 return None;
329 }
330
331 let init_to_child = NETSOCK_MAP_VADDR as *mut u8;
332 let child_to_init = (NETSOCK_MAP_VADDR + NETSOCK_RING_HALF as u64) as *mut u8;
333
334 let tx = unsafe { PacketRingWriter::init(init_to_child, NETSOCK_RING_HALF, NETSOCK_SLOT_SIZE) };
335 let _ = unsafe { PacketRingWriter::init(child_to_init, NETSOCK_RING_HALF, NETSOCK_SLOT_SIZE) };
336 let rx = unsafe { PacketRingReader::attach(child_to_init, NETSOCK_RING_HALF) };
337
338 let _ = tx.try_push(line);
339
340 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_NOTIFICATION, 0, notif_slot, 1);
341 if r < 0 {
342 syscall::frame_unmap(mem_slot, NETSOCK_MAP_VADDR);
343 syscall::cap_revoke(mem_slot);
344 return None;
345 }
346
347 let r = syscall::cap_grant(mem_slot, child_proc_slot, 3, RIGHTS_ALL);
348 if r < 0 {
349 syscall::cap_revoke(notif_slot);
350 syscall::frame_unmap(mem_slot, NETSOCK_MAP_VADDR);
351 syscall::cap_revoke(mem_slot);
352 return None;
353 }
354
355 let r = syscall::cap_grant(notif_slot, child_proc_slot, 4, RIGHTS_ALL);
356 if r < 0 {
357 syscall::cap_revoke(notif_slot);
358 syscall::frame_unmap(mem_slot, NETSOCK_MAP_VADDR);
359 syscall::cap_revoke(mem_slot);
360 return None;
361 }
362
363 if has_netring {
364 let r = syscall::cap_grant(SLOT_SHELL_NOTIF, child_proc_slot, 5, 0b0010);
365 if r < 0 {
366 syscall::cap_revoke(notif_slot);
367 syscall::frame_unmap(mem_slot, NETSOCK_MAP_VADDR);
368 syscall::cap_revoke(mem_slot);
369 return None;
370 }
371 }
372
373 Some(ChildNetsock { tx, rx })
374}
375
376fn cleanup_child_netsock(mem_slot: u64, notif_slot: u64) {
377 syscall::frame_unmap(mem_slot, NETSOCK_MAP_VADDR);
378 syscall::cap_revoke(mem_slot);
379 syscall::cap_revoke(notif_slot);
380}
381
382fn relay_child_to_netstack(child_rx: &PacketRingReader, netstack_tx: &PacketRingWriter) -> bool {
383 let mut buf = [0u8; 64];
384 let mut relayed = false;
385
386 core::iter::from_fn(|| match netstack_tx.has_space() {
387 false => None,
388 true => match child_rx.try_pop(&mut buf[1..]) {
389 Some(n) if n > 0 => {
390 buf[0] = 0;
391 let _ = netstack_tx.try_push(&buf[..1 + n]);
392 relayed = true;
393 Some(())
394 }
395 _ => None,
396 },
397 })
398 .take(16)
399 .count();
400
401 if relayed {
402 syscall::notify_signal(SLOT_NETSTACK_NOTIF, 4);
403 }
404
405 relayed
406}
407
408fn relay_netstack_to_child(netstack_rx: &PacketRingReader, child_tx: &PacketRingWriter) -> bool {
409 let mut buf = [0u8; 128];
410 let mut relayed = false;
411
412 core::iter::from_fn(|| match netstack_rx.try_pop(&mut buf) {
413 Some(n) if n >= 2 => match child_tx.try_push(&buf[1..n]) {
414 true => {
415 relayed = true;
416 Some(())
417 }
418 false => None,
419 },
420 _ => None,
421 })
422 .take(16)
423 .count();
424
425 if relayed {
426 syscall::notify_signal(SLOT_CHILD_NETSOCK_NOTIF, 1);
427 }
428
429 relayed
430}
431
432#[allow(clippy::too_many_arguments)]
433fn spawn_module(
434 module_idx: u64,
435 line: &[u8],
436 has_netring: bool,
437 has_vfs: bool,
438 netstack_tx: Option<&PacketRingWriter>,
439 netstack_rx: Option<&PacketRingReader>,
440 vfs_tracker: &mut VfsClientTracker,
441 remote: bool,
442) -> Option<bool> {
443 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_ENDPOINT, 0, SLOT_CHILD_ENDPOINT, 1);
444 if r < 0 {
445 print!("error: failed to create endpoint\n");
446 return None;
447 }
448
449 let child_pid = syscall::proc_create(SLOT_UNTYPED, SLOT_CHILD_PROC);
450 if child_pid < 0 {
451 print!("error: failed to create process\n");
452 syscall::cap_revoke(SLOT_CHILD_ENDPOINT);
453 return None;
454 }
455
456 let r = syscall::proc_load_module(SLOT_CHILD_PROC, module_idx);
457 if r < 0 {
458 print!("error: failed to load module\n");
459 syscall::proc_destroy(SLOT_CHILD_PROC);
460 syscall::cap_revoke(SLOT_CHILD_ENDPOINT);
461 return None;
462 }
463
464 let r = syscall::cap_grant(SLOT_CHILD_ENDPOINT, SLOT_CHILD_PROC, 2, RIGHTS_ALL);
465 if r < 0 {
466 print!("error: failed to grant endpoint\n");
467 syscall::proc_destroy(SLOT_CHILD_PROC);
468 syscall::cap_revoke(SLOT_CHILD_ENDPOINT);
469 return None;
470 }
471
472 let vfs_client_idx = match has_vfs {
473 true => match vfs_tracker.alloc() {
474 None => None,
475 Some(idx) => {
476 syscall::cap_revoke(SLOT_TEMP_CLIENT_RING);
477 syscall::cap_revoke(SLOT_TEMP_CLIENT_NOTIF);
478 match create_vfs_client(
479 idx,
480 SLOT_CHILD_PROC,
481 SLOT_TEMP_CLIENT_RING,
482 SLOT_TEMP_CLIENT_NOTIF,
483 ) {
484 true => Some(idx),
485 false => {
486 vfs_tracker.release(idx);
487 None
488 }
489 }
490 }
491 },
492 false => None,
493 };
494
495 let _ = syscall::cap_grant(SLOT_UNTYPED, SLOT_CHILD_PROC, 30, RIGHTS_ALL);
496
497 let netsock = setup_child_netsock(
498 line,
499 has_netring,
500 SLOT_CHILD_PROC,
501 SLOT_NETSOCK_MEM,
502 SLOT_CHILD_NETSOCK_NOTIF,
503 );
504
505 let death_notif_slot = match has_netring {
506 true => SLOT_SHELL_NOTIF,
507 false => {
508 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_NOTIFICATION, 0, SLOT_CHILD_NOTIF, 1);
509 if r < 0 {
510 if netsock.is_some() {
511 cleanup_child_netsock(SLOT_NETSOCK_MEM, SLOT_CHILD_NETSOCK_NOTIF);
512 }
513 if let Some(idx) = vfs_client_idx {
514 destroy_vfs_client(idx);
515 vfs_tracker.release(idx);
516 }
517 syscall::proc_destroy(SLOT_CHILD_PROC);
518 syscall::cap_revoke(SLOT_CHILD_ENDPOINT);
519 return None;
520 }
521 SLOT_CHILD_NOTIF
522 }
523 };
524
525 syscall::proc_bind_death_notif(SLOT_CHILD_PROC, death_notif_slot, NOTIFY_BIT_CHILD_DEATH);
526
527 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_SCHED_CONTEXT, 0, SLOT_CHILD_SCHED, 1);
528 if r < 0 {
529 print!("error: failed to create sched context\n");
530 if netsock.is_some() {
531 cleanup_child_netsock(SLOT_NETSOCK_MEM, SLOT_CHILD_NETSOCK_NOTIF);
532 }
533 if let Some(idx) = vfs_client_idx {
534 destroy_vfs_client(idx);
535 vfs_tracker.release(idx);
536 }
537 syscall::proc_destroy(SLOT_CHILD_PROC);
538 if !has_netring {
539 syscall::cap_revoke(SLOT_CHILD_NOTIF);
540 }
541 syscall::cap_revoke(SLOT_CHILD_ENDPOINT);
542 return None;
543 }
544
545 let r = syscall::sched_configure(SLOT_CHILD_SCHED, 10_000_000, 10_000_000, 100);
546 if r < 0 {
547 print!("error: failed to configure sched context\n");
548 if netsock.is_some() {
549 cleanup_child_netsock(SLOT_NETSOCK_MEM, SLOT_CHILD_NETSOCK_NOTIF);
550 }
551 if let Some(idx) = vfs_client_idx {
552 destroy_vfs_client(idx);
553 vfs_tracker.release(idx);
554 }
555 syscall::proc_destroy(SLOT_CHILD_PROC);
556 syscall::cap_revoke(SLOT_CHILD_SCHED);
557 if !has_netring {
558 syscall::cap_revoke(SLOT_CHILD_NOTIF);
559 }
560 syscall::cap_revoke(SLOT_CHILD_ENDPOINT);
561 return None;
562 }
563
564 let r = syscall::sched_attach(SLOT_CHILD_SCHED, child_pid as u64);
565 if r < 0 {
566 print!("error: failed to attach sched context\n");
567 if netsock.is_some() {
568 cleanup_child_netsock(SLOT_NETSOCK_MEM, SLOT_CHILD_NETSOCK_NOTIF);
569 }
570 if let Some(idx) = vfs_client_idx {
571 destroy_vfs_client(idx);
572 vfs_tracker.release(idx);
573 }
574 syscall::proc_destroy(SLOT_CHILD_PROC);
575 syscall::cap_revoke(SLOT_CHILD_SCHED);
576 if !has_netring {
577 syscall::cap_revoke(SLOT_CHILD_NOTIF);
578 }
579 syscall::cap_revoke(SLOT_CHILD_ENDPOINT);
580 return None;
581 }
582
583 let bootstrap_val = match remote {
584 true => 2,
585 false => 0,
586 };
587 let r = syscall::proc_start(SLOT_CHILD_PROC, bootstrap_val);
588 if r < 0 {
589 print!("error: failed to start process\n");
590 if netsock.is_some() {
591 cleanup_child_netsock(SLOT_NETSOCK_MEM, SLOT_CHILD_NETSOCK_NOTIF);
592 }
593 if let Some(idx) = vfs_client_idx {
594 destroy_vfs_client(idx);
595 vfs_tracker.release(idx);
596 }
597 syscall::proc_destroy(SLOT_CHILD_PROC);
598 syscall::cap_revoke(SLOT_CHILD_SCHED);
599 if !has_netring {
600 syscall::cap_revoke(SLOT_CHILD_NOTIF);
601 }
602 syscall::cap_revoke(SLOT_CHILD_ENDPOINT);
603 return None;
604 }
605
606 let can_relay =
607 has_netring && netsock.is_some() && netstack_tx.is_some() && netstack_rx.is_some();
608
609 let had_output = match (can_relay, remote) {
610 (true, false) => {
611 let child_netsock = netsock.unwrap();
612 let ns_tx = netstack_tx.unwrap();
613 let ns_rx = netstack_rx.unwrap();
614 run_relay_loop(&child_netsock, ns_tx, ns_rx);
615 cleanup_child_netsock(SLOT_NETSOCK_MEM, SLOT_CHILD_NETSOCK_NOTIF);
616 true
617 }
618 (true, true) => {
619 let child_netsock = netsock.unwrap();
620 let ns_tx = netstack_tx.unwrap();
621 let ns_rx = netstack_rx.unwrap();
622 run_relay_loop_remote(&child_netsock, ns_tx, ns_rx);
623 cleanup_child_netsock(SLOT_NETSOCK_MEM, SLOT_CHILD_NETSOCK_NOTIF);
624 true
625 }
626 (_, true) => {
627 run_simple_wait_remote(death_notif_slot);
628 if netsock.is_some() {
629 cleanup_child_netsock(SLOT_NETSOCK_MEM, SLOT_CHILD_NETSOCK_NOTIF);
630 }
631 false
632 }
633 (_, false) => {
634 run_simple_wait(death_notif_slot);
635 if netsock.is_some() {
636 cleanup_child_netsock(SLOT_NETSOCK_MEM, SLOT_CHILD_NETSOCK_NOTIF);
637 }
638 false
639 }
640 };
641
642 if let Some(idx) = vfs_client_idx {
643 destroy_vfs_client(idx);
644 vfs_tracker.release(idx);
645 }
646
647 syscall::cap_revoke(SLOT_CHILD_SCHED);
648 if !has_netring {
649 syscall::cap_revoke(SLOT_CHILD_NOTIF);
650 }
651 syscall::cap_revoke(SLOT_CHILD_ENDPOINT);
652
653 Some(had_output)
654}
655
656fn run_simple_wait(death_notif_slot: u64) {
657 loop {
658 let (_, bits) = syscall::notify_wait(death_notif_slot);
659 if bits & NOTIFY_BIT_CHILD_DEATH != 0 {
660 syscall::proc_destroy(SLOT_CHILD_PROC);
661 break;
662 }
663 }
664}
665
666fn run_simple_wait_remote(death_notif_slot: u64) {
667 syscall::ntfn_bind(death_notif_slot);
668 loop {
669 let (status, msg) = syscall::recv(SLOT_CHILD_ENDPOINT);
670 match status == syscall::BOUND_NOTIFICATION_BADGE {
671 true => {
672 if msg[1] & NOTIFY_BIT_CHILD_DEATH != 0 {
673 syscall::proc_destroy(SLOT_CHILD_PROC);
674 break;
675 }
676 }
677 false if status >= 0 => {
678 let _ = syscall::send(2, msg);
679 }
680 _ => {}
681 }
682 }
683 syscall::ntfn_unbind();
684}
685
686fn drain_stale_netstack_replies(netstack_rx: &PacketRingReader) {
687 let mut discard = [0u8; 128];
688 core::iter::from_fn(|| netstack_rx.try_pop(&mut discard).map(|_| ()))
689 .take(64)
690 .count();
691}
692
693fn run_relay_loop(
694 child_netsock: &ChildNetsock,
695 netstack_tx: &PacketRingWriter,
696 netstack_rx: &PacketRingReader,
697) {
698 drain_stale_netstack_replies(netstack_rx);
699
700 loop {
701 let (_, bits) = syscall::notify_wait(SLOT_SHELL_NOTIF);
702
703 relay_child_to_netstack(&child_netsock.rx, netstack_tx);
704 relay_netstack_to_child(netstack_rx, &child_netsock.tx);
705
706 if bits & NOTIFY_BIT_CHILD_DEATH != 0 {
707 relay_child_to_netstack(&child_netsock.rx, netstack_tx);
708 syscall::proc_destroy(SLOT_CHILD_PROC);
709 break;
710 }
711 }
712}
713
714fn run_relay_loop_remote(
715 child_netsock: &ChildNetsock,
716 netstack_tx: &PacketRingWriter,
717 netstack_rx: &PacketRingReader,
718) {
719 drain_stale_netstack_replies(netstack_rx);
720
721 syscall::ntfn_bind(SLOT_SHELL_NOTIF);
722 loop {
723 let (status, msg) = syscall::recv(SLOT_CHILD_ENDPOINT);
724 match status == syscall::BOUND_NOTIFICATION_BADGE {
725 true => {
726 let bits = msg[1];
727 relay_child_to_netstack(&child_netsock.rx, netstack_tx);
728 relay_netstack_to_child(netstack_rx, &child_netsock.tx);
729
730 if bits & NOTIFY_BIT_CHILD_DEATH != 0 {
731 relay_child_to_netstack(&child_netsock.rx, netstack_tx);
732 syscall::proc_destroy(SLOT_CHILD_PROC);
733 break;
734 }
735 }
736 false if status >= 0 => {
737 let _ = syscall::send(2, msg);
738 }
739 _ => {}
740 }
741 }
742 syscall::ntfn_unbind();
743}
744
745#[allow(clippy::too_many_arguments)]
746fn spawn_conc_child(
747 module_idx: u64,
748 args: &[u8],
749 proc_slot: u64,
750 sched_slot: u64,
751 endpoint_slot: u64,
752 death_notif_slot: u64,
753 death_bit: u64,
754 vfs_client_idx: u8,
755 vfs_tracker: &mut VfsClientTracker,
756 netsock_mem: u64,
757 netsock_notif: u64,
758 vfs_ring_tmp: u64,
759 vfs_notif_tmp: u64,
760) -> bool {
761 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_ENDPOINT, 0, endpoint_slot, 1);
762 if r < 0 {
763 return false;
764 }
765
766 let child_pid = syscall::proc_create(SLOT_UNTYPED, proc_slot);
767 if child_pid < 0 {
768 syscall::cap_revoke(endpoint_slot);
769 return false;
770 }
771
772 let r = syscall::proc_load_module(proc_slot, module_idx);
773 if r < 0 {
774 syscall::proc_destroy(proc_slot);
775 syscall::cap_revoke(endpoint_slot);
776 return false;
777 }
778
779 let r = syscall::cap_grant(endpoint_slot, proc_slot, 2, RIGHTS_ALL);
780 if r < 0 {
781 syscall::proc_destroy(proc_slot);
782 syscall::cap_revoke(endpoint_slot);
783 return false;
784 }
785
786 match create_vfs_client(vfs_client_idx, proc_slot, vfs_ring_tmp, vfs_notif_tmp) {
787 true => {}
788 false => {
789 vfs_tracker.release(vfs_client_idx);
790 syscall::proc_destroy(proc_slot);
791 syscall::cap_revoke(endpoint_slot);
792 return false;
793 }
794 }
795
796 let _ = syscall::cap_grant(SLOT_UNTYPED, proc_slot, 30, RIGHTS_ALL);
797
798 let netsock = setup_child_netsock(args, false, proc_slot, netsock_mem, netsock_notif);
799 if netsock.is_none() {
800 destroy_vfs_client(vfs_client_idx);
801 vfs_tracker.release(vfs_client_idx);
802 syscall::cap_revoke(vfs_ring_tmp);
803 syscall::cap_revoke(vfs_notif_tmp);
804 syscall::proc_destroy(proc_slot);
805 syscall::cap_revoke(endpoint_slot);
806 return false;
807 }
808
809 syscall::proc_bind_death_notif(proc_slot, death_notif_slot, death_bit);
810
811 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_SCHED_CONTEXT, 0, sched_slot, 1);
812 if r < 0 {
813 cleanup_child_netsock(netsock_mem, netsock_notif);
814 destroy_vfs_client(vfs_client_idx);
815 vfs_tracker.release(vfs_client_idx);
816 syscall::cap_revoke(vfs_ring_tmp);
817 syscall::cap_revoke(vfs_notif_tmp);
818 syscall::proc_destroy(proc_slot);
819 syscall::cap_revoke(endpoint_slot);
820 return false;
821 }
822
823 let r = syscall::sched_configure(sched_slot, 10_000_000, 10_000_000, 100);
824 if r < 0 {
825 cleanup_child_netsock(netsock_mem, netsock_notif);
826 destroy_vfs_client(vfs_client_idx);
827 vfs_tracker.release(vfs_client_idx);
828 syscall::cap_revoke(vfs_ring_tmp);
829 syscall::cap_revoke(vfs_notif_tmp);
830 syscall::proc_destroy(proc_slot);
831 syscall::cap_revoke(sched_slot);
832 syscall::cap_revoke(endpoint_slot);
833 return false;
834 }
835
836 let r = syscall::sched_attach(sched_slot, child_pid as u64);
837 if r < 0 {
838 cleanup_child_netsock(netsock_mem, netsock_notif);
839 destroy_vfs_client(vfs_client_idx);
840 vfs_tracker.release(vfs_client_idx);
841 syscall::cap_revoke(vfs_ring_tmp);
842 syscall::cap_revoke(vfs_notif_tmp);
843 syscall::proc_destroy(proc_slot);
844 syscall::cap_revoke(sched_slot);
845 syscall::cap_revoke(endpoint_slot);
846 return false;
847 }
848
849 let r = syscall::proc_start(proc_slot, 0);
850 if r < 0 {
851 cleanup_child_netsock(netsock_mem, netsock_notif);
852 destroy_vfs_client(vfs_client_idx);
853 vfs_tracker.release(vfs_client_idx);
854 syscall::cap_revoke(vfs_ring_tmp);
855 syscall::cap_revoke(vfs_notif_tmp);
856 syscall::proc_destroy(proc_slot);
857 syscall::cap_revoke(sched_slot);
858 syscall::cap_revoke(endpoint_slot);
859 return false;
860 }
861
862 syscall::frame_unmap(netsock_mem, NETSOCK_MAP_VADDR);
863 true
864}
865
866fn cleanup_conc_child(
867 proc_slot: u64,
868 sched_slot: u64,
869 endpoint_slot: u64,
870 vfs_client_idx: u8,
871 vfs_tracker: &mut VfsClientTracker,
872) {
873 destroy_vfs_client(vfs_client_idx);
874 vfs_tracker.release(vfs_client_idx);
875 syscall::proc_destroy(proc_slot);
876 syscall::cap_revoke(sched_slot);
877 syscall::cap_revoke(endpoint_slot);
878}
879
880fn run_conctest(vfs_tracker: &mut VfsClientTracker) {
881 let module_idx = match find_module_by_name(b"fstest") {
882 Some(idx) => idx,
883 None => {
884 show!(rsh, error, "fstest module not found");
885 return;
886 }
887 };
888
889 let vfs_idx_a = match vfs_tracker.alloc() {
890 Some(idx) => idx,
891 None => {
892 show!(rsh, error, "no free vfs client slots");
893 return;
894 }
895 };
896
897 let vfs_idx_b = match vfs_tracker.alloc() {
898 Some(idx) => idx,
899 None => {
900 vfs_tracker.release(vfs_idx_a);
901 show!(rsh, error, "no free vfs client slots for b");
902 return;
903 }
904 };
905
906 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_NOTIFICATION, 0, SLOT_CHILD_NOTIF, 1);
907 if r < 0 {
908 vfs_tracker.release(vfs_idx_b);
909 vfs_tracker.release(vfs_idx_a);
910 show!(rsh, error, "failed to create death notif");
911 return;
912 }
913
914 syscall::cap_revoke(SLOT_TEMP_CLIENT_RING);
915 syscall::cap_revoke(SLOT_TEMP_CLIENT_NOTIF);
916 syscall::cap_revoke(SLOT_NETSOCK_MEM);
917 syscall::cap_revoke(SLOT_CHILD_NETSOCK_NOTIF);
918
919 let ok_a = spawn_conc_child(
920 module_idx,
921 b"fstest concurrent-a",
922 SLOT_CHILD_PROC,
923 SLOT_CHILD_SCHED,
924 SLOT_CHILD_ENDPOINT,
925 SLOT_CHILD_NOTIF,
926 0x01,
927 vfs_idx_a,
928 vfs_tracker,
929 SLOT_NETSOCK_MEM,
930 SLOT_CHILD_NETSOCK_NOTIF,
931 SLOT_TEMP_CLIENT_RING,
932 SLOT_TEMP_CLIENT_NOTIF,
933 );
934
935 if !ok_a {
936 vfs_tracker.release(vfs_idx_b);
937 syscall::cap_revoke(SLOT_CHILD_NOTIF);
938 show!(rsh, error, "failed to spawn child a");
939 return;
940 }
941
942 let ok_b = spawn_conc_child(
943 module_idx,
944 b"fstest concurrent-b",
945 SLOT_CHILD_PROC_B,
946 SLOT_CHILD_SCHED_B,
947 SLOT_CHILD_ENDPOINT_B,
948 SLOT_CHILD_NOTIF,
949 0x02,
950 vfs_idx_b,
951 vfs_tracker,
952 SLOT_NETSOCK_MEM_B,
953 SLOT_CHILD_NETSOCK_NOTIF_B,
954 SLOT_TEMP_CLIENT_RING_B,
955 SLOT_TEMP_CLIENT_NOTIF_B,
956 );
957
958 if !ok_b {
959 cleanup_conc_child(
960 SLOT_CHILD_PROC,
961 SLOT_CHILD_SCHED,
962 SLOT_CHILD_ENDPOINT,
963 vfs_idx_a,
964 vfs_tracker,
965 );
966 syscall::cap_revoke(SLOT_TEMP_CLIENT_RING);
967 syscall::cap_revoke(SLOT_TEMP_CLIENT_NOTIF);
968 syscall::cap_revoke(SLOT_NETSOCK_MEM);
969 syscall::cap_revoke(SLOT_CHILD_NETSOCK_NOTIF);
970 syscall::cap_revoke(SLOT_CHILD_NOTIF);
971 show!(rsh, error, "failed to spawn child b");
972 return;
973 }
974
975 let mut deaths: u64 = 0;
976 core::iter::from_fn(|| match deaths & 0x03 == 0x03 {
977 true => None,
978 false => {
979 let (_, bits) = syscall::notify_wait(SLOT_CHILD_NOTIF);
980 deaths |= bits;
981 Some(())
982 }
983 })
984 .take(100)
985 .count();
986
987 let both_dead = deaths & 0x03 == 0x03;
988 if !both_dead {
989 show!(rsh, error, "timed out waiting for children");
990 }
991
992 cleanup_conc_child(
993 SLOT_CHILD_PROC,
994 SLOT_CHILD_SCHED,
995 SLOT_CHILD_ENDPOINT,
996 vfs_idx_a,
997 vfs_tracker,
998 );
999 cleanup_conc_child(
1000 SLOT_CHILD_PROC_B,
1001 SLOT_CHILD_SCHED_B,
1002 SLOT_CHILD_ENDPOINT_B,
1003 vfs_idx_b,
1004 vfs_tracker,
1005 );
1006 syscall::cap_revoke(SLOT_TEMP_CLIENT_RING);
1007 syscall::cap_revoke(SLOT_TEMP_CLIENT_NOTIF);
1008 syscall::cap_revoke(SLOT_TEMP_CLIENT_RING_B);
1009 syscall::cap_revoke(SLOT_TEMP_CLIENT_NOTIF_B);
1010 syscall::cap_revoke(SLOT_NETSOCK_MEM);
1011 syscall::cap_revoke(SLOT_CHILD_NETSOCK_NOTIF);
1012 syscall::cap_revoke(SLOT_NETSOCK_MEM_B);
1013 syscall::cap_revoke(SLOT_CHILD_NETSOCK_NOTIF_B);
1014 syscall::cap_revoke(SLOT_CHILD_NOTIF);
1015
1016 if !both_dead {
1017 return;
1018 }
1019
1020 let mut fs_client = unsafe {
1021 lancer_user::fs::FsClient::reattach(
1022 VFS_RING_VADDR as usize,
1023 SLOT_VFS_NOTIF as u8,
1024 SLOT_VFS_CLIENT_NOTIF as u8,
1025 )
1026 };
1027
1028 let mut pass = 0u32;
1029 let mut fail = 0u32;
1030
1031 let mut buf_a = [0u8; 64];
1032 match fs_client.open(0, b"conc_a", lancer_vfs_proto::FsRights::READ) {
1033 Ok(h) => {
1034 match fs_client.read(h, 0, &mut buf_a) {
1035 Ok(n) => match &buf_a[..n] == b"alpha_data" {
1036 true => pass += 1,
1037 false => {
1038 show!(rsh, error, "conc_a mismatch");
1039 fail += 1;
1040 }
1041 },
1042 Err(_) => {
1043 show!(rsh, error, "read conc_a failed");
1044 fail += 1;
1045 }
1046 }
1047 let _ = fs_client.close(h);
1048 let _ = fs_client.unlink(0, b"conc_a");
1049 }
1050 Err(_) => {
1051 show!(rsh, error, "open conc_a failed");
1052 fail += 1;
1053 }
1054 }
1055
1056 let mut buf_b = [0u8; 64];
1057 match fs_client.open(0, b"conc_b", lancer_vfs_proto::FsRights::READ) {
1058 Ok(h) => {
1059 match fs_client.read(h, 0, &mut buf_b) {
1060 Ok(n) => match &buf_b[..n] == b"bravo_data" {
1061 true => pass += 1,
1062 false => {
1063 show!(rsh, error, "conc_b mismatch");
1064 fail += 1;
1065 }
1066 },
1067 Err(_) => {
1068 show!(rsh, error, "read conc_b failed");
1069 fail += 1;
1070 }
1071 }
1072 let _ = fs_client.close(h);
1073 let _ = fs_client.unlink(0, b"conc_b");
1074 }
1075 Err(_) => {
1076 show!(rsh, error, "open conc_b failed");
1077 fail += 1;
1078 }
1079 }
1080
1081 match fail {
1082 0 => show!(rsh, "results: {pass} pass, {fail} fail - ok"),
1083 _ => show!(rsh, error, "results: {pass} pass, {fail} fail - failed"),
1084 }
1085}
1086
1087#[allow(clippy::too_many_arguments)]
1088fn process_line(
1089 line: &[u8],
1090 has_netring: bool,
1091 has_vfs: bool,
1092 netstack_tx: Option<&PacketRingWriter>,
1093 netstack_rx: Option<&PacketRingReader>,
1094 cwd: &mut [u8; 256],
1095 cwd_len: &mut usize,
1096 vfs_tracker: &mut VfsClientTracker,
1097 remote: bool,
1098) {
1099 if line.is_empty() {
1100 return;
1101 }
1102
1103 let cmd = first_word(line);
1104
1105 if bytes_eq(cmd, b"exit") {
1106 syscall::exit();
1107 }
1108
1109 if bytes_eq(cmd, b"pwd") {
1110 if let Ok(s) = core::str::from_utf8(&cwd[..*cwd_len]) {
1111 print!("{s}\n");
1112 }
1113 return;
1114 }
1115
1116 if bytes_eq(cmd, b"conctest") {
1117 match has_vfs {
1118 true => run_conctest(vfs_tracker),
1119 false => show!(rsh, warn, "vfs not available"),
1120 }
1121 return;
1122 }
1123
1124 let mut resolved = [0u8; 256];
1125 let resolved_len = build_resolved_line(&cwd[..], *cwd_len, line, &mut resolved);
1126 let child_line = &resolved[..resolved_len];
1127
1128 if bytes_eq(cmd, b"cd") {
1129 let orig_rest = skip_leading_spaces(&line[cmd.len()..]);
1130 if first_word(orig_rest).is_empty() {
1131 return;
1132 }
1133 let after_cmd = &resolved[cmd.len()..resolved_len];
1134 let rest = skip_leading_spaces(after_cmd);
1135 let target = first_word(rest);
1136 match find_module_by_name(b"cd") {
1137 Some(idx) => {
1138 if let Some(false) = spawn_module(
1139 idx,
1140 child_line,
1141 false,
1142 has_vfs,
1143 None,
1144 None,
1145 vfs_tracker,
1146 remote,
1147 ) {
1148 let t_len = target.len().min(256);
1149 cwd[..t_len].copy_from_slice(&target[..t_len]);
1150 *cwd_len = t_len;
1151 }
1152 }
1153 None => print!("cd: command not found\n"),
1154 }
1155 return;
1156 }
1157
1158 match find_module_by_name(cmd) {
1159 Some(idx) => {
1160 let _ = spawn_module(
1161 idx,
1162 child_line,
1163 has_netring,
1164 has_vfs,
1165 netstack_tx,
1166 netstack_rx,
1167 vfs_tracker,
1168 remote,
1169 );
1170 }
1171 None => match core::str::from_utf8(cmd) {
1172 Ok(s) => print!("{s}: command not found\n"),
1173 Err(_) => print!("(invalid): command not found\n"),
1174 },
1175 }
1176}
1177
1178#[allow(clippy::too_many_arguments)]
1179fn process_input_byte(
1180 b: u8,
1181 line: &mut LineBuf,
1182 has_netring: bool,
1183 has_vfs: bool,
1184 netstack_tx: Option<&PacketRingWriter>,
1185 netstack_rx: Option<&PacketRingReader>,
1186 cwd: &mut [u8; 256],
1187 cwd_len: &mut usize,
1188 vfs_tracker: &mut VfsClientTracker,
1189 remote: bool,
1190) {
1191 match b {
1192 0x0D | 0x0A => {
1193 print!("\n");
1194 process_line(
1195 line.as_bytes(),
1196 has_netring,
1197 has_vfs,
1198 netstack_tx,
1199 netstack_rx,
1200 cwd,
1201 cwd_len,
1202 vfs_tracker,
1203 remote,
1204 );
1205 line.clear();
1206 print_prompt(cwd, *cwd_len);
1207 }
1208 0x7F | 0x08 => {
1209 if line.pop() {
1210 print!("\x08 \x08");
1211 }
1212 }
1213 0x20..=0x7E => {
1214 if line.push(b) {
1215 let ch = [b];
1216 if let Ok(s) = core::str::from_utf8(&ch) {
1217 print!("{s}");
1218 }
1219 }
1220 }
1221 _ => {}
1222 }
1223}
1224
1225#[unsafe(no_mangle)]
1226pub extern "C" fn lancer_main() -> ! {
1227 let remote = !lancer_user::io::debug_print_enabled();
1228
1229 let has_netring = (0..NETSTACK_RING_FRAME_COUNT).all(|i| {
1230 syscall::frame_map(
1231 NETSTACK_RING_BASE_SLOT + i,
1232 NETSTACK_RING_VADDR + i * 4096,
1233 1,
1234 ) >= 0
1235 });
1236 let (netstack_tx, netstack_rx) = match has_netring {
1237 true => {
1238 let rx_base = NETSTACK_RING_VADDR as *mut u8;
1239 let tx_base = (NETSTACK_RING_VADDR + NETSTACK_RING_HALF as u64) as *mut u8;
1240 let _ = unsafe { PacketRingWriter::init(rx_base, NETSTACK_RING_HALF, 128) };
1241 let tx = unsafe { PacketRingWriter::init(tx_base, NETSTACK_RING_HALF, 128) };
1242 let rx = unsafe { PacketRingReader::attach(rx_base, NETSTACK_RING_HALF) };
1243 show!(rsh, "netstack ring mapped");
1244 (Some(tx), Some(rx))
1245 }
1246 false => {
1247 show!(rsh, warn, "netstack ring not available");
1248 (None, None)
1249 }
1250 };
1251
1252 let has_vfs = (0..VFS_RING_FRAME_COUNT_SHELL)
1253 .all(|i| syscall::frame_map(VFS_RING_BASE_SLOT + i, VFS_RING_VADDR + i * 4096, 1) >= 0);
1254 match has_vfs {
1255 true => show!(rsh, "vfs ring mapped"),
1256 false => show!(rsh, warn, "vfs not available"),
1257 }
1258
1259 let mut cwd = [0u8; 256];
1260 cwd[0] = b'/';
1261 let mut cwd_len: usize = 1;
1262 let mut vfs_tracker = VfsClientTracker::new();
1263
1264 print_prompt(&cwd, cwd_len);
1265
1266 let mut line = LineBuf::new();
1267
1268 match remote {
1269 true => {
1270 let r = syscall::frame_map(1, INPUT_RING_VADDR, 1);
1271 if r < 0 {
1272 show!(rsh, error, "input ring frame_map failed");
1273 syscall::exit();
1274 }
1275 let input_rx =
1276 unsafe { PacketRingReader::attach(INPUT_RING_VADDR as *mut u8, INPUT_RING_SIZE) };
1277
1278 loop {
1279 let (_, _bits) = syscall::notify_wait(SLOT_SHELL_NOTIF);
1280
1281 let mut buf = [0u8; 64];
1282 core::iter::from_fn(|| {
1283 input_rx.try_pop(&mut buf).map(|n| {
1284 (0..n).fold((), |(), i| {
1285 process_input_byte(
1286 buf[i],
1287 &mut line,
1288 has_netring,
1289 has_vfs,
1290 netstack_tx.as_ref(),
1291 netstack_rx.as_ref(),
1292 &mut cwd,
1293 &mut cwd_len,
1294 &mut vfs_tracker,
1295 true,
1296 );
1297 });
1298 })
1299 })
1300 .take(64)
1301 .count();
1302 }
1303 }
1304 false => loop {
1305 let (_status, msg) = syscall::recv(INPUT_ENDPOINT);
1306
1307 let mut raw_bytes = [0u8; MAX_IPC_BYTES];
1308 let count = unpack_bytes(&msg, &mut raw_bytes);
1309
1310 (0..count).fold((), |(), i| {
1311 process_input_byte(
1312 raw_bytes[i],
1313 &mut line,
1314 has_netring,
1315 has_vfs,
1316 netstack_tx.as_ref(),
1317 netstack_rx.as_ref(),
1318 &mut cwd,
1319 &mut cwd_len,
1320 &mut vfs_tracker,
1321 false,
1322 );
1323 });
1324 },
1325 }
1326}