root/sys/kern/subr_efi_map.c
/*
 * Copyright (c) 2014 The FreeBSD Foundation
 * Copyright (c) 2018 Andrew Turner
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/efi.h>
#include <sys/efi_map.h>
#include <sys/physmem.h>

#include <machine/efi.h>
#include <machine/vmparam.h>

void
efi_map_foreach_entry(struct efi_map_header *efihdr, efi_map_entry_cb cb, void *argp)
{
        struct efi_md *map, *p;
        size_t efisz;
        int ndesc, i;

        /*
         * Memory map data provided by UEFI via the GetMemoryMap
         * Boot Services API.
         */
        efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf;
        map = (struct efi_md *)((uint8_t *)efihdr + efisz);

        if (efihdr->descriptor_size == 0)
                return;
        ndesc = efihdr->memory_size / efihdr->descriptor_size;

        for (i = 0, p = map; i < ndesc; i++,
            p = efi_next_descriptor(p, efihdr->descriptor_size)) {
                cb(p, argp);
        }
}

/*
 * Handle the EFI memory map list.
 *
 * We will make two passes at this, the first (exclude == false) to populate
 * physmem with valid physical memory ranges from recognized map entry types.
 * In the second pass we will exclude memory ranges from physmem which must not
 * be used for general allocations, either because they are used by runtime
 * firmware or otherwise reserved.
 *
 * Adding the runtime-reserved memory ranges to physmem and excluding them
 * later ensures that they are included in the DMAP, but excluded from
 * phys_avail[].
 *
 * Entry types not explicitly listed here are ignored and not mapped.
 */
static void
handle_efi_map_entry(struct efi_md *p, void *argp)
{
        bool exclude = *(bool *)argp;

        switch (p->md_type) {
        case EFI_MD_TYPE_RECLAIM:
                /*
                 * The recomended location for ACPI tables. Map into the
                 * DMAP so we can access them from userspace via /dev/mem.
                 */
        case EFI_MD_TYPE_RT_CODE:
                /*
                 * Some UEFI implementations put the system table in the
                 * runtime code section. Include it in the DMAP, but will
                 * be excluded from phys_avail.
                 */
        case EFI_MD_TYPE_RT_DATA:
                /*
                 * Runtime data will be excluded after the DMAP
                 * region is created to stop it from being added
                 * to phys_avail.
                 */
                if (exclude) {
                        physmem_exclude_region(p->md_phys,
                            p->md_pages * EFI_PAGE_SIZE, EXFLAG_NOALLOC);
                        break;
                }
                /* FALLTHROUGH */
        case EFI_MD_TYPE_CODE:
        case EFI_MD_TYPE_DATA:
        case EFI_MD_TYPE_BS_CODE:
        case EFI_MD_TYPE_BS_DATA:
        case EFI_MD_TYPE_FREE:
                /*
                 * We're allowed to use any entry with these types.
                 */
                if (!exclude)
                        physmem_hardware_region(p->md_phys,
                            p->md_pages * EFI_PAGE_SIZE);
                break;
        default:
                /* Other types shall not be handled by physmem. */
                break;
        }
}

void
efi_map_add_entries(struct efi_map_header *efihdr)
{
        bool exclude = false;
        efi_map_foreach_entry(efihdr, handle_efi_map_entry, &exclude);
}

void
efi_map_exclude_entries(struct efi_map_header *efihdr)
{
        bool exclude = true;
        efi_map_foreach_entry(efihdr, handle_efi_map_entry, &exclude);
}

static void
print_efi_map_entry(struct efi_md *p, void *argp __unused)
{
        const char *type;
        static const char *types[] = {
                "Reserved",
                "LoaderCode",
                "LoaderData",
                "BootServicesCode",
                "BootServicesData",
                "RuntimeServicesCode",
                "RuntimeServicesData",
                "ConventionalMemory",
                "UnusableMemory",
                "ACPIReclaimMemory",
                "ACPIMemoryNVS",
                "MemoryMappedIO",
                "MemoryMappedIOPortSpace",
                "PalCode",
                "PersistentMemory"
        };

        if (p->md_type < nitems(types))
                type = types[p->md_type];
        else
                type = "<INVALID>";
        printf("%23s %012jx %012jx %08jx ", type, (uintmax_t)p->md_phys,
            (uintmax_t)p->md_virt, (uintmax_t)p->md_pages);
        if (p->md_attr & EFI_MD_ATTR_UC)
                printf("UC ");
        if (p->md_attr & EFI_MD_ATTR_WC)
                printf("WC ");
        if (p->md_attr & EFI_MD_ATTR_WT)
                printf("WT ");
        if (p->md_attr & EFI_MD_ATTR_WB)
                printf("WB ");
        if (p->md_attr & EFI_MD_ATTR_UCE)
                printf("UCE ");
        if (p->md_attr & EFI_MD_ATTR_WP)
                printf("WP ");
        if (p->md_attr & EFI_MD_ATTR_RP)
                printf("RP ");
        if (p->md_attr & EFI_MD_ATTR_XP)
                printf("XP ");
        if (p->md_attr & EFI_MD_ATTR_NV)
                printf("NV ");
        if (p->md_attr & EFI_MD_ATTR_MORE_RELIABLE)
                printf("MORE_RELIABLE ");
        if (p->md_attr & EFI_MD_ATTR_RO)
                printf("RO ");
        if (p->md_attr & EFI_MD_ATTR_RT)
                printf("RUNTIME");
        printf("\n");
}

void
efi_map_print_entries(struct efi_map_header *efihdr)
{

        printf("%23s %12s %12s %8s %4s\n",
            "Type", "Physical", "Virtual", "#Pages", "Attr");
        efi_map_foreach_entry(efihdr, print_efi_map_entry, NULL);
}