//! K410: a real serial `ls` lists names from completed, validated TurkFS //! root-sector reads. Neither the command handler nor this probe embeds names. use super::*; use turkos_kernel::{ serial_console::{ConsoleEvent, SerialConsole}, turkfs, }; const RXFE: u32 = 1 << 4; const DATA_ERROR: u32 = 0x0f00; fn fail(reason: &str) -> ! { write_str("TURKOS/LS-ERROR: "); write_line(reason); park() } fn ticks() -> u64 { let value: u64; unsafe { asm!("mrs {value}, CNTPCT_EL0", value = out(reg) value, options(nomem, nostack, preserves_flags)) }; value } pub(super) fn smoke_test(boot: &BootData, allocator: &mut memory::FrameAllocator) -> ! { let mut disk = virtio_ro_turkfs::RoDisk::open(boot, allocator, "TURKOS/LS-GET") .unwrap_or_else(|e| fail(e)); let mut superblock = [0u8; 512]; let mut root_block = [0u8; 512]; disk.read(turkfs::SUPERBLOCK_LBA, &mut superblock) .unwrap_or_else(|e| fail(e)); turkfs::require_format_version(&superblock).unwrap_or_else(|_| fail("format version")); turkfs::require_superblock_signature(&superblock) .unwrap_or_else(|_| fail("superblock signature")); let geometry = turkfs::require_block_geometry(&superblock, disk.capacity() * 512) .unwrap_or_else(|_| fail("block geometry")); let (root_lba, root_count) = turkfs::root_directory_location(&superblock, geometry) .unwrap_or_else(|_| fail("root descriptor")); disk.read(root_lba, &mut root_block) .unwrap_or_else(|e| fail(e)); let root = turkfs::parse_root_directory(root_lba, root_count, &root_block) .unwrap_or_else(|_| fail("root entry")); turkfs::require_utf8_names(&root).unwrap_or_else(|_| fail("root UTF-8")); for entry in &root.entries[..root.count] { let name = &entry.name[..entry.name_len as usize]; if name .iter() .any(|byte| *byte < 0x20 || *byte == 0x7f || *byte == b'/') { fail("unsafe root name"); } } disk.close(allocator).unwrap_or_else(|e| fail(e)); write_str("TURKOS/LS-MOUNT: ROOT=0x"); write_hex(root_lba as usize); write_str(" COUNT=0x"); write_hex(root.count); write_str(" DMA-RESTORED=0x1\r\n"); let base = UART_BASE.load(Ordering::Relaxed); let frequency: u64; unsafe { asm!("mrs {value}, CNTFRQ_EL0", value = out(reg) frequency, options(nomem, nostack, preserves_flags)) }; if frequency == 0 { fail("zero counter frequency"); } let mut console = SerialConsole::ls_only(); write_line("TURKOS/LS-PROMPT: NUMBER=0x1"); write_line("TURKOS/LS: READY"); loop { let deadline = ticks() .checked_add(frequency.saturating_mul(5)) .unwrap_or(u64::MAX); while unsafe { read_volatile((base + 0x18) as *const u32) } & RXFE != 0 { if ticks() >= deadline { fail("input timeout"); } core::hint::spin_loop(); } let data = unsafe { read_volatile(base as *const u32) }; if data & DATA_ERROR != 0 { fail("PL011 data error"); } match console.feed(data as u8) { ConsoleEvent::Input | ConsoleEvent::SuppressedLf => {} ConsoleEvent::List { prompt } => { write_str("TURKOS/LS-OUTPUT: COUNT=0x"); write_hex(root.count); write_str("\r\n"); for (index, entry) in root.entries[..root.count].iter().enumerate() { write_str("TURKOS/LS-ENTRY: INDEX=0x"); write_hex(index); write_str(" INODE=0x"); write_hex(entry.inode as usize); write_str(" NAME="); let name = &entry.name[..entry.name_len as usize]; write_str(core::str::from_utf8(name).unwrap_or_else(|_| fail("root UTF-8"))); write_str("\r\n"); } write_str("TURKOS/LS-PROMPT: NUMBER=0x"); write_hex(prompt as usize); write_str("\r\n"); write_line("TURKOS/LS: PASS"); park() } ConsoleEvent::Help { prompt } => { write_str("TURKOS/LS-HELP: COMMANDS="); write_line(console.commands()); write_str("TURKOS/LS-PROMPT: NUMBER=0x"); write_hex(prompt as usize); write_str("\r\n"); } ConsoleEvent::Blank { prompt } => { write_str("TURKOS/LS-PROMPT: NUMBER=0x"); write_hex(prompt as usize); write_str("\r\n"); } ConsoleEvent::Echo { prompt, bytes, len } => { write_str("TURKOS/LS-ECHO: DATA="); for byte in &bytes[..len] { write_byte(*byte); } write_str("\r\nTURKOS/LS-PROMPT: NUMBER=0x"); write_hex(prompt as usize); write_str("\r\n"); } ConsoleEvent::Unhandled(_, _) => fail("unhandled command"), ConsoleEvent::Cat { .. } => fail("unexpected cat command"), ConsoleEvent::Run { .. } => fail("unexpected run command"), ConsoleEvent::Wait { .. } => fail("unexpected wait command"), ConsoleEvent::InvalidUtf8 { .. } => { fail("unexpected UTF-8 error in historical fixture") } ConsoleEvent::MalformedArguments { .. } => { fail("unexpected argument error in historical fixture") } ConsoleEvent::Cancelled { .. } => fail("unexpected Ctrl-C in historical fixture"), ConsoleEvent::TooLong => fail("overlong command"), } } }