1 /* $OpenBSD: efiboot.c,v 1.11 2016/07/01 09:34:39 patrick Exp $ */ 2 3 /* 4 * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> 5 * Copyright (c) 2016 Mark Kettenis 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/param.h> 21 #include <sys/queue.h> 22 #include <dev/cons.h> 23 #include <sys/disklabel.h> 24 25 #include <efi.h> 26 #include <efiapi.h> 27 #include <efiprot.h> 28 #include <eficonsctl.h> 29 30 #include <lib/libkern/libkern.h> 31 #include <stand/boot/cmd.h> 32 33 #include "disk.h" 34 #include "eficall.h" 35 #include "fdt.h" 36 #include "libsa.h" 37 38 EFI_SYSTEM_TABLE *ST; 39 EFI_BOOT_SERVICES *BS; 40 EFI_RUNTIME_SERVICES *RS; 41 EFI_HANDLE IH; 42 43 EFI_HANDLE efi_bootdp; 44 45 static EFI_GUID imgp_guid = LOADED_IMAGE_PROTOCOL; 46 static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL; 47 static EFI_GUID devp_guid = DEVICE_PATH_PROTOCOL; 48 49 static void efi_timer_init(void); 50 static void efi_timer_cleanup(void); 51 52 EFI_STATUS 53 efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab) 54 { 55 extern char *progname; 56 EFI_LOADED_IMAGE *imgp; 57 EFI_DEVICE_PATH *dp = NULL; 58 EFI_STATUS status; 59 60 ST = systab; 61 BS = ST->BootServices; 62 IH = image; 63 64 status = EFI_CALL(BS->HandleProtocol, image, &imgp_guid, 65 (void **)&imgp); 66 if (status == EFI_SUCCESS) 67 status = EFI_CALL(BS->HandleProtocol, imgp->DeviceHandle, 68 &devp_guid, (void **)&dp); 69 if (status == EFI_SUCCESS) 70 efi_bootdp = dp; 71 72 progname = "BOOTARM"; 73 74 boot(0); 75 76 return (EFI_SUCCESS); 77 } 78 79 static SIMPLE_TEXT_OUTPUT_INTERFACE *conout; 80 static SIMPLE_INPUT_INTERFACE *conin; 81 82 void 83 efi_cons_probe(struct consdev *cn) 84 { 85 cn->cn_pri = CN_MIDPRI; 86 cn->cn_dev = makedev(12, 0); 87 } 88 89 void 90 efi_cons_init(struct consdev *cp) 91 { 92 conin = ST->ConIn; 93 conout = ST->ConOut; 94 } 95 96 int 97 efi_cons_getc(dev_t dev) 98 { 99 EFI_INPUT_KEY key; 100 EFI_STATUS status; 101 #if 0 102 UINTN dummy; 103 #endif 104 static int lastchar = 0; 105 106 if (lastchar) { 107 int r = lastchar; 108 if ((dev & 0x80) == 0) 109 lastchar = 0; 110 return (r); 111 } 112 113 status = conin->ReadKeyStroke(conin, &key); 114 while (status == EFI_NOT_READY) { 115 if (dev & 0x80) 116 return (0); 117 /* 118 * XXX The implementation of WaitForEvent() in U-boot 119 * is broken and neverreturns. 120 */ 121 #if 0 122 BS->WaitForEvent(1, &conin->WaitForKey, &dummy); 123 #endif 124 status = conin->ReadKeyStroke(conin, &key); 125 } 126 127 if (dev & 0x80) 128 lastchar = key.UnicodeChar; 129 130 return (key.UnicodeChar); 131 } 132 133 void 134 efi_cons_putc(dev_t dev, int c) 135 { 136 CHAR16 buf[2]; 137 138 if (c == '\n') 139 efi_cons_putc(dev, '\r'); 140 141 buf[0] = c; 142 buf[1] = 0; 143 144 conout->OutputString(conout, buf); 145 } 146 147 EFI_PHYSICAL_ADDRESS heap; 148 UINTN heapsiz = 1 * 1024 * 1024; 149 150 static void 151 efi_heap_init(void) 152 { 153 EFI_STATUS status; 154 155 status = EFI_CALL(BS->AllocatePages, AllocateAnyPages, EfiLoaderData, 156 EFI_SIZE_TO_PAGES(heapsiz), &heap); 157 if (status != EFI_SUCCESS) 158 panic("BS->AllocatePages()"); 159 } 160 161 EFI_BLOCK_IO *disk; 162 163 void 164 efi_diskprobe(void) 165 { 166 int i, bootdev; 167 UINTN sz; 168 EFI_STATUS status; 169 EFI_HANDLE *handles = NULL; 170 EFI_BLOCK_IO *blkio; 171 EFI_BLOCK_IO_MEDIA *media; 172 EFI_DEVICE_PATH *dp, *bp; 173 174 sz = 0; 175 status = EFI_CALL(BS->LocateHandle, ByProtocol, &blkio_guid, 0, &sz, 0); 176 if (status == EFI_BUFFER_TOO_SMALL) { 177 handles = alloc(sz); 178 status = EFI_CALL(BS->LocateHandle, ByProtocol, &blkio_guid, 179 0, &sz, handles); 180 } 181 if (handles == NULL || EFI_ERROR(status)) 182 panic("BS->LocateHandle() returns %d", status); 183 184 for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) { 185 bootdev = 0; 186 status = EFI_CALL(BS->HandleProtocol, handles[i], &blkio_guid, 187 (void **)&blkio); 188 if (EFI_ERROR(status)) 189 panic("BS->HandleProtocol() returns %d", status); 190 191 media = blkio->Media; 192 if (media->LogicalPartition || !media->MediaPresent) 193 continue; 194 195 if (efi_bootdp == NULL) 196 goto next; 197 status = EFI_CALL(BS->HandleProtocol, handles[i], &devp_guid, 198 (void **)&dp); 199 if (EFI_ERROR(status)) 200 goto next; 201 bp = efi_bootdp; 202 while (1) { 203 if (IsDevicePathEnd(dp)) { 204 bootdev = 1; 205 break; 206 } 207 if (memcmp(dp, bp, sizeof(EFI_DEVICE_PATH)) != 0 || 208 memcmp(dp, bp, DevicePathNodeLength(dp)) != 0) 209 break; 210 dp = NextDevicePathNode(dp); 211 bp = NextDevicePathNode(bp); 212 } 213 next: 214 if (bootdev) { 215 disk = blkio; 216 break; 217 } 218 } 219 220 free(handles, sz); 221 } 222 223 struct board_id { 224 const char *name; 225 uint32_t board_id; 226 }; 227 228 struct board_id board_id_table[] = { 229 { "allwinner,sun4i-a10", 4104 }, 230 { "allwinner,sun7i-a20", 4283 }, 231 { "arm,vexpress", 2272 }, 232 { "boundary,imx6q-nitrogen6_max", 3769 }, 233 { "boundary,imx6q-nitrogen6x", 3769 }, 234 { "compulab,cm-fx6", 4273 }, 235 { "fsl,imx6q-sabrelite", 3769 }, 236 { "fsl,imx6q-sabresd", 3980 }, 237 { "google,snow", 3774 }, 238 { "google,spring", 3774 }, 239 { "kosagi,imx6q-novena", 4269 }, 240 { "samsung,universal_c210", 2838 }, 241 { "solidrun,cubox-i/dl", 4821 }, 242 { "solidrun,cubox-i/q", 4821 }, 243 { "solidrun,hummingboard/dl", 4773 }, 244 { "solidrun,hummingboard/q", 4773 }, 245 { "ti,am335x-bone", 3589 }, 246 { "ti,omap3-beagle", 1546 }, 247 { "ti,omap3-beagle-xm", 1546 }, 248 { "ti,omap4-panda", 2791 }, 249 { "udoo,imx6q-udoo", 4800 }, 250 { "wand,imx6q-wandboard", 4412 }, 251 }; 252 253 static EFI_GUID fdt_guid = FDT_TABLE_GUID; 254 255 #define efi_guidcmp(_a, _b) memcmp((_a), (_b), sizeof(EFI_GUID)) 256 257 void * 258 efi_makebootargs(char *bootargs, uint32_t *board_id) 259 { 260 void *fdt = NULL; 261 u_char bootduid[8]; 262 u_char zero[8]; 263 void *node; 264 size_t len; 265 int i; 266 267 for (i = 0; i < ST->NumberOfTableEntries; i++) { 268 if (efi_guidcmp(&fdt_guid, 269 &ST->ConfigurationTable[i].VendorGuid) == 0) 270 fdt = ST->ConfigurationTable[i].VendorTable; 271 } 272 273 if (!fdt_init(fdt)) 274 return NULL; 275 276 node = fdt_find_node("/chosen"); 277 if (!node) 278 return NULL; 279 280 len = strlen(bootargs) + 1; 281 fdt_node_add_property(node, "bootargs", bootargs, len); 282 283 /* Pass DUID of the boot disk. */ 284 memset(&zero, 0, sizeof(zero)); 285 memcpy(&bootduid, diskinfo.disklabel.d_uid, sizeof(bootduid)); 286 if (memcmp(bootduid, zero, sizeof(bootduid)) != 0) { 287 fdt_node_add_property(node, "openbsd,bootduid", bootduid, 288 sizeof(bootduid)); 289 } 290 291 fdt_finalize(); 292 293 node = fdt_find_node("/"); 294 for (i = 0; i < nitems(board_id_table); i++) { 295 if (fdt_node_is_compatible(node, board_id_table[i].name)) { 296 *board_id = board_id_table[i].board_id; 297 break; 298 } 299 } 300 301 return fdt; 302 } 303 304 u_long efi_loadaddr; 305 306 void 307 machdep(void) 308 { 309 EFI_PHYSICAL_ADDRESS addr; 310 EFI_STATUS status; 311 312 cninit(); 313 314 /* 315 * The kernel expects to be loaded at offset 0x00300000 into a 316 * block of memory aligned on a 256MB boundary. We allocate a 317 * block of 32MB of memory, which gives us plenty of room for 318 * growth. 319 */ 320 for (addr = 0x10000000; addr <= 0xf0000000; addr += 0x10000000) { 321 status = BS->AllocatePages(AllocateAddress, EfiLoaderData, 322 EFI_SIZE_TO_PAGES(32 * 1024 * 1024), &addr); 323 if (status == EFI_SUCCESS) { 324 efi_loadaddr = addr; 325 break; 326 } 327 } 328 if (efi_loadaddr == 0) 329 printf("Can't allocate memory\n"); 330 331 efi_heap_init(); 332 efi_timer_init(); 333 efi_diskprobe(); 334 } 335 336 void 337 efi_cleanup(void) 338 { 339 efi_timer_cleanup(); 340 341 BS->ExitBootServices(NULL, 0); 342 } 343 344 void 345 _rtt(void) 346 { 347 #ifdef EFI_DEBUG 348 printf("Hit any key to reboot\n"); 349 efi_cons_getc(0); 350 #endif 351 /* 352 * XXX ResetSystem doesn't seem to work on U-Boot 2016.05 on 353 * the CuBox-i. So trigger an unimplemented instruction trap 354 * instead. 355 */ 356 #if 1 357 asm volatile(".word 0xa000f7f0\n"); 358 #else 359 RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); 360 #endif 361 while (1) { } 362 } 363 364 /* 365 * U-Boot only implements the GetTime() Runtime Service if it has been 366 * configured with CONFIG_DM_RTC. Most board configurations don't 367 * include that option, so we can't use it to implement our boot 368 * prompt timeout. Instead we use timer events to simulate a clock 369 * that ticks ever second. 370 */ 371 372 EFI_EVENT timer; 373 int ticks; 374 375 static VOID 376 efi_timer(EFI_EVENT event, VOID *context) 377 { 378 ticks++; 379 } 380 381 static void 382 efi_timer_init(void) 383 { 384 EFI_STATUS status; 385 386 status = BS->CreateEvent(EVT_TIMER, TPL_CALLBACK, 387 efi_timer, NULL, &timer); 388 if (status == EFI_SUCCESS) 389 status = BS->SetTimer(timer, TimerPeriodic, 10000000); 390 if (EFI_ERROR(status)) 391 printf("Can't create timer\n"); 392 } 393 394 static void 395 efi_timer_cleanup(void) 396 { 397 BS->CloseEvent(timer); 398 } 399 400 time_t 401 getsecs(void) 402 { 403 return ticks; 404 } 405 406 /* 407 * Various device-related bits. 408 */ 409 410 void 411 devboot(dev_t dev, char *p) 412 { 413 strlcpy(p, "sd0a", 5); 414 } 415 416 int 417 cnspeed(dev_t dev, int sp) 418 { 419 return 115200; 420 } 421 422 char * 423 ttyname(int fd) 424 { 425 return "com0"; 426 } 427 428 dev_t 429 ttydev(char *name) 430 { 431 return NODEV; 432 } 433 434 #define MAXDEVNAME 16 435 436 /* 437 * Parse a device spec. 438 * 439 * [A-Za-z]*[0-9]*[A-Za-z]:file 440 * dev uint part 441 */ 442 int 443 devparse(const char *fname, int *dev, int *unit, int *part, const char **file) 444 { 445 const char *s; 446 447 *unit = 0; /* default to wd0a */ 448 *part = 0; 449 *dev = 0; 450 451 s = strchr(fname, ':'); 452 if (s != NULL) { 453 int devlen; 454 int i, u, p = 0; 455 struct devsw *dp; 456 char devname[MAXDEVNAME]; 457 458 devlen = s - fname; 459 if (devlen > MAXDEVNAME) 460 return (EINVAL); 461 462 /* extract device name */ 463 for (i = 0; isalpha(fname[i]) && (i < devlen); i++) 464 devname[i] = fname[i]; 465 devname[i] = 0; 466 467 if (!isdigit(fname[i])) 468 return (EUNIT); 469 470 /* device number */ 471 for (u = 0; isdigit(fname[i]) && (i < devlen); i++) 472 u = u * 10 + (fname[i] - '0'); 473 474 if (!isalpha(fname[i])) 475 return (EPART); 476 477 /* partition number */ 478 if (i < devlen) 479 p = fname[i++] - 'a'; 480 481 if (i != devlen) 482 return (ENXIO); 483 484 /* check device name */ 485 for (dp = devsw, i = 0; i < ndevs; dp++, i++) { 486 if (dp->dv_name && !strcmp(devname, dp->dv_name)) 487 break; 488 } 489 490 if (i >= ndevs) 491 return (ENXIO); 492 493 *unit = u; 494 *part = p; 495 *dev = i; 496 fname = ++s; 497 } 498 499 *file = fname; 500 501 return (0); 502 } 503 504 int 505 devopen(struct open_file *f, const char *fname, char **file) 506 { 507 struct devsw *dp; 508 int dev, unit, part, error; 509 510 error = devparse(fname, &dev, &unit, &part, (const char **)file); 511 if (error) 512 return (error); 513 514 dp = &devsw[0]; 515 f->f_dev = dp; 516 517 return (*dp->dv_open)(f, unit, part); 518 } 519