This is an automated email from the ASF dual-hosted git repository.

yuanz pushed a commit to branch main
in repository 
https://gitbox.apache.org/repos/asf/incubator-teaclave-trustzone-sdk.git


The following commit(s) were added to refs/heads/main by this push:
     new 8bb315f  optee-utee: support setting capacity of shared buffer of 
LoadablePlugin
8bb315f is described below

commit 8bb315f2e0cc1105ad372b8de9024652b234fc2b
Author: ivila <[email protected]>
AuthorDate: Fri Dec 6 15:07:28 2024 +0800

    optee-utee: support setting capacity of shared buffer of LoadablePlugin
    
    1) introduce a Command struct for flexible use
    2) allow developers settings capacity of shared buffer when invoking
       command
    
    Signed-off-by: ivila <[email protected]>
    Reviewed-by: Yuan Zhuang <[email protected]>
    Acked-by: Sumit Garg <[email protected]>
---
 optee-utee/Cargo.toml         |   8 +-
 optee-utee/src/extension.rs   | 331 ++++++++++++++++++++++++++++++++++++++++--
 optee-utee/systest/Cargo.toml |   2 +-
 3 files changed, 324 insertions(+), 17 deletions(-)

diff --git a/optee-utee/Cargo.toml b/optee-utee/Cargo.toml
index a532333..049c09e 100644
--- a/optee-utee/Cargo.toml
+++ b/optee-utee/Cargo.toml
@@ -30,7 +30,13 @@ optee-utee-macros = { version = "0.2.0", path = "macros" }
 bitflags = "1.0.4"
 uuid = { version = "0.8", default-features = false }
 hex = { version = "0.4", default-features = false, features = ["alloc"] }
-libc_alloc = "=1.0.5"
+libc_alloc = "1.0.5"
+
+[dev-dependencies]
+rand = "0.8.5"
+once_cell = "1.20.2"
+serde = { version = "1.0.215" }
+serde_json = { version = "1.0.133" }
 
 [features]
 no_panic_handler = []
diff --git a/optee-utee/src/extension.rs b/optee-utee/src/extension.rs
index fc92a7b..dd07905 100644
--- a/optee-utee/src/extension.rs
+++ b/optee-utee/src/extension.rs
@@ -15,7 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use crate::{Error, Result, Uuid};
+use crate::{Error, ErrorKind, Result, Uuid};
 use optee_utee_sys as raw;
 #[cfg(not(target_os = "optee"))]
 use alloc::vec::Vec;
@@ -26,32 +26,333 @@ pub struct LoadablePlugin {
     uuid: Uuid
 }
 
