1 // Copyright 2016 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6
7 #include <arch/x86/bootstrap16.h>
8 #include <arch/x86/feature.h>
9 #include <arch/x86/mmu.h>
10 #include <assert.h>
11 #include <dev/interrupt.h>
12 #include <efi/boot-services.h>
13 #include <err.h>
14 #include <fbl/algorithm.h>
15 #include <inttypes.h>
16 #include <lib/memory_limit.h>
17 #include <lk/init.h>
18 #include <platform.h>
19 #include <platform/pc/bootloader.h>
20 #include <string.h>
21 #include <trace.h>
22 #include <vm/vm.h>
23 #include <zircon/types.h>
24 #include <zircon/boot/e820.h>
25 #include <object/resource_dispatcher.h>
26
27 #include "platform_p.h"
28
29 #define LOCAL_TRACE 0
30
31 struct addr_range {
32 uint64_t base;
33 uint64_t size;
34 bool is_mem;
35 };
36
37 /* Values that will store the largest low-memory contiguous address space
38 * that we can let the PCIe bus driver use for allocations */
39 paddr_t pcie_mem_lo_base;
40 size_t pcie_mem_lo_size;
41
42 // These are used to track memory arenas found during boot so they can
43 // be exclusively reserved within the resource system after the heap
44 // has been initialized.
45 constexpr uint8_t kMaxReservedMmioEntries = 64;
46 typedef struct reserved_mmio_space {
47 uint64_t base;
48 size_t len;
49 ResourceDispatcher::RefPtr dispatcher;;
50 } reserved_mmio_space_t;
51 reserved_mmio_space_t reserved_mmio_entries[kMaxReservedMmioEntries];
52 static uint8_t reserved_mmio_count = 0;
53
mark_mmio_region_to_reserve(uint64_t base,size_t len)54 static void mark_mmio_region_to_reserve(uint64_t base, size_t len) {
55 reserved_mmio_entries[reserved_mmio_count].base = base;
56 reserved_mmio_entries[reserved_mmio_count].len = len;
57 reserved_mmio_count++;
58 }
59
60 #define DEFAULT_MEMEND (16 * 1024 * 1024)
61
62 /* boot_addr_range_t is an iterator which iterates over address ranges from
63 * the boot loader
64 */
65 struct boot_addr_range;
66
67 typedef void (*boot_addr_range_advance_func)(
68 struct boot_addr_range* range_struct);
69 typedef void (*boot_addr_range_reset_func)(
70 struct boot_addr_range* range_struct);
71
72 typedef struct boot_addr_range {
73 /* the base of the current address range */
74 uint64_t base;
75 /* the size of the current address range */
76 uint64_t size;
77 /* whether this range contains memory */
78 int is_mem;
79 /* whether this range is currently reset and invalid */
80 int is_reset;
81
82 /* private information for the advance function to keep its place */
83 void* seq;
84 /* a function which advances this iterator to the next address range */
85 boot_addr_range_advance_func advance;
86 /* a function which resets this range and its sequencing information */
87 boot_addr_range_reset_func reset;
88 } boot_addr_range_t;
89
90 /* a utility function to reset the common parts of a boot_addr_range_t */
boot_addr_range_reset(boot_addr_range_t * range)91 static void boot_addr_range_reset(boot_addr_range_t* range) {
92 range->base = 0;
93 range->size = 0;
94 range->is_mem = 0;
95 range->is_reset = 1;
96 }
97
98 /* this function uses the boot_addr_range_t iterator to walk through address
99 * ranges described by the boot loader. it fills in the mem_arenas global
100 * array with the ranges of memory it finds, compacted to the start of the
101 * array. it returns the total count of arenas which have been populated.
102 */
mem_arena_init(boot_addr_range_t * range)103 static zx_status_t mem_arena_init(boot_addr_range_t* range) {
104 bool have_limit = (memory_limit_init() == ZX_OK);
105 // Create the kernel's singleton for address space management
106 // Set up a base arena template to use
107 pmm_arena_info_t base_arena;
108 snprintf(base_arena.name, sizeof(base_arena.name), "%s", "memory");
109 base_arena.priority = 1;
110 base_arena.flags = 0;
111
112 zx_status_t status;
113 for (range->reset(range), range->advance(range); !range->is_reset; range->advance(range)) {
114 LTRACEF("Range at %#" PRIx64 " of %#" PRIx64 " bytes is %smemory.\n",
115 range->base, range->size, range->is_mem ? "" : "not ");
116
117 if (!range->is_mem) {
118 continue;
119 }
120
121 // trim off parts of memory ranges that are smaller than a page
122 uint64_t base = ROUNDUP(range->base, PAGE_SIZE);
123 uint64_t size = ROUNDDOWN(range->base + range->size, PAGE_SIZE) -
124 base;
125
126 // trim any memory below 1MB for safety and SMP booting purposes
127 if (base < 1 * MB) {
128 uint64_t adjust = 1 * MB - base;
129 if (adjust >= size)
130 continue;
131
132 base += adjust;
133 size -= adjust;
134 }
135
136 mark_mmio_region_to_reserve(base, static_cast<size_t>(size));
137 if (have_limit) {
138 status = memory_limit_add_range(base, size, base_arena);
139 }
140
141 // If there is no limit, or we failed to add arenas from processing
142 // ranges then add the original range.
143 if (!have_limit || status != ZX_OK) {
144 auto arena = base_arena;
145 arena.base = base;
146 arena.size = size;
147
148 LTRACEF("Adding pmm range at %#" PRIxPTR " of %#zx bytes.\n", arena.base, arena.size);
149 status = pmm_add_arena(&arena);
150
151 // print a warning and continue
152 if (status != ZX_OK) {
153 printf("MEM: Failed to add pmm range at %#" PRIxPTR " size %#zx\n", arena.base, arena.size);
154 }
155 }
156 }
157
158 if (have_limit) {
159 memory_limit_add_arenas(base_arena);
160 }
161
162 return ZX_OK;
163 }
164
165 typedef struct e820_range_seq {
166 e820entry_t* map;
167 int index;
168 int count;
169 } e820_range_seq_t;
170
e820_range_reset(boot_addr_range_t * range)171 static void e820_range_reset(boot_addr_range_t* range) {
172 boot_addr_range_reset(range);
173
174 e820_range_seq_t* seq = (e820_range_seq_t*)(range->seq);
175 seq->index = -1;
176 }
177
e820_range_advance(boot_addr_range_t * range)178 static void e820_range_advance(boot_addr_range_t* range) {
179 e820_range_seq_t* seq = (e820_range_seq_t*)(range->seq);
180
181 seq->index++;
182
183 if (seq->index == seq->count) {
184 /* reset range to signal that we're at the end of the map */
185 e820_range_reset(range);
186 return;
187 }
188
189 e820entry_t* entry = &seq->map[seq->index];
190 range->base = entry->addr;
191 range->size = entry->size;
192 range->is_mem = (entry->type == E820_RAM) ? 1 : 0;
193 range->is_reset = 0;
194 }
195
e820_range_init(boot_addr_range_t * range,e820_range_seq_t * seq)196 static zx_status_t e820_range_init(boot_addr_range_t* range, e820_range_seq_t* seq) {
197 range->seq = seq;
198 range->advance = &e820_range_advance;
199 range->reset = &e820_range_reset;
200
201 if (bootloader.e820_count) {
202 seq->count = static_cast<int>(bootloader.e820_count);
203 seq->map = static_cast<e820entry_t*>(bootloader.e820_table);
204 range->reset(range);
205 return ZX_OK;
206 }
207
208 return ZX_ERR_NO_MEMORY;
209 }
210
211 typedef struct efi_range_seq {
212 void* base;
213 size_t entrysz;
214 int index;
215 int count;
216 } efi_range_seq_t;
217
efi_range_reset(boot_addr_range_t * range)218 static void efi_range_reset(boot_addr_range_t* range) {
219 boot_addr_range_reset(range);
220
221 efi_range_seq_t* seq = (efi_range_seq_t*)(range->seq);
222 seq->index = -1;
223 }
224
efi_is_mem(uint32_t type)225 static int efi_is_mem(uint32_t type) {
226 switch (type) {
227 case EfiLoaderCode:
228 case EfiLoaderData:
229 case EfiBootServicesCode:
230 case EfiBootServicesData:
231 case EfiConventionalMemory:
232 return 1;
233 default:
234 return 0;
235 }
236 }
237
efi_print(const char * tag,efi_memory_descriptor * e)238 static void efi_print(const char* tag, efi_memory_descriptor* e) {
239 bool mb = e->NumberOfPages > 256;
240 LTRACEF("%s%016lx %08x %lu%s\n",
241 tag, e->PhysicalStart, e->Type,
242 mb ? e->NumberOfPages / 256 : e->NumberOfPages * 4,
243 mb ? "MB" : "KB");
244 }
245
efi_range_advance(boot_addr_range_t * range)246 static void efi_range_advance(boot_addr_range_t* range) {
247 efi_range_seq_t* seq = (efi_range_seq_t*)(range->seq);
248
249 seq->index++;
250
251 if (seq->index == seq->count) {
252 /* reset range to signal that we're at the end of the map */
253 efi_range_reset(range);
254 return;
255 }
256
257 const uintptr_t addr = reinterpret_cast<uintptr_t>(seq->base) + (seq->index * seq->entrysz);
258 efi_memory_descriptor* entry = reinterpret_cast<efi_memory_descriptor*>(addr);
259 efi_print("EFI: ", entry);
260 range->base = entry->PhysicalStart;
261 range->size = entry->NumberOfPages * PAGE_SIZE;
262 range->is_reset = 0;
263 range->is_mem = efi_is_mem(entry->Type);
264
265 // coalesce adjacent memory ranges
266 while ((seq->index + 1) < seq->count) {
267 const uintptr_t addr = reinterpret_cast<uintptr_t>(seq->base) +
268 ((seq->index + 1) * seq->entrysz);
269 efi_memory_descriptor* next = reinterpret_cast<efi_memory_descriptor*>(addr);
270 if ((range->base + range->size) != next->PhysicalStart) {
271 break;
272 }
273 if (efi_is_mem(next->Type) != range->is_mem) {
274 break;
275 }
276 efi_print("EFI+ ", next);
277 range->size += next->NumberOfPages * PAGE_SIZE;
278 seq->index++;
279 }
280 }
281
efi_range_init(boot_addr_range_t * range,efi_range_seq_t * seq)282 static zx_status_t efi_range_init(boot_addr_range_t* range, efi_range_seq_t* seq) {
283 range->seq = seq;
284 range->advance = &efi_range_advance;
285 range->reset = &efi_range_reset;
286
287 if (bootloader.efi_mmap &&
288 (bootloader.efi_mmap_size > sizeof(uint64_t))) {
289 seq->entrysz = *((uint64_t*)bootloader.efi_mmap);
290 if (seq->entrysz < sizeof(efi_memory_descriptor)) {
291 return ZX_ERR_NO_MEMORY;
292 }
293
294 seq->count = static_cast<int>((bootloader.efi_mmap_size - sizeof(uint64_t)) / seq->entrysz);
295 seq->base = reinterpret_cast<void*>(
296 reinterpret_cast<uintptr_t>(bootloader.efi_mmap) + sizeof(uint64_t));
297 range->reset(range);
298 return ZX_OK;
299 } else {
300 return ZX_ERR_NO_MEMORY;
301 }
302 }
303
addr_range_cmp(const void * p1,const void * p2)304 static int addr_range_cmp(const void* p1, const void* p2) {
305 const struct addr_range* a1 = static_cast<const struct addr_range*>(p1);
306 const struct addr_range* a2 = static_cast<const struct addr_range*>(p2);
307
308 if (a1->base < a2->base)
309 return -1;
310 else if (a1->base == a2->base)
311 return 0;
312 return 1;
313 }
314
platform_mem_range_init(void)315 static zx_status_t platform_mem_range_init(void) {
316 zx_status_t status;
317 boot_addr_range_t range;
318
319 /* first try the efi memory table */
320 efi_range_seq_t efi_seq;
321 status = efi_range_init(&range, &efi_seq);
322 if (status == ZX_OK) {
323 status = mem_arena_init(&range);
324 if (status != ZX_OK) {
325 printf("MEM: failure while adding EFI memory ranges\n");
326 }
327 return ZX_OK;
328 }
329
330 /* then try getting range info from e820 */
331 e820_range_seq_t e820_seq;
332 status = e820_range_init(&range, &e820_seq);
333 if (status == ZX_OK) {
334 status = mem_arena_init(&range);
335 if (status != ZX_OK) {
336 printf("MEM: failure while adding e820 memory ranges\n");
337 }
338 return ZX_OK;
339 }
340
341 /* if still no ranges were found, make a safe guess */
342 printf("MEM: no arena range source: falling back to fixed size\n");
343 e820_range_init(&range, &e820_seq);
344 e820entry_t entry = {
345 .addr = 0,
346 .size = DEFAULT_MEMEND,
347 .type = E820_RAM,
348 };
349 e820_seq.map = &entry;
350 e820_seq.count = 1;
351 return mem_arena_init(&range);
352 }
353
354 static size_t cached_e820_entry_count;
355 static struct addr_range cached_e820_entries[64];
356
enumerate_e820(enumerate_e820_callback callback,void * ctx)357 zx_status_t enumerate_e820(enumerate_e820_callback callback, void* ctx) {
358 if (callback == NULL)
359 return ZX_ERR_INVALID_ARGS;
360
361 if (!cached_e820_entry_count)
362 return ZX_ERR_BAD_STATE;
363
364 DEBUG_ASSERT(cached_e820_entry_count <= fbl::count_of(cached_e820_entries));
365 for (size_t i = 0; i < cached_e820_entry_count; ++i)
366 callback(cached_e820_entries[i].base, cached_e820_entries[i].size,
367 cached_e820_entries[i].is_mem, ctx);
368
369 return ZX_OK;
370 }
371
372 /* Discover the basic memory map */
pc_mem_init(void)373 void pc_mem_init(void) {
374 if (platform_mem_range_init() != ZX_OK) {
375 TRACEF("Error adding arenas from provided memory tables.\n");
376 }
377
378 // Cache the e820 entries so that they will be available for enumeration
379 // later in the boot.
380 //
381 // TODO(teisenbe, johngro): do not hardcode a limit on the number of
382 // entries we may have. Find some other way to make this information
383 // available at any point in time after we boot.
384 boot_addr_range_t range;
385 efi_range_seq_t efi_seq;
386 e820_range_seq_t e820_seq;
387 bool initialized_bootstrap16 = false;
388
389 cached_e820_entry_count = 0;
390 if ((efi_range_init(&range, &efi_seq) == ZX_OK) ||
391 (e820_range_init(&range, &e820_seq) == ZX_OK)) {
392 for (range.reset(&range),
393 range.advance(&range);
394 !range.is_reset; range.advance(&range)) {
395 if (cached_e820_entry_count >= fbl::count_of(cached_e820_entries)) {
396 TRACEF("ERROR - Too many e820 entries to hold in the cache!\n");
397 cached_e820_entry_count = 0;
398 break;
399 }
400
401 struct addr_range* entry = &cached_e820_entries[cached_e820_entry_count++];
402 entry->base = range.base;
403 entry->size = range.size;
404 entry->is_mem = range.is_mem ? true : false;
405
406 const uint64_t alloc_size = 2 * PAGE_SIZE;
407 const uint64_t min_base = 2 * PAGE_SIZE;
408 if (!initialized_bootstrap16 && entry->is_mem &&
409 entry->base <= 1 * MB - alloc_size && entry->size >= alloc_size) {
410
411 uint64_t adj_base = entry->base;
412 if (entry->base < min_base) {
413 uint64_t size_adj = min_base - entry->base;
414 if (entry->size < size_adj + alloc_size) {
415 continue;
416 }
417 adj_base = min_base;
418 }
419
420 LTRACEF("Selected %" PRIxPTR " as bootstrap16 region\n", adj_base);
421 x86_bootstrap16_init(adj_base);
422 initialized_bootstrap16 = true;
423 }
424 }
425 } else {
426 TRACEF("ERROR - No e820 range entries found! This is going to end badly for everyone.\n");
427 }
428
429 if (!initialized_bootstrap16) {
430 TRACEF("WARNING - Failed to assign bootstrap16 region, SMP won't work\n");
431 }
432 }
433
434
435
436 // Initialize the higher level PhysicalAspaceManager after the heap is initialized.
x86_resource_init_hook(unsigned int rl)437 static void x86_resource_init_hook(unsigned int rl) {
438 // An error is likely fatal if the bookkeeping is broken and driver
439 ResourceDispatcher::InitializeAllocator(ZX_RSRC_KIND_MMIO,
440 0,
441 (1ull << (x86_physical_address_width())) - 1);
442 ResourceDispatcher::InitializeAllocator(ZX_RSRC_KIND_IOPORT,
443 0,
444 UINT16_MAX);
445 ResourceDispatcher::InitializeAllocator(ZX_RSRC_KIND_IRQ,
446 interrupt_get_base_vector(),
447 interrupt_get_max_vector());
448
449 // Exclusively reserve the regions marked as memory earlier so that physical
450 // vmos cannot be created against them.
451 for (uint8_t i = 0; i < reserved_mmio_count; i++) {
452 zx_rights_t rights;
453 auto& entry = reserved_mmio_entries[i];
454 zx_status_t st = ResourceDispatcher::Create(&entry.dispatcher, &rights, ZX_RSRC_KIND_MMIO,
455 entry.base, entry.len, ZX_RSRC_FLAG_EXCLUSIVE,
456 "platform_memory");
457 if (st == ZX_OK) {
458 } else {
459 TRACEF("failed to create backing resource for boot memory region %#lx - %#lx: %d\n",
460 entry.base, entry.base + entry.len, st);
461 }
462
463 }
464 }
465
466 LK_INIT_HOOK(x86_resource_init, x86_resource_init_hook, LK_INIT_LEVEL_HEAP);
467