#![no_std] #![no_main] #![allow(clippy::single_match)] use lancer_user::bootstrap as boot; use lancer_user::show; use lancer_user::syscall; use lancer_user::untyped::UntypedAllocator; const SERIAL_ENDPOINT: u64 = 1; const FB_ENDPOINT: u64 = 2; const TAG_NOTIFICATION: u64 = 2; const TAG_SCHED_CONTEXT: u64 = 5; const RIGHTS_ALL: u64 = 0b1111; const SLOT_INIT_NOTIF: u64 = 9; const SLOT_NETSTACK_NOTIF: u64 = 10; const SLOT_VFS_CLIENT_NOTIF: u64 = 12; const SLOT_VFS_NOTIF: u64 = 13; const SLOT_VFS_PROC: u64 = 15; const VFS_RING_BASE_SLOT: u64 = 320; const VFS_RING_FRAME_COUNT: u64 = 16; const NETSTACK_RING_BASE_SLOT: u64 = 288; const NETSTACK_RING_FRAME_COUNT: u64 = 2; const SHELL_NETSTACK_RING_DST: u64 = 64; const SHELL_VFS_RING_DST: u64 = 128; const VFS_RING_VADDR: u64 = 0x7000_0000; const NOTIFY_BIT_CHILD_DEATH: u64 = 0x01; const SLOT_SHELL_PROC: u64 = 25; const SLOT_SHELL_SCHED: u64 = 26; const SLOT_SHELL_DEATH_NOTIF: u64 = 27; #[allow(dead_code)] mod staging { pub const SERIAL_PROC: u64 = 400; pub const SERIAL_SCHED: u64 = 401; pub const SERIAL_IRQ: u64 = 402; pub const SERIAL_NOTIF: u64 = 403; pub const PS2KBD_PROC: u64 = 410; pub const PS2KBD_SCHED: u64 = 411; pub const PS2KBD_IRQ: u64 = 412; pub const PS2KBD_NOTIF: u64 = 413; pub const FBCONSOLE_PROC: u64 = 420; pub const FBCONSOLE_SCHED: u64 = 421; pub const FBCONSOLE_FB: u64 = 422; pub const FBCONSOLE_RING_BASE: u64 = 424; pub const FBCONSOLE_RING_COUNT: u64 = 16; pub const NVME_PROC: u64 = 450; pub const NVME_SCHED: u64 = 451; pub const NVME_PCI: u64 = 452; pub const NVME_IRQ: u64 = 453; pub const NVME_NOTIF: u64 = 454; pub const LANCERFS_PROC: u64 = 500; pub const LANCERFS_SCHED: u64 = 501; pub const LANCERFS_NOTIF: u64 = 502; pub const LANCERFS_BLOCK_RING_BASE: u64 = 504; pub const LANCERFS_BLOCK_RING_COUNT: u64 = 16; pub const VFS_PROC_SLOT: u64 = 530; pub const VFS_SCHED: u64 = 531; pub const VFS_NOTIF: u64 = 532; pub const VFS_LANCERFS_RING_BASE: u64 = 534; pub const VFS_LANCERFS_RING_COUNT: u64 = 16; pub const VFS_RAMFS_RING_BASE: u64 = 560; pub const VFS_RAMFS_RING_COUNT: u64 = 16; pub const VFS_CLIENT_RING_BASE: u64 = 576; pub const VFS_CLIENT_RING_COUNT: u64 = 16; pub const RAMFS_PROC: u64 = 600; pub const RAMFS_SCHED: u64 = 601; pub const RAMFS_NOTIF: u64 = 602; pub const RAMFS_VFS_RING_BASE: u64 = 604; pub const RAMFS_VFS_RING_COUNT: u64 = 16; pub const VIRTIO_NET_PROC: u64 = 640; pub const VIRTIO_NET_SCHED: u64 = 641; pub const VIRTIO_NET_PCI: u64 = 642; pub const VIRTIO_NET_IRQ: u64 = 643; pub const VIRTIO_NET_NOTIF: u64 = 644; pub const VIRTIO_NET_PACKET_RING_BASE: u64 = 646; pub const VIRTIO_NET_PACKET_RING_COUNT: u64 = 16; pub const NETSTACK_PROC: u64 = 680; pub const NETSTACK_SCHED: u64 = 681; pub const NETSTACK_NOTIF: u64 = 682; pub const NETSTACK_DRIVER_RING_BASE: u64 = 684; pub const NETSTACK_DRIVER_RING_COUNT: u64 = 16; pub const NETSTACK_SHELL_RING_BASE: u64 = 700; pub const NETSTACK_SHELL_RING_COUNT: u64 = 4; pub const NETSTACK_INIT_RING_BASE: u64 = 720; pub const NETSTACK_INIT_RING_COUNT: u64 = 2; pub const REMOTE_SHELL_PROC: u64 = 750; pub const REMOTE_SHELL_SCHED: u64 = 751; pub const REMOTE_SHELL_NOTIF: u64 = 752; pub const REMOTE_SHELL_NET_RING_BASE: u64 = 754; pub const REMOTE_SHELL_VFS_RING_BASE: u64 = 770; pub const REMOTE_SHELL_VFS_RING_COUNT: u64 = 16; } mod driver_slots { pub const EP: u64 = 1; pub const IRQ: u64 = 2; pub const NOTIF: u64 = 3; pub const FB: u64 = 2; pub const CONSOLE_RING_BASE: u64 = 64; pub const PCI: u64 = 2; pub const PCI_IRQ: u64 = 3; pub const PCI_NOTIF: u64 = 4; pub const CLIENT_NOTIF: u64 = 6; pub const NETSTACK_NOTIF: u64 = 7; pub const BLOCK_RING_BASE: u64 = 64; pub const FS_NOTIF: u64 = 3; pub const FS_DRIVER_NOTIF: u64 = 4; pub const FS_CLIENT_NOTIF: u64 = 6; pub const FS_CLIENT_RING_BASE: u64 = 96; pub const VFS_NOTIF: u64 = 3; pub const VFS_LANCERFS_NOTIF: u64 = 4; pub const VFS_LANCERFS_RING_BASE: u64 = 64; pub const VFS_CLIENT0_RING_BASE: u64 = 160; pub const VFS_CLIENT0_NOTIF: u64 = 176; pub const VFS_RAMFS_NOTIF: u64 = 6; pub const VFS_RAMFS_RING_BASE: u64 = 96; pub const RAMFS_NOTIF: u64 = 3; pub const RAMFS_VFS_NOTIF: u64 = 4; pub const RAMFS_CLIENT_RING_BASE: u64 = 64; pub const NET_EP: u64 = 1; pub const NET_NOTIF: u64 = 3; pub const NET_DRIVER_NOTIF: u64 = 4; pub const NET_SHELL_NOTIF: u64 = 6; pub const NET_INIT_NOTIF: u64 = 8; pub const NET_PACKET_RING_BASE: u64 = 64; pub const NET_SHELL_RING_BASE: u64 = 96; pub const NET_INIT_RING_BASE: u64 = 128; pub const RSHELL_EP: u64 = 1; pub const RSHELL_NOTIF: u64 = 3; pub const RSHELL_NETSTACK_NOTIF: u64 = 4; pub const RSHELL_SHELL_RING_BASE: u64 = 64; pub const RSHELL_VFS_RING_BASE: u64 = 128; } struct BootState { has_serial: bool, has_ps2kbd: bool, has_fbconsole: bool, has_nvme: bool, has_lancerfs: bool, has_vfs: bool, has_ramfs: bool, has_virtio_net: bool, has_netstack: bool, has_remote_shell: bool, has_vfs_client: bool, } impl BootState { const fn new() -> Self { Self { has_serial: false, has_ps2kbd: false, has_fbconsole: false, has_nvme: false, has_lancerfs: false, has_vfs: false, has_ramfs: false, has_virtio_net: false, has_netstack: false, has_remote_shell: false, has_vfs_client: false, } } } fn bootstrap_serial(allocator: &mut UntypedAllocator, com1_gsi: u64) -> bool { let pid = match boot::create_process(allocator, b"serial", staging::SERIAL_PROC) { Some(p) => p, None => return false, }; boot::create_endpoint(allocator, SERIAL_ENDPOINT); boot::grant_cap(SERIAL_ENDPOINT, staging::SERIAL_PROC, driver_slots::EP); boot::create_irq_handler( allocator, staging::SERIAL_IRQ, com1_gsi, boot::COM1_VECTOR, 0x3F8, 8, ); boot::create_notification(allocator, staging::SERIAL_NOTIF); syscall::irq_bind(staging::SERIAL_IRQ, staging::SERIAL_NOTIF, 0); boot::grant_cap(staging::SERIAL_IRQ, staging::SERIAL_PROC, driver_slots::IRQ); boot::grant_cap( staging::SERIAL_NOTIF, staging::SERIAL_PROC, driver_slots::NOTIF, ); boot::start_with_sched( allocator, staging::SERIAL_PROC, staging::SERIAL_SCHED, pid, u64::MAX, ) } fn bootstrap_ps2kbd(allocator: &mut UntypedAllocator, kbd_gsi: u64) -> bool { let pid = match boot::create_process(allocator, b"ps2kbd", staging::PS2KBD_PROC) { Some(p) => p, None => return false, }; boot::grant_cap(SERIAL_ENDPOINT, staging::PS2KBD_PROC, driver_slots::EP); boot::create_irq_handler( allocator, staging::PS2KBD_IRQ, kbd_gsi, boot::KBD_VECTOR, 0x60, 5, ); boot::create_notification(allocator, staging::PS2KBD_NOTIF); syscall::irq_bind(staging::PS2KBD_IRQ, staging::PS2KBD_NOTIF, 0); boot::grant_cap(staging::PS2KBD_IRQ, staging::PS2KBD_PROC, driver_slots::IRQ); boot::grant_cap( staging::PS2KBD_NOTIF, staging::PS2KBD_PROC, driver_slots::NOTIF, ); boot::start_with_sched( allocator, staging::PS2KBD_PROC, staging::PS2KBD_SCHED, pid, u64::MAX, ) } fn bootstrap_fbconsole(allocator: &mut UntypedAllocator) -> bool { let pid = match boot::create_process(allocator, b"fbconsole", staging::FBCONSOLE_PROC) { Some(p) => p, None => return false, }; boot::create_endpoint(allocator, FB_ENDPOINT); boot::grant_cap(FB_ENDPOINT, staging::FBCONSOLE_PROC, driver_slots::EP); let r = syscall::fb_create_cap(staging::FBCONSOLE_FB); match r < 0 { true => { show!(init, warn, "failed to create fb cap"); return false; } false => {} } boot::grant_cap( staging::FBCONSOLE_FB, staging::FBCONSOLE_PROC, driver_slots::FB, ); let ring_count = syscall::console_ring_create_cap(staging::FBCONSOLE_RING_BASE); match ring_count <= 0 { true => { show!(init, warn, "failed to create console ring caps"); return false; } false => {} } boot::grant_frames( staging::FBCONSOLE_RING_BASE, staging::FBCONSOLE_PROC, driver_slots::CONSOLE_RING_BASE, ring_count as u64, ); boot::start_with_sched( allocator, staging::FBCONSOLE_PROC, staging::FBCONSOLE_SCHED, pid, u64::MAX, ) } fn bootstrap_nvme(allocator: &mut UntypedAllocator, device_idx: u8) -> bool { let pid = match boot::create_process(allocator, b"nvme", staging::NVME_PROC) { Some(p) => p, None => return false, }; boot::grant_cap(SERIAL_ENDPOINT, staging::NVME_PROC, driver_slots::EP); let r = syscall::pci_create_cap(device_idx as u64, staging::NVME_PCI); if r < 0 { show!(init, warn, "nvme: pci_create_cap failed"); return false; } show!(init, "nvme: pci cap created"); syscall::pci_enable_bus_master(staging::NVME_PCI); show!(init, "nvme: bus master enabled"); syscall::iommu_setup_device(staging::NVME_PCI, staging::NVME_PROC); show!(init, "nvme: iommu setup done"); let msix_r = syscall::pci_msix_configure(staging::NVME_PCI, 0, boot::NVME_VECTOR, staging::NVME_IRQ); if msix_r < 0 { show!(init, warn, "nvme msix configure failed, skipping"); return false; } boot::grant_cap(staging::NVME_PCI, staging::NVME_PROC, driver_slots::PCI); boot::create_notification(allocator, staging::NVME_NOTIF); syscall::irq_bind(staging::NVME_IRQ, staging::NVME_NOTIF, 0); boot::grant_cap(staging::NVME_IRQ, staging::NVME_PROC, driver_slots::PCI_IRQ); boot::grant_cap( staging::NVME_NOTIF, staging::NVME_PROC, driver_slots::PCI_NOTIF, ); boot::start_with_sched( allocator, staging::NVME_PROC, staging::NVME_SCHED, pid, u64::MAX, ) } fn bootstrap_nvme_service(allocator: &mut UntypedAllocator) -> bool { boot::create_shared_frames( allocator, staging::LANCERFS_BLOCK_RING_BASE, staging::NVME_PROC, driver_slots::BLOCK_RING_BASE, staging::LANCERFS_BLOCK_RING_COUNT, ); boot::grant_frames( staging::LANCERFS_BLOCK_RING_BASE, staging::LANCERFS_PROC, driver_slots::BLOCK_RING_BASE, staging::LANCERFS_BLOCK_RING_COUNT, ); boot::create_notification(allocator, staging::LANCERFS_NOTIF); boot::grant_cap( staging::LANCERFS_NOTIF, staging::NVME_PROC, driver_slots::CLIENT_NOTIF, ); boot::grant_cap( staging::NVME_NOTIF, staging::LANCERFS_PROC, driver_slots::FS_DRIVER_NOTIF, ); boot::grant_cap( staging::LANCERFS_NOTIF, staging::LANCERFS_PROC, driver_slots::FS_NOTIF, ); true } fn bootstrap_lancerfs(allocator: &mut UntypedAllocator) -> bool { let pid = match boot::create_process(allocator, b"lancerfs", staging::LANCERFS_PROC) { Some(p) => p, None => return false, }; boot::grant_cap(SERIAL_ENDPOINT, staging::LANCERFS_PROC, driver_slots::EP); boot::start_with_sched( allocator, staging::LANCERFS_PROC, staging::LANCERFS_SCHED, pid, u64::MAX, ) } fn bootstrap_vfs(allocator: &mut UntypedAllocator) -> bool { let pid = match boot::create_process(allocator, b"vfs", staging::VFS_PROC_SLOT) { Some(p) => p, None => return false, }; boot::grant_cap(SERIAL_ENDPOINT, staging::VFS_PROC_SLOT, driver_slots::EP); boot::start_with_sched( allocator, staging::VFS_PROC_SLOT, staging::VFS_SCHED, pid, u64::MAX, ) } fn bootstrap_vfs_lancerfs(allocator: &mut UntypedAllocator) -> bool { boot::create_shared_frames( allocator, staging::VFS_LANCERFS_RING_BASE, staging::VFS_PROC_SLOT, driver_slots::VFS_LANCERFS_RING_BASE, staging::VFS_LANCERFS_RING_COUNT, ); boot::grant_frames( staging::VFS_LANCERFS_RING_BASE, staging::LANCERFS_PROC, driver_slots::FS_CLIENT_RING_BASE, staging::VFS_LANCERFS_RING_COUNT, ); boot::create_notification(allocator, staging::VFS_NOTIF); boot::grant_cap( staging::VFS_NOTIF, staging::VFS_PROC_SLOT, driver_slots::VFS_NOTIF, ); boot::grant_cap( staging::VFS_NOTIF, staging::LANCERFS_PROC, driver_slots::FS_CLIENT_NOTIF, ); boot::grant_cap( staging::LANCERFS_NOTIF, staging::VFS_PROC_SLOT, driver_slots::VFS_LANCERFS_NOTIF, ); true } fn bootstrap_vfs_client(allocator: &mut UntypedAllocator) -> bool { boot::create_shared_frames( allocator, staging::VFS_CLIENT_RING_BASE, staging::VFS_PROC_SLOT, driver_slots::VFS_CLIENT0_RING_BASE, staging::VFS_CLIENT_RING_COUNT, ); (0..staging::VFS_CLIENT_RING_COUNT).all(|i| { let src = staging::VFS_CLIENT_RING_BASE + i; let dst = VFS_RING_BASE_SLOT + i; syscall::cnode_copy(src, dst, RIGHTS_ALL) >= 0 }); boot::create_notification(allocator, SLOT_VFS_CLIENT_NOTIF); boot::grant_cap( SLOT_VFS_CLIENT_NOTIF, staging::VFS_PROC_SLOT, driver_slots::VFS_CLIENT0_NOTIF, ); syscall::cnode_copy(staging::VFS_NOTIF, SLOT_VFS_NOTIF, RIGHTS_ALL); let proc_cap_slot: u64 = SLOT_VFS_PROC; syscall::cnode_copy(staging::VFS_PROC_SLOT, proc_cap_slot, RIGHTS_ALL); true } fn bootstrap_ramfs(allocator: &mut UntypedAllocator) -> Option { let pid = boot::create_process(allocator, b"ramfs", staging::RAMFS_PROC)?; boot::grant_cap(SERIAL_ENDPOINT, staging::RAMFS_PROC, driver_slots::EP); Some(pid) } fn bootstrap_vfs_ramfs(allocator: &mut UntypedAllocator) -> bool { boot::create_shared_frames( allocator, staging::VFS_RAMFS_RING_BASE, staging::VFS_PROC_SLOT, driver_slots::VFS_RAMFS_RING_BASE, staging::VFS_RAMFS_RING_COUNT, ); boot::grant_frames( staging::VFS_RAMFS_RING_BASE, staging::RAMFS_PROC, driver_slots::RAMFS_CLIENT_RING_BASE, staging::RAMFS_VFS_RING_COUNT, ); boot::create_notification(allocator, staging::RAMFS_NOTIF); boot::grant_cap( staging::RAMFS_NOTIF, staging::RAMFS_PROC, driver_slots::RAMFS_NOTIF, ); boot::grant_cap( staging::VFS_NOTIF, staging::RAMFS_PROC, driver_slots::RAMFS_VFS_NOTIF, ); boot::grant_cap( staging::RAMFS_NOTIF, staging::VFS_PROC_SLOT, driver_slots::VFS_RAMFS_NOTIF, ); true } fn bootstrap_virtio_net( allocator: &mut UntypedAllocator, device_idx: u8, virtio_net_gsi: u64, ) -> Option { let pid = boot::create_process(allocator, b"virtio-net", staging::VIRTIO_NET_PROC)?; boot::grant_cap(SERIAL_ENDPOINT, staging::VIRTIO_NET_PROC, driver_slots::EP); let r = syscall::pci_create_cap(device_idx as u64, staging::VIRTIO_NET_PCI); match r < 0 { true => return None, false => {} } syscall::pci_enable_bus_master(staging::VIRTIO_NET_PCI); syscall::iommu_setup_device(staging::VIRTIO_NET_PCI, staging::VIRTIO_NET_PROC); let msix_r = syscall::pci_msix_configure( staging::VIRTIO_NET_PCI, 0, boot::VIRTIO_NET_VECTOR, staging::VIRTIO_NET_IRQ, ); match msix_r < 0 { true => { boot::create_irq_handler( allocator, staging::VIRTIO_NET_IRQ, virtio_net_gsi, boot::VIRTIO_NET_VECTOR, 0, 1, ); } false => {} } boot::grant_cap( staging::VIRTIO_NET_PCI, staging::VIRTIO_NET_PROC, driver_slots::PCI, ); boot::create_notification(allocator, staging::VIRTIO_NET_NOTIF); syscall::irq_bind(staging::VIRTIO_NET_IRQ, staging::VIRTIO_NET_NOTIF, 0); boot::grant_cap( staging::VIRTIO_NET_IRQ, staging::VIRTIO_NET_PROC, driver_slots::PCI_IRQ, ); boot::grant_cap( staging::VIRTIO_NET_NOTIF, staging::VIRTIO_NET_PROC, driver_slots::PCI_NOTIF, ); Some(pid) } fn bootstrap_netstack(allocator: &mut UntypedAllocator) -> Option { let pid = boot::create_process(allocator, b"netstack", staging::NETSTACK_PROC)?; boot::grant_cap( SERIAL_ENDPOINT, staging::NETSTACK_PROC, driver_slots::NET_EP, ); Some(pid) } fn bootstrap_net_service(allocator: &mut UntypedAllocator) -> bool { boot::create_shared_frames( allocator, staging::VIRTIO_NET_PACKET_RING_BASE, staging::VIRTIO_NET_PROC, driver_slots::BLOCK_RING_BASE, staging::VIRTIO_NET_PACKET_RING_COUNT, ); boot::grant_frames( staging::VIRTIO_NET_PACKET_RING_BASE, staging::NETSTACK_PROC, driver_slots::NET_PACKET_RING_BASE, staging::NETSTACK_DRIVER_RING_COUNT, ); boot::create_notification(allocator, staging::NETSTACK_NOTIF); boot::grant_cap( staging::NETSTACK_NOTIF, staging::NETSTACK_PROC, driver_slots::NET_NOTIF, ); boot::grant_cap( staging::VIRTIO_NET_NOTIF, staging::NETSTACK_PROC, driver_slots::NET_DRIVER_NOTIF, ); boot::grant_cap( staging::NETSTACK_NOTIF, staging::VIRTIO_NET_PROC, driver_slots::NETSTACK_NOTIF, ); true } fn bootstrap_init_netring(allocator: &mut UntypedAllocator) -> bool { boot::create_shared_frames( allocator, NETSTACK_RING_BASE_SLOT, staging::NETSTACK_PROC, driver_slots::NET_INIT_RING_BASE, NETSTACK_RING_FRAME_COUNT, ); boot::create_notification(allocator, SLOT_INIT_NOTIF); syscall::cnode_copy(staging::NETSTACK_NOTIF, SLOT_NETSTACK_NOTIF, RIGHTS_ALL); boot::grant_cap( SLOT_INIT_NOTIF, staging::NETSTACK_PROC, driver_slots::NET_INIT_NOTIF, ); true } fn bootstrap_remote_shell(allocator: &mut UntypedAllocator, has_vfs: bool) -> bool { let pid = match boot::create_process(allocator, b"remote-shell", staging::REMOTE_SHELL_PROC) { Some(p) => p, None => return false, }; boot::grant_cap( SERIAL_ENDPOINT, staging::REMOTE_SHELL_PROC, driver_slots::RSHELL_EP, ); boot::grant_frames( staging::REMOTE_SHELL_NET_RING_BASE, staging::REMOTE_SHELL_PROC, driver_slots::RSHELL_SHELL_RING_BASE, staging::NETSTACK_SHELL_RING_COUNT, ); boot::create_notification(allocator, staging::REMOTE_SHELL_NOTIF); boot::grant_cap( staging::REMOTE_SHELL_NOTIF, staging::REMOTE_SHELL_PROC, driver_slots::RSHELL_NOTIF, ); boot::grant_cap( staging::NETSTACK_NOTIF, staging::REMOTE_SHELL_PROC, driver_slots::RSHELL_NETSTACK_NOTIF, ); boot::grant_cap( staging::REMOTE_SHELL_NOTIF, staging::NETSTACK_PROC, driver_slots::NET_SHELL_NOTIF, ); match has_vfs { true => { boot::create_shared_frames( allocator, staging::REMOTE_SHELL_VFS_RING_BASE, staging::REMOTE_SHELL_PROC, driver_slots::RSHELL_VFS_RING_BASE, staging::REMOTE_SHELL_VFS_RING_COUNT, ); boot::grant_frames( staging::REMOTE_SHELL_VFS_RING_BASE, staging::VFS_PROC_SLOT, driver_slots::VFS_CLIENT0_RING_BASE, staging::REMOTE_SHELL_VFS_RING_COUNT, ); boot::grant_cap(SLOT_VFS_CLIENT_NOTIF, staging::REMOTE_SHELL_PROC, 12); boot::grant_cap(SLOT_VFS_NOTIF, staging::REMOTE_SHELL_PROC, 13); boot::grant_cap(staging::VFS_PROC_SLOT, staging::REMOTE_SHELL_PROC, 15); } false => {} } match boot::start_with_sched( allocator, staging::REMOTE_SHELL_PROC, staging::REMOTE_SHELL_SCHED, pid, u64::MAX, ) { true => true, false => { show!(init, warn, "remote-shell start_with_sched failed"); syscall::proc_destroy(staging::REMOTE_SHELL_PROC); false } } } fn bootstrap_all(allocator: &mut UntypedAllocator) -> BootState { let mut state = BootState::new(); let com1_gsi = syscall::platform_info(boot::PLATFORM_COM1_GSI); let kbd_gsi = syscall::platform_info(boot::PLATFORM_KBD_GSI); let virtio_net_gsi = syscall::platform_info(boot::PLATFORM_VIRTIO_NET_GSI); let has_fb = syscall::platform_info(boot::PLATFORM_HAS_FRAMEBUFFER) == 1; match com1_gsi >= 0 { true => { state.has_serial = bootstrap_serial(allocator, com1_gsi as u64); match state.has_serial { true => show!(init, "serial driver started"), false => show!(init, warn, "serial driver failed"), } } false => show!(init, "skipping serial, no com1"), } match kbd_gsi >= 0 && state.has_serial { true => { state.has_ps2kbd = bootstrap_ps2kbd(allocator, kbd_gsi as u64); match state.has_ps2kbd { true => show!(init, "ps2kbd driver started"), false => show!(init, warn, "ps2kbd driver failed"), } } false => show!(init, "skipping ps2kbd"), } match has_fb { true => { state.has_fbconsole = bootstrap_fbconsole(allocator); match state.has_fbconsole { true => show!(init, "fbconsole driver started"), false => show!(init, warn, "fbconsole driver failed"), } } false => show!(init, "skipping fbconsole, no framebuffer"), } let nvme_dev_idx = boot::find_pci_device_by_class(0x01, 0x08, 0x02); match nvme_dev_idx { Some(idx) => { state.has_nvme = bootstrap_nvme(allocator, idx); match state.has_nvme { true => show!(init, "nvme driver started"), false => show!(init, warn, "nvme driver failed"), } } None => show!(init, "skipping nvme, no device"), } if state.has_nvme { show!(init, "creating lancerfs"); state.has_lancerfs = bootstrap_lancerfs(allocator); match state.has_lancerfs { true => { bootstrap_nvme_service(allocator); show!(init, "lancerfs started, linked to nvme"); } false => show!(init, warn, "lancerfs failed"), } } if state.has_lancerfs { show!(init, "creating vfs"); state.has_vfs = bootstrap_vfs(allocator); match state.has_vfs { true => { bootstrap_vfs_lancerfs(allocator); state.has_vfs_client = bootstrap_vfs_client(allocator); show!(init, "vfs started, linked to lancerfs"); } false => show!(init, warn, "vfs failed"), } } let ramfs_available = boot::find_module(b"ramfs").is_some(); if ramfs_available && state.has_vfs { show!(init, "creating ramfs"); match bootstrap_ramfs(allocator) { Some(pid) => { bootstrap_vfs_ramfs(allocator); state.has_ramfs = boot::start_with_sched( allocator, staging::RAMFS_PROC, staging::RAMFS_SCHED, pid, u64::MAX, ); match state.has_ramfs { true => show!(init, "ramfs started, linked to vfs"), false => { syscall::proc_destroy(staging::RAMFS_PROC); show!(init, warn, "ramfs start failed"); } } } None => show!(init, warn, "ramfs failed"), } } show!(init, "pci scan for virtio-net"); let virtio_dev_idx = boot::find_pci_device_by_vendor(0x1AF4, 0x02, 0x00); let mut virtio_net_pid: Option = None; match virtio_dev_idx { Some(idx) if virtio_net_gsi >= 0 => { virtio_net_pid = bootstrap_virtio_net(allocator, idx, virtio_net_gsi as u64); state.has_virtio_net = virtio_net_pid.is_some(); match state.has_virtio_net { true => show!(init, "virtio-net configured"), false => show!(init, warn, "virtio-net driver failed"), } } _ => show!(init, "skipping virtio-net"), } let mut netstack_pid: Option = None; match state.has_virtio_net { true => { netstack_pid = bootstrap_netstack(allocator); state.has_netstack = netstack_pid.is_some(); match state.has_netstack { true => { bootstrap_net_service(allocator); bootstrap_init_netring(allocator); show!(init, "netstack created, linking services"); } false => show!(init, warn, "netstack failed"), } } false => {} } if let Some(pid) = virtio_net_pid { match boot::start_with_sched( allocator, staging::VIRTIO_NET_PROC, staging::VIRTIO_NET_SCHED, pid, u64::MAX, ) { true => show!(init, "virtio-net driver started"), false => { state.has_virtio_net = false; syscall::proc_destroy(staging::VIRTIO_NET_PROC); show!(init, warn, "virtio-net start failed"); } } } if state.has_netstack { let shell_frames_ok = boot::create_shared_frames( allocator, staging::REMOTE_SHELL_NET_RING_BASE, staging::NETSTACK_PROC, driver_slots::NET_SHELL_RING_BASE, staging::NETSTACK_SHELL_RING_COUNT, ); match shell_frames_ok { true => {} false => show!(init, warn, "shell ring frame alloc failed"), } } let remote_shell_available = boot::find_module(b"remote-shell").is_some(); match remote_shell_available && state.has_netstack { true => { state.has_remote_shell = bootstrap_remote_shell(allocator, state.has_vfs_client); match state.has_remote_shell { true => show!(init, "remote-shell started"), false => show!(init, warn, "remote-shell failed"), } } false => {} } if let Some(pid) = netstack_pid { match boot::start_with_sched( allocator, staging::NETSTACK_PROC, staging::NETSTACK_SCHED, pid, u64::MAX, ) { true => show!(init, "netstack started"), false => { state.has_netstack = false; syscall::proc_destroy(staging::NETSTACK_PROC); show!(init, warn, "netstack start failed"); } } } state } use core::sync::atomic::{AtomicU64, Ordering}; static FLIGHT_CURSOR: AtomicU64 = AtomicU64::new(0); fn ensure_mount_points() { let mut client = unsafe { lancer_user::fs::FsClient::new( VFS_RING_VADDR as usize, SLOT_VFS_NOTIF as u8, SLOT_VFS_CLIENT_NOTIF as u8, ) }; match client.mkdir(0, b"tmp") { Ok(()) => { show!(init, "created /tmp mount point"); } Err(lancer_user::fs::FsStatus::FileExists) => {} Err(_) => { show!(init, warn, "failed to create /tmp"); } } } fn ensure_dir( client: &mut lancer_user::fs::FsClient, parent: u8, name: &[u8], ) -> Result<(), lancer_user::fs::FsStatus> { match client.mkdir(parent, name) { Ok(()) => Ok(()), Err(lancer_user::fs::FsStatus::FileExists) => Ok(()), Err(e) => Err(e), } } fn open_persistent_log(client: &mut lancer_user::fs::FsClient) -> Option<(u8, &'static str)> { use lancer_user::fs::FsRights; let rw_create = FsRights::from_raw(FsRights::READ.raw() | FsRights::WRITE.raw() | FsRights::CREATE.raw()); let var_h = client.open(0, b"var", FsRights::ALL).ok()?; let log_ok = ensure_dir(client, var_h, b"log").is_ok(); match log_ok { true => { let log_h = client.open(var_h, b"log", FsRights::ALL).ok(); let _ = client.close(var_h); let log_h = log_h?; match client.open(log_h, b"flight", rw_create) { Ok(h) => { let _ = client.close(log_h); Some((h, "/var/log/flight")) } Err(_) => { let _ = client.close(log_h); None } } } false => { let _ = client.close(var_h); None } } } fn open_tmp_log(client: &mut lancer_user::fs::FsClient) -> Option<(u8, &'static str)> { use lancer_user::fs::FsRights; let rw_create = FsRights::from_raw(FsRights::READ.raw() | FsRights::WRITE.raw() | FsRights::CREATE.raw()); let tmp_h = client.open(0, b"tmp", FsRights::ALL).ok()?; match client.open(tmp_h, b"flight", rw_create) { Ok(h) => { let _ = client.close(tmp_h); Some((h, "/tmp/flight")) } Err(_) => { let _ = client.close(tmp_h); None } } } fn try_drain( client: &mut lancer_user::fs::FsClient, handle: u8, path: &str, start_cursor: u64, ) -> (u64, u64, bool) { let file_offset = match client.stat(handle) { Ok(st) => st.size, Err(_) => 0, }; let mut cursor = start_cursor; let mut written: u64 = 0; let mut buf = [0u8; 4096]; let mut disk_err = false; loop { let (new_cursor, bytes_read) = syscall::blackbox_read(cursor, &mut buf); match bytes_read { 0 => break, n => match client.write(handle, file_offset + written, &buf[..n]) { Ok(w) => { written += w as u64; cursor = new_cursor; } Err(_) => { disk_err = true; break; } }, } } let _ = client.close(handle); match (written, disk_err) { (0, false) => show!(init, "flight log empty at cursor {}", start_cursor), (0, true) => show!(init, warn, "flight log write failed to {}", path), (_, true) => show!( init, warn, "flight log partial drain {} bytes to {}", written, path ), (_, false) => show!(init, "flight log drained {} bytes to {}", written, path), } (cursor, written, disk_err) } fn drain_flight_log() { use lancer_user::fs::FsClient; let mut client = unsafe { FsClient::reattach( VFS_RING_VADDR as usize, SLOT_VFS_NOTIF as u8, SLOT_VFS_CLIENT_NOTIF as u8, ) }; let cursor = FLIGHT_CURSOR.load(Ordering::Relaxed); if let Some((handle, path)) = open_persistent_log(&mut client) { let (new_cursor, written, failed) = try_drain(&mut client, handle, path, cursor); match failed && written == 0 { false => { FLIGHT_CURSOR.store(new_cursor, Ordering::Relaxed); return; } true => { show!(init, warn, "persistent log full, falling back to /tmp"); } } } match open_tmp_log(&mut client) { Some((handle, path)) => { let (new_cursor, _, _) = try_drain(&mut client, handle, path, cursor); FLIGHT_CURSOR.store(new_cursor, Ordering::Relaxed); } None => { show!(init, warn, "flight log not writable"); } } } fn spawn_shell(allocator: &mut UntypedAllocator) -> bool { let module_idx = match boot::find_module(b"shell") { Some(idx) => idx, None => { show!(init, error, "shell module not found"); return false; } }; let ut_slot = match allocator.find_for_retype(boot::TAG_PROCESS, 0) { Ok(s) => s, Err(_) => { show!(init, error, "no untyped large enough for process"); return false; } }; let child_pid = syscall::proc_create(ut_slot, SLOT_SHELL_PROC); if child_pid < 0 { show!(init, error, "failed to create shell process"); return false; } let r = syscall::proc_load_module(SLOT_SHELL_PROC, module_idx); if r < 0 { show!(init, error, "failed to load shell module"); syscall::proc_destroy(SLOT_SHELL_PROC); return false; } let shell_ut = match allocator.find_large_untyped(256 * 1024) { Ok(s) => s, Err(_) => { show!(init, error, "no large untyped for shell"); syscall::proc_destroy(SLOT_SHELL_PROC); return false; } }; let r = syscall::cap_grant(shell_ut, SLOT_SHELL_PROC, 28, RIGHTS_ALL); if r < 0 { show!(init, error, "failed to grant untyped to shell"); syscall::proc_destroy(SLOT_SHELL_PROC); return false; } let r = syscall::cap_grant(SERIAL_ENDPOINT, SLOT_SHELL_PROC, 1, RIGHTS_ALL); if r < 0 { show!(init, error, "failed to grant serial endpoint to shell"); syscall::proc_destroy(SLOT_SHELL_PROC); return false; } [ (SLOT_INIT_NOTIF, 9u64), (SLOT_NETSTACK_NOTIF, 10), (SLOT_VFS_CLIENT_NOTIF, 12), (SLOT_VFS_NOTIF, 13), (SLOT_VFS_PROC, 15), ] .iter() .for_each(|&(src_slot, dst_slot)| { let _ = syscall::cap_grant(src_slot, SLOT_SHELL_PROC, dst_slot, RIGHTS_ALL); }); (0..NETSTACK_RING_FRAME_COUNT).for_each(|i| { let _ = syscall::cap_grant( NETSTACK_RING_BASE_SLOT + i, SLOT_SHELL_PROC, SHELL_NETSTACK_RING_DST + i, RIGHTS_ALL, ); }); (0..VFS_RING_FRAME_COUNT).for_each(|i| { let _ = syscall::cap_grant( VFS_RING_BASE_SLOT + i, SLOT_SHELL_PROC, SHELL_VFS_RING_DST + i, RIGHTS_ALL, ); }); let r = match allocator.alloc_object(TAG_NOTIFICATION, 0, SLOT_SHELL_DEATH_NOTIF) { Ok(_) => 0, Err(_) => -1, }; if r < 0 { show!(init, error, "failed to create shell death notif"); syscall::proc_destroy(SLOT_SHELL_PROC); return false; } syscall::proc_bind_death_notif( SLOT_SHELL_PROC, SLOT_SHELL_DEATH_NOTIF, NOTIFY_BIT_CHILD_DEATH, ); let r = match allocator.alloc_object(TAG_SCHED_CONTEXT, 0, SLOT_SHELL_SCHED) { Ok(_) => 0, Err(_) => -1, }; if r < 0 { show!(init, error, "failed to create shell sched context"); syscall::proc_destroy(SLOT_SHELL_PROC); syscall::cap_revoke(SLOT_SHELL_DEATH_NOTIF); return false; } let r = syscall::sched_configure(SLOT_SHELL_SCHED, 10_000_000, 10_000_000, 100); if r < 0 { show!(init, error, "failed to configure shell sched context"); syscall::proc_destroy(SLOT_SHELL_PROC); syscall::cap_revoke(SLOT_SHELL_SCHED); syscall::cap_revoke(SLOT_SHELL_DEATH_NOTIF); return false; } let r = syscall::sched_attach(SLOT_SHELL_SCHED, child_pid as u64); if r < 0 { show!(init, error, "failed to attach shell sched context"); syscall::proc_destroy(SLOT_SHELL_PROC); syscall::cap_revoke(SLOT_SHELL_SCHED); syscall::cap_revoke(SLOT_SHELL_DEATH_NOTIF); return false; } let r = syscall::proc_start(SLOT_SHELL_PROC, 0); if r < 0 { show!(init, error, "failed to start shell"); syscall::proc_destroy(SLOT_SHELL_PROC); syscall::cap_revoke(SLOT_SHELL_SCHED); syscall::cap_revoke(SLOT_SHELL_DEATH_NOTIF); return false; } true } fn wait_for_shell_death() { loop { let (_, bits) = syscall::notify_wait(SLOT_SHELL_DEATH_NOTIF); if bits & NOTIFY_BIT_CHILD_DEATH != 0 { syscall::proc_destroy(SLOT_SHELL_PROC); syscall::cap_revoke(SLOT_SHELL_SCHED); syscall::cap_revoke(SLOT_SHELL_DEATH_NOTIF); break; } } } #[unsafe(no_mangle)] pub extern "C" fn lancer_main() -> ! { let mut allocator = match UntypedAllocator::from_boot_info() { Ok(a) => a, Err(_) => { show!(init, error, "failed to init untyped allocator"); syscall::exit(); } }; let boot_state = bootstrap_all(&mut allocator); let has_vfs = boot_state.has_vfs_client && (0..VFS_RING_FRAME_COUNT) .all(|i| syscall::frame_map(VFS_RING_BASE_SLOT + i, VFS_RING_VADDR + i * 4096, 1) >= 0); match has_vfs { true => { syscall::notify_wait(SLOT_VFS_CLIENT_NOTIF); show!(init, "vfs ring mapped"); ensure_mount_points(); drain_flight_log(); } false => { show!(init, warn, "vfs not available"); } } loop { match spawn_shell(&mut allocator) { true => { wait_for_shell_death(); show!(init, "shell exited, respawning"); if has_vfs { drain_flight_log(); } } false => { show!(init, error, "shell spawn failed, retrying"); syscall::sched_yield(); } } } }