permalink |
---|
/trustzone-sdk-docs/writing-rust-tas-using-optee-utee-build.md |
Currently we provide a optee-utee-build
crate to simplify the compilcated
building process of TA, and we recommend everyone use it in future developement.
- For legacy app structures migrating to use this crate, refer to Migration Guide
- If you're new to development, start with Minimal Example
- To customize the build process, see Customization
Assuming currently we are developing a hello_world
TA, and we want to build it
with optee-utee-build
crate, we can do it by following steps.
Firstly, we should add optee-utee-build
in build-dependencies
:
cargo add --build optee-utee-build
Secondly, we set a ta_config
and call optee-utee-build::build
with it in
build.rs:
use proto;
use optee_utee_build::{TaConfig, Error, RustEdition};
fn main() -> Result<(), Error> {
let ta_config = TaConfig::new_default_with_cargo_env(proto::UUID)?;
optee_utee_build::build(RustEdition::Before2024, ta_config)
}
It will generate a user_ta_header.rs
file and setup all the required
configurations of the linker of rustc.
Finally, we include the generated user_ta_header.rs
in the source codes,
normally we put it in src/main.rs
.
// src/main.rs
include!(concat!(env!("OUT_DIR"), "/user_ta_header.rs"));
After that, everything finished, we can start building the TA now.
For full codes, you can check the hello_world-rs example
This is a struct that use for the configuration of the TA we are developing, it has some public fields:
- uuid: the identifier of TA.
- ta_flags: combination of some bitflags.
for available values, you may check user_ta_header.h in optee_os - ta_data_size: the size in bytes of the TA allocation pool.
- ta_stack_size: the size in bytes of the stack used for TA execution.
- ta_version: a version string of TA, should be in semver format.
- ta_description: the desciption of TA.
- trace_level: the default trace level of TA.
for available values, you may check trace_levels.h in optee_os - trace_ext: an extra prefix string when output trace log.
- ta_framework_stack_size: the size in bytes of the stack used for Trusted
Core Framework.
currently used for trace framework and invoke command, should not be less than 2048. - ext_properties: the extra custom properties.
We can construct the TaConfig
by providing all of the public fields manually,
or use our standard constructor:
- new_default: construct a default TaConfig by providing uuid, ta_version and ta_description, with other configurations set to suggested values, you can update those configurations later.
- new_default_with_cargo_env: it's a constructor wrapped with new_default,
but take
version
anddescription
from cargo.toml so simply providing a uuid as parameter is enough.
The generated user_ta_header.rs
must be different between edition of 2024
and edition before 2024
, and currently there is no official stable way to know
what edition we are compiling with, so we provide a argument to set with.
the generated
user_ta_header.rs
file include some const variables and global functions tagged withno_mangle
andlink_section
, start from rust edition of 2024, they must be wrapped with unsafe, or rustc will output a compilation error (while before edition of 2024 it must not, or rustc will output a syntax error).
optee-utee-build
provide some structs for flexible use.
Instead of calling the build
function directly, you can use Builder for
customization.
Usage:
use proto;
use optee_utee_build::{TaConfig, Builder, Error, RustEdition, LinkType};
fn main() -> Result<(), Error> {
let ta_config = TaConfig::new_default_with_cargo_env(proto::UUID)?;
Builder::new(RustEdition::Before2024, ta_config)
.out_dir("/tmp")
.header_file_name("my_generated_user_ta_header.rs")
.link_type(LinkType::CC)
.build()
}
As you can see from the codes, there are some customizations of the builder:
- out_dir: change directory of output files.
default to OUT_DIR by cargo. - header_file_name: change name of output header file.
default touser_ta_header.rs
- link_type: set link_type manually.
there are some difference in parameters in linkers betweenCC
andLD
types, for example,--sort-section
inCC
types of linkers changes to-Wl,--sort-section
, we will try to detect current linker that cargo using, you can use this function to set it manually if you think our detection mismatch.
For developers who prefer to use a hand-written user_ta_header.rs
and only
want optee-utee-build
to handle the linking process, they can use the
Linker
, otherwise, try Builder
instead.
Usage:
use optee_utee_build::{Linker, Error};
use std::env;
fn main() -> Result<(), Error> {
let out_dir = env::var("OUT_DIR")?;
Linker::auto().link_all(out_dir)?;
Ok(())
}
When linking manually, developers construct a Linker
and calling the
link_all
method by providing the out_dir, and linker will generate some
required files (link script, etc, used by linker) into out_dir and handle all
the linking stuff.
In above codes, we use auto
to construct the linker, it will detect current
linker that cargo using automatically, you can use new
function to construct
the linker manually if you think our detection mismatch.
use optee_utee_build::{Linker, Error, LinkType};
use std::env;
fn main() -> Result<(), Error> {
let out_dir = env::var("OUT_DIR")?;
Linker::new(LinkerType::CC).link_all(out_dir)?;
Ok(())
}
For developers who prefer to do the linking themselves and only want
optee-utee-build
to generate the header file, they can use the
HeaderFileGenerator
, otherwise, try Builder
instead.
Usage:
use optee_utee_build::{HeaderFileGenerator, TaConfig, RustEdition, Error};
fn main() -> Result<(), Error> {
const UUID: &str = "26509cec-4a2b-4935-87ab-762d89fbf0b0";
let ta_config = TaConfig::new_default(UUID, "0.1.0", "example")?;
let codes = HeaderFileGenerator::new(RustEdition::Before2024).generate(&ta_config)?;
Ok(std::io::Write("/tmp/user_ta_header.rs", codes.as_bytes())?)
}
For developers still using const configuration values
in src/main.rs
and
custom build scripts
in build.rs
(described in [migrating-to-new-building-env]),
they can upgrade to optee-utee-build
by following step:
Firstly, add optee-utee-build
as build-dependencies
:
cargo add --build optee-utee-build
Secondly, in build.rs
, remove codes of custom build scripts
, and use
optee_utee_build::build
instead:
// ... other imports
use optee_utee_build::{TaConfig, Error}
fn main() -> Result<(), Error> {
// should customize the ta_config with the same as const configuration values
// in your src/main.rs
let ta_config = TaConfig::new_default_with_cargo_env(proto::UUID)?
.ta_stack_size(10 * 1024);
optee_utee_build::build(RustEdition::Before2024, ta_config)?;
// ... other build scripts
}
Thirdly, remove const configuration values
in src/main.rs
, keep the line of
include user_ta_header.rs
.
/// ... other codes in src/main.rs
/* remove const configuration values, move them to TaConfig in src/main.rs
// TA configurations
const TA_FLAGS: u32 = 0;
const TA_DATA_SIZE: u32 = 32 * 1024;
const TA_STACK_SIZE: u32 = 2 * 1024;
const TA_VERSION: &[u8] = b"0.1\0";
const TA_DESCRIPTION: &[u8] = b"This is a hello world example.\0";
const EXT_PROP_VALUE_1: &[u8] = b"Hello World TA\0";
const EXT_PROP_VALUE_2: u32 = 0x0010;
const TRACE_LEVEL: i32 = 4;
const TRACE_EXT_PREFIX: &[u8] = b"TA\0";
const TA_FRAMEWORK_STACK_SIZE: u32 = 2048;
*/
include!(concat!(env!("OUT_DIR"), "/user_ta_header.rs")); // keep this line
Finally, delete the useless ta_static.rs
and start building now.