/* ----------------------------------------------------------------------- * * * Copyright 2006 Erwan Velu - All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, Inc., 53 Temple Place Ste 330, * Boston MA 02111-1307, USA; either version 2 of the License, or * (at your option) any later version; incorporated herein by reference. * * ----------------------------------------------------------------------- */ #include #include #include "dmi/dmi.h" void to_dmi_header(struct dmi_header *h, u8 *data) { h->type=data[0]; h->length=data[1]; h->handle=WORD(data+2); h->data=data; } void dmi_system_uuid(u8 *p, s_dmi *dmi) { int only0xFF=1, only0x00=1; int i; for(i=0; i<16 && (only0x00 || only0xFF); i++) { if(p[i]!=0x00) only0x00=0; if(p[i]!=0xFF) only0xFF=0; } if(only0xFF) { sprintf(dmi->system.uuid,"Not Present"); return; } if(only0x00) { sprintf(dmi->system.uuid,"Not Settable"); return; } sprintf(dmi->system.uuid,"%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); } static void dmi_base_board_features(u8 code, s_dmi *dmi) { if((code&0x1F)!=0) { int i; printf("\n"); for(i=0; i<=4; i++) if(code&(1<base_board.features))[i]=true; } } static void dmi_processor_voltage(u8 code, s_dmi *dmi) { /* 3.3.5.4 */ static const float voltage[]={ 5.0, 3.3, 2.9 }; int i; if(code&0x80) dmi->processor.voltage=((float)(code&0x7f)/10); else { for(i=0; i<=2; i++) if(code&(1<processor.voltage=voltage[i]; } } static void dmi_processor_id(u8 type, u8 *p, const char *version, s_dmi *dmi) { /* * Extra flags are now returned in the ECX register when one calls * the CPUID instruction. Their meaning is explained in table 6, but * DMI doesn't support this yet. */ u32 eax, edx; int sig=0; /* * This might help learn about new processors supporting the * CPUID instruction or another form of identification. */ sprintf(dmi->processor.id,"ID: %02X %02X %02X %02X %02X %02X %02X %02X\n", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); if(type==0x05) /* 80386 */ { u16 dx=WORD(p); /* * 80386 have a different signature. */ dmi->processor.signature.type=(dx >>12); dmi->processor.signature.family=((dx>>8)&0xF); dmi->processor.signature.stepping=(dx>>4)&0xF; dmi->processor.signature.minor_stepping=(dx&0xF); return; } if(type==0x06) /* 80486 */ { u16 dx=WORD(p); /* * Not all 80486 CPU support the CPUID instruction, we have to find * wether the one we have here does or not. Note that this trick * works only because we know that 80486 must be little-endian. */ if((dx&0x0F00)==0x0400 && ((dx&0x00F0)==0x0040 || (dx&0x00F0)>=0x0070) && ((dx&0x000F)>=0x0003)) sig=1; else { dmi->processor.signature.type=((dx >>12)&0x3); dmi->processor.signature.family=((dx>>8)&0xF); dmi->processor.signature.model=((dx>>4)&0xF); dmi->processor.signature.stepping=(dx&0xF); return; } } else if((type>=0x0B && type<=0x13) /* Intel, Cyrix */ || (type>=0xB0 && type<=0xB3) /* Intel */ || type==0xB5 /* Intel */ || type==0xB9) /* Intel */ sig=1; else if((type>=0x18 && type<=0x1D) /* AMD */ || type==0x1F /* AMD */ || (type>=0xB6 && type<=0xB7) /* AMD */ || (type>=0x83 && type<=0x85)) /* AMD */ sig=2; else if(type==0x01 || type==0x02) { /* * Some X86-class CPU have family "Other" or "Unknown". In this case, * we use the version string to determine if they are known to * support the CPUID instruction. */ if(strncmp(version, "Pentium III MMX", 15)==0) sig=1; else if(strncmp(version, "AMD Athlon(TM)", 14)==0 || strncmp(version, "AMD Opteron(tm)", 15)==0) sig=2; else return; } else /* not X86-class */ return; eax=DWORD(p); edx=DWORD(p+4); switch(sig) { case 1: /* Intel */ dmi->processor.signature.type=((eax >>12)&0x3); dmi->processor.signature.family=(((eax>>16)&0xFF0)+((eax>>8)&0x00F)); dmi->processor.signature.model=(((eax>>12)&0xF0)+((eax>>4)&0x0F)); dmi->processor.signature.stepping=(eax&0xF); break; case 2: /* AMD */ dmi->processor.signature.family=(((eax>>8)&0xF)==0xF?(eax>>20)&0xFF:(eax>>8)&0xF); dmi->processor.signature.model =(((eax>>4)&0xF)==0xF?(eax>>16)&0xF :(eax>>4)&0xF); dmi->processor.signature.stepping=(eax&0xF); break; } edx=DWORD(p+4); if((edx&0x3FF7FDFF)!=0) { int i; for(i=0; i<=31; i++) if(cpu_flags_strings[i]!=NULL && edx&(1<processor.cpu_flags))[i]=true; //printf("%s\t%s\n", prefix, flags[i]); } } void dmi_system_wake_up_type(u8 code, s_dmi *dmi) { /* 3.3.2.1 */ static const char *type[]={ "Reserved", /* 0x00 */ "Other", "Unknown", "APM Timer", "Modem Ring", "LAN Remote", "Power Switch", "PCI PME#", "AC Power Restored" /* 0x08 */ }; if(code<=0x08) { strcpy(dmi->system.wakeup_type,type[code]); } else { strcpy(dmi->system.wakeup_type,out_of_spec); } return; } void dmi_bios_runtime_size(u32 code, s_dmi *dmi) { if(code&0x000003FF) { dmi->bios.runtime_size=code; strcpy(dmi->bios.runtime_size_unit,"bytes"); } else { dmi->bios.runtime_size=code >>10; strcpy(dmi->bios.runtime_size_unit,"KB"); } } void dmi_bios_characteristics(u64 code, s_dmi *dmi) { int i; /* * This isn't very clear what this bit is supposed to mean */ if(code.l&(1<<3)) { ((bool *)(& dmi->bios.characteristics))[0]=true; return; } for(i=4; i<=31; i++) if(code.l&(1<bios.characteristics))[i-3]=true; } void dmi_bios_characteristics_x1(u8 code, s_dmi *dmi) { int i; for(i=0; i<=7; i++) if(code&(1<bios.characteristics_x1))[i]=true; } void dmi_bios_characteristics_x2(u8 code, s_dmi *dmi) { int i; for(i=0; i<=2; i++) if(code&(1<bios.characteristics_x2))[i]=true; } const char *dmi_string(struct dmi_header *dm, u8 s) { char *bp=(char *)dm->data; size_t i, len; if(s==0) return "Not Specified"; bp+=dm->length; while(s>1 && *bp) { bp+=strlen(bp); bp++; s--; } if(!*bp) return bad_index; /* ASCII filtering */ len=strlen(bp); for(i=0; i>4, buf[14]&0x0F); else printf("DMI present.\n"); printf("%d structures occupying %d bytes.\n",dmitable.num, dmitable.len); printf("DMI table at 0x%08X.\n",dmitable.base); return 1; } } dmitable.base=0; dmitable.num=0; dmitable.ver=0; dmitable.len=0; return 0; } void dmi_decode(struct dmi_header *h, u16 ver, s_dmi *dmi) { u8 *data=h->data; /* * Note: DMI types 37, 38 and 39 are untested */ switch(h->type) { case 0: /* 3.3.1 BIOS Information */ // printf("BIOS Information\n"); if(h->length<0x12) break; strcpy(dmi->bios.vendor,dmi_string(h,data[0x04])); strcpy(dmi->bios.version,dmi_string(h,data[0x05])); strcpy(dmi->bios.release_date,dmi_string(h,data[0x08])); dmi->bios.address=WORD(data+0x06); dmi_bios_runtime_size((0x10000-WORD(data+0x06))<<4,dmi); dmi->bios.rom_size=(data[0x09]+1)<<6; strcpy(dmi->bios.rom_size_unit,"kB"); dmi_bios_characteristics(QWORD(data+0x0A),dmi); if(h->length<0x13) break; dmi_bios_characteristics_x1(data[0x12], dmi); if(h->length<0x14) break; dmi_bios_characteristics_x2(data[0x13], dmi); if(h->length<0x18) break; if(data[0x14]!=0xFF && data[0x15]!=0xFF) sprintf(dmi->bios.bios_revision,"%u.%u", data[0x14], data[0x15]); if(data[0x16]!=0xFF && data[0x17]!=0xFF) sprintf(dmi->bios.firmware_revision,"%u.%u", data[0x16], data[0x17]); break; case 1: /* 3.3.2 System Information */ // printf("System Information\n"); if(h->length<0x08) break; strcpy(dmi->system.manufacturer,dmi_string(h,data[0x04])); strcpy(dmi->system.product_name,dmi_string(h,data[0x05])); strcpy(dmi->system.version,dmi_string(h,data[0x06])); strcpy(dmi->system.serial,dmi_string(h,data[0x07])); if(h->length<0x19) break; dmi_system_uuid(data+0x08,dmi); dmi_system_wake_up_type(data[0x18],dmi); if(h->length<0x1B) break; strcpy(dmi->system.sku_number,dmi_string(h,data[0x19])); strcpy(dmi->system.family,dmi_string(h,data[0x1A])); break; case 2: /* 3.3.3 Base Board Information */ // printf("Base Board Information\n"); if(h->length<0x08) break; strcpy(dmi->base_board.manufacturer,dmi_string(h,data[0x04])); strcpy(dmi->base_board.product_name,dmi_string(h,data[0x05])); strcpy(dmi->base_board.version,dmi_string(h,data[0x06])); strcpy(dmi->base_board.serial,dmi_string(h,data[0x07])); if(h->length<0x0F) break; strcpy(dmi->base_board.asset_tag,dmi_string(h,data[0x08])); dmi_base_board_features(data[0x09], dmi); strcpy(dmi->base_board.location,dmi_string(h,data[0x0A])); strcpy(dmi->base_board.type,dmi_string(h,data[0x0D])); if(h->length<0x0F+data[0x0E]*sizeof(u16)) break; break; case 3: /* 3.3.4 Chassis Information */ // printf("Chassis Information\n"); if(h->length<0x09) break; strcpy(dmi->chassis.manufacturer,dmi_string(h,data[0x04])); strcpy(dmi->chassis.type,dmi_chassis_type(data[0x05]&0x7F)); strcpy(dmi->chassis.lock,dmi_chassis_lock(data[0x05]>>7)); strcpy(dmi->chassis.version,dmi_string(h,data[0x06])); strcpy(dmi->chassis.serial,dmi_string(h,data[0x07])); strcpy(dmi->chassis.asset_tag,dmi_string(h,data[0x08])); if(h->length<0x0D) break; strcpy(dmi->chassis.boot_up_state,dmi_chassis_state(data[0x09])); strcpy(dmi->chassis.power_supply_state,dmi_chassis_state(data[0x0A])); strcpy(dmi->chassis.thermal_state,dmi_chassis_state(data[0x0B])); strcpy(dmi->chassis.security_status,dmi_chassis_security_status(data[0x0C])); if(h->length<0x11) break; sprintf(dmi->chassis.oem_information,"0x%08X\n",DWORD(data+0x0D)); if(h->length<0x15) break; dmi->chassis.height=data[0x11]; dmi->chassis.nb_power_cords=data[0x12]; break; case 4: /* 3.3.5 Processor Information */ // printf("Processor Information\n"); if(h->length<0x1A) break; strcpy(dmi->processor.socket_designation,dmi_string(h, data[0x04])); strcpy(dmi->processor.type,dmi_processor_type(data[0x05])); strcpy(dmi->processor.family,dmi_processor_family(data[0x06])); strcpy(dmi->processor.manufacturer,dmi_string(h, data[0x07])); dmi_processor_id(data[0x06], data+8, dmi_string(h, data[0x10]), dmi); strcpy(dmi->processor.version,dmi_string(h, data[0x10])); dmi_processor_voltage(data[0x11],dmi); dmi->processor.external_clock=WORD(data+0x12); dmi->processor.max_speed=WORD(data+0x14); dmi->processor.current_speed=WORD(data+0x16); if(data[0x18]&(1<<6)) strcpy(dmi->processor.status,dmi_processor_status(data[0x18]&0x07)); else sprintf(dmi->processor.status,"Unpopulated"); sprintf(dmi->processor.upgrade,dmi_processor_upgrade(data[0x19])); if(h->length<0x20) break; dmi_processor_cache(WORD(data+0x1A), "L1", ver,dmi->processor.cache1); dmi_processor_cache(WORD(data+0x1C), "L2", ver,dmi->processor.cache2); dmi_processor_cache(WORD(data+0x1E), "L3", ver,dmi->processor.cache3); if(h->length<0x23) break; strcpy(dmi->processor.serial,dmi_string(h, data[0x20])); strcpy(dmi->processor.asset_tag,dmi_string(h, data[0x21])); strcpy(dmi->processor.part_number,dmi_string(h, data[0x22])); break; } } void parse_dmitable(s_dmi *dmi) { int i=0; u8 *data = NULL; u8 buf[dmitable.len]; memcpy(buf,(int *)dmitable.base,sizeof(u8) * dmitable.len); data=buf; while(i\n"); printf("\n"); data=next; i++; } }