Martin Roth | 9b1b335 | 2016-02-24 12:27:06 -0800 | [diff] [blame] | 1 | /* dmi.c using the DMI from SMBIOS to read information about the hardware's |
| 2 | * memory devices capabilities and where they are mapped into the address space |
| 3 | * |
| 4 | * Copyright (c) Joachim Deguara, AMD 2006 |
| 5 | * |
| 6 | * Release under the GPL version 2 |
| 7 | * ---------------------------------------------------- |
| 8 | * Memtest86+ V4.00 - Added compliance with SMBIOS Spec V2.6.1 |
| 9 | */ |
| 10 | |
| 11 | |
| 12 | #include "test.h" |
| 13 | #include <stdint.h> |
| 14 | |
| 15 | |
| 16 | #define round_up(x,y) (((x) + (y) - 1) & ~((y)-1)) |
| 17 | #define round_down(x,y) ((x) & ~((y)-1)) |
| 18 | |
| 19 | |
| 20 | struct dmi_eps { |
| 21 | uint8_t anchor[4]; |
| 22 | int8_t checksum; |
| 23 | uint8_t length; |
| 24 | uint8_t majorversion; |
| 25 | uint8_t minorversion; |
| 26 | uint16_t maxstructsize; |
| 27 | uint8_t revision; |
| 28 | uint8_t pad[5]; |
| 29 | uint8_t intanchor[5]; |
| 30 | int8_t intchecksum; |
| 31 | uint16_t tablelength; |
| 32 | uint32_t tableaddress; |
| 33 | uint16_t numstructs; |
| 34 | uint8_t SMBIOSrev; |
| 35 | } __attribute__((packed)); |
| 36 | |
| 37 | struct tstruct_header{ |
| 38 | uint8_t type; |
| 39 | uint8_t length; |
| 40 | uint16_t handle; |
| 41 | } __attribute__((packed)); |
| 42 | |
| 43 | struct system_map { |
| 44 | struct tstruct_header header; |
| 45 | uint8_t manufacturer; |
| 46 | uint8_t productname; |
| 47 | uint8_t version; |
| 48 | uint8_t serialnumber; |
| 49 | uint8_t uuidbytes[16]; |
| 50 | uint8_t wut; |
| 51 | } __attribute__((packed)); |
| 52 | |
| 53 | struct cpu_map { |
| 54 | struct tstruct_header header; |
| 55 | uint8_t cpu_socket; |
| 56 | uint8_t cpu_type; |
| 57 | uint8_t cpu_family; |
| 58 | uint8_t cpu_manufacturer; |
| 59 | uint32_t cpu_id; |
| 60 | uint8_t cpu_version; |
| 61 | uint8_t cpu_voltage; |
| 62 | uint16_t ext_clock; |
| 63 | uint16_t max_speed; |
| 64 | uint16_t cur_speed; |
| 65 | uint8_t cpu_status; |
| 66 | uint8_t cpu_upgrade; |
| 67 | uint16_t l1_handle; |
| 68 | uint16_t l2_handle; |
| 69 | uint16_t l3_handle; |
| 70 | uint8_t cpu_serial; |
| 71 | uint8_t cpu_asset_tag; |
| 72 | uint8_t cpu_part_number; |
| 73 | uint8_t core_count; |
| 74 | uint8_t core_enabled; |
| 75 | uint8_t thread_count; |
| 76 | uint16_t cpu_specs; |
| 77 | uint16_t cpu_family_2; |
| 78 | } __attribute__((packed)); |
| 79 | |
| 80 | struct mem_dev { |
| 81 | struct tstruct_header header; |
| 82 | uint16_t pma_handle; |
| 83 | uint16_t err_handle; |
| 84 | uint16_t tot_width; |
| 85 | uint16_t dat_width; |
| 86 | uint16_t size; |
| 87 | uint8_t form; |
| 88 | uint8_t set; |
| 89 | uint8_t dev_locator; |
| 90 | uint8_t bank_locator; |
| 91 | uint8_t type; |
| 92 | uint16_t typedetail; |
| 93 | uint16_t speed; |
| 94 | uint8_t manufacturer; |
| 95 | uint8_t serialnum; |
| 96 | uint8_t asset; |
| 97 | uint8_t partnum; |
| 98 | } __attribute__((packed)); |
| 99 | |
| 100 | struct md_map{ |
| 101 | struct tstruct_header header; |
| 102 | uint32_t start; |
| 103 | uint32_t end; |
| 104 | uint16_t md_handle; |
| 105 | uint16_t mama_handle; |
| 106 | uint8_t row_pos; |
| 107 | uint8_t interl_pos; |
| 108 | uint8_t interl_depth; |
| 109 | } __attribute__((packed)); |
| 110 | |
| 111 | struct pma{ |
| 112 | struct tstruct_header header; |
| 113 | uint8_t location; |
| 114 | uint8_t use; |
| 115 | uint8_t ecc; |
| 116 | uint32_t capacity; |
| 117 | uint16_t errhandle; |
| 118 | uint16_t numdevs; |
| 119 | } __attribute__((packed)); |
| 120 | |
| 121 | static char *form_factors[] = { |
| 122 | "?", |
| 123 | "Other", "Unknown", "SIMM", "SIP", "Chip", "DIP", "ZIP", |
| 124 | "Proprietary Card", "DIMM", "TSOP", "Row of chips", "RIMM", |
| 125 | "SODIMM", "SRIMM", "FB-DIMM" |
| 126 | }; |
| 127 | |
| 128 | |
| 129 | static char *memory_types[] = { |
| 130 | "?", |
| 131 | "Other", "????", "DRAM", "EDRAM", "VRAM", "SRAM", "RAM", |
| 132 | "ROM", "FLASH", "EEPROM", "FEPROM", "EPROM", "CDRAM", "3DRAM", |
| 133 | "SDRAM", "SGRAM", "RDRAM", "DDR", "DDR2", "DDR2 FB", "RSVD", |
| 134 | "RSVD","RSVD","DDR3","FBD2" |
| 135 | }; |
| 136 | |
| 137 | |
| 138 | struct mem_dev * mem_devs[MAX_DMI_MEMDEVS]; |
| 139 | int mem_devs_count=0; |
| 140 | struct md_map * md_maps[MAX_DMI_MEMDEVS]; |
| 141 | struct system_map * dmi_system_info; |
| 142 | struct cpu_map * dmi_cpu_info; |
| 143 | int md_maps_count=0; |
| 144 | int dmi_err_cnts[MAX_DMI_MEMDEVS]; |
| 145 | short dmi_initialized=0; |
| 146 | |
| 147 | char * get_tstruct_string(struct tstruct_header *header, int n){ |
| 148 | if(n<1) |
| 149 | return 0; |
| 150 | char * a = (char *)header + header->length; |
| 151 | n--; |
| 152 | do{ |
| 153 | if (!*a) |
| 154 | n--; |
| 155 | if (!n && *a) |
| 156 | return a; |
| 157 | a++; |
| 158 | }while (!(*a==0 && *(a-1)==0)); |
| 159 | return 0; |
| 160 | } |
| 161 | |
| 162 | |
| 163 | int open_dmi(void){ |
| 164 | char *dmi, *dmi_search_start, *dmi_start; |
| 165 | int found=0; |
| 166 | struct dmi_eps *eps; |
| 167 | char *table_start; |
| 168 | int tstruct_count=0; |
| 169 | dmi_search_start = (char *)DMI_SEARCH_START; |
| 170 | |
| 171 | //find anchor |
| 172 | for(dmi = dmi_search_start; dmi < dmi_search_start + 0xf0000; dmi +=16){ |
| 173 | if( *dmi == '_' && |
| 174 | *(dmi+1) == 'S' && |
| 175 | *(dmi+2) == 'M' && |
| 176 | *(dmi+3) == '_'){ |
| 177 | found =1; |
| 178 | break; |
| 179 | } |
| 180 | } |
| 181 | if (!found) { |
| 182 | return -1; |
| 183 | } |
| 184 | dmi_start=dmi; |
| 185 | eps=(struct dmi_eps *)dmi; |
| 186 | |
| 187 | //check checksum |
| 188 | int8_t checksum=0; |
| 189 | for (; dmi < dmi_start + eps->length; dmi++) |
| 190 | checksum += *dmi; |
| 191 | if (checksum){ |
| 192 | return -1; |
| 193 | } |
| 194 | |
| 195 | //we need at least revision 2.1 of SMBIOS |
| 196 | if ( eps->majorversion < 2 && |
| 197 | eps->minorversion < 1){ |
| 198 | return -1; |
| 199 | } |
| 200 | |
| 201 | |
| 202 | table_start=(char *)eps->tableaddress; |
| 203 | dmi=table_start; |
| 204 | //look at all structs |
| 205 | while(dmi < table_start + eps->tablelength){ |
| 206 | struct tstruct_header *header = (struct tstruct_header *)dmi; |
| 207 | |
| 208 | if (header->type == 17) |
| 209 | mem_devs[mem_devs_count++] = (struct mem_dev *)dmi; |
| 210 | |
| 211 | // Need fix (SMBIOS/DDR3) |
| 212 | if (header->type == 20 || header->type == 1) |
| 213 | md_maps[md_maps_count++] = (struct md_map *)dmi; |
| 214 | |
| 215 | // MB_SPEC |
| 216 | if (header->type == 2) |
| 217 | { |
| 218 | dmi_system_info = (struct system_map *)dmi; |
| 219 | } |
| 220 | |
| 221 | // CPU_SPEC |
| 222 | if (header->type == 4) |
| 223 | { |
| 224 | dmi_cpu_info = (struct cpu_map *)dmi; |
| 225 | } |
| 226 | |
| 227 | dmi+=header->length; |
| 228 | |
| 229 | while( ! (*dmi == 0 && *(dmi+1) == 0 ) ) |
| 230 | dmi++; |
| 231 | dmi+=2; |
| 232 | |
| 233 | if (++tstruct_count > eps->numstructs) |
| 234 | return -1; |
| 235 | } |
| 236 | return 0; |
| 237 | } |
| 238 | |
| 239 | void init_dmi(void){ |
| 240 | int i; |
| 241 | for(i=0; i < MAX_DMI_MEMDEVS; i++) |
| 242 | dmi_err_cnts[i]=0; |
| 243 | open_dmi(); |
| 244 | dmi_initialized=1; |
| 245 | } |
| 246 | |
| 247 | void print_dmi_startup_info(void) |
| 248 | { |
| 249 | char *string1; |
| 250 | char *string2; |
| 251 | char *string3; |
| 252 | int dmicol = 78; |
| 253 | int slenght; |
| 254 | int sl1, sl2, sl3; |
| 255 | |
| 256 | if(!dmi_initialized) { init_dmi(); } |
| 257 | |
| 258 | string1 = get_tstruct_string(&dmi_system_info->header,dmi_system_info->manufacturer); |
| 259 | sl1 = strlen(string1); |
| 260 | string2 = get_tstruct_string(&dmi_system_info->header,dmi_system_info->productname); |
| 261 | sl2 = strlen(string2); |
| 262 | string3 = get_tstruct_string(&dmi_cpu_info->header,dmi_cpu_info->cpu_socket); |
| 263 | sl3 = strlen(string3); |
| 264 | |
| 265 | slenght = sl1 + sl2; |
| 266 | if(sl3 > 2) { slenght += sl3 + 4; } else { slenght++; } |
| 267 | |
| 268 | if(sl1 && sl2) |
| 269 | { |
| 270 | //dmicol -= slenght; // right align |
| 271 | dmicol = 39 - slenght/2; // center align |
| 272 | cprint(LINE_DMI, dmicol, string1); |
| 273 | dmicol += sl1 + 1; |
| 274 | cprint(LINE_DMI, dmicol, string2); |
| 275 | dmicol += sl2 + 1; |
| 276 | |
| 277 | if(sl3 > 2){ |
| 278 | cprint(LINE_DMI, dmicol, "("); |
| 279 | dmicol++; |
| 280 | cprint(LINE_DMI, dmicol, string3); |
| 281 | dmicol += sl3; |
| 282 | cprint(LINE_DMI, dmicol, ")"); |
| 283 | } |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | void print_dmi_info(void){ |
| 288 | int i,j,page; |
| 289 | char * string=0; |
| 290 | |
| 291 | if(!dmi_initialized) |
| 292 | init_dmi(); |
| 293 | |
| 294 | if (mem_devs_count == 0){ |
| 295 | cprint(POP2_Y+1, POP2_X+2, "No valid DMI Memory Devices info found"); |
| 296 | while (get_key() == 0); |
| 297 | return; |
| 298 | } |
| 299 | |
| 300 | for(page=1; page <= 1 + (mem_devs_count-1)/8; page++){ |
| 301 | pop2clear(); |
| 302 | cprint(POP2_Y+1, POP2_X+2, "DMI Memory Device Info (page "); |
| 303 | itoa(string,page); |
| 304 | cprint(POP2_Y+1, POP2_X+32, string); |
| 305 | cprint(POP2_Y+1, POP2_X+33, "/"); |
| 306 | itoa(string,1 + (mem_devs_count-1)/8); |
| 307 | cprint(POP2_Y+1, POP2_X+34, string); |
| 308 | cprint(POP2_Y+1, POP2_X+35, ")"); |
| 309 | |
| 310 | cprint(POP2_Y+3, POP2_X+4, "Location Size(MB) Speed(MHz) Type Form"); |
| 311 | cprint(POP2_Y+4, POP2_X+4, "--------------------------------------------------------------"); |
| 312 | |
| 313 | for(i=8*(page-1); i<mem_devs_count && i<8*page; i++){ |
| 314 | int size_in_mb; |
| 315 | int yof; |
| 316 | |
| 317 | yof=POP2_Y+5+2*(i-8*(page-1)); |
| 318 | cprint(yof, POP2_X+4, get_tstruct_string(&(mem_devs[i]->header), mem_devs[i]->dev_locator)); |
| 319 | |
| 320 | if (mem_devs[i]->size == 0){ |
| 321 | cprint(yof, POP2_X+4+18, "Empty"); |
| 322 | }else if (mem_devs[i]->size == 0xFFFF){ |
| 323 | cprint(yof, POP2_X+4+18, "Unknown"); |
| 324 | }else{ |
| 325 | size_in_mb = 0xEFFF & mem_devs[i]->size; |
| 326 | if (mem_devs[i]->size & 0x8000) |
| 327 | size_in_mb = size_in_mb<<10; |
| 328 | itoa(string, size_in_mb); |
| 329 | cprint(yof, POP2_X+4+18, string); |
| 330 | } |
| 331 | |
| 332 | //this is the only field that needs to be SMBIOS 2.3+ |
| 333 | if ( mem_devs[i]->speed && |
| 334 | mem_devs[i]->header.length > 21){ |
| 335 | itoa(string, mem_devs[i]->speed); |
| 336 | cprint(yof, POP2_X+4+27, string); |
| 337 | }else{ |
| 338 | cprint(yof, POP2_X+4+27, "Unknown"); |
| 339 | } |
| 340 | cprint(yof, POP2_X+4+37, memory_types[mem_devs[i]->type]); |
| 341 | cprint(yof, POP2_X+4+44, form_factors[mem_devs[i]->form]); |
| 342 | |
| 343 | //print mappings |
| 344 | int mapped=0,of=0; |
| 345 | cprint(yof+1, POP2_X+6,"mapped to: "); |
| 346 | for(j=0; j<md_maps_count; j++) |
| 347 | { |
| 348 | if (mem_devs[i]->header.handle != md_maps[j]->md_handle) |
| 349 | continue; |
| 350 | if (mapped++){ |
| 351 | cprint(yof+1, POP2_X+17+of, ","); |
| 352 | of++; |
| 353 | } |
| 354 | hprint3(yof+1, POP2_X+17+of, md_maps[j]->start>>22, 4); |
| 355 | of += 4; |
| 356 | hprint3(yof+1, POP2_X+17+of, md_maps[j]->start<<10, 8); |
| 357 | of += 8; |
| 358 | cprint(yof+1, POP2_X+17+of, "-"); |
| 359 | of++; |
| 360 | hprint3(yof+1, POP2_X+17+of, md_maps[j]->end>>22, 4); |
| 361 | of += 4; |
| 362 | hprint3(yof+1, POP2_X+17+of, ((md_maps[j]->end+1)<<10) - 1, 8); |
| 363 | of += 8; |
| 364 | if(md_maps[j]->end == 0) { hprint3(yof+1, POP2_X+17+of-8,0,8); } |
| 365 | } |
| 366 | if (!mapped) |
| 367 | { |
| 368 | cprint(yof+1, POP2_X+17, "No mapping (Interleaved Device)"); |
| 369 | } |
| 370 | |
| 371 | } |
| 372 | |
| 373 | wait_keyup(); |
| 374 | while (get_key() == 0); |
| 375 | } |
| 376 | } |
| 377 | |
| 378 | //return 1 if the list of bad memory devices changes, 0 otherwise, -1 if no mapped |
| 379 | int add_dmi_err(ulong adr){ |
| 380 | int i,j,found=-1; |
| 381 | |
| 382 | if(!dmi_initialized) |
| 383 | init_dmi(); |
| 384 | |
| 385 | for(i=0; i < md_maps_count; i++){ |
| 386 | if ( adr < (md_maps[i]->start<<10) || |
| 387 | adr > (md_maps[i]->end<<10) ) |
| 388 | continue; |
| 389 | |
| 390 | //matching map found, now check find corresponding dev |
| 391 | for(j=0; j < mem_devs_count; j++){ |
| 392 | if (mem_devs[j]->header.handle != md_maps[i]->md_handle) |
| 393 | continue; |
| 394 | if (dmi_err_cnts[j]){ |
| 395 | found=0; |
| 396 | }else{ |
| 397 | found = dmi_err_cnts[j] = 1; |
| 398 | } |
| 399 | } |
| 400 | } |
| 401 | |
| 402 | return found; |
| 403 | } |
| 404 | |
| 405 | void print_dmi_err(void){ |
| 406 | int i,count,of; |
| 407 | char *string; |
| 408 | |
| 409 | scroll(); |
| 410 | |
| 411 | cprint(v->msg_line, 0,"Bad Memory Devices: "); |
| 412 | of=20; |
| 413 | for ( i=count=0; i < MAX_DMI_MEMDEVS; i++){ |
| 414 | if (!dmi_err_cnts[i]) |
| 415 | continue; |
| 416 | struct mem_dev *md = mem_devs[i]; |
| 417 | if(count++){ |
| 418 | cprint(v->msg_line, of, ", "); |
| 419 | of+=2; |
| 420 | } |
| 421 | string=get_tstruct_string((struct tstruct_header *)md,md->dev_locator); |
| 422 | if (strlen(string) + of > 80){ |
| 423 | scroll(); |
| 424 | of=7; |
| 425 | } |
| 426 | cprint(v->msg_line, of, string); |
| 427 | of += strlen(string); |
| 428 | } |
| 429 | } |