system_update_configurator/bridge/vx_ta/
mod.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5mod tee_client_api;
6
7use self::tee_client_api::*;
8use log::debug;
9use std::fmt::Debug;
10use std::{fmt, mem, ptr};
11use thiserror::Error;
12
13use self::tee_client_api::{TEEC_Operation as TeecOperation, TEEC_Value as TeecValue};
14
15use self::tee_client_api::TEEC_Parameter as TeecParameter;
16
17const TA_VX_CMD_OTA_CONFIG_SET: u32 = 24;
18const TA_VX_CMD_OTA_CONFIG_GET: u32 = 25;
19
20/// The general error type returned by TEE
21#[derive(Debug, Error)]
22#[allow(missing_docs)]
23pub enum TeeError {
24    General(u32),
25    Busy,
26}
27
28impl fmt::Display for TeeError {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        write!(f, "{self:?}")
31    }
32}
33
34pub fn ota_config_get(default_value: u32) -> Result<u32, TeeError> {
35    let param_type = teec_param_types(TEEC_VALUE_INPUT, TEEC_VALUE_OUTPUT, TEEC_NONE, TEEC_NONE);
36    let params = [
37        get_value_parameter(default_value, 0),
38        get_value_parameter(0, 0),
39        get_zero_parameter(),
40        get_zero_parameter(),
41    ];
42    let mut op = create_operation(param_type, params);
43    // SAFETY: op was initialized by create_operation and does not contain TEEC_MEMREF_*
44    // parameters
45    unsafe { call_command(&mut op, TA_VX_CMD_OTA_CONFIG_GET).map_err(map_tee_error)? };
46    // SAFETY: op.params[1] is safe to use here because it was initialized by
47    // call_command->tee_session.invoke_command invocation.
48    let value = unsafe { op.params[1].value.a };
49    Ok(value)
50}
51
52pub fn ota_config_set(value: u32) -> Result<(), TeeError> {
53    let param_type = teec_param_types(TEEC_VALUE_INPUT, TEEC_NONE, TEEC_NONE, TEEC_NONE);
54    let params = [
55        get_value_parameter(value, 0),
56        get_zero_parameter(),
57        get_zero_parameter(),
58        get_zero_parameter(),
59    ];
60    let mut op = create_operation(param_type, params);
61    // SAFETY: op was initialized by create_operation and does not contain TEEC_MEMREF_*
62    // parameters
63    unsafe { call_command(&mut op, TA_VX_CMD_OTA_CONFIG_SET).map_err(map_tee_error) }
64}
65
66fn map_tee_error(error_code: u32) -> TeeError {
67    match error_code {
68        TEEC_ERROR_BUSY => TeeError::Busy,
69        _ => TeeError::General(error_code),
70    }
71}
72
73/// The TA UUID for VX: 99dc95b2-938e-47eb-80e8-9404ae8a1385.
74static VX_TA_UUID: TEEC_UUID = TEEC_UUID {
75    timeLow: 0x99dc95b2,
76    timeMid: 0x938e,
77    timeHiAndVersion: 0x47eb,
78    clockSeqAndNode: [0x80, 0xe8, 0x94, 0x04, 0xae, 0x8a, 0x13, 0x85],
79};
80
81/// Gets a None parameter.
82fn get_zero_parameter() -> TeecParameter {
83    // SAFETY: All zeroes is a valid byte pattern for TeecParameter
84    let zero_parameter: TeecParameter = unsafe { mem::zeroed() };
85    zero_parameter
86}
87
88/// Gets a value parameter.
89fn get_value_parameter(a: u32, b: u32) -> TeecParameter {
90    TeecParameter { value: TeecValue { a, b } }
91}
92
93/// Creates an operation object that would be used in call_command.
94fn create_operation(param_type: u32, params: [TeecParameter; 4]) -> TeecOperation {
95    TeecOperation {
96        started: 0,
97        paramTypes: param_type,
98        params,
99        imp: teec_operation_impl { reserved: 0 as ::std::os::raw::c_char },
100    }
101}
102
103/// This is the same macro definition as TEEC_PARAM_TYPES in tee-client-types.h
104fn teec_param_types(param0_type: u32, param1_type: u32, param2_type: u32, param3_type: u32) -> u32 {
105    (param0_type & 0xF)
106        | ((param1_type & 0xF) << 4)
107        | ((param2_type & 0xF) << 8)
108        | ((param3_type & 0xF) << 12)
109}
110
111/// Creates a temporary session and call a command.
112///
113/// Returns error code on failure.
114///
115/// # Safety
116///  - op should be prepared carefully (especially for TEEC_MEMREF_TEMP_*
117///    param types: TEEC_TempMemoryReference::buffer should point to valid
118///    memory block) otherwise dereference of arbitrary memory can happened.
119///  - command_id is a valid TEE request ID
120///
121unsafe fn call_command(op: &mut TeecOperation, command_id: u32) -> Result<(), u32> {
122    let mut tee_context = TeeContext::new()?;
123    // SAFETY: tee_session is dropped at the end of the function, before the spawning context
124    let mut tee_session = unsafe { tee_context.new_session()? };
125    let mut return_origin: u32 = 0;
126    // SAFETY: op is a valid operation, return_origin points to a u32 that is valid for writes
127    unsafe { tee_session.invoke_command(command_id, op, &mut return_origin) }
128}
129
130struct TeeContext {
131    context: TEEC_Context,
132}
133
134impl TeeContext {
135    pub fn new() -> Result<Self, u32> {
136        // SAFETY: All zeroes is a valid byte pattern for TEEC_Context
137        let mut context: TEEC_Context = unsafe { mem::zeroed() };
138        // SAFETY: null is a valid name argument, context points to a TEEC_Context that is valid
139        // for writes
140        let result = unsafe { TEEC_InitializeContext(ptr::null(), &mut context) };
141        if result != TEEC_SUCCESS {
142            debug!("Failed to initialize context: {:?}", result);
143            return Err(result);
144        }
145        Ok(TeeContext { context })
146    }
147
148    /// # Safety
149    ///
150    /// The returned session must be dropped before the context is dropped
151    ///
152    pub unsafe fn new_session(&mut self) -> Result<TeeSession, u32> {
153        // SAFETY: All zeroes is a valid byte pattern for TEEC_Session
154        let mut session: TEEC_Session = unsafe { mem::zeroed() };
155
156        let mut return_origin: u32 = 0;
157        // SAFETY:
158        //  - self.context is initialized
159        //  - session points to a TEEC_Session that is valid for writes
160        //  - VA_TA_UUID points to a TEEC_UUID that is valid for reads
161        //  - null is a valid argument for connection_data and operation
162        //  - return_origin points to a u32 that is valid for writes
163        let result = unsafe {
164            TEEC_OpenSession(
165                &mut self.context,
166                &mut session,
167                &VX_TA_UUID,
168                TEEC_LOGIN_PUBLIC,
169                ptr::null_mut(),
170                ptr::null_mut(),
171                &mut return_origin,
172            )
173        };
174        if result != TEEC_SUCCESS {
175            debug!("Failed to open session ({:?})\n", result);
176            return Err(result);
177        }
178        Ok(TeeSession { session })
179    }
180}
181
182impl Drop for TeeContext {
183    fn drop(&mut self) {
184        // SAFETY: all sessions related to this TEE context have been closed.
185        unsafe { TEEC_FinalizeContext(&mut self.context) };
186    }
187}
188
189struct TeeSession {
190    session: TEEC_Session,
191}
192
193impl TeeSession {
194    /// # Safety
195    ///
196    ///  - self.session points to an open connection
197    ///  - command_id is valid TA request ID to invoke
198    ///  - operation should be prepared carefully (especially for
199    ///    TEEC_MEMREF_TEMP_* param types: TEEC_TempMemoryReference::buffer
200    ///    should point to valid memory block) otherwise dereference of
201    ///    arbitrary memory can happened.
202    ///  - return_origin points to a u32 that is valid for writes.
203    pub unsafe fn invoke_command(
204        &mut self,
205        command_id: u32,
206        operation: *mut TEEC_Operation,
207        return_origin: *mut u32,
208    ) -> Result<(), u32> {
209        // SAFETY:
210        //  - self.session points to an open connection
211        //  - command_id is the ID of the command to invoke
212        //  - operation points to a TEEC_Operation that is valid for reads and writes
213        //  - return_origin points to a u32 that is valid for writes
214        let result =
215            unsafe { TEEC_InvokeCommand(&mut self.session, command_id, operation, return_origin) };
216        if result != TEEC_SUCCESS {
217            debug!("TEEC_InvokeCommand failed with code {:?}", result);
218            return Err(result);
219        }
220        Ok(())
221    }
222}
223
224impl Drop for TeeSession {
225    fn drop(&mut self) {
226        // SAFETY: self.session is open and may be closed
227        unsafe { TEEC_CloseSession(&mut self.session) };
228    }
229}
230
231#[cfg(test)]
232mod tests {
233    use super::*;
234    use assert_matches::assert_matches;
235
236    #[fuchsia::test]
237    async fn no_tee_connection_test() {
238        let rc = ota_config_get(0);
239        assert_matches!(rc, Err(TeeError::General(TEEC_ERROR_NOT_SUPPORTED)));
240
241        let rc = ota_config_set(0);
242        assert_matches!(rc, Err(TeeError::General(TEEC_ERROR_NOT_SUPPORTED)));
243    }
244}