diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/efi_loader/efi_memory.c | 55 |
1 files changed, 45 insertions, 10 deletions
diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index ad713d0e8b..df2381e42c 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -22,6 +22,10 @@ struct efi_mem_list { struct efi_mem_desc desc; }; +#define EFI_CARVE_NO_OVERLAP -1 +#define EFI_CARVE_LOOP_AGAIN -2 +#define EFI_CARVE_OVERLAPS_NONRAM -3 + /* This list contains all memory map items */ LIST_HEAD(efi_mem); @@ -76,11 +80,11 @@ static int efi_mem_carve_out(struct efi_mem_list *map, /* check whether we're overlapping */ if ((carve_end <= map_start) || (carve_start >= map_end)) - return 0; + return EFI_CARVE_NO_OVERLAP; /* We're overlapping with non-RAM, warn the caller if desired */ if (overlap_only_ram && (map_desc->type != EFI_CONVENTIONAL_MEMORY)) - return -1; + return EFI_CARVE_OVERLAPS_NONRAM; /* Sanitize carve_start and carve_end to lie within our bounds */ carve_start = max(carve_start, map_start); @@ -95,7 +99,7 @@ static int efi_mem_carve_out(struct efi_mem_list *map, map_desc->physical_start = carve_end; map_desc->num_pages = (map_end - carve_end) >> EFI_PAGE_SHIFT; - return 1; + return (carve_end - carve_start) >> EFI_PAGE_SHIFT; } /* @@ -115,7 +119,7 @@ static int efi_mem_carve_out(struct efi_mem_list *map, /* Shrink the map to [ map_start ... carve_start ] */ map_desc->num_pages = (carve_start - map_start) >> EFI_PAGE_SHIFT; - return 1; + return EFI_CARVE_LOOP_AGAIN; } uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type, @@ -123,7 +127,8 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type, { struct list_head *lhandle; struct efi_mem_list *newlist; - bool do_carving; + bool carve_again; + uint64_t carved_pages = 0; if (!pages) return start; @@ -150,7 +155,7 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type, /* Add our new map */ do { - do_carving = false; + carve_again = false; list_for_each(lhandle, &efi_mem) { struct efi_mem_list *lmem; int r; @@ -158,14 +163,44 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type, lmem = list_entry(lhandle, struct efi_mem_list, link); r = efi_mem_carve_out(lmem, &newlist->desc, overlap_only_ram); - if (r < 0) { + switch (r) { + case EFI_CARVE_OVERLAPS_NONRAM: + /* + * The user requested to only have RAM overlaps, + * but we hit a non-RAM region. Error out. + */ return 0; - } else if (r) { - do_carving = true; + case EFI_CARVE_NO_OVERLAP: + /* Just ignore this list entry */ + break; + case EFI_CARVE_LOOP_AGAIN: + /* + * We split an entry, but need to loop through + * the list again to actually carve it. + */ + carve_again = true; + break; + default: + /* We carved a number of pages */ + carved_pages += r; + carve_again = true; + break; + } + + if (carve_again) { + /* The list changed, we need to start over */ break; } } - } while (do_carving); + } while (carve_again); + + if (overlap_only_ram && (carved_pages != pages)) { + /* + * The payload wanted to have RAM overlaps, but we overlapped + * with an unallocated region. Error out. + */ + return 0; + } /* Add our new map */ list_add_tail(&newlist->link, &efi_mem); |