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