lowrisc/
otbn.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//! OTBN Control
6
7use core::cell::Cell;
8use kernel::utilities::cells::{OptionalCell, TakeCell};
9use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
10use kernel::utilities::registers::{
11    register_bitfields, register_structs, ReadOnly, ReadWrite, WriteOnly,
12};
13use kernel::utilities::StaticRef;
14use kernel::ErrorCode;
15
16/// Implement this trait and use `set_client()` in order to receive callbacks.
17pub trait Client<'a> {
18    /// This callback is called when a operation is computed.
19    /// On error or success `output` will contain a reference to the original
20    /// data supplied to `run()`.
21    fn op_done(&'a self, result: Result<(), ErrorCode>, output: &'static mut [u8]);
22}
23
24register_structs! {
25    pub OtbnRegisters {
26        (0x00 => intr_state: ReadWrite<u32, INTR::Register>),
27        (0x04 => intr_enable: ReadWrite<u32, INTR::Register>),
28        (0x08 => intr_test: WriteOnly<u32, INTR::Register>),
29        (0x0C => alert_test: WriteOnly<u32, ALERT_TEST::Register>),
30        (0x10 => cmd: ReadWrite<u32, CMD::Register>),
31        (0x14 => ctrl: ReadWrite<u32, CTRL::Register>),
32        (0x18 => status: ReadOnly<u32, STATUS::Register>),
33        (0x1C => err_bits: ReadOnly<u32, ERR_BITS::Register>),
34        (0x20 => fatal_alert_cause: ReadOnly<u32, FATAL_ALERT_CAUSE::Register>),
35        (0x24 => insn_cnt: ReadWrite<u32>),
36        (0x28 => load_checksum: ReadWrite<u32>),
37        (0x2C => _reserved0),
38        (0x4000 => imem: [ReadWrite<u32>; 1024]),
39        (0x5000 => _reserved1),
40        (0x8000 => dmem: [ReadWrite<u32>; 768]),
41        (0x8C00 => @END),
42    }
43}
44
45register_bitfields![u32,
46    INTR [
47        DONE OFFSET(0) NUMBITS(1) [],
48    ],
49    ALERT_TEST [
50        FATAL OFFSET(0) NUMBITS(1) [],
51        RECOV OFFSET(1) NUMBITS(1) [],
52    ],
53    CMD [
54        CMD OFFSET(0) NUMBITS(8) [
55            EXECUTE = 0xD8,
56            SEC_WIPE_DMEM = 0xC3,
57            SEC_WIPE_IMEM = 0x1E,
58        ],
59    ],
60    CTRL [
61        SOFTWARE_ERRS_FATAL OFFSET(0) NUMBITS(1) [],
62    ],
63    STATUS [
64        STATUS OFFSET(0) NUMBITS(8) [
65            IDLE = 0x00,
66            BUSY_EXECUTE = 0x01,
67            BUSY_SEC_WIPE_DMEM = 0x02,
68            BUSY_SEC_WIPE_IMEM = 0x03,
69            BUSY_SEC_WIPE_INT = 0x04,
70            LOCKED = 0xFF,
71        ],
72    ],
73    ERR_BITS [
74        BAD_DATA_ADDR OFFSET(0) NUMBITS(1) [],
75        BAD_INSN_ADDR OFFSET(1) NUMBITS(1) [],
76        CALL_STACK OFFSET(2) NUMBITS(1) [],
77        ILLEGAL_INSN OFFSET(3) NUMBITS(1) [],
78        LOOP_BIT OFFSET(4) NUMBITS(1) [],
79        KEY_INVAL OFFSET(5) NUMBITS(1) [],
80        RND_REP_CHK_FAIL OFFSET(6) NUMBITS(1) [],
81        RND_FIPS_CHK_FAIL OFFSET(7) NUMBITS(1) [],
82        IMEM_INTG_VIOLATION OFFSET(16) NUMBITS(1) [],
83        DMEM_INTG_VIOLATION OFFSET(17) NUMBITS(1) [],
84        REG_INTG_VIOLATION OFFSET(18) NUMBITS(1) [],
85        BUS_INTG_VIOLATION OFFSET(19) NUMBITS(1) [],
86        BAD_INTERNAL_STATE OFFSET(20) NUMBITS(1) [],
87        ILLEGAL_BUS_ACCESS OFFSET(21) NUMBITS(1) [],
88        LIFECYCLE_ESCALATION OFFSET(22) NUMBITS(1) [],
89        FATAL_SOFTWARE OFFSET(23) NUMBITS(1) [],
90    ],
91    FATAL_ALERT_CAUSE [
92        IMEM_INTG_VIOLATION OFFSET(0) NUMBITS(1) [],
93        DMEM_INTG_VIOLATION OFFSET(1) NUMBITS(1) [],
94        REG_INTG_VIOLATION OFFSET(2) NUMBITS(1) [],
95        BUS_INTG_VIOLATION OFFSET(3) NUMBITS(1) [],
96        BAD_INTERNAL_STATE OFFSET(4) NUMBITS(1) [],
97        ILLEGAL_BUS_ACCESS OFFSET(5) NUMBITS(1) [],
98        LIFECYCLE_ESCALATION OFFSET(6) NUMBITS(1) [],
99        FATAL_SOFTWARE OFFSET(7) NUMBITS(1) [],
100    ],
101];
102
103pub struct Otbn<'a> {
104    registers: StaticRef<OtbnRegisters>,
105    client: OptionalCell<&'a dyn Client<'a>>,
106
107    out_buffer: TakeCell<'static, [u8]>,
108
109    copy_address: Cell<usize>,
110}
111
112impl<'a> Otbn<'a> {
113    pub fn new(base: StaticRef<OtbnRegisters>) -> Self {
114        Otbn {
115            registers: base,
116            client: OptionalCell::empty(),
117            out_buffer: TakeCell::empty(),
118            copy_address: Cell::new(0),
119        }
120    }
121
122    pub fn handle_interrupt(&self) {
123        self.registers.intr_enable.set(0x00);
124        self.registers.intr_state.set(0xFFFF_FFFF);
125
126        // Check if there is an error
127        if self.registers.err_bits.get() > 0 {
128            self.client.map(|client| {
129                self.out_buffer.take().map(|buf| {
130                    client.op_done(Err(ErrorCode::FAIL), buf);
131                })
132            });
133            return;
134        }
135
136        if self.registers.status.matches_all(STATUS::STATUS::IDLE) {
137            let out_buf = self.out_buffer.take().unwrap();
138
139            for i in 0..(out_buf.len() / 4) {
140                let idx = i * 4;
141                let d = self.registers.dmem[self.copy_address.get() / 4 + i]
142                    .get()
143                    .to_ne_bytes();
144
145                out_buf[idx + 0] = d[0];
146                out_buf[idx + 1] = d[1];
147                out_buf[idx + 2] = d[2];
148                out_buf[idx + 3] = d[3];
149            }
150
151            self.client.map(|client| {
152                client.op_done(Ok(()), out_buf);
153            });
154        }
155    }
156
157    /// Set the client instance which will receive
158    pub fn set_client(&'a self, client: &'a dyn Client<'a>) {
159        self.client.set(client);
160    }
161
162    /// Load the acceleration binary data into the accelerator.
163    /// This data will be accelerator specific and could be an
164    /// elf file which will be run or could be binary settings used to
165    /// configure the accelerator.
166    /// This function can be called multiple times if multiple binary blobs
167    /// are required.
168    /// On error the return value will contain a return code and the original data
169    pub fn load_binary(&self, input: &[u8]) -> Result<(), ErrorCode> {
170        if !self.registers.status.matches_all(STATUS::STATUS::IDLE) {
171            // OTBN is performing an operation, we can't make any changes
172            return Err(ErrorCode::BUSY);
173        }
174        // Instruction memory is too large to fit
175        if (input.len() / 4) > self.registers.imem.len() {
176            return Err(ErrorCode::SIZE);
177        }
178
179        for i in 0..(input.len() / 4) {
180            let idx = i * 4;
181
182            let mut d = (input[idx + 0] as u32) << 0;
183            d |= (input[idx + 1] as u32) << 8;
184            d |= (input[idx + 2] as u32) << 16;
185            d |= (input[idx + 3] as u32) << 24;
186
187            self.registers.imem[i].set(d);
188        }
189
190        Ok(())
191    }
192
193    /// Load the data into the accelerator
194    /// This function can be called multiple times if multiple loads
195    /// are required.
196    /// On error the return value will contain a return code and the original data
197    /// The `data` buffer should be in little endian
198    pub fn load_data(&self, address: usize, data: &[u8]) -> Result<(), ErrorCode> {
199        if !self.registers.status.matches_all(STATUS::STATUS::IDLE) {
200            // OTBN is performing an operation, we can't make any changes
201            return Err(ErrorCode::BUSY);
202        }
203
204        for i in 0..(data.len() / 4) {
205            let idx = i * 4;
206
207            let mut d = (data[idx + 0] as u32) << 0;
208            d |= (data[idx + 1] as u32) << 8;
209            d |= (data[idx + 2] as u32) << 16;
210            d |= (data[idx + 3] as u32) << 24;
211
212            self.registers.dmem[(address / 4) + i].set(d);
213        }
214
215        Ok(())
216    }
217
218    /// Run the acceleration operation.
219    /// This doesn't return any data, instead the client needs to have
220    /// set a `op_done` handler to determine when this is complete.
221    ///
222    /// The data returned via `op_done()` will be starting at `address` and of
223    /// the full length of `output`.
224    ///
225    /// On error the return value will contain a return code and the original data
226    /// If there is data from the `load_binary()` command asyncrously waiting to
227    /// be written it will be written before the operation starts.
228    pub fn run(
229        &self,
230        address: usize,
231        output: &'static mut [u8],
232    ) -> Result<(), (ErrorCode, &'static mut [u8])> {
233        if !self.registers.status.matches_all(STATUS::STATUS::IDLE) {
234            // OTBN is performing an operation
235            return Err((ErrorCode::BUSY, output));
236        }
237
238        self.registers.ctrl.modify(CTRL::SOFTWARE_ERRS_FATAL::CLEAR);
239
240        // Clear and enable interrupts
241        self.registers.intr_state.modify(INTR::DONE::SET);
242        self.registers.intr_enable.modify(INTR::DONE::SET);
243
244        self.out_buffer.replace(output);
245        self.copy_address.set(address);
246
247        self.registers.cmd.modify(CMD::CMD::EXECUTE);
248
249        Ok(())
250    }
251
252    /// Clear the keys and any other sensitive data.
253    /// This won't clear the buffers provided to this API, that is up to the
254    /// user to clear those.
255    pub fn clear_data(&self) {
256        self.registers.cmd.write(CMD::CMD::SEC_WIPE_DMEM);
257        self.registers.cmd.write(CMD::CMD::SEC_WIPE_IMEM);
258    }
259}