2 * Copyright (c) 2017, Linaro Limited
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
30 #include <tee/fs_htree.h>
31 #include <tee/tee_fs_rpc.h>
33 #include <types_ext.h>
36 #include "core_self_tests.h"
39 * The smallest blocks size that can hold two struct
40 * tee_fs_htree_node_image or two struct tee_fs_htree_image.
42 #define TEST_BLOCK_SIZE 144
51 static TEE_Result test_get_offs_size(enum tee_fs_htree_type type, size_t idx,
52 uint8_t vers, size_t *offs, size_t *size)
54 const size_t node_size = sizeof(struct tee_fs_htree_node_image);
55 const size_t block_nodes = TEST_BLOCK_SIZE / (node_size * 2);
59 COMPILE_TIME_ASSERT(TEST_BLOCK_SIZE >
60 sizeof(struct tee_fs_htree_node_image) * 2);
61 COMPILE_TIME_ASSERT(TEST_BLOCK_SIZE >
62 sizeof(struct tee_fs_htree_image) * 2);
64 assert(vers == 0 || vers == 1);
70 * tee_fs_htree_image vers 0 @ offs = 0
71 * tee_fs_htree_image vers 1 @ offs = sizeof(tee_fs_htree_image)
74 * tee_fs_htree_node_image 0 vers 0 @ offs = 0
75 * tee_fs_htree_node_image 0 vers 1 @ offs = node_size
81 * tee_fs_htree_node_image 1 vers 0 @ offs = 0
82 * tee_fs_htree_node_image 1 vers 1 @ offs = node_size
91 case TEE_FS_HTREE_TYPE_HEAD:
92 *offs = sizeof(struct tee_fs_htree_image) * vers;
93 *size = sizeof(struct tee_fs_htree_image);
95 case TEE_FS_HTREE_TYPE_NODE:
96 pbn = 1 + ((idx / block_nodes) * block_nodes * 2);
97 *offs = pbn * TEST_BLOCK_SIZE +
98 2 * node_size * (idx % block_nodes) +
102 case TEE_FS_HTREE_TYPE_BLOCK:
103 bidx = 2 * idx + vers;
104 pbn = 2 + bidx + bidx / (block_nodes * 2 - 1);
105 *offs = pbn * TEST_BLOCK_SIZE;
106 *size = TEST_BLOCK_SIZE;
109 return TEE_ERROR_GENERIC;
113 static TEE_Result test_read_init(void *aux, struct tee_fs_rpc_operation *op,
114 enum tee_fs_htree_type type, size_t idx,
115 uint8_t vers, void **data)
118 struct test_aux *a = aux;
122 res = test_get_offs_size(type, idx, vers, &offs, &sz);
123 if (res == TEE_SUCCESS) {
124 memset(op, 0, sizeof(*op));
125 op->params[0].u.value.a = (vaddr_t)aux;
126 op->params[0].u.value.b = offs;
127 op->params[0].u.value.c = sz;
134 static void *uint_to_ptr(uintptr_t p)
139 static TEE_Result test_read_final(struct tee_fs_rpc_operation *op,
142 struct test_aux *a = uint_to_ptr(op->params[0].u.value.a);
143 size_t offs = op->params[0].u.value.b;
144 size_t sz = op->params[0].u.value.c;
146 if (offs + sz <= a->data_len)
148 else if (offs <= a->data_len)
149 *bytes = a->data_len - offs;
153 memcpy(a->block, a->data + offs, *bytes);
157 static TEE_Result test_write_init(void *aux, struct tee_fs_rpc_operation *op,
158 enum tee_fs_htree_type type, size_t idx,
159 uint8_t vers, void **data)
161 return test_read_init(aux, op, type, idx, vers, data);
164 static TEE_Result test_write_final(struct tee_fs_rpc_operation *op)
166 struct test_aux *a = uint_to_ptr(op->params[0].u.value.a);
167 size_t offs = op->params[0].u.value.b;
168 size_t sz = op->params[0].u.value.c;
169 size_t end = offs + sz;
171 if (end > a->data_alloced) {
172 EMSG("out of bounds");
173 return TEE_ERROR_GENERIC;
176 memcpy(a->data + offs, a->block, sz);
177 if (end > a->data_len)
183 static const struct tee_fs_htree_storage test_htree_ops = {
184 .block_size = TEST_BLOCK_SIZE,
185 .rpc_read_init = test_read_init,
186 .rpc_read_final = test_read_final,
187 .rpc_write_init = test_write_init,
188 .rpc_write_final = test_write_final,
191 #define CHECK_RES(res, cleanup) \
193 TEE_Result _res = (res); \
195 if (_res != TEE_SUCCESS) { \
196 EMSG("error: res = %#" PRIx32, _res); \
201 static uint32_t val_from_bn_n_salt(size_t bn, size_t n, uint8_t salt)
203 assert(bn < UINT16_MAX);
204 assert(n < UINT8_MAX);
205 return SHIFT_U32(n, 16) | SHIFT_U32(bn, 8) | salt;
208 static TEE_Result write_block(struct tee_fs_htree **ht, size_t bn, uint8_t salt)
210 uint32_t b[TEST_BLOCK_SIZE / sizeof(uint32_t)];
213 for (n = 0; n < ARRAY_SIZE(b); n++)
214 b[n] = val_from_bn_n_salt(bn, n, salt);
216 return tee_fs_htree_write_block(ht, bn, b);
219 static TEE_Result read_block(struct tee_fs_htree **ht, size_t bn, uint8_t salt)
222 uint32_t b[TEST_BLOCK_SIZE / sizeof(uint32_t)];
225 res = tee_fs_htree_read_block(ht, bn, b);
226 if (res != TEE_SUCCESS)
229 for (n = 0; n < ARRAY_SIZE(b); n++) {
230 if (b[n] != val_from_bn_n_salt(bn, n, salt)) {
231 DMSG("Unpected b[%zu] %#" PRIx32
232 "(expected %#" PRIx32 ")",
233 n, b[n], val_from_bn_n_salt(bn, n, salt));
234 return TEE_ERROR_SECURITY;
241 static TEE_Result do_range(TEE_Result (*fn)(struct tee_fs_htree **ht,
242 size_t bn, uint8_t salt),
243 struct tee_fs_htree **ht, size_t begin,
244 size_t num_blocks, size_t salt)
246 TEE_Result res = TEE_SUCCESS;
249 for (n = 0; n < num_blocks; n++) {
250 res = fn(ht, n + begin, salt);
251 CHECK_RES(res, goto out);
258 static TEE_Result do_range_backwards(TEE_Result (*fn)(struct tee_fs_htree **ht,
259 size_t bn, uint8_t salt),
260 struct tee_fs_htree **ht, size_t begin,
261 size_t num_blocks, size_t salt)
263 TEE_Result res = TEE_SUCCESS;
266 for (n = 0; n < num_blocks; n++) {
267 res = fn(ht, num_blocks - 1 - n + begin, salt);
268 CHECK_RES(res, goto out);
275 static TEE_Result htree_test_rewrite(struct test_aux *aux, size_t num_blocks,
276 size_t w_unsync_begin, size_t w_unsync_num)
279 struct tee_fs_htree *ht = NULL;
282 assert((w_unsync_begin + w_unsync_num) <= num_blocks);
285 memset(aux->data, 0xce, aux->data_alloced);
287 res = tee_fs_htree_open(true, &test_htree_ops, aux, &ht);
288 CHECK_RES(res, goto out);
291 * Intialize all blocks and verify that they read back as
294 res = do_range(write_block, &ht, 0, num_blocks, salt);
295 CHECK_RES(res, goto out);
297 res = do_range(read_block, &ht, 0, num_blocks, salt);
298 CHECK_RES(res, goto out);
301 * Write all blocks again, but starting from the end using a new
302 * salt, then verify that that read back as expected.
305 res = do_range_backwards(write_block, &ht, 0, num_blocks, salt);
306 CHECK_RES(res, goto out);
308 res = do_range(read_block, &ht, 0, num_blocks, salt);
309 CHECK_RES(res, goto out);
312 * Use a new salt to write all blocks once more and verify that
313 * they read back as expected.
316 res = do_range(write_block, &ht, 0, num_blocks, salt);
317 CHECK_RES(res, goto out);
319 res = do_range(read_block, &ht, 0, num_blocks, salt);
320 CHECK_RES(res, goto out);
323 * Sync the changes of the nodes to memory, verify that all
324 * blocks are read back as expected.
326 res = tee_fs_htree_sync_to_storage(&ht);
327 CHECK_RES(res, goto out);
329 res = do_range(read_block, &ht, 0, num_blocks, salt);
330 CHECK_RES(res, goto out);
333 * Close and reopen the hash-tree
335 tee_fs_htree_close(&ht);
336 res = tee_fs_htree_open(false, &test_htree_ops, aux, &ht);
337 CHECK_RES(res, goto out);
340 * Verify that all blocks are read as expected.
342 res = do_range(read_block, &ht, 0, num_blocks, salt);
343 CHECK_RES(res, goto out);
346 * Rewrite a few blocks and verify that all blocks are read as
349 res = do_range_backwards(write_block, &ht, w_unsync_begin, w_unsync_num,
351 CHECK_RES(res, goto out);
353 res = do_range(read_block, &ht, 0, w_unsync_begin, salt);
354 CHECK_RES(res, goto out);
355 res = do_range(read_block, &ht, w_unsync_begin, w_unsync_num, salt + 1);
356 CHECK_RES(res, goto out);
357 res = do_range(read_block, &ht, w_unsync_begin + w_unsync_num,
358 num_blocks - (w_unsync_begin + w_unsync_num), salt);
359 CHECK_RES(res, goto out);
362 * Rewrite the blocks from above again with another salt and
363 * verify that they are read back as expected.
365 res = do_range(write_block, &ht, w_unsync_begin, w_unsync_num,
367 CHECK_RES(res, goto out);
369 res = do_range(read_block, &ht, 0, w_unsync_begin, salt);
370 CHECK_RES(res, goto out);
371 res = do_range(read_block, &ht, w_unsync_begin, w_unsync_num, salt + 2);
372 CHECK_RES(res, goto out);
373 res = do_range(read_block, &ht, w_unsync_begin + w_unsync_num,
374 num_blocks - (w_unsync_begin + w_unsync_num), salt);
375 CHECK_RES(res, goto out);
378 * Skip tee_fs_htree_sync_to_storage() and call
379 * tee_fs_htree_close() directly to undo the changes since last
380 * call to tee_fs_htree_sync_to_storage(). Reopen the hash-tree
381 * and verify that recent changes indeed was discarded.
383 tee_fs_htree_close(&ht);
384 res = tee_fs_htree_open(false, &test_htree_ops, aux, &ht);
385 CHECK_RES(res, goto out);
387 res = do_range(read_block, &ht, 0, num_blocks, salt);
388 CHECK_RES(res, goto out);
391 * Close, reopen and verify that all blocks are read as expected
394 tee_fs_htree_close(&ht);
395 res = tee_fs_htree_open(false, &test_htree_ops, aux, &ht);
396 CHECK_RES(res, goto out);
398 res = do_range(read_block, &ht, 0, num_blocks, salt);
399 CHECK_RES(res, goto out);
402 tee_fs_htree_close(&ht);
406 TEE_Result core_fs_htree_tests(uint32_t nParamTypes,
407 TEE_Param pParams[TEE_NUM_PARAMS] __unused)
411 size_t num_blocks = 10;
419 return TEE_ERROR_BAD_PARAMETERS;
421 res = test_get_offs_size(TEE_FS_HTREE_TYPE_BLOCK, num_blocks, 1,
423 CHECK_RES(res, return res);
425 aux.data_alloced = offs + sz;
426 aux.data = malloc(aux.data_alloced);
427 aux.block = malloc(TEST_BLOCK_SIZE);
428 if (!aux.data || !aux.block) {
429 res = TEE_ERROR_OUT_OF_MEMORY;
434 * n is the number of block we're going to initialize/use.
435 * m is the offset from where we'll rewrite blocks and expect
436 * the changes to be visible until tee_fs_htree_close() is called
437 * without a call to tee_fs_htree_sync_to_storage() before.
438 * o is the number of blocks we're rewriting starting at m.
440 for (n = 0; n < num_blocks; n += 3) {
441 for (m = 0; m < n; m += 3) {
442 for (o = 0; o < (n - m); o++) {
443 res = htree_test_rewrite(&aux, n, m, o);
444 CHECK_RES(res, goto out);