system_update_configurator/bridge/vx_ta/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

mod tee_client_api;

use self::tee_client_api::*;
use std::fmt::Debug;
use std::{fmt, mem, ptr};
use thiserror::Error;
use tracing::debug;

use self::tee_client_api::{TEEC_Operation as TeecOperation, TEEC_Value as TeecValue};

use self::tee_client_api::TEEC_Parameter as TeecParameter;

const TA_VX_CMD_OTA_CONFIG_SET: u32 = 24;
const TA_VX_CMD_OTA_CONFIG_GET: u32 = 25;

/// The general error type returned by TEE
#[derive(Debug, Error)]
#[allow(missing_docs)]
pub enum TeeError {
    General(u32),
    Busy,
}

impl fmt::Display for TeeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{self:?}")
    }
}

pub fn ota_config_get(default_value: u32) -> Result<u32, TeeError> {
    let param_type = teec_param_types(TEEC_VALUE_INPUT, TEEC_VALUE_OUTPUT, TEEC_NONE, TEEC_NONE);
    let params = [
        get_value_parameter(default_value, 0),
        get_value_parameter(0, 0),
        get_zero_parameter(),
        get_zero_parameter(),
    ];
    let mut op = create_operation(param_type, params);
    // SAFETY: op was initialized by create_operation and does not contain TEEC_MEMREF_*
    // parameters
    unsafe { call_command(&mut op, TA_VX_CMD_OTA_CONFIG_GET).map_err(map_tee_error)? };
    // SAFETY: op.params[1] is safe to use here because it was initialized by
    // call_command->tee_session.invoke_command invocation.
    let value = unsafe { op.params[1].value.a };
    Ok(value)
}

pub fn ota_config_set(value: u32) -> Result<(), TeeError> {
    let param_type = teec_param_types(TEEC_VALUE_INPUT, TEEC_NONE, TEEC_NONE, TEEC_NONE);
    let params = [
        get_value_parameter(value, 0),
        get_zero_parameter(),
        get_zero_parameter(),
        get_zero_parameter(),
    ];
    let mut op = create_operation(param_type, params);
    // SAFETY: op was initialized by create_operation and does not contain TEEC_MEMREF_*
    // parameters
    unsafe { call_command(&mut op, TA_VX_CMD_OTA_CONFIG_SET).map_err(map_tee_error) }
}

fn map_tee_error(error_code: u32) -> TeeError {
    match error_code {
        TEEC_ERROR_BUSY => TeeError::Busy,
        _ => TeeError::General(error_code),
    }
}

/// The TA UUID for VX: 99dc95b2-938e-47eb-80e8-9404ae8a1385.
static VX_TA_UUID: TEEC_UUID = TEEC_UUID {
    timeLow: 0x99dc95b2,
    timeMid: 0x938e,
    timeHiAndVersion: 0x47eb,
    clockSeqAndNode: [0x80, 0xe8, 0x94, 0x04, 0xae, 0x8a, 0x13, 0x85],
};

/// Gets a None parameter.
fn get_zero_parameter() -> TeecParameter {
    // SAFETY: All zeroes is a valid byte pattern for TeecParameter
    let zero_parameter: TeecParameter = unsafe { mem::zeroed() };
    zero_parameter
}

/// Gets a value parameter.
fn get_value_parameter(a: u32, b: u32) -> TeecParameter {
    TeecParameter { value: TeecValue { a, b } }
}

/// Creates an operation object that would be used in call_command.
fn create_operation(param_type: u32, params: [TeecParameter; 4]) -> TeecOperation {
    TeecOperation {
        started: 0,
        paramTypes: param_type,
        params,
        imp: teec_operation_impl { reserved: 0 as ::std::os::raw::c_char },
    }
}

/// This is the same macro definition as TEEC_PARAM_TYPES in tee-client-types.h
fn teec_param_types(param0_type: u32, param1_type: u32, param2_type: u32, param3_type: u32) -> u32 {
    (param0_type & 0xF)
        | ((param1_type & 0xF) << 4)
        | ((param2_type & 0xF) << 8)
        | ((param3_type & 0xF) << 12)
}

