1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2019, Linaro Limited 4 */ 5 6 #include <kernel/panic.h> 7 #include <kernel/refcount.h> 8 #include <mm/file.h> 9 #include <mm/fobj.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <sys/queue.h> 13 #include <types_ext.h> 14 #include <util.h> 15 16 struct file_slice_elem { 17 struct file_slice slice; 18 SLIST_ENTRY(file_slice_elem) link; 19 }; 20 21 /* 22 * struct file - file resources 23 * @tag: Tag or hash uniquely identifying a file 24 * @taglen: Byte length of @tag 25 * @refc: Reference counter 26 * @link: Linked list element 27 * @num_slices: Number of elements in the @slices array below 28 * @slices: Array of file slices holding the fobjs of this file 29 * 30 * A file is constructed of slices which may be shared in different 31 * mappings/contexts. There may be holes in the file for ranges of the file 32 * that can't be shared. 33 */ 34 struct file { 35 uint8_t tag[FILE_TAG_SIZE]; 36 unsigned int taglen; 37 struct refcount refc; 38 TAILQ_ENTRY(file) link; 39 struct mutex mu; 40 SLIST_HEAD(, file_slice_elem) slice_head; 41 }; 42 43 static struct mutex file_mu = MUTEX_INITIALIZER; 44 static TAILQ_HEAD(, file) file_head = TAILQ_HEAD_INITIALIZER(file_head); 45 46 static int file_tag_cmp(const struct file *f, const uint8_t *tag, 47 unsigned int taglen) 48 { 49 if (f->taglen != taglen) 50 return -1; 51 return memcmp(tag, f->tag, taglen); 52 } 53 54 static struct file *file_find_tag_unlocked(const uint8_t *tag, 55 unsigned int taglen) 56 { 57 struct file *f = NULL; 58 59 TAILQ_FOREACH(f, &file_head, link) 60 if (!file_tag_cmp(f, tag, taglen)) 61 return f; 62 63 return NULL; 64 } 65 66 static void file_free(struct file *f) 67 { 68 mutex_destroy(&f->mu); 69 70 while (!SLIST_EMPTY(&f->slice_head)) { 71 struct file_slice_elem *fse = SLIST_FIRST(&f->slice_head); 72 73 SLIST_REMOVE_HEAD(&f->slice_head, link); 74 fobj_put(fse->slice.fobj); 75 free(fse); 76 } 77 78 free(f); 79 } 80 81 TEE_Result file_add_slice(struct file *f, struct fobj *fobj, 82 unsigned int page_offset) 83 { 84 struct file_slice_elem *fse = NULL; 85 unsigned int s = 0; 86 87 /* Check for conflicts */ 88 if (file_find_slice(f, page_offset)) 89 return TEE_ERROR_BAD_PARAMETERS; 90 91 fse = calloc(1, sizeof(*fse)); 92 if (!fse) 93 return TEE_ERROR_OUT_OF_MEMORY; 94 95 fse->slice.fobj = fobj_get(fobj); 96 if (!fse->slice.fobj || 97 ADD_OVERFLOW(page_offset, fse->slice.fobj->num_pages, &s)) { 98 fobj_put(fse->slice.fobj); 99 free(fse); 100 return TEE_ERROR_BAD_PARAMETERS; 101 } 102 103 fse->slice.page_offset = page_offset; 104 SLIST_INSERT_HEAD(&f->slice_head, fse, link); 105 106 return TEE_SUCCESS; 107 } 108 109 struct file *file_get(struct file *f) 110 { 111 if (f && !refcount_inc(&f->refc)) 112 panic(); 113 114 return f; 115 } 116 117 struct file *file_get_by_tag(const uint8_t *tag, unsigned int taglen) 118 { 119 struct file *f = NULL; 120 121 if (taglen > sizeof(f->tag)) 122 return NULL; 123 124 mutex_lock(&file_mu); 125 f = file_get(file_find_tag_unlocked(tag, taglen)); 126 if (f) 127 goto out; 128 f = calloc(1, sizeof(*f)); 129 if (!f) 130 goto out; 131 memcpy(f->tag, tag, taglen); 132 f->taglen = taglen; 133 refcount_set(&f->refc, 1); 134 mutex_init(&f->mu); 135 SLIST_INIT(&f->slice_head); 136 TAILQ_INSERT_TAIL(&file_head, f, link); 137 138 out: 139 mutex_unlock(&file_mu); 140 141 return f; 142 } 143 144 void file_put(struct file *f) 145 { 146 if (f && refcount_dec(&f->refc)) { 147 mutex_lock(&file_mu); 148 TAILQ_REMOVE(&file_head, f, link); 149 mutex_unlock(&file_mu); 150 151 file_free(f); 152 } 153 154 } 155 156 struct file_slice *file_find_slice(struct file *f, unsigned int page_offset) 157 { 158 struct file_slice_elem *fse = NULL; 159 160 assert(f->mu.state); 161 162 SLIST_FOREACH(fse, &f->slice_head, link) { 163 struct file_slice *fs = &fse->slice; 164 165 if (page_offset >= fs->page_offset && 166 page_offset < fs->page_offset + fs->fobj->num_pages) 167 return fs; 168 } 169 170 return NULL; 171 } 172 173 void file_lock(struct file *f) 174 { 175 mutex_lock(&f->mu); 176 } 177 178 bool file_trylock(struct file *f) 179 { 180 return mutex_trylock(&f->mu); 181 } 182 183 void file_unlock(struct file *f) 184 { 185 mutex_unlock(&f->mu); 186 } 187