Lines 36-41
Link Here
|
36 |
|
36 |
|
37 |
static void efi_panic(EFI_STATUS s, const char *fmt, ...) __dead2 __printflike(2, 3); |
37 |
static void efi_panic(EFI_STATUS s, const char *fmt, ...) __dead2 __printflike(2, 3); |
38 |
|
38 |
|
|
|
39 |
static EFI_STATUS try_boot(const boot_module_t *mod, dev_info_t *dev, void *loaderbuf, size_t loadersize); |
40 |
|
39 |
static const boot_module_t *boot_modules[] = |
41 |
static const boot_module_t *boot_modules[] = |
40 |
{ |
42 |
{ |
41 |
#ifdef EFI_ZFS_BOOT |
43 |
#ifdef EFI_ZFS_BOOT |
Lines 79-104
Link Here
|
79 |
} |
81 |
} |
80 |
|
82 |
|
81 |
/* |
83 |
/* |
82 |
* nodes_match returns TRUE if the imgpath isn't NULL and the nodes match, |
|
|
83 |
* FALSE otherwise. |
84 |
*/ |
85 |
static BOOLEAN |
86 |
nodes_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath) |
87 |
{ |
88 |
size_t len; |
89 |
|
90 |
if (imgpath == NULL || imgpath->Type != devpath->Type || |
91 |
imgpath->SubType != devpath->SubType) |
92 |
return (FALSE); |
93 |
|
94 |
len = DevicePathNodeLength(imgpath); |
95 |
if (len != DevicePathNodeLength(devpath)) |
96 |
return (FALSE); |
97 |
|
98 |
return (memcmp(imgpath, devpath, (size_t)len) == 0); |
99 |
} |
100 |
|
101 |
/* |
102 |
* device_paths_match returns TRUE if the imgpath isn't NULL and all nodes |
84 |
* device_paths_match returns TRUE if the imgpath isn't NULL and all nodes |
103 |
* in imgpath and devpath match up to their respective occurrences of a |
85 |
* in imgpath and devpath match up to their respective occurrences of a |
104 |
* media node, FALSE otherwise. |
86 |
* media node, FALSE otherwise. |
Lines 115-121
Link Here
|
115 |
IsDevicePathType(devpath, MEDIA_DEVICE_PATH)) |
97 |
IsDevicePathType(devpath, MEDIA_DEVICE_PATH)) |
116 |
return (TRUE); |
98 |
return (TRUE); |
117 |
|
99 |
|
118 |
if (!nodes_match(imgpath, devpath)) |
100 |
if (!efi_devpath_match_node(imgpath, devpath)) |
119 |
return (FALSE); |
101 |
return (FALSE); |
120 |
|
102 |
|
121 |
imgpath = NextDevicePathNode(imgpath); |
103 |
imgpath = NextDevicePathNode(imgpath); |
Lines 125-144
Link Here
|
125 |
return (FALSE); |
107 |
return (FALSE); |
126 |
} |
108 |
} |
127 |
|
109 |
|
|
|
110 |
/* quick and dirty work for boot partition menu */ |
111 |
|
112 |
#define NUM_DEV_LIST 35 |
113 |
#define SELECT_TIMEOUT 10 |
114 |
|
115 |
typedef struct { |
116 |
const boot_module_t *modp; |
117 |
dev_info_t *devinfop; |
118 |
} moddev_t; |
119 |
|
120 |
static moddev_t dev_list[NUM_DEV_LIST]; |
121 |
|
122 |
static char * |
123 |
humanize_number_kmgt(UINT64 size) { |
124 |
static char buf2[6]; |
125 |
UINT64 size0; |
126 |
|
127 |
if (size < 1024) |
128 |
sprintf(buf2, "%luB", size); |
129 |
else if (size0 = (size*10+512)/1024, size0 < 100) |
130 |
sprintf(buf2, "%lu.%luK", size0 / 10, size0 % 10); |
131 |
else if (size0 = (size+512)/1024, size0 < 1024) |
132 |
sprintf(buf2, "%luK", size0); |
133 |
else if (size0 = (size*10/1024+512)/1024, size0 < 100) |
134 |
sprintf(buf2, "%lu.%luM", size0 / 10, size0 % 10); |
135 |
else if (size0 = (size/1024+512)/1024, size0 < 1024) |
136 |
sprintf(buf2, "%luM", size0); |
137 |
else if (size0 = (size*10/1024/1024+512)/1024, size0 < 100) |
138 |
sprintf(buf2, "%lu.%luG", size0 / 10, size0 % 10); |
139 |
else if (size0 = (size/1024/1024+512)/1024, size0 < 1024) |
140 |
sprintf(buf2, "%luG", size0); |
141 |
else if (size0 = (size*10/1024/1024/1024+512)/1024, size0 < 100) |
142 |
sprintf(buf2, "%lu.%luT", size0 / 10, size0 % 10); |
143 |
else |
144 |
sprintf(buf2, "%luT", (size/1024/1024/1024+512)/1024); |
145 |
return(buf2); |
146 |
} |
147 |
|
148 |
static char |
149 |
idx2char(int i) |
150 |
{ |
151 |
return (i<10 ? '0'+i : 'a'+i-10); |
152 |
} |
153 |
|
154 |
static int |
155 |
char2idx(char c) |
156 |
{ |
157 |
return ((c >= '0' && c <= '9') ? c - '0' : |
158 |
(c >= 'A' && c <= 'Z') ? c - 'A' + 10 : |
159 |
(c >= 'a' && c <= 'z') ? c - 'a' + 10 : |
160 |
-1 ); |
161 |
} |
162 |
|
163 |
static void |
164 |
move_to_tol() |
165 |
{ |
166 |
SIMPLE_TEXT_OUTPUT_INTERFACE *conout; |
167 |
int x,y; |
168 |
|
169 |
conout = ST->ConOut; |
170 |
x = conout->Mode->CursorColumn; |
171 |
y = conout->Mode->CursorRow; |
172 |
conout->SetCursorPosition(conout, 0, y); |
173 |
} |
174 |
|
175 |
int |
176 |
getchar(void) |
177 |
{ |
178 |
EFI_INPUT_KEY key; |
179 |
EFI_STATUS status; |
180 |
UINTN junk; |
181 |
|
182 |
/* Try to read a key stroke. We wait for one if none is pending. */ |
183 |
status = ST->ConIn->ReadKeyStroke(ST->ConIn, &key); |
184 |
if (status == EFI_NOT_READY) { |
185 |
BS->WaitForEvent(1, &ST->ConIn->WaitForKey, &junk); |
186 |
status = ST->ConIn->ReadKeyStroke(ST->ConIn, &key); |
187 |
} |
188 |
switch (key.ScanCode) { |
189 |
case 0x17: /* ESC */ |
190 |
return (0x1b); /* esc */ |
191 |
} |
192 |
|
193 |
/* this can return */ |
194 |
return (key.UnicodeChar); |
195 |
} |
196 |
|
128 |
/* |
197 |
/* |
129 |
* devpath_last returns the last non-path end node in devpath. |
198 |
* Output of EFI_DEVICE_PATH_TO_TEXT_PROTOCOL is not easy to read. |
|
|
199 |
" I hope this looks better. |
130 |
*/ |
200 |
*/ |
131 |
static EFI_DEVICE_PATH * |
201 |
static int |
132 |
devpath_last(EFI_DEVICE_PATH *devpath) |
202 |
mediapath_node_str(char *buf, size_t size, EFI_DEVICE_PATH *devpath) |
133 |
{ |
203 |
{ |
|
|
204 |
switch (devpath->Type) { |
205 |
case MEDIA_DEVICE_PATH: |
206 |
switch (devpath->SubType) { |
207 |
case MEDIA_CDROM_DP: { |
208 |
CDROM_DEVICE_PATH *cdrom; |
134 |
|
209 |
|
135 |
while (!IsDevicePathEnd(NextDevicePathNode(devpath))) |
210 |
cdrom = (CDROM_DEVICE_PATH *)(void *)devpath; |
136 |
devpath = NextDevicePathNode(devpath); |
211 |
return snprintf(buf, size, "cdrom(%x)", |
|
|
212 |
cdrom->BootEntry); |
213 |
} |
214 |
case MEDIA_HARDDRIVE_DP: { |
215 |
HARDDRIVE_DEVICE_PATH *hd; |
137 |
|
216 |
|
138 |
return (devpath); |
217 |
hd = (HARDDRIVE_DEVICE_PATH *)(void *)devpath; |
|
|
218 |
return snprintf(buf, size, "hd(p%d) (%s)", |
219 |
hd->PartitionNumber, |
220 |
humanize_number_kmgt(hd->PartitionSize * 512)); |
221 |
} |
222 |
default: |
223 |
return snprintf(buf, size, "media(0x%02x)", |
224 |
devpath->SubType); |
225 |
} |
226 |
} |
227 |
|
228 |
return snprintf(buf, size, "type(0x%02x, 0x%02x)", devpath->Type, |
229 |
devpath->SubType); |
139 |
} |
230 |
} |
140 |
|
231 |
|
141 |
/* |
232 |
/* |
|
|
233 |
* mediapath_str is convenience method which returns the text description of |
234 |
* mediapath using a static buffer, so it isn't thread safe! |
235 |
*/ |
236 |
char * |
237 |
mediapath_str(EFI_DEVICE_PATH *devpath) |
238 |
{ |
239 |
static char buf[256]; |
240 |
|
241 |
mediapath_node_str(buf, sizeof(buf), devpath); |
242 |
|
243 |
return buf; |
244 |
} |
245 |
|
246 |
static int |
247 |
list_devices(void) |
248 |
{ |
249 |
UINTN i, j; |
250 |
dev_info_t *dev; |
251 |
const boot_module_t *mod; |
252 |
|
253 |
j = 0; |
254 |
for (i = 0; i < NUM_BOOT_MODULES; i++) { |
255 |
if (boot_modules[i] == NULL) |
256 |
continue; |
257 |
mod = boot_modules[i]; |
258 |
for (dev = mod->devices(); dev != NULL; dev = dev->next) { |
259 |
dev_list[j].devinfop = dev; |
260 |
dev_list[j].modp = mod; |
261 |
j++; |
262 |
if (j >= NUM_DEV_LIST) |
263 |
break; |
264 |
} |
265 |
} |
266 |
|
267 |
return (j); |
268 |
} |
269 |
|
270 |
|
271 |
static int |
272 |
get_sel(int ndevs,int time_left) |
273 |
{ |
274 |
int i; |
275 |
int c, n; |
276 |
EFI_STATUS status; |
277 |
EFI_EVENT timer; |
278 |
EFI_EVENT events[2]; |
279 |
EFI_DEVICE_PATH *mediapath; |
280 |
UINTN idx; |
281 |
dev_info_t *dev; |
282 |
CHAR16 *text; |
283 |
|
284 |
status = BS->CreateEvent(EVT_TIMER, 0, 0, NULL, &timer); |
285 |
if (status != EFI_SUCCESS) { |
286 |
printf("Can't allocate timer event.\n"); |
287 |
return (-1); |
288 |
} |
289 |
printf(" 0: AutoSelected Partition (Default): 0\n"); |
290 |
for (i = 0; i < ndevs; i++) { |
291 |
if (dev_list[i].devinfop->preferred == TRUE) |
292 |
c = '*'; |
293 |
else |
294 |
c = ' '; |
295 |
mediapath = efi_devpath_to_media_path( |
296 |
dev_list[i].devinfop->devpath); |
297 |
printf(" %c%c: %s: %s: %c\n", c, idx2char(i+1), |
298 |
dev_list[i].modp->name, |
299 |
mediapath_str(mediapath), |
300 |
idx2char(i+1)); |
301 |
} |
302 |
/* One alpha-num selection only. Is this big enough ?? */ |
303 |
BS->SetTimer(timer, TimerPeriodic, 10000000); |
304 |
events[0] = timer; |
305 |
events[1] = ST->ConIn->WaitForKey; |
306 |
|
307 |
while (1) { |
308 |
if (time_left > 0) { |
309 |
printf("Select from 0 to %c. Timeout in %2d seconds," |
310 |
" [Space] to pause : ", |
311 |
idx2char(ndevs), time_left); |
312 |
} |
313 |
status = BS->WaitForEvent(2, events, &idx); |
314 |
if (status != EFI_SUCCESS) { |
315 |
BS->CloseEvent(timer); |
316 |
return (-1); |
317 |
} |
318 |
if (idx == 0) { |
319 |
time_left--; |
320 |
if (time_left <=0) { |
321 |
printf("\nTimeout. " |
322 |
"Partition is AutoSelected.\n"); |
323 |
n = 0; |
324 |
break; |
325 |
} else { |
326 |
move_to_tol(); |
327 |
} |
328 |
} |
329 |
if (idx == 1) { |
330 |
c = getchar(); |
331 |
if ((c == '\n') || (c == '\r')) { |
332 |
putchar('\n'); |
333 |
n = 0; |
334 |
break; |
335 |
} else if ((time_left > 0) && (c == ' ')) { |
336 |
BS->SetTimer(timer, TimerCancel, 0); |
337 |
time_left = -1; |
338 |
printf("\nTimer stopeed.\n Please Key in: "); |
339 |
} |
340 |
n = char2idx(c); |
341 |
if ((n >= 0) && (n <= ndevs)) { |
342 |
BS->SetTimer(timer, TimerCancel, 0); |
343 |
time_left = -1; |
344 |
printf("\n %c is selected.\n", c); |
345 |
break; |
346 |
} else { |
347 |
/* Invalid charecter. */ |
348 |
move_to_tol(); |
349 |
} |
350 |
} |
351 |
}; |
352 |
BS->CloseEvent(timer); |
353 |
return (n); |
354 |
} |
355 |
|
356 |
/* |
142 |
* load_loader attempts to load the loader image data. |
357 |
* load_loader attempts to load the loader image data. |
143 |
* |
358 |
* |
144 |
* It tries each module and its respective devices, identified by mod->probe, |
359 |
* It tries each module and its respective devices, identified by mod->probe, |
Lines 173-204
Link Here
|
173 |
return (EFI_NOT_FOUND); |
388 |
return (EFI_NOT_FOUND); |
174 |
} |
389 |
} |
175 |
|
390 |
|
|
|
391 |
static EFI_STATUS |
392 |
select_loader(const boot_module_t **modp, dev_info_t **devinfop, |
393 |
void **bufp, size_t *bufsize) |
394 |
{ |
395 |
int n; |
396 |
int time_left; |
397 |
EFI_STATUS status; |
398 |
dev_info_t *dev; |
399 |
const boot_module_t *mod; |
400 |
int ndevs; |
401 |
|
402 |
ndevs = list_devices(); |
403 |
if ((ndevs <= 0) || (ndevs >= NUM_DEV_LIST)) { |
404 |
return (EFI_NOT_FOUND); |
405 |
} else if (ndevs == 1) { |
406 |
/* Only one condidate. */ |
407 |
*modp = mod = dev_list[0].modp; |
408 |
*devinfop = dev = dev_list[0].devinfop; |
409 |
return (mod->load(PATH_LOADER_EFI, dev,bufp, bufsize)); |
410 |
} |
411 |
|
412 |
/* Two or more candidate exist. */ |
413 |
|
414 |
/* Reset the console input. */ |
415 |
ST->ConIn->Reset(ST->ConIn, TRUE); |
416 |
|
417 |
time_left = SELECT_TIMEOUT; |
418 |
while (1) { |
419 |
n = get_sel(ndevs, time_left); |
420 |
if (n < 0) { |
421 |
return (EFI_NOT_FOUND); |
422 |
} else if (n == 0) { /* AutoSelect */ |
423 |
break; |
424 |
} else { |
425 |
mod = dev_list[n-1].modp; |
426 |
dev = dev_list[n-1].devinfop; |
427 |
status = mod->load(PATH_LOADER_EFI,dev, bufp, bufsize); |
428 |
if (status == EFI_SUCCESS) { |
429 |
break; |
430 |
} |
431 |
printf("Failed to load '%s'\n", PATH_LOADER_EFI); |
432 |
printf("Please select again: "); |
433 |
time_left = -1; |
434 |
} |
435 |
} |
436 |
if (n == 0) { /* AutoSelect */ |
437 |
status = load_loader(modp, devinfop, bufp, bufsize, TRUE); |
438 |
if (status != EFI_SUCCESS) { |
439 |
status = load_loader(modp, devinfop, bufp, bufsize, FALSE); |
440 |
} |
441 |
} else { |
442 |
*modp = dev_list[n-1].modp; |
443 |
*devinfop = dev_list[n-1].devinfop; |
444 |
status = EFI_SUCCESS; |
445 |
} |
446 |
return (status); |
447 |
} |
448 |
|
449 |
void |
450 |
sel_boot(void) |
451 |
{ |
452 |
size_t loadersize; |
453 |
void *loaderbuf = NULL; |
454 |
dev_info_t *dev; |
455 |
const boot_module_t *mod; |
456 |
EFI_STATUS status; |
457 |
|
458 |
status = select_loader(&mod, &dev, &loaderbuf, &loadersize); |
459 |
if (status != EFI_SUCCESS) { |
460 |
printf("Failed to load '%s'\n", PATH_LOADER_EFI); |
461 |
return; |
462 |
} |
463 |
|
464 |
try_boot(mod, dev, loaderbuf, loadersize); |
465 |
} |
466 |
|
176 |
/* |
467 |
/* |
177 |
* try_boot only returns if it fails to load the loader. If it succeeds |
468 |
* try_boot only returns if it fails to load the loader. If it succeeds |
178 |
* it simply boots, otherwise it returns the status of last EFI call. |
469 |
* it simply boots, otherwise it returns the status of last EFI call. |
179 |
*/ |
470 |
*/ |
180 |
static EFI_STATUS |
471 |
EFI_STATUS |
181 |
try_boot(void) |
472 |
try_boot(const boot_module_t *mod, dev_info_t *dev, void *loaderbuf, size_t loadersize) |
182 |
{ |
473 |
{ |
183 |
size_t bufsize, loadersize, cmdsize; |
474 |
size_t bufsize, cmdsize; |
184 |
void *buf, *loaderbuf; |
475 |
void *buf; |
185 |
char *cmd; |
476 |
char *cmd; |
186 |
dev_info_t *dev; |
|
|
187 |
const boot_module_t *mod; |
188 |
EFI_HANDLE loaderhandle; |
477 |
EFI_HANDLE loaderhandle; |
189 |
EFI_LOADED_IMAGE *loaded_image; |
478 |
EFI_LOADED_IMAGE *loaded_image; |
190 |
EFI_STATUS status; |
479 |
EFI_STATUS status; |
191 |
|
480 |
|
192 |
status = load_loader(&mod, &dev, &loaderbuf, &loadersize, TRUE); |
|
|
193 |
if (status != EFI_SUCCESS) { |
194 |
status = load_loader(&mod, &dev, &loaderbuf, &loadersize, |
195 |
FALSE); |
196 |
if (status != EFI_SUCCESS) { |
197 |
printf("Failed to load '%s'\n", PATH_LOADER_EFI); |
198 |
return (status); |
199 |
} |
200 |
} |
201 |
|
202 |
/* |
481 |
/* |
203 |
* Read in and parse the command line from /boot.config or /boot/config, |
482 |
* Read in and parse the command line from /boot.config or /boot/config, |
204 |
* if present. We'll pass it the next stage via a simple ASCII |
483 |
* if present. We'll pass it the next stage via a simple ASCII |
Lines 225-231
Link Here
|
225 |
buf = NULL; |
504 |
buf = NULL; |
226 |
} |
505 |
} |
227 |
|
506 |
|
228 |
if ((status = BS->LoadImage(TRUE, IH, devpath_last(dev->devpath), |
507 |
if ((status = BS->LoadImage(TRUE, IH, efi_devpath_last_node(dev->devpath), |
229 |
loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) { |
508 |
loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) { |
230 |
printf("Failed to load image provided by %s, size: %zu, (%lu)\n", |
509 |
printf("Failed to load image provided by %s, size: %zu, (%lu)\n", |
231 |
mod->name, loadersize, EFI_ERROR_CODE(status)); |
510 |
mod->name, loadersize, EFI_ERROR_CODE(status)); |
Lines 522-528
Link Here
|
522 |
boot_modules[i]->status(); |
801 |
boot_modules[i]->status(); |
523 |
} |
802 |
} |
524 |
|
803 |
|
525 |
try_boot(); |
804 |
sel_boot(); |
526 |
|
805 |
|
527 |
/* If we get here, we're out of luck... */ |
806 |
/* If we get here, we're out of luck... */ |
528 |
efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!"); |
807 |
efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!"); |