/// Creates a temporary session and call a command.
///
/// Returns error code on failure.
///
/// # Safety
///  - op should be prepared carefully (especially for TEEC_MEMREF_TEMP_*
///    param types: TEEC_TempMemoryReference::buffer should point to valid
///    memory block) otherwise dereference of arbitrary memory can happened.
///  - command_id is a valid TEE request ID
///
unsafe fn call_command(op: &mut TeecOperation, command_id: u32) -> Result<(), u32> {
    let mut tee_context = TeeContext::new()?;
    // SAFETY: tee_session is dropped at the end of the function, before the spawning context
    let mut tee_session = tee_context.new_session()?;
    let mut return_origin: u32 = 0;
    // SAFETY: op is a valid operation, return_origin points to a u32 that is valid for writes
    tee_session.invoke_command(command_id, op, &mut return_origin)
}

struct TeeContext {
    context: TEEC_Context,
}

impl TeeContext {
    pub fn new() -> Result<Self, u32> {
        // SAFETY: All zeroes is a valid byte pattern for TEEC_Context
        let mut context: TEEC_Context = unsafe { mem::zeroed() };
        // SAFETY: null is a valid name argument, context points to a TEEC_Context that is valid
        // for writes
        let result = unsafe { TEEC_InitializeContext(ptr::null(), &mut context) };
        if result != TEEC_SUCCESS {
            debug!("Failed to initialize context: {:?}", result);
            return Err(result);
        }
        Ok(TeeContext { context })
    }

    /// # Safety
    ///
    /// The returned session must be dropped before the context is dropped
    ///
    pub unsafe fn new_session(&mut self) -> Result<TeeSession, u32> {
        // SAFETY: All zeroes is a valid byte pattern for TEEC_Session
        let mut session: TEEC_Session = mem::zeroed();

        let mut return_origin: u32 = 0;
        // SAFETY:
        //  - self.context is initialized
        //  - session points to a TEEC_Session that is valid for writes
        //  - VA_TA_UUID points to a TEEC_UUID that is valid for reads
        //  - null is a valid argument for connection_data and operation
        //  - return_origin points to a u32 that is valid for writes
        let result = TEEC_OpenSession(
            &mut self.context,
            &mut session,
            &VX_TA_UUID,
            TEEC_LOGIN_PUBLIC,
            ptr::null_mut(),
            ptr::null_mut(),
            &mut return_origin,
        );
        if result != TEEC_SUCCESS {
            debug!("Failed to open session ({:?})\n", result);
            return Err(result);
        }
        Ok(TeeSession { session })
    }
}

impl Drop for TeeContext {
    fn drop(&mut self) {
        // SAFETY: all sessions related to this TEE context have been closed.
        unsafe { TEEC_FinalizeContext(&mut self.context) };
    }
}

struct TeeSession {
    session: TEEC_Session,
}

impl TeeSession {
    /// # Safety
    ///
    ///  - self.session points to an open connection
    ///  - command_id is valid TA request ID to invoke
    ///  - operation should be prepared carefully (especially for
    ///    TEEC_MEMREF_TEMP_* param types: TEEC_TempMemoryReference::buffer
    ///    should point to valid memory block) otherwise dereference of
    ///    arbitrary memory can happened.
    ///  - return_origin points to a u32 that is valid for writes.
    pub unsafe fn invoke_command(
        &mut self,
        command_id: u32,
        operation: *mut TEEC_Operation,
        return_origin: *mut u32,
    ) -> Result<(), u32> {
        // SAFETY:
        //  - self.session points to an open connection
        //  - command_id is the ID of the command to invoke
        //  - operation points to a TEEC_Operation that is valid for reads and writes
        //  - return_origin points to a u32 that is valid for writes
        let result = TEEC_InvokeCommand(&mut self.session, command_id, operation, return_origin);
        if result != TEEC_SUCCESS {
            debug!("TEEC_InvokeCommand failed with code {:?}", result);
            return Err(result);
        }
        Ok(())
    }
}

impl Drop for TeeSession {
    fn drop(&mut self) {
        // SAFETY: self.session is open and may be closed
        unsafe { TEEC_CloseSession(&mut self.session) };
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use assert_matches::assert_matches;

    #[fuchsia::test]
    async fn no_tee_connection_test() {
        let rc = ota_config_get(0);
        assert_matches!(rc, Err(TeeError::General(TEEC_ERROR_NOT_SUPPORTED)));

        let rc = ota_config_set(0);
        assert_matches!(rc, Err(TeeError::General(TEEC_ERROR_NOT_SUPPORTED)));
    }
}