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