Skip to content

Commit

Permalink
Doc: add tutorial about how to use optee-utee-build
Browse files Browse the repository at this point in the history
1. add document "Writing Rust TAs using optee-utee-build"
2. suggest developers using optee-utee-build in
   "Migrating to new building env"
  • Loading branch information
ivila committed Dec 23, 2024
1 parent d6a3b7d commit 227a76d
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ permalink: /trustzone-sdk-docs
* [Debugging OP-TEE TA](debugging-optee-ta.md)
* [Expanding TA Secure Memory on QEMUv8](expanding-ta-secure-memory-on-qemuv8.md)
* [Building Rust CA as Android ELF](building-rust-ca-as-android-elf.md)
* [Writing Rust TAs using optee-utee-build](writing-rust-tas-using-optee-utee-build.md)
2 changes: 2 additions & 0 deletions docs/migrating-to-new-building-env.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
permalink: /trustzone-sdk-docs/migrating-to-new-building-env.md
---

> After optee-utee-build release, this doc is keeping for developers who intend to know the detail of building process, we suggest use optee-utee-build for building instead.
## Migration Guide: Moving from `master` to `main` Branch (Post-Oct 2024)

Since the `main` branch (after October 2024) introduces breaking changes
Expand Down
183 changes: 183 additions & 0 deletions docs/writing-rust-tas-using-optee-utee-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
---
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.

# Minimal Example
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`:
```shell
cargo add --build optee-utee-build
```

Secondly, we set a `ta_config` and call `optee-utee-build::build` with it in build.rs:
```rust
use proto;
use optee_utee_build::{TAConfig, Error, RustEdition};

fn main() -> Result<(), Error> {
let ta_config = TAConfig::new_standard_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`
```rust
// 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`](https://github.com/apache/incubator-teaclave-trustzone-sdk/tree/main/examples/hello_world-rs/ta)

# Explaination of Minimal Example

### 1. The TAConfig

This is a struct that use for the configuration of the TA we are developing, it has some public fields:

|Field Name|Description|
|----|----|
|uuid|the identifier of current TA|
|ta_flags|combination of some bitflags, check [this](https://github.com/OP-TEE/optee_os/blob/c2e42a8f03a5bb6b894ef85ae409f54760c1f50e/lib/libutee/include/user_ta_header.h#L13-L53) for available values|
|ta_data_size|defines the size in bytes of the TA allocation pool|
|ta_stack_size|defines the size in bytes of the stack used for TA execution|
|ta_version|a version string of current TA, should be in semver format|
|ta_description|the desciption of current TA|
|trace_level|the default trace level of current TA, check [this](https://github.com/OP-TEE/optee_os/blob/c2e42a8f03a5bb6b894ef85ae409f54760c1f50e/lib/libutils/ext/include/trace_levels.h#L26-L31) for available values|
|trace_ext|an extra prefix string when output trace log|
|ta_framework_stack_size|defines 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 that will be added|

We can construct the `TAConfig` by providing all of the public fields manually, or use our standard constructor.
|Constructor|Description|
|----|----|
|new_standard|construct a standard TAConfig by providing uuid, ta_version and ta_description, with other configurations set to suggested values, you can update those configurations later|
|new_standard_with_cargo_env|it's a constructor wrapped with new_standard, but take `version` and `description` from cargo.toml so simply providing a uuid as parameter is enough|
### 2. The RustEdition

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.

> #### What’s the difference?
> the generated `user_ta_header.rs` file include some const variables and methods tagged with `no_mangle` and `link_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)
# Customization

`optee-utee-build` provide some structs for flexible use.

### 1. Builder
Expect calling `build` function directly, you can also use Builder for customization.

Usage:
```Rust
use proto;
use optee_utee_build::{TAConfig, Builder, Error, RustEdition, LinkType};

fn main() -> Result<(), Error> {
let ta_config = TAConfig::new_standard_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:
|Configuration|Description|
|----|----|
|out_dir|change directory of output files, default to OUT_DIR by cargo|
|header_file_name|change name of output header file. default to `user_ta_header.rs`|
|link_type|set link_type manually. <br/>there are some difference in parameters in linkers between `CC` and `LD` types, for example, `--sort-section` in `CC` 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.|

### 2. Linker
For some developers who want to use a hand-written `user_ta_header.rs` and just want `optee-utee-build` to handle the building stuff only, they can use the `Linker`, otherwise, try `Builder` instead.

Usage:
``` rust
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.
```rust
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(())
}
```

### 3. HeaderFileGenerator
For some developers who want to do the linking themselves and just want `optee-utee-build` to generate a header file only, they can use the `HeaderFileGenerator`, otherwise, try `Builder` instead.

Usage:
```rust
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_standard(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())?)
}

```

# Migration Guide
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\]](https://github.com/apache/incubator-teaclave-trustzone-sdk/blob/main/docs/migrating-to-new-building-env.md)), they can upgrade to `optee-utee-build` by following step:

Firstly, add `optee-utee-build` as `build-dependencies`:
```shell
cargo add --build optee-utee-build
```

Secondly, in `build.rs`, remove codes of `custom build scripts`, and use `optee_utee_build::build` instead:
```rust
// ... 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_standard_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`
```rust
/// ... 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.

0 comments on commit 227a76d

Please sign in to comment.