Nothing to see here, move along
at main 1291 lines 39 kB view raw
1#![no_std] 2#![no_main] 3#![allow(clippy::single_match)] 4 5use lancer_user::bootstrap as boot; 6 7use lancer_user::show; 8use lancer_user::syscall; 9use lancer_user::untyped::UntypedAllocator; 10 11const SERIAL_ENDPOINT: u64 = 1; 12const FB_ENDPOINT: u64 = 2; 13 14const TAG_NOTIFICATION: u64 = 2; 15const TAG_SCHED_CONTEXT: u64 = 5; 16const RIGHTS_ALL: u64 = 0b1111; 17 18const SLOT_INIT_NOTIF: u64 = 9; 19const SLOT_NETSTACK_NOTIF: u64 = 10; 20const SLOT_VFS_CLIENT_NOTIF: u64 = 12; 21const SLOT_VFS_NOTIF: u64 = 13; 22const SLOT_VFS_PROC: u64 = 15; 23const VFS_RING_BASE_SLOT: u64 = 320; 24const VFS_RING_FRAME_COUNT: u64 = 16; 25const NETSTACK_RING_BASE_SLOT: u64 = 288; 26const NETSTACK_RING_FRAME_COUNT: u64 = 2; 27const SHELL_NETSTACK_RING_DST: u64 = 64; 28const SHELL_VFS_RING_DST: u64 = 128; 29 30const VFS_RING_VADDR: u64 = 0x7000_0000; 31 32const NOTIFY_BIT_CHILD_DEATH: u64 = 0x01; 33 34const SLOT_SHELL_PROC: u64 = 25; 35const SLOT_SHELL_SCHED: u64 = 26; 36const SLOT_SHELL_DEATH_NOTIF: u64 = 27; 37 38#[allow(dead_code)] 39mod staging { 40 pub const SERIAL_PROC: u64 = 400; 41 pub const SERIAL_SCHED: u64 = 401; 42 pub const SERIAL_IRQ: u64 = 402; 43 pub const SERIAL_NOTIF: u64 = 403; 44 45 pub const PS2KBD_PROC: u64 = 410; 46 pub const PS2KBD_SCHED: u64 = 411; 47 pub const PS2KBD_IRQ: u64 = 412; 48 pub const PS2KBD_NOTIF: u64 = 413; 49 50 pub const FBCONSOLE_PROC: u64 = 420; 51 pub const FBCONSOLE_SCHED: u64 = 421; 52 pub const FBCONSOLE_FB: u64 = 422; 53 pub const FBCONSOLE_RING_BASE: u64 = 424; 54 pub const FBCONSOLE_RING_COUNT: u64 = 16; 55 56 pub const NVME_PROC: u64 = 450; 57 pub const NVME_SCHED: u64 = 451; 58 pub const NVME_PCI: u64 = 452; 59 pub const NVME_IRQ: u64 = 453; 60 pub const NVME_NOTIF: u64 = 454; 61 62 pub const LANCERFS_PROC: u64 = 500; 63 pub const LANCERFS_SCHED: u64 = 501; 64 pub const LANCERFS_NOTIF: u64 = 502; 65 pub const LANCERFS_BLOCK_RING_BASE: u64 = 504; 66 pub const LANCERFS_BLOCK_RING_COUNT: u64 = 16; 67 68 pub const VFS_PROC_SLOT: u64 = 530; 69 pub const VFS_SCHED: u64 = 531; 70 pub const VFS_NOTIF: u64 = 532; 71 pub const VFS_LANCERFS_RING_BASE: u64 = 534; 72 pub const VFS_LANCERFS_RING_COUNT: u64 = 16; 73 pub const VFS_RAMFS_RING_BASE: u64 = 560; 74 pub const VFS_RAMFS_RING_COUNT: u64 = 16; 75 pub const VFS_CLIENT_RING_BASE: u64 = 576; 76 pub const VFS_CLIENT_RING_COUNT: u64 = 16; 77 78 pub const RAMFS_PROC: u64 = 600; 79 pub const RAMFS_SCHED: u64 = 601; 80 pub const RAMFS_NOTIF: u64 = 602; 81 pub const RAMFS_VFS_RING_BASE: u64 = 604; 82 pub const RAMFS_VFS_RING_COUNT: u64 = 16; 83 84 pub const VIRTIO_NET_PROC: u64 = 640; 85 pub const VIRTIO_NET_SCHED: u64 = 641; 86 pub const VIRTIO_NET_PCI: u64 = 642; 87 pub const VIRTIO_NET_IRQ: u64 = 643; 88 pub const VIRTIO_NET_NOTIF: u64 = 644; 89 pub const VIRTIO_NET_PACKET_RING_BASE: u64 = 646; 90 pub const VIRTIO_NET_PACKET_RING_COUNT: u64 = 16; 91 92 pub const NETSTACK_PROC: u64 = 680; 93 pub const NETSTACK_SCHED: u64 = 681; 94 pub const NETSTACK_NOTIF: u64 = 682; 95 pub const NETSTACK_DRIVER_RING_BASE: u64 = 684; 96 pub const NETSTACK_DRIVER_RING_COUNT: u64 = 16; 97 pub const NETSTACK_SHELL_RING_BASE: u64 = 700; 98 pub const NETSTACK_SHELL_RING_COUNT: u64 = 4; 99 pub const NETSTACK_INIT_RING_BASE: u64 = 720; 100 pub const NETSTACK_INIT_RING_COUNT: u64 = 2; 101 102 pub const REMOTE_SHELL_PROC: u64 = 750; 103 pub const REMOTE_SHELL_SCHED: u64 = 751; 104 pub const REMOTE_SHELL_NOTIF: u64 = 752; 105 pub const REMOTE_SHELL_NET_RING_BASE: u64 = 754; 106 pub const REMOTE_SHELL_VFS_RING_BASE: u64 = 770; 107 pub const REMOTE_SHELL_VFS_RING_COUNT: u64 = 16; 108} 109 110mod driver_slots { 111 pub const EP: u64 = 1; 112 pub const IRQ: u64 = 2; 113 pub const NOTIF: u64 = 3; 114 115 pub const FB: u64 = 2; 116 pub const CONSOLE_RING_BASE: u64 = 64; 117 118 pub const PCI: u64 = 2; 119 pub const PCI_IRQ: u64 = 3; 120 pub const PCI_NOTIF: u64 = 4; 121 pub const CLIENT_NOTIF: u64 = 6; 122 pub const NETSTACK_NOTIF: u64 = 7; 123 pub const BLOCK_RING_BASE: u64 = 64; 124 125 pub const FS_NOTIF: u64 = 3; 126 pub const FS_DRIVER_NOTIF: u64 = 4; 127 pub const FS_CLIENT_NOTIF: u64 = 6; 128 pub const FS_CLIENT_RING_BASE: u64 = 96; 129 130 pub const VFS_NOTIF: u64 = 3; 131 pub const VFS_LANCERFS_NOTIF: u64 = 4; 132 pub const VFS_LANCERFS_RING_BASE: u64 = 64; 133 pub const VFS_CLIENT0_RING_BASE: u64 = 160; 134 pub const VFS_CLIENT0_NOTIF: u64 = 176; 135 pub const VFS_RAMFS_NOTIF: u64 = 6; 136 pub const VFS_RAMFS_RING_BASE: u64 = 96; 137 138 pub const RAMFS_NOTIF: u64 = 3; 139 pub const RAMFS_VFS_NOTIF: u64 = 4; 140 pub const RAMFS_CLIENT_RING_BASE: u64 = 64; 141 142 pub const NET_EP: u64 = 1; 143 pub const NET_NOTIF: u64 = 3; 144 pub const NET_DRIVER_NOTIF: u64 = 4; 145 pub const NET_SHELL_NOTIF: u64 = 6; 146 pub const NET_INIT_NOTIF: u64 = 8; 147 pub const NET_PACKET_RING_BASE: u64 = 64; 148 pub const NET_SHELL_RING_BASE: u64 = 96; 149 pub const NET_INIT_RING_BASE: u64 = 128; 150 151 pub const RSHELL_EP: u64 = 1; 152 pub const RSHELL_NOTIF: u64 = 3; 153 pub const RSHELL_NETSTACK_NOTIF: u64 = 4; 154 pub const RSHELL_SHELL_RING_BASE: u64 = 64; 155 pub const RSHELL_VFS_RING_BASE: u64 = 128; 156} 157 158struct BootState { 159 has_serial: bool, 160 has_ps2kbd: bool, 161 has_fbconsole: bool, 162 has_nvme: bool, 163 has_lancerfs: bool, 164 has_vfs: bool, 165 has_ramfs: bool, 166 has_virtio_net: bool, 167 has_netstack: bool, 168 has_remote_shell: bool, 169 has_vfs_client: bool, 170} 171 172impl BootState { 173 const fn new() -> Self { 174 Self { 175 has_serial: false, 176 has_ps2kbd: false, 177 has_fbconsole: false, 178 has_nvme: false, 179 has_lancerfs: false, 180 has_vfs: false, 181 has_ramfs: false, 182 has_virtio_net: false, 183 has_netstack: false, 184 has_remote_shell: false, 185 has_vfs_client: false, 186 } 187 } 188} 189 190fn bootstrap_serial(allocator: &mut UntypedAllocator, com1_gsi: u64) -> bool { 191 let pid = match boot::create_process(allocator, b"serial", staging::SERIAL_PROC) { 192 Some(p) => p, 193 None => return false, 194 }; 195 196 boot::create_endpoint(allocator, SERIAL_ENDPOINT); 197 boot::grant_cap(SERIAL_ENDPOINT, staging::SERIAL_PROC, driver_slots::EP); 198 199 boot::create_irq_handler( 200 allocator, 201 staging::SERIAL_IRQ, 202 com1_gsi, 203 boot::COM1_VECTOR, 204 0x3F8, 205 8, 206 ); 207 boot::create_notification(allocator, staging::SERIAL_NOTIF); 208 syscall::irq_bind(staging::SERIAL_IRQ, staging::SERIAL_NOTIF, 0); 209 boot::grant_cap(staging::SERIAL_IRQ, staging::SERIAL_PROC, driver_slots::IRQ); 210 boot::grant_cap( 211 staging::SERIAL_NOTIF, 212 staging::SERIAL_PROC, 213 driver_slots::NOTIF, 214 ); 215 216 boot::start_with_sched( 217 allocator, 218 staging::SERIAL_PROC, 219 staging::SERIAL_SCHED, 220 pid, 221 u64::MAX, 222 ) 223} 224 225fn bootstrap_ps2kbd(allocator: &mut UntypedAllocator, kbd_gsi: u64) -> bool { 226 let pid = match boot::create_process(allocator, b"ps2kbd", staging::PS2KBD_PROC) { 227 Some(p) => p, 228 None => return false, 229 }; 230 231 boot::grant_cap(SERIAL_ENDPOINT, staging::PS2KBD_PROC, driver_slots::EP); 232 233 boot::create_irq_handler( 234 allocator, 235 staging::PS2KBD_IRQ, 236 kbd_gsi, 237 boot::KBD_VECTOR, 238 0x60, 239 5, 240 ); 241 boot::create_notification(allocator, staging::PS2KBD_NOTIF); 242 syscall::irq_bind(staging::PS2KBD_IRQ, staging::PS2KBD_NOTIF, 0); 243 boot::grant_cap(staging::PS2KBD_IRQ, staging::PS2KBD_PROC, driver_slots::IRQ); 244 boot::grant_cap( 245 staging::PS2KBD_NOTIF, 246 staging::PS2KBD_PROC, 247 driver_slots::NOTIF, 248 ); 249 250 boot::start_with_sched( 251 allocator, 252 staging::PS2KBD_PROC, 253 staging::PS2KBD_SCHED, 254 pid, 255 u64::MAX, 256 ) 257} 258 259fn bootstrap_fbconsole(allocator: &mut UntypedAllocator) -> bool { 260 let pid = match boot::create_process(allocator, b"fbconsole", staging::FBCONSOLE_PROC) { 261 Some(p) => p, 262 None => return false, 263 }; 264 265 boot::create_endpoint(allocator, FB_ENDPOINT); 266 boot::grant_cap(FB_ENDPOINT, staging::FBCONSOLE_PROC, driver_slots::EP); 267 268 let r = syscall::fb_create_cap(staging::FBCONSOLE_FB); 269 match r < 0 { 270 true => { 271 show!(init, warn, "failed to create fb cap"); 272 return false; 273 } 274 false => {} 275 } 276 boot::grant_cap( 277 staging::FBCONSOLE_FB, 278 staging::FBCONSOLE_PROC, 279 driver_slots::FB, 280 ); 281 282 let ring_count = syscall::console_ring_create_cap(staging::FBCONSOLE_RING_BASE); 283 match ring_count <= 0 { 284 true => { 285 show!(init, warn, "failed to create console ring caps"); 286 return false; 287 } 288 false => {} 289 } 290 boot::grant_frames( 291 staging::FBCONSOLE_RING_BASE, 292 staging::FBCONSOLE_PROC, 293 driver_slots::CONSOLE_RING_BASE, 294 ring_count as u64, 295 ); 296 297 boot::start_with_sched( 298 allocator, 299 staging::FBCONSOLE_PROC, 300 staging::FBCONSOLE_SCHED, 301 pid, 302 u64::MAX, 303 ) 304} 305 306fn bootstrap_nvme(allocator: &mut UntypedAllocator, device_idx: u8) -> bool { 307 let pid = match boot::create_process(allocator, b"nvme", staging::NVME_PROC) { 308 Some(p) => p, 309 None => return false, 310 }; 311 312 boot::grant_cap(SERIAL_ENDPOINT, staging::NVME_PROC, driver_slots::EP); 313 314 let r = syscall::pci_create_cap(device_idx as u64, staging::NVME_PCI); 315 if r < 0 { 316 show!(init, warn, "nvme: pci_create_cap failed"); 317 return false; 318 } 319 show!(init, "nvme: pci cap created"); 320 321 syscall::pci_enable_bus_master(staging::NVME_PCI); 322 show!(init, "nvme: bus master enabled"); 323 324 syscall::iommu_setup_device(staging::NVME_PCI, staging::NVME_PROC); 325 show!(init, "nvme: iommu setup done"); 326 327 let msix_r = 328 syscall::pci_msix_configure(staging::NVME_PCI, 0, boot::NVME_VECTOR, staging::NVME_IRQ); 329 if msix_r < 0 { 330 show!(init, warn, "nvme msix configure failed, skipping"); 331 return false; 332 } 333 334 boot::grant_cap(staging::NVME_PCI, staging::NVME_PROC, driver_slots::PCI); 335 336 boot::create_notification(allocator, staging::NVME_NOTIF); 337 syscall::irq_bind(staging::NVME_IRQ, staging::NVME_NOTIF, 0); 338 boot::grant_cap(staging::NVME_IRQ, staging::NVME_PROC, driver_slots::PCI_IRQ); 339 boot::grant_cap( 340 staging::NVME_NOTIF, 341 staging::NVME_PROC, 342 driver_slots::PCI_NOTIF, 343 ); 344 345 boot::start_with_sched( 346 allocator, 347 staging::NVME_PROC, 348 staging::NVME_SCHED, 349 pid, 350 u64::MAX, 351 ) 352} 353 354fn bootstrap_nvme_service(allocator: &mut UntypedAllocator) -> bool { 355 boot::create_shared_frames( 356 allocator, 357 staging::LANCERFS_BLOCK_RING_BASE, 358 staging::NVME_PROC, 359 driver_slots::BLOCK_RING_BASE, 360 staging::LANCERFS_BLOCK_RING_COUNT, 361 ); 362 boot::grant_frames( 363 staging::LANCERFS_BLOCK_RING_BASE, 364 staging::LANCERFS_PROC, 365 driver_slots::BLOCK_RING_BASE, 366 staging::LANCERFS_BLOCK_RING_COUNT, 367 ); 368 369 boot::create_notification(allocator, staging::LANCERFS_NOTIF); 370 boot::grant_cap( 371 staging::LANCERFS_NOTIF, 372 staging::NVME_PROC, 373 driver_slots::CLIENT_NOTIF, 374 ); 375 boot::grant_cap( 376 staging::NVME_NOTIF, 377 staging::LANCERFS_PROC, 378 driver_slots::FS_DRIVER_NOTIF, 379 ); 380 boot::grant_cap( 381 staging::LANCERFS_NOTIF, 382 staging::LANCERFS_PROC, 383 driver_slots::FS_NOTIF, 384 ); 385 386 true 387} 388 389fn bootstrap_lancerfs(allocator: &mut UntypedAllocator) -> bool { 390 let pid = match boot::create_process(allocator, b"lancerfs", staging::LANCERFS_PROC) { 391 Some(p) => p, 392 None => return false, 393 }; 394 395 boot::grant_cap(SERIAL_ENDPOINT, staging::LANCERFS_PROC, driver_slots::EP); 396 397 boot::start_with_sched( 398 allocator, 399 staging::LANCERFS_PROC, 400 staging::LANCERFS_SCHED, 401 pid, 402 u64::MAX, 403 ) 404} 405 406fn bootstrap_vfs(allocator: &mut UntypedAllocator) -> bool { 407 let pid = match boot::create_process(allocator, b"vfs", staging::VFS_PROC_SLOT) { 408 Some(p) => p, 409 None => return false, 410 }; 411 412 boot::grant_cap(SERIAL_ENDPOINT, staging::VFS_PROC_SLOT, driver_slots::EP); 413 414 boot::start_with_sched( 415 allocator, 416 staging::VFS_PROC_SLOT, 417 staging::VFS_SCHED, 418 pid, 419 u64::MAX, 420 ) 421} 422 423fn bootstrap_vfs_lancerfs(allocator: &mut UntypedAllocator) -> bool { 424 boot::create_shared_frames( 425 allocator, 426 staging::VFS_LANCERFS_RING_BASE, 427 staging::VFS_PROC_SLOT, 428 driver_slots::VFS_LANCERFS_RING_BASE, 429 staging::VFS_LANCERFS_RING_COUNT, 430 ); 431 boot::grant_frames( 432 staging::VFS_LANCERFS_RING_BASE, 433 staging::LANCERFS_PROC, 434 driver_slots::FS_CLIENT_RING_BASE, 435 staging::VFS_LANCERFS_RING_COUNT, 436 ); 437 438 boot::create_notification(allocator, staging::VFS_NOTIF); 439 boot::grant_cap( 440 staging::VFS_NOTIF, 441 staging::VFS_PROC_SLOT, 442 driver_slots::VFS_NOTIF, 443 ); 444 boot::grant_cap( 445 staging::VFS_NOTIF, 446 staging::LANCERFS_PROC, 447 driver_slots::FS_CLIENT_NOTIF, 448 ); 449 boot::grant_cap( 450 staging::LANCERFS_NOTIF, 451 staging::VFS_PROC_SLOT, 452 driver_slots::VFS_LANCERFS_NOTIF, 453 ); 454 455 true 456} 457 458fn bootstrap_vfs_client(allocator: &mut UntypedAllocator) -> bool { 459 boot::create_shared_frames( 460 allocator, 461 staging::VFS_CLIENT_RING_BASE, 462 staging::VFS_PROC_SLOT, 463 driver_slots::VFS_CLIENT0_RING_BASE, 464 staging::VFS_CLIENT_RING_COUNT, 465 ); 466 467 (0..staging::VFS_CLIENT_RING_COUNT).all(|i| { 468 let src = staging::VFS_CLIENT_RING_BASE + i; 469 let dst = VFS_RING_BASE_SLOT + i; 470 syscall::cnode_copy(src, dst, RIGHTS_ALL) >= 0 471 }); 472 473 boot::create_notification(allocator, SLOT_VFS_CLIENT_NOTIF); 474 boot::grant_cap( 475 SLOT_VFS_CLIENT_NOTIF, 476 staging::VFS_PROC_SLOT, 477 driver_slots::VFS_CLIENT0_NOTIF, 478 ); 479 480 syscall::cnode_copy(staging::VFS_NOTIF, SLOT_VFS_NOTIF, RIGHTS_ALL); 481 482 let proc_cap_slot: u64 = SLOT_VFS_PROC; 483 syscall::cnode_copy(staging::VFS_PROC_SLOT, proc_cap_slot, RIGHTS_ALL); 484 485 true 486} 487 488fn bootstrap_ramfs(allocator: &mut UntypedAllocator) -> Option<i64> { 489 let pid = boot::create_process(allocator, b"ramfs", staging::RAMFS_PROC)?; 490 boot::grant_cap(SERIAL_ENDPOINT, staging::RAMFS_PROC, driver_slots::EP); 491 Some(pid) 492} 493 494fn bootstrap_vfs_ramfs(allocator: &mut UntypedAllocator) -> bool { 495 boot::create_shared_frames( 496 allocator, 497 staging::VFS_RAMFS_RING_BASE, 498 staging::VFS_PROC_SLOT, 499 driver_slots::VFS_RAMFS_RING_BASE, 500 staging::VFS_RAMFS_RING_COUNT, 501 ); 502 boot::grant_frames( 503 staging::VFS_RAMFS_RING_BASE, 504 staging::RAMFS_PROC, 505 driver_slots::RAMFS_CLIENT_RING_BASE, 506 staging::RAMFS_VFS_RING_COUNT, 507 ); 508 509 boot::create_notification(allocator, staging::RAMFS_NOTIF); 510 boot::grant_cap( 511 staging::RAMFS_NOTIF, 512 staging::RAMFS_PROC, 513 driver_slots::RAMFS_NOTIF, 514 ); 515 boot::grant_cap( 516 staging::VFS_NOTIF, 517 staging::RAMFS_PROC, 518 driver_slots::RAMFS_VFS_NOTIF, 519 ); 520 boot::grant_cap( 521 staging::RAMFS_NOTIF, 522 staging::VFS_PROC_SLOT, 523 driver_slots::VFS_RAMFS_NOTIF, 524 ); 525 526 true 527} 528 529fn bootstrap_virtio_net( 530 allocator: &mut UntypedAllocator, 531 device_idx: u8, 532 virtio_net_gsi: u64, 533) -> Option<i64> { 534 let pid = boot::create_process(allocator, b"virtio-net", staging::VIRTIO_NET_PROC)?; 535 536 boot::grant_cap(SERIAL_ENDPOINT, staging::VIRTIO_NET_PROC, driver_slots::EP); 537 538 let r = syscall::pci_create_cap(device_idx as u64, staging::VIRTIO_NET_PCI); 539 match r < 0 { 540 true => return None, 541 false => {} 542 } 543 syscall::pci_enable_bus_master(staging::VIRTIO_NET_PCI); 544 syscall::iommu_setup_device(staging::VIRTIO_NET_PCI, staging::VIRTIO_NET_PROC); 545 546 let msix_r = syscall::pci_msix_configure( 547 staging::VIRTIO_NET_PCI, 548 0, 549 boot::VIRTIO_NET_VECTOR, 550 staging::VIRTIO_NET_IRQ, 551 ); 552 match msix_r < 0 { 553 true => { 554 boot::create_irq_handler( 555 allocator, 556 staging::VIRTIO_NET_IRQ, 557 virtio_net_gsi, 558 boot::VIRTIO_NET_VECTOR, 559 0, 560 1, 561 ); 562 } 563 false => {} 564 } 565 566 boot::grant_cap( 567 staging::VIRTIO_NET_PCI, 568 staging::VIRTIO_NET_PROC, 569 driver_slots::PCI, 570 ); 571 572 boot::create_notification(allocator, staging::VIRTIO_NET_NOTIF); 573 syscall::irq_bind(staging::VIRTIO_NET_IRQ, staging::VIRTIO_NET_NOTIF, 0); 574 boot::grant_cap( 575 staging::VIRTIO_NET_IRQ, 576 staging::VIRTIO_NET_PROC, 577 driver_slots::PCI_IRQ, 578 ); 579 boot::grant_cap( 580 staging::VIRTIO_NET_NOTIF, 581 staging::VIRTIO_NET_PROC, 582 driver_slots::PCI_NOTIF, 583 ); 584 585 Some(pid) 586} 587 588fn bootstrap_netstack(allocator: &mut UntypedAllocator) -> Option<i64> { 589 let pid = boot::create_process(allocator, b"netstack", staging::NETSTACK_PROC)?; 590 boot::grant_cap( 591 SERIAL_ENDPOINT, 592 staging::NETSTACK_PROC, 593 driver_slots::NET_EP, 594 ); 595 Some(pid) 596} 597 598fn bootstrap_net_service(allocator: &mut UntypedAllocator) -> bool { 599 boot::create_shared_frames( 600 allocator, 601 staging::VIRTIO_NET_PACKET_RING_BASE, 602 staging::VIRTIO_NET_PROC, 603 driver_slots::BLOCK_RING_BASE, 604 staging::VIRTIO_NET_PACKET_RING_COUNT, 605 ); 606 boot::grant_frames( 607 staging::VIRTIO_NET_PACKET_RING_BASE, 608 staging::NETSTACK_PROC, 609 driver_slots::NET_PACKET_RING_BASE, 610 staging::NETSTACK_DRIVER_RING_COUNT, 611 ); 612 613 boot::create_notification(allocator, staging::NETSTACK_NOTIF); 614 boot::grant_cap( 615 staging::NETSTACK_NOTIF, 616 staging::NETSTACK_PROC, 617 driver_slots::NET_NOTIF, 618 ); 619 boot::grant_cap( 620 staging::VIRTIO_NET_NOTIF, 621 staging::NETSTACK_PROC, 622 driver_slots::NET_DRIVER_NOTIF, 623 ); 624 boot::grant_cap( 625 staging::NETSTACK_NOTIF, 626 staging::VIRTIO_NET_PROC, 627 driver_slots::NETSTACK_NOTIF, 628 ); 629 630 true 631} 632 633fn bootstrap_init_netring(allocator: &mut UntypedAllocator) -> bool { 634 boot::create_shared_frames( 635 allocator, 636 NETSTACK_RING_BASE_SLOT, 637 staging::NETSTACK_PROC, 638 driver_slots::NET_INIT_RING_BASE, 639 NETSTACK_RING_FRAME_COUNT, 640 ); 641 642 boot::create_notification(allocator, SLOT_INIT_NOTIF); 643 syscall::cnode_copy(staging::NETSTACK_NOTIF, SLOT_NETSTACK_NOTIF, RIGHTS_ALL); 644 boot::grant_cap( 645 SLOT_INIT_NOTIF, 646 staging::NETSTACK_PROC, 647 driver_slots::NET_INIT_NOTIF, 648 ); 649 650 true 651} 652 653fn bootstrap_remote_shell(allocator: &mut UntypedAllocator, has_vfs: bool) -> bool { 654 let pid = match boot::create_process(allocator, b"remote-shell", staging::REMOTE_SHELL_PROC) { 655 Some(p) => p, 656 None => return false, 657 }; 658 659 boot::grant_cap( 660 SERIAL_ENDPOINT, 661 staging::REMOTE_SHELL_PROC, 662 driver_slots::RSHELL_EP, 663 ); 664 665 boot::grant_frames( 666 staging::REMOTE_SHELL_NET_RING_BASE, 667 staging::REMOTE_SHELL_PROC, 668 driver_slots::RSHELL_SHELL_RING_BASE, 669 staging::NETSTACK_SHELL_RING_COUNT, 670 ); 671 672 boot::create_notification(allocator, staging::REMOTE_SHELL_NOTIF); 673 boot::grant_cap( 674 staging::REMOTE_SHELL_NOTIF, 675 staging::REMOTE_SHELL_PROC, 676 driver_slots::RSHELL_NOTIF, 677 ); 678 boot::grant_cap( 679 staging::NETSTACK_NOTIF, 680 staging::REMOTE_SHELL_PROC, 681 driver_slots::RSHELL_NETSTACK_NOTIF, 682 ); 683 boot::grant_cap( 684 staging::REMOTE_SHELL_NOTIF, 685 staging::NETSTACK_PROC, 686 driver_slots::NET_SHELL_NOTIF, 687 ); 688 689 match has_vfs { 690 true => { 691 boot::create_shared_frames( 692 allocator, 693 staging::REMOTE_SHELL_VFS_RING_BASE, 694 staging::REMOTE_SHELL_PROC, 695 driver_slots::RSHELL_VFS_RING_BASE, 696 staging::REMOTE_SHELL_VFS_RING_COUNT, 697 ); 698 boot::grant_frames( 699 staging::REMOTE_SHELL_VFS_RING_BASE, 700 staging::VFS_PROC_SLOT, 701 driver_slots::VFS_CLIENT0_RING_BASE, 702 staging::REMOTE_SHELL_VFS_RING_COUNT, 703 ); 704 705 boot::grant_cap(SLOT_VFS_CLIENT_NOTIF, staging::REMOTE_SHELL_PROC, 12); 706 boot::grant_cap(SLOT_VFS_NOTIF, staging::REMOTE_SHELL_PROC, 13); 707 boot::grant_cap(staging::VFS_PROC_SLOT, staging::REMOTE_SHELL_PROC, 15); 708 } 709 false => {} 710 } 711 712 match boot::start_with_sched( 713 allocator, 714 staging::REMOTE_SHELL_PROC, 715 staging::REMOTE_SHELL_SCHED, 716 pid, 717 u64::MAX, 718 ) { 719 true => true, 720 false => { 721 show!(init, warn, "remote-shell start_with_sched failed"); 722 syscall::proc_destroy(staging::REMOTE_SHELL_PROC); 723 false 724 } 725 } 726} 727 728fn bootstrap_all(allocator: &mut UntypedAllocator) -> BootState { 729 let mut state = BootState::new(); 730 731 let com1_gsi = syscall::platform_info(boot::PLATFORM_COM1_GSI); 732 let kbd_gsi = syscall::platform_info(boot::PLATFORM_KBD_GSI); 733 let virtio_net_gsi = syscall::platform_info(boot::PLATFORM_VIRTIO_NET_GSI); 734 let has_fb = syscall::platform_info(boot::PLATFORM_HAS_FRAMEBUFFER) == 1; 735 736 match com1_gsi >= 0 { 737 true => { 738 state.has_serial = bootstrap_serial(allocator, com1_gsi as u64); 739 match state.has_serial { 740 true => show!(init, "serial driver started"), 741 false => show!(init, warn, "serial driver failed"), 742 } 743 } 744 false => show!(init, "skipping serial, no com1"), 745 } 746 747 match kbd_gsi >= 0 && state.has_serial { 748 true => { 749 state.has_ps2kbd = bootstrap_ps2kbd(allocator, kbd_gsi as u64); 750 match state.has_ps2kbd { 751 true => show!(init, "ps2kbd driver started"), 752 false => show!(init, warn, "ps2kbd driver failed"), 753 } 754 } 755 false => show!(init, "skipping ps2kbd"), 756 } 757 758 match has_fb { 759 true => { 760 state.has_fbconsole = bootstrap_fbconsole(allocator); 761 match state.has_fbconsole { 762 true => show!(init, "fbconsole driver started"), 763 false => show!(init, warn, "fbconsole driver failed"), 764 } 765 } 766 false => show!(init, "skipping fbconsole, no framebuffer"), 767 } 768 769 let nvme_dev_idx = boot::find_pci_device_by_class(0x01, 0x08, 0x02); 770 match nvme_dev_idx { 771 Some(idx) => { 772 state.has_nvme = bootstrap_nvme(allocator, idx); 773 match state.has_nvme { 774 true => show!(init, "nvme driver started"), 775 false => show!(init, warn, "nvme driver failed"), 776 } 777 } 778 None => show!(init, "skipping nvme, no device"), 779 } 780 781 if state.has_nvme { 782 show!(init, "creating lancerfs"); 783 state.has_lancerfs = bootstrap_lancerfs(allocator); 784 match state.has_lancerfs { 785 true => { 786 bootstrap_nvme_service(allocator); 787 show!(init, "lancerfs started, linked to nvme"); 788 } 789 false => show!(init, warn, "lancerfs failed"), 790 } 791 } 792 793 if state.has_lancerfs { 794 show!(init, "creating vfs"); 795 state.has_vfs = bootstrap_vfs(allocator); 796 match state.has_vfs { 797 true => { 798 bootstrap_vfs_lancerfs(allocator); 799 state.has_vfs_client = bootstrap_vfs_client(allocator); 800 show!(init, "vfs started, linked to lancerfs"); 801 } 802 false => show!(init, warn, "vfs failed"), 803 } 804 } 805 806 let ramfs_available = boot::find_module(b"ramfs").is_some(); 807 if ramfs_available && state.has_vfs { 808 show!(init, "creating ramfs"); 809 match bootstrap_ramfs(allocator) { 810 Some(pid) => { 811 bootstrap_vfs_ramfs(allocator); 812 state.has_ramfs = boot::start_with_sched( 813 allocator, 814 staging::RAMFS_PROC, 815 staging::RAMFS_SCHED, 816 pid, 817 u64::MAX, 818 ); 819 match state.has_ramfs { 820 true => show!(init, "ramfs started, linked to vfs"), 821 false => { 822 syscall::proc_destroy(staging::RAMFS_PROC); 823 show!(init, warn, "ramfs start failed"); 824 } 825 } 826 } 827 None => show!(init, warn, "ramfs failed"), 828 } 829 } 830 831 show!(init, "pci scan for virtio-net"); 832 let virtio_dev_idx = boot::find_pci_device_by_vendor(0x1AF4, 0x02, 0x00); 833 let mut virtio_net_pid: Option<i64> = None; 834 match virtio_dev_idx { 835 Some(idx) if virtio_net_gsi >= 0 => { 836 virtio_net_pid = bootstrap_virtio_net(allocator, idx, virtio_net_gsi as u64); 837 state.has_virtio_net = virtio_net_pid.is_some(); 838 match state.has_virtio_net { 839 true => show!(init, "virtio-net configured"), 840 false => show!(init, warn, "virtio-net driver failed"), 841 } 842 } 843 _ => show!(init, "skipping virtio-net"), 844 } 845 846 let mut netstack_pid: Option<i64> = None; 847 match state.has_virtio_net { 848 true => { 849 netstack_pid = bootstrap_netstack(allocator); 850 state.has_netstack = netstack_pid.is_some(); 851 match state.has_netstack { 852 true => { 853 bootstrap_net_service(allocator); 854 bootstrap_init_netring(allocator); 855 show!(init, "netstack created, linking services"); 856 } 857 false => show!(init, warn, "netstack failed"), 858 } 859 } 860 false => {} 861 } 862 863 if let Some(pid) = virtio_net_pid { 864 match boot::start_with_sched( 865 allocator, 866 staging::VIRTIO_NET_PROC, 867 staging::VIRTIO_NET_SCHED, 868 pid, 869 u64::MAX, 870 ) { 871 true => show!(init, "virtio-net driver started"), 872 false => { 873 state.has_virtio_net = false; 874 syscall::proc_destroy(staging::VIRTIO_NET_PROC); 875 show!(init, warn, "virtio-net start failed"); 876 } 877 } 878 } 879 880 if state.has_netstack { 881 let shell_frames_ok = boot::create_shared_frames( 882 allocator, 883 staging::REMOTE_SHELL_NET_RING_BASE, 884 staging::NETSTACK_PROC, 885 driver_slots::NET_SHELL_RING_BASE, 886 staging::NETSTACK_SHELL_RING_COUNT, 887 ); 888 match shell_frames_ok { 889 true => {} 890 false => show!(init, warn, "shell ring frame alloc failed"), 891 } 892 } 893 894 let remote_shell_available = boot::find_module(b"remote-shell").is_some(); 895 match remote_shell_available && state.has_netstack { 896 true => { 897 state.has_remote_shell = bootstrap_remote_shell(allocator, state.has_vfs_client); 898 match state.has_remote_shell { 899 true => show!(init, "remote-shell started"), 900 false => show!(init, warn, "remote-shell failed"), 901 } 902 } 903 false => {} 904 } 905 906 if let Some(pid) = netstack_pid { 907 match boot::start_with_sched( 908 allocator, 909 staging::NETSTACK_PROC, 910 staging::NETSTACK_SCHED, 911 pid, 912 u64::MAX, 913 ) { 914 true => show!(init, "netstack started"), 915 false => { 916 state.has_netstack = false; 917 syscall::proc_destroy(staging::NETSTACK_PROC); 918 show!(init, warn, "netstack start failed"); 919 } 920 } 921 } 922 923 state 924} 925 926use core::sync::atomic::{AtomicU64, Ordering}; 927 928static FLIGHT_CURSOR: AtomicU64 = AtomicU64::new(0); 929 930fn ensure_mount_points() { 931 let mut client = unsafe { 932 lancer_user::fs::FsClient::new( 933 VFS_RING_VADDR as usize, 934 SLOT_VFS_NOTIF as u8, 935 SLOT_VFS_CLIENT_NOTIF as u8, 936 ) 937 }; 938 match client.mkdir(0, b"tmp") { 939 Ok(()) => { 940 show!(init, "created /tmp mount point"); 941 } 942 Err(lancer_user::fs::FsStatus::FileExists) => {} 943 Err(_) => { 944 show!(init, warn, "failed to create /tmp"); 945 } 946 } 947} 948 949fn ensure_dir( 950 client: &mut lancer_user::fs::FsClient, 951 parent: u8, 952 name: &[u8], 953) -> Result<(), lancer_user::fs::FsStatus> { 954 match client.mkdir(parent, name) { 955 Ok(()) => Ok(()), 956 Err(lancer_user::fs::FsStatus::FileExists) => Ok(()), 957 Err(e) => Err(e), 958 } 959} 960 961fn open_persistent_log(client: &mut lancer_user::fs::FsClient) -> Option<(u8, &'static str)> { 962 use lancer_user::fs::FsRights; 963 964 let rw_create = 965 FsRights::from_raw(FsRights::READ.raw() | FsRights::WRITE.raw() | FsRights::CREATE.raw()); 966 967 let var_h = client.open(0, b"var", FsRights::ALL).ok()?; 968 let log_ok = ensure_dir(client, var_h, b"log").is_ok(); 969 match log_ok { 970 true => { 971 let log_h = client.open(var_h, b"log", FsRights::ALL).ok(); 972 let _ = client.close(var_h); 973 let log_h = log_h?; 974 match client.open(log_h, b"flight", rw_create) { 975 Ok(h) => { 976 let _ = client.close(log_h); 977 Some((h, "/var/log/flight")) 978 } 979 Err(_) => { 980 let _ = client.close(log_h); 981 None 982 } 983 } 984 } 985 false => { 986 let _ = client.close(var_h); 987 None 988 } 989 } 990} 991 992fn open_tmp_log(client: &mut lancer_user::fs::FsClient) -> Option<(u8, &'static str)> { 993 use lancer_user::fs::FsRights; 994 995 let rw_create = 996 FsRights::from_raw(FsRights::READ.raw() | FsRights::WRITE.raw() | FsRights::CREATE.raw()); 997 998 let tmp_h = client.open(0, b"tmp", FsRights::ALL).ok()?; 999 match client.open(tmp_h, b"flight", rw_create) { 1000 Ok(h) => { 1001 let _ = client.close(tmp_h); 1002 Some((h, "/tmp/flight")) 1003 } 1004 Err(_) => { 1005 let _ = client.close(tmp_h); 1006 None 1007 } 1008 } 1009} 1010 1011fn try_drain( 1012 client: &mut lancer_user::fs::FsClient, 1013 handle: u8, 1014 path: &str, 1015 start_cursor: u64, 1016) -> (u64, u64, bool) { 1017 let file_offset = match client.stat(handle) { 1018 Ok(st) => st.size, 1019 Err(_) => 0, 1020 }; 1021 1022 let mut cursor = start_cursor; 1023 let mut written: u64 = 0; 1024 let mut buf = [0u8; 4096]; 1025 let mut disk_err = false; 1026 1027 loop { 1028 let (new_cursor, bytes_read) = syscall::blackbox_read(cursor, &mut buf); 1029 match bytes_read { 1030 0 => break, 1031 n => match client.write(handle, file_offset + written, &buf[..n]) { 1032 Ok(w) => { 1033 written += w as u64; 1034 cursor = new_cursor; 1035 } 1036 Err(_) => { 1037 disk_err = true; 1038 break; 1039 } 1040 }, 1041 } 1042 } 1043 1044 let _ = client.close(handle); 1045 1046 match (written, disk_err) { 1047 (0, false) => show!(init, "flight log empty at cursor {}", start_cursor), 1048 (0, true) => show!(init, warn, "flight log write failed to {}", path), 1049 (_, true) => show!( 1050 init, 1051 warn, 1052 "flight log partial drain {} bytes to {}", 1053 written, 1054 path 1055 ), 1056 (_, false) => show!(init, "flight log drained {} bytes to {}", written, path), 1057 } 1058 1059 (cursor, written, disk_err) 1060} 1061 1062fn drain_flight_log() { 1063 use lancer_user::fs::FsClient; 1064 1065 let mut client = unsafe { 1066 FsClient::reattach( 1067 VFS_RING_VADDR as usize, 1068 SLOT_VFS_NOTIF as u8, 1069 SLOT_VFS_CLIENT_NOTIF as u8, 1070 ) 1071 }; 1072 1073 let cursor = FLIGHT_CURSOR.load(Ordering::Relaxed); 1074 1075 if let Some((handle, path)) = open_persistent_log(&mut client) { 1076 let (new_cursor, written, failed) = try_drain(&mut client, handle, path, cursor); 1077 match failed && written == 0 { 1078 false => { 1079 FLIGHT_CURSOR.store(new_cursor, Ordering::Relaxed); 1080 return; 1081 } 1082 true => { 1083 show!(init, warn, "persistent log full, falling back to /tmp"); 1084 } 1085 } 1086 } 1087 1088 match open_tmp_log(&mut client) { 1089 Some((handle, path)) => { 1090 let (new_cursor, _, _) = try_drain(&mut client, handle, path, cursor); 1091 FLIGHT_CURSOR.store(new_cursor, Ordering::Relaxed); 1092 } 1093 None => { 1094 show!(init, warn, "flight log not writable"); 1095 } 1096 } 1097} 1098 1099fn spawn_shell(allocator: &mut UntypedAllocator) -> bool { 1100 let module_idx = match boot::find_module(b"shell") { 1101 Some(idx) => idx, 1102 None => { 1103 show!(init, error, "shell module not found"); 1104 return false; 1105 } 1106 }; 1107 1108 let ut_slot = match allocator.find_for_retype(boot::TAG_PROCESS, 0) { 1109 Ok(s) => s, 1110 Err(_) => { 1111 show!(init, error, "no untyped large enough for process"); 1112 return false; 1113 } 1114 }; 1115 1116 let child_pid = syscall::proc_create(ut_slot, SLOT_SHELL_PROC); 1117 if child_pid < 0 { 1118 show!(init, error, "failed to create shell process"); 1119 return false; 1120 } 1121 1122 let r = syscall::proc_load_module(SLOT_SHELL_PROC, module_idx); 1123 if r < 0 { 1124 show!(init, error, "failed to load shell module"); 1125 syscall::proc_destroy(SLOT_SHELL_PROC); 1126 return false; 1127 } 1128 1129 let shell_ut = match allocator.find_large_untyped(256 * 1024) { 1130 Ok(s) => s, 1131 Err(_) => { 1132 show!(init, error, "no large untyped for shell"); 1133 syscall::proc_destroy(SLOT_SHELL_PROC); 1134 return false; 1135 } 1136 }; 1137 let r = syscall::cap_grant(shell_ut, SLOT_SHELL_PROC, 28, RIGHTS_ALL); 1138 if r < 0 { 1139 show!(init, error, "failed to grant untyped to shell"); 1140 syscall::proc_destroy(SLOT_SHELL_PROC); 1141 return false; 1142 } 1143 1144 let r = syscall::cap_grant(SERIAL_ENDPOINT, SLOT_SHELL_PROC, 1, RIGHTS_ALL); 1145 if r < 0 { 1146 show!(init, error, "failed to grant serial endpoint to shell"); 1147 syscall::proc_destroy(SLOT_SHELL_PROC); 1148 return false; 1149 } 1150 1151 [ 1152 (SLOT_INIT_NOTIF, 9u64), 1153 (SLOT_NETSTACK_NOTIF, 10), 1154 (SLOT_VFS_CLIENT_NOTIF, 12), 1155 (SLOT_VFS_NOTIF, 13), 1156 (SLOT_VFS_PROC, 15), 1157 ] 1158 .iter() 1159 .for_each(|&(src_slot, dst_slot)| { 1160 let _ = syscall::cap_grant(src_slot, SLOT_SHELL_PROC, dst_slot, RIGHTS_ALL); 1161 }); 1162 1163 (0..NETSTACK_RING_FRAME_COUNT).for_each(|i| { 1164 let _ = syscall::cap_grant( 1165 NETSTACK_RING_BASE_SLOT + i, 1166 SLOT_SHELL_PROC, 1167 SHELL_NETSTACK_RING_DST + i, 1168 RIGHTS_ALL, 1169 ); 1170 }); 1171 1172 (0..VFS_RING_FRAME_COUNT).for_each(|i| { 1173 let _ = syscall::cap_grant( 1174 VFS_RING_BASE_SLOT + i, 1175 SLOT_SHELL_PROC, 1176 SHELL_VFS_RING_DST + i, 1177 RIGHTS_ALL, 1178 ); 1179 }); 1180 1181 let r = match allocator.alloc_object(TAG_NOTIFICATION, 0, SLOT_SHELL_DEATH_NOTIF) { 1182 Ok(_) => 0, 1183 Err(_) => -1, 1184 }; 1185 if r < 0 { 1186 show!(init, error, "failed to create shell death notif"); 1187 syscall::proc_destroy(SLOT_SHELL_PROC); 1188 return false; 1189 } 1190 1191 syscall::proc_bind_death_notif( 1192 SLOT_SHELL_PROC, 1193 SLOT_SHELL_DEATH_NOTIF, 1194 NOTIFY_BIT_CHILD_DEATH, 1195 ); 1196 1197 let r = match allocator.alloc_object(TAG_SCHED_CONTEXT, 0, SLOT_SHELL_SCHED) { 1198 Ok(_) => 0, 1199 Err(_) => -1, 1200 }; 1201 if r < 0 { 1202 show!(init, error, "failed to create shell sched context"); 1203 syscall::proc_destroy(SLOT_SHELL_PROC); 1204 syscall::cap_revoke(SLOT_SHELL_DEATH_NOTIF); 1205 return false; 1206 } 1207 let r = syscall::sched_configure(SLOT_SHELL_SCHED, 10_000_000, 10_000_000, 100); 1208 if r < 0 { 1209 show!(init, error, "failed to configure shell sched context"); 1210 syscall::proc_destroy(SLOT_SHELL_PROC); 1211 syscall::cap_revoke(SLOT_SHELL_SCHED); 1212 syscall::cap_revoke(SLOT_SHELL_DEATH_NOTIF); 1213 return false; 1214 } 1215 1216 let r = syscall::sched_attach(SLOT_SHELL_SCHED, child_pid as u64); 1217 if r < 0 { 1218 show!(init, error, "failed to attach shell sched context"); 1219 syscall::proc_destroy(SLOT_SHELL_PROC); 1220 syscall::cap_revoke(SLOT_SHELL_SCHED); 1221 syscall::cap_revoke(SLOT_SHELL_DEATH_NOTIF); 1222 return false; 1223 } 1224 1225 let r = syscall::proc_start(SLOT_SHELL_PROC, 0); 1226 if r < 0 { 1227 show!(init, error, "failed to start shell"); 1228 syscall::proc_destroy(SLOT_SHELL_PROC); 1229 syscall::cap_revoke(SLOT_SHELL_SCHED); 1230 syscall::cap_revoke(SLOT_SHELL_DEATH_NOTIF); 1231 return false; 1232 } 1233 1234 true 1235} 1236 1237fn wait_for_shell_death() { 1238 loop { 1239 let (_, bits) = syscall::notify_wait(SLOT_SHELL_DEATH_NOTIF); 1240 if bits & NOTIFY_BIT_CHILD_DEATH != 0 { 1241 syscall::proc_destroy(SLOT_SHELL_PROC); 1242 syscall::cap_revoke(SLOT_SHELL_SCHED); 1243 syscall::cap_revoke(SLOT_SHELL_DEATH_NOTIF); 1244 break; 1245 } 1246 } 1247} 1248 1249#[unsafe(no_mangle)] 1250pub extern "C" fn lancer_main() -> ! { 1251 let mut allocator = match UntypedAllocator::from_boot_info() { 1252 Ok(a) => a, 1253 Err(_) => { 1254 show!(init, error, "failed to init untyped allocator"); 1255 syscall::exit(); 1256 } 1257 }; 1258 1259 let boot_state = bootstrap_all(&mut allocator); 1260 1261 let has_vfs = boot_state.has_vfs_client 1262 && (0..VFS_RING_FRAME_COUNT) 1263 .all(|i| syscall::frame_map(VFS_RING_BASE_SLOT + i, VFS_RING_VADDR + i * 4096, 1) >= 0); 1264 match has_vfs { 1265 true => { 1266 syscall::notify_wait(SLOT_VFS_CLIENT_NOTIF); 1267 show!(init, "vfs ring mapped"); 1268 ensure_mount_points(); 1269 drain_flight_log(); 1270 } 1271 false => { 1272 show!(init, warn, "vfs not available"); 1273 } 1274 } 1275 1276 loop { 1277 match spawn_shell(&mut allocator) { 1278 true => { 1279 wait_for_shell_death(); 1280 show!(init, "shell exited, respawning"); 1281 if has_vfs { 1282 drain_flight_log(); 1283 } 1284 } 1285 false => { 1286 show!(init, error, "shell spawn failed, retrying"); 1287 syscall::sched_yield(); 1288 } 1289 } 1290 } 1291}