xref: /freebsd-src/stand/efi/boot1/proto.c (revision 7c43148a974877188a930e4078a164f83da8e652)
1f46eb752SWarner Losh /*-
2f46eb752SWarner Losh  * Copyright (c) 1998 Robert Nordier
3f46eb752SWarner Losh  * All rights reserved.
4f46eb752SWarner Losh  * Copyright (c) 2001 Robert Drehmel
5f46eb752SWarner Losh  * All rights reserved.
6f46eb752SWarner Losh  * Copyright (c) 2014 Nathan Whitehorn
7f46eb752SWarner Losh  * All rights reserved.
8f46eb752SWarner Losh  * Copyright (c) 2015 Eric McCorkle
9f46eb752SWarner Losh  * All rights reserved.
10f46eb752SWarner Losh  *
11f46eb752SWarner Losh  * Redistribution and use in source and binary forms are freely
12f46eb752SWarner Losh  * permitted provided that the above copyright notice and this
13f46eb752SWarner Losh  * paragraph and the following disclaimer are duplicated in all
14f46eb752SWarner Losh  * such forms.
15f46eb752SWarner Losh  *
16f46eb752SWarner Losh  * This software is provided "AS IS" and without any express or
17f46eb752SWarner Losh  * implied warranties, including, without limitation, the implied
18f46eb752SWarner Losh  * warranties of merchantability and fitness for a particular
19f46eb752SWarner Losh  * purpose.
20f46eb752SWarner Losh  */
21f46eb752SWarner Losh 
22f46eb752SWarner Losh #include <sys/param.h>
23f46eb752SWarner Losh #include <machine/elf.h>
24f46eb752SWarner Losh #include <machine/stdarg.h>
25f46eb752SWarner Losh #include <stand.h>
26f46eb752SWarner Losh 
27f46eb752SWarner Losh #include <efi.h>
28f46eb752SWarner Losh #include <eficonsctl.h>
29f46eb752SWarner Losh #include <efichar.h>
30f46eb752SWarner Losh 
31f46eb752SWarner Losh #include "boot_module.h"
32f46eb752SWarner Losh #include "paths.h"
33f46eb752SWarner Losh #include "proto.h"
34f46eb752SWarner Losh 
35f46eb752SWarner Losh static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL;
36f46eb752SWarner Losh static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
37f46eb752SWarner Losh 
38*4495f368SAndrew Turner #ifndef EFI_DEBUG
39f46eb752SWarner Losh static const char *prio_str[] = {
40f46eb752SWarner Losh 	"error",
41f46eb752SWarner Losh 	"not supported",
42f46eb752SWarner Losh 	"good",
43f46eb752SWarner Losh 	"better"
44f46eb752SWarner Losh };
45*4495f368SAndrew Turner #endif
46f46eb752SWarner Losh 
47f46eb752SWarner Losh /*
48f46eb752SWarner Losh  * probe_handle determines if the passed handle represents a logical partition
49f46eb752SWarner Losh  * if it does it uses each module in order to probe it and if successful it
50f46eb752SWarner Losh  * returns EFI_SUCCESS.
51f46eb752SWarner Losh  */
52f46eb752SWarner Losh static int
probe_handle(EFI_HANDLE h,EFI_DEVICE_PATH * imgpath)53f46eb752SWarner Losh probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath)
54f46eb752SWarner Losh {
55f46eb752SWarner Losh 	dev_info_t *devinfo;
56f46eb752SWarner Losh 	EFI_BLOCK_IO *blkio;
57f46eb752SWarner Losh 	EFI_DEVICE_PATH *devpath;
58f46eb752SWarner Losh 	EFI_STATUS status;
59f46eb752SWarner Losh 	UINTN i;
60f46eb752SWarner Losh 	int preferred;
61f46eb752SWarner Losh 
62f46eb752SWarner Losh 	/* Figure out if we're dealing with an actual partition. */
63110d56cbSToomas Soome 	status = OpenProtocolByHandle(h, &DevicePathGUID, (void **)&devpath);
64f46eb752SWarner Losh 	if (status == EFI_UNSUPPORTED)
65f46eb752SWarner Losh 		return (0);
66f46eb752SWarner Losh 
67f46eb752SWarner Losh 	if (status != EFI_SUCCESS) {
68f46eb752SWarner Losh 		DPRINTF("\nFailed to query DevicePath (%lu)\n",
69f46eb752SWarner Losh 		    EFI_ERROR_CODE(status));
70f46eb752SWarner Losh 		return (-1);
71f46eb752SWarner Losh 	}
72f46eb752SWarner Losh #ifdef EFI_DEBUG
73f46eb752SWarner Losh 	{
74f46eb752SWarner Losh 		CHAR16 *text = efi_devpath_name(devpath);
75f46eb752SWarner Losh 		DPRINTF("probing: %S ", text);
76f46eb752SWarner Losh 		efi_free_devpath_name(text);
77f46eb752SWarner Losh 	}
78f46eb752SWarner Losh #endif
79110d56cbSToomas Soome 	status = OpenProtocolByHandle(h, &BlockIoProtocolGUID, (void **)&blkio);
80f46eb752SWarner Losh 	if (status == EFI_UNSUPPORTED)
81f46eb752SWarner Losh 		return (0);
82f46eb752SWarner Losh 
83f46eb752SWarner Losh 	if (status != EFI_SUCCESS) {
84f46eb752SWarner Losh 		DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n",
85f46eb752SWarner Losh 		    EFI_ERROR_CODE(status));
86f46eb752SWarner Losh 		return (-1);
87f46eb752SWarner Losh 	}
88f46eb752SWarner Losh 
89f46eb752SWarner Losh 	if (!blkio->Media->LogicalPartition)
90f46eb752SWarner Losh 		return (0);
91f46eb752SWarner Losh 
92f46eb752SWarner Losh 	preferred = efi_devpath_same_disk(imgpath, devpath);
93f46eb752SWarner Losh 
94f46eb752SWarner Losh 	/* Run through each module, see if it can load this partition */
95f46eb752SWarner Losh 	devinfo = malloc(sizeof(*devinfo));
96f46eb752SWarner Losh 	if (devinfo == NULL) {
97f46eb752SWarner Losh 		DPRINTF("\nFailed to allocate devinfo\n");
98f46eb752SWarner Losh 		return (-1);
99f46eb752SWarner Losh 	}
100f46eb752SWarner Losh 	devinfo->dev = blkio;
101f46eb752SWarner Losh 	devinfo->devpath = devpath;
102f46eb752SWarner Losh 	devinfo->devhandle = h;
103f46eb752SWarner Losh 	devinfo->preferred = preferred;
104f46eb752SWarner Losh 	devinfo->next = NULL;
105f46eb752SWarner Losh 
106f46eb752SWarner Losh 	for (i = 0; i < num_boot_modules; i++) {
107f46eb752SWarner Losh 		devinfo->devdata = NULL;
108f46eb752SWarner Losh 
109f46eb752SWarner Losh 		status = boot_modules[i]->probe(devinfo);
110f46eb752SWarner Losh 		if (status == EFI_SUCCESS)
111f46eb752SWarner Losh 			return (preferred + 1);
112f46eb752SWarner Losh 	}
113f46eb752SWarner Losh 	free(devinfo);
114f46eb752SWarner Losh 
115f46eb752SWarner Losh 	return (0);
116f46eb752SWarner Losh }
117f46eb752SWarner Losh 
118f46eb752SWarner Losh /*
119f46eb752SWarner Losh  * load_loader attempts to load the loader image data.
120f46eb752SWarner Losh  *
121f46eb752SWarner Losh  * It tries each module and its respective devices, identified by mod->probe,
122f46eb752SWarner Losh  * in order until a successful load occurs at which point it returns EFI_SUCCESS
123f46eb752SWarner Losh  * and EFI_NOT_FOUND otherwise.
124f46eb752SWarner Losh  *
125f46eb752SWarner Losh  * Only devices which have preferred matching the preferred parameter are tried.
126f46eb752SWarner Losh  */
127f46eb752SWarner Losh static EFI_STATUS
load_loader(const boot_module_t ** modp,dev_info_t ** devinfop,void ** bufp,size_t * bufsize,int preferred)128f46eb752SWarner Losh load_loader(const boot_module_t **modp, dev_info_t **devinfop, void **bufp,
129f46eb752SWarner Losh     size_t *bufsize, int preferred)
130f46eb752SWarner Losh {
131f46eb752SWarner Losh 	UINTN i;
132f46eb752SWarner Losh 	dev_info_t *dev;
133f46eb752SWarner Losh 	const boot_module_t *mod;
134f46eb752SWarner Losh 
135f46eb752SWarner Losh 	for (i = 0; i < num_boot_modules; i++) {
136f46eb752SWarner Losh 		mod = boot_modules[i];
137f46eb752SWarner Losh 		for (dev = mod->devices(); dev != NULL; dev = dev->next) {
138f46eb752SWarner Losh 			if (dev->preferred != preferred)
139f46eb752SWarner Losh 				continue;
140f46eb752SWarner Losh 
141f46eb752SWarner Losh 			if (mod->load(PATH_LOADER_EFI, dev, bufp, bufsize) ==
142f46eb752SWarner Losh 			    EFI_SUCCESS) {
143f46eb752SWarner Losh 				*devinfop = dev;
144f46eb752SWarner Losh 				*modp = mod;
145f46eb752SWarner Losh 				return (EFI_SUCCESS);
146f46eb752SWarner Losh 			}
147f46eb752SWarner Losh 		}
148f46eb752SWarner Losh 	}
149f46eb752SWarner Losh 
150f46eb752SWarner Losh 	return (EFI_NOT_FOUND);
151f46eb752SWarner Losh }
152f46eb752SWarner Losh 
153f46eb752SWarner Losh void
choice_protocol(EFI_HANDLE * handles,UINTN nhandles,EFI_DEVICE_PATH * imgpath)154f46eb752SWarner Losh choice_protocol(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath)
155f46eb752SWarner Losh {
156f46eb752SWarner Losh 	UINT16 boot_current;
157f46eb752SWarner Losh 	size_t sz;
158f46eb752SWarner Losh 	UINT16 boot_order[100];
159f46eb752SWarner Losh 	unsigned i;
160f46eb752SWarner Losh 	int rv;
161f46eb752SWarner Losh 	EFI_STATUS status;
162f46eb752SWarner Losh 	const boot_module_t *mod;
163f46eb752SWarner Losh 	dev_info_t *dev;
164f46eb752SWarner Losh 	void *loaderbuf;
165f46eb752SWarner Losh 	size_t loadersize;
166f46eb752SWarner Losh 
167f46eb752SWarner Losh 	/* Report UEFI Boot Manager Protocol details */
168f46eb752SWarner Losh 	boot_current = 0;
169f46eb752SWarner Losh 	sz = sizeof(boot_current);
170f46eb752SWarner Losh 	if (efi_global_getenv("BootCurrent", &boot_current, &sz) == EFI_SUCCESS) {
171f46eb752SWarner Losh 		printf("   BootCurrent: %04x\n", boot_current);
172f46eb752SWarner Losh 
173f46eb752SWarner Losh 		sz = sizeof(boot_order);
174f46eb752SWarner Losh 		if (efi_global_getenv("BootOrder", &boot_order, &sz) == EFI_SUCCESS) {
175f46eb752SWarner Losh 			printf("   BootOrder:");
176f46eb752SWarner Losh 			for (i = 0; i < sz / sizeof(boot_order[0]); i++)
177f46eb752SWarner Losh 				printf(" %04x%s", boot_order[i],
178f46eb752SWarner Losh 				    boot_order[i] == boot_current ? "[*]" : "");
179f46eb752SWarner Losh 			printf("\n");
180f46eb752SWarner Losh 		}
181f46eb752SWarner Losh 	}
182f46eb752SWarner Losh 
183f46eb752SWarner Losh #ifdef TEST_FAILURE
184f46eb752SWarner Losh 	/*
185f46eb752SWarner Losh 	 * For testing failover scenarios, it's nice to be able to fail fast.
186f46eb752SWarner Losh 	 * Define TEST_FAILURE to create a boot1.efi that always fails after
187f46eb752SWarner Losh 	 * reporting the boot manager protocol details.
188f46eb752SWarner Losh 	 */
189f46eb752SWarner Losh 	BS->Exit(IH, EFI_OUT_OF_RESOURCES, 0, NULL);
190f46eb752SWarner Losh #endif
191f46eb752SWarner Losh 
192f46eb752SWarner Losh 	/* Scan all partitions, probing with all modules. */
193f46eb752SWarner Losh 	printf("   Probing %zu block devices...", nhandles);
194f46eb752SWarner Losh 	DPRINTF("\n");
195f46eb752SWarner Losh 	for (i = 0; i < nhandles; i++) {
196f46eb752SWarner Losh 		rv = probe_handle(handles[i], imgpath);
197f46eb752SWarner Losh #ifdef EFI_DEBUG
198f46eb752SWarner Losh 		printf("%c", "x.+*"[rv + 1]);
199f46eb752SWarner Losh #else
200f46eb752SWarner Losh 		printf("%s\n", prio_str[rv + 1]);
201f46eb752SWarner Losh #endif
202f46eb752SWarner Losh 	}
203f46eb752SWarner Losh 	printf(" done\n");
204f46eb752SWarner Losh 
205f46eb752SWarner Losh 
206f46eb752SWarner Losh 	/* Status summary. */
207f46eb752SWarner Losh 	for (i = 0; i < num_boot_modules; i++) {
208f46eb752SWarner Losh 		printf("    ");
209f46eb752SWarner Losh 		boot_modules[i]->status();
210f46eb752SWarner Losh 	}
211f46eb752SWarner Losh 
212f46eb752SWarner Losh 	status = load_loader(&mod, &dev, &loaderbuf, &loadersize, 1);
213f46eb752SWarner Losh 	if (status != EFI_SUCCESS) {
214f46eb752SWarner Losh 		status = load_loader(&mod, &dev, &loaderbuf, &loadersize, 0);
215f46eb752SWarner Losh 		if (status != EFI_SUCCESS) {
216f46eb752SWarner Losh 			printf("Failed to load '%s'\n", PATH_LOADER_EFI);
217f46eb752SWarner Losh 			return;
218f46eb752SWarner Losh 		}
219f46eb752SWarner Losh 	}
220f46eb752SWarner Losh 
221f46eb752SWarner Losh 	try_boot(mod, dev, loaderbuf, loadersize);
222f46eb752SWarner Losh }
223