Copilot commented on code in PR #263: URL: https://github.com/apache/teaclave-trustzone-sdk/pull/263#discussion_r2678192952
########## cargo-optee/src/main.rs: ########## @@ -0,0 +1,402 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use anyhow::bail; +use clap::Parser; +use std::env; +use std::path::PathBuf; +use std::process; + +mod ca_builder; +mod cli; +mod common; +mod config; +mod ta_builder; + +use cli::{BuildCommand, Cli, Command, CommonBuildArgs, InstallCommand}; +use config::ComponentType; + +/// Path type for validation +enum PathType { + /// Expects a directory + Directory, + /// Expects a file + File, +} + +fn main() { + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")) + .format_timestamp_millis() + .init(); + + // Setup cargo environment + if let Err(e) = setup_cargo_environment() { + eprintln!("Error: {}", e); + process::exit(1); + } + + // Drop extra `optee` argument provided by `cargo`. + let mut found_optee = false; + let filtered_args: Vec<String> = env::args() + .filter(|x| { + if found_optee { + true + } else { + found_optee = x == "optee"; + x != "optee" + } + }) + .collect(); + + let cli = Cli::parse_from(filtered_args); + let result = execute_command(cli.cmd); + + if let Err(e) = result { + eprintln!("Error: {}", e); + process::exit(1); + } +} + +fn execute_command(cmd: Command) -> anyhow::Result<()> { + match cmd { + Command::Build(build_cmd) => match build_cmd { + BuildCommand::TA { build_cmd } => { + // Convert bool flags to Option<bool>: --std -> Some(true), --no-std -> Some(false), neither -> None + let std_mode = match (build_cmd.std, build_cmd.no_std) { + (true, false) => Some(true), + (false, true) => Some(false), + _ => None, + }; + + execute_ta_command( + build_cmd.common, + std_mode, + build_cmd.ta_dev_kit_dir, + build_cmd.signing_key, + build_cmd.uuid_path, + None, + ) + } + BuildCommand::CA { build_cmd } => execute_ca_command( + build_cmd.common, + build_cmd.optee_client_export, + None, + false, + None, + ), + BuildCommand::Plugin { build_cmd } => execute_ca_command( + build_cmd.common, + build_cmd.optee_client_export, + build_cmd.uuid_path, + true, + None, + ), + }, + Command::Install(install_cmd) => match install_cmd { + InstallCommand::TA { + target_dir, + build_cmd, + } => { + // Convert bool flags to Option<bool>: --std -> Some(true), --no-std -> Some(false), neither -> None + let std = if build_cmd.std { + Some(true) + } else if build_cmd.no_std { + Some(false) + } else { + None + }; + execute_ta_command( + build_cmd.common, + std, + build_cmd.ta_dev_kit_dir, + build_cmd.signing_key, + build_cmd.uuid_path, + Some(&target_dir), + ) + } + InstallCommand::CA { + target_dir, + build_cmd, + } => execute_ca_command( + build_cmd.common, + build_cmd.optee_client_export, + None, + false, + Some(&target_dir), + ), + InstallCommand::Plugin { + target_dir, + build_cmd, + } => execute_ca_command( + build_cmd.common, + build_cmd.optee_client_export, + build_cmd.uuid_path, + true, + Some(&target_dir), + ), + }, + Command::Clean { clean_cmd } => { + let project_path = resolve_project_path(clean_cmd.manifest_path.as_ref())?; + + // Clean build artifacts using the common function + crate::common::clean_project(&project_path) + } + } +} + +/// Execute TA build or install (shared logic) +fn execute_ta_command( + common: CommonBuildArgs, + std: Option<bool>, + ta_dev_kit_dir: Option<PathBuf>, + signing_key: Option<PathBuf>, + uuid_path: Option<PathBuf>, + install_target_dir: Option<&PathBuf>, +) -> anyhow::Result<()> { + // Resolve project path from manifest or current directory + let project_path = resolve_project_path(common.manifest_path.as_ref())?; + + // Resolve build configuration with priority: CLI > metadata > error + let build_config = config::BuildConfig::resolve( + &project_path, + ComponentType::Ta, + common.arch, + Some(common.debug), + std, // None means read from config, Some(true/false) means CLI override + ta_dev_kit_dir, + None, // optee_client_export not needed for TA + signing_key, + uuid_path.clone(), + )?; + + // Print the final configuration being used + build_config.print_config(ComponentType::Ta, &project_path); + + // Get required ta_dev_kit_dir and resolve relative to project + let ta_dev_kit_dir_config = build_config.require_ta_dev_kit_dir()?; + let ta_dev_kit_dir = resolve_path_relative_to_project( + &ta_dev_kit_dir_config, + &project_path, + PathType::Directory, + "TA development kit directory", + )?; + + // Resolve signing key relative to project directory + let signing_key_config = build_config.resolve_signing_key(&ta_dev_kit_dir_config); + let signing_key_path = resolve_path_relative_to_project( + &signing_key_config, + &project_path, + PathType::File, + "Signing key file", + )?; + + // Resolve UUID path: if provided via CLI, it's relative to current dir + // if from metadata, it's relative to project dir + let resolved_uuid_path = if uuid_path.is_some() { + // CLI provided - resolve relative to current directory + std::env::current_dir()?.join(build_config.get_uuid_path()) + } else { + // From metadata or default - resolve relative to project directory + project_path.join(build_config.get_uuid_path()) + }; + + // Merge env variables: CLI overrides + metadata env + let mut merged_env = build_config.env.clone(); + merged_env.extend(common.env); + + let ta_config = ta_builder::TaBuildConfig { + arch: build_config.arch, + std: build_config.std, + ta_dev_kit_dir, + signing_key: signing_key_path, + debug: build_config.debug, + path: project_path, + uuid_path: resolved_uuid_path, + env: merged_env, + no_default_features: common.no_default_features, + features: common.features, + }; + + ta_builder::build_ta(ta_config, install_target_dir.map(|p| p.as_path())) +} + +/// Execute CA build or install (shared logic) +fn execute_ca_command( + common: CommonBuildArgs, + optee_client_export: Option<PathBuf>, + uuid_path: Option<PathBuf>, + plugin: bool, + install_target_dir: Option<&PathBuf>, +) -> anyhow::Result<()> { + // Resolve project path from manifest or current directory + let project_path = resolve_project_path(common.manifest_path.as_ref())?; + + let component_type = if plugin { + ComponentType::Plugin + } else { + ComponentType::Ca + }; + + // Resolve build configuration + let build_config = config::BuildConfig::resolve( + &project_path, + component_type, + common.arch, + Some(common.debug), + None, // std not applicable for CA/Plugin + None, // ta_dev_kit_dir not needed for CA/Plugin + optee_client_export, + None, // signing_key not needed for CA/Plugin + uuid_path, + )?; + + // Print the final configuration being used + build_config.print_config(component_type, &project_path); + + // Get required optee_client_export and resolve relative to project + let optee_client_export_config = build_config.require_optee_client_export()?; + let optee_client_export = resolve_path_relative_to_project( + &optee_client_export_config, + &project_path, + PathType::Directory, + "OP-TEE client export directory", + )?; + + // Merge env variables: CLI overrides + metadata env + let mut merged_env = build_config.env.clone(); + merged_env.extend(common.env); + + let ca_config = ca_builder::CaBuildConfig { + arch: build_config.arch, + optee_client_export, + debug: build_config.debug, + path: project_path, + plugin, + uuid_path: if plugin { + build_config.uuid_path.clone() + } else { + None + }, + env: merged_env, + no_default_features: common.no_default_features, + features: common.features, + }; + + ca_builder::build_ca(ca_config, install_target_dir.map(|p| p.as_path())) +} + +/// Resolve project path from manifest path or current directory +fn resolve_project_path(manifest_path: Option<&PathBuf>) -> anyhow::Result<PathBuf> { + if let Some(manifest) = manifest_path { + let parent = manifest + .parent() + .ok_or_else(|| anyhow::anyhow!("Invalid manifest path"))?; + + // Normalize: if parent is empty (e.g., manifest is just "Cargo.toml"), + // use current directory instead + if parent.as_os_str().is_empty() { + std::env::current_dir().map_err(Into::into) + } else { + // Canonicalize must succeed, otherwise treat as invalid manifest path + parent + .canonicalize() + .map_err(|_| anyhow::anyhow!("Invalid manifest path")) + } + } else { + std::env::current_dir().map_err(Into::into) + } +} + +/// Resolve a potentially relative path to an absolute path based on the project directory +/// and validate that it exists +fn resolve_path_relative_to_project( + path: &PathBuf, + project_path: &std::path::Path, + path_type: PathType, + error_context: &str, +) -> anyhow::Result<PathBuf> { + let resolved_path = if path.is_absolute() { + path.clone() + } else { + project_path.join(path) + }; + + // Validate that the path exists + if !resolved_path.exists() { + bail!("{} does not exist: {:?}", error_context, resolved_path); + } + + // Additional validation: check if it's actually a directory or file as expected + match path_type { + PathType::Directory => { + if !resolved_path.is_dir() { + bail!("{} is not a directory: {:?}", error_context, resolved_path); + } + } + PathType::File => { + if !resolved_path.is_file() { + bail!("{} is not a file: {:?}", error_context, resolved_path); + } + } + } + + Ok(resolved_path) +} + +/// Setup cargo environment by checking availability and sourcing environment if needed +fn setup_cargo_environment() -> anyhow::Result<()> { + // Check if cargo is available + let cargo_available = std::process::Command::new("which") + .arg("cargo") + .output() + .is_ok_and(|output| output.status.success()); + + if cargo_available { + return Ok(()); + } + + // Try to source .cargo/env from ~/.cargo/env or $CARGO_HOME/env + let mut sourced = false; + if let Ok(home) = env::var("HOME") { + sourced = source_cargo_env(&format!("{}/.cargo/env", home)); + } + if !sourced { + if let Ok(cargo_home) = env::var("CARGO_HOME") { + sourced = source_cargo_env(&format!("{}/env", cargo_home)); + } + } + + if !sourced { + anyhow::bail!("cargo command not found. Please install Rust: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"); + } + + Ok(()) +} + +/// Source cargo environment from a given path +fn source_cargo_env(env_path: &str) -> bool { + if std::path::Path::new(env_path).exists() { + std::process::Command::new("bash") + .arg("-c") + .arg(format!("source {}", env_path)) Review Comment: The `source_cargo_env` function has a command injection vulnerability. The `env_path` parameter is directly interpolated into a bash command without proper escaping. If a user's HOME or CARGO_HOME environment variable contains special shell characters (e.g., `;`, `$()`, backticks), this could lead to arbitrary command execution. Use proper command argument passing or escape the path before using it in a shell command. ```suggestion .arg("source \"$1\"") .arg("bash") .arg(env_path) ``` ########## ci/build.sh: ########## @@ -0,0 +1,435 @@ +#!/bin/bash + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +set -e + +# Show usage +show_usage() { + cat << EOF +Usage: TA_DEV_KIT_DIR=<path> OPTEE_CLIENT_EXPORT=<path> $0 [OPTIONS] + +Required environment variables: + TA_DEV_KIT_DIR Path to OP-TEE OS TA dev kit directory + OPTEE_CLIENT_EXPORT Path to OP-TEE client export directory + +Options: + --ta <arch> TA architecture: aarch64 or arm (default: aarch64) + --host <arch> Host architecture for CA and plugins: aarch64 or arm (default: aarch64) + --std Install with std support (default: no-std) + --ta-install-dir <path> TA installation directory (default: ./tests/shared) + --ca-install-dir <path> CA installation directory (default: ./tests/shared) + --plugin-install-dir <path> Plugin installation directory (default: ./tests/shared) + --help Show this help message + +Examples: + # Install for aarch64 in no-std mode + TA_DEV_KIT_DIR=/path/to/export-ta_arm64 OPTEE_CLIENT_EXPORT=/path/to/export ./build.sh + + # Install for ARM32 in std mode with custom directories + TA_DEV_KIT_DIR=/path/to/export-ta_arm32 OPTEE_CLIENT_EXPORT=/path/to/export ./build.sh --ta arm --host arm --std --ta-install-dir /target/lib/optee_armtz --ca-install-dir /target/usr/bin + +Note: Binaries are installed to './tests/shared' directory by default. +EOF +} + +# Parse command line arguments +ARCH_TA="aarch64" # Default: aarch64 +ARCH_HOST="aarch64" # Default: aarch64 +STD="" # Default: empty (no-std) +TA_INSTALL_DIR="" # Default: will be set to ./tests/shared if not specified +CA_INSTALL_DIR="" # Default: will be set to ./tests/shared if not specified +PLUGIN_INSTALL_DIR="" # Default: will be set to ./tests/shared if not specified + +# Parse arguments (support both positional and flag-style) +while [[ $# -gt 0 ]]; do + case "$1" in + --help|-h) + show_usage + exit 0 + ;; + --ta) + ARCH_TA="$2" + shift 2 + ;; + --host) + ARCH_HOST="$2" + shift 2 + ;; + --std) + STD="std" + shift + ;; + --ta-install-dir) + TA_INSTALL_DIR="$2" + shift 2 + ;; + --ca-install-dir) + CA_INSTALL_DIR="$2" + shift 2 + ;; + --plugin-install-dir) + PLUGIN_INSTALL_DIR="$2" + shift 2 + ;; + *) + # Positional arguments (backward compatibility) + if [[ -z "${ARCH_TA_SET:-}" ]]; then + ARCH_TA="$1" + ARCH_TA_SET=1 + elif [[ -z "${ARCH_HOST_SET:-}" ]]; then + ARCH_HOST="$1" + ARCH_HOST_SET=1 + elif [[ "$1" == "std" ]]; then + STD="std" + fi + shift + ;; + esac +done + +# Validate architecture +if [[ "$ARCH_TA" != "aarch64" && "$ARCH_TA" != "arm" ]]; then + echo "Error: ARCH_TA must be 'aarch64' or 'arm'" + exit 1 +fi + +if [[ "$ARCH_HOST" != "aarch64" && "$ARCH_HOST" != "arm" ]]; then + echo "Error: ARCH_HOST must be 'aarch64' or 'arm'" + exit 1 +fi + +# Check required environment variables +if [ -z "$TA_DEV_KIT_DIR" ]; then + echo "Error: TA_DEV_KIT_DIR environment variable is not set" + exit 1 +fi + +if [ -z "$OPTEE_CLIENT_EXPORT" ]; then + echo "Error: OPTEE_CLIENT_EXPORT environment variable is not set" + exit 1 +fi + +echo "===========================================" +echo "Installing with configuration:" +echo " ARCH_TA: $ARCH_TA" +echo " ARCH_HOST: $ARCH_HOST" +echo " STD: ${STD:-no-std}" +echo " TA_DEV_KIT_DIR: $TA_DEV_KIT_DIR" +echo " OPTEE_CLIENT_EXPORT: $OPTEE_CLIENT_EXPORT" +echo "===========================================" + +# Step 1: Build cargo-optee tool +echo "" +echo "Step 1: Building cargo-optee tool..." +cd cargo-optee +cargo build --release +CARGO_OPTEE="$(pwd)/target/release/cargo-optee" +cd .. + +if [ ! -f "$CARGO_OPTEE" ]; then + echo "Error: Failed to build cargo-optee" + exit 1 +fi + +echo "cargo-optee built successfully: $CARGO_OPTEE" + +# Prepare std flag for cargo-optee +STD_FLAG="" +if [ -n "$STD" ]; then + STD_FLAG="--std" +fi + +# Step 2: Install all examples to shared directory +echo "" +echo "Step 2: Installing all examples..." + +# Set up installation directories +# Each directory defaults to ./tests/shared if not specified +if [ -z "$TA_INSTALL_DIR" ]; then + TA_INSTALL_DIR="$(pwd)/tests/shared" +fi + +if [ -z "$CA_INSTALL_DIR" ]; then + CA_INSTALL_DIR="$(pwd)/tests/shared" +fi + +if [ -z "$PLUGIN_INSTALL_DIR" ]; then + PLUGIN_INSTALL_DIR="$(pwd)/tests/shared" +fi + +# Create all directories +mkdir -p "$TA_INSTALL_DIR" +mkdir -p "$CA_INSTALL_DIR" +mkdir -p "$PLUGIN_INSTALL_DIR" + +echo "Installing binaries to:" +echo " TAs: $TA_INSTALL_DIR" +echo " CAs: $CA_INSTALL_DIR" +echo " Plugins: $PLUGIN_INSTALL_DIR" + +EXAMPLES_DIR="$(pwd)/examples" +METADATA_JSON="$EXAMPLES_DIR/metadata.json" + +if [ ! -f "$METADATA_JSON" ]; then + echo "Error: $METADATA_JSON not found" + exit 1 +fi + +# Check if jq is available for JSON parsing +if ! command -v jq &> /dev/null; then + echo "Error: jq is required to parse metadata.json" + echo "Please install jq: apt-get install jq" + exit 1 +fi + +echo "Loading example metadata from $METADATA_JSON..." + +# Get all example names +ALL_EXAMPLES=($(jq -r '.examples | keys[]' "$METADATA_JSON")) + +if [ -n "$STD" ]; then + echo "Building in STD mode (std-only + common examples)" +else + echo "Building in NO-STD mode (no-std-only + common examples)" +fi + +CURRENT=0 +FAILED_EXAMPLES="" + +# Build examples +for EXAMPLE_NAME in "${ALL_EXAMPLES[@]}"; do + CATEGORY=$(jq -r ".examples[\"$EXAMPLE_NAME\"].category" "$METADATA_JSON") + + # Determine if we should build this example + SHOULD_BUILD=false + if [ -n "$STD" ]; then + # STD mode: build std-only and common + if [[ "$CATEGORY" == "std-only" || "$CATEGORY" == "common" ]]; then + SHOULD_BUILD=true + fi + else + # NO-STD mode: build no-std-only and common + if [[ "$CATEGORY" == "no-std-only" || "$CATEGORY" == "common" ]]; then + SHOULD_BUILD=true + fi + fi + + if [ "$SHOULD_BUILD" = false ]; then + continue + fi + + CURRENT=$((CURRENT + 1)) + EXAMPLE_DIR="$EXAMPLES_DIR/$EXAMPLE_NAME" + + if [ ! -d "$EXAMPLE_DIR" ]; then + echo "ERROR: Example directory not found: $EXAMPLE_DIR" + FAILED_EXAMPLES="$FAILED_EXAMPLES\n - $EXAMPLE_NAME" + continue + fi + + echo "" + echo "==========================================" + echo "[$CURRENT] Building: $EXAMPLE_NAME ($CATEGORY)" + echo "==========================================" + + # Get TA, CA, and Plugin directories from metadata + TAS_JSON=$(jq -c ".examples[\"$EXAMPLE_NAME\"].tas" "$METADATA_JSON") + CAS_JSON=$(jq -c ".examples[\"$EXAMPLE_NAME\"].cas" "$METADATA_JSON") + PLUGINS_JSON=$(jq -c ".examples[\"$EXAMPLE_NAME\"].plugins // []" "$METADATA_JSON") + + # Build all TAs for this example + TA_COUNT=$(echo "$TAS_JSON" | jq 'length') + CA_COUNT=$(echo "$CAS_JSON" | jq 'length') + PLUGIN_COUNT=$(echo "$PLUGINS_JSON" | jq 'length') + + echo "→ Found $TA_COUNT TA(s), $CA_COUNT CA(s), and $PLUGIN_COUNT Plugin(s)" + + if [ "$TA_COUNT" -gt 0 ]; then + for ((i=0; i<$TA_COUNT; i++)); do + TA_DIR=$(echo "$TAS_JSON" | jq -r ".[$i]") + TA_DIR_FULL_PATH="$EXAMPLES_DIR/$TA_DIR" + + if [ ! -d "$TA_DIR_FULL_PATH" ]; then + echo "ERROR: TA directory not found: $TA_DIR_FULL_PATH" + FAILED_EXAMPLES="$FAILED_EXAMPLES\n - $EXAMPLE_NAME ($TA_DIR)" + continue + fi + + if [ ! -f "$TA_DIR_FULL_PATH/Cargo.toml" ]; then + echo "ERROR: Cargo.toml not found in TA directory: $TA_DIR_FULL_PATH" + FAILED_EXAMPLES="$FAILED_EXAMPLES\n - $EXAMPLE_NAME ($TA_DIR)" + continue + fi + + echo "" + echo "→ Building TA [$((i+1))/$TA_COUNT]: $TA_DIR" + + # Determine STD_FLAG for TA + TA_STD_FLAG="" + if [ -n "$STD" ]; then + # In std mode: always pass --std flag to cargo-optee + TA_STD_FLAG="--std" + fi + + # Change to TA directory and run cargo-optee without --manifest-path + cd "$TA_DIR_FULL_PATH" + + # Run cargo-optee install and capture both stdout and stderr + if $CARGO_OPTEE install ta \ + --target-dir "$TA_INSTALL_DIR" \ + --ta-dev-kit-dir "$TA_DEV_KIT_DIR" \ + --arch "$ARCH_TA" \ + $TA_STD_FLAG; then + echo " ✓ TA installed successfully" + # Clean up build artifacts + $CARGO_OPTEE clean + else + echo " ✗ ERROR: Failed to install TA: $TA_DIR" + FAILED_EXAMPLES="$FAILED_EXAMPLES\n - $EXAMPLE_NAME ($TA_DIR)" + cd "$EXAMPLES_DIR" # Return to examples directory + continue + fi + + # Return to examples directory + cd "$EXAMPLES_DIR" + done + else + echo "WARNING: No TAs defined for $EXAMPLE_NAME" + fi + + # Build each CA + CA_INDEX=0 + while [[ "$CA_INDEX" -lt "$CA_COUNT" ]]; do + CA_DIR=$(echo "$CAS_JSON" | jq -r ".[$CA_INDEX]") + CA_DIR_FULL_PATH="$EXAMPLES_DIR/$CA_DIR" + + echo "" + echo "→ Building CA [$((CA_INDEX+1))/$CA_COUNT]: $CA_DIR" + + if [ ! -d "$CA_DIR_FULL_PATH" ]; then + echo "ERROR: CA directory not found: $CA_DIR_FULL_PATH" + FAILED_EXAMPLES="$FAILED_EXAMPLES\n - $EXAMPLE_NAME ($CA_DIR)" + CA_INDEX=$((CA_INDEX + 1)) + continue + fi + + if [ ! -f "$CA_DIR_FULL_PATH/Cargo.toml" ]; then + echo "ERROR: Cargo.toml not found in CA directory: $CA_DIR_FULL_PATH" + FAILED_EXAMPLES="$FAILED_EXAMPLES\n - $EXAMPLE_NAME ($CA_DIR)" + CA_INDEX=$((CA_INDEX + 1)) + continue + fi + + # Change to CA directory and run cargo-optee without --manifest-path + cd "$CA_DIR_FULL_PATH" + + if $CARGO_OPTEE install ca \ + --target-dir "$CA_INSTALL_DIR" \ + --optee-client-export "$OPTEE_CLIENT_EXPORT" \ + --arch "$ARCH_HOST"; then + echo " ✓ CA installed successfully" + # Clean up build artifacts + $CARGO_OPTEE clean + else + echo " ✗ ERROR: Failed to install CA: $CA_DIR" + FAILED_EXAMPLES="$FAILED_EXAMPLES\n - $EXAMPLE_NAME ($CA_DIR)" + cd "$EXAMPLES_DIR" # Return to examples directory + CA_INDEX=$((CA_INDEX + 1)) + continue + fi + + # Return to examples directory + cd "$EXAMPLES_DIR" + CA_INDEX=$((CA_INDEX + 1)) + done + + # Build each Plugin + PLUGIN_INDEX=0 + while [[ "$PLUGIN_INDEX" -lt "$PLUGIN_COUNT" ]]; do + PLUGIN_DIR=$(echo "$PLUGINS_JSON" | jq -r ".[$PLUGIN_INDEX]") + PLUGIN_DIR_FULL_PATH="$EXAMPLES_DIR/$PLUGIN_DIR" + + echo "" + echo "→ Building Plugin [$((PLUGIN_INDEX+1))/$PLUGIN_COUNT]: $PLUGIN_DIR" + + if [ ! -d "$PLUGIN_DIR_FULL_PATH" ]; then + echo "ERROR: Plugin directory not found: $PLUGIN_DIR_FULL_PATH" + FAILED_EXAMPLES="$FAILED_EXAMPLES\n - $EXAMPLE_NAME ($PLUGIN_DIR)" + PLUGIN_INDEX=$((PLUGIN_INDEX + 1)) + continue + fi + + if [ ! -f "$PLUGIN_DIR_FULL_PATH/Cargo.toml" ]; then + echo "ERROR: Cargo.toml not found in Plugin directory: $PLUGIN_DIR_FULL_PATH" + FAILED_EXAMPLES="$FAILED_EXAMPLES\n - $EXAMPLE_NAME ($PLUGIN_DIR)" + PLUGIN_INDEX=$((PLUGIN_INDEX + 1)) + continue + fi + + # Change to Plugin directory and run cargo-optee without --manifest-path + cd "$PLUGIN_DIR_FULL_PATH" + + if $CARGO_OPTEE install plugin \ + --target-dir "$PLUGIN_INSTALL_DIR" \ + --optee-client-export "$OPTEE_CLIENT_EXPORT" \ + --arch "$ARCH_HOST"; then + echo " ✓ Plugin installed successfully" + # Clean up build artifacts + $CARGO_OPTEE clean + else + echo " ✗ ERROR: Failed to install Plugin: $PLUGIN_DIR" + FAILED_EXAMPLES="$FAILED_EXAMPLES\n - $EXAMPLE_NAME ($PLUGIN_DIR)" + cd "$EXAMPLES_DIR" # Return to examples directory + PLUGIN_INDEX=$((PLUGIN_INDEX + 1)) + continue + fi + + # Return to examples directory + cd "$EXAMPLES_DIR" + PLUGIN_INDEX=$((PLUGIN_INDEX + 1)) + done + + echo "" + echo "✓ Example $EXAMPLE_NAME completed successfully" +done + +# Summary +echo "" +echo "===========================================" +echo " INSTALL SUMMARY" +echo "===========================================" +echo "" +echo "Mode: ${STD:-no-std}" +echo "Architecture: TA=$ARCH_TA, CA=$ARCH_CA" Review Comment: The variable `ARCH_CA` is used in the summary output but is never defined in the script. The script uses `ARCH_HOST` for the CA/Plugin architecture. This will result in an empty or undefined variable being printed in the summary. ```suggestion echo "Architecture: TA=$ARCH_TA, CA=$ARCH_HOST" ``` ########## docs/emulate-and-dev-in-docker-std.md: ########## @@ -7,7 +7,8 @@ permalink: /trustzone-sdk-docs/emulate-and-dev-in-docker-std.md This guide covers the **dev-env with std support** that enables **developing TA using Rust standard library (std)**, compared to the regular no-std environment documented in [emulate-and-dev-in-docker.md](emulate-and-dev-in-docker.md). -The **dev-env with std support** provides a complete setup for building TAs that can use Rust's standard library features like collections, networking, etc. +The **dev-env with std support** provides a complete setup for building TAs that can use Rust's standard library +features like collections, networking, etc. Review Comment: Inconsistent line breaking pattern. Lines 10-11 split a sentence across lines while line 38 keeps "no-std-only" intact. Consider consistent line breaking: either break after complete phrases or keep related terms together. ```suggestion The **dev-env with std support** provides a complete setup for building TAs that can use Rust's standard library features like collections, networking, etc. ``` ########## cargo-optee/aarch64-unknown-optee.json: ########## @@ -0,0 +1,21 @@ +{ + "arch": "aarch64", + "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", + "features": "+strict-align", + "dynamic-linking": false, + "executables": true, + "has-rpath": true, + "linker-flavor": "ld", + "linker-is-gnu": true, + "llvm-target": "aarch64-unknown-linux-gnu", + "max-atomic-width": 128, + "os": "optee", + "position-independent-executables": true, + "relro-level": "full", + "target-c-int-width": "32", + "target-endian": "little", + "target-pointer-width": "64", + "vendor": "unknown", + "panic-strategy": "abort" +} + Review Comment: There is trailing whitespace at the end of this JSON file. While this doesn't affect functionality, it's inconsistent with the ARM target JSON file which has no trailing whitespace. ```suggestion } ``` ########## examples/tls_server-rs/ta/Cargo.toml: ########## @@ -45,3 +49,11 @@ optee-utee-build = { path = "../../../optee-utee-build" } panic = "abort" lto = false opt-level = 3 + +[package.metadata.optee.ta] +arch = "aarch64" +debug = false +std = true +uuid-path = "../uuid.txt" +ta-dev-kit-dir = { aarch64 = "/opt/teaclave/optee/optee_os/out/arm-plat-vexpress/export-ta_arm64", arm = "/opt/teaclave/optee/optee_os/out/arm-plat-vexpress/export-ta_arm32" } +signing-key = "/opt/teaclave/optee/optee_os/out/arm-plat-vexpress/export-ta_arm64/keys/default_ta.pem" Review Comment: Hardcoded absolute paths in the metadata configuration may not be portable across different development environments. While this is intended as example configuration, consider documenting that these paths should be adjusted for each developer's environment, or consider using environment variables like `${OPTEE_OS_DIR}` if supported by the configuration system. ########## cargo-optee/src/config.rs: ########## @@ -0,0 +1,690 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use anyhow::{bail, Result}; +use cargo_metadata::MetadataCommand; +use serde_json::Value; +use std::path::{Path, PathBuf}; + +use crate::common::Arch; + +/// Component type for OP-TEE builds +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ComponentType { + /// Trusted Application (TA) + Ta, + /// Client Application (CA) + Ca, + /// Plugin (Shared Library) + Plugin, +} + +impl ComponentType { + /// Convert to string for metadata lookup in Cargo.toml + pub fn as_str(&self) -> &'static str { + match self { + ComponentType::Ta => "ta", + ComponentType::Ca => "ca", + ComponentType::Plugin => "plugin", + } + } + + /// Convert to display name (capitalized) + pub fn display_name(&self) -> &'static str { + match self { + ComponentType::Ta => "TA", + ComponentType::Ca => "CA", + ComponentType::Plugin => "Plugin", + } + } + + /// Parse from string (for metadata reading) + /// This may be useful for future extensions or dynamic component type parsing + #[allow(dead_code)] + pub fn from_str(s: &str) -> Result<Self> { + match s { + "ta" => Ok(ComponentType::Ta), + "ca" => Ok(ComponentType::Ca), + "plugin" => Ok(ComponentType::Plugin), + _ => Err(anyhow::anyhow!("Invalid component type: {}", s)), + } + } +} + +/// Build configuration that can be discovered from proto metadata +#[derive(Debug, Clone)] +pub struct BuildConfig { + pub arch: Arch, + pub debug: bool, + pub std: bool, + pub ta_dev_kit_dir: Option<PathBuf>, + pub optee_client_export: Option<PathBuf>, + pub signing_key: Option<PathBuf>, + pub uuid_path: Option<PathBuf>, + /// additional environment key-value pairs, that should be passed to underlying + /// build commands + pub env: Vec<(String, String)>, +} + +impl BuildConfig { + /// Create a new build config by resolving parameters with priority: + /// 1. Command line arguments (highest priority) + /// 2. [package.metadata.optee.<component_type>] in Cargo.toml + /// 3. Default values or error for mandatory parameters + #[allow(clippy::too_many_arguments)] + pub fn resolve( + project_path: &Path, + component_type: ComponentType, + cmd_arch: Option<Arch>, + cmd_debug: Option<bool>, + cmd_std: Option<bool>, + cmd_ta_dev_kit_dir: Option<PathBuf>, + cmd_optee_client_export: Option<PathBuf>, + cmd_signing_key: Option<PathBuf>, + cmd_uuid_path: Option<PathBuf>, + ) -> Result<Self> { + // Try to find application metadata (optional) + let app_metadata = discover_app_metadata(project_path).ok(); + + // Try to get metadata config if available + let metadata_config = app_metadata + .as_ref() + .and_then(|meta| extract_build_config_from_metadata(meta, component_type).ok()); + + // Resolve architecture with priority: CLI > metadata > default + let arch = cmd_arch + .or_else(|| metadata_config.as_ref().map(|m| m.arch)) + .unwrap_or(Arch::Aarch64); + + // Re-resolve metadata with the final architecture if it was overridden + let final_metadata_config = if let Some(ref app_meta) = app_metadata { + if cmd_arch.is_some() && cmd_arch != metadata_config.as_ref().map(|m| m.arch) { + extract_build_config_with_arch(app_meta, arch, component_type).ok() + } else { + metadata_config + } + } else { + None + }; + + // Resolve parameters with priority: CLI > metadata > default + let debug = cmd_debug + .or_else(|| final_metadata_config.as_ref().map(|m| m.debug)) + .unwrap_or(false); + + let std = cmd_std + .or_else(|| final_metadata_config.as_ref().map(|m| m.std)) + .unwrap_or(false); + + // Resolve library paths with priority: CLI > metadata > None + let ta_dev_kit_dir = cmd_ta_dev_kit_dir.or_else(|| { + final_metadata_config + .as_ref() + .and_then(|m| m.ta_dev_kit_dir.clone()) + }); + + let optee_client_export = cmd_optee_client_export.or_else(|| { + final_metadata_config + .as_ref() + .and_then(|m| m.optee_client_export.clone()) + }); + + let signing_key = cmd_signing_key.or_else(|| { + final_metadata_config + .as_ref() + .and_then(|m| m.signing_key.clone()) + }); + + // Resolve uuid_path with priority: CLI > Cargo.toml metadata > default (../uuid.txt) + let uuid_path = cmd_uuid_path + .or_else(|| { + // Try to read uuid_path from package metadata + app_metadata + .as_ref() + .and_then(|meta| extract_uuid_path_from_metadata(meta).ok()) + }) + .unwrap_or_else(|| PathBuf::from("../uuid.txt")); + + Ok(BuildConfig { + arch, + debug, + std, + ta_dev_kit_dir, + optee_client_export, + signing_key, + uuid_path: Some(uuid_path), + env: final_metadata_config + .as_ref() + .map(|m| m.env.clone()) + .unwrap_or_default(), + }) + } + + /// Print the final configuration parameters being used + pub fn print_config(&self, component_type: ComponentType, project_path: &Path) { + println!("Building {} with:", component_type.display_name()); + println!(" Arch: {:?}", self.arch); + println!(" Debug: {}", self.debug); + + if component_type == ComponentType::Ta { + println!(" Std: {}", self.std); + if let Some(ref ta_dev_kit_dir) = self.ta_dev_kit_dir { + let absolute_ta_dev_kit_dir = if ta_dev_kit_dir.is_absolute() { + ta_dev_kit_dir.clone() + } else { + project_path.join(ta_dev_kit_dir) + }; + println!(" TA dev kit dir: {:?}", absolute_ta_dev_kit_dir); + } + if let Some(ref signing_key) = self.signing_key { + let absolute_signing_key = if signing_key.is_absolute() { + signing_key.clone() + } else { + project_path.join(signing_key) + }; + println!(" Signing key: {:?}", absolute_signing_key); + } + if let Some(ref uuid_path) = self.uuid_path { + let absolute_uuid_path = project_path + .join(uuid_path) + .canonicalize() + .unwrap_or_else(|_| project_path.join(uuid_path)); + println!(" UUID path: {:?}", absolute_uuid_path); + } + } + + if component_type == ComponentType::Ca || component_type == ComponentType::Plugin { + if let Some(ref optee_client_export) = self.optee_client_export { + let absolute_optee_client_export = if optee_client_export.is_absolute() { + optee_client_export.clone() + } else { + project_path.join(optee_client_export) + }; + println!(" OP-TEE client export: {:?}", absolute_optee_client_export); + } + if component_type == ComponentType::Plugin { + if let Some(ref uuid_path) = self.uuid_path { + let absolute_uuid_path = project_path + .join(uuid_path) + .canonicalize() + .unwrap_or_else(|_| project_path.join(uuid_path)); + println!(" UUID path: {:?}", absolute_uuid_path); + } + } + } + if !self.env.is_empty() { + println!(" Environment variables: {} set", self.env.len()); + } + } + + /// Get required ta_dev_kit_dir or return error + pub fn require_ta_dev_kit_dir(&self) -> Result<PathBuf> { + self.ta_dev_kit_dir + .clone() + .ok_or_else(|| anyhow::anyhow!( + "ta-dev-kit-dir is MANDATORY but not configured.\n\ + Please set it via:\n\ + 1. Command line: --ta-dev-kit-dir <path>\n\ + 2. Cargo.toml metadata: [package.metadata.optee.ta] section\n\ + \n\ + Example Cargo.toml:\n\ + [package.metadata.optee.ta]\n\ + ta-dev-kit-dir = {{ aarch64 = \"/path/to/optee_os/out/arm-plat-vexpress/export-ta_arm64\" }}\n\ + # arm architecture omitted (defaults to null)\n\ + \n\ + For help with available options, run: cargo-optee build ta --help" + )) + } + + /// Get required optee_client_export or return error + pub fn require_optee_client_export(&self) -> Result<PathBuf> { + self.optee_client_export + .clone() + .ok_or_else(|| anyhow::anyhow!( + "optee-client-export is MANDATORY but not configured.\n\ + Please set it via:\n\ + 1. Command line: --optee-client-export <path>\n\ + 2. Cargo.toml metadata: [package.metadata.optee.ca] or [package.metadata.optee.plugin] section\n\ + \n\ + Example Cargo.toml:\n\ + [package.metadata.optee.ca]\n\ + optee-client-export = {{ aarch64 = \"/path/to/optee_client/export_arm64\" }}\n\ + # arm architecture omitted (defaults to null)\n\ + \n\ + For help with available options, run: cargo-optee build ca --help" + )) + } + + /// Get uuid_path (defaults to "../uuid.txt" if not specified) + pub fn get_uuid_path(&self) -> PathBuf { + self.uuid_path + .clone() + .unwrap_or_else(|| PathBuf::from("../uuid.txt")) + } + + /// Get signing key with fallback to default + pub fn resolve_signing_key(&self, ta_dev_kit_dir: &Path) -> PathBuf { + self.signing_key + .clone() + .unwrap_or_else(|| ta_dev_kit_dir.join("keys").join("default_ta.pem")) + } +} + +/// Discover application metadata from the current project +fn discover_app_metadata(project_path: &Path) -> Result<Value> { + let cargo_toml_path = project_path.join("Cargo.toml"); + if !cargo_toml_path.exists() { + bail!( + "Cargo.toml not found in project directory: {:?}", + project_path + ); + } + + // Get metadata for the current project + let metadata = MetadataCommand::new() + .manifest_path(&cargo_toml_path) + .no_deps() + .exec()?; + + // Find the current project package + // First try to get root package (for non-workspace projects) + let current_package = if let Some(root_pkg) = metadata.root_package() { + root_pkg + } else { + // For workspace projects, find the package that corresponds to this manifest + let cargo_toml_path_str = cargo_toml_path.to_string_lossy(); + metadata + .packages + .iter() + .find(|pkg| { + pkg.manifest_path + .to_string() + .contains(&*cargo_toml_path_str) + }) Review Comment: The package lookup logic for workspace projects uses a substring match (`contains`) which could incorrectly match packages with similar path names. For example, if you have paths like "/project/my_ta/Cargo.toml" and "/project/my_ta_test/Cargo.toml", searching for "my_ta/Cargo.toml" could incorrectly match "my_ta_test/Cargo.toml". Consider using exact path comparison or comparing canonicalized paths instead. ```suggestion .find(|pkg| pkg.manifest_path.to_string() == cargo_toml_path_str) ``` -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
