Skip to main content

optee_utee_build/
code_generator.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use crate::Error;
19use crate::{PropertyValue, TaConfig};
20use quote::{format_ident, quote};
21use std::str::FromStr;
22
23/// Start from rust edition 2024, `no_mangle` and `link_section` must be wrapped with unsafe
24#[derive(Clone)]
25pub enum RustEdition {
26    Before2024,
27    Edition2024,
28}
29
30/// Generator of head file, use it to generate a header file and then include it in user codes.
31///
32/// Use only if you just want to generate a header file, and do the linking job yourself,
33/// or you should use Builder instead.
34///
35/// Examples:
36/// ```rust
37/// use optee_utee_build::{HeaderFileGenerator, TaConfig, RustEdition};
38/// # use optee_utee_build::Error;
39/// # fn main() -> Result<(), Error> {
40/// const UUID: &str = "26509cec-4a2b-4935-87ab-762d89fbf0b0";
41/// let ta_config = TaConfig::new_default(UUID, "0.1.0", "example")?;
42/// let codes = HeaderFileGenerator::new(RustEdition::Before2024).generate(&ta_config)?;
43/// # Ok(())
44/// # }
45/// ```
46pub struct HeaderFileGenerator {
47    code: proc_macro2::TokenStream,
48    edition: RustEdition,
49}
50
51impl HeaderFileGenerator {
52    pub fn new(edition: RustEdition) -> Self {
53        Self {
54            code: quote!(),
55            edition,
56        }
57    }
58    pub fn generate(mut self, conf: &TaConfig) -> Result<String, Error> {
59        self.write_includes();
60        self.write_configurations(conf);
61        self.write_trace(conf);
62        self.write_properties(conf)?;
63        self.write_ta_head(conf)?;
64        self.write_ta_heap();
65
66        const LICENSE_STR: &str = include_str!("./license_str.txt");
67        let f = syn::parse2(self.code).unwrap();
68        // prettyplease will remove all of the comments in it, and the
69        // maintainer will not support keeping comments,
70        // so we just add the comments to codes after formatting
71        let code_string = format!("{}\n{}", LICENSE_STR, prettyplease::unparse(&f));
72        Ok(code_string)
73    }
74}
75
76impl HeaderFileGenerator {
77    fn write_includes(&mut self) {
78        self.code.extend(quote! {
79        use core::ffi::*;
80        use core::mem;
81        use core::primitive::u64;
82                });
83    }
84
85    fn write_trace(&mut self, conf: &TaConfig) {
86        let trace_ext = string_to_binary_codes(&conf.trace_ext_prefix);
87        let trace_level = conf.trace_level;
88        let no_mangle_attribute = self.edition.no_mangle_attribute_codes();
89        self.code.extend(quote! {
90        #no_mangle_attribute
91        pub static mut trace_level: c_int = #trace_level;
92
93        #no_mangle_attribute
94        pub static trace_ext_prefix: &[u8] = #trace_ext;
95
96        /// # Safety
97        /// This function is called by the OP-TEE framework to get the trace level.
98        /// It's safe to call as it only reads a static variable.
99        #no_mangle_attribute
100        pub unsafe extern "C" fn tahead_get_trace_level() -> c_int {
101            unsafe { trace_level }
102        }
103                })
104    }
105    fn write_configurations(&mut self, conf: &TaConfig) {
106        let ta_version = string_to_binary_codes(&conf.ta_version);
107        let ta_description = string_to_binary_codes(&conf.ta_description);
108        let ta_flags = conf.ta_flags;
109        let ta_data_size = conf.ta_data_size;
110        let ta_stack_size = conf.ta_stack_size;
111        self.code.extend(quote! {
112        const TA_FLAGS: u32 = #ta_flags;
113        const TA_DATA_SIZE: u32 = #ta_data_size;
114        const TA_STACK_SIZE: u32 = #ta_stack_size;
115        const TA_VERSION: &[u8] = #ta_version;
116        const TA_DESCRIPTION: &[u8] = #ta_description;
117                });
118    }
119    fn write_properties(&mut self, conf: &TaConfig) -> Result<(), Error> {
120        let mut ext_property_codes =
121            Vec::<proc_macro2::TokenStream>::with_capacity(conf.ext_properties.len());
122        for (index, prop) in conf.ext_properties.iter().enumerate() {
123            let var_name = format!("EXT_PROP_VALUE_{}", index + 1);
124            let rust_type_name_codes = property_value_rust_type_declaration_codes(&prop.value);
125            let value_codes = property_value_data_codes(&prop.value)?;
126            let var_name_codes = format_ident!("{}", var_name);
127            self.code.extend(quote! {
128            const #var_name_codes: #rust_type_name_codes = #value_codes;
129                        });
130            let prop_name_codes = string_to_binary_codes(&prop.name);
131            let utee_type_name_codes = property_value_utee_type_codes(&prop.value);
132            let utee_value_conv_codes = property_value_as_utee_value_codes(&var_name, &prop.value);
133            ext_property_codes.push(quote! {
134                optee_utee_sys::user_ta_property {
135                    name: #prop_name_codes.as_ptr(),
136                    prop_type: #utee_type_name_codes,
137                    value: #utee_value_conv_codes,
138                }
139            });
140        }
141
142        const ORIGIN_PROPERTY_LEN: usize = 7;
143        let property_len = ORIGIN_PROPERTY_LEN + conf.ext_properties.len();
144        let no_mangle_attribute = self.edition.no_mangle_attribute_codes();
145        self.code.extend(quote! {
146        static FLAG_BOOL: bool = (TA_FLAGS & optee_utee_sys::TA_FLAG_SINGLE_INSTANCE) != 0;
147        static FLAG_MULTI: bool = (TA_FLAGS & optee_utee_sys::TA_FLAG_MULTI_SESSION) != 0;
148        static FLAG_INSTANCE: bool = (TA_FLAGS & optee_utee_sys::TA_FLAG_INSTANCE_KEEP_ALIVE) != 0;
149        #no_mangle_attribute
150        pub static ta_num_props: usize = #property_len;
151        #no_mangle_attribute
152        pub static ta_props: [optee_utee_sys::user_ta_property; #property_len] = [
153            optee_utee_sys::user_ta_property {
154                name: optee_utee_sys::TA_PROP_STR_SINGLE_INSTANCE,
155                prop_type: optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_BOOL,
156                value: &FLAG_BOOL as *const bool as *mut _,
157            },
158            optee_utee_sys::user_ta_property {
159                name: optee_utee_sys::TA_PROP_STR_MULTI_SESSION,
160                prop_type: optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_BOOL,
161                value: &FLAG_MULTI as *const bool as *mut _,
162            },
163            optee_utee_sys::user_ta_property {
164                name: optee_utee_sys::TA_PROP_STR_KEEP_ALIVE,
165                prop_type: optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_BOOL,
166                value: &FLAG_INSTANCE as *const bool as *mut _,
167            },
168            optee_utee_sys::user_ta_property {
169                name: optee_utee_sys::TA_PROP_STR_DATA_SIZE,
170                prop_type: optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_U32,
171                value: &TA_DATA_SIZE as *const u32 as *mut _,
172            },
173            optee_utee_sys::user_ta_property {
174                name: optee_utee_sys::TA_PROP_STR_STACK_SIZE,
175                prop_type: optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_U32,
176                value: &TA_STACK_SIZE as *const u32 as *mut _,
177            },
178            optee_utee_sys::user_ta_property {
179                name: optee_utee_sys::TA_PROP_STR_VERSION,
180                prop_type: optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_STRING,
181                value: TA_VERSION as *const [u8] as *mut _,
182            },
183            optee_utee_sys::user_ta_property {
184                name: optee_utee_sys::TA_PROP_STR_DESCRIPTION,
185                prop_type: optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_STRING,
186                value: TA_DESCRIPTION as *const [u8] as *mut _,
187            },
188            #(#ext_property_codes),*
189        ];
190                });
191
192        Ok(())
193    }
194    fn write_ta_head(&mut self, conf: &TaConfig) -> Result<(), Error> {
195        let uuid_value_codes = uuid_to_tee_uuid_value_codes(&conf.uuid)?;
196        let stack_size = conf.ta_stack_size + conf.ta_framework_stack_size;
197        let no_mangle_attribute = self.edition.no_mangle_attribute_codes();
198        let ta_head_session_attribute = self.edition.link_section_attribute_codes(".ta_head");
199        self.code.extend(quote! {
200        #no_mangle_attribute
201        #ta_head_session_attribute
202        pub static ta_head: optee_utee_sys::ta_head = optee_utee_sys::ta_head {
203            uuid: #uuid_value_codes,
204            stack_size: #stack_size,
205            flags: TA_FLAGS,
206            depr_entry: u64::MAX,
207        };
208                });
209
210        Ok(())
211    }
212    fn write_ta_heap(&mut self) {
213        let no_mangle_attribute = self.edition.no_mangle_attribute_codes();
214        let bss_session_attribute = self.edition.link_section_attribute_codes(".bss");
215        self.code.extend(quote! {
216        #no_mangle_attribute
217        #bss_session_attribute
218        pub static ta_heap: [u8; TA_DATA_SIZE as usize] = [0; TA_DATA_SIZE as usize];
219
220        #no_mangle_attribute
221        pub static ta_heap_size: usize = mem::size_of::<u8>() * TA_DATA_SIZE as usize;
222                })
223    }
224}
225
226fn property_value_data_codes(value: &PropertyValue) -> Result<proc_macro2::TokenStream, Error> {
227    match value {
228        PropertyValue::U32(v) => Ok(quote! { #v }),
229        PropertyValue::U64(v) => Ok(quote! { #v }),
230        PropertyValue::Bool(v) => Ok(quote! { #v }),
231        PropertyValue::Uuid(v) => uuid_to_tee_uuid_value_codes(v),
232        PropertyValue::Str(v) => Ok(string_to_binary_codes(v)),
233        PropertyValue::BinaryBlock(v) => Ok(string_to_binary_codes(v)),
234        PropertyValue::Identity(login, uuid) => {
235            identity_to_tee_identity_value_codes(login.clone(), &uuid)
236        }
237    }
238}
239
240fn uuid_to_tee_uuid_value_codes(uuid: &uuid::Uuid) -> Result<proc_macro2::TokenStream, Error> {
241    let (time_low, time_mid, time_hi_and_version, clock_seq_and_node) = uuid.as_fields();
242    Ok(quote! {
243    optee_utee_sys::TEE_UUID {
244        timeLow: #time_low,
245        timeMid: #time_mid,
246        timeHiAndVersion: #time_hi_and_version,
247        clockSeqAndNode: [#(#clock_seq_and_node),* ],
248    }
249        })
250}
251
252fn identity_to_tee_identity_value_codes(
253    login: u32,
254    uuid: &uuid::Uuid,
255) -> Result<proc_macro2::TokenStream, Error> {
256    let tee_uuid_codes = uuid_to_tee_uuid_value_codes(uuid)?;
257    Ok(quote! {
258        optee_utee_sys::TEE_Identity {
259            login: #login,
260            uuid: #tee_uuid_codes,
261        }
262    })
263}
264
265fn property_value_rust_type_declaration_codes(value: &PropertyValue) -> proc_macro2::TokenStream {
266    proc_macro2::TokenStream::from_str(match value {
267        PropertyValue::U32(_) => "u32",
268        PropertyValue::U64(_) => "u64",
269        PropertyValue::Bool(_) => "bool",
270        PropertyValue::Uuid(_) => "optee_utee_sys::TEE_UUID",
271        PropertyValue::Str(_) => "&[u8]",
272        PropertyValue::BinaryBlock(_) => "&[u8]",
273        PropertyValue::Identity(..) => "optee_utee_sys::TEE_Identity",
274    })
275    .unwrap()
276}
277
278fn property_value_as_utee_value_codes(
279    var_name: &str,
280    value: &PropertyValue,
281) -> proc_macro2::TokenStream {
282    proc_macro2::TokenStream::from_str(
283        match value {
284            PropertyValue::U32(_) => format!("&{} as *const u32 as *mut _", var_name),
285            PropertyValue::U64(_) => format!("&{} as *const u64 as *mut _", var_name),
286            PropertyValue::Bool(_) => format!("&{} as *const bool as *mut _", var_name),
287            PropertyValue::Uuid(_) => {
288                format!("&{} as *const optee_utee_sys::TEE_UUID as *mut _", var_name)
289            }
290            PropertyValue::Str(_) => format!("{} as *const [u8] as *mut _", var_name),
291            PropertyValue::BinaryBlock(_) => format!("{} as *const [u8] as *mut _", var_name),
292            PropertyValue::Identity(..) => format!(
293                "&{} as *const optee_utee_sys::TEE_Identity as *mut _",
294                var_name
295            ),
296        }
297        .as_str(),
298    )
299    .unwrap()
300}
301
302fn property_value_utee_type_codes(value: &PropertyValue) -> proc_macro2::TokenStream {
303    proc_macro2::TokenStream::from_str(match value {
304        PropertyValue::U32(_) => "optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_U32",
305        PropertyValue::U64(_) => "optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_U64",
306        PropertyValue::Bool(_) => "optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_BOOL",
307        PropertyValue::Uuid(_) => "optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_UUID",
308        PropertyValue::Str(_) => "optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_STRING",
309        PropertyValue::BinaryBlock(_) => {
310            "optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_BINARY_BLOCK"
311        }
312        PropertyValue::Identity(..) => {
313            "optee_utee_sys::user_ta_prop_type::USER_TA_PROP_TYPE_IDENTITY"
314        }
315    })
316    .unwrap()
317}
318
319fn string_to_binary_codes(s: &str) -> proc_macro2::TokenStream {
320    let wrapped = format!("b\"{}\\0\"", s);
321    proc_macro2::TokenStream::from_str(&wrapped).unwrap()
322}
323
324impl RustEdition {
325    fn no_mangle_attribute_codes(&self) -> proc_macro2::TokenStream {
326        match self {
327            RustEdition::Before2024 => quote! { #[no_mangle] },
328            RustEdition::Edition2024 => quote! { #[unsafe(no_mangle)] },
329        }
330    }
331    fn link_section_attribute_codes(&self, session_name: &str) -> proc_macro2::TokenStream {
332        match self {
333            RustEdition::Before2024 => quote! { #[link_section = #session_name] },
334            RustEdition::Edition2024 => quote! { #[unsafe(link_section = #session_name)] },
335        }
336    }
337}
338
339#[cfg(test)]
340mod tests {
341    use super::*;
342
343    #[test]
344    fn test_edition_before_2024() {
345        let uuid = "26509cec-4a2b-4935-87ab-762d89fbf0b0";
346        let conf = TaConfig::new_default(uuid, "0.1.0", "test_before_2024")
347            .unwrap()
348            .ta_data_size(1024 * 1024);
349        let generator = HeaderFileGenerator::new(RustEdition::Before2024);
350        let codes = generator.generate(&conf).unwrap();
351        let exp_result = include_str!("../test_files/test_edition_before_2024_result.rs");
352        assert_eq!(codes, exp_result);
353    }
354    #[test]
355    fn test_edition_2024() {
356        let uuid = "26509cec-4a2b-4935-87ab-762d89fbf0b0";
357        let conf = TaConfig::new_default(uuid, "0.1.0", "test_edition_2024").unwrap();
358        let generator = HeaderFileGenerator::new(RustEdition::Edition2024);
359        let codes = generator.generate(&conf).unwrap();
360        let exp_result = include_str!("../test_files/test_edition_2024_result.rs");
361        assert_eq!(codes, exp_result);
362    }
363}