blob: 2dc486f1a9f27e0c094b3e035d1e01bcd74da45f [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) &&
67 (uid == context->entries[pos].info.uid)) {
68
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{
96 if (entry->info.attributes & EFI_VARIABLE_NON_VOLATILE)
97 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{
132 return sizeof(struct variable_info) * context->max_variables;
133}
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
177 if (context->entries[pos].in_use) {
178
179 result = &context->entries[pos].info;
180 break;
181 }
182
183 ++pos;
184 }
185 }
186 }
187 else {
188
189 /* Find first */
190 int pos = 0;
191
192 while (pos < context->max_variables) {
193
194 if (context->entries[pos].in_use) {
195
196 result = &context->entries[pos].info;
197 break;
198 }
199
200 ++pos;
201 }
202 }
203 }
204
205 return result;
206}
207
208static void set_variable_name(
209 struct variable_info *info,
210 size_t name_size,
211 const int16_t *name)
212{
213 size_t trimmed_size = 0;
214
215 /* Trim the saved name to only include a single null terminator.
216 * Any additional terminators included in the client-set name size
217 * are discarded.
218 */
219 for (size_t i = 0; i < name_size; i++) {
220
221 ++trimmed_size;
222 info->name[i] = name[i];
223
224 if (!name[i]) break;
225 }
226
227 info->name_size = trimmed_size * sizeof(int16_t);
228}
229
230const struct variable_info *variable_index_add(
231 struct variable_index *context,
232 const EFI_GUID *guid,
233 size_t name_size,
234 const int16_t *name,
235 uint32_t attributes)
236{
237 struct variable_info *info = NULL;
238
239 if (name_size <= (VARIABLE_INDEX_MAX_NAME_SIZE * sizeof(int16_t))) {
240
241 int pos = find_free(context);
242
243 if (pos >= 0) {
244
245 struct variable_entry *entry = &context->entries[pos];
246
247 info = &entry->info;
248 info->uid = generate_uid(context, guid, name_size, name);
249 info->guid = *guid;
250 info->attributes = attributes;
251 set_variable_name(info, name_size, name);
252
253 mark_dirty(entry);
254 entry->in_use = true;
255 }
256 }
257
258 return info;
259}
260
261void variable_index_remove(
262 struct variable_index *context,
Julian Hallc0d54dc2021-10-13 15:18:30 +0100263 const struct variable_info *info)
Julian Hall65f7eb42021-11-22 16:06:42 +0100264{
Julian Hallc0d54dc2021-10-13 15:18:30 +0100265 if (info) {
Julian Hall65f7eb42021-11-22 16:06:42 +0100266
Julian Hallc0d54dc2021-10-13 15:18:30 +0100267 struct variable_entry *entry = containing_entry(info);
Julian Hall65f7eb42021-11-22 16:06:42 +0100268 mark_dirty(entry);
269 entry->in_use = false;
270
271 memset(&entry->info, 0, sizeof(struct variable_info));
272 }
273}
274
275void variable_index_update_attributes(
276 struct variable_index *context,
277 const struct variable_info *info,
278 uint32_t attributes)
279{
280 if (info) {
281
282 struct variable_info *modified_info = (struct variable_info*)info;
Julian Hallc0d54dc2021-10-13 15:18:30 +0100283 struct variable_entry *entry = containing_entry(modified_info);
Julian Hall65f7eb42021-11-22 16:06:42 +0100284
285 if (attributes != modified_info->attributes) {
286
287 modified_info->attributes = attributes;
288 mark_dirty(entry);
289 }
290 }
291}
292
293bool variable_index_dump(
294 struct variable_index *context,
295 size_t buffer_size,
296 uint8_t *buffer,
297 size_t *data_len)
298{
299 bool any_dirty = false;
300 uint8_t *dump_pos = buffer;
301 size_t bytes_dumped = 0;
302
303 for (int pos = 0; pos < context->max_variables; pos++) {
304
305 struct variable_entry *entry = &context->entries[pos];
306 struct variable_info *info = &entry->info;
307
308 if (entry->in_use &&
309 (info->attributes & EFI_VARIABLE_NON_VOLATILE) &&
310 ((bytes_dumped + sizeof(struct variable_info)) <= buffer_size)) {
311
312 memcpy(dump_pos, info, sizeof(struct variable_info));
313 bytes_dumped += sizeof(struct variable_info);
314 dump_pos += sizeof(struct variable_info);
315 }
316
317 any_dirty |= entry->dirty;
318 entry->dirty = false;
319 }
320
321 *data_len = bytes_dumped;
322
323 return any_dirty;
324}
325
326size_t variable_index_restore(
327 const struct variable_index *context,
328 size_t data_len,
329 const uint8_t *buffer)
330{
331 size_t bytes_loaded = 0;
332 const uint8_t *load_pos = buffer;
333 int pos = 0;
334
335 while (bytes_loaded < data_len) {
336
337 if ((data_len - bytes_loaded) >= sizeof(struct variable_info)) {
338
339 struct variable_entry *entry = &context->entries[pos];
340 struct variable_info *info = &entry->info;
341
342 memcpy(info, load_pos, sizeof(struct variable_info));
343
344 entry->in_use = true;
345
346 bytes_loaded += sizeof(struct variable_info);
347 load_pos += sizeof(struct variable_info);
348
349 ++pos;
350 }
351 else {
352
353 /* Not a whole number of variable_info structs! */
354 break;
355 }
356 }
357
358 return bytes_loaded;
359}