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