Skip to main content

hrx/execution/
contract.rs

1use crate::{Error, Result};
2use serde::{Deserialize, Serialize};
3
4/// Device access used for dependency inference and kernel contract validation.
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
6pub enum Access {
7    /// Reads existing bytes without modifying them.
8    Read,
9    /// Writes bytes without reading their previous contents.
10    Write,
11    /// Reads and writes bytes.
12    ReadWrite,
13}
14impl Access {
15    pub(super) fn writes(self) -> bool {
16        self != Self::Read
17    }
18}
19/// A fixed-specialization buffer argument contract.
20#[derive(Clone, Debug, Serialize, Deserialize)]
21#[serde(deny_unknown_fields)]
22pub struct BindingContract {
23    /// Minimum accessible extent in bytes, including all DMA and vector accesses.
24    pub bytes: usize,
25    /// Required byte offset alignment; must be a power of two.
26    pub alignment: usize,
27    /// All reads and writes the kernel may perform through this argument.
28    pub access: Access,
29    /// Layout identity for diagnostics (packing remains the caller's responsibility).
30    pub layout: String,
31}
32/// The trusted ABI of a fixed kernel specialization.
33///
34/// A manifest describes this contract but does not prove that code obeys it.
35#[derive(Clone, Debug, Serialize, Deserialize)]
36#[serde(deny_unknown_fields)]
37pub struct KernelContract {
38    /// Arguments in kernel ABI order.
39    pub bindings: Vec<BindingContract>,
40    /// Exact accepted scalar bytes. Specialize again to change scalar values.
41    pub constants: Vec<u8>,
42}
43impl KernelContract {
44    /// Check structural validity without asserting that a kernel obeys this contract.
45    pub fn validate(&self) -> Result<()> {
46        for binding in &self.bindings {
47            if binding.bytes == 0
48                || binding.bytes > isize::MAX as usize
49                || !binding.alignment.is_power_of_two()
50            {
51                return Err(Error::Message(
52                    "invalid kernel binding extent or alignment".into(),
53                ));
54            }
55        }
56        Ok(())
57    }
58    pub(super) fn check(&self, bindings: &[super::BufferView]) -> Result<()> {
59        self.check_extents(bindings)?;
60        self.check_aliases(bindings)
61    }
62    pub(super) fn check_extents(&self, bindings: &[super::BufferView]) -> Result<()> {
63        self.validate()?;
64        if bindings.len() != self.bindings.len() {
65            return Err(Error::Message("kernel binding count mismatch".into()));
66        }
67        for (view, contract) in bindings.iter().zip(&self.bindings) {
68            if view.len() < contract.bytes || view.offset() % contract.alignment != 0 {
69                return Err(Error::Message(format!(
70                    "binding does not satisfy {}: {} bytes aligned to {} required",
71                    contract.layout, contract.bytes, contract.alignment
72                )));
73            }
74        }
75        Ok(())
76    }
77    pub(super) fn check_aliases(&self, bindings: &[super::BufferView]) -> Result<()> {
78        // Aliasing across arguments is not asserted by the initial contract format.
79        for (index, a) in bindings.iter().enumerate() {
80            for (other, b) in bindings[..index].iter().enumerate() {
81                if a.overlaps(b)
82                    && (self.bindings[index].access.writes()
83                        || self.bindings[other].access.writes())
84                {
85                    return Err(Error::Message(
86                        "overlapping writable kernel arguments require an unsafe dispatch".into(),
87                    ));
88                }
89            }
90        }
91        Ok(())
92    }
93}