1// Copyright 2017 The Fuchsia Authors 2// 3// Use of this source code is governed by a MIT-style 4// license that can be found in the LICENSE file or at 5// https://opensource.org/licenses/MIT 6// 7#include <platform/pc/smbios.h> 8 9#include <fbl/auto_call.h> 10#include <lib/console.h> 11#include <lib/smbios/smbios.h> 12#include <platform/pc/bootloader.h> 13#include <stdint.h> 14#include <string.h> 15#include <vm/physmap.h> 16#include <vm/vm_aspace.h> 17#include <vm/vm_address_region.h> 18#include <vm/vm_object_physical.h> 19#include <zircon/compiler.h> 20#include <zircon/types.h> 21 22namespace { 23 24smbios::EntryPointVersion kEpVersion = smbios::EntryPointVersion::Unknown; 25union { 26 const uint8_t* raw; 27 const smbios::EntryPoint2_1* ep2_1; 28} kEntryPoint; 29uintptr_t kStructBase = 0; // Address of first SMBIOS struct 30 31zx_status_t FindEntryPoint(const uint8_t** base, smbios::EntryPointVersion* version) { 32 // See if our bootloader told us where the table is 33 if (bootloader.smbios != 0) { 34 const uint8_t* p = reinterpret_cast<const uint8_t*>(paddr_to_physmap(bootloader.smbios)); 35 if (!memcmp(p, SMBIOS2_ANCHOR, strlen(SMBIOS2_ANCHOR))) { 36 *base = p; 37 *version = smbios::EntryPointVersion::V2_1; 38 return ZX_OK; 39 } else if (!memcmp(p, SMBIOS3_ANCHOR, strlen(SMBIOS3_ANCHOR))) { 40 *base = p; 41 *version = smbios::EntryPointVersion::V3_0; 42 return ZX_OK; 43 } 44 } 45 46 // Fallback to non-EFI SMBIOS search if we haven't found it yet 47 for (paddr_t target = 0x000f0000; target < 0x00100000; target += 16) { 48 const uint8_t* p = reinterpret_cast<const uint8_t*>(paddr_to_physmap(target)); 49 if (!memcmp(p, SMBIOS2_ANCHOR, strlen(SMBIOS2_ANCHOR))) { 50 *base = p; 51 *version = smbios::EntryPointVersion::V2_1; 52 return ZX_OK; 53 } 54 if (!memcmp(p, SMBIOS3_ANCHOR, strlen(SMBIOS3_ANCHOR))) { 55 *base = p; 56 *version = smbios::EntryPointVersion::V3_0; 57 return ZX_OK; 58 } 59 } 60 61 return ZX_ERR_NOT_FOUND; 62} 63 64zx_status_t MapStructs2_1(const smbios::EntryPoint2_1* ep, 65 fbl::RefPtr<VmMapping>* mapping, uintptr_t* struct_table_virt) { 66 paddr_t base = ep->struct_table_phys; 67 paddr_t end = base + ep->struct_table_length; 68 const size_t subpage_offset = base & (PAGE_SIZE - 1); 69 base -= subpage_offset; 70 size_t len = ROUNDUP(end - base, PAGE_SIZE); 71 72 auto vmar = VmAspace::kernel_aspace()->RootVmar(); 73 fbl::RefPtr<VmObject> vmo; 74 zx_status_t status = VmObjectPhysical::Create(base, len, &vmo); 75 if (status != ZX_OK) { 76 return status; 77 } 78 fbl::RefPtr<VmMapping> m; 79 status = vmar->CreateVmMapping(0, len, 0, 0 /* vmar_flags */, fbl::move(vmo), 0, 80 ARCH_MMU_FLAG_CACHED | ARCH_MMU_FLAG_PERM_READ, 81 "smbios", &m); 82 if (status != ZX_OK) { 83 return status; 84 } 85 *struct_table_virt = m->base() + subpage_offset; 86 *mapping = fbl::move(m); 87 return ZX_OK; 88} 89 90} // namespace 91 92// Walk the known SMBIOS structures. The callback will be called once for each 93// structure found. 94zx_status_t SmbiosWalkStructs(smbios::StructWalkCallback cb, void* ctx) { 95 switch (kEpVersion) { 96 case smbios::EntryPointVersion::V2_1: { 97 return kEntryPoint.ep2_1->WalkStructs(kStructBase, cb, ctx); 98 } 99 case smbios::EntryPointVersion::V3_0: 100 return ZX_ERR_NOT_SUPPORTED; 101 default: 102 return ZX_ERR_NOT_SUPPORTED; 103 } 104} 105 106void pc_init_smbios() { 107 fbl::RefPtr<VmMapping> mapping; 108 auto cleanup_mapping = fbl::MakeAutoCall([&mapping] { 109 if (mapping) { 110 mapping->Destroy(); 111 } 112 }); 113 114 const uint8_t* start = nullptr; 115 auto version = smbios::EntryPointVersion::Unknown; 116 uintptr_t struct_table_virt = 0; 117 118 zx_status_t status = FindEntryPoint(&start, &version); 119 if (status != ZX_OK) { 120 printf("smbios: Failed to locate entry point\n"); 121 return; 122 } 123 124 switch (version) { 125 case smbios::EntryPointVersion::V2_1: { 126 auto ep = reinterpret_cast<const smbios::EntryPoint2_1*>(start); 127 if (!ep->IsValid()) { 128 return; 129 } 130 131 status = MapStructs2_1(ep, &mapping, &struct_table_virt); 132 if (status != ZX_OK) { 133 printf("smbios: failed to map structs: %d\n", status); 134 return; 135 } 136 break; 137 } 138 case smbios::EntryPointVersion::V3_0: 139 printf("smbios: version 3 not yet implemented\n"); 140 return; 141 default: 142 DEBUG_ASSERT(false); 143 printf("smbios: Unknown version?\n"); 144 return; 145 } 146 147 kEntryPoint.raw = start; 148 kEpVersion = version; 149 kStructBase = struct_table_virt; 150 cleanup_mapping.cancel(); 151} 152 153static zx_status_t DebugStructWalk(smbios::SpecVersion ver, 154 const smbios::Header* hdr, const smbios::StringTable& st, 155 void* ctx) { 156 switch (hdr->type) { 157 case smbios::StructType::BiosInfo: { 158 if (ver.IncludesVersion(2, 4)) { 159 auto entry = reinterpret_cast<const smbios::BiosInformationStruct2_4*>(hdr); 160 entry->Dump(st); 161 return ZX_OK; 162 } else if (ver.IncludesVersion(2, 0)) { 163 auto entry = reinterpret_cast<const smbios::BiosInformationStruct2_0*>(hdr); 164 entry->Dump(st); 165 return ZX_OK; 166 } 167 break; 168 } 169 case smbios::StructType::SystemInfo: { 170 if (ver.IncludesVersion(2, 4)) { 171 auto entry = reinterpret_cast<const smbios::SystemInformationStruct2_4*>(hdr); 172 entry->Dump(st); 173 return ZX_OK; 174 } else if (ver.IncludesVersion(2, 1)) { 175 auto entry = reinterpret_cast<const smbios::SystemInformationStruct2_1*>(hdr); 176 entry->Dump(st); 177 return ZX_OK; 178 } else if (ver.IncludesVersion(2, 0)) { 179 auto entry = reinterpret_cast<const smbios::SystemInformationStruct2_0*>(hdr); 180 entry->Dump(st); 181 return ZX_OK; 182 } 183 break; 184 } 185 default: break; 186 } 187 printf("smbios: found struct@%p: typ=%u len=%u st_len=%zu\n", hdr, 188 static_cast<uint8_t>(hdr->type), hdr->length, st.length()); 189 st.Dump(); 190 191 return ZX_OK; 192} 193 194static int CmdSmbios(int argc, const cmd_args *argv, uint32_t flags) 195{ 196 if (argc < 2) { 197 printf("not enough arguments\n"); 198usage: 199 printf("usage:\n"); 200 printf("%s dump\n", argv[0].str); 201 return ZX_ERR_INTERNAL; 202 } 203 204 if (!strcmp(argv[1].str, "dump")) { 205 zx_status_t status = SmbiosWalkStructs(DebugStructWalk, nullptr); 206 if (status != ZX_OK) { 207 printf("smbios: failed to walk structs: %d\n", status); 208 } 209 return ZX_OK; 210 } else { 211 printf("unknown command\n"); 212 goto usage; 213 } 214 215 return ZX_OK; 216} 217 218STATIC_COMMAND_START 219#if LK_DEBUGLEVEL > 0 220STATIC_COMMAND("smbios", "smbios", &CmdSmbios) 221#endif 222STATIC_COMMAND_END(smbios); 223