diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-01 18:25:55 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-01 18:25:55 +0200 |
| commit | 1ea2f68e6a9af135e6cb3274fdf28d4ddc5812d5 (patch) | |
| tree | f28246586e387293d0c8beaf6b445210aaab8c48 /rsnix | |
| parent | klog/tty/pager: no more truncated lines — full lines flow, terminal wraps (diff) | |
| download | rs-dos-1ea2f68e6a9af135e6cb3274fdf28d4ddc5812d5.tar.gz rs-dos-1ea2f68e6a9af135e6cb3274fdf28d4ddc5812d5.zip | |
fix pi-lens warnings: unwraps, shell SC2046, dup code (fmtbuf/scripts/hexdump/cat/ahci/pci)
- memory.rs: self-test Layout::from_size_align no longer panics — the
test fails cleanly on an invalid layout
- fat.rs: v() no longer unwraps the active-volume slot — invariant
violation logs + halts instead of panicking mid-disk-io
- strbuf: shared FmtBuf replaces the duplicated PanicBuf/FmtBuf
(main.rs + sched.rs)
- test-disk.sh: SC2046 — conditional --fresh arg no longer relies on
word splitting
- scripts: boot.sh/boot-image.sh share lib-boot-args.sh (dup args loop)
- shell: cat/hexdump stdin pairs unified with Option<&str> marker;
hexdump format_row folded into format_partial
- ahci: read/write_sectors unified behind dma_sectors
- pci: config_read/write share config_addr
Diffstat (limited to 'rsnix')
| -rw-r--r-- | rsnix/src/ahci.rs | 18 | ||||
| -rw-r--r-- | rsnix/src/fat.rs | 12 | ||||
| -rw-r--r-- | rsnix/src/memory.rs | 12 | ||||
| -rw-r--r-- | rsnix/src/pci.rs | 28 | ||||
| -rw-r--r-- | rsnix/src/sched.rs | 18 | ||||
| -rw-r--r-- | rsnix/src/strbuf.rs | 28 |
6 files changed, 76 insertions, 40 deletions
diff --git a/rsnix/src/ahci.rs b/rsnix/src/ahci.rs index 72da07d..a77b341 100644 --- a/rsnix/src/ahci.rs +++ b/rsnix/src/ahci.rs | |||
| @@ -510,22 +510,24 @@ fn active() -> Option<&'static Disk> { | |||
| 510 | disks().get(unsafe { ACTIVE }).and_then(|d| d.as_ref()) | 510 | disks().get(unsafe { ACTIVE }).and_then(|d| d.as_ref()) |
| 511 | } | 511 | } |
| 512 | 512 | ||
| 513 | /// DMA `count` sectors starting at `lba` into the selected disk's buffer. | 513 | /// Bounds-checked DMA of `count` sectors starting at `lba` into/out of the |
| 514 | pub fn read_sectors(lba: u64, count: u32) -> Result<(), ()> { | 514 | /// selected disk's buffer (single code path for both directions). |
| 515 | fn dma_sectors(lba: u64, count: u32, cmd: u8, write: bool) -> Result<(), ()> { | ||
| 515 | let d = active().ok_or(())?; | 516 | let d = active().ok_or(())?; |
| 516 | if lba + count as u64 > d.sectors { | 517 | if lba + count as u64 > d.sectors { |
| 517 | return Err(()); | 518 | return Err(()); |
| 518 | } | 519 | } |
| 519 | issue(d, ATA_READ_DMA_EXT, lba, count, false) | 520 | issue(d, cmd, lba, count, write) |
| 521 | } | ||
| 522 | |||
| 523 | /// DMA `count` sectors starting at `lba` into the selected disk's buffer. | ||
| 524 | pub fn read_sectors(lba: u64, count: u32) -> Result<(), ()> { | ||
| 525 | dma_sectors(lba, count, ATA_READ_DMA_EXT, false) | ||
| 520 | } | 526 | } |
| 521 | 527 | ||
| 522 | /// DMA `count` sectors starting at `lba` out of the selected disk's buffer. | 528 | /// DMA `count` sectors starting at `lba` out of the selected disk's buffer. |
| 523 | pub fn write_sectors(lba: u64, count: u32) -> Result<(), ()> { | 529 | pub fn write_sectors(lba: u64, count: u32) -> Result<(), ()> { |
| 524 | let d = active().ok_or(())?; | 530 | dma_sectors(lba, count, ATA_WRITE_DMA_EXT, true) |
| 525 | if lba + count as u64 > d.sectors { | ||
| 526 | return Err(()); | ||
| 527 | } | ||
| 528 | issue(d, ATA_WRITE_DMA_EXT, lba, count, true) | ||
| 529 | } | 531 | } |
| 530 | 532 | ||
| 531 | /// Pointer to the selected disk's 4 KiB DMA buffer. | 533 | /// Pointer to the selected disk's 4 KiB DMA buffer. |
diff --git a/rsnix/src/fat.rs b/rsnix/src/fat.rs index bad01cd..eda81ed 100644 --- a/rsnix/src/fat.rs +++ b/rsnix/src/fat.rs | |||
| @@ -167,8 +167,18 @@ fn write_sector(lba: u64, data: &[u8; SECTOR]) -> Result<(), ()> { | |||
| 167 | 167 | ||
| 168 | // --- cluster math ---------------------------------------------------------- | 168 | // --- cluster math ---------------------------------------------------------- |
| 169 | 169 | ||
| 170 | /// Current volume's metadata — only ever reached with a selected volume | ||
| 171 | /// (the fs layer calls `select_vol` first), so a missing slot is a kernel | ||
| 172 | /// invariant violation, not a recoverable error. | ||
| 170 | fn v() -> &'static mut Volume { | 173 | fn v() -> &'static mut Volume { |
| 171 | vols().get_mut(*active_vol()).unwrap() | 174 | let i = *active_vol(); |
| 175 | match vols().get_mut(i) { | ||
| 176 | Some(v) => v, | ||
| 177 | None => { | ||
| 178 | crate::serial_println!("fat: internal error — no volume at slot {}", i); | ||
| 179 | crate::interrupts::halt(); | ||
| 180 | } | ||
| 181 | } | ||
| 172 | } | 182 | } |
| 173 | 183 | ||
| 174 | fn spc() -> usize { | 184 | fn spc() -> usize { |
diff --git a/rsnix/src/memory.rs b/rsnix/src/memory.rs index 7c3eda0..e03af8b 100644 --- a/rsnix/src/memory.rs +++ b/rsnix/src/memory.rs | |||
| @@ -458,7 +458,9 @@ pub fn self_test() -> bool { | |||
| 458 | seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1); | 458 | seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1); |
| 459 | let align = 16usize << (seed as usize % 8); | 459 | let align = 16usize << (seed as usize % 8); |
| 460 | seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1); | 460 | seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1); |
| 461 | let layout = Layout::from_size_align(size, align).unwrap(); | 461 | let Ok(layout) = Layout::from_size_align(size, align) else { |
| 462 | return false; // impossible: size <= 2048, align <= 2048 | ||
| 463 | }; | ||
| 462 | let p = ALLOCATOR.alloc(layout); | 464 | let p = ALLOCATOR.alloc(layout); |
| 463 | if p.is_null() { | 465 | if p.is_null() { |
| 464 | break; // heap exhausted — stop | 466 | break; // heap exhausted — stop |
| @@ -493,7 +495,9 @@ pub fn self_test() -> bool { | |||
| 493 | seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1); | 495 | seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1); |
| 494 | let align = 16usize << (seed as usize % 8); | 496 | let align = 16usize << (seed as usize % 8); |
| 495 | seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1); | 497 | seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1); |
| 496 | let layout = Layout::from_size_align(size, align).unwrap(); | 498 | let Ok(layout) = Layout::from_size_align(size, align) else { |
| 499 | return false; // impossible: size <= 2048, align <= 2048 | ||
| 500 | }; | ||
| 497 | let p = ALLOCATOR.alloc(layout); | 501 | let p = ALLOCATOR.alloc(layout); |
| 498 | if p.is_null() { | 502 | if p.is_null() { |
| 499 | return false; // failed to reuse freed space | 503 | return false; // failed to reuse freed space |
| @@ -510,7 +514,9 @@ pub fn self_test() -> bool { | |||
| 510 | if *ptr == 0 { | 514 | if *ptr == 0 { |
| 511 | continue; | 515 | continue; |
| 512 | } | 516 | } |
| 513 | let layout = Layout::from_size_align(*size, *align).unwrap(); | 517 | let Ok(layout) = Layout::from_size_align(*size, *align) else { |
| 518 | return false; // impossible: sizes/aligns came from the loop above | ||
| 519 | }; | ||
| 514 | ALLOCATOR.dealloc(*ptr as *mut u8, layout); | 520 | ALLOCATOR.dealloc(*ptr as *mut u8, layout); |
| 515 | } | 521 | } |
| 516 | let stats = heap_stats(); | 522 | let stats = heap_stats(); |
diff --git a/rsnix/src/pci.rs b/rsnix/src/pci.rs index 21d7536..b9c2265 100644 --- a/rsnix/src/pci.rs +++ b/rsnix/src/pci.rs | |||
| @@ -46,25 +46,25 @@ fn inl(port: u16) -> u32 { | |||
| 46 | val | 46 | val |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | /// Read a dword from a device's config space (`reg` must be dword-aligned). | 49 | /// Encode the PCI config-space port address for (bus, dev, fn, reg); |
| 50 | pub fn config_read(bus: u8, device: u8, func: u8, reg: u8) -> u32 { | 50 | /// `reg` must be dword-aligned. |
| 51 | let addr = 0x8000_0000 | 51 | fn config_addr(bus: u8, device: u8, func: u8, reg: u8) -> u32 { |
| 52 | 0x8000_0000 | ||
| 52 | | ((bus as u32) << 16) | 53 | | ((bus as u32) << 16) |
| 53 | | ((device as u32) << 11) | 54 | | ((device as u32) << 11) |
| 54 | | ((func as u32) << 8) | 55 | | ((func as u32) << 8) |
| 55 | | ((reg as u32) & 0xFC); | 56 | | ((reg as u32) & 0xFC) |
| 56 | outl(CONFIG_ADDRESS, addr); | 57 | } |
| 58 | |||
| 59 | /// Read a dword from a device's config space (`reg` must be dword-aligned). | ||
| 60 | pub fn config_read(bus: u8, device: u8, func: u8, reg: u8) -> u32 { | ||
| 61 | outl(CONFIG_ADDRESS, config_addr(bus, device, func, reg)); | ||
| 57 | inl(CONFIG_DATA) | 62 | inl(CONFIG_DATA) |
| 58 | } | 63 | } |
| 59 | 64 | ||
| 60 | /// Write a dword to a device's config space (`reg` must be dword-aligned). | 65 | /// Write a dword to a device's config space (`reg` must be dword-aligned). |
| 61 | pub fn config_write(bus: u8, device: u8, func: u8, reg: u8, val: u32) { | 66 | pub fn config_write(bus: u8, device: u8, func: u8, reg: u8, val: u32) { |
| 62 | let addr = 0x8000_0000 | 67 | outl(CONFIG_ADDRESS, config_addr(bus, device, func, reg)); |
| 63 | | ((bus as u32) << 16) | ||
| 64 | | ((device as u32) << 11) | ||
| 65 | | ((func as u32) << 8) | ||
| 66 | | ((reg as u32) & 0xFC); | ||
| 67 | outl(CONFIG_ADDRESS, addr); | ||
| 68 | outl(CONFIG_DATA, val) | 68 | outl(CONFIG_DATA, val) |
| 69 | } | 69 | } |
| 70 | 70 | ||
| @@ -83,7 +83,11 @@ pub fn bar64(bus: u8, device: u8, func: u8, bar_index: u8) -> Option<u64> { | |||
| 83 | 0 | 83 | 0 |
| 84 | }; | 84 | }; |
| 85 | let base = ((hi as u64) << 32) | (lo as u64 & 0xFFFF_FFF0); | 85 | let base = ((hi as u64) << 32) | (lo as u64 & 0xFFFF_FFF0); |
| 86 | if base == 0 { None } else { Some(base) } | 86 | if base == 0 { |
| 87 | None | ||
| 88 | } else { | ||
| 89 | Some(base) | ||
| 90 | } | ||
| 87 | } | 91 | } |
| 88 | 92 | ||
| 89 | /// Enumerate every device/function present on all buses. | 93 | /// Enumerate every device/function present on all buses. |
diff --git a/rsnix/src/sched.rs b/rsnix/src/sched.rs index 4b10503..6ffa745 100644 --- a/rsnix/src/sched.rs +++ b/rsnix/src/sched.rs | |||
| @@ -372,7 +372,7 @@ pub fn page_fault(cr2: u64, frame_ptr: usize) -> usize { | |||
| 372 | let rip = unsafe { (frame_ptr as *const IntFrame).read().rip }; | 372 | let rip = unsafe { (frame_ptr as *const IntFrame).read().rip }; |
| 373 | let mut buf = [0u8; 96]; | 373 | let mut buf = [0u8; 96]; |
| 374 | let n = { | 374 | let n = { |
| 375 | let mut f = FmtBuf { b: &mut buf, n: 0 }; | 375 | let mut f = crate::strbuf::FmtBuf::new(&mut buf); |
| 376 | let _ = fmt::write( | 376 | let _ = fmt::write( |
| 377 | &mut f, | 377 | &mut f, |
| 378 | core::format_args!( | 378 | core::format_args!( |
| @@ -386,7 +386,7 @@ pub fn page_fault(cr2: u64, frame_ptr: usize) -> usize { | |||
| 386 | rip | 386 | rip |
| 387 | ), | 387 | ), |
| 388 | ); | 388 | ); |
| 389 | f.n | 389 | f.len() |
| 390 | }; | 390 | }; |
| 391 | abort_current(core::str::from_utf8(&buf[..n]).unwrap_or("task fault")) | 391 | abort_current(core::str::from_utf8(&buf[..n]).unwrap_or("task fault")) |
| 392 | } | 392 | } |
| @@ -528,17 +528,3 @@ fn idle_main() { | |||
| 528 | } | 528 | } |
| 529 | } | 529 | } |
| 530 | } | 530 | } |
| 531 | |||
| 532 | struct FmtBuf<'a> { | ||
| 533 | b: &'a mut [u8], | ||
| 534 | n: usize, | ||
| 535 | } | ||
| 536 | |||
| 537 | impl fmt::Write for FmtBuf<'_> { | ||
| 538 | fn write_str(&mut self, s: &str) -> fmt::Result { | ||
| 539 | let m = s.len().min(self.b.len() - self.n); | ||
| 540 | self.b[self.n..self.n + m].copy_from_slice(&s.as_bytes()[..m]); | ||
| 541 | self.n += m; | ||
| 542 | Ok(()) | ||
| 543 | } | ||
| 544 | } | ||
diff --git a/rsnix/src/strbuf.rs b/rsnix/src/strbuf.rs index 262720d..0e3ea56 100644 --- a/rsnix/src/strbuf.rs +++ b/rsnix/src/strbuf.rs | |||
| @@ -69,3 +69,31 @@ impl<const N: usize> fmt::Debug for String<N> { | |||
| 69 | write!(f, "{:?}", self.as_str()) | 69 | write!(f, "{:?}", self.as_str()) |
| 70 | } | 70 | } |
| 71 | } | 71 | } |
| 72 | |||
| 73 | /// Small `fmt::Write` over a byte slice — no allocation, bounded. | ||
| 74 | /// Shared by the panic handler (task-panic message) and the scheduler | ||
| 75 | /// (page-fault abort message) so they don't each reinvent it. | ||
| 76 | pub struct FmtBuf<'a> { | ||
| 77 | b: &'a mut [u8], | ||
| 78 | n: usize, | ||
| 79 | } | ||
| 80 | |||
| 81 | impl<'a> FmtBuf<'a> { | ||
| 82 | pub fn new(b: &'a mut [u8]) -> Self { | ||
| 83 | FmtBuf { b, n: 0 } | ||
| 84 | } | ||
| 85 | |||
| 86 | /// Bytes written so far (clamped at the buffer length). | ||
| 87 | pub fn len(&self) -> usize { | ||
| 88 | self.n | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | impl fmt::Write for FmtBuf<'_> { | ||
| 93 | fn write_str(&mut self, s: &str) -> fmt::Result { | ||
| 94 | let m = s.len().min(self.b.len() - self.n); | ||
| 95 | self.b[self.n..self.n + m].copy_from_slice(&s.as_bytes()[..m]); | ||
| 96 | self.n += m; | ||
| 97 | Ok(()) | ||
| 98 | } | ||
| 99 | } | ||
