From 63d01e9d8eb6f3ccb6a96b7c577834276cf51add Mon Sep 17 00:00:00 2001 From: salmonik Date: Sat, 1 Aug 2026 03:44:57 +0200 Subject: Clean up warnings, fix FAT32 multi-cluster dirs, prep for edition 2024 Warning cleanup (cargo check + clippy now 0 across the workspace): - Migrate all 21 static_mut_refs sites to &raw mut accessors (fat, gop, ps2, vt, ahci, interrupts, memory) so edition 2024 won't deny them - panic = "abort" profiles: bare cargo check/build now works without nightly/-Zbuild-std - Wire TICKS in the timer ISR (it was never incremented, so the vt scroll batching trigger was dead code) - Clippy: efi_main becomes unsafe extern "efiapi" with a # Safety doc, KeyBuf::new uses inline-const array repeat (no interior-mutable const), editor main loop is a while-let, PIT control byte regrouped - [workspace.lints.clippy] allows result_unit_err + needless_range_loop (deliberate kernel style); large_enum_variant allowed on LineInput - Drop now-stale static_mut_refs allow from the rsdos bin FAT32 correctness fixes (both pre-existing, exposed by the SFN fix): - Unique 8.3 short names: to_8_3() never disambiguated collisions, so files sharing a long-name prefix got identical SFNs and fsck reported duplicate directory entries. Add dir_short_names() + unique_short_name() (~N tails, Windows-style) and force LFN when the SFN changes. - Multi-cluster directories: scan_dir/dir_short_names stopped at the first 0x00 slot, hiding every entry written into a chained cluster (writes failed with 'cannot write', duplicate SFNs accumulated); add_dir_entry now packs the cluster (0xE5 deleted-markers) before extending so other FAT readers (mtools, Windows) see the continuation too. Other: delete dead fields (Disk.fis, Volume.fat_count), drop timer_irq/ keyboard_irq/rendered, refresh_and_exit is a documented unsafe fn, CONTEXT .md corrected (stable toolchain, IRQ input, 4-crate layout), .gitignore excludes .pi-subagents artifacts. Validated: cargo check/clippy/fmt clean, make test-disk PASS on fresh and reused images (incl. two directory-chain extensions and ~10 SFN tails), editor round-trip smoke test PASS. --- rsnix/src/ahci.rs | 8 +-- rsnix/src/fat.rs | 130 ++++++++++++++++++++++++++++++++++++++---------- rsnix/src/font.rs | 8 +-- rsnix/src/gop.rs | 26 +++++----- rsnix/src/interrupts.rs | 70 ++++---------------------- rsnix/src/memory.rs | 11 ++-- rsnix/src/ps2.rs | 10 ++-- rsnix/src/tty.rs | 1 + rsnix/src/vt.rs | 8 +-- 9 files changed, 158 insertions(+), 114 deletions(-) (limited to 'rsnix/src') diff --git a/rsnix/src/ahci.rs b/rsnix/src/ahci.rs index 427345d..9a49431 100644 --- a/rsnix/src/ahci.rs +++ b/rsnix/src/ahci.rs @@ -54,7 +54,6 @@ const ATA_WRITE_DMA_EXT: u8 = 0x35; struct Disk { port: usize, // port register base = ABAR + 0x100 clb: usize, // command list, 1024 B (32 × 32 B), 256 B aligned - fis: usize, // FIS receive area, 256 B aligned ct: usize, // command table, 256 B, 128 B aligned buf: usize, // 4 KiB DMA data buffer (page aligned) sectors: u64, // total sectors (LBA48 from IDENTIFY) @@ -175,7 +174,6 @@ fn start_port(abar: usize, port_idx: u32) -> Option { let mut d = Disk { port, clb, - fis, ct, buf, sectors: 0, @@ -389,8 +387,12 @@ fn issue(d: &Disk, cmd: u8, lba: u64, count: u32, write: bool) -> Result<(), ()> Ok(()) } +fn disks() -> &'static mut [Option; MAX_DISKS] { + unsafe { &mut *core::ptr::addr_of_mut!(DISKS) } +} + fn active() -> Option<&'static Disk> { - unsafe { DISKS.get(ACTIVE).and_then(|d| d.as_ref()) } + disks().get(unsafe { ACTIVE }).and_then(|d| d.as_ref()) } /// DMA `count` sectors starting at `lba` into the selected disk's buffer. diff --git a/rsnix/src/fat.rs b/rsnix/src/fat.rs index 52aa641..13d19df 100644 --- a/rsnix/src/fat.rs +++ b/rsnix/src/fat.rs @@ -38,7 +38,6 @@ struct Volume { uid: String, // unique mount name: "LABEL-XXXXXXXX" sectors_per_cluster: u8, reserved: u16, - fat_count: u8, fat_size: u32, // sectors per FAT root_cluster: u32, first_data: u32, // LBA of cluster 2 @@ -50,28 +49,36 @@ struct Volume { static mut VOLS: Vec = Vec::new(); static mut ACTIVE_VOL: usize = 0; +/// Raw-pointer accessors for the volume statics. The kernel is +/// single-threaded, so `&'static` refs are sound; the `addr_of_mut!` +/// round-trip keeps `static_mut_refs` (warn today, deny in edition +/// 2024) quiet. +fn vols() -> &'static mut Vec { + unsafe { &mut *core::ptr::addr_of_mut!(VOLS) } +} + +fn active_vol() -> &'static mut usize { + unsafe { &mut *core::ptr::addr_of_mut!(ACTIVE_VOL) } +} + pub fn volume_count() -> usize { - unsafe { VOLS.len() } + vols().len() } /// Unique mount name of volume `vol` ("RSDOS-5BD1E55F"). pub fn uid(vol: usize) -> &'static str { - unsafe { VOLS.get(vol) } - .map(|v| v.uid.as_str()) - .unwrap_or("") + vols().get(vol).map(|v| v.uid.as_str()).unwrap_or("") } pub fn root_cluster(vol: usize) -> u32 { - unsafe { VOLS.get(vol) } - .map(|v| v.root_cluster) - .unwrap_or(0) + vols().get(vol).map(|v| v.root_cluster).unwrap_or(0) } /// Point all FAT operations at volume `vol`; also selects the backing /// AHCI disk. Called by every public entry point (single-threaded /// kernel, so a process-wide "current volume" is fine). fn select_vol(vol: usize) -> Result<(), ()> { - let disk = unsafe { VOLS.get(vol) }.map(|v| v.disk).ok_or(())?; + let disk = vols().get(vol).map(|v| v.disk).ok_or(())?; unsafe { ACTIVE_VOL = vol; } @@ -97,7 +104,7 @@ fn write_sector(lba: u64, data: &[u8; SECTOR]) -> Result<(), ()> { // --- cluster math ---------------------------------------------------------- fn v() -> &'static mut Volume { - unsafe { VOLS.get_mut(ACTIVE_VOL) }.unwrap() + vols().get_mut(*active_vol()).unwrap() } fn spc() -> usize { @@ -351,13 +358,13 @@ fn scan_dir(dir_cluster: u32) -> Result, ()> { return Err(()); // loop protection on a corrupt chain } read_cluster(c, &mut buf)?; - let mut done = false; for (i, e) in buf.chunks_exact(32).enumerate() { match e[0] { - 0x00 => { - done = true; - break; - } + // 0x00 ends the entries in THIS cluster, not the whole + // directory: a dir that spills into a second cluster keeps + // its 0x00 marker in the first cluster, so we must keep + // walking the chain to see the rest. + 0x00 => break, 0xE5 => lfn.clear(), // deleted entry; drop any partial LFN _ => { let attr = e[11]; @@ -415,9 +422,6 @@ fn scan_dir(dir_cluster: u32) -> Result, ()> { } } } - if done { - break; - } cur = next_cluster(c).ok(); } Ok(items) @@ -729,10 +733,77 @@ fn to_8_3(name: &str) -> Option<([u8; 11], bool)> { Some((sfn, needs)) } +/// Collect the raw short names already present in a directory. +fn dir_short_names(parent: u32) -> Result, ()> { + let cs = cluster_size(); + let mut buf = vec![0u8; cs]; + let mut out = Vec::new(); + let mut cur: Option = Some(parent); + let mut guard = 0; + while let Some(c) = cur { + guard += 1; + if guard > 1_000_000 { + return Err(()); // loop protection on a corrupt chain + } + read_cluster(c, &mut buf)?; + for e in buf.chunks_exact(32) { + match e[0] { + // Same rule as scan_dir: 0x00 ends this cluster's entries + // but the directory may continue in the next cluster. + 0x00 => break, + 0xE5 => {} + _ => { + if e[11] != 0x0F { + let mut sfn = [0u8; 11]; + sfn.copy_from_slice(&e[0..11]); + out.push(sfn); + } + } + } + } + cur = next_cluster(c).ok(); + } + Ok(out) +} + +/// Make `base` unique within a directory: on collision, append a numeric +/// ~N tail (DISK-TE~1, DISK-TE~2, …) — the standard FAT short-name +/// disambiguation that fsck and other OSes expect. Without it, two files +/// whose long names share an 8.3 prefix would get the same short name and +/// fsck reports them as duplicate directory entries. +fn unique_short_name(base: &[u8; 11], used: &[[u8; 11]]) -> [u8; 11] { + if !used.contains(base) { + return *base; + } + let mut name_len = 0; + while name_len < 8 && base[name_len] != b' ' { + name_len += 1; + } + for n in 1..=999u32 { + let tail = alloc::format!("~{}", n); + let tb = tail.as_bytes(); + let keep = name_len.saturating_sub(tb.len()); + let mut cand = [b' '; 11]; + cand[..keep].copy_from_slice(&base[..keep]); + cand[keep..keep + tb.len()].copy_from_slice(tb); + cand[8..11].copy_from_slice(&base[8..11]); + if !used.contains(&cand) { + return cand; + } + } + *base // pathological; keep the colliding name +} + /// Write the LFN entries (if any) + SFN entry for `name` into the parent /// directory, extending its chain with a fresh zeroed cluster if needed. fn add_dir_entry(parent: u32, name: &str, is_dir: bool, cluster: u32) -> Result<(), ()> { - let (sfn, needs_lfn) = to_8_3(name).ok_or(())?; + let (base_sfn, mut needs_lfn) = to_8_3(name).ok_or(())?; + // Short names must be unique within the directory; a plain truncation + // collides when two long names share an 8.3 prefix. + let sfn = unique_short_name(&base_sfn, &dir_short_names(parent)?); + if sfn != base_sfn { + needs_lfn = true; // keep the original name via an LFN entry + } let lfn_chars: Vec = name.encode_utf16().collect(); let groups = if needs_lfn { lfn_chars.len().div_ceil(13) @@ -810,6 +881,18 @@ fn add_dir_entry(parent: u32, name: &str, is_dir: bool, cluster: u32) -> Result< write_cluster(c, &buf)?; return Ok(()); } + // Chain full: pack this cluster before extending. A mid-chain + // 0x00 would look like the end of the directory to other FAT + // readers (Windows, mtools stop at the first 0x00), hiding every + // entry written into the new cluster. Marking the free slots as + // 0xE5 (deleted) keeps them skippable while the chain walk finds + // the continuation. + for i in 0..slots { + if buf[i * 32] == 0x00 { + buf[i * 32] = 0xE5; + } + } + write_cluster(c, &buf)?; // Chain full: extend with a fresh zeroed cluster and retry. match next_cluster(c) { Ok(n) => cur = Some(n), @@ -910,10 +993,8 @@ pub fn init() -> bool { match try_mount(d) { Some(volume) => { let uid = volume.uid.clone(); - let vol = unsafe { - VOLS.push(volume); - VOLS.len() - 1 - }; + vols().push(volume); + let vol = vols().len() - 1; // Re-sync the FSInfo free-cluster count (may be stale // from earlier sessions). if select_vol(vol).is_ok() { @@ -1026,7 +1107,7 @@ fn try_mount(d: usize) -> Option { let mut uid = alloc::format!("{}-{:08X}", label, serial); let mut n = 2u32; loop { - let taken = unsafe { VOLS.iter() }.any(|v| v.uid == uid); + let taken = vols().iter().any(|v| v.uid == uid); if !taken { break; } @@ -1038,7 +1119,6 @@ fn try_mount(d: usize) -> Option { uid, sectors_per_cluster: spc, reserved, - fat_count, fat_size, root_cluster, first_data, diff --git a/rsnix/src/font.rs b/rsnix/src/font.rs index e188513..35327e7 100644 --- a/rsnix/src/font.rs +++ b/rsnix/src/font.rs @@ -1,5 +1,5 @@ -/// 8×16 bitmap font. -/// -/// Glyphs are generated at build time by `build.rs` from the system PSF2 font -/// (`/usr/share/kbd/consolefonts/lat1-16.psfu.gz`). +// 8×16 bitmap font. +// +// Glyphs are generated at build time by `build.rs` from the system PSF2 font +// (`/usr/share/kbd/consolefonts/lat1-16.psfu.gz`). include!(concat!(env!("OUT_DIR"), "/font_data.rs")); diff --git a/rsnix/src/gop.rs b/rsnix/src/gop.rs index 77cc514..0d24d7d 100644 --- a/rsnix/src/gop.rs +++ b/rsnix/src/gop.rs @@ -105,23 +105,25 @@ pub fn clear_screen() { crate::vt::clear_all(); } +/// Raw-pointer accessor for the framebuffer static (single-threaded +/// kernel; keeps `static_mut_refs` quiet). +fn fb() -> &'static mut Option { + unsafe { &mut *core::ptr::addr_of_mut!(FB) } +} + /// (cols, rows) of the text surface, from the current framebuffer mode. pub fn dims() -> (usize, usize) { - unsafe { - match FB.as_ref() { - Some(f) => (f.width / 8, f.height / 16), - None => (0, 0), - } + match fb().as_ref() { + Some(f) => (f.width / 8, f.height / 16), + None => (0, 0), } } /// Pack an (r, g, b) colour for the framebuffer's pixel format. pub fn rgb(r: u8, g: u8, b: u8) -> u32 { - unsafe { - match FB.as_ref() { - Some(f) if f.bgr => ((r as u32) << 16) | ((g as u32) << 8) | b as u32, - _ => ((b as u32) << 16) | ((g as u32) << 8) | r as u32, - } + match fb().as_ref() { + Some(f) if f.bgr => ((r as u32) << 16) | ((g as u32) << 8) | b as u32, + _ => ((b as u32) << 16) | ((g as u32) << 8) | r as u32, } } @@ -340,7 +342,7 @@ fn unicode_glyph_bits(ch: u16) -> Option<[u8; 16]> { } pub(crate) fn blit_glyph(col: usize, row: usize, ch: u16, fg: (u8, u8, u8), bg: (u8, u8, u8)) { - if let Some(fb) = unsafe { FB.as_ref() } { + if let Some(fb) = fb().as_ref() { let glyph = font::GLYPH_8X16; let data = if ch < 256 { match glyph.get(ch as usize) { @@ -381,7 +383,7 @@ pub(crate) fn blit_glyph(col: usize, row: usize, ch: u16, fg: (u8, u8, u8), bg: /// Shift the framebuffer content up `n` text rows and clear the exposed /// bottom rows. `n` batches deferred scrolls into one push. pub(crate) fn shift_up_n(n: usize) { - if let Some(fb) = unsafe { FB.as_ref() } { + if let Some(fb) = fb().as_ref() { fb.shift_up_n(n); } } diff --git a/rsnix/src/interrupts.rs b/rsnix/src/interrupts.rs index 66dc59f..cbd234e 100644 --- a/rsnix/src/interrupts.rs +++ b/rsnix/src/interrupts.rs @@ -98,7 +98,7 @@ fn pit_set_rate(hz: u64) { let divisor = (PIT_FREQ / hz) as u16; unsafe { // Mode 2, channel 0, lobyte/hibyte, binary - asm!("out dx, al", in("dx") PIT_CMD, in("al") 0b00_11_010_0u8, + asm!("out dx, al", in("dx") PIT_CMD, in("al") 0b0011_0100u8, options(nostack, preserves_flags)); asm!("out dx, al", in("dx") PIT_CH0, in("al") (divisor & 0xFF) as u8, options(nostack, preserves_flags)); @@ -122,47 +122,10 @@ unsafe impl Sync for KeyBuf {} impl KeyBuf { const fn new() -> Self { - // UnsafeCell requires const initialisation in a const ctx. - // Build an array the long way — one cell at a time. - const NEW_CELL: core::cell::UnsafeCell = core::cell::UnsafeCell::new(0); - // `[NEW_CELL; 256]` needs Clone, which UnsafeCell doesn't have. - // Use a union trick or just mark the whole buffer init with zeroes. - // SAFETY: we initialise via the const-fn path in init(). + // Inline-const array repeat (stable since 1.79): each element is a + // fresh `UnsafeCell::new(0)` value, so no interior-mutable const. Self { - buf: [ - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, - ], + buf: [const { core::cell::UnsafeCell::new(0) }; 256], head: AtomicU16::new(0), tail: AtomicU16::new(0), } @@ -297,7 +260,10 @@ struct IntFrame { unsafe extern "C" fn isr_dispatch(frame: &IntFrame) { unsafe { match frame.vector { - 32 => eoi(0), + 32 => { + TICKS.fetch_add(1, Ordering::Relaxed); + eoi(0); + } 33 => { let byte: u8; core::arch::asm!("in al, dx", out("al") byte, in("dx") 0x60u16, @@ -311,22 +277,6 @@ unsafe extern "C" fn isr_dispatch(frame: &IntFrame) { } } -/// Timer IRQ (PIT channel 0 ~ 100 Hz). -fn timer_irq() { - eoi(0); -} - -/// Keyboard IRQ — read scancode from port 0x60 and push to ring buffer. -fn keyboard_irq() { - let byte: u8; - unsafe { - asm!("in al, dx", out("al") byte, in("dx") 0x60, - options(nostack, preserves_flags)); - } - KEYBUF.push(byte); - eoi(1); -} - // --------------------------------------------------------------------------- // Public API // --------------------------------------------------------------------------- @@ -348,7 +298,7 @@ pub fn init() { let cs = cs as u16; // Build the IDT. - let idt_ptr = IDT.as_mut_ptr(); + let idt_ptr = core::ptr::addr_of_mut!(IDT[0]); let spurious = isr_stub_spurious as *const () as u64; // Fill all 256 entries with the spurious handler. @@ -370,7 +320,7 @@ pub fn init() { ]; // Load IDT. - asm!("lidt [{}]", in(reg) &IDTR, options(nostack, preserves_flags)); + asm!("lidt [{}]", in(reg) core::ptr::addr_of!(IDTR), options(nostack, preserves_flags)); // Remap PIC and mask everything. remap_pic(); diff --git a/rsnix/src/memory.rs b/rsnix/src/memory.rs index c80edab..54ff7be 100644 --- a/rsnix/src/memory.rs +++ b/rsnix/src/memory.rs @@ -289,7 +289,7 @@ pub fn capture_memory_map(bs: &BootServices) -> Result { let buf_size = buf_size.div_ceil(desc_size) * desc_size; // If our static buffer is too small, fall back to pool (rare) - if buf_size > unsafe { MAP_WORK_BUF.len() } { + if buf_size > unsafe { (*core::ptr::addr_of!(MAP_WORK_BUF)).len() } { return Err(EFI_BUFFER_TOO_SMALL); } @@ -337,7 +337,12 @@ pub fn capture_memory_map(bs: &BootServices) -> Result { } /// Retry ExitBootServices with a fresh memory map. -pub fn refresh_and_exit(bs: &BootServices, image_handle: Handle) -> Result { +/// +/// # Safety +/// +/// `image_handle` must be the valid EFI image handle this kernel was +/// started with (opaque firmware pointer; never dereferenced here). +pub unsafe fn refresh_and_exit(bs: &BootServices, image_handle: Handle) -> Result { let key = capture_memory_map(bs)?; let status = unsafe { (bs.exit_boot_services)(image_handle, key) }; if status == EFI_SUCCESS { @@ -591,7 +596,7 @@ pub fn init_page_allocator() -> bool { // The bitmap itself lives in the heap (one allocation, never freed). // `bytes` >= 1 and align 16 is a power of two, so this cannot fail. - let layout = unsafe { Layout::from_size_align_unchecked(bytes, 16) }; + let layout = Layout::from_size_align_unchecked(bytes, 16); let bm = ALLOCATOR.alloc(layout); if bm.is_null() { return false; diff --git a/rsnix/src/ps2.rs b/rsnix/src/ps2.rs index 4747813..35848b6 100644 --- a/rsnix/src/ps2.rs +++ b/rsnix/src/ps2.rs @@ -263,8 +263,12 @@ pub fn try_poll_key() -> Option { /// Process a single scancode byte. Returns Some(KeyEvent) when a visible /// event happens; returns None for modifier changes, releases, and prefixes. +fn kbd() -> &'static mut KbdState { + unsafe { &mut *core::ptr::addr_of_mut!(KBD) } +} + fn process_byte(byte: u8) -> Option { - let state = unsafe { &mut KBD }; + let state = kbd(); if byte == 0xE0 { state.got_e0 = true; @@ -368,7 +372,7 @@ fn process_e0(byte: u8) -> Option { } fn update_modifiers(scancode: u8, pressed: bool, e0: bool) { - let state = unsafe { &mut KBD }; + let state = kbd(); match (scancode, e0) { (0x2A, false) => set_mod(state, MOD_LSHIFT, pressed), (0x36, false) => set_mod(state, MOD_RSHIFT, pressed), @@ -393,6 +397,6 @@ fn set_mod(state: &mut KbdState, bit: u8, pressed: bool) { /// Is either Ctrl key held? fn ctrl_held() -> bool { - let state = unsafe { &mut KBD }; + let state = kbd(); state.modifiers & (MOD_LCTRL | MOD_RCTRL) != 0 } diff --git a/rsnix/src/tty.rs b/rsnix/src/tty.rs index b6b6cc8..81a7a24 100644 --- a/rsnix/src/tty.rs +++ b/rsnix/src/tty.rs @@ -20,6 +20,7 @@ use crate::{gop, serial}; pub const LINE_BUF_SIZE: usize = 256; /// Result of one canonical line-input session. +#[allow(clippy::large_enum_variant)] // stack buffer by design (no heap churn) pub enum LineInput { Line(strbuf::String), Interrupted, // ^C diff --git a/rsnix/src/vt.rs b/rsnix/src/vt.rs index 59378f3..2cfb0f3 100644 --- a/rsnix/src/vt.rs +++ b/rsnix/src/vt.rs @@ -146,8 +146,8 @@ fn cell(r: usize, c: usize) -> &'static mut Cell { &mut cells()[idx(r, c)] } -fn rendered(r: usize, c: usize) -> &'static mut Cell { - &mut rendered_cells()[idx(r, c)] +fn params() -> &'static mut [usize; 4] { + unsafe { &mut *core::ptr::addr_of_mut!(PARAMS) } } fn put_cell(r: usize, c: usize, ch: u16) { @@ -204,14 +204,14 @@ fn put_byte(b: u8) { CUR = CUR.saturating_mul(10).saturating_add((b - b'0') as usize); } b';' => { - if NPARAMS < PARAMS.len() { + if NPARAMS < params().len() { PARAMS[NPARAMS] = CUR; NPARAMS += 1; } CUR = 0; } _ => { - if NPARAMS < PARAMS.len() { + if NPARAMS < params().len() { PARAMS[NPARAMS] = CUR; NPARAMS += 1; } -- cgit v1.2.3