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 <lib/smbios/smbios.h>
8 
9 #include <fbl/algorithm.h>
10 #include <inttypes.h>
11 #include <string.h>
12 #include <zircon/compiler.h>
13 #include <zircon/types.h>
14 
15 namespace {
16 
ComputeChecksum(const uint8_t * data,size_t len)17 uint8_t ComputeChecksum(const uint8_t* data, size_t len) {
18     unsigned int sum = 0;
19     for (size_t i = 0; i < len; ++i) {
20         sum += data[i];
21     }
22     return static_cast<uint8_t>(sum);
23 }
24 
25 } // namespace
26 
27 
28 namespace smbios {
29 
StringTable()30 StringTable::StringTable() {
31 }
~StringTable()32 StringTable::~StringTable() {
33 }
34 
Init(const Header * h,size_t max_struct_len)35 zx_status_t StringTable::Init(const Header* h, size_t max_struct_len) {
36     if (h->length > max_struct_len) {
37         return ZX_ERR_IO_DATA_INTEGRITY;
38     }
39 
40     size_t max_string_table_len = max_struct_len - h->length;
41     start_ = reinterpret_cast<const char*>(h) + h->length;
42 
43     // Make sure the table is big enough to include the two trailing NULs
44     if (max_string_table_len < 2) {
45         return ZX_ERR_IO_DATA_INTEGRITY;
46     }
47 
48     // Check if the string table is empty
49     if (start_[0] == 0 && start_[1] == 0) {
50         length_ = 2;
51         return ZX_OK;
52     }
53 
54     size_t start_idx = 0;
55     if (start_[0] == 0) {
56         // We know that this isn't the end of the table, since the next byte
57         // isn't NUL.  Skip examining this leading zero-length string in the
58         // loop below, so that we can simplify the iteration.  During the
59         // iteration below, we have the invariant that either
60         // 1) i points to the start of the first string in the table and that
61         // string is not 0-length
62         // 2) i points to a subsequent string in the table, so a zero-length
63         // string implies two consecutive NULs were found (the end of table
64         // marker).
65         start_idx = 1;
66     }
67 
68     for (size_t i = start_idx; i < max_string_table_len; ) {
69         size_t len = strnlen(start_ + i, max_string_table_len - i);
70 
71         if (len == 0) {
72             length_ = i + 1; // Include the trailing null
73             return ZX_OK;
74         }
75 
76         // strnlen returns the length not including the NUL.  Note that if
77         // no NUL was found, it returns max_string_table_len - i, which will exceed
78         // the loop conditions.
79         i += len + 1;
80     }
81     return ZX_ERR_IO_DATA_INTEGRITY;
82 }
83 
GetString(size_t idx,const char ** out) const84 zx_status_t StringTable::GetString(size_t idx, const char** out) const {
85     if (idx == 0) {
86         *out = "<null>";
87         return ZX_OK;
88     }
89     *out = "<missing string>";
90 
91     for (size_t i = 0; i < length_; ) {
92         size_t len = strnlen(start_ + i, length_ - i);
93 
94         if (len == 0) {
95             if (i != 0) {
96                 return ZX_ERR_NOT_FOUND;
97             }
98 
99             if (length_ - i < 2) {
100                 return ZX_ERR_IO_DATA_INTEGRITY;
101             }
102             if (start_[i + 1] == 0) {
103                 return ZX_ERR_NOT_FOUND;
104             }
105         }
106         if (idx == 1) {
107             *out = start_ + i;
108             return ZX_OK;
109         }
110         idx--;
111         i += len + 1;
112     }
113     ZX_DEBUG_ASSERT(false);
114     // Should not be reachable, since Init should have checked
115     return ZX_ERR_IO_DATA_INTEGRITY;
116 }
117 
Dump() const118 void StringTable::Dump() const {
119     const char* str;
120     for (size_t i = 1; GetString(i, &str) == ZX_OK; ++i) {
121         printf("  str %zu: %s\n", i, str);
122     }
123 }
124 
IsValid() const125 bool EntryPoint2_1::IsValid() const {
126     if (memcmp(anchor_string, SMBIOS2_ANCHOR, fbl::count_of(anchor_string))) {
127         printf("smbios: bad anchor %4s\n", anchor_string);
128         return false;
129     }
130 
131     uint8_t real_length = length;
132     if (length != 0x1f) {
133         // 0x1e is allowed due to errata in the SMBIOS 2.1 spec.  It really means
134         // 0x1f.
135         if (length == 0x1e) {
136             real_length = 0x1f;
137         } else {
138             printf("smbios: bad len: %u\n", real_length);
139             return false;
140         }
141     }
142 
143     if (ComputeChecksum(reinterpret_cast<const uint8_t*>(this), real_length) != 0) {
144         printf("smbios: bad checksum\n");
145         return false;
146     }
147     if (ep_rev != 0) {
148         printf("smbios: bad version %u\n", ep_rev);
149         return false;
150     }
151 
152     if (memcmp(intermediate_anchor_string, SMBIOS2_INTERMEDIATE_ANCHOR,
153                fbl::count_of(intermediate_anchor_string))) {
154         printf("smbios: bad intermediate anchor %5s\n", intermediate_anchor_string);
155         return false;
156     }
157     if (ComputeChecksum(reinterpret_cast<const uint8_t*>(&intermediate_anchor_string),
158                         real_length - offsetof(EntryPoint2_1, intermediate_anchor_string)) != 0) {
159         printf("smbios: bad intermediate checksum\n");
160         return false;
161     }
162 
163     if ((uint32_t)(struct_table_phys + struct_table_length) < struct_table_phys) {
164         return false;
165     }
166 
167     return true;
168 }
169 
Dump() const170 void EntryPoint2_1::Dump() const {
171     printf("SMBIOS EntryPoint v2.1:\n");
172     printf("  specification version: %u.%u\n", major_ver, minor_ver);
173     printf("  max struct size: %u\n", max_struct_size);
174     printf("  struct table: %u bytes @0x%08x, %u entries\n", struct_table_length, struct_table_phys,
175            struct_count);
176 }
177 
IncludesVersion(uint8_t spec_major_ver,uint8_t spec_minor_ver) const178 bool SpecVersion::IncludesVersion(uint8_t spec_major_ver, uint8_t spec_minor_ver) const {
179     if (major_ver > spec_major_ver) {
180         return true;
181     }
182     if (major_ver < spec_major_ver) {
183         return false;
184     }
185     return minor_ver >= spec_minor_ver;
186 }
187 
Dump(const StringTable & st) const188 void BiosInformationStruct2_0::Dump(const StringTable& st) const {
189     printf("SMBIOS BIOS Information Struct v2.0:\n");
190     printf("  vendor: %s\n", st.GetString(vendor_str_idx));
191     printf("  BIOS version: %s\n", st.GetString(bios_version_str_idx));
192     printf("  BIOS starting address segment: 0x%04x\n", bios_starting_address_segment);
193     printf("  BIOS release date: %s\n", st.GetString(bios_release_date_str_idx));
194     printf("  BIOS ROM size: 0x%02x\n", bios_rom_size);
195     printf("  BIOS characteristics: 0x%016" PRIx64 "\n", bios_characteristics);
196     for (size_t i = sizeof(*this); i < hdr.length; ++i) {
197         printf("  BIOS characteristics extended: 0x%02x\n", bios_characteristics_ext[i]);
198     }
199 }
200 
Dump(const StringTable & st) const201 void BiosInformationStruct2_4::Dump(const StringTable& st) const {
202     printf("SMBIOS BIOS Information Struct v2.4:\n");
203     printf("  vendor: %s\n", st.GetString(vendor_str_idx));
204     printf("  BIOS version: %s\n", st.GetString(bios_version_str_idx));
205     printf("  BIOS starting address segment: 0x%04x\n", bios_starting_address_segment);
206     printf("  BIOS release date: %s\n", st.GetString(bios_release_date_str_idx));
207     printf("  BIOS ROM size: 0x%02x\n", bios_rom_size);
208     printf("  BIOS characteristics: 0x%016" PRIx64 "\n", bios_characteristics);
209     printf("  BIOS characteristics extended: 0x%04x\n", bios_characteristics_ext);
210     printf("  BIOS version number: %u.%u\n", bios_major_release, bios_minor_release);
211     printf("  EC version number: %u.%u\n", ec_major_release, ec_minor_release);
212     if (hdr.length > sizeof(*this)) {
213         printf("  %zu bytes of unknown trailing contents\n", hdr.length - sizeof(*this));
214     }
215 }
216 
Dump(const StringTable & st) const217 void SystemInformationStruct2_0::Dump(const StringTable& st) const {
218     printf("SMBIOS System Information Struct v2.0:\n");
219     printf("  manufacturer: %s\n", st.GetString(manufacturer_str_idx));
220     printf("  product: %s\n", st.GetString(product_name_str_idx));
221     printf("  version: %s\n", st.GetString(version_str_idx));
222     if (hdr.length > sizeof(*this)) {
223         printf("  %zu bytes of unknown trailing contents\n", hdr.length - sizeof(*this));
224     }
225 }
226 
Dump(const StringTable & st) const227 void SystemInformationStruct2_1::Dump(const StringTable& st) const {
228     printf("SMBIOS System Information Struct v2.1:\n");
229     printf("  manufacturer: %s\n", st.GetString(manufacturer_str_idx));
230     printf("  product: %s\n", st.GetString(product_name_str_idx));
231     printf("  version: %s\n", st.GetString(version_str_idx));
232     printf("  wakeup_type: 0x%x\n", wakeup_type);
233     if (hdr.length > sizeof(*this)) {
234         printf("  %zu bytes of unknown trailing contents\n", hdr.length - sizeof(*this));
235     }
236 }
237 
Dump(const StringTable & st) const238 void SystemInformationStruct2_4::Dump(const StringTable& st) const {
239     printf("SMBIOS System Information Struct v2.4:\n");
240     printf("  manufacturer: %s\n", st.GetString(manufacturer_str_idx));
241     printf("  product: %s\n", st.GetString(product_name_str_idx));
242     printf("  version: %s\n", st.GetString(version_str_idx));
243     printf("  wakeup_type: 0x%x\n", wakeup_type);
244     printf("  SKU: %s\n", st.GetString(sku_number_str_idx));
245     printf("  family: %s\n", st.GetString(family_str_idx));
246     if (hdr.length > sizeof(*this)) {
247         printf("  %zu bytes of unknown trailing contents\n", hdr.length - sizeof(*this));
248     }
249 }
250 
WalkStructs(uintptr_t struct_table_virt,StructWalkCallback cb,void * ctx) const251 zx_status_t EntryPoint2_1::WalkStructs(uintptr_t struct_table_virt, StructWalkCallback cb,
252                                        void* ctx) const {
253     size_t idx = 0;
254     uintptr_t curr_addr = struct_table_virt;
255     const uintptr_t table_end = curr_addr + struct_table_length;
256     while (curr_addr + sizeof(Header) < table_end) {
257         auto hdr = reinterpret_cast<const Header*>(curr_addr);
258         if (curr_addr + hdr->length > table_end) {
259             return ZX_ERR_IO_DATA_INTEGRITY;
260         }
261         StringTable st;
262         zx_status_t status = st.Init(hdr, fbl::max(table_end - curr_addr,
263                                                    static_cast<size_t>(max_struct_size)));
264         if (status != ZX_OK) {
265             return status;
266         }
267 
268         status = cb(version(), hdr, st, ctx);
269         if (status == ZX_ERR_STOP) {
270             break;
271         } else if (status != ZX_OK && status != ZX_ERR_NEXT) {
272             return status;
273         }
274         idx++;
275 
276         if (idx == struct_count) {
277             return ZX_OK;
278         }
279 
280         // Skip over the embedded strings
281         curr_addr += hdr->length + st.length();
282     }
283 
284     return ZX_ERR_IO_DATA_INTEGRITY;
285 }
286 
287 } // namespace smbios
288