baryl 0.0.2

Public SDK for Baryl, a full-system emulation and introspection engine
//! The word a guest passes out when it wants a component's attention, and the
//! two macros that name the values inside it.
//!
//! Code inside the guest puts a 48-bit body in RAX and executes a vmcall; a
//! component subscribed to `#[core(vmcall)]` is handed it as a [`VMCall`],
//! already decoded. That splits into the 32-bit call type and the 16-bit
//! destination component id, which is what the call is routed on.

/// Define a `#[repr(u32)]` enum whose discriminants are `fnv1a_32` of the
/// variant names, plus a `from_raw` that turns a wire value back into one.
///
/// Use it for the call-type half of a vmcall body. A variant's number follows
/// from its name, so adding one in the middle of the list renumbers nothing and
/// renaming one changes the wire value.
///
/// The hash covers the variant name alone, so `Ready` is the same number in
/// every `vmcall_enum!`. Where two enums must not collide, reach for
/// [`tag_enum!`](crate::tag_enum) instead, which hashes the enum name in as well.
///
/// `from_raw` answers `None` for a word that matches no variant — a guest that
/// is newer than the component, or a body that was never a call at all.
///
/// # Examples
///
/// ```ignore
/// vmcall_enum! {
///     pub enum AgentCalls {
///         Ready,
///         Poll,
///         Result,
///     }
/// }
///
/// assert_eq!(AgentCalls::Ready as u32, 0x0bca_3294);
/// assert_eq!(AgentCalls::from_raw(0x0bca_3294), Some(AgentCalls::Ready));
/// assert_eq!(AgentCalls::from_raw(0), None);
///
/// #[core(vmcall)]
/// fn on_vmcall(&mut self, _t: &mut Control, call: VMCall) {
///     match AgentCalls::from_raw(call.ty_raw()) {
///         Some(AgentCalls::Ready) => self.armed = true,
///         Some(_) | None => {},
///     }
/// }
/// ```
#[macro_export]
macro_rules! vmcall_enum {
    (
        $(#[$meta:meta])*
        $vis:vis enum $name:ident {
            $($variant:ident),* $(,)?
        }
    ) => {
        $(#[$meta])*
        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
        #[repr(u32)]
        $vis enum $name {
            $($variant = $crate::fnv1a_32(stringify!($variant))),*
        }

        impl $name {
            pub fn from_raw(v: u32) -> ::core::option::Option<Self> {
                $(if v == $crate::fnv1a_32(stringify!($variant)) {
                    return ::core::option::Option::Some(Self::$variant);
                })*
                ::core::option::Option::None
            }
        }
    };
}

/// Define a `#[repr(u64)]` enum whose discriminants are
/// `fnv1a_64("EnumName::VariantName")`, plus a `from_raw` that turns a wire
/// value back into one.
///
/// The enum name is part of the hash, so a `Read` in one of these and a `Read`
/// in another are different numbers. Reach for it over
/// [`vmcall_enum!`](crate::vmcall_enum) for a tag riding in a shared buffer,
/// where a value from the wrong protocol must not decode as a plausible member
/// of yours.
///
/// `from_raw` answers `None` for anything that matches no variant.
///
/// # Examples
///
/// ```ignore
/// tag_enum! {
///     pub enum CmdTag {
///         WriteFile,
///         ReadFile,
///     }
/// }
///
/// assert_eq!(CmdTag::WriteFile as u64, 0x0e3a_03f3_7c93_bb56);
/// assert_eq!(CmdTag::from_raw(0x0e3a_03f3_7c93_bb56), Some(CmdTag::WriteFile));
/// assert_eq!(CmdTag::from_raw(1), None);
/// ```
#[macro_export]
macro_rules! tag_enum {
    (
        $(#[$meta:meta])*
        $vis:vis enum $name:ident {
            $($variant:ident),* $(,)?
        }
    ) => {
        $(#[$meta])*
        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
        #[repr(u64)]
        $vis enum $name {
            $($variant = $crate::fnv1a_64(concat!(stringify!($name), "::", stringify!($variant)))),*
        }

        impl $name {
            pub fn from_raw(v: u64) -> ::core::option::Option<Self> {
                $(if v == $crate::fnv1a_64(concat!(stringify!($name), "::", stringify!($variant))) {
                    return ::core::option::Option::Some(Self::$variant);
                })*
                ::core::option::Option::None
            }
        }
    };
}

/// The 48 bits of RAX that carry a body; the top 16 are not part of it.
pub const VMC_BODY_MASK: u64 = 0x0000_FFFF_FFFF_FFFF;
/// Bits `[47:16]`, the call type.
pub const VMC_TYPE_MASK: u64 = 0x0000_FFFF_FFFF_0000;
/// Bits `[15:0]`, the destination component id.
pub const VMC_DST_MASK: u64 = 0x0000_0000_0000_FFFF;

/// The destination that reaches every loaded component instead of one.
pub const VMC_DST_BROADCAST: u16 = 0;

/// One vmcall body: a 32-bit call type and a 16-bit destination, packed into 48
/// bits.
///
/// A `#[core(vmcall)]` handler is passed one of these directly. Read
/// [`dst`](VMCall::dst) to see whether the call was aimed at you, and
/// [`ty_raw`](VMCall::ty_raw) to see which call it was.
/// [`from_body`](VMCall::from_body) is for decoding a raw register value
/// yourself.
///
/// # Examples
///
/// ```ignore
/// vmcall_enum! { pub enum AgentCalls { Ready, Poll } }
///
/// // What the guest agent puts in RAX, aimed at every component.
/// let call = VMCall::new(AgentCalls::Ready as u32, VMC_DST_BROADCAST);
/// assert_eq!(call.body(), 0x0bca_3294_0000);
///
/// // What the handler does with it.
/// let got = VMCall::from_body(call.body());
/// assert_eq!(got.dst(), VMC_DST_BROADCAST);
/// assert_eq!(AgentCalls::from_raw(got.ty_raw()), Some(AgentCalls::Ready));
/// ```
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(transparent)]
pub struct VMCall(u64);

impl VMCall {
    /// Pack a call type and a destination into a body.
    ///
    /// `VMC_DST_BROADCAST` for `dst` reaches every component; any other value
    /// names one.
    pub const fn new(ty: u32, dst: u16) -> Self {
        Self(((ty as u64) << 16) | (dst as u64))
    }

    /// Read a body out of a full RAX value, dropping the top 16 bits.
    ///
    /// A `#[core(vmcall)]` handler needs this only if it took the raw register
    /// value from somewhere else; the attribute hands it a `VMCall` already.
    pub const fn from_body(raw: u64) -> Self {
        Self(raw & VMC_BODY_MASK)
    }

    /// The packed 48 bits, to put back in a register or a log line.
    pub const fn body(self) -> u64 {
        self.0
    }

    /// The call type. Feed it to your `vmcall_enum!`'s `from_raw`.
    pub const fn ty_raw(self) -> u32 {
        ((self.0 & VMC_TYPE_MASK) >> 16) as u32
    }

    /// Which component the call was aimed at, or `VMC_DST_BROADCAST` when it
    /// was aimed at all of them.
    pub const fn dst(self) -> u16 {
        (self.0 & VMC_DST_MASK) as u16
    }
}