19f31ef5aSJens Wiklander // SPDX-License-Identifier: BSD-2-Clause
29f31ef5aSJens Wiklander /*
39f31ef5aSJens Wiklander * Copyright (c) 2019, Linaro Limited
49f31ef5aSJens Wiklander */
59f31ef5aSJens Wiklander
69f31ef5aSJens Wiklander #include <kernel/panic.h>
79f31ef5aSJens Wiklander #include <kernel/refcount.h>
89f31ef5aSJens Wiklander #include <mm/file.h>
99f31ef5aSJens Wiklander #include <mm/fobj.h>
109f31ef5aSJens Wiklander #include <stdlib.h>
119f31ef5aSJens Wiklander #include <string.h>
129f31ef5aSJens Wiklander #include <sys/queue.h>
139f31ef5aSJens Wiklander #include <types_ext.h>
149f31ef5aSJens Wiklander #include <util.h>
159f31ef5aSJens Wiklander
16bae0f170SJens Wiklander struct file_slice_elem {
17bae0f170SJens Wiklander struct file_slice slice;
18bae0f170SJens Wiklander SLIST_ENTRY(file_slice_elem) link;
19bae0f170SJens Wiklander };
20bae0f170SJens Wiklander
219f31ef5aSJens Wiklander /*
229f31ef5aSJens Wiklander * struct file - file resources
239f31ef5aSJens Wiklander * @tag: Tag or hash uniquely identifying a file
249f31ef5aSJens Wiklander * @taglen: Byte length of @tag
259f31ef5aSJens Wiklander * @refc: Reference counter
269f31ef5aSJens Wiklander * @link: Linked list element
279f31ef5aSJens Wiklander * @num_slices: Number of elements in the @slices array below
289f31ef5aSJens Wiklander * @slices: Array of file slices holding the fobjs of this file
299f31ef5aSJens Wiklander *
309f31ef5aSJens Wiklander * A file is constructed of slices which may be shared in different
319f31ef5aSJens Wiklander * mappings/contexts. There may be holes in the file for ranges of the file
329f31ef5aSJens Wiklander * that can't be shared.
339f31ef5aSJens Wiklander */
349f31ef5aSJens Wiklander struct file {
359f31ef5aSJens Wiklander uint8_t tag[FILE_TAG_SIZE];
369f31ef5aSJens Wiklander unsigned int taglen;
379f31ef5aSJens Wiklander struct refcount refc;
389f31ef5aSJens Wiklander TAILQ_ENTRY(file) link;
39bae0f170SJens Wiklander struct mutex mu;
40bae0f170SJens Wiklander SLIST_HEAD(, file_slice_elem) slice_head;
419f31ef5aSJens Wiklander };
429f31ef5aSJens Wiklander
439f31ef5aSJens Wiklander static struct mutex file_mu = MUTEX_INITIALIZER;
449f31ef5aSJens Wiklander static TAILQ_HEAD(, file) file_head = TAILQ_HEAD_INITIALIZER(file_head);
459f31ef5aSJens Wiklander
file_tag_cmp(const struct file * f,const uint8_t * tag,unsigned int taglen)469f31ef5aSJens Wiklander static int file_tag_cmp(const struct file *f, const uint8_t *tag,
479f31ef5aSJens Wiklander unsigned int taglen)
489f31ef5aSJens Wiklander {
499f31ef5aSJens Wiklander if (f->taglen != taglen)
509f31ef5aSJens Wiklander return -1;
519f31ef5aSJens Wiklander return memcmp(tag, f->tag, taglen);
529f31ef5aSJens Wiklander }
539f31ef5aSJens Wiklander
file_find_tag_unlocked(const uint8_t * tag,unsigned int taglen)549f31ef5aSJens Wiklander static struct file *file_find_tag_unlocked(const uint8_t *tag,
559f31ef5aSJens Wiklander unsigned int taglen)
569f31ef5aSJens Wiklander {
579f31ef5aSJens Wiklander struct file *f = NULL;
589f31ef5aSJens Wiklander
599f31ef5aSJens Wiklander TAILQ_FOREACH(f, &file_head, link)
609f31ef5aSJens Wiklander if (!file_tag_cmp(f, tag, taglen))
619f31ef5aSJens Wiklander return f;
629f31ef5aSJens Wiklander
639f31ef5aSJens Wiklander return NULL;
649f31ef5aSJens Wiklander }
659f31ef5aSJens Wiklander
file_free(struct file * f)669f31ef5aSJens Wiklander static void file_free(struct file *f)
679f31ef5aSJens Wiklander {
68bae0f170SJens Wiklander mutex_destroy(&f->mu);
699f31ef5aSJens Wiklander
70bae0f170SJens Wiklander while (!SLIST_EMPTY(&f->slice_head)) {
71bae0f170SJens Wiklander struct file_slice_elem *fse = SLIST_FIRST(&f->slice_head);
72bae0f170SJens Wiklander
73bae0f170SJens Wiklander SLIST_REMOVE_HEAD(&f->slice_head, link);
74bae0f170SJens Wiklander fobj_put(fse->slice.fobj);
75bae0f170SJens Wiklander free(fse);
76bae0f170SJens Wiklander }
779f31ef5aSJens Wiklander
789f31ef5aSJens Wiklander free(f);
799f31ef5aSJens Wiklander }
809f31ef5aSJens Wiklander
file_add_slice(struct file * f,struct fobj * fobj,unsigned int page_offset)81bae0f170SJens Wiklander TEE_Result file_add_slice(struct file *f, struct fobj *fobj,
82bae0f170SJens Wiklander unsigned int page_offset)
839f31ef5aSJens Wiklander {
84bae0f170SJens Wiklander struct file_slice_elem *fse = NULL;
859f31ef5aSJens Wiklander unsigned int s = 0;
869f31ef5aSJens Wiklander
87bae0f170SJens Wiklander /* Check for conflicts */
88bae0f170SJens Wiklander if (file_find_slice(f, page_offset))
89bae0f170SJens Wiklander return TEE_ERROR_BAD_PARAMETERS;
909f31ef5aSJens Wiklander
91bae0f170SJens Wiklander fse = calloc(1, sizeof(*fse));
92bae0f170SJens Wiklander if (!fse)
93bae0f170SJens Wiklander return TEE_ERROR_OUT_OF_MEMORY;
949f31ef5aSJens Wiklander
95bae0f170SJens Wiklander fse->slice.fobj = fobj_get(fobj);
96bae0f170SJens Wiklander if (!fse->slice.fobj ||
97bae0f170SJens Wiklander ADD_OVERFLOW(page_offset, fse->slice.fobj->num_pages, &s)) {
98bae0f170SJens Wiklander fobj_put(fse->slice.fobj);
99bae0f170SJens Wiklander free(fse);
100bae0f170SJens Wiklander return TEE_ERROR_BAD_PARAMETERS;
1019f31ef5aSJens Wiklander }
1029f31ef5aSJens Wiklander
103bae0f170SJens Wiklander fse->slice.page_offset = page_offset;
104bae0f170SJens Wiklander SLIST_INSERT_HEAD(&f->slice_head, fse, link);
1059f31ef5aSJens Wiklander
106bae0f170SJens Wiklander return TEE_SUCCESS;
1079f31ef5aSJens Wiklander }
1089f31ef5aSJens Wiklander
file_get(struct file * f)1099f31ef5aSJens Wiklander struct file *file_get(struct file *f)
1109f31ef5aSJens Wiklander {
1119f31ef5aSJens Wiklander if (f && !refcount_inc(&f->refc))
1129f31ef5aSJens Wiklander panic();
1139f31ef5aSJens Wiklander
1149f31ef5aSJens Wiklander return f;
1159f31ef5aSJens Wiklander }
1169f31ef5aSJens Wiklander
file_get_by_tag(const uint8_t * tag,unsigned int taglen)117bae0f170SJens Wiklander struct file *file_get_by_tag(const uint8_t *tag, unsigned int taglen)
1189f31ef5aSJens Wiklander {
1199f31ef5aSJens Wiklander struct file *f = NULL;
1209f31ef5aSJens Wiklander
121bae0f170SJens Wiklander if (taglen > sizeof(f->tag))
122bae0f170SJens Wiklander return NULL;
123bae0f170SJens Wiklander
1249f31ef5aSJens Wiklander mutex_lock(&file_mu);
125*b2cd936aSJens Wiklander
126*b2cd936aSJens Wiklander /*
127*b2cd936aSJens Wiklander * If file is found and reference counter can be increased, we're done.
128*b2cd936aSJens Wiklander * If file can't be found, it doesn't exist so it has to be added.
129*b2cd936aSJens Wiklander * If it's found but reference counter is 0, the situation is
130*b2cd936aSJens Wiklander * a bit complicated:
131*b2cd936aSJens Wiklander * - file_put() is about to free the file as soon as it can obtain the
132*b2cd936aSJens Wiklander * mutex.
133*b2cd936aSJens Wiklander * - Unless there's a mismatch between file_get() and file_put() only
134*b2cd936aSJens Wiklander * one thread calling file_put() is about to free the file.
135*b2cd936aSJens Wiklander *
136*b2cd936aSJens Wiklander * There's a window of opportunity where file_put() is called
137*b2cd936aSJens Wiklander * (without a mutex being held, which is quite OK) while we're
138*b2cd936aSJens Wiklander * holding the mutex here and are searching for the file and it's
139*b2cd936aSJens Wiklander * found, but just after file_put() has decreased the reference
140*b2cd936aSJens Wiklander * counter.
141*b2cd936aSJens Wiklander *
142*b2cd936aSJens Wiklander * To keep it simple we're adding a new file at the head (so new
143*b2cd936aSJens Wiklander * searches finds this file instead of the old being freed) instead
144*b2cd936aSJens Wiklander * of complicating file_put() by trying to rescue the file and
145*b2cd936aSJens Wiklander * possibly hiding a case of mismatching file_put() and file_get().
146*b2cd936aSJens Wiklander */
147*b2cd936aSJens Wiklander f = file_find_tag_unlocked(tag, taglen);
148*b2cd936aSJens Wiklander if (f && refcount_inc(&f->refc))
149bae0f170SJens Wiklander goto out;
150*b2cd936aSJens Wiklander
151bae0f170SJens Wiklander f = calloc(1, sizeof(*f));
152bae0f170SJens Wiklander if (!f)
153bae0f170SJens Wiklander goto out;
154bae0f170SJens Wiklander memcpy(f->tag, tag, taglen);
155bae0f170SJens Wiklander f->taglen = taglen;
156bae0f170SJens Wiklander refcount_set(&f->refc, 1);
157bae0f170SJens Wiklander mutex_init(&f->mu);
158bae0f170SJens Wiklander SLIST_INIT(&f->slice_head);
159*b2cd936aSJens Wiklander TAILQ_INSERT_HEAD(&file_head, f, link);
160bae0f170SJens Wiklander
161bae0f170SJens Wiklander out:
1629f31ef5aSJens Wiklander mutex_unlock(&file_mu);
1639f31ef5aSJens Wiklander
1649f31ef5aSJens Wiklander return f;
1659f31ef5aSJens Wiklander }
1669f31ef5aSJens Wiklander
file_put(struct file * f)1679f31ef5aSJens Wiklander void file_put(struct file *f)
1689f31ef5aSJens Wiklander {
169bae0f170SJens Wiklander if (f && refcount_dec(&f->refc)) {
1709f31ef5aSJens Wiklander mutex_lock(&file_mu);
1719f31ef5aSJens Wiklander TAILQ_REMOVE(&file_head, f, link);
1729f31ef5aSJens Wiklander mutex_unlock(&file_mu);
1739f31ef5aSJens Wiklander
1749f31ef5aSJens Wiklander file_free(f);
1759f31ef5aSJens Wiklander }
1769f31ef5aSJens Wiklander
177bae0f170SJens Wiklander }
178bae0f170SJens Wiklander
file_find_slice(struct file * f,unsigned int page_offset)1799f31ef5aSJens Wiklander struct file_slice *file_find_slice(struct file *f, unsigned int page_offset)
1809f31ef5aSJens Wiklander {
181bae0f170SJens Wiklander struct file_slice_elem *fse = NULL;
1829f31ef5aSJens Wiklander
183bae0f170SJens Wiklander assert(f->mu.state);
1849f31ef5aSJens Wiklander
185bae0f170SJens Wiklander SLIST_FOREACH(fse, &f->slice_head, link) {
186bae0f170SJens Wiklander struct file_slice *fs = &fse->slice;
1879f31ef5aSJens Wiklander
1889f31ef5aSJens Wiklander if (page_offset >= fs->page_offset &&
1899f31ef5aSJens Wiklander page_offset < fs->page_offset + fs->fobj->num_pages)
1909f31ef5aSJens Wiklander return fs;
1919f31ef5aSJens Wiklander }
1929f31ef5aSJens Wiklander
1939f31ef5aSJens Wiklander return NULL;
1949f31ef5aSJens Wiklander }
195bae0f170SJens Wiklander
file_lock(struct file * f)196bae0f170SJens Wiklander void file_lock(struct file *f)
197bae0f170SJens Wiklander {
198bae0f170SJens Wiklander mutex_lock(&f->mu);
199bae0f170SJens Wiklander }
200bae0f170SJens Wiklander
file_trylock(struct file * f)201bae0f170SJens Wiklander bool file_trylock(struct file *f)
202bae0f170SJens Wiklander {
203bae0f170SJens Wiklander return mutex_trylock(&f->mu);
204bae0f170SJens Wiklander }
205bae0f170SJens Wiklander
file_unlock(struct file * f)206bae0f170SJens Wiklander void file_unlock(struct file *f)
207bae0f170SJens Wiklander {
208bae0f170SJens Wiklander mutex_unlock(&f->mu);
209bae0f170SJens Wiklander }
210