xref: /optee_os/core/tee/tee_rpmb_fs.c (revision 7892cb1bcf8618990ed87458b898b37d6351428f)
1 /*
2  * Copyright (c) 2014, STMicroelectronics International N.V.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  *
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.
14  *
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.
26  */
27 
28 #include <kernel/tee_common.h>
29 #include <kernel/handle.h>
30 #include <tee/tee_rpmb_fs.h>
31 #include <tee/tee_rpmb.h>
32 #include <tee/tee_fs_defs.h>
33 #include <tee/tee_fs.h>
34 #include <tee/tee_fs_key_manager.h>
35 #include <mm/tee_mm.h>
36 #include <trace.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <string_ext.h>
40 #include <util.h>
41 #include <sys/queue.h>
42 
43 #ifdef CFG_ENC_FS
44 #include <tee/tee_cryp_provider.h>
45 #endif
46 
47 #define RPMB_STORAGE_START_ADDRESS      0
48 #define RPMB_FS_FAT_START_ADDRESS       512
49 #define RPMB_BLOCK_SIZE_SHIFT           8
50 
51 #define RPMB_FS_MAGIC                   0x52504D42
52 #define FS_VERSION                      2
53 #define N_ENTRIES                       8
54 
55 #define FILE_IS_ACTIVE                  (1u << 0)
56 #define FILE_IS_LAST_ENTRY              (1u << 1)
57 
58 /**
59  * FS parameters: Information often used by internal functions.
60  * fat_start_address will be set by rpmb_fs_setup().
61  * rpmb_fs_parameters can be read by any other function.
62  */
63 struct rpmb_fs_parameters {
64 	uint32_t fat_start_address;
65 	uint32_t max_rpmb_address;
66 };
67 
68 /**
69  * File entry for a single file in a RPMB_FS partition.
70  */
71 struct rpmb_fat_entry {
72 	uint32_t start_address;
73 	uint32_t data_size;
74 	uint32_t flags;
75 	uint32_t write_counter;
76 	uint8_t fek[TEE_FS_KM_FEK_SIZE];
77 	char filename[TEE_RPMB_FS_FILENAME_LENGTH];
78 };
79 
80 /**
81  * FAT entry context with reference to a FAT entry and its
82  * location in RPMB.
83  */
84 struct rpmb_file_handle {
85 	/* Pointer to a fat_entry */
86 	struct rpmb_fat_entry fat_entry;
87 	/* Pointer to a filename */
88 	char filename[TEE_RPMB_FS_FILENAME_LENGTH];
89 	/* Adress for current entry in RPMB */
90 	uint32_t rpmb_fat_address;
91 	/* Current position */
92 	uint32_t pos;
93 };
94 
95 /**
96  * RPMB_FS partition data
97  */
98 struct rpmb_fs_partition {
99 	uint32_t rpmb_fs_magic;
100 	uint32_t fs_version;
101 	uint32_t write_counter;
102 	uint32_t fat_start_address;
103 	/* Do not use reserved[] for other purpose than partition data. */
104 	uint8_t reserved[112];
105 };
106 
107 /**
108  * A node in a list of directory entries. entry->name is a
109  * pointer to name here.
110  */
111 struct tee_rpmb_fs_dirent {
112 	struct tee_fs_dirent entry;
113 	char name[TEE_RPMB_FS_FILENAME_LENGTH];
114 	SIMPLEQ_ENTRY(tee_rpmb_fs_dirent) link;
115 };
116 
117 /**
118  * The RPMB directory representation. It contains a queue of
119  * RPMB directory entries: 'next'.
120  * The current pointer points to the last directory entry
121  * returned by readdir().
122  */
123 struct tee_fs_dir {
124 	struct tee_rpmb_fs_dirent *current;
125 	SIMPLEQ_HEAD(next_head, tee_rpmb_fs_dirent) next;
126 };
127 
128 static TEE_Result get_fat_start_address(uint32_t *addr);
129 
130 static struct rpmb_fs_parameters *fs_par;
131 
132 static struct handle_db fs_handle_db = HANDLE_DB_INITIALIZER;
133 
134 static void dump_fat(void)
135 {
136 	TEE_Result res = TEE_ERROR_GENERIC;
137 	struct rpmb_fat_entry *fat_entries = NULL;
138 	uint32_t fat_address;
139 	size_t size;
140 	int i;
141 	bool last_entry_found = false;
142 
143 	res = get_fat_start_address(&fat_address);
144 	if (res != TEE_SUCCESS)
145 		goto out;
146 
147 	size = N_ENTRIES * sizeof(struct rpmb_fat_entry);
148 	fat_entries = malloc(size);
149 	if (!fat_entries) {
150 		res = TEE_ERROR_OUT_OF_MEMORY;
151 		goto out;
152 	}
153 
154 	while (!last_entry_found) {
155 		res = tee_rpmb_read(CFG_RPMB_FS_DEV_ID, fat_address,
156 				    (uint8_t *)fat_entries, size, NULL);
157 		if (res != TEE_SUCCESS)
158 			goto out;
159 
160 		for (i = 0; i < N_ENTRIES; i++) {
161 
162 			FMSG("flags 0x%x, size %d, address 0x%x, filename '%s'",
163 				fat_entries[i].flags,
164 				fat_entries[i].data_size,
165 				fat_entries[i].start_address,
166 				fat_entries[i].filename);
167 
168 			if ((fat_entries[i].flags & FILE_IS_LAST_ENTRY) != 0) {
169 				last_entry_found = true;
170 				break;
171 			}
172 
173 			/* Move to next fat_entry. */
174 			fat_address += sizeof(struct rpmb_fat_entry);
175 		}
176 	}
177 
178 out:
179 	free(fat_entries);
180 }
181 
182 #if (TRACE_LEVEL >= TRACE_DEBUG)
183 static void dump_fh(struct rpmb_file_handle *fh)
184 {
185 	DMSG("fh->filename=%s", fh->filename);
186 	DMSG("fh->pos=%u", fh->pos);
187 	DMSG("fh->rpmb_fat_address=%u", fh->rpmb_fat_address);
188 	DMSG("fh->fat_entry.start_address=%u", fh->fat_entry.start_address);
189 	DMSG("fh->fat_entry.data_size=%u", fh->fat_entry.data_size);
190 }
191 #else
192 static void dump_fh(struct rpmb_file_handle *fh __unused)
193 {
194 }
195 #endif
196 
197 static struct rpmb_file_handle *alloc_file_handle(const char *filename)
198 {
199 	struct rpmb_file_handle *fh = NULL;
200 
201 	fh = calloc(1, sizeof(struct rpmb_file_handle));
202 	if (!fh)
203 		return NULL;
204 
205 	if (filename)
206 		strlcpy(fh->filename, filename, sizeof(fh->filename));
207 
208 	return fh;
209 }
210 
211 /**
212  * write_fat_entry: Store info in a fat_entry to RPMB.
213  */
214 static TEE_Result write_fat_entry(struct rpmb_file_handle *fh,
215 				  bool update_write_counter)
216 {
217 	TEE_Result res = TEE_ERROR_GENERIC;
218 
219 	/* Protect partition data. */
220 	if (fh->rpmb_fat_address < sizeof(struct rpmb_fs_partition)) {
221 		res = TEE_ERROR_ACCESS_CONFLICT;
222 		goto out;
223 	}
224 
225 	if (fh->rpmb_fat_address % sizeof(struct rpmb_fat_entry) != 0) {
226 		res = TEE_ERROR_BAD_PARAMETERS;
227 		goto out;
228 	}
229 
230 	if (update_write_counter) {
231 		res = tee_rpmb_get_write_counter(CFG_RPMB_FS_DEV_ID,
232 						 &fh->fat_entry.write_counter);
233 		if (res != TEE_SUCCESS)
234 			goto out;
235 	}
236 
237 	res = tee_rpmb_write(CFG_RPMB_FS_DEV_ID, fh->rpmb_fat_address,
238 			     (uint8_t *)&fh->fat_entry,
239 			     sizeof(struct rpmb_fat_entry), NULL);
240 
241 	dump_fat();
242 
243 out:
244 	return res;
245 }
246 
247 /**
248  * rpmb_fs_setup: Setup rpmb fs.
249  * Set initial partition and FS values and write to RPMB.
250  * Store frequently used data in RAM.
251  */
252 static TEE_Result rpmb_fs_setup(void)
253 {
254 	TEE_Result res = TEE_ERROR_GENERIC;
255 	struct rpmb_fs_partition *partition_data = NULL;
256 	struct rpmb_file_handle *fh = NULL;
257 	uint32_t max_rpmb_block = 0;
258 
259 	if (fs_par) {
260 		res = TEE_SUCCESS;
261 		goto out;
262 	}
263 
264 	res = tee_rpmb_get_max_block(CFG_RPMB_FS_DEV_ID, &max_rpmb_block);
265 	if (res != TEE_SUCCESS)
266 		goto out;
267 
268 	partition_data = calloc(1, sizeof(struct rpmb_fs_partition));
269 	if (!partition_data) {
270 		res = TEE_ERROR_OUT_OF_MEMORY;
271 		goto out;
272 	}
273 
274 	res = tee_rpmb_read(CFG_RPMB_FS_DEV_ID, RPMB_STORAGE_START_ADDRESS,
275 			    (uint8_t *)partition_data,
276 			    sizeof(struct rpmb_fs_partition), NULL);
277 	if (res != TEE_SUCCESS)
278 		goto out;
279 
280 #ifndef CFG_RPMB_RESET_FAT
281 	if (partition_data->rpmb_fs_magic == RPMB_FS_MAGIC) {
282 		if (partition_data->fs_version == FS_VERSION) {
283 			res = TEE_SUCCESS;
284 			goto store_fs_par;
285 		} else {
286 			/* Wrong software is in use. */
287 			res = TEE_ERROR_ACCESS_DENIED;
288 			goto out;
289 		}
290 	}
291 #else
292 	EMSG("**** Clearing Storage ****");
293 #endif
294 
295 	/* Setup new partition data. */
296 	partition_data->rpmb_fs_magic = RPMB_FS_MAGIC;
297 	partition_data->fs_version = FS_VERSION;
298 	partition_data->fat_start_address = RPMB_FS_FAT_START_ADDRESS;
299 
300 	/* Initial FAT entry with FILE_IS_LAST_ENTRY flag set. */
301 	fh = alloc_file_handle(NULL);
302 	if (!fh) {
303 		res = TEE_ERROR_OUT_OF_MEMORY;
304 		goto out;
305 	}
306 	fh->fat_entry.flags = FILE_IS_LAST_ENTRY;
307 	fh->rpmb_fat_address = partition_data->fat_start_address;
308 
309 	/* Write init FAT entry and partition data to RPMB. */
310 	res = write_fat_entry(fh, true);
311 	if (res != TEE_SUCCESS)
312 		goto out;
313 
314 	res =
315 	    tee_rpmb_get_write_counter(CFG_RPMB_FS_DEV_ID,
316 				       &partition_data->write_counter);
317 	if (res != TEE_SUCCESS)
318 		goto out;
319 	res = tee_rpmb_write(CFG_RPMB_FS_DEV_ID, RPMB_STORAGE_START_ADDRESS,
320 			     (uint8_t *)partition_data,
321 			     sizeof(struct rpmb_fs_partition), NULL);
322 
323 #ifndef CFG_RPMB_RESET_FAT
324 store_fs_par:
325 #endif
326 
327 	/* Store FAT start address. */
328 	fs_par = calloc(1, sizeof(struct rpmb_fs_parameters));
329 	if (!fs_par) {
330 		res = TEE_ERROR_OUT_OF_MEMORY;
331 		goto out;
332 	}
333 
334 	fs_par->fat_start_address = partition_data->fat_start_address;
335 	fs_par->max_rpmb_address = max_rpmb_block << RPMB_BLOCK_SIZE_SHIFT;
336 
337 	dump_fat();
338 
339 out:
340 	free(fh);
341 	free(partition_data);
342 	return res;
343 }
344 
345 /**
346  * get_fat_start_address:
347  * FAT start_address from fs_par.
348  */
349 static TEE_Result get_fat_start_address(uint32_t *addr)
350 {
351 	if (!fs_par)
352 		return TEE_ERROR_NO_DATA;
353 
354 	*addr = fs_par->fat_start_address;
355 
356 	return TEE_SUCCESS;
357 }
358 
359 /**
360  * read_fat: Read FAT entries
361  * Return matching FAT entry for read, rm rename and stat.
362  * Build up memory pool and return matching entry for write operation.
363  * "Last FAT entry" can be returned during write.
364  */
365 static TEE_Result read_fat(struct rpmb_file_handle *fh, tee_mm_pool_t *p)
366 {
367 	TEE_Result res = TEE_ERROR_GENERIC;
368 	tee_mm_entry_t *mm = NULL;
369 	struct rpmb_fat_entry *fat_entries = NULL;
370 	uint32_t fat_address;
371 	size_t size;
372 	int i;
373 	bool entry_found = false;
374 	bool last_entry_found = false;
375 	bool expand_fat = false;
376 	struct rpmb_file_handle last_fh;
377 
378 	DMSG("fat_address %d", fh->rpmb_fat_address);
379 
380 	res = rpmb_fs_setup();
381 	if (res != TEE_SUCCESS)
382 		goto out;
383 
384 	res = get_fat_start_address(&fat_address);
385 	if (res != TEE_SUCCESS)
386 		goto out;
387 
388 	size = N_ENTRIES * sizeof(struct rpmb_fat_entry);
389 	fat_entries = malloc(size);
390 	if (!fat_entries) {
391 		res = TEE_ERROR_OUT_OF_MEMORY;
392 		goto out;
393 	}
394 
395 	/*
396 	 * The pool is used to represent the current RPMB layout. To find
397 	 * a slot for the file tee_mm_alloc is called on the pool. Thus
398 	 * if it is not NULL the entire FAT must be traversed to fill in
399 	 * the pool.
400 	 */
401 	while (!last_entry_found && (!entry_found || p)) {
402 		res = tee_rpmb_read(CFG_RPMB_FS_DEV_ID, fat_address,
403 				    (uint8_t *)fat_entries, size, NULL);
404 		if (res != TEE_SUCCESS)
405 			goto out;
406 
407 		for (i = 0; i < N_ENTRIES; i++) {
408 			/*
409 			 * Look for an entry, matching filenames. (read, rm,
410 			 * rename and stat.). Only store first filename match.
411 			 */
412 			if (fh->filename &&
413 			    (strcmp(fh->filename,
414 				    fat_entries[i].filename) == 0) &&
415 			    (fat_entries[i].flags & FILE_IS_ACTIVE) &&
416 			    (!entry_found)) {
417 				entry_found = true;
418 				fh->rpmb_fat_address = fat_address;
419 				memcpy(&fh->fat_entry, &fat_entries[i],
420 				       sizeof(struct rpmb_fat_entry));
421 				if (!p)
422 					break;
423 			}
424 
425 			/* Add existing files to memory pool. (write) */
426 			if (p) {
427 				if ((fat_entries[i].flags & FILE_IS_ACTIVE) &&
428 				    (fat_entries[i].data_size > 0)) {
429 
430 					mm = tee_mm_alloc2
431 						(p,
432 						 fat_entries[i].start_address,
433 						 fat_entries[i].data_size);
434 					if (!mm) {
435 						res = TEE_ERROR_OUT_OF_MEMORY;
436 						goto out;
437 					}
438 				}
439 
440 				/* Unused FAT entries can be reused (write) */
441 				if (((fat_entries[i].flags & FILE_IS_ACTIVE) ==
442 				     0) && (fh->rpmb_fat_address == 0)) {
443 					fh->rpmb_fat_address = fat_address;
444 					memcpy(&fh->fat_entry, &fat_entries[i],
445 					       sizeof(struct rpmb_fat_entry));
446 				}
447 			}
448 
449 			if ((fat_entries[i].flags & FILE_IS_LAST_ENTRY) != 0) {
450 				last_entry_found = true;
451 
452 				/*
453 				 * If the last entry was reached and was chosen
454 				 * by the previous check, then the FAT needs to
455 				 * be expanded.
456 				 * fh->rpmb_fat_address is the address chosen
457 				 * to store the files FAT entry and fat_address
458 				 * is the current FAT entry address being
459 				 * compared.
460 				 */
461 				if (p && fh->rpmb_fat_address == fat_address)
462 					expand_fat = true;
463 				break;
464 			}
465 
466 			/* Move to next fat_entry. */
467 			fat_address += sizeof(struct rpmb_fat_entry);
468 		}
469 	}
470 
471 	/*
472 	 * Represent the FAT table in the pool.
473 	 */
474 	if (p) {
475 		/*
476 		 * Since fat_address is the start of the last entry it needs to
477 		 * be moved up by an entry.
478 		 */
479 		fat_address += sizeof(struct rpmb_fat_entry);
480 
481 		/* Make room for yet a FAT entry and add to memory pool. */
482 		if (expand_fat)
483 			fat_address += sizeof(struct rpmb_fat_entry);
484 
485 		mm = tee_mm_alloc2(p, RPMB_STORAGE_START_ADDRESS, fat_address);
486 		if (!mm) {
487 			res = TEE_ERROR_OUT_OF_MEMORY;
488 			goto out;
489 		}
490 
491 		if (expand_fat) {
492 			/*
493 			 * Point fat_address to the beginning of the new
494 			 * entry.
495 			 */
496 			fat_address -= sizeof(struct rpmb_fat_entry);
497 			memset(&last_fh, 0, sizeof(last_fh));
498 			last_fh.fat_entry.flags = FILE_IS_LAST_ENTRY;
499 			last_fh.rpmb_fat_address = fat_address;
500 			res = write_fat_entry(&last_fh, true);
501 			if (res != TEE_SUCCESS)
502 				goto out;
503 		}
504 	}
505 
506 	if (fh->filename && !fh->rpmb_fat_address)
507 		res = TEE_ERROR_FILE_NOT_FOUND;
508 
509 out:
510 	free(fat_entries);
511 	return res;
512 }
513 
514 #ifdef CFG_ENC_FS
515 static bool is_zero(const uint8_t *buf, size_t size)
516 {
517 	size_t i;
518 
519 	for (i = 0; i < size; i++)
520 		if (buf[i])
521 			return false;
522 	return true;
523 }
524 
525 static TEE_Result generate_fek(struct rpmb_fat_entry *fe)
526 {
527 	TEE_Result res;
528 
529 again:
530 	res = crypto_ops.prng.read(fe->fek, sizeof(fe->fek));
531 	if (res != TEE_SUCCESS)
532 		return res;
533 
534 	if (is_zero(fe->fek, sizeof(fe->fek)))
535 		goto again;
536 
537 	return res;
538 }
539 #else
540 static TEE_Result generate_fek(struct rpmb_fat_entry *fe)
541 {
542 	memset(fe->fek, 0, sizeof(fe->fek));
543 	return TEE_SUCCESS;
544 }
545 #endif
546 
547 int tee_rpmb_fs_open(const char *file, int flags, ...)
548 {
549 	int fd = -1;
550 	struct rpmb_file_handle *fh = NULL;
551 	size_t filelen;
552 	tee_mm_pool_t p;
553 	bool pool_result;
554 	TEE_Result res = TEE_ERROR_GENERIC;
555 
556 	if (!file) {
557 		res = TEE_ERROR_BAD_PARAMETERS;
558 		goto out;
559 	}
560 
561 	filelen = strlen(file);
562 	if (filelen >= TEE_RPMB_FS_FILENAME_LENGTH - 1 || filelen == 0) {
563 		res = TEE_ERROR_BAD_PARAMETERS;
564 		goto out;
565 	}
566 
567 	if (file[filelen - 1] == '/') {
568 		res = TEE_ERROR_BAD_PARAMETERS;
569 		goto out;
570 	}
571 
572 	fh = alloc_file_handle(file);
573 	if (!fh) {
574 		res = TEE_ERROR_OUT_OF_MEMORY;
575 		goto out;
576 	}
577 
578 	/* We need to do setup in order to make sure fs_par is filled in */
579 	res = rpmb_fs_setup();
580 	if (res != TEE_SUCCESS)
581 		goto out;
582 
583 	if (flags & TEE_FS_O_CREATE) {
584 		/* Upper memory allocation must be used for RPMB_FS. */
585 		pool_result = tee_mm_init(&p,
586 					  RPMB_STORAGE_START_ADDRESS,
587 					  fs_par->max_rpmb_address,
588 					  RPMB_BLOCK_SIZE_SHIFT,
589 					  TEE_MM_POOL_HI_ALLOC);
590 
591 		if (!pool_result) {
592 			res = TEE_ERROR_OUT_OF_MEMORY;
593 			goto out;
594 		}
595 
596 		res = read_fat(fh, &p);
597 		tee_mm_final(&p);
598 		if (res != TEE_SUCCESS)
599 			goto out;
600 	} else {
601 		res = read_fat(fh, NULL);
602 		if (res != TEE_SUCCESS)
603 			goto out;
604 	}
605 
606 	/* Add the handle to the db */
607 	fd = handle_get(&fs_handle_db, fh);
608 	if (fd == -1) {
609 		res = TEE_ERROR_OUT_OF_MEMORY;
610 		goto out;
611 	}
612 
613 	/*
614 	 * If this is opened with create and the entry found was not active
615 	 * then this is a new file and the FAT entry must be written
616 	 */
617 	if (flags & TEE_FS_O_CREATE) {
618 		if ((fh->fat_entry.flags & FILE_IS_ACTIVE) == 0) {
619 			memset(&fh->fat_entry, 0,
620 				sizeof(struct rpmb_fat_entry));
621 			memcpy(fh->fat_entry.filename, file, strlen(file));
622 			/* Start address and size are 0 */
623 			fh->fat_entry.flags = FILE_IS_ACTIVE;
624 
625 			res = generate_fek(&fh->fat_entry);
626 			if (res != TEE_SUCCESS) {
627 				handle_put(&fs_handle_db, fd);
628 				fd = -1;
629 				goto out;
630 			}
631 			DMSG("GENERATE FEK key: %p",
632 			     (void *)fh->fat_entry.fek);
633 			DHEXDUMP(fh->fat_entry.fek, sizeof(fh->fat_entry.fek));
634 
635 			res = write_fat_entry(fh, true);
636 			if (res != TEE_SUCCESS) {
637 				handle_put(&fs_handle_db, fd);
638 				fd = -1;
639 				goto out;
640 			}
641 		}
642 	}
643 
644 	res = TEE_SUCCESS;
645 
646 out:
647 	if (res != TEE_SUCCESS) {
648 		if (fh)
649 			free(fh);
650 
651 		fd = -1;
652 	}
653 
654 	return fd;
655 }
656 
657 int tee_rpmb_fs_close(int fd)
658 {
659 	struct rpmb_file_handle *fh;
660 
661 	fh = handle_put(&fs_handle_db, fd);
662 	if (fh) {
663 		free(fh);
664 		return 0;
665 	}
666 
667 	return -1;
668 }
669 
670 int tee_rpmb_fs_read(int fd, uint8_t *buf, size_t size)
671 {
672 	TEE_Result res = TEE_ERROR_GENERIC;
673 	struct rpmb_file_handle *fh;
674 	int read_size = -1;
675 
676 	if (!size)
677 		return 0;
678 
679 	if (!buf) {
680 		res = TEE_ERROR_BAD_PARAMETERS;
681 		goto out;
682 	}
683 
684 	fh = handle_lookup(&fs_handle_db, fd);
685 	if (!fh) {
686 		res = TEE_ERROR_BAD_PARAMETERS;
687 		goto out;
688 	}
689 	dump_fh(fh);
690 
691 	res = read_fat(fh, NULL);
692 	if (res != TEE_SUCCESS)
693 		goto out;
694 
695 	size = MIN(size, fh->fat_entry.data_size - fh->pos);
696 	if (size > 0) {
697 		res = tee_rpmb_read(CFG_RPMB_FS_DEV_ID,
698 				    fh->fat_entry.start_address + fh->pos, buf,
699 				    size, fh->fat_entry.fek);
700 		if (res != TEE_SUCCESS)
701 			goto out;
702 	}
703 
704 	read_size = size;
705 	res = TEE_SUCCESS;
706 
707 out:
708 	if (res != TEE_SUCCESS)
709 		read_size = -1;
710 
711 	return read_size;
712 }
713 
714 int tee_rpmb_fs_write(int fd, uint8_t *buf, size_t size)
715 {
716 	TEE_Result res = TEE_ERROR_GENERIC;
717 	struct rpmb_file_handle *fh;
718 	tee_mm_pool_t p;
719 	bool pool_result = false;
720 	tee_mm_entry_t *mm;
721 	size_t end;
722 	size_t newsize;
723 	uint8_t *newbuf = NULL;
724 	uintptr_t newaddr;
725 	uint32_t start_addr;
726 
727 	if (!size)
728 		return 0;
729 
730 	if (!buf) {
731 		res = TEE_ERROR_BAD_PARAMETERS;
732 		goto out;
733 	}
734 
735 	if (!fs_par) {
736 		res = TEE_ERROR_GENERIC;
737 		goto out;
738 	}
739 
740 	fh = handle_lookup(&fs_handle_db, fd);
741 	if (!fh) {
742 		res = TEE_ERROR_BAD_PARAMETERS;
743 		goto out;
744 	}
745 	dump_fh(fh);
746 
747 	/* Upper memory allocation must be used for RPMB_FS. */
748 	pool_result = tee_mm_init(&p,
749 				  RPMB_STORAGE_START_ADDRESS,
750 				  fs_par->max_rpmb_address,
751 				  RPMB_BLOCK_SIZE_SHIFT,
752 				  TEE_MM_POOL_HI_ALLOC);
753 	if (!pool_result) {
754 		res = TEE_ERROR_OUT_OF_MEMORY;
755 		goto out;
756 	}
757 
758 	res = read_fat(fh, &p);
759 	if (res != TEE_SUCCESS)
760 		goto out;
761 
762 	TEE_ASSERT(!(fh->fat_entry.flags & FILE_IS_LAST_ENTRY));
763 
764 	end = fh->pos + size;
765 	start_addr = fh->fat_entry.start_address + fh->pos;
766 
767 	if (end <= fh->fat_entry.data_size &&
768 	    tee_rpmb_write_is_atomic(CFG_RPMB_FS_DEV_ID, start_addr, size)) {
769 
770 		DMSG("Updating data in-place");
771 		res = tee_rpmb_write(CFG_RPMB_FS_DEV_ID, start_addr, buf,
772 				     size, fh->fat_entry.fek);
773 		if (res != TEE_SUCCESS)
774 			goto out;
775 	} else {
776 		/*
777 		 * File must be extended, or update cannot be atomic: allocate,
778 		 * read, update, write.
779 		 */
780 
781 		DMSG("Need to re-allocate");
782 		newsize = MAX(end, fh->fat_entry.data_size);
783 		mm = tee_mm_alloc(&p, newsize);
784 		newbuf = calloc(newsize, 1);
785 		if (!mm || !newbuf) {
786 			res = TEE_ERROR_OUT_OF_MEMORY;
787 			goto out;
788 		}
789 
790 		if (fh->fat_entry.data_size) {
791 			res = tee_rpmb_read(CFG_RPMB_FS_DEV_ID,
792 					    fh->fat_entry.start_address,
793 					    newbuf, fh->fat_entry.data_size,
794 					    fh->fat_entry.fek);
795 			if (res != TEE_SUCCESS)
796 				goto out;
797 		}
798 
799 		memcpy(newbuf + fh->pos, buf, size);
800 
801 		newaddr = tee_mm_get_smem(mm);
802 		res = tee_rpmb_write(CFG_RPMB_FS_DEV_ID, newaddr, newbuf,
803 				     newsize, fh->fat_entry.fek);
804 		if (res != TEE_SUCCESS)
805 			goto out;
806 
807 		fh->fat_entry.data_size = newsize;
808 		fh->fat_entry.start_address = newaddr;
809 		res = write_fat_entry(fh, true);
810 		if (res != TEE_SUCCESS)
811 			goto out;
812 	}
813 
814 	fh->pos += size;
815 out:
816 	if (pool_result)
817 		tee_mm_final(&p);
818 	if (newbuf)
819 		free(newbuf);
820 
821 	if (res == TEE_SUCCESS)
822 		return size;
823 
824 	return -1;
825 }
826 
827 tee_fs_off_t tee_rpmb_fs_lseek(int fd, tee_fs_off_t offset, int whence)
828 {
829 	struct rpmb_file_handle *fh;
830 	TEE_Result res;
831 	tee_fs_off_t ret = -1;
832 	tee_fs_off_t new_pos;
833 
834 	fh = handle_lookup(&fs_handle_db, fd);
835 	if (!fh)
836 		return TEE_ERROR_BAD_PARAMETERS;
837 
838 	res = read_fat(fh, NULL);
839 	if (res != TEE_SUCCESS)
840 		return -1;
841 
842 	switch (whence) {
843 	case TEE_FS_SEEK_SET:
844 		new_pos = offset;
845 		break;
846 
847 	case TEE_FS_SEEK_CUR:
848 		new_pos = fh->pos + offset;
849 		break;
850 
851 	case TEE_FS_SEEK_END:
852 		new_pos = fh->fat_entry.data_size + offset;
853 		break;
854 
855 	default:
856 		goto exit;
857 	}
858 
859 	if (new_pos < 0)
860 		new_pos = 0;
861 
862 	if (new_pos > TEE_DATA_MAX_POSITION) {
863 		EMSG("Position is beyond TEE_DATA_MAX_POSITION");
864 		goto exit;
865 	}
866 
867 	ret = fh->pos = new_pos;
868 exit:
869 	return ret;
870 }
871 
872 TEE_Result tee_rpmb_fs_rm(const char *filename)
873 {
874 	TEE_Result res = TEE_ERROR_GENERIC;
875 	struct rpmb_file_handle *fh = NULL;
876 
877 	if (!filename || strlen(filename) >= TEE_RPMB_FS_FILENAME_LENGTH - 1) {
878 		res = TEE_ERROR_BAD_PARAMETERS;
879 		goto out;
880 	}
881 
882 	fh = alloc_file_handle(filename);
883 	if (!fh) {
884 		res = TEE_ERROR_OUT_OF_MEMORY;
885 		goto out;
886 	}
887 
888 	res = read_fat(fh, NULL);
889 	if (res != TEE_SUCCESS)
890 		goto out;
891 
892 	/* Clear this file entry. */
893 	memset(&fh->fat_entry, 0, sizeof(struct rpmb_fat_entry));
894 	res = write_fat_entry(fh, false);
895 
896 out:
897 	free(fh);
898 	return res;
899 }
900 
901 TEE_Result tee_rpmb_fs_rename(const char *old_name, const char *new_name)
902 {
903 	TEE_Result res = TEE_ERROR_GENERIC;
904 	struct rpmb_file_handle *fh_old = NULL;
905 	struct rpmb_file_handle *fh_new = NULL;
906 	uint32_t old_len;
907 	uint32_t new_len;
908 
909 	if (!old_name || !new_name) {
910 		res = TEE_ERROR_BAD_PARAMETERS;
911 		goto out;
912 	}
913 
914 	old_len = strlen(old_name);
915 	new_len = strlen(new_name);
916 
917 	if ((old_len >= TEE_RPMB_FS_FILENAME_LENGTH - 1) ||
918 	    (new_len >= TEE_RPMB_FS_FILENAME_LENGTH - 1) || (new_len == 0)) {
919 
920 		res = TEE_ERROR_BAD_PARAMETERS;
921 		goto out;
922 	}
923 
924 	fh_old = alloc_file_handle(old_name);
925 	if (!fh_old) {
926 		res = TEE_ERROR_OUT_OF_MEMORY;
927 		goto out;
928 	}
929 
930 	fh_new = alloc_file_handle(new_name);
931 	if (!fh_new) {
932 		res = TEE_ERROR_OUT_OF_MEMORY;
933 		goto out;
934 	}
935 
936 	res = read_fat(fh_old, NULL);
937 	if (res != TEE_SUCCESS)
938 		goto out;
939 
940 	res = read_fat(fh_new, NULL);
941 	if (res == TEE_SUCCESS) {
942 		res = TEE_ERROR_BAD_PARAMETERS;
943 		goto out;
944 	}
945 
946 	memset(fh_old->fat_entry.filename, 0, TEE_RPMB_FS_FILENAME_LENGTH);
947 	memcpy(fh_old->fat_entry.filename, new_name, new_len);
948 
949 	res = write_fat_entry(fh_old, false);
950 
951 out:
952 	free(fh_old);
953 	free(fh_new);
954 
955 	return res;
956 }
957 
958 int tee_rpmb_fs_mkdir(const char *path __unused, tee_fs_mode_t mode __unused)
959 {
960 	/*
961 	 * FIXME: mkdir() should really create some entry in the FAT so that
962 	 * access() would return success when the directory exists but is
963 	 * empty. This does not matter for the current use cases.
964 	 */
965 	return 0;
966 }
967 
968 int tee_rpmb_fs_ftruncate(int fd, tee_fs_off_t length)
969 {
970 	struct rpmb_file_handle *fh;
971 	tee_mm_pool_t p;
972 	bool pool_result = false;
973 	tee_mm_entry_t *mm;
974 	uint32_t newsize;
975 	uint8_t *newbuf = NULL;
976 	uintptr_t newaddr;
977 	TEE_Result res = TEE_ERROR_GENERIC;
978 
979 	if (length < 0 || length > INT32_MAX) {
980 		res = TEE_ERROR_BAD_PARAMETERS;
981 		goto out;
982 	}
983 	newsize = length;
984 
985 	fh = handle_lookup(&fs_handle_db, fd);
986 	if (!fh) {
987 		res = TEE_ERROR_BAD_PARAMETERS;
988 		goto out;
989 	}
990 
991 	res = read_fat(fh, NULL);
992 	if (res != TEE_SUCCESS)
993 		goto out;
994 
995 	if (newsize > fh->fat_entry.data_size) {
996 		/* Extend file */
997 
998 		pool_result = tee_mm_init(&p,
999 					  RPMB_STORAGE_START_ADDRESS,
1000 					  fs_par->max_rpmb_address,
1001 					  RPMB_BLOCK_SIZE_SHIFT,
1002 					  TEE_MM_POOL_HI_ALLOC);
1003 		if (!pool_result) {
1004 			res = TEE_ERROR_OUT_OF_MEMORY;
1005 			goto out;
1006 		}
1007 		res = read_fat(fh, &p);
1008 		if (res != TEE_SUCCESS)
1009 			goto out;
1010 
1011 		mm = tee_mm_alloc(&p, newsize);
1012 		newbuf = calloc(newsize, 1);
1013 		if (!mm || !newbuf) {
1014 			res = TEE_ERROR_OUT_OF_MEMORY;
1015 			goto out;
1016 		}
1017 
1018 		if (fh->fat_entry.data_size) {
1019 			res = tee_rpmb_read(CFG_RPMB_FS_DEV_ID,
1020 					    fh->fat_entry.start_address,
1021 					    newbuf, fh->fat_entry.data_size,
1022 					    fh->fat_entry.fek);
1023 			if (res != TEE_SUCCESS)
1024 				goto out;
1025 		}
1026 
1027 		newaddr = tee_mm_get_smem(mm);
1028 		res = tee_rpmb_write(CFG_RPMB_FS_DEV_ID, newaddr, newbuf,
1029 				     newsize, fh->fat_entry.fek);
1030 		if (res != TEE_SUCCESS)
1031 			goto out;
1032 
1033 	} else {
1034 		/* Don't change file location */
1035 		newaddr = fh->fat_entry.start_address;
1036 	}
1037 
1038 	/* fh->pos is unchanged */
1039 	fh->fat_entry.data_size = newsize;
1040 	fh->fat_entry.start_address = newaddr;
1041 	res = write_fat_entry(fh, true);
1042 
1043 out:
1044 	if (pool_result)
1045 		tee_mm_final(&p);
1046 	if (newbuf)
1047 		free(newbuf);
1048 
1049 	if (res == TEE_SUCCESS)
1050 		return 0;
1051 
1052 	return -1;
1053 }
1054 
1055 static void tee_rpmb_fs_dir_free(tee_fs_dir *dir)
1056 {
1057 	struct tee_rpmb_fs_dirent *e;
1058 
1059 	if (!dir)
1060 		return;
1061 
1062 	free(dir->current);
1063 
1064 	while ((e = SIMPLEQ_FIRST(&dir->next))) {
1065 		SIMPLEQ_REMOVE_HEAD(&dir->next, link);
1066 		free(e);
1067 	}
1068 }
1069 
1070 static TEE_Result tee_rpmb_fs_dir_populate(const char *path, tee_fs_dir *dir)
1071 {
1072 	struct tee_rpmb_fs_dirent *current = NULL;
1073 	struct rpmb_fat_entry *fat_entries = NULL;
1074 	uint32_t fat_address;
1075 	uint32_t filelen;
1076 	char *filename;
1077 	int i;
1078 	bool last_entry_found = false;
1079 	bool matched;
1080 	struct tee_rpmb_fs_dirent *next = NULL;
1081 	uint32_t pathlen;
1082 	TEE_Result res = TEE_ERROR_GENERIC;
1083 	uint32_t size;
1084 	char temp;
1085 
1086 	res = rpmb_fs_setup();
1087 	if (res != TEE_SUCCESS)
1088 		goto out;
1089 
1090 	res = get_fat_start_address(&fat_address);
1091 	if (res != TEE_SUCCESS)
1092 		goto out;
1093 
1094 	size = N_ENTRIES * sizeof(struct rpmb_fat_entry);
1095 	fat_entries = malloc(size);
1096 	if (!fat_entries) {
1097 		res = TEE_ERROR_OUT_OF_MEMORY;
1098 		goto out;
1099 	}
1100 
1101 	pathlen = strlen(path);
1102 	while (!last_entry_found) {
1103 		res = tee_rpmb_read(CFG_RPMB_FS_DEV_ID, fat_address,
1104 				    (uint8_t *)fat_entries, size, NULL);
1105 		if (res != TEE_SUCCESS)
1106 			goto out;
1107 
1108 		for (i = 0; i < N_ENTRIES; i++) {
1109 			filename = fat_entries[i].filename;
1110 			if (fat_entries[i].flags & FILE_IS_ACTIVE) {
1111 				matched = false;
1112 				filelen = strlen(filename);
1113 				if (filelen > pathlen) {
1114 					temp = filename[pathlen];
1115 					filename[pathlen] = '\0';
1116 					if (strcmp(filename, path) == 0)
1117 						matched = true;
1118 
1119 					filename[pathlen] = temp;
1120 				}
1121 
1122 				if (matched) {
1123 					next = malloc(sizeof(*next));
1124 					if (!next) {
1125 						res = TEE_ERROR_OUT_OF_MEMORY;
1126 						goto out;
1127 					}
1128 
1129 					memset(next, 0, sizeof(*next));
1130 					next->entry.d_name = next->name;
1131 					memcpy(next->name,
1132 						&filename[pathlen],
1133 						filelen - pathlen);
1134 
1135 					SIMPLEQ_INSERT_TAIL(&dir->next, next,
1136 							    link);
1137 					current = next;
1138 				}
1139 			}
1140 
1141 			if (fat_entries[i].flags & FILE_IS_LAST_ENTRY) {
1142 				last_entry_found = true;
1143 				break;
1144 			}
1145 
1146 			/* Move to next fat_entry. */
1147 			fat_address += sizeof(struct rpmb_fat_entry);
1148 		}
1149 	}
1150 
1151 	/* No directories were found. */
1152 	if (!current) {
1153 		res = TEE_ERROR_NO_DATA;
1154 		goto out;
1155 	}
1156 
1157 	res = TEE_SUCCESS;
1158 
1159 out:
1160 	if (res != TEE_SUCCESS)
1161 		tee_rpmb_fs_dir_free(dir);
1162 	if (fat_entries)
1163 		free(fat_entries);
1164 
1165 	return res;
1166 }
1167 
1168 static TEE_Result tee_rpmb_fs_opendir_internal(const char *path,
1169 						tee_fs_dir **dir)
1170 {
1171 	uint32_t len;
1172 	uint32_t max_size;
1173 	char path_local[TEE_RPMB_FS_FILENAME_LENGTH];
1174 	TEE_Result res = TEE_ERROR_GENERIC;
1175 	tee_fs_dir *rpmb_dir = NULL;
1176 
1177 	if (!path || !dir) {
1178 		res = TEE_ERROR_BAD_PARAMETERS;
1179 		goto out;
1180 	}
1181 
1182 	/*
1183 	 * There must be room for at least the NULL char and a char for the
1184 	 * filename after the path.
1185 	 */
1186 	max_size = TEE_RPMB_FS_FILENAME_LENGTH - 2;
1187 	len = strlen(path);
1188 	if (len > max_size || len == 0) {
1189 		res = TEE_ERROR_BAD_PARAMETERS;
1190 		goto out;
1191 	}
1192 
1193 	memset(path_local, 0, sizeof(path_local));
1194 	memcpy(path_local, path, len);
1195 
1196 	/* Add a slash to correctly match the full directory name. */
1197 	if (path_local[len - 1] != '/')
1198 		path_local[len] = '/';
1199 
1200 	rpmb_dir = calloc(1, sizeof(tee_fs_dir));
1201 	if (!rpmb_dir) {
1202 		res = TEE_ERROR_OUT_OF_MEMORY;
1203 		goto out;
1204 	}
1205 	SIMPLEQ_INIT(&rpmb_dir->next);
1206 
1207 	res = tee_rpmb_fs_dir_populate(path_local, rpmb_dir);
1208 	if (res != TEE_SUCCESS) {
1209 		free(rpmb_dir);
1210 		rpmb_dir = NULL;
1211 		goto out;
1212 	}
1213 
1214 	*dir = rpmb_dir;
1215 
1216 out:
1217 	return res;
1218 }
1219 
1220 tee_fs_dir *tee_rpmb_fs_opendir(const char *path)
1221 {
1222 	tee_fs_dir *dir = NULL;
1223 	TEE_Result res = TEE_ERROR_GENERIC;
1224 
1225 	res = tee_rpmb_fs_opendir_internal(path, &dir);
1226 	if (res != TEE_SUCCESS)
1227 		dir = NULL;
1228 
1229 	return dir;
1230 }
1231 
1232 
1233 struct tee_fs_dirent *tee_rpmb_fs_readdir(tee_fs_dir *dir)
1234 {
1235 	if (!dir)
1236 		return NULL;
1237 
1238 	free(dir->current);
1239 
1240 	dir->current = SIMPLEQ_FIRST(&dir->next);
1241 	if (!dir->current)
1242 		return NULL;
1243 
1244 	SIMPLEQ_REMOVE_HEAD(&dir->next, link);
1245 
1246 	return &dir->current->entry;
1247 }
1248 
1249 int tee_rpmb_fs_closedir(tee_fs_dir *dir)
1250 {
1251 	TEE_Result res = TEE_ERROR_GENERIC;
1252 
1253 	if (!dir) {
1254 		res = TEE_SUCCESS;
1255 		goto out;
1256 	}
1257 
1258 	tee_rpmb_fs_dir_free(dir);
1259 	free(dir);
1260 	res = TEE_SUCCESS;
1261 out:
1262 	if (res == TEE_SUCCESS)
1263 		return 0;
1264 
1265 	return -1;
1266 }
1267 
1268 int tee_rpmb_fs_rmdir(const char *path)
1269 {
1270 	tee_fs_dir *dir = NULL;
1271 	TEE_Result res = TEE_ERROR_GENERIC;
1272 	int ret = -1;
1273 
1274 	/* Open the directory anyting other than NO_DATA is a failure */
1275 	res = tee_rpmb_fs_opendir_internal(path, &dir);
1276 	if (res == TEE_SUCCESS) {
1277 		tee_rpmb_fs_closedir(dir);
1278 		ret = -1;
1279 
1280 	} else if (res == TEE_ERROR_NO_DATA) {
1281 		ret = 0;
1282 
1283 	} else {
1284 		/* The case any other failure is returned */
1285 		ret = -1;
1286 	}
1287 
1288 
1289 	return ret;
1290 }
1291 
1292 TEE_Result tee_rpmb_fs_stat(const char *filename,
1293 			    struct tee_rpmb_fs_stat *stat)
1294 {
1295 	TEE_Result res = TEE_ERROR_GENERIC;
1296 	struct rpmb_file_handle *fh = NULL;
1297 
1298 	if (!stat || !filename) {
1299 		res = TEE_ERROR_BAD_PARAMETERS;
1300 		goto out;
1301 	}
1302 
1303 	fh = alloc_file_handle(filename);
1304 	if (!fh) {
1305 		res = TEE_ERROR_OUT_OF_MEMORY;
1306 		goto out;
1307 	}
1308 
1309 	res = read_fat(fh, NULL);
1310 	if (res != TEE_SUCCESS)
1311 		goto out;
1312 
1313 	stat->size = (size_t)fh->fat_entry.data_size;
1314 	stat->reserved = 0;
1315 
1316 out:
1317 	free(fh);
1318 	return res;
1319 }
1320 
1321 int tee_rpmb_fs_access(const char *filename, int mode)
1322 {
1323 	struct tee_rpmb_fs_stat stat;
1324 	TEE_Result res;
1325 
1326 	/* Mode is currently ignored, this only checks for existence */
1327 	(void)mode;
1328 
1329 	res = tee_rpmb_fs_stat(filename, &stat);
1330 
1331 	if (res == TEE_SUCCESS)
1332 		return 0;
1333 
1334 	return -1;
1335 }
1336 
1337