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