Skip to main content

optee_teec_build/
plugin.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 std::path::PathBuf;
19pub use uuid;
20
21/// Default name for the plugin init function.
22pub const DEFAULT_INIT_FN_NAME: &str = "__plugin_bindgen_init";
23/// Default name for the plugin invoke function.
24pub const DEFAULT_INVOKE_FN_NAME: &str = "__plugin_bindgen_invoke";
25
26/// Configuration for building an OP-TEE supplicant plugin binding.
27///
28/// Holds the plugin name, UUID, init/invoke function names, and optional
29/// output destination. Use the builder-style API (`with_*` methods) to
30/// customize defaults, then call [`PluginConfig::build`] to generate the
31/// `plugin_static.rs` file.
32pub struct PluginConfig {
33    name: String,
34    uuid: uuid::Uuid,
35    init_fn_name: String,
36    invoke_fn_name: String,
37    dest: Option<PathBuf>,
38}
39
40impl PluginConfig {
41    /// Creates a new `PluginConfig` with the given UUID.
42    ///
43    /// The plugin name defaults to `CARGO_PKG_NAME`, and the init/invoke
44    /// function names default to [`DEFAULT_INIT_FN_NAME`] and
45    /// [`DEFAULT_INVOKE_FN_NAME`] respectively.
46    pub fn new(uuid: uuid::Uuid) -> Self {
47        Self {
48            name: env!("CARGO_PKG_NAME").to_string(),
49            uuid,
50            init_fn_name: DEFAULT_INIT_FN_NAME.to_owned(),
51            invoke_fn_name: DEFAULT_INVOKE_FN_NAME.to_owned(),
52            dest: None,
53        }
54    }
55    /// Sets a custom plugin name.
56    pub fn with_name(mut self, name: &str) -> Self {
57        self.name = name.to_string();
58        self
59    }
60    /// Sets a custom init function name (overriding [`DEFAULT_INIT_FN_NAME`]).
61    pub fn with_init_fn_name(mut self, fn_name: &str) -> Self {
62        self.init_fn_name = fn_name.to_string();
63        self
64    }
65    /// Sets a custom invoke function name (overriding [`DEFAULT_INVOKE_FN_NAME`]).
66    pub fn with_invoke_fn_name(mut self, fn_name: &str) -> Self {
67        self.invoke_fn_name = fn_name.to_string();
68        self
69    }
70    /// Sets a custom output file path
71    pub fn with_dest(mut self, out_path: PathBuf) -> Self {
72        self.dest = Some(out_path);
73        self
74    }
75    /// Generates the plugin binding source and writes it to the output file.
76    ///
77    /// If the output file already exists with identical content, the write is
78    /// skipped. The default output path is `$OUT_DIR/plugin_static.rs`.
79    pub fn build(&self) -> std::io::Result<()> {
80        let codes = generate_binding(
81            &self.name,
82            &self.uuid,
83            &self.init_fn_name,
84            &self.invoke_fn_name,
85        )
86        .to_string();
87        let out_path = self.get_out_path();
88        if let Ok(v) = std::fs::read(&out_path)
89            && v.eq(codes.as_bytes())
90        {
91            return Ok(());
92        }
93
94        if let Some(parent_dir) = out_path.parent() {
95            std::fs::create_dir_all(parent_dir)?;
96        }
97        std::fs::write(out_path, codes.as_bytes())
98    }
99}
100
101impl PluginConfig {
102    /// Returns the output file path for the generated binding.
103    ///
104    /// Uses the custom destination if set, otherwise defaults to
105    /// `$OUT_DIR/plugin_static.rs`.
106    fn get_out_path(&self) -> PathBuf {
107        match self.dest.as_ref() {
108            Some(v) => v.clone(),
109            None => {
110                let out_dir = PathBuf::from(
111                    std::env::var("OUT_DIR").expect("Infallible when using in build.rs"),
112                );
113                out_dir.join("plugin_static.rs")
114            }
115        }
116    }
117}
118
119fn generate_binding(
120    name: &str,
121    uuid: &uuid::Uuid,
122    init_fn_name: &str,
123    invoke_fn_name: &str,
124) -> proc_macro2::TokenStream {
125    let (uuid_f1, uuid_f2, uuid_f3, uuid_f4) = uuid.as_fields();
126    let name_bytes_with_null = format!("{}\0", name);
127    let init_fn_name = quote::format_ident!("{}", init_fn_name);
128    let invoke_fn_name = quote::format_ident!("{}", invoke_fn_name);
129    quote::quote! {
130        const _: () = {
131            use optee_teec::raw::{PluginMethod, TEEC_UUID};
132
133            static PLUGIN_NAME: &str = #name_bytes_with_null;
134
135            #[unsafe(no_mangle)]
136            pub static mut plugin_method: PluginMethod = PluginMethod {
137                name: PLUGIN_NAME.as_ptr() as *const _,
138                uuid: TEEC_UUID {
139                    timeLow: #uuid_f1,
140                    timeMid: #uuid_f2,
141                    timeHiAndVersion: #uuid_f3,
142                    clockSeqAndNode: [#(#uuid_f4),*],
143                },
144                init: #init_fn_name,
145                invoke: #invoke_fn_name,
146            };
147        };
148    }
149}