blob: 99d7c97a8b1d02412867b5c84a699cb38970ef98 [file] [log] [blame]
Julian Hall65f7eb42021-11-22 16:06:42 +01001/*
2 * Copyright (c) 2021, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8#include <stdlib.h>
9#include <string.h>
10#include "variable_index.h"
11
12/* Private functions */
13static uint64_t name_hash(
14 const EFI_GUID *guid,
15 size_t name_size,
16 const int16_t *name)
17{
18 /* Using djb2 hash by Dan Bernstein */
19 uint64_t hash = 5381;
20
21 /* Calculate hash over GUID */
22 hash = ((hash << 5) + hash) + guid->Data1;
23 hash = ((hash << 5) + hash) + guid->Data2;
24 hash = ((hash << 5) + hash) + guid->Data3;
25
26 for (int i = 0; i < 8; ++i) {
27
28 hash = ((hash << 5) + hash) + guid->Data4[i];
29 }
30
31 /* Extend to cover name up to but not including null terminator */
32 for (int i = 0; i < name_size / sizeof(int16_t); ++i) {
33
34 if (!name[i]) break;
35 hash = ((hash << 5) + hash) + name[i];
36 }
37
38 return hash;
39}
40
41static uint64_t generate_uid(
42 const struct variable_index *context,
43 const EFI_GUID *guid,
44 size_t name_size,
45 const int16_t *name)
46{
47 uint64_t uid = name_hash(guid, name_size, name);
48
49 /* todo - handle collision */
50 (void)context;
51
52 return uid;
53}
54
55static int find_variable(
56 const struct variable_index *context,
57 const EFI_GUID *guid,
58 size_t name_size,
59 const int16_t *name)
60{
61 int found_pos = -1;
62 uint64_t uid = name_hash(guid, name_size, name);
63
64 for (int pos = 0; pos < context->max_variables; pos++) {
65
66 if ((context->entries[pos].in_use) &&
Julian Hall0a86f762021-11-08 13:31:23 +000067 (uid == context->entries[pos].info.metadata.uid)) {
Julian Hall65f7eb42021-11-22 16:06:42 +010068
69 found_pos = pos;
70 break;
71 }
72 }
73
74 return found_pos;
75}
76
77static int find_free(
78 const struct variable_index *context)
79{
80 int free_pos = -1;
81
82 for (int pos = 0; pos < context->max_variables; pos++) {
83
84 if (!context->entries[pos].in_use) {
85
86 free_pos = pos;
87 break;
88 }
89 }
90
91 return free_pos;
92}
93
94static void mark_dirty(struct variable_entry *entry)
95{
Julian Hall0a86f762021-11-08 13:31:23 +000096 if (entry->info.metadata.attributes & EFI_VARIABLE_NON_VOLATILE)
Julian Hall65f7eb42021-11-22 16:06:42 +010097 entry->dirty = true;
98}
99
Julian Hallc0d54dc2021-10-13 15:18:30 +0100100static struct variable_entry *containing_entry(const struct variable_info *info)
101{
102 size_t info_offset = offsetof(struct variable_entry, info);
103 struct variable_entry *entry = (struct variable_entry*)((uint8_t*)info - info_offset);
104 return entry;
105}
106
Julian Hall65f7eb42021-11-22 16:06:42 +0100107/* Public functions */
108efi_status_t variable_index_init(
109 struct variable_index *context,
110 size_t max_variables)
111{
112 context->max_variables = max_variables;
113 context->entries = (struct variable_entry*)
114 malloc(sizeof(struct variable_entry) * max_variables);
115
116 if (context->entries) {
117 memset(context->entries, 0, sizeof(struct variable_entry) * max_variables);
118 }
119
120 return (context->entries) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
121}
122
123void variable_index_deinit(
124 struct variable_index *context)
125{
126 free(context->entries);
127}
128
129size_t variable_index_max_dump_size(
130 struct variable_index *context)
131{
Julian Hall0a86f762021-11-08 13:31:23 +0000132 return sizeof(struct variable_metadata) * context->max_variables;
Julian Hall65f7eb42021-11-22 16:06:42 +0100133}
134
135const struct variable_info *variable_index_find(
136 const struct variable_index *context,
137 const EFI_GUID *guid,
138 size_t name_size,
139 const int16_t *name)
140{
141 const struct variable_info *result = NULL;
142 int pos = find_variable(context, guid, name_size, name);
143
144 if (pos >= 0) {
145
146 result = &context->entries[pos].info;
147 }
148
149 return result;
150}
151
152const struct variable_info *variable_index_find_next(
153 const struct variable_index *context,
154 const EFI_GUID *guid,
155 size_t name_size,
156 const int16_t *name)
157{
158 const struct variable_info *result = NULL;
159
160 if (name_size >= sizeof(int16_t)) {
161
162 /*
163 * Name must be at least one character long to accommodate
164 * the mandatory null terminator.
165 */
166 if (name[0] != 0) {
167
168 /* Find next from current name */
169 int pos = find_variable(context, guid, name_size, name);
170
171 if (pos >= 0) {
172
173 /* Iterate to next used entry */
174 ++pos;
175 while (pos < context->max_variables) {
176
Julian Hall0a86f762021-11-08 13:31:23 +0000177 if (context->entries[pos].in_use &&
178 context->entries[pos].info.is_variable_set) {
Julian Hall65f7eb42021-11-22 16:06:42 +0100179
180 result = &context->entries[pos].info;
181 break;
182 }
183
184 ++pos;
185 }
186 }
187 }
188 else {
189
190 /* Find first */
191 int pos = 0;
192
193 while (pos < context->max_variables) {
194
Julian Hall0a86f762021-11-08 13:31:23 +0000195 if (context->entries[pos].in_use &&
196 context->entries[pos].info.is_variable_set) {
Julian Hall65f7eb42021-11-22 16:06:42 +0100197
198 result = &context->entries[pos].info;
199 break;
200 }
201
202 ++pos;
203 }
204 }
205 }
206
207 return result;
208}
209
210static void set_variable_name(
211 struct variable_info *info,
212 size_t name_size,
213 const int16_t *name)
214{
215 size_t trimmed_size = 0;
216
217 /* Trim the saved name to only include a single null terminator.
218 * Any additional terminators included in the client-set name size
219 * are discarded.
220 */
221 for (size_t i = 0; i < name_size; i++) {
222
223 ++trimmed_size;
Julian Hall0a86f762021-11-08 13:31:23 +0000224 info->metadata.name[i] = name[i];
Julian Hall65f7eb42021-11-22 16:06:42 +0100225
226 if (!name[i]) break;
227 }
228
Julian Hall0a86f762021-11-08 13:31:23 +0000229 info->metadata.name_size = trimmed_size * sizeof(int16_t);
Julian Hall65f7eb42021-11-22 16:06:42 +0100230}
231
Julian Hall0a86f762021-11-08 13:31:23 +0000232static struct variable_entry *add_entry(
Julian Hall65f7eb42021-11-22 16:06:42 +0100233 struct variable_index *context,
234 const EFI_GUID *guid,
235 size_t name_size,
Julian Hall0a86f762021-11-08 13:31:23 +0000236 const int16_t *name)
Julian Hall65f7eb42021-11-22 16:06:42 +0100237{
Julian Hall0a86f762021-11-08 13:31:23 +0000238 struct variable_entry *entry = NULL;
Julian Hall65f7eb42021-11-22 16:06:42 +0100239
240 if (name_size <= (VARIABLE_INDEX_MAX_NAME_SIZE * sizeof(int16_t))) {
241
242 int pos = find_free(context);
243
244 if (pos >= 0) {
245
Julian Hall0a86f762021-11-08 13:31:23 +0000246 entry = &context->entries[pos];
Julian Hall65f7eb42021-11-22 16:06:42 +0100247
Julian Hall0a86f762021-11-08 13:31:23 +0000248 struct variable_info *info = &entry->info;
249
250 /* Initialize metadata */
251 info->metadata.uid = generate_uid(context, guid, name_size, name);
252 info->metadata.guid = *guid;
253 info->metadata.attributes = 0;
Julian Hall65f7eb42021-11-22 16:06:42 +0100254 set_variable_name(info, name_size, name);
255
Julian Hall0a86f762021-11-08 13:31:23 +0000256 info->is_constraints_set = false;
257 info->is_variable_set = false;
258
Julian Hall65f7eb42021-11-22 16:06:42 +0100259 entry->in_use = true;
260 }
261 }
262
Julian Hall0a86f762021-11-08 13:31:23 +0000263 return entry;
264}
265
266const struct variable_info *variable_index_add_variable(
267 struct variable_index *context,
268 const EFI_GUID *guid,
269 size_t name_size,
270 const int16_t *name,
271 uint32_t attributes)
272{
273 struct variable_info *info = NULL;
274 struct variable_entry *entry = add_entry(context, guid, name_size, name);
275
276 if (entry) {
277
278 info = &entry->info;
279
280 info->metadata.attributes = attributes;
281 info->is_variable_set = true;
282
283 mark_dirty(entry);
284 }
285
Julian Hall65f7eb42021-11-22 16:06:42 +0100286 return info;
287}
288
Julian Hall0a86f762021-11-08 13:31:23 +0000289const struct variable_info *variable_index_add_constraints(
290 struct variable_index *context,
291 const EFI_GUID *guid,
292 size_t name_size,
293 const int16_t *name,
294 const struct variable_constraints *constraints)
295{
296 struct variable_info *info = NULL;
297 struct variable_entry *entry = add_entry(context, guid, name_size, name);
298
299 if (entry) {
300
301 info = &entry->info;
302
303 info->check_constraints = *constraints;
304 info->is_constraints_set = true;
305 }
306
307 return info;
308}
309
310void variable_index_remove_variable(
Julian Hall65f7eb42021-11-22 16:06:42 +0100311 struct variable_index *context,
Julian Hallc0d54dc2021-10-13 15:18:30 +0100312 const struct variable_info *info)
Julian Hall65f7eb42021-11-22 16:06:42 +0100313{
Julian Hallc0d54dc2021-10-13 15:18:30 +0100314 if (info) {
Julian Hall65f7eb42021-11-22 16:06:42 +0100315
Julian Hallc0d54dc2021-10-13 15:18:30 +0100316 struct variable_entry *entry = containing_entry(info);
Julian Hall65f7eb42021-11-22 16:06:42 +0100317 mark_dirty(entry);
Julian Hall65f7eb42021-11-22 16:06:42 +0100318
Julian Hall0a86f762021-11-08 13:31:23 +0000319 /* Mark variable as no longer set */
320 entry->info.is_variable_set = false;
321
322 /* Entry may still be needed if check constraints were set */
323 entry->in_use = info->is_constraints_set;
324
325 if (!entry->in_use) {
326
327 /* Entry not needed so wipe */
328 memset(&entry->info, 0, sizeof(struct variable_info));
329 }
Julian Hall65f7eb42021-11-22 16:06:42 +0100330 }
331}
332
Julian Hall0a86f762021-11-08 13:31:23 +0000333void variable_index_update_variable(
Julian Hall65f7eb42021-11-22 16:06:42 +0100334 const struct variable_info *info,
335 uint32_t attributes)
336{
337 if (info) {
338
339 struct variable_info *modified_info = (struct variable_info*)info;
Julian Hallc0d54dc2021-10-13 15:18:30 +0100340 struct variable_entry *entry = containing_entry(modified_info);
Julian Hall65f7eb42021-11-22 16:06:42 +0100341
Julian Hall0a86f762021-11-08 13:31:23 +0000342 if (!modified_info->is_variable_set ||
343 (attributes != modified_info->metadata.attributes)) {
Julian Hall65f7eb42021-11-22 16:06:42 +0100344
Julian Hall0a86f762021-11-08 13:31:23 +0000345 /* The update changes the variable_info state */
346 modified_info->is_variable_set = true;
347 modified_info->metadata.attributes = attributes;
Julian Hall65f7eb42021-11-22 16:06:42 +0100348 mark_dirty(entry);
349 }
350 }
351}
352
Julian Hall0a86f762021-11-08 13:31:23 +0000353void variable_index_update_constraints(
354 const struct variable_info *info,
355 const struct variable_constraints *constraints)
356{
357 if (info) {
358
359 struct variable_info *modified_info = (struct variable_info*)info;
360
361 modified_info->check_constraints = *constraints;
362 modified_info->is_constraints_set = true;
363 }
364}
365
Julian Hall65f7eb42021-11-22 16:06:42 +0100366bool variable_index_dump(
367 struct variable_index *context,
368 size_t buffer_size,
369 uint8_t *buffer,
370 size_t *data_len)
371{
372 bool any_dirty = false;
373 uint8_t *dump_pos = buffer;
374 size_t bytes_dumped = 0;
375
376 for (int pos = 0; pos < context->max_variables; pos++) {
377
378 struct variable_entry *entry = &context->entries[pos];
Julian Hall0a86f762021-11-08 13:31:23 +0000379 struct variable_metadata *metadata = &entry->info.metadata;
Julian Hall65f7eb42021-11-22 16:06:42 +0100380
381 if (entry->in_use &&
Julian Hall0a86f762021-11-08 13:31:23 +0000382 entry->info.is_variable_set &&
383 (metadata->attributes & EFI_VARIABLE_NON_VOLATILE) &&
384 ((bytes_dumped + sizeof(struct variable_metadata)) <= buffer_size)) {
Julian Hall65f7eb42021-11-22 16:06:42 +0100385
Julian Hall0a86f762021-11-08 13:31:23 +0000386 memcpy(dump_pos, metadata, sizeof(struct variable_metadata));
387 bytes_dumped += sizeof(struct variable_metadata);
388 dump_pos += sizeof(struct variable_metadata);
Julian Hall65f7eb42021-11-22 16:06:42 +0100389 }
390
391 any_dirty |= entry->dirty;
392 entry->dirty = false;
393 }
394
395 *data_len = bytes_dumped;
396
397 return any_dirty;
398}
399
400size_t variable_index_restore(
401 const struct variable_index *context,
402 size_t data_len,
403 const uint8_t *buffer)
404{
405 size_t bytes_loaded = 0;
406 const uint8_t *load_pos = buffer;
407 int pos = 0;
408
409 while (bytes_loaded < data_len) {
410
Julian Hall0a86f762021-11-08 13:31:23 +0000411 if ((data_len - bytes_loaded) >= sizeof(struct variable_metadata)) {
Julian Hall65f7eb42021-11-22 16:06:42 +0100412
413 struct variable_entry *entry = &context->entries[pos];
Julian Hall0a86f762021-11-08 13:31:23 +0000414 struct variable_metadata *metadata = &entry->info.metadata;
Julian Hall65f7eb42021-11-22 16:06:42 +0100415
Julian Hall0a86f762021-11-08 13:31:23 +0000416 memcpy(metadata, load_pos, sizeof(struct variable_metadata));
Julian Hall65f7eb42021-11-22 16:06:42 +0100417
Julian Hall0a86f762021-11-08 13:31:23 +0000418 entry->info.is_variable_set = true;
Julian Hall65f7eb42021-11-22 16:06:42 +0100419 entry->in_use = true;
420
Julian Hall0a86f762021-11-08 13:31:23 +0000421 bytes_loaded += sizeof(struct variable_metadata);
422 load_pos += sizeof(struct variable_metadata);
Julian Hall65f7eb42021-11-22 16:06:42 +0100423
424 ++pos;
425 }
426 else {
427
Julian Hall0a86f762021-11-08 13:31:23 +0000428 /* Not a whole number of variable_metadata structs! */
Julian Hall65f7eb42021-11-22 16:06:42 +0100429 break;
430 }
431 }
432
433 return bytes_loaded;
434}