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}