Commit 82d17a29 authored by Matthieu Cattin's avatar Matthieu Cattin

Fix bug and IPMI/FMC standards incompatibilities, add an python example.

- Fix typelength mask to 0x3f, was 0x1f.
- Fix OEM data structure, p1_gbt and p2_gbt are in the same byte.
- Fix OEM data byte 1 arrangement, fields were in the wrong place.
- Fix typelength for part number in Board Info Area, was taking product typelength.
- Fix Board Info Area checksum calculation, the "end of fields" typelength was
  assigned after checksum calculation.
- Fix OEM manufacturer ID byte order to LS Byte first, was MS Byte first.
- Add common header format in common header byte 0.
- Fix DC load structure length calculation and write to file.
  As the structure contains uint8_t and uint16_t, the compiler is aligning
  all entries to 16-bit boundary. This was resulting in a extra 0x00 byte,
  increasing the structure length by 1 byte.
- Internal Use Area is now optionnal.
parent 4823acc5
......@@ -39,7 +39,7 @@ class c_BoardInfoArea(Structure):
("fru_fid_data", c_char_p),
("typelen_end", c_ubyte),
("pad_len", c_ubyte),
("checksum", c_ubyte),
("checksum", c_ubyte)
]
class c_DCLoadRecord(Structure):
......@@ -50,7 +50,7 @@ class c_DCLoadRecord(Structure):
("max_voltage", c_ushort),
("spec_ripple", c_ushort),
("min_current", c_ushort),
("max_current", c_ushort),
("max_current", c_ushort)
]
class c_DCOutputRecord(Structure):
......@@ -61,14 +61,14 @@ class c_DCOutputRecord(Structure):
("max_pos_voltage_dev", c_ushort),
("ripple", c_ushort),
("min_current_draw", c_ushort),
("max_current_draw", c_ushort),
("max_current_draw", c_ushort)
]
class c_InternalUseArea(Structure):
_fields_ = [
("format", c_ubyte),
("len", c_int),
("data", c_char_p),
("data", c_char_p)
]
class InternalUseArea:
......@@ -105,8 +105,8 @@ class BoardInfoArea:
self.struct = c_BoardInfoArea()
self._as_parameter_ = byref(self.struct)
self.struct.format = 0x1
self.struct.area_len = 0 # English
self.struct.language = 0
self.struct.area_len = 0
self.struct.language = 0 # English
self.set_manufacture_date(date)
self.set_manufacturer(mfgr)
self.set_product_name(product)
......@@ -122,23 +122,23 @@ class BoardInfoArea:
def set_manufacturer(self, data):
self.struct.mfgr_data = c_char_p(data)
self.struct.mfgr_typelen = (len(bytearray(data)) & 0x1f) | (0x3 << 6)
self.struct.mfgr_typelen = (len(bytearray(data)) & 0x3f) | (0x3 << 6)
def set_product_name(self, data):
self.struct.product_data = c_char_p(data)
self.struct.product_typelen = (len(bytearray(data)) & 0x1f) | (0x3 << 6)
self.struct.product_typelen = (len(bytearray(data)) & 0x3f) | (0x3 << 6)
def set_serial_number(self, data):
self.struct.serial_data = c_char_p(data)
self.struct.serial_typelen = (len(bytearray(data)) & 0x1f) | (0x3 << 6)
self.struct.serial_typelen = (len(bytearray(data)) & 0x3f) | (0x3 << 6)
def set_part_number(self, data):
self.struct.partnum_data = c_char_p(data)
self.struct.partnum_typelen = (len(bytearray(data)) & 0x1f) | (0x3 << 6)
self.struct.partnum_typelen = (len(bytearray(data)) & 0x3f) | (0x3 << 6)
def set_fru_file_id(self, data):
self.struct.fru_fid_data = c_char_p(data)
self.struct.fru_fid_typelen = (len(bytearray(data)) & 0x1f) | (0x3 << 6)
self.struct.fru_fid_typelen = (len(bytearray(data)) & 0x3f) | (0x3 << 6)
class DCLoadRecord:
......@@ -229,9 +229,8 @@ class c_FMCOEMData(Structure):
("p1_b_nsig", c_ubyte),
("p2_a_nsig", c_ubyte),
("p2_b_nsig", c_ubyte),
("p1_gbt_ntran", c_ubyte),
("p2_gbt_ntran", c_ubyte),
("max_clock", c_ubyte),
("p1_p2_gbt_ntran", c_ubyte),
("max_clock", c_ubyte)
]
class c_OEMRecord(Structure):
......@@ -239,7 +238,7 @@ class c_OEMRecord(Structure):
("mfg_id0", c_ubyte),
("mfg_id1", c_ubyte),
("mfg_id2", c_ubyte),
("data", c_FMCOEMData),
("data", c_FMCOEMData)
]
......@@ -269,19 +268,20 @@ class OEMRecord:
self.set_max_clock(maxclk)
def set_module_size(self, val):
self.struct.data.other &= ~(0x3)
self.struct.data.other |= val & 0x3
self.struct.data.other &= ~(0xc0)
self.struct.data.other |= (val << 6) & 0xc0
def set_p1_connector_size(self, val):
self.struct.data.other &= ~(0xc)
self.struct.data.other |= (val << 2) & 0xc
def set_p2_connector_size(self, val):
self.struct.data.other &= ~(0x30)
self.struct.data.other |= (val << 4) & 0x30
def set_p2_connector_size(self, val):
self.struct.data.other &= ~(0xc)
self.struct.data.other |= (val << 2) & 0xc
def set_clock_direction(self, val):
self.struct.data.other |= (val & 0x1) << 4
self.struct.data.other &= ~(0x2)
self.struct.data.other |= (val << 1) & 0x2
def set_nsignals(self, port, bank, num):
if (port == 1):
......@@ -297,9 +297,11 @@ class OEMRecord:
def set_num_gbt_transceivers(self, port, num):
if (port == 1):
self.struct.data.p1_gbt_ntran = num
self.struct.data.p1_p2_gbt_ntran &= ~(0xf0)
self.struct.data.p1_p2_gbt_ntran |= (num << 4) & 0xf0
elif (port == 2):
self.struct.data.p2_gbt_ntran = num
self.struct.data.p1_p2_gbt_ntran &= ~(0xf)
self.struct.data.p1_p2_gbt_ntran |= num & 0xf
def set_max_clock(self, clock):
self.struct.data.max_clock = clock
......@@ -311,14 +313,15 @@ def ipmi_open_file(name):
def ipmi_close_file():
lib.ipmi_file_close()
def ipmi_set(bia, dcload, dcout, oem, iua):
def ipmi_set(bia, dcload, dcout, oem, iua=None):
lib.ipmi_set_board_info_area(bia)
for r in dcload:
lib.ipmi_add_dc_load_record(r)
for r in dcout:
lib.ipmi_add_dc_output_record(r)
lib.ipmi_set_oem_record(oem)
lib.ipmi_set_internal_use_area(iua)
if iua != None:
lib.ipmi_set_internal_use_area(iua)
def ipmi_write():
lib.ipmi_write()
......
#! /usr/bin/env python
# coding: utf8
# Copyright CERN, 2011
# Author: Matthieu Cattin <matthieu.cattin@cern.ch>
# Licence: GPL v2 or later.
# Website: http://www.ohwr.org
# Last modifications: 16/5/2012
# Import system modules
import sys
import time
import datetime
import os
from fmc_eeprom import *
"""
Tests IPMI formatting library.
"""
def main (default_directory='.'):
# Constants declaration
PART_NUMBER = "EDA-02063-V5-0"
SERIAL = "HCCFFIA___-CR000003"
EEPROM_BIN_FILENAME = "eeprom_content.out"
EEPROM_BIN_FILENAME = os.path.join(default_directory, EEPROM_BIN_FILENAME)
EEPROM_SIZE = 8192 # in Bytes
#==================================================
# Serial number
serial = SERIAL
print "Board's serial number: %s\n" % serial
#==================================================
# Calculate number of minutes since 0:00 1/1/96
now_date = datetime.datetime.now()
ref_date = datetime.datetime(1996, 1, 1)
diff_date = now_date - ref_date
current_date = int(diff_date.total_seconds()//60)
print "Current date/time: %d minutes (since 0:00 1/1/96)\n" % current_date
# Manufacturiing date = current date
print "Manufacturing date : %d 0x%06X" % (current_date, current_date)
mfg_date = current_date
#==================================================
# Create Board Info Area
# FRU field is used to store the date of generation of the eeprom content
# This could be used later to determine if the content has to be udated (bug fix, ...)
print "EEPROM content generated: %s\n" % now_date
fru = "%s" % now_date
bia = BoardInfoArea(mfg_date, "CERN", "FmcAdc100m14b4cha", serial, PART_NUMBER, fru)
#==================================================
# Multirecords Area
# output number, vnom, vmin, vmax, ripple, imin, imax
dcload0 = DCLoadRecord(0, 2.5, 2.375, 2.625, 0.0, 0, 4000) # VADJ
dcload1 = DCLoadRecord(1, 3.3, 3.135, 3.465, 0.0, 0, 3000) # P3V3
dcload2 = DCLoadRecord(2, 12.0, 11.4, 12.6, 0.0, 0, 1000) # P12V
dcload = [ dcload0, dcload1, dcload2 ]
# output number, vnom, vmin, vmax, ripple, imin, imax
dcout0 = DCOutputRecord(3, 0.0, 0.0, 0.0, 0.0, 0, 0) # VIO_B_M2C
dcout1 = DCOutputRecord(4, 0.0, 0.0, 0.0, 0.0, 0, 0) # VREF_A_M2C
dcout2 = DCOutputRecord(5, 0.0, 0.0, 0.0, 0.0, 0, 0) # VREF_B_M2C
dcout = [ dcout0, dcout1, dcout2 ]
# module size : 0=single width, 1=double width
# P1 size : 0=LPC, 1=HPC
# P2 size : 0=LPC, 1=HPC, 3=not fitted
# clock dir : 0=M2C, 1=C2M
# nb sig P1 A : number
# nb sig P1 B : number
# nb sig P2 A : number
# nb sig P2 B : number
# nb GBT P1 : number
# nb GBT P2 : number
# max TCK freq : frequency in MHz
oem = OEMRecord(0, 1, 3, 1, 68, 0, 0, 0, 0, 0, 0)
#==================================================
# Internal Use Area
# Takes an array of byte as parameter
iua_data = [0x1,0x2,0x3,0x4]
iua = InternalUseArea(iua_data)
#==================================================
# Write eeprom content to a binary file
ipmi_open_file(EEPROM_BIN_FILENAME)
#ipmi_set(bia, dcload, dcout, oem, iua)
ipmi_set(bia, dcload, dcout, oem)
ipmi_write()
ipmi_close_file()
#==================================================
# Read eeprom content from binary file
f_eeprom = open(EEPROM_BIN_FILENAME, "rb")
eeprom_data = []
byte = f_eeprom.read(1) # reads one byte
while byte:
eeprom_data.append(ord(byte))
byte = f_eeprom.read(1) # reads one byte
f_eeprom.close()
print "Raw EEPROM data:"
i = 0
for data in eeprom_data:
if i%8 == 0:
print "0x%02X (%3d) : %02X" % (i, i, data),
else:
print "%02X" % (data),
i += 1
if i%8 == 0:
print ""
print("\n")
dsum = 0
for data in eeprom_data[158:162]:
dsum += data
print("0x%02X 0x%X" % (data, dsum))
print("\n\nsum: 0x%02X" % dsum)
checksum = (0xff & (1 + ~dsum))
print("calculated checksum: 0x%02X" % checksum)
print("data checksum : 0x%02X" % eeprom_data[162])
print("")
print("check data: 0x%02X" % (dsum + eeprom_data[162]))
print("check data: 0x%02X" % (dsum + checksum))
if __name__ == '__main__' :
main()
......@@ -42,12 +42,12 @@ uint8_t checksum(uint8_t *data, int len)
int board_info_area_get_size(uint8_t *pad)
{
int size = 13 +
(bia->mfgr_typelen & 0x1f) +
(bia->product_typelen & 0x1f) +
(bia->serial_typelen & 0x1f) +
(bia->partnum_typelen & 0x1f) +
(bia->fru_fid_typelen & 0x1f);
int size = 13 +
(bia->mfgr_typelen & 0x3f) +
(bia->product_typelen & 0x3f) +
(bia->serial_typelen & 0x3f) +
(bia->partnum_typelen & 0x3f) +
(bia->fru_fid_typelen & 0x3f);
if (size & 0x7) {
if (pad) {
*pad = 8 - (size & 0x7);
......@@ -126,29 +126,32 @@ int ipmi_board_info_area_write(void)
/* Write upto the mfgr_data */
ret = fwrite(bia, 6, 1, f);
len = bia->mfgr_typelen & 0x1f;
len = bia->mfgr_typelen & 0x3f;
ret = fwrite(&bia->mfgr_typelen, 1, sizeof(uint8_t), f);
ret = fwrite(bia->mfgr_data, len, 1, f);
len = bia->product_typelen & 0x1f;
len = bia->product_typelen & 0x3f;
ret = fwrite(&bia->product_typelen, 1, sizeof(uint8_t), f);
ret = fwrite(bia->product_data, len, 1, f);
len = bia->serial_typelen & 0x1f;
len = bia->serial_typelen & 0x3f;
ret = fwrite(&bia->serial_typelen, 1, sizeof(uint8_t), f);
ret = fwrite(bia->serial_data, len, 1, f);
len = bia->partnum_typelen & 0x1f;
ret = fwrite(&bia->product_typelen, 1, sizeof(uint8_t), f);
len = bia->partnum_typelen & 0x3f;
ret = fwrite(&bia->partnum_typelen, 1, sizeof(uint8_t), f);
ret = fwrite(bia->partnum_data, len, 1, f);
len = bia->fru_fid_typelen & 0x1f;
len = bia->fru_fid_typelen & 0x3f;
ret = fwrite(&bia->fru_fid_typelen, 1, sizeof(uint8_t), f);
ret = fwrite(bia->fru_fid_data, len, 1, f);
bia->typelen_end = 0xc1;
ret = fwrite(&bia->typelen_end, 1, sizeof(uint8_t), f);
/* calculate checksum here */
checksum = 0;
checksum +=
checksum +=
bia->format +
bia->area_len +
bia->language +
......@@ -162,23 +165,25 @@ int ipmi_board_info_area_write(void)
bia->fru_fid_typelen +
bia->typelen_end;
for (i = 0; i < (bia->mfgr_typelen & 0x1f); i++)
for (i = 0; i < (bia->mfgr_typelen & 0x3f); i++)
checksum += bia->mfgr_data[i];
for (i = 0; i < (bia->product_typelen & 0x1f); i++)
for (i = 0; i < (bia->product_typelen & 0x3f); i++)
checksum += bia->product_data[i];
for (i = 0; i < (bia->serial_typelen & 0x1f); i++)
for (i = 0; i < (bia->serial_typelen & 0x3f); i++)
checksum += bia->serial_data[i];
for (i = 0; i < (bia->partnum_typelen & 0x1f); i++)
for (i = 0; i < (bia->partnum_typelen & 0x3f); i++)
checksum += bia->partnum_data[i];
for (i = 0; i < (bia->fru_fid_typelen & 0x1f); i++)
for (i = 0; i < (bia->fru_fid_typelen & 0x3f); i++)
checksum += bia->fru_fid_data[i];
checksum = -checksum;
checksum &= 0xff;
bia->checksum = checksum;
bia->typelen_end = 0xc1;
ret = fwrite(&bia->typelen_end, 1, sizeof(uint8_t), f);
uint8_t nul = 0;
board_info_area_get_size(&pad);
for (i = 0; i < pad; i++)
......@@ -203,14 +208,15 @@ int ipmi_dc_load_record_write(int end)
head.extra = 0x2;
if (end)
head.extra |= (1 << 7);
head.record_len = sizeof(struct dc_load_record);
head.record_len = 13;
head.record_checksum = checksum((uint8_t *)t->rec,
sizeof(struct dc_load_record));
head.header_checksum = checksum((uint8_t *)&head,
sizeof(struct multirecord_header) - 1);
ret = fwrite(&head, 1, sizeof(struct multirecord_header), f);
ret = fwrite(t->rec, 1, sizeof(struct dc_load_record), f);
ret = fwrite(&t->rec->voltage_required, 1, 1, f);
ret = fwrite(&t->rec->nominal_voltage, 1, 12, f);
t = t->next;
}
......@@ -232,14 +238,15 @@ int ipmi_dc_output_record_write(int end)
head.extra = 0x2;
if (end)
head.extra |= (1 << 7);
head.record_len = sizeof(struct dc_output_record);
head.record_len = 13;
head.record_checksum = checksum((uint8_t *)t->rec,
sizeof(struct dc_output_record));
head.header_checksum = checksum((uint8_t *)&head,
sizeof(struct multirecord_header) - 1);
ret = fwrite(&head, 1, sizeof(struct multirecord_header), f);
ret = fwrite(t->rec, 1, sizeof(struct dc_output_record), f);
ret = fwrite(&t->rec->output_info, 1, 1, f);
ret = fwrite(&t->rec->nominal_voltage, 1, 12, f);
t = t->next;
}
......@@ -254,10 +261,10 @@ int ipmi_oem_record_write(int end)
if (!oem || !f)
return -1;
/* VITA ID: 0x0012a2 */
oem->mfg_id0 = 0x00;
/* VITA ID: 0x0012a2 (LS Byte first) */
oem->mfg_id0 = 0xa2;
oem->mfg_id1 = 0x12;
oem->mfg_id2 = 0xa2;
oem->mfg_id2 = 0x00;
head.record_typeid = 0xfa; /* OEM record type */
head.extra = 0x2;
......@@ -282,12 +289,12 @@ int multirecord_area_get_size(int *diff)
int sum = 0;
while (l1) {
sum += sizeof(struct multirecord_header);
sum += sizeof(struct dc_load_record);
sum += 13;
l1 = l1->next;
}
while (l2) {
sum += sizeof(struct multirecord_header);
sum += sizeof(struct dc_output_record);
sum += 13;
l2 = l2->next;
}
sum += sizeof(struct multirecord_header) + sizeof(struct oem_record);
......@@ -308,23 +315,59 @@ int ipmi_write(void)
ch = malloc(sizeof(struct common_header));
memset(ch, 0, sizeof(struct common_header));
ch->board_area_off = sizeof(struct common_header)/8;
ch->format = 1; // Format version
/*
* IPMI areas arrangement in memory
*
* +------------------------------+
* | Common header |
* +------------------------------+
* | Board area |
* +------------------------------+
* | Multi-record area |
* | +------------------------+
* | | 3x DC load records |
* | +------------------------+
* | | 3x DC output records |
* | +------------------------+
* | | OEM record |
* +-----+------------------------+
* | Internal use area (optional) |
* +------------------------------+
*/
// Compute area offsets
ch->board_area_off = sizeof(struct common_header)/8; // always 1
ch->multirecord_off = (sizeof(struct common_header) + board_info_area_get_size(NULL))/8;
ch->internal_use_off = (sizeof(struct common_header) + board_info_area_get_size(NULL) + multirecord_area_get_size(NULL))/8;
bia->area_len = board_info_area_get_size(NULL);
if (iua)
ch->internal_use_off = (sizeof(struct common_header) + board_info_area_get_size(NULL) + multirecord_area_get_size(NULL))/8;
else
ch->internal_use_off = 0;
// Write common heade
ipmi_common_header_write();
// Write board info area, padding (to 8 byte multiple) is done inside the write function
bia->area_len = board_info_area_get_size(NULL);
ipmi_board_info_area_write();
// Write multi-record area
ipmi_dc_load_record_write(0);
ipmi_dc_output_record_write(0);
ipmi_oem_record_write(1);
// Padding after multi-record area
multirecord_area_get_size(&padlen);
if (padlen) {
int i;
for (i = 0; i < padlen; i++)
fwrite(&pad, 1, 1, f);
}
ipmi_internal_use_area_write();
// Write Internal Use area, if exists
if (iua)
ipmi_internal_use_area_write();
return 0;
}
......
......@@ -94,8 +94,7 @@ struct fmc_oem_data {
uint8_t p1_b_nsig;
uint8_t p2_a_nsig;
uint8_t p2_b_nsig;
uint8_t p1_gbt_ntran;
uint8_t p2_gbt_ntran;
uint8_t p1_p2_gbt_ntran;
uint8_t max_clock;
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment