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 32static nbfile nbkernel; 33static nbfile nbramdisk; 34static nbfile nbcmdline; 35 36nbfile* 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. 79char key_prompt(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 int wait_idx = 0; 104 int key_idx = wait_idx; 105 WaitList[wait_idx++] = gSys->ConIn->WaitForKey; 106 int 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 161void 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 (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 186void do_bootmenu(bool have_fb) { 187 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 214static char cmdbuf[CMDLINE_MAX]; 215void print_cmdline(void) { 216 cmdline_to_string(cmdbuf, sizeof(cmdbuf)); 217 printf("cmdline: %s\n", cmdbuf); 218} 219 220static char netboot_cmdline[CMDLINE_MAX]; 221void 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"; 315static 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 336size_t kernel_zone_size; 337efi_physical_addr kernel_zone_base; 338 339EFIAPI 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 memory for kernel @ %p\n", (void*) kernel_zone_base); 393 kernel_zone_size = 0; 394 } 395 printf("KALLOC DONE\n"); 396 397 // Default boot defaults to network 398 const char* defboot = cmdline_get("bootloader.default", "network"); 399 const char* nodename = cmdline_get("zircon.nodename", ""); 400 401 // See if there's a network interface 402 bool have_network = netboot_init(nodename) == 0; 403 if (have_network) { 404 if (have_fb) { 405 draw_nodename(netboot_nodename()); 406 } else { 407 printf("\nNodename: %s\n", netboot_nodename()); 408 } 409 // If nodename was set through cmdline earlier in the code path then 410 // netboot_nodename will return that same value, otherwise it will 411 // return the generated value in which case it needs to be added to 412 // the command line arguments. 413 if (nodename[0] == 0) { 414 cmdline_set("zircon.nodename", netboot_nodename()); 415 } 416 } 417 418 printf("\n\n"); 419 print_cmdline(); 420 421 // First look for a self-contained zircon boot image 422 size_t zedboot_size = 0; 423 void* zedboot_kernel = NULL; 424 425 zedboot_kernel = xefi_load_file(L"zedboot.bin", &zedboot_size, 0); 426 switch (identify_image(zedboot_kernel, zedboot_size)) { 427 case IMAGE_COMBO: 428 printf("zedboot.bin is a valid kernel+ramdisk combo image\n"); 429 break; 430 case IMAGE_EMPTY: 431 break; 432 default: 433 zedboot_kernel = NULL; 434 zedboot_size = 0; 435 } 436 437 // Look for a kernel image on disk 438 size_t ksz = 0; 439 unsigned ktype = IMAGE_INVALID; 440 void* kernel = NULL; 441 442 kernel = image_load_from_disk(img, sys, &ksz); 443 if (kernel != NULL) { 444 printf("zircon image loaded from zircon partition\n"); 445 ktype = IMAGE_COMBO; 446 } else { 447 kernel = xefi_load_file(L"zircon.bin", &ksz, 0); 448 switch ((ktype = identify_image(kernel, ksz))) { 449 case IMAGE_EMPTY: 450 break; 451 case IMAGE_KERNEL: 452 printf("zircon.bin is a kernel image\n"); 453 break; 454 case IMAGE_COMBO: 455 printf("zircon.bin is a kernel+ramdisk combo image\n"); 456 break; 457 case IMAGE_RAMDISK: 458 printf("zircon.bin is a ramdisk?!\n"); 459 case IMAGE_INVALID: 460 printf("zircon.bin is not a valid kernel or combo image\n"); 461 ktype = IMAGE_INVALID; 462 ksz = 0; 463 kernel = NULL; 464 } 465 } 466 467 if (!have_network && zedboot_kernel == NULL && kernel == NULL) { 468 goto fail; 469 } 470 471 char valid_keys[5]; 472 memset(valid_keys, 0, sizeof(valid_keys)); 473 int key_idx = 0; 474 475 if (have_network) { 476 valid_keys[key_idx++] = 'n'; 477 } 478 if (kernel != NULL) { 479 valid_keys[key_idx++] = 'm'; 480 } 481 if (zedboot_kernel) { 482 valid_keys[key_idx++] = 'z'; 483 } 484 485 // The first entry in valid_keys will be the default after the timeout. 486 // Use the value of bootloader.default to determine the first entry. If 487 // bootloader.default is not set, use "network". 488 if (!memcmp(defboot, "local", 5)) { 489 swap_to_head('m', valid_keys, key_idx); 490 } else if (!memcmp(defboot, "zedboot", 7)) { 491 swap_to_head('z', valid_keys, key_idx); 492 } else { 493 swap_to_head('n', valid_keys, key_idx); 494 } 495 valid_keys[key_idx++] = 'b'; 496 497 // make sure we update valid_keys if we ever add new options 498 if (key_idx >= sizeof(valid_keys)) goto fail; 499 500 // Disable WDT 501 // The second parameter can be any value outside of the range [0,0xffff] 502 gBS->SetWatchdogTimer(0, 0x10000, 0, NULL); 503 504 int timeout_s = cmdline_get_uint32("bootloader.timeout", DEFAULT_TIMEOUT); 505 while (true) { 506 printf("\nPress (b) for the boot menu"); 507 if (have_network) { 508 printf(", "); 509 if (!kernel) printf("or "); 510 printf("(n) for network boot"); 511 } 512 if (kernel) { 513 printf(", "); 514 printf("or (m) to boot the zircon.bin on the device"); 515 } 516 if (zedboot_kernel) { 517 printf(", "); 518 printf("or (z) to launch zedboot"); 519 } 520 printf(" ..."); 521 522 char key = key_prompt(valid_keys, timeout_s); 523 printf("\n\n"); 524 525 switch (key) { 526 case 'b': 527 do_bootmenu(have_fb); 528 break; 529 case 'n': 530 do_netboot(); 531 break; 532 case 'm': 533 if (ktype == IMAGE_COMBO) { 534 zedboot(img, sys, kernel, ksz); 535 } else { 536 size_t rsz = 0; 537 void* ramdisk = NULL; 538 efi_file_protocol* ramdisk_file = xefi_open_file(L"bootdata.bin"); 539 const char* ramdisk_name = "bootdata.bin"; 540 if (ramdisk_file == NULL) { 541 ramdisk_file = xefi_open_file(L"ramdisk.bin"); 542 ramdisk_name = "ramdisk.bin"; 543 } 544 if (ramdisk_file) { 545 printf("Loading %s...\n", ramdisk_name); 546 ramdisk = xefi_read_file(ramdisk_file, &rsz, FRONT_BYTES); 547 ramdisk_file->Close(ramdisk_file); 548 } 549 boot_kernel(gImg, gSys, kernel, ksz, ramdisk, rsz); 550 } 551 goto fail; 552 case 'z': 553 zedboot(img, sys, zedboot_kernel, zedboot_size); 554 goto fail; 555 default: 556 goto fail; 557 } 558 } 559 560fail: 561 printf("\nBoot Failure\n"); 562 xefi_wait_any_key(); 563 return EFI_SUCCESS; 564} 565