1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <inttypes.h>
6 #include <limits.h>
7 #include <stdio.h>
8 #include <string.h>
9
10 #include <efi/boot-services.h>
11 #include <efi/protocol/device-path.h>
12 #include <efi/protocol/graphics-output.h>
13 #include <efi/protocol/simple-text-input.h>
14 #include <efi/system-table.h>
15
16 #include <cmdline.h>
17 #include <device_id.h>
18 #include <framebuffer.h>
19 #include <inet6.h>
20 #include <xefi.h>
21
22 #include "osboot.h"
23
24 #include <zircon/boot/netboot.h>
25
26 #define DEFAULT_TIMEOUT 3
27
28 #define KBUFSIZE (32*1024*1024)
29 #define RBUFSIZE (512 * 1024 * 1024)
30
31
32 static nbfile nbkernel;
33 static nbfile nbramdisk;
34 static nbfile nbcmdline;
35
netboot_get_buffer(const char * name,size_t size)36 nbfile* netboot_get_buffer(const char* name, size_t size) {
37 if (!strcmp(name, NB_KERNEL_FILENAME)) {
38 return &nbkernel;
39 }
40 if (!strcmp(name, NB_RAMDISK_FILENAME)) {
41 efi_physical_addr mem = 0xFFFFFFFF;
42 size_t buf_size = size > 0 ? (size + PAGE_MASK) & ~PAGE_MASK : RBUFSIZE;
43
44 if (nbramdisk.size > 0) {
45 if (nbramdisk.size < buf_size) {
46 mem = (efi_physical_addr)nbramdisk.data;
47 nbramdisk.data = 0;
48 if (gBS->FreePages(mem - FRONT_BYTES, (nbramdisk.size / PAGE_SIZE) + FRONT_PAGES)) {
49 printf("Could not free previous ramdisk allocation\n");
50 nbramdisk.size = 0;
51 return NULL;
52 }
53 nbramdisk.size = 0;
54 } else {
55 return &nbramdisk;
56 }
57 }
58
59 printf("netboot: allocating %zu for ramdisk (requested %zu)\n", buf_size, size);
60 if (gBS->AllocatePages(AllocateMaxAddress, EfiLoaderData,
61 (buf_size / PAGE_SIZE) + FRONT_PAGES, &mem)) {
62 printf("Failed to allocate network io buffer\n");
63 return NULL;
64 }
65 nbramdisk.data = (void*) (mem + FRONT_BYTES);
66 nbramdisk.size = buf_size;
67
68 return &nbramdisk;
69 }
70 if (!strcmp(name, NB_CMDLINE_FILENAME)) {
71 return &nbcmdline;
72 }
73 return NULL;
74 }
75
76 // Wait for a keypress from a set of valid keys. If 0 < timeout_s < INT_MAX, the
77 // first key in the set of valid keys will be returned after timeout_s seconds
78 // if no other valid key is pressed.
key_prompt(const char * valid_keys,int timeout_s)79 char key_prompt(const char* valid_keys, int timeout_s) {
80 if (strlen(valid_keys) < 1) return 0;
81 if (timeout_s <= 0) return valid_keys[0];
82
83 efi_event TimerEvent;
84 efi_event WaitList[2];
85
86 efi_status status;
87 size_t Index;
88 efi_input_key key;
89 memset(&key, 0, sizeof(key));
90
91 status = gBS->CreateEvent(EVT_TIMER, 0, NULL, NULL, &TimerEvent);
92 if (status != EFI_SUCCESS) {
93 printf("could not create event timer: %s\n", xefi_strerror(status));
94 return 0;
95 }
96
97 status = gBS->SetTimer(TimerEvent, TimerPeriodic, 10000000);
98 if (status != EFI_SUCCESS) {
99 printf("could not set timer: %s\n", xefi_strerror(status));
100 return 0;
101 }
102
103 size_t wait_idx = 0;
104 size_t key_idx = wait_idx;
105 WaitList[wait_idx++] = gSys->ConIn->WaitForKey;
106 size_t timer_idx = wait_idx; // timer should always be last
107 WaitList[wait_idx++] = TimerEvent;
108
109 bool cur_vis = gConOut->Mode->CursorVisible;
110 int32_t col = gConOut->Mode->CursorColumn;
111 int32_t row = gConOut->Mode->CursorRow;
112 gConOut->EnableCursor(gConOut, false);
113
114 // TODO: better event loop
115 char pressed = 0;
116 if (timeout_s < INT_MAX) {
117 printf("%-10d", timeout_s);
118 }
119 do {
120 status = gBS->WaitForEvent(wait_idx, WaitList, &Index);
121
122 // Check the timer
123 if (!EFI_ERROR(status)) {
124 if (Index == timer_idx) {
125 if (timeout_s < INT_MAX) {
126 timeout_s--;
127 gConOut->SetCursorPosition(gConOut, col, row);
128 printf("%-10d", timeout_s);
129 }
130 continue;
131 } else if (Index == key_idx) {
132 status = gSys->ConIn->ReadKeyStroke(gSys->ConIn, &key);
133 if (EFI_ERROR(status)) {
134 // clear the key and wait for another event
135 memset(&key, 0, sizeof(key));
136 } else {
137 char* which_key = strchr(valid_keys, key.UnicodeChar);
138 if (which_key) {
139 pressed = *which_key;
140 break;
141 }
142 }
143 }
144 } else {
145 printf("Error waiting for event: %s\n", xefi_strerror(status));
146 gConOut->EnableCursor(gConOut, cur_vis);
147 return 0;
148 }
149 } while (timeout_s);
150
151 gBS->CloseEvent(TimerEvent);
152 gConOut->EnableCursor(gConOut, cur_vis);
153 if (timeout_s > 0 && pressed) {
154 return pressed;
155 }
156
157 // Default to first key in list
158 return valid_keys[0];
159 }
160
do_select_fb()161 void do_select_fb() {
162 uint32_t cur_mode = get_gfx_mode();
163 uint32_t max_mode = get_gfx_max_mode();
164 while (true) {
165 printf("\n");
166 print_fb_modes();
167 printf("Choose a framebuffer mode or press (b) to return to the menu\n");
168 char key = key_prompt("b0123456789", INT_MAX);
169 if (key == 'b') break;
170 if ((uint32_t)(key - '0') >= max_mode) {
171 printf("invalid mode: %c\n", key);
172 continue;
173 }
174 set_gfx_mode(key - '0');
175 printf("Use \"bootloader.fbres=%ux%u\" to use this resolution by default\n",
176 get_gfx_hres(), get_gfx_vres());
177 printf("Press space to accept or (r) to choose again ...");
178 key = key_prompt("r ", 5);
179 if (key == ' ') {
180 return;
181 }
182 set_gfx_mode(cur_mode);
183 }
184 }
185
do_bootmenu(bool have_fb)186 void do_bootmenu(bool have_fb) {
187 const char* menukeys;
188 if (have_fb)
189 menukeys = "rfx";
190 else
191 menukeys = "rx";
192 printf(" BOOT MENU \n");
193 printf(" --------- \n");
194 if (have_fb)
195 printf(" (f) list framebuffer modes\n");
196 printf(" (r) reset\n");
197 printf(" (x) exit menu\n");
198 printf("\n");
199 char key = key_prompt(menukeys, INT_MAX);
200 switch (key) {
201 case 'f': {
202 do_select_fb();
203 break;
204 }
205 case 'r':
206 gSys->RuntimeServices->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
207 break;
208 case 'x':
209 default:
210 break;
211 }
212 }
213
214 static char cmdbuf[CMDLINE_MAX];
print_cmdline(void)215 void print_cmdline(void) {
216 cmdline_to_string(cmdbuf, sizeof(cmdbuf));
217 printf("cmdline: %s\n", cmdbuf);
218 }
219
220 static char netboot_cmdline[CMDLINE_MAX];
do_netboot()221 void do_netboot() {
222 efi_physical_addr mem = 0xFFFFFFFF;
223 if (gBS->AllocatePages(AllocateMaxAddress, EfiLoaderData, KBUFSIZE / 4096, &mem)) {
224 printf("Failed to allocate network io buffer\n");
225 return;
226 }
227 nbkernel.data = (void*) mem;
228 nbkernel.size = KBUFSIZE;
229
230 // ramdisk is dynamically allocated now
231 nbramdisk.data = 0;
232 nbramdisk.size = 0;
233
234 nbcmdline.data = (void*) netboot_cmdline;
235 nbcmdline.size = sizeof(netboot_cmdline);
236 nbcmdline.offset = 0;
237
238 printf("\nNetBoot Server Started...\n\n");
239 efi_tpl prev_tpl = gBS->RaiseTPL(TPL_NOTIFY);
240 while (true) {
241 int n = netboot_poll();
242 if (n < 1) {
243 continue;
244 }
245 if (nbkernel.offset < 32768) {
246 // too small to be a kernel
247 continue;
248 }
249 uint8_t* x = nbkernel.data;
250 if ((x[0] == 'M') && (x[1] == 'Z') && (x[0x80] == 'P') && (x[0x81] == 'E')) {
251 size_t exitdatasize;
252 efi_status r;
253 efi_handle h;
254
255 efi_device_path_hw_memmap mempath[2] = {
256 {
257 .Header = {
258 .Type = DEVICE_PATH_HARDWARE,
259 .SubType = DEVICE_PATH_HW_MEMMAP,
260 .Length = { (uint8_t)(sizeof(efi_device_path_hw_memmap) & 0xff),
261 (uint8_t)((sizeof(efi_device_path_hw_memmap) >> 8) & 0xff), },
262 },
263 .MemoryType = EfiLoaderData,
264 .StartAddress = (efi_physical_addr)nbkernel.data,
265 .EndAddress = (efi_physical_addr)(nbkernel.data + nbkernel.offset),
266 },
267 {
268 .Header = {
269 .Type = DEVICE_PATH_END,
270 .SubType = DEVICE_PATH_ENTIRE_END,
271 .Length = { (uint8_t)(sizeof(efi_device_path_protocol) & 0xff),
272 (uint8_t)((sizeof(efi_device_path_protocol) >> 8) & 0xff), },
273 },
274 },
275 };
276
277 printf("Attempting to run EFI binary...\n");
278 r = gBS->LoadImage(false, gImg, (efi_device_path_protocol*)mempath, (void*)nbkernel.data, nbkernel.offset, &h);
279 if (EFI_ERROR(r)) {
280 printf("LoadImage Failed (%s)\n", xefi_strerror(r));
281 continue;
282 }
283 r = gBS->StartImage(h, &exitdatasize, NULL);
284 if (EFI_ERROR(r)) {
285 printf("StartImage Failed %zu\n", r);
286 continue;
287 }
288 printf("\nNetBoot Server Resuming...\n");
289 continue;
290 }
291
292 // make sure network traffic is not in flight, etc
293 netboot_close();
294
295 // Restore the TPL before booting the kernel, or failing to netboot
296 gBS->RestoreTPL(prev_tpl);
297
298 cmdline_append((void*) nbcmdline.data, nbcmdline.offset);
299 print_cmdline();
300
301 const char* fbres = cmdline_get("bootloader.fbres", NULL);
302 if (fbres) {
303 set_gfx_mode_from_cmdline(fbres);
304 }
305
306 // maybe it's a kernel image?
307 boot_kernel(gImg, gSys, (void*) nbkernel.data, nbkernel.offset,
308 (void*) nbramdisk.data, nbramdisk.offset);
309 break;
310 }
311 }
312
313 // Finds c in s and swaps it with the character at s's head. For example:
314 // swap_to_head('b', "foobar", 6) = "boofar";
swap_to_head(const char c,char * s,const size_t n)315 static inline void swap_to_head(const char c, char* s, const size_t n) {
316 // Empty buffer?
317 if (n == 0) return;
318
319 // Find c in s
320 size_t i;
321 for (i = 0; i < n; i++) {
322 if (c == s[i]) {
323 break;
324 }
325 }
326
327 // Couldn't find c in s
328 if (i == n) return;
329
330 // Swap c to the head.
331 const char tmp = s[0];
332 s[0] = s[i];
333 s[i] = tmp;
334 }
335
336 size_t kernel_zone_size;
337 efi_physical_addr kernel_zone_base;
338
efi_main(efi_handle img,efi_system_table * sys)339 EFIAPI efi_status efi_main(efi_handle img, efi_system_table* sys) {
340 xefi_init(img, sys);
341 gConOut->ClearScreen(gConOut);
342
343 uint64_t mmio;
344 if (xefi_find_pci_mmio(gBS, 0x0C, 0x03, 0x30, &mmio) == EFI_SUCCESS) {
345 char tmp[32];
346 sprintf(tmp, "%#" PRIx64 , mmio);
347 cmdline_set("xdc.mmio", tmp);
348 }
349
350 // Load the cmdline
351 size_t csz = 0;
352 char* cmdline_file = xefi_load_file(L"cmdline", &csz, 0);
353 if (cmdline_file) {
354 cmdline_append(cmdline_file, csz);
355 }
356
357 efi_graphics_output_protocol* gop;
358 efi_status status = gBS->LocateProtocol(&GraphicsOutputProtocol, NULL,
359 (void**)&gop);
360 bool have_fb = !EFI_ERROR(status);
361
362 if (have_fb) {
363 const char* fbres = cmdline_get("bootloader.fbres", NULL);
364 if (fbres) {
365 set_gfx_mode_from_cmdline(fbres);
366 }
367 draw_logo();
368 }
369
370 int32_t prev_attr = gConOut->Mode->Attribute;
371 gConOut->SetAttribute(gConOut, EFI_LIGHTZIRCON | EFI_BACKGROUND_BLACK);
372 draw_version(BOOTLOADER_VERSION);
373 gConOut->SetAttribute(gConOut, prev_attr);
374
375 if (have_fb) {
376 printf("Framebuffer base is at %" PRIx64 "\n\n",
377 gop->Mode->FrameBufferBase);
378 }
379
380 // Set aside space for the kernel down at the 1MB mark up front
381 // to avoid other allocations getting in the way.
382 // The kernel itself is about 1MB, but we leave generous space
383 // for its BSS afterwards.
384 //
385 // Previously we requested 32MB but that caused issues. When the kernel
386 // becomes relocatable this won't be an problem. See ZX-2368.
387 kernel_zone_base = 0x100000;
388 kernel_zone_size = 6 * 1024 * 1024;
389
390 if (gBS->AllocatePages(AllocateAddress, EfiLoaderData,
391 BYTES_TO_PAGES(kernel_zone_size), &kernel_zone_base)) {
392 printf("boot: cannot obtain %zu bytes for kernel @ %p\n", kernel_zone_size,
393 (void*) kernel_zone_base);
394 kernel_zone_size = 0;
395 }
396 // HACK: Try again with a smaller size - certain platforms (ex: GCE) are unable
397 // to support a large fixed allocation at 0x100000.
398 if (kernel_zone_size == 0) {
399 kernel_zone_size = 3 * 1024 * 1024;
400 efi_status status = gBS->AllocatePages(AllocateAddress, EfiLoaderData,
401 BYTES_TO_PAGES(kernel_zone_size),
402 &kernel_zone_base);
403 if (status) {
404 printf("boot: cannot obtain %zu bytes for kernel @ %p\n",
405 kernel_zone_size,
406 (void*) kernel_zone_base);
407 kernel_zone_size = 0;
408 }
409 }
410 printf("KALLOC DONE\n");
411
412 // Default boot defaults to network
413 const char* defboot = cmdline_get("bootloader.default", "network");
414 const char* nodename = cmdline_get("zircon.nodename", "");
415
416 // See if there's a network interface
417 bool have_network = netboot_init(nodename) == 0;
418 if (have_network) {
419 if (have_fb) {
420 draw_nodename(netboot_nodename());
421 } else {
422 printf("\nNodename: %s\n", netboot_nodename());
423 }
424 // If nodename was set through cmdline earlier in the code path then
425 // netboot_nodename will return that same value, otherwise it will
426 // return the generated value in which case it needs to be added to
427 // the command line arguments.
428 if (nodename[0] == 0) {
429 cmdline_set("zircon.nodename", netboot_nodename());
430 }
431 }
432
433 printf("\n\n");
434 print_cmdline();
435
436 // First look for a self-contained zircon boot image
437 size_t zedboot_size = 0;
438 void* zedboot_kernel = NULL;
439
440 zedboot_kernel = xefi_load_file(L"zedboot.bin", &zedboot_size, 0);
441 switch (identify_image(zedboot_kernel, zedboot_size)) {
442 case IMAGE_COMBO:
443 printf("zedboot.bin is a valid kernel+ramdisk combo image\n");
444 break;
445 case IMAGE_EMPTY:
446 break;
447 default:
448 zedboot_kernel = NULL;
449 zedboot_size = 0;
450 }
451
452 // Look for a kernel image on disk
453 size_t ksz = 0;
454 unsigned ktype = IMAGE_INVALID;
455 void* kernel = NULL;
456
457 kernel = image_load_from_disk(img, sys, &ksz);
458 if (kernel != NULL) {
459 printf("zircon image loaded from zircon partition\n");
460 ktype = IMAGE_COMBO;
461 } else {
462 kernel = xefi_load_file(L"zircon.bin", &ksz, 0);
463 switch ((ktype = identify_image(kernel, ksz))) {
464 case IMAGE_EMPTY:
465 break;
466 case IMAGE_KERNEL:
467 printf("zircon.bin is a kernel image\n");
468 break;
469 case IMAGE_COMBO:
470 printf("zircon.bin is a kernel+ramdisk combo image\n");
471 break;
472 case IMAGE_RAMDISK:
473 printf("zircon.bin is a ramdisk?!\n");
474 case IMAGE_INVALID:
475 printf("zircon.bin is not a valid kernel or combo image\n");
476 ktype = IMAGE_INVALID;
477 ksz = 0;
478 kernel = NULL;
479 }
480 }
481
482 if (!have_network && zedboot_kernel == NULL && kernel == NULL) {
483 goto fail;
484 }
485
486 char valid_keys[5];
487 memset(valid_keys, 0, sizeof(valid_keys));
488 size_t key_idx = 0;
489
490 if (have_network) {
491 valid_keys[key_idx++] = 'n';
492 }
493 if (kernel != NULL) {
494 valid_keys[key_idx++] = 'm';
495 }
496 if (zedboot_kernel) {
497 valid_keys[key_idx++] = 'z';
498 }
499
500 // The first entry in valid_keys will be the default after the timeout.
501 // Use the value of bootloader.default to determine the first entry. If
502 // bootloader.default is not set, use "network".
503 if (!memcmp(defboot, "local", 5)) {
504 swap_to_head('m', valid_keys, key_idx);
505 } else if (!memcmp(defboot, "zedboot", 7)) {
506 swap_to_head('z', valid_keys, key_idx);
507 } else {
508 swap_to_head('n', valid_keys, key_idx);
509 }
510 valid_keys[key_idx++] = 'b';
511
512 // make sure we update valid_keys if we ever add new options
513 if (key_idx >= sizeof(valid_keys)) goto fail;
514
515 // Disable WDT
516 // The second parameter can be any value outside of the range [0,0xffff]
517 gBS->SetWatchdogTimer(0, 0x10000, 0, NULL);
518
519 int timeout_s = cmdline_get_uint32("bootloader.timeout", DEFAULT_TIMEOUT);
520 while (true) {
521 printf("\nPress (b) for the boot menu");
522 if (have_network) {
523 printf(", ");
524 if (!kernel) printf("or ");
525 printf("(n) for network boot");
526 }
527 if (kernel) {
528 printf(", ");
529 printf("or (m) to boot the zircon.bin on the device");
530 }
531 if (zedboot_kernel) {
532 printf(", ");
533 printf("or (z) to launch zedboot");
534 }
535 printf(" ...");
536
537 char key = key_prompt(valid_keys, timeout_s);
538 printf("\n\n");
539
540 switch (key) {
541 case 'b':
542 do_bootmenu(have_fb);
543 break;
544 case 'n':
545 do_netboot();
546 break;
547 case 'm':
548 if (ktype == IMAGE_COMBO) {
549 zedboot(img, sys, kernel, ksz);
550 } else {
551 size_t rsz = 0;
552 void* ramdisk = NULL;
553 efi_file_protocol* ramdisk_file = xefi_open_file(L"bootdata.bin");
554 const char* ramdisk_name = "bootdata.bin";
555 if (ramdisk_file == NULL) {
556 ramdisk_file = xefi_open_file(L"ramdisk.bin");
557 ramdisk_name = "ramdisk.bin";
558 }
559 if (ramdisk_file) {
560 printf("Loading %s...\n", ramdisk_name);
561 ramdisk = xefi_read_file(ramdisk_file, &rsz, FRONT_BYTES);
562 ramdisk_file->Close(ramdisk_file);
563 }
564 boot_kernel(gImg, gSys, kernel, ksz, ramdisk, rsz);
565 }
566 goto fail;
567 case 'z':
568 zedboot(img, sys, zedboot_kernel, zedboot_size);
569 goto fail;
570 default:
571 goto fail;
572 }
573 }
574
575 fail:
576 printf("\nBoot Failure\n");
577 xefi_wait_any_key();
578 return EFI_SUCCESS;
579 }
580