/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2016 Toomas Soome */ /* * Build smap like memory map from efi memmap. */ #include #include #include #include #include #include #include #include #include #include #include "bootstrap.h" struct smap_buf { struct bios_smap sb_smap; STAILQ_ENTRY(smap_buf) sb_bufs; }; static struct bios_smap *smapbase; static int smaplen; /* * See ACPI 6.1 Table 15-330 UEFI Memory Types and mapping to ACPI address * range types. */ static int smap_type(int type) { switch (type) { case EfiLoaderCode: case EfiLoaderData: case EfiBootServicesCode: case EfiBootServicesData: case EfiConventionalMemory: return (SMAP_TYPE_MEMORY); case EfiReservedMemoryType: case EfiRuntimeServicesCode: case EfiRuntimeServicesData: case EfiMemoryMappedIO: case EfiMemoryMappedIOPortSpace: case EfiPalCode: case EfiUnusableMemory: return (SMAP_TYPE_RESERVED); case EfiACPIReclaimMemory: return (SMAP_TYPE_ACPI_RECLAIM); case EfiACPIMemoryNVS: return (SMAP_TYPE_ACPI_NVS); } return (SMAP_TYPE_RESERVED); } void efi_getsmap(void) { UINTN size, desc_size, key; EFI_MEMORY_DESCRIPTOR *efi_mmap, *p; EFI_PHYSICAL_ADDRESS addr; EFI_STATUS status; STAILQ_HEAD(smap_head, smap_buf) head = STAILQ_HEAD_INITIALIZER(head); struct smap_buf *cur, *next; int i, n, ndesc; int type = -1; size = 0; efi_mmap = NULL; status = BS->GetMemoryMap(&size, efi_mmap, &key, &desc_size, NULL); efi_mmap = malloc(size); status = BS->GetMemoryMap(&size, efi_mmap, &key, &desc_size, NULL); if (EFI_ERROR(status)) { printf("GetMemoryMap: error %lu\n", EFI_ERROR_CODE(status)); free(efi_mmap); return; } STAILQ_INIT(&head); n = 0; i = 0; p = efi_mmap; next = NULL; ndesc = size / desc_size; while (i < ndesc) { if (next == NULL) { next = malloc(sizeof(*next)); if (next == NULL) break; next->sb_smap.base = p->PhysicalStart; next->sb_smap.length = p->NumberOfPages << EFI_PAGE_SHIFT; /* * ACPI 6.1 tells the lower memory should be * reported as normal memory, so we enforce * page 0 type even as vmware maps it as * acpi reclaimable. */ if (next->sb_smap.base == 0) type = SMAP_TYPE_MEMORY; else type = smap_type(p->Type); next->sb_smap.type = type; STAILQ_INSERT_TAIL(&head, next, sb_bufs); n++; p = NextMemoryDescriptor(p, desc_size); i++; continue; } addr = next->sb_smap.base + next->sb_smap.length; if ((smap_type(p->Type) == type) && (p->PhysicalStart == addr)) { next->sb_smap.length += (p->NumberOfPages << EFI_PAGE_SHIFT); p = NextMemoryDescriptor(p, desc_size); i++; } else next = NULL; } smaplen = n; if (smaplen > 0) { smapbase = malloc(smaplen * sizeof(*smapbase)); if (smapbase != NULL) { n = 0; STAILQ_FOREACH(cur, &head, sb_bufs) smapbase[n++] = cur->sb_smap; } cur = STAILQ_FIRST(&head); while (cur != NULL) { next = STAILQ_NEXT(cur, sb_bufs); free(cur); cur = next; } } free(efi_mmap); } void efi_addsmapdata(struct preloaded_file *kfp) { size_t size; if (smapbase == NULL || smaplen == 0) return; size = smaplen * sizeof(*smapbase); file_addmetadata(kfp, MODINFOMD_SMAP, size, smapbase); } COMMAND_SET(smap, "smap", "show BIOS SMAP", command_smap); static int command_smap(int argc __unused, char *argv[] __unused) { u_int i; if (smapbase == NULL || smaplen == 0) return (CMD_ERROR); for (i = 0; i < smaplen; i++) printf("SMAP type=%02" PRIx32 " base=%016" PRIx64 " len=%016" PRIx64 "\n", smapbase[i].type, smapbase[i].base, smapbase[i].length); return (CMD_OK); }