1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * File interface for UEFI variables
4  *
5  * Copyright (c) 2020, Heinrich Schuchardt
6  */
7 
8 #define LOG_CATEGORY LOGC_EFI
9 
10 #include <common.h>
11 #include <charset.h>
12 #include <fs.h>
13 #include <log.h>
14 #include <malloc.h>
15 #include <mapmem.h>
16 #include <efi_loader.h>
17 #include <efi_variable.h>
18 #include <u-boot/crc.h>
19 
20 #define PART_STR_LEN 10
21 
22 /**
23  * efi_set_blk_dev_to_system_partition() - select EFI system partition
24  *
25  * Set the EFI system partition as current block device.
26  *
27  * Return:	status code
28  */
efi_set_blk_dev_to_system_partition(void)29 static efi_status_t __maybe_unused efi_set_blk_dev_to_system_partition(void)
30 {
31 	char part_str[PART_STR_LEN];
32 	int r;
33 
34 	if (!efi_system_partition.if_type) {
35 		log_err("No EFI system partition\n");
36 		return EFI_DEVICE_ERROR;
37 	}
38 	snprintf(part_str, PART_STR_LEN, "%u:%u",
39 		 efi_system_partition.devnum, efi_system_partition.part);
40 	r = fs_set_blk_dev(blk_get_if_type_name(efi_system_partition.if_type),
41 			   part_str, FS_TYPE_ANY);
42 	if (r) {
43 		log_err("Cannot read EFI system partition\n");
44 		return EFI_DEVICE_ERROR;
45 	}
46 	return EFI_SUCCESS;
47 }
48 
efi_var_collect(struct efi_var_file ** bufp,loff_t * lenp,u32 check_attr_mask)49 efi_status_t __maybe_unused efi_var_collect(struct efi_var_file **bufp, loff_t *lenp,
50 					    u32 check_attr_mask)
51 {
52 	size_t len = EFI_VAR_BUF_SIZE;
53 	struct efi_var_file *buf;
54 	struct efi_var_entry *var, *old_var;
55 	size_t old_var_name_length = 2;
56 
57 	*bufp = NULL; /* Avoid double free() */
58 	buf = calloc(1, len);
59 	if (!buf)
60 		return EFI_OUT_OF_RESOURCES;
61 	var = buf->var;
62 	old_var = var;
63 	for (;;) {
64 		efi_uintn_t data_length, var_name_length;
65 		u8 *data;
66 		efi_status_t ret;
67 
68 		if ((uintptr_t)buf + len <=
69 		    (uintptr_t)var->name + old_var_name_length)
70 			return EFI_BUFFER_TOO_SMALL;
71 
72 		var_name_length = (uintptr_t)buf + len - (uintptr_t)var->name;
73 		memcpy(var->name, old_var->name, old_var_name_length);
74 		guidcpy(&var->guid, &old_var->guid);
75 		ret = efi_get_next_variable_name_int(
76 				&var_name_length, var->name, &var->guid);
77 		if (ret == EFI_NOT_FOUND)
78 			break;
79 		if (ret != EFI_SUCCESS) {
80 			free(buf);
81 			return ret;
82 		}
83 		old_var_name_length = var_name_length;
84 		old_var = var;
85 
86 		data = (u8 *)var->name + old_var_name_length;
87 		data_length = (uintptr_t)buf + len - (uintptr_t)data;
88 		ret = efi_get_variable_int(var->name, &var->guid,
89 					   &var->attr, &data_length, data,
90 					   &var->time);
91 		if (ret != EFI_SUCCESS) {
92 			free(buf);
93 			return ret;
94 		}
95 		if ((var->attr & check_attr_mask) == check_attr_mask) {
96 			var->length = data_length;
97 			var = (struct efi_var_entry *)ALIGN((uintptr_t)data + data_length, 8);
98 		}
99 	}
100 
101 	buf->reserved = 0;
102 	buf->magic = EFI_VAR_FILE_MAGIC;
103 	len = (uintptr_t)var - (uintptr_t)buf;
104 	buf->crc32 = crc32(0, (u8 *)buf->var,
105 			   len - sizeof(struct efi_var_file));
106 	buf->length = len;
107 	*bufp = buf;
108 	*lenp = len;
109 
110 	return EFI_SUCCESS;
111 }
112 
113 /**
114  * efi_var_to_file() - save non-volatile variables as file
115  *
116  * File ubootefi.var is created on the EFI system partion.
117  *
118  * Return:	status code
119  */
efi_var_to_file(void)120 efi_status_t efi_var_to_file(void)
121 {
122 #ifdef CONFIG_EFI_VARIABLE_FILE_STORE
123 	efi_status_t ret;
124 	struct efi_var_file *buf;
125 	loff_t len;
126 	loff_t actlen;
127 	int r;
128 
129 	ret = efi_var_collect(&buf, &len, EFI_VARIABLE_NON_VOLATILE);
130 	if (ret != EFI_SUCCESS)
131 		goto error;
132 
133 	ret = efi_set_blk_dev_to_system_partition();
134 	if (ret != EFI_SUCCESS)
135 		goto error;
136 
137 	r = fs_write(EFI_VAR_FILE_NAME, map_to_sysmem(buf), 0, len, &actlen);
138 	if (r || len != actlen)
139 		ret = EFI_DEVICE_ERROR;
140 
141 error:
142 	if (ret != EFI_SUCCESS)
143 		log_err("Failed to persist EFI variables\n");
144 	free(buf);
145 	return ret;
146 #else
147 	return EFI_SUCCESS;
148 #endif
149 }
150 
efi_var_restore(struct efi_var_file * buf)151 efi_status_t efi_var_restore(struct efi_var_file *buf)
152 {
153 	struct efi_var_entry *var, *last_var;
154 	efi_status_t ret;
155 
156 	if (buf->reserved || buf->magic != EFI_VAR_FILE_MAGIC ||
157 	    buf->crc32 != crc32(0, (u8 *)buf->var,
158 				buf->length - sizeof(struct efi_var_file))) {
159 		log_err("Invalid EFI variables file\n");
160 		return EFI_INVALID_PARAMETER;
161 	}
162 
163 	var = buf->var;
164 	last_var = (struct efi_var_entry *)((u8 *)buf + buf->length);
165 	while (var < last_var) {
166 		u16 *data = var->name + u16_strlen(var->name) + 1;
167 
168 		if (var->attr & EFI_VARIABLE_NON_VOLATILE && var->length) {
169 			ret = efi_var_mem_ins(var->name, &var->guid, var->attr,
170 					      var->length, data, 0, NULL,
171 					      var->time);
172 			if (ret != EFI_SUCCESS)
173 				log_err("Failed to set EFI variable %ls\n",
174 					var->name);
175 		}
176 		var = (struct efi_var_entry *)
177 		      ALIGN((uintptr_t)data + var->length, 8);
178 	}
179 	return EFI_SUCCESS;
180 }
181 
182 /**
183  * efi_var_from_file() - read variables from file
184  *
185  * File ubootefi.var is read from the EFI system partitions and the variables
186  * stored in the file are created.
187  *
188  * In case the file does not exist yet or a variable cannot be set EFI_SUCCESS
189  * is returned.
190  *
191  * Return:	status code
192  */
efi_var_from_file(void)193 efi_status_t efi_var_from_file(void)
194 {
195 #ifdef CONFIG_EFI_VARIABLE_FILE_STORE
196 	struct efi_var_file *buf;
197 	loff_t len;
198 	efi_status_t ret;
199 	int r;
200 
201 	buf = calloc(1, EFI_VAR_BUF_SIZE);
202 	if (!buf) {
203 		log_err("Out of memory\n");
204 		return EFI_OUT_OF_RESOURCES;
205 	}
206 
207 	ret = efi_set_blk_dev_to_system_partition();
208 	if (ret != EFI_SUCCESS)
209 		goto error;
210 	r = fs_read(EFI_VAR_FILE_NAME, map_to_sysmem(buf), 0, EFI_VAR_BUF_SIZE,
211 		    &len);
212 	if (r || len < sizeof(struct efi_var_file)) {
213 		log_err("Failed to load EFI variables\n");
214 		goto error;
215 	}
216 	if (buf->length != len || efi_var_restore(buf) != EFI_SUCCESS)
217 		log_err("Invalid EFI variables file\n");
218 error:
219 	free(buf);
220 #endif
221 	return EFI_SUCCESS;
222 }
223