Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | // SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (c) 2019, Linaro Limited
*/
#include <kernel/panic.h>
#include <kernel/refcount.h>
#include <mm/file.h>
#include <mm/fobj.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include <types_ext.h>
#include <util.h>
struct file_slice_elem {
struct file_slice slice;
SLIST_ENTRY(file_slice_elem) link;
};
/*
* struct file - file resources
* @tag: Tag or hash uniquely identifying a file
* @taglen: Byte length of @tag
* @refc: Reference counter
* @link: Linked list element
* @num_slices: Number of elements in the @slices array below
* @slices: Array of file slices holding the fobjs of this file
*
* A file is constructed of slices which may be shared in different
* mappings/contexts. There may be holes in the file for ranges of the file
* that can't be shared.
*/
struct file {
uint8_t tag[FILE_TAG_SIZE];
unsigned int taglen;
struct refcount refc;
TAILQ_ENTRY(file) link;
struct mutex mu;
SLIST_HEAD(, file_slice_elem) slice_head;
};
static struct mutex file_mu = MUTEX_INITIALIZER;
static TAILQ_HEAD(, file) file_head = TAILQ_HEAD_INITIALIZER(file_head);
static int file_tag_cmp(const struct file *f, const uint8_t *tag,
unsigned int taglen)
{
if (f->taglen != taglen)
return -1;
return memcmp(tag, f->tag, taglen);
}
static struct file *file_find_tag_unlocked(const uint8_t *tag,
unsigned int taglen)
{
struct file *f = NULL;
TAILQ_FOREACH(f, &file_head, link)
if (!file_tag_cmp(f, tag, taglen))
return f;
return NULL;
}
static void file_free(struct file *f)
{
mutex_destroy(&f->mu);
while (!SLIST_EMPTY(&f->slice_head)) {
struct file_slice_elem *fse = SLIST_FIRST(&f->slice_head);
SLIST_REMOVE_HEAD(&f->slice_head, link);
fobj_put(fse->slice.fobj);
free(fse);
}
free(f);
}
TEE_Result file_add_slice(struct file *f, struct fobj *fobj,
unsigned int page_offset)
{
struct file_slice_elem *fse = NULL;
unsigned int s = 0;
/* Check for conflicts */
if (file_find_slice(f, page_offset))
return TEE_ERROR_BAD_PARAMETERS;
fse = calloc(1, sizeof(*fse));
if (!fse)
return TEE_ERROR_OUT_OF_MEMORY;
fse->slice.fobj = fobj_get(fobj);
if (!fse->slice.fobj ||
ADD_OVERFLOW(page_offset, fse->slice.fobj->num_pages, &s)) {
fobj_put(fse->slice.fobj);
free(fse);
return TEE_ERROR_BAD_PARAMETERS;
}
fse->slice.page_offset = page_offset;
SLIST_INSERT_HEAD(&f->slice_head, fse, link);
return TEE_SUCCESS;
}
struct file *file_get(struct file *f)
{
if (f && !refcount_inc(&f->refc))
panic();
return f;
}
struct file *file_get_by_tag(const uint8_t *tag, unsigned int taglen)
{
struct file *f = NULL;
if (taglen > sizeof(f->tag))
return NULL;
mutex_lock(&file_mu);
/*
* If file is found and reference counter can be increased, we're done.
* If file can't be found, it doesn't exist so it has to be added.
* If it's found but reference counter is 0, the situation is
* a bit complicated:
* - file_put() is about to free the file as soon as it can obtain the
* mutex.
* - Unless there's a mismatch between file_get() and file_put() only
* one thread calling file_put() is about to free the file.
*
* There's a window of opportunity where file_put() is called
* (without a mutex being held, which is quite OK) while we're
* holding the mutex here and are searching for the file and it's
* found, but just after file_put() has decreased the reference
* counter.
*
* To keep it simple we're adding a new file at the head (so new
* searches finds this file instead of the old being freed) instead
* of complicating file_put() by trying to rescue the file and
* possibly hiding a case of mismatching file_put() and file_get().
*/
f = file_find_tag_unlocked(tag, taglen);
if (f && refcount_inc(&f->refc))
goto out;
f = calloc(1, sizeof(*f));
if (!f)
goto out;
memcpy(f->tag, tag, taglen);
f->taglen = taglen;
refcount_set(&f->refc, 1);
mutex_init(&f->mu);
SLIST_INIT(&f->slice_head);
TAILQ_INSERT_HEAD(&file_head, f, link);
out:
mutex_unlock(&file_mu);
return f;
}
void file_put(struct file *f)
{
if (f && refcount_dec(&f->refc)) {
mutex_lock(&file_mu);
TAILQ_REMOVE(&file_head, f, link);
mutex_unlock(&file_mu);
file_free(f);
}
}
struct file_slice *file_find_slice(struct file *f, unsigned int page_offset)
{
struct file_slice_elem *fse = NULL;
assert(f->mu.state);
SLIST_FOREACH(fse, &f->slice_head, link) {
struct file_slice *fs = &fse->slice;
if (page_offset >= fs->page_offset &&
page_offset < fs->page_offset + fs->fobj->num_pages)
return fs;
}
return NULL;
}
void file_lock(struct file *f)
{
mutex_lock(&f->mu);
}
bool file_trylock(struct file *f)
{
return mutex_trylock(&f->mu);
}
void file_unlock(struct file *f)
{
mutex_unlock(&f->mu);
}
|