xref: /optee_os/core/tee/tee_rpmb_fs.c (revision b01047730e77127c23a36591643eeb8bb0487d68)
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 <tee/tee_rpmb_fs.h>
30 #include <tee/tee_rpmb.h>
31 #include <mm/tee_mm.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 #define RPMB_STORAGE_START_ADDRESS      0
36 #define RPMB_FS_FAT_START_ADDRESS       512
37 #define RPMB_STORAGE_END_ADDRESS        ((1024 * 128) - 1)
38 #define RPMB_BLOCK_SIZE_SHIFT           8
39 
40 #define DEV_ID                          0
41 #define RPMB_FS_MAGIC                   0x52504D42
42 #define FS_VERSION                      1
43 #define N_ENTRIES                       16
44 
45 #define FILE_IS_ACTIVE                  (1u << 0)
46 #define FILE_IS_LAST_ENTRY              (1u << 1)
47 
48 /**
49  * FS parameters: Information often used by internal functions.
50  * fat_start_address will be set by rpmb_fs_setup().
51  * rpmb_fs_parameters can be read by any other function.
52  */
53 struct rpmb_fs_parameters {
54 	uint32_t fat_start_address;
55 };
56 
57 /**
58  * File entry for a single file in a RPMB_FS partition.
59  */
60 struct rpmb_fat_entry {
61 	uint32_t start_address;
62 	uint32_t data_size;
63 	uint32_t flags;
64 	uint32_t write_counter;
65 	char filename[FILENAME_LENGTH];
66 };
67 
68 /**
69  * FAT entry context with reference to a FAT entry and its
70  * location in RPMB.
71  */
72 struct file_handle {
73 	/* Pointer to a fat_entry */
74 	struct rpmb_fat_entry fat_entry;
75 	/* Pointer to a filename */
76 	const char *filename;
77 	/* Adress for current entry in RPMB */
78 	uint32_t rpmb_fat_address;
79 };
80 
81 /**
82  * RPMB_FS partition data
83  */
84 struct rpmb_fs_partition {
85 	uint32_t rpmb_fs_magic;
86 	uint32_t fs_version;
87 	uint32_t write_counter;
88 	uint32_t fat_start_address;
89 	/* Do not use reserved[] for other purpose than partition data. */
90 	uint8_t reserved[112];
91 };
92 
93 static struct rpmb_fs_parameters *fs_par;
94 
95 static struct file_handle *alloc_file_handle(const char *filename)
96 {
97 	struct file_handle *fh = NULL;
98 
99 	fh = calloc(1, sizeof(struct file_handle));
100 	if (fh == NULL)
101 		return NULL;
102 
103 	if (filename != NULL)
104 		fh->filename = filename;
105 
106 	return fh;
107 }
108 
109 /**
110  * write_fat_entry: Store info in a fat_entry to RPMB.
111  */
112 static TEE_Result write_fat_entry(struct file_handle *fh,
113 				  bool update_write_counter)
114 {
115 	TEE_Result res = TEE_ERROR_GENERIC;
116 
117 	/* Protect partition data. */
118 	if (fh->rpmb_fat_address < sizeof(struct rpmb_fs_partition)) {
119 		res = TEE_ERROR_ACCESS_CONFLICT;
120 		goto out;
121 	}
122 
123 	if (fh->rpmb_fat_address % sizeof(struct rpmb_fat_entry) != 0) {
124 		res = TEE_ERROR_BAD_PARAMETERS;
125 		goto out;
126 	}
127 
128 	if (update_write_counter) {
129 		res = tee_rpmb_get_write_counter(DEV_ID,
130 						 &fh->fat_entry.write_counter);
131 		if (res != TEE_SUCCESS)
132 			goto out;
133 	}
134 
135 	res = tee_rpmb_write(DEV_ID, fh->rpmb_fat_address,
136 			     (uint8_t *)&fh->fat_entry,
137 			     sizeof(struct rpmb_fat_entry));
138 
139 out:
140 	return res;
141 }
142 
143 /**
144  * rpmb_fs_setup: Setup rpmb fs.
145  * Set initial partition and FS values and write to RPMB.
146  * Store frequently used data in RAM.
147  */
148 static TEE_Result rpmb_fs_setup(void)
149 {
150 	TEE_Result res = TEE_ERROR_GENERIC;
151 	struct rpmb_fs_partition *partition_data = NULL;
152 	struct file_handle *fh = NULL;
153 
154 	partition_data = calloc(1, sizeof(struct rpmb_fs_partition));
155 	if (partition_data == NULL) {
156 		res = TEE_ERROR_OUT_OF_MEMORY;
157 		goto out;
158 	}
159 
160 	res = tee_rpmb_read(DEV_ID, RPMB_STORAGE_START_ADDRESS,
161 			    (uint8_t *)partition_data,
162 			    sizeof(struct rpmb_fs_partition));
163 	if (res != TEE_SUCCESS)
164 		goto out;
165 
166 	if (partition_data->rpmb_fs_magic == RPMB_FS_MAGIC) {
167 		if (partition_data->fs_version == FS_VERSION) {
168 			res = TEE_SUCCESS;
169 			goto store_fs_par;
170 		} else {
171 			/* Wrong software is in use. */
172 			res = TEE_ERROR_ACCESS_DENIED;
173 			goto out;
174 		}
175 	}
176 
177 	/* Setup new partition data. */
178 	partition_data->rpmb_fs_magic = RPMB_FS_MAGIC;
179 	partition_data->fs_version = FS_VERSION;
180 	partition_data->fat_start_address = RPMB_FS_FAT_START_ADDRESS;
181 
182 	/* Initial FAT entry with FILE_IS_LAST_ENTRY flag set. */
183 	fh = alloc_file_handle(NULL);
184 	if (fh == NULL) {
185 		res = TEE_ERROR_OUT_OF_MEMORY;
186 		goto out;
187 	}
188 	fh->fat_entry.flags = FILE_IS_LAST_ENTRY;
189 	fh->rpmb_fat_address = partition_data->fat_start_address;
190 
191 	/* Write init FAT entry and partition data to RPMB. */
192 	res = write_fat_entry(fh, true);
193 	if (res != TEE_SUCCESS)
194 		goto out;
195 
196 	res =
197 	    tee_rpmb_get_write_counter(DEV_ID, &partition_data->write_counter);
198 	if (res != TEE_SUCCESS)
199 		goto out;
200 	res = tee_rpmb_write(DEV_ID, RPMB_STORAGE_START_ADDRESS,
201 			     (uint8_t *)partition_data,
202 			     sizeof(struct rpmb_fs_partition));
203 
204 store_fs_par:
205 	/* Store FAT start address. */
206 	if (fs_par == NULL) {
207 		fs_par = calloc(1, sizeof(struct rpmb_fs_parameters));
208 		if (fs_par == NULL) {
209 			res = TEE_ERROR_OUT_OF_MEMORY;
210 			goto out;
211 		}
212 	}
213 
214 	fs_par->fat_start_address = partition_data->fat_start_address;
215 
216 out:
217 	free(fh);
218 	free(partition_data);
219 
220 	return res;
221 }
222 
223 /**
224  * get_fat_start_address:
225  * FAT start_address from fs_par.
226  */
227 static TEE_Result get_fat_start_address(uint32_t *addr)
228 {
229 	TEE_Result res = TEE_ERROR_GENERIC;
230 
231 	if (fs_par == NULL) {
232 		res = rpmb_fs_setup();
233 		if (res != TEE_SUCCESS)
234 			goto out;
235 	}
236 
237 	*addr = fs_par->fat_start_address;
238 	res = TEE_SUCCESS;
239 
240 out:
241 	return res;
242 }
243 
244 /**
245  * read_fat: Read FAT entries
246  * Return matching FAT entry for read, rm rename and stat.
247  * Build up memory pool and return matching entry for write operation.
248  * "Last FAT entry" can be returned during write.
249  */
250 static TEE_Result read_fat(struct file_handle *fh, tee_mm_pool_t *p)
251 {
252 	TEE_Result res = TEE_ERROR_GENERIC;
253 	tee_mm_entry_t *mm = NULL;
254 	struct rpmb_fat_entry *fat_entries = NULL;
255 	uint32_t fat_address;
256 	size_t size;
257 	int i;
258 	bool entry_found = false;
259 	bool last_entry_found = false;
260 
261 	res = rpmb_fs_setup();
262 	if (res != TEE_SUCCESS)
263 		goto out;
264 
265 	res = get_fat_start_address(&fat_address);
266 	if (res != TEE_SUCCESS)
267 		goto out;
268 
269 	size = N_ENTRIES * sizeof(struct rpmb_fat_entry);
270 	fat_entries = malloc(size);
271 	if (fat_entries == NULL) {
272 		res = TEE_ERROR_OUT_OF_MEMORY;
273 		goto out;
274 	}
275 
276 	while (!last_entry_found && !entry_found) {
277 		res = tee_rpmb_read(DEV_ID, fat_address,
278 				    (uint8_t *)fat_entries, size);
279 		if (res != TEE_SUCCESS)
280 			goto out;
281 
282 		for (i = 0; i < N_ENTRIES; i++) {
283 			/*
284 			 * Look for an entry, matching filenames. (read, rm,
285 			 * rename and stat.). Only store first filename match.
286 			 */
287 			if ((fh->filename != NULL) &&
288 			    (strcmp(fh->filename,
289 				    fat_entries[i].filename) == 0) &&
290 			    (fat_entries[i].flags & FILE_IS_ACTIVE) &&
291 			    (!entry_found)) {
292 				entry_found = true;
293 				fh->rpmb_fat_address = fat_address;
294 				memcpy(&fh->fat_entry, &fat_entries[i],
295 				       sizeof(struct rpmb_fat_entry));
296 				if (p == NULL)
297 					break;
298 			}
299 
300 			/* Add existing files to memory pool. (write) */
301 			if (p != NULL) {
302 				if ((fat_entries[i].flags & FILE_IS_ACTIVE) !=
303 				    0) {
304 					mm = tee_mm_alloc2
305 						(p,
306 						 fat_entries[i].start_address,
307 						 fat_entries[i].data_size);
308 					if (mm == NULL) {
309 						res = TEE_ERROR_OUT_OF_MEMORY;
310 						goto out;
311 					}
312 				}
313 
314 				/* Unused FAT entries can be reused (write) */
315 				if (((fat_entries[i].flags & FILE_IS_ACTIVE) ==
316 				     0) && (fh->rpmb_fat_address == 0)) {
317 					fh->rpmb_fat_address = fat_address;
318 					memcpy(&fh->fat_entry, &fat_entries[i],
319 					       sizeof(struct rpmb_fat_entry));
320 				}
321 			}
322 
323 			if ((fat_entries[i].flags & FILE_IS_LAST_ENTRY) != 0) {
324 				last_entry_found = true;
325 				if (p != NULL && fh->rpmb_fat_address == 0) {
326 					fh->rpmb_fat_address = fat_address;
327 					fh->fat_entry.flags =
328 					    FILE_IS_LAST_ENTRY;
329 				}
330 				break;
331 			}
332 
333 			/* Move to next fat_entry. */
334 			fat_address += sizeof(struct rpmb_fat_entry);
335 		}
336 	}
337 
338 	if ((p != NULL) && last_entry_found) {
339 		/* Make room for yet a FAT entry and add to memory pool. */
340 		fat_address += 2 * sizeof(struct rpmb_fat_entry);
341 		mm = tee_mm_alloc2(p, RPMB_STORAGE_START_ADDRESS, fat_address);
342 		if (mm == NULL) {
343 			res = TEE_ERROR_OUT_OF_MEMORY;
344 			goto out;
345 		}
346 	}
347 
348 	if (fh->filename != NULL && fh->rpmb_fat_address == 0)
349 		res = TEE_ERROR_FILE_NOT_FOUND;
350 
351 out:
352 	free(fat_entries);
353 	return res;
354 }
355 
356 /**
357  * add_fat_entry:
358  * Populate last FAT entry.
359  */
360 static TEE_Result add_fat_entry(struct file_handle *fh)
361 {
362 	TEE_Result res = TEE_ERROR_GENERIC;
363 
364 	fh->rpmb_fat_address += sizeof(struct rpmb_fat_entry);
365 	res = write_fat_entry(fh, true);
366 	fh->rpmb_fat_address -= sizeof(struct rpmb_fat_entry);
367 
368 	return res;
369 }
370 
371 int tee_rpmb_fs_read(const char *filename, uint8_t *buf, size_t size)
372 {
373 	TEE_Result res = TEE_ERROR_GENERIC;
374 	struct file_handle *fh = NULL;
375 	int read_size = -1;
376 
377 	if (filename == NULL || buf == NULL ||
378 	    strlen(filename) >= FILENAME_LENGTH - 1) {
379 		res = TEE_ERROR_BAD_PARAMETERS;
380 		goto out;
381 	}
382 
383 	fh = alloc_file_handle(filename);
384 	if (fh == NULL) {
385 		res = TEE_ERROR_OUT_OF_MEMORY;
386 		goto out;
387 	}
388 
389 	res = read_fat(fh, NULL);
390 	if (res != TEE_SUCCESS)
391 		goto out;
392 
393 	if (size < fh->fat_entry.data_size) {
394 		res = TEE_ERROR_SHORT_BUFFER;
395 		goto out;
396 	}
397 
398 	res = tee_rpmb_read(DEV_ID, fh->fat_entry.start_address, buf,
399 			    fh->fat_entry.data_size);
400 
401 out:
402 	if (res == TEE_SUCCESS)
403 		read_size = fh->fat_entry.data_size;
404 
405 	free(fh);
406 
407 	return read_size;
408 }
409 
410 int tee_rpmb_fs_write(const char *filename, uint8_t *buf, size_t size)
411 {
412 	TEE_Result res = TEE_ERROR_GENERIC;
413 	struct file_handle *fh = NULL;
414 	tee_mm_pool_t p;
415 	tee_mm_entry_t *mm = NULL;
416 	size_t length;
417 	uint32_t mm_flags;
418 
419 	if (filename == NULL || buf == NULL) {
420 		res = TEE_ERROR_BAD_PARAMETERS;
421 		goto out;
422 	}
423 
424 	length = strlen(filename);
425 	if ((length >= FILENAME_LENGTH - 1) || (length == 0)) {
426 		res = TEE_ERROR_BAD_PARAMETERS;
427 		goto out;
428 	}
429 
430 	/* Create a FAT entry for the file to write. */
431 	fh = alloc_file_handle(filename);
432 	if (fh == NULL) {
433 		res = TEE_ERROR_OUT_OF_MEMORY;
434 		goto out;
435 	}
436 
437 	/* Upper memory allocation must be used for RPMB_FS. */
438 	mm_flags = TEE_MM_POOL_HI_ALLOC;
439 	if (!tee_mm_init
440 	    (&p, RPMB_STORAGE_START_ADDRESS, RPMB_STORAGE_END_ADDRESS,
441 	     RPMB_BLOCK_SIZE_SHIFT, mm_flags)) {
442 		res = TEE_ERROR_OUT_OF_MEMORY;
443 		goto out;
444 	}
445 
446 	res = read_fat(fh, &p);
447 	if (res != TEE_SUCCESS)
448 		goto out;
449 
450 	mm = tee_mm_alloc(&p, size);
451 	if (mm == NULL) {
452 		res = TEE_ERROR_OUT_OF_MEMORY;
453 		goto out;
454 	}
455 
456 	if ((fh->fat_entry.flags & FILE_IS_LAST_ENTRY) != 0) {
457 		res = add_fat_entry(fh);
458 		if (res != TEE_SUCCESS)
459 			goto out;
460 	}
461 
462 	memset(&fh->fat_entry, 0, sizeof(struct rpmb_fat_entry));
463 	memcpy(fh->fat_entry.filename, filename, length);
464 	fh->fat_entry.data_size = size;
465 	fh->fat_entry.flags = FILE_IS_ACTIVE;
466 	fh->fat_entry.start_address = tee_mm_get_smem(mm);
467 
468 	res = tee_rpmb_write(DEV_ID, fh->fat_entry.start_address, buf, size);
469 	if (res != TEE_SUCCESS)
470 		goto out;
471 
472 	res = write_fat_entry(fh, true);
473 
474 out:
475 	free(fh);
476 	if (mm != NULL)
477 		tee_mm_final(&p);
478 
479 	if (res == TEE_SUCCESS)
480 		return size;
481 
482 	return -1;
483 }
484 
485 TEE_Result tee_rpmb_fs_rm(const char *filename)
486 {
487 	TEE_Result res = TEE_ERROR_GENERIC;
488 	struct file_handle *fh = NULL;
489 
490 	if (filename == NULL || strlen(filename) >= FILENAME_LENGTH - 1) {
491 		res = TEE_ERROR_BAD_PARAMETERS;
492 		goto out;
493 	}
494 
495 	fh = alloc_file_handle(filename);
496 	if (fh == NULL) {
497 		res = TEE_ERROR_OUT_OF_MEMORY;
498 		goto out;
499 	}
500 
501 	res = read_fat(fh, NULL);
502 	if (res != TEE_SUCCESS)
503 		goto out;
504 
505 	/* Clear this file entry. */
506 	memset(&fh->fat_entry, 0, sizeof(struct rpmb_fat_entry));
507 	res = write_fat_entry(fh, false);
508 
509 out:
510 	free(fh);
511 
512 	return res;
513 }
514 
515 TEE_Result tee_rpmb_fs_rename(const char *old_name, const char *new_name)
516 {
517 	TEE_Result res = TEE_ERROR_GENERIC;
518 	struct file_handle *fh_old = NULL;
519 	struct file_handle *fh_new = NULL;
520 	uint32_t old_len;
521 	uint32_t new_len;
522 
523 	if (old_name == NULL || new_name == NULL) {
524 		res = TEE_ERROR_BAD_PARAMETERS;
525 		goto out;
526 	}
527 
528 	old_len = strlen(old_name);
529 	new_len = strlen(new_name);
530 
531 	if ((old_len >= FILENAME_LENGTH - 1) ||
532 	    (new_len >= FILENAME_LENGTH - 1) || (new_len == 0)) {
533 		res = TEE_ERROR_BAD_PARAMETERS;
534 		goto out;
535 	}
536 
537 	fh_old = alloc_file_handle(old_name);
538 	if (fh_old == NULL) {
539 		res = TEE_ERROR_OUT_OF_MEMORY;
540 		goto out;
541 	}
542 
543 	fh_new = alloc_file_handle(new_name);
544 	if (fh_new == NULL) {
545 		res = TEE_ERROR_OUT_OF_MEMORY;
546 		goto out;
547 	}
548 
549 	res = read_fat(fh_old, NULL);
550 	if (res != TEE_SUCCESS)
551 		goto out;
552 
553 	res = read_fat(fh_new, NULL);
554 	if (res == TEE_SUCCESS) {
555 		res = TEE_ERROR_BAD_PARAMETERS;
556 		goto out;
557 	}
558 
559 	memset(fh_old->fat_entry.filename, 0, FILENAME_LENGTH);
560 	memcpy(fh_old->fat_entry.filename, new_name, new_len);
561 
562 	res = write_fat_entry(fh_old, false);
563 
564 out:
565 	free(fh_old);
566 	free(fh_new);
567 
568 	return res;
569 }
570 
571 TEE_Result tee_rpmb_fs_stat(const char *filename,
572 			    struct tee_rpmb_fs_stat *stat)
573 {
574 	TEE_Result res = TEE_ERROR_GENERIC;
575 	struct file_handle *fh = NULL;
576 
577 	if (stat == NULL || filename == NULL) {
578 		res = TEE_ERROR_BAD_PARAMETERS;
579 		goto out;
580 	}
581 
582 	fh = alloc_file_handle(filename);
583 	if (fh == NULL) {
584 		res = TEE_ERROR_OUT_OF_MEMORY;
585 		goto out;
586 	}
587 
588 	res = read_fat(fh, NULL);
589 	if (res != TEE_SUCCESS)
590 		goto out;
591 
592 	stat->size = fh->fat_entry.data_size;
593 	stat->reserved = 0;
594 
595 out:
596 	free(fh);
597 	return res;
598 }
599