From c60b684d238cd3662ad5f4044cc99e740dae7260 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Thu, 16 Apr 2015 16:53:38 -0400 Subject: [PATCH] Implement FRU inventory Implement parsing of FRU data. This phase omits Multirecord area. Provide access to either just the names or names and extended information. Change-Id: I8b0adf649769a880bf40cbe973864e889f1a6959 --- pyghmi/ipmi/command.py | 30 ++ pyghmi/ipmi/fru.py | 309 +++++++++++++++ pyghmi/ipmi/private/spd.py | 768 +++++++++++++++++++++++++++++++++++++ pyghmi/ipmi/sdr.py | 7 +- 4 files changed, 1111 insertions(+), 3 deletions(-) create mode 100644 pyghmi/ipmi/fru.py create mode 100644 pyghmi/ipmi/private/spd.py diff --git a/pyghmi/ipmi/command.py b/pyghmi/ipmi/command.py index d495a710..4cf3b910 100644 --- a/pyghmi/ipmi/command.py +++ b/pyghmi/ipmi/command.py @@ -19,6 +19,7 @@ import pyghmi.constants as const import pyghmi.exceptions as exc +import pyghmi.ipmi.fru as fru from pyghmi.ipmi.private import session import pyghmi.ipmi.sdr as sdr @@ -337,6 +338,35 @@ class Command(object): if 'error' in response: raise exc.IpmiException(response['error']) + def get_inventory_descriptions(self): + """Retrieve list of things that could be inventoried + + This permits a caller to examine the available items + without actually causing the inventory data to be gathered. It + returns an iterable of string descriptions + """ + yield "System" + if self._sdr is None: + self._sdr = sdr.SDR(self) + for fruid in self._sdr.fru: + yield self._sdr.fru[fruid].fru_name + + def get_inventory(self): + """Retrieve inventory of system + + Retrieve inventory of the targeted system. This frequently includes + serial numbers, sometimes hardware addresses, sometimes memory modules + This function will retrieve whatever the underlying platform provides + and apply some structure. Iterating over the return yields tuples + of a name for the inventoried item and + """ + yield ("System", fru.FRU(ipmicmd=self, fruid=0).info) + if self._sdr is None: + self._sdr = sdr.SDR(self) + for fruid in self._sdr.fru: + yield (self._sdr.fru[fruid].fru_name, fru.FRU( + ipmicmd=self, fruid=fruid, sdr=self._sdr.fru[fruid]).info) + def get_health(self): """Summarize health of managed system diff --git a/pyghmi/ipmi/fru.py b/pyghmi/ipmi/fru.py new file mode 100644 index 00000000..acd699df --- /dev/null +++ b/pyghmi/ipmi/fru.py @@ -0,0 +1,309 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# coding=utf8 + +# Copyright 2015 Lenovo +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This module provides access to SDR offered by a BMC +# This data is common between 'sensors' and 'inventory' modules since SDR +# is both used to enumerate sensors for sensor commands and FRU ids for FRU +# commands + +# For now, we will not offer persistent SDR caching as we do in xCAT's IPMI +# code. Will see if it is adequate to advocate for high object reuse in a +# persistent process for the moment. + +# Focus is at least initially on the aspects that make the most sense for a +# remote client to care about. For example, smbus information is being +# skipped for now + +# This file handles parsing of fru format records as presented by IPMI +# devices. This format is documented in the 'Platform Management FRU +# Information Storage Definition (Document Revision 1.2) + +import pyghmi.exceptions as iexc +import pyghmi.ipmi.private.spd as spd +import struct +import time + +fruepoch = time.mktime(time.strptime('1/1/1996', "%m/%d/%Y")) + +# This is from SMBIOS specification Table 16 +enclosure_types = { + 1: 'Other', + 2: 'Unknown', + 3: 'Desktop', + 4: 'Low Profile Desktop', + 5: 'Pizza Box', + 6: 'Mini Tower', + 7: 'Tower', + 8: 'Portable', + 9: 'Laptop', + 0xa: 'Notebook', + 0xb: 'Hand Held', + 0xc: 'Docking Station', + 0xd: 'All in One', + 0xe: 'Sub Notebook', + 0xf: 'Space-saving', + 0x10: 'Lunch Box', + 0x11: 'Main Server Chassis', + 0x12: 'Expansion Chassis', + 0x13: 'SubChassis', + 0x14: 'Bus Expansion Chassis', + 0x15: 'Peripheral Chassis', + 0x16: 'RAID Chassis', + 0x17: 'Rack Mount Chassis', + 0x18: 'Sealed-case PC', + 0x19: 'Multi-system Chassis', + 0x1a: 'Compact PCI', + 0x1b: 'Advanced TCA', + 0x1c: 'Blade', + 0x1d: 'Blade Enclosure', +} + + +def unpack6bitascii(inputdata): + # This is a text encoding scheme that seems unique + # to IPMI FRU. It seems to be relatively rare in practice + result = '' + while len(inputdata) > 0: + currchunk = inputdata[:3] + del inputdata[:3] + currchar = currchunk[0] & 0b111111 + result += chr(0x20 + currchar) + currchar = (currchunk[0] & 0b11000000) >> 6 + currchar |= (currchunk[1] & 0b1111) << 2 + result += chr(0x20 + currchar) + currchar = (currchunk[1] & 0b1111000) >> 4 + currchar |= (currchunk[2] & 0b11) << 4 + result += chr(0x20 + currchar) + currchar = (currchunk[2] & 0b11111100) >> 2 + result += chr(0x20 + currchar) + return result + + +def decode_fru_date(datebytes): + # Returns ISO + datebytes.append(0) + minutesfromepoch = struct.unpack(' frusize: + chunksize = frusize + offset = 0 + self.rawfru = bytearray([]) + while chunksize: + response = self.ipmicmd.raw_command( + netfn=0xa, command=0x11, data=[fruid, offset & 0xff, + offset >> 8, chunksize]) + if response['code'] == 201: + # if it was too big, back off and try smaller + # Try just over half to mitigate the chance of + # one request becoming three rather than just two + if chunksize == 3: + raise iexc.IpmiException(response['error']) + chunksize //= 2 + chunksize += 2 + continue + elif 'error' in response: + raise iexc.IpmiException(response['error']) + self.rawfru.extend(response['data'][1:]) + offset += response['data'][0] + if offset + chunksize > frusize: + chunksize = frusize - offset + + def parsedata(self): + self.info = {} + rawdata = self.rawfru + self.databytes = bytearray(rawdata) + if self.sdr is not None: + frutype = self.sdr.fru_type_and_modifier >> 8 + frusubtype = self.sdr.fru_type_and_modifier & 0xff + if frutype > 0x10 or frutype < 0x8 or frusubtype not in (0, 1, 2): + raise iexc.PyghmiException( + 'Unsupported FRU device: {0:x}h, {1:x}h'.format(frutype, + frusubtype + )) + elif frusubtype == 1: + self.myspd = spd.SPD(self.databytes) + self.info = self.myspd.info + return + if self.databytes[0] != 1: + raise iexc.BmcErrorException("Invalid/Unsupported FRU format") + # Ignore the internal use even if present. + self._parse_chassis() + self._parse_board() + self._parse_prod() + # TODO(jjohnson2): Multi Record area + + def _decode_tlv(self, offset, lang=0): + currtlv = self.databytes[offset] + currlen = currtlv & 0b111111 + currtype = (currtlv & 0b11000000) >> 6 + retinfo = self.databytes[offset + 1:offset + currlen] + newoffset = offset + currlen + 1 + if currlen == 0: + return None, newoffset + if currtype == 0: + # return it as a bytearray, not much to be done for it + return retinfo, newoffset + elif currtype == 3: # text string + if lang == 0: + retinfo = retinfo.decode('utf-8') + else: + retinfo = retinfo.decode('utf-16le') + retinfo = retinfo.replace('\x00', '') + return retinfo, newoffset + elif currtype == 1: # BCD 'plus' + retdata = '' + for byte in retinfo: + byte = hex(byte).replace('0x', '').replace('a', ' ').replace( + 'b', '-').replace('c', '.') + retdata += byte + return retdata, newoffset + elif currtype == 2: # 6-bit ascii + retinfo = unpack6bitascii(retinfo) + return retinfo, newoffset + + def _parse_chassis(self): + offset = 8 * self.databytes[2] + if offset == 0: + return + if self.databytes[offset] & 0b1111 != 1: + raise iexc.BmcErrorException("Invallid/Unsupported chassis area") + inf = self.info + # ignore length field, just process the data + inf['chassis_type'] = enclosure_types[self.databytes[offset + 2]] + inf['chassis_part_number'], offset = self._decode_tlv(offset + 3) + inf['chassis_serial'], offset = self._decode_tlv(offset) + inf['chassis_extra'] = [] + while self.databytes[offset] != 0xc1: + fielddata, offset = self._decode_tlv(offset) + inf['chassis_extra'].append(fielddata) + + def _parse_board(self): + offset = 8 * self.databytes[3] + if offset == 0: + return + if self.databytes[offset] & 0b1111 != 1: + raise iexc.BmcErrorException("Invalid/Unsupported board info area") + inf = self.info + language = self.databytes[offset + 2] + inf['board_mfg_date'] = decode_fru_date( + self.databytes[offset + 3:offset + 6]) + inf['board_manufacturer'], offset = self._decode_tlv(offset + 6) + inf['board_product'], offset = self._decode_tlv(offset, language) + inf['board_serial'], offset = self._decode_tlv(offset, language) + inf['board_model'], offset = self._decode_tlv(offset, language) + _, offset = self._decode_tlv(offset, language) # decode but discard + inf['board_extra'] = [] + while self.databytes[offset] != 0xc1: + fielddata, offset = self._decode_tlv(offset, language) + inf['board_extra'].append(fielddata) + + def _parse_prod(self): + offset = 8 * self.databytes[4] + if offset == 0: + return + inf = self.info + language = self.databytes[offset + 2] + inf['product_manufacturer'], offset = self._decode_tlv(offset + 3, + language) + inf['product_name'], offset = self._decode_tlv(offset, language) + inf['product_model'], offset = self._decode_tlv(offset, language) + inf['product_version'], offset = self._decode_tlv(offset, language) + inf['product_serial'], offset = self._decode_tlv(offset, language) + inf['product_asset'], offset = self._decode_tlv(offset, language) + _, offset = self._decode_tlv(offset, language) + inf['product_extra'] = [] + while self.databytes[offset] != 0xc1: + fielddata, offset = self._decode_tlv(offset, language) + inf['product_extra'].append(fielddata) + + def __repr__(self): + return repr(self.info) + # retdata = 'Chassis data\n' + # retdata += ' Type: ' + repr(self.chassis_type) + '\n' + # retdata += ' Part Number: ' + repr(self.chassis_part_number) + '\n' + # retdata += ' Serial Number: ' + repr(self.chassis_serial) + '\n' + # retdata += ' Extra: ' + repr(self.chassis_extra) + '\n' + # retdata += 'Board data\n' + # retdata += ' Manufacturer: ' + repr(self.board_manufacturer) + '\n' + # retdata += ' Date: ' + repr(self.board_mfg_date) + '\n' + # retdata += ' Product' + repr(self.board_product) + '\n' + # retdata += ' Serial: ' + repr(self.board_serial) + '\n' + # retdata += ' Model: ' + repr(self.board_model) + '\n' + # retdata += ' Extra: ' + repr(self.board_extra) + '\n' + # retdata += 'Product data\n' + # retdata += ' Manufacturer: ' + repr(self.product_manufacturer)+'\n' + # retdata += ' Name: ' + repr(self.product_name) + '\n' + # retdata += ' Model: ' + repr(self.product_model) + '\n' + # retdata += ' Version: ' + repr(self.product_version) + '\n' + # retdata += ' Serial: ' + repr(self.product_serial) + '\n' + # retdata += ' Asset: ' + repr(self.product_asset) + '\n' + # retdata += ' Extra: ' + repr(self.product_extra) + '\n' + # return retdata diff --git a/pyghmi/ipmi/private/spd.py b/pyghmi/ipmi/private/spd.py new file mode 100644 index 00000000..05871ab4 --- /dev/null +++ b/pyghmi/ipmi/private/spd.py @@ -0,0 +1,768 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# coding=utf8 + +# Copyright 2015 Lenovo +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This implements parsing of DDR SPD data. This is offered up in a pass +# through fashion by some service processors. + +# For now, just doing DDR3 and DDR4 + +# In many cases, astute readers will note that some of the lookup tables +# should be a matter of math rather than lookup. However the SPD +# specification explicitly reserves values not in the lookup tables for +# future use. It has happened, for example, that a spec was amended +# with discontinuous values for a field that was until that point +# possible to derive in a formulaic way + +import struct + +jedec_ids = [ + { + 0x01: "AMD", + 0x02: "AMI", + 0x83: "Fairchild", + 0x04: "Fujitsu", + 0x85: "GTE", + 0x86: "Harris", + 0x07: "Hitachi", + 0x08: "Inmos", + 0x89: "Intel", + 0x8a: "I.T.T.", + 0x0b: "Intersil", + 0x8c: "Monolithic Memories", + 0x0d: "Mostek", + 0x0e: "Motorola", + 0x8f: "National", + 0x10: "NEC", + 0x91: "RCA", + 0x92: "Raytheon", + 0x13: "Conexant (Rockwell)", + 0x94: "Seeq", + 0x15: "Philips Semi. (Signetics)", + 0x16: "Synertek", + 0x97: "Texas Instruments", + 0x98: "Toshiba", + 0x19: "Xicor", + 0x1a: "Zilog", + 0x9b: "Eurotechnique", + 0x1c: "Mitsubishi", + 0x9d: "Lucent (AT&T)", + 0x9e: "Exel", + 0x1f: "Atmel", + 0x20: "SGS/Thomson", + 0xa1: "Lattice Semi.", + 0xa2: "NCR", + 0x23: "Wafer Scale Integration", + 0xa4: "IBM", + 0x25: "Tristar", + 0x26: "Visic", + 0xa7: "Intl. CMOS Technology", + 0xa8: "SSSI", + 0x29: "Microchip Technology", + 0x2a: "Ricoh Ltd.", + 0xab: "VLSI", + 0x2c: "Micron Technology", + 0xad: "Hyundai Electronics", + 0xae: "OKI Semiconductor", + 0x2f: "ACTEL", + 0xb0: "Sharp", + 0x31: "Catalyst", + 0x32: "Panasonic", + 0xb3: "IDT", + 0x34: "Cypress", + 0xb5: "DEC", + 0xb6: "LSI Logic", + 0x37: "Zarlink", + 0x38: "UTMC", + 0xb9: "Thinking Machine", + 0xba: "Thomson CSF", + 0x3b: "Integrated CMOS(Vertex)", + 0xbc: "Honeywell", + 0x3d: "Tektronix", + 0x3e: "Sun Microsystems", + 0xbf: "SST", + 0x40: "MOSEL", + 0xc1: "Infineon", + 0xc2: "Macronix", + 0x43: "Xerox", + 0xc4: "Plus Logic", + 0x45: "SunDisk", + 0x46: "Elan Circuit Tech.", + 0xc7: "European Silicon Str.", + 0xc8: "Apple Computer", + 0xc9: "Xilinx", + 0x4a: "Compaq", + 0xcb: "Protocol Engines", + 0x4c: "SCI", + 0xcd: "Seiko Instruments", + 0xce: "Samsung", + 0x4f: "I3 Design System", + 0xd0: "Klic", + 0x51: "Crosspoint Solutions", + 0x52: "Alliance Semiconductor", + 0xd3: "Tandem", + 0x54: "Hewlett-Packard", + 0xd5: "Intg. Silicon Solutions", + 0xd6: "Brooktree", + 0x57: "New Media", + 0x58: "MHS Electronic", + 0xd9: "Performance Semi.", + 0xda: "Winbond Electronic", + 0x5b: "Kawasaki Steel", + 0xdc: "Bright Micro", + 0x5d: "TECMAR", + 0x5e: "Exar", + 0xdf: "PCMCIA", + 0xe0: "LG Semiconductor", + 0x61: "Northern Telecom", + 0x62: "Sanyo", + 0xe3: "Array Microsystems", + 0x64: "Crystal Semiconductor", + 0xe5: "Analog Devices", + 0xe6: "PMC-Sierra", + 0x67: "Asparix", + 0x68: "Convex Computer", + 0xe9: "Quality Semiconductor", + 0xea: "Nimbus Technology", + 0x6b: "Transwitch", + 0xec: "Micronas (ITT Intermetall)", + 0x6d: "Cannon", + 0x6e: "Altera", + 0xef: "NEXCOM", + 0x70: "QUALCOMM", + 0xf1: "Sony", + 0xf2: "Cray Research", + 0x73: "AMS (Austria Micro)", + 0xf4: "Vitesse", + 0x75: "Aster Electronics", + 0x76: "Bay Networks (Synoptic)", + 0xf7: "Zentrum", + 0xf8: "TRW", + 0x79: "Thesys", + 0x7a: "Solbourne Computer", + 0xfb: "Allied-Signal", + 0x7c: "Dialog", + 0xfd: "Media Vision", + 0xfe: "Level One Communication", + }, + { + 0x01: "Cirrus Logic", + 0x02: "National Instruments", + 0x83: "ILC Data Device", + 0x04: "Alcatel Mietec", + 0x85: "Micro Linear", + 0x86: "Univ. of NC", + 0x07: "JTAG Technologies", + 0x08: "Loral", + 0x89: "Nchip", + 0x8A: "Galileo Tech", + 0x0B: "Bestlink Systems", + 0x8C: "Graychip", + 0x0D: "GENNUM", + 0x0E: "VideoLogic", + 0x8F: "Robert Bosch", + 0x10: "Chip Express", + 0x91: "DATARAM", + 0x92: "United Microelec Corp.", + 0x13: "TCSI", + 0x94: "Smart Modular", + 0x15: "Hughes Aircraft", + 0x16: "Lanstar Semiconductor", + 0x97: "Qlogic", + 0x98: "Kingston", + 0x19: "Music Semi", + 0x1A: "Ericsson Components", + 0x9B: "SpaSE", + 0x1C: "Eon Silicon Devices", + 0x9D: "Programmable Micro Corp", + 0x9E: "DoD", + 0x1F: "Integ. Memories Tech.", + 0x20: "Corollary Inc.", + 0xA1: "Dallas Semiconductor", + 0xA2: "Omnivision", + 0x23: "EIV(Switzerland)", + 0xA4: "Novatel Wireless", + 0x25: "Zarlink (formerly Mitel)", + 0x26: "Clearpoint", + 0xA7: "Cabletron", + 0xA8: "Silicon Technology", + 0x29: "Vanguard", + 0x2A: "Hagiwara Sys-Com", + 0xAB: "Vantis", + 0x2C: "Celestica", + 0xAD: "Century", + 0xAE: "Hal Computers", + 0x2F: "Rohm Company Ltd.", + 0xB0: "Juniper Networks", + 0x31: "Libit Signal Processing", + 0x32: "Enhanced Memories Inc.", + 0xB3: "Tundra Semiconductor", + 0x34: "Adaptec Inc.", + 0xB5: "LightSpeed Semi.", + 0xB6: "ZSP Corp.", + 0x37: "AMIC Technology", + 0x38: "Adobe Systems", + 0xB9: "Dynachip", + 0xBA: "PNY Electronics", + 0x3B: "Newport Digital", + 0xBC: "MMC Networks", + 0x3D: "T Square", + 0x3E: "Seiko Epson", + 0xBF: "Broadcom", + 0x40: "Viking Components", + 0xC1: "V3 Semiconductor", + 0xC2: "Flextronics (formerly Orbit)", + 0x43: "Suwa Electronics", + 0xC4: "Transmeta", + 0x45: "Micron CMS", + 0x46: "American Computer & Digital Components Inc", + 0xC7: "Enhance 3000 Inc", + 0xC8: "Tower Semiconductor", + 0x49: "CPU Design", + 0x4A: "Price Point", + 0xCB: "Maxim Integrated Product", + 0x4C: "Tellabs", + 0xCD: "Centaur Technology", + 0xCE: "Unigen Corporation", + 0x4F: "Transcend Information", + 0xD0: "Memory Card Technology", + 0x51: "CKD Corporation Ltd.", + 0x52: "Capital Instruments, Inc.", + 0xD3: "Aica Kogyo, Ltd.", + 0x54: "Linvex Technology", + 0xD5: "MSC Vertriebs GmbH", + 0xD6: "AKM Company, Ltd.", + 0x57: "Dynamem, Inc.", + 0x58: "NERA ASA", + 0xD9: "GSI Technology", + 0xDA: "Dane-Elec (C Memory)", + 0x5B: "Acorn Computers", + 0xDC: "Lara Technology", + 0x5D: "Oak Technology, Inc.", + 0x5E: "Itec Memory", + 0xDF: "Tanisys Technology", + 0xE0: "Truevision", + 0x61: "Wintec Industries", + 0x62: "Super PC Memory", + 0xE3: "MGV Memory", + 0x64: "Galvantech", + 0xE5: "Gadzoox Nteworks", + 0xE6: "Multi Dimensional Cons.", + 0x67: "GateField", + 0x68: "Integrated Memory System", + 0xE9: "Triscend", + 0xEA: "XaQti", + 0x6B: "Goldenram", + 0xEC: "Clear Logic", + 0x6D: "Cimaron Communications", + 0x6E: "Nippon Steel Semi. Corp.", + 0xEF: "Advantage Memory", + 0x70: "AMCC", + 0xF1: "LeCroy", + 0xF2: "Yamaha Corporation", + 0x73: "Digital Microwave", + 0xF4: "NetLogic Microsystems", + 0x75: "MIMOS Semiconductor", + 0x76: "Advanced Fibre", + 0xF7: "BF Goodrich Data.", + 0xF8: "Epigram", + 0x79: "Acbel Polytech Inc.", + 0x7A: "Apacer Technology", + 0xFB: "Admor Memory", + 0x7C: "FOXCONN", + 0xFD: "Quadratics Superconductor", + 0xFE: "3COM", + }, + { + 0x01: "Camintonn Corporation", + 0x02: "ISOA Incorporated", + 0x83: "Agate Semiconductor", + 0x04: "ADMtek Incorporated", + 0x85: "HYPERTEC", + 0x86: "Adhoc Technologies", + 0x07: "MOSAID Technologies", + 0x08: "Ardent Technologies", + 0x89: "Switchcore", + 0x8A: "Cisco Systems, Inc.", + 0x0B: "Allayer Technologies", + 0x8C: "WorkX AG", + 0x0D: "Oasis Semiconductor", + 0x0E: "Novanet Semiconductor", + 0x8F: "E-M Solutions", + 0x10: "Power General", + 0x91: "Advanced Hardware Arch.", + 0x92: "Inova Semiconductors GmbH", + 0x13: "Telocity", + 0x94: "Delkin Devices", + 0x15: "Symagery Microsystems", + 0x16: "C-Port Corporation", + 0x97: "SiberCore Technologies", + 0x98: "Southland Microsystems", + 0x19: "Malleable Technologies", + 0x1A: "Kendin Communications", + 0x9B: "Great Technology Microcomputer", + 0x1C: "Sanmina Corporation", + 0x9D: "HADCO Corporation", + 0x9E: "Corsair", + 0x1F: "Actrans System Inc.", + 0x20: "ALPHA Technologies", + 0xA1: "Cygnal Integrated Products Incorporated", + 0xA2: "Artesyn Technologies", + 0x23: "Align Manufacturing", + 0xA4: "Peregrine Semiconductor", + 0x25: "Chameleon Systems", + 0x26: "Aplus Flash Technology", + 0xA7: "MIPS Technologies", + 0xA8: "Chrysalis ITS", + 0x29: "ADTEC Corporation", + 0x2A: "Kentron Technologies", + 0xAB: "Win Technologies", + 0x2C: "ASIC Designs Inc", + 0xAD: "Extreme Packet Devices", + 0xAE: "RF Micro Devices", + 0x2F: "Siemens AG", + 0xB0: "Sarnoff Corporation", + 0x31: "Itautec Philco SA", + 0x32: "Radiata Inc.", + 0xB3: "Benchmark Elect. (AVEX)", + 0x34: "Legend", + 0xB5: "SpecTek Incorporated", + 0xB6: "Hi/fn", + 0x37: "Enikia Incorporated", + 0x38: "SwitchOn Networks", + 0xB9: "AANetcom Incorporated", + 0xBA: "Micro Memory Bank", + 0x3B: "ESS Technology", + 0xBC: "Virata Corporation", + 0x3D: "Excess Bandwidth", + 0x3E: "West Bay Semiconductor", + 0xBF: "DSP Group", + 0x40: "Newport Communications", + 0xC1: "Chip2Chip Incorporated", + 0xC2: "Phobos Corporation", + 0x43: "Intellitech Corporation", + 0xC4: "Nordic VLSI ASA", + 0x45: "Ishoni Networks", + 0x46: "Silicon Spice", + 0xC7: "Alchemy Semiconductor", + 0xC8: "Agilent Technologies", + 0x49: "Centillium Communications", + 0x4A: "W.L. Gore", + 0xCB: "HanBit Electronics", + 0x4C: "GlobeSpan", + 0xCD: "Element 14", + 0xCE: "Pycon", + 0x4F: "Saifun Semiconductors", + 0xD0: "Sibyte, Incorporated", + 0x51: "MetaLink Technologies", + 0x52: "Feiya Technology", + 0xD3: "I & C Technology", + 0x54: "Shikatronics", + 0xD5: "Elektrobit", + 0xD6: "Megic", + 0x57: "Com-Tier", + 0x58: "Malaysia Micro Solutions", + 0xD9: "Hyperchip", + 0xDA: "Gemstone Communications", + 0x5B: "Anadyne Microelectronics", + 0xDC: "3ParData", + 0x5D: "Mellanox Technologies", + 0x5E: "Tenx Technologies", + 0xDF: "Helix AG", + 0xE0: "Domosys", + 0x61: "Skyup Technology", + 0x62: "HiNT Corporation", + 0xE3: "Chiaro", + 0x64: "MCI Computer GMBH", + 0xE5: "Exbit Technology A/S", + 0xE6: "Integrated Technology Express", + 0x67: "AVED Memory", + 0x68: "Legerity", + 0xE9: "Jasmine Networks", + 0xEA: "Caspian Networks", + 0x6B: "nCUBE", + 0xEC: "Silicon Access Networks", + 0x6D: "FDK Corporation", + 0x6E: "High Bandwidth Access", + 0xEF: "MultiLink Technology", + 0x70: "BRECIS", + 0xF1: "World Wide Packets", + 0xF2: "APW", + 0x73: "Chicory Systems", + 0xF4: "Xstream Logic", + 0x75: "Fast-Chip", + 0x76: "Zucotto Wireless", + 0xF7: "Realchip", + 0xF8: "Galaxy Power", + 0x79: "eSilicon", + 0x7A: "Morphics Technology", + 0xFB: "Accelerant Networks", + 0x7C: "Silicon Wave", + 0xFD: "SandCraft", + 0xFE: "Elpida", + }, + { + 0x01: "Solectron", + 0x02: "Optosys Technologies", + 0x83: "Buffalo (Formerly Melco)", + 0x04: "TriMedia Technologies", + 0x85: "Cyan Technologies", + 0x86: "Global Locate", + 0x07: "Optillion", + 0x08: "Terago Communications", + 0x89: "Ikanos Communications", + 0x8A: "Princeton Technology", + 0x0B: "Nanya Technology", + 0x8C: "Elite Flash Storage", + 0x0D: "Mysticom", + 0x0E: "LightSand Communications", + 0x8F: "ATI Technologies", + 0x10: "Agere Systems", + 0x91: "NeoMagic", + 0x92: "AuroraNetics", + 0x13: "Golden Empire", + 0x94: "Muskin", + 0x15: "Tioga Technologies", + 0x16: "Netlist", + 0x97: "TeraLogic", + 0x98: "Cicada Semiconductor", + 0x19: "Centon Electronics", + 0x1A: "Tyco Electronics", + 0x9B: "Magis Works", + 0x1C: "Zettacom", + 0x9D: "Cogency Semiconductor", + 0x9E: "Chipcon AS", + 0x1F: "Aspex Technology", + 0x20: "F5 Networks", + 0xA1: "Programmable Silicon Solutions", + 0xA2: "ChipWrights", + 0x23: "Acorn Networks", + 0xA4: "Quicklogic", + 0x25: "Kingmax Semiconductor", + 0x26: "BOPS", + 0xA7: "Flasys", + 0xA8: "BitBlitz Communications", + 0x29: "eMemory Technology", + 0x2A: "Procket Networks", + 0xAB: "Purple Ray", + 0x2C: "Trebia Networks", + 0xAD: "Delta Electronics", + 0xAE: "Onex Communications", + 0x2F: "Ample Communications", + 0xB0: "Memory Experts Intl", + 0x31: "Astute Networks", + 0x32: "Azanda Network Devices", + 0xB3: "Dibcom", + 0x34: "Tekmos", + 0xB5: "API NetWorks", + 0xB6: "Bay Microsystems", + 0x37: "Firecron Ltd", + 0x38: "Resonext Communications", + 0xB9: "Tachys Technologies", + 0xBA: "Equator Technology", + 0x3B: "Concept Computer", + 0xBC: "SILCOM", + 0x3D: "3Dlabs", + 0x3E: "ct Magazine", + 0xBF: "Sanera Systems", + 0x40: "Silicon Packets", + 0xC1: "Viasystems Group", + 0xC2: "Simtek", + 0x43: "Semicon Devices Singapore", + 0xC4: "Satron Handelsges", + 0x45: "Improv Systems", + 0x46: "INDUSYS GmbH", + 0xC7: "Corrent", + 0xC8: "Infrant Technologies", + 0x49: "Ritek Corp", + 0x4A: "empowerTel Networks", + 0xCB: "Hypertec", + 0x4C: "Cavium Networks", + 0xCD: "PLX Technology", + 0xCE: "Massana Design", + 0x4F: "Intrinsity", + 0xD0: "Valence Semiconductor", + 0x51: "Terawave Communications", + 0x52: "IceFyre Semiconductor", + 0xD3: "Primarion", + 0x54: "Picochip Designs Ltd", + 0xD5: "Silverback Systems", + 0xD6: "Jade Star Technologies", + 0x57: "Pijnenburg Securealink", + 0x58: "MemorySolutioN", + 0xD9: "Cambridge Silicon Radio", + 0xDA: "Swissbit", + 0x5B: "Nazomi Communications", + 0xDC: "eWave System", + 0x5D: "Rockwell Collins", + 0x5E: "PAION", + 0xDF: "Alphamosaic Ltd", + 0xE0: "Sandburst", + 0x61: "SiCon Video", + 0x62: "NanoAmp Solutions", + 0xE3: "Ericsson Technology", + 0x64: "PrairieComm", + 0xE5: "Mitac International", + 0xE6: "Layer N Networks", + 0x67: "Atsana Semiconductor", + 0x68: "Allegro Networks", + 0xE9: "Marvell Semiconductors", + 0xEA: "Netergy Microelectronic", + 0x6B: "NVIDIA", + 0xEC: "Internet Machines", + 0x6D: "Peak Electronics", + 0xEF: "Accton Technology", + 0x70: "Teradiant Networks", + 0xF1: "Europe Technologies", + 0xF2: "Cortina Systems", + 0x73: "RAM Components", + 0xF4: "Raqia Networks", + 0x75: "ClearSpeed", + 0x76: "Matsushita Battery", + 0xF7: "Xelerated", + 0xF8: "SimpleTech", + 0x79: "Utron Technology", + 0x7A: "Astec International", + 0xFB: "AVM gmbH", + 0x7C: "Redux Communications", + 0xFD: "Dot Hill Systems", + 0xFE: "TeraChip", + }, + { + 0x01: "T-RAM Incorporated", + 0x02: "Innovics Wireless", + 0x83: "Teknovus", + 0x04: "KeyEye Communications", + 0x85: "Runcom Technologies", + 0x86: "RedSwitch", + 0x07: "Dotcast", + 0x08: "Silicon Mountain Memory", + 0x89: "Signia Technologies", + 0x8A: "Pixim", + 0x0B: "Galazar Networks", + 0x8C: "White Electronic Designs", + 0x0D: "Patriot Scientific", + 0x0E: "Neoaxiom Corporation", + 0x8F: "3Y Power Technology", + 0x10: "Europe Technologies", + 0x91: "Potentia Power Systems", + 0x92: "C-guys Incorporated", + 0x13: "Digital Communications Technology Incorporated", + 0x94: "Silicon-Based Technology", + 0x15: "Fulcrum Microsystems", + 0x16: "Positivo Informatica Ltd", + 0x97: "XIOtech Corporation", + 0x98: "PortalPlayer", + 0x19: "Zhiying Software", + 0x1A: "Direct2Data", + 0x9B: "Phonex Broadband", + 0x1C: "Skyworks Solutions", + 0x9D: "Entropic Communications", + 0x9E: "Pacific Force Technology", + 0x1F: "Zensys A/S", + 0x20: "Legend Silicon Corp.", + 0xA1: "sci-worx GmbH", + 0xA2: "Oasis Silicon Systems", + 0x23: "Renesas Technology", + 0xA4: "Raza Microelectronics", + 0x25: "Phyworks", + 0x26: "MediaTek", + 0xA7: "Non-cents Productions", + 0xA8: "US Modular", + 0x29: "Wintegra Ltd", + 0x2A: "Mathstar", + 0xAB: "StarCore", + 0x2C: "Oplus Technologies", + 0xAD: "Mindspeed", + 0xAE: "Just Young Computer", + 0x2F: "Radia Communications", + 0xB0: "OCZ", + 0x31: "Emuzed", + 0x32: "LOGIC Devices", + 0xB3: "Inphi Corporation", + 0x34: "Quake Technologies", + 0xB5: "Vixel", + 0xB6: "SolusTek", + 0x37: "Kongsberg Maritime", + 0x38: "Faraday Technology", + 0xB9: "Altium Ltd.", + 0xBA: "Insyte", + 0x3B: "ARM Ltd.", + 0xBC: "DigiVision", + 0x3D: "Vativ Technologies", + 0x3E: "Endicott Interconnect Technologies", + 0xBF: "Pericom", + 0x40: "Bandspeed", + 0xC1: "LeWiz Communications", + 0xC2: "CPU Technology", + 0x43: "Ramaxel Technology", + 0xC4: "DSP Group", + 0x45: "Axis Communications", + 0x46: "Legacy Electronics", + 0xC7: "Chrontel", + 0xC8: "Powerchip Semiconductor", + 0x49: "MobilEye Technologies", + 0x4A: "Excel Semiconductor", + 0xCB: "A-DATA Technology", + 0x4C: "VirtualDigm", + }, +] + +memory_types = { + 1: "STD FPM DRAM", + 2: "EDO", + 3: "Pipelined Nibble", + 4: "SDRAM", + 5: "ROM", + 6: "DDR SGRAM", + 7: "DDR SDRAM", + 8: "DDR2 SDRAM", + 9: "DDR2 SDRAM FB-DIMM", + 10: "DDR2 SDRAM FB-DIMM PROBE", + 11: "DDR3 SDRAM", + 12: "DDR4 SDRAM", +} + +module_types = { + 1: "RDIMM", + 2: "UDIMM", + 3: "SODIMM", + 4: "Micro-DIMM", + 5: "Mini-RDIMM", + 6: "Mini-UDIMM", +} + +ddr3_module_capacity = { + 0: 256, + 1: 512, + 2: 1024, + 3: 2048, + 4: 4096, + 5: 8192, + 6: 16384, + 7: 32768, +} + +ddr3_dev_width = { + 0: 4, + 1: 8, + 2: 16, + 3: 32, +} + +ddr3_ranks = { + 0: 1, + 1: 2, + 2: 3, + 3: 4 +} + +ddr3_bus_width = { + 0: 8, + 1: 16, + 2: 32, + 3: 64, +} + +speed_by_clock = { + 800: 6400, + 1066: 8500, + 1333: 10600, + 1600: 12800, + 1867: 14900, + 2132: 17000, + 2133: 17000, + 2134: 17000, +} + + +def decode_manufacturer(index, mfg): + index &= 0x7f + try: + return jedec_ids[index][mfg] + except (KeyError, IndexError): + return 'Unknown ({0}, {1})'.format(index, mfg) + + +def decode_spd_date(year, week): + if year == 0 and week == 0: + return 'Unknown' + return '20{0:02x}-W{1:x}'.format(year, week) + + +class SPD(object): + def __init__(self, bytedata): + """Parsed memory information + + Parse bytedata input and provide a structured detail about the + described memory component + + :param bytedata: A bytearray of data to decode + :return: + """ + self.rawdata = bytearray(bytedata) + spd = self.rawdata + self.info = {'memory_type': memory_types.get(spd[2], 'Unknown')} + if spd[2] == 11: + self._decode_ddr3() + elif spd[2] == 12: + self._decode_ddr4() + + def _decode_ddr3(self): + spd = self.rawdata + finetime = (spd[9] >> 4) / (spd[9] & 0xf) + fineoffset = spd[34] + if fineoffset & 0b10000000: + # Take two's complement for negative offset + fineoffset = 0 - ((fineoffset ^ 0xff) + 1) + fineoffset = (finetime * fineoffset) * 10**-3 + mtb = spd[10] / float(spd[11]) + clock = 2 // ((mtb * spd[12] + fineoffset)*10**-3) + self.info['speed'] = speed_by_clock.get(clock, 'Unknown') + self.info['ecc'] = (spd[8] & 0b11000) != 0 + self.info['module_type'] = module_types.get(spd[3] & 0xf, 'Unknown') + sdramcap = ddr3_module_capacity[spd[4] & 0xf] + buswidth = ddr3_bus_width[spd[8] & 0b111] + sdramwidth = ddr3_dev_width[spd[7] & 0b111] + ranks = ddr3_ranks[(spd[7] & 0b111000) >> 3] + self.info['capacity_mb'] = sdramcap / 8 * buswidth / sdramwidth * ranks + self.info['manufacturer'] = decode_manufacturer(spd[117], spd[118]) + self.info['manufacture_location'] = spd[119] + self.info['manufacture_date'] = decode_spd_date(spd[120], spd[121]) + self.info['model'] = struct.pack('18B', *spd[128:146]) + + def _decode_ddr4(self): + spd = self.rawdata + if spd[17] == 0: + fineoffset = spd[125] + if fineoffset & 0b10000000: + fineoffset = 0 - ((fineoffset ^ 0xff) + 1) + clock = 2 // ((0.125 * spd[18] + fineoffset * 0.001) * 0.001) + self.info['speed'] = speed_by_clock.get(clock, 'Unknown') + else: + self.info['speed'] = 'Unknown' + self.info['ecc'] = (spd[13] & 0b11000) == 0b1000 + self.info['module_type'] = module_types.get(spd[3] & 0xf, + 'Unknown') + sdramcap = ddr3_module_capacity[spd[4] & 0xf] + buswidth = ddr3_bus_width[spd[13] & 0b111] + sdramwidth = ddr3_dev_width[spd[12] & 0b111] + ranks = ddr3_ranks[(spd[12] & 0b111000) >> 3] + self.info['capacity_mb'] = sdramcap / 8 * buswidth / sdramwidth * ranks + self.info['manufacturer'] = decode_manufacturer(spd[320], spd[321]) + self.info['manufacture_location'] = spd[322] + self.info['manufacture_date'] = decode_spd_date(spd[323], spd[324]) + self.info['model'] = struct.pack('18B', *spd[329:347]) diff --git a/pyghmi/ipmi/sdr.py b/pyghmi/ipmi/sdr.py index ac05f03d..905fbc62 100644 --- a/pyghmi/ipmi/sdr.py +++ b/pyghmi/ipmi/sdr.py @@ -2,6 +2,7 @@ # coding=utf8 # Copyright 2014 IBM Corporation +# Copyright 2015 Lenovo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -269,9 +270,6 @@ class SDREntry(object): self.sdrtype = TYPE_UNKNOWN # assume undefined self.oem_decode(entrybytes[5:]) elif self.reportunsupported: - #will remove once I see it stop being thrown for now - #perhaps need some explicit mode to check for - #unsupported things, but make do otherwise raise NotImplementedError else: self.sdrtype = TYPE_UNKNOWN @@ -301,6 +299,9 @@ class SDREntry(object): self.sdrtype = TYPE_FRU self.fru_name = self.tlv_decode(entry[10], entry[11:]) self.fru_number = entry[1] + self.fru_logical = (entry[2] & 0b10000000) == 0b10000000 + # 0x8 to 0x10.. 0 unspecified except on 0x10, 1 is dimm + self.fru_type_and_modifier = (entry[5] << 8) + entry[6] def association_decode(self, entry): # table 43-4 Entity Associaition Record