// SPDX-License-Identifier: BSD-2-Clause /* * Copyright (c) 2015, Linaro Limited * All rights reserved. */ #include #include #include #include #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; uint32_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; uint32_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=%u", 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) { uint32_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\n", 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 | TEE_DATA_FLAG_OVERWRITE, &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; }