diff options
Diffstat (limited to 'src/dmidecode')
-rw-r--r-- | src/dmidecode/config.h | 29 | ||||
-rw-r--r-- | src/dmidecode/dmidecode.c | 382 | ||||
-rw-r--r-- | src/dmidecode/dmidecode.h | 55 | ||||
-rw-r--r-- | src/dmidecode/dmioem.c | 120 | ||||
-rw-r--r-- | src/dmidecode/dmioem.h | 26 | ||||
-rw-r--r-- | src/dmidecode/types.h | 27 | ||||
-rw-r--r-- | src/dmidecode/util.c | 165 | ||||
-rw-r--r-- | src/dmidecode/util.h | 8 |
8 files changed, 812 insertions, 0 deletions
diff --git a/src/dmidecode/config.h b/src/dmidecode/config.h new file mode 100644 index 0000000..52fd3c3 --- /dev/null +++ b/src/dmidecode/config.h @@ -0,0 +1,29 @@ +/* + * Configuration + */ + +#ifndef CONFIG_H +#define CONFIG_H + +/* Default memory device file */ +#ifdef __BEOS__ +#define DEFAULT_MEM_DEV "/dev/misc/mem" +#else +#ifdef __sun +#define DEFAULT_MEM_DEV "/dev/xsvc" +#else +#define DEFAULT_MEM_DEV "/dev/mem" +#endif +#endif + +/* Use mmap or not */ +#ifndef __BEOS__ +#define USE_MMAP +#endif + +/* Use memory alignment workaround or not */ +#ifdef __ia64__ +#define ALIGNMENT_WORKAROUND +#endif + +#endif diff --git a/src/dmidecode/dmidecode.c b/src/dmidecode/dmidecode.c new file mode 100644 index 0000000..5c542a6 --- /dev/null +++ b/src/dmidecode/dmidecode.c @@ -0,0 +1,382 @@ +/* + * Trimmed from DMI Decode http://savannah.nongnu.org/projects/dmidecode + * + * (C) 2000-2002 Alan Cox <alan@redhat.com> + * (C) 2002-2007 Jean Delvare <khali@linux-fr.org> + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For the avoidance of doubt the "preferred form" of this code is one which + * is in an open unpatent encumbered format. Where cryptographic key signing + * forms part of the process of creating an executable the information + * including keys needed to generate an equivalently functional executable + * are deemed to be part of the source code. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "config.h" +#include "types.h" +#include "util.h" +#include "dmidecode.h" +#include "dmioem.h" +#include "../state.h" +#include "../pci.h" + +static const char *out_of_spec = "<OUT OF SPEC>"; +static const char *bad_index = "<BAD INDEX>"; + +/* + * Type-independant Stuff + */ + +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<len; i++) + if(bp[i]<32 || bp[i]==127) + bp[i]='.'; + + return bp; +} + +static const char *dmi_slot_current_usage(u8 code) +{ + /* 3.3.10.3 */ + static const char *usage[]={ + "Other", /* 0x01 */ + "Unknown", + "Available", + "In Use" /* 0x04 */ + }; + + if(code>=0x01 && code<=0x04) + return usage[code-0x01]; + return out_of_spec; +} + +static void dmi_slot_segment_bus_func(u16 code1, u8 code2, u8 code3, u8 type, const char *prefix) +{ + /* 3.3.10.8 */ + if (!(code1==0xFFFF && code2==0xFF && code3==0xFF)) + printf("%sSegment Group %u, Bus %u, Device %u, Function %u ", + prefix, code1, code2, (code3>>3)&0x1F, (code3&0x7)); + switch(type) + { + case 0x06: /* PCI */ + case 0x0E: /* PCI */ + case 0x0F: /* AGP */ + case 0x10: /* AGP */ + case 0x11: /* AGP */ + case 0x12: /* PCI-X */ + case 0x13: /* AGP */ + case 0xA5: /* PCI Express */ + printf("\n"); + break; + default: + if (code1 != 0xFF || code2 != 0xFF || code3 != 0xFF) + printf("%s\n", out_of_spec); + break; + } +} + +static u8 onboard_device_type(u8 code, const char *prefix) +{ + /* 3.3.x.2 */ + u8 e = (code & 0x80)>>7; + static const char *type[]={ + "Other", /* 1 */ + "Unknown", + "Video", + "SCSI Controller", + "Ethernet", + "Token Ring", + "Sound", + "PATA Controller", + "SATA Controller", + "SAS Controller" /* 0x0A */ + }; + code = code & 0x7F; + if(code>=0x01 && code<=0x0A) { + printf("%sStatus: %s\n", prefix, e?"Enabled":"Disabled"); + printf("%sDevice Type: %s\n", prefix, type[code-0x01]); + } + else + printf("%sDevice Type: %s\n", prefix, out_of_spec); +} + +/* + * Main + */ + +#define MIN(a,b) (((a)<(b))?(a):(b)) + +static void dmi_decode(struct dmi_header *h, u16 ver, const struct libbiosdevname_state *state) +{ + u8 *data=h->data; + int domain, bus, device, function; + struct pci_device *pdev; + switch(h->type) + { + case 9: /* 3.3.10 System Slots */ + if (h->length >= 0x0E && h->length >=0x11) { + domain = WORD(data+0x0D); + bus = data[0x0F]; + device = (data[0x10]>>3)&0x1F; + function = data[0x10] & 7; + if (! (domain == 0xFFFF && bus == 0xFF && data[0x10] == 0xFF)) { + device = (data[0x10]>>3)&0x1F; + function = data[0x10] & 7; + pdev = find_pci_dev_by_pci_addr(state, domain, bus, device, function); + if (pdev) { + pdev->physical_slot = WORD(data+0x09); + pdev->smbios_type = 0; + pdev->smbios_instance = 0; + } + } + } + break; + case 41: /* 3.3.xx Onboard Device Information */ + domain = WORD(data+0x07); + bus = data[0x09]; + device = (data[0xa]>>3) & 0x1F; + function = data[0xa] & 0x7; + pdev = find_pci_dev_by_pci_addr(state, domain, bus, device, function); + if (pdev) { + pdev->physical_slot = 0; + pdev->smbios_enabled = !!(data[0x05] & 0x80); + pdev->smbios_type = data[0x05] & 0x7F; + pdev->smbios_instance = data[0x06]; + } + break; + + default: + if(dmi_decode_oem(h, state)) + break; + } +} + +static 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; +} + +static void dmi_table(u32 base, u16 len, u16 num, u16 ver, const char *devmem, const struct libbiosdevname_state *state) +{ + u8 *buf; + u8 *data; + int i=0; + + if((buf=mem_chunk(base, len, devmem))==NULL) + { +#ifndef USE_MMAP + printf("Table is unreachable, sorry. Try compiling dmidecode with -DUSE_MMAP.\n"); +#endif + return; + } + + data=buf; + while(i<num && data+4<=buf+len) /* 4 is the length of an SMBIOS structure header */ + { + u8 *next; + struct dmi_header h; + + to_dmi_header(&h, data); + + /* + * If a short entry is found (less than 4 bytes), not only it + * is invalid, but we cannot reliably locate the next entry. + * Better stop at this point, and let the user know his/her + * table is broken. + */ + if(h.length<4) + break; + + /* assign vendor for vendor-specific decodes later */ + if(h.type==0 && h.length>=5) + dmi_set_vendor(dmi_string(&h, data[0x04])); + + /* look for the next handle */ + next=data+h.length; + while(next-buf+1<len && (next[0]!=0 || next[1]!=0)) + next++; + next+=2; + if(next-buf<=len) + dmi_decode(&h, ver, state); + + data=next; + i++; + } + free(buf); +} + + +static int smbios_decode(u8 *buf, const char *devmem, const struct libbiosdevname_state *state) +{ + if(checksum(buf, buf[0x05]) + && memcmp(buf+0x10, "_DMI_", 5)==0 + && checksum(buf+0x10, 0x0F)) + { + dmi_table(DWORD(buf+0x18), WORD(buf+0x16), WORD(buf+0x1C), + (buf[0x06]<<8)+buf[0x07], devmem, state); + return 1; + } + + return 0; +} + +static int legacy_decode(u8 *buf, const char *devmem, const struct libbiosdevname_state *state) +{ + if(checksum(buf, 0x0F)) + { + dmi_table(DWORD(buf+0x08), WORD(buf+0x06), WORD(buf+0x0C), + ((buf[0x0E]&0xF0)<<4)+(buf[0x0E]&0x0F), devmem, state); + return 1; + } + + return 0; +} + +/* + * Probe for EFI interface + */ +#define EFI_NOT_FOUND (-1) +#define EFI_NO_SMBIOS (-2) +static int address_from_efi(size_t *address) +{ + FILE *efi_systab; + const char *filename; + char linebuf[64]; + int ret; + + *address=0; /* Prevent compiler warning */ + + /* + * Linux up to 2.6.6: /proc/efi/systab + * Linux 2.6.7 and up: /sys/firmware/efi/systab + */ + if((efi_systab=fopen(filename="/sys/firmware/efi/systab", "r"))==NULL + && (efi_systab=fopen(filename="/proc/efi/systab", "r"))==NULL) + { + /* No EFI interface, fallback to memory scan */ + return EFI_NOT_FOUND; + } + ret=EFI_NO_SMBIOS; + while((fgets(linebuf, sizeof(linebuf)-1, efi_systab))!=NULL) + { + char *addrp=strchr(linebuf, '='); + *(addrp++)='\0'; + if(strcmp(linebuf, "SMBIOS")==0) + { + *address=strtoul(addrp, NULL, 0); + ret=0; + break; + } + } + if(fclose(efi_systab)!=0) + perror(filename); + + if(ret==EFI_NO_SMBIOS) + fprintf(stderr, "%s: SMBIOS entry point missing\n", filename); + return ret; +} + +static const char *devmem = "/dev/mem"; + +int dmidecode_main(const struct libbiosdevname_state *state) +{ + int ret=0; /* Returned value */ + int found=0; + size_t fp; + int efi; + u8 *buf; + + /* First try EFI (ia64, Intel-based Mac) */ + efi=address_from_efi(&fp); + switch(efi) + { + case EFI_NOT_FOUND: + goto memory_scan; + case EFI_NO_SMBIOS: + ret=1; + goto exit_free; + } + + if((buf=mem_chunk(fp, 0x20, devmem))==NULL) + { + ret=1; + goto exit_free; + } + + if(smbios_decode(buf, devmem, state)) + found++; + goto done; + +memory_scan: + /* Fallback to memory scan (x86, x86_64) */ + if((buf=mem_chunk(0xF0000, 0x10000, devmem))==NULL) + { + ret=1; + goto exit_free; + } + + for(fp=0; fp<=0xFFF0; fp+=16) + { + if(memcmp(buf+fp, "_SM_", 4)==0 && fp<=0xFFE0) + { + if(smbios_decode(buf+fp, devmem, state)) + { + found++; + fp+=16; + } + } + else if(memcmp(buf+fp, "_DMI_", 5)==0) + { + if (legacy_decode(buf+fp, devmem, state)) + found++; + } + } + +done: + free(buf); + + +exit_free: + return ret; +} diff --git a/src/dmidecode/dmidecode.h b/src/dmidecode/dmidecode.h new file mode 100644 index 0000000..db8f7bd --- /dev/null +++ b/src/dmidecode/dmidecode.h @@ -0,0 +1,55 @@ +/* + * This file is part of the dmidecode project. + * + * (C) 2005-2007 Jean Delvare <khali@linux-fr.org> + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +struct dmi_header +{ + u8 type; + u8 length; + u16 handle; + u8 *data; +}; + +const char *dmi_string(struct dmi_header *dm, u8 s); + +struct dmi_embedded_device +{ + u8 type; + u8 instance; + u16 domain; + u8 bus; + u8 device; + u8 function; + const char *reference_designation; +}; + +struct dmi_addon_device +{ + u8 type; + u8 instance; + u16 domain; + u8 bus; + u8 device; + u8 function; + const char *reference_designation; +}; + +struct libbiosdevname_state; +int dmidecode_main(const struct libbiosdevname_state *state); + diff --git a/src/dmidecode/dmioem.c b/src/dmidecode/dmioem.c new file mode 100644 index 0000000..217c13a --- /dev/null +++ b/src/dmidecode/dmioem.c @@ -0,0 +1,120 @@ +/* + * Decoding of OEM-specific entries + * This file is part of the dmidecode project. + * + * (C) 2007 Jean Delvare <khali@linux-fr.org> + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <string.h> + +#include "types.h" +#include "dmidecode.h" +#include "dmioem.h" +#include "../pci.h" + +/* + * Globals for vendor-specific decodes + */ + +enum DMI_VENDORS { VENDOR_UNKNOWN, VENDOR_HP }; + +static enum DMI_VENDORS dmi_vendor=VENDOR_UNKNOWN; + +/* + * Remember the system vendor for later use. We only actually store the + * value if we know how to decode at least one specific entry type for + * that vendor. + */ +void dmi_set_vendor(const char *s) +{ + if(strcmp(s, "HP")==0) + dmi_vendor=VENDOR_HP; +} + +/* + * HP-specific data structures are decoded here. + * + * Code contributed by John Cagle. + */ + +static int dmi_decode_hp(struct dmi_header *h, const struct libbiosdevname_state *state) +{ + u8 *data=h->data; + int nic, ptr; + u8 smbios_type = 0; + u8 bus, device, func; + struct pci_device *pdev; + + switch(h->type) + { + case 209: + case 221: + /* + * Vendor Specific: HP ProLiant NIC MAC Information + * + * This prints the BIOS NIC number, + * PCI bus/device/function, and MAC address + */ + if (h->type == 221) + smbios_type=0; + else + smbios_type=0x05; + + nic=1; + ptr=4; + while(h->length>=ptr+8) + { + bus = data[ptr+1]; + device = data[ptr]>>3; + func = data[ptr]&7; + pdev = find_pci_dev_by_pci_addr(state, 0, bus, device, func); + if (pdev) { + if((data[ptr]==0x00 && data[ptr+1]==0x00) || + (data[ptr]==0xFF && data[ptr+1]==0xFF)) + pdev->smbios_enabled = 0; + else { + pdev->smbios_enabled = 1; + pdev->smbios_type = smbios_type; + pdev->smbios_instance = nic; + } + } + nic++; + ptr+=8; + } + break; + + default: + return 0; + } + return 1; +} + +/* + * Dispatch vendor-specific entries decoding + * Return 1 if decoding was successful, 0 otherwise + */ +int dmi_decode_oem(struct dmi_header *h, const struct libbiosdevname_state *state) +{ + switch(dmi_vendor) + { + case VENDOR_HP: + return dmi_decode_hp(h, state); + default: + return 0; + } +} diff --git a/src/dmidecode/dmioem.h b/src/dmidecode/dmioem.h new file mode 100644 index 0000000..bacdab7 --- /dev/null +++ b/src/dmidecode/dmioem.h @@ -0,0 +1,26 @@ +/* + * Decoding of OEM-specific entries + * This file is part of the dmidecode project. + * + * (C) 2007 Jean Delvare <khali@linux-fr.org> + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "../state.h" +struct dmi_header; + +void dmi_set_vendor(const char *s); +int dmi_decode_oem(struct dmi_header *h, const struct libbiosdevname_state *state); diff --git a/src/dmidecode/types.h b/src/dmidecode/types.h new file mode 100644 index 0000000..12f3c65 --- /dev/null +++ b/src/dmidecode/types.h @@ -0,0 +1,27 @@ +#ifndef TYPES_H +#define TYPES_H + +#include "config.h" + +typedef unsigned char u8; +typedef unsigned short u16; +typedef signed short i16; +typedef unsigned int u32; + +#ifdef ALIGNMENT_WORKAROUND +# ifdef BIGENDIAN +# define WORD(x) (u16)((x)[1]+((x)[0]<<8)) +# define DWORD(x) (u32)((x)[3]+((x)[2]<<8)+((x)[1]<<16)+((x)[0]<<24)) +# define QWORD(x) (U64(DWORD(x+4), DWORD(x))) +# else /* BIGENDIAN */ +# define WORD(x) (u16)((x)[0]+((x)[1]<<8)) +# define DWORD(x) (u32)((x)[0]+((x)[1]<<8)+((x)[2]<<16)+((x)[3]<<24)) +# define QWORD(x) (U64(DWORD(x), DWORD(x+4))) +# endif /* BIGENDIAN */ +#else /* ALIGNMENT_WORKAROUND */ +#define WORD(x) (u16)(*(const u16 *)(x)) +#define DWORD(x) (u32)(*(const u32 *)(x)) +#define QWORD(x) (*(const u64 *)(x)) +#endif /* ALIGNMENT_WORKAROUND */ + +#endif diff --git a/src/dmidecode/util.c b/src/dmidecode/util.c new file mode 100644 index 0000000..9eda714 --- /dev/null +++ b/src/dmidecode/util.c @@ -0,0 +1,165 @@ +/* + * Common "util" functions + * This file is part of the dmidecode project. + * + * (C) 2002-2005 Jean Delvare <khali@linux-fr> + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For the avoidance of doubt the "preferred form" of this code is one which + * is in an open unpatent encumbered format. Where cryptographic key signing + * forms part of the process of creating an executable the information + * including keys needed to generate an equivalently functional executable + * are deemed to be part of the source code. + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include "config.h" + +#ifdef USE_MMAP +#include <sys/mman.h> +#ifndef MAP_FAILED +#define MAP_FAILED ((void *) -1) +#endif /* !MAP_FAILED */ +#endif /* USE MMAP */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> + +#include "types.h" +#include "util.h" + +#ifndef USE_MMAP +static int myread(int fd, u8 *buf, size_t count, const char *prefix) +{ + ssize_t r=1; + size_t r2=0; + + while(r2!=count && r!=0) + { + r=read(fd, buf+r2, count-r2); + if(r==-1) + { + if(errno!=EINTR) + { + close(fd); + perror(prefix); + return -1; + } + } + else + r2+=r; + } + + if(r2!=count) + { + close(fd); + fprintf(stderr, "%s: Unexpected end of file\n", prefix); + return -1; + } + + return 0; +} +#endif + +int checksum(const u8 *buf, size_t len) +{ + u8 sum=0; + size_t a; + + for(a=0; a<len; a++) + sum+=buf[a]; + return (sum==0); +} + +/* + * Copy a physical memory chunk into a memory buffer. + * This function allocates memory. + */ +void *mem_chunk(size_t base, size_t len, const char *devmem) +{ + void *p; + int fd; +#ifdef USE_MMAP + size_t mmoffset; + void *mmp; +#endif + + if((fd=open(devmem, O_RDONLY))==-1) + { + perror(devmem); + return NULL; + } + + if((p=malloc(len))==NULL) + { + perror("malloc"); + return NULL; + } + +#ifdef USE_MMAP +#ifdef _SC_PAGESIZE + mmoffset=base%sysconf(_SC_PAGESIZE); +#else + mmoffset=base%getpagesize(); +#endif /* _SC_PAGESIZE */ + /* + * Please note that we don't use mmap() for performance reasons here, + * but to workaround problems many people encountered when trying + * to read from /dev/mem using regular read() calls. + */ + mmp=mmap(0, mmoffset+len, PROT_READ, MAP_SHARED, fd, base-mmoffset); + if(mmp==MAP_FAILED) + { + fprintf(stderr, "%s: ", devmem); + perror("mmap"); + free(p); + return NULL; + } + + memcpy(p, (u8 *)mmp+mmoffset, len); + + if(munmap(mmp, mmoffset+len)==-1) + { + fprintf(stderr, "%s: ", devmem); + perror("munmap"); + } +#else /* USE_MMAP */ + if(lseek(fd, base, SEEK_SET)==-1) + { + fprintf(stderr, "%s: ", devmem); + perror("lseek"); + free(p); + return NULL; + } + + if(myread(fd, p, len, devmem)==-1) + { + free(p); + return NULL; + } +#endif /* USE_MMAP */ + + if(close(fd)==-1) + perror(devmem); + + return p; +} diff --git a/src/dmidecode/util.h b/src/dmidecode/util.h new file mode 100644 index 0000000..b546f64 --- /dev/null +++ b/src/dmidecode/util.h @@ -0,0 +1,8 @@ +#include <sys/types.h> + +#include "types.h" + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) + +int checksum(const u8 *buf, size_t len); +void *mem_chunk(size_t base, size_t len, const char *devmem); |