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}