rigless 0.0.2

Virtual hardware-in-the-loop runtime: run unmodified firmware on an emulated MCU with every peripheral, chip model, and unit of virtual time in Rust; needs the rigless-sidecar package from rigless-labs/qemu
Documentation
// Copyright (c) 2026 Rigless Labs.
// SPDX-License-Identifier: Apache-2.0
//! The one file that maps the shared page and casts it to a control block.
#![expect(
    unsafe_code,
    reason = "the control block is a mapped file; every other module takes the reference this one hands out"
)]

use std::fs::File;
use std::io;

use memmap2::MmapMut;

use crate::sidecar::layout::ControlBlock;

#[derive(Debug)]
pub(crate) struct Mapping(MmapMut);

impl Mapping {
    pub(crate) fn open(file: &File) -> io::Result<Self> {
        // SAFETY: the page is only ever touched through the atomics of `ControlBlock`, so the peer's concurrent
        // writes are atomic accesses, not a data race; a peer that truncates the file makes the next access SIGBUS,
        // a crash rather than corruption. The length and the alignment are checked below before the page is cast.
        let map = unsafe { MmapMut::map_mut(file) }?;
        if map.len() != size_of::<ControlBlock>() {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "the mapping is not one control block long",
            ));
        }
        #[expect(
            clippy::cast_ptr_alignment,
            reason = "a mapping starts on a page, and the check below proves it"
        )]
        if !map.as_ptr().cast::<ControlBlock>().is_aligned() {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "the mapping is not page-aligned",
            ));
        }
        Ok(Self(map))
    }

    pub(crate) fn control_block(&self) -> &ControlBlock {
        #[expect(
            clippy::cast_ptr_alignment,
            reason = "`open` refused a mapping that is not page-aligned"
        )]
        let pointer = self.0.as_ptr().cast::<ControlBlock>();
        // SAFETY: `open` checked the length against `size_of::<ControlBlock>()` and the alignment against the
        // page, so the pointer covers one whole, aligned block; every field behind the reference is an atomic, so
        // reads and writes through it are sound while the peer writes; the mapping lives as long as `self`.
        unsafe { &*pointer }
    }
}

#[cfg(test)]
mod tests {
    use std::fs::OpenOptions;
    use std::path::PathBuf;

    use super::*;
    use crate::sidecar::layout::MAGIC;
    use crate::sidecar::sync::Ordering;

    fn scratch_file(name: &str, size: u64) -> (PathBuf, File) {
        let path = PathBuf::from(format!(
            "/dev/shm/rigless-mapping-test-{}-{name}",
            std::process::id()
        ));
        let file = OpenOptions::new()
            .read(true)
            .write(true)
            .create_new(true)
            .open(&path)
            .unwrap();
        file.set_len(size).unwrap();
        (path, file)
    }

    #[test]
    #[cfg_attr(miri, ignore = "Miri cannot map a file")]
    fn a_fresh_page_reads_as_zeros_and_takes_stores() {
        let (path, file) = scratch_file("fresh", size_of::<ControlBlock>() as u64);
        std::fs::remove_file(&path).unwrap();
        let mapping = Mapping::open(&file).unwrap();
        let block = mapping.control_block();
        assert_eq!(block.magic.load(Ordering::Relaxed), 0);
        ControlBlock::initialise(block);
        assert_eq!(block.magic.load(Ordering::Relaxed), MAGIC);
    }

    #[test]
    #[cfg_attr(miri, ignore = "Miri cannot map a file")]
    fn a_file_of_the_wrong_length_is_refused() {
        let (path, file) = scratch_file("short", size_of::<ControlBlock>() as u64 / 2);
        std::fs::remove_file(&path).unwrap();
        assert_eq!(
            Mapping::open(&file).unwrap_err().kind(),
            io::ErrorKind::InvalidData
        );
    }
}