uboot/cmd/efi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2015 Google, Inc
   4 * Written by Simon Glass <sjg@chromium.org>
   5 */
   6
   7#include <common.h>
   8#include <command.h>
   9#include <efi.h>
  10#include <errno.h>
  11#include <log.h>
  12#include <malloc.h>
  13#include <sort.h>
  14#include <asm/global_data.h>
  15
  16static const char *const type_name[] = {
  17        "reserved",
  18        "loader_code",
  19        "loader_data",
  20        "bs_code",
  21        "bs_data",
  22        "rt_code",
  23        "rt_data",
  24        "conv",
  25        "unusable",
  26        "acpi_reclaim",
  27        "acpi_nvs",
  28        "io",
  29        "io_port",
  30        "pal_code",
  31};
  32
  33static struct attr_info {
  34        u64 val;
  35        const char *name;
  36} mem_attr[] = {
  37        { EFI_MEMORY_UC, "uncached" },
  38        { EFI_MEMORY_WC, "write-coalescing" },
  39        { EFI_MEMORY_WT, "write-through" },
  40        { EFI_MEMORY_WB, "write-back" },
  41        { EFI_MEMORY_UCE, "uncached & exported" },
  42        { EFI_MEMORY_WP, "write-protect" },
  43        { EFI_MEMORY_RP, "read-protect" },
  44        { EFI_MEMORY_XP, "execute-protect" },
  45        { EFI_MEMORY_NV, "non-volatile" },
  46        { EFI_MEMORY_MORE_RELIABLE, "higher reliability" },
  47        { EFI_MEMORY_RO, "read-only" },
  48        { EFI_MEMORY_SP, "specific purpose" },
  49        { EFI_MEMORY_RUNTIME, "needs runtime mapping" }
  50};
  51
  52/* Maximum different attribute values we can track */
  53#define ATTR_SEEN_MAX   30
  54
  55static inline bool is_boot_services(int type)
  56{
  57        return type == EFI_LOADER_CODE || type == EFI_LOADER_DATA ||
  58                type == EFI_BOOT_SERVICES_CODE ||
  59                type == EFI_BOOT_SERVICES_DATA;
  60}
  61
  62static int h_cmp_entry(const void *v1, const void *v2)
  63{
  64        const struct efi_mem_desc *desc1 = v1;
  65        const struct efi_mem_desc *desc2 = v2;
  66        int64_t diff = desc1->physical_start - desc2->physical_start;
  67
  68        /*
  69         * Manually calculate the difference to avoid sign loss in the 64-bit
  70         * to 32-bit conversion
  71         */
  72        return diff < 0 ? -1 : diff > 0 ? 1 : 0;
  73}
  74
  75/**
  76 * efi_build_mem_table() - make a sorted copy of the memory table
  77 *
  78 * @map:        Pointer to EFI memory map table
  79 * @size:       Size of table in bytes
  80 * @skip_bs:    True to skip boot-time memory and merge it with conventional
  81 *              memory. This will significantly reduce the number of table
  82 *              entries.
  83 * Return:      pointer to the new table. It should be freed with free() by the
  84 *              caller.
  85 */
  86static void *efi_build_mem_table(struct efi_entry_memmap *map, int size,
  87                                 bool skip_bs)
  88{
  89        struct efi_mem_desc *desc, *end, *base, *dest, *prev;
  90        int count;
  91        u64 addr;
  92
  93        base = malloc(size + sizeof(*desc));
  94        if (!base) {
  95                debug("%s: Cannot allocate %#x bytes\n", __func__, size);
  96                return NULL;
  97        }
  98        end = (struct efi_mem_desc *)((ulong)map + size);
  99        count = ((ulong)end - (ulong)map->desc) / map->desc_size;
 100        memcpy(base, map->desc, (ulong)end - (ulong)map->desc);
 101        qsort(base, count, map->desc_size, h_cmp_entry);
 102        prev = NULL;
 103        addr = 0;
 104        dest = base;
 105        end = (struct efi_mem_desc *)((ulong)base + count * map->desc_size);
 106        for (desc = base; desc < end; desc = efi_get_next_mem_desc(map, desc)) {
 107                bool merge = true;
 108                u32 type = desc->type;
 109
 110                if (type >= EFI_MAX_MEMORY_TYPE) {
 111                        printf("Memory map contains invalid entry type %u\n",
 112                               type);
 113                        continue;
 114                }
 115
 116                if (skip_bs && is_boot_services(desc->type))
 117                        type = EFI_CONVENTIONAL_MEMORY;
 118
 119                memcpy(dest, desc, map->desc_size);
 120                dest->type = type;
 121                if (!skip_bs || !prev)
 122                        merge = false;
 123                else if (desc->physical_start != addr)
 124                        merge = false;
 125                else if (type != EFI_CONVENTIONAL_MEMORY)
 126                        merge = false;
 127                else if (prev->type != EFI_CONVENTIONAL_MEMORY)
 128                        merge = false;
 129
 130                if (merge) {
 131                        prev->num_pages += desc->num_pages;
 132                } else {
 133                        prev = dest;
 134                        dest = efi_get_next_mem_desc(map, dest);
 135                }
 136                addr = desc->physical_start + (desc->num_pages <<
 137                                EFI_PAGE_SHIFT);
 138        }
 139
 140        /* Mark the end */
 141        dest->type = EFI_MAX_MEMORY_TYPE;
 142
 143        return base;
 144}
 145
 146static void efi_print_mem_table(struct efi_entry_memmap *map,
 147                                struct efi_mem_desc *desc, bool skip_bs)
 148{
 149        u64 attr_seen[ATTR_SEEN_MAX];
 150        int attr_seen_count;
 151        int upto, i;
 152        u64 addr;
 153
 154        printf(" #  %-14s  %10s  %10s  %10s  %s\n", "Type", "Physical",
 155               "Virtual", "Size", "Attributes");
 156
 157        /* Keep track of all the different attributes we have seen */
 158        attr_seen_count = 0;
 159        addr = 0;
 160        for (upto = 0; desc->type != EFI_MAX_MEMORY_TYPE;
 161             upto++, desc = efi_get_next_mem_desc(map, desc)) {
 162                const char *name;
 163                u64 size;
 164
 165                if (skip_bs && is_boot_services(desc->type))
 166                        continue;
 167                if (desc->physical_start != addr) {
 168                        printf("    %-14s  %010llx  %10s  %010llx\n", "<gap>",
 169                               addr, "", desc->physical_start - addr);
 170                }
 171                size = desc->num_pages << EFI_PAGE_SHIFT;
 172
 173                name = desc->type < ARRAY_SIZE(type_name) ?
 174                                type_name[desc->type] : "<invalid>";
 175                printf("%2d  %x:%-12s  %010llx  %010llx  %010llx  ", upto,
 176                       desc->type, name, desc->physical_start,
 177                       desc->virtual_start, size);
 178                if (desc->attribute & EFI_MEMORY_RUNTIME)
 179                        putc('r');
 180                printf("%llx", desc->attribute & ~EFI_MEMORY_RUNTIME);
 181                putc('\n');
 182
 183                for (i = 0; i < attr_seen_count; i++) {
 184                        if (attr_seen[i] == desc->attribute)
 185                                break;
 186                }
 187                if (i == attr_seen_count && i < ATTR_SEEN_MAX)
 188                        attr_seen[attr_seen_count++] = desc->attribute;
 189                addr = desc->physical_start + size;
 190        }
 191
 192        printf("\nAttributes key:\n");
 193        for (i = 0; i < attr_seen_count; i++) {
 194                u64 attr = attr_seen[i];
 195                bool first;
 196                int j;
 197
 198                printf("%c%llx: ", (attr & EFI_MEMORY_RUNTIME) ? 'r' : ' ',
 199                       attr & ~EFI_MEMORY_RUNTIME);
 200                for (j = 0, first = true; j < ARRAY_SIZE(mem_attr); j++) {
 201                        if (attr & mem_attr[j].val) {
 202                                if (first)
 203                                        first = false;
 204                                else
 205                                        printf(", ");
 206                                printf("%s", mem_attr[j].name);
 207                        }
 208                }
 209                putc('\n');
 210        }
 211        if (skip_bs)
 212                printf("*Some areas are merged (use 'all' to see)\n");
 213}
 214
 215static int do_efi_mem(struct cmd_tbl *cmdtp, int flag, int argc,
 216                      char *const argv[])
 217{
 218        struct efi_mem_desc *desc;
 219        struct efi_entry_memmap *map;
 220        int size, ret;
 221        bool skip_bs;
 222
 223        skip_bs = !argc || *argv[0] != 'a';
 224        ret = efi_info_get(EFIET_MEMORY_MAP, (void **)&map, &size);
 225        switch (ret) {
 226        case -ENOENT:
 227                printf("No EFI table available\n");
 228                goto done;
 229        case -EPROTONOSUPPORT:
 230                printf("Incorrect EFI table version\n");
 231                goto done;
 232        }
 233        printf("EFI table at %lx, memory map %p, size %x, version %x, descr. size %#x\n",
 234               gd->arch.table, map, size, map->version, map->desc_size);
 235        if (map->version != EFI_MEM_DESC_VERSION) {
 236                printf("Incorrect memory map version\n");
 237                ret = -EPROTONOSUPPORT;
 238                goto done;
 239        }
 240
 241        desc = efi_build_mem_table(map, size, skip_bs);
 242        if (!desc) {
 243                ret = -ENOMEM;
 244                goto done;
 245        }
 246
 247        efi_print_mem_table(map, desc, skip_bs);
 248        free(desc);
 249done:
 250        if (ret)
 251                printf("Error: %d\n", ret);
 252
 253        return ret ? CMD_RET_FAILURE : 0;
 254}
 255
 256static struct cmd_tbl efi_commands[] = {
 257        U_BOOT_CMD_MKENT(mem, 1, 1, do_efi_mem, "", ""),
 258};
 259
 260static int do_efi(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 261{
 262        struct cmd_tbl *efi_cmd;
 263        int ret;
 264
 265        if (argc < 2)
 266                return CMD_RET_USAGE;
 267        efi_cmd = find_cmd_tbl(argv[1], efi_commands, ARRAY_SIZE(efi_commands));
 268        argc -= 2;
 269        argv += 2;
 270        if (!efi_cmd || argc > efi_cmd->maxargs)
 271                return CMD_RET_USAGE;
 272
 273        ret = efi_cmd->cmd(efi_cmd, flag, argc, argv);
 274
 275        return cmd_process_error(efi_cmd, ret);
 276}
 277
 278U_BOOT_CMD(
 279        efi,     3,      1,      do_efi,
 280        "EFI access",
 281        "mem [all]        Dump memory information [include boot services]"
 282);
 283