Skip to main content

nrf5x_unsafe/
aes.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! AES128 driver, nRF5X-family, unsafe code
6
7use kernel::utilities::StaticRef;
8use kernel::utilities::cells::MapCell;
9use kernel::utilities::dma_slice::DmaSliceMut;
10use kernel::utilities::registers::interfaces::Writeable;
11use kernel::utilities::registers::{ReadWrite, WriteOnly, register_bitfields};
12
13#[repr(C)]
14pub struct AesEcbRegisters {
15    /// Start ECB block encrypt
16    /// - Address 0x000 - 0x004
17    task_startecb: WriteOnly<u32, Task::Register>,
18    /// Abort a possible executing ECB operation
19    /// - Address: 0x004 - 0x008
20    task_stopecb: WriteOnly<u32, Task::Register>,
21    /// Reserved
22    _reserved1: [u32; 62],
23    /// ECB block encrypt complete
24    /// - Address: 0x100 - 0x104
25    pub event_endecb: ReadWrite<u32, Event::Register>,
26    /// ECB block encrypt aborted because of a STOPECB task or due to an error
27    /// - Address: 0x104 - 0x108
28    pub event_errorecb: ReadWrite<u32, Event::Register>,
29    /// Reserved
30    _reserved2: [u32; 127],
31    /// Enable interrupt
32    /// - Address: 0x304 - 0x308
33    pub intenset: ReadWrite<u32, Intenset::Register>,
34    /// Disable interrupt
35    /// - Address: 0x308 - 0x30c
36    pub intenclr: ReadWrite<u32, Intenclr::Register>,
37    /// Reserved
38    _reserved3: [u32; 126],
39    /// ECB block encrypt memory pointers
40    /// - Address: 0x504 - 0x508
41    ecbdataptr: ReadWrite<u32, EcbDataPointer::Register>,
42}
43
44register_bitfields! [u32,
45    /// Start task
46    pub Task [
47        ENABLE OFFSET(0) NUMBITS(1)
48    ],
49
50    /// Read event
51    pub Event [
52        READY OFFSET(0) NUMBITS(1)
53    ],
54
55    /// Enabled interrupt
56    pub Intenset [
57        ENDECB OFFSET(0) NUMBITS(1),
58        ERRORECB OFFSET(1) NUMBITS(1)
59    ],
60
61    /// Disable interrupt
62    pub Intenclr [
63        ENDECB OFFSET(0) NUMBITS(1),
64        ERRORECB OFFSET(1) NUMBITS(1)
65    ],
66
67    /// ECB block encrypt memory pointers
68    EcbDataPointer [
69        POINTER OFFSET(0) NUMBITS(32)
70    ]
71];
72
73/// Wrapper for managing MMIO for the AES ECB peripheral.
74pub struct AesEcbRegistersManager {
75    /// MMIO registers for the AES ECB peripheral.
76    pub registers: StaticRef<AesEcbRegisters>,
77    /// Holding place for the DMA buffer while DMA is in progress.
78    dma_buf: MapCell<DmaSliceMut<'static, u8>>,
79}
80
81impl AesEcbRegistersManager {
82    /// Create a new AES registers manager.
83    ///
84    /// # Safety
85    ///
86    /// This is only valid on an nrf5x-based MCU. This must only be called once
87    /// as having multiple interfaces to DMA registers is not safe. This must
88    /// be the only way the AES DMA registers are controlled.
89    pub unsafe fn new(regs: StaticRef<AesEcbRegisters>) -> Self {
90        Self {
91            registers: regs,
92            dma_buf: MapCell::empty(),
93        }
94    }
95
96    /// Start an ECB encryption with DMA.
97    ///
98    /// The buffer must cover the full 48-byte ECB data block:
99    /// bytes 0–15 = key, bytes 16–31 = plaintext/counter, bytes 32–47 = ciphertext output.
100    ///
101    /// # Return
102    ///
103    /// `Ok(())` on successfully starting the DMA operation. `Err(())` if DMA
104    /// is already busy.
105    pub fn start_ecb_dma(&self, buf: &'static mut [u8]) -> Result<(), ()> {
106        if self.dma_pending() {
107            return Err(());
108        }
109
110        // To create a DmaFence we must trust the implementation.
111        //
112        // # Safety
113        //
114        // The architecture-provided version is correct for the nRF52.
115        let fence = unsafe { cortexm4f::dma_fence::CortexMDmaFence::new() };
116
117        // Create DmaSubSliceMut for the ECB data buffer. This ensures that we
118        // can soundly share it with the DMA hardware.
119        let ecb_dma_slice = DmaSliceMut::new_static(buf, fence);
120
121        // Provide the buffer pointer to the ECB hardware. The hardware expects
122        // the pointer to point at byte 0 of the 48-byte data block (the key).
123        // The SubSliceMut covers the full ecb_data buffer (never sliced), so
124        // as_mut_ptr() correctly points to byte 0.
125        self.registers
126            .ecbdataptr
127            .write(EcbDataPointer::POINTER.val(ecb_dma_slice.as_mut_ptr() as u32));
128
129        // Save the DmaSubSliceMut while the DMA operation executes.
130        self.dma_buf.replace(ecb_dma_slice);
131
132        // Clear the end event and start the ECB task.
133        self.registers.event_endecb.write(Event::READY::CLEAR);
134        self.registers.task_startecb.write(Task::ENABLE::SET);
135
136        Ok(())
137    }
138
139    pub fn finish_ecb_dma(&self) -> Option<&'static mut [u8]> {
140        // Clear the end event before releasing the buffer.
141        self.registers.event_endecb.write(Event::READY::CLEAR);
142
143        self.dma_buf.take().map(|dma_slice| {
144            // To create a DmaFence we must trust the implementation.
145            //
146            // # Safety
147            //
148            // The architecture-provided version is correct for the nRF52.
149            let fence = unsafe { cortexm4f::dma_fence::CortexMDmaFence::new() };
150
151            // # Safety
152            //
153            // We must ensure that the DMA hardware no longer has any access to
154            // this buffer. We ensure that by clearing `event_endecb` above;
155            // the hardware sets this event only after completing the operation.
156            unsafe { dma_slice.take(fence) }
157        })
158    }
159
160    pub fn dma_pending(&self) -> bool {
161        self.dma_buf.is_some()
162    }
163}