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 /* 17 * struct file - file resources 18 * @tag: Tag or hash uniquely identifying a file 19 * @taglen: Byte length of @tag 20 * @refc: Reference counter 21 * @link: Linked list element 22 * @num_slices: Number of elements in the @slices array below 23 * @slices: Array of file slices holding the fobjs of this file 24 * 25 * A file is constructed of slices which may be shared in different 26 * mappings/contexts. There may be holes in the file for ranges of the file 27 * that can't be shared. 28 */ 29 struct file { 30 uint8_t tag[FILE_TAG_SIZE]; 31 unsigned int taglen; 32 struct refcount refc; 33 TAILQ_ENTRY(file) link; 34 unsigned int num_slices; 35 struct file_slice slices[]; 36 }; 37 38 static struct mutex file_mu = MUTEX_INITIALIZER; 39 static TAILQ_HEAD(, file) file_head = TAILQ_HEAD_INITIALIZER(file_head); 40 41 static int file_tag_cmp(const struct file *f, const uint8_t *tag, 42 unsigned int taglen) 43 { 44 if (f->taglen != taglen) 45 return -1; 46 return memcmp(tag, f->tag, taglen); 47 } 48 49 static struct file *file_find_tag_unlocked(const uint8_t *tag, 50 unsigned int taglen) 51 { 52 struct file *f = NULL; 53 54 TAILQ_FOREACH(f, &file_head, link) 55 if (!file_tag_cmp(f, tag, taglen)) 56 return f; 57 58 return NULL; 59 } 60 61 static void file_free(struct file *f) 62 { 63 size_t n = 0; 64 65 for (n = 0; n < f->num_slices; n++) 66 fobj_put(f->slices[n].fobj); 67 68 free(f); 69 } 70 71 struct file *file_new(uint8_t *tag, unsigned int taglen, 72 struct file_slice *slices, unsigned int num_slices) 73 { 74 unsigned int s = 0; 75 unsigned int n = 0; 76 struct file *f = NULL; 77 bool did_insert = false; 78 79 if (taglen > sizeof(f->tag)) 80 return NULL; 81 if (MUL_OVERFLOW(num_slices, sizeof(*slices), &s)) 82 return NULL; 83 if (ADD_OVERFLOW(s, sizeof(*f), &s)) 84 return NULL; 85 f = calloc(1, s); 86 if (!f) 87 return NULL; 88 89 memcpy(f->tag, tag, taglen); 90 f->taglen = taglen; 91 refcount_set(&f->refc, 1); 92 for (n = 0; n < num_slices; n++) { 93 if (file_find_slice(f, slices[n].page_offset)) 94 goto err; 95 96 f->slices[n].fobj = fobj_get(slices[n].fobj); 97 f->num_slices = n + 1; 98 if (!f->slices[n].fobj || 99 ADD_OVERFLOW(slices[n].page_offset, 100 slices[n].fobj->num_pages, &s)) 101 goto err; 102 f->slices[n].page_offset = slices[n].page_offset; 103 } 104 105 mutex_lock(&file_mu); 106 if (!file_find_tag_unlocked(tag, taglen)) { 107 TAILQ_INSERT_TAIL(&file_head, f, link); 108 did_insert = true; 109 } 110 mutex_unlock(&file_mu); 111 112 if (did_insert) 113 return f; 114 err: 115 file_free(f); 116 117 return NULL; 118 } 119 120 struct file *file_get(struct file *f) 121 { 122 if (f && !refcount_inc(&f->refc)) 123 panic(); 124 125 return f; 126 } 127 128 struct file *file_get_by_tag(uint8_t *tag, unsigned int taglen) 129 { 130 struct file *f = NULL; 131 132 mutex_lock(&file_mu); 133 f = file_get(file_find_tag_unlocked(tag, taglen)); 134 mutex_unlock(&file_mu); 135 136 return f; 137 } 138 139 void file_put(struct file *f) 140 { 141 bool did_remove = false; 142 143 if (!f) 144 return; 145 146 mutex_lock(&file_mu); 147 if (refcount_dec(&f->refc)) { 148 TAILQ_REMOVE(&file_head, f, link); 149 did_remove = true; 150 } 151 mutex_unlock(&file_mu); 152 153 if (did_remove) 154 file_free(f); 155 } 156 157 struct file_slice *file_find_slice(struct file *f, unsigned int page_offset) 158 { 159 size_t n = 0; 160 161 if (!f) 162 return NULL; 163 164 for (n = 0; n < f->num_slices; n++) { 165 struct file_slice *fs = f->slices + n; 166 167 if (page_offset >= fs->page_offset && 168 page_offset < fs->page_offset + fs->fobj->num_pages) 169 return fs; 170 } 171 172 return NULL; 173 } 174