Commit 74c16acce30bb882ad5951829d8dafef8eea564c

Authored by Alexander Graf
Committed by Tom Rini
1 parent edcef3ba1d

efi_loader: Don't allocate from memory holes

When a payload calls our memory allocator with the exact address hint, we
happily allocate memory from completely unpopulated regions. Payloads however
expect this to only succeed if they would be allocating from free conventional
memory.

This patch makes the logic behind those checks a bit more obvious and ensures
that we always allocate from known good free conventional memory regions if we
want to allocate ram.

Reported-by: Jonathan Gray <jsg@jsg.id.au>
Signed-off-by: Alexander Graf <agraf@suse.de>

Showing 1 changed file with 45 additions and 10 deletions Side-by-side Diff

lib/efi_loader/efi_memory.c
... ... @@ -22,6 +22,10 @@
22 22 struct efi_mem_desc desc;
23 23 };
24 24  
  25 +#define EFI_CARVE_NO_OVERLAP -1
  26 +#define EFI_CARVE_LOOP_AGAIN -2
  27 +#define EFI_CARVE_OVERLAPS_NONRAM -3
  28 +
25 29 /* This list contains all memory map items */
26 30 LIST_HEAD(efi_mem);
27 31  
28 32  
... ... @@ -76,11 +80,11 @@
76 80  
77 81 /* check whether we're overlapping */
78 82 if ((carve_end <= map_start) || (carve_start >= map_end))
79   - return 0;
  83 + return EFI_CARVE_NO_OVERLAP;
80 84  
81 85 /* We're overlapping with non-RAM, warn the caller if desired */
82 86 if (overlap_only_ram && (map_desc->type != EFI_CONVENTIONAL_MEMORY))
83   - return -1;
  87 + return EFI_CARVE_OVERLAPS_NONRAM;
84 88  
85 89 /* Sanitize carve_start and carve_end to lie within our bounds */
86 90 carve_start = max(carve_start, map_start);
... ... @@ -95,7 +99,7 @@
95 99  
96 100 map_desc->physical_start = carve_end;
97 101 map_desc->num_pages = (map_end - carve_end) >> EFI_PAGE_SHIFT;
98   - return 1;
  102 + return (carve_end - carve_start) >> EFI_PAGE_SHIFT;
99 103 }
100 104  
101 105 /*
... ... @@ -115,7 +119,7 @@
115 119 /* Shrink the map to [ map_start ... carve_start ] */
116 120 map_desc->num_pages = (carve_start - map_start) >> EFI_PAGE_SHIFT;
117 121  
118   - return 1;
  122 + return EFI_CARVE_LOOP_AGAIN;
119 123 }
120 124  
121 125 uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
... ... @@ -123,7 +127,8 @@
123 127 {
124 128 struct list_head *lhandle;
125 129 struct efi_mem_list *newlist;
126   - bool do_carving;
  130 + bool carve_again;
  131 + uint64_t carved_pages = 0;
127 132  
128 133 if (!pages)
129 134 return start;
... ... @@ -150,7 +155,7 @@
150 155  
151 156 /* Add our new map */
152 157 do {
153   - do_carving = false;
  158 + carve_again = false;
154 159 list_for_each(lhandle, &efi_mem) {
155 160 struct efi_mem_list *lmem;
156 161 int r;
157 162  
158 163  
159 164  
160 165  
... ... @@ -158,14 +163,44 @@
158 163 lmem = list_entry(lhandle, struct efi_mem_list, link);
159 164 r = efi_mem_carve_out(lmem, &newlist->desc,
160 165 overlap_only_ram);
161   - if (r < 0) {
  166 + switch (r) {
  167 + case EFI_CARVE_OVERLAPS_NONRAM:
  168 + /*
  169 + * The user requested to only have RAM overlaps,
  170 + * but we hit a non-RAM region. Error out.
  171 + */
162 172 return 0;
163   - } else if (r) {
164   - do_carving = true;
  173 + case EFI_CARVE_NO_OVERLAP:
  174 + /* Just ignore this list entry */
165 175 break;
  176 + case EFI_CARVE_LOOP_AGAIN:
  177 + /*
  178 + * We split an entry, but need to loop through
  179 + * the list again to actually carve it.
  180 + */
  181 + carve_again = true;
  182 + break;
  183 + default:
  184 + /* We carved a number of pages */
  185 + carved_pages += r;
  186 + carve_again = true;
  187 + break;
166 188 }
  189 +
  190 + if (carve_again) {
  191 + /* The list changed, we need to start over */
  192 + break;
  193 + }
167 194 }
168   - } while (do_carving);
  195 + } while (carve_again);
  196 +
  197 + if (overlap_only_ram && (carved_pages != pages)) {
  198 + /*
  199 + * The payload wanted to have RAM overlaps, but we overlapped
  200 + * with an unallocated region. Error out.
  201 + */
  202 + return 0;
  203 + }
169 204  
170 205 /* Add our new map */
171 206 list_add_tail(&newlist->link, &efi_mem);