1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2016 Toomas Soome <tsoome@me.com> 14 */ 15 16 /* 17 * Build smap like memory map from efi memmap. 18 */ 19 20 #include <stand.h> 21 #include <inttypes.h> 22 #include <efi.h> 23 #include <efilib.h> 24 #include <sys/param.h> 25 #include <sys/linker.h> 26 #include <sys/queue.h> 27 #include <sys/stddef.h> 28 #include <machine/metadata.h> 29 #include <machine/pc/bios.h> 30 #include "bootstrap.h" 31 32 struct smap_buf { 33 struct bios_smap sb_smap; 34 STAILQ_ENTRY(smap_buf) sb_bufs; 35 }; 36 37 static struct bios_smap *smapbase; 38 static int smaplen; 39 40 /* 41 * See "ACPI Specification Release 6.5 Errata A" 42 * Table 15-6 (page 785), UEFI Memory Types and mapping to ACPI address 43 * range types. 44 */ 45 static int 46 smap_type(int type) 47 { 48 switch (type) { 49 case EfiLoaderCode: 50 case EfiLoaderData: 51 case EfiBootServicesCode: 52 case EfiBootServicesData: 53 case EfiConventionalMemory: 54 return (SMAP_TYPE_MEMORY); 55 case EfiReservedMemoryType: 56 case EfiRuntimeServicesCode: 57 case EfiRuntimeServicesData: 58 case EfiMemoryMappedIO: 59 case EfiMemoryMappedIOPortSpace: 60 case EfiPalCode: 61 case EfiUnusableMemory: 62 return (SMAP_TYPE_RESERVED); 63 case EfiACPIReclaimMemory: 64 return (SMAP_TYPE_ACPI_RECLAIM); 65 case EfiACPIMemoryNVS: 66 return (SMAP_TYPE_ACPI_NVS); 67 } 68 return (SMAP_TYPE_RESERVED); 69 } 70 71 void 72 efi_getsmap(void) 73 { 74 UINTN size, desc_size, key; 75 EFI_MEMORY_DESCRIPTOR *efi_mmap, *p; 76 EFI_PHYSICAL_ADDRESS addr; 77 EFI_STATUS status; 78 STAILQ_HEAD(smap_head, smap_buf) head = 79 STAILQ_HEAD_INITIALIZER(head); 80 struct smap_buf *cur, *next; 81 int i, n, ndesc; 82 int type = -1; 83 84 size = 0; 85 efi_mmap = NULL; 86 status = BS->GetMemoryMap(&size, efi_mmap, &key, &desc_size, NULL); 87 efi_mmap = malloc(size); 88 status = BS->GetMemoryMap(&size, efi_mmap, &key, &desc_size, NULL); 89 if (EFI_ERROR(status)) { 90 printf("GetMemoryMap: error %lu\n", DECODE_ERROR(status)); 91 free(efi_mmap); 92 return; 93 } 94 95 STAILQ_INIT(&head); 96 n = 0; 97 i = 0; 98 p = efi_mmap; 99 next = NULL; 100 ndesc = size / desc_size; 101 while (i < ndesc) { 102 if (next == NULL) { 103 next = malloc(sizeof (*next)); 104 if (next == NULL) 105 break; 106 107 next->sb_smap.base = p->PhysicalStart; 108 next->sb_smap.length = 109 p->NumberOfPages << EFI_PAGE_SHIFT; 110 /* 111 * ACPI 6.1 tells the lower memory should be 112 * reported as normal memory, so we enforce 113 * page 0 type even as vmware maps it as 114 * acpi reclaimable. 115 */ 116 if (next->sb_smap.base == 0) 117 type = SMAP_TYPE_MEMORY; 118 else 119 type = smap_type(p->Type); 120 next->sb_smap.type = type; 121 122 STAILQ_INSERT_TAIL(&head, next, sb_bufs); 123 n++; 124 p = NextMemoryDescriptor(p, desc_size); 125 i++; 126 continue; 127 } 128 addr = next->sb_smap.base + next->sb_smap.length; 129 if ((smap_type(p->Type) == type) && 130 (p->PhysicalStart == addr)) { 131 next->sb_smap.length += 132 (p->NumberOfPages << EFI_PAGE_SHIFT); 133 p = NextMemoryDescriptor(p, desc_size); 134 i++; 135 } else 136 next = NULL; 137 } 138 smaplen = n; 139 if (smaplen > 0) { 140 smapbase = malloc(smaplen * sizeof (*smapbase)); 141 if (smapbase != NULL) { 142 n = 0; 143 STAILQ_FOREACH(cur, &head, sb_bufs) 144 smapbase[n++] = cur->sb_smap; 145 } 146 cur = STAILQ_FIRST(&head); 147 while (cur != NULL) { 148 next = STAILQ_NEXT(cur, sb_bufs); 149 free(cur); 150 cur = next; 151 } 152 } 153 free(efi_mmap); 154 } 155 156 void 157 efi_addsmapdata(struct preloaded_file *kfp) 158 { 159 size_t size; 160 161 if (smapbase == NULL || smaplen == 0) 162 return; 163 size = smaplen * sizeof (*smapbase); 164 file_addmetadata(kfp, MODINFOMD_SMAP, size, smapbase); 165 } 166 167 COMMAND_SET(smap, "smap", "show BIOS SMAP", command_smap); 168 169 static int 170 command_smap(int argc __unused, char *argv[] __unused) 171 { 172 uint_t i; 173 174 if (smapbase == NULL || smaplen == 0) 175 return (CMD_ERROR); 176 177 for (i = 0; i < smaplen; i++) 178 printf("SMAP type=%02" PRIx32 " base=%016" PRIx64 179 " len=%016" PRIx64 "\n", smapbase[i].type, 180 smapbase[i].base, smapbase[i].length); 181 return (CMD_OK); 182 } 183