xref: /titanic_53/usr/src/boot/sys/boot/efi/libefi/env.c (revision d5a0772bd7066293674d17391f116c692addc58d)
1 /*
2  * Copyright (c) 2015 Netflix, Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 
28 #include <stand.h>
29 #include <string.h>
30 #include <efi.h>
31 #include <efilib.h>
32 #include <efigpt.h>	/* Partition GUIDS */
33 #include <Guid/MemoryTypeInformation.h>
34 #include <Guid/MtcVendor.h>
35 #include <Guid/ZeroGuid.h>
36 #include <Protocol/EdidActive.h>
37 #include <Protocol/EdidDiscovered.h>
38 #include <uuid.h>
39 #include <stdbool.h>
40 #include <sys/param.h>
41 #include "bootstrap.h"
42 #include "ficl.h"
43 
44 static struct efi_uuid_mapping {
45 	const char *efi_guid_name;
46 	EFI_GUID efi_guid;
47 } efi_uuid_mapping[] = {
48 	{ .efi_guid_name = "global", .efi_guid = EFI_GLOBAL_VARIABLE },
49 	/* EFI Systab entry names. */
50 	{ .efi_guid_name = "MPS Table", .efi_guid = MPS_TABLE_GUID },
51 	{ .efi_guid_name = "ACPI Table", .efi_guid = ACPI_TABLE_GUID },
52 	{ .efi_guid_name = "ACPI 2.0 Table", .efi_guid = ACPI_20_TABLE_GUID },
53 	{ .efi_guid_name = "SMBIOS Table", .efi_guid = SMBIOS_TABLE_GUID },
54 	{ .efi_guid_name = "SMBIOS3 Table", .efi_guid = SMBIOS3_TABLE_GUID },
55 	{ .efi_guid_name = "DXE Table", .efi_guid = DXE_SERVICES_TABLE_GUID },
56 	{ .efi_guid_name = "HOB List Table", .efi_guid = HOB_LIST_TABLE_GUID },
57 	{ .efi_guid_name = EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME,
58 	    .efi_guid = EFI_MEMORY_TYPE_INFORMATION_GUID },
59 	{ .efi_guid_name = "Debug Image Info Table",
60 	    .efi_guid = DEBUG_IMAGE_INFO_TABLE_GUID },
61 	{ .efi_guid_name = "FDT Table", .efi_guid = FDT_TABLE_GUID },
62 	/*
63 	 * Protocol names for debug purposes.
64 	 * Can be removed along with lsefi command.
65 	 */
66 	{ .efi_guid_name = "device path", .efi_guid = DEVICE_PATH_PROTOCOL },
67 	{ .efi_guid_name = "block io", .efi_guid = BLOCK_IO_PROTOCOL },
68 	{ .efi_guid_name = "disk io", .efi_guid = DISK_IO_PROTOCOL },
69 	{ .efi_guid_name = "disk info", .efi_guid =
70 	    EFI_DISK_INFO_PROTOCOL_GUID },
71 	{ .efi_guid_name = "simple fs",
72 	    .efi_guid = SIMPLE_FILE_SYSTEM_PROTOCOL },
73 	{ .efi_guid_name = "load file", .efi_guid = LOAD_FILE_PROTOCOL },
74 	{ .efi_guid_name = "device io", .efi_guid = DEVICE_IO_PROTOCOL },
75 	{ .efi_guid_name = "unicode collation",
76 	    .efi_guid = UNICODE_COLLATION_PROTOCOL },
77 	{ .efi_guid_name = "unicode collation2",
78 	    .efi_guid = EFI_UNICODE_COLLATION2_PROTOCOL_GUID },
79 	{ .efi_guid_name = "simple network",
80 	    .efi_guid = EFI_SIMPLE_NETWORK_PROTOCOL },
81 	{ .efi_guid_name = "simple text output",
82 	    .efi_guid = SIMPLE_TEXT_OUTPUT_PROTOCOL },
83 	{ .efi_guid_name = "simple text input",
84 	    .efi_guid = SIMPLE_TEXT_INPUT_PROTOCOL },
85 	{ .efi_guid_name = "simple text ex input",
86 	    .efi_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID },
87 	{ .efi_guid_name = "console control",
88 	    .efi_guid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID },
89 	{ .efi_guid_name = "stdin", .efi_guid = EFI_CONSOLE_IN_DEVICE_GUID },
90 	{ .efi_guid_name = "stdout", .efi_guid = EFI_CONSOLE_OUT_DEVICE_GUID },
91 	{ .efi_guid_name = "stderr",
92 	    .efi_guid = EFI_STANDARD_ERROR_DEVICE_GUID },
93 	{ .efi_guid_name = "GOP",
94 	    .efi_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID },
95 	{ .efi_guid_name = "UGA draw", .efi_guid = EFI_UGA_DRAW_PROTOCOL_GUID },
96 	{ .efi_guid_name = "PXE base code",
97 	    .efi_guid = EFI_PXE_BASE_CODE_PROTOCOL },
98 	{ .efi_guid_name = "PXE base code callback",
99 	    .efi_guid = EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL },
100 	{ .efi_guid_name = "serial io", .efi_guid = SERIAL_IO_PROTOCOL },
101 	{ .efi_guid_name = "loaded image", .efi_guid = LOADED_IMAGE_PROTOCOL },
102 	{ .efi_guid_name = "loaded image device path",
103 	    .efi_guid = EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID },
104 	{ .efi_guid_name = "ISA io", .efi_guid = EFI_ISA_IO_PROTOCOL_GUID },
105         { .efi_guid_name = "IDE controller init",
106 	    .efi_guid = EFI_IDE_CONTROLLER_INIT_PROTOCOL_GUID },
107         { .efi_guid_name = "ISA ACPI", .efi_guid = EFI_ISA_ACPI_PROTOCOL_GUID },
108         { .efi_guid_name = "PCI", .efi_guid = EFI_PCI_IO_PROTOCOL_GUID },
109         { .efi_guid_name = "PCI root", .efi_guid = EFI_PCI_ROOT_IO_GUID },
110         { .efi_guid_name = "PCI enumeration",
111 	    .efi_guid = EFI_PCI_ENUMERATION_COMPLETE_GUID },
112         { .efi_guid_name = "Driver diagnostics",
113 	    .efi_guid = EFI_DRIVER_DIAGNOSTICS_PROTOCOL_GUID },
114         { .efi_guid_name = "Driver diagnostics2",
115 	    .efi_guid = EFI_DRIVER_DIAGNOSTICS2_PROTOCOL_GUID },
116         { .efi_guid_name = "simple pointer",
117 	    .efi_guid = EFI_SIMPLE_POINTER_PROTOCOL_GUID },
118         { .efi_guid_name = "absolute pointer",
119 	    .efi_guid = EFI_ABSOLUTE_POINTER_PROTOCOL_GUID },
120         { .efi_guid_name = "VLAN config",
121 	    .efi_guid = EFI_VLAN_CONFIG_PROTOCOL_GUID },
122         { .efi_guid_name = "ARP service binding",
123 	    .efi_guid = EFI_ARP_SERVICE_BINDING_PROTOCOL_GUID },
124         { .efi_guid_name = "ARP", .efi_guid = EFI_ARP_PROTOCOL_GUID },
125         { .efi_guid_name = "IPv4 service binding",
126 	    .efi_guid = EFI_IP4_SERVICE_BINDING_PROTOCOL },
127         { .efi_guid_name = "IPv4", .efi_guid = EFI_IP4_PROTOCOL },
128         { .efi_guid_name = "IPv4 config",
129 	    .efi_guid = EFI_IP4_CONFIG_PROTOCOL_GUID },
130         { .efi_guid_name = "IPv6 service binding",
131 	    .efi_guid = EFI_IP6_SERVICE_BINDING_PROTOCOL },
132         { .efi_guid_name = "IPv6", .efi_guid = EFI_IP6_PROTOCOL },
133         { .efi_guid_name = "IPv6 config",
134 	    .efi_guid = EFI_IP6_CONFIG_PROTOCOL_GUID },
135         { .efi_guid_name = "UDPv4", .efi_guid = EFI_UDP4_PROTOCOL },
136         { .efi_guid_name = "UDPv4 service binding",
137 	    .efi_guid = EFI_UDP4_SERVICE_BINDING_PROTOCOL },
138         { .efi_guid_name = "UDPv6", .efi_guid = EFI_UDP6_PROTOCOL },
139         { .efi_guid_name = "UDPv6 service binding",
140 	    .efi_guid = EFI_UDP6_SERVICE_BINDING_PROTOCOL },
141         { .efi_guid_name = "TCPv4", .efi_guid = EFI_TCP4_PROTOCOL },
142         { .efi_guid_name = "TCPv4 service binding",
143 	    .efi_guid = EFI_TCP4_SERVICE_BINDING_PROTOCOL },
144         { .efi_guid_name = "TCPv6", .efi_guid = EFI_TCP6_PROTOCOL },
145         { .efi_guid_name = "TCPv6 service binding",
146 	    .efi_guid = EFI_TCP6_SERVICE_BINDING_PROTOCOL },
147         { .efi_guid_name = "EFI System partition",
148 	    .efi_guid = EFI_PART_TYPE_EFI_SYSTEM_PART_GUID },
149         { .efi_guid_name = "MBR legacy",
150 	    .efi_guid = EFI_PART_TYPE_LEGACY_MBR_GUID },
151         { .efi_guid_name = "device tree", .efi_guid = EFI_DEVICE_TREE_GUID },
152         { .efi_guid_name = "USB io", .efi_guid = EFI_USB_IO_PROTOCOL_GUID },
153         { .efi_guid_name = "USB2 HC", .efi_guid = EFI_USB2_HC_PROTOCOL_GUID },
154         { .efi_guid_name = "component name",
155 	    .efi_guid = EFI_COMPONENT_NAME_PROTOCOL_GUID },
156         { .efi_guid_name = "component name2",
157 	    .efi_guid = EFI_COMPONENT_NAME2_PROTOCOL_GUID },
158         { .efi_guid_name = "driver binding",
159 	    .efi_guid = EFI_DRIVER_BINDING_PROTOCOL_GUID },
160         { .efi_guid_name = "driver configuration",
161 	    .efi_guid = EFI_DRIVER_CONFIGURATION_PROTOCOL_GUID },
162         { .efi_guid_name = "driver configuration2",
163 	    .efi_guid = EFI_DRIVER_CONFIGURATION2_PROTOCOL_GUID },
164         { .efi_guid_name = "decompress",
165 	    .efi_guid = EFI_DECOMPRESS_PROTOCOL_GUID },
166         { .efi_guid_name = "ebc interpreter",
167 	    .efi_guid = EFI_EBC_INTERPRETER_PROTOCOL_GUID },
168         { .efi_guid_name = "network interface identifier",
169 	    .efi_guid = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL },
170         { .efi_guid_name = "network interface identifier_31",
171 	    .efi_guid = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_31 },
172         { .efi_guid_name = "managed network service binding",
173 	    .efi_guid = EFI_MANAGED_NETWORK_SERVICE_BINDING_PROTOCOL_GUID },
174         { .efi_guid_name = "managed network",
175 	    .efi_guid = EFI_MANAGED_NETWORK_PROTOCOL_GUID },
176         { .efi_guid_name = "form browser",
177 	    .efi_guid = EFI_FORM_BROWSER2_PROTOCOL_GUID },
178         { .efi_guid_name = "HII config routing",
179 	    .efi_guid = EFI_HII_CONFIG_ROUTING_PROTOCOL_GUID },
180         { .efi_guid_name = "HII database",
181 	    .efi_guid = EFI_HII_DATABASE_PROTOCOL_GUID },
182         { .efi_guid_name = "HII string",
183 	    .efi_guid = EFI_HII_STRING_PROTOCOL_GUID },
184         { .efi_guid_name = "HII image",
185 	    .efi_guid = EFI_HII_IMAGE_PROTOCOL_GUID },
186         { .efi_guid_name = "HII font", .efi_guid = EFI_HII_FONT_PROTOCOL_GUID },
187         { .efi_guid_name = "HII config",
188 	    .efi_guid = EFI_HII_CONFIGURATION_ACCESS_PROTOCOL_GUID },
189         { .efi_guid_name = "MTFTP4 service binding",
190 	    .efi_guid = EFI_MTFTP4_SERVICE_BINDING_PROTOCOL_GUID },
191         { .efi_guid_name = "MTFTP4", .efi_guid = EFI_MTFTP4_PROTOCOL_GUID },
192         { .efi_guid_name = "MTFTP6 service binding",
193 	    .efi_guid = EFI_MTFTP6_SERVICE_BINDING_PROTOCOL_GUID },
194         { .efi_guid_name = "MTFTP6", .efi_guid = EFI_MTFTP6_PROTOCOL_GUID },
195         { .efi_guid_name = "DHCP4 service binding",
196 	    .efi_guid = EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID },
197         { .efi_guid_name = "DHCP4", .efi_guid = EFI_DHCP4_PROTOCOL_GUID },
198         { .efi_guid_name = "DHCP6 service binding",
199 	    .efi_guid = EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID },
200         { .efi_guid_name = "DHCP6", .efi_guid = EFI_DHCP6_PROTOCOL_GUID },
201         { .efi_guid_name = "SCSI io", .efi_guid = EFI_SCSI_IO_PROTOCOL_GUID },
202         { .efi_guid_name = "SCSI pass thru",
203 	    .efi_guid = EFI_SCSI_PASS_THRU_PROTOCOL_GUID },
204         { .efi_guid_name = "SCSI pass thru ext",
205 	    .efi_guid = EFI_EXT_SCSI_PASS_THRU_PROTOCOL_GUID },
206         { .efi_guid_name = "Capsule arch",
207 	    .efi_guid = EFI_CAPSULE_ARCH_PROTOCOL_GUID },
208         { .efi_guid_name = "monotonic counter arch",
209 	    .efi_guid = EFI_MONOTONIC_COUNTER_ARCH_PROTOCOL_GUID },
210         { .efi_guid_name = "realtime clock arch",
211 	    .efi_guid = EFI_REALTIME_CLOCK_ARCH_PROTOCOL_GUID },
212         { .efi_guid_name = "variable arch",
213 	    .efi_guid = EFI_VARIABLE_ARCH_PROTOCOL_GUID },
214         { .efi_guid_name = "variable write arch",
215 	    .efi_guid = EFI_VARIABLE_WRITE_ARCH_PROTOCOL_GUID },
216         { .efi_guid_name = "watchdog timer arch",
217 	    .efi_guid = EFI_WATCHDOG_TIMER_ARCH_PROTOCOL_GUID },
218         { .efi_guid_name = "ACPI support",
219 	    .efi_guid = EFI_ACPI_SUPPORT_PROTOCOL_GUID },
220         { .efi_guid_name = "BDS arch", .efi_guid = EFI_BDS_ARCH_PROTOCOL_GUID },
221         { .efi_guid_name = "metronome arch",
222 	    .efi_guid = EFI_METRONOME_ARCH_PROTOCOL_GUID },
223         { .efi_guid_name = "timer arch",
224 	    .efi_guid = EFI_TIMER_ARCH_PROTOCOL_GUID },
225         { .efi_guid_name = "DPC", .efi_guid = EFI_DPC_PROTOCOL_GUID },
226         { .efi_guid_name = "print2", .efi_guid = EFI_PRINT2_PROTOCOL_GUID },
227         { .efi_guid_name = "device path to text",
228 	    .efi_guid = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID },
229         { .efi_guid_name = "reset arch",
230 	    .efi_guid = EFI_RESET_ARCH_PROTOCOL_GUID },
231         { .efi_guid_name = "CPU arch", .efi_guid = EFI_CPU_ARCH_PROTOCOL_GUID },
232         { .efi_guid_name = "CPU IO2", .efi_guid = EFI_CPU_IO2_PROTOCOL_GUID },
233         { .efi_guid_name = "Legacy 8259",
234 	    .efi_guid = EFI_LEGACY_8259_PROTOCOL_GUID },
235         { .efi_guid_name = "Security arch",
236 	    .efi_guid = EFI_SECURITY_ARCH_PROTOCOL_GUID },
237         { .efi_guid_name = "Security2 arch",
238 	    .efi_guid = EFI_SECURITY2_ARCH_PROTOCOL_GUID },
239         { .efi_guid_name = "Runtime arch",
240 	    .efi_guid = EFI_RUNTIME_ARCH_PROTOCOL_GUID },
241         { .efi_guid_name = "status code runtime",
242 	    .efi_guid = EFI_STATUS_CODE_RUNTIME_PROTOCOL_GUID },
243         { .efi_guid_name = "data hub", .efi_guid = EFI_DATA_HUB_PROTOCOL_GUID },
244         { .efi_guid_name = "PCD", .efi_guid = PCD_PROTOCOL_GUID },
245         { .efi_guid_name = "EFI PCD", .efi_guid = EFI_PCD_PROTOCOL_GUID },
246         { .efi_guid_name = "firmware volume block",
247 	    .efi_guid = EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL_GUID },
248         { .efi_guid_name = "firmware volume2",
249 	    .efi_guid = EFI_FIRMWARE_VOLUME2_PROTOCOL_GUID },
250         { .efi_guid_name = "firmware volume dispatch",
251 	    .efi_guid = EFI_FIRMWARE_VOLUME_DISPATCH_PROTOCOL_GUID },
252         { .efi_guid_name = "lzma compress", .efi_guid = LZMA_COMPRESS_GUID },
253         { .efi_guid_name = "MP services",
254 	    .efi_guid = EFI_MP_SERVICES_PROTOCOL_GUID },
255         { .efi_guid_name = MTC_VARIABLE_NAME, .efi_guid = MTC_VENDOR_GUID },
256         { .efi_guid_name = "RTC", .efi_guid = { 0x378D7B65, 0x8DA9, 0x4773,
257 	    { 0xB6, 0xE4, 0xA4, 0x78, 0x26, 0xA8, 0x33, 0xE1} } },
258         { .efi_guid_name = "Active EDID",
259 	    .efi_guid = EFI_EDID_ACTIVE_PROTOCOL_GUID },
260         { .efi_guid_name = "Discovered EDID",
261 	    .efi_guid = EFI_EDID_DISCOVERED_PROTOCOL_GUID }
262 };
263 
264 bool
efi_guid_to_str(const EFI_GUID * guid,char ** sp)265 efi_guid_to_str(const EFI_GUID *guid, char **sp)
266 {
267 	uint32_t status;
268 
269 	uuid_to_string((const uuid_t *)guid, sp, &status);
270 	return (status == uuid_s_ok ? true : false);
271 }
272 
273 bool
efi_str_to_guid(const char * s,EFI_GUID * guid)274 efi_str_to_guid(const char *s, EFI_GUID *guid)
275 {
276 	uint32_t status;
277 
278 	uuid_from_string(s, (uuid_t *)guid, &status);
279 	return (status == uuid_s_ok ? true : false);
280 }
281 
282 bool
efi_name_to_guid(const char * name,EFI_GUID * guid)283 efi_name_to_guid(const char *name, EFI_GUID *guid)
284 {
285 	uint32_t i;
286 
287 	for (i = 0; i < nitems(efi_uuid_mapping); i++) {
288 		if (strcasecmp(name, efi_uuid_mapping[i].efi_guid_name) == 0) {
289 			*guid = efi_uuid_mapping[i].efi_guid;
290 			return (true);
291 		}
292 	}
293 	return (efi_str_to_guid(name, guid));
294 }
295 
296 bool
efi_guid_to_name(EFI_GUID * guid,char ** name)297 efi_guid_to_name(EFI_GUID *guid, char **name)
298 {
299 	uint32_t i;
300 	int rv;
301 
302 	for (i = 0; i < nitems(efi_uuid_mapping); i++) {
303 		rv = uuid_equal((uuid_t *)guid,
304 		    (uuid_t *)&efi_uuid_mapping[i].efi_guid, NULL);
305 		if (rv != 0) {
306 			*name = strdup(efi_uuid_mapping[i].efi_guid_name);
307 			if (*name == NULL)
308 				return (false);
309 			return (true);
310 		}
311 	}
312 	return (efi_guid_to_str(guid, name));
313 }
314 
315 /*
316  * Simple wrappers to the underlying UEFI functions.
317  * See http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES
318  * for details.
319  */
320 EFI_STATUS
efi_get_next_variable_name(UINTN * variable_name_size,CHAR16 * variable_name,EFI_GUID * vendor_guid)321 efi_get_next_variable_name(UINTN *variable_name_size, CHAR16 *variable_name,
322     EFI_GUID *vendor_guid)
323 {
324 	return (RS->GetNextVariableName(variable_name_size, variable_name,
325 	    vendor_guid));
326 }
327 
328 EFI_STATUS
efi_get_variable(CHAR16 * variable_name,EFI_GUID * vendor_guid,UINT32 * attributes,UINTN * data_size,void * data)329 efi_get_variable(CHAR16 *variable_name, EFI_GUID *vendor_guid,
330     UINT32 *attributes, UINTN *data_size, void *data)
331 {
332 	return (RS->GetVariable(variable_name, vendor_guid, attributes,
333 	    data_size, data));
334 }
335 
336 EFI_STATUS
efi_set_variable(CHAR16 * variable_name,EFI_GUID * vendor_guid,UINT32 attributes,UINTN data_size,void * data)337 efi_set_variable(CHAR16 *variable_name, EFI_GUID *vendor_guid,
338     UINT32 attributes, UINTN data_size, void *data)
339 {
340 	return (RS->SetVariable(variable_name, vendor_guid, attributes,
341 	    data_size, data));
342 }
343 
344 void
efi_init_environment(void)345 efi_init_environment(void)
346 {
347 	char var[128];
348 
349 	snprintf(var, sizeof(var), "%d.%02d", ST->Hdr.Revision >> 16,
350 	    ST->Hdr.Revision & 0xffff);
351 	env_setenv("efi-version", EV_VOLATILE, var, env_noset, env_nounset);
352 }
353 
354 COMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_show);
355 
356 static int
efi_print_other_value(uint8_t * data,UINTN datasz)357 efi_print_other_value(uint8_t *data, UINTN datasz)
358 {
359 	UINTN i;
360 	bool is_ascii = true;
361 
362 	printf(" = ");
363 	for (i = 0; i < datasz - 1; i++) {
364 		/*
365 		 * Quick hack to see if this ascii-ish string is printable
366 		 * range plus tab, cr and lf.
367 		 */
368 		if ((data[i] < 32 || data[i] > 126)
369 		    && data[i] != 9 && data[i] != 10 && data[i] != 13) {
370 			is_ascii = false;
371 			break;
372 		}
373 	}
374 	if (data[datasz - 1] != '\0')
375 		is_ascii = false;
376 	if (is_ascii == true) {
377 		printf("%s", data);
378 		if (pager_output("\n"))
379 			return (CMD_WARN);
380 	} else {
381 		if (pager_output("\n"))
382 			return (CMD_WARN);
383 		/*
384 		 * Dump hex bytes grouped by 4.
385 		 */
386 		for (i = 0; i < datasz; i++) {
387 			printf("%02x ", data[i]);
388 			if ((i + 1) % 4 == 0)
389 				printf(" ");
390 			if ((i + 1) % 20 == 0) {
391 				if (pager_output("\n"))
392 					return (CMD_WARN);
393 			}
394 		}
395 		if (pager_output("\n"))
396 			return (CMD_WARN);
397 	}
398 
399 	return (CMD_OK);
400 }
401 
402 /* This appears to be some sort of UEFI shell alias table. */
403 static int
efi_print_shell_str(const CHAR16 * varnamearg,uint8_t * data,UINTN datasz)404 efi_print_shell_str(const CHAR16 *varnamearg, uint8_t *data, UINTN datasz)
405 {
406 	printf(" = %S", (CHAR16 *)data);
407 	if (pager_output("\n"))
408 		return (CMD_WARN);
409 	return (CMD_OK);
410 }
411 
412 const char *
efi_memory_type(EFI_MEMORY_TYPE type)413 efi_memory_type(EFI_MEMORY_TYPE type)
414 {
415 	const char *types[] = {
416 	    "Reserved",
417 	    "LoaderCode",
418 	    "LoaderData",
419 	    "BootServicesCode",
420 	    "BootServicesData",
421 	    "RuntimeServicesCode",
422 	    "RuntimeServicesData",
423 	    "ConventionalMemory",
424 	    "UnusableMemory",
425 	    "ACPIReclaimMemory",
426 	    "ACPIMemoryNVS",
427 	    "MemoryMappedIO",
428 	    "MemoryMappedIOPortSpace",
429 	    "PalCode",
430 	    "PersistentMemory"
431 	};
432 	switch (type) {
433 	case EfiReservedMemoryType:
434 	case EfiLoaderCode:
435 	case EfiLoaderData:
436 	case EfiBootServicesCode:
437 	case EfiBootServicesData:
438 	case EfiRuntimeServicesCode:
439 	case EfiRuntimeServicesData:
440 	case EfiConventionalMemory:
441 	case EfiUnusableMemory:
442 	case EfiACPIReclaimMemory:
443 	case EfiACPIMemoryNVS:
444 	case EfiMemoryMappedIO:
445 	case EfiMemoryMappedIOPortSpace:
446 	case EfiPalCode:
447 	case EfiPersistentMemory:
448 		return (types[type]);
449 	}
450 	return ("Unknown");
451 }
452 
453 /* Print memory type table. */
454 static int
efi_print_mem_type(const CHAR16 * varnamearg,uint8_t * data,UINTN datasz)455 efi_print_mem_type(const CHAR16 *varnamearg, uint8_t *data, UINTN datasz)
456 {
457 	int i, n;
458 	EFI_MEMORY_TYPE_INFORMATION *ti;
459 
460 	ti = (EFI_MEMORY_TYPE_INFORMATION *)data;
461 	if (pager_output(" = \n"))
462 		return (CMD_WARN);
463 
464 	n = datasz / sizeof (EFI_MEMORY_TYPE_INFORMATION);
465 	for (i = 0; i < n && ti[i].NumberOfPages != 0; i++) {
466 		printf("\t%23s pages: %u", efi_memory_type(ti[i].Type),
467 		    ti[i].NumberOfPages);
468 		if (pager_output("\n"))
469 			return (CMD_WARN);
470 	}
471 
472 	return (CMD_OK);
473 }
474 
475 /* Print global variables. */
476 static int
efi_print_global(const CHAR16 * varnamearg,uint8_t * data,UINTN datasz)477 efi_print_global(const CHAR16 *varnamearg, uint8_t *data, UINTN datasz)
478 {
479 	int len;
480 	int rv = -1;
481 	char *var;
482 
483 	for (len = 0; varnamearg[len] != 0; len++)
484 		;
485 
486 	if (len == 0)
487 		return (CMD_OK);
488 	len++;
489 
490 	if ((var = malloc(len)) == NULL)
491 		return (CMD_ERROR);
492 	cpy16to8(varnamearg, var, len);
493 
494 	if (strcmp("AuditMode", var) == 0) {
495 		printf(" = ");
496 		printf("0x%x", *data);	/* 8-bit int */
497 		goto done;
498 	}
499 
500 	if (strcmp("BootOptionSupport", var) == 0) {
501 		printf(" = ");
502 		printf("0x%x", *((uint32_t *)data));	/* UINT32 */
503 		goto done;
504 	}
505 
506 	if (strcmp("BootCurrent", var) == 0 ||
507 	    strcmp("BootNext", var) == 0 ||
508 	    strcmp("Timeout", var) == 0) {
509 		printf(" = ");
510 		printf("%u", *((uint16_t *)data));	/* UINT16 */
511 		goto done;
512 	}
513 
514 	if (strcmp("BootOrder", var) == 0 ||
515 	    strcmp("DriverOrder", var) == 0) {
516 		int i;
517 		UINT16 *u16 = (UINT16 *)data;
518 
519 		printf(" =");
520 		for (i = 0; i < datasz / sizeof (UINT16); i++)
521 			printf(" %u", u16[i]);
522 		goto done;
523 	}
524 	if (strncmp("Boot", var, 4) == 0 ||
525 	    strncmp("Driver", var, 5) == 0 ||
526 	    strncmp("SysPrep", var, 7) == 0 ||
527 	    strncmp("OsRecovery", var, 10) == 0) {
528 		UINT32 attr;
529 		UINT16 filepathlistlen;
530 		CHAR16 *text;
531 		int desclen;
532 		EFI_DEVICE_PATH *dp;
533 
534 		attr = *(uint32_t *)data;
535 		data += sizeof(UINT32);
536 		filepathlistlen = *(uint16_t *)data;
537 		data += sizeof (UINT16);
538 		text = (CHAR16 *)data;
539 
540 		for (desclen = 0; text[desclen] != 0; desclen++)
541 			;
542 		if (desclen != 0) {
543 			/* Add terminating zero and we have CHAR16. */
544 			desclen = (desclen + 1) * 2;
545 		}
546 
547 		printf(" = ");
548 		printf("%S", text);
549 		if (filepathlistlen != 0) {
550 			/* Output pathname from new line. */
551 			if (pager_output("\n")) {
552 				rv = CMD_WARN;
553 				goto done;
554 			}
555 			dp = malloc(filepathlistlen);
556 			if (dp == NULL)
557 				goto done;
558 
559 			memcpy(dp, data + desclen, filepathlistlen);
560 			text = efi_devpath_name(dp);
561 			if (text != NULL) {
562 				printf("\t%S", text);
563 				efi_free_devpath_name(text);
564 			}
565 			free(dp);
566 		}
567 		goto done;
568 	}
569 
570 	if (strcmp("ConIn", var) == 0 ||
571 	    strcmp("ConInDev", var) == 0 ||
572 	    strcmp("ConOut", var) == 0 ||
573 	    strcmp("ConOutDev", var) == 0 ||
574 	    strcmp("ErrOut", var) == 0 ||
575 	    strcmp("ErrOutDev", var) == 0) {
576 		CHAR16 *text;
577 
578 		printf(" = ");
579 		text = efi_devpath_name((EFI_DEVICE_PATH *)data);
580 		if (text != NULL) {
581 			printf("%S", text);
582 			efi_free_devpath_name(text);
583 		}
584 		goto done;
585 	}
586 
587 	if (strcmp("PlatformLang", var) == 0 ||
588 	    strcmp("PlatformLangCodes", var) == 0 ||
589 	    strcmp("LangCodes", var) == 0 ||
590 	    strcmp("Lang", var) == 0) {
591 		printf(" = ");
592 		printf("%s", data);	/* ASCII string */
593 		goto done;
594 	}
595 
596 	/*
597 	 * Feature bitmap from firmware to OS.
598 	 * Older UEFI provides UINT32, newer UINT64.
599 	 */
600 	if (strcmp("OsIndicationsSupported", var) == 0) {
601 		printf(" = ");
602 		if (datasz == 4)
603 			printf("0x%x", *((uint32_t *)data));
604 		else
605 			printf("0x%jx", *((uint64_t *)data));
606 		goto done;
607 	}
608 
609 	/* Fallback for anything else. */
610 	rv = efi_print_other_value(data, datasz);
611 done:
612 	if (rv == -1) {
613 		if (pager_output("\n"))
614 			rv = CMD_WARN;
615 		else
616 			rv = CMD_OK;
617 	}
618 	free(var);
619 	return (rv);
620 }
621 
622 static void
efi_print_var_attr(UINT32 attr)623 efi_print_var_attr(UINT32 attr)
624 {
625 	bool comma = false;
626 
627 	if (attr & EFI_VARIABLE_NON_VOLATILE) {
628 		printf("NV");
629 		comma = true;
630 	}
631 	if (attr & EFI_VARIABLE_BOOTSERVICE_ACCESS) {
632 		if (comma == true)
633 			printf(",");
634 		printf("BS");
635 		comma = true;
636 	}
637 	if (attr & EFI_VARIABLE_RUNTIME_ACCESS) {
638 		if (comma == true)
639 			printf(",");
640 		printf("RS");
641 		comma = true;
642 	}
643 	if (attr & EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
644 		if (comma == true)
645 			printf(",");
646 		printf("HR");
647 		comma = true;
648 	}
649 	if (attr & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) {
650 		if (comma == true)
651 			printf(",");
652 		printf("AT");
653 		comma = true;
654 	}
655 }
656 
657 static int
efi_print_var(CHAR16 * varnamearg,EFI_GUID * matchguid,int lflag)658 efi_print_var(CHAR16 *varnamearg, EFI_GUID *matchguid, int lflag)
659 {
660 	UINTN		datasz;
661 	EFI_STATUS	status;
662 	UINT32		attr;
663 	char		*str, *data;
664 	int		rv = CMD_OK;
665 
666 	str = NULL;
667 	datasz = 0;
668 	status = efi_get_variable(varnamearg, matchguid, &attr, &datasz, NULL);
669 	if (status != EFI_BUFFER_TOO_SMALL) {
670 		printf("Can't get the variable: error %#lx\n",
671 		    EFI_ERROR_CODE(status));
672 		return (CMD_ERROR);
673 	}
674 	data = malloc(datasz);
675 	if (data == NULL) {
676 		printf("Out of memory\n");
677 		return (CMD_ERROR);
678 	}
679 
680 	status = efi_get_variable(varnamearg, matchguid, &attr, &datasz, data);
681 	if (status != EFI_SUCCESS) {
682 		printf("Can't get the variable: error %#lx\n",
683 		    EFI_ERROR_CODE(status));
684 		free(data);
685 		return (CMD_ERROR);
686 	}
687 
688 	if (efi_guid_to_name(matchguid, &str) == false) {
689 		rv = CMD_ERROR;
690 		goto done;
691 	}
692 	printf("%s ", str);
693 	efi_print_var_attr(attr);
694 	printf(" %S", varnamearg);
695 
696 	if (lflag == 0) {
697 		if (strcmp(str, "global") == 0)
698 			rv = efi_print_global(varnamearg, data, datasz);
699 		else if (strcmp(str,
700 		    EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME) == 0)
701 			rv = efi_print_mem_type(varnamearg, data, datasz);
702 		else if (strcmp(str,
703 		    "47c7b227-c42a-11d2-8e57-00a0c969723b") == 0)
704 			rv = efi_print_shell_str(varnamearg, data, datasz);
705 		else if (strcmp(str, MTC_VARIABLE_NAME) == 0) {
706 			printf(" = ");
707 			printf("%u", *((uint32_t *)data));	/* UINT32 */
708 			rv = CMD_OK;
709 			if (pager_output("\n"))
710 				rv = CMD_WARN;
711 		} else
712 			rv = efi_print_other_value(data, datasz);
713 	} else if (pager_output("\n"))
714 		rv =  CMD_WARN;
715 
716 done:
717 	free(str);
718 	free(data);
719 	return (rv);
720 }
721 
722 static int
command_efi_show(int argc,char * argv[])723 command_efi_show(int argc, char *argv[])
724 {
725 	/*
726 	 * efi-show [-a]
727 	 *	print all the env
728 	 * efi-show -g UUID
729 	 *	print all the env vars tagged with UUID
730 	 * efi-show -v var
731 	 *	search all the env vars and print the ones matching var
732 	 * eif-show -g UUID -v var
733 	 * eif-show UUID var
734 	 *	print all the env vars that match UUID and var
735 	 */
736 	/* NB: We assume EFI_GUID is the same as uuid_t */
737 	int		aflag = 0, gflag = 0, lflag = 0, vflag = 0;
738 	int		ch, rv;
739 	unsigned	i;
740 	EFI_STATUS	status;
741 	EFI_GUID	varguid = ZERO_GUID;
742 	EFI_GUID	matchguid = ZERO_GUID;
743 	CHAR16		*varname;
744 	CHAR16		*newnm;
745 	CHAR16		varnamearg[128];
746 	UINTN		varalloc;
747 	UINTN		varsz;
748 
749 	optind = 1;
750 	optreset = 1;
751 	opterr = 1;
752 
753 	while ((ch = getopt(argc, argv, "ag:lv:")) != -1) {
754 		switch (ch) {
755 		case 'a':
756 			aflag = 1;
757 			break;
758 		case 'g':
759 			gflag = 1;
760 			if (efi_name_to_guid(optarg, &matchguid) == false) {
761 				printf("uuid %s could not be parsed\n", optarg);
762 				return (CMD_ERROR);
763 			}
764 			break;
765 		case 'l':
766 			lflag = 1;
767 			break;
768 		case 'v':
769 			vflag = 1;
770 			if (strlen(optarg) >= nitems(varnamearg)) {
771 				printf("Variable %s is longer than %zd "
772 				    "characters\n", optarg, nitems(varnamearg));
773 				return (CMD_ERROR);
774 			}
775 			cpy8to16(optarg, varnamearg, nitems(varnamearg));
776 			break;
777 		default:
778 			return (CMD_ERROR);
779 		}
780 	}
781 
782 	if (argc == 1)		/* default is -a */
783 		aflag = 1;
784 
785 	if (aflag && (gflag || vflag)) {
786 		printf("-a isn't compatible with -g or -v\n");
787 		return (CMD_ERROR);
788 	}
789 
790 	if (aflag && optind < argc) {
791 		printf("-a doesn't take any args\n");
792 		return (CMD_ERROR);
793 	}
794 
795 	argc -= optind;
796 	argv += optind;
797 
798 	pager_open();
799 	if (vflag && gflag) {
800 		rv = efi_print_var(varnamearg, &matchguid, lflag);
801 		if (rv == CMD_WARN)
802 			rv = CMD_OK;
803 		pager_close();
804 		return (rv);
805 	}
806 
807 	if (argc == 2) {
808 		optarg = argv[0];
809 		if (strlen(optarg) >= nitems(varnamearg)) {
810 			printf("Variable %s is longer than %zd characters\n",
811 			    optarg, nitems(varnamearg));
812 			pager_close();
813 			return (CMD_ERROR);
814 		}
815 		for (i = 0; i < strlen(optarg); i++)
816 			varnamearg[i] = optarg[i];
817 		varnamearg[i] = 0;
818 		optarg = argv[1];
819 		if (efi_name_to_guid(optarg, &matchguid) == false) {
820 			printf("uuid %s could not be parsed\n", optarg);
821 			pager_close();
822 			return (CMD_ERROR);
823 		}
824 		rv = efi_print_var(varnamearg, &matchguid, lflag);
825 		if (rv == CMD_WARN)
826 			rv = CMD_OK;
827 		pager_close();
828 		return (rv);
829 	}
830 
831 	if (argc > 0) {
832 		printf("Too many args: %d\n", argc);
833 		pager_close();
834 		return (CMD_ERROR);
835 	}
836 
837 	/*
838 	 * Initiate the search -- note the standard takes pain
839 	 * to specify the initial call must be a poiner to a NULL
840 	 * character.
841 	 */
842 	varalloc = 1024;
843 	varname = malloc(varalloc);
844 	if (varname == NULL) {
845 		printf("Can't allocate memory to get variables\n");
846 		pager_close();
847 		return (CMD_ERROR);
848 	}
849 	varname[0] = 0;
850 	while (1) {
851 		varsz = varalloc;
852 		status = efi_get_next_variable_name(&varsz, varname, &varguid);
853 		if (status == EFI_BUFFER_TOO_SMALL) {
854 			varalloc = varsz;
855 			newnm = realloc(varname, varalloc);
856 			if (newnm == NULL) {
857 				printf("Can't allocate memory to get "
858 				    "variables\n");
859 				rv = CMD_ERROR;
860 				break;
861 			}
862 			varname = newnm;
863 			continue; /* Try again with bigger buffer */
864 		}
865 		if (status == EFI_NOT_FOUND) {
866 			rv = CMD_OK;
867 			break;
868 		}
869 		if (status != EFI_SUCCESS) {
870 			rv = CMD_ERROR;
871 			break;
872 		}
873 
874 		if (aflag) {
875 			rv = efi_print_var(varname, &varguid, lflag);
876 			if (rv != CMD_OK) {
877 				if (rv == CMD_WARN)
878 					rv = CMD_OK;
879 				break;
880 			}
881 			continue;
882 		}
883 		if (vflag) {
884 			if (wcscmp(varnamearg, varname) == 0) {
885 				rv = efi_print_var(varname, &varguid, lflag);
886 				if (rv != CMD_OK) {
887 					if (rv == CMD_WARN)
888 						rv = CMD_OK;
889 					break;
890 				}
891 				continue;
892 			}
893 		}
894 		if (gflag) {
895 			rv = uuid_equal((uuid_t *)&varguid,
896 			    (uuid_t *)&matchguid, NULL);
897 			if (rv != 0) {
898 				rv = efi_print_var(varname, &varguid, lflag);
899 				if (rv != CMD_OK) {
900 					if (rv == CMD_WARN)
901 						rv = CMD_OK;
902 					break;
903 				}
904 				continue;
905 			}
906 		}
907 	}
908 	free(varname);
909 	pager_close();
910 
911 	return (rv);
912 }
913 
914 COMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set);
915 
916 static int
command_efi_set(int argc,char * argv[])917 command_efi_set(int argc, char *argv[])
918 {
919 	char *uuid, *var, *val;
920 	CHAR16 wvar[128];
921 	EFI_GUID guid;
922 	EFI_STATUS err;
923 
924 	if (argc != 4) {
925 		printf("efi-set uuid var new-value\n");
926 		return (CMD_ERROR);
927 	}
928 	uuid = argv[1];
929 	var = argv[2];
930 	val = argv[3];
931 	if (efi_name_to_guid(uuid, &guid) == false) {
932 		printf("Invalid uuid %s\n", uuid);
933 		return (CMD_ERROR);
934 	}
935 	cpy8to16(var, wvar, nitems(wvar));
936 #if 0
937 	err = efi_set_variable(wvar, &guid, EFI_VARIABLE_NON_VOLATILE |
938 	    EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,
939 	    strlen(val) + 1, val);
940 	if (EFI_ERROR(err)) {
941 		printf("Failed to set variable: error %lu\n",
942 		    EFI_ERROR_CODE(err));
943 		return (CMD_ERROR);
944 	}
945 #else
946 	printf("would set %s %s = %s\n", uuid, var, val);
947 #endif
948 	return (CMD_OK);
949 }
950 
951 COMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset);
952 
953 static int
command_efi_unset(int argc,char * argv[])954 command_efi_unset(int argc, char *argv[])
955 {
956 	char *uuid, *var;
957 	CHAR16 wvar[128];
958 	EFI_GUID guid;
959 	EFI_STATUS err;
960 
961 	if (argc != 3) {
962 		printf("efi-unset uuid var\n");
963 		return (CMD_ERROR);
964 	}
965 	uuid = argv[1];
966 	var = argv[2];
967 	if (efi_name_to_guid(uuid, &guid) == false) {
968 		printf("Invalid uuid %s\n", uuid);
969 		return (CMD_ERROR);
970 	}
971 	cpy8to16(var, wvar, nitems(wvar));
972 #if 0
973 	err = efi_set_variable(wvar, &guid, 0, 0, NULL);
974 	if (EFI_ERROR(err)) {
975 		printf("Failed to unset variable: error %lu\n",
976 		    EFI_ERROR_CODE(err));
977 		return (CMD_ERROR);
978 	}
979 #else
980 	printf("would unset %s %s \n", uuid, var);
981 #endif
982 	return (CMD_OK);
983 }
984 
985 /*
986  * Loader interaction words and extras
987  *
988  * 	efi-setenv  ( value n name n guid n attr -- 0 | -1)
989  * 	efi-getenv  ( guid n addr n -- addr' n' | -1 )
990  * 	efi-unsetenv ( name n guid n'' -- )
991  */
992 
993 /*
994  * efi-setenv
995  * 	efi-setenv  ( value n name n guid n attr -- 0 | -1)
996  *
997  * Set environment variables using the SetVariable EFI runtime service.
998  *
999  * Value and guid are passed through in binary form (so guid needs to be
1000  * converted to binary form from its string form). Name is converted from
1001  * ASCII to CHAR16. Since ficl doesn't have support for internationalization,
1002  * there's no native CHAR16 interface provided.
1003  *
1004  * attr is an int in the bitmask of the following attributes for this variable.
1005  *
1006  *	1	Non volatile
1007  *	2	Boot service access
1008  *	4	Run time access
1009  * (corresponding to the same bits in the UEFI spec).
1010  */
1011 static void
ficlEfiSetenv(ficlVm * pVM)1012 ficlEfiSetenv(ficlVm *pVM)
1013 {
1014 	char	*value = NULL, *guid = NULL;
1015 	CHAR16	*name = NULL;
1016 	int	i;
1017 	char	*namep, *valuep, *guidp;
1018 	int	names, values, guids, attr;
1019 	EFI_STATUS status;
1020 	uuid_t	u;
1021 	uint32_t ustatus;
1022 	char	*error = NULL;
1023 	ficlStack *pStack = ficlVmGetDataStack(pVM);
1024 
1025 	FICL_STACK_CHECK(pStack, 6, 0);
1026 
1027 	attr = ficlStackPopInteger(pStack);
1028 	guids = ficlStackPopInteger(pStack);
1029 	guidp = (char*)ficlStackPopPointer(pStack);
1030 	names = ficlStackPopInteger(pStack);
1031 	namep = (char*)ficlStackPopPointer(pStack);
1032 	values = ficlStackPopInteger(pStack);
1033 	valuep = (char*)ficlStackPopPointer(pStack);
1034 
1035 	guid = ficlMalloc(guids);
1036 	if (guid == NULL)
1037 		goto out;
1038 	memcpy(guid, guidp, guids);
1039 	uuid_from_string(guid, &u, &ustatus);
1040 	if (ustatus != uuid_s_ok) {
1041 		switch (ustatus) {
1042 		case uuid_s_bad_version:
1043 			error = "uuid: bad string";
1044 			break;
1045 		case uuid_s_invalid_string_uuid:
1046 			error = "uuid: invalid string";
1047 			break;
1048 		case uuid_s_no_memory:
1049 			error = "Out of memory";
1050 			break;
1051 		default:
1052 			error = "uuid: Unknown error";
1053 			break;
1054 		}
1055 		ficlStackPushInteger(pStack, -1);
1056 		goto out;
1057 	}
1058 
1059 	name = ficlMalloc((names + 1) * sizeof (CHAR16));
1060 	if (name == NULL) {
1061 		error = "Out of memory";
1062 		goto out;
1063 	}
1064 	for (i = 0; i < names; i++)
1065 		name[i] = namep[i];
1066 	name[names] = 0;
1067 
1068 	value = ficlMalloc(values + 1);
1069 	if (value == NULL) {
1070 		error = "Out of memory";
1071 		goto out;
1072 	}
1073 	memcpy(value, valuep, values);
1074 
1075 	status = efi_set_variable(name, (EFI_GUID *)&u, attr, values, value);
1076 	if (status == EFI_SUCCESS) {
1077 		ficlStackPushInteger(pStack, 0);
1078 	} else {
1079 		ficlStackPushInteger(pStack, -1);
1080 		error = "Error: efi_set_variable failed";
1081 	}
1082 
1083 out:
1084 	ficlFree(name);
1085 	ficlFree(value);
1086 	ficlFree(guid);
1087 	if (error != NULL)
1088 		ficlVmThrowError(pVM, error);
1089 }
1090 
1091 static void
ficlEfiGetenv(ficlVm * pVM)1092 ficlEfiGetenv(ficlVm *pVM)
1093 {
1094 	char	*name, *value;
1095 	char	*namep;
1096 	int	names;
1097 
1098 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 2, 2);
1099 
1100 	names = ficlStackPopInteger(ficlVmGetDataStack(pVM));
1101 	namep = (char*)ficlStackPopPointer(ficlVmGetDataStack(pVM));
1102 
1103 	name = ficlMalloc(names+1);
1104 	if (name == NULL)
1105 		ficlVmThrowError(pVM, "Error: out of memory");
1106 	strncpy(name, namep, names);
1107 	name[names] = '\0';
1108 
1109 	value = getenv(name);
1110 	ficlFree(name);
1111 
1112 	if(value != NULL) {
1113 		ficlStackPushPointer(ficlVmGetDataStack(pVM), value);
1114 		ficlStackPushInteger(ficlVmGetDataStack(pVM), strlen(value));
1115 	} else {
1116 		ficlStackPushInteger(ficlVmGetDataStack(pVM), -1);
1117 	}
1118 }
1119 
1120 static void
ficlEfiUnsetenv(ficlVm * pVM)1121 ficlEfiUnsetenv(ficlVm *pVM)
1122 {
1123 	char	*name;
1124 	char	*namep;
1125 	int	names;
1126 
1127 	FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 2, 0);
1128 
1129 	names = ficlStackPopInteger(ficlVmGetDataStack(pVM));
1130 	namep = (char*)ficlStackPopPointer(ficlVmGetDataStack(pVM));
1131 
1132 	name = ficlMalloc(names+1);
1133 	if (name == NULL)
1134 		ficlVmThrowError(pVM, "Error: out of memory");
1135 	strncpy(name, namep, names);
1136 	name[names] = '\0';
1137 
1138 	unsetenv(name);
1139 	ficlFree(name);
1140 }
1141 
1142 /*
1143  * Build platform extensions into the system dictionary
1144  */
1145 static void
ficlEfiCompilePlatform(ficlSystem * pSys)1146 ficlEfiCompilePlatform(ficlSystem *pSys)
1147 {
1148 	ficlDictionary *dp = ficlSystemGetDictionary(pSys);
1149 
1150 	FICL_SYSTEM_ASSERT(pSys, dp);
1151 
1152 	ficlDictionarySetPrimitive(dp, "efi-setenv", ficlEfiSetenv,
1153 	    FICL_WORD_DEFAULT);
1154 	ficlDictionarySetPrimitive(dp, "efi-getenv", ficlEfiGetenv,
1155 	    FICL_WORD_DEFAULT);
1156 	ficlDictionarySetPrimitive(dp, "efi-unsetenv", ficlEfiUnsetenv,
1157 	    FICL_WORD_DEFAULT);
1158 }
1159 
1160 FICL_COMPILE_SET(ficlEfiCompilePlatform);
1161