static void grub_claim_heap (void) { unsigned long total = 0; if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM)) heap_init (GRUB_IEEE1275_STATIC_HEAP_START, GRUB_IEEE1275_STATIC_HEAP_LEN, 1, &total); else grub_machine_mmap_iterate (heap_init, &total); }
void grub_machine_init (void) { #ifdef GRUB_MACHINE_QEMU grub_qemu_init_cirrus (); #endif /* Initialize the console as early as possible. */ grub_vga_text_init (); auto int NESTED_FUNC_ATTR heap_init (grub_uint64_t, grub_uint64_t, grub_memory_type_t); int NESTED_FUNC_ATTR heap_init (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type) { #if GRUB_CPU_SIZEOF_VOID_P == 4 /* Restrict ourselves to 32-bit memory space. */ if (addr > GRUB_ULONG_MAX) return 0; if (addr + size > GRUB_ULONG_MAX) size = GRUB_ULONG_MAX - addr; #endif if (type != GRUB_MEMORY_AVAILABLE) return 0; /* Avoid the lower memory. */ if (addr < GRUB_MEMORY_MACHINE_LOWER_SIZE) { if (addr + size <= GRUB_MEMORY_MACHINE_LOWER_SIZE) return 0; else { size -= GRUB_MEMORY_MACHINE_LOWER_SIZE - addr; addr = GRUB_MEMORY_MACHINE_LOWER_SIZE; } } grub_mm_init_region ((void *) (grub_addr_t) addr, (grub_size_t) size); return 0; } #if defined (GRUB_MACHINE_MULTIBOOT) || defined (GRUB_MACHINE_QEMU) grub_machine_mmap_init (); #endif grub_machine_mmap_iterate (heap_init); grub_tsc_init (); }
/* We need to call this before grub_claim_memory. */ static void grub_get_extended_memory (void) { auto int NESTED_FUNC_ATTR find_ext_mem (grub_uint64_t addr, grub_uint64_t len, grub_uint32_t type); int NESTED_FUNC_ATTR find_ext_mem (grub_uint64_t addr, grub_uint64_t len, grub_uint32_t type) { if (type == 1 && addr == 0x100000) { grub_upper_mem = len; return 1; } return 0; } grub_machine_mmap_iterate (find_ext_mem); }
grub_err_t grub_mmap_iterate (int (*hook) (grub_uint64_t, grub_uint64_t, grub_uint32_t, void *), void *closure) { /* This function resolves overlapping regions and sorts the memory map. It uses scanline (sweeping) algorithm. */ /* If same page is used by multiple types it's resolved according to priority: 1 - free memory 2 - memory usable by firmware-aware code 3 - unusable memory 4 - a range deliberately empty */ int priority[GRUB_MACHINE_MEMORY_MAX_TYPE + 2] = { #ifdef GRUB_MACHINE_MEMORY_AVAILABLE [GRUB_MACHINE_MEMORY_AVAILABLE] = 1, #endif #if defined (GRUB_MACHINE_MEMORY_RESERVED) && GRUB_MACHINE_MEMORY_RESERVED != GRUB_MACHINE_MEMORY_HOLE [GRUB_MACHINE_MEMORY_RESERVED] = 3, #endif #ifdef GRUB_MACHINE_MEMORY_ACPI [GRUB_MACHINE_MEMORY_ACPI] = 2, #endif #ifdef GRUB_MACHINE_MEMORY_CODE [GRUB_MACHINE_MEMORY_CODE] = 3, #endif #ifdef GRUB_MACHINE_MEMORY_NVS [GRUB_MACHINE_MEMORY_NVS] = 3, #endif [GRUB_MACHINE_MEMORY_HOLE] = 4, }; int i, k, done; struct grub_mmap_scan *scanline_events; struct grub_mmap_scan t; /* Previous scanline event. */ grub_uint64_t lastaddr; int lasttype; /* Current scanline event. */ int curtype; /* How many regions of given type overlap at current location? */ int present[GRUB_MACHINE_MEMORY_MAX_TYPE + 2]; /* Number of mmap chunks. */ int mmap_num; struct grub_mmap_iterate_closure c; #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE struct grub_mmap_region *cur; #endif mmap_num = 0; #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE for (cur = grub_mmap_overlays; cur; cur = cur->next) mmap_num++; #endif grub_machine_mmap_iterate (count_hook, &mmap_num); /* Initialize variables. */ grub_memset (present, 0, sizeof (present)); scanline_events = (struct grub_mmap_scan *) grub_malloc (sizeof (struct grub_mmap_scan) * 2 * mmap_num); if (! scanline_events) { return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate space for new memory map"); } i = 0; #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE /* Register scanline events. */ for (cur = grub_mmap_overlays; cur; cur = cur->next) { scanline_events[i].pos = cur->start; scanline_events[i].type = 0; if (cur->type == GRUB_MACHINE_MEMORY_HOLE || (cur->type >= 0 && cur->type <= GRUB_MACHINE_MEMORY_MAX_TYPE && priority[cur->type])) scanline_events[i].memtype = cur->type; else scanline_events[i].memtype = GRUB_MACHINE_MEMORY_RESERVED; i++; scanline_events[i].pos = cur->end; scanline_events[i].type = 1; scanline_events[i].memtype = scanline_events[i - 1].memtype; i++; } #endif /* ! GRUB_MMAP_REGISTER_BY_FIRMWARE */ c.i = i; c.scanline_events = scanline_events; c.priority = priority; grub_machine_mmap_iterate (fill_hook, &c); /* Primitive bubble sort. It has complexity O(n^2) but since we're unlikely to have more than 100 chunks it's probably one of the fastest for one purpose. */ done = 1; while (done) { done = 0; for (i = 0; i < 2 * mmap_num - 1; i++) if (scanline_events[i + 1].pos < scanline_events[i].pos || (scanline_events[i + 1].pos == scanline_events[i].pos && scanline_events[i + 1].type == 0 && scanline_events[i].type == 1)) { t = scanline_events[i + 1]; scanline_events[i + 1] = scanline_events[i]; scanline_events[i] = t; done = 1; } } lastaddr = scanline_events[0].pos; lasttype = scanline_events[0].memtype; for (i = 0; i < 2 * mmap_num; i++) { /* Process event. */ if (scanline_events[i].type) present[scanline_events[i].memtype]--; else present[scanline_events[i].memtype]++; /* Determine current region type. */ curtype = -1; for (k = 0; k <= GRUB_MACHINE_MEMORY_MAX_TYPE + 1; k++) if (present[k] && (curtype == -1 || priority[k] > priority[curtype])) curtype = k; /* Announce region to the hook if necessary. */ if ((curtype == -1 || curtype != lasttype) && lastaddr != scanline_events[i].pos && lasttype != -1 && lasttype != GRUB_MACHINE_MEMORY_HOLE && hook (lastaddr, scanline_events[i].pos - lastaddr, lasttype, closure)) { grub_free (scanline_events); return GRUB_ERR_NONE; } /* Update last values if necessary. */ if (curtype == -1 || curtype != lasttype) { lasttype = curtype; lastaddr = scanline_events[i].pos; } } grub_free (scanline_events); return GRUB_ERR_NONE; }
/* Claim some available memory in the first /memory node. */ static void grub_claim_heap (void) { unsigned long total = 0; auto int NESTED_FUNC_ATTR heap_init (grub_uint64_t addr, grub_uint64_t len, grub_uint32_t type); int NESTED_FUNC_ATTR heap_init (grub_uint64_t addr, grub_uint64_t len, grub_uint32_t type) { if (type != 1) return 0; if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_PRE1_5M_CLAIM)) { if (addr + len <= 0x180000) return 0; if (addr < 0x180000) { len = addr + len - 0x180000; addr = 0x180000; } } len -= 1; /* Required for some firmware. */ /* Never exceed HEAP_MAX_SIZE */ if (total + len > HEAP_MAX_SIZE) len = HEAP_MAX_SIZE - total; /* Avoid claiming anything above HEAP_MAX_ADDR, if possible. */ if ((addr < HEAP_MAX_ADDR) && /* if it's too late, don't bother */ (addr + len > HEAP_MAX_ADDR) && /* if it wasn't available anyway, don't bother */ (total + (HEAP_MAX_ADDR - addr) > HEAP_MIN_SIZE)) /* only limit ourselves when we can afford to */ len = HEAP_MAX_ADDR - addr; /* In theory, firmware should already prevent this from happening by not listing our own image in /memory/available. The check below is intended as a safeguard in case that doesn't happen. However, it doesn't protect us from corrupting our module area, which extends up to a yet-undetermined region above _end. */ if ((addr < (grub_addr_t) _end) && ((addr + len) > (grub_addr_t) _start)) { grub_printf ("Warning: attempt to claim over our own code!\n"); len = 0; } if (len) { /* Claim and use it. */ if (grub_claimmap (addr, len) < 0) return grub_error (GRUB_ERR_OUT_OF_MEMORY, "failed to claim heap at 0x%llx, len 0x%llx", addr, len); grub_mm_init_region ((void *) (grub_addr_t) addr, len); } total += len; if (total >= HEAP_MAX_SIZE) return 1; return 0; } if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CANNOT_INTERPRET)) heap_init (HEAP_MAX_ADDR - HEAP_MIN_SIZE, HEAP_MIN_SIZE, 1); else grub_machine_mmap_iterate (heap_init); }
void grub_machine_init (void) { int i; int grub_lower_mem; /* Initialize the console as early as possible. */ grub_console_init (); grub_lower_mem = grub_get_conv_memsize () << 10; /* Sanity check. */ if (grub_lower_mem < GRUB_MEMORY_MACHINE_RESERVED_END) grub_fatal ("too small memory"); /* FIXME: This prevents loader/i386/linux.c from using low memory. When our heap implements support for requesting a chunk in low memory, this should no longer be a problem. */ #if 0 /* Add the lower memory into free memory. */ if (grub_lower_mem >= GRUB_MEMORY_MACHINE_RESERVED_END) add_mem_region (GRUB_MEMORY_MACHINE_RESERVED_END, grub_lower_mem - GRUB_MEMORY_MACHINE_RESERVED_END); #endif auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_memory_type_t); int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type) { /* Avoid the lower memory. */ if (addr < 0x100000) { if (size <= 0x100000 - addr) return 0; size -= 0x100000 - addr; addr = 0x100000; } /* Ignore >4GB. */ if (addr <= 0xFFFFFFFF && type == GRUB_MEMORY_AVAILABLE) { grub_size_t len; len = (grub_size_t) ((addr + size > 0xFFFFFFFF) ? 0xFFFFFFFF - addr : size); add_mem_region (addr, len); } return 0; } grub_machine_mmap_iterate (hook); compact_mem_regions (); for (i = 0; i < num_regions; i++) grub_mm_init_region ((void *) mem_regions[i].addr, mem_regions[i].size); grub_tsc_init (); }