Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ goblin = { version = "0.9", default-features = false, features = [
"elf32",
"alloc",
] }
iced-x86 = { version = "1.21.0", features = ["decoder", "gas", "no_std"], default-features = false }
miniz_oxide = "0.8"
seq-macro = "0.3"
sha2 = { version = "0.10.8", default-features = false, features = ["force-soft"] }
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ Supported commands include:
* `mount <addr,len>` to mount the UFS ramdisk.
* `ls <file>` to list a file or directory on the ramdisk.
* `cat <file>` to display the contents of a file.
* `copy <file> <dst addr>,<dst len>` to copy the contents of a
file to a region of memory.
* `elfinfo <file>` to read the contents of the ELF header and
segment headers of an ELF file.
* `load <file>` to load the given ELF file and retrieve its
Expand Down Expand Up @@ -199,6 +201,10 @@ Supported commands include:
extended configuration space for the given bus/device/function
* `ecamwr <b/d/f> <offset> <value>` writes a 32-bit word to PCIe
extended configuration space for the given bus/device/function
* `getbits <start>,<end> <value>` returns the given bit range
from `<value>`
* `setbits <start>,<end> <new bits> <value>` sets the given bit
range in `<value>` to `<new bits>`

## Building bldb

Expand Down
6 changes: 6 additions & 0 deletions src/bldb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,12 @@ fn range_4k(start: mem::V4KA) -> Range<mem::V4KA> {
start..end
}

pub(crate) fn loader_text() -> Range<u64> {
let start = text_addr().addr() as u64;
let end = rodata_addr().addr() as u64;
start..end
}

/// When the loader enters Rust code, we know that we have a
/// minimal virtual memory environment where the loader itself
/// is mapped rwx, and the UART registers region is mapped
Expand Down
40 changes: 36 additions & 4 deletions src/idt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,32 @@ impl Idt {
}
}

// Tries to skip over an instruction.
//
// # Safety
// The caller must ensure `rip` points to an instruction
// somewhere in the loader text.
unsafe fn skip_instr(rip: u64) -> u64 {
use iced_x86::{Code, Decoder, DecoderOptions};
const MAX_INSTR_LEN: usize = 15;
let loader_text = crate::bldb::loader_text();
let ripmax = rip + MAX_INSTR_LEN as u64;
if !loader_text.contains(&rip) || !loader_text.contains(&ripmax) {
panic!("PC does not point into loader text");
}
let ptr = core::ptr::with_exposed_provenance(rip as usize);
let bytes = unsafe { core::slice::from_raw_parts(ptr, MAX_INSTR_LEN) };
let mut decoder = Decoder::with_ip(64, bytes, rip, DecoderOptions::NONE);
let instr = decoder.decode();
if instr.code() == Code::INVALID {
panic!("Invalid instruction; can't skip: {instr:x?}");
} else {
rip + instr.len() as u64
}
}

