xref: /optee_os/core/mm/file.c (revision 41e5aa8f18c4d48083341ff3df9e75f0c77cf703)
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