|
@@ -519,71 +519,47 @@ fail:
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
-static efi_status_t exit_boot(struct boot_params *boot_params,
|
|
|
- void *handle)
|
|
|
+static void add_e820ext(struct boot_params *params,
|
|
|
+ struct setup_data *e820ext, u32 nr_entries)
|
|
|
{
|
|
|
- struct efi_info *efi = &boot_params->efi_info;
|
|
|
- struct e820entry *e820_map = &boot_params->e820_map[0];
|
|
|
- struct e820entry *prev = NULL;
|
|
|
- unsigned long size, key, desc_size, _size;
|
|
|
- efi_memory_desc_t *mem_map;
|
|
|
+ struct setup_data *data;
|
|
|
efi_status_t status;
|
|
|
- __u32 desc_version;
|
|
|
- bool called_exit = false;
|
|
|
- u8 nr_entries;
|
|
|
- int i;
|
|
|
-
|
|
|
-get_map:
|
|
|
- status = efi_get_memory_map(sys_table, &mem_map, &size, &desc_size,
|
|
|
- &desc_version, &key);
|
|
|
-
|
|
|
- if (status != EFI_SUCCESS)
|
|
|
- return status;
|
|
|
+ unsigned long size;
|
|
|
|
|
|
- memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32));
|
|
|
- efi->efi_systab = (unsigned long)sys_table;
|
|
|
- efi->efi_memdesc_size = desc_size;
|
|
|
- efi->efi_memdesc_version = desc_version;
|
|
|
- efi->efi_memmap = (unsigned long)mem_map;
|
|
|
- efi->efi_memmap_size = size;
|
|
|
+ e820ext->type = SETUP_E820_EXT;
|
|
|
+ e820ext->len = nr_entries * sizeof(struct e820entry);
|
|
|
+ e820ext->next = 0;
|
|
|
|
|
|
-#ifdef CONFIG_X86_64
|
|
|
- efi->efi_systab_hi = (unsigned long)sys_table >> 32;
|
|
|
- efi->efi_memmap_hi = (unsigned long)mem_map >> 32;
|
|
|
-#endif
|
|
|
+ data = (struct setup_data *)(unsigned long)params->hdr.setup_data;
|
|
|
|
|
|
- /* Might as well exit boot services now */
|
|
|
- status = efi_call_phys2(sys_table->boottime->exit_boot_services,
|
|
|
- handle, key);
|
|
|
- if (status != EFI_SUCCESS) {
|
|
|
- /*
|
|
|
- * ExitBootServices() will fail if any of the event
|
|
|
- * handlers change the memory map. In which case, we
|
|
|
- * must be prepared to retry, but only once so that
|
|
|
- * we're guaranteed to exit on repeated failures instead
|
|
|
- * of spinning forever.
|
|
|
- */
|
|
|
- if (called_exit)
|
|
|
- goto free_mem_map;
|
|
|
+ while (data && data->next)
|
|
|
+ data = (struct setup_data *)(unsigned long)data->next;
|
|
|
|
|
|
- called_exit = true;
|
|
|
- efi_call_phys1(sys_table->boottime->free_pool, mem_map);
|
|
|
- goto get_map;
|
|
|
- }
|
|
|
+ if (data)
|
|
|
+ data->next = (unsigned long)e820ext;
|
|
|
+ else
|
|
|
+ params->hdr.setup_data = (unsigned long)e820ext;
|
|
|
+}
|
|
|
|
|
|
- /* Historic? */
|
|
|
- boot_params->alt_mem_k = 32 * 1024;
|
|
|
+static efi_status_t setup_e820(struct boot_params *params,
|
|
|
+ struct setup_data *e820ext, u32 e820ext_size)
|
|
|
+{
|
|
|
+ struct e820entry *e820_map = ¶ms->e820_map[0];
|
|
|
+ struct efi_info *efi = ¶ms->efi_info;
|
|
|
+ struct e820entry *prev = NULL;
|
|
|
+ u32 nr_entries;
|
|
|
+ u32 nr_desc;
|
|
|
+ int i;
|
|
|
|
|
|
- /*
|
|
|
- * Convert the EFI memory map to E820.
|
|
|
- */
|
|
|
nr_entries = 0;
|
|
|
- for (i = 0; i < size / desc_size; i++) {
|
|
|
+ nr_desc = efi->efi_memmap_size / efi->efi_memdesc_size;
|
|
|
+
|
|
|
+ for (i = 0; i < nr_desc; i++) {
|
|
|
efi_memory_desc_t *d;
|
|
|
unsigned int e820_type = 0;
|
|
|
- unsigned long m = (unsigned long)mem_map;
|
|
|
+ unsigned long m = efi->efi_memmap;
|
|
|
|
|
|
- d = (efi_memory_desc_t *)(m + (i * desc_size));
|
|
|
+ d = (efi_memory_desc_t *)(m + (i * efi->efi_memdesc_size));
|
|
|
switch (d->type) {
|
|
|
case EFI_RESERVED_TYPE:
|
|
|
case EFI_RUNTIME_SERVICES_CODE:
|
|
@@ -620,18 +596,142 @@ get_map:
|
|
|
|
|
|
/* Merge adjacent mappings */
|
|
|
if (prev && prev->type == e820_type &&
|
|
|
- (prev->addr + prev->size) == d->phys_addr)
|
|
|
+ (prev->addr + prev->size) == d->phys_addr) {
|
|
|
prev->size += d->num_pages << 12;
|
|
|
- else {
|
|
|
- e820_map->addr = d->phys_addr;
|
|
|
- e820_map->size = d->num_pages << 12;
|
|
|
- e820_map->type = e820_type;
|
|
|
- prev = e820_map++;
|
|
|
- nr_entries++;
|
|
|
+ continue;
|
|
|
}
|
|
|
+
|
|
|
+ if (nr_entries == ARRAY_SIZE(params->e820_map)) {
|
|
|
+ u32 need = (nr_desc - i) * sizeof(struct e820entry) +
|
|
|
+ sizeof(struct setup_data);
|
|
|
+
|
|
|
+ if (!e820ext || e820ext_size < need)
|
|
|
+ return EFI_BUFFER_TOO_SMALL;
|
|
|
+
|
|
|
+ /* boot_params map full, switch to e820 extended */
|
|
|
+ e820_map = (struct e820entry *)e820ext->data;
|
|
|
+ }
|
|
|
+
|
|
|
+ e820_map->addr = d->phys_addr;
|
|
|
+ e820_map->size = d->num_pages << PAGE_SHIFT;
|
|
|
+ e820_map->type = e820_type;
|
|
|
+ prev = e820_map++;
|
|
|
+ nr_entries++;
|
|
|
}
|
|
|
|
|
|
- boot_params->e820_entries = nr_entries;
|
|
|
+ if (nr_entries > ARRAY_SIZE(params->e820_map)) {
|
|
|
+ u32 nr_e820ext = nr_entries - ARRAY_SIZE(params->e820_map);
|
|
|
+
|
|
|
+ add_e820ext(params, e820ext, nr_e820ext);
|
|
|
+ nr_entries -= nr_e820ext;
|
|
|
+ }
|
|
|
+
|
|
|
+ params->e820_entries = (u8)nr_entries;
|
|
|
+
|
|
|
+ return EFI_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+static efi_status_t alloc_e820ext(u32 nr_desc, struct setup_data **e820ext,
|
|
|
+ u32 *e820ext_size)
|
|
|
+{
|
|
|
+ efi_status_t status;
|
|
|
+ unsigned long size;
|
|
|
+
|
|
|
+ size = sizeof(struct setup_data) +
|
|
|
+ sizeof(struct e820entry) * nr_desc;
|
|
|
+
|
|
|
+ if (*e820ext) {
|
|
|
+ efi_call_phys1(sys_table->boottime->free_pool, *e820ext);
|
|
|
+ *e820ext = NULL;
|
|
|
+ *e820ext_size = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = efi_call_phys3(sys_table->boottime->allocate_pool,
|
|
|
+ EFI_LOADER_DATA, size, e820ext);
|
|
|
+
|
|
|
+ if (status == EFI_SUCCESS)
|
|
|
+ *e820ext_size = size;
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+static efi_status_t exit_boot(struct boot_params *boot_params,
|
|
|
+ void *handle)
|
|
|
+{
|
|
|
+ struct efi_info *efi = &boot_params->efi_info;
|
|
|
+ unsigned long map_sz, key, desc_size;
|
|
|
+ efi_memory_desc_t *mem_map;
|
|
|
+ struct setup_data *e820ext;
|
|
|
+ __u32 e820ext_size;
|
|
|
+ __u32 nr_desc, prev_nr_desc;
|
|
|
+ efi_status_t status;
|
|
|
+ __u32 desc_version;
|
|
|
+ bool called_exit = false;
|
|
|
+ u8 nr_entries;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ nr_desc = 0;
|
|
|
+ e820ext = NULL;
|
|
|
+ e820ext_size = 0;
|
|
|
+
|
|
|
+get_map:
|
|
|
+ status = efi_get_memory_map(sys_table, &mem_map, &map_sz, &desc_size,
|
|
|
+ &desc_version, &key);
|
|
|
+
|
|
|
+ if (status != EFI_SUCCESS)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ prev_nr_desc = nr_desc;
|
|
|
+ nr_desc = map_sz / desc_size;
|
|
|
+ if (nr_desc > prev_nr_desc &&
|
|
|
+ nr_desc > ARRAY_SIZE(boot_params->e820_map)) {
|
|
|
+ u32 nr_e820ext = nr_desc - ARRAY_SIZE(boot_params->e820_map);
|
|
|
+
|
|
|
+ status = alloc_e820ext(nr_e820ext, &e820ext, &e820ext_size);
|
|
|
+ if (status != EFI_SUCCESS)
|
|
|
+ goto free_mem_map;
|
|
|
+
|
|
|
+ efi_call_phys1(sys_table->boottime->free_pool, mem_map);
|
|
|
+ goto get_map; /* Allocated memory, get map again */
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32));
|
|
|
+ efi->efi_systab = (unsigned long)sys_table;
|
|
|
+ efi->efi_memdesc_size = desc_size;
|
|
|
+ efi->efi_memdesc_version = desc_version;
|
|
|
+ efi->efi_memmap = (unsigned long)mem_map;
|
|
|
+ efi->efi_memmap_size = map_sz;
|
|
|
+
|
|
|
+#ifdef CONFIG_X86_64
|
|
|
+ efi->efi_systab_hi = (unsigned long)sys_table >> 32;
|
|
|
+ efi->efi_memmap_hi = (unsigned long)mem_map >> 32;
|
|
|
+#endif
|
|
|
+
|
|
|
+ /* Might as well exit boot services now */
|
|
|
+ status = efi_call_phys2(sys_table->boottime->exit_boot_services,
|
|
|
+ handle, key);
|
|
|
+ if (status != EFI_SUCCESS) {
|
|
|
+ /*
|
|
|
+ * ExitBootServices() will fail if any of the event
|
|
|
+ * handlers change the memory map. In which case, we
|
|
|
+ * must be prepared to retry, but only once so that
|
|
|
+ * we're guaranteed to exit on repeated failures instead
|
|
|
+ * of spinning forever.
|
|
|
+ */
|
|
|
+ if (called_exit)
|
|
|
+ goto free_mem_map;
|
|
|
+
|
|
|
+ called_exit = true;
|
|
|
+ efi_call_phys1(sys_table->boottime->free_pool, mem_map);
|
|
|
+ goto get_map;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Historic? */
|
|
|
+ boot_params->alt_mem_k = 32 * 1024;
|
|
|
+
|
|
|
+ status = setup_e820(boot_params, e820ext, e820ext_size);
|
|
|
+ if (status != EFI_SUCCESS)
|
|
|
+ return status;
|
|
|
|
|
|
return EFI_SUCCESS;
|
|
|
|