blob: a9484dcbe38ca9fe83bf3253326a24b51d7d0557 [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
100/* Public functions */
101efi_status_t variable_index_init(
102 struct variable_index *context,
103 size_t max_variables)
104{
105 context->max_variables = max_variables;
106 context->entries = (struct variable_entry*)
107 malloc(sizeof(struct variable_entry) * max_variables);
108
109 if (context->entries) {
110 memset(context->entries, 0, sizeof(struct variable_entry) * max_variables);
111 }
112
113 return (context->entries) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
114}
115
116void variable_index_deinit(
117 struct variable_index *context)
118{
119 free(context->entries);
120}
121
122size_t variable_index_max_dump_size(
123 struct variable_index *context)
124{
125 return sizeof(struct variable_info) * context->max_variables;
126}
127
128const struct variable_info *variable_index_find(
129 const struct variable_index *context,
130 const EFI_GUID *guid,
131 size_t name_size,
132 const int16_t *name)
133{
134 const struct variable_info *result = NULL;
135 int pos = find_variable(context, guid, name_size, name);
136
137 if (pos >= 0) {
138
139 result = &context->entries[pos].info;
140 }
141
142 return result;
143}
144
145const struct variable_info *variable_index_find_next(
146 const struct variable_index *context,
147 const EFI_GUID *guid,
148 size_t name_size,
149 const int16_t *name)
150{
151 const struct variable_info *result = NULL;
152
153 if (name_size >= sizeof(int16_t)) {
154
155 /*
156 * Name must be at least one character long to accommodate
157 * the mandatory null terminator.
158 */
159 if (name[0] != 0) {
160
161 /* Find next from current name */
162 int pos = find_variable(context, guid, name_size, name);
163
164 if (pos >= 0) {
165
166 /* Iterate to next used entry */
167 ++pos;
168 while (pos < context->max_variables) {
169
170 if (context->entries[pos].in_use) {
171
172 result = &context->entries[pos].info;
173 break;
174 }
175
176 ++pos;
177 }
178 }
179 }
180 else {
181
182 /* Find first */
183 int pos = 0;
184
185 while (pos < context->max_variables) {
186
187 if (context->entries[pos].in_use) {
188
189 result = &context->entries[pos].info;
190 break;
191 }
192
193 ++pos;
194 }
195 }
196 }
197
198 return result;
199}
200
201static void set_variable_name(
202 struct variable_info *info,
203 size_t name_size,
204 const int16_t *name)
205{
206 size_t trimmed_size = 0;
207
208 /* Trim the saved name to only include a single null terminator.
209 * Any additional terminators included in the client-set name size
210 * are discarded.
211 */
212 for (size_t i = 0; i < name_size; i++) {
213
214 ++trimmed_size;
215 info->name[i] = name[i];
216
217 if (!name[i]) break;
218 }
219
220 info->name_size = trimmed_size * sizeof(int16_t);
221}
222
223const struct variable_info *variable_index_add(
224 struct variable_index *context,
225 const EFI_GUID *guid,
226 size_t name_size,
227 const int16_t *name,
228 uint32_t attributes)
229{
230 struct variable_info *info = NULL;
231
232 if (name_size <= (VARIABLE_INDEX_MAX_NAME_SIZE * sizeof(int16_t))) {
233
234 int pos = find_free(context);
235
236 if (pos >= 0) {
237
238 struct variable_entry *entry = &context->entries[pos];
239
240 info = &entry->info;
241 info->uid = generate_uid(context, guid, name_size, name);
242 info->guid = *guid;
243 info->attributes = attributes;
244 set_variable_name(info, name_size, name);
245
246 mark_dirty(entry);
247 entry->in_use = true;
248 }
249 }
250
251 return info;
252}
253
254void variable_index_remove(
255 struct variable_index *context,
256 const EFI_GUID *guid,
257 size_t name_size,
258 const int16_t *name)
259{
260 int pos = find_variable(context, guid, name_size, name);
261
262 if (pos >= 0) {
263
264 struct variable_entry *entry = &context->entries[pos];
265 mark_dirty(entry);
266 entry->in_use = false;
267
268 memset(&entry->info, 0, sizeof(struct variable_info));
269 }
270}
271
272void variable_index_update_attributes(
273 struct variable_index *context,
274 const struct variable_info *info,
275 uint32_t attributes)
276{
277 if (info) {
278
279 struct variable_info *modified_info = (struct variable_info*)info;
280
281 size_t info_offset = offsetof(struct variable_entry, info);
282 struct variable_entry *entry = (struct variable_entry*)((uint8_t*)modified_info - info_offset);
283
284 if (attributes != modified_info->attributes) {
285
286 modified_info->attributes = attributes;
287 mark_dirty(entry);
288 }
289 }
290}
291
292bool variable_index_dump(
293 struct variable_index *context,
294 size_t buffer_size,
295 uint8_t *buffer,
296 size_t *data_len)
297{
298 bool any_dirty = false;
299 uint8_t *dump_pos = buffer;
300 size_t bytes_dumped = 0;
301
302 for (int pos = 0; pos < context->max_variables; pos++) {
303
304 struct variable_entry *entry = &context->entries[pos];
305 struct variable_info *info = &entry->info;
306
307 if (entry->in_use &&
308 (info->attributes & EFI_VARIABLE_NON_VOLATILE) &&
309 ((bytes_dumped + sizeof(struct variable_info)) <= buffer_size)) {
310
311 memcpy(dump_pos, info, sizeof(struct variable_info));
312 bytes_dumped += sizeof(struct variable_info);
313 dump_pos += sizeof(struct variable_info);
314 }
315
316 any_dirty |= entry->dirty;
317 entry->dirty = false;
318 }
319
320 *data_len = bytes_dumped;
321
322 return any_dirty;
323}
324
325size_t variable_index_restore(
326 const struct variable_index *context,
327 size_t data_len,
328 const uint8_t *buffer)
329{
330 size_t bytes_loaded = 0;
331 const uint8_t *load_pos = buffer;
332 int pos = 0;
333
334 while (bytes_loaded < data_len) {
335
336 if ((data_len - bytes_loaded) >= sizeof(struct variable_info)) {
337
338 struct variable_entry *entry = &context->entries[pos];
339 struct variable_info *info = &entry->info;
340
341 memcpy(info, load_pos, sizeof(struct variable_info));
342
343 entry->in_use = true;
344
345 bytes_loaded += sizeof(struct variable_info);
346 load_pos += sizeof(struct variable_info);
347
348 ++pos;
349 }
350 else {
351
352 /* Not a whole number of variable_info structs! */
353 break;
354 }
355 }
356
357 return bytes_loaded;
358}