diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-01 15:00:47 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-01 15:00:47 +0200 |
| commit | d562e9df2bc5e69bba7298a9439e2adca5c18faf (patch) | |
| tree | 8d763e60d512e7a4230413f13caaf9153cb2ec54 /shell/src/lib.rs | |
| parent | klog: [ssss.mmm] millisecond timestamps from the PIT (diff) | |
| download | rs-dos-d562e9df2bc5e69bba7298a9439e2adca5c18faf.tar.gz rs-dos-d562e9df2bc5e69bba7298a9439e2adca5c18faf.zip | |
rsnix: runtime device detection — rescan for hotplugged/removed disks
Phase 1: make the boot discovery pipeline re-runnable and diff it
against live hardware state.
- ahci: store ABAR/NP/port bitmap; poll_dirty() (cheap PxSSTS.DET check
per port) and rescan() which starts newly-present ports and tombstones
the slots of removed ones. Slots never shift, so disk indices stay
valid. select() now rejects tombstoned slots.
- fat: Volume gets a mounted flag; rescan() unmounts volumes whose disk
left and mounts every disk without a mounted volume (re-trying failed
boot mounts). UID dedup ignores tombstones so a re-plugged disk keeps
its /disk/<UID> name.
- fs: init's mount loop factored into rescan_disks(), which lazily
creates /disk, ensures a mount node per mounted volume and tombstones
nodes of unmounted ones.
- shell: 'rescan' command + automatic deep rescan before each prompt
when ahci::poll_dirty() sees a port-presence change.
Rescan events flow through serial_println so they land in klog too.
Removal (PxSSTS) is a real-hardware path: QEMU q35 rejects disk hotplug
on the ich9-ahci IDE buses, and PCIe device_del is a guest-cooperative
protocol this kernel does not implement.
Diffstat (limited to '')
| -rw-r--r-- | shell/src/lib.rs | 35 |
1 files changed, 35 insertions, 0 deletions
diff --git a/shell/src/lib.rs b/shell/src/lib.rs index ddd8688..28eb737 100644 --- a/shell/src/lib.rs +++ b/shell/src/lib.rs | |||
| @@ -45,6 +45,12 @@ pub fn run() -> ! { | |||
| 45 | // Push any deferred scrolls so the prompt renders on a current | 45 | // Push any deferred scrolls so the prompt renders on a current |
| 46 | // framebuffer (e.g. after a streaming command's batch of output). | 46 | // framebuffer (e.g. after a streaming command's batch of output). |
| 47 | rsnix::vt::sync_scroll(); | 47 | rsnix::vt::sync_scroll(); |
| 48 | // Cheap hotplug probe: if any AHCI port's device presence changed | ||
| 49 | // since the last scan, re-run discovery (mounts new volumes, | ||
| 50 | // unmounts gone ones) before showing the prompt. | ||
| 51 | if rsnix::ahci::poll_dirty() { | ||
| 52 | report_rescan(rescan_all()); | ||
| 53 | } | ||
| 48 | // Print prompt | 54 | // Print prompt |
| 49 | term_print("> "); | 55 | term_print("> "); |
| 50 | 56 | ||
| @@ -681,6 +687,7 @@ write `{} <path> TIL <marker>`", | |||
| 681 | "stress" => cmd_stress(&parsed), | 687 | "stress" => cmd_stress(&parsed), |
| 682 | "date" => cmd_date(&parsed), | 688 | "date" => cmd_date(&parsed), |
| 683 | "pci" => cmd_pci(&parsed), | 689 | "pci" => cmd_pci(&parsed), |
| 690 | "rescan" => cmd_rescan(&parsed), | ||
| 684 | "ls" => cmd_ls(&parsed), | 691 | "ls" => cmd_ls(&parsed), |
| 685 | "cat" => cmd_cat(&parsed), | 692 | "cat" => cmd_cat(&parsed), |
| 686 | "klog" => cmd_klog(&parsed), | 693 | "klog" => cmd_klog(&parsed), |
| @@ -710,6 +717,7 @@ fn cmd_help(_p: &Parsed) { | |||
| 710 | stress — stress-test heap/page allocators | 717 | stress — stress-test heap/page allocators |
| 711 | date — show current date/time (RTC) | 718 | date — show current date/time (RTC) |
| 712 | pci — list PCI devices | 719 | pci — list PCI devices |
| 720 | rescan — re-scan for hotplugged/removed disks (also runs automatically) | ||
| 713 | ls — list a directory | 721 | ls — list a directory |
| 714 | cat — print files (cat USE a TO b copies a→b; cat TIL END is a heredoc) | 722 | cat — print files (cat USE a TO b copies a→b; cat TIL END is a heredoc) |
| 715 | klog — kernel boot log (cat /dev/klog; WRITING <dst> persists it) | 723 | klog — kernel boot log (cat /dev/klog; WRITING <dst> persists it) |
| @@ -1527,6 +1535,33 @@ fn cmd_date(p: &Parsed) { | |||
| 1527 | pipe.end(); | 1535 | pipe.end(); |
| 1528 | } | 1536 | } |
| 1529 | 1537 | ||
| 1538 | /// Deep device re-scan: AHCI ports → FAT volumes → VFS mount table. | ||
| 1539 | /// Returns (disks added, disks removed, vols mounted, vols unmounted, | ||
| 1540 | /// nodes added, nodes removed) so callers can report the result. | ||
| 1541 | fn rescan_all() -> (usize, usize, usize, usize, usize, usize) { | ||
| 1542 | let (da, dr) = rsnix::ahci::rescan(); | ||
| 1543 | let (ma, mu) = rsnix::fat::rescan(); | ||
| 1544 | let (na, nr) = rsnix::fs::rescan_disks(); | ||
| 1545 | (da, dr, ma, mu, na, nr) | ||
| 1546 | } | ||
| 1547 | |||
| 1548 | /// Print the one-line change summary; returns true if anything changed. | ||
| 1549 | fn report_rescan((da, dr, ma, mu, na, nr): (usize, usize, usize, usize, usize, usize)) -> bool { | ||
| 1550 | term_println(&alloc::format!( | ||
| 1551 | "rescan: {} disk(s) added, {} removed · {} volume(s) mounted, {} unmounted · {} node(s) added, {} removed", | ||
| 1552 | da, dr, ma, mu, na, nr | ||
| 1553 | )); | ||
| 1554 | da + dr + ma + mu + na + nr > 0 | ||
| 1555 | } | ||
| 1556 | |||
| 1557 | fn cmd_rescan(_p: &Parsed) { | ||
| 1558 | if report_rescan(rescan_all()) { | ||
| 1559 | term_println("rescan done — /disk updated"); | ||
| 1560 | } else { | ||
| 1561 | term_println("rescan done — nothing changed"); | ||
| 1562 | } | ||
| 1563 | } | ||
| 1564 | |||
| 1530 | fn cmd_pci(p: &Parsed) { | 1565 | fn cmd_pci(p: &Parsed) { |
| 1531 | let devices = rsnix::pci::probe(); | 1566 | let devices = rsnix::pci::probe(); |
| 1532 | let mut pipe = Pipeline::new(&p.filters, &p.output); | 1567 | let mut pipe = Pipeline::new(&p.filters, &p.output); |
