diff options
author | Linn Crosetto <linn@hp.com> | 2013-09-22 21:59:08 -0400 |
---|---|---|
committer | Matt Fleming <matt.fleming@intel.com> | 2013-09-30 05:23:10 -0400 |
commit | d2078d5adbe227d64d7963d93f13479c890a9a16 (patch) | |
tree | 2f9e6e2abc7f3d9aa69fd0587c3ae407f4e19b18 | |
parent | 3203209d61440e02429fc52f192797adf5d8c053 (diff) |
x86: EFI stub support for large memory maps
This patch fixes a problem with EFI memory maps larger than 128 entries
when booting using the EFI stub, which results in overflowing e820_map
in boot_params and an eventual halt when checking the map size in
sanitize_e820_map().
If the number of map entries is greater than what can fit in e820_map,
add the extra entries to the setup_data list using type SETUP_E820_EXT.
These extra entries are then picked up when the setup_data list is
parsed in parse_e820_ext().
Signed-off-by: Linn Crosetto <linn@hp.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
-rw-r--r-- | arch/x86/boot/compressed/eboot.c | 222 |
1 files changed, 161 insertions, 61 deletions
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index beb07a4529ac..5c0dc55f2387 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c | |||
@@ -519,71 +519,47 @@ fail: | |||
519 | return NULL; | 519 | return NULL; |
520 | } | 520 | } |
521 | 521 | ||
522 | static efi_status_t exit_boot(struct boot_params *boot_params, | 522 | static void add_e820ext(struct boot_params *params, |
523 | void *handle) | 523 | struct setup_data *e820ext, u32 nr_entries) |
524 | { | 524 | { |
525 | struct efi_info *efi = &boot_params->efi_info; | 525 | struct setup_data *data; |
526 | struct e820entry *e820_map = &boot_params->e820_map[0]; | ||
527 | struct e820entry *prev = NULL; | ||
528 | unsigned long size, key, desc_size, _size; | ||
529 | efi_memory_desc_t *mem_map; | ||
530 | efi_status_t status; | 526 | efi_status_t status; |
531 | __u32 desc_version; | 527 | unsigned long size; |
532 | bool called_exit = false; | ||
533 | u8 nr_entries; | ||
534 | int i; | ||
535 | |||
536 | get_map: | ||
537 | status = efi_get_memory_map(sys_table, &mem_map, &size, &desc_size, | ||
538 | &desc_version, &key); | ||
539 | |||
540 | if (status != EFI_SUCCESS) | ||
541 | return status; | ||
542 | 528 | ||
543 | memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32)); | 529 | e820ext->type = SETUP_E820_EXT; |
544 | efi->efi_systab = (unsigned long)sys_table; | 530 | e820ext->len = nr_entries * sizeof(struct e820entry); |
545 | efi->efi_memdesc_size = desc_size; | 531 | e820ext->next = 0; |
546 | efi->efi_memdesc_version = desc_version; | ||
547 | efi->efi_memmap = (unsigned long)mem_map; | ||
548 | efi->efi_memmap_size = size; | ||
549 | 532 | ||
550 | #ifdef CONFIG_X86_64 | 533 | data = (struct setup_data *)(unsigned long)params->hdr.setup_data; |
551 | efi->efi_systab_hi = (unsigned long)sys_table >> 32; | ||
552 | efi->efi_memmap_hi = (unsigned long)mem_map >> 32; | ||
553 | #endif | ||
554 | 534 | ||
555 | /* Might as well exit boot services now */ | 535 | while (data && data->next) |
556 | status = efi_call_phys2(sys_table->boottime->exit_boot_services, | 536 | data = (struct setup_data *)(unsigned long)data->next; |
557 | handle, key); | ||
558 | if (status != EFI_SUCCESS) { | ||
559 | /* | ||
560 | * ExitBootServices() will fail if any of the event | ||
561 | * handlers change the memory map. In which case, we | ||
562 | * must be prepared to retry, but only once so that | ||
563 | * we're guaranteed to exit on repeated failures instead | ||
564 | * of spinning forever. | ||
565 | */ | ||
566 | if (called_exit) | ||
567 | goto free_mem_map; | ||
568 | 537 | ||
569 | called_exit = true; | 538 | if (data) |
570 | efi_call_phys1(sys_table->boottime->free_pool, mem_map); | 539 | data->next = (unsigned long)e820ext; |
571 | goto get_map; | 540 | else |
572 | } | 541 | params->hdr.setup_data = (unsigned long)e820ext; |
542 | } | ||
573 | 543 | ||
574 | /* Historic? */ | 544 | static efi_status_t setup_e820(struct boot_params *params, |
575 | boot_params->alt_mem_k = 32 * 1024; | 545 | struct setup_data *e820ext, u32 e820ext_size) |
546 | { | ||
547 | struct e820entry *e820_map = ¶ms->e820_map[0]; | ||
548 | struct efi_info *efi = ¶ms->efi_info; | ||
549 | struct e820entry *prev = NULL; | ||
550 | u32 nr_entries; | ||
551 | u32 nr_desc; | ||
552 | int i; | ||
576 | 553 | ||
577 | /* | ||
578 | * Convert the EFI memory map to E820. | ||
579 | */ | ||
580 | nr_entries = 0; | 554 | nr_entries = 0; |
581 | for (i = 0; i < size / desc_size; i++) { | 555 | nr_desc = efi->efi_memmap_size / efi->efi_memdesc_size; |
556 | |||
557 | for (i = 0; i < nr_desc; i++) { | ||
582 | efi_memory_desc_t *d; | 558 | efi_memory_desc_t *d; |
583 | unsigned int e820_type = 0; | 559 | unsigned int e820_type = 0; |
584 | unsigned long m = (unsigned long)mem_map; | 560 | unsigned long m = efi->efi_memmap; |
585 | 561 | ||
586 | d = (efi_memory_desc_t *)(m + (i * desc_size)); | 562 | d = (efi_memory_desc_t *)(m + (i * efi->efi_memdesc_size)); |
587 | switch (d->type) { | 563 | switch (d->type) { |
588 | case EFI_RESERVED_TYPE: | 564 | case EFI_RESERVED_TYPE: |
589 | case EFI_RUNTIME_SERVICES_CODE: | 565 | case EFI_RUNTIME_SERVICES_CODE: |
@@ -620,18 +596,142 @@ get_map: | |||
620 | 596 | ||
621 | /* Merge adjacent mappings */ | 597 | /* Merge adjacent mappings */ |
622 | if (prev && prev->type == e820_type && | 598 | if (prev && prev->type == e820_type && |
623 | (prev->addr + prev->size) == d->phys_addr) | 599 | (prev->addr + prev->size) == d->phys_addr) { |
624 | prev->size += d->num_pages << 12; | 600 | prev->size += d->num_pages << 12; |
625 | else { | 601 | continue; |
626 | e820_map->addr = d->phys_addr; | ||
627 | e820_map->size = d->num_pages << 12; | ||
628 | e820_map->type = e820_type; | ||
629 | prev = e820_map++; | ||
630 | nr_entries++; | ||
631 | } | 602 | } |
603 | |||
604 | if (nr_entries == ARRAY_SIZE(params->e820_map)) { | ||
605 | u32 need = (nr_desc - i) * sizeof(struct e820entry) + | ||
606 | sizeof(struct setup_data); | ||
607 | |||
608 | if (!e820ext || e820ext_size < need) | ||
609 | return EFI_BUFFER_TOO_SMALL; | ||
610 | |||
611 | /* boot_params map full, switch to e820 extended */ | ||
612 | e820_map = (struct e820entry *)e820ext->data; | ||
613 | } | ||
614 | |||
615 | e820_map->addr = d->phys_addr; | ||
616 | e820_map->size = d->num_pages << PAGE_SHIFT; | ||
617 | e820_map->type = e820_type; | ||
618 | prev = e820_map++; | ||
619 | nr_entries++; | ||
632 | } | 620 | } |
633 | 621 | ||
634 | boot_params->e820_entries = nr_entries; | 622 | if (nr_entries > ARRAY_SIZE(params->e820_map)) { |
623 | u32 nr_e820ext = nr_entries - ARRAY_SIZE(params->e820_map); | ||
624 | |||
625 | add_e820ext(params, e820ext, nr_e820ext); | ||
626 | nr_entries -= nr_e820ext; | ||
627 | } | ||
628 | |||
629 | params->e820_entries = (u8)nr_entries; | ||
630 | |||
631 | return EFI_SUCCESS; | ||
632 | } | ||
633 | |||
634 | static efi_status_t alloc_e820ext(u32 nr_desc, struct setup_data **e820ext, | ||
635 | u32 *e820ext_size) | ||
636 | { | ||
637 | efi_status_t status; | ||
638 | unsigned long size; | ||
639 | |||
640 | size = sizeof(struct setup_data) + | ||
641 | sizeof(struct e820entry) * nr_desc; | ||
642 | |||
643 | if (*e820ext) { | ||
644 | efi_call_phys1(sys_table->boottime->free_pool, *e820ext); | ||
645 | *e820ext = NULL; | ||
646 | *e820ext_size = 0; | ||
647 | } | ||
648 | |||
649 | status = efi_call_phys3(sys_table->boottime->allocate_pool, | ||
650 | EFI_LOADER_DATA, size, e820ext); | ||
651 | |||
652 | if (status == EFI_SUCCESS) | ||
653 | *e820ext_size = size; | ||
654 | |||
655 | return status; | ||
656 | } | ||
657 | |||
658 | static efi_status_t exit_boot(struct boot_params *boot_params, | ||
659 | void *handle) | ||
660 | { | ||
661 | struct efi_info *efi = &boot_params->efi_info; | ||
662 | unsigned long map_sz, key, desc_size; | ||
663 | efi_memory_desc_t *mem_map; | ||
664 | struct setup_data *e820ext; | ||
665 | __u32 e820ext_size; | ||
666 | __u32 nr_desc, prev_nr_desc; | ||
667 | efi_status_t status; | ||
668 | __u32 desc_version; | ||
669 | bool called_exit = false; | ||
670 | u8 nr_entries; | ||
671 | int i; | ||
672 | |||
673 | nr_desc = 0; | ||
674 | e820ext = NULL; | ||
675 | e820ext_size = 0; | ||
676 | |||
677 | get_map: | ||
678 | status = efi_get_memory_map(sys_table, &mem_map, &map_sz, &desc_size, | ||
679 | &desc_version, &key); | ||
680 | |||
681 | if (status != EFI_SUCCESS) | ||
682 | return status; | ||
683 | |||
684 | prev_nr_desc = nr_desc; | ||
685 | nr_desc = map_sz / desc_size; | ||
686 | if (nr_desc > prev_nr_desc && | ||
687 | nr_desc > ARRAY_SIZE(boot_params->e820_map)) { | ||
688 | u32 nr_e820ext = nr_desc - ARRAY_SIZE(boot_params->e820_map); | ||
689 | |||
690 | status = alloc_e820ext(nr_e820ext, &e820ext, &e820ext_size); | ||
691 | if (status != EFI_SUCCESS) | ||
692 | goto free_mem_map; | ||
693 | |||
694 | efi_call_phys1(sys_table->boottime->free_pool, mem_map); | ||
695 | goto get_map; /* Allocated memory, get map again */ | ||
696 | } | ||
697 | |||
698 | memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32)); | ||
699 | efi->efi_systab = (unsigned long)sys_table; | ||
700 | efi->efi_memdesc_size = desc_size; | ||
701 | efi->efi_memdesc_version = desc_version; | ||
702 | efi->efi_memmap = (unsigned long)mem_map; | ||
703 | efi->efi_memmap_size = map_sz; | ||
704 | |||
705 | #ifdef CONFIG_X86_64 | ||
706 | efi->efi_systab_hi = (unsigned long)sys_table >> 32; | ||
707 | efi->efi_memmap_hi = (unsigned long)mem_map >> 32; | ||
708 | #endif | ||
709 | |||
710 | /* Might as well exit boot services now */ | ||
711 | status = efi_call_phys2(sys_table->boottime->exit_boot_services, | ||
712 | handle, key); | ||
713 | if (status != EFI_SUCCESS) { | ||
714 | /* | ||
715 | * ExitBootServices() will fail if any of the event | ||
716 | * handlers change the memory map. In which case, we | ||
717 | * must be prepared to retry, but only once so that | ||
718 | * we're guaranteed to exit on repeated failures instead | ||
719 | * of spinning forever. | ||
720 | */ | ||
721 | if (called_exit) | ||
722 | goto free_mem_map; | ||
723 | |||
724 | called_exit = true; | ||
725 | efi_call_phys1(sys_table->boottime->free_pool, mem_map); | ||
726 | goto get_map; | ||
727 | } | ||
728 | |||
729 | /* Historic? */ | ||
730 | boot_params->alt_mem_k = 32 * 1024; | ||
731 | |||
732 | status = setup_e820(boot_params, e820ext, e820ext_size); | ||
733 | if (status != EFI_SUCCESS) | ||
734 | return status; | ||
635 | 735 | ||
636 | return EFI_SUCCESS; | 736 | return EFI_SUCCESS; |
637 | 737 | ||