]>
Commit | Line | Data |
---|---|---|
f042e47e IA |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * EFI variable service via OP-TEE | |
4 | * | |
5 | * Copyright (C) 2019 Linaro Ltd. <[email protected]> | |
6 | * Copyright (C) 2019 Linaro Ltd. <[email protected]> | |
7 | */ | |
8 | ||
9 | #include <common.h> | |
10 | #include <efi.h> | |
11 | #include <efi_api.h> | |
12 | #include <efi_loader.h> | |
f2d2b3a1 | 13 | #include <efi_variable.h> |
f042e47e IA |
14 | #include <tee.h> |
15 | #include <malloc.h> | |
16 | #include <mm_communication.h> | |
17 | ||
e01aed47 IA |
18 | #define OPTEE_PAGE_SIZE BIT(12) |
19 | extern struct efi_var_file __efi_runtime_data *efi_var_buf; | |
f042e47e IA |
20 | static efi_uintn_t max_buffer_size; /* comm + var + func + data */ |
21 | static efi_uintn_t max_payload_size; /* func + data */ | |
22 | ||
23 | struct mm_connection { | |
24 | struct udevice *tee; | |
25 | u32 session; | |
26 | }; | |
27 | ||
28 | /** | |
29 | * get_connection() - Retrieve OP-TEE session for a specific UUID. | |
30 | * | |
31 | * @conn: session buffer to fill | |
32 | * Return: status code | |
33 | */ | |
34 | static int get_connection(struct mm_connection *conn) | |
35 | { | |
36 | static const struct tee_optee_ta_uuid uuid = PTA_STMM_UUID; | |
37 | struct udevice *tee = NULL; | |
38 | struct tee_open_session_arg arg; | |
548fb67e | 39 | int rc = -ENODEV; |
f042e47e IA |
40 | |
41 | tee = tee_find_device(tee, NULL, NULL, NULL); | |
42 | if (!tee) | |
548fb67e | 43 | goto out; |
f042e47e IA |
44 | |
45 | memset(&arg, 0, sizeof(arg)); | |
46 | tee_optee_ta_uuid_to_octets(arg.uuid, &uuid); | |
47 | rc = tee_open_session(tee, &arg, 0, NULL); | |
548fb67e IA |
48 | if (rc) |
49 | goto out; | |
50 | ||
51 | /* Check the internal OP-TEE result */ | |
52 | if (arg.ret != TEE_SUCCESS) { | |
53 | rc = -EIO; | |
54 | goto out; | |
f042e47e IA |
55 | } |
56 | ||
548fb67e IA |
57 | conn->tee = tee; |
58 | conn->session = arg.session; | |
59 | ||
60 | return 0; | |
61 | out: | |
f042e47e IA |
62 | return rc; |
63 | } | |
64 | ||
65 | /** | |
66 | * optee_mm_communicate() - Pass a buffer to StandaloneMM running in OP-TEE | |
67 | * | |
68 | * @comm_buf: locally allocted communcation buffer | |
69 | * @dsize: buffer size | |
70 | * Return: status code | |
71 | */ | |
72 | static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) | |
73 | { | |
74 | ulong buf_size; | |
75 | efi_status_t ret; | |
76 | struct efi_mm_communicate_header *mm_hdr; | |
77 | struct mm_connection conn = { NULL, 0 }; | |
78 | struct tee_invoke_arg arg; | |
79 | struct tee_param param[2]; | |
80 | struct tee_shm *shm = NULL; | |
81 | int rc; | |
82 | ||
83 | if (!comm_buf) | |
84 | return EFI_INVALID_PARAMETER; | |
85 | ||
86 | mm_hdr = (struct efi_mm_communicate_header *)comm_buf; | |
87 | buf_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t); | |
88 | ||
89 | if (dsize != buf_size) | |
90 | return EFI_INVALID_PARAMETER; | |
91 | ||
92 | rc = get_connection(&conn); | |
93 | if (rc) { | |
94 | log_err("Unable to open OP-TEE session (err=%d)\n", rc); | |
95 | return EFI_UNSUPPORTED; | |
96 | } | |
97 | ||
98 | if (tee_shm_register(conn.tee, comm_buf, buf_size, 0, &shm)) { | |
99 | log_err("Unable to register shared memory\n"); | |
548fb67e | 100 | tee_close_session(conn.tee, conn.session); |
f042e47e IA |
101 | return EFI_UNSUPPORTED; |
102 | } | |
103 | ||
104 | memset(&arg, 0, sizeof(arg)); | |
105 | arg.func = PTA_STMM_CMDID_COMMUNICATE; | |
106 | arg.session = conn.session; | |
107 | ||
108 | memset(param, 0, sizeof(param)); | |
109 | param[0].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INOUT; | |
110 | param[0].u.memref.size = buf_size; | |
111 | param[0].u.memref.shm = shm; | |
112 | param[1].attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT; | |
113 | ||
114 | rc = tee_invoke_func(conn.tee, &arg, 2, param); | |
f042e47e IA |
115 | tee_shm_free(shm); |
116 | tee_close_session(conn.tee, conn.session); | |
9b87d442 IA |
117 | if (rc || arg.ret != TEE_SUCCESS) |
118 | return EFI_DEVICE_ERROR; | |
f042e47e IA |
119 | |
120 | switch (param[1].u.value.a) { | |
5d1f79ba | 121 | case ARM_SVC_SPM_RET_SUCCESS: |
f042e47e IA |
122 | ret = EFI_SUCCESS; |
123 | break; | |
124 | ||
5d1f79ba | 125 | case ARM_SVC_SPM_RET_INVALID_PARAMS: |
f042e47e IA |
126 | ret = EFI_INVALID_PARAMETER; |
127 | break; | |
128 | ||
5d1f79ba | 129 | case ARM_SVC_SPM_RET_DENIED: |
f042e47e IA |
130 | ret = EFI_ACCESS_DENIED; |
131 | break; | |
132 | ||
5d1f79ba | 133 | case ARM_SVC_SPM_RET_NO_MEMORY: |
f042e47e IA |
134 | ret = EFI_OUT_OF_RESOURCES; |
135 | break; | |
136 | ||
137 | default: | |
138 | ret = EFI_ACCESS_DENIED; | |
139 | } | |
140 | ||
141 | return ret; | |
142 | } | |
143 | ||
144 | /** | |
145 | * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send | |
146 | * it to OP-TEE | |
147 | * | |
148 | * @comm_buf: locally allocted communcation buffer | |
149 | * @dsize: buffer size | |
150 | * Return: status code | |
151 | */ | |
152 | static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) | |
153 | { | |
154 | efi_status_t ret; | |
155 | struct efi_mm_communicate_header *mm_hdr; | |
156 | struct smm_variable_communicate_header *var_hdr; | |
157 | ||
158 | dsize += MM_COMMUNICATE_HEADER_SIZE + MM_VARIABLE_COMMUNICATE_SIZE; | |
159 | mm_hdr = (struct efi_mm_communicate_header *)comm_buf; | |
160 | var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data; | |
161 | ||
162 | ret = optee_mm_communicate(comm_buf, dsize); | |
163 | if (ret != EFI_SUCCESS) { | |
164 | log_err("%s failed!\n", __func__); | |
165 | return ret; | |
166 | } | |
167 | ||
168 | return var_hdr->ret_status; | |
169 | } | |
170 | ||
171 | /** | |
172 | * setup_mm_hdr() - Allocate a buffer for StandAloneMM and initialize the | |
173 | * header data. | |
174 | * | |
175 | * @dptr: pointer address of the corresponding StandAloneMM | |
176 | * function | |
177 | * @payload_size: buffer size | |
178 | * @func: standAloneMM function number | |
179 | * @ret: EFI return code | |
180 | * Return: buffer or NULL | |
181 | */ | |
182 | static u8 *setup_mm_hdr(void **dptr, efi_uintn_t payload_size, | |
183 | efi_uintn_t func, efi_status_t *ret) | |
184 | { | |
185 | const efi_guid_t mm_var_guid = EFI_MM_VARIABLE_GUID; | |
186 | struct efi_mm_communicate_header *mm_hdr; | |
187 | struct smm_variable_communicate_header *var_hdr; | |
188 | u8 *comm_buf; | |
189 | ||
190 | /* In the init function we initialize max_buffer_size with | |
191 | * get_max_payload(). So skip the test if max_buffer_size is initialized | |
192 | * StandAloneMM will perform similar checks and drop the buffer if it's | |
193 | * too long | |
194 | */ | |
195 | if (max_buffer_size && max_buffer_size < | |
196 | (MM_COMMUNICATE_HEADER_SIZE + | |
197 | MM_VARIABLE_COMMUNICATE_SIZE + | |
198 | payload_size)) { | |
199 | *ret = EFI_INVALID_PARAMETER; | |
200 | return NULL; | |
201 | } | |
202 | ||
203 | comm_buf = calloc(1, MM_COMMUNICATE_HEADER_SIZE + | |
204 | MM_VARIABLE_COMMUNICATE_SIZE + | |
205 | payload_size); | |
206 | if (!comm_buf) { | |
207 | *ret = EFI_OUT_OF_RESOURCES; | |
208 | return NULL; | |
209 | } | |
210 | ||
211 | mm_hdr = (struct efi_mm_communicate_header *)comm_buf; | |
212 | guidcpy(&mm_hdr->header_guid, &mm_var_guid); | |
213 | mm_hdr->message_len = MM_VARIABLE_COMMUNICATE_SIZE + payload_size; | |
214 | ||
215 | var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data; | |
216 | var_hdr->function = func; | |
217 | if (dptr) | |
218 | *dptr = var_hdr->data; | |
219 | *ret = EFI_SUCCESS; | |
220 | ||
221 | return comm_buf; | |
222 | } | |
223 | ||
224 | /** | |
225 | * get_max_payload() - Get variable payload size from StandAloneMM. | |
226 | * | |
227 | * @size: size of the variable in storage | |
228 | * Return: status code | |
229 | */ | |
230 | efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) | |
231 | { | |
232 | struct smm_variable_payload_size *var_payload = NULL; | |
233 | efi_uintn_t payload_size; | |
234 | u8 *comm_buf = NULL; | |
235 | efi_status_t ret; | |
236 | ||
237 | if (!size) { | |
238 | ret = EFI_INVALID_PARAMETER; | |
239 | goto out; | |
240 | } | |
241 | ||
242 | payload_size = sizeof(*var_payload); | |
243 | comm_buf = setup_mm_hdr((void **)&var_payload, payload_size, | |
244 | SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE, &ret); | |
245 | if (!comm_buf) | |
246 | goto out; | |
247 | ||
248 | ret = mm_communicate(comm_buf, payload_size); | |
249 | if (ret != EFI_SUCCESS) | |
250 | goto out; | |
251 | ||
e01aed47 IA |
252 | /* Make sure the buffer is big enough for storing variables */ |
253 | if (var_payload->size < MM_VARIABLE_ACCESS_HEADER_SIZE + 0x20) { | |
254 | ret = EFI_DEVICE_ERROR; | |
255 | goto out; | |
256 | } | |
f042e47e | 257 | *size = var_payload->size; |
e01aed47 IA |
258 | /* |
259 | * Although the max payload is configurable on StMM, we only share a | |
260 | * single page from OP-TEE for the non-secure buffer used to communicate | |
261 | * with StMM. Since OP-TEE will reject to map anything bigger than that, | |
262 | * make sure we are in bounds. | |
263 | */ | |
264 | if (*size > OPTEE_PAGE_SIZE) | |
265 | *size = OPTEE_PAGE_SIZE - MM_COMMUNICATE_HEADER_SIZE - | |
266 | MM_VARIABLE_COMMUNICATE_SIZE; | |
267 | /* | |
268 | * There seems to be a bug in EDK2 miscalculating the boundaries and | |
269 | * size checks, so deduct 2 more bytes to fulfill this requirement. Fix | |
270 | * it up here to ensure backwards compatibility with older versions | |
271 | * (cf. StandaloneMmPkg/Drivers/StandaloneMmCpu/AArch64/EventHandle.c. | |
272 | * sizeof (EFI_MM_COMMUNICATE_HEADER) instead the size minus the | |
273 | * flexible array member). | |
274 | * | |
275 | * size is guaranteed to be > 2 due to checks on the beginning. | |
276 | */ | |
277 | *size -= 2; | |
f042e47e IA |
278 | out: |
279 | free(comm_buf); | |
280 | return ret; | |
281 | } | |
282 | ||
f96744b2 IA |
283 | /* |
284 | * StMM can store internal attributes and properties for variables, i.e enabling | |
285 | * R/O variables | |
286 | */ | |
287 | static efi_status_t set_property_int(u16 *variable_name, efi_uintn_t name_size, | |
288 | const efi_guid_t *vendor, | |
289 | struct var_check_property *var_property) | |
290 | { | |
291 | struct smm_variable_var_check_property *smm_property; | |
292 | efi_uintn_t payload_size; | |
293 | u8 *comm_buf = NULL; | |
294 | efi_status_t ret; | |
295 | ||
296 | payload_size = sizeof(*smm_property) + name_size; | |
297 | if (payload_size > max_payload_size) { | |
298 | ret = EFI_INVALID_PARAMETER; | |
299 | goto out; | |
300 | } | |
301 | comm_buf = setup_mm_hdr((void **)&smm_property, payload_size, | |
302 | SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET, | |
303 | &ret); | |
304 | if (!comm_buf) | |
305 | goto out; | |
306 | ||
307 | guidcpy(&smm_property->guid, vendor); | |
308 | smm_property->name_size = name_size; | |
309 | memcpy(&smm_property->property, var_property, | |
310 | sizeof(smm_property->property)); | |
311 | memcpy(smm_property->name, variable_name, name_size); | |
312 | ||
313 | ret = mm_communicate(comm_buf, payload_size); | |
314 | ||
315 | out: | |
316 | free(comm_buf); | |
317 | return ret; | |
318 | } | |
319 | ||
320 | static efi_status_t get_property_int(u16 *variable_name, efi_uintn_t name_size, | |
321 | const efi_guid_t *vendor, | |
322 | struct var_check_property *var_property) | |
323 | { | |
324 | struct smm_variable_var_check_property *smm_property; | |
325 | efi_uintn_t payload_size; | |
326 | u8 *comm_buf = NULL; | |
327 | efi_status_t ret; | |
328 | ||
329 | memset(var_property, 0, sizeof(*var_property)); | |
330 | payload_size = sizeof(*smm_property) + name_size; | |
331 | if (payload_size > max_payload_size) { | |
332 | ret = EFI_INVALID_PARAMETER; | |
333 | goto out; | |
334 | } | |
335 | comm_buf = setup_mm_hdr((void **)&smm_property, payload_size, | |
336 | SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET, | |
337 | &ret); | |
338 | if (!comm_buf) | |
339 | goto out; | |
340 | ||
341 | guidcpy(&smm_property->guid, vendor); | |
342 | smm_property->name_size = name_size; | |
343 | memcpy(smm_property->name, variable_name, name_size); | |
344 | ||
345 | ret = mm_communicate(comm_buf, payload_size); | |
346 | /* | |
347 | * Currently only R/O property is supported in StMM. | |
348 | * Variables that are not set to R/O will not set the property in StMM | |
349 | * and the call will return EFI_NOT_FOUND. We are setting the | |
350 | * properties to 0x0 so checking against that is enough for the | |
351 | * EFI_NOT_FOUND case. | |
352 | */ | |
353 | if (ret == EFI_NOT_FOUND) | |
354 | ret = EFI_SUCCESS; | |
355 | if (ret != EFI_SUCCESS) | |
356 | goto out; | |
357 | memcpy(var_property, &smm_property->property, sizeof(*var_property)); | |
358 | ||
359 | out: | |
360 | free(comm_buf); | |
361 | return ret; | |
362 | } | |
363 | ||
f2d2b3a1 HS |
364 | efi_status_t efi_get_variable_int(u16 *variable_name, const efi_guid_t *vendor, |
365 | u32 *attributes, efi_uintn_t *data_size, | |
366 | void *data, u64 *timep) | |
f042e47e | 367 | { |
f96744b2 | 368 | struct var_check_property var_property; |
f042e47e IA |
369 | struct smm_variable_access *var_acc; |
370 | efi_uintn_t payload_size; | |
371 | efi_uintn_t name_size; | |
372 | efi_uintn_t tmp_dsize; | |
373 | u8 *comm_buf = NULL; | |
374 | efi_status_t ret; | |
375 | ||
f2d2b3a1 | 376 | if (!variable_name || !vendor || !data_size) { |
f042e47e IA |
377 | ret = EFI_INVALID_PARAMETER; |
378 | goto out; | |
379 | } | |
380 | ||
381 | /* Check payload size */ | |
f2d2b3a1 | 382 | name_size = u16_strsize(variable_name); |
f042e47e IA |
383 | if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) { |
384 | ret = EFI_INVALID_PARAMETER; | |
385 | goto out; | |
386 | } | |
387 | ||
388 | /* Trim output buffer size */ | |
389 | tmp_dsize = *data_size; | |
390 | if (name_size + tmp_dsize > | |
391 | max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) { | |
392 | tmp_dsize = max_payload_size - | |
393 | MM_VARIABLE_ACCESS_HEADER_SIZE - | |
394 | name_size; | |
395 | } | |
396 | ||
397 | /* Get communication buffer and initialize header */ | |
398 | payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + tmp_dsize; | |
399 | comm_buf = setup_mm_hdr((void **)&var_acc, payload_size, | |
400 | SMM_VARIABLE_FUNCTION_GET_VARIABLE, &ret); | |
401 | if (!comm_buf) | |
402 | goto out; | |
403 | ||
404 | /* Fill in contents */ | |
f2d2b3a1 | 405 | guidcpy(&var_acc->guid, vendor); |
f042e47e IA |
406 | var_acc->data_size = tmp_dsize; |
407 | var_acc->name_size = name_size; | |
f2d2b3a1 HS |
408 | var_acc->attr = attributes ? *attributes : 0; |
409 | memcpy(var_acc->name, variable_name, name_size); | |
f042e47e IA |
410 | |
411 | /* Communicate */ | |
412 | ret = mm_communicate(comm_buf, payload_size); | |
413 | if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) { | |
414 | /* Update with reported data size for trimmed case */ | |
415 | *data_size = var_acc->data_size; | |
416 | } | |
417 | if (ret != EFI_SUCCESS) | |
418 | goto out; | |
419 | ||
f96744b2 IA |
420 | ret = get_property_int(variable_name, name_size, vendor, &var_property); |
421 | if (ret != EFI_SUCCESS) | |
422 | goto out; | |
423 | ||
424 | if (attributes) { | |
f2d2b3a1 | 425 | *attributes = var_acc->attr; |
f96744b2 IA |
426 | if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) |
427 | *attributes |= EFI_VARIABLE_READ_ONLY; | |
428 | } | |
429 | ||
f042e47e IA |
430 | if (data) |
431 | memcpy(data, (u8 *)var_acc->name + var_acc->name_size, | |
432 | var_acc->data_size); | |
433 | else | |
434 | ret = EFI_INVALID_PARAMETER; | |
435 | ||
436 | out: | |
437 | free(comm_buf); | |
f2d2b3a1 | 438 | return ret; |
f042e47e IA |
439 | } |
440 | ||
01df8cf3 HS |
441 | efi_status_t efi_get_next_variable_name_int(efi_uintn_t *variable_name_size, |
442 | u16 *variable_name, | |
443 | efi_guid_t *guid) | |
f042e47e IA |
444 | { |
445 | struct smm_variable_getnext *var_getnext; | |
446 | efi_uintn_t payload_size; | |
447 | efi_uintn_t out_name_size; | |
448 | efi_uintn_t in_name_size; | |
f042e47e IA |
449 | u8 *comm_buf = NULL; |
450 | efi_status_t ret; | |
451 | ||
f042e47e IA |
452 | if (!variable_name_size || !variable_name || !guid) { |
453 | ret = EFI_INVALID_PARAMETER; | |
454 | goto out; | |
455 | } | |
456 | ||
457 | out_name_size = *variable_name_size; | |
458 | in_name_size = u16_strsize(variable_name); | |
459 | ||
460 | if (out_name_size < in_name_size) { | |
461 | ret = EFI_INVALID_PARAMETER; | |
462 | goto out; | |
463 | } | |
464 | ||
ecb833a0 | 465 | if (in_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) { |
f042e47e IA |
466 | ret = EFI_INVALID_PARAMETER; |
467 | goto out; | |
468 | } | |
469 | ||
470 | /* Trim output buffer size */ | |
db94dfbd IA |
471 | if (out_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) |
472 | out_name_size = max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE; | |
f042e47e IA |
473 | |
474 | payload_size = MM_VARIABLE_GET_NEXT_HEADER_SIZE + out_name_size; | |
475 | comm_buf = setup_mm_hdr((void **)&var_getnext, payload_size, | |
476 | SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME, | |
477 | &ret); | |
478 | if (!comm_buf) | |
479 | goto out; | |
480 | ||
481 | /* Fill in contents */ | |
482 | guidcpy(&var_getnext->guid, guid); | |
483 | var_getnext->name_size = out_name_size; | |
484 | memcpy(var_getnext->name, variable_name, in_name_size); | |
485 | memset((u8 *)var_getnext->name + in_name_size, 0x0, | |
486 | out_name_size - in_name_size); | |
487 | ||
488 | /* Communicate */ | |
489 | ret = mm_communicate(comm_buf, payload_size); | |
490 | if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) { | |
491 | /* Update with reported data size for trimmed case */ | |
492 | *variable_name_size = var_getnext->name_size; | |
493 | } | |
494 | if (ret != EFI_SUCCESS) | |
495 | goto out; | |
496 | ||
497 | guidcpy(guid, &var_getnext->guid); | |
db94dfbd | 498 | memcpy(variable_name, var_getnext->name, var_getnext->name_size); |
f042e47e IA |
499 | |
500 | out: | |
501 | free(comm_buf); | |
01df8cf3 | 502 | return ret; |
f042e47e IA |
503 | } |
504 | ||
f2d2b3a1 HS |
505 | efi_status_t efi_set_variable_int(u16 *variable_name, const efi_guid_t *vendor, |
506 | u32 attributes, efi_uintn_t data_size, | |
507 | const void *data, bool ro_check) | |
f042e47e | 508 | { |
f96744b2 IA |
509 | efi_status_t ret, alt_ret = EFI_SUCCESS; |
510 | struct var_check_property var_property; | |
f042e47e IA |
511 | struct smm_variable_access *var_acc; |
512 | efi_uintn_t payload_size; | |
513 | efi_uintn_t name_size; | |
514 | u8 *comm_buf = NULL; | |
f96744b2 | 515 | bool ro; |
f042e47e | 516 | |
f2d2b3a1 | 517 | if (!variable_name || variable_name[0] == 0 || !vendor) { |
f042e47e IA |
518 | ret = EFI_INVALID_PARAMETER; |
519 | goto out; | |
520 | } | |
521 | if (data_size > 0 && !data) { | |
522 | ret = EFI_INVALID_PARAMETER; | |
523 | goto out; | |
524 | } | |
f042e47e | 525 | /* Check payload size */ |
f2d2b3a1 | 526 | name_size = u16_strsize(variable_name); |
f042e47e IA |
527 | payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size; |
528 | if (payload_size > max_payload_size) { | |
529 | ret = EFI_INVALID_PARAMETER; | |
530 | goto out; | |
531 | } | |
532 | ||
f96744b2 IA |
533 | /* |
534 | * Allocate the buffer early, before switching to RW (if needed) | |
535 | * so we won't need to account for any failures in reading/setting | |
536 | * the properties, if the allocation fails | |
537 | */ | |
f042e47e IA |
538 | comm_buf = setup_mm_hdr((void **)&var_acc, payload_size, |
539 | SMM_VARIABLE_FUNCTION_SET_VARIABLE, &ret); | |
540 | if (!comm_buf) | |
541 | goto out; | |
542 | ||
f96744b2 IA |
543 | ro = !!(attributes & EFI_VARIABLE_READ_ONLY); |
544 | attributes &= EFI_VARIABLE_MASK; | |
545 | ||
546 | /* | |
547 | * The API has the ability to override RO flags. If no RO check was | |
548 | * requested switch the variable to RW for the duration of this call | |
549 | */ | |
550 | ret = get_property_int(variable_name, name_size, vendor, | |
551 | &var_property); | |
552 | if (ret != EFI_SUCCESS) | |
553 | goto out; | |
554 | ||
555 | if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) { | |
556 | /* Bypass r/o check */ | |
557 | if (!ro_check) { | |
558 | var_property.property &= ~VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY; | |
559 | ret = set_property_int(variable_name, name_size, vendor, &var_property); | |
560 | if (ret != EFI_SUCCESS) | |
561 | goto out; | |
562 | } else { | |
563 | ret = EFI_WRITE_PROTECTED; | |
564 | goto out; | |
565 | } | |
566 | } | |
567 | ||
f042e47e | 568 | /* Fill in contents */ |
f2d2b3a1 | 569 | guidcpy(&var_acc->guid, vendor); |
f042e47e IA |
570 | var_acc->data_size = data_size; |
571 | var_acc->name_size = name_size; | |
f2d2b3a1 HS |
572 | var_acc->attr = attributes; |
573 | memcpy(var_acc->name, variable_name, name_size); | |
f042e47e IA |
574 | memcpy((u8 *)var_acc->name + name_size, data, data_size); |
575 | ||
576 | /* Communicate */ | |
577 | ret = mm_communicate(comm_buf, payload_size); | |
f96744b2 IA |
578 | if (ret != EFI_SUCCESS) |
579 | alt_ret = ret; | |
580 | ||
581 | if (ro && !(var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY)) { | |
582 | var_property.revision = VAR_CHECK_VARIABLE_PROPERTY_REVISION; | |
583 | var_property.property |= VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY; | |
584 | var_property.attributes = attributes; | |
585 | var_property.minsize = 1; | |
586 | var_property.maxsize = var_acc->data_size; | |
587 | ret = set_property_int(variable_name, name_size, vendor, &var_property); | |
588 | } | |
1a7b0f6e HS |
589 | |
590 | if (alt_ret != EFI_SUCCESS) | |
591 | goto out; | |
592 | ||
593 | if (!u16_strcmp(variable_name, L"PK")) | |
594 | alt_ret = efi_init_secure_state(); | |
f042e47e IA |
595 | out: |
596 | free(comm_buf); | |
f96744b2 | 597 | return alt_ret == EFI_SUCCESS ? ret : alt_ret; |
f042e47e IA |
598 | } |
599 | ||
01df8cf3 HS |
600 | efi_status_t efi_query_variable_info_int(u32 attributes, |
601 | u64 *max_variable_storage_size, | |
602 | u64 *remain_variable_storage_size, | |
603 | u64 *max_variable_size) | |
f042e47e IA |
604 | { |
605 | struct smm_variable_query_info *mm_query_info; | |
606 | efi_uintn_t payload_size; | |
607 | efi_status_t ret; | |
608 | u8 *comm_buf; | |
609 | ||
f042e47e IA |
610 | payload_size = sizeof(*mm_query_info); |
611 | comm_buf = setup_mm_hdr((void **)&mm_query_info, payload_size, | |
612 | SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO, | |
613 | &ret); | |
614 | if (!comm_buf) | |
615 | goto out; | |
616 | ||
617 | mm_query_info->attr = attributes; | |
618 | ret = mm_communicate(comm_buf, payload_size); | |
619 | if (ret != EFI_SUCCESS) | |
620 | goto out; | |
621 | *max_variable_storage_size = mm_query_info->max_variable_storage; | |
622 | *remain_variable_storage_size = | |
623 | mm_query_info->remaining_variable_storage; | |
624 | *max_variable_size = mm_query_info->max_variable_size; | |
625 | ||
626 | out: | |
627 | free(comm_buf); | |
01df8cf3 | 628 | return ret; |
f042e47e IA |
629 | } |
630 | ||
f042e47e IA |
631 | /** |
632 | * efi_query_variable_info() - get information about EFI variables | |
633 | * | |
634 | * This function implements the QueryVariableInfo() runtime service. | |
635 | * | |
636 | * See the Unified Extensible Firmware Interface (UEFI) specification for | |
637 | * details. | |
638 | * | |
639 | * @attributes: bitmask to select variables to be | |
640 | * queried | |
641 | * @maximum_variable_storage_size: maximum size of storage area for the | |
642 | * selected variable types | |
643 | * @remaining_variable_storage_size: remaining size of storage are for the | |
644 | * selected variable types | |
645 | * @maximum_variable_size: maximum size of a variable of the | |
646 | * selected type | |
647 | * Return: status code | |
648 | */ | |
649 | efi_status_t EFIAPI __efi_runtime | |
650 | efi_query_variable_info_runtime(u32 attributes, u64 *max_variable_storage_size, | |
651 | u64 *remain_variable_storage_size, | |
652 | u64 *max_variable_size) | |
653 | { | |
654 | return EFI_UNSUPPORTED; | |
655 | } | |
656 | ||
657 | /** | |
658 | * efi_set_variable_runtime() - runtime implementation of SetVariable() | |
659 | * | |
660 | * @variable_name: name of the variable | |
661 | * @guid: vendor GUID | |
662 | * @attributes: attributes of the variable | |
663 | * @data_size: size of the buffer with the variable value | |
664 | * @data: buffer with the variable value | |
665 | * Return: status code | |
666 | */ | |
667 | static efi_status_t __efi_runtime EFIAPI | |
668 | efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *guid, | |
669 | u32 attributes, efi_uintn_t data_size, | |
670 | const void *data) | |
671 | { | |
672 | return EFI_UNSUPPORTED; | |
673 | } | |
674 | ||
675 | /** | |
676 | * efi_variables_boot_exit_notify() - notify ExitBootServices() is called | |
677 | */ | |
678 | void efi_variables_boot_exit_notify(void) | |
679 | { | |
f042e47e | 680 | efi_status_t ret; |
e01aed47 IA |
681 | u8 *comm_buf; |
682 | loff_t len; | |
683 | struct efi_var_file *var_buf; | |
f042e47e IA |
684 | |
685 | comm_buf = setup_mm_hdr(NULL, 0, | |
686 | SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE, &ret); | |
687 | if (comm_buf) | |
688 | ret = mm_communicate(comm_buf, 0); | |
689 | else | |
690 | ret = EFI_NOT_FOUND; | |
691 | ||
692 | if (ret != EFI_SUCCESS) | |
693 | log_err("Unable to notify StMM for ExitBootServices\n"); | |
694 | free(comm_buf); | |
695 | ||
e01aed47 IA |
696 | /* |
697 | * Populate the list for runtime variables. | |
698 | * asking EFI_VARIABLE_RUNTIME_ACCESS is redundant, since | |
699 | * efi_var_mem_notify_exit_boot_services will clean those, but that's fine | |
700 | */ | |
701 | ret = efi_var_collect(&var_buf, &len, EFI_VARIABLE_RUNTIME_ACCESS); | |
702 | if (ret != EFI_SUCCESS) | |
703 | log_err("Can't populate EFI variables. No runtime variables will be available\n"); | |
704 | else | |
53e54bf5 | 705 | efi_var_buf_update(var_buf); |
e01aed47 IA |
706 | free(var_buf); |
707 | ||
f042e47e IA |
708 | /* Update runtime service table */ |
709 | efi_runtime_services.query_variable_info = | |
710 | efi_query_variable_info_runtime; | |
711 | efi_runtime_services.get_variable = efi_get_variable_runtime; | |
712 | efi_runtime_services.get_next_variable_name = | |
713 | efi_get_next_variable_name_runtime; | |
714 | efi_runtime_services.set_variable = efi_set_variable_runtime; | |
715 | efi_update_table_header_crc32(&efi_runtime_services.hdr); | |
716 | } | |
717 | ||
718 | /** | |
719 | * efi_init_variables() - initialize variable services | |
720 | * | |
721 | * Return: status code | |
722 | */ | |
723 | efi_status_t efi_init_variables(void) | |
724 | { | |
725 | efi_status_t ret; | |
726 | ||
e01aed47 IA |
727 | /* Create a cached copy of the variables that will be enabled on ExitBootServices() */ |
728 | ret = efi_var_mem_init(); | |
729 | if (ret != EFI_SUCCESS) | |
730 | return ret; | |
731 | ||
f042e47e IA |
732 | ret = get_max_payload(&max_payload_size); |
733 | if (ret != EFI_SUCCESS) | |
734 | return ret; | |
735 | ||
736 | max_buffer_size = MM_COMMUNICATE_HEADER_SIZE + | |
737 | MM_VARIABLE_COMMUNICATE_SIZE + | |
738 | max_payload_size; | |
739 | ||
1a7b0f6e HS |
740 | ret = efi_init_secure_state(); |
741 | if (ret != EFI_SUCCESS) | |
742 | return ret; | |
743 | ||
f042e47e IA |
744 | return EFI_SUCCESS; |
745 | } |