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