Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/efi/boot1/boot1.c
34860 views
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 void efi_panic(EFI_STATUS s, const char *fmt, ...) __dead2 __printflike(2, 3);
38
39
const boot_module_t *boot_modules[] =
40
{
41
#ifdef EFI_ZFS_BOOT
42
&zfs_module,
43
#endif
44
#ifdef EFI_UFS_BOOT
45
&ufs_module
46
#endif
47
};
48
const UINTN num_boot_modules = nitems(boot_modules);
49
50
static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL;
51
static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
52
static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
53
static EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
54
55
static EFI_PHYSICAL_ADDRESS heap;
56
static UINTN heapsize;
57
58
/*
59
* try_boot only returns if it fails to load the loader. If it succeeds
60
* it simply boots, otherwise it returns the status of last EFI call.
61
*/
62
EFI_STATUS
63
try_boot(const boot_module_t *mod, dev_info_t *dev, void *loaderbuf, size_t loadersize)
64
{
65
size_t bufsize, cmdsize;
66
void *buf;
67
char *cmd;
68
EFI_HANDLE loaderhandle;
69
EFI_LOADED_IMAGE *loaded_image;
70
EFI_STATUS status;
71
72
/*
73
* Read in and parse the command line from /boot.config or /boot/config,
74
* if present. We'll pass it the next stage via a simple ASCII
75
* string. loader.efi has a hack for ASCII strings, so we'll use that to
76
* keep the size down here. We only try to read the alternate file if
77
* we get EFI_NOT_FOUND because all other errors mean that the boot_module
78
* had troubles with the filesystem. We could return early, but we'll let
79
* loading the actual kernel sort all that out. Since these files are
80
* optional, we don't report errors in trying to read them.
81
*/
82
cmd = NULL;
83
cmdsize = 0;
84
status = mod->load(PATH_DOTCONFIG, dev, &buf, &bufsize);
85
if (status == EFI_NOT_FOUND)
86
status = mod->load(PATH_CONFIG, dev, &buf, &bufsize);
87
if (status == EFI_SUCCESS) {
88
cmdsize = bufsize + 1;
89
cmd = malloc(cmdsize);
90
if (cmd == NULL)
91
goto errout;
92
memcpy(cmd, buf, bufsize);
93
cmd[bufsize] = '\0';
94
free(buf);
95
buf = NULL;
96
}
97
98
/*
99
* See if there's any env variables the module wants to set. If so,
100
* append it to any config present.
101
*/
102
if (mod->extra_env != NULL) {
103
const char *env = mod->extra_env();
104
if (env != NULL) {
105
size_t newlen = cmdsize + strlen(env) + 1;
106
107
cmd = realloc(cmd, newlen);
108
if (cmd == NULL)
109
goto errout;
110
if (cmdsize > 0)
111
strlcat(cmd, " ", newlen);
112
strlcat(cmd, env, newlen);
113
cmdsize = strlen(cmd);
114
free(__DECONST(char *, env));
115
}
116
}
117
118
if ((status = BS->LoadImage(TRUE, IH, efi_devpath_last_node(dev->devpath),
119
loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) {
120
printf("Failed to load image provided by %s, size: %zu, (%lu)\n",
121
mod->name, loadersize, EFI_ERROR_CODE(status));
122
goto errout;
123
}
124
125
status = OpenProtocolByHandle(loaderhandle, &LoadedImageGUID,
126
(void **)&loaded_image);
127
if (status != EFI_SUCCESS) {
128
printf("Failed to query LoadedImage provided by %s (%lu)\n",
129
mod->name, EFI_ERROR_CODE(status));
130
goto errout;
131
}
132
133
if (cmd != NULL)
134
printf(" command args: %s\n", cmd);
135
136
loaded_image->DeviceHandle = dev->devhandle;
137
loaded_image->LoadOptionsSize = cmdsize;
138
loaded_image->LoadOptions = cmd;
139
140
DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI);
141
DSTALL(1000000);
142
DPRINTF(".");
143
DSTALL(1000000);
144
DPRINTF(".");
145
DSTALL(1000000);
146
DPRINTF(".");
147
DSTALL(1000000);
148
DPRINTF(".");
149
DSTALL(1000000);
150
DPRINTF(".\n");
151
152
if ((status = BS->StartImage(loaderhandle, NULL, NULL)) !=
153
EFI_SUCCESS) {
154
printf("Failed to start image provided by %s (%lu)\n",
155
mod->name, EFI_ERROR_CODE(status));
156
loaded_image->LoadOptionsSize = 0;
157
loaded_image->LoadOptions = NULL;
158
}
159
160
errout:
161
if (cmd != NULL)
162
free(cmd);
163
if (buf != NULL)
164
free(buf);
165
if (loaderbuf != NULL)
166
free(loaderbuf);
167
168
return (status);
169
}
170
171
EFI_STATUS
172
efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab)
173
{
174
EFI_HANDLE *handles;
175
EFI_LOADED_IMAGE *img;
176
EFI_DEVICE_PATH *imgpath;
177
EFI_STATUS status;
178
EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
179
SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL;
180
UINTN i, hsize, nhandles;
181
CHAR16 *text;
182
183
/* Basic initialization*/
184
ST = Xsystab;
185
IH = Ximage;
186
BS = ST->BootServices;
187
RS = ST->RuntimeServices;
188
189
heapsize = 64 * 1024 * 1024;
190
status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
191
EFI_SIZE_TO_PAGES(heapsize), &heap);
192
if (status != EFI_SUCCESS) {
193
ST->ConOut->OutputString(ST->ConOut,
194
__DECONST(CHAR16 *,
195
L"Failed to allocate memory for heap.\r\n"));
196
BS->Exit(IH, status, 0, NULL);
197
}
198
199
setheap((void *)(uintptr_t)heap, (void *)(uintptr_t)(heap + heapsize));
200
201
/* Set up the console, so printf works. */
202
status = BS->LocateProtocol(&ConsoleControlGUID, NULL,
203
(VOID **)&ConsoleControl);
204
if (status == EFI_SUCCESS)
205
(void)ConsoleControl->SetMode(ConsoleControl,
206
EfiConsoleControlScreenText);
207
/*
208
* Reset the console enable the cursor. Later we'll choose a better
209
* console size through GOP/UGA.
210
*/
211
conout = ST->ConOut;
212
conout->Reset(conout, TRUE);
213
/* Explicitly set conout to mode 0, 80x25 */
214
conout->SetMode(conout, 0);
215
conout->EnableCursor(conout, TRUE);
216
conout->ClearScreen(conout);
217
218
printf("\n>> FreeBSD EFI boot block\n");
219
printf(" Loader path: %s\n\n", PATH_LOADER_EFI);
220
printf(" Initializing modules:");
221
for (i = 0; i < num_boot_modules; i++) {
222
printf(" %s", boot_modules[i]->name);
223
if (boot_modules[i]->init != NULL)
224
boot_modules[i]->init();
225
}
226
putchar('\n');
227
228
/* Fetch all the block I/O handles, we have to search through them later */
229
hsize = 0;
230
BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL,
231
&hsize, NULL);
232
handles = malloc(hsize);
233
if (handles == NULL)
234
efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n",
235
hsize);
236
status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID,
237
NULL, &hsize, handles);
238
if (status != EFI_SUCCESS)
239
efi_panic(status, "Failed to get device handles\n");
240
nhandles = hsize / sizeof(*handles);
241
242
/* Determine the devpath of our image so we can prefer it. */
243
status = OpenProtocolByHandle(IH, &LoadedImageGUID, (void **)&img);
244
imgpath = NULL;
245
if (status == EFI_SUCCESS) {
246
text = efi_devpath_name(img->FilePath);
247
if (text != NULL) {
248
printf(" Load Path: %S\n", text);
249
efi_setenv_freebsd_wcs("Boot1Path", text);
250
efi_free_devpath_name(text);
251
}
252
253
status = OpenProtocolByHandle(img->DeviceHandle,
254
&DevicePathGUID, (void **)&imgpath);
255
if (status != EFI_SUCCESS) {
256
DPRINTF("Failed to get image DevicePath (%lu)\n",
257
EFI_ERROR_CODE(status));
258
} else {
259
text = efi_devpath_name(imgpath);
260
if (text != NULL) {
261
printf(" Load Device: %S\n", text);
262
efi_setenv_freebsd_wcs("Boot1Dev", text);
263
efi_free_devpath_name(text);
264
}
265
}
266
}
267
268
choice_protocol(handles, nhandles, imgpath);
269
270
/* If we get here, we're out of luck... */
271
efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!");
272
}
273
274
/*
275
* add_device adds a device to the passed devinfo list.
276
*/
277
void
278
add_device(dev_info_t **devinfop, dev_info_t *devinfo)
279
{
280
dev_info_t *dev;
281
282
if (*devinfop == NULL) {
283
*devinfop = devinfo;
284
return;
285
}
286
287
for (dev = *devinfop; dev->next != NULL; dev = dev->next)
288
;
289
290
dev->next = devinfo;
291
}
292
293
void
294
efi_exit(EFI_STATUS s)
295
{
296
297
BS->FreePages(heap, EFI_SIZE_TO_PAGES(heapsize));
298
BS->Exit(IH, s, 0, NULL);
299
}
300
301
void
302
exit(int error __unused)
303
{
304
efi_exit(EFI_LOAD_ERROR);
305
}
306
307
/*
308
* OK. We totally give up. Exit back to EFI with a sensible status so
309
* it can try the next option on the list.
310
*/
311
static void
312
efi_panic(EFI_STATUS s, const char *fmt, ...)
313
{
314
va_list ap;
315
316
printf("panic: ");
317
va_start(ap, fmt);
318
vprintf(fmt, ap);
319
va_end(ap);
320
printf("\n");
321
322
efi_exit(s);
323
}
324
325
int getchar(void)
326
{
327
return (-1);
328
}
329
330
void
331
putchar(int c)
332
{
333
CHAR16 buf[2];
334
335
if (c == '\n') {
336
buf[0] = '\r';
337
buf[1] = 0;
338
ST->ConOut->OutputString(ST->ConOut, buf);
339
}
340
buf[0] = c;
341
buf[1] = 0;
342
ST->ConOut->OutputString(ST->ConOut, buf);
343
}
344
345