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.
45mod tee_client_api;
67use self::tee_client_api::*;
8use log::debug;
9use std::fmt::Debug;
10use std::{fmt, mem, ptr};
11use thiserror::Error;
1213use self::tee_client_api::{TEEC_Operation as TeecOperation, TEEC_Value as TeecValue};
1415use self::tee_client_api::TEEC_Parameter as TeecParameter;
1617const TA_VX_CMD_OTA_CONFIG_SET: u32 = 24;
18const TA_VX_CMD_OTA_CONFIG_GET: u32 = 25;
1920/// The general error type returned by TEE
21#[derive(Debug, Error)]
22#[allow(missing_docs)]
23pub enum TeeError {
24 General(u32),
25 Busy,
26}
2728impl fmt::Display for TeeError {
29fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30write!(f, "{self:?}")
31 }
32}
3334pub fn ota_config_get(default_value: u32) -> Result<u32, TeeError> {
35let param_type = teec_param_types(TEEC_VALUE_INPUT, TEEC_VALUE_OUTPUT, TEEC_NONE, TEEC_NONE);
36let params = [
37 get_value_parameter(default_value, 0),
38 get_value_parameter(0, 0),
39 get_zero_parameter(),
40 get_zero_parameter(),
41 ];
42let mut op = create_operation(param_type, params);
43// SAFETY: op was initialized by create_operation and does not contain TEEC_MEMREF_*
44 // parameters
45unsafe { 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.
48let value = unsafe { op.params[1].value.a };
49Ok(value)
50}
5152pub fn ota_config_set(value: u32) -> Result<(), TeeError> {
53let param_type = teec_param_types(TEEC_VALUE_INPUT, TEEC_NONE, TEEC_NONE, TEEC_NONE);
54let params = [
55 get_value_parameter(value, 0),
56 get_zero_parameter(),
57 get_zero_parameter(),
58 get_zero_parameter(),
59 ];
60let mut op = create_operation(param_type, params);
61// SAFETY: op was initialized by create_operation and does not contain TEEC_MEMREF_*
62 // parameters
63unsafe { call_command(&mut op, TA_VX_CMD_OTA_CONFIG_SET).map_err(map_tee_error) }
64}
6566fn map_tee_error(error_code: u32) -> TeeError {
67match error_code {
68 TEEC_ERROR_BUSY => TeeError::Busy,
69_ => TeeError::General(error_code),
70 }
71}
7273/// 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};
8081/// Gets a None parameter.
82fn get_zero_parameter() -> TeecParameter {
83// SAFETY: All zeroes is a valid byte pattern for TeecParameter
84let zero_parameter: TeecParameter = unsafe { mem::zeroed() };
85 zero_parameter
86}
8788/// Gets a value parameter.
89fn get_value_parameter(a: u32, b: u32) -> TeecParameter {
90 TeecParameter { value: TeecValue { a, b } }
91}
9293/// 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}
102103/// 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}
110111/// 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> {
122let mut tee_context = TeeContext::new()?;
123// SAFETY: tee_session is dropped at the end of the function, before the spawning context
124let mut tee_session = tee_context.new_session()?;
125let mut return_origin: u32 = 0;
126// SAFETY: op is a valid operation, return_origin points to a u32 that is valid for writes
127tee_session.invoke_command(command_id, op, &mut return_origin)
128}
129130struct TeeContext {
131 context: TEEC_Context,
132}
133134impl TeeContext {
135pub fn new() -> Result<Self, u32> {
136// SAFETY: All zeroes is a valid byte pattern for TEEC_Context
137let 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
140let result = unsafe { TEEC_InitializeContext(ptr::null(), &mut context) };
141if result != TEEC_SUCCESS {
142debug!("Failed to initialize context: {:?}", result);
143return Err(result);
144 }
145Ok(TeeContext { context })
146 }
147148/// # Safety
149 ///
150 /// The returned session must be dropped before the context is dropped
151 ///
152pub unsafe fn new_session(&mut self) -> Result<TeeSession, u32> {
153// SAFETY: All zeroes is a valid byte pattern for TEEC_Session
154let mut session: TEEC_Session = mem::zeroed();
155156let 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
163let result = TEEC_OpenSession(
164&mut self.context,
165&mut session,
166&VX_TA_UUID,
167 TEEC_LOGIN_PUBLIC,
168 ptr::null_mut(),
169 ptr::null_mut(),
170&mut return_origin,
171 );
172if result != TEEC_SUCCESS {
173debug!("Failed to open session ({:?})\n", result);
174return Err(result);
175 }
176Ok(TeeSession { session })
177 }
178}
179180impl Drop for TeeContext {
181fn drop(&mut self) {
182// SAFETY: all sessions related to this TEE context have been closed.
183unsafe { TEEC_FinalizeContext(&mut self.context) };
184 }
185}
186187struct TeeSession {
188 session: TEEC_Session,
189}
190191impl TeeSession {
192/// # Safety
193 ///
194 /// - self.session points to an open connection
195 /// - command_id is valid TA request ID to invoke
196 /// - operation should be prepared carefully (especially for
197 /// TEEC_MEMREF_TEMP_* param types: TEEC_TempMemoryReference::buffer
198 /// should point to valid memory block) otherwise dereference of
199 /// arbitrary memory can happened.
200 /// - return_origin points to a u32 that is valid for writes.
201pub unsafe fn invoke_command(
202&mut self,
203 command_id: u32,
204 operation: *mut TEEC_Operation,
205 return_origin: *mut u32,
206 ) -> Result<(), u32> {
207// SAFETY:
208 // - self.session points to an open connection
209 // - command_id is the ID of the command to invoke
210 // - operation points to a TEEC_Operation that is valid for reads and writes
211 // - return_origin points to a u32 that is valid for writes
212let result = TEEC_InvokeCommand(&mut self.session, command_id, operation, return_origin);
213if result != TEEC_SUCCESS {
214debug!("TEEC_InvokeCommand failed with code {:?}", result);
215return Err(result);
216 }
217Ok(())
218 }
219}
220221impl Drop for TeeSession {
222fn drop(&mut self) {
223// SAFETY: self.session is open and may be closed
224unsafe { TEEC_CloseSession(&mut self.session) };
225 }
226}
227228#[cfg(test)]
229mod tests {
230use super::*;
231use assert_matches::assert_matches;
232233#[fuchsia::test]
234async fn no_tee_connection_test() {
235let rc = ota_config_get(0);
236assert_matches!(rc, Err(TeeError::General(TEEC_ERROR_NOT_SUPPORTED)));
237238let rc = ota_config_set(0);
239assert_matches!(rc, Err(TeeError::General(TEEC_ERROR_NOT_SUPPORTED)));
240 }
241}