diff --git a/README.md b/README.md index f791147..b48b517 100644 --- a/README.md +++ b/README.md @@ -208,10 +208,12 @@ Supported commands include: extended configuration space for the given bus/device/function * `ecamwr ` writes a 32-bit word to PCIe extended configuration space for the given bus/device/function -* `getbits , ` returns the given bit range +* `getbits , ` returns the given bit range from `` * `setbits , ` sets the given bit range in `` to `` +* `spinner` displays a moving "spinner" on the terminal until a + byte is received on the UART. ## Building bldb diff --git a/src/clock.rs b/src/clock.rs new file mode 100644 index 0000000..e013272 --- /dev/null +++ b/src/clock.rs @@ -0,0 +1,28 @@ +// Copyright 2021 The Hypatia Authors +// All rights reserved +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +use crate::cpuid; + +pub const NANOS_PER_SEC: u128 = 1_000_000_000; + +/// Returns the clock frequency of the current CPU in Hertz. +pub fn frequency() -> u128 { + const DEFAULT_HZ: u128 = 2_000_000_000; + if let Some(tsc_info) = cpuid::tscinfo() { + if tsc_info.nominal_frequency() != 0 { + return tsc_info + .tsc_frequency() + .map(|freq| freq.into()) + .unwrap_or(DEFAULT_HZ); + } + } + DEFAULT_HZ +} + +pub fn rdtsc() -> u64 { + unsafe { core::arch::x86_64::_rdtsc() } +} diff --git a/src/cpuid.rs b/src/cpuid.rs index 93c9ed1..565c084 100644 --- a/src/cpuid.rs +++ b/src/cpuid.rs @@ -2,17 +2,24 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -pub(crate) fn cpuid(leaf: u32, subleaf: u32) -> x86::cpuid::CpuIdResult { - x86::cpuid::native_cpuid::cpuid_count(leaf, subleaf) +use x86::cpuid; + +pub(crate) fn cpuid(leaf: u32, subleaf: u32) -> cpuid::CpuIdResult { + cpuid::native_cpuid::cpuid_count(leaf, subleaf) } /// Returns information about the current processor and its /// package. pub(crate) fn cpuinfo() -> Option<(u8, u8, u8, Option)> { - let cpuid = x86::cpuid::CpuId::new(); + let cpuid = cpuid::CpuId::new(); let features = cpuid.get_feature_info()?; let family = features.family_id(); let ext = cpuid.get_extended_processor_and_feature_identifiers()?; let pkg_type = (family > 0x10).then_some(ext.pkg_type()); Some((family, features.model_id(), features.stepping_id(), pkg_type)) } + +pub(crate) fn tscinfo() -> Option { + let cpuid = cpuid::CpuId::new(); + cpuid.get_tsc_info() +} diff --git a/src/main.rs b/src/main.rs index ae8ee6c..1301f86 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,6 +16,7 @@ extern crate alloc; mod allocator; mod bldb; +mod clock; mod cons; mod cpuid; mod gpio; diff --git a/src/ramdisk.rs b/src/ramdisk.rs index 4cffca0..d1baf8b 100644 --- a/src/ramdisk.rs +++ b/src/ramdisk.rs @@ -12,7 +12,7 @@ use crate::ufs; pub fn mount( ramdisk: &'static [u8], ) -> Result>> { - let fs = ufs::FileSystem::new(ramdisk); + let fs = ufs::FileSystem::new(ramdisk)?; if let Ok(ufs::State::Clean) = fs.state() { let flags = fs.flags(); println!("ramdisk mounted successfully (Clean, {flags:?})"); diff --git a/src/repl/load.rs b/src/repl/load.rs index 6acfbb2..4f6be79 100644 --- a/src/repl/load.rs +++ b/src/repl/load.rs @@ -9,6 +9,28 @@ use crate::repl::{self, Value}; use crate::result::{Error, Result}; use alloc::vec::Vec; +pub fn loadcpio( + config: &mut bldb::Config, + env: &mut Vec, +) -> Result { + let usage = |error| { + println!("usage: loadcpio , "); + error + }; + let cpio = repl::popenv(env) + .as_slice(&config.page_table, 0) + .and_then(|o| o.ok_or(Error::BadArgs)) + .map_err(usage)?; + let path = repl::popenv(env).as_string().map_err(usage)?; + let src = cpio_reader::iter_files(cpio) + .find(|entry| entry.name() == path) + .ok_or(Error::CpioNoFile)? + .file(); + let entry = loader::load_bytes(&mut config.page_table, src)?; + let entry = entry.try_into().unwrap(); + Ok(Value::Pointer(src.as_ptr().with_addr(entry).cast_mut())) +} + pub fn loadmem( config: &mut bldb::Config, env: &mut Vec, diff --git a/src/repl/mod.rs b/src/repl/mod.rs index ce1e471..72acc8d 100644 --- a/src/repl/mod.rs +++ b/src/repl/mod.rs @@ -36,6 +36,7 @@ mod rx; mod rz; mod sha; mod smn; +mod spinner; mod vm; #[derive(Clone)] @@ -239,6 +240,7 @@ fn evalcmd( "inw" => pio::inw(config, env), "jfmt" => jfmt::run(config, env), "load" => load::run(config, env), + "loadcpio" => load::loadcpio(config, env), "loadmem" => load::loadmem(config, env), "ls" | "list" => list::run(config, env), "map" => vm::map(config, env), @@ -251,6 +253,7 @@ fn evalcmd( "peek" => memory::read(config, env), "poke" => memory::write(config, env), "pop" => Ok(pop2(env)), + "pulser" | "throbber" => spinner::pulser(config, env), "push" => Ok(Value::Nil), "rdmsr" => msr::read(config, env), "rdsmn" => smn::read(config, env), @@ -259,6 +262,7 @@ fn evalcmd( "setbits" => bits::set(config, env), "sha256" => sha::run(config, env), "sha256mem" => sha::mem(config, env), + "spinner" => spinner::spinner(config, env), "unmap" => vm::unmap(config, env), "wrmsr" => msr::write(config, env), "wrsmn" => smn::write(config, env), diff --git a/src/repl/reader.rs b/src/repl/reader.rs index e2d918c..6b73b85 100644 --- a/src/repl/reader.rs +++ b/src/repl/reader.rs @@ -399,6 +399,8 @@ Supported commands include: from `` * `setbits , ` sets the given bit range in `` to `` +* `spinner` displays a moving "spinner" on the terminal until a + byte is received on the UART. "# ); } diff --git a/src/repl/spinner.rs b/src/repl/spinner.rs new file mode 100644 index 0000000..b79e56d --- /dev/null +++ b/src/repl/spinner.rs @@ -0,0 +1,36 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use crate::bldb; +use crate::repl; +use crate::uart; +use core::time::Duration; + +fn run(term: &mut uart::Uart, bs: &[u8], timeout: Duration) { + const BS: u8 = 8; + for &b in bs.iter().cycle() { + term.putb(b); + if term.try_getb_timeout(timeout).is_ok() { + break; + } + term.putb(BS); + } + term.putb(BS); +} + +pub(super) fn spinner( + config: &mut bldb::Config, + _env: &mut [repl::Value], +) -> repl::Result { + run(&mut config.cons, b"|/-\\", Duration::from_millis(100)); + Ok(repl::Value::Nil) +} + +pub(super) fn pulser( + config: &mut bldb::Config, + _env: &mut [repl::Value], +) -> repl::Result { + run(&mut config.cons, b"oOo.", Duration::from_millis(500)); + Ok(repl::Value::Nil) +} diff --git a/src/result.rs b/src/result.rs index b748f76..5506675 100644 --- a/src/result.rs +++ b/src/result.rs @@ -11,12 +11,15 @@ pub(crate) enum Error { UartParity, UartFraming, UartBreak, + Timeout, + FsInvMagic, FsNoRoot, FsInvPath, FsNoFile, FsOffset, FsInvState, FsRead, + CpioNoFile, ElfTruncatedObj, ElfParseObject, ElfParseHeader, @@ -57,11 +60,14 @@ impl Error { Self::UartParity => "UART parity error", Self::UartFraming => "UART framing error", Self::UartBreak => "UART BREAK", + Self::Timeout => "Timeout", Self::FsNoRoot => "No file system currently mounted", + Self::FsInvMagic => "FFS: Bad magic number in superblock", Self::FsInvPath => "Invalid path", Self::FsNoFile => "No such file or directory", Self::FsOffset => "Invalid file offset (exceeds maximum)", Self::FsRead => "Read error", + Self::CpioNoFile => "File not found in archive", Self::FsInvState => "Invalid UFS filesystem state", Self::ElfTruncatedObj => "ELF: Object truncated", Self::ElfParseObject => "ELF: Failed to parse object", diff --git a/src/uart.rs b/src/uart.rs index ca88bd9..62344fc 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -486,7 +486,8 @@ impl Uart { pub fn try_getb(&mut self) -> Result { while { - let lsr = unsafe { ptr::read_volatile(&self.read_mmio_mut().lsr) }; + let lsr: Lsr = + unsafe { ptr::read_volatile(&self.read_mmio_mut().lsr) }; if lsr.break_intr() { return Err(Error::UartBreak); } @@ -507,6 +508,39 @@ impl Uart { Ok(data.data()) } + pub fn try_getb_timeout( + &mut self, + timeout: core::time::Duration, + ) -> Result { + use crate::clock; + let ns = timeout.as_nanos(); + let cycles = ns * clock::frequency() / clock::NANOS_PER_SEC; + let start = u128::from(clock::rdtsc()); + let end = u64::try_from(start.checked_add(cycles).unwrap()).unwrap(); + while clock::rdtsc() < end { + let lsr = unsafe { ptr::read_volatile(&self.read_mmio_mut().lsr) }; + if lsr.break_intr() { + return Err(Error::UartBreak); + } + if lsr.overrun_err() { + return Err(Error::UartFifoOverrun); + } + if lsr.framing_err() { + return Err(Error::UartFraming); + } + if lsr.parity_err() { + return Err(Error::UartParity); + } + if !lsr.data_ready() { + hint::spin_loop(); + continue; + } + let data = unsafe { ptr::read_volatile(&self.read_mmio_mut().rbr) }; + return Ok(data.data()); + } + Err(Error::Timeout) + } + pub fn getb(&mut self) -> u8 { loop { if let Ok(b) = self.try_getb() { diff --git a/src/ufs/mod.rs b/src/ufs/mod.rs index dae9a14..0caf585 100644 --- a/src/ufs/mod.rs +++ b/src/ufs/mod.rs @@ -230,13 +230,15 @@ const_assert!(core::mem::size_of::() <= SUPER_BLOCK_SIZE); impl SuperBlock { /// Returns the superblock, as "read" from the given "disk." - pub fn read(disk: &[u8]) -> SuperBlock { + pub fn read(disk: &[u8]) -> Result { let sbb = &disk[SUPER_BLOCK_OFFSET..SUPER_BLOCK_OFFSET + SUPER_BLOCK_SIZE]; let p = sbb.as_ptr().cast::(); let sb = unsafe { ptr::read_unaligned(p) }; - assert_eq!(sb.magic, MAGIC); - sb + if sb.magic != MAGIC { + return Err(Error::FsInvMagic); + } + Ok(sb) } /// Returns the block address of the given cylinder group, as @@ -432,9 +434,9 @@ pub struct FileSystem<'a> { } impl<'a> FileSystem<'a> { - pub fn new(sd: &'a [u8]) -> FileSystem<'a> { - let sb = SuperBlock::read(sd); - FileSystem { sd, sb } + pub fn new(sd: &'a [u8]) -> Result> { + let sb = SuperBlock::read(sd)?; + Ok(FileSystem { sd, sb }) } pub fn root_inode(&self) -> Inode {