Path: blob/master/drivers/firmware/efi/libstub/unaccepted_memory.c
26483 views
// SPDX-License-Identifier: GPL-2.0-only12#include <linux/efi.h>3#include <asm/efi.h>4#include "efistub.h"56struct efi_unaccepted_memory *unaccepted_table;78efi_status_t allocate_unaccepted_bitmap(__u32 nr_desc,9struct efi_boot_memmap *map)10{11efi_guid_t unaccepted_table_guid = LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID;12u64 unaccepted_start = ULLONG_MAX, unaccepted_end = 0, bitmap_size;13efi_status_t status;14int i;1516/* Check if the table is already installed */17unaccepted_table = get_efi_config_table(unaccepted_table_guid);18if (unaccepted_table) {19if (unaccepted_table->version != 1) {20efi_err("Unknown version of unaccepted memory table\n");21return EFI_UNSUPPORTED;22}23return EFI_SUCCESS;24}2526/* Check if there's any unaccepted memory and find the max address */27for (i = 0; i < nr_desc; i++) {28efi_memory_desc_t *d;29unsigned long m = (unsigned long)map->map;3031d = efi_memdesc_ptr(m, map->desc_size, i);32if (d->type != EFI_UNACCEPTED_MEMORY)33continue;3435unaccepted_start = min(unaccepted_start, d->phys_addr);36unaccepted_end = max(unaccepted_end,37d->phys_addr + d->num_pages * PAGE_SIZE);38}3940if (unaccepted_start == ULLONG_MAX)41return EFI_SUCCESS;4243unaccepted_start = round_down(unaccepted_start,44EFI_UNACCEPTED_UNIT_SIZE);45unaccepted_end = round_up(unaccepted_end, EFI_UNACCEPTED_UNIT_SIZE);4647/*48* If unaccepted memory is present, allocate a bitmap to track what49* memory has to be accepted before access.50*51* One bit in the bitmap represents 2MiB in the address space:52* A 4k bitmap can track 64GiB of physical address space.53*54* In the worst case scenario -- a huge hole in the middle of the55* address space -- It needs 256MiB to handle 4PiB of the address56* space.57*58* The bitmap will be populated in setup_e820() according to the memory59* map after efi_exit_boot_services().60*/61bitmap_size = DIV_ROUND_UP(unaccepted_end - unaccepted_start,62EFI_UNACCEPTED_UNIT_SIZE * BITS_PER_BYTE);6364status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY,65sizeof(*unaccepted_table) + bitmap_size,66(void **)&unaccepted_table);67if (status != EFI_SUCCESS) {68efi_err("Failed to allocate unaccepted memory config table\n");69return status;70}7172unaccepted_table->version = 1;73unaccepted_table->unit_size = EFI_UNACCEPTED_UNIT_SIZE;74unaccepted_table->phys_base = unaccepted_start;75unaccepted_table->size = bitmap_size;76memset(unaccepted_table->bitmap, 0, bitmap_size);7778status = efi_bs_call(install_configuration_table,79&unaccepted_table_guid, unaccepted_table);80if (status != EFI_SUCCESS) {81efi_bs_call(free_pool, unaccepted_table);82efi_err("Failed to install unaccepted memory config table!\n");83}8485return status;86}8788/*89* The accepted memory bitmap only works at unit_size granularity. Take90* unaligned start/end addresses and either:91* 1. Accepts the memory immediately and in its entirety92* 2. Accepts unaligned parts, and marks *some* aligned part unaccepted93*94* The function will never reach the bitmap_set() with zero bits to set.95*/96void process_unaccepted_memory(u64 start, u64 end)97{98u64 unit_size = unaccepted_table->unit_size;99u64 unit_mask = unaccepted_table->unit_size - 1;100u64 bitmap_size = unaccepted_table->size;101102/*103* Ensure that at least one bit will be set in the bitmap by104* immediately accepting all regions under 2*unit_size. This is105* imprecise and may immediately accept some areas that could106* have been represented in the bitmap. But, results in simpler107* code below108*109* Consider case like this (assuming unit_size == 2MB):110*111* | 4k | 2044k | 2048k |112* ^ 0x0 ^ 2MB ^ 4MB113*114* Only the first 4k has been accepted. The 0MB->2MB region can not be115* represented in the bitmap. The 2MB->4MB region can be represented in116* the bitmap. But, the 0MB->4MB region is <2*unit_size and will be117* immediately accepted in its entirety.118*/119if (end - start < 2 * unit_size) {120arch_accept_memory(start, end);121return;122}123124/*125* No matter how the start and end are aligned, at least one unaccepted126* unit_size area will remain to be marked in the bitmap.127*/128129/* Immediately accept a <unit_size piece at the start: */130if (start & unit_mask) {131arch_accept_memory(start, round_up(start, unit_size));132start = round_up(start, unit_size);133}134135/* Immediately accept a <unit_size piece at the end: */136if (end & unit_mask) {137arch_accept_memory(round_down(end, unit_size), end);138end = round_down(end, unit_size);139}140141/*142* Accept part of the range that before phys_base and cannot be recorded143* into the bitmap.144*/145if (start < unaccepted_table->phys_base) {146arch_accept_memory(start,147min(unaccepted_table->phys_base, end));148start = unaccepted_table->phys_base;149}150151/* Nothing to record */152if (end < unaccepted_table->phys_base)153return;154155/* Translate to offsets from the beginning of the bitmap */156start -= unaccepted_table->phys_base;157end -= unaccepted_table->phys_base;158159/* Accept memory that doesn't fit into bitmap */160if (end > bitmap_size * unit_size * BITS_PER_BYTE) {161unsigned long phys_start, phys_end;162163phys_start = bitmap_size * unit_size * BITS_PER_BYTE +164unaccepted_table->phys_base;165phys_end = end + unaccepted_table->phys_base;166167arch_accept_memory(phys_start, phys_end);168end = bitmap_size * unit_size * BITS_PER_BYTE;169}170171/*172* 'start' and 'end' are now both unit_size-aligned.173* Record the range as being unaccepted:174*/175bitmap_set(unaccepted_table->bitmap,176start / unit_size, (end - start) / unit_size);177}178179void accept_memory(phys_addr_t start, unsigned long size)180{181unsigned long range_start, range_end;182phys_addr_t end = start + size;183unsigned long bitmap_size;184u64 unit_size;185186if (!unaccepted_table)187return;188189unit_size = unaccepted_table->unit_size;190191/*192* Only care for the part of the range that is represented193* in the bitmap.194*/195if (start < unaccepted_table->phys_base)196start = unaccepted_table->phys_base;197if (end < unaccepted_table->phys_base)198return;199200/* Translate to offsets from the beginning of the bitmap */201start -= unaccepted_table->phys_base;202end -= unaccepted_table->phys_base;203204/* Make sure not to overrun the bitmap */205if (end > unaccepted_table->size * unit_size * BITS_PER_BYTE)206end = unaccepted_table->size * unit_size * BITS_PER_BYTE;207208range_start = start / unit_size;209bitmap_size = DIV_ROUND_UP(end, unit_size);210211for_each_set_bitrange_from(range_start, range_end,212unaccepted_table->bitmap, bitmap_size) {213unsigned long phys_start, phys_end;214215phys_start = range_start * unit_size + unaccepted_table->phys_base;216phys_end = range_end * unit_size + unaccepted_table->phys_base;217218arch_accept_memory(phys_start, phys_end);219bitmap_clear(unaccepted_table->bitmap,220range_start, range_end - range_start);221}222}223224225