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 6.1 Table 15-330 UEFI Memory Types and mapping to ACPI address 42 * range types. 43 */ 44 static int 45 smap_type(int type) 46 { 47 switch (type) { 48 case EfiLoaderCode: 49 case EfiLoaderData: 50 case EfiBootServicesCode: 51 case EfiBootServicesData: 52 case EfiConventionalMemory: 53 return (SMAP_TYPE_MEMORY); 54 case EfiReservedMemoryType: 55 case EfiRuntimeServicesCode: 56 case EfiRuntimeServicesData: 57 case EfiMemoryMappedIO: 58 case EfiMemoryMappedIOPortSpace: 59 case EfiPalCode: 60 case EfiUnusableMemory: 61 return (SMAP_TYPE_RESERVED); 62 case EfiACPIReclaimMemory: 63 return (SMAP_TYPE_ACPI_RECLAIM); 64 case EfiACPIMemoryNVS: 65 return (SMAP_TYPE_ACPI_NVS); 66 } 67 return (SMAP_TYPE_RESERVED); 68 } 69 70 void 71 efi_getsmap(void) 72 { 73 UINTN size, desc_size, key; 74 EFI_MEMORY_DESCRIPTOR *efi_mmap, *p; 75 EFI_PHYSICAL_ADDRESS addr; 76 EFI_STATUS status; 77 STAILQ_HEAD(smap_head, smap_buf) head = 78 STAILQ_HEAD_INITIALIZER(head); 79 struct smap_buf *cur, *next; 80 int i, n, ndesc; 81 int type = -1; 82 83 size = 0; 84 efi_mmap = NULL; 85 status = BS->GetMemoryMap(&size, efi_mmap, &key, &desc_size, NULL); 86 efi_mmap = malloc(size); 87 status = BS->GetMemoryMap(&size, efi_mmap, &key, &desc_size, NULL); 88 if (EFI_ERROR(status)) { 89 printf("GetMemoryMap: error %lu\n", DECODE_ERROR(status)); 90 free(efi_mmap); 91 return; 92 } 93 94 STAILQ_INIT(&head); 95 n = 0; 96 i = 0; 97 p = efi_mmap; 98 next = NULL; 99 ndesc = size / desc_size; 100 while (i < ndesc) { 101 if (next == NULL) { 102 next = malloc(sizeof (*next)); 103 if (next == NULL) 104 break; 105 106 next->sb_smap.base = p->PhysicalStart; 107 next->sb_smap.length = 108 p->NumberOfPages << EFI_PAGE_SHIFT; 109 /* 110 * ACPI 6.1 tells the lower memory should be 111 * reported as normal memory, so we enforce 112 * page 0 type even as vmware maps it as 113 * acpi reclaimable. 114 */ 115 if (next->sb_smap.base == 0) 116 type = SMAP_TYPE_MEMORY; 117 else 118 type = smap_type(p->Type); 119 next->sb_smap.type = type; 120 121 STAILQ_INSERT_TAIL(&head, next, sb_bufs); 122 n++; 123 p = NextMemoryDescriptor(p, desc_size); 124 i++; 125 continue; 126 } 127 addr = next->sb_smap.base + next->sb_smap.length; 128 if ((smap_type(p->Type) == type) && 129 (p->PhysicalStart == addr)) { 130 next->sb_smap.length += 131 (p->NumberOfPages << EFI_PAGE_SHIFT); 132 p = NextMemoryDescriptor(p, desc_size); 133 i++; 134 } else 135 next = NULL; 136 } 137 smaplen = n; 138 if (smaplen > 0) { 139 smapbase = malloc(smaplen * sizeof (*smapbase)); 140 if (smapbase != NULL) { 141 n = 0; 142 STAILQ_FOREACH(cur, &head, sb_bufs) 143 smapbase[n++] = cur->sb_smap; 144 } 145 cur = STAILQ_FIRST(&head); 146 while (cur != NULL) { 147 next = STAILQ_NEXT(cur, sb_bufs); 148 free(cur); 149 cur = next; 150 } 151 } 152 free(efi_mmap); 153 } 154 155 void 156 efi_addsmapdata(struct preloaded_file *kfp) 157 { 158 size_t size; 159 160 if (smapbase == NULL || smaplen == 0) 161 return; 162 size = smaplen * sizeof (*smapbase); 163 file_addmetadata(kfp, MODINFOMD_SMAP, size, smapbase); 164 } 165 166 COMMAND_SET(smap, "smap", "show BIOS SMAP", command_smap); 167 168 static int 169 command_smap(int argc __unused, char *argv[] __unused) 170 { 171 uint_t i; 172 173 if (smapbase == NULL || smaplen == 0) 174 return (CMD_ERROR); 175 176 for (i = 0; i < smaplen; i++) 177 printf("SMAP type=%02" PRIx32 " base=%016" PRIx64 178 " len=%016" PRIx64 "\n", smapbase[i].type, 179 smapbase[i].base, smapbase[i].length); 180 return (CMD_OK); 181 } 182