Add a basic TA

Add a base config for the TA, define commands, add simple error
translation between optee-utee and KeyMint, add a basic main.rs
to handle KeyMint IPC, and the first crypto trait implementation
for Sha256.

Test: make ta
Change-Id: I7baa1826d6f5ec6191cdc5dbb3603c7338f44316
diff --git a/src/command.rs b/src/command.rs
new file mode 100644
index 0000000..d70772a
--- /dev/null
+++ b/src/command.rs
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed 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.
+
+//! Keymint TA command definitions.
+
+// param[0]: input (unused)
+// param[1]: output
+pub const GET_AUTH_TOKEN_KEY: u32 = 512;
+
+// param[0]: input
+#[allow(dead_code)]
+pub const WRITE: u32 = 1024;
+
+// param[0]: output
+#[allow(dead_code)]
+pub const READ: u32 = 1025;
+
+#[cfg(feature = "dev")]
+pub const RUN_TEST: u32 = 2048;
+
+#[cfg(feature = "dev")]
+#[allow(dead_code)]
+pub const KILL: u32 = 2049;
diff --git a/src/config.rs b/src/config.rs
new file mode 100644
index 0000000..7666b07
--- /dev/null
+++ b/src/config.rs
@@ -0,0 +1,126 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed 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 core::ffi::{c_int, c_size_t};
+use core::primitive::u64;
+use optee_utee_sys::user_ta_prop_type::{
+    USER_TA_PROP_TYPE_BOOL, USER_TA_PROP_TYPE_STRING, USER_TA_PROP_TYPE_U32,
+};
+use optee_utee_sys::{
+    ta_head, user_ta_property, TA_FLAG_MULTI_SESSION, TA_FLAG_SINGLE_INSTANCE,
+    TA_PROP_STR_DATA_SIZE, TA_PROP_STR_DESCRIPTION, TA_PROP_STR_MULTI_SESSION,
+    TA_PROP_STR_SINGLE_INSTANCE, TA_PROP_STR_STACK_SIZE, TA_PROP_STR_VERSION, TEE_UUID,
+};
+
+// UUID
+const UUID: TEE_UUID = TEE_UUID {
+    // dc274baf-5f8c-48cd-89ab-cd9a4d38ed12
+    timeLow: 0xdc274baf,
+    timeMid: 0x5f8c,
+    timeHiAndVersion: 0x48cd,
+    clockSeqAndNode: [0x89, 0xab, 0xcd, 0x9a, 0x4d, 0x38, 0xed, 0x12],
+};
+
+// Flags
+//
+// Configure the Keymint TA to accept multiple sessions from a single instance,
+// so that clients other than the Keymint HAL (e.g. the Gatekeeper TA) can
+// communicate with the Keymint TA over a separate session.
+//
+// According to GlobalPlatform internal core API spec, section 2.1.3:
+// "Sequencial execution of entry points", the TEE-OS core framework guarantees
+// no two TA entry points (including command invocation entry points) execute
+// at the same time, even when commands are associated with different sessions.
+// Sessions are logical contexts that accompany command invocations (passed as
+// a parameter).
+const FLAGS: u32 = TA_FLAG_SINGLE_INSTANCE | TA_FLAG_MULTI_SESSION;
+
+// Memory
+const STACK_SIZE: u32 = 66 * 1024;
+const HEAP_SIZE: usize = 512 * 1024;
+
+// Heap
+#[no_mangle]
+pub static ta_heap_size: c_size_t = HEAP_SIZE;
+#[no_mangle]
+#[link_section = ".bss"]
+pub static ta_heap: [u8; HEAP_SIZE] = [0; HEAP_SIZE];
+
+// Header
+#[no_mangle]
+#[link_section = ".ta_head"]
+pub static ta_head: ta_head =
+    ta_head { uuid: UUID, stack_size: STACK_SIZE, flags: FLAGS, depr_entry: u64::MAX };
+
+// Properties
+#[no_mangle]
+pub static ta_props: [user_ta_property; 8] = [
+    // Flags
+    user_ta_property {
+        name: TA_PROP_STR_SINGLE_INSTANCE,
+        prop_type: USER_TA_PROP_TYPE_BOOL,
+        value: &(FLAGS & TA_FLAG_SINGLE_INSTANCE != 0) as *const bool as _,
+    },
+    user_ta_property {
+        name: TA_PROP_STR_MULTI_SESSION,
+        prop_type: USER_TA_PROP_TYPE_BOOL,
+        value: &(FLAGS & TA_FLAG_MULTI_SESSION != 0) as *const bool as _,
+    },
+    // Memory
+    user_ta_property {
+        name: TA_PROP_STR_DATA_SIZE,
+        prop_type: USER_TA_PROP_TYPE_U32,
+        value: &(HEAP_SIZE as u32) as *const u32 as *mut _,
+    },
+    user_ta_property {
+        name: TA_PROP_STR_STACK_SIZE,
+        prop_type: USER_TA_PROP_TYPE_U32,
+        value: &STACK_SIZE as *const u32 as *mut _,
+    },
+    // Description
+    user_ta_property {
+        name: TA_PROP_STR_DESCRIPTION,
+        prop_type: USER_TA_PROP_TYPE_STRING,
+        value: b"This is the Android Keymint TA for OP-TEE.\0".as_ptr() as _,
+    },
+    user_ta_property {
+        name: "gp.ta.description\0".as_ptr(),
+        prop_type: USER_TA_PROP_TYPE_STRING,
+        value: b"Android Keymint TA for OP-TEE\0".as_ptr() as _,
+    },
+    // Version
+    user_ta_property {
+        name: TA_PROP_STR_VERSION,
+        prop_type: USER_TA_PROP_TYPE_STRING,
+        value: b"0.1\0".as_ptr() as _,
+    },
+    user_ta_property {
+        name: "gp.ta.version\0".as_ptr(),
+        prop_type: USER_TA_PROP_TYPE_U32,
+        value: &0x0010u32 as *const u32 as *mut _,
+    },
+];
+#[no_mangle]
+pub static ta_num_props: c_size_t = ta_props.len();
+
+// Tracing
+#[no_mangle]
+pub static trace_ext_prefix: &[u8] = b"TA\0";
+#[no_mangle]
+pub static mut trace_level: c_int = 4;
+#[no_mangle]
+pub unsafe extern "C" fn tahead_get_trace_level() -> c_int {
+    return trace_level;
+}
diff --git a/src/crypto.rs b/src/crypto.rs
new file mode 100644
index 0000000..a8688d6
--- /dev/null
+++ b/src/crypto.rs
@@ -0,0 +1,19 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed 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.
+
+pub mod sha;
+
+#[cfg(feature = "dev")]
+pub mod tests;
diff --git a/src/crypto/sha.rs b/src/crypto/sha.rs
new file mode 100644
index 0000000..ee01c80
--- /dev/null
+++ b/src/crypto/sha.rs
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed 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 crate::error::Error;
+use kmr_common::crypto;
+use optee_utee::{AlgorithmId, Digest};
+
+pub struct Sha256;
+
+impl crypto::Sha256 for Sha256 {
+    fn hash(&self, data: &[u8]) -> Result<[u8; 32], kmr_common::Error> {
+        let mut hash: [u8; 32] = [0; 32];
+        let op = Digest::allocate(AlgorithmId::Sha256).map_err(Error::kmerr)?;
+        op.do_final(data, &mut hash).map_err(Error::kmerr)?;
+        Ok(hash)
+    }
+}
diff --git a/src/crypto/tests.rs b/src/crypto/tests.rs
new file mode 100644
index 0000000..3f45115
--- /dev/null
+++ b/src/crypto/tests.rs
@@ -0,0 +1,49 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed 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 hex;
+use kmr_common::crypto::Sha256;
+use log::info;
+
+/// Test basic SHA-256 functionality.
+pub fn test_sha256<S: Sha256>(sha256: S) {
+    struct TestCase {
+        msg: &'static [u8],
+        want: &'static str,
+    }
+    let tests = vec![
+        TestCase {
+            msg: b"",
+            want: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+        },
+        TestCase {
+            msg: b"abc",
+            want: "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
+        },
+    ];
+    for test in tests {
+        let got = sha256.hash(test.msg).unwrap();
+        assert_eq!(hex::encode(&got), test.want, "for input {}", hex::encode(test.msg));
+    }
+    info!("test_sha256: Success");
+}
+
+pub fn run() {
+    info!("crypto::tests::run: Starting");
+
+    test_sha256(crate::crypto::sha::Sha256 {});
+
+    info!("crypto::tests::run: Done");
+}
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..6470cbc
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,94 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed 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 kmr_common;
+use log;
+
+pub trait Error {
+    fn kmerr(e: Self) -> kmr_common::Error;
+}
+
+impl Error for optee_utee::Error {
+    fn kmerr(error: optee_utee::Error) -> kmr_common::Error {
+        log::error!("{}", error);
+        match error.kind() {
+            optee_utee::ErrorKind::BadParameters => {
+                kmr_common::km_err!(InvalidArgument, "{}", error)
+            }
+            optee_utee::ErrorKind::MacInvalid | optee_utee::ErrorKind::SignatureInvalid => {
+                kmr_common::km_err!(VerificationFailed, "{}", error)
+            }
+            optee_utee::ErrorKind::NotImplemented => {
+                kmr_common::km_err!(Unimplemented, "{}", error)
+            }
+            optee_utee::ErrorKind::NotSupported => {
+                kmr_common::km_err!(UnsupportedAlgorithm, "{}", error)
+            }
+            optee_utee::ErrorKind::OutOfMemory => {
+                kmr_common::km_err!(MemoryAllocationFailed, "{}", error)
+            }
+            _ => kmr_common::km_err!(UnknownError, "{}", error),
+        }
+    }
+}
+
+impl Error for der::Error {
+    fn kmerr(error: der::Error) -> kmr_common::Error {
+        log::error!("der error: {}", error);
+        kmr_common::km_err!(UnsupportedKeyFormat, "der error: {}", error)
+    }
+}
+
+impl Error for pkcs1::Error {
+    fn kmerr(error: pkcs1::Error) -> kmr_common::Error {
+        log::error!("pkcs1 error: {}", error);
+        kmr_common::km_err!(UnsupportedKeyFormat, "pkcs1 error: {}", error)
+    }
+}
+
+impl Error for pkcs8::spki::Error {
+    fn kmerr(error: pkcs8::spki::Error) -> kmr_common::Error {
+        log::error!("spki error: {}", error);
+        kmr_common::km_err!(UnsupportedKeyFormat, "spki error: {}", error)
+    }
+}
+
+impl Error for sec1::Error {
+    fn kmerr(error: sec1::Error) -> kmr_common::Error {
+        log::error!("sec1 error: {}", error);
+        kmr_common::km_err!(UnsupportedKeyFormat, "sec1 error: {}", error)
+    }
+}
+
+impl Error for uuid::Error {
+    fn kmerr(error: uuid::Error) -> kmr_common::Error {
+        log::error!("uuid error: {}", error);
+        kmr_common::km_err!(UnknownError, "uuid error: {}", error)
+    }
+}
+
+impl Error for core::convert::Infallible {
+    fn kmerr(error: core::convert::Infallible) -> kmr_common::Error {
+        log::error!("Infallible error: {}", error);
+        kmr_common::km_err!(MemoryAllocationFailed, "Infallible error: {}", error)
+    }
+}
+
+impl Error for core::array::TryFromSliceError {
+    fn kmerr(error: core::array::TryFromSliceError) -> kmr_common::Error {
+        log::error!("TryFromSliceError error: {}", error);
+        kmr_common::km_err!(MemoryAllocationFailed, "TryFromSliceError error: {}", error)
+    }
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..2e364cc
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,234 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed 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.
+
+#![no_std]
+#![no_main]
+#![feature(c_size_t)]
+
+#[macro_use]
+extern crate alloc;
+
+mod command;
+mod config;
+mod crypto;
+mod error;
+mod stubs;
+
+use alloc::boxed::Box;
+use alloc::collections::vec_deque::VecDeque;
+use alloc::vec::Vec;
+use core::ffi::c_void;
+use log::info;
+use optee_utee::{ta_close_session, ta_create, ta_destroy, ta_invoke_command, ta_open_session};
+use optee_utee::{Error, ErrorKind, ParamType, Parameters, Result};
+use spin::Mutex;
+use spin::Once;
+
+// SAFETY:
+//
+// Each OP-TEE TA, per GlobalPlatform internal core API spec v1.1+,
+// is essentially a single-threaded environment, in which all TA
+// entry points (create, destroy, open_session, close_session, and
+// invoke_command) are executed sequencially as by the TEE OS.
+//
+// Thus it is safe to mark the global `kmr_ta::KeyMintTa` variable
+// as Send.
+//
+// We use spin::Once and spin::Mutex for readability rather than
+// necessity.
+struct SingleThreadedTa(kmr_ta::KeyMintTa);
+unsafe impl Send for SingleThreadedTa {}
+
+fn keymint_ta() -> &'static Mutex<SingleThreadedTa> {
+    static KEYMINT_TA: Once<Mutex<SingleThreadedTa>> = Once::new();
+    KEYMINT_TA.call_once(|| {
+        let ta = kmr_ta::KeyMintTa::new(
+            kmr_ta::HardwareInfo {
+                version_number: 3,
+                security_level: kmr_common::wire::keymint::SecurityLevel::TrustedEnvironment,
+                impl_name: "TEE KeyMint in Rust",
+                author_name: "Google",
+                unique_id: "TEE KeyMint TA",
+            },
+            kmr_ta::RpcInfo::V3(kmr_ta::RpcInfoV3 {
+                author_name: "Google",
+                unique_id: "TEE KeyMint TA",
+                fused: false,
+                supported_num_of_keys_in_csr: kmr_wire::rpc::MINIMUM_SUPPORTED_KEYS_IN_CSR,
+            }),
+            kmr_common::crypto::Implementation {
+                rng: Box::new(kmr_common::crypto::NoOpRng),
+                clock: None,
+                compare: Box::new(kmr_common::crypto::InsecureEq),
+                aes: Box::new(kmr_common::crypto::NoOpAes),
+                des: Box::new(kmr_common::crypto::NoOpDes),
+                hmac: Box::new(kmr_common::crypto::NoOpHmac),
+                rsa: Box::new(kmr_common::crypto::NoOpRsa),
+                ec: Box::new(kmr_common::crypto::NoOpEc),
+                ckdf: Box::new(kmr_common::crypto::NoOpAesCmac),
+                hkdf: Box::new(kmr_common::crypto::NoOpHmac),
+                sha256: Box::new(crypto::sha::Sha256),
+            },
+            kmr_ta::device::Implementation {
+                keys: Box::new(kmr_ta::device::NoOpRetrieveKeyMaterial),
+                sign_info: None,
+                attest_ids: None,
+                sdd_mgr: None,
+                bootloader: Box::new(kmr_ta::device::BootloaderDone),
+                sk_wrapper: None,
+                tup: Box::new(kmr_ta::device::TrustedPresenceUnsupported),
+                legacy_key: None,
+                rpc: Box::new(kmr_ta::device::NoOpRetrieveRpcArtifacts),
+            },
+        );
+        Mutex::new(SingleThreadedTa(ta))
+    })
+}
+
+// The maximum size an incoming TIPC message can be.
+//
+// This can be used to pre-allocate buffer space in order to ensure that your
+// read buffer can always hold an incoming message.
+const KEYMINT_MAX_BUFFER_LENGTH: usize = 4096;
+
+// The maximum size of a message's payload. A longer message is split up into
+// multiple segments.
+const KEYMINT_MAX_MESSAGE_CONTENT_SIZE: usize = 4000;
+
+// The Keymint TA session context.
+//
+// `ipc_resp` holds the last response of the ta that is to be transferred to the
+// client upon request. It is a Vec of Vec because a longer message is broken
+// into multiple sub-messages.
+//
+// `ipc_resp` is only used by command::READ and command::WRITE handlers.
+struct SessionContext {
+    ipc_resp: Option<VecDeque<Vec<u8>>>,
+}
+
+// This default trait is required by ta_* macros that sets up the optional
+// session context parameter in ta_open_session, ta_close_session, and
+// ta_invoke_command.
+impl Default for SessionContext {
+    fn default() -> Self {
+        info!("Creating SessionContext...");
+        Self { ipc_resp: None }
+    }
+}
+
+#[ta_create]
+fn create() -> Result<()> {
+    let config = optee_logger::OpteeLoggerConfig::default();
+    optee_logger::init_with_config(config);
+
+    info!("Hello from OP-TEE Keymint in Rust!");
+
+    Ok(())
+}
+
+#[ta_open_session]
+fn open_session(_params: &mut Parameters, _sess_ctx: &mut SessionContext) -> Result<()> {
+    info!("Keymint TA connected");
+    Ok(())
+}
+
+#[ta_close_session]
+fn close_session(_sess_ctx: &mut SessionContext) {
+    info!("Keymint TA disconnected");
+}
+
+#[ta_destroy]
+fn destroy() {
+    info!("Keymint TA unloading... ");
+}
+
+#[ta_invoke_command]
+fn invoke_command(
+    sess_ctx: &mut SessionContext,
+    cmd_id: u32,
+    params: &mut Parameters,
+) -> Result<()> {
+    match cmd_id {
+        command::GET_AUTH_TOKEN_KEY => Err(Error::new(ErrorKind::NotSupported)),
+        command::WRITE => handle_ipc_request(sess_ctx, params),
+        command::READ => handle_ipc_response(sess_ctx, params),
+        #[cfg(feature = "dev")]
+        command::RUN_TEST => {
+            crypto::tests::run();
+            Ok(())
+        }
+        #[cfg(feature = "dev")]
+        command::KILL => {
+            info!("Killing the TA as requested...");
+            panic!("TA killed");
+        }
+        _ => Err(Error::new(ErrorKind::NotSupported)),
+    }
+}
+
+fn handle_ipc_request(keymint_ctx: &mut SessionContext, params: &mut Parameters) -> Result<()> {
+    if keymint_ctx.ipc_resp.is_some() {
+        // The response of the previous IPC has not been cleared.
+        return Err(Error::new(ErrorKind::Busy));
+    }
+
+    // Check MemrefInput here because as_memref() does not differentiate
+    // between input and output.
+    let mut p = unsafe { params.0.as_memref()? };
+    if let ParamType::MemrefInput = p.param_type() {
+        let req = p.buffer();
+        // Deny requests that are too big.
+        if req.len() > KEYMINT_MAX_BUFFER_LENGTH {
+            return Err(Error::new(ErrorKind::Overflow));
+        }
+        let resp = keymint_ta().lock().0.process(req);
+        keymint_ctx.ipc_resp = Some(VecDeque::from(
+            kmr_ta::split_rsp(resp.as_slice(), KEYMINT_MAX_MESSAGE_CONTENT_SIZE)
+                .map_err(|_| Error::new(ErrorKind::Generic))?,
+        ));
+        Ok(())
+    } else {
+        Err(Error::new(ErrorKind::BadParameters))
+    }
+}
+
+fn handle_ipc_response(keymint_ctx: &mut SessionContext, params: &mut Parameters) -> Result<()> {
+    // No outstanding response.
+    if keymint_ctx.ipc_resp.is_none() {
+        return Err(Error::new(ErrorKind::NoData));
+    }
+
+    // Check MemrefOutput here because as_memref() does not differentiate
+    // between input and output.
+    let mut p = unsafe { params.0.as_memref()? };
+    if let ParamType::MemrefOutput = p.param_type() {
+        // Take all packets out. Unwrap() never panics.
+        let mut packets = keymint_ctx.ipc_resp.take().unwrap();
+        // Send only the packet at the front. Unwrap() never panics.
+        let packet = packets.pop_front().unwrap();
+        if packet.len() > p.buffer().len() {
+            return Err(Error::new(ErrorKind::ShortBuffer));
+        }
+        p.set_updated_size(packet.len());
+        p.buffer().copy_from_slice(packet.as_slice());
+        if packets.len() > 0 {
+            // Put any remaining packets back for the next request.
+            keymint_ctx.ipc_resp = Some(packets);
+        }
+        Ok(())
+    } else {
+        Err(Error::new(ErrorKind::BadParameters))
+    }
+}
diff --git a/src/stubs.rs b/src/stubs.rs
new file mode 100644
index 0000000..3d07edf
--- /dev/null
+++ b/src/stubs.rs
@@ -0,0 +1,23 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed 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.
+
+// https://github.com/rust-lang/rust/issues/106864
+
+#[no_mangle]
+extern "C" fn rust_eh_personality() {}
+
+#[allow(non_snake_case)]
+#[no_mangle]
+extern "C" fn _Unwind_Resume() {}