xref: /illumos-gate/usr/src/boot/efi/loader/main.c (revision f334afcf)
19f23ea4bSToomas Soome /*
2199767f8SToomas Soome  * Copyright (c) 2008-2010 Rui Paulo
3199767f8SToomas Soome  * Copyright (c) 2006 Marcel Moolenaar
4199767f8SToomas Soome  * All rights reserved.
5199767f8SToomas Soome  *
6199767f8SToomas Soome  * Redistribution and use in source and binary forms, with or without
7199767f8SToomas Soome  * modification, are permitted provided that the following conditions
8199767f8SToomas Soome  * are met:
9199767f8SToomas Soome  *
10199767f8SToomas Soome  * 1. Redistributions of source code must retain the above copyright
11199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer.
12199767f8SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
13199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
14199767f8SToomas Soome  *    documentation and/or other materials provided with the distribution.
15199767f8SToomas Soome  *
16199767f8SToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17199767f8SToomas Soome  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18199767f8SToomas Soome  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19199767f8SToomas Soome  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20199767f8SToomas Soome  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21199767f8SToomas Soome  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22199767f8SToomas Soome  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23199767f8SToomas Soome  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24199767f8SToomas Soome  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25199767f8SToomas Soome  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26199767f8SToomas Soome  */
27199767f8SToomas Soome 
28199767f8SToomas Soome #include <sys/cdefs.h>
29199767f8SToomas Soome 
30edb35047SToomas Soome #include <sys/disk.h>
31199767f8SToomas Soome #include <sys/param.h>
32199767f8SToomas Soome #include <sys/reboot.h>
33199767f8SToomas Soome #include <sys/boot.h>
349890ff83SToomas Soome #include <sys/consplat.h>
35b713c91eSToomas Soome #include <sys/zfs_bootenv.h>
36199767f8SToomas Soome #include <stand.h>
37eee59048SToomas Soome #include <inttypes.h>
38199767f8SToomas Soome #include <string.h>
39199767f8SToomas Soome #include <setjmp.h>
40dbacaf56SToomas Soome #include <disk.h>
41199767f8SToomas Soome 
42199767f8SToomas Soome #include <efi.h>
43199767f8SToomas Soome #include <efilib.h>
440e8f244eSToomas Soome #include <efichar.h>
45*f334afcfSToomas Soome #include <eficonsctl.h>
46*f334afcfSToomas Soome #include <efidevp.h>
47*f334afcfSToomas Soome #include <Guid/SmBios.h>
48*f334afcfSToomas Soome #include <Protocol/DevicePath.h>
49*f334afcfSToomas Soome #include <Protocol/LoadedImage.h>
50*f334afcfSToomas Soome #include <Protocol/SerialIo.h>
51*f334afcfSToomas Soome #include <Protocol/SimpleTextIn.h>
52*f334afcfSToomas Soome #include <Uefi/UefiGpt.h>
53199767f8SToomas Soome 
54eee59048SToomas Soome #include <uuid.h>
55eee59048SToomas Soome 
56199767f8SToomas Soome #include <bootstrap.h>
579890ff83SToomas Soome #include <gfx_fb.h>
58199767f8SToomas Soome #include <smbios.h>
59199767f8SToomas Soome 
60199767f8SToomas Soome #include <libzfs.h>
619f23ea4bSToomas Soome #include <efizfs.h>
62199767f8SToomas Soome 
63199767f8SToomas Soome #include "loader_efi.h"
64199767f8SToomas Soome 
65199767f8SToomas Soome struct arch_switch archsw;	/* MI/MD interface boundary */
66199767f8SToomas Soome 
67*f334afcfSToomas Soome EFI_GUID gEfiLoadedImageProtocolGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
68*f334afcfSToomas Soome EFI_GUID gEfiSmbiosTableGuid = SMBIOS_TABLE_GUID;
69*f334afcfSToomas Soome EFI_GUID gEfiSmbios3TableGuid = SMBIOS3_TABLE_GUID;
70199767f8SToomas Soome 
71dcba96feSToomas Soome extern void acpi_detect(void);
72f9feecc1SToomas Soome extern void efi_getsmap(void);
739f23ea4bSToomas Soome 
74*f334afcfSToomas Soome static EFI_LOADED_IMAGE_PROTOCOL *img;
759f23ea4bSToomas Soome 
76e3026534SToomas Soome /*
77e3026534SToomas Soome  * Number of seconds to wait for a keystroke before exiting with failure
78e3026534SToomas Soome  * in the event no currdev is found. -2 means always break, -1 means
79e3026534SToomas Soome  * never break, 0 means poll once and then reboot, > 0 means wait for
80e3026534SToomas Soome  * that many seconds. "fail_timeout" can be set in the environment as
81e3026534SToomas Soome  * well.
82e3026534SToomas Soome  */
83e3026534SToomas Soome static int fail_timeout = 5;
84e3026534SToomas Soome 
859f23ea4bSToomas Soome bool
efi_zfs_is_preferred(EFI_HANDLE * h)869f23ea4bSToomas Soome efi_zfs_is_preferred(EFI_HANDLE *h)
879f23ea4bSToomas Soome {
88e3026534SToomas Soome 	EFI_DEVICE_PATH *devpath, *dp, *node;
89e3026534SToomas Soome 	HARDDRIVE_DEVICE_PATH *hd;
90e3026534SToomas Soome 	bool ret;
915ac07b12SToomas Soome 	extern UINT64 start_sector;	/* from mb_header.S */
92e3026534SToomas Soome 
93e3026534SToomas Soome 	/* This check is true for chainloader case. */
94e3026534SToomas Soome 	if (h == img->DeviceHandle)
95e3026534SToomas Soome 		return (true);
96e3026534SToomas Soome 
97e3026534SToomas Soome 	/*
98e3026534SToomas Soome 	 * Make sure the image was loaded from the hard disk.
99e3026534SToomas Soome 	 */
100e3026534SToomas Soome 	devpath = efi_lookup_devpath(img->DeviceHandle);
101e3026534SToomas Soome 	if (devpath == NULL)
102e3026534SToomas Soome 		return (false);
103e3026534SToomas Soome 	node = efi_devpath_last_node(devpath);
104e3026534SToomas Soome 	if (node == NULL)
105e3026534SToomas Soome 		return (false);
10676eff6adSToomas Soome 	if (DevicePathType(node) != MEDIA_DEVICE_PATH ||
10776eff6adSToomas Soome 	    (DevicePathSubType(node) != MEDIA_FILEPATH_DP &&
108e3026534SToomas Soome 	    DevicePathSubType(node) != MEDIA_HARDDRIVE_DP)) {
109e3026534SToomas Soome 		return (false);
110e3026534SToomas Soome 	}
111e3026534SToomas Soome 
112e3026534SToomas Soome 	/*
113e3026534SToomas Soome 	 * XXX We ignore the MEDIA_FILEPATH_DP here for now as it is
114e3026534SToomas Soome 	 * used on arm and we do not support arm.
115e3026534SToomas Soome 	 */
116e3026534SToomas Soome 	ret = false;
117e3026534SToomas Soome 	dp = efi_devpath_trim(devpath);
118e3026534SToomas Soome 	devpath = NULL;
119e3026534SToomas Soome 	if (dp == NULL)
120e3026534SToomas Soome 		goto done;
121e3026534SToomas Soome 
122e3026534SToomas Soome 	devpath = efi_lookup_devpath(h);
123e3026534SToomas Soome 	if (devpath == NULL)
124e3026534SToomas Soome 		goto done;
125e3026534SToomas Soome 	hd = (HARDDRIVE_DEVICE_PATH *)efi_devpath_last_node(devpath);
126e3026534SToomas Soome 	if (hd == NULL) {
127e3026534SToomas Soome 		devpath = NULL;
128e3026534SToomas Soome 		goto done;
129e3026534SToomas Soome 	}
130e3026534SToomas Soome 	devpath = efi_devpath_trim(devpath);
131e3026534SToomas Soome 	if (devpath == NULL)
132e3026534SToomas Soome 		goto done;
133e3026534SToomas Soome 
134e3026534SToomas Soome 	if (!efi_devpath_match(dp, devpath))
135e3026534SToomas Soome 		goto done;
136e3026534SToomas Soome 
137e3026534SToomas Soome 	/* It is the same disk, do we have partition start? */
138e3026534SToomas Soome 	if (start_sector == 0)
139e3026534SToomas Soome 		ret = true;
140e3026534SToomas Soome 	else if (start_sector == hd->PartitionStart)
141e3026534SToomas Soome 		ret = true;
142e3026534SToomas Soome 
143e3026534SToomas Soome done:
144e3026534SToomas Soome 	free(dp);
145e3026534SToomas Soome 	free(devpath);
146e3026534SToomas Soome 	return (ret);
1479f23ea4bSToomas Soome }
148199767f8SToomas Soome 
149afa95bedSToomas Soome static bool
has_keyboard(void)150199767f8SToomas Soome has_keyboard(void)
151199767f8SToomas Soome {
152199767f8SToomas Soome 	EFI_STATUS status;
153199767f8SToomas Soome 	EFI_DEVICE_PATH *path;
15469764de3SToomas Soome 	EFI_HANDLE *hin;
15569764de3SToomas Soome 	uint_t i, nhandles;
156afa95bedSToomas Soome 	bool retval = false;
1579f23ea4bSToomas Soome 
158199767f8SToomas Soome 	/*
159199767f8SToomas Soome 	 * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and
160199767f8SToomas Soome 	 * do the typical dance to get the right sized buffer.
161199767f8SToomas Soome 	 */
162*f334afcfSToomas Soome 	status = efi_get_protocol_handles(&gEfiSimpleTextInProtocolGuid,
163*f334afcfSToomas Soome 	    &nhandles, &hin);
164199767f8SToomas Soome 	if (EFI_ERROR(status))
165afa95bedSToomas Soome 		return (retval);
166199767f8SToomas Soome 
167199767f8SToomas Soome 	/*
168199767f8SToomas Soome 	 * Look at each of the handles. If it supports the device path protocol,
169199767f8SToomas Soome 	 * use it to get the device path for this handle. Then see if that
170199767f8SToomas Soome 	 * device path matches either the USB device path for keyboards or the
171199767f8SToomas Soome 	 * legacy device path for keyboards.
172199767f8SToomas Soome 	 */
17369764de3SToomas Soome 	for (i = 0; i < nhandles; i++) {
174*f334afcfSToomas Soome 		status = OpenProtocolByHandle(hin[i],
175*f334afcfSToomas Soome 		    &gEfiDevicePathProtocolGuid, (void **)&path);
176199767f8SToomas Soome 		if (EFI_ERROR(status))
177199767f8SToomas Soome 			continue;
178199767f8SToomas Soome 
179199767f8SToomas Soome 		while (!IsDevicePathEnd(path)) {
180199767f8SToomas Soome 			/*
181199767f8SToomas Soome 			 * Check for the ACPI keyboard node. All PNP3xx nodes
182199767f8SToomas Soome 			 * are keyboards of different flavors. Note: It is
183199767f8SToomas Soome 			 * unclear of there's always a keyboard node when
184199767f8SToomas Soome 			 * there's a keyboard controller, or if there's only one
185199767f8SToomas Soome 			 * when a keyboard is detected at boot.
186199767f8SToomas Soome 			 */
187199767f8SToomas Soome 			if (DevicePathType(path) == ACPI_DEVICE_PATH &&
188199767f8SToomas Soome 			    (DevicePathSubType(path) == ACPI_DP ||
189083aabddSToomas Soome 			    DevicePathSubType(path) == ACPI_EXTENDED_DP)) {
190199767f8SToomas Soome 				ACPI_HID_DEVICE_PATH  *acpi;
191199767f8SToomas Soome 
192199767f8SToomas Soome 				acpi = (ACPI_HID_DEVICE_PATH *)(void *)path;
193083aabddSToomas Soome 				if ((EISA_ID_TO_NUM(acpi->HID) & 0xff00) ==
194083aabddSToomas Soome 				    0x300 &&
195199767f8SToomas Soome 				    (acpi->HID & 0xffff) == PNP_EISA_ID_CONST) {
196afa95bedSToomas Soome 					retval = true;
197199767f8SToomas Soome 					goto out;
198199767f8SToomas Soome 				}
199199767f8SToomas Soome 			/*
200199767f8SToomas Soome 			 * Check for USB keyboard node, if present. Unlike a
201199767f8SToomas Soome 			 * PS/2 keyboard, these definitely only appear when
202199767f8SToomas Soome 			 * connected to the system.
203199767f8SToomas Soome 			 */
204083aabddSToomas Soome 			} else if (DevicePathType(path) ==
205083aabddSToomas Soome 			    MESSAGING_DEVICE_PATH &&
206199767f8SToomas Soome 			    DevicePathSubType(path) == MSG_USB_CLASS_DP) {
207199767f8SToomas Soome 				USB_CLASS_DEVICE_PATH *usb;
208c142ce19SToomas Soome 
209083aabddSToomas Soome 				/*
210083aabddSToomas Soome 				 * Check for:
211083aabddSToomas Soome 				 * DeviceClass: HID
212083aabddSToomas Soome 				 * DeviceSubClass: Boot devices
213083aabddSToomas Soome 				 * DeviceProtocol: Boot keyboards
214083aabddSToomas Soome 				 */
215199767f8SToomas Soome 				usb = (USB_CLASS_DEVICE_PATH *)(void *)path;
216083aabddSToomas Soome 				if (usb->DeviceClass == 3 &&
217083aabddSToomas Soome 				    usb->DeviceSubClass == 1 &&
218083aabddSToomas Soome 				    usb->DeviceProtocol == 1) {
219afa95bedSToomas Soome 					retval = true;
220199767f8SToomas Soome 					goto out;
221199767f8SToomas Soome 				}
222199767f8SToomas Soome 			}
223199767f8SToomas Soome 			path = NextDevicePathNode(path);
224199767f8SToomas Soome 		}
225199767f8SToomas Soome 	}
226199767f8SToomas Soome out:
227199767f8SToomas Soome 	free(hin);
228afa95bedSToomas Soome 	return (retval);
229199767f8SToomas Soome }
230199767f8SToomas Soome 
231b713c91eSToomas Soome static void
set_currdev(const char * devname)232b713c91eSToomas Soome set_currdev(const char *devname)
233b713c91eSToomas Soome {
234b713c91eSToomas Soome 
235b713c91eSToomas Soome 	/*
236b713c91eSToomas Soome 	 * Don't execute hooks here; we may need to try setting these more than
237b713c91eSToomas Soome 	 * once here if we're probing for the ZFS pool we're supposed to boot.
238b713c91eSToomas Soome 	 * The currdev hook is intended to just validate user input anyways,
239b713c91eSToomas Soome 	 * while the loaddev hook makes it immutable once we've determined what
240b713c91eSToomas Soome 	 * the proper currdev is.
241b713c91eSToomas Soome 	 */
242b713c91eSToomas Soome 	env_setenv("currdev", EV_VOLATILE | EV_NOHOOK, devname, efi_setcurrdev,
243b713c91eSToomas Soome 	    env_nounset);
244b713c91eSToomas Soome 	env_setenv("loaddev", EV_VOLATILE | EV_NOHOOK, devname, env_noset,
245b713c91eSToomas Soome 	    env_nounset);
246b713c91eSToomas Soome }
247b713c91eSToomas Soome 
248dbacaf56SToomas Soome static void
set_currdev_devdesc(struct devdesc * currdev)249e3026534SToomas Soome set_currdev_devdesc(struct devdesc *currdev)
250dbacaf56SToomas Soome {
251dbacaf56SToomas Soome 	char *devname;
252dbacaf56SToomas Soome 
253e3026534SToomas Soome 	devname = efi_fmtdev(currdev);
254e3026534SToomas Soome 
255e3026534SToomas Soome 	printf("Setting currdev to %s\n", devname);
256b713c91eSToomas Soome 	set_currdev(devname);
257dbacaf56SToomas Soome }
258dbacaf56SToomas Soome 
259e3026534SToomas Soome static void
set_currdev_devsw(struct devsw * dev,int unit)260e3026534SToomas Soome set_currdev_devsw(struct devsw *dev, int unit)
261e3026534SToomas Soome {
262e3026534SToomas Soome 	struct devdesc currdev;
263e3026534SToomas Soome 
264e3026534SToomas Soome 	currdev.d_dev = dev;
265e3026534SToomas Soome 	currdev.d_unit = unit;
266e3026534SToomas Soome 
267e3026534SToomas Soome 	set_currdev_devdesc(&currdev);
268e3026534SToomas Soome }
269e3026534SToomas Soome 
270e3026534SToomas Soome static void
set_currdev_pdinfo(pdinfo_t * dp)271e3026534SToomas Soome set_currdev_pdinfo(pdinfo_t *dp)
272e3026534SToomas Soome {
273e3026534SToomas Soome 
274e3026534SToomas Soome 	/*
275e3026534SToomas Soome 	 * Disks are special: they have partitions. if the parent
276e3026534SToomas Soome 	 * pointer is non-null, we're a partition not a full disk
277e3026534SToomas Soome 	 * and we need to adjust currdev appropriately.
278e3026534SToomas Soome 	 */
279e3026534SToomas Soome 	if (dp->pd_devsw->dv_type == DEVT_DISK) {
280e3026534SToomas Soome 		struct disk_devdesc currdev;
281e3026534SToomas Soome 
282e3026534SToomas Soome 		currdev.dd.d_dev = dp->pd_devsw;
283e3026534SToomas Soome 		if (dp->pd_parent == NULL) {
284e3026534SToomas Soome 			currdev.dd.d_unit = dp->pd_unit;
2859a34674dSToomas Soome 			currdev.d_slice = D_SLICENONE;
2869a34674dSToomas Soome 			currdev.d_partition = D_PARTNONE;
287e3026534SToomas Soome 		} else {
288e3026534SToomas Soome 			currdev.dd.d_unit = dp->pd_parent->pd_unit;
289e3026534SToomas Soome 			currdev.d_slice = dp->pd_unit;
2909a34674dSToomas Soome 			currdev.d_partition = D_PARTISGPT; /* Assumes GPT */
291e3026534SToomas Soome 		}
292e3026534SToomas Soome 		set_currdev_devdesc((struct devdesc *)&currdev);
293e3026534SToomas Soome 	} else {
294e3026534SToomas Soome 		set_currdev_devsw(dp->pd_devsw, dp->pd_unit);
295e3026534SToomas Soome 	}
296e3026534SToomas Soome }
297e3026534SToomas Soome 
298e3026534SToomas Soome static bool
sanity_check_currdev(void)299e3026534SToomas Soome sanity_check_currdev(void)
300e3026534SToomas Soome {
301e3026534SToomas Soome 	struct stat st;
302e3026534SToomas Soome 
303e3026534SToomas Soome 	return (stat("/boot/defaults/loader.conf", &st) == 0);
304e3026534SToomas Soome }
305e3026534SToomas Soome 
306e3026534SToomas Soome static bool
probe_zfs_currdev(uint64_t guid)307e3026534SToomas Soome probe_zfs_currdev(uint64_t guid)
308e3026534SToomas Soome {
309e3026534SToomas Soome 	struct zfs_devdesc currdev;
310b713c91eSToomas Soome 	char *bootonce;
311b713c91eSToomas Soome 	bool rv;
312e3026534SToomas Soome 
313e3026534SToomas Soome 	currdev.dd.d_dev = &zfs_dev;
314e3026534SToomas Soome 	currdev.dd.d_unit = 0;
315e3026534SToomas Soome 	currdev.pool_guid = guid;
316e3026534SToomas Soome 	currdev.root_guid = 0;
317e3026534SToomas Soome 	set_currdev_devdesc((struct devdesc *)&currdev);
318e3026534SToomas Soome 
319b713c91eSToomas Soome 	rv = sanity_check_currdev();
320b713c91eSToomas Soome 	if (rv) {
321b713c91eSToomas Soome 		bootonce = malloc(VDEV_PAD_SIZE);
322b713c91eSToomas Soome 		if (bootonce != NULL) {
323b713c91eSToomas Soome 			if (zfs_get_bootonce(&currdev, OS_BOOTONCE, bootonce,
324b713c91eSToomas Soome 			    VDEV_PAD_SIZE) == 0) {
325b713c91eSToomas Soome 				printf("zfs bootonce: %s\n", bootonce);
326b713c91eSToomas Soome 				set_currdev(bootonce);
327b713c91eSToomas Soome 				setenv("zfs-bootonce", bootonce, 1);
328b713c91eSToomas Soome 			}
329b713c91eSToomas Soome 			free(bootonce);
330b713c91eSToomas Soome 			(void) zfs_attach_nvstore(&currdev);
331b713c91eSToomas Soome 		} else {
332b713c91eSToomas Soome 			printf("Failed to process bootonce data: %s\n",
333b713c91eSToomas Soome 			    strerror(errno));
334b713c91eSToomas Soome 		}
335b713c91eSToomas Soome 	}
336b713c91eSToomas Soome 	return (rv);
337e3026534SToomas Soome }
338e3026534SToomas Soome 
339e3026534SToomas Soome static bool
try_as_currdev(pdinfo_t * pp)340e3026534SToomas Soome try_as_currdev(pdinfo_t *pp)
341e3026534SToomas Soome {
342e3026534SToomas Soome 	uint64_t guid;
343e3026534SToomas Soome 
344e3026534SToomas Soome 	/*
345e3026534SToomas Soome 	 * If there's a zpool on this device, try it as a ZFS
346e3026534SToomas Soome 	 * filesystem, which has somewhat different setup than all
347e3026534SToomas Soome 	 * other types of fs due to imperfect loader integration.
348e3026534SToomas Soome 	 * This all stems from ZFS being both a device (zpool) and
349e3026534SToomas Soome 	 * a filesystem, plus the boot env feature.
350e3026534SToomas Soome 	 */
351e3026534SToomas Soome 	if (efizfs_get_guid_by_handle(pp->pd_handle, &guid))
352e3026534SToomas Soome 		return (probe_zfs_currdev(guid));
353e3026534SToomas Soome 
354e3026534SToomas Soome 	/*
355e3026534SToomas Soome 	 * All other filesystems just need the pdinfo
356e3026534SToomas Soome 	 * initialized in the standard way.
357e3026534SToomas Soome 	 */
358e3026534SToomas Soome 	set_currdev_pdinfo(pp);
359e3026534SToomas Soome 	return (sanity_check_currdev());
360e3026534SToomas Soome }
361e3026534SToomas Soome 
362e3026534SToomas Soome static bool
find_currdev(EFI_LOADED_IMAGE_PROTOCOL * img)363*f334afcfSToomas Soome find_currdev(EFI_LOADED_IMAGE_PROTOCOL *img)
364ab8c1d40SToomas Soome {
365dbacaf56SToomas Soome 	pdinfo_t *dp, *pp;
366ab8c1d40SToomas Soome 	EFI_DEVICE_PATH *devpath, *copy;
367ab8c1d40SToomas Soome 	EFI_HANDLE h;
368e3026534SToomas Soome 	CHAR16 *text;
369dbacaf56SToomas Soome 	struct devsw *dev;
370dbacaf56SToomas Soome 	int unit;
371dbacaf56SToomas Soome 	uint64_t extra;
372dbacaf56SToomas Soome 
373e3026534SToomas Soome 	/*
374e3026534SToomas Soome 	 * Did efi_zfs_probe() detect the boot pool? If so, use the zpool
375e3026534SToomas Soome 	 * it found, if it's sane. ZFS is the only thing that looks for
376e3026534SToomas Soome 	 * disks and pools to boot.
377e3026534SToomas Soome 	 */
378dbacaf56SToomas Soome 	if (pool_guid != 0) {
379e3026534SToomas Soome 		printf("Trying ZFS pool\n");
380e3026534SToomas Soome 		if (probe_zfs_currdev(pool_guid))
381e3026534SToomas Soome 			return (true);
382dbacaf56SToomas Soome 	}
383dbacaf56SToomas Soome 
384e3026534SToomas Soome 	/*
385e3026534SToomas Soome 	 * Try to find the block device by its handle based on the
386e3026534SToomas Soome 	 * image we're booting. If we can't find a sane partition,
387e3026534SToomas Soome 	 * search all the other partitions of the disk. We do not
388e3026534SToomas Soome 	 * search other disks because it's a violation of the UEFI
389e3026534SToomas Soome 	 * boot protocol to do so. We fail and let UEFI go on to
390e3026534SToomas Soome 	 * the next candidate.
391e3026534SToomas Soome 	 */
392e3026534SToomas Soome 	dp = efiblk_get_pdinfo_by_handle(img->DeviceHandle);
393e3026534SToomas Soome 	if (dp != NULL) {
394e3026534SToomas Soome 		text = efi_devpath_name(dp->pd_devpath);
395e3026534SToomas Soome 		if (text != NULL) {
396e3026534SToomas Soome 			printf("Trying ESP: %S\n", text);
397e3026534SToomas Soome 			efi_free_devpath_name(text);
398dbacaf56SToomas Soome 		}
399e3026534SToomas Soome 		set_currdev_pdinfo(dp);
400e3026534SToomas Soome 		if (sanity_check_currdev())
401e3026534SToomas Soome 			return (true);
402e3026534SToomas Soome 		if (dp->pd_parent != NULL) {
403e3026534SToomas Soome 			dp = dp->pd_parent;
404e3026534SToomas Soome 			STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
405e3026534SToomas Soome 				text = efi_devpath_name(pp->pd_devpath);
406e3026534SToomas Soome 				if (text != NULL) {
407e3026534SToomas Soome 					printf("And now the part: %S\n", text);
408e3026534SToomas Soome 					efi_free_devpath_name(text);
409e3026534SToomas Soome 				}
410e3026534SToomas Soome 				/*
411e3026534SToomas Soome 				 * Roll up the ZFS special case
412e3026534SToomas Soome 				 * for those partitions that have
413e3026534SToomas Soome 				 * zpools on them
414e3026534SToomas Soome 				 */
415e3026534SToomas Soome 				if (try_as_currdev(pp))
416e3026534SToomas Soome 					return (true);
417dbacaf56SToomas Soome 			}
418dbacaf56SToomas Soome 		}
419e3026534SToomas Soome 	} else {
420e3026534SToomas Soome 		printf("Can't find device by handle\n");
421dbacaf56SToomas Soome 	}
422ab8c1d40SToomas Soome 
423ab8c1d40SToomas Soome 	/*
424ab8c1d40SToomas Soome 	 * Try the device handle from our loaded image first.  If that
425ab8c1d40SToomas Soome 	 * fails, use the device path from the loaded image and see if
426ab8c1d40SToomas Soome 	 * any of the nodes in that path match one of the enumerated
427e3026534SToomas Soome 	 * handles. Currently, this handle list is only for netboot.
428ab8c1d40SToomas Soome 	 */
429dbacaf56SToomas Soome 	if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &extra) == 0) {
430e3026534SToomas Soome 		set_currdev_devsw(dev, unit);
431e3026534SToomas Soome 		if (sanity_check_currdev())
432e3026534SToomas Soome 			return (true);
433dbacaf56SToomas Soome 	}
434ab8c1d40SToomas Soome 
435ab8c1d40SToomas Soome 	copy = NULL;
436ab8c1d40SToomas Soome 	devpath = efi_lookup_image_devpath(IH);
437ab8c1d40SToomas Soome 	while (devpath != NULL) {
438ab8c1d40SToomas Soome 		h = efi_devpath_handle(devpath);
439ab8c1d40SToomas Soome 		if (h == NULL)
440ab8c1d40SToomas Soome 			break;
441ab8c1d40SToomas Soome 
442ab8c1d40SToomas Soome 		free(copy);
443ab8c1d40SToomas Soome 		copy = NULL;
444ab8c1d40SToomas Soome 
445dbacaf56SToomas Soome 		if (efi_handle_lookup(h, &dev, &unit, &extra) == 0) {
446e3026534SToomas Soome 			set_currdev_devsw(dev, unit);
447e3026534SToomas Soome 			if (sanity_check_currdev())
448e3026534SToomas Soome 				return (true);
449dbacaf56SToomas Soome 		}
450ab8c1d40SToomas Soome 
451ab8c1d40SToomas Soome 		devpath = efi_lookup_devpath(h);
452ab8c1d40SToomas Soome 		if (devpath != NULL) {
453ab8c1d40SToomas Soome 			copy = efi_devpath_trim(devpath);
454ab8c1d40SToomas Soome 			devpath = copy;
455ab8c1d40SToomas Soome 		}
456ab8c1d40SToomas Soome 	}
457ab8c1d40SToomas Soome 	free(copy);
458ab8c1d40SToomas Soome 
459e3026534SToomas Soome 	return (false);
460e3026534SToomas Soome }
461e3026534SToomas Soome 
462e3026534SToomas Soome static bool
interactive_interrupt(const char * msg)463e3026534SToomas Soome interactive_interrupt(const char *msg)
464e3026534SToomas Soome {
465e3026534SToomas Soome 	time_t now, then, last;
466e3026534SToomas Soome 
467e3026534SToomas Soome 	last = 0;
468e3026534SToomas Soome 	now = then = getsecs();
469e3026534SToomas Soome 	printf("%s\n", msg);
470e3026534SToomas Soome 	if (fail_timeout == -2)			/* Always break to OK */
471e3026534SToomas Soome 		return (true);
472e3026534SToomas Soome 	if (fail_timeout == -1)			/* Never break to OK */
473e3026534SToomas Soome 		return (false);
474e3026534SToomas Soome 	do {
475e3026534SToomas Soome 		if (last != now) {
476083aabddSToomas Soome 			printf("press any key to interrupt reboot "
477083aabddSToomas Soome 			    "in %d seconds\r",
478083aabddSToomas Soome 			    fail_timeout - (int)(now - then));
479e3026534SToomas Soome 			last = now;
480e3026534SToomas Soome 		}
481e3026534SToomas Soome 
482e3026534SToomas Soome 		/* XXX no pause or timeout wait for char */
483e3026534SToomas Soome 		if (ischar())
484e3026534SToomas Soome 			return (true);
485e3026534SToomas Soome 		now = getsecs();
486e3026534SToomas Soome 	} while (now - then < fail_timeout);
487e3026534SToomas Soome 	return (false);
488ab8c1d40SToomas Soome }
489ab8c1d40SToomas Soome 
490e27085dfSToomas Soome static void
setenv_int(const char * key,int val)491e27085dfSToomas Soome setenv_int(const char *key, int val)
492e27085dfSToomas Soome {
493e27085dfSToomas Soome 	char buf[20];
494e27085dfSToomas Soome 
495e27085dfSToomas Soome 	(void) snprintf(buf, sizeof (buf), "%d", val);
496e27085dfSToomas Soome 	(void) setenv(key, buf, 1);
497e27085dfSToomas Soome }
498e27085dfSToomas Soome 
499e27085dfSToomas Soome /*
500e27085dfSToomas Soome  * Parse ConOut (the list of consoles active) and see if we can find a
501e27085dfSToomas Soome  * serial port and/or a video port. It would be nice to also walk the
502e27085dfSToomas Soome  * ACPI name space to map the UID for the serial port to a port. The
503e27085dfSToomas Soome  * latter is especially hard.
504e27085dfSToomas Soome  */
505e27085dfSToomas Soome static int
parse_uefi_con_out(void)506e27085dfSToomas Soome parse_uefi_con_out(void)
507e27085dfSToomas Soome {
508e27085dfSToomas Soome 	int how, rv;
509e27085dfSToomas Soome 	int vid_seen = 0, com_seen = 0, seen = 0;
510e27085dfSToomas Soome 	size_t sz;
511e27085dfSToomas Soome 	char buf[4096], *ep;
512e27085dfSToomas Soome 	EFI_DEVICE_PATH *node;
513e27085dfSToomas Soome 	ACPI_HID_DEVICE_PATH *acpi;
514e27085dfSToomas Soome 	UART_DEVICE_PATH *uart;
515e27085dfSToomas Soome 	bool pci_pending = false;
516e27085dfSToomas Soome 
517e27085dfSToomas Soome 	how = 0;
518e27085dfSToomas Soome 	sz = sizeof (buf);
519e27085dfSToomas Soome 	rv = efi_global_getenv("ConOut", buf, &sz);
520e27085dfSToomas Soome 	if (rv != EFI_SUCCESS)
521e27085dfSToomas Soome 		rv = efi_global_getenv("ConOutDev", buf, &sz);
522e27085dfSToomas Soome 	if (rv != EFI_SUCCESS) {
523e27085dfSToomas Soome 		/*
524e27085dfSToomas Soome 		 * If we don't have any ConOut default to video.
525e27085dfSToomas Soome 		 * non-server systems may not have serial.
526e27085dfSToomas Soome 		 */
527e27085dfSToomas Soome 		goto out;
528e27085dfSToomas Soome 	}
529e27085dfSToomas Soome 	ep = buf + sz;
530e27085dfSToomas Soome 	node = (EFI_DEVICE_PATH *)buf;
531e27085dfSToomas Soome 	while ((char *)node < ep) {
532e27085dfSToomas Soome 		if (IsDevicePathEndType(node)) {
533e27085dfSToomas Soome 			if (pci_pending && vid_seen == 0)
534e27085dfSToomas Soome 				vid_seen = ++seen;
535e27085dfSToomas Soome 		}
536e27085dfSToomas Soome 		pci_pending = false;
537e27085dfSToomas Soome 		if (DevicePathType(node) == ACPI_DEVICE_PATH &&
538e27085dfSToomas Soome 		    (DevicePathSubType(node) == ACPI_DP ||
539e27085dfSToomas Soome 		    DevicePathSubType(node) == ACPI_EXTENDED_DP)) {
540e27085dfSToomas Soome 			/* Check for Serial node */
541e27085dfSToomas Soome 			acpi = (void *)node;
542e27085dfSToomas Soome 			if (EISA_ID_TO_NUM(acpi->HID) == 0x501) {
543e27085dfSToomas Soome 				setenv_int("efi_8250_uid", acpi->UID);
544e27085dfSToomas Soome 				com_seen = ++seen;
545e27085dfSToomas Soome 			}
546e27085dfSToomas Soome 		} else if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
547e27085dfSToomas Soome 		    DevicePathSubType(node) == MSG_UART_DP) {
548e27085dfSToomas Soome 			com_seen = ++seen;
549e27085dfSToomas Soome 			uart = (void *)node;
550e27085dfSToomas Soome 			setenv_int("efi_com_speed", uart->BaudRate);
551e27085dfSToomas Soome 		} else if (DevicePathType(node) == ACPI_DEVICE_PATH &&
552e27085dfSToomas Soome 		    DevicePathSubType(node) == ACPI_ADR_DP) {
553e27085dfSToomas Soome 			/* Check for AcpiAdr() Node for video */
554e27085dfSToomas Soome 			vid_seen = ++seen;
555e27085dfSToomas Soome 		} else if (DevicePathType(node) == HARDWARE_DEVICE_PATH &&
556e27085dfSToomas Soome 		    DevicePathSubType(node) == HW_PCI_DP) {
557e27085dfSToomas Soome 			/*
558e27085dfSToomas Soome 			 * Note, vmware fusion has a funky console device
559e27085dfSToomas Soome 			 *	PciRoot(0x0)/Pci(0xf,0x0)
560e27085dfSToomas Soome 			 * which we can only detect at the end since we also
561e27085dfSToomas Soome 			 * have to cope with:
562e27085dfSToomas Soome 			 *	PciRoot(0x0)/Pci(0x1f,0x0)/Serial(0x1)
563e27085dfSToomas Soome 			 * so only match it if it's last.
564e27085dfSToomas Soome 			 */
565e27085dfSToomas Soome 			pci_pending = true;
566e27085dfSToomas Soome 		}
567e27085dfSToomas Soome 		node = NextDevicePathNode(node); /* Skip the end node */
568e27085dfSToomas Soome 	}
569e27085dfSToomas Soome 
570e27085dfSToomas Soome 	/*
571e27085dfSToomas Soome 	 * Truth table for RB_MULTIPLE | RB_SERIAL
572e27085dfSToomas Soome 	 * Value		Result
573e27085dfSToomas Soome 	 * 0			Use only video console
574e27085dfSToomas Soome 	 * RB_SERIAL		Use only serial console
575e27085dfSToomas Soome 	 * RB_MULTIPLE		Use both video and serial console
576e27085dfSToomas Soome 	 *			(but video is primary so gets rc messages)
577e27085dfSToomas Soome 	 * both			Use both video and serial console
578e27085dfSToomas Soome 	 *			(but serial is primary so gets rc messages)
579e27085dfSToomas Soome 	 *
580e27085dfSToomas Soome 	 * Try to honor this as best we can. If only one of serial / video
581e27085dfSToomas Soome 	 * found, then use that. Otherwise, use the first one we found.
582e27085dfSToomas Soome 	 * This also implies if we found nothing, default to video.
583e27085dfSToomas Soome 	 */
584e27085dfSToomas Soome 	how = 0;
585e27085dfSToomas Soome 	if (vid_seen && com_seen) {
586e27085dfSToomas Soome 		how |= RB_MULTIPLE;
587e27085dfSToomas Soome 		if (com_seen < vid_seen)
588e27085dfSToomas Soome 			how |= RB_SERIAL;
589e27085dfSToomas Soome 	} else if (com_seen)
590e27085dfSToomas Soome 		how |= RB_SERIAL;
591e27085dfSToomas Soome out:
592e27085dfSToomas Soome 	return (how);
593e27085dfSToomas Soome }
594e27085dfSToomas Soome 
59522028508SToomas Soome caddr_t
ptov(uintptr_t x)59622028508SToomas Soome ptov(uintptr_t x)
59722028508SToomas Soome {
59822028508SToomas Soome 	return ((caddr_t)x);
59922028508SToomas Soome }
60022028508SToomas Soome 
601e27085dfSToomas Soome static int
efi_serial_get_uid(EFI_DEVICE_PATH * devpath)602e27085dfSToomas Soome efi_serial_get_uid(EFI_DEVICE_PATH *devpath)
603e27085dfSToomas Soome {
604e27085dfSToomas Soome 	ACPI_HID_DEVICE_PATH  *acpi;
605e27085dfSToomas Soome 
606e27085dfSToomas Soome 	while (!IsDevicePathEnd(devpath)) {
607e27085dfSToomas Soome 		if (DevicePathType(devpath) == ACPI_DEVICE_PATH &&
608e27085dfSToomas Soome 		    (DevicePathSubType(devpath) == ACPI_DP ||
609e27085dfSToomas Soome 		    DevicePathSubType(devpath) == ACPI_EXTENDED_DP)) {
610e27085dfSToomas Soome 			acpi = (ACPI_HID_DEVICE_PATH *)devpath;
611e27085dfSToomas Soome 			if (EISA_ID_TO_NUM(acpi->HID) == 0x501) {
612e27085dfSToomas Soome 				return (acpi->UID);
613e27085dfSToomas Soome 			}
614e27085dfSToomas Soome 		}
615e27085dfSToomas Soome 
616e27085dfSToomas Soome 		devpath = NextDevicePathNode(devpath);
617e27085dfSToomas Soome 	}
618e27085dfSToomas Soome 	return (-1);
619e27085dfSToomas Soome }
620e27085dfSToomas Soome 
621e27085dfSToomas Soome /*
622e27085dfSToomas Soome  * Walk serialio protocol handle array and find index for serial console
623e27085dfSToomas Soome  * device. The problem is, we check for acpi UID value, but we can not be sure,
624e27085dfSToomas Soome  * if it will start from 0 or 1.
625e27085dfSToomas Soome  */
626e27085dfSToomas Soome static const char *
uefi_serial_console(void)627e27085dfSToomas Soome uefi_serial_console(void)
628e27085dfSToomas Soome {
629e27085dfSToomas Soome 	EFI_STATUS status;
630e27085dfSToomas Soome 	EFI_HANDLE *handles;
63169764de3SToomas Soome 	uint_t i, nhandles;
632e27085dfSToomas Soome 	unsigned long uid, lowest;
633e27085dfSToomas Soome 	char *env, *ep;
634e27085dfSToomas Soome 
635e27085dfSToomas Soome 	env = getenv("efi_8250_uid");
636e27085dfSToomas Soome 	if (env == NULL)
637e27085dfSToomas Soome 		return (NULL);
638e27085dfSToomas Soome 	(void) unsetenv("efi_8250_uid");
639e27085dfSToomas Soome 	errno = 0;
640e27085dfSToomas Soome 	uid = strtoul(env, &ep, 10);
641e27085dfSToomas Soome 	if (errno != 0 || *ep != '\0')
642e27085dfSToomas Soome 		return (NULL);
643e27085dfSToomas Soome 
644e27085dfSToomas Soome 	/* if uid is 0, this is first serial port */
645e27085dfSToomas Soome 	if (uid == 0)
646d3bd5503SToomas Soome 		return ("ttya");
647e27085dfSToomas Soome 
648*f334afcfSToomas Soome 	status = efi_get_protocol_handles(&gEfiSerialIoProtocolGuid,
649*f334afcfSToomas Soome 	    &nhandles, &handles);
650e27085dfSToomas Soome 	if (EFI_ERROR(status)) {
651e27085dfSToomas Soome 		return (NULL);
652e27085dfSToomas Soome 	}
653e27085dfSToomas Soome 
654e27085dfSToomas Soome 	lowest = 255;	/* high enough value */
655e27085dfSToomas Soome 	for (i = 0; i < nhandles; i++) {
656e27085dfSToomas Soome 		EFI_DEVICE_PATH *devpath;
657e27085dfSToomas Soome 		unsigned long _uid;
658e27085dfSToomas Soome 
659e27085dfSToomas Soome 		devpath = efi_lookup_devpath(handles[i]);
660e27085dfSToomas Soome 		_uid = efi_serial_get_uid(devpath);
661e27085dfSToomas Soome 		if (_uid < lowest)
662e27085dfSToomas Soome 			lowest = _uid;
663e27085dfSToomas Soome 	}
664e27085dfSToomas Soome 	free(handles);
665e27085dfSToomas Soome 	switch (uid - lowest) {
666e27085dfSToomas Soome 	case 0:
667d3bd5503SToomas Soome 		return ("ttya");
668e27085dfSToomas Soome 	case 1:
669d3bd5503SToomas Soome 		return ("ttyb");
670e27085dfSToomas Soome 	case 2:
671d3bd5503SToomas Soome 		return ("ttyc");
672e27085dfSToomas Soome 	case 3:
673d3bd5503SToomas Soome 		return ("ttyd");
674e27085dfSToomas Soome 	}
675e27085dfSToomas Soome 	return (NULL);
676e27085dfSToomas Soome }
677e27085dfSToomas Soome 
678199767f8SToomas Soome EFI_STATUS
main(int argc,CHAR16 * argv[])679199767f8SToomas Soome main(int argc, CHAR16 *argv[])
680199767f8SToomas Soome {
681199767f8SToomas Soome 	char var[128];
6828fe0f540SToomas Soome 	int i, j, howto;
6838fe0f540SToomas Soome 	bool vargood;
684dcba96feSToomas Soome 	void *ptr;
685afa95bedSToomas Soome 	bool has_kbd;
686e3026534SToomas Soome 	char *s;
687e27085dfSToomas Soome 	const char *serial;
688e3026534SToomas Soome 	EFI_DEVICE_PATH *imgpath;
689e3026534SToomas Soome 	CHAR16 *text;
690e3026534SToomas Soome 	EFI_STATUS status;
691e3026534SToomas Soome 	UINT16 boot_current;
692e3026534SToomas Soome 	size_t sz;
693e3026534SToomas Soome 	UINT16 boot_order[100];
694199767f8SToomas Soome 
695199767f8SToomas Soome 	archsw.arch_autoload = efi_autoload;
696199767f8SToomas Soome 	archsw.arch_getdev = efi_getdev;
697199767f8SToomas Soome 	archsw.arch_copyin = efi_copyin;
698199767f8SToomas Soome 	archsw.arch_copyout = efi_copyout;
699199767f8SToomas Soome 	archsw.arch_readin = efi_readin;
700f9feecc1SToomas Soome 	archsw.arch_loadaddr = efi_loadaddr;
701f9feecc1SToomas Soome 	archsw.arch_free_loadaddr = efi_free_loadaddr;
7028c653870SToomas Soome #if defined(__amd64) || defined(__i386)
7038c653870SToomas Soome 	archsw.arch_hypervisor = x86_hypervisor;
7048c653870SToomas Soome #endif
705199767f8SToomas Soome 	/* Note this needs to be set before ZFS init. */
706199767f8SToomas Soome 	archsw.arch_zfs_probe = efi_zfs_probe;
7079f23ea4bSToomas Soome 
7089f23ea4bSToomas Soome 	/* Get our loaded image protocol interface structure. */
709*f334afcfSToomas Soome 	(void) OpenProtocolByHandle(IH, &gEfiLoadedImageProtocolGuid,
710*f334afcfSToomas Soome 	    (void **)&img);
711199767f8SToomas Soome 
712199767f8SToomas Soome 	/*
713199767f8SToomas Soome 	 * XXX Chicken-and-egg problem; we want to have console output
714199767f8SToomas Soome 	 * early, but some console attributes may depend on reading from
715199767f8SToomas Soome 	 * eg. the boot device, which we can't do yet.  We can use
716199767f8SToomas Soome 	 * printf() etc. once this is done.
717199767f8SToomas Soome 	 */
718e27085dfSToomas Soome 	setenv("console", "text", 1);
719e27085dfSToomas Soome 	howto = parse_uefi_con_out();
720e27085dfSToomas Soome 	serial = uefi_serial_console();
721199767f8SToomas Soome 	cons_probe();
722f9feecc1SToomas Soome 	efi_getsmap();
723199767f8SToomas Soome 
724e27085dfSToomas Soome 	if ((s = getenv("efi_com_speed")) != NULL) {
725e27085dfSToomas Soome 		char *name;
726e27085dfSToomas Soome 
727e27085dfSToomas Soome 		(void) snprintf(var, sizeof (var), "%s,8,n,1,-", s);
728e27085dfSToomas Soome 		if (asprintf(&name, "%s-mode", serial) > 0) {
729c7d108c0SToomas Soome 			(void) setenv(name, var, 1);
730e27085dfSToomas Soome 			free(name);
731e27085dfSToomas Soome 		}
732e27085dfSToomas Soome 		if (asprintf(&name, "%s-spcr-mode", serial) > 0) {
733c7d108c0SToomas Soome 			(void) setenv(name, var, 1);
734e27085dfSToomas Soome 			free(name);
735e27085dfSToomas Soome 		}
736e27085dfSToomas Soome 		(void) unsetenv("efi_com_speed");
737e27085dfSToomas Soome 	}
738e27085dfSToomas Soome 
739e27085dfSToomas Soome 	/* Init the time source */
740e27085dfSToomas Soome 	efi_time_init();
741e27085dfSToomas Soome 
742199767f8SToomas Soome 	/*
743199767f8SToomas Soome 	 * Initialise the block cache. Set the upper limit.
744199767f8SToomas Soome 	 */
745199767f8SToomas Soome 	bcache_init(32768, 512);
746199767f8SToomas Soome 
747e27085dfSToomas Soome 	has_kbd = has_keyboard();
748e27085dfSToomas Soome 
749199767f8SToomas Soome 	/*
750199767f8SToomas Soome 	 * Parse the args to set the console settings, etc
751083aabddSToomas Soome 	 * iPXE may be setup to pass these in. Or the optional argument in the
752e3026534SToomas Soome 	 * boot environment was used to pass these arguments in (in which case
753e3026534SToomas Soome 	 * neither /boot.config nor /boot/config are consulted).
754199767f8SToomas Soome 	 *
755199767f8SToomas Soome 	 * Loop through the args, and for each one that contains an '=' that is
756199767f8SToomas Soome 	 * not the first character, add it to the environment.  This allows
757199767f8SToomas Soome 	 * loader and kernel env vars to be passed on the command line.  Convert
758083aabddSToomas Soome 	 * args from UCS-2 to ASCII (16 to 8 bit) as they are copied (though
759083aabddSToomas Soome 	 * this method is flawed for non-ASCII characters).
760199767f8SToomas Soome 	 */
761199767f8SToomas Soome 	for (i = 1; i < argc; i++) {
762199767f8SToomas Soome 		if (argv[i][0] == '-') {
763199767f8SToomas Soome 			for (j = 1; argv[i][j] != 0; j++) {
764199767f8SToomas Soome 				int ch;
765199767f8SToomas Soome 
766199767f8SToomas Soome 				ch = argv[i][j];
767199767f8SToomas Soome 				switch (ch) {
768199767f8SToomas Soome 				case 'a':
769199767f8SToomas Soome 					howto |= RB_ASKNAME;
770199767f8SToomas Soome 					break;
771199767f8SToomas Soome 				case 'd':
772199767f8SToomas Soome 					howto |= RB_KDB;
773199767f8SToomas Soome 					break;
774199767f8SToomas Soome 				case 'D':
775199767f8SToomas Soome 					howto |= RB_MULTIPLE;
776199767f8SToomas Soome 					break;
777199767f8SToomas Soome 				case 'h':
778199767f8SToomas Soome 					howto |= RB_SERIAL;
779199767f8SToomas Soome 					break;
780199767f8SToomas Soome 				case 'm':
781199767f8SToomas Soome 					howto |= RB_MUTE;
782199767f8SToomas Soome 					break;
783199767f8SToomas Soome 				case 'p':
784199767f8SToomas Soome 					howto |= RB_PAUSE;
785199767f8SToomas Soome 					break;
786199767f8SToomas Soome 				case 'P':
787083aabddSToomas Soome 					if (!has_kbd) {
788083aabddSToomas Soome 						howto |= RB_SERIAL;
789083aabddSToomas Soome 						howto |= RB_MULTIPLE;
790083aabddSToomas Soome 					}
791199767f8SToomas Soome 					break;
792199767f8SToomas Soome 				case 'r':
793199767f8SToomas Soome 					howto |= RB_DFLTROOT;
794199767f8SToomas Soome 					break;
795199767f8SToomas Soome 				case 's':
796199767f8SToomas Soome 					howto |= RB_SINGLE;
797199767f8SToomas Soome 					break;
798199767f8SToomas Soome 				case 'S':
799199767f8SToomas Soome 					if (argv[i][j + 1] == 0) {
800199767f8SToomas Soome 						if (i + 1 == argc) {
8012aca6c63SToomas Soome 							strncpy(var, "115200",
802083aabddSToomas Soome 							    sizeof (var));
803199767f8SToomas Soome 						} else {
8042aca6c63SToomas Soome 							CHAR16 *ptr;
8052aca6c63SToomas Soome 							ptr = &argv[i + 1][0];
8062aca6c63SToomas Soome 							cpy16to8(ptr, var,
807083aabddSToomas Soome 							    sizeof (var));
808199767f8SToomas Soome 						}
809199767f8SToomas Soome 						i++;
810199767f8SToomas Soome 					} else {
811eee59048SToomas Soome 						cpy16to8(&argv[i][j + 1], var,
812083aabddSToomas Soome 						    sizeof (var));
813199767f8SToomas Soome 					}
814083aabddSToomas Soome 					strncat(var, ",8,n,1,-", sizeof (var));
8152aca6c63SToomas Soome 					setenv("ttya-mode", var, 1);
8162aca6c63SToomas Soome 					break;
817199767f8SToomas Soome 				case 'v':
818199767f8SToomas Soome 					howto |= RB_VERBOSE;
819199767f8SToomas Soome 					break;
820199767f8SToomas Soome 				}
821199767f8SToomas Soome 			}
822199767f8SToomas Soome 		} else {
8238fe0f540SToomas Soome 			vargood = false;
824199767f8SToomas Soome 			for (j = 0; argv[i][j] != 0; j++) {
825083aabddSToomas Soome 				if (j == sizeof (var)) {
8268fe0f540SToomas Soome 					vargood = false;
827199767f8SToomas Soome 					break;
828199767f8SToomas Soome 				}
829199767f8SToomas Soome 				if (j > 0 && argv[i][j] == '=')
8308fe0f540SToomas Soome 					vargood = true;
831199767f8SToomas Soome 				var[j] = (char)argv[i][j];
832199767f8SToomas Soome 			}
833199767f8SToomas Soome 			if (vargood) {
834199767f8SToomas Soome 				var[j] = 0;
835199767f8SToomas Soome 				putenv(var);
836199767f8SToomas Soome 			}
837199767f8SToomas Soome 		}
838199767f8SToomas Soome 	}
839199767f8SToomas Soome 	for (i = 0; howto_names[i].ev != NULL; i++)
840199767f8SToomas Soome 		if (howto & howto_names[i].mask)
841199767f8SToomas Soome 			setenv(howto_names[i].ev, "YES", 1);
842e3026534SToomas Soome 
843e3026534SToomas Soome 	/*
844e3026534SToomas Soome 	 * XXX we need fallback to this stuff after looking at the ConIn,
845e3026534SToomas Soome 	 * ConOut and ConErr variables.
846e3026534SToomas Soome 	 */
847199767f8SToomas Soome 	if (howto & RB_MULTIPLE) {
848199767f8SToomas Soome 		if (howto & RB_SERIAL)
849e27085dfSToomas Soome 			(void) snprintf(var, sizeof (var), "%s text", serial);
850199767f8SToomas Soome 		else
851e27085dfSToomas Soome 			(void) snprintf(var, sizeof (var), "text %s", serial);
852199767f8SToomas Soome 	} else if (howto & RB_SERIAL) {
853e27085dfSToomas Soome 		(void) snprintf(var, sizeof (var), "%s", serial);
854e27085dfSToomas Soome 	} else {
855e27085dfSToomas Soome 		(void) snprintf(var, sizeof (var), "text");
856e27085dfSToomas Soome 	}
857e27085dfSToomas Soome 	(void) setenv("console", var, 1);
858e3026534SToomas Soome 
859e3026534SToomas Soome 	if ((s = getenv("fail_timeout")) != NULL)
860e3026534SToomas Soome 		fail_timeout = strtol(s, NULL, 10);
861199767f8SToomas Soome 
862199767f8SToomas Soome 	/*
863c00b6c91SToomas Soome 	 * Scan the BLOCK IO MEDIA handles then
864c00b6c91SToomas Soome 	 * march through the device switch probing for things.
865199767f8SToomas Soome 	 */
866c00b6c91SToomas Soome 	if ((i = efipart_inithandles()) == 0) {
867c00b6c91SToomas Soome 		for (i = 0; devsw[i] != NULL; i++)
868c00b6c91SToomas Soome 			if (devsw[i]->dv_init != NULL)
869c00b6c91SToomas Soome 				(devsw[i]->dv_init)();
870c00b6c91SToomas Soome 	} else
871c00b6c91SToomas Soome 		printf("efipart_inithandles failed %d, expect failures", i);
872199767f8SToomas Soome 
873199767f8SToomas Soome 	printf("Command line arguments:");
874199767f8SToomas Soome 	for (i = 0; i < argc; i++) {
875eee59048SToomas Soome 		printf(" %S", argv[i]);
876199767f8SToomas Soome 	}
877199767f8SToomas Soome 	printf("\n");
878199767f8SToomas Soome 
879083aabddSToomas Soome 	printf("Image base: 0x%lx\n", (unsigned long)img->ImageBase);
880199767f8SToomas Soome 	printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16,
881199767f8SToomas Soome 	    ST->Hdr.Revision & 0xffff);
882eee59048SToomas Soome 	printf("EFI Firmware: %S (rev %d.%02d)\n", ST->FirmwareVendor,
883eee59048SToomas Soome 	    ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
884199767f8SToomas Soome 
885f6c94443SToomas Soome 	printf("\n%s", bootprog_info);
886199767f8SToomas Soome 
887e3026534SToomas Soome 	/* Determine the devpath of our image so we can prefer it. */
888e3026534SToomas Soome 	text = efi_devpath_name(img->FilePath);
889e3026534SToomas Soome 	if (text != NULL) {
890e3026534SToomas Soome 		printf("   Load Path: %S\n", text);
891e3026534SToomas Soome 		efi_setenv_illumos_wcs("LoaderPath", text);
892e3026534SToomas Soome 		efi_free_devpath_name(text);
893e3026534SToomas Soome 	}
894e3026534SToomas Soome 
895*f334afcfSToomas Soome 	status = OpenProtocolByHandle(img->DeviceHandle,
896*f334afcfSToomas Soome 	    &gEfiDevicePathProtocolGuid, (void **)&imgpath);
897e3026534SToomas Soome 	if (status == EFI_SUCCESS) {
898e3026534SToomas Soome 		text = efi_devpath_name(imgpath);
899e3026534SToomas Soome 		if (text != NULL) {
900e3026534SToomas Soome 			printf("   Load Device: %S\n", text);
901e3026534SToomas Soome 			efi_setenv_illumos_wcs("LoaderDev", text);
902e3026534SToomas Soome 			efi_free_devpath_name(text);
903e3026534SToomas Soome 		}
904e3026534SToomas Soome 	}
905e3026534SToomas Soome 
906e3026534SToomas Soome 	boot_current = 0;
907083aabddSToomas Soome 	sz = sizeof (boot_current);
908e3026534SToomas Soome 	efi_global_getenv("BootCurrent", &boot_current, &sz);
909e3026534SToomas Soome 	printf("   BootCurrent: %04x\n", boot_current);
910e3026534SToomas Soome 
911083aabddSToomas Soome 	sz = sizeof (boot_order);
912e3026534SToomas Soome 	efi_global_getenv("BootOrder", &boot_order, &sz);
913e3026534SToomas Soome 	printf("   BootOrder:");
914083aabddSToomas Soome 	for (i = 0; i < sz / sizeof (boot_order[0]); i++)
915e3026534SToomas Soome 		printf(" %04x%s", boot_order[i],
916e3026534SToomas Soome 		    boot_order[i] == boot_current ? "[*]" : "");
917e3026534SToomas Soome 	printf("\n");
918e3026534SToomas Soome 
919199767f8SToomas Soome 	/*
920199767f8SToomas Soome 	 * Disable the watchdog timer. By default the boot manager sets
921199767f8SToomas Soome 	 * the timer to 5 minutes before invoking a boot option. If we
922199767f8SToomas Soome 	 * want to return to the boot manager, we have to disable the
923199767f8SToomas Soome 	 * watchdog timer and since we're an interactive program, we don't
924199767f8SToomas Soome 	 * want to wait until the user types "quit". The timer may have
925199767f8SToomas Soome 	 * fired by then. We don't care if this fails. It does not prevent
926199767f8SToomas Soome 	 * normal functioning in any way...
927199767f8SToomas Soome 	 */
928199767f8SToomas Soome 	BS->SetWatchdogTimer(0, 0, 0, NULL);
929199767f8SToomas Soome 
930e3026534SToomas Soome 	/*
931e3026534SToomas Soome 	 * Try and find a good currdev based on the image that was booted.
932e3026534SToomas Soome 	 * It might be desirable here to have a short pause to allow falling
933e3026534SToomas Soome 	 * through to the boot loader instead of returning instantly to follow
934e3026534SToomas Soome 	 * the boot protocol and also allow an escape hatch for users wishing
935e3026534SToomas Soome 	 * to try something different.
936e3026534SToomas Soome 	 */
937e3026534SToomas Soome 	if (!find_currdev(img))
938e3026534SToomas Soome 		if (!interactive_interrupt("Failed to find bootable partition"))
939e3026534SToomas Soome 			return (EFI_NOT_FOUND);
940199767f8SToomas Soome 
941e0721d5aSToomas Soome 	autoload_font(false);		/* Set up the font list for console. */
942eee59048SToomas Soome 	efi_init_environment();
943d9256fffSToomas Soome 	bi_isadir();			/* set ISADIR */
944dcba96feSToomas Soome 	acpi_detect();
945199767f8SToomas Soome 
946*f334afcfSToomas Soome 	if ((ptr = efi_get_table(&gEfiSmbios3TableGuid)) == NULL)
947*f334afcfSToomas Soome 		ptr = efi_get_table(&gEfiSmbiosTableGuid);
948dcba96feSToomas Soome 	smbios_detect(ptr);
949199767f8SToomas Soome 
950199767f8SToomas Soome 	interact(NULL);			/* doesn't return */
951199767f8SToomas Soome 
952199767f8SToomas Soome 	return (EFI_SUCCESS);		/* keep compiler happy */
953199767f8SToomas Soome }
954199767f8SToomas Soome 
955199767f8SToomas Soome COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
956199767f8SToomas Soome 
9570e8f244eSToomas Soome static void
fw_setup(void)9580e8f244eSToomas Soome fw_setup(void)
9590e8f244eSToomas Soome {
9600e8f244eSToomas Soome 	uint64_t os_indications;
9610e8f244eSToomas Soome 	size_t size;
9620e8f244eSToomas Soome 	EFI_STATUS status;
9630e8f244eSToomas Soome 
9640e8f244eSToomas Soome 	size = sizeof (os_indications);
9650e8f244eSToomas Soome 	status = efi_global_getenv("OsIndicationsSupported",
9660e8f244eSToomas Soome 	    &os_indications, &size);
9670e8f244eSToomas Soome 	if (EFI_ERROR(status) || size != sizeof (os_indications) ||
9680e8f244eSToomas Soome 	    (os_indications & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) == 0) {
9690e8f244eSToomas Soome 		printf("Booting to Firmware UI is not supported in "
9700e8f244eSToomas Soome 		    "this system.");
9710e8f244eSToomas Soome 		for (int i = 0; i < 3; i++) {
9720e8f244eSToomas Soome 			delay(1000 * 1000); /* 1 second */
9730e8f244eSToomas Soome 			if (ischar())
9740e8f244eSToomas Soome 				break;
9750e8f244eSToomas Soome 		}
9760e8f244eSToomas Soome 		return;
9770e8f244eSToomas Soome 	}
9780e8f244eSToomas Soome 
9790e8f244eSToomas Soome 	os_indications = EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
9800e8f244eSToomas Soome 
9810e8f244eSToomas Soome 	status = efi_global_setenv("OsIndications", &os_indications,
9820e8f244eSToomas Soome 	    sizeof (os_indications));
9830e8f244eSToomas Soome }
9840e8f244eSToomas Soome 
985199767f8SToomas Soome static int
command_reboot(int argc,char * argv[])9860e8f244eSToomas Soome command_reboot(int argc, char *argv[])
987199767f8SToomas Soome {
9880e8f244eSToomas Soome 	int i, ch;
9890e8f244eSToomas Soome 	bool fw = false;
9900e8f244eSToomas Soome 
9910e8f244eSToomas Soome 	optind = 1;
9920e8f244eSToomas Soome 	optreset = 1;
9930e8f244eSToomas Soome 
9940e8f244eSToomas Soome 	while ((ch = getopt(argc, argv, "fh")) != -1) {
9950e8f244eSToomas Soome 		switch (ch) {
9960e8f244eSToomas Soome 		case 'f':
9970e8f244eSToomas Soome 			fw = true;
9980e8f244eSToomas Soome 			break;
9990e8f244eSToomas Soome 		case 'h':
10000e8f244eSToomas Soome 			printf("Usage: reboot [-f]\n");
10010e8f244eSToomas Soome 			return (CMD_OK);
10020e8f244eSToomas Soome 		case '?':
10030e8f244eSToomas Soome 		default:
10040e8f244eSToomas Soome 			return (CMD_OK);
10050e8f244eSToomas Soome 		}
10060e8f244eSToomas Soome 	}
10070e8f244eSToomas Soome 
10080e8f244eSToomas Soome 	if (fw || getenv("BOOT_TO_FW_UI") != NULL)
10090e8f244eSToomas Soome 		fw_setup();
1010199767f8SToomas Soome 
1011199767f8SToomas Soome 	for (i = 0; devsw[i] != NULL; ++i)
1012199767f8SToomas Soome 		if (devsw[i]->dv_cleanup != NULL)
1013199767f8SToomas Soome 			(devsw[i]->dv_cleanup)();
1014199767f8SToomas Soome 
101548d8443eSToomas Soome 	RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
1016199767f8SToomas Soome 
1017199767f8SToomas Soome 	/* NOTREACHED */
1018199767f8SToomas Soome 	return (CMD_ERROR);
1019199767f8SToomas Soome }
1020199767f8SToomas Soome 
1021b31ca808SToomas Soome COMMAND_SET(poweroff, "poweroff", "power off the system", command_poweroff);
1022b31ca808SToomas Soome 
1023b31ca808SToomas Soome static int
command_poweroff(int argc __unused,char * argv[]__unused)1024b31ca808SToomas Soome command_poweroff(int argc __unused, char *argv[] __unused)
1025b31ca808SToomas Soome {
1026b31ca808SToomas Soome 	int i;
1027b31ca808SToomas Soome 
1028b31ca808SToomas Soome 	for (i = 0; devsw[i] != NULL; ++i)
1029b31ca808SToomas Soome 		if (devsw[i]->dv_cleanup != NULL)
1030b31ca808SToomas Soome 			(devsw[i]->dv_cleanup)();
1031b31ca808SToomas Soome 
1032b31ca808SToomas Soome 	RS->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL);
1033b31ca808SToomas Soome 
1034b31ca808SToomas Soome 	/* NOTREACHED */
1035b31ca808SToomas Soome 	return (CMD_ERROR);
1036b31ca808SToomas Soome }
1037b31ca808SToomas Soome 
1038199767f8SToomas Soome COMMAND_SET(memmap, "memmap", "print memory map", command_memmap);
1039199767f8SToomas Soome 
1040199767f8SToomas Soome static int
command_memmap(int argc __unused,char * argv[]__unused)104130c75cb0SToomas Soome command_memmap(int argc __unused, char *argv[] __unused)
1042199767f8SToomas Soome {
1043199767f8SToomas Soome 	UINTN sz;
1044199767f8SToomas Soome 	EFI_MEMORY_DESCRIPTOR *map, *p;
1045199767f8SToomas Soome 	UINTN key, dsz;
1046199767f8SToomas Soome 	UINT32 dver;
1047199767f8SToomas Soome 	EFI_STATUS status;
1048199767f8SToomas Soome 	int i, ndesc;
1049199767f8SToomas Soome 	int rv = 0;
1050199767f8SToomas Soome 	char line[80];
1051199767f8SToomas Soome 
1052199767f8SToomas Soome 	sz = 0;
1053199767f8SToomas Soome 	status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver);
1054199767f8SToomas Soome 	if (status != EFI_BUFFER_TOO_SMALL) {
1055199767f8SToomas Soome 		printf("Can't determine memory map size\n");
1056199767f8SToomas Soome 		return (CMD_ERROR);
1057199767f8SToomas Soome 	}
1058199767f8SToomas Soome 	map = malloc(sz);
1059199767f8SToomas Soome 	status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver);
1060199767f8SToomas Soome 	if (EFI_ERROR(status)) {
1061199767f8SToomas Soome 		printf("Can't read memory map\n");
1062199767f8SToomas Soome 		return (CMD_ERROR);
1063199767f8SToomas Soome 	}
1064199767f8SToomas Soome 
1065199767f8SToomas Soome 	ndesc = sz / dsz;
1066199767f8SToomas Soome 	snprintf(line, 80, "%23s %12s %12s %8s %4s\n",
1067083aabddSToomas Soome 	    "Type", "Physical", "Virtual", "#Pages", "Attr");
1068199767f8SToomas Soome 	pager_open();
1069199767f8SToomas Soome 	rv = pager_output(line);
1070199767f8SToomas Soome 	if (rv) {
1071199767f8SToomas Soome 		pager_close();
1072199767f8SToomas Soome 		return (CMD_OK);
1073199767f8SToomas Soome 	}
1074199767f8SToomas Soome 
1075199767f8SToomas Soome 	for (i = 0, p = map; i < ndesc;
1076083aabddSToomas Soome 	    i++, p = NextMemoryDescriptor(p, dsz)) {
107783b4671eSToomas Soome 		snprintf(line, 80, "%23s %012jx %012jx %08jx ",
107883b4671eSToomas Soome 		    efi_memory_type(p->Type), p->PhysicalStart,
107983b4671eSToomas Soome 		    p->VirtualStart, p->NumberOfPages);
1080199767f8SToomas Soome 		rv = pager_output(line);
1081199767f8SToomas Soome 		if (rv)
1082199767f8SToomas Soome 			break;
1083199767f8SToomas Soome 
1084199767f8SToomas Soome 		if (p->Attribute & EFI_MEMORY_UC)
1085199767f8SToomas Soome 			printf("UC ");
1086199767f8SToomas Soome 		if (p->Attribute & EFI_MEMORY_WC)
1087199767f8SToomas Soome 			printf("WC ");
1088199767f8SToomas Soome 		if (p->Attribute & EFI_MEMORY_WT)
1089199767f8SToomas Soome 			printf("WT ");
1090199767f8SToomas Soome 		if (p->Attribute & EFI_MEMORY_WB)
1091199767f8SToomas Soome 			printf("WB ");
1092199767f8SToomas Soome 		if (p->Attribute & EFI_MEMORY_UCE)
1093199767f8SToomas Soome 			printf("UCE ");
1094199767f8SToomas Soome 		if (p->Attribute & EFI_MEMORY_WP)
1095199767f8SToomas Soome 			printf("WP ");
1096199767f8SToomas Soome 		if (p->Attribute & EFI_MEMORY_RP)
1097199767f8SToomas Soome 			printf("RP ");
1098199767f8SToomas Soome 		if (p->Attribute & EFI_MEMORY_XP)
1099199767f8SToomas Soome 			printf("XP ");
1100eee59048SToomas Soome 		if (p->Attribute & EFI_MEMORY_NV)
1101eee59048SToomas Soome 			printf("NV ");
1102eee59048SToomas Soome 		if (p->Attribute & EFI_MEMORY_MORE_RELIABLE)
1103eee59048SToomas Soome 			printf("MR ");
1104eee59048SToomas Soome 		if (p->Attribute & EFI_MEMORY_RO)
1105eee59048SToomas Soome 			printf("RO ");
1106199767f8SToomas Soome 		rv = pager_output("\n");
1107199767f8SToomas Soome 		if (rv)
1108199767f8SToomas Soome 			break;
1109199767f8SToomas Soome 	}
1110199767f8SToomas Soome 
1111199767f8SToomas Soome 	pager_close();
1112199767f8SToomas Soome 	return (CMD_OK);
1113199767f8SToomas Soome }
1114199767f8SToomas Soome 
1115199767f8SToomas Soome COMMAND_SET(configuration, "configuration", "print configuration tables",
1116199767f8SToomas Soome     command_configuration);
1117199767f8SToomas Soome 
1118199767f8SToomas Soome static int
command_configuration(int argc __unused,char * argv[]__unused)111930c75cb0SToomas Soome command_configuration(int argc __unused, char *argv[] __unused)
1120199767f8SToomas Soome {
1121199767f8SToomas Soome 	UINTN i;
1122eee59048SToomas Soome 	char *name;
1123199767f8SToomas Soome 
1124199767f8SToomas Soome 	printf("NumberOfTableEntries=%lu\n",
1125083aabddSToomas Soome 	    (unsigned long)ST->NumberOfTableEntries);
1126199767f8SToomas Soome 	for (i = 0; i < ST->NumberOfTableEntries; i++) {
1127199767f8SToomas Soome 		EFI_GUID *guid;
1128199767f8SToomas Soome 
1129199767f8SToomas Soome 		printf("  ");
1130199767f8SToomas Soome 		guid = &ST->ConfigurationTable[i].VendorGuid;
1131eee59048SToomas Soome 
1132eee59048SToomas Soome 		if (efi_guid_to_name(guid, &name) == true) {
1133eee59048SToomas Soome 			printf(name);
1134eee59048SToomas Soome 			free(name);
1135eee59048SToomas Soome 		} else {
1136eee59048SToomas Soome 			printf("Error while translating UUID to name");
1137eee59048SToomas Soome 		}
1138199767f8SToomas Soome 		printf(" at %p\n", ST->ConfigurationTable[i].VendorTable);
1139199767f8SToomas Soome 	}
1140199767f8SToomas Soome 
1141199767f8SToomas Soome 	return (CMD_OK);
1142199767f8SToomas Soome }
1143199767f8SToomas Soome 
1144199767f8SToomas Soome 
1145199767f8SToomas Soome COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode);
1146199767f8SToomas Soome 
1147199767f8SToomas Soome static int
command_mode(int argc,char * argv[])1148199767f8SToomas Soome command_mode(int argc, char *argv[])
1149199767f8SToomas Soome {
1150199767f8SToomas Soome 	UINTN cols, rows;
1151199767f8SToomas Soome 	unsigned int mode;
1152199767f8SToomas Soome 	int i;
1153199767f8SToomas Soome 	char *cp;
1154199767f8SToomas Soome 	EFI_STATUS status;
1155199767f8SToomas Soome 	SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
11569890ff83SToomas Soome 	EFI_CONSOLE_CONTROL_SCREEN_MODE sm;
11579890ff83SToomas Soome 
11589890ff83SToomas Soome 	if (plat_stdout_is_framebuffer())
11599890ff83SToomas Soome 		sm = EfiConsoleControlScreenGraphics;
11609890ff83SToomas Soome 	else
11619890ff83SToomas Soome 		sm = EfiConsoleControlScreenText;
1162199767f8SToomas Soome 
1163199767f8SToomas Soome 	conout = ST->ConOut;
1164199767f8SToomas Soome 
1165199767f8SToomas Soome 	if (argc > 1) {
1166199767f8SToomas Soome 		mode = strtol(argv[1], &cp, 0);
1167199767f8SToomas Soome 		if (cp[0] != '\0') {
1168199767f8SToomas Soome 			printf("Invalid mode\n");
1169199767f8SToomas Soome 			return (CMD_ERROR);
1170199767f8SToomas Soome 		}
1171199767f8SToomas Soome 		status = conout->QueryMode(conout, mode, &cols, &rows);
1172199767f8SToomas Soome 		if (EFI_ERROR(status)) {
1173199767f8SToomas Soome 			printf("invalid mode %d\n", mode);
1174199767f8SToomas Soome 			return (CMD_ERROR);
1175199767f8SToomas Soome 		}
1176199767f8SToomas Soome 		status = conout->SetMode(conout, mode);
1177199767f8SToomas Soome 		if (EFI_ERROR(status)) {
1178199767f8SToomas Soome 			printf("couldn't set mode %d\n", mode);
1179199767f8SToomas Soome 			return (CMD_ERROR);
1180199767f8SToomas Soome 		}
11819890ff83SToomas Soome 		plat_cons_update_mode(sm);
1182199767f8SToomas Soome 		return (CMD_OK);
1183199767f8SToomas Soome 	}
1184199767f8SToomas Soome 
1185199767f8SToomas Soome 	printf("Current mode: %d\n", conout->Mode->Mode);
1186199767f8SToomas Soome 	for (i = 0; i <= conout->Mode->MaxMode; i++) {
1187199767f8SToomas Soome 		status = conout->QueryMode(conout, i, &cols, &rows);
1188199767f8SToomas Soome 		if (EFI_ERROR(status))
1189199767f8SToomas Soome 			continue;
1190199767f8SToomas Soome 		printf("Mode %d: %u columns, %u rows\n", i, (unsigned)cols,
1191199767f8SToomas Soome 		    (unsigned)rows);
1192199767f8SToomas Soome 	}
1193199767f8SToomas Soome 
1194199767f8SToomas Soome 	if (i != 0)
1195199767f8SToomas Soome 		printf("Select a mode with the command \"mode <number>\"\n");
1196199767f8SToomas Soome 
1197199767f8SToomas Soome 	return (CMD_OK);
1198199767f8SToomas Soome }
1199199767f8SToomas Soome 
1200199767f8SToomas Soome COMMAND_SET(lsefi, "lsefi", "list EFI handles", command_lsefi);
1201199767f8SToomas Soome 
1202199767f8SToomas Soome static int
command_lsefi(int argc __unused,char * argv[]__unused)120330c75cb0SToomas Soome command_lsefi(int argc __unused, char *argv[] __unused)
1204199767f8SToomas Soome {
1205eee59048SToomas Soome 	char *name;
1206199767f8SToomas Soome 	EFI_HANDLE *buffer = NULL;
1207199767f8SToomas Soome 	EFI_HANDLE handle;
1208199767f8SToomas Soome 	UINTN bufsz = 0, i, j;
1209199767f8SToomas Soome 	EFI_STATUS status;
1210083aabddSToomas Soome 	int ret = 0;
1211199767f8SToomas Soome 
1212199767f8SToomas Soome 	status = BS->LocateHandle(AllHandles, NULL, NULL, &bufsz, buffer);
1213199767f8SToomas Soome 	if (status != EFI_BUFFER_TOO_SMALL) {
1214199767f8SToomas Soome 		snprintf(command_errbuf, sizeof (command_errbuf),
1215199767f8SToomas Soome 		    "unexpected error: %lld", (long long)status);
1216199767f8SToomas Soome 		return (CMD_ERROR);
1217199767f8SToomas Soome 	}
1218199767f8SToomas Soome 	if ((buffer = malloc(bufsz)) == NULL) {
1219199767f8SToomas Soome 		sprintf(command_errbuf, "out of memory");
1220199767f8SToomas Soome 		return (CMD_ERROR);
1221199767f8SToomas Soome 	}
1222199767f8SToomas Soome 
1223199767f8SToomas Soome 	status = BS->LocateHandle(AllHandles, NULL, NULL, &bufsz, buffer);
1224199767f8SToomas Soome 	if (EFI_ERROR(status)) {
1225199767f8SToomas Soome 		free(buffer);
1226199767f8SToomas Soome 		snprintf(command_errbuf, sizeof (command_errbuf),
1227199767f8SToomas Soome 		    "LocateHandle() error: %lld", (long long)status);
1228199767f8SToomas Soome 		return (CMD_ERROR);
1229199767f8SToomas Soome 	}
1230199767f8SToomas Soome 
1231199767f8SToomas Soome 	pager_open();
1232199767f8SToomas Soome 	for (i = 0; i < (bufsz / sizeof (EFI_HANDLE)); i++) {
1233199767f8SToomas Soome 		UINTN nproto = 0;
1234199767f8SToomas Soome 		EFI_GUID **protocols = NULL;
1235c7d108c0SToomas Soome 		EFI_DEVICE_PATH *dp;
1236c7d108c0SToomas Soome 		CHAR16 *text;
1237199767f8SToomas Soome 
1238199767f8SToomas Soome 		handle = buffer[i];
1239199767f8SToomas Soome 		printf("Handle %p", handle);
1240199767f8SToomas Soome 		if (pager_output("\n"))
1241199767f8SToomas Soome 			break;
1242c7d108c0SToomas Soome 
1243c7d108c0SToomas Soome 		ret = 0;
1244c7d108c0SToomas Soome 		dp = efi_lookup_devpath(handle);
1245c7d108c0SToomas Soome 		if (dp != NULL) {
1246c7d108c0SToomas Soome 			text = efi_devpath_name(dp);
1247c7d108c0SToomas Soome 			if (text != NULL) {
1248c7d108c0SToomas Soome 				printf("  %S", text);
1249c7d108c0SToomas Soome 				efi_free_devpath_name(text);
1250c7d108c0SToomas Soome 				ret = pager_output("\n");
1251c7d108c0SToomas Soome 			}
1252c7d108c0SToomas Soome 			efi_close_devpath(handle);
1253c7d108c0SToomas Soome 		}
1254c7d108c0SToomas Soome 		if (ret != 0)
1255c7d108c0SToomas Soome 			break;
1256199767f8SToomas Soome 
1257199767f8SToomas Soome 		status = BS->ProtocolsPerHandle(handle, &protocols, &nproto);
1258199767f8SToomas Soome 		if (EFI_ERROR(status)) {
1259199767f8SToomas Soome 			snprintf(command_errbuf, sizeof (command_errbuf),
1260199767f8SToomas Soome 			    "ProtocolsPerHandle() error: %lld",
1261199767f8SToomas Soome 			    (long long)status);
1262199767f8SToomas Soome 			continue;
1263199767f8SToomas Soome 		}
1264eee59048SToomas Soome 
1265199767f8SToomas Soome 		for (j = 0; j < nproto; j++) {
1266eee59048SToomas Soome 			if (efi_guid_to_name(protocols[j], &name) == true) {
1267eee59048SToomas Soome 				printf("  %s", name);
1268eee59048SToomas Soome 				free(name);
1269eee59048SToomas Soome 			} else {
1270eee59048SToomas Soome 				printf("Error while translating UUID to name");
1271eee59048SToomas Soome 			}
1272eee59048SToomas Soome 			if ((ret = pager_output("\n")) != 0)
1273199767f8SToomas Soome 				break;
1274199767f8SToomas Soome 		}
1275199767f8SToomas Soome 		BS->FreePool(protocols);
1276eee59048SToomas Soome 		if (ret != 0)
1277199767f8SToomas Soome 			break;
1278199767f8SToomas Soome 	}
1279199767f8SToomas Soome 	pager_close();
1280199767f8SToomas Soome 	free(buffer);
1281199767f8SToomas Soome 	return (CMD_OK);
1282199767f8SToomas Soome }
1283199767f8SToomas Soome 
1284199767f8SToomas Soome #ifdef LOADER_FDT_SUPPORT
1285199767f8SToomas Soome extern int command_fdt_internal(int argc, char *argv[]);
1286199767f8SToomas Soome 
1287199767f8SToomas Soome /*
1288199767f8SToomas Soome  * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
1289199767f8SToomas Soome  * and declaring it as extern is in contradiction with COMMAND_SET() macro
1290199767f8SToomas Soome  * (which uses static pointer), we're defining wrapper function, which
1291199767f8SToomas Soome  * calls the proper fdt handling routine.
1292199767f8SToomas Soome  */
1293199767f8SToomas Soome static int
command_fdt(int argc,char * argv[])1294199767f8SToomas Soome command_fdt(int argc, char *argv[])
1295199767f8SToomas Soome {
1296199767f8SToomas Soome 	return (command_fdt_internal(argc, argv));
1297199767f8SToomas Soome }
1298199767f8SToomas Soome 
1299199767f8SToomas Soome COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
1300199767f8SToomas Soome #endif
1301199767f8SToomas Soome 
1302f9feecc1SToomas Soome /*
1303f9feecc1SToomas Soome  * Chain load another efi loader.
1304f9feecc1SToomas Soome  */
1305f9feecc1SToomas Soome static int
command_chain(int argc,char * argv[])1306f9feecc1SToomas Soome command_chain(int argc, char *argv[])
1307f9feecc1SToomas Soome {
1308f9feecc1SToomas Soome 	EFI_HANDLE loaderhandle;
1309*f334afcfSToomas Soome 	EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
1310f9feecc1SToomas Soome 	EFI_STATUS status;
1311f9feecc1SToomas Soome 	struct stat st;
1312f9feecc1SToomas Soome 	struct devdesc *dev;
1313f9feecc1SToomas Soome 	char *name, *path;
1314f9feecc1SToomas Soome 	void *buf;
1315f9feecc1SToomas Soome 	int fd;
1316f9feecc1SToomas Soome 
1317f9feecc1SToomas Soome 	if (argc < 2) {
1318f9feecc1SToomas Soome 		command_errmsg = "wrong number of arguments";
1319f9feecc1SToomas Soome 		return (CMD_ERROR);
1320f9feecc1SToomas Soome 	}
1321f9feecc1SToomas Soome 
1322f9feecc1SToomas Soome 	name = argv[1];
1323f9feecc1SToomas Soome 
1324f9feecc1SToomas Soome 	if ((fd = open(name, O_RDONLY)) < 0) {
1325f9feecc1SToomas Soome 		command_errmsg = "no such file";
1326f9feecc1SToomas Soome 		return (CMD_ERROR);
1327f9feecc1SToomas Soome 	}
1328f9feecc1SToomas Soome 
1329f9feecc1SToomas Soome 	if (fstat(fd, &st) < -1) {
1330f9feecc1SToomas Soome 		command_errmsg = "stat failed";
1331f9feecc1SToomas Soome 		close(fd);
1332f9feecc1SToomas Soome 		return (CMD_ERROR);
1333f9feecc1SToomas Soome 	}
1334f9feecc1SToomas Soome 
1335f9feecc1SToomas Soome 	status = BS->AllocatePool(EfiLoaderCode, (UINTN)st.st_size, &buf);
1336f9feecc1SToomas Soome 	if (status != EFI_SUCCESS) {
1337f9feecc1SToomas Soome 		command_errmsg = "failed to allocate buffer";
1338f9feecc1SToomas Soome 		close(fd);
1339f9feecc1SToomas Soome 		return (CMD_ERROR);
1340f9feecc1SToomas Soome 	}
1341f9feecc1SToomas Soome 	if (read(fd, buf, st.st_size) != st.st_size) {
1342f9feecc1SToomas Soome 		command_errmsg = "error while reading the file";
1343083aabddSToomas Soome 		(void) BS->FreePool(buf);
1344f9feecc1SToomas Soome 		close(fd);
1345f9feecc1SToomas Soome 		return (CMD_ERROR);
1346f9feecc1SToomas Soome 	}
1347f9feecc1SToomas Soome 	close(fd);
1348f9feecc1SToomas Soome 	status = BS->LoadImage(FALSE, IH, NULL, buf, st.st_size, &loaderhandle);
1349083aabddSToomas Soome 	(void) BS->FreePool(buf);
1350f9feecc1SToomas Soome 	if (status != EFI_SUCCESS) {
1351f9feecc1SToomas Soome 		command_errmsg = "LoadImage failed";
1352f9feecc1SToomas Soome 		return (CMD_ERROR);
1353f9feecc1SToomas Soome 	}
1354*f334afcfSToomas Soome 	status = OpenProtocolByHandle(loaderhandle,
1355*f334afcfSToomas Soome 	    &gEfiLoadedImageProtocolGuid, (void **)&loaded_image);
1356f9feecc1SToomas Soome 
1357f9feecc1SToomas Soome 	if (argc > 2) {
1358f9feecc1SToomas Soome 		int i, len = 0;
1359f9feecc1SToomas Soome 		CHAR16 *argp;
1360f9feecc1SToomas Soome 
1361f9feecc1SToomas Soome 		for (i = 2; i < argc; i++)
1362f9feecc1SToomas Soome 			len += strlen(argv[i]) + 1;
1363f9feecc1SToomas Soome 
1364f9feecc1SToomas Soome 		len *= sizeof (*argp);
1365083aabddSToomas Soome 		loaded_image->LoadOptions = argp = malloc(len);
1366f9feecc1SToomas Soome 		if (loaded_image->LoadOptions == NULL) {
1367f9feecc1SToomas Soome 			(void) BS->UnloadImage(loaded_image);
1368f9feecc1SToomas Soome 			return (CMD_ERROR);
1369f9feecc1SToomas Soome 		}
1370f9feecc1SToomas Soome 		loaded_image->LoadOptionsSize = len;
1371f9feecc1SToomas Soome 		for (i = 2; i < argc; i++) {
1372f9feecc1SToomas Soome 			char *ptr = argv[i];
1373f9feecc1SToomas Soome 			while (*ptr)
1374f9feecc1SToomas Soome 				*(argp++) = *(ptr++);
1375f9feecc1SToomas Soome 			*(argp++) = ' ';
1376f9feecc1SToomas Soome 		}
1377f9feecc1SToomas Soome 		*(--argv) = 0;
1378f9feecc1SToomas Soome 	}
1379f9feecc1SToomas Soome 
1380eea30b26SToomas Soome 	if (efi_getdev((void **)&dev, name, (const char **)&path) == 0) {
1381eea30b26SToomas Soome 		struct zfs_devdesc *z_dev;
1382eea30b26SToomas Soome 		struct disk_devdesc *d_dev;
1383eea30b26SToomas Soome 		pdinfo_t *hd, *pd;
1384eea30b26SToomas Soome 
1385c142ce19SToomas Soome 		switch (dev->d_dev->dv_type) {
1386eea30b26SToomas Soome 		case DEVT_ZFS:
1387eea30b26SToomas Soome 			z_dev = (struct zfs_devdesc *)dev;
1388eea30b26SToomas Soome 			loaded_image->DeviceHandle =
1389eea30b26SToomas Soome 			    efizfs_get_handle_by_guid(z_dev->pool_guid);
1390eea30b26SToomas Soome 			break;
1391eea30b26SToomas Soome 		case DEVT_NET:
1392eea30b26SToomas Soome 			loaded_image->DeviceHandle =
1393eea30b26SToomas Soome 			    efi_find_handle(dev->d_dev, dev->d_unit);
1394eea30b26SToomas Soome 			break;
1395eea30b26SToomas Soome 		default:
1396eea30b26SToomas Soome 			hd = efiblk_get_pdinfo(dev);
1397eea30b26SToomas Soome 			if (STAILQ_EMPTY(&hd->pd_part)) {
1398eea30b26SToomas Soome 				loaded_image->DeviceHandle = hd->pd_handle;
1399eea30b26SToomas Soome 				break;
1400eea30b26SToomas Soome 			}
1401eea30b26SToomas Soome 			d_dev = (struct disk_devdesc *)dev;
1402eea30b26SToomas Soome 			STAILQ_FOREACH(pd, &hd->pd_part, pd_link) {
1403eea30b26SToomas Soome 				/*
1404eea30b26SToomas Soome 				 * d_partition should be 255
1405eea30b26SToomas Soome 				 */
1406eea30b26SToomas Soome 				if (pd->pd_unit == d_dev->d_slice) {
1407eea30b26SToomas Soome 					loaded_image->DeviceHandle =
1408eea30b26SToomas Soome 					    pd->pd_handle;
1409eea30b26SToomas Soome 					break;
1410eea30b26SToomas Soome 				}
1411eea30b26SToomas Soome 			}
1412eea30b26SToomas Soome 			break;
1413eea30b26SToomas Soome 		}
1414eea30b26SToomas Soome 	}
1415f9feecc1SToomas Soome 
1416f9feecc1SToomas Soome 	dev_cleanup();
1417f9feecc1SToomas Soome 	status = BS->StartImage(loaderhandle, NULL, NULL);
1418f9feecc1SToomas Soome 	if (status != EFI_SUCCESS) {
1419f9feecc1SToomas Soome 		command_errmsg = "StartImage failed";
1420f9feecc1SToomas Soome 		free(loaded_image->LoadOptions);
1421f9feecc1SToomas Soome 		loaded_image->LoadOptions = NULL;
1422f9feecc1SToomas Soome 		status = BS->UnloadImage(loaded_image);
1423f9feecc1SToomas Soome 		return (CMD_ERROR);
1424f9feecc1SToomas Soome 	}
1425f9feecc1SToomas Soome 
1426f9feecc1SToomas Soome 	return (CMD_ERROR);	/* not reached */
1427f9feecc1SToomas Soome }
1428f9feecc1SToomas Soome 
1429f9feecc1SToomas Soome COMMAND_SET(chain, "chain", "chain load file", command_chain);
1430