diff options
author | Bob Gilligan <gilligan@vyatta.com> | 2010-02-05 09:54:11 -0800 |
---|---|---|
committer | Bob Gilligan <gilligan@vyatta.com> | 2010-02-05 09:54:11 -0800 |
commit | e1971e4774a6ebb5ed33a09bdd60afa2c0534b6f (patch) | |
tree | f4fc5aaccb4d9dac43ae7a825df9006532c1a059 /src/dmidecode/dmidecode.c | |
download | vyatta-biosdevname-e1971e4774a6ebb5ed33a09bdd60afa2c0534b6f.tar.gz vyatta-biosdevname-e1971e4774a6ebb5ed33a09bdd60afa2c0534b6f.zip |
Initial commit.debian/0.1
Diffstat (limited to 'src/dmidecode/dmidecode.c')
-rw-r--r-- | src/dmidecode/dmidecode.c | 382 |
1 files changed, 382 insertions, 0 deletions
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; +} |