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