blob: 4abb718f4b7dca38b69b757821d1298f60f17419 [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
Julian Hall4b8dd0a2021-12-02 10:15:54 +0000135struct variable_info *variable_index_find(
136 struct variable_index *context,
Julian Hall65f7eb42021-11-22 16:06:42 +0100137 const EFI_GUID *guid,
138 size_t name_size,
139 const int16_t *name)
140{
Julian Hall4b8dd0a2021-12-02 10:15:54 +0000141 struct variable_info *result = NULL;
Julian Hall65f7eb42021-11-22 16:06:42 +0100142 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
Julian Hall4b8dd0a2021-12-02 10:15:54 +0000152struct variable_info *variable_index_find_next(
Julian Hall65f7eb42021-11-22 16:06:42 +0100153 const struct variable_index *context,
154 const EFI_GUID *guid,
155 size_t name_size,
Julian Halld8242e62022-01-06 09:44:15 +0000156 const int16_t *name,
157 efi_status_t *status)
Julian Hall65f7eb42021-11-22 16:06:42 +0100158{
Julian Hall4b8dd0a2021-12-02 10:15:54 +0000159 struct variable_info *result = NULL;
Julian Halld8242e62022-01-06 09:44:15 +0000160 *status = EFI_NOT_FOUND;
Julian Hall65f7eb42021-11-22 16:06:42 +0100161
162 if (name_size >= sizeof(int16_t)) {
163
164 /*
165 * Name must be at least one character long to accommodate
166 * the mandatory null terminator.
167 */
168 if (name[0] != 0) {
169
170 /* Find next from current name */
171 int pos = find_variable(context, guid, name_size, name);
172
173 if (pos >= 0) {
174
175 /* Iterate to next used entry */
176 ++pos;
177 while (pos < context->max_variables) {
178
Julian Hall0a86f762021-11-08 13:31:23 +0000179 if (context->entries[pos].in_use &&
180 context->entries[pos].info.is_variable_set) {
Julian Hall65f7eb42021-11-22 16:06:42 +0100181
182 result = &context->entries[pos].info;
Julian Halld8242e62022-01-06 09:44:15 +0000183 *status = EFI_SUCCESS;
Julian Hall65f7eb42021-11-22 16:06:42 +0100184 break;
185 }
186
187 ++pos;
188 }
189 }
Julian Halld8242e62022-01-06 09:44:15 +0000190 else {
191
192 /* A non-empty name was provided but it wasn't found */
193 *status = EFI_INVALID_PARAMETER;
194 }
Julian Hall65f7eb42021-11-22 16:06:42 +0100195 }
196 else {
197
198 /* Find first */
199 int pos = 0;
200
201 while (pos < context->max_variables) {
202
Julian Hall0a86f762021-11-08 13:31:23 +0000203 if (context->entries[pos].in_use &&
204 context->entries[pos].info.is_variable_set) {
Julian Hall65f7eb42021-11-22 16:06:42 +0100205
206 result = &context->entries[pos].info;
Julian Halld8242e62022-01-06 09:44:15 +0000207 *status = EFI_SUCCESS;
Julian Hall65f7eb42021-11-22 16:06:42 +0100208 break;
209 }
210
211 ++pos;
212 }
213 }
214 }
215
216 return result;
217}
218
219static void set_variable_name(
220 struct variable_info *info,
221 size_t name_size,
222 const int16_t *name)
223{
224 size_t trimmed_size = 0;
225
226 /* Trim the saved name to only include a single null terminator.
227 * Any additional terminators included in the client-set name size
228 * are discarded.
229 */
230 for (size_t i = 0; i < name_size; i++) {
231
232 ++trimmed_size;
Julian Hall0a86f762021-11-08 13:31:23 +0000233 info->metadata.name[i] = name[i];
Julian Hall65f7eb42021-11-22 16:06:42 +0100234
235 if (!name[i]) break;
236 }
237
Julian Hall0a86f762021-11-08 13:31:23 +0000238 info->metadata.name_size = trimmed_size * sizeof(int16_t);
Julian Hall65f7eb42021-11-22 16:06:42 +0100239}
240
Julian Hall0a86f762021-11-08 13:31:23 +0000241static struct variable_entry *add_entry(
Julian Hall65f7eb42021-11-22 16:06:42 +0100242 struct variable_index *context,
243 const EFI_GUID *guid,
244 size_t name_size,
Julian Hall0a86f762021-11-08 13:31:23 +0000245 const int16_t *name)
Julian Hall65f7eb42021-11-22 16:06:42 +0100246{
Julian Hall0a86f762021-11-08 13:31:23 +0000247 struct variable_entry *entry = NULL;
Julian Hall65f7eb42021-11-22 16:06:42 +0100248
249 if (name_size <= (VARIABLE_INDEX_MAX_NAME_SIZE * sizeof(int16_t))) {
250
251 int pos = find_free(context);
252
253 if (pos >= 0) {
254
Julian Hall0a86f762021-11-08 13:31:23 +0000255 entry = &context->entries[pos];
Julian Hall65f7eb42021-11-22 16:06:42 +0100256
Julian Hall0a86f762021-11-08 13:31:23 +0000257 struct variable_info *info = &entry->info;
258
259 /* Initialize metadata */
260 info->metadata.uid = generate_uid(context, guid, name_size, name);
261 info->metadata.guid = *guid;
262 info->metadata.attributes = 0;
Julian Hall65f7eb42021-11-22 16:06:42 +0100263 set_variable_name(info, name_size, name);
264
Julian Hall0a86f762021-11-08 13:31:23 +0000265 info->is_constraints_set = false;
266 info->is_variable_set = false;
267
Julian Hall65f7eb42021-11-22 16:06:42 +0100268 entry->in_use = true;
269 }
270 }
271
Julian Hall0a86f762021-11-08 13:31:23 +0000272 return entry;
273}
274
Julian Hall4b8dd0a2021-12-02 10:15:54 +0000275struct variable_info *variable_index_add_entry(
Julian Hall0a86f762021-11-08 13:31:23 +0000276 struct variable_index *context,
277 const EFI_GUID *guid,
278 size_t name_size,
Julian Hall4b8dd0a2021-12-02 10:15:54 +0000279 const int16_t *name)
280{
281 struct variable_info *info = NULL;
282 struct variable_entry *entry = add_entry(context, guid, name_size, name);
283
284 if (entry) {
285
286 info = &entry->info;
287 }
288
289 return info;
290}
291
292void variable_index_remove_unused_entry(
293 struct variable_index *context,
294 struct variable_info *info)
295{
296 if (info &&
297 !info->is_constraints_set &&
298 !info->is_variable_set) {
299
300 struct variable_entry *entry = containing_entry(info);
301 entry->in_use = false;
302
303 memset(info, 0, sizeof(struct variable_info));
304 }
305}
306
307void variable_index_set_variable(
308 struct variable_info *info,
Julian Hall0a86f762021-11-08 13:31:23 +0000309 uint32_t attributes)
310{
Julian Hall4b8dd0a2021-12-02 10:15:54 +0000311 struct variable_entry *entry = containing_entry(info);
Julian Hall0a86f762021-11-08 13:31:23 +0000312
Julian Hall4b8dd0a2021-12-02 10:15:54 +0000313 info->metadata.attributes = attributes;
314 info->is_variable_set = true;
Julian Hall0a86f762021-11-08 13:31:23 +0000315
Julian Hall4b8dd0a2021-12-02 10:15:54 +0000316 mark_dirty(entry);
Julian Hall65f7eb42021-11-22 16:06:42 +0100317}
318
Julian Hall4b8dd0a2021-12-02 10:15:54 +0000319void variable_index_clear_variable(
Julian Hall0a86f762021-11-08 13:31:23 +0000320 struct variable_index *context,
Julian Hall4b8dd0a2021-12-02 10:15:54 +0000321 struct variable_info *info)
Julian Hall65f7eb42021-11-22 16:06:42 +0100322{
Julian Hallc0d54dc2021-10-13 15:18:30 +0100323 if (info) {
Julian Hall65f7eb42021-11-22 16:06:42 +0100324
Julian Hallc0d54dc2021-10-13 15:18:30 +0100325 struct variable_entry *entry = containing_entry(info);
Julian Hall65f7eb42021-11-22 16:06:42 +0100326 mark_dirty(entry);
Julian Hall65f7eb42021-11-22 16:06:42 +0100327
Julian Hall0a86f762021-11-08 13:31:23 +0000328 /* Mark variable as no longer set */
329 entry->info.is_variable_set = false;
Julian Hall65f7eb42021-11-22 16:06:42 +0100330 }
331}
332
Julian Hall4b8dd0a2021-12-02 10:15:54 +0000333void variable_index_set_constraints(
334 struct variable_info *info,
Julian Hall0a86f762021-11-08 13:31:23 +0000335 const struct variable_constraints *constraints)
336{
337 if (info) {
338
Julian Hall4b8dd0a2021-12-02 10:15:54 +0000339 info->check_constraints = *constraints;
340 info->is_constraints_set = true;
Julian Hall0a86f762021-11-08 13:31:23 +0000341 }
342}
343
Julian Hall65f7eb42021-11-22 16:06:42 +0100344bool variable_index_dump(
345 struct variable_index *context,
346 size_t buffer_size,
347 uint8_t *buffer,
348 size_t *data_len)
349{
350 bool any_dirty = false;
351 uint8_t *dump_pos = buffer;
352 size_t bytes_dumped = 0;
353
354 for (int pos = 0; pos < context->max_variables; pos++) {
355
356 struct variable_entry *entry = &context->entries[pos];
Julian Hall0a86f762021-11-08 13:31:23 +0000357 struct variable_metadata *metadata = &entry->info.metadata;
Julian Hall65f7eb42021-11-22 16:06:42 +0100358
359 if (entry->in_use &&
Julian Hall0a86f762021-11-08 13:31:23 +0000360 entry->info.is_variable_set &&
361 (metadata->attributes & EFI_VARIABLE_NON_VOLATILE) &&
362 ((bytes_dumped + sizeof(struct variable_metadata)) <= buffer_size)) {
Julian Hall65f7eb42021-11-22 16:06:42 +0100363
Julian Hall0a86f762021-11-08 13:31:23 +0000364 memcpy(dump_pos, metadata, sizeof(struct variable_metadata));
365 bytes_dumped += sizeof(struct variable_metadata);
366 dump_pos += sizeof(struct variable_metadata);
Julian Hall65f7eb42021-11-22 16:06:42 +0100367 }
368
369 any_dirty |= entry->dirty;
370 entry->dirty = false;
371 }
372
373 *data_len = bytes_dumped;
374
375 return any_dirty;
376}
377
378size_t variable_index_restore(
379 const struct variable_index *context,
380 size_t data_len,
381 const uint8_t *buffer)
382{
383 size_t bytes_loaded = 0;
384 const uint8_t *load_pos = buffer;
385 int pos = 0;
386
387 while (bytes_loaded < data_len) {
388
Julian Hall0a86f762021-11-08 13:31:23 +0000389 if ((data_len - bytes_loaded) >= sizeof(struct variable_metadata)) {
Julian Hall65f7eb42021-11-22 16:06:42 +0100390
391 struct variable_entry *entry = &context->entries[pos];
Julian Hall0a86f762021-11-08 13:31:23 +0000392 struct variable_metadata *metadata = &entry->info.metadata;
Julian Hall65f7eb42021-11-22 16:06:42 +0100393
Julian Hall0a86f762021-11-08 13:31:23 +0000394 memcpy(metadata, load_pos, sizeof(struct variable_metadata));
Julian Hall65f7eb42021-11-22 16:06:42 +0100395
Julian Hall0a86f762021-11-08 13:31:23 +0000396 entry->info.is_variable_set = true;
Julian Hall65f7eb42021-11-22 16:06:42 +0100397 entry->in_use = true;
398
Julian Hall0a86f762021-11-08 13:31:23 +0000399 bytes_loaded += sizeof(struct variable_metadata);
400 load_pos += sizeof(struct variable_metadata);
Julian Hall65f7eb42021-11-22 16:06:42 +0100401
402 ++pos;
403 }
404 else {
405
Julian Hall0a86f762021-11-08 13:31:23 +0000406 /* Not a whole number of variable_metadata structs! */
Julian Hall65f7eb42021-11-22 16:06:42 +0100407 break;
408 }
409 }
410
411 return bytes_loaded;
412}