use crate::{Error, Result};
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Access {
Read,
Write,
ReadWrite,
}
impl Access {
pub(super) fn writes(self) -> bool {
self != Self::Read
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BindingContract {
pub bytes: usize,
pub alignment: usize,
pub access: Access,
pub layout: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct KernelContract {
pub bindings: Vec<BindingContract>,
pub constants: Vec<u8>,
}
impl KernelContract {
pub fn validate(&self) -> Result<()> {
for binding in &self.bindings {
if binding.bytes == 0
|| binding.bytes > isize::MAX as usize
|| !binding.alignment.is_power_of_two()
{
return Err(Error::Message(
"invalid kernel binding extent or alignment".into(),
));
}
}
Ok(())
}
pub(super) fn check(&self, bindings: &[super::BufferView]) -> Result<()> {
self.check_extents(bindings)?;
self.check_aliases(bindings)
}
pub(super) fn check_extents(&self, bindings: &[super::BufferView]) -> Result<()> {
self.validate()?;
if bindings.len() != self.bindings.len() {
return Err(Error::Message("kernel binding count mismatch".into()));
}
for (view, contract) in bindings.iter().zip(&self.bindings) {
if view.len() < contract.bytes || view.offset() % contract.alignment != 0 {
return Err(Error::Message(format!(
"binding does not satisfy {}: {} bytes aligned to {} required",
contract.layout, contract.bytes, contract.alignment
)));
}
}
Ok(())
}
pub(super) fn check_aliases(&self, bindings: &[super::BufferView]) -> Result<()> {
for (index, a) in bindings.iter().enumerate() {
for (other, b) in bindings[..index].iter().enumerate() {
if a.overlaps(b)
&& (self.bindings[index].access.writes()
|| self.bindings[other].access.writes())
{
return Err(Error::Message(
"overlapping writable kernel arguments require an unsafe dispatch".into(),
));
}
}
}
Ok(())
}
}