#![warn(missing_docs)]
use crate::mboot::{
KeyProvisioningResponse, McuBoot, ResultStatus,
protocols::ProtocolOpen,
tags::{
command::{KeyProvOperation, KeyProvUserKeyType, TrustProvOperation},
property::PropertyTagDiscriminants,
},
};
use crate::{
protocols::{i2c::I2CProtocol, protocol_impl::ProtocolImpl, uart::UARTProtocol, usb::USBProtocol},
tags::status::StatusCode,
};
use std::{
ffi::{CStr, CString},
ptr, slice,
str::FromStr,
};
type CMcuBoot = libc::c_void;
type CStatus = i32;
#[repr(C)]
#[derive(Default, Debug, Clone, Copy)]
pub struct CGetPropertyResponse {
pub status: CStatus,
pub response_words: *mut u32,
pub response_words_len: usize,
pub property_type: u8,
}
#[repr(C)]
#[derive(Default, Debug, Clone, Copy)]
pub struct CReadMemoryResponse {
pub status: CStatus,
pub response_words: *mut u32,
pub response_words_len: usize,
pub bytes: *mut u8,
pub bytes_len: usize,
}
#[repr(C)]
#[derive(Default, Debug, Clone, Copy)]
pub struct CFlashReadOnceResponse {
pub status: CStatus,
pub bytes: *mut u8,
pub bytes_len: usize,
}
#[repr(C)]
#[derive(Default, Debug, Clone, Copy)]
pub struct CTrustProvisioningResponse {
pub status: CStatus,
pub response_words: *mut u32,
pub response_words_len: usize,
}
#[repr(C)]
#[derive(Default, Debug, Clone, Copy)]
pub struct CKeyStoreResponse {
pub status: CStatus,
pub response_words: *mut u32,
pub response_words_len: usize,
pub bytes: *mut u8,
pub bytes_len: usize,
}
#[repr(C)]
pub enum CProtocol {
UART,
I2c,
Usb,
}
pub const ERROR_NULL_POINTER_ARG: CStatus = -1;
pub const ERROR_INVALID_PROPERTY_TAG: CStatus = -2;
pub const ERROR_COMMUNICATION_ERROR: CStatus = -3;
pub const ERROR_INVALID_KEY_TYPE: CStatus = -4;
unsafe fn get_mboot<'a>(mboot: *mut CMcuBoot) -> &'a mut McuBoot<ProtocolImpl> {
unsafe { &mut *mboot.cast::<McuBoot<ProtocolImpl>>() }
}
#[unsafe(no_mangle)]
#[expect(
clippy::missing_panics_doc,
reason = "CString panics only if the data contains 0, this will not happen with the status string"
)]
#[must_use]
pub unsafe extern "C" fn mboot_get_status_text(status: CStatus) -> *mut libc::c_char {
let text: CString = match u32::try_from(status) {
Ok(status) => match StatusCode::try_from(status) {
Ok(status) => CString::from_str(&status.to_string()).unwrap(),
Err(_) => CString::from_str("invalid status code").unwrap(),
},
Err(_) => CString::from_str(match status {
ERROR_NULL_POINTER_ARG => "passed NULL pointer in function argument",
ERROR_INVALID_PROPERTY_TAG => "invalid propery tag passed in arguments",
ERROR_COMMUNICATION_ERROR => "error while communicating with the device",
ERROR_INVALID_KEY_TYPE => "invalid key provisioning user key type passed in arguments",
_ => "unknown status code",
})
.unwrap(),
};
text.into_raw()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_free_status_text(status_text: *mut libc::c_char) {
if !status_text.is_null() {
drop(unsafe { CString::from_raw(status_text) });
}
}
fn return_error(status: &ResultStatus) -> CStatus {
match status {
Ok(status) => *status as CStatus,
Err(_) => ERROR_COMMUNICATION_ERROR,
}
}
unsafe fn free_box_data<T>(value: *mut T) {
if !value.is_null() {
drop(unsafe { Box::from_raw(value) });
}
}
unsafe fn free_boxed_slice<T>(ptr: *mut T, len: usize) {
if !ptr.is_null() {
drop(unsafe { Box::from_raw(ptr::slice_from_raw_parts_mut(ptr, len)) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_create(device_path: *const libc::c_char, protocol: CProtocol) -> *mut CMcuBoot {
let c_str = unsafe { CStr::from_ptr(device_path) };
let Ok(device_path_str) = c_str.to_str() else {
return ptr::null_mut();
};
let device: ProtocolImpl = match protocol {
CProtocol::UART => match UARTProtocol::open(device_path_str) {
Ok(p) => p.into(),
Err(_) => return ptr::null_mut(),
},
CProtocol::I2c => match I2CProtocol::open(device_path_str) {
Ok(p) => p.into(),
Err(_) => return ptr::null_mut(),
},
CProtocol::Usb => match USBProtocol::open(device_path_str) {
Ok(p) => p.into(),
Err(_) => return ptr::null_mut(),
},
};
let mboot = Box::new(McuBoot::new(device));
Box::into_raw(mboot).cast::<CMcuBoot>()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_destroy(mboot: *mut CMcuBoot) {
unsafe { free_box_data(mboot.cast::<McuBoot<ProtocolImpl>>()) };
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_get_property(
mboot: *mut CMcuBoot,
tag: u8,
memory_index: u32,
response: *mut CGetPropertyResponse,
) -> CStatus {
if mboot.is_null() || response.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let response = unsafe { &mut *response };
*response = CGetPropertyResponse::default();
let mboot = unsafe { get_mboot(mboot) };
let Ok(tag_enum) = PropertyTagDiscriminants::try_from(tag) else {
return ERROR_INVALID_PROPERTY_TAG;
};
match mboot.get_property(tag_enum, memory_index) {
Ok(res) => {
let words: Box<[u32]> = if res.response_words.is_empty() {
Box::new([0u32])
} else {
res.response_words
};
let words_len = words.len();
let words_ptr = Box::into_raw(words);
let status = res.status as CStatus;
*response = CGetPropertyResponse {
status,
response_words: words_ptr.cast::<u32>(),
response_words_len: words_len,
property_type: tag,
};
status
}
Err(_) => ERROR_COMMUNICATION_ERROR,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_read_memory(
mboot: *mut CMcuBoot,
start_address: u32,
byte_count: u32,
memory_id: u32,
response: *mut CReadMemoryResponse,
) -> libc::c_int {
if mboot.is_null() || response.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let response = unsafe { &mut *response };
*response = CReadMemoryResponse::default();
let mboot = unsafe { get_mboot(mboot) };
match mboot.read_memory(start_address, byte_count, memory_id) {
Ok(res) => {
let words: Box<[u32]> = if res.response_words.is_empty() {
Box::new([0u32])
} else {
res.response_words
};
let words_len = words.len();
let words_ptr = Box::into_raw(words);
let bytes: Box<[u8]> = if res.bytes.is_empty() {
Box::new([0u8])
} else {
res.bytes
};
let bytes_len = bytes.len();
let bytes_ptr = Box::into_raw(bytes).cast::<u8>();
let status = res.status as CStatus;
*response = CReadMemoryResponse {
status,
response_words: words_ptr.cast::<u32>(),
response_words_len: words_len,
bytes: bytes_ptr,
bytes_len,
};
status
}
Err(_) => ERROR_COMMUNICATION_ERROR,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_write_memory(
mboot: *mut CMcuBoot,
start_address: u32,
memory_id: u32,
bytes: *const u8,
byte_count: usize,
) -> CStatus {
if mboot.is_null() || bytes.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let mboot = unsafe { get_mboot(mboot) };
let bytes = unsafe { slice::from_raw_parts(bytes, byte_count) };
return_error(&mboot.write_memory(start_address, memory_id, bytes))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_flash_erase_all(mboot: *mut CMcuBoot, memory_id: u32) -> CStatus {
if mboot.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let mboot = unsafe { get_mboot(mboot) };
return_error(&mboot.flash_erase_all(memory_id))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_receive_sb_file(mboot: *mut CMcuBoot, bytes: *const u8, byte_count: usize) -> CStatus {
if mboot.is_null() || bytes.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let bytes = unsafe { slice::from_raw_parts(bytes, byte_count) };
let mboot = unsafe { get_mboot(mboot) };
return_error(&mboot.receive_sb_file(bytes))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_flash_program_once(
mboot: *mut CMcuBoot,
index: u32,
count: u32,
data: u64,
verify: bool,
) -> CStatus {
if mboot.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let mboot = unsafe { get_mboot(mboot) };
return_error(&mboot.flash_program_once(index, count, data, verify))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_flash_read_once(
mboot: *mut CMcuBoot,
index: u32,
count: u32,
response: *mut CFlashReadOnceResponse,
) -> CStatus {
if mboot.is_null() || response.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let response = unsafe { &mut *response };
*response = CFlashReadOnceResponse::default();
let mboot = unsafe { get_mboot(mboot) };
match mboot.flash_read_once(index, count) {
Ok(data) => {
let status = StatusCode::Success as CStatus;
let bytes_len = data.len();
let bytes_ptr = if data.is_empty() {
ptr::null_mut()
} else {
Box::into_raw(data).cast::<u8>()
};
*response = CFlashReadOnceResponse {
status,
bytes: bytes_ptr,
bytes_len,
};
status
}
Err(_) => ERROR_COMMUNICATION_ERROR,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_efuse_program_once(
mboot: *mut CMcuBoot,
index: u32,
data: u32,
verify: bool,
) -> CStatus {
if mboot.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let mboot = unsafe { get_mboot(mboot) };
return_error(&mboot.efuse_program_once(index, data, verify))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_efuse_read_once(mboot: *mut CMcuBoot, index: u32, value: *mut u32) -> CStatus {
if mboot.is_null() || value.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let mboot = unsafe { get_mboot(mboot) };
match mboot.efuse_read_once(index) {
Ok(read) => {
unsafe { *value = read };
StatusCode::Success as CStatus
}
Err(_) => ERROR_COMMUNICATION_ERROR,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_reset(mboot: *mut CMcuBoot) -> CStatus {
if mboot.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let mboot = unsafe { get_mboot(mboot) };
return_error(&mboot.reset())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_set_property(mboot: *mut CMcuBoot, tag: u8, value: u32) -> CStatus {
if mboot.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let Ok(tag_enum) = PropertyTagDiscriminants::try_from(tag) else {
return ERROR_INVALID_PROPERTY_TAG;
};
let mboot = unsafe { get_mboot(mboot) };
return_error(&mboot.set_property(tag_enum, value))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_flash_erase_region(
mboot: *mut CMcuBoot,
start_address: u32,
byte_count: u32,
memory_id: u32,
) -> CStatus {
if mboot.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let mboot = unsafe { get_mboot(mboot) };
return_error(&mboot.flash_erase_region(start_address, byte_count, memory_id))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_flash_erase_all_unsecure(mboot: *mut CMcuBoot) -> CStatus {
if mboot.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let mboot = unsafe { get_mboot(mboot) };
return_error(&mboot.flash_erase_all_unsecure())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_fill_memory(
mboot: *mut CMcuBoot,
start_address: u32,
byte_count: u32,
pattern: u32,
) -> CStatus {
if mboot.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let mboot = unsafe { get_mboot(mboot) };
return_error(&mboot.fill_memory(start_address, byte_count, pattern))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_execute(
mboot: *mut CMcuBoot,
start_address: u32,
argument: u32,
stackpointer: u32,
) -> CStatus {
if mboot.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let mboot = unsafe { get_mboot(mboot) };
return_error(&mboot.execute(start_address, argument, stackpointer))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_call(mboot: *mut CMcuBoot, start_address: u32, argument: u32) -> CStatus {
if mboot.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let mboot = unsafe { get_mboot(mboot) };
return_error(&mboot.call(start_address, argument))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_configure_memory(mboot: *mut CMcuBoot, memory_id: u32, address: u32) -> CStatus {
if mboot.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let mboot = unsafe { get_mboot(mboot) };
return_error(&mboot.configure_memory(memory_id, address))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_load_image(mboot: *mut CMcuBoot, bytes: *const u8, byte_count: usize) -> CStatus {
if mboot.is_null() || bytes.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let bytes = unsafe { slice::from_raw_parts(bytes, byte_count) };
let mboot = unsafe { get_mboot(mboot) };
return_error(&mboot.load_image(bytes))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_fuse_program(
mboot: *mut CMcuBoot,
start_address: u32,
memory_id: u32,
bytes: *const u8,
byte_count: usize,
) -> CStatus {
if mboot.is_null() || bytes.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let bytes = unsafe { slice::from_raw_parts(bytes, byte_count) };
let mboot = unsafe { get_mboot(mboot) };
return_error(&mboot.fuse_program(start_address, memory_id, bytes))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_fuse_read(
mboot: *mut CMcuBoot,
start_address: u32,
byte_count: u32,
memory_id: u32,
response: *mut CReadMemoryResponse,
) -> CStatus {
if mboot.is_null() || response.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let response = unsafe { &mut *response };
*response = CReadMemoryResponse::default();
let mboot = unsafe { get_mboot(mboot) };
match mboot.fuse_read(start_address, byte_count, memory_id) {
Ok(res) => {
let words: Box<[u32]> = if res.response_words.is_empty() {
Box::new([0u32])
} else {
res.response_words
};
let words_len = words.len();
let words_ptr = Box::into_raw(words);
let bytes: Box<[u8]> = if res.bytes.is_empty() {
Box::new([0u8])
} else {
res.bytes
};
let bytes_len = bytes.len();
let bytes_ptr = Box::into_raw(bytes).cast::<u8>();
let status = res.status as CStatus;
*response = CReadMemoryResponse {
status,
response_words: words_ptr.cast::<u32>(),
response_words_len: words_len,
bytes: bytes_ptr,
bytes_len,
};
status
}
Err(_) => ERROR_COMMUNICATION_ERROR,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_trust_provisioning_oem_gen_master_share(
mboot: *mut CMcuBoot,
oem_share_input_addr: u32,
oem_share_input_size: u32,
oem_enc_share_output_addr: u32,
oem_enc_share_output_size: u32,
oem_enc_master_share_output_addr: u32,
oem_enc_master_share_output_size: u32,
oem_cust_cert_puk_output_addr: u32,
oem_cust_cert_puk_output_size: u32,
response: *mut CTrustProvisioningResponse,
) -> CStatus {
if mboot.is_null() || response.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let operation = TrustProvOperation::OemGenMasterShare {
oem_share_input_addr,
oem_share_input_size,
oem_enc_share_output_addr,
oem_enc_share_output_size,
oem_enc_master_share_output_addr,
oem_enc_master_share_output_size,
oem_cust_cert_puk_output_addr,
oem_cust_cert_puk_output_size,
};
unsafe { mboot_trust_provisioning_run(mboot, &operation, response) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_trust_provisioning_oem_set_master_share(
mboot: *mut CMcuBoot,
oem_share_input_addr: u32,
oem_share_input_size: u32,
oem_enc_master_share_input_addr: u32,
oem_enc_master_share_input_size: u32,
response: *mut CTrustProvisioningResponse,
) -> CStatus {
if mboot.is_null() || response.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let operation = TrustProvOperation::OemSetMasterShare {
oem_share_input_addr,
oem_share_input_size,
oem_enc_master_share_input_addr,
oem_enc_master_share_input_size,
};
unsafe { mboot_trust_provisioning_run(mboot, &operation, response) }
}
unsafe fn mboot_trust_provisioning_run(
mboot: *mut CMcuBoot,
operation: &TrustProvOperation,
response: *mut CTrustProvisioningResponse,
) -> CStatus {
let response = unsafe { &mut *response };
*response = CTrustProvisioningResponse::default();
let mboot = unsafe { get_mboot(mboot) };
match mboot.trust_provisioning(operation) {
Ok((status, data)) => {
let words: Box<[u32]> = if data.is_empty() { Box::new([0u32]) } else { data };
let words_len = words.len();
let words_ptr = Box::into_raw(words);
let status = status as CStatus;
*response = CTrustProvisioningResponse {
status,
response_words: words_ptr.cast::<u32>(),
response_words_len: words_len,
};
status
}
Err(_) => ERROR_COMMUNICATION_ERROR,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_key_provisioning_enroll(mboot: *mut CMcuBoot) -> CStatus {
if mboot.is_null() {
return ERROR_NULL_POINTER_ARG;
}
unsafe { mboot_key_provisioning_run_status(mboot, &KeyProvOperation::Enroll) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_key_provisioning_set_user_key(
mboot: *mut CMcuBoot,
key_type: u32,
key_data: *const u8,
key_count: usize,
) -> CStatus {
if mboot.is_null() || key_data.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let Ok(key_type) = KeyProvUserKeyType::try_from(key_type) else {
return ERROR_INVALID_KEY_TYPE;
};
let key_data: Box<[u8]> = unsafe { slice::from_raw_parts(key_data, key_count) }.into();
let operation = KeyProvOperation::SetUserKey { key_type, key_data };
unsafe { mboot_key_provisioning_run_status(mboot, &operation) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_key_provisioning_set_key(mboot: *mut CMcuBoot, key_type: u32, key_size: u32) -> CStatus {
if mboot.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let Ok(key_type) = KeyProvUserKeyType::try_from(key_type) else {
return ERROR_INVALID_KEY_TYPE;
};
let operation = KeyProvOperation::SetKey { key_type, key_size };
unsafe { mboot_key_provisioning_run_status(mboot, &operation) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_key_provisioning_write_key_nonvolatile(mboot: *mut CMcuBoot, memory_id: u32) -> CStatus {
if mboot.is_null() {
return ERROR_NULL_POINTER_ARG;
}
unsafe { mboot_key_provisioning_run_status(mboot, &KeyProvOperation::WriteKeyNonvolatile { memory_id }) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_key_provisioning_read_key_nonvolatile(mboot: *mut CMcuBoot, memory_id: u32) -> CStatus {
if mboot.is_null() {
return ERROR_NULL_POINTER_ARG;
}
unsafe { mboot_key_provisioning_run_status(mboot, &KeyProvOperation::ReadKeyNonvolatile { memory_id }) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_key_provisioning_write_key_store(
mboot: *mut CMcuBoot,
keystore_data: *const u8,
keystore_count: usize,
) -> CStatus {
if mboot.is_null() || keystore_data.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let keystore_data: Box<[u8]> = unsafe { slice::from_raw_parts(keystore_data, keystore_count) }.into();
let operation = KeyProvOperation::WriteKeyStore { keystore_data };
unsafe { mboot_key_provisioning_run_status(mboot, &operation) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_key_provisioning_read_key_store(
mboot: *mut CMcuBoot,
response: *mut CKeyStoreResponse,
) -> CStatus {
if mboot.is_null() || response.is_null() {
return ERROR_NULL_POINTER_ARG;
}
let response = unsafe { &mut *response };
*response = CKeyStoreResponse::default();
let mboot = unsafe { get_mboot(mboot) };
let operation = KeyProvOperation::ReadKeyStore {
file: String::new(),
use_hexdump: false,
};
match mboot.key_provisioning(&operation) {
Ok(KeyProvisioningResponse::KeyStore {
status,
response_words,
bytes,
}) => {
let words: Box<[u32]> = if response_words.is_empty() {
Box::new([0u32])
} else {
response_words
};
let words_len = words.len();
let words_ptr = Box::into_raw(words);
let bytes: Box<[u8]> = if bytes.is_empty() { Box::new([0u8]) } else { bytes };
let bytes_len = bytes.len();
let bytes_ptr = Box::into_raw(bytes).cast::<u8>();
let status = status as CStatus;
*response = CKeyStoreResponse {
status,
response_words: words_ptr.cast::<u32>(),
response_words_len: words_len,
bytes: bytes_ptr,
bytes_len,
};
status
}
Ok(KeyProvisioningResponse::Status(_)) | Err(_) => ERROR_COMMUNICATION_ERROR,
}
}
unsafe fn mboot_key_provisioning_run_status(mboot: *mut CMcuBoot, operation: &KeyProvOperation) -> CStatus {
let mboot = unsafe { get_mboot(mboot) };
match mboot.key_provisioning(operation) {
Ok(KeyProvisioningResponse::Status(status) | KeyProvisioningResponse::KeyStore { status, .. }) => {
status as CStatus
}
Err(_) => ERROR_COMMUNICATION_ERROR,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_free_response_words(words: *mut u32, len: usize) {
unsafe { free_boxed_slice(words, len) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_free_bytes(bytes: *mut u8, len: usize) {
unsafe { free_boxed_slice(bytes, len) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_free_read_memory_response(response: *mut CReadMemoryResponse) {
if response.is_null() {
return;
}
let response = unsafe { *response };
unsafe {
free_boxed_slice(response.response_words, response.response_words_len);
free_boxed_slice(response.bytes, response.bytes_len);
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_free_flash_read_once_response(response: *mut CFlashReadOnceResponse) {
if response.is_null() {
return;
}
let response = unsafe { *response };
if !response.bytes.is_null() {
drop(unsafe { Box::from_raw(ptr::slice_from_raw_parts_mut(response.bytes, response.bytes_len)) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_free_trust_provisioning_response(response: *mut CTrustProvisioningResponse) {
if response.is_null() {
return;
}
let response = unsafe { *response };
if !response.response_words.is_null() {
drop(unsafe {
Box::from_raw(ptr::slice_from_raw_parts_mut(
response.response_words,
response.response_words_len,
))
});
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mboot_free_key_store_response(response: *mut CKeyStoreResponse) {
if response.is_null() {
return;
}
let response = unsafe { *response };
if !response.response_words.is_null() {
drop(unsafe {
Box::from_raw(ptr::slice_from_raw_parts_mut(
response.response_words,
response.response_words_len,
))
});
}
if !response.bytes.is_null() {
drop(unsafe { Box::from_raw(ptr::slice_from_raw_parts_mut(response.bytes, response.bytes_len)) });
}
}
#[cfg(test)]
mod tests {
use super::{free_box_data, free_boxed_slice};
use std::slice;
#[test]
fn read_back_boxed_slice_u32_matches_original_data() {
let original: Vec<u32> = vec![0xDEAD_BEEF, 0x1234_5678, 0, u32::MAX, 42];
let len = original.len();
let boxed: Box<[u32]> = original.clone().into_boxed_slice();
let ptr = Box::into_raw(boxed).cast::<u32>();
let read_back = unsafe { slice::from_raw_parts(ptr, len) };
assert_eq!(
read_back,
original.as_slice(),
"data read back through the raw pointer must match the original words, not fat-pointer metadata"
);
unsafe { free_boxed_slice(ptr, len) };
}
#[test]
fn read_back_boxed_slice_u8_matches_original_data() {
let original: Vec<u8> = (0..=255u8).collect();
let len = original.len();
let boxed: Box<[u8]> = original.clone().into_boxed_slice();
let ptr = Box::into_raw(boxed).cast::<u8>();
let read_back = unsafe { slice::from_raw_parts(ptr, len) };
assert_eq!(read_back, original.as_slice());
unsafe { free_boxed_slice(ptr, len) };
}
#[test]
fn free_boxed_slice_single_element() {
let boxed: Box<[u32]> = Box::new([0u32]);
let ptr = Box::into_raw(boxed).cast::<u32>();
unsafe { free_boxed_slice(ptr, 1) };
}
#[test]
fn free_boxed_slice_null_is_noop() {
unsafe { free_boxed_slice::<u32>(std::ptr::null_mut(), 0) };
}
#[test]
fn free_box_data_roundtrip_and_null_is_noop() {
let boxed: Box<u64> = Box::new(0x1122_3344_5566_7788);
let ptr = Box::into_raw(boxed);
unsafe { free_box_data(ptr) };
unsafe { free_box_data::<u64>(std::ptr::null_mut()) };
}
}