1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2015, Linaro Limited
4  * All rights reserved.
5  */
6 
7 #include <tee_api.h>
8 #include <storage_benchmark.h>
9 #include <ta_storage_benchmark.h>
10 #include <tee_internal_api_extensions.h>
11 
12 #define DEFAULT_CHUNK_SIZE (1 << 10)
13 #define DEFAULT_DATA_SIZE (1024)
14 
15 #define SCRAMBLE(x) ((x & 0xff) ^ 0xaa)
16 
17 #define ASSERT_PARAM_TYPE(pt_in, pt_expect) \
18 do { \
19 	if ((pt_in) != (pt_expect)) \
20 		return TEE_ERROR_BAD_PARAMETERS; \
21 } while (0)
22 
23 static uint8_t filename[] = "BenchmarkTestFile";
24 
fill_buffer(uint8_t * buf,size_t size)25 static void fill_buffer(uint8_t *buf, size_t size)
26 {
27 	size_t i = 0;
28 
29 	if (!buf)
30 		return;
31 
32 	for (i = 0; i < size; i++)
33 		buf[i] = SCRAMBLE(i);
34 }
35 
verify_buffer(uint8_t * buf,size_t size)36 static TEE_Result verify_buffer(uint8_t *buf, size_t size)
37 {
38 	size_t i = 0;
39 
40 	if (!buf)
41 		return TEE_ERROR_BAD_PARAMETERS;
42 
43 	for (i = 0; i < size; i++) {
44 		uint8_t expect_data = SCRAMBLE(i);
45 
46 		if (expect_data != buf[i]) {
47 			return TEE_ERROR_CORRUPT_OBJECT;
48 		}
49 	}
50 
51 	return TEE_SUCCESS;
52 }
53 
tee_time_to_ms(TEE_Time t)54 static inline uint32_t tee_time_to_ms(TEE_Time t)
55 {
56 	return t.seconds * 1000 + t.millis;
57 }
58 
get_delta_time_in_ms(TEE_Time start,TEE_Time stop)59 static inline uint32_t get_delta_time_in_ms(TEE_Time start, TEE_Time stop)
60 {
61 	return tee_time_to_ms(stop) - tee_time_to_ms(start);
62 }
63 
prepare_test_file(size_t data_size,uint8_t * chunk_buf,size_t chunk_size)64 static TEE_Result prepare_test_file(size_t data_size, uint8_t *chunk_buf,
65 				size_t chunk_size)
66 {
67 	size_t remain_bytes = data_size;
68 	TEE_Result res = TEE_SUCCESS;
69 	TEE_ObjectHandle object = TEE_HANDLE_NULL;
70 
71 	res = TEE_CreatePersistentObject(TEE_STORAGE_PRIVATE,
72 			filename, sizeof(filename),
73 			TEE_DATA_FLAG_ACCESS_READ |
74 			TEE_DATA_FLAG_ACCESS_WRITE |
75 			TEE_DATA_FLAG_ACCESS_WRITE_META |
76 			TEE_DATA_FLAG_OVERWRITE,
77 			NULL, NULL, 0, &object);
78 	if (res != TEE_SUCCESS) {
79 		EMSG("Failed to create persistent object, res=0x%08x",
80 				res);
81 		goto exit;
82 	}
83 
84 	while (remain_bytes) {
85 		size_t write_size = 0;
86 
87 		if (remain_bytes < chunk_size)
88 			write_size = remain_bytes;
89 		else
90 			write_size = chunk_size;
91 		res = TEE_WriteObjectData(object, chunk_buf, write_size);
92 		if (res != TEE_SUCCESS) {
93 			EMSG("Failed to write data, res=0x%08x", res);
94 			goto exit_close_object;
95 		}
96 		remain_bytes -= write_size;
97 	}
98 exit_close_object:
99 	TEE_CloseObject(object);
100 exit:
101 	return res;
102 }
103 
test_write(TEE_ObjectHandle object,size_t data_size,uint8_t * chunk_buf,size_t chunk_size,uint32_t * spent_time_in_ms)104 static TEE_Result test_write(TEE_ObjectHandle object, size_t data_size,
105 		uint8_t *chunk_buf, size_t chunk_size,
106 		uint32_t *spent_time_in_ms)
107 {
108 	TEE_Time start_time = { };
109 	TEE_Time stop_time = { };
110 	size_t remain_bytes = data_size;
111 	TEE_Result res = TEE_SUCCESS;
112 
113 	TEE_GetSystemTime(&start_time);
114 
115 	while (remain_bytes) {
116 		size_t write_size = 0;
117 
118 		DMSG("Write data, remain bytes: %zu", remain_bytes);
119 		if (chunk_size > remain_bytes)
120 			write_size = remain_bytes;
121 		else
122 			write_size = chunk_size;
123 		res = TEE_WriteObjectData(object, chunk_buf, write_size);
124 		if (res != TEE_SUCCESS) {
125 			EMSG("Failed to write data, res=0x%08x", res);
126 			goto exit;
127 		}
128 		remain_bytes -= write_size;
129 	}
130 
131 	TEE_GetSystemTime(&stop_time);
132 
133 	*spent_time_in_ms = get_delta_time_in_ms(start_time, stop_time);
134 
135 	IMSG("start: %u.%u(s), stop: %u.%u(s), delta: %u(ms)",
136 			start_time.seconds, start_time.millis,
137 			stop_time.seconds, stop_time.millis,
138 			*spent_time_in_ms);
139 
140 exit:
141 	return res;
142 }
143 
test_read(TEE_ObjectHandle object,size_t data_size,uint8_t * chunk_buf,size_t chunk_size,uint32_t * spent_time_in_ms)144 static TEE_Result test_read(TEE_ObjectHandle object, size_t data_size,
145 		uint8_t *chunk_buf, size_t chunk_size,
146 		uint32_t *spent_time_in_ms)
147 {
148 	TEE_Time start_time = { };
149 	TEE_Time stop_time = { };
150 	size_t remain_bytes = data_size;
151 	TEE_Result res = TEE_SUCCESS;
152 	uint32_t read_bytes = 0;
153 
154 	TEE_GetSystemTime(&start_time);
155 
156 	while (remain_bytes) {
157 		size_t read_size = 0;
158 
159 		DMSG("Read data, remain bytes: %zu", remain_bytes);
160 		if (remain_bytes < chunk_size)
161 			read_size = remain_bytes;
162 		else
163 			read_size = chunk_size;
164 		res = TEE_ReadObjectData(object, chunk_buf, read_size,
165 				&read_bytes);
166 		if (res != TEE_SUCCESS) {
167 			EMSG("Failed to read data, res=0x%08x", res);
168 			goto exit;
169 		}
170 
171 		remain_bytes -= read_size;
172 	}
173 
174 	TEE_GetSystemTime(&stop_time);
175 
176 	*spent_time_in_ms = get_delta_time_in_ms(start_time, stop_time);
177 
178 	IMSG("start: %u.%u(s), stop: %u.%u(s), delta: %u(ms)",
179 			start_time.seconds, start_time.millis,
180 			stop_time.seconds, stop_time.millis,
181 			*spent_time_in_ms);
182 
183 exit:
184 	return res;
185 }
186 
test_rewrite(TEE_ObjectHandle object,size_t data_size,uint8_t * chunk_buf,size_t chunk_size,uint32_t * spent_time_in_ms)187 static TEE_Result test_rewrite(TEE_ObjectHandle object, size_t data_size,
188 		uint8_t *chunk_buf, size_t chunk_size,
189 		uint32_t *spent_time_in_ms)
190 {
191 	TEE_Time start_time = { };
192 	TEE_Time stop_time = { };
193 	size_t remain_bytes = data_size;
194 	TEE_Result res = TEE_SUCCESS;
195 	uint32_t read_bytes = 0;
196 
197 	TEE_GetSystemTime(&start_time);
198 
199 	while (remain_bytes) {
200 		size_t write_size = 0;
201 		int32_t negative_chunk_size = 0;
202 
203 		if (remain_bytes < chunk_size)
204 			write_size = remain_bytes;
205 		else
206 			write_size = chunk_size;
207 		negative_chunk_size = -(int32_t)write_size;
208 
209 		/* Read a chunk */
210 		res = TEE_ReadObjectData(object, chunk_buf, write_size,
211 				&read_bytes);
212 		if (res != TEE_SUCCESS) {
213 			EMSG("Failed to read data, res=0x%08x", res);
214 			goto exit;
215 		}
216 
217 		if (read_bytes != write_size) {
218 			EMSG("Partial data read, bytes=%u", read_bytes);
219 			res = TEE_ERROR_CORRUPT_OBJECT;
220 			goto exit;
221 		}
222 
223 		/* Seek to the position before read */
224 		res = TEE_SeekObjectData(object, negative_chunk_size,
225 				TEE_DATA_SEEK_CUR);
226 		if (res != TEE_SUCCESS) {
227 			EMSG("Failed to seek to previous offset");
228 			goto exit;
229 		}
230 
231 		/* Write a chunk*/
232 		res = TEE_WriteObjectData(object, chunk_buf, write_size);
233 		if (res != TEE_SUCCESS) {
234 			EMSG("Failed to write data, res=0x%08x", res);
235 			goto exit;
236 		}
237 
238 		remain_bytes -= write_size;
239 	}
240 
241 	TEE_GetSystemTime(&stop_time);
242 
243 	*spent_time_in_ms = get_delta_time_in_ms(start_time, stop_time);
244 
245 	IMSG("start: %u.%u(s), stop: %u.%u(s), delta: %u(ms)",
246 			start_time.seconds, start_time.millis,
247 			stop_time.seconds, stop_time.millis,
248 			*spent_time_in_ms);
249 
250 exit:
251 	return res;
252 }
253 
verify_file_data(TEE_ObjectHandle object,size_t data_size,uint8_t * chunk_buf,size_t chunk_size)254 static TEE_Result verify_file_data(TEE_ObjectHandle object, size_t data_size,
255 		uint8_t *chunk_buf, size_t chunk_size)
256 {
257 	TEE_Result res = TEE_ERROR_GENERIC;
258 	size_t tmp_data_size = data_size;
259 
260 	res = TEE_SeekObjectData(object, 0, TEE_DATA_SEEK_SET);
261 	if (res != TEE_SUCCESS) {
262 		EMSG("Failed to seek to offset 0");
263 		goto exit;
264 	}
265 
266 	TEE_MemFill(chunk_buf, 0, chunk_size);
267 
268 	tmp_data_size = data_size;
269 	while (tmp_data_size > 0) {
270 		uint32_t read_bytes = 0;
271 
272 		res = TEE_ReadObjectData(object, chunk_buf, chunk_size,
273 				&read_bytes);
274 		if (res != TEE_SUCCESS) {
275 			EMSG("Failed to read data, res=0x%08x", res);
276 			goto exit;
277 		}
278 
279 		if (read_bytes != chunk_size) {
280 			EMSG("Data size not match");
281 			res = TEE_ERROR_CORRUPT_OBJECT;
282 			goto exit;
283 		}
284 
285 		res = verify_buffer(chunk_buf, chunk_size);
286 		if (res != TEE_SUCCESS) {
287 			EMSG("Verify data failed, res=0x%08x", res);
288 			goto exit;
289 		}
290 
291 		tmp_data_size -= chunk_size;
292 	}
293 
294 exit:
295 	return res;
296 }
297 
ta_stroage_benchmark_chunk_access_test(uint32_t nCommandID,uint32_t param_types,TEE_Param params[4])298 static TEE_Result ta_stroage_benchmark_chunk_access_test(uint32_t nCommandID,
299 		uint32_t param_types, TEE_Param params[4])
300 {
301 	TEE_Result res = TEE_ERROR_GENERIC;
302 	size_t data_size = 0;
303 	size_t chunk_size = 0;
304 	TEE_ObjectHandle object = TEE_HANDLE_NULL;
305 	uint8_t *chunk_buf = NULL;
306 	uint32_t *spent_time_in_ms = &params[2].value.a;
307 	bool do_verify = false;
308 
309 	ASSERT_PARAM_TYPE(param_types, TEE_PARAM_TYPES(
310 					TEE_PARAM_TYPE_VALUE_INPUT,
311 					TEE_PARAM_TYPE_VALUE_INPUT,
312 					TEE_PARAM_TYPE_VALUE_OUTPUT,
313 					TEE_PARAM_TYPE_NONE));
314 
315 	data_size = params[0].value.a;
316 	chunk_size = params[0].value.b;
317 	do_verify = params[1].value.a;
318 
319 	if (data_size == 0)
320 		data_size = DEFAULT_DATA_SIZE;
321 
322 	if (chunk_size == 0)
323 		chunk_size = DEFAULT_CHUNK_SIZE;
324 
325 	IMSG("command id: %u, test data size: %zd, chunk size: %zd\n",
326 			nCommandID, data_size, chunk_size);
327 
328 	chunk_buf = TEE_Malloc(chunk_size, TEE_MALLOC_FILL_ZERO);
329 	if (!chunk_buf) {
330 		EMSG("Failed to allocate memory");
331 		res = TEE_ERROR_OUT_OF_MEMORY;
332 		goto exit;
333 	}
334 
335 	fill_buffer(chunk_buf, chunk_size);
336 	res = prepare_test_file(data_size, chunk_buf, chunk_size);
337 	if (res != TEE_SUCCESS) {
338 		EMSG("Failed to create test file, res=0x%08x",
339 				res);
340 		goto exit_free_chunk_buf;
341 	}
342 
343 	res = TEE_OpenPersistentObject(TEE_STORAGE_PRIVATE,
344 			filename, sizeof(filename),
345 			TEE_DATA_FLAG_ACCESS_READ |
346 			TEE_DATA_FLAG_ACCESS_WRITE |
347 			TEE_DATA_FLAG_ACCESS_WRITE_META,
348 			&object);
349 	if (res != TEE_SUCCESS) {
350 		EMSG("Failed to open persistent object, res=0x%08x",
351 				res);
352 		goto exit_remove_object;
353 	}
354 
355 	switch (nCommandID) {
356 	case TA_STORAGE_BENCHMARK_CMD_TEST_READ:
357 		res = test_read(object, data_size, chunk_buf,
358 				chunk_size, spent_time_in_ms);
359 		break;
360 
361 	case TA_STORAGE_BENCHMARK_CMD_TEST_WRITE:
362 		res = test_write(object, data_size, chunk_buf,
363 				chunk_size, spent_time_in_ms);
364 		break;
365 
366 	case TA_STORAGE_BENCHMARK_CMD_TEST_REWRITE:
367 		res = test_rewrite(object, data_size, chunk_buf,
368 				chunk_size, spent_time_in_ms);
369 		break;
370 
371 	default:
372 		res = TEE_ERROR_BAD_PARAMETERS;
373 	}
374 
375 	if (res != TEE_SUCCESS)
376 		goto exit_remove_object;
377 
378 	if (do_verify)
379 		res = verify_file_data(object, data_size,
380 				chunk_buf, chunk_size);
381 
382 
383 exit_remove_object:
384 	TEE_CloseAndDeletePersistentObject1(object);
385 exit_free_chunk_buf:
386 	TEE_Free(chunk_buf);
387 exit:
388 
389 	return res;
390 }
391 
ta_storage_benchmark_cmd_handler(uint32_t nCommandID,uint32_t param_types,TEE_Param params[4])392 TEE_Result ta_storage_benchmark_cmd_handler(uint32_t nCommandID,
393 		uint32_t param_types, TEE_Param params[4])
394 {
395 	TEE_Result res = TEE_ERROR_GENERIC;
396 
397 	switch (nCommandID) {
398 	case TA_STORAGE_BENCHMARK_CMD_TEST_READ:
399 	case TA_STORAGE_BENCHMARK_CMD_TEST_WRITE:
400 	case TA_STORAGE_BENCHMARK_CMD_TEST_REWRITE:
401 		res = ta_stroage_benchmark_chunk_access_test(nCommandID,
402 				param_types, params);
403 		break;
404 
405 	default:
406 		res = TEE_ERROR_BAD_PARAMETERS;
407 	}
408 
409 	return res;
410 }
411 
412