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 126 /* 127 * If file is found and reference counter can be increased, we're done. 128 * If file can't be found, it doesn't exist so it has to be added. 129 * If it's found but reference counter is 0, the situation is 130 * a bit complicated: 131 * - file_put() is about to free the file as soon as it can obtain the 132 * mutex. 133 * - Unless there's a mismatch between file_get() and file_put() only 134 * one thread calling file_put() is about to free the file. 135 * 136 * There's a window of opportunity where file_put() is called 137 * (without a mutex being held, which is quite OK) while we're 138 * holding the mutex here and are searching for the file and it's 139 * found, but just after file_put() has decreased the reference 140 * counter. 141 * 142 * To keep it simple we're adding a new file at the head (so new 143 * searches finds this file instead of the old being freed) instead 144 * of complicating file_put() by trying to rescue the file and 145 * possibly hiding a case of mismatching file_put() and file_get(). 146 */ 147 f = file_find_tag_unlocked(tag, taglen); 148 if (f && refcount_inc(&f->refc)) 149 goto out; 150 151 f = calloc(1, sizeof(*f)); 152 if (!f) 153 goto out; 154 memcpy(f->tag, tag, taglen); 155 f->taglen = taglen; 156 refcount_set(&f->refc, 1); 157 mutex_init(&f->mu); 158 SLIST_INIT(&f->slice_head); 159 TAILQ_INSERT_HEAD(&file_head, f, link); 160 161 out: 162 mutex_unlock(&file_mu); 163 164 return f; 165 } 166 167 void file_put(struct file *f) 168 { 169 if (f && refcount_dec(&f->refc)) { 170 mutex_lock(&file_mu); 171 TAILQ_REMOVE(&file_head, f, link); 172 mutex_unlock(&file_mu); 173 174 file_free(f); 175 } 176 177 } 178 179 struct file_slice *file_find_slice(struct file *f, unsigned int page_offset) 180 { 181 struct file_slice_elem *fse = NULL; 182 183 assert(f->mu.state); 184 185 SLIST_FOREACH(fse, &f->slice_head, link) { 186 struct file_slice *fs = &fse->slice; 187 188 if (page_offset >= fs->page_offset && 189 page_offset < fs->page_offset + fs->fobj->num_pages) 190 return fs; 191 } 192 193 return NULL; 194 } 195 196 void file_lock(struct file *f) 197 { 198 mutex_lock(&f->mu); 199 } 200 201 bool file_trylock(struct file *f) 202 { 203 return mutex_trylock(&f->mu); 204 } 205 206 void file_unlock(struct file *f) 207 { 208 mutex_unlock(&f->mu); 209 } 210