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