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