extern "C" fn trap(frame: &mut TrapFrame) {
const GPF: u64 = 13;
println!("Exception:");
println!("{frame:#x?}");
println!("cr0: {:#x}", unsafe { x86::controlregs::cr0() });
Expand All @@ -275,10 +300,17 @@ extern "C" fn trap(frame: &mut TrapFrame) {
unsafe {
backtrace(frame.rbp);
}
// Arrange for the exception return to land in a halt loop.
// The seemingly superfluous cast to usize and then again to
// u64 keeps clippy happy.
frame.rip = crate::bldb::dnr as usize as u64;
// If this is a GPF, attempt to recover by skipping to the
// next instruction. Otherwise, arrange for the exception
// return to land in a halt loop.
if frame.vector == GPF {
println!("GPF OK; attempting to resume");
frame.rip = unsafe { skip_instr(frame.rip) };
} else {
// The seemingly superfluous cast to usize and then
// again to u64 keeps clippy happy.
frame.rip = crate::bldb::dnr as usize as u64;
}
}

/// Prints a call backtrace starting from the given frame
Expand Down
26 changes: 25 additions & 1 deletion src/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub const fn is_canonical_range(start: usize, end: usize) -> bool {
}
// If in the lower portion of the canonical address space,
// end is permitted to be exactly one beyond the supremum.
if start < LOW_CANON_SUP && end <= LOW_CANON_SUP {
if start < LOW_CANON_SUP && start <= end && end <= LOW_CANON_SUP {
return true;
}
// Otherwise, the range is valid IFF it is in the upper
Expand Down Expand Up @@ -213,6 +213,12 @@ impl Attrs {
pub(crate) fn set_c(&mut self, c: bool) {
self.set_nc(!c);
}

pub(crate) fn permits(self, wants: Attrs) -> bool {
(!wants.r() || self.r())
&& (!wants.w() || self.w())
&& (!wants.nx() || self.nx())
}
}

/// A region of virtual memory.
Expand Down Expand Up @@ -256,3 +262,21 @@ pub fn round_up_4k(va: usize) -> usize {
pub fn round_down_4k(va: usize) -> usize {
va & !V4KA::MASK
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn attrs_permits() {
let has = Attrs::new_data();
assert!(has.nx());
assert!(has.r());
assert!(has.w());
let wants = Attrs::new_ro();
assert!(!wants.nx());
assert!(!wants.w());
assert!(wants.r());
assert!(has.permits(wants));
}
}
48 changes: 38 additions & 10 deletions src/mmu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
extern crate alloc;

use crate::mem;
#[cfg(not(any(test, clippy)))]
use crate::println;
use crate::result::{Error, Result};
#[cfg(not(any(test, clippy)))]
Expand Down Expand Up @@ -1193,32 +1194,27 @@ impl PageTable {
/// mapped with the given permissions. Supports mapping at the end of the
/// address range.
fn is_region_mapped(&self, region: &mem::Region) -> bool {
fn permits(eattrs: &mem::Attrs, attrs: &mem::Attrs) -> bool {
(!attrs.r() || eattrs.r())
&& (!attrs.w() || eattrs.w())
&& (!attrs.x() || eattrs.x())
}
let mut start = region.start().addr();
let end = region.end().addr();
if !mem::is_canonical_range(start, end) {
return false;
}
let attrs = region.attrs();
let attrs: mem::Attrs = region.attrs();
while start != end {
let va = core::ptr::without_provenance(start);
let len = match self.pml4.lookup(va) {
Some(EntryParts::Entry1G(_, eattrs))
if permits(&eattrs, &attrs) =>
if eattrs.permits(attrs) =>
{
PFN1G::SIZE - (start % PFN1G::SIZE)
}
Some(EntryParts::Entry2M(_, eattrs))
if permits(&eattrs, &attrs) =>
if eattrs.permits(attrs) =>
{
PFN2M::SIZE - (start % PFN2M::SIZE)
}
Some(EntryParts::Entry4K(_, eattrs))
if permits(&eattrs, &attrs) =>
if eattrs.permits(attrs) =>
{
PFN4K::SIZE
}
Expand Down Expand Up @@ -1478,6 +1474,9 @@ impl LoaderPageTable {
Ok(())
}

/// Maps the given virtual address range to the given physical
/// address with the given attributes, but restricted so that the
/// physical region can only map RAM, not MMIO space.
pub(crate) unsafe fn map_ram(
&mut self,
range: Range<mem::V4KA>,
Expand All @@ -1500,7 +1499,7 @@ impl LoaderPageTable {
unsafe { self.page_table.unmap_range(&range) }
}

/// Returns the page table entry entry for the given virtual address, if it is
/// Returns the page table entry for the given virtual address, if it is
/// mapped in this address space.
pub(crate) fn lookup(&self, va: *const ()) -> Option<Entry> {
self.page_table.lookup(va).map(|entry| match entry {
Expand All @@ -1527,6 +1526,15 @@ impl LoaderPageTable {
self.page_table.is_region_mapped(&region)
}

pub(crate) fn is_region_readable(&self, range: Range<mem::V4KA>) -> bool {
self.is_region_mapped(range, mem::Attrs::new_ro())
}

pub(crate) fn is_region_writeable(&self, range: Range<mem::V4KA>) -> bool {
!Self::overlaps(&self.reserved, &range)
&& self.is_region_mapped(range, mem::Attrs::new_rw())
}

/// Returns true iff region `a` overlaps any of the regions
/// in `rs`.
///
Expand Down Expand Up @@ -1556,6 +1564,7 @@ impl LoaderPageTable {

/// Dumps the contents of the page table.
pub(crate) fn dump(&self) {
println!("Root (PML4): {root:#x}", root = self.phys_addr());
self.page_table.pml4.dump(0);
}
}
Expand Down Expand Up @@ -1601,6 +1610,25 @@ mod loader_page_table_tests {
.is_err()
});
}

#[test]
fn region_is_readable() {
let page_table = PageTable::new();
let mut loader_page_table = LoaderPageTable::new(page_table, &[], &[]);
let region = mem::V4KA::new(0x8000)..mem::V4KA::new(0xa000);
assert!(unsafe {
loader_page_table
.map_region(
region,
mem::Attrs::new_text(),
mem::P4KA::new(0x8000),
)
.is_ok()
});
let ptr = ptr::without_provenance(0x9001);
let range = mem::page_range_raw(ptr, 20);
assert!(loader_page_table.is_region_readable(range));
}
}

mod arena {
Expand Down
16 changes: 16 additions & 0 deletions src/ramdisk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,22 @@ pub fn cat(
Ok(())
}

pub fn copy(
fs: &ufs::FileSystem<'_>,
path: &str,
dst: &mut [u8],
) -> Result<usize> {
let path = path.as_bytes();
let file = fs.namei(path)?;
if file.file_type() != ufs::FileType::Regular {
println!("copy: not a regular file");
return Err(Error::BadArgs);
}
let len = core::cmp::min(file.size(), dst.len());
let nb = file.read(0, &mut dst[..len])?;
Ok(nb)
}

pub fn sha256(fs: &ufs::FileSystem<'_>, path: &str) -> Result<[u8; 32]> {
use sha2::{Digest, Sha256};

Expand Down
Loading