[e16e8f2] | 1 | /* |
---|
| 2 | * Copyright 2011-2014 Intel Corporation - All Rights Reserved |
---|
| 3 | */ |
---|
| 4 | |
---|
| 5 | #include <codepage.h> |
---|
| 6 | #include <core.h> |
---|
| 7 | #include <fs.h> |
---|
| 8 | #include <com32.h> |
---|
| 9 | #include <syslinux/memscan.h> |
---|
| 10 | #include <syslinux/firmware.h> |
---|
| 11 | #include <syslinux/linux.h> |
---|
| 12 | #include <sys/ansi.h> |
---|
| 13 | #include <setjmp.h> |
---|
| 14 | |
---|
| 15 | #include "efi.h" |
---|
| 16 | #include "fio.h" |
---|
| 17 | #include "version.h" |
---|
| 18 | |
---|
| 19 | __export uint16_t PXERetry; |
---|
| 20 | __export char copyright_str[] = "Copyright (C) 2011-" YEAR_STR "\n"; |
---|
| 21 | uint8_t SerialNotice = 1; |
---|
| 22 | __export char syslinux_banner[] = "Syslinux " VERSION_STR " (EFI; " DATE_STR ")\n"; |
---|
| 23 | char CurrentDirName[CURRENTDIR_MAX]; |
---|
| 24 | struct com32_sys_args __com32; |
---|
| 25 | |
---|
| 26 | uint32_t _IdleTimer = 0; |
---|
| 27 | char __lowmem_heap[32]; |
---|
| 28 | uint32_t BIOS_timer_next; |
---|
| 29 | uint32_t timer_irq; |
---|
| 30 | __export uint8_t KbdMap[256]; |
---|
| 31 | char aux_seg[256]; |
---|
| 32 | |
---|
| 33 | static jmp_buf load_error_buf; |
---|
| 34 | |
---|
| 35 | static inline EFI_STATUS |
---|
| 36 | efi_close_protocol(EFI_HANDLE handle, EFI_GUID *guid, EFI_HANDLE agent, |
---|
| 37 | EFI_HANDLE controller) |
---|
| 38 | { |
---|
| 39 | return uefi_call_wrapper(BS->CloseProtocol, 4, handle, |
---|
| 40 | guid, agent, controller); |
---|
| 41 | } |
---|
| 42 | |
---|
| 43 | struct efi_binding *efi_create_binding(EFI_GUID *bguid, EFI_GUID *pguid) |
---|
| 44 | { |
---|
| 45 | EFI_SERVICE_BINDING *sbp; |
---|
| 46 | struct efi_binding *b; |
---|
| 47 | EFI_STATUS status; |
---|
| 48 | EFI_HANDLE protocol, child, *handles = NULL; |
---|
| 49 | UINTN i, nr_handles = 0; |
---|
| 50 | |
---|
| 51 | b = malloc(sizeof(*b)); |
---|
| 52 | if (!b) |
---|
| 53 | return NULL; |
---|
| 54 | |
---|
| 55 | status = LibLocateHandle(ByProtocol, bguid, NULL, &nr_handles, &handles); |
---|
| 56 | if (status != EFI_SUCCESS) |
---|
| 57 | goto free_binding; |
---|
| 58 | |
---|
| 59 | for (i = 0; i < nr_handles; i++) { |
---|
| 60 | status = uefi_call_wrapper(BS->OpenProtocol, 6, handles[i], |
---|
| 61 | bguid, (void **)&sbp, |
---|
| 62 | image_handle, handles[i], |
---|
| 63 | EFI_OPEN_PROTOCOL_GET_PROTOCOL); |
---|
| 64 | if (status == EFI_SUCCESS) |
---|
| 65 | break; |
---|
| 66 | |
---|
| 67 | uefi_call_wrapper(BS->CloseProtocol, 4, handles[i], bguid, |
---|
| 68 | image_handle, handles[i]); |
---|
| 69 | } |
---|
| 70 | |
---|
| 71 | if (i == nr_handles) |
---|
| 72 | goto free_binding; |
---|
| 73 | |
---|
| 74 | child = NULL; |
---|
| 75 | |
---|
| 76 | status = uefi_call_wrapper(sbp->CreateChild, 2, sbp, (EFI_HANDLE *)&child); |
---|
| 77 | if (status != EFI_SUCCESS) |
---|
| 78 | goto close_protocol; |
---|
| 79 | |
---|
| 80 | status = uefi_call_wrapper(BS->OpenProtocol, 6, child, |
---|
| 81 | pguid, (void **)&protocol, |
---|
| 82 | image_handle, sbp, |
---|
| 83 | EFI_OPEN_PROTOCOL_GET_PROTOCOL); |
---|
| 84 | if (status != EFI_SUCCESS) |
---|
| 85 | goto destroy_child; |
---|
| 86 | |
---|
| 87 | b->parent = handles[i]; |
---|
| 88 | b->binding = sbp; |
---|
| 89 | b->child = child; |
---|
| 90 | b->this = protocol; |
---|
| 91 | |
---|
| 92 | return b; |
---|
| 93 | |
---|
| 94 | destroy_child: |
---|
| 95 | uefi_call_wrapper(sbp->DestroyChild, 2, sbp, child); |
---|
| 96 | |
---|
| 97 | close_protocol: |
---|
| 98 | uefi_call_wrapper(BS->CloseProtocol, 4, handles[i], bguid, |
---|
| 99 | image_handle, handles[i]); |
---|
| 100 | |
---|
| 101 | free_binding: |
---|
| 102 | free(b); |
---|
| 103 | return NULL; |
---|
| 104 | } |
---|
| 105 | |
---|
| 106 | void efi_destroy_binding(struct efi_binding *b, EFI_GUID *guid) |
---|
| 107 | { |
---|
| 108 | efi_close_protocol(b->child, guid, image_handle, b->binding); |
---|
| 109 | uefi_call_wrapper(b->binding->DestroyChild, 2, b->binding, b->child); |
---|
| 110 | efi_close_protocol(b->parent, guid, image_handle, b->parent); |
---|
| 111 | |
---|
| 112 | free(b); |
---|
| 113 | } |
---|
| 114 | |
---|
| 115 | #undef kaboom |
---|
| 116 | void kaboom(void) |
---|
| 117 | { |
---|
| 118 | } |
---|
| 119 | |
---|
| 120 | void printf_init(void) |
---|
| 121 | { |
---|
| 122 | } |
---|
| 123 | |
---|
| 124 | __export void local_boot(uint16_t ax) |
---|
| 125 | { |
---|
| 126 | /* |
---|
| 127 | * Inform the firmware that we failed to execute correctly, which |
---|
| 128 | * will trigger the next entry in the EFI Boot Manager list. |
---|
| 129 | */ |
---|
| 130 | longjmp(load_error_buf, 1); |
---|
| 131 | } |
---|
| 132 | |
---|
| 133 | void bios_timer_cleanup(void) |
---|
| 134 | { |
---|
| 135 | } |
---|
| 136 | |
---|
| 137 | char trackbuf[4096]; |
---|
| 138 | |
---|
| 139 | void __cdecl core_farcall(uint32_t c, const com32sys_t *a, com32sys_t *b) |
---|
| 140 | { |
---|
| 141 | } |
---|
| 142 | |
---|
| 143 | __export struct firmware *firmware = NULL; |
---|
| 144 | __export void *__syslinux_adv_ptr; |
---|
| 145 | __export size_t __syslinux_adv_size; |
---|
| 146 | char core_xfer_buf[65536]; |
---|
| 147 | struct iso_boot_info { |
---|
| 148 | uint32_t pvd; /* LBA of primary volume descriptor */ |
---|
| 149 | uint32_t file; /* LBA of boot file */ |
---|
| 150 | uint32_t length; /* Length of boot file */ |
---|
| 151 | uint32_t csum; /* Checksum of boot file */ |
---|
| 152 | uint32_t reserved[10]; /* Currently unused */ |
---|
| 153 | } iso_boot_info; |
---|
| 154 | |
---|
| 155 | uint8_t DHCPMagic; |
---|
| 156 | uint32_t RebootTime; |
---|
| 157 | |
---|
| 158 | void pxenv(void) |
---|
| 159 | { |
---|
| 160 | } |
---|
| 161 | |
---|
| 162 | uint16_t BIOS_fbm = 1; |
---|
| 163 | far_ptr_t InitStack; |
---|
| 164 | far_ptr_t PXEEntry; |
---|
| 165 | |
---|
| 166 | void gpxe_unload(void) |
---|
| 167 | { |
---|
| 168 | } |
---|
| 169 | |
---|
| 170 | void do_idle(void) |
---|
| 171 | { |
---|
| 172 | } |
---|
| 173 | |
---|
| 174 | void pxe_int1a(void) |
---|
| 175 | { |
---|
| 176 | } |
---|
| 177 | |
---|
| 178 | uint8_t KeepPXE; |
---|
| 179 | |
---|
| 180 | struct semaphore; |
---|
| 181 | mstime_t sem_down(struct semaphore *sem, mstime_t time) |
---|
| 182 | { |
---|
| 183 | /* EFI is single threaded */ |
---|
| 184 | return 0; |
---|
| 185 | } |
---|
| 186 | |
---|
| 187 | void sem_up(struct semaphore *sem) |
---|
| 188 | { |
---|
| 189 | /* EFI is single threaded */ |
---|
| 190 | } |
---|
| 191 | |
---|
| 192 | __export volatile uint32_t __ms_timer = 0; |
---|
| 193 | volatile uint32_t __jiffies = 0; |
---|
| 194 | |
---|
| 195 | void efi_write_char(uint8_t ch, uint8_t attribute) |
---|
| 196 | { |
---|
| 197 | SIMPLE_TEXT_OUTPUT_INTERFACE *out = ST->ConOut; |
---|
| 198 | uint16_t c[2]; |
---|
| 199 | |
---|
| 200 | uefi_call_wrapper(out->SetAttribute, 2, out, attribute); |
---|
| 201 | |
---|
| 202 | /* Lookup primary Unicode encoding in the system codepage */ |
---|
| 203 | c[0] = codepage.uni[0][ch]; |
---|
| 204 | c[1] = '\0'; |
---|
| 205 | |
---|
| 206 | uefi_call_wrapper(out->OutputString, 2, out, c); |
---|
| 207 | } |
---|
| 208 | |
---|
| 209 | static void efi_showcursor(const struct term_state *st) |
---|
| 210 | { |
---|
| 211 | SIMPLE_TEXT_OUTPUT_INTERFACE *out = ST->ConOut; |
---|
| 212 | bool cursor = st->cursor ? true : false; |
---|
| 213 | |
---|
| 214 | uefi_call_wrapper(out->EnableCursor, 2, out, cursor); |
---|
| 215 | } |
---|
| 216 | |
---|
| 217 | static void efi_set_cursor(int x, int y, bool visible) |
---|
| 218 | { |
---|
| 219 | SIMPLE_TEXT_OUTPUT_INTERFACE *out = ST->ConOut; |
---|
| 220 | |
---|
| 221 | uefi_call_wrapper(out->SetCursorPosition, 3, out, x, y); |
---|
| 222 | } |
---|
| 223 | |
---|
| 224 | static void efi_scroll_up(uint8_t cols, uint8_t rows, uint8_t attribute) |
---|
| 225 | { |
---|
| 226 | efi_write_char('\n', 0); |
---|
| 227 | efi_write_char('\r', 0); |
---|
| 228 | } |
---|
| 229 | |
---|
| 230 | static void efi_get_mode(int *cols, int *rows) |
---|
| 231 | { |
---|
| 232 | SIMPLE_TEXT_OUTPUT_INTERFACE *out = ST->ConOut; |
---|
| 233 | UINTN c, r; |
---|
| 234 | |
---|
| 235 | uefi_call_wrapper(out->QueryMode, 4, out, out->Mode->Mode, &c, &r); |
---|
| 236 | *rows = r; |
---|
| 237 | *cols = c; |
---|
| 238 | } |
---|
| 239 | |
---|
| 240 | static void efi_erase(int x0, int y0, int x1, int y1, uint8_t attribute) |
---|
| 241 | { |
---|
| 242 | SIMPLE_TEXT_OUTPUT_INTERFACE *out = ST->ConOut; |
---|
| 243 | int cols, rows; |
---|
| 244 | |
---|
| 245 | efi_get_mode(&cols, &rows); |
---|
| 246 | |
---|
| 247 | /* |
---|
| 248 | * The BIOS version of this function has the ability to erase |
---|
| 249 | * parts or all of the screen - the UEFI console doesn't |
---|
| 250 | * support this so we just set the cursor position unless |
---|
| 251 | * we're clearing the whole screen. |
---|
| 252 | */ |
---|
| 253 | if (!x0 && y0 == (cols - 1)) { |
---|
| 254 | /* Really clear the screen */ |
---|
| 255 | uefi_call_wrapper(out->ClearScreen, 1, out); |
---|
| 256 | } else { |
---|
| 257 | uefi_call_wrapper(out->SetCursorPosition, 3, out, y1, x1); |
---|
| 258 | } |
---|
| 259 | } |
---|
| 260 | |
---|
| 261 | static void efi_text_mode(void) |
---|
| 262 | { |
---|
| 263 | } |
---|
| 264 | |
---|
| 265 | static void efi_get_cursor(uint8_t *x, uint8_t *y) |
---|
| 266 | { |
---|
| 267 | SIMPLE_TEXT_OUTPUT_INTERFACE *out = ST->ConOut; |
---|
| 268 | *x = out->Mode->CursorColumn; |
---|
| 269 | *y = out->Mode->CursorRow; |
---|
| 270 | } |
---|
| 271 | |
---|
| 272 | struct output_ops efi_ops = { |
---|
| 273 | .erase = efi_erase, |
---|
| 274 | .write_char = efi_write_char, |
---|
| 275 | .showcursor = efi_showcursor, |
---|
| 276 | .set_cursor = efi_set_cursor, |
---|
| 277 | .scroll_up = efi_scroll_up, |
---|
| 278 | .get_mode = efi_get_mode, |
---|
| 279 | .text_mode = efi_text_mode, |
---|
| 280 | .get_cursor = efi_get_cursor, |
---|
| 281 | }; |
---|
| 282 | |
---|
| 283 | char SubvolName[2]; |
---|
| 284 | static inline EFI_MEMORY_DESCRIPTOR * |
---|
| 285 | get_memory_map(UINTN *nr_entries, UINTN *key, UINTN *desc_sz, |
---|
| 286 | uint32_t *desc_ver) |
---|
| 287 | { |
---|
| 288 | return LibMemoryMap(nr_entries, key, desc_sz, desc_ver); |
---|
| 289 | } |
---|
| 290 | |
---|
| 291 | |
---|
| 292 | int efi_scan_memory(scan_memory_callback_t callback, void *data) |
---|
| 293 | { |
---|
| 294 | UINTN i, nr_entries, key, desc_sz; |
---|
| 295 | UINTN buf, bufpos; |
---|
| 296 | UINT32 desc_ver; |
---|
| 297 | int rv = 0; |
---|
| 298 | |
---|
| 299 | buf = (UINTN)get_memory_map(&nr_entries, &key, &desc_sz, &desc_ver); |
---|
| 300 | if (!buf) |
---|
| 301 | return -1; |
---|
| 302 | bufpos = buf; |
---|
| 303 | |
---|
| 304 | for (i = 0; i < nr_entries; bufpos += desc_sz, i++) { |
---|
| 305 | EFI_MEMORY_DESCRIPTOR *m; |
---|
| 306 | UINT64 region_sz; |
---|
| 307 | enum syslinux_memmap_types type; |
---|
| 308 | |
---|
| 309 | m = (EFI_MEMORY_DESCRIPTOR *)bufpos; |
---|
| 310 | region_sz = m->NumberOfPages * EFI_PAGE_SIZE; |
---|
| 311 | |
---|
| 312 | switch (m->Type) { |
---|
| 313 | case EfiConventionalMemory: |
---|
| 314 | type = SMT_FREE; |
---|
| 315 | break; |
---|
| 316 | default: |
---|
| 317 | type = SMT_RESERVED; |
---|
| 318 | break; |
---|
| 319 | } |
---|
| 320 | |
---|
| 321 | rv = callback(data, m->PhysicalStart, region_sz, type); |
---|
| 322 | if (rv) |
---|
| 323 | break; |
---|
| 324 | } |
---|
| 325 | |
---|
| 326 | FreePool((void *)buf); |
---|
| 327 | return rv; |
---|
| 328 | } |
---|
| 329 | |
---|
| 330 | static struct syslinux_memscan efi_memscan = { |
---|
| 331 | .func = efi_scan_memory, |
---|
| 332 | }; |
---|
| 333 | |
---|
| 334 | extern uint16_t *bios_free_mem; |
---|
| 335 | void efi_init(void) |
---|
| 336 | { |
---|
| 337 | /* XXX timer */ |
---|
| 338 | *bios_free_mem = 0; |
---|
| 339 | syslinux_memscan_add(&efi_memscan); |
---|
| 340 | mem_init(); |
---|
| 341 | } |
---|
| 342 | |
---|
| 343 | char efi_getchar(char *hi) |
---|
| 344 | { |
---|
| 345 | SIMPLE_INPUT_INTERFACE *in = ST->ConIn; |
---|
| 346 | EFI_INPUT_KEY key; |
---|
| 347 | EFI_STATUS status; |
---|
| 348 | |
---|
| 349 | do { |
---|
| 350 | status = uefi_call_wrapper(in->ReadKeyStroke, 2, in, &key); |
---|
| 351 | } while (status == EFI_NOT_READY); |
---|
| 352 | |
---|
| 353 | if (!key.ScanCode) |
---|
| 354 | return (char)key.UnicodeChar; |
---|
| 355 | |
---|
| 356 | /* |
---|
| 357 | * We currently only handle scan codes that fit in 8 bits. |
---|
| 358 | */ |
---|
| 359 | *hi = (char)key.ScanCode; |
---|
| 360 | return 0; |
---|
| 361 | } |
---|
| 362 | |
---|
| 363 | int efi_pollchar(void) |
---|
| 364 | { |
---|
| 365 | SIMPLE_INPUT_INTERFACE *in = ST->ConIn; |
---|
| 366 | EFI_STATUS status; |
---|
| 367 | |
---|
| 368 | status = WaitForSingleEvent(in->WaitForKey, 1); |
---|
| 369 | return status != EFI_TIMEOUT; |
---|
| 370 | } |
---|
| 371 | |
---|
| 372 | struct input_ops efi_iops = { |
---|
| 373 | .getchar = efi_getchar, |
---|
| 374 | .pollchar = efi_pollchar, |
---|
| 375 | }; |
---|
| 376 | |
---|
| 377 | extern void efi_adv_init(void); |
---|
| 378 | extern int efi_adv_write(void); |
---|
| 379 | |
---|
| 380 | struct adv_ops efi_adv_ops = { |
---|
| 381 | .init = efi_adv_init, |
---|
| 382 | .write = efi_adv_write, |
---|
| 383 | }; |
---|
| 384 | |
---|
| 385 | struct efi_info { |
---|
| 386 | uint32_t load_signature; |
---|
| 387 | uint32_t systab; |
---|
| 388 | uint32_t desc_size; |
---|
| 389 | uint32_t desc_version; |
---|
| 390 | uint32_t memmap; |
---|
| 391 | uint32_t memmap_size; |
---|
| 392 | uint32_t systab_hi; |
---|
| 393 | uint32_t memmap_hi; |
---|
| 394 | }; |
---|
| 395 | |
---|
| 396 | #define E820MAX 128 |
---|
| 397 | #define E820_RAM 1 |
---|
| 398 | #define E820_RESERVED 2 |
---|
| 399 | #define E820_ACPI 3 |
---|
| 400 | #define E820_NVS 4 |
---|
| 401 | #define E820_UNUSABLE 5 |
---|
| 402 | |
---|
| 403 | #define BOOT_SIGNATURE 0xaa55 |
---|
| 404 | #define SYSLINUX_EFILDR 0x30 /* Is this published value? */ |
---|
| 405 | #define DEFAULT_TIMER_TICK_DURATION 500000 /* 500000 == 500000 * 100 * 10^-9 == 50 msec */ |
---|
| 406 | #define DEFAULT_MSTIMER_INC 0x32 /* 50 msec */ |
---|
| 407 | struct e820_entry { |
---|
| 408 | uint64_t start; |
---|
| 409 | uint64_t len; |
---|
| 410 | uint32_t type; |
---|
| 411 | } __packed; |
---|
| 412 | |
---|
| 413 | struct boot_params { |
---|
| 414 | struct screen_info screen_info; |
---|
| 415 | uint8_t _pad[0x1c0 - sizeof(struct screen_info)]; |
---|
| 416 | struct efi_info efi; |
---|
| 417 | uint8_t _pad2[8]; |
---|
| 418 | uint8_t e820_entries; |
---|
| 419 | uint8_t _pad3[0x2d0 - 0x1e8 - sizeof(uint8_t)]; |
---|
| 420 | struct e820_entry e820_map[E820MAX]; |
---|
| 421 | } __packed; |
---|
| 422 | |
---|
| 423 | /* Allocate boot parameter block aligned to page */ |
---|
| 424 | #define BOOT_PARAM_BLKSIZE EFI_SIZE_TO_PAGES(sizeof(struct boot_params)) * EFI_PAGE_SIZE |
---|
| 425 | |
---|
| 426 | /* Routines in support of efi boot loader were obtained from |
---|
| 427 | * http://git.kernel.org/?p=boot/efilinux/efilinux.git: |
---|
| 428 | * kernel_jump(), handover_jump(), |
---|
| 429 | * emalloc()/efree, alloc_pages/free_pages |
---|
| 430 | * allocate_pool()/free_pool() |
---|
| 431 | * memory_map() |
---|
| 432 | */ |
---|
| 433 | extern void kernel_jump(EFI_PHYSICAL_ADDRESS kernel_start, |
---|
| 434 | struct boot_params *boot_params); |
---|
| 435 | #if __SIZEOF_POINTER__ == 4 |
---|
| 436 | #define EFI_LOAD_SIG "EL32" |
---|
| 437 | #elif __SIZEOF_POINTER__ == 8 |
---|
| 438 | #define EFI_LOAD_SIG "EL64" |
---|
| 439 | #else |
---|
| 440 | #error "unsupported architecture" |
---|
| 441 | #endif |
---|
| 442 | |
---|
| 443 | struct dt_desc { |
---|
| 444 | uint16_t limit; |
---|
| 445 | uint64_t *base; |
---|
| 446 | } __packed; |
---|
| 447 | |
---|
| 448 | struct dt_desc gdt = { 0x800, (uint64_t *)0 }; |
---|
| 449 | struct dt_desc idt = { 0, 0 }; |
---|
| 450 | |
---|
| 451 | static inline EFI_MEMORY_DESCRIPTOR * |
---|
| 452 | get_mem_desc(unsigned long memmap, UINTN desc_sz, int i) |
---|
| 453 | { |
---|
| 454 | return (EFI_MEMORY_DESCRIPTOR *)(memmap + (i * desc_sz)); |
---|
| 455 | } |
---|
| 456 | |
---|
| 457 | EFI_HANDLE image_handle; |
---|
| 458 | |
---|
| 459 | static inline UINT64 round_up(UINT64 x, UINT64 y) |
---|
| 460 | { |
---|
| 461 | return (((x - 1) | (y - 1)) + 1); |
---|
| 462 | } |
---|
| 463 | |
---|
| 464 | static inline UINT64 round_down(UINT64 x, UINT64 y) |
---|
| 465 | { |
---|
| 466 | return (x & ~(y - 1)); |
---|
| 467 | } |
---|
| 468 | |
---|
| 469 | static void find_addr(EFI_PHYSICAL_ADDRESS *first, |
---|
| 470 | EFI_PHYSICAL_ADDRESS *last, |
---|
| 471 | EFI_PHYSICAL_ADDRESS min, |
---|
| 472 | EFI_PHYSICAL_ADDRESS max, |
---|
| 473 | size_t size, size_t align) |
---|
| 474 | { |
---|
| 475 | EFI_MEMORY_DESCRIPTOR *map; |
---|
| 476 | UINT32 desc_ver; |
---|
| 477 | UINTN i, nr_entries, key, desc_sz; |
---|
| 478 | |
---|
| 479 | map = get_memory_map(&nr_entries, &key, &desc_sz, &desc_ver); |
---|
| 480 | if (!map) |
---|
| 481 | return; |
---|
| 482 | |
---|
| 483 | for (i = 0; i < nr_entries; i++) { |
---|
| 484 | EFI_MEMORY_DESCRIPTOR *m; |
---|
| 485 | EFI_PHYSICAL_ADDRESS best; |
---|
| 486 | UINT64 start, end; |
---|
| 487 | |
---|
| 488 | m = get_mem_desc((unsigned long)map, desc_sz, i); |
---|
| 489 | if (m->Type != EfiConventionalMemory) |
---|
| 490 | continue; |
---|
| 491 | |
---|
| 492 | if (m->NumberOfPages < EFI_SIZE_TO_PAGES(size)) |
---|
| 493 | continue; |
---|
| 494 | |
---|
| 495 | start = m->PhysicalStart; |
---|
| 496 | end = m->PhysicalStart + (m->NumberOfPages << EFI_PAGE_SHIFT); |
---|
| 497 | if (first) { |
---|
| 498 | if (end < min) |
---|
| 499 | continue; |
---|
| 500 | |
---|
| 501 | /* What's the best address? */ |
---|
| 502 | if (start < min && min < end) |
---|
| 503 | best = min; |
---|
| 504 | else |
---|
| 505 | best = m->PhysicalStart; |
---|
| 506 | |
---|
| 507 | start = round_up(best, align); |
---|
| 508 | if (start > max) |
---|
| 509 | continue; |
---|
| 510 | |
---|
| 511 | /* Have we run out of space in this region? */ |
---|
| 512 | if (end < start || (start + size) > end) |
---|
| 513 | continue; |
---|
| 514 | |
---|
| 515 | if (start < *first) |
---|
| 516 | *first = start; |
---|
| 517 | } |
---|
| 518 | |
---|
| 519 | if (last) { |
---|
| 520 | if (start > max) |
---|
| 521 | continue; |
---|
| 522 | |
---|
| 523 | /* What's the best address? */ |
---|
| 524 | if (start < max && max < end) |
---|
| 525 | best = max - size; |
---|
| 526 | else |
---|
| 527 | best = end - size; |
---|
| 528 | |
---|
| 529 | start = round_down(best, align); |
---|
| 530 | if (start < min || start < m->PhysicalStart) |
---|
| 531 | continue; |
---|
| 532 | |
---|
| 533 | if (start > *last) |
---|
| 534 | *last = start; |
---|
| 535 | } |
---|
| 536 | } |
---|
| 537 | |
---|
| 538 | FreePool(map); |
---|
| 539 | } |
---|
| 540 | |
---|
| 541 | /** |
---|
| 542 | * allocate_pages - Allocate memory pages from the system |
---|
| 543 | * @atype: type of allocation to perform |
---|
| 544 | * @mtype: type of memory to allocate |
---|
| 545 | * @num_pages: number of contiguous 4KB pages to allocate |
---|
| 546 | * @memory: used to return the address of allocated pages |
---|
| 547 | * |
---|
| 548 | * Allocate @num_pages physically contiguous pages from the system |
---|
| 549 | * memory and return a pointer to the base of the allocation in |
---|
| 550 | * @memory if the allocation succeeds. On success, the firmware memory |
---|
| 551 | * map is updated accordingly. |
---|
| 552 | * |
---|
| 553 | * If @atype is AllocateAddress then, on input, @memory specifies the |
---|
| 554 | * address at which to attempt to allocate the memory pages. |
---|
| 555 | */ |
---|
| 556 | static inline EFI_STATUS |
---|
| 557 | allocate_pages(EFI_ALLOCATE_TYPE atype, EFI_MEMORY_TYPE mtype, |
---|
| 558 | UINTN num_pages, EFI_PHYSICAL_ADDRESS *memory) |
---|
| 559 | { |
---|
| 560 | return uefi_call_wrapper(BS->AllocatePages, 4, atype, |
---|
| 561 | mtype, num_pages, memory); |
---|
| 562 | } |
---|
| 563 | /** |
---|
| 564 | * free_pages - Return memory allocated by allocate_pages() to the firmware |
---|
| 565 | * @memory: physical base address of the page range to be freed |
---|
| 566 | * @num_pages: number of contiguous 4KB pages to free |
---|
| 567 | * |
---|
| 568 | * On success, the firmware memory map is updated accordingly. |
---|
| 569 | */ |
---|
| 570 | static inline EFI_STATUS |
---|
| 571 | free_pages(EFI_PHYSICAL_ADDRESS memory, UINTN num_pages) |
---|
| 572 | { |
---|
| 573 | return uefi_call_wrapper(BS->FreePages, 2, memory, num_pages); |
---|
| 574 | } |
---|
| 575 | |
---|
| 576 | static EFI_STATUS allocate_addr(EFI_PHYSICAL_ADDRESS *addr, size_t size) |
---|
| 577 | { |
---|
| 578 | UINTN npages = EFI_SIZE_TO_PAGES(size); |
---|
| 579 | |
---|
| 580 | return uefi_call_wrapper(BS->AllocatePages, 4, |
---|
| 581 | AllocateAddress, |
---|
| 582 | EfiLoaderData, npages, |
---|
| 583 | addr); |
---|
| 584 | } |
---|
| 585 | /** |
---|
| 586 | * allocate_pool - Allocate pool memory |
---|
| 587 | * @type: the type of pool to allocate |
---|
| 588 | * @size: number of bytes to allocate from pool of @type |
---|
| 589 | * @buffer: used to return the address of allocated memory |
---|
| 590 | * |
---|
| 591 | * Allocate memory from pool of @type. If the pool needs more memory |
---|
| 592 | * pages are allocated from EfiConventionalMemory in order to grow the |
---|
| 593 | * pool. |
---|
| 594 | * |
---|
| 595 | * All allocations are eight-byte aligned. |
---|
| 596 | */ |
---|
| 597 | static inline EFI_STATUS |
---|
| 598 | allocate_pool(EFI_MEMORY_TYPE type, UINTN size, void **buffer) |
---|
| 599 | { |
---|
| 600 | return uefi_call_wrapper(BS->AllocatePool, 3, type, size, buffer); |
---|
| 601 | } |
---|
| 602 | |
---|
| 603 | /** |
---|
| 604 | * free_pool - Return pool memory to the system |
---|
| 605 | * @buffer: the buffer to free |
---|
| 606 | * |
---|
| 607 | * Return @buffer to the system. The returned memory is marked as |
---|
| 608 | * EfiConventionalMemory. |
---|
| 609 | */ |
---|
| 610 | static inline EFI_STATUS free_pool(void *buffer) |
---|
| 611 | { |
---|
| 612 | return uefi_call_wrapper(BS->FreePool, 1, buffer); |
---|
| 613 | } |
---|
| 614 | |
---|
| 615 | static void free_addr(EFI_PHYSICAL_ADDRESS addr, size_t size) |
---|
| 616 | { |
---|
| 617 | UINTN npages = EFI_SIZE_TO_PAGES(size); |
---|
| 618 | |
---|
| 619 | uefi_call_wrapper(BS->FreePages, 2, addr, npages); |
---|
| 620 | } |
---|
| 621 | |
---|
| 622 | /* cancel the established timer */ |
---|
| 623 | static EFI_STATUS cancel_timer(EFI_EVENT ev) |
---|
| 624 | { |
---|
| 625 | return uefi_call_wrapper(BS->SetTimer, 3, ev, TimerCancel, 0); |
---|
| 626 | } |
---|
| 627 | |
---|
| 628 | /* Check if timer went off and update default timer counter */ |
---|
| 629 | void timer_handler(EFI_EVENT ev, VOID *ctx) |
---|
| 630 | { |
---|
| 631 | __ms_timer += DEFAULT_MSTIMER_INC; |
---|
| 632 | ++__jiffies; |
---|
| 633 | } |
---|
| 634 | |
---|
| 635 | /* Setup a default periodic timer */ |
---|
| 636 | static EFI_STATUS setup_default_timer(EFI_EVENT *ev) |
---|
| 637 | { |
---|
| 638 | EFI_STATUS efi_status; |
---|
| 639 | |
---|
| 640 | *ev = NULL; |
---|
| 641 | efi_status = uefi_call_wrapper( BS->CreateEvent, 5, EVT_TIMER|EVT_NOTIFY_SIGNAL, TPL_NOTIFY, (EFI_EVENT_NOTIFY)timer_handler, NULL, ev); |
---|
| 642 | if (efi_status == EFI_SUCCESS) { |
---|
| 643 | efi_status = uefi_call_wrapper(BS->SetTimer, 3, *ev, TimerPeriodic, DEFAULT_TIMER_TICK_DURATION); |
---|
| 644 | } |
---|
| 645 | return efi_status; |
---|
| 646 | } |
---|
| 647 | |
---|
| 648 | /** |
---|
| 649 | * emalloc - Allocate memory with a strict alignment requirement |
---|
| 650 | * @size: size in bytes of the requested allocation |
---|
| 651 | * @align: the required alignment of the allocation |
---|
| 652 | * @addr: a pointer to the allocated address on success |
---|
| 653 | * |
---|
| 654 | * If we cannot satisfy @align we return 0. |
---|
| 655 | */ |
---|
| 656 | EFI_STATUS emalloc(UINTN size, UINTN align, EFI_PHYSICAL_ADDRESS *addr) |
---|
| 657 | { |
---|
| 658 | UINTN i, nr_entries, map_key, desc_size; |
---|
| 659 | EFI_MEMORY_DESCRIPTOR *map_buf; |
---|
| 660 | UINTN d; |
---|
| 661 | UINT32 desc_version; |
---|
| 662 | EFI_STATUS err; |
---|
| 663 | UINTN nr_pages = EFI_SIZE_TO_PAGES(size); |
---|
| 664 | |
---|
| 665 | map_buf = get_memory_map(&nr_entries, &map_key, |
---|
| 666 | &desc_size, &desc_version); |
---|
| 667 | if (!map_buf) |
---|
| 668 | goto fail; |
---|
| 669 | |
---|
| 670 | d = (UINTN)map_buf; |
---|
| 671 | |
---|
| 672 | for (i = 0; i < nr_entries; i++, d += desc_size) { |
---|
| 673 | EFI_MEMORY_DESCRIPTOR *desc; |
---|
| 674 | EFI_PHYSICAL_ADDRESS start, end, aligned; |
---|
| 675 | |
---|
| 676 | desc = (EFI_MEMORY_DESCRIPTOR *)d; |
---|
| 677 | if (desc->Type != EfiConventionalMemory) |
---|
| 678 | continue; |
---|
| 679 | |
---|
| 680 | if (desc->NumberOfPages < nr_pages) |
---|
| 681 | continue; |
---|
| 682 | |
---|
| 683 | start = desc->PhysicalStart; |
---|
| 684 | end = start + (desc->NumberOfPages << EFI_PAGE_SHIFT); |
---|
| 685 | |
---|
| 686 | /* Low-memory is super-precious! */ |
---|
| 687 | if (end <= 1 << 20) |
---|
| 688 | continue; |
---|
| 689 | if (start < 1 << 20) |
---|
| 690 | start = (1 << 20); |
---|
| 691 | |
---|
| 692 | aligned = (start + align -1) & ~(align -1); |
---|
| 693 | |
---|
| 694 | if ((aligned + size) <= end) { |
---|
| 695 | err = allocate_pages(AllocateAddress, EfiLoaderData, |
---|
| 696 | nr_pages, &aligned); |
---|
| 697 | if (err == EFI_SUCCESS) { |
---|
| 698 | *addr = aligned; |
---|
| 699 | break; |
---|
| 700 | } |
---|
| 701 | } |
---|
| 702 | } |
---|
| 703 | |
---|
| 704 | if (i == nr_entries) |
---|
| 705 | err = EFI_OUT_OF_RESOURCES; |
---|
| 706 | |
---|
| 707 | free_pool(map_buf); |
---|
| 708 | fail: |
---|
| 709 | return err; |
---|
| 710 | } |
---|
| 711 | /** |
---|
| 712 | * efree - Return memory allocated with emalloc |
---|
| 713 | * @memory: the address of the emalloc() allocation |
---|
| 714 | * @size: the size of the allocation |
---|
| 715 | */ |
---|
| 716 | void efree(EFI_PHYSICAL_ADDRESS memory, UINTN size) |
---|
| 717 | { |
---|
| 718 | UINTN nr_pages = EFI_SIZE_TO_PAGES(size); |
---|
| 719 | |
---|
| 720 | free_pages(memory, nr_pages); |
---|
| 721 | } |
---|
| 722 | |
---|
| 723 | /* |
---|
| 724 | * Check whether 'buf' contains a PE/COFF header and that the PE/COFF |
---|
| 725 | * file can be executed by this architecture. |
---|
| 726 | */ |
---|
| 727 | static bool valid_pecoff_image(char *buf) |
---|
| 728 | { |
---|
| 729 | struct pe_header { |
---|
| 730 | uint16_t signature; |
---|
| 731 | uint8_t _pad[0x3a]; |
---|
| 732 | uint32_t offset; |
---|
| 733 | } *pehdr = (struct pe_header *)buf; |
---|
| 734 | struct coff_header { |
---|
| 735 | uint32_t signature; |
---|
| 736 | uint16_t machine; |
---|
| 737 | } *chdr; |
---|
| 738 | |
---|
| 739 | if (pehdr->signature != 0x5a4d) { |
---|
| 740 | dprintf("Invalid MS-DOS header signature\n"); |
---|
| 741 | return false; |
---|
| 742 | } |
---|
| 743 | |
---|
| 744 | if (!pehdr->offset || pehdr->offset > 512) { |
---|
| 745 | dprintf("Invalid PE header offset\n"); |
---|
| 746 | return false; |
---|
| 747 | } |
---|
| 748 | |
---|
| 749 | chdr = (struct coff_header *)&buf[pehdr->offset]; |
---|
| 750 | if (chdr->signature != 0x4550) { |
---|
| 751 | dprintf("Invalid PE header signature\n"); |
---|
| 752 | return false; |
---|
| 753 | } |
---|
| 754 | |
---|
| 755 | #if defined(__x86_64__) |
---|
| 756 | if (chdr->machine != 0x8664) { |
---|
| 757 | dprintf("Invalid PE machine field\n"); |
---|
| 758 | return false; |
---|
| 759 | } |
---|
| 760 | #else |
---|
| 761 | if (chdr->machine != 0x14c) { |
---|
| 762 | dprintf("Invalid PE machine field\n"); |
---|
| 763 | return false; |
---|
| 764 | } |
---|
| 765 | #endif |
---|
| 766 | |
---|
| 767 | return true; |
---|
| 768 | } |
---|
| 769 | |
---|
| 770 | /* |
---|
| 771 | * Boot a Linux kernel using the EFI boot stub handover protocol. |
---|
| 772 | * |
---|
| 773 | * This function will not return to its caller if booting the kernel |
---|
| 774 | * image succeeds. If booting the kernel image fails, a legacy boot |
---|
| 775 | * method should be attempted. |
---|
| 776 | */ |
---|
| 777 | static void handover_boot(struct linux_header *hdr, struct boot_params *bp) |
---|
| 778 | { |
---|
| 779 | unsigned long address = hdr->code32_start + hdr->handover_offset; |
---|
| 780 | handover_func_t *func = efi_handover; |
---|
| 781 | |
---|
| 782 | dprintf("Booting kernel using handover protocol\n"); |
---|
| 783 | |
---|
| 784 | /* |
---|
| 785 | * Ensure that the kernel is a valid PE32(+) file and that the |
---|
| 786 | * architecture of the file matches this version of Syslinux - we |
---|
| 787 | * can't mix firmware and kernel bitness (e.g. 32-bit kernel on |
---|
| 788 | * 64-bit EFI firmware) using the handover protocol. |
---|
| 789 | */ |
---|
| 790 | if (!valid_pecoff_image((char *)hdr)) |
---|
| 791 | return; |
---|
| 792 | |
---|
| 793 | if (hdr->version >= 0x20c) { |
---|
| 794 | if (hdr->xloadflags & XLF_EFI_HANDOVER_32) |
---|
| 795 | func = efi_handover_32; |
---|
| 796 | |
---|
| 797 | if (hdr->xloadflags & XLF_EFI_HANDOVER_64) |
---|
| 798 | func = efi_handover_64; |
---|
| 799 | } |
---|
| 800 | |
---|
| 801 | efi_console_restore(); |
---|
| 802 | func(image_handle, ST, bp, address); |
---|
| 803 | } |
---|
| 804 | |
---|
| 805 | static int check_linux_header(struct linux_header *hdr) |
---|
| 806 | { |
---|
| 807 | if (hdr->version < 0x205) |
---|
| 808 | hdr->relocatable_kernel = 0; |
---|
| 809 | |
---|
| 810 | /* FIXME: check boot sector signature */ |
---|
| 811 | if (hdr->boot_flag != BOOT_SIGNATURE) { |
---|
| 812 | printf("Invalid Boot signature 0x%x, bailing out\n", hdr->boot_flag); |
---|
| 813 | return -1; |
---|
| 814 | } |
---|
| 815 | |
---|
| 816 | return 0; |
---|
| 817 | } |
---|
| 818 | |
---|
| 819 | static char *build_cmdline(char *str) |
---|
| 820 | { |
---|
| 821 | EFI_PHYSICAL_ADDRESS addr; |
---|
| 822 | EFI_STATUS status; |
---|
| 823 | char *cmdline = NULL; /* internal, in efi_physical below 0x3FFFFFFF */ |
---|
| 824 | |
---|
| 825 | /* |
---|
| 826 | * The kernel expects cmdline to be allocated pretty low, |
---|
| 827 | * Documentation/x86/boot.txt says, |
---|
| 828 | * |
---|
| 829 | * "The kernel command line can be located anywhere |
---|
| 830 | * between the end of the setup heap and 0xA0000" |
---|
| 831 | */ |
---|
| 832 | addr = 0xA0000; |
---|
| 833 | status = allocate_pages(AllocateMaxAddress, EfiLoaderData, |
---|
| 834 | EFI_SIZE_TO_PAGES(strlen(str) + 1), |
---|
| 835 | &addr); |
---|
| 836 | if (status != EFI_SUCCESS) { |
---|
| 837 | printf("Failed to allocate memory for kernel command line, bailing out\n"); |
---|
| 838 | return NULL; |
---|
| 839 | } |
---|
| 840 | cmdline = (char *)(UINTN)addr; |
---|
| 841 | memcpy(cmdline, str, strlen(str) + 1); |
---|
| 842 | return cmdline; |
---|
| 843 | } |
---|
| 844 | |
---|
| 845 | static int build_gdt(void) |
---|
| 846 | { |
---|
| 847 | EFI_STATUS status; |
---|
| 848 | |
---|
| 849 | /* Allocate gdt consistent with the alignment for architecture */ |
---|
| 850 | status = emalloc(gdt.limit, __SIZEOF_POINTER__ , (EFI_PHYSICAL_ADDRESS *)&gdt.base); |
---|
| 851 | if (status != EFI_SUCCESS) { |
---|
| 852 | printf("Failed to allocate memory for GDT, bailing out\n"); |
---|
| 853 | return -1; |
---|
| 854 | } |
---|
| 855 | memset(gdt.base, 0x0, gdt.limit); |
---|
| 856 | |
---|
| 857 | /* |
---|
| 858 | * 4Gb - (0x100000*0x1000 = 4Gb) |
---|
| 859 | * base address=0 |
---|
| 860 | * code read/exec |
---|
| 861 | * granularity=4096, 386 (+5th nibble of limit) |
---|
| 862 | */ |
---|
| 863 | gdt.base[2] = 0x00cf9a000000ffff; |
---|
| 864 | |
---|
| 865 | /* |
---|
| 866 | * 4Gb - (0x100000*0x1000 = 4Gb) |
---|
| 867 | * base address=0 |
---|
| 868 | * data read/write |
---|
| 869 | * granularity=4096, 386 (+5th nibble of limit) |
---|
| 870 | */ |
---|
| 871 | gdt.base[3] = 0x00cf92000000ffff; |
---|
| 872 | |
---|
| 873 | /* Task segment value */ |
---|
| 874 | gdt.base[4] = 0x0080890000000000; |
---|
| 875 | |
---|
| 876 | return 0; |
---|
| 877 | } |
---|
| 878 | |
---|
| 879 | /* |
---|
| 880 | * Callers use ->ramdisk_size to check whether any memory was |
---|
| 881 | * allocated (and therefore needs free'ing). The return value indicates |
---|
| 882 | * hard error conditions, such as failing to alloc memory for the |
---|
| 883 | * ramdisk image. Having no initramfs is not an error. |
---|
| 884 | */ |
---|
| 885 | static int handle_ramdisks(struct linux_header *hdr, |
---|
| 886 | struct initramfs *initramfs) |
---|
| 887 | { |
---|
| 888 | EFI_PHYSICAL_ADDRESS last; |
---|
| 889 | struct initramfs *ip; |
---|
| 890 | EFI_STATUS status; |
---|
| 891 | addr_t irf_size; |
---|
| 892 | addr_t next_addr, len, pad; |
---|
| 893 | |
---|
| 894 | hdr->ramdisk_image = 0; |
---|
| 895 | hdr->ramdisk_size = 0; |
---|
| 896 | |
---|
| 897 | /* |
---|
| 898 | * Figure out the size of the initramfs, and where to put it. |
---|
| 899 | * We should put it at the highest possible address which is |
---|
| 900 | * <= hdr->initrd_addr_max, which fits the entire initramfs. |
---|
| 901 | */ |
---|
| 902 | irf_size = initramfs_size(initramfs); /* Handles initramfs == NULL */ |
---|
| 903 | if (!irf_size) |
---|
| 904 | return 0; |
---|
| 905 | |
---|
| 906 | last = 0; |
---|
| 907 | find_addr(NULL, &last, 0x1000, hdr->initrd_addr_max, |
---|
| 908 | irf_size, INITRAMFS_MAX_ALIGN); |
---|
| 909 | if (last) |
---|
| 910 | status = allocate_addr(&last, irf_size); |
---|
| 911 | |
---|
| 912 | if (!last || status != EFI_SUCCESS) { |
---|
| 913 | printf("Failed to allocate initramfs memory, bailing out\n"); |
---|
| 914 | return -1; |
---|
| 915 | } |
---|
| 916 | |
---|
| 917 | hdr->ramdisk_image = (uint32_t)last; |
---|
| 918 | hdr->ramdisk_size = irf_size; |
---|
| 919 | |
---|
| 920 | /* Copy initramfs into allocated memory */ |
---|
| 921 | for (ip = initramfs->next; ip->len; ip = ip->next) { |
---|
| 922 | len = ip->len; |
---|
| 923 | next_addr = last + len; |
---|
| 924 | |
---|
| 925 | /* |
---|
| 926 | * If this isn't the last entry, extend the |
---|
| 927 | * zero-pad region to enforce the alignment of |
---|
| 928 | * the next chunk. |
---|
| 929 | */ |
---|
| 930 | if (ip->next->len) { |
---|
| 931 | pad = -next_addr & (ip->next->align - 1); |
---|
| 932 | len += pad; |
---|
| 933 | next_addr += pad; |
---|
| 934 | } |
---|
| 935 | |
---|
| 936 | if (ip->data_len) |
---|
| 937 | memcpy((void *)(UINTN)last, ip->data, ip->data_len); |
---|
| 938 | |
---|
| 939 | if (len > ip->data_len) |
---|
| 940 | memset((void *)(UINTN)(last + ip->data_len), 0, |
---|
| 941 | len - ip->data_len); |
---|
| 942 | |
---|
| 943 | last = next_addr; |
---|
| 944 | } |
---|
| 945 | return 0; |
---|
| 946 | } |
---|
| 947 | |
---|
| 948 | static int exit_boot(struct boot_params *bp) |
---|
| 949 | { |
---|
| 950 | struct e820_entry *e820buf, *e; |
---|
| 951 | EFI_MEMORY_DESCRIPTOR *map; |
---|
| 952 | EFI_STATUS status; |
---|
| 953 | uint32_t e820_type; |
---|
| 954 | UINTN i, nr_entries, key, desc_sz; |
---|
| 955 | UINT32 desc_ver; |
---|
| 956 | |
---|
| 957 | /* Build efi memory map */ |
---|
| 958 | map = get_memory_map(&nr_entries, &key, &desc_sz, &desc_ver); |
---|
| 959 | if (!map) |
---|
| 960 | return -1; |
---|
| 961 | |
---|
| 962 | bp->efi.memmap = (uint32_t)(unsigned long)map; |
---|
| 963 | bp->efi.memmap_size = nr_entries * desc_sz; |
---|
| 964 | bp->efi.systab = (uint32_t)(unsigned long)ST; |
---|
| 965 | bp->efi.desc_size = desc_sz; |
---|
| 966 | bp->efi.desc_version = desc_ver; |
---|
| 967 | #if defined(__x86_64__) |
---|
| 968 | bp->efi.systab_hi = ((unsigned long)ST) >> 32; |
---|
| 969 | bp->efi.memmap_hi = ((unsigned long)map) >> 32; |
---|
| 970 | #endif |
---|
| 971 | |
---|
| 972 | |
---|
| 973 | /* |
---|
| 974 | * Even though 'memmap' contains the memory map we provided |
---|
| 975 | * previously in efi_scan_memory(), we should recalculate the |
---|
| 976 | * e820 map because it will most likely have changed in the |
---|
| 977 | * interim. |
---|
| 978 | */ |
---|
| 979 | e = e820buf = bp->e820_map; |
---|
| 980 | for (i = 0; i < nr_entries && i < E820MAX; i++) { |
---|
| 981 | struct e820_entry *prev = NULL; |
---|
| 982 | |
---|
| 983 | if (e > e820buf) |
---|
| 984 | prev = e - 1; |
---|
| 985 | |
---|
| 986 | map = get_mem_desc(bp->efi.memmap, desc_sz, i); |
---|
| 987 | e->start = map->PhysicalStart; |
---|
| 988 | e->len = map->NumberOfPages << EFI_PAGE_SHIFT; |
---|
| 989 | |
---|
| 990 | switch (map->Type) { |
---|
| 991 | case EfiReservedMemoryType: |
---|
| 992 | case EfiRuntimeServicesCode: |
---|
| 993 | case EfiRuntimeServicesData: |
---|
| 994 | case EfiMemoryMappedIO: |
---|
| 995 | case EfiMemoryMappedIOPortSpace: |
---|
| 996 | case EfiPalCode: |
---|
| 997 | e820_type = E820_RESERVED; |
---|
| 998 | break; |
---|
| 999 | |
---|
| 1000 | case EfiUnusableMemory: |
---|
| 1001 | e820_type = E820_UNUSABLE; |
---|
| 1002 | break; |
---|
| 1003 | |
---|
| 1004 | case EfiACPIReclaimMemory: |
---|
| 1005 | e820_type = E820_ACPI; |
---|
| 1006 | break; |
---|
| 1007 | |
---|
| 1008 | case EfiLoaderCode: |
---|
| 1009 | case EfiLoaderData: |
---|
| 1010 | case EfiBootServicesCode: |
---|
| 1011 | case EfiBootServicesData: |
---|
| 1012 | case EfiConventionalMemory: |
---|
| 1013 | e820_type = E820_RAM; |
---|
| 1014 | break; |
---|
| 1015 | |
---|
| 1016 | case EfiACPIMemoryNVS: |
---|
| 1017 | e820_type = E820_NVS; |
---|
| 1018 | break; |
---|
| 1019 | default: |
---|
| 1020 | continue; |
---|
| 1021 | } |
---|
| 1022 | |
---|
| 1023 | e->type = e820_type; |
---|
| 1024 | |
---|
| 1025 | /* Check for adjacent entries we can merge. */ |
---|
| 1026 | if (prev && (prev->start + prev->len) == e->start && |
---|
| 1027 | prev->type == e->type) |
---|
| 1028 | prev->len += e->len; |
---|
| 1029 | else |
---|
| 1030 | e++; |
---|
| 1031 | } |
---|
| 1032 | |
---|
| 1033 | bp->e820_entries = e - e820buf; |
---|
| 1034 | |
---|
| 1035 | status = uefi_call_wrapper(BS->ExitBootServices, 2, image_handle, key); |
---|
| 1036 | if (status != EFI_SUCCESS) { |
---|
| 1037 | printf("Failed to exit boot services: 0x%016lx\n", status); |
---|
| 1038 | FreePool(map); |
---|
| 1039 | return -1; |
---|
| 1040 | } |
---|
| 1041 | |
---|
| 1042 | return 0; |
---|
| 1043 | } |
---|
| 1044 | |
---|
| 1045 | /* efi_boot_linux: |
---|
| 1046 | * Boots the linux kernel using the image and parameters to boot with. |
---|
| 1047 | * The EFI boot loader is reworked taking the cue from |
---|
| 1048 | * http://git.kernel.org/?p=boot/efilinux/efilinux.git on the need to |
---|
| 1049 | * cap key kernel data structures at * 0x3FFFFFFF. |
---|
| 1050 | * The kernel image, kernel command line and boot parameter block are copied |
---|
| 1051 | * into allocated memory areas that honor the address capping requirement |
---|
| 1052 | * prior to kernel handoff. |
---|
| 1053 | * |
---|
| 1054 | * FIXME |
---|
| 1055 | * Can we move this allocation requirement to com32 linux loader in order |
---|
| 1056 | * to avoid double copying kernel image? |
---|
| 1057 | */ |
---|
| 1058 | int efi_boot_linux(void *kernel_buf, size_t kernel_size, |
---|
| 1059 | struct initramfs *initramfs, |
---|
| 1060 | struct setup_data *setup_data, |
---|
| 1061 | char *cmdline) |
---|
| 1062 | { |
---|
| 1063 | struct linux_header *hdr; |
---|
| 1064 | struct boot_params *bp; |
---|
| 1065 | EFI_STATUS status; |
---|
| 1066 | EFI_PHYSICAL_ADDRESS addr, pref_address, kernel_start = 0; |
---|
| 1067 | UINT64 setup_sz, init_size = 0; |
---|
| 1068 | char *_cmdline; |
---|
| 1069 | |
---|
| 1070 | if (check_linux_header(kernel_buf)) |
---|
| 1071 | goto bail; |
---|
| 1072 | |
---|
| 1073 | /* allocate for boot parameter block */ |
---|
| 1074 | addr = 0x3FFFFFFF; |
---|
| 1075 | status = allocate_pages(AllocateMaxAddress, EfiLoaderData, |
---|
| 1076 | BOOT_PARAM_BLKSIZE, &addr); |
---|
| 1077 | if (status != EFI_SUCCESS) { |
---|
| 1078 | printf("Failed to allocate memory for kernel boot parameter block, bailing out\n"); |
---|
| 1079 | goto bail; |
---|
| 1080 | } |
---|
| 1081 | |
---|
| 1082 | bp = (struct boot_params *)(UINTN)addr; |
---|
| 1083 | |
---|
| 1084 | memset((void *)bp, 0x0, BOOT_PARAM_BLKSIZE); |
---|
| 1085 | /* Copy the first two sectors to boot_params */ |
---|
| 1086 | memcpy((char *)bp, kernel_buf, 2 * 512); |
---|
| 1087 | hdr = (struct linux_header *)bp; |
---|
| 1088 | |
---|
| 1089 | setup_sz = (hdr->setup_sects + 1) * 512; |
---|
| 1090 | if (hdr->version >= 0x20a) { |
---|
| 1091 | pref_address = hdr->pref_address; |
---|
| 1092 | init_size = hdr->init_size; |
---|
| 1093 | } else { |
---|
| 1094 | pref_address = 0x100000; |
---|
| 1095 | |
---|
| 1096 | /* |
---|
| 1097 | * We need to account for the fact that the kernel |
---|
| 1098 | * needs room for decompression, otherwise we could |
---|
| 1099 | * end up trashing other chunks of allocated memory. |
---|
| 1100 | */ |
---|
| 1101 | init_size = (kernel_size - setup_sz) * 3; |
---|
| 1102 | } |
---|
| 1103 | hdr->type_of_loader = SYSLINUX_EFILDR; /* SYSLINUX boot loader module */ |
---|
| 1104 | _cmdline = build_cmdline(cmdline); |
---|
| 1105 | if (!_cmdline) |
---|
| 1106 | goto bail; |
---|
| 1107 | |
---|
| 1108 | hdr->cmd_line_ptr = (UINT32)(UINTN)_cmdline; |
---|
| 1109 | |
---|
| 1110 | addr = pref_address; |
---|
| 1111 | status = allocate_pages(AllocateAddress, EfiLoaderData, |
---|
| 1112 | EFI_SIZE_TO_PAGES(init_size), &addr); |
---|
| 1113 | if (status != EFI_SUCCESS) { |
---|
| 1114 | /* |
---|
| 1115 | * We failed to allocate the preferred address, so |
---|
| 1116 | * just allocate some memory and hope for the best. |
---|
| 1117 | */ |
---|
| 1118 | if (!hdr->relocatable_kernel) { |
---|
| 1119 | printf("Cannot relocate kernel, bailing out\n"); |
---|
| 1120 | goto bail; |
---|
| 1121 | } |
---|
| 1122 | |
---|
| 1123 | status = emalloc(init_size, hdr->kernel_alignment, &addr); |
---|
| 1124 | if (status != EFI_SUCCESS) { |
---|
| 1125 | printf("Failed to allocate memory for kernel image, bailing out\n"); |
---|
| 1126 | goto free_map; |
---|
| 1127 | } |
---|
| 1128 | } |
---|
| 1129 | kernel_start = addr; |
---|
| 1130 | /* FIXME: we copy the kernel into the physical memory allocated here |
---|
| 1131 | * The syslinux kernel image load elsewhere could allocate the EFI memory from here |
---|
| 1132 | * prior to copying kernel and save an extra copy |
---|
| 1133 | */ |
---|
| 1134 | memcpy((void *)(UINTN)kernel_start, kernel_buf+setup_sz, kernel_size-setup_sz); |
---|
| 1135 | |
---|
| 1136 | hdr->code32_start = (UINT32)((UINT64)kernel_start); |
---|
| 1137 | |
---|
| 1138 | dprintf("efi_boot_linux: kernel_start 0x%x kernel_size 0x%x initramfs 0x%x setup_data 0x%x cmdline 0x%x\n", |
---|
| 1139 | kernel_start, kernel_size, initramfs, setup_data, _cmdline); |
---|
| 1140 | |
---|
| 1141 | if (handle_ramdisks(hdr, initramfs)) |
---|
| 1142 | goto free_map; |
---|
| 1143 | |
---|
| 1144 | /* Attempt to use the handover protocol if available */ |
---|
| 1145 | if (hdr->version >= 0x20b && hdr->handover_offset) |
---|
| 1146 | handover_boot(hdr, bp); |
---|
| 1147 | |
---|
| 1148 | setup_screen(&bp->screen_info); |
---|
| 1149 | |
---|
| 1150 | if (build_gdt()) |
---|
| 1151 | goto free_map; |
---|
| 1152 | |
---|
| 1153 | dprintf("efi_boot_linux: setup_sects %d kernel_size %d\n", hdr->setup_sects, kernel_size); |
---|
| 1154 | |
---|
| 1155 | efi_console_restore(); |
---|
| 1156 | |
---|
| 1157 | if (exit_boot(bp)) |
---|
| 1158 | goto free_map; |
---|
| 1159 | |
---|
| 1160 | memcpy(&bp->efi.load_signature, EFI_LOAD_SIG, sizeof(uint32_t)); |
---|
| 1161 | |
---|
| 1162 | asm volatile ("lidt %0" :: "m" (idt)); |
---|
| 1163 | asm volatile ("lgdt %0" :: "m" (gdt)); |
---|
| 1164 | |
---|
| 1165 | kernel_jump(kernel_start, bp); |
---|
| 1166 | |
---|
| 1167 | /* NOTREACHED */ |
---|
| 1168 | |
---|
| 1169 | free_map: |
---|
| 1170 | if (_cmdline) |
---|
| 1171 | efree((EFI_PHYSICAL_ADDRESS)(unsigned long)_cmdline, |
---|
| 1172 | strlen(_cmdline) + 1); |
---|
| 1173 | |
---|
| 1174 | if (bp) |
---|
| 1175 | efree((EFI_PHYSICAL_ADDRESS)(unsigned long)bp, |
---|
| 1176 | BOOT_PARAM_BLKSIZE); |
---|
| 1177 | if (kernel_start) efree(kernel_start, init_size); |
---|
| 1178 | if (hdr->ramdisk_size) |
---|
| 1179 | free_addr(hdr->ramdisk_image, hdr->ramdisk_size); |
---|
| 1180 | bail: |
---|
| 1181 | return -1; |
---|
| 1182 | } |
---|
| 1183 | |
---|
| 1184 | extern struct disk *efi_disk_init(EFI_HANDLE); |
---|
| 1185 | extern void serialcfg(uint16_t *, uint16_t *, uint16_t *); |
---|
| 1186 | |
---|
| 1187 | extern struct vesa_ops efi_vesa_ops; |
---|
| 1188 | |
---|
| 1189 | struct mem_ops efi_mem_ops = { |
---|
| 1190 | .malloc = efi_malloc, |
---|
| 1191 | .realloc = efi_realloc, |
---|
| 1192 | .free = efi_free, |
---|
| 1193 | }; |
---|
| 1194 | |
---|
| 1195 | struct firmware efi_fw = { |
---|
| 1196 | .init = efi_init, |
---|
| 1197 | .disk_init = efi_disk_init, |
---|
| 1198 | .o_ops = &efi_ops, |
---|
| 1199 | .i_ops = &efi_iops, |
---|
| 1200 | .get_serial_console_info = serialcfg, |
---|
| 1201 | .adv_ops = &efi_adv_ops, |
---|
| 1202 | .boot_linux = efi_boot_linux, |
---|
| 1203 | .vesa = &efi_vesa_ops, |
---|
| 1204 | .mem = &efi_mem_ops, |
---|
| 1205 | }; |
---|
| 1206 | |
---|
| 1207 | static inline void syslinux_register_efi(void) |
---|
| 1208 | { |
---|
| 1209 | firmware = &efi_fw; |
---|
| 1210 | } |
---|
| 1211 | |
---|
| 1212 | extern void init(void); |
---|
| 1213 | extern const struct fs_ops vfat_fs_ops; |
---|
| 1214 | extern const struct fs_ops pxe_fs_ops; |
---|
| 1215 | |
---|
| 1216 | char free_high_memory[4096]; |
---|
| 1217 | |
---|
| 1218 | extern char __bss_start[]; |
---|
| 1219 | extern char __bss_end[]; |
---|
| 1220 | |
---|
| 1221 | static void efi_setcwd(CHAR16 *dp) |
---|
| 1222 | { |
---|
| 1223 | CHAR16 *c16; |
---|
| 1224 | char *c8; |
---|
| 1225 | int i, j; |
---|
| 1226 | |
---|
| 1227 | /* Search for the start of the last path component */ |
---|
| 1228 | for (i = StrLen(dp) - 1; i >= 0; i--) { |
---|
| 1229 | if (dp[i] == '\\' || dp[i] == '/') |
---|
| 1230 | break; |
---|
| 1231 | } |
---|
| 1232 | |
---|
| 1233 | if (i < 0 || i > CURRENTDIR_MAX) { |
---|
| 1234 | dp = L"\\"; |
---|
| 1235 | i = 1; |
---|
| 1236 | } |
---|
| 1237 | |
---|
| 1238 | c8 = CurrentDirName; |
---|
| 1239 | c16 = dp; |
---|
| 1240 | |
---|
| 1241 | for (j = 0; j < i; j++) { |
---|
| 1242 | if (*c16 == '\\') { |
---|
| 1243 | *c8++ = '/'; |
---|
| 1244 | c16++; |
---|
| 1245 | } else |
---|
| 1246 | *c8++ = *c16++; |
---|
| 1247 | } |
---|
| 1248 | |
---|
| 1249 | *c8 = '\0'; |
---|
| 1250 | } |
---|
| 1251 | |
---|
| 1252 | EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *table) |
---|
| 1253 | { |
---|
| 1254 | EFI_PXE_BASE_CODE *pxe; |
---|
| 1255 | EFI_LOADED_IMAGE *info; |
---|
| 1256 | EFI_STATUS status = EFI_SUCCESS; |
---|
| 1257 | const struct fs_ops *ops[] = { NULL, NULL }; |
---|
| 1258 | unsigned long len = (unsigned long)__bss_end - (unsigned long)__bss_start; |
---|
| 1259 | static struct efi_disk_private priv; |
---|
| 1260 | SIMPLE_INPUT_INTERFACE *in; |
---|
| 1261 | EFI_INPUT_KEY key; |
---|
| 1262 | EFI_EVENT timer_ev; |
---|
| 1263 | |
---|
| 1264 | memset(__bss_start, 0, len); |
---|
| 1265 | InitializeLib(image, table); |
---|
| 1266 | |
---|
| 1267 | image_handle = image; |
---|
| 1268 | syslinux_register_efi(); |
---|
| 1269 | |
---|
| 1270 | efi_console_save(); |
---|
| 1271 | init(); |
---|
| 1272 | |
---|
| 1273 | status = uefi_call_wrapper(BS->HandleProtocol, 3, image, |
---|
| 1274 | &LoadedImageProtocol, (void **)&info); |
---|
| 1275 | if (status != EFI_SUCCESS) { |
---|
| 1276 | Print(L"Failed to lookup LoadedImageProtocol\n"); |
---|
| 1277 | goto out; |
---|
| 1278 | } |
---|
| 1279 | |
---|
| 1280 | status = uefi_call_wrapper(BS->HandleProtocol, 3, info->DeviceHandle, |
---|
| 1281 | &PxeBaseCodeProtocol, (void **)&pxe); |
---|
| 1282 | if (status != EFI_SUCCESS) { |
---|
| 1283 | /* |
---|
| 1284 | * Use device handle to set up the volume root to |
---|
| 1285 | * proceed with ADV init. |
---|
| 1286 | */ |
---|
| 1287 | if (EFI_ERROR(efi_set_volroot(info->DeviceHandle))) { |
---|
| 1288 | Print(L"Failed to locate root device to prep for "); |
---|
| 1289 | Print(L"file operations & ADV initialization\n"); |
---|
| 1290 | goto out; |
---|
| 1291 | } |
---|
| 1292 | |
---|
| 1293 | efi_derivative(SYSLINUX_FS_SYSLINUX); |
---|
| 1294 | ops[0] = &vfat_fs_ops; |
---|
| 1295 | } else { |
---|
| 1296 | efi_derivative(SYSLINUX_FS_PXELINUX); |
---|
| 1297 | ops[0] = &pxe_fs_ops; |
---|
| 1298 | } |
---|
| 1299 | |
---|
| 1300 | /* setup timer for boot menu system support */ |
---|
| 1301 | status = setup_default_timer(&timer_ev); |
---|
| 1302 | if (status != EFI_SUCCESS) { |
---|
| 1303 | Print(L"Failed to set up EFI timer support, bailing out\n"); |
---|
| 1304 | goto out; |
---|
| 1305 | } |
---|
| 1306 | |
---|
| 1307 | /* TODO: once all errors are captured in efi_errno, bail out if necessary */ |
---|
| 1308 | |
---|
| 1309 | priv.dev_handle = info->DeviceHandle; |
---|
| 1310 | |
---|
| 1311 | /* |
---|
| 1312 | * Set the current working directory, which should be the |
---|
| 1313 | * directory that syslinux.efi resides in. |
---|
| 1314 | */ |
---|
| 1315 | efi_setcwd(DevicePathToStr(info->FilePath)); |
---|
| 1316 | |
---|
| 1317 | fs_init(ops, (void *)&priv); |
---|
| 1318 | |
---|
| 1319 | /* |
---|
| 1320 | * There may be pending user input that wasn't processed by |
---|
| 1321 | * whatever application invoked us. Consume and discard that |
---|
| 1322 | * data now. |
---|
| 1323 | */ |
---|
| 1324 | in = ST->ConIn; |
---|
| 1325 | do { |
---|
| 1326 | status = uefi_call_wrapper(in->ReadKeyStroke, 2, in, &key); |
---|
| 1327 | } while (status != EFI_NOT_READY); |
---|
| 1328 | |
---|
| 1329 | if (!setjmp(load_error_buf)) |
---|
| 1330 | load_env32(NULL); |
---|
| 1331 | |
---|
| 1332 | /* load_env32() failed.. cancel timer and bailout */ |
---|
| 1333 | status = cancel_timer(timer_ev); |
---|
| 1334 | if (status != EFI_SUCCESS) |
---|
| 1335 | Print(L"Failed to cancel EFI timer: %x\n", status); |
---|
| 1336 | |
---|
| 1337 | /* |
---|
| 1338 | * Tell the firmware that Syslinux failed to load. |
---|
| 1339 | */ |
---|
| 1340 | status = EFI_LOAD_ERROR; |
---|
| 1341 | out: |
---|
| 1342 | efi_console_restore(); |
---|
| 1343 | return status; |
---|
| 1344 | } |
---|