1 /*
2 * Copyright (c) 2021-2023, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include "variable_index.h"
9
10 #include <stdlib.h>
11 #include <string.h>
12
generate_uid(const struct variable_index * context,const EFI_GUID * guid,size_t name_size,const int16_t * name)13 static uint64_t generate_uid(const struct variable_index *context, const EFI_GUID *guid,
14 size_t name_size, const int16_t *name)
15 {
16 /* Find the first unsed UID in the: 1..max_variables inclusive range */
17 for (size_t candidate = 1; candidate <= context->max_variables; candidate++) {
18 for (size_t pos = 0; pos < context->max_variables; pos++) {
19 if (context->entries[pos].in_use &&
20 context->entries[pos].info.metadata.uid == candidate) {
21 // The candidate UID is already being used
22 goto skip;
23 }
24 }
25
26 return candidate;
27
28 skip:
29 }
30
31 return 0;
32 }
33
is_matching_entry(const EFI_GUID * guid,const int16_t * name,size_t name_size,const struct variable_metadata * metadata)34 static bool is_matching_entry(const EFI_GUID *guid, const int16_t *name, size_t name_size,
35 const struct variable_metadata *metadata)
36 {
37 if (!compare_guid(guid, &metadata->guid))
38 return false;
39
40 if (name_size != metadata->name_size)
41 return false;
42
43 return (memcmp(name, metadata->name, name_size) == 0);
44 }
45
find_variable(const struct variable_index * context,const EFI_GUID * guid,size_t name_size,const int16_t * name)46 static int find_variable(const struct variable_index *context, const EFI_GUID *guid,
47 size_t name_size, const int16_t *name)
48 {
49 int found_pos = -1;
50
51 for (size_t pos = 0; pos < context->max_variables; pos++) {
52 if ((context->entries[pos].in_use) &&
53 is_matching_entry(guid, name, name_size,
54 &context->entries[pos].info.metadata)) {
55 found_pos = pos;
56 break;
57 }
58 }
59
60 return found_pos;
61 }
62
find_free(const struct variable_index * context)63 static int find_free(const struct variable_index *context)
64 {
65 int free_pos = -1;
66
67 for (size_t pos = 0; pos < context->max_variables; pos++) {
68 if (!context->entries[pos].in_use) {
69 free_pos = pos;
70 break;
71 }
72 }
73
74 return free_pos;
75 }
76
mark_dirty(struct variable_entry * entry)77 static void mark_dirty(struct variable_entry *entry)
78 {
79 if (entry->info.metadata.attributes & EFI_VARIABLE_NON_VOLATILE)
80 entry->dirty = true;
81 }
82
containing_entry(const struct variable_info * info)83 static struct variable_entry *containing_entry(const struct variable_info *info)
84 {
85 size_t info_offset = offsetof(struct variable_entry, info);
86 struct variable_entry *entry = (struct variable_entry *)((uint8_t *)info - info_offset);
87 return entry;
88 }
89
90 /* Public functions */
variable_index_init(struct variable_index * context,size_t max_variables)91 efi_status_t variable_index_init(struct variable_index *context, size_t max_variables)
92 {
93 context->max_variables = max_variables;
94 context->counter = 0;
95 context->entries =
96 (struct variable_entry *)malloc(sizeof(struct variable_entry) * max_variables);
97
98 if (context->entries) {
99 memset(context->entries, 0, sizeof(struct variable_entry) * max_variables);
100 }
101
102 return (context->entries) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
103 }
104
variable_index_deinit(struct variable_index * context)105 void variable_index_deinit(struct variable_index *context)
106 {
107 free(context->entries);
108 }
109
variable_index_max_dump_size(struct variable_index * context)110 size_t variable_index_max_dump_size(struct variable_index *context)
111 {
112 return sizeof(context->counter) + (sizeof(struct variable_metadata) + sizeof(bool) +
113 sizeof(struct variable_constraints)) *
114 context->max_variables;
115 }
116
variable_index_find(const struct variable_index * context,const EFI_GUID * guid,size_t name_size,const int16_t * name)117 struct variable_info *variable_index_find(const struct variable_index *context,
118 const EFI_GUID *guid, size_t name_size,
119 const int16_t *name)
120 {
121 struct variable_info *result = NULL;
122 int pos = find_variable(context, guid, name_size, name);
123
124 if (pos >= 0) {
125 result = &context->entries[pos].info;
126 }
127
128 return result;
129 }
130
variable_index_find_next(const struct variable_index * context,const EFI_GUID * guid,size_t name_size,const int16_t * name,efi_status_t * status)131 struct variable_info *variable_index_find_next(const struct variable_index *context,
132 const EFI_GUID *guid, size_t name_size,
133 const int16_t *name, efi_status_t *status)
134 {
135 struct variable_info *result = NULL;
136 *status = EFI_NOT_FOUND;
137
138 if (name_size >= sizeof(int16_t)) {
139 /*
140 * Name must be at least one character long to accommodate
141 * the mandatory null terminator.
142 */
143 if (name[0] != 0) {
144 /* Find next from current name */
145 int pos = find_variable(context, guid, name_size, name);
146
147 if (pos >= 0) {
148 /* Iterate to next used entry */
149 ++pos;
150 while (pos < (int)context->max_variables) {
151 if (context->entries[pos].in_use &&
152 context->entries[pos].info.is_variable_set) {
153 result = &context->entries[pos].info;
154 *status = EFI_SUCCESS;
155 break;
156 }
157
158 ++pos;
159 }
160 } else {
161 /* A non-empty name was provided but it wasn't found */
162 *status = EFI_INVALID_PARAMETER;
163 }
164 } else {
165 /* Find first */
166 int pos = 0;
167
168 while (pos < (int)context->max_variables) {
169 if (context->entries[pos].in_use &&
170 context->entries[pos].info.is_variable_set) {
171 result = &context->entries[pos].info;
172 *status = EFI_SUCCESS;
173 break;
174 }
175
176 ++pos;
177 }
178 }
179 }
180
181 return result;
182 }
183
add_entry(const struct variable_index * context,const EFI_GUID * guid,size_t name_size,const int16_t * name)184 static struct variable_entry *add_entry(const struct variable_index *context, const EFI_GUID *guid,
185 size_t name_size, const int16_t *name)
186 {
187 struct variable_entry *entry = NULL;
188
189 if (name_size <= (VARIABLE_INDEX_MAX_NAME_SIZE * sizeof(int16_t))) {
190 int pos = find_free(context);
191
192 if (pos >= 0) {
193 entry = &context->entries[pos];
194
195 struct variable_info *info = &entry->info;
196
197 /* Initialize metadata */
198 info->metadata.uid = generate_uid(context, guid, name_size, name);
199 if (!info->metadata.uid)
200 return NULL;
201
202 info->metadata.guid = *guid;
203 memset(&info->metadata.timestamp, 0, sizeof(EFI_TIME));
204 memset(&info->metadata.fingerprint, 0, FINGERPRINT_SIZE);
205 info->metadata.attributes = 0;
206 info->metadata.name_size = name_size;
207 memcpy(info->metadata.name, name, name_size);
208
209 info->is_constraints_set = false;
210 info->is_variable_set = false;
211
212 entry->in_use = true;
213 }
214 }
215
216 return entry;
217 }
218
variable_index_add_entry(const struct variable_index * context,const EFI_GUID * guid,size_t name_size,const int16_t * name)219 struct variable_info *variable_index_add_entry(const struct variable_index *context,
220 const EFI_GUID *guid, size_t name_size,
221 const int16_t *name)
222 {
223 struct variable_info *info = NULL;
224 struct variable_entry *entry = add_entry(context, guid, name_size, name);
225
226 if (entry) {
227 info = &entry->info;
228 }
229
230 return info;
231 }
232
variable_index_remove_unused_entry(const struct variable_index * context,struct variable_info * info)233 void variable_index_remove_unused_entry(const struct variable_index *context,
234 struct variable_info *info)
235 {
236 (void)context;
237
238 if (info && !info->is_constraints_set && !info->is_variable_set) {
239 struct variable_entry *entry = containing_entry(info);
240 entry->in_use = false;
241
242 memset(info, 0, sizeof(struct variable_info));
243 }
244 }
245
variable_index_set_variable(struct variable_info * info,uint32_t attributes)246 void variable_index_set_variable(struct variable_info *info, uint32_t attributes)
247 {
248 struct variable_entry *entry = containing_entry(info);
249
250 info->metadata.attributes = attributes;
251 info->is_variable_set = true;
252
253 mark_dirty(entry);
254 }
255
variable_index_clear_variable(const struct variable_index * context,struct variable_info * info)256 void variable_index_clear_variable(const struct variable_index *context, struct variable_info *info)
257 {
258 (void)context;
259
260 if (info) {
261 struct variable_entry *entry = containing_entry(info);
262 mark_dirty(entry);
263
264 /* Mark variable as no longer set */
265 entry->info.is_variable_set = false;
266 }
267 }
268
variable_index_set_constraints(struct variable_info * info,const struct variable_constraints * constraints)269 void variable_index_set_constraints(struct variable_info *info,
270 const struct variable_constraints *constraints)
271 {
272 if (info) {
273 struct variable_entry *entry = containing_entry(info);
274
275 info->check_constraints = *constraints;
276 info->is_constraints_set = true;
277
278 mark_dirty(entry);
279 }
280 }
281
variable_index_dump(const struct variable_index * context,size_t buffer_size,uint8_t * buffer,size_t * data_len,bool * any_dirty)282 efi_status_t variable_index_dump(const struct variable_index *context, size_t buffer_size,
283 uint8_t *buffer, size_t *data_len, bool *any_dirty)
284 {
285 uint8_t *dump_pos = buffer;
286 size_t bytes_dumped = 0;
287
288 *data_len = 0;
289 *any_dirty = false;
290
291 /*
292 * Intentionally letting the counter overflow.
293 * The buffer (index_sync_buffer) is provided by malloc, which allocates memory to a boundary
294 * suitable for any default data type of the system (e.g uint32_t)
295 */
296 *((uint32_t *)dump_pos) = context->counter + 1;
297 bytes_dumped += sizeof(context->counter);
298 dump_pos += sizeof(context->counter);
299
300 /* Store variables */
301 for (size_t pos = 0; pos < context->max_variables; pos++) {
302 struct variable_entry *entry = &context->entries[pos];
303 struct variable_metadata *metadata = &entry->info.metadata;
304 struct variable_constraints *constraints = &entry->info.check_constraints;
305
306 if (entry->in_use && entry->info.is_variable_set &&
307 (metadata->attributes & EFI_VARIABLE_NON_VOLATILE)) {
308 /* Store metadata */
309 if (bytes_dumped + sizeof(struct variable_metadata) > buffer_size)
310 return EFI_BUFFER_TOO_SMALL;
311
312 memcpy(dump_pos, metadata, sizeof(struct variable_metadata));
313 bytes_dumped += sizeof(struct variable_metadata);
314 dump_pos += sizeof(struct variable_metadata);
315
316 /* Store constraints' status */
317 if (bytes_dumped + sizeof(entry->info.is_constraints_set) > buffer_size)
318 return EFI_BUFFER_TOO_SMALL;
319
320 memcpy(dump_pos, &entry->info.is_constraints_set,
321 sizeof(entry->info.is_constraints_set));
322 bytes_dumped += sizeof(entry->info.is_constraints_set);
323 dump_pos += sizeof(entry->info.is_constraints_set);
324
325 /* Store constraints, if they are set */
326 if (entry->info.is_constraints_set) {
327 if (bytes_dumped + sizeof(entry->info.check_constraints) >
328 buffer_size)
329 return EFI_BUFFER_TOO_SMALL;
330
331 memcpy(dump_pos, constraints,
332 sizeof(entry->info.check_constraints));
333 bytes_dumped += sizeof(entry->info.check_constraints);
334 dump_pos += sizeof(entry->info.check_constraints);
335 }
336 }
337
338 *any_dirty |= entry->dirty;
339 entry->dirty = false;
340 }
341
342 *data_len = bytes_dumped;
343
344 return EFI_SUCCESS;
345 }
346
variable_index_confirm_write(struct variable_index * context)347 void variable_index_confirm_write(struct variable_index *context)
348 {
349 context->counter++;
350 }
351
variable_index_restore(struct variable_index * context,size_t data_len,const uint8_t * buffer)352 size_t variable_index_restore(struct variable_index *context, size_t data_len,
353 const uint8_t *buffer)
354 {
355 size_t bytes_loaded = 0;
356 const uint8_t *load_pos = buffer;
357 int pos = 0;
358
359 if (data_len >= sizeof(context->counter)) {
360 context->counter = *((uint32_t *)load_pos);
361 bytes_loaded += sizeof(context->counter);
362 load_pos += sizeof(context->counter);
363 }
364
365 while (bytes_loaded < data_len) {
366 struct variable_entry *entry = &context->entries[pos];
367
368 if ((data_len - bytes_loaded) >= sizeof(struct variable_metadata)) {
369 struct variable_metadata *metadata = &entry->info.metadata;
370
371 /* Load metadata */
372 memcpy(metadata, load_pos, sizeof(struct variable_metadata));
373 bytes_loaded += sizeof(struct variable_metadata);
374 load_pos += sizeof(struct variable_metadata);
375 } else {
376 /* Not a whole number of variable_metadata structs! */
377 break;
378 }
379
380 if ((data_len - bytes_loaded) >= sizeof(entry->info.is_constraints_set)) {
381 /* Load constraints' status */
382 memcpy(&entry->info.is_constraints_set, load_pos,
383 sizeof(entry->info.is_constraints_set));
384 bytes_loaded += sizeof(entry->info.is_constraints_set);
385 load_pos += sizeof(entry->info.is_constraints_set);
386 } else {
387 /* Not enough space for constraints' status! */
388 break;
389 }
390
391 if (entry->info.is_constraints_set) {
392 if ((data_len - bytes_loaded) >= sizeof(struct variable_constraints)) {
393 struct variable_constraints *constraints =
394 &entry->info.check_constraints;
395
396 /* Load constraints if they are set */
397 memcpy(constraints, load_pos, sizeof(struct variable_constraints));
398 bytes_loaded += sizeof(struct variable_constraints);
399 load_pos += sizeof(struct variable_constraints);
400 } else {
401 /* Not a whole number of variable_constraints structs! */
402 break;
403 }
404 }
405
406 entry->info.is_variable_set = true;
407 entry->in_use = true;
408
409 ++pos;
410 }
411
412 return bytes_loaded;
413 }
414