smmu 1.8.0

ARM SMMU v3 (System Memory Management Unit) implementation - Production-grade translation engine
Documentation
//! Page Request Interface types for ARM SMMU v3
//!
//! PRI queue management per ARM SMMU v3 specification Section 7.

use crate::types::{AccessType, SecurityState};

/// Page Request Interface entry
///
/// Contains information about a page request in the PRI queue.
/// Follows ARM SMMU v3 PRI format.
///
/// # ARM SMMU v3 Compliance
///
/// The `prg_index` field is the Page Request Group Index (ARM §8.2).  Every
/// page request belongs to a Page Request Group (PRG) identified by this value.
/// Software must echo the same `prg_index` back in `CMD_PRI_RESP` (ARM §8.3)
/// so that the SMMU can locate and retire the pending request.
///
/// The `security_state` field records the security context of the request.
/// Per ARM §7.3.19 / FINDING-NEW-32 the E_PAGE_REQUEST event generated by
/// `process_pri_queue()` must carry this field rather than a hardcoded NonSecure.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct PRIEntry {
    /// Source stream identifier (raw u32 for simpler access)
    pub stream_id: u32,
    /// Process Address Space ID (raw u32 for simpler access)
    pub pasid: u32,
    /// Requested page address (raw u64 for simpler access)
    pub requested_address: u64,
    /// Access type requested (Read/Write/Execute)
    pub access_type: AccessType,
    /// True if this is the last request in a group
    pub is_last_request: bool,
    /// Request timestamp
    pub timestamp: u64,
    /// Page Request Group index (ARM §8.2).
    ///
    /// Identifies the Page Request Group (PRG) this entry belongs to.
    /// Software must echo this value in `CMD_PRI_RESP` to complete the
    /// response cycle (ARM §8.3).  Defaults to 0.
    pub prg_index: u16,
    /// Security state of the page request (ARM §7.3.19 / FINDING-NEW-32).
    ///
    /// The E_PAGE_REQUEST event must carry the security state of the originating
    /// transaction.  Defaults to `SecurityState::NonSecure`.
    pub security_state: SecurityState,
    /// Access span hint in units of 4096 bytes (ARM §7.3.19).
    ///
    /// The size of the intended access span in bytes is `span * 4096`.
    /// A value of 0 means a single page (the hint conveys no multi-page span).
    /// This is propagated directly to the `span` field of the emitted
    /// `E_PAGE_REQUEST` event record.
    pub span: u8,
}

/// Auto-generated PRG_RESPONSE for a PPR discarded due to PRIQ overflow (§8.1).
///
/// When the PRIQ overflows (or overflow is already active) and an incoming PPR
/// has `is_last_request=true`, the SMMU must auto-generate a PRG_RESPONSE so
/// the device does not wait indefinitely.  The `response_code` follows §8.1:
///
/// - `0x0` (Success) — when the PPR carries no PASID (`pasid == 0`), the
///   device can tolerate a Success response without harmful side-effects.
/// - `0xF` (Failure) — when the PPR carries a PASID, the conservative
///   approach is Failure; the device must retry via a new page fault later.
///
/// # ARM SMMU v3 Compliance
///
/// ARM IHI0070G.b §8.1: "If the queue overflows, the SMMU must generate a
/// PRG_RESPONSE with RESPONSE=FAILURE for each Last=1 page request that is
/// discarded."
///
/// The `response_code` field is a 4-bit value encoded in the CMD_PRI_RESP
/// RESP[3:0] field (§4.6.13).  The two defined values relevant to overflow:
/// - `0x0` Success
/// - `0xF` Failure
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct PriAutoFailureResponse {
    /// The PPR that was discarded and triggered this auto-response.
    pub entry: PRIEntry,
    /// The 4-bit response code to send back to the device (0x0=Success, 0xF=Failure).
    pub response_code: u8,
}

impl PriAutoFailureResponse {
    /// Construct an auto-failure response for the given entry.
    ///
    /// Selects `response_code` per §8.1:
    /// - `0x0` (Success) when `entry.pasid == 0` (no PASID present).
    /// - `0xF` (Failure) when `entry.pasid != 0` (PASID present; conservative).
    ///
    /// # Examples
    ///
    /// ```
    /// use smmu::types::{AccessType, PRIEntry, PriAutoFailureResponse};
    ///
    /// let entry = PRIEntry { stream_id: 1, pasid: 0, requested_address: 0x1000,
    ///     access_type: AccessType::Read, is_last_request: true, timestamp: 0,
    ///     prg_index: 7, security_state: smmu::types::SecurityState::NonSecure, span: 0 };
    /// let resp = PriAutoFailureResponse::new(entry);
    /// assert_eq!(resp.response_code, 0x0); // no PASID → Success
    ///
    /// let entry2 = PRIEntry { pasid: 42, ..entry };
    /// let resp2 = PriAutoFailureResponse::new(entry2);
    /// assert_eq!(resp2.response_code, 0xF); // PASID present → Failure
    /// ```
    #[must_use]
    pub fn new(entry: PRIEntry) -> Self {
        // §8.1: if no PASID, return Success; if PASID present, return Failure (conservative).
        let response_code = if entry.pasid == 0 { 0x0 } else { 0xF };
        Self { entry, response_code }
    }
}

impl PRIEntry {
    /// Create a new PRI entry with stream ID and PASID only.
    ///
    /// Defaults `requested_address` to 0 and `access_type` to `AccessType::Read`.
    /// Use [`PRIEntry::with_address`] when you need to specify the full set of fields.
    ///
    /// # Examples
    ///
    /// ```
    /// use smmu::types::PRIEntry;
    ///
    /// let entry = PRIEntry::new(1, 0);
    /// assert_eq!(entry.stream_id, 1);
    /// assert_eq!(entry.pasid, 0);
    /// assert_eq!(entry.prg_index, 0);
    /// ```
    #[must_use]
    pub const fn new(stream_id: u32, pasid: u32) -> Self {
        Self {
            stream_id,
            pasid,
            requested_address: 0,
            access_type: AccessType::Read,
            is_last_request: false,
            timestamp: 0,
            prg_index: 0,
            security_state: SecurityState::NonSecure,
            span: 0,
        }
    }

    /// Create a new PRI entry with a full set of fields.
    ///
    /// # Examples
    ///
    /// ```
    /// use smmu::types::{AccessType, PRIEntry};
    ///
    /// let entry = PRIEntry::with_address(1, 0, 0x1000, AccessType::Read);
    /// assert_eq!(entry.requested_address, 0x1000);
    /// assert_eq!(entry.prg_index, 0);
    /// ```
    #[must_use]
    pub const fn with_address(stream_id: u32, pasid: u32, requested_address: u64, access_type: AccessType) -> Self {
        Self {
            stream_id,
            pasid,
            requested_address,
            access_type,
            is_last_request: false,
            timestamp: 0,
            prg_index: 0,
            security_state: SecurityState::NonSecure,
            span: 0,
        }
    }

    /// Set the Page Request Group index and return `self` (builder pattern).
    ///
    /// Used to associate this entry with a specific PRG so that the matching
    /// `CMD_PRI_RESP` can retire it (ARM §8.2, §8.3).
    ///
    /// # Examples
    ///
    /// ```
    /// use smmu::types::{AccessType, PRIEntry};
    ///
    /// let entry = PRIEntry::with_address(1, 0, 0x1000, AccessType::Read)
    ///     .with_prg_index(42);
    /// assert_eq!(entry.prg_index, 42);
    /// ```
    #[must_use]
    pub const fn with_prg_index(mut self, prg_index: u16) -> Self {
        self.prg_index = prg_index;
        self
    }

    /// Set the security state and return `self` (builder pattern).
    ///
    /// Used to specify the security context of the page request, so that the
    /// generated `E_PAGE_REQUEST` event correctly reflects the transaction's
    /// security state (ARM §7.3.19 / FINDING-NEW-32).
    ///
    /// # Examples
    ///
    /// ```
    /// use smmu::types::{AccessType, PRIEntry, SecurityState};
    ///
    /// let entry = PRIEntry::with_address(1, 0, 0x1000, AccessType::Read)
    ///     .with_security_state(SecurityState::Secure);
    /// assert_eq!(entry.security_state, SecurityState::Secure);
    /// ```
    #[must_use]
    pub const fn with_security_state(mut self, security_state: SecurityState) -> Self {
        self.security_state = security_state;
        self
    }

    /// Set the access span hint and return `self` (builder pattern).
    ///
    /// The span is in units of 4096 bytes: the intended access span in bytes
    /// is `span * 4096`.  A value of 0 means a single page (no multi-page hint).
    /// This value is propagated to the `span` field of the emitted
    /// `E_PAGE_REQUEST` event record per ARM §7.3.19.
    ///
    /// # Examples
    ///
    /// ```
    /// use smmu::types::{AccessType, PRIEntry};
    ///
    /// let entry = PRIEntry::with_address(1, 0, 0x1000, AccessType::Read)
    ///     .with_span(4);
    /// assert_eq!(entry.span, 4); // 4 * 4096 = 16 KiB span
    /// ```
    #[must_use]
    pub const fn with_span(mut self, span: u8) -> Self {
        self.span = span;
        self
    }
}