blob: b171d2d1a8f7a9fc4395c655f1e3acb76dd2082d [file] [log] [blame]
Heinrich Schuchardt09a8d502020-03-19 18:21:58 +00001// 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 */
29static 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
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +030049efi_status_t __maybe_unused efi_var_collect(struct efi_var_file **bufp, loff_t *lenp,
50 u32 check_attr_mask)
Heinrich Schuchardt09a8d502020-03-19 18:21:58 +000051{
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 }
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +030095 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 }
Heinrich Schuchardt09a8d502020-03-19 18:21:58 +000099 }
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 */
120efi_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
Ilias Apalodimasa4d1b1b2020-07-23 15:49:49 +0300129 ret = efi_var_collect(&buf, &len, EFI_VARIABLE_NON_VOLATILE);
Heinrich Schuchardt09a8d502020-03-19 18:21:58 +0000130 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
141error:
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
Heinrich Schuchardt4b7d5c12020-07-14 21:25:28 +0200151efi_status_t efi_var_restore(struct efi_var_file *buf)
Heinrich Schuchardt09a8d502020-03-19 18:21:58 +0000152{
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) {
Heinrich Schuchardt5be59712020-03-24 19:54:53 +0000169 ret = efi_var_mem_ins(var->name, &var->guid, var->attr,
170 var->length, data, 0, NULL,
171 var->time);
Heinrich Schuchardt09a8d502020-03-19 18:21:58 +0000172 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 */
193efi_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");
218error:
219 free(buf);
220#endif
221 return EFI_SUCCESS;
222}