psoc-drivers 0.1.0

Hardware driver implementations for psoc-rs
//! Security configuration for TrustZone-enabled chips.
//!
//! # Introduction to TrustZone in ModusToolbox
//!
//! [TrustZone][trustzone] is an ARM feature that provides low-overhead software fault isolation by
//! compartmentalizing an application into "secure" and "non-secure" sub-applications, and limiting
//! access to memory and hardware peripherals based on the security state of the processor.
//!
//! Under TrustZone, the CPU core has two execution contexts with independent sets of registers:
//! the secure- and non-secure context. Each bus transaction also has a "security" attribute bit
//! indicating whether the transaction is a secure- or non-secure transaction. The secure context
//! may issue either secure- or non-secure transactions, whereas the non-secure context may only
//! issue non-secure transactions.
//!
//! On Infineon devices, every memory and peripheral region has a Memory Protection Controller
//! (MPC) or Peripheral Protection Controller (PPC) that configures the region's security
//! properties. MPCs and PPCs can be configured to allow access from either secure or non-secure
//! transactions (but not both).
//!
//! Whether the CPU issues a secure- or non-secure transaction is a property of the address. On
//! Infineon devices, bit 28 of the request address is used as a flag to determine the transaction
//! type. If the non-secure context attempts to access an address with bit 28 set, it will result
//! in a fault.
//!
//! # Interactions with drivers
//!
//! Drivers in this crate carry a generic parameter indicating the security attribute that will be
//! used to access the peripheral. When building a non-secure application, the only valid value for
//! this parameter is `NS`. When building a secure application, either `S` or `NS` may be used, with
//! the parameter defaulting to `S`. Generally, you should leave this parameter at its default
//! value; the only time you need to override it is when sharing peripherals between a secure- and
//! non-secure context (in which case the peripheral must be accessed as non-secure from both
//! contexts).
//!
//! [trustzone]: https://developer.arm.com/documentation/100690/0201/Arm-TrustZone-technology
// Copyright (c) 2026, Infineon Technologies AG or an affiliate of Infineon Technologies AG.
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing permissions and
// limitations under the License.

use crate::regs;

/// The address bit used to indicate secure- vs. non-secure access.
#[cfg(trustzone)]
pub const SECURE_ADDRESS_MASK: usize = 1 << 28;

cfg_select! {
    secure => {
         /// The default security attribute for the application being built.
        pub type Default = S;

         /// The default security attribute for the application being built.
         pub const DEFAULT: Default = S;
    },
     _ => {
         /// The default security attribute for the application being built.
        pub type Default = NS;

         /// The default security attribute for the application being built.
         pub const DEFAULT: Default = NS;
    },
}

/// Trait representing a secure- or non-secure attribute.
pub trait Security: Into<SecurityAttribute> + Copy + core::fmt::Debug {
    /// Returns whether this is the secure attribute.
    fn is_secure(&self) -> bool;

    /// Returns whether this is the non-secure attribute.
    fn is_nonsecure(&self) -> bool {
        !self.is_secure()
    }

    // Transforms a memory address into the secure- or non-secure mapping depending on the value
    /// of this security attribute.
    fn map_addr(&self, addr: usize) -> usize {
        (*self).into().map_addr(addr)
    }
}

/// Helper trait for applying a security attribute to a register address.
pub trait WithSecurity {
    /// Applies a security attribute to the address referenced by `self`.
    fn with_security(self, security: impl Into<SecurityAttribute>) -> Self;
}

impl<T: regs::AsPtr + 'static> WithSecurity for &T {
    fn with_security(self, security: impl Into<SecurityAttribute>) -> Self {
        unsafe { T::from_ptr(security.into().map_addr(self.as_ptr() as usize) as *mut u8) }
    }
}

/// Typestate for the secure attribute.
#[cfg(trustzone)]
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct S;

#[cfg(trustzone)]
impl Security for S {
    fn is_secure(&self) -> bool {
        true
    }
}

#[cfg(trustzone)]
impl S {
    /// Const version of SecurityAttribute::from(self).
    pub const fn as_security_attribute(&self) -> SecurityAttribute {
        SecurityAttribute::S
    }
}

/// Typestate for the non-secure attribute.
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct NS;

impl Security for NS {
    fn is_secure(&self) -> bool {
        false
    }
}
impl NS {
    /// Const version of SecurityAttribute::from(self).
    pub const fn as_security_attribute(&self) -> SecurityAttribute {
        SecurityAttribute::NS
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
/// An enum representing a security attribute.
pub enum SecurityAttribute {
    /// The secure attribute.
    #[cfg(trustzone)]
    S,
    /// The non-secure attribute.
    NS,
}

impl SecurityAttribute {
    /// Returns whether this is the secure attribute.
    pub fn is_secure(&self) -> bool {
        true
    }

    /// Returns whether this is the non-secure attribute.
    pub fn is_nonsecure(&self) -> bool {
        !self.is_secure()
    }

    // Transforms a memory address into the secure- or non-secure mapping depending on the value
    /// of this security attribute.
    pub fn map_addr(&self, addr: usize) -> usize {
        cfg_select! {
            trustzone => {
                if self.is_secure() {
                    addr | SECURE_ADDRESS_MASK
                } else {
                    addr & !SECURE_ADDRESS_MASK
                }
            },
             _ => addr,
        }
    }
}

#[cfg(trustzone)]
impl From<S> for SecurityAttribute {
    fn from(_: S) -> Self {
        SecurityAttribute::S
    }
}
impl From<NS> for SecurityAttribute {
    fn from(_: NS) -> Self {
        SecurityAttribute::NS
    }
}