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
file_tag_cmp(const struct file * f,const uint8_t * tag,unsigned int taglen)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
file_find_tag_unlocked(const uint8_t * tag,unsigned int taglen)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
file_free(struct file * f)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
file_add_slice(struct file * f,struct fobj * fobj,unsigned int page_offset)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
file_get(struct file * f)109 struct file *file_get(struct file *f)
110 {
111 if (f && !refcount_inc(&f->refc))
112 panic();
113
114 return f;
115 }
116
file_get_by_tag(const uint8_t * tag,unsigned int taglen)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
file_put(struct file * f)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
file_find_slice(struct file * f,unsigned int page_offset)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
file_lock(struct file * f)196 void file_lock(struct file *f)
197 {
198 mutex_lock(&f->mu);
199 }
200
file_trylock(struct file * f)201 bool file_trylock(struct file *f)
202 {
203 return mutex_trylock(&f->mu);
204 }
205
file_unlock(struct file * f)206 void file_unlock(struct file *f)
207 {
208 mutex_unlock(&f->mu);
209 }
210