| // SPDX-License-Identifier: BSD-2-Clause |
| /* |
| * Copyright (c) 2015, Linaro Limited |
| * All rights reserved. |
| */ |
| |
| #include <tee_api.h> |
| #include <storage_benchmark.h> |
| #include <ta_storage_benchmark.h> |
| #include <tee_internal_api_extensions.h> |
| |
| #define DEFAULT_CHUNK_SIZE (1 << 10) |
| #define DEFAULT_DATA_SIZE (1024) |
| |
| #define SCRAMBLE(x) ((x & 0xff) ^ 0xaa) |
| |
| #define ASSERT_PARAM_TYPE(pt_in, pt_expect) \ |
| do { \ |
| if ((pt_in) != (pt_expect)) \ |
| return TEE_ERROR_BAD_PARAMETERS; \ |
| } while (0) |
| |
| static uint8_t filename[] = "BenchmarkTestFile"; |
| |
| static void fill_buffer(uint8_t *buf, size_t size) |
| { |
| size_t i = 0; |
| |
| if (!buf) |
| return; |
| |
| for (i = 0; i < size; i++) |
| buf[i] = SCRAMBLE(i); |
| } |
| |
| static TEE_Result verify_buffer(uint8_t *buf, size_t size) |
| { |
| size_t i = 0; |
| |
| if (!buf) |
| return TEE_ERROR_BAD_PARAMETERS; |
| |
| for (i = 0; i < size; i++) { |
| uint8_t expect_data = SCRAMBLE(i); |
| |
| if (expect_data != buf[i]) { |
| return TEE_ERROR_CORRUPT_OBJECT; |
| } |
| } |
| |
| return TEE_SUCCESS; |
| } |
| |
| static inline uint32_t tee_time_to_ms(TEE_Time t) |
| { |
| return t.seconds * 1000 + t.millis; |
| } |
| |
| static inline uint32_t get_delta_time_in_ms(TEE_Time start, TEE_Time stop) |
| { |
| return tee_time_to_ms(stop) - tee_time_to_ms(start); |
| } |
| |
| static TEE_Result prepare_test_file(size_t data_size, uint8_t *chunk_buf, |
| size_t chunk_size) |
| { |
| size_t remain_bytes = data_size; |
| TEE_Result res = TEE_SUCCESS; |
| TEE_ObjectHandle object = TEE_HANDLE_NULL; |
| |
| res = TEE_CreatePersistentObject(TEE_STORAGE_PRIVATE, |
| filename, sizeof(filename), |
| TEE_DATA_FLAG_ACCESS_READ | |
| TEE_DATA_FLAG_ACCESS_WRITE | |
| TEE_DATA_FLAG_ACCESS_WRITE_META | |
| TEE_DATA_FLAG_OVERWRITE, |
| NULL, NULL, 0, &object); |
| if (res != TEE_SUCCESS) { |
| EMSG("Failed to create persistent object, res=0x%08x", |
| res); |
| goto exit; |
| } |
| |
| while (remain_bytes) { |
| size_t write_size = 0; |
| |
| if (remain_bytes < chunk_size) |
| write_size = remain_bytes; |
| else |
| write_size = chunk_size; |
| res = TEE_WriteObjectData(object, chunk_buf, write_size); |
| if (res != TEE_SUCCESS) { |
| EMSG("Failed to write data, res=0x%08x", res); |
| goto exit_close_object; |
| } |
| remain_bytes -= write_size; |
| } |
| exit_close_object: |
| TEE_CloseObject(object); |
| exit: |
| return res; |
| } |
| |
| static TEE_Result test_write(TEE_ObjectHandle object, size_t data_size, |
| uint8_t *chunk_buf, size_t chunk_size, |
| uint32_t *spent_time_in_ms) |
| { |
| TEE_Time start_time = { }; |
| TEE_Time stop_time = { }; |
| size_t remain_bytes = data_size; |
| TEE_Result res = TEE_SUCCESS; |
| |
| TEE_GetSystemTime(&start_time); |
| |
| while (remain_bytes) { |
| size_t write_size = 0; |
| |
| DMSG("Write data, remain bytes: %zu", remain_bytes); |
| if (chunk_size > remain_bytes) |
| write_size = remain_bytes; |
| else |
| write_size = chunk_size; |
| res = TEE_WriteObjectData(object, chunk_buf, write_size); |
| if (res != TEE_SUCCESS) { |
| EMSG("Failed to write data, res=0x%08x", res); |
| goto exit; |
| } |
| remain_bytes -= write_size; |
| } |
| |
| TEE_GetSystemTime(&stop_time); |
| |
| *spent_time_in_ms = get_delta_time_in_ms(start_time, stop_time); |
| |
| IMSG("start: %u.%u(s), stop: %u.%u(s), delta: %u(ms)", |
| start_time.seconds, start_time.millis, |
| stop_time.seconds, stop_time.millis, |
| *spent_time_in_ms); |
| |
| exit: |
| return res; |
| } |
| |
| static TEE_Result test_read(TEE_ObjectHandle object, size_t data_size, |
| uint8_t *chunk_buf, size_t chunk_size, |
| uint32_t *spent_time_in_ms) |
| { |
| TEE_Time start_time = { }; |
| TEE_Time stop_time = { }; |
| size_t remain_bytes = data_size; |
| TEE_Result res = TEE_SUCCESS; |
| size_t read_bytes = 0; |
| |
| TEE_GetSystemTime(&start_time); |
| |
| while (remain_bytes) { |
| size_t read_size = 0; |
| |
| DMSG("Read data, remain bytes: %zu", remain_bytes); |
| if (remain_bytes < chunk_size) |
| read_size = remain_bytes; |
| else |
| read_size = chunk_size; |
| res = TEE_ReadObjectData(object, chunk_buf, read_size, |
| &read_bytes); |
| if (res != TEE_SUCCESS) { |
| EMSG("Failed to read data, res=0x%08x", res); |
| goto exit; |
| } |
| |
| remain_bytes -= read_size; |
| } |
| |
| TEE_GetSystemTime(&stop_time); |
| |
| *spent_time_in_ms = get_delta_time_in_ms(start_time, stop_time); |
| |
| IMSG("start: %u.%u(s), stop: %u.%u(s), delta: %u(ms)", |
| start_time.seconds, start_time.millis, |
| stop_time.seconds, stop_time.millis, |
| *spent_time_in_ms); |
| |
| exit: |
| return res; |
| } |
| |
| static TEE_Result test_rewrite(TEE_ObjectHandle object, size_t data_size, |
| uint8_t *chunk_buf, size_t chunk_size, |
| uint32_t *spent_time_in_ms) |
| { |
| TEE_Time start_time = { }; |
| TEE_Time stop_time = { }; |
| size_t remain_bytes = data_size; |
| TEE_Result res = TEE_SUCCESS; |
| size_t read_bytes = 0; |
| |
| TEE_GetSystemTime(&start_time); |
| |
| while (remain_bytes) { |
| size_t write_size = 0; |
| int32_t negative_chunk_size = 0; |
| |
| if (remain_bytes < chunk_size) |
| write_size = remain_bytes; |
| else |
| write_size = chunk_size; |
| negative_chunk_size = -(int32_t)write_size; |
| |
| /* Read a chunk */ |
| res = TEE_ReadObjectData(object, chunk_buf, write_size, |
| &read_bytes); |
| if (res != TEE_SUCCESS) { |
| EMSG("Failed to read data, res=0x%08x", res); |
| goto exit; |
| } |
| |
| if (read_bytes != write_size) { |
| EMSG("Partial data read, bytes=%zu", read_bytes); |
| res = TEE_ERROR_CORRUPT_OBJECT; |
| goto exit; |
| } |
| |
| /* Seek to the position before read */ |
| res = TEE_SeekObjectData(object, negative_chunk_size, |
| TEE_DATA_SEEK_CUR); |
| if (res != TEE_SUCCESS) { |
| EMSG("Failed to seek to previous offset"); |
| goto exit; |
| } |
| |
| /* Write a chunk*/ |
| res = TEE_WriteObjectData(object, chunk_buf, write_size); |
| if (res != TEE_SUCCESS) { |
| EMSG("Failed to write data, res=0x%08x", res); |
| goto exit; |
| } |
| |
| remain_bytes -= write_size; |
| } |
| |
| TEE_GetSystemTime(&stop_time); |
| |
| *spent_time_in_ms = get_delta_time_in_ms(start_time, stop_time); |
| |
| IMSG("start: %u.%u(s), stop: %u.%u(s), delta: %u(ms)", |
| start_time.seconds, start_time.millis, |
| stop_time.seconds, stop_time.millis, |
| *spent_time_in_ms); |
| |
| exit: |
| return res; |
| } |
| |
| static TEE_Result verify_file_data(TEE_ObjectHandle object, size_t data_size, |
| uint8_t *chunk_buf, size_t chunk_size) |
| { |
| TEE_Result res = TEE_ERROR_GENERIC; |
| size_t tmp_data_size = data_size; |
| |
| res = TEE_SeekObjectData(object, 0, TEE_DATA_SEEK_SET); |
| if (res != TEE_SUCCESS) { |
| EMSG("Failed to seek to offset 0"); |
| goto exit; |
| } |
| |
| TEE_MemFill(chunk_buf, 0, chunk_size); |
| |
| tmp_data_size = data_size; |
| while (tmp_data_size > 0) { |
| size_t read_bytes = 0; |
| |
| res = TEE_ReadObjectData(object, chunk_buf, chunk_size, |
| &read_bytes); |
| if (res != TEE_SUCCESS) { |
| EMSG("Failed to read data, res=0x%08x", res); |
| goto exit; |
| } |
| |
| if (read_bytes != chunk_size) { |
| EMSG("Data size not match"); |
| res = TEE_ERROR_CORRUPT_OBJECT; |
| goto exit; |
| } |
| |
| res = verify_buffer(chunk_buf, chunk_size); |
| if (res != TEE_SUCCESS) { |
| EMSG("Verify data failed, res=0x%08x", res); |
| goto exit; |
| } |
| |
| tmp_data_size -= chunk_size; |
| } |
| |
| exit: |
| return res; |
| } |
| |
| static TEE_Result ta_stroage_benchmark_chunk_access_test(uint32_t nCommandID, |
| uint32_t param_types, TEE_Param params[4]) |
| { |
| TEE_Result res = TEE_ERROR_GENERIC; |
| size_t data_size = 0; |
| size_t chunk_size = 0; |
| TEE_ObjectHandle object = TEE_HANDLE_NULL; |
| uint8_t *chunk_buf = NULL; |
| uint32_t *spent_time_in_ms = ¶ms[2].value.a; |
| bool do_verify = false; |
| |
| ASSERT_PARAM_TYPE(param_types, TEE_PARAM_TYPES( |
| TEE_PARAM_TYPE_VALUE_INPUT, |
| TEE_PARAM_TYPE_VALUE_INPUT, |
| TEE_PARAM_TYPE_VALUE_OUTPUT, |
| TEE_PARAM_TYPE_NONE)); |
| |
| data_size = params[0].value.a; |
| chunk_size = params[0].value.b; |
| do_verify = params[1].value.a; |
| |
| if (data_size == 0) |
| data_size = DEFAULT_DATA_SIZE; |
| |
| if (chunk_size == 0) |
| chunk_size = DEFAULT_CHUNK_SIZE; |
| |
| IMSG("command id: %u, test data size: %zd, chunk size: %zd", |
| nCommandID, data_size, chunk_size); |
| |
| chunk_buf = TEE_Malloc(chunk_size, TEE_MALLOC_FILL_ZERO); |
| if (!chunk_buf) { |
| EMSG("Failed to allocate memory"); |
| res = TEE_ERROR_OUT_OF_MEMORY; |
| goto exit; |
| } |
| |
| fill_buffer(chunk_buf, chunk_size); |
| res = prepare_test_file(data_size, chunk_buf, chunk_size); |
| if (res != TEE_SUCCESS) { |
| EMSG("Failed to create test file, res=0x%08x", |
| res); |
| goto exit_free_chunk_buf; |
| } |
| |
| res = TEE_OpenPersistentObject(TEE_STORAGE_PRIVATE, |
| filename, sizeof(filename), |
| TEE_DATA_FLAG_ACCESS_READ | |
| TEE_DATA_FLAG_ACCESS_WRITE | |
| TEE_DATA_FLAG_ACCESS_WRITE_META, |
| &object); |
| if (res != TEE_SUCCESS) { |
| EMSG("Failed to open persistent object, res=0x%08x", |
| res); |
| goto exit_remove_object; |
| } |
| |
| switch (nCommandID) { |
| case TA_STORAGE_BENCHMARK_CMD_TEST_READ: |
| res = test_read(object, data_size, chunk_buf, |
| chunk_size, spent_time_in_ms); |
| break; |
| |
| case TA_STORAGE_BENCHMARK_CMD_TEST_WRITE: |
| res = test_write(object, data_size, chunk_buf, |
| chunk_size, spent_time_in_ms); |
| break; |
| |
| case TA_STORAGE_BENCHMARK_CMD_TEST_REWRITE: |
| res = test_rewrite(object, data_size, chunk_buf, |
| chunk_size, spent_time_in_ms); |
| break; |
| |
| default: |
| res = TEE_ERROR_BAD_PARAMETERS; |
| } |
| |
| if (res != TEE_SUCCESS) |
| goto exit_remove_object; |
| |
| if (do_verify) |
| res = verify_file_data(object, data_size, |
| chunk_buf, chunk_size); |
| |
| |
| exit_remove_object: |
| TEE_CloseAndDeletePersistentObject1(object); |
| exit_free_chunk_buf: |
| TEE_Free(chunk_buf); |
| exit: |
| |
| return res; |
| } |
| |
| TEE_Result ta_storage_benchmark_cmd_handler(uint32_t nCommandID, |
| uint32_t param_types, TEE_Param params[4]) |
| { |
| TEE_Result res = TEE_ERROR_GENERIC; |
| |
| switch (nCommandID) { |
| case TA_STORAGE_BENCHMARK_CMD_TEST_READ: |
| case TA_STORAGE_BENCHMARK_CMD_TEST_WRITE: |
| case TA_STORAGE_BENCHMARK_CMD_TEST_REWRITE: |
| res = ta_stroage_benchmark_chunk_access_test(nCommandID, |
| param_types, params); |
| break; |
| |
| default: |
| res = TEE_ERROR_BAD_PARAMETERS; |
| } |
| |
| return res; |
| } |
| |