+pub struct LoadablePluginCommand<'a> {
+    plugin: &'a LoadablePlugin,
+    cmd_id: u32,
+    sub_cmd_id: u32,
+    buffer: Vec<u8>,
+}
+
 impl LoadablePlugin {
     pub fn new(uuid: &Uuid) -> Self {
         Self { uuid: uuid.to_owned() }
     }
-    pub fn invoke(&mut self, command_id: u32, subcommand_id: u32, data: &[u8]) 
-> Result<Vec<u8>> {
-        let raw_uuid: Uuid = self.uuid;
+    /// Invoke plugin with given request data, use when you want to post 
something into REE.
+    /// ```no_run
+    /// let result = plugin.invoke(command_id, subcommand_id, request_data)?;
+    /// ```
+    /// Caution: the size of the shared buffer is set to the len of data, you 
could get a 
+    ///          ShortBuffer error if Plugin return more data than shared 
buffer, in that case,
+    ///          use invoke_with_capacity and set the capacity manually.
+    pub fn invoke(&self, command_id: u32, subcommand_id: u32, data: &[u8]) -> 
Result<Vec<u8>> {
+        self.invoke_with_capacity(command_id, subcommand_id, data.len())
+            .chain_write_body(data)
+            .call()
+    }
+    /// Construct a command with shared buffer up to capacity size, write the 
buffer and call it
+    /// manually, use when you need to control details of the invoking process.
+    /// ```no_run
+    /// let mut cmd = plugin.invoke_with_capacity(command_id, sub_command_id, 
capacity);
+    /// cmd.write(request_data);
+    /// let result = cmd.call()?;
+    /// ```
+    /// You can also imply a wrapper for performance, for example, imply a 
std::io::Write so
+    /// serde_json can write to the buffer directly.
+    /// ```no_run
+    /// struct Wrapper<'a, 'b>(&'b mut LoadablePluginCommand<'a>);
+    /// impl<'a, 'b> std::io::Write for Wrapper<'a, 'b> {
+    ///     fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+    ///         self.0.write_body(buf);
+    ///         Ok(buf.len())
+    ///     }
+    ///     fn flush(&mut self) -> std::io::Result<()> {
+    ///         Ok(())
+    ///     }
+    /// }
+    /// // serialize data into command directly
+    /// let request_data = serde_json::json!({
+    ///     "age": 100,
+    ///     "name": "name"
+    /// });
+    /// let mut cmd = plugin.invoke_with_capacity(command_id, subcommand_id, 
capacity);
+    /// serde_json::to_writer(Wrapper(&mut plugin_cmd), &request_data)?;
+    /// let result = cmd.call()?;
+    /// ```
+    /// Notice: the shared buffer could grow to fit the request data 
automatically.
+    pub fn invoke_with_capacity<'a>(
+        &'a self,
+        command_id: u32,
+        subcommand_id: u32,
+        capacity: usize,
+    ) -> LoadablePluginCommand<'a> {
+        LoadablePluginCommand::new_with_capacity(self, command_id, 
subcommand_id, capacity)
+    }
+}
+
+impl<'a> LoadablePluginCommand<'a> {
+    // use this to write request body if needed
+    pub fn write_body(&mut self, data: &[u8]) {
+        self.buffer.extend_from_slice(data);
+    }
+    // same with write_body, but chainable
+    pub fn chain_write_body(mut self, data: &[u8]) -> Self {
+        self.write_body(data);
+        self
+    }
+    // invoke the command, and get result from it
+    pub fn call(self) -> Result<Vec<u8>> {
         let mut outlen: usize = 0;
+        let mut buffer = self.buffer;
+        buffer.resize(buffer.capacity(), 0); // resize to capacity first
         match unsafe {
             raw::tee_invoke_supp_plugin(
-                raw_uuid.as_raw_ptr(),
-                command_id as u32,
-                subcommand_id as u32,
-                data.as_ptr() as _,
-                data.len(),
+                self.plugin.uuid.as_raw_ptr(),
+                self.cmd_id,
+                self.sub_cmd_id,
+                // convert the pointer manually, as in some platform c_char is 
i8
+                buffer.as_mut_slice().as_mut_ptr() as *mut _,
+                buffer.len(),
                 &mut outlen as *mut usize,
             )
         } {
             raw::TEE_SUCCESS => {
-                assert!(outlen <= (data.len()));
-                let mut outbuf = vec![0; outlen];
-                outbuf.copy_from_slice(&data[..outlen]);
-                
-                Ok(outbuf)
-            },
+                if outlen > buffer.len() {
+                    return Err(ErrorKind::ShortBuffer.into());
+                }
+                buffer.resize(outlen, 0);
+                Ok(buffer)
+            }
             code => Err(Error::from_raw_error(code)),
         }
-       
+    }
+}
+
+impl<'a> LoadablePluginCommand<'a> {
+    fn new_with_capacity(
+        plugin: &'a LoadablePlugin,
+        cmd_id: u32,
+        sub_cmd_id: u32,
+        capacity: usize,
+    ) -> Self {
+        Self {
+            plugin,
+            cmd_id,
+            sub_cmd_id,
+            buffer: Vec::with_capacity(capacity),
+        }
+    }
+}
+
+#[cfg(test)]
+pub mod test_loadable_plugin {
+    extern crate std;
+    use super::*;
+    use core::ffi::c_char;
+    use once_cell::sync::Lazy;
+    use optee_utee_sys::{TEE_Result, TEE_UUID};
+    use rand::distributions::Alphanumeric;
+    use rand::Rng;
+    use std::collections::HashMap;
+    use std::sync::RwLock;
+
+    static REE_RETURN_VALUES: RwLock<Lazy<HashMap<(u32, u32), Vec<u8>>>> =
+        RwLock::new(Lazy::new(|| HashMap::new()));
+    static REE_EXPECTED_VALUES: RwLock<Lazy<HashMap<(u32, u32), Vec<u8>>>> =
+        RwLock::new(Lazy::new(|| HashMap::new()));
+
+    fn set_ree_return_value(cmd: u32, sub_cmd: u32, value: Vec<u8>) {
+        let mut values = REE_RETURN_VALUES.write().unwrap();
+        let key = (cmd, sub_cmd);
+        assert!(!values.contains_key(&key));
+        values.insert(key, value);
+    }
+
+    fn set_ree_expected_value(cmd: u32, sub_cmd: u32, value: Vec<u8>) {
+        let mut values = REE_EXPECTED_VALUES.write().unwrap();
+        let key = (cmd, sub_cmd);
+        assert!(!values.contains_key(&key));
+        values.insert(key, value);
+    }
+
+    fn get_ree_return_value(cmd: u32, sub_cmd: u32) -> Vec<u8> {
+        let values = REE_RETURN_VALUES.read().unwrap();
+        let key = (cmd, sub_cmd);
+        values.get(&key).unwrap().to_owned()
+    }
+
+    fn get_ree_expected_value(cmd: u32, sub_cmd: u32) -> Vec<u8> {
+        let values = REE_EXPECTED_VALUES.read().unwrap();
+        let key = (cmd, sub_cmd);
+        values.get(&key).unwrap().to_owned()
+    }
+
+    fn generate_random_bytes(len: usize) -> Vec<u8> {
+        rand::thread_rng()
+            .sample_iter(&Alphanumeric)
+            .take(len)
+            .collect()
+    }
+
+    fn generate_test_pairs(
+        request_size: usize,
+        response_size: usize,
+    ) -> (u32, u32, Vec<u8>, Vec<u8>) {
+        let cmd: u32 = rand::thread_rng().r#gen();
+        let sub_cmd: u32 = rand::thread_rng().r#gen();
+        let random_request: Vec<u8> = generate_random_bytes(request_size);
+        let random_response: Vec<u8> = generate_random_bytes(response_size);
+        (cmd, sub_cmd, random_request, random_response)
+    }
+
+    #[no_mangle]
+    extern "C" fn tee_invoke_supp_plugin(
+        _uuid: *const TEE_UUID,
+        cmd: u32,
+        sub_cmd: u32,
+        buf: *mut c_char,
+        len: usize,
+        outlen: *mut usize,
+    ) -> TEE_Result {
+        // must convert buf to u8, for in some platform c_char was treated as 
i8
+        let inbuf = unsafe { core::slice::from_raw_parts_mut(buf as *mut u8, 
len) };
+        std::println!(
+            "*plugin*: receive value: {:?} length {:?}",
+            inbuf,
+            inbuf.len()
+        );
+        let expected_value = get_ree_expected_value(cmd, sub_cmd);
+        assert_eq!(inbuf, expected_value.as_slice());
+
+        let return_value = get_ree_return_value(cmd, sub_cmd);
+        assert!(return_value.len() <= len);
+        std::println!("*plugin*: write value '{:?}' to buffer", return_value);
+
+        inbuf[0..return_value.len()].copy_from_slice(&return_value);
+        unsafe {
+            *outlen = return_value.len();
+        }
+        return raw::TEE_SUCCESS;
+    }
+
+    #[test]
+    fn test_invoke() {
+        let plugin = LoadablePlugin {
+            uuid: 
Uuid::parse_str("7dd54ee6-a705-4e4d-8b6b-aa5024dfcd10").unwrap(),
+        };
+        const REQUEST_LEN: usize = 32;
+
+        // test calling with output size less than input
+        let (cmd, sub_cmd, request, exp_response) =
+            generate_test_pairs(REQUEST_LEN, REQUEST_LEN / 2);
+        set_ree_expected_value(cmd, sub_cmd, request.clone());
+        set_ree_return_value(cmd, sub_cmd, exp_response.clone());
+        let response = plugin.invoke(cmd, sub_cmd, &request).unwrap();
+        std::println!("*TA*: response is {:?}", response);
+        assert_eq!(response, exp_response);
+
+        // test calling with output size equals to input
+        let (cmd, sub_cmd, request, exp_response) = 
generate_test_pairs(REQUEST_LEN, REQUEST_LEN);
+        set_ree_expected_value(cmd, sub_cmd, request.clone());
+        set_ree_return_value(cmd, sub_cmd, exp_response.clone());
+        let response = plugin.invoke(cmd, sub_cmd, &request).unwrap();
+        std::println!("*TA*: response is {:?}", response);
+        assert_eq!(response, exp_response);
+    }
+
+    #[test]
+    fn test_invoke_with_capacity() {
+        let plugin = LoadablePlugin {
+            uuid: 
Uuid::parse_str("7dd54ee6-a705-4e4d-8b6b-aa5024dfcd10").unwrap(),
+        };
+        const RESPONSE_LEN: usize = 32;
+
+        // test calling with output size less than input
+        let (cmd, sub_cmd, request, exp_response) =
+            generate_test_pairs(2 * RESPONSE_LEN, RESPONSE_LEN);
+        set_ree_expected_value(cmd, sub_cmd, request.clone());
+        set_ree_return_value(cmd, sub_cmd, exp_response.clone());
+        let response = plugin
+            .invoke_with_capacity(cmd, sub_cmd, exp_response.len())
+            .chain_write_body(&request)
+            .call()
+            .unwrap();
+        std::println!("*TA*: response is {:?}", response);
+        assert_eq!(response, exp_response);
+
+        // test calling with output size equals to input
+        let (cmd, sub_cmd, request, exp_response) = 
generate_test_pairs(RESPONSE_LEN, RESPONSE_LEN);
+        set_ree_expected_value(cmd, sub_cmd, request.clone());
+        set_ree_return_value(cmd, sub_cmd, exp_response.clone());
+        let response = plugin
+            .invoke_with_capacity(cmd, sub_cmd, exp_response.len())
+            .chain_write_body(&request)
+            .call()
+            .unwrap();
+        std::println!("*TA*: response is {:?}", response);
+        assert_eq!(response, exp_response);
+
+        // test calling with output size greater than input
+        let (cmd, sub_cmd, mut request, exp_response) =
+            generate_test_pairs(RESPONSE_LEN / 2, RESPONSE_LEN);
+        request.resize(exp_response.len(), 0);
+        set_ree_expected_value(cmd, sub_cmd, request.clone());
+        set_ree_return_value(cmd, sub_cmd, exp_response.clone());
+        let response = plugin
+            .invoke_with_capacity(cmd, sub_cmd, exp_response.len())
+            .chain_write_body(&request)
+            .call()
+            .unwrap();
+        std::println!("*TA*: response is {:?}", response);
+        assert_eq!(response, exp_response);
+    }
+    #[test]
+    fn test_invoke_with_writer() {
+        let plugin = LoadablePlugin {
+            uuid: 
Uuid::parse_str("7dd54ee6-a705-4e4d-8b6b-aa5024dfcd10").unwrap(),
+        };
+        // impl a writer for Command
+        struct Wrapper<'a, 'b>(&'b mut LoadablePluginCommand<'a>);
+        impl<'a, 'b> std::io::Write for Wrapper<'a, 'b> {
+            fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+                self.0.write_body(buf);
+                Ok(buf.len())
+            }
+            fn flush(&mut self) -> std::io::Result<()> {
+                Ok(())
+            }
+        }
+        // serialize data into command directly
+        let test_data = serde_json::json!({
+            "code": 100,
+            "message": "error"
+        });
+        let mut exp_request = serde_json::to_vec(&test_data).unwrap();
+        let buffer_len = exp_request.len() * 2;
+        let (cmd, sub_cmd, _, exp_response) = generate_test_pairs(0, 
buffer_len);
+        let mut plugin_cmd = plugin.invoke_with_capacity(cmd, sub_cmd, 
buffer_len);
+        exp_request.resize(exp_response.len(), 0);
+        set_ree_expected_value(cmd, sub_cmd, exp_request);
+        set_ree_return_value(cmd, sub_cmd, exp_response.clone());
+        serde_json::to_writer(Wrapper(&mut plugin_cmd), &test_data).unwrap();
+        let response = plugin_cmd.call().unwrap();
+        std::println!("*TA*: response is {:?}", response);
+        assert_eq!(response, exp_response);
+    }
+    #[test]
+    fn test_invoke_with_no_data() {
+        let plugin = LoadablePlugin {
+            uuid: 
Uuid::parse_str("7dd54ee6-a705-4e4d-8b6b-aa5024dfcd10").unwrap(),
+        };
+        const OUTPUT_LEN: usize = 50;
+        let (cmd, sub_cmd, _, exp_response) = generate_test_pairs(0, 
OUTPUT_LEN);
+        let exp_request = vec![0_u8; OUTPUT_LEN];
+        set_ree_expected_value(cmd, sub_cmd, exp_request);
+        set_ree_return_value(cmd, sub_cmd, exp_response.clone());
+        let response = plugin
+            .invoke_with_capacity(cmd, sub_cmd, OUTPUT_LEN)
+            .call()
+            .unwrap();
+        std::println!("*TA*: response is {:?}", response);
+        assert_eq!(response, exp_response);
     }
 }
diff --git a/optee-utee/systest/Cargo.toml b/optee-utee/systest/Cargo.toml
index d714db5..0fa0ec3 100644
--- a/optee-utee/systest/Cargo.toml
+++ b/optee-utee/systest/Cargo.toml
@@ -26,7 +26,7 @@ edition = "2018"
 
 [dependencies]
 optee-utee-sys = { path = "../optee-utee-sys" }
-libc = "=0.2.59"
+libc = "0.2.59"
 
 [build-dependencies]
 ctest = "0.2"


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to