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
13 changes: 0 additions & 13 deletions Cargo.lock

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

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ members = [
"lib/tmpdir",
"lib/trace",
"lib/tsc",
"lib/untrusted",
"lib/update",
"lib/userbound",
"lib/vdagent",
Expand Down
1 change: 0 additions & 1 deletion build/sdkversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ pub struct Crate {
/// The published crates, in the order a publisher must take them.
pub const PUBLISHED: &[Crate] = &[
Crate { name: "toyos-abi", dir: "sdk/abi" },
Crate { name: "toyos-untrusted", dir: "lib/untrusted" },
Crate { name: "toyos-transport", dir: "sdk/transport" },
Crate { name: "toyos-keymap", dir: "sdk/keymap" },
Crate { name: "toyos-font", dir: "sdk/font" },
Expand Down
19 changes: 7 additions & 12 deletions build/sysroot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,13 @@ use crate::toolchain::{self, host_triple, GuestTarget, Owner, Role, GUEST_TARGET
use whole_toolchain::{whole, Whole};

/// The per-worktree sources that end up inside a sysroot: std links the
/// `toyos-abi`, `toyos`, `toyos-transport` and `toyos-untrusted` [`std_patch`]
/// hands it and compiles its ToyOS backend from `sdk/std`, and
/// `libtoyos_c.a` is `sdk/libc` with `toyos-elf` and `toyos-osrelease`.
pub const SYSROOT_SOURCES: [&str; 9] = [
/// `toyos-abi`, `toyos` and `toyos-transport` [`std_patch`] hands it and
/// compiles its ToyOS backend from `sdk/std`, and `libtoyos_c.a` is `sdk/libc`
/// with `toyos-elf` and `toyos-osrelease`.
pub const SYSROOT_SOURCES: [&str; 8] = [
"sdk/abi/src",
"sdk/toyos/src",
"sdk/transport/src",
"lib/untrusted/src",
"sdk/std",
"lib/elf/src",
"sdk/osrelease/src",
Expand All @@ -82,11 +81,10 @@ pub const SYSROOT_SOURCES: [&str; 9] = [

/// Their manifests, and the lockfile and cargo configuration libc is built
/// under: the features, versions and flags of the same build.
pub(crate) const SYSROOT_MANIFESTS: [&str; 9] = [
pub(crate) const SYSROOT_MANIFESTS: [&str; 8] = [
"sdk/abi/Cargo.toml",
"sdk/toyos/Cargo.toml",
"sdk/transport/Cargo.toml",
"lib/untrusted/Cargo.toml",
"lib/elf/Cargo.toml",
"sdk/osrelease/Cargo.toml",
"sdk/libc/Cargo.toml",
Expand All @@ -97,8 +95,7 @@ pub(crate) const SYSROOT_MANIFESTS: [&str; 9] = [
/// Of [`SYSROOT_MANIFESTS`], the crates std names by version, which std's
/// lockfile resolves with the fork's own through [`std_patch`]: what of a
/// worktree can move a freestanding target's dependency versions.
const STD_MANIFESTS: [&str; 4] =
["sdk/abi/Cargo.toml", "sdk/toyos/Cargo.toml", "sdk/transport/Cargo.toml", "lib/untrusted/Cargo.toml"];
const STD_MANIFESTS: [&str; 3] = ["sdk/abi/Cargo.toml", "sdk/toyos/Cargo.toml", "sdk/transport/Cargo.toml"];

/// One of the crates std names by version, in a worktree.
pub(crate) struct StdCrate {
Expand Down Expand Up @@ -1279,7 +1276,7 @@ pub(crate) mod tests {
/// The manifests [`std_crates`] reads, in a worktree at `root` that has
/// no other source: what a bootstrap run is handed std's patch from.
pub(crate) fn std_manifests(root: &Path) {
for (manifest, name) in STD_MANIFESTS.into_iter().zip(["toyos-abi", "toyos", "toyos-transport", "toyos-untrusted"]) {
for (manifest, name) in STD_MANIFESTS.into_iter().zip(["toyos-abi", "toyos", "toyos-transport"]) {
write(&root.join(manifest), &format!("[package]\nname = \"{name}\"\n"));
}
}
Expand Down Expand Up @@ -1322,8 +1319,6 @@ pub(crate) mod tests {
format!("patch.crates-io.toyos.path=\"{at}/sdk/toyos\""),
"--config".to_string(),
format!("patch.crates-io.toyos-transport.path=\"{at}/sdk/transport\""),
"--config".to_string(),
format!("patch.crates-io.toyos-untrusted.path=\"{at}/lib/untrusted\""),
],
);
for name in ["work tree", "work\"tree", "work\\tree", "work\ttree"] {
Expand Down
2 changes: 1 addition & 1 deletion build/toolchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -825,7 +825,7 @@ mod tests {
let registry = |name: &str, version: &str| {
format!("/home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/{name}-{version}/src/lib.rs")
};
let rest = format!("{} {}", ours("sdk/transport"), ours("lib/untrusted"));
let rest = ours("sdk/transport");
let built_from = |abi: &str, toyos: &str| {
fs::write(built.join("dist/libstd.d"), format!("{}/dist/libstd.rlib: {abi} {toyos} {rest}\n", built.display()))
.unwrap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ adds to `PUBLISHED` is not on crates.io.
Owner: the owner's crates.io account for the first versions; `build/sdkversion.rs`
for the rule.

**Exit**: `toyos-untrusted` and `toyos-transport` are on crates.io, each naming
`publish.yml` its trusted publisher; a `publish` run at main's tip and the next
`toyos-untrusted` has since been removed from the tree and from `PUBLISHED`,
so `toyos-transport` is the one crate still owed.

**Exit**: `toyos-transport` is on crates.io, naming `publish.yml` its trusted
publisher; a `publish` run at main's tip and the next
nightly's `release` are green; and `PUBLISHED`'s doc comment says a crate joins
it only once crates.io holds it under that publisher.
1 change: 0 additions & 1 deletion kernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,6 @@ toyos-rootimage = { path = "../lib/rootimage" }
toyos-quiesce = { path = "../lib/quiesce" }
toyos-random = { path = "../lib/random" }
toyos-symbols = { path = "../lib/symbols" }
toyos-untrusted = { path = "../lib/untrusted" }
toyos-usbhid = { path = "usbhid" }
toyos-userbound = { path = "../lib/userbound" }
toyos-wallclock = { path = "../lib/wallclock" }
Expand Down
5 changes: 2 additions & 3 deletions kernel/dma/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# A member of the host workspace (root `Cargo.toml`), like toyos-userbound,
# toyos-untrusted and toyos-pci: it is pure, it has no dependencies, and its
# tests run on the host.
# A member of the host workspace (root `Cargo.toml`), like toyos-userbound and
# toyos-pci: it is pure, it has no dependencies, and its tests run on the host.
#
# What lives here is where a typed access into a DMA region lands and whether it
# may land there. The kernel's `mm::Dma` is the only caller; it is a crate
Expand Down
7 changes: 1 addition & 6 deletions kernel/pci/src/bar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,7 @@
//! these bits are the device's, and a field that says "this is not memory" is
//! not a field to decode past.
//!
//! **Why this is a decode and not a `toyos_untrusted::Untrusted`.** That type
//! is for a number used as an *index* or a *length*, where the fix is a
//! comparison with a bound. Nothing here is indexed and nothing is sized: the
//! defect was reading the register as the wrong *kind* of thing, and the fix is
//! a type that has no address in it until the encoding said there was one.
//! [`Width`] is that type — a caller cannot reach an address without having
//! [`Width`] is the decode — a caller cannot reach an address without having
//! been handed a [`Memory`], and it cannot be handed one for an I/O BAR.
//!
//! **What this does not bound is the address.** A BAR that says memory names a
Expand Down
62 changes: 37 additions & 25 deletions kernel/src/drivers/virtio.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use crate::arch::barrier;

use toyos_untrusted::{Refused, Untrusted};

use crate::mm::Mmio;
use super::pci::PciDevice;
use crate::mm::policy::MmioPolicy;
Expand Down Expand Up @@ -92,8 +90,21 @@ struct VirtioPciConfig {
/// Why a virtio PCI capability names no config window, or the window it names.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum CapWindow {
Refused(Refused),
Unmapped,
NoBar { index: u8 },
PastBar { offset: u32, length: u32, window: u64 },
}

impl core::fmt::Display for CapWindow {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Unmapped => write!(f, "names a BAR no window was mapped for"),
Self::NoBar { index } => write!(f, "names BAR {index}, past the six a header has"),
Self::PastBar { offset, length, window } => {
write!(f, "names {length:#x} bytes at {offset:#x}, past its BAR's {window:#x}")
}
}
}
}

/// The required capability a device's chain never produced a window for; its
Expand Down Expand Up @@ -168,15 +179,16 @@ fn cap_subwindow(
offset: u32,
length: u32,
) -> Result<Mmio, CapWindow> {
let bar_idx = Untrusted::new(bar_byte).index(mapped_bars.len()).map_err(CapWindow::Refused)?;
let bar = mapped_bars[bar_idx].as_ref().ok_or(CapWindow::Unmapped)?;
let slot = mapped_bars.get(usize::from(bar_byte)).ok_or(CapWindow::NoBar { index: bar_byte })?;
let bar = slot.as_ref().ok_or(CapWindow::Unmapped)?;
// The mapped window is the BAR's advertised size or the ceiling, never under 16 bytes.
let window = bar.size();
let size = (length as u64).max(4);
// length ≤ window keeps `window - size` from underflowing and offset + size inside the window.
Untrusted::new(length).at_most(window).map_err(CapWindow::Refused)?;
let offset = Untrusted::new(offset).at_most(window - size).map_err(CapWindow::Refused)?;
Ok(bar.subregion(offset, size))
let size = u64::from(length).max(4);
// size ≤ window keeps `window - size` from underflowing and offset + size inside the window.
if size > window || u64::from(offset) > window - size {
return Err(CapWindow::PastBar { offset, length, window });
}
Ok(bar.subregion(u64::from(offset), size))
}

impl VirtioPciConfig {
Expand All @@ -191,7 +203,10 @@ impl VirtioPciConfig {
for cap in pci_dev.capabilities() {
if cap.id() != PCI_CAP_ID_VENDOR { continue; }
// The BAR index is the device's; one past the six the array holds skips the capability rather than indexing it.
let Ok(bar_idx) = Untrusted::new(cap.read_u8(4)).index(mapped_bars.len()) else { continue };
let bar_idx = usize::from(cap.read_u8(4));
if bar_idx >= mapped_bars.len() {
continue;
}
if mapped_bars[bar_idx].is_none() {
// A capability naming a non-memory or unsizable BAR maps to no window.
match pci_dev.memory_bar(bar_idx as u8) {
Expand Down Expand Up @@ -226,7 +241,7 @@ impl VirtioPciConfig {
let mmio = match cap_subwindow(&mapped_bars, cap.read_u8(4), cap.read_u32(8), cap.read_u32(12)) {
Ok(mmio) => mmio,
Err(CapWindow::Unmapped) => continue,
Err(CapWindow::Refused(why)) => {
Err(why) => {
log!("VirtIO: PCI {:02x}:{:02x}.{} capability {why} — skipped",
pci_dev.bus, pci_dev.dev, pci_dev.func);
continue;
Expand Down Expand Up @@ -354,13 +369,12 @@ impl<'pool> Virtqueue<'pool> {
USED_RING_OFF + i as usize * USED_ELEM_SIZE
}

/// The two fields of a used element, returned as [`Untrusted`] so no caller can use them without naming a bound.
/// The only reads of a used element's fields in this queue; nothing else here touches them unwrapped.
fn used_ring_id(&self, i: u16) -> Untrusted<u32> {
Untrusted::new(self.used.read(self.used_elem_at(i)))
/// The two fields of a used element, as the device wrote them: [`Self::parse_used`] is their only reader.
fn used_ring_id(&self, i: u16) -> u32 {
self.used.read(self.used_elem_at(i))
}
fn used_ring_len(&self, i: u16) -> Untrusted<u32> {
Untrusted::new(self.used.read(self.used_elem_at(i) + 4))
fn used_ring_len(&self, i: u16) -> u32 {
self.used.read(self.used_elem_at(i) + 4)
}

/// The initial pool of descriptor slots; call once after construction.
Expand Down Expand Up @@ -462,17 +476,15 @@ impl<'pool> Virtqueue<'pool> {
/// What a used-ring element must satisfy: a head inside this queue's table, at a published
/// chain, written no further than that chain. Refused rather than clamped: there is nothing
/// here to recover from a forged completion.
fn parse_used(&self, id: Untrusted<u32>, len: Untrusted<u32>) -> Option<(DescSlot, u32)> {
fn parse_used(&self, id: u32, len: u32) -> Option<(DescSlot, u32)> {
// `chain_bytes` is exactly `size` long: the descriptor table's own bound, not a constant beside it.
let head = id.index(self.chain_bytes.len()).ok()?;
let head = usize::try_from(id).ok().filter(|&head| head < self.chain_bytes.len())?;
let chain = self.chain_bytes[head];
if chain == 0 {
if chain == 0 || len > chain {
return None;
}
let written = len.at_most(chain as u64).ok()?;
// Exact: `index` proved `head < size`, a `u16`, and `at_most` that `written` is no more
// than `chain`, a `u32`.
Some((DescSlot(head as u16), written as u32))
// Exact: `head < size`, a `u16`.
Some((DescSlot(head as u16), len))
}

/// Submit a descriptor chain and block until the device completes it, returning the recovered `DescSlot`.
Expand Down
8 changes: 4 additions & 4 deletions kernel/src/drivers/xhci/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ use crate::mm::Dma;
use crate::log;
use super::pci::PciDevice;
use crate::sync::{OwedGuard, OwedLock};
use toyos_untrusted::Untrusted;
use toyos_xhci::job::{Await, Outcome, Outstanding, Stages, CC_SHORT_PACKET, CC_SUCCESS};
use toyos_xhci::port::{self as portmachine, GaveUp, Gone, PortState, Reset, Step};
use toyos_xhci::call::AfterBreak;
Expand Down Expand Up @@ -530,10 +529,11 @@ impl Layout {
}

/// The block for a 1-based slot id, or `None` when the pool has no room; `slot_id` is untrusted, from the controller's own answer.
///
/// `wrapping_sub` is sound only because `index` bounds the result: slot 0 wraps to `u8::MAX`, never `< dev_blocks`.
fn device(&self, slot_id: u8) -> Option<usize> {
let index = Untrusted::new(slot_id).map(|v| v.wrapping_sub(1)).index(self.dev_blocks).ok()?;
let index = usize::from(slot_id.checked_sub(1)?);
if index >= self.dev_blocks {
return None;
}
Some(self.dev_base + index * DEV_STRIDE)
}

Expand Down
2 changes: 1 addition & 1 deletion kernel/src/mm/dma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ impl<'pool, D: Discipline> Dma<'pool, D> {
unsafe { copy_nonoverlapping(self.base.add(offset), dst.as_mut_ptr(), dst.len()) }
}

// Panics, not `Result`: every offset/length here is the driver's own arithmetic, never a device-chosen number (those arrive as `Untrusted` and are bounded before becoming an offset).
// Panics, not `Result`: every offset/length here is the driver's own arithmetic, never a device-chosen number (those are bounded at their decode before becoming an offset).
#[inline]
fn check(self, offset: usize, len: usize) {
if let Err(why) = toyos_dma::within(offset, len, self.size) {
Expand Down
Loading