xref: /optee_os/core/mm/file.c (revision bae0f170e3217a9322f4c36879a65bc12f50e96a)
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 	f = file_get(file_find_tag_unlocked(tag, taglen));
126 	if (f)
127 		goto out;
128 	f = calloc(1, sizeof(*f));
129 	if (!f)
130 		goto out;
131 	memcpy(f->tag, tag, taglen);
132 	f->taglen = taglen;
133 	refcount_set(&f->refc, 1);
134 	mutex_init(&f->mu);
135 	SLIST_INIT(&f->slice_head);
136 	TAILQ_INSERT_TAIL(&file_head, f, link);
137 
138 out:
139 	mutex_unlock(&file_mu);
140 
141 	return f;
142 }
143 
144 void file_put(struct file *f)
145 {
146 	if (f && refcount_dec(&f->refc)) {
147 		mutex_lock(&file_mu);
148 		TAILQ_REMOVE(&file_head, f, link);
149 		mutex_unlock(&file_mu);
150 
151 		file_free(f);
152 	}
153 
154 }
155 
156 struct file_slice *file_find_slice(struct file *f, unsigned int page_offset)
157 {
158 	struct file_slice_elem *fse = NULL;
159 
160 	assert(f->mu.state);
161 
162 	SLIST_FOREACH(fse, &f->slice_head, link) {
163 		struct file_slice *fs = &fse->slice;
164 
165 		if (page_offset >= fs->page_offset &&
166 		    page_offset < fs->page_offset + fs->fobj->num_pages)
167 			return fs;
168 	}
169 
170 	return NULL;
171 }
172 
173 void file_lock(struct file *f)
174 {
175 	mutex_lock(&f->mu);
176 }
177 
178 bool file_trylock(struct file *f)
179 {
180 	return mutex_trylock(&f->mu);
181 }
182 
183 void file_unlock(struct file *f)
184 {
185 	mutex_unlock(&f->mu);
186 }
187