[e16e8f2] | 1 | /* ----------------------------------------------------------------------- * |
---|
| 2 | * |
---|
| 3 | * Copyright 2006 Erwan Velu - All Rights Reserved |
---|
| 4 | * |
---|
| 5 | * Permission is hereby granted, free of charge, to any person |
---|
| 6 | * obtaining a copy of this software and associated documentation |
---|
| 7 | * files (the "Software"), to deal in the Software without |
---|
| 8 | * restriction, including without limitation the rights to use, |
---|
| 9 | * copy, modify, merge, publish, distribute, sublicense, and/or |
---|
| 10 | * sell copies of the Software, and to permit persons to whom |
---|
| 11 | * the Software is furnished to do so, subject to the following |
---|
| 12 | * conditions: |
---|
| 13 | * |
---|
| 14 | * The above copyright notice and this permission notice shall |
---|
| 15 | * be included in all copies or substantial portions of the Software. |
---|
| 16 | * |
---|
| 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
---|
| 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
---|
| 19 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
---|
| 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
---|
| 21 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
---|
| 22 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
---|
| 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
---|
| 24 | * OTHER DEALINGS IN THE SOFTWARE. |
---|
| 25 | * |
---|
| 26 | * ----------------------------------------------------------------------- |
---|
| 27 | */ |
---|
| 28 | |
---|
| 29 | #include <stdio.h> |
---|
| 30 | #include <string.h> |
---|
| 31 | #include "dmi/dmi.h" |
---|
| 32 | |
---|
| 33 | const char *out_of_spec = "<OUT OF SPEC>"; |
---|
| 34 | const char *bad_index = "<BAD INDEX>"; |
---|
| 35 | |
---|
| 36 | /* |
---|
| 37 | * Misc. util stuff |
---|
| 38 | */ |
---|
| 39 | |
---|
| 40 | /* |
---|
| 41 | * 3.3.11 On Board Devices Information (Type 10) |
---|
| 42 | */ |
---|
| 43 | |
---|
| 44 | static const char *dmi_on_board_devices_type(uint8_t code) |
---|
| 45 | { |
---|
| 46 | /* 3.3.11.1 */ |
---|
| 47 | static const char *type[] = { |
---|
| 48 | "Other", /* 0x01 */ |
---|
| 49 | "Unknown", |
---|
| 50 | "Video", |
---|
| 51 | "SCSI Controller", |
---|
| 52 | "Ethernet", |
---|
| 53 | "Token Ring", |
---|
| 54 | "Sound", |
---|
| 55 | "PATA Controller", |
---|
| 56 | "SATA Controller", |
---|
| 57 | "SAS Controller" /* 0x0A */ |
---|
| 58 | }; |
---|
| 59 | |
---|
| 60 | if (code >= 0x01 && code <= 0x0A) |
---|
| 61 | return type[code - 0x01]; |
---|
| 62 | return out_of_spec; |
---|
| 63 | } |
---|
| 64 | |
---|
| 65 | static void dmi_on_board_devices(struct dmi_header *h, s_dmi * dmi) |
---|
| 66 | { |
---|
| 67 | uint8_t *p = h->data + 4; |
---|
| 68 | uint8_t count = (h->length - 0x04) / 2; |
---|
| 69 | unsigned int i; |
---|
| 70 | |
---|
| 71 | for (i = 0; |
---|
| 72 | i < count |
---|
| 73 | && i < |
---|
| 74 | sizeof dmi->base_board.devices_information / |
---|
| 75 | sizeof *dmi->base_board.devices_information; i++) { |
---|
| 76 | strlcpy(dmi->base_board.devices_information[i].type, |
---|
| 77 | dmi_on_board_devices_type(p[2 * i] & 0x7F), |
---|
| 78 | sizeof dmi->base_board.devices_information[i].type); |
---|
| 79 | dmi->base_board.devices_information[i].status = p[2 * i] & 0x80; |
---|
| 80 | strlcpy(dmi->base_board.devices_information[i].description, |
---|
| 81 | dmi_string(h, p[2 * i + 1]), |
---|
| 82 | sizeof dmi->base_board.devices_information[i].description); |
---|
| 83 | } |
---|
| 84 | } |
---|
| 85 | |
---|
| 86 | /* |
---|
| 87 | * 3.3.24 System Reset (Type 23) |
---|
| 88 | */ |
---|
| 89 | |
---|
| 90 | static const char *dmi_system_reset_boot_option(uint8_t code) |
---|
| 91 | { |
---|
| 92 | static const char *option[] = { |
---|
| 93 | "Operating System", /* 0x1 */ |
---|
| 94 | "System Utilities", |
---|
| 95 | "Do Not Reboot" /* 0x3 */ |
---|
| 96 | }; |
---|
| 97 | |
---|
| 98 | if (code >= 0x1) |
---|
| 99 | return option[code - 0x1]; |
---|
| 100 | return out_of_spec; |
---|
| 101 | } |
---|
| 102 | |
---|
| 103 | static void dmi_system_reset_count(uint16_t code, char *array) |
---|
| 104 | { |
---|
| 105 | if (code == 0xFFFF) |
---|
| 106 | strlcpy(array, "Unknown", sizeof array); |
---|
| 107 | else |
---|
| 108 | snprintf(array, sizeof array, "%u", code); |
---|
| 109 | } |
---|
| 110 | |
---|
| 111 | static void dmi_system_reset_timer(uint16_t code, char *array) |
---|
| 112 | { |
---|
| 113 | if (code == 0xFFFF) |
---|
| 114 | strlcpy(array, "Unknown", sizeof array); |
---|
| 115 | else |
---|
| 116 | snprintf(array, sizeof array, "%u min", code); |
---|
| 117 | } |
---|
| 118 | |
---|
| 119 | /* |
---|
| 120 | * 3.3.25 Hardware Security (Type 24) |
---|
| 121 | */ |
---|
| 122 | |
---|
| 123 | static const char *dmi_hardware_security_status(uint8_t code) |
---|
| 124 | { |
---|
| 125 | static const char *status[] = { |
---|
| 126 | "Disabled", /* 0x00 */ |
---|
| 127 | "Enabled", |
---|
| 128 | "Not Implemented", |
---|
| 129 | "Unknown" /* 0x03 */ |
---|
| 130 | }; |
---|
| 131 | |
---|
| 132 | return status[code]; |
---|
| 133 | } |
---|
| 134 | |
---|
| 135 | /* |
---|
| 136 | * 3.3.12 OEM Strings (Type 11) |
---|
| 137 | */ |
---|
| 138 | |
---|
| 139 | static void dmi_oem_strings(struct dmi_header *h, const char *prefix, |
---|
| 140 | s_dmi * dmi) |
---|
| 141 | { |
---|
| 142 | uint8_t *p = h->data + 4; |
---|
| 143 | uint8_t count = p[0x00]; |
---|
| 144 | int i; |
---|
| 145 | |
---|
| 146 | for (i = 1; i <= count; i++) |
---|
| 147 | snprintf(dmi->oem_strings, OEM_STRINGS_SIZE, "%s %s %s\n", |
---|
| 148 | dmi->oem_strings, prefix, dmi_string(h, i)); |
---|
| 149 | } |
---|
| 150 | |
---|
| 151 | /* |
---|
| 152 | * 3.3.13 System Configuration Options (Type 12) |
---|
| 153 | */ |
---|
| 154 | static void dmi_system_configuration_options(struct dmi_header *h, |
---|
| 155 | const char *prefix, s_dmi * dmi) |
---|
| 156 | { |
---|
| 157 | uint8_t *p = h->data + 4; |
---|
| 158 | uint8_t count = p[0x00]; |
---|
| 159 | int i; |
---|
| 160 | |
---|
| 161 | for (i = 1; i <= count; i++) |
---|
| 162 | snprintf(dmi->system.configuration_options, |
---|
| 163 | SYSTEM_CONFIGURATION_OPTIONS_SIZE, "%s %s %s\n", |
---|
| 164 | dmi->system.configuration_options, prefix, dmi_string(h, i)); |
---|
| 165 | } |
---|
| 166 | |
---|
| 167 | static void dmi_system_boot_status(uint8_t code, char *array) |
---|
| 168 | { |
---|
| 169 | static const char *status[] = { |
---|
| 170 | "No errors detected", /* 0 */ |
---|
| 171 | "No bootable media", |
---|
| 172 | "Operating system failed to load", |
---|
| 173 | "Firmware-detected hardware failure", |
---|
| 174 | "Operating system-detected hardware failure", |
---|
| 175 | "User-requested boot", |
---|
| 176 | "System security violation", |
---|
| 177 | "Previously-requested image", |
---|
| 178 | "System watchdog timer expired" /* 8 */ |
---|
| 179 | }; |
---|
| 180 | |
---|
| 181 | if (code <= 8) |
---|
| 182 | strlcpy(array, status[code], SYSTEM_BOOT_STATUS_SIZE); |
---|
| 183 | if (code >= 128 && code <= 191) |
---|
| 184 | strlcpy(array, "OEM-specific", SYSTEM_BOOT_STATUS_SIZE); |
---|
| 185 | if (code >= 192) |
---|
| 186 | strlcpy(array, "Product-specific", SYSTEM_BOOT_STATUS_SIZE); |
---|
| 187 | } |
---|
| 188 | |
---|
| 189 | void dmi_bios_runtime_size(uint32_t code, s_dmi * dmi) |
---|
| 190 | { |
---|
| 191 | if (code & 0x000003FF) { |
---|
| 192 | dmi->bios.runtime_size = code; |
---|
| 193 | strlcpy(dmi->bios.runtime_size_unit, "bytes", |
---|
| 194 | sizeof(dmi->bios.runtime_size_unit)); |
---|
| 195 | } else { |
---|
| 196 | dmi->bios.runtime_size = code >> 10; |
---|
| 197 | strlcpy(dmi->bios.runtime_size_unit, "KB", |
---|
| 198 | sizeof(dmi->bios.runtime_size_unit)); |
---|
| 199 | |
---|
| 200 | } |
---|
| 201 | } |
---|
| 202 | |
---|
| 203 | void dmi_bios_characteristics(uint64_t code, s_dmi * dmi) |
---|
| 204 | { |
---|
| 205 | int i; |
---|
| 206 | /* |
---|
| 207 | * This isn't very clear what this bit is supposed to mean |
---|
| 208 | */ |
---|
| 209 | //if(code.l&(1<<3)) |
---|
| 210 | if (code && (1 << 3)) { |
---|
| 211 | ((bool *) (&dmi->bios.characteristics))[0] = true; |
---|
| 212 | return; |
---|
| 213 | } |
---|
| 214 | |
---|
| 215 | for (i = 4; i <= 31; i++) |
---|
| 216 | //if(code.l&(1<<i)) |
---|
| 217 | if (code & (1 << i)) |
---|
| 218 | ((bool *) (&dmi->bios.characteristics))[i - 3] = true; |
---|
| 219 | } |
---|
| 220 | |
---|
| 221 | void dmi_bios_characteristics_x1(uint8_t code, s_dmi * dmi) |
---|
| 222 | { |
---|
| 223 | int i; |
---|
| 224 | |
---|
| 225 | for (i = 0; i <= 7; i++) |
---|
| 226 | if (code & (1 << i)) |
---|
| 227 | ((bool *) (&dmi->bios.characteristics_x1))[i] = true; |
---|
| 228 | } |
---|
| 229 | |
---|
| 230 | void dmi_bios_characteristics_x2(uint8_t code, s_dmi * dmi) |
---|
| 231 | { |
---|
| 232 | int i; |
---|
| 233 | |
---|
| 234 | for (i = 0; i <= 2; i++) |
---|
| 235 | if (code & (1 << i)) |
---|
| 236 | ((bool *) (&dmi->bios.characteristics_x2))[i] = true; |
---|
| 237 | } |
---|
| 238 | |
---|
| 239 | void dmi_system_uuid(uint8_t * p, s_dmi * dmi) |
---|
| 240 | { |
---|
| 241 | int only0xFF = 1, only0x00 = 1; |
---|
| 242 | int i; |
---|
| 243 | |
---|
| 244 | for (i = 0; i < 16 && (only0x00 || only0xFF); i++) { |
---|
| 245 | if (p[i] != 0x00) |
---|
| 246 | only0x00 = 0; |
---|
| 247 | if (p[i] != 0xFF) |
---|
| 248 | only0xFF = 0; |
---|
| 249 | } |
---|
| 250 | |
---|
| 251 | if (only0xFF) { |
---|
| 252 | sprintf(dmi->system.uuid, "Not Present"); |
---|
| 253 | return; |
---|
| 254 | } |
---|
| 255 | if (only0x00) { |
---|
| 256 | sprintf(dmi->system.uuid, "Not Settable"); |
---|
| 257 | return; |
---|
| 258 | } |
---|
| 259 | |
---|
| 260 | sprintf(dmi->system.uuid, |
---|
| 261 | "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", |
---|
| 262 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], |
---|
| 263 | p[11], p[12], p[13], p[14], p[15]); |
---|
| 264 | } |
---|
| 265 | |
---|
| 266 | void dmi_system_wake_up_type(uint8_t code, s_dmi * dmi) |
---|
| 267 | { |
---|
| 268 | /* 3.3.2.1 */ |
---|
| 269 | static const char *type[] = { |
---|
| 270 | "Reserved", /* 0x00 */ |
---|
| 271 | "Other", |
---|
| 272 | "Unknown", |
---|
| 273 | "APM Timer", |
---|
| 274 | "Modem Ring", |
---|
| 275 | "LAN Remote", |
---|
| 276 | "Power Switch", |
---|
| 277 | "PCI PME#", |
---|
| 278 | "AC Power Restored" /* 0x08 */ |
---|
| 279 | }; |
---|
| 280 | |
---|
| 281 | if (code <= 0x08) { |
---|
| 282 | strlcpy(dmi->system.wakeup_type, type[code], |
---|
| 283 | sizeof(dmi->system.wakeup_type)); |
---|
| 284 | } else { |
---|
| 285 | strlcpy(dmi->system.wakeup_type, out_of_spec, |
---|
| 286 | sizeof(dmi->system.wakeup_type)); |
---|
| 287 | } |
---|
| 288 | return; |
---|
| 289 | } |
---|
| 290 | |
---|
| 291 | static void dmi_base_board_features(uint8_t code, s_dmi * dmi) |
---|
| 292 | { |
---|
| 293 | if ((code & 0x1F) != 0) { |
---|
| 294 | int i; |
---|
| 295 | |
---|
| 296 | for (i = 0; i <= 4; i++) |
---|
| 297 | if (code & (1 << i)) |
---|
| 298 | ((bool *) (&dmi->base_board.features))[i] = true; |
---|
| 299 | } |
---|
| 300 | } |
---|
| 301 | |
---|
| 302 | static void dmi_base_board_type(uint8_t code, s_dmi * dmi) |
---|
| 303 | { |
---|
| 304 | /* 3.3.3.2 */ |
---|
| 305 | static const char *type[] = { |
---|
| 306 | "Unknown", /* 0x01 */ |
---|
| 307 | "Other", |
---|
| 308 | "Server Blade", |
---|
| 309 | "Connectivity Switch", |
---|
| 310 | "System Management Module", |
---|
| 311 | "Processor Module", |
---|
| 312 | "I/O Module", |
---|
| 313 | "Memory Module", |
---|
| 314 | "Daughter Board", |
---|
| 315 | "Motherboard", |
---|
| 316 | "Processor+Memory Module", |
---|
| 317 | "Processor+I/O Module", |
---|
| 318 | "Interconnect Board" /* 0x0D */ |
---|
| 319 | }; |
---|
| 320 | |
---|
| 321 | if (code >= 0x01 && code <= 0x0D) { |
---|
| 322 | strlcpy(dmi->base_board.type, type[code], |
---|
| 323 | sizeof(dmi->base_board.type)); |
---|
| 324 | } else { |
---|
| 325 | strlcpy(dmi->base_board.type, out_of_spec, |
---|
| 326 | sizeof(dmi->base_board.type)); |
---|
| 327 | } |
---|
| 328 | return; |
---|
| 329 | } |
---|
| 330 | |
---|
| 331 | static void dmi_processor_voltage(uint8_t code, s_dmi * dmi) |
---|
| 332 | { |
---|
| 333 | /* 3.3.5.4 */ |
---|
| 334 | static const uint16_t voltage[] = { |
---|
| 335 | 5000, |
---|
| 336 | 3300, |
---|
| 337 | 2900 |
---|
| 338 | }; |
---|
| 339 | int i; |
---|
| 340 | |
---|
| 341 | if (code & 0x80) |
---|
| 342 | dmi->processor.voltage_mv = (code & 0x7f) * 100; |
---|
| 343 | else { |
---|
| 344 | for (i = 0; i <= 2; i++) |
---|
| 345 | if (code & (1 << i)) |
---|
| 346 | dmi->processor.voltage_mv = voltage[i]; |
---|
| 347 | } |
---|
| 348 | } |
---|
| 349 | |
---|
| 350 | static void dmi_processor_id(uint8_t type, uint8_t * p, const char *version, |
---|
| 351 | s_dmi * dmi) |
---|
| 352 | { |
---|
| 353 | /* |
---|
| 354 | * Extra flags are now returned in the ECX register when one calls |
---|
| 355 | * the CPUID instruction. Their meaning is explained in table 6, but |
---|
| 356 | * DMI doesn't support this yet. |
---|
| 357 | */ |
---|
| 358 | uint32_t eax, edx; |
---|
| 359 | int sig = 0; |
---|
| 360 | |
---|
| 361 | /* |
---|
| 362 | * This might help learn about new processors supporting the |
---|
| 363 | * CPUID instruction or another form of identification. |
---|
| 364 | */ |
---|
| 365 | sprintf(dmi->processor.id, "ID: %02X %02X %02X %02X %02X %02X %02X %02X\n", |
---|
| 366 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); |
---|
| 367 | |
---|
| 368 | if (type == 0x05) { /* 80386 */ |
---|
| 369 | uint16_t dx = WORD(p); |
---|
| 370 | /* |
---|
| 371 | * 80386 have a different signature. |
---|
| 372 | */ |
---|
| 373 | dmi->processor.signature.type = (dx >> 12); |
---|
| 374 | dmi->processor.signature.family = ((dx >> 8) & 0xF); |
---|
| 375 | dmi->processor.signature.stepping = (dx >> 4) & 0xF; |
---|
| 376 | dmi->processor.signature.minor_stepping = (dx & 0xF); |
---|
| 377 | return; |
---|
| 378 | } |
---|
| 379 | if (type == 0x06) { /* 80486 */ |
---|
| 380 | uint16_t dx = WORD(p); |
---|
| 381 | /* |
---|
| 382 | * Not all 80486 CPU support the CPUID instruction, we have to find |
---|
| 383 | * wether the one we have here does or not. Note that this trick |
---|
| 384 | * works only because we know that 80486 must be little-endian. |
---|
| 385 | */ |
---|
| 386 | if ((dx & 0x0F00) == 0x0400 |
---|
| 387 | && ((dx & 0x00F0) == 0x0040 || (dx & 0x00F0) >= 0x0070) |
---|
| 388 | && ((dx & 0x000F) >= 0x0003)) |
---|
| 389 | sig = 1; |
---|
| 390 | else { |
---|
| 391 | dmi->processor.signature.type = ((dx >> 12) & 0x3); |
---|
| 392 | dmi->processor.signature.family = ((dx >> 8) & 0xF); |
---|
| 393 | dmi->processor.signature.model = ((dx >> 4) & 0xF); |
---|
| 394 | dmi->processor.signature.stepping = (dx & 0xF); |
---|
| 395 | return; |
---|
| 396 | } |
---|
| 397 | } else if ((type >= 0x0B && type <= 0x13) /* Intel, Cyrix */ |
---|
| 398 | ||(type >= 0xB0 && type <= 0xB3) /* Intel */ |
---|
| 399 | ||type == 0xB5 /* Intel */ |
---|
| 400 | || type == 0xB9) /* Intel */ |
---|
| 401 | sig = 1; |
---|
| 402 | else if ((type >= 0x18 && type <= 0x1D) /* AMD */ |
---|
| 403 | ||type == 0x1F /* AMD */ |
---|
| 404 | || (type >= 0xB6 && type <= 0xB7) /* AMD */ |
---|
| 405 | ||(type >= 0x83 && type <= 0x85)) /* AMD */ |
---|
| 406 | sig = 2; |
---|
| 407 | else if (type == 0x01 || type == 0x02) { |
---|
| 408 | /* |
---|
| 409 | * Some X86-class CPU have family "Other" or "Unknown". In this case, |
---|
| 410 | * we use the version string to determine if they are known to |
---|
| 411 | * support the CPUID instruction. |
---|
| 412 | */ |
---|
| 413 | if (strncmp(version, "Pentium III MMX", 15) == 0) |
---|
| 414 | sig = 1; |
---|
| 415 | else if (strncmp(version, "AMD Athlon(TM)", 14) == 0 |
---|
| 416 | || strncmp(version, "AMD Opteron(tm)", 15) == 0) |
---|
| 417 | sig = 2; |
---|
| 418 | else |
---|
| 419 | return; |
---|
| 420 | } else /* not X86-class */ |
---|
| 421 | return; |
---|
| 422 | |
---|
| 423 | eax = DWORD(p); |
---|
| 424 | edx = DWORD(p + 4); |
---|
| 425 | switch (sig) { |
---|
| 426 | case 1: /* Intel */ |
---|
| 427 | dmi->processor.signature.type = ((eax >> 12) & 0x3); |
---|
| 428 | dmi->processor.signature.family = |
---|
| 429 | (((eax >> 16) & 0xFF0) + ((eax >> 8) & 0x00F)); |
---|
| 430 | dmi->processor.signature.model = |
---|
| 431 | (((eax >> 12) & 0xF0) + ((eax >> 4) & 0x0F)); |
---|
| 432 | dmi->processor.signature.stepping = (eax & 0xF); |
---|
| 433 | break; |
---|
| 434 | case 2: /* AMD */ |
---|
| 435 | dmi->processor.signature.family = |
---|
| 436 | (((eax >> 8) & 0xF) == 0xF ? (eax >> 20) & 0xFF : (eax >> 8) & 0xF); |
---|
| 437 | dmi->processor.signature.model = |
---|
| 438 | (((eax >> 4) & 0xF) == 0xF ? (eax >> 16) & 0xF : (eax >> 4) & 0xF); |
---|
| 439 | dmi->processor.signature.stepping = (eax & 0xF); |
---|
| 440 | break; |
---|
| 441 | } |
---|
| 442 | |
---|
| 443 | edx = DWORD(p + 4); |
---|
| 444 | if ((edx & 0x3FF7FDFF) != 0) { |
---|
| 445 | int i; |
---|
| 446 | for (i = 0; i <= 31; i++) |
---|
| 447 | if (cpu_flags_strings[i] != NULL && edx & (1 << i)) |
---|
| 448 | ((bool *) (&dmi->processor.cpu_flags))[i] = true; |
---|
| 449 | } |
---|
| 450 | } |
---|
| 451 | |
---|
| 452 | void to_dmi_header(struct dmi_header *h, uint8_t * data) |
---|
| 453 | { |
---|
| 454 | h->type = data[0]; |
---|
| 455 | h->length = data[1]; |
---|
| 456 | h->handle = WORD(data + 2); |
---|
| 457 | h->data = data; |
---|
| 458 | } |
---|
| 459 | |
---|
| 460 | const char *dmi_string(struct dmi_header *dm, uint8_t s) |
---|
| 461 | { |
---|
| 462 | char *bp = (char *)dm->data; |
---|
| 463 | size_t i, len; |
---|
| 464 | |
---|
| 465 | if (s == 0) |
---|
| 466 | return "Not Specified"; |
---|
| 467 | |
---|
| 468 | bp += dm->length; |
---|
| 469 | while (s > 1 && *bp) { |
---|
| 470 | bp += strlen(bp); |
---|
| 471 | bp++; |
---|
| 472 | s--; |
---|
| 473 | } |
---|
| 474 | |
---|
| 475 | if (!*bp) |
---|
| 476 | return bad_index; |
---|
| 477 | |
---|
| 478 | /* ASCII filtering */ |
---|
| 479 | len = strlen(bp); |
---|
| 480 | for (i = 0; i < len; i++) |
---|
| 481 | if (bp[i] < 32 || bp[i] == 127) |
---|
| 482 | bp[i] = '.'; |
---|
| 483 | |
---|
| 484 | return bp; |
---|
| 485 | } |
---|
| 486 | |
---|
| 487 | int checksum(uint8_t * buf, int len) |
---|
| 488 | { |
---|
| 489 | uint8_t sum = 0; |
---|
| 490 | int a; |
---|
| 491 | |
---|
| 492 | for (a = 0; a < len; a++) |
---|
| 493 | sum += buf[a]; |
---|
| 494 | return (sum == 0); |
---|
| 495 | } |
---|
| 496 | |
---|
| 497 | static int smbios_decode(s_dmi * dmi, uint8_t * buf) |
---|
| 498 | { |
---|
| 499 | |
---|
| 500 | dmi->dmitable.ver = (buf[0x06] << 8) + buf[0x07]; |
---|
| 501 | /* Some BIOS report weird SMBIOS version, fix that up */ |
---|
| 502 | switch (dmi->dmitable.ver) { |
---|
| 503 | case 0x021F: |
---|
| 504 | dmi->dmitable.ver = 0x0203; |
---|
| 505 | break; |
---|
| 506 | case 0x0233: |
---|
| 507 | dmi->dmitable.ver = 0x0206; |
---|
| 508 | break; |
---|
| 509 | } |
---|
| 510 | dmi->dmitable.major_version = dmi->dmitable.ver >> 8; |
---|
| 511 | dmi->dmitable.minor_version = dmi->dmitable.ver & 0xFF; |
---|
| 512 | |
---|
| 513 | return DMI_TABLE_PRESENT; |
---|
| 514 | } |
---|
| 515 | |
---|
| 516 | static int legacy_decode(s_dmi * dmi, uint8_t * buf) |
---|
| 517 | { |
---|
| 518 | dmi->dmitable.num = buf[13] << 8 | buf[12]; |
---|
| 519 | dmi->dmitable.len = buf[7] << 8 | buf[6]; |
---|
| 520 | dmi->dmitable.base = buf[11] << 24 | buf[10] << 16 | buf[9] << 8 | buf[8]; |
---|
| 521 | |
---|
| 522 | /* Version already found? */ |
---|
| 523 | if (dmi->dmitable.ver > 0) |
---|
| 524 | return DMI_TABLE_PRESENT; |
---|
| 525 | |
---|
| 526 | dmi->dmitable.ver = (buf[0x06] << 8) + buf[0x07]; |
---|
| 527 | |
---|
| 528 | /* |
---|
| 529 | * DMI version 0.0 means that the real version is taken from |
---|
| 530 | * the SMBIOS version, which we don't know at this point. |
---|
| 531 | */ |
---|
| 532 | if (buf[14] != 0) { |
---|
| 533 | dmi->dmitable.major_version = buf[14] >> 4; |
---|
| 534 | dmi->dmitable.minor_version = buf[14] & 0x0F; |
---|
| 535 | } else { |
---|
| 536 | dmi->dmitable.major_version = 0; |
---|
| 537 | dmi->dmitable.minor_version = 0; |
---|
| 538 | } |
---|
| 539 | return DMI_TABLE_PRESENT; |
---|
| 540 | } |
---|
| 541 | |
---|
| 542 | int dmi_iterate(s_dmi * dmi) |
---|
| 543 | { |
---|
| 544 | uint8_t *p, *q; |
---|
| 545 | int found = 0; |
---|
| 546 | |
---|
| 547 | /* Cleaning structures */ |
---|
| 548 | memset(dmi, 0, sizeof(s_dmi)); |
---|
| 549 | |
---|
| 550 | memset(&dmi->base_board, 0, sizeof(s_base_board)); |
---|
| 551 | memset(&dmi->battery, 0, sizeof(s_battery)); |
---|
| 552 | memset(&dmi->bios, 0, sizeof(s_bios)); |
---|
| 553 | memset(&dmi->chassis, 0, sizeof(s_chassis)); |
---|
| 554 | for (int i = 0; i < MAX_DMI_MEMORY_ITEMS; i++) |
---|
| 555 | memset(&dmi->memory[i], 0, sizeof(s_memory)); |
---|
| 556 | memset(&dmi->processor, 0, sizeof(s_processor)); |
---|
| 557 | memset(&dmi->system, 0, sizeof(s_system)); |
---|
| 558 | |
---|
| 559 | /* Until we found this elements in the dmitable, we consider them as not filled */ |
---|
| 560 | dmi->base_board.filled = false; |
---|
| 561 | dmi->battery.filled = false; |
---|
| 562 | dmi->bios.filled = false; |
---|
| 563 | dmi->chassis.filled = false; |
---|
| 564 | for (int i = 0; i < MAX_DMI_MEMORY_ITEMS; i++) |
---|
| 565 | dmi->memory[i].filled = false; |
---|
| 566 | dmi->processor.filled = false; |
---|
| 567 | dmi->system.filled = false; |
---|
| 568 | |
---|
| 569 | p = (uint8_t *) 0xF0000; /* The start address to look at the dmi table */ |
---|
| 570 | /* The anchor-string is 16-bytes aligned */ |
---|
| 571 | for (q = p; q < p + 0x10000; q += 16) { |
---|
| 572 | /* To validate the presence of SMBIOS: |
---|
| 573 | * + the overall checksum must be correct |
---|
| 574 | * + the intermediate anchor-string must be _DMI_ |
---|
| 575 | * + the intermediate checksum must be correct |
---|
| 576 | */ |
---|
| 577 | if (memcmp(q, "_SM_", 4) == 0 && |
---|
| 578 | checksum(q, q[0x05]) && |
---|
| 579 | memcmp(q + 0x10, "_DMI_", 5) == 0 && checksum(q + 0x10, 0x0F)) { |
---|
| 580 | /* Do not return, legacy_decode will need to be called |
---|
| 581 | * on the intermediate structure to get the table length |
---|
| 582 | * and address |
---|
| 583 | */ |
---|
| 584 | smbios_decode(dmi, q); |
---|
| 585 | } else if (memcmp(q, "_DMI_", 5) == 0 && checksum(q, 0x0F)) { |
---|
| 586 | found = 1; |
---|
| 587 | legacy_decode(dmi, q); |
---|
| 588 | } |
---|
| 589 | } |
---|
| 590 | |
---|
| 591 | if (found) |
---|
| 592 | return DMI_TABLE_PRESENT; |
---|
| 593 | |
---|
| 594 | dmi->dmitable.base = 0; |
---|
| 595 | dmi->dmitable.num = 0; |
---|
| 596 | dmi->dmitable.ver = 0; |
---|
| 597 | dmi->dmitable.len = 0; |
---|
| 598 | return -ENODMITABLE; |
---|
| 599 | } |
---|
| 600 | |
---|
| 601 | void dmi_decode(struct dmi_header *h, uint16_t ver, s_dmi * dmi) |
---|
| 602 | { |
---|
| 603 | uint8_t *data = h->data; |
---|
| 604 | |
---|
| 605 | /* |
---|
| 606 | * Note: DMI types 37, 38 and 39 are untested |
---|
| 607 | */ |
---|
| 608 | switch (h->type) { |
---|
| 609 | case 0: /* 3.3.1 BIOS Information */ |
---|
| 610 | if (h->length < 0x12) |
---|
| 611 | break; |
---|
| 612 | dmi->bios.filled = true; |
---|
| 613 | strlcpy(dmi->bios.vendor, dmi_string(h, data[0x04]), |
---|
| 614 | sizeof(dmi->bios.vendor)); |
---|
| 615 | strlcpy(dmi->bios.version, dmi_string(h, data[0x05]), |
---|
| 616 | sizeof(dmi->bios.version)); |
---|
| 617 | strlcpy(dmi->bios.release_date, dmi_string(h, data[0x08]), |
---|
| 618 | sizeof(dmi->bios.release_date)); |
---|
| 619 | dmi->bios.address = WORD(data + 0x06); |
---|
| 620 | dmi_bios_runtime_size((0x10000 - WORD(data + 0x06)) << 4, dmi); |
---|
| 621 | dmi->bios.rom_size = (data[0x09] + 1) << 6; |
---|
| 622 | strlcpy(dmi->bios.rom_size_unit, "kB", sizeof(dmi->bios.rom_size_unit)); |
---|
| 623 | dmi_bios_characteristics(QWORD(data + 0x0A), dmi); |
---|
| 624 | |
---|
| 625 | if (h->length < 0x13) |
---|
| 626 | break; |
---|
| 627 | dmi_bios_characteristics_x1(data[0x12], dmi); |
---|
| 628 | if (h->length < 0x14) |
---|
| 629 | break; |
---|
| 630 | dmi_bios_characteristics_x2(data[0x13], dmi); |
---|
| 631 | if (h->length < 0x18) |
---|
| 632 | break; |
---|
| 633 | if (data[0x14] != 0xFF && data[0x15] != 0xFF) |
---|
| 634 | snprintf(dmi->bios.bios_revision, sizeof(dmi->bios.bios_revision), |
---|
| 635 | "%u.%u", data[0x14], data[0x15]); |
---|
| 636 | if (data[0x16] != 0xFF && data[0x17] != 0xFF) |
---|
| 637 | snprintf(dmi->bios.firmware_revision, |
---|
| 638 | sizeof(dmi->bios.firmware_revision), "%u.%u", data[0x16], |
---|
| 639 | data[0x17]); |
---|
| 640 | break; |
---|
| 641 | case 1: /* 3.3.2 System Information */ |
---|
| 642 | if (h->length < 0x08) |
---|
| 643 | break; |
---|
| 644 | dmi->system.filled = true; |
---|
| 645 | strlcpy(dmi->system.manufacturer, dmi_string(h, data[0x04]), |
---|
| 646 | sizeof(dmi->system.manufacturer)); |
---|
| 647 | strlcpy(dmi->system.product_name, dmi_string(h, data[0x05]), |
---|
| 648 | sizeof(dmi->system.product_name)); |
---|
| 649 | strlcpy(dmi->system.version, dmi_string(h, data[0x06]), |
---|
| 650 | sizeof(dmi->system.version)); |
---|
| 651 | strlcpy(dmi->system.serial, dmi_string(h, data[0x07]), |
---|
| 652 | sizeof(dmi->system.serial)); |
---|
| 653 | if (h->length < 0x19) |
---|
| 654 | break; |
---|
| 655 | dmi_system_uuid(data + 0x08, dmi); |
---|
| 656 | dmi_system_wake_up_type(data[0x18], dmi); |
---|
| 657 | if (h->length < 0x1B) |
---|
| 658 | break; |
---|
| 659 | strlcpy(dmi->system.sku_number, dmi_string(h, data[0x19]), |
---|
| 660 | sizeof(dmi->system.sku_number)); |
---|
| 661 | strlcpy(dmi->system.family, dmi_string(h, data[0x1A]), |
---|
| 662 | sizeof(dmi->system.family)); |
---|
| 663 | break; |
---|
| 664 | |
---|
| 665 | case 2: /* 3.3.3 Base Board Information */ |
---|
| 666 | if (h->length < 0x08) |
---|
| 667 | break; |
---|
| 668 | dmi->base_board.filled = true; |
---|
| 669 | strlcpy(dmi->base_board.manufacturer, dmi_string(h, data[0x04]), |
---|
| 670 | sizeof(dmi->base_board.manufacturer)); |
---|
| 671 | strlcpy(dmi->base_board.product_name, dmi_string(h, data[0x05]), |
---|
| 672 | sizeof(dmi->base_board.product_name)); |
---|
| 673 | strlcpy(dmi->base_board.version, dmi_string(h, data[0x06]), |
---|
| 674 | sizeof(dmi->base_board.version)); |
---|
| 675 | strlcpy(dmi->base_board.serial, dmi_string(h, data[0x07]), |
---|
| 676 | sizeof(dmi->base_board.serial)); |
---|
| 677 | if (h->length < 0x0F) |
---|
| 678 | break; |
---|
| 679 | strlcpy(dmi->base_board.asset_tag, dmi_string(h, data[0x08]), |
---|
| 680 | sizeof(dmi->base_board.asset_tag)); |
---|
| 681 | dmi_base_board_features(data[0x09], dmi); |
---|
| 682 | strlcpy(dmi->base_board.location, dmi_string(h, data[0x0A]), |
---|
| 683 | sizeof(dmi->base_board.location)); |
---|
| 684 | dmi_base_board_type(data[0x0D], dmi); |
---|
| 685 | if (h->length < 0x0F + data[0x0E] * sizeof(uint16_t)) |
---|
| 686 | break; |
---|
| 687 | break; |
---|
| 688 | case 3: /* 3.3.4 Chassis Information */ |
---|
| 689 | if (h->length < 0x09) |
---|
| 690 | break; |
---|
| 691 | dmi->chassis.filled = true; |
---|
| 692 | strlcpy(dmi->chassis.manufacturer, dmi_string(h, data[0x04]), |
---|
| 693 | sizeof(dmi->chassis.manufacturer)); |
---|
| 694 | strlcpy(dmi->chassis.type, dmi_chassis_type(data[0x05] & 0x7F), |
---|
| 695 | sizeof(dmi->chassis.type)); |
---|
| 696 | strlcpy(dmi->chassis.lock, dmi_chassis_lock(data[0x05] >> 7), |
---|
| 697 | sizeof(dmi->chassis.lock)); |
---|
| 698 | strlcpy(dmi->chassis.version, dmi_string(h, data[0x06]), |
---|
| 699 | sizeof(dmi->chassis.version)); |
---|
| 700 | strlcpy(dmi->chassis.serial, dmi_string(h, data[0x07]), |
---|
| 701 | sizeof(dmi->chassis.serial)); |
---|
| 702 | strlcpy(dmi->chassis.asset_tag, dmi_string(h, data[0x08]), |
---|
| 703 | sizeof(dmi->chassis.asset_tag)); |
---|
| 704 | if (h->length < 0x0D) |
---|
| 705 | break; |
---|
| 706 | strlcpy(dmi->chassis.boot_up_state, dmi_chassis_state(data[0x09]), |
---|
| 707 | sizeof(dmi->chassis.boot_up_state)); |
---|
| 708 | strlcpy(dmi->chassis.power_supply_state, |
---|
| 709 | dmi_chassis_state(data[0x0A]), |
---|
| 710 | sizeof(dmi->chassis.power_supply_state)); |
---|
| 711 | strlcpy(dmi->chassis.thermal_state, |
---|
| 712 | dmi_chassis_state(data[0x0B]), |
---|
| 713 | sizeof(dmi->chassis.thermal_state)); |
---|
| 714 | strlcpy(dmi->chassis.security_status, |
---|
| 715 | dmi_chassis_security_status(data[0x0C]), |
---|
| 716 | sizeof(dmi->chassis.security_status)); |
---|
| 717 | if (h->length < 0x11) |
---|
| 718 | break; |
---|
| 719 | snprintf(dmi->chassis.oem_information, |
---|
| 720 | sizeof(dmi->chassis.oem_information), "0x%08X", |
---|
| 721 | DWORD(data + 0x0D)); |
---|
| 722 | if (h->length < 0x15) |
---|
| 723 | break; |
---|
| 724 | dmi->chassis.height = data[0x11]; |
---|
| 725 | dmi->chassis.nb_power_cords = data[0x12]; |
---|
| 726 | break; |
---|
| 727 | case 4: /* 3.3.5 Processor Information */ |
---|
| 728 | if (h->length < 0x1A) |
---|
| 729 | break; |
---|
| 730 | dmi->processor.filled = true; |
---|
| 731 | strlcpy(dmi->processor.socket_designation, |
---|
| 732 | dmi_string(h, data[0x04]), |
---|
| 733 | sizeof(dmi->processor.socket_designation)); |
---|
| 734 | strlcpy(dmi->processor.type, |
---|
| 735 | dmi_processor_type(data[0x05]), sizeof(dmi->processor.type)); |
---|
| 736 | strlcpy(dmi->processor.manufacturer, |
---|
| 737 | dmi_string(h, data[0x07]), sizeof(dmi->processor.manufacturer)); |
---|
| 738 | strlcpy(dmi->processor.family, |
---|
| 739 | dmi_processor_family(data[0x06], |
---|
| 740 | dmi->processor.manufacturer), |
---|
| 741 | sizeof(dmi->processor.family)); |
---|
| 742 | dmi_processor_id(data[0x06], data + 8, dmi_string(h, data[0x10]), dmi); |
---|
| 743 | strlcpy(dmi->processor.version, |
---|
| 744 | dmi_string(h, data[0x10]), sizeof(dmi->processor.version)); |
---|
| 745 | dmi_processor_voltage(data[0x11], dmi); |
---|
| 746 | dmi->processor.external_clock = WORD(data + 0x12); |
---|
| 747 | dmi->processor.max_speed = WORD(data + 0x14); |
---|
| 748 | dmi->processor.current_speed = WORD(data + 0x16); |
---|
| 749 | if (data[0x18] & (1 << 6)) |
---|
| 750 | strlcpy(dmi->processor.status, |
---|
| 751 | dmi_processor_status(data[0x18] & 0x07), |
---|
| 752 | sizeof(dmi->processor.status)); |
---|
| 753 | else |
---|
| 754 | sprintf(dmi->processor.status, "Unpopulated"); |
---|
| 755 | strlcpy(dmi->processor.upgrade, |
---|
| 756 | dmi_processor_upgrade(data[0x19]), |
---|
| 757 | sizeof(dmi->processor.upgrade)); |
---|
| 758 | if (h->length < 0x20) |
---|
| 759 | break; |
---|
| 760 | dmi_processor_cache(WORD(data + 0x1A), "L1", ver, |
---|
| 761 | dmi->processor.cache1); |
---|
| 762 | dmi_processor_cache(WORD(data + 0x1C), "L2", ver, |
---|
| 763 | dmi->processor.cache2); |
---|
| 764 | dmi_processor_cache(WORD(data + 0x1E), "L3", ver, |
---|
| 765 | dmi->processor.cache3); |
---|
| 766 | if (h->length < 0x23) |
---|
| 767 | break; |
---|
| 768 | strlcpy(dmi->processor.serial, dmi_string(h, data[0x20]), |
---|
| 769 | sizeof(dmi->processor.serial)); |
---|
| 770 | strlcpy(dmi->processor.asset_tag, dmi_string(h, data[0x21]), |
---|
| 771 | sizeof(dmi->processor.asset_tag)); |
---|
| 772 | strlcpy(dmi->processor.part_number, dmi_string(h, data[0x22]), |
---|
| 773 | sizeof(dmi->processor.part_number)); |
---|
| 774 | dmi->processor.core_count = 0; |
---|
| 775 | dmi->processor.core_enabled = 0; |
---|
| 776 | dmi->processor.thread_count = 0; |
---|
| 777 | if (h->length < 0x28) |
---|
| 778 | break; |
---|
| 779 | dmi->processor.core_count = data[0x23]; |
---|
| 780 | dmi->processor.core_enabled = data[0x24]; |
---|
| 781 | dmi->processor.thread_count = data[0x25]; |
---|
| 782 | break; |
---|
| 783 | case 6: /* 3.3.7 Memory Module Information */ |
---|
| 784 | if (h->length < 0x0C) |
---|
| 785 | break; |
---|
| 786 | dmi->memory_module_count++; |
---|
| 787 | s_memory_module *module = |
---|
| 788 | &dmi->memory_module[dmi->memory_module_count - 1]; |
---|
| 789 | dmi->memory_module[dmi->memory_module_count - 1].filled = true; |
---|
| 790 | strlcpy(module->socket_designation, dmi_string(h, data[0x04]), |
---|
| 791 | sizeof(module->socket_designation)); |
---|
| 792 | dmi_memory_module_connections(data[0x05], module->bank_connections, sizeof(module->bank_connections)); |
---|
| 793 | dmi_memory_module_speed(data[0x06], module->speed); |
---|
| 794 | dmi_memory_module_types(WORD(data + 0x07), " ", module->type, sizeof(module->type)); |
---|
| 795 | dmi_memory_module_size(data[0x09], module->installed_size, sizeof(module->installed_size)); |
---|
| 796 | dmi_memory_module_size(data[0x0A], module->enabled_size, sizeof(module->enabled_size)); |
---|
| 797 | dmi_memory_module_error(data[0x0B], "\t\t", module->error_status); |
---|
| 798 | break; |
---|
| 799 | case 7: /* 3.3.8 Cache Information */ |
---|
| 800 | if (h->length < 0x0F) |
---|
| 801 | break; |
---|
| 802 | dmi->cache_count++; |
---|
| 803 | if (dmi->cache_count > MAX_DMI_CACHE_ITEMS) |
---|
| 804 | break; |
---|
| 805 | strlcpy(dmi->cache[dmi->cache_count - 1].socket_designation, |
---|
| 806 | dmi_string(h, data[0x04]), |
---|
| 807 | sizeof(dmi->cache[dmi->cache_count - 1].socket_designation)); |
---|
| 808 | snprintf(dmi->cache[dmi->cache_count - 1].configuration, |
---|
| 809 | sizeof(dmi->cache[dmi->cache_count - 1].configuration), |
---|
| 810 | "%s, %s, %u", |
---|
| 811 | WORD(data + 0x05) & 0x0080 ? "Enabled" : "Disabled", |
---|
| 812 | WORD(data + |
---|
| 813 | 0x05) & 0x0008 ? "Socketed" : "Not Socketed", |
---|
| 814 | (WORD(data + 0x05) & 0x0007) + 1); |
---|
| 815 | strlcpy(dmi->cache[dmi->cache_count - 1].mode, |
---|
| 816 | dmi_cache_mode((WORD(data + 0x05) >> 8) & 0x0003), |
---|
| 817 | sizeof(dmi->cache[dmi->cache_count - 1].mode)); |
---|
| 818 | strlcpy(dmi->cache[dmi->cache_count - 1].location, |
---|
| 819 | dmi_cache_location((WORD(data + 0x05) >> 5) & 0x0003), |
---|
| 820 | sizeof(dmi->cache[dmi->cache_count - 1].location)); |
---|
| 821 | dmi->cache[dmi->cache_count - 1].installed_size = |
---|
| 822 | dmi_cache_size(WORD(data + 0x09)); |
---|
| 823 | dmi->cache[dmi->cache_count - 1].max_size = |
---|
| 824 | dmi_cache_size(WORD(data + 0x07)); |
---|
| 825 | dmi_cache_types(WORD(data + 0x0B), " ", |
---|
| 826 | dmi->cache[dmi->cache_count - 1].supported_sram_types); |
---|
| 827 | dmi_cache_types(WORD(data + 0x0D), " ", |
---|
| 828 | dmi->cache[dmi->cache_count - 1].installed_sram_types); |
---|
| 829 | if (h->length < 0x13) |
---|
| 830 | break; |
---|
| 831 | dmi->cache[dmi->cache_count - 1].speed = data[0x0F]; /* ns */ |
---|
| 832 | strlcpy(dmi->cache[dmi->cache_count - 1].error_correction_type, |
---|
| 833 | dmi_cache_ec_type(data[0x10]), |
---|
| 834 | sizeof(dmi->cache[dmi->cache_count - 1].error_correction_type)); |
---|
| 835 | strlcpy(dmi->cache[dmi->cache_count - 1].system_type, |
---|
| 836 | dmi_cache_type(data[0x11]), |
---|
| 837 | sizeof(dmi->cache[dmi->cache_count - 1].system_type)); |
---|
| 838 | strlcpy(dmi->cache[dmi->cache_count - 1].associativity, |
---|
| 839 | dmi_cache_associativity(data[0x12]), |
---|
| 840 | sizeof(dmi->cache[dmi->cache_count - 1].associativity)); |
---|
| 841 | break; |
---|
| 842 | case 10: /* 3.3.11 On Board Devices Information */ |
---|
| 843 | dmi_on_board_devices(h, dmi); |
---|
| 844 | break; |
---|
| 845 | case 11: /* 3.3.12 OEM Strings */ |
---|
| 846 | if (h->length < 0x05) |
---|
| 847 | break; |
---|
| 848 | dmi_oem_strings(h, "\t", dmi); |
---|
| 849 | break; |
---|
| 850 | case 12: /* 3.3.13 System Configuration Options */ |
---|
| 851 | if (h->length < 0x05) |
---|
| 852 | break; |
---|
| 853 | dmi_system_configuration_options(h, "\t", dmi); |
---|
| 854 | break; |
---|
| 855 | case 17: /* 3.3.18 Memory Device */ |
---|
| 856 | if (h->length < 0x15) |
---|
| 857 | break; |
---|
| 858 | dmi->memory_count++; |
---|
| 859 | if (dmi->memory_count > MAX_DMI_MEMORY_ITEMS) |
---|
| 860 | break; |
---|
| 861 | s_memory *mem = &dmi->memory[dmi->memory_count - 1]; |
---|
| 862 | dmi->memory[dmi->memory_count - 1].filled = true; |
---|
| 863 | dmi_memory_array_error_handle(WORD(data + 0x06), mem->error); |
---|
| 864 | dmi_memory_device_width(WORD(data + 0x08), mem->total_width); |
---|
| 865 | dmi_memory_device_width(WORD(data + 0x0A), mem->data_width); |
---|
| 866 | dmi_memory_device_size(WORD(data + 0x0C), mem->size); |
---|
| 867 | strlcpy(mem->form_factor, |
---|
| 868 | dmi_memory_device_form_factor(data[0x0E]), |
---|
| 869 | sizeof(mem->form_factor)); |
---|
| 870 | dmi_memory_device_set(data[0x0F], mem->device_set); |
---|
| 871 | strlcpy(mem->device_locator, dmi_string(h, data[0x10]), |
---|
| 872 | sizeof(mem->device_locator)); |
---|
| 873 | strlcpy(mem->bank_locator, dmi_string(h, data[0x11]), |
---|
| 874 | sizeof(mem->bank_locator)); |
---|
| 875 | strlcpy(mem->type, dmi_memory_device_type(data[0x12]), |
---|
| 876 | sizeof(mem->type)); |
---|
| 877 | dmi_memory_device_type_detail(WORD(data + 0x13), mem->type_detail, sizeof(mem->type_detail)); |
---|
| 878 | if (h->length < 0x17) |
---|
| 879 | break; |
---|
| 880 | dmi_memory_device_speed(WORD(data + 0x15), mem->speed); |
---|
| 881 | if (h->length < 0x1B) |
---|
| 882 | break; |
---|
| 883 | strlcpy(mem->manufacturer, dmi_string(h, data[0x17]), |
---|
| 884 | sizeof(mem->manufacturer)); |
---|
| 885 | strlcpy(mem->serial, dmi_string(h, data[0x18]), sizeof(mem->serial)); |
---|
| 886 | strlcpy(mem->asset_tag, dmi_string(h, data[0x19]), |
---|
| 887 | sizeof(mem->asset_tag)); |
---|
| 888 | strlcpy(mem->part_number, dmi_string(h, data[0x1A]), |
---|
| 889 | sizeof(mem->part_number)); |
---|
| 890 | break; |
---|
| 891 | case 22: /* 3.3.23 Portable Battery */ |
---|
| 892 | if (h->length < 0x10) |
---|
| 893 | break; |
---|
| 894 | dmi->battery.filled = true; |
---|
| 895 | strlcpy(dmi->battery.location, dmi_string(h, data[0x04]), |
---|
| 896 | sizeof(dmi->battery.location)); |
---|
| 897 | strlcpy(dmi->battery.manufacturer, dmi_string(h, data[0x05]), |
---|
| 898 | sizeof(dmi->battery.manufacturer)); |
---|
| 899 | if (data[0x06] || h->length < 0x1A) |
---|
| 900 | strlcpy(dmi->battery.manufacture_date, |
---|
| 901 | dmi_string(h, data[0x06]), |
---|
| 902 | sizeof(dmi->battery.manufacture_date)); |
---|
| 903 | if (data[0x07] || h->length < 0x1A) |
---|
| 904 | strlcpy(dmi->battery.serial, dmi_string(h, data[0x07]), |
---|
| 905 | sizeof(dmi->battery.serial)); |
---|
| 906 | strlcpy(dmi->battery.name, dmi_string(h, data[0x08]), |
---|
| 907 | sizeof(dmi->battery.name)); |
---|
| 908 | if (data[0x09] != 0x02 || h->length < 0x1A) |
---|
| 909 | strlcpy(dmi->battery.chemistry, |
---|
| 910 | dmi_battery_chemistry(data[0x09]), |
---|
| 911 | sizeof(dmi->battery.chemistry)); |
---|
| 912 | if (h->length < 0x1A) |
---|
| 913 | dmi_battery_capacity(WORD(data + 0x0A), 1, |
---|
| 914 | dmi->battery.design_capacity); |
---|
| 915 | else |
---|
| 916 | dmi_battery_capacity(WORD(data + 0x0A), data[0x15], |
---|
| 917 | dmi->battery.design_capacity); |
---|
| 918 | dmi_battery_voltage(WORD(data + 0x0C), dmi->battery.design_voltage); |
---|
| 919 | strlcpy(dmi->battery.sbds, dmi_string(h, data[0x0E]), |
---|
| 920 | sizeof(dmi->battery.sbds)); |
---|
| 921 | dmi_battery_maximum_error(data[0x0F], dmi->battery.maximum_error); |
---|
| 922 | if (h->length < 0x1A) |
---|
| 923 | break; |
---|
| 924 | if (data[0x07] == 0) |
---|
| 925 | sprintf(dmi->battery.sbds_serial, "%04X", WORD(data + 0x10)); |
---|
| 926 | if (data[0x06] == 0) |
---|
| 927 | sprintf(dmi->battery.sbds_manufacture_date, "%u-%02u-%02u", |
---|
| 928 | 1980 + (WORD(data + 0x12) >> 9), |
---|
| 929 | (WORD(data + 0x12) >> 5) & 0x0F, WORD(data + 0x12) & 0x1F); |
---|
| 930 | if (data[0x09] == 0x02) |
---|
| 931 | strlcpy(dmi->battery.sbds_chemistry, dmi_string(h, data[0x14]), |
---|
| 932 | sizeof(dmi->battery.sbds_chemistry)); |
---|
| 933 | // sprintf(dmi->battery.oem_info,"0x%08X",DWORD(h, data+0x16)); |
---|
| 934 | break; |
---|
| 935 | case 23: /* 3.3.24 System Reset */ |
---|
| 936 | if (h->length < 0x0D) |
---|
| 937 | break; |
---|
| 938 | dmi->system.system_reset.filled = true; |
---|
| 939 | dmi->system.system_reset.status = data[0x04] & (1 << 0); |
---|
| 940 | dmi->system.system_reset.watchdog = data[0x04] & (1 << 5); |
---|
| 941 | if (!(data[0x04] & (1 << 5))) |
---|
| 942 | break; |
---|
| 943 | strlcpy(dmi->system.system_reset.boot_option, |
---|
| 944 | dmi_system_reset_boot_option((data[0x04] >> 1) & 0x3), |
---|
| 945 | sizeof dmi->system.system_reset.boot_option); |
---|
| 946 | strlcpy(dmi->system.system_reset.boot_option_on_limit, |
---|
| 947 | dmi_system_reset_boot_option((data[0x04] >> 3) & 0x3), |
---|
| 948 | sizeof dmi->system.system_reset.boot_option_on_limit); |
---|
| 949 | dmi_system_reset_count(WORD(data + 0x05), |
---|
| 950 | dmi->system.system_reset.reset_count); |
---|
| 951 | dmi_system_reset_count(WORD(data + 0x07), |
---|
| 952 | dmi->system.system_reset.reset_limit); |
---|
| 953 | dmi_system_reset_timer(WORD(data + 0x09), |
---|
| 954 | dmi->system.system_reset.timer_interval); |
---|
| 955 | dmi_system_reset_timer(WORD(data + 0x0B), |
---|
| 956 | dmi->system.system_reset.timeout); |
---|
| 957 | break; |
---|
| 958 | case 24: /* 3.3.25 Hardware Security */ |
---|
| 959 | if (h->length < 0x05) |
---|
| 960 | break; |
---|
| 961 | dmi->hardware_security.filled = true; |
---|
| 962 | strlcpy(dmi->hardware_security.power_on_passwd_status, |
---|
| 963 | dmi_hardware_security_status(data[0x04] >> 6), |
---|
| 964 | sizeof dmi->hardware_security.power_on_passwd_status); |
---|
| 965 | strlcpy(dmi->hardware_security.keyboard_passwd_status, |
---|
| 966 | dmi_hardware_security_status((data[0x04] >> 4) & 0x3), |
---|
| 967 | sizeof dmi->hardware_security.keyboard_passwd_status); |
---|
| 968 | strlcpy(dmi->hardware_security.administrator_passwd_status, |
---|
| 969 | dmi_hardware_security_status((data[0x04] >> 2) & 0x3), |
---|
| 970 | sizeof dmi->hardware_security.administrator_passwd_status); |
---|
| 971 | strlcpy(dmi->hardware_security.front_panel_reset_status, |
---|
| 972 | dmi_hardware_security_status(data[0x04] & 0x3), |
---|
| 973 | sizeof dmi->hardware_security.front_panel_reset_status); |
---|
| 974 | break; |
---|
| 975 | case 32: /* 3.3.33 System Boot Information */ |
---|
| 976 | if (h->length < 0x0B) |
---|
| 977 | break; |
---|
| 978 | dmi_system_boot_status(data[0x0A], dmi->system.system_boot_status); |
---|
| 979 | case 38: /* 3.3.39 IPMI Device Information */ |
---|
| 980 | if (h->length < 0x10) |
---|
| 981 | break; |
---|
| 982 | dmi->ipmi.filled = true; |
---|
| 983 | snprintf(dmi->ipmi.interface_type, |
---|
| 984 | sizeof(dmi->ipmi.interface_type), "%s", |
---|
| 985 | dmi_ipmi_interface_type(data[0x04])); |
---|
| 986 | dmi->ipmi.major_specification_version = data[0x05] >> 4; |
---|
| 987 | dmi->ipmi.minor_specification_version = data[0x05] & 0x0F; |
---|
| 988 | dmi->ipmi.I2C_slave_address = data[0x06] >> 1; |
---|
| 989 | if (data[0x07] != 0xFF) |
---|
| 990 | dmi->ipmi.nv_address = data[0x07]; |
---|
| 991 | else |
---|
| 992 | dmi->ipmi.nv_address = 0; /* Not Present */ |
---|
| 993 | dmi_ipmi_base_address(data[0x04], data + 0x08, &dmi->ipmi); |
---|
| 994 | if (h->length < 0x12) |
---|
| 995 | break; |
---|
| 996 | if (data[0x11] != 0x00) { |
---|
| 997 | dmi->ipmi.irq = data[0x11]; |
---|
| 998 | } |
---|
| 999 | break; |
---|
| 1000 | } |
---|
| 1001 | } |
---|
| 1002 | |
---|
| 1003 | void parse_dmitable(s_dmi * dmi) |
---|
| 1004 | { |
---|
| 1005 | int i = 0; |
---|
| 1006 | uint8_t *data = NULL; |
---|
| 1007 | uint8_t buf[dmi->dmitable.len]; |
---|
| 1008 | memcpy(buf, (int *)dmi->dmitable.base, sizeof(uint8_t) * dmi->dmitable.len); |
---|
| 1009 | data = buf; |
---|
| 1010 | dmi->memory_count = 0; |
---|
| 1011 | while (i < dmi->dmitable.num && data + 4 <= buf + dmi->dmitable.len) { /* 4 is the length of an SMBIOS structure header */ |
---|
| 1012 | uint8_t *next; |
---|
| 1013 | struct dmi_header h; |
---|
| 1014 | to_dmi_header(&h, data); |
---|
| 1015 | /* |
---|
| 1016 | * If a short entry is found (less than 4 bytes), not only it |
---|
| 1017 | * is invalid, but we cannot reliably locate the next entry. |
---|
| 1018 | * Better stop at this point, and let the user know his/her |
---|
| 1019 | * table is broken. |
---|
| 1020 | */ |
---|
| 1021 | if (h.length < 4) { |
---|
| 1022 | printf |
---|
| 1023 | ("Invalid entry length (%u). DMI table is broken! Stop.\n\n", |
---|
| 1024 | (unsigned int)h.length); |
---|
| 1025 | break; |
---|
| 1026 | } |
---|
| 1027 | |
---|
| 1028 | /* loo for the next handle */ |
---|
| 1029 | next = data + h.length; |
---|
| 1030 | while (next - buf + 1 < dmi->dmitable.len |
---|
| 1031 | && (next[0] != 0 || next[1] != 0)) |
---|
| 1032 | next++; |
---|
| 1033 | next += 2; |
---|
| 1034 | if (next - buf <= dmi->dmitable.len) { |
---|
| 1035 | dmi_decode(&h, dmi->dmitable.ver, dmi); |
---|
| 1036 | } |
---|
| 1037 | data = next; |
---|
| 1038 | i++; |
---|
| 1039 | } |
---|
| 1040 | } |
---|