From 8341f1b9e858c44425435ae2c3fef8610a10069d Mon Sep 17 00:00:00 2001 From: Toomas Soome Date: Sat, 3 Feb 2018 14:16:26 +0200 Subject: [PATCH] 9022 loader.efi: module placement must check memory map --- usr/src/boot/sys/boot/common/multiboot2.c | 48 ++++++++---- usr/src/boot/sys/boot/efi/loader/copy.c | 106 +++++++++++++++++++++++++- usr/src/boot/sys/boot/efi/loader/loader_efi.h | 2 + 3 files changed, 139 insertions(+), 17 deletions(-) diff --git a/usr/src/boot/sys/boot/common/multiboot2.c b/usr/src/boot/sys/boot/common/multiboot2.c index 876a5cd6a2..0e3b7bbaa3 100644 --- a/usr/src/boot/sys/boot/common/multiboot2.c +++ b/usr/src/boot/sys/boot/common/multiboot2.c @@ -791,8 +791,9 @@ multiboot2_exec(struct preloaded_file *fp) size_t size; struct bios_smap *smap; #if defined (EFI) - multiboot_tag_module_t *module; + multiboot_tag_module_t *module, *mp; EFI_MEMORY_DESCRIPTOR *map; + UINTN map_size, desc_size; struct relocator *relocator; struct chunk_head *head; struct chunk *chunk; @@ -928,7 +929,7 @@ multiboot2_exec(struct preloaded_file *fp) * - tmp != mfp->f_addr only in case of EFI. */ #if defined (EFI) - tmp = roundup2(load_addr + fp->f_size, MULTIBOOT_MOD_ALIGN); + tmp = roundup2(load_addr + fp->f_size + 1, MULTIBOOT_MOD_ALIGN); module = (multiboot_tag_module_t *)last_addr; #endif @@ -958,9 +959,12 @@ multiboot2_exec(struct preloaded_file *fp) tag->mb_type = MULTIBOOT_TAG_TYPE_MODULE; tag->mb_size = sizeof (*tag) + num; #if defined (EFI) - tag->mb_mod_start = tmp; - tag->mb_mod_end = tmp + mfp->f_size; - tmp = roundup2(tag->mb_mod_end + 1, MULTIBOOT_MOD_ALIGN); + /* + * We can assign module addresses only after BS have been + * switched off. + */ + tag->mb_mod_start = 0; + tag->mb_mod_end = mfp->f_size; #else tag->mb_mod_start = mfp->f_addr; tag->mb_mod_end = mfp->f_addr + mfp->f_size; @@ -1100,21 +1104,21 @@ multiboot2_exec(struct preloaded_file *fp) /* Leave EFI memmap last as we will also switch off the BS. */ { multiboot_tag_efi_mmap_t *tag; - UINTN size, desc_size, key; + UINTN key; EFI_STATUS status; tag = (multiboot_tag_efi_mmap_t *) mb_malloc(sizeof (*tag)); - size = 0; - status = BS->GetMemoryMap(&size, + map_size = 0; + status = BS->GetMemoryMap(&map_size, (EFI_MEMORY_DESCRIPTOR *)tag->mb_efi_mmap, &key, &desc_size, &tag->mb_descr_vers); if (status != EFI_BUFFER_TOO_SMALL) { error = EINVAL; goto error; } - status = BS->GetMemoryMap(&size, + status = BS->GetMemoryMap(&map_size, (EFI_MEMORY_DESCRIPTOR *)tag->mb_efi_mmap, &key, &desc_size, &tag->mb_descr_vers); if (EFI_ERROR(status)) { @@ -1122,7 +1126,7 @@ multiboot2_exec(struct preloaded_file *fp) goto error; } tag->mb_type = MULTIBOOT_TAG_TYPE_EFI_MMAP; - tag->mb_size = sizeof (*tag) + size; + tag->mb_size = sizeof (*tag) + map_size; tag->mb_descr_size = (uint32_t) desc_size; /* @@ -1132,7 +1136,7 @@ multiboot2_exec(struct preloaded_file *fp) * relocator data, trampoline, copy, memmove, stack. */ for (i = 0, map = (EFI_MEMORY_DESCRIPTOR *)tag->mb_efi_mmap; - i < size / desc_size; + i < map_size / desc_size; i++, map = NextMemoryDescriptor(map, desc_size)) { if (map->PhysicalStart == 0) continue; @@ -1154,7 +1158,7 @@ multiboot2_exec(struct preloaded_file *fp) } } - last_addr += size; + last_addr += map_size; last_addr = roundup2(last_addr, MULTIBOOT_TAG_ALIGN); } #endif @@ -1191,20 +1195,32 @@ multiboot2_exec(struct preloaded_file *fp) STAILQ_INSERT_TAIL(head, chunk, chunk_next); + mp = module; for (mfp = fp->f_next; mfp != NULL; mfp = mfp->f_next) { chunk = &relocator->rel_chunklist[i++]; chunk->chunk_vaddr = mfp->f_addr; - chunk->chunk_paddr = module->mb_mod_start; + + /* + * fix the mb_mod_start and mb_mod_end. + */ + mp->mb_mod_start = efi_physaddr(module, tmp, map, + map_size / desc_size, desc_size, mp->mb_mod_end); + if (mp->mb_mod_start == 0) + panic("Could not find memory for module\n"); + + mp->mb_mod_end += mp->mb_mod_start; + chunk->chunk_paddr = mp->mb_mod_start; chunk->chunk_size = mfp->f_size; STAILQ_INSERT_TAIL(head, chunk, chunk_next); - module = (multiboot_tag_module_t *) - roundup2((uintptr_t)module + module->mb_size, + mp = (multiboot_tag_module_t *) + roundup2((uintptr_t)mp + mp->mb_size, MULTIBOOT_TAG_ALIGN); } chunk = &relocator->rel_chunklist[i++]; chunk->chunk_vaddr = (EFI_VIRTUAL_ADDRESS)mbi; - chunk->chunk_paddr = tmp; + chunk->chunk_paddr = efi_physaddr(module, tmp, map, + map_size / desc_size, desc_size, mbi->mbi_total_size); chunk->chunk_size = mbi->mbi_total_size; STAILQ_INSERT_TAIL(head, chunk, chunk_next); diff --git a/usr/src/boot/sys/boot/efi/loader/copy.c b/usr/src/boot/sys/boot/efi/loader/copy.c index a4f44bc072..9658a659d4 100644 --- a/usr/src/boot/sys/boot/efi/loader/copy.c +++ b/usr/src/boot/sys/boot/efi/loader/copy.c @@ -1,4 +1,4 @@ -/*- +/* * Copyright (c) 2013 The FreeBSD Foundation * All rights reserved. * @@ -41,6 +41,110 @@ #include "loader_efi.h" /* + * Verify the address is not in use by existing modules. + */ +static vm_offset_t +addr_verify(multiboot_tag_module_t *module, vm_offset_t addr, size_t size) +{ + vm_offset_t start, end; + + for (;module->mb_type == MULTIBOOT_TAG_TYPE_MODULE; + module = (multiboot_tag_module_t *) + roundup((uintptr_t)module + module->mb_size, MULTIBOOT_TAG_ALIGN)) { + + start = module->mb_mod_start; + end = module->mb_mod_end; + + /* Does this module have address assigned? */ + if (start == 0) + continue; + + if ((start <= addr) && (end >= addr)) { + return (0); + } + if ((start >= addr) && (start <= addr + size)) { + return (0); + } + } + return (addr); +} + +/* + * Find memory map entry above 1MB, able to contain size bytes from addr. + */ +static vm_offset_t +memmap_find(EFI_MEMORY_DESCRIPTOR *map, size_t count, UINTN dsize, + vm_offset_t addr, size_t size) +{ + int i; + + for (i = 0; i < count; i++, map = NextMemoryDescriptor(map, dsize)) { + + if (map->Type != EfiConventionalMemory) + continue; + + /* We do not want address below 1MB. */ + if (map->PhysicalStart < 0x100000) + continue; + + /* Do we fit into current entry? */ + if ((map->PhysicalStart <= addr) && + (map->PhysicalStart + + (map->NumberOfPages << EFI_PAGE_SHIFT) >= addr + size)) { + return (addr); + } + + /* Do we fit into new entry? */ + if ((map->PhysicalStart > addr) && + (map->NumberOfPages >= EFI_SIZE_TO_PAGES(size))) { + return (map->PhysicalStart); + } + } + return (0); +} + +/* + * Find usable address for loading. The address for the kernel is fixed, as + * it is determined by kernel linker map (dboot PT_LOAD address). + * For modules, we need to consult memory map, the module address has to be + * aligned to page boundary and we have to fit into map entry. + */ +vm_offset_t +efi_physaddr(multiboot_tag_module_t *module, vm_offset_t addr, + EFI_MEMORY_DESCRIPTOR *map, size_t count, UINTN dsize, size_t size) +{ + multiboot_tag_module_t *mp; + vm_offset_t off; + + if (addr == 0) + return (addr); + + mp = module; + do { + off = addr; + /* Test proposed address */ + off = memmap_find(map, count, dsize, off, size); + if (off != 0) + off = addr_verify(module, off, size); + if (off != 0) + break; + + /* The module list is exhausted */ + if (mp->mb_type != MULTIBOOT_TAG_TYPE_MODULE) + break; + + if (mp->mb_mod_start != 0) { + addr = roundup2(mp->mb_mod_end + 1, + MULTIBOOT_MOD_ALIGN); + } + mp = (multiboot_tag_module_t *) + roundup((uintptr_t)mp + mp->mb_size, MULTIBOOT_TAG_ALIGN); + } while (off == 0); + + return (off); +} + +/* * Allocate pages for data to be loaded. As we can not expect AllocateAddress * to succeed, we allocate using AllocateMaxAddress from 4GB limit. * 4GB limit is because reportedly some 64bit systems are reported to have diff --git a/usr/src/boot/sys/boot/efi/loader/loader_efi.h b/usr/src/boot/sys/boot/efi/loader/loader_efi.h index 6378a662d3..59b1ecc5bd 100644 --- a/usr/src/boot/sys/boot/efi/loader/loader_efi.h +++ b/usr/src/boot/sys/boot/efi/loader/loader_efi.h @@ -65,6 +65,8 @@ ssize_t efi_readin(const int, vm_offset_t, const size_t); uint64_t efi_loadaddr(u_int, void *, uint64_t); void efi_free_loadaddr(uint64_t, uint64_t); void * efi_translate(vm_offset_t); +vm_offset_t efi_physaddr(multiboot_tag_module_t *, vm_offset_t, + EFI_MEMORY_DESCRIPTOR *, size_t, UINTN, size_t); multiboot2_info_header_t *efi_copy_finish(struct relocator *); void multiboot_tramp(uint32_t, struct relocator *, uint64_t); -- 2.11.4.GIT