Commit bb979257 authored by Matthieu Cattin's avatar Matthieu Cattin Committed by Alessandro Rubini

tools/{fru-generator,libipmi}: copied from PTS::1564157

The contents of this commit are part of the "fmcadc100m14b4cha" branch
of the "pts" project (Python Test Suite) on ohwr, commit 1564157 (Nov
13 2012).

Files are renamed like this:

 test/fmceeprom/python/fmc_eeprom_example.py  -> tools/fru-generator
 test/fmceeprom/python/libipmi/               -> tools/libipmi/
 test/fmceeprom/python/fmc_eeprom.py          -> tools/libipmi/fmc_eeprom.py

The rationale is that tools/fru-generator will be called by our users,
while fmc_eeprom.py is part of the library.

Later commits in fmc-bus change the files (now copied literally) to fit
the different use case.

Matthieu is the author of this code with Manohar Vanga, but neither is
reposnsible for this commit. The committer marked Matthieu as author
so "git blame" will show him not me.

It is lifted here (and renamed) unchanged before being customized.
parent be23bf8e
#! /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
total_seconds = diff_date.days * 86400 + diff_date.seconds
current_date = int(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()
OBJ=ipmi.o
OUT=libipmi.a
OUT_SO=libipmi.so
CFLAGS+=-fPIC -shared -Wall -Wextra -ggdb
all: $(OUT) $(OUT_SO)
$(OUT): $(OBJ)
ar rcs $(OUT) $(OBJ)
$(OUT_SO): $(OBJ)
$(CC) $< $(CFLAGS) -shared -fPIC -L. -Wl,-soname,$@ -o $@
clean:
rm -rf $(OBJ) $(OUT) $(OUT_SO)
This diff is collapsed.
#include "ipmi.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static FILE *f = NULL;
struct common_header *ch = NULL;
struct board_info_area *bia = NULL;
struct oem_record *oem = NULL;
struct internal_use_area *iua = NULL;
struct dc_load_list *dcll = NULL;
struct dc_output_list *dcol = NULL;
int ipmi_file_open(const char *name)
{
if (f)
fclose(f);
f = fopen(name, "w");
if (!f)
return -1;
return 0;
}
void ipmi_file_close(void)
{
if (f)
fclose(f);
}
uint8_t checksum(uint8_t *data, int len)
{
int i;
int sum = 0;
for (i = 0; i < len; i++)
sum += data[i];
return (-sum)&0xff;
}
int board_info_area_get_size(uint8_t *pad)
{
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);
}
size -= size % 8;
size += 8;
}
return size;
}
int internal_use_area_get_size(void)
{
return 1 + iua->len;
}
int ipmi_common_header_write(void)
{
int ret;
if (!ch || !f)
return -1;
ch->checksum = checksum((uint8_t *)ch, sizeof(struct common_header) - 1);
ret = fwrite(ch, 1, sizeof(struct common_header), f);
return 0;
}
void ipmi_set_board_info_area(struct board_info_area *d)
{
bia = d;
}
void ipmi_add_dc_load_record(struct dc_load_record *d)
{
struct dc_load_list *l = malloc(sizeof(struct dc_load_list));
l->rec = d;
l->next = NULL;
if (!dcll) {
dcll = l;
} else {
l->next = dcll;
dcll = l;
}
}
void ipmi_add_dc_output_record(struct dc_output_record *d)
{
struct dc_output_list *l = malloc(sizeof(struct dc_output_list));
l->rec = d;
l->next = NULL;
if (!dcol) {
dcol = l;
} else {
l->next = dcol;
dcol = l;
}
}
void ipmi_set_oem_record(struct oem_record *d)
{
oem = d;
}
int ipmi_board_info_area_write(void)
{
int i;
int len;
int ret;
uint8_t pad = 0;
uint8_t checksum;
if (!bia || !f)
return -1;
/* Write upto the mfgr_data */
ret = fwrite(bia, 6, 1, f);
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 & 0x3f;
ret = fwrite(&bia->product_typelen, 1, sizeof(uint8_t), f);
ret = fwrite(bia->product_data, len, 1, f);
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 & 0x3f;
ret = fwrite(&bia->partnum_typelen, 1, sizeof(uint8_t), f);
ret = fwrite(bia->partnum_data, len, 1, f);
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 +=
bia->format +
bia->area_len +
bia->language +
bia->mfg_date0 +
bia->mfg_date1 +
bia->mfg_date2 +
bia->mfgr_typelen +
bia->product_typelen +
bia->serial_typelen +
bia->partnum_typelen +
bia->fru_fid_typelen +
bia->typelen_end;
for (i = 0; i < (bia->mfgr_typelen & 0x3f); i++)
checksum += bia->mfgr_data[i];
for (i = 0; i < (bia->product_typelen & 0x3f); i++)
checksum += bia->product_data[i];
for (i = 0; i < (bia->serial_typelen & 0x3f); i++)
checksum += bia->serial_data[i];
for (i = 0; i < (bia->partnum_typelen & 0x3f); i++)
checksum += bia->partnum_data[i];
for (i = 0; i < (bia->fru_fid_typelen & 0x3f); i++)
checksum += bia->fru_fid_data[i];
checksum = -checksum;
checksum &= 0xff;
bia->checksum = checksum;
uint8_t nul = 0;
board_info_area_get_size(&pad);
for (i = 0; i < pad; i++)
ret = fwrite(&nul, 1, sizeof(uint8_t), f);
ret = fwrite(&bia->checksum, 1, sizeof(uint8_t), f);
return 0;
}
int ipmi_dc_load_record_write(int end)
{
int ret;
struct dc_load_list *t;
if (!dcll || !f)
return -1;
t = dcll;
while (t) {
struct multirecord_header head;
head.record_typeid = 0x2; /* DC load type */
head.extra = 0x2;
if (end)
head.extra |= (1 << 7);
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->voltage_required, 1, 1, f);
ret = fwrite(&t->rec->nominal_voltage, 1, 12, f);
t = t->next;
}
return 0;
}
int ipmi_dc_output_record_write(int end)
{
int ret;
struct dc_output_list *t;
if (!dcol || !f)
return -1;
t = dcol;
while (t) {
struct multirecord_header head;
head.record_typeid = 0x1; /* DC output type */
head.extra = 0x2;
if (end)
head.extra |= (1 << 7);
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->output_info, 1, 1, f);
ret = fwrite(&t->rec->nominal_voltage, 1, 12, f);
t = t->next;
}
return 0;
}
int ipmi_oem_record_write(int end)
{
int ret;
struct multirecord_header head;
if (!oem || !f)
return -1;
/* VITA ID: 0x0012a2 (LS Byte first) */
oem->mfg_id0 = 0xa2;
oem->mfg_id1 = 0x12;
oem->mfg_id2 = 0x00;
head.record_typeid = 0xfa; /* OEM record type */
head.extra = 0x2;
if (end)
head.extra |= (1 << 7);
head.record_len = sizeof(struct oem_record);
head.record_checksum = checksum((uint8_t *)oem,
sizeof(struct oem_record));
head.header_checksum = checksum((uint8_t *)&head,
sizeof(struct multirecord_header) - 1);
ret = fwrite(&head, 1, sizeof(struct multirecord_header), f);
ret = fwrite(oem, 1, sizeof(struct oem_record), f);
return 0;
}
int multirecord_area_get_size(int *diff)
{
struct dc_load_list *l1 = dcll;
struct dc_output_list *l2 = dcol;
int sum = 0;
while (l1) {
sum += sizeof(struct multirecord_header);
sum += 13;
l1 = l1->next;
}
while (l2) {
sum += sizeof(struct multirecord_header);
sum += 13;
l2 = l2->next;
}
sum += sizeof(struct multirecord_header) + sizeof(struct oem_record);
if (sum % 8) {
if (diff) {
*diff = 8 - (sum % 8);
}
sum += 8;
sum &= ~7;
}
return sum;
}
int ipmi_write(void)
{
int pad = 0;
int padlen = 0;
ch = malloc(sizeof(struct common_header));
memset(ch, 0, sizeof(struct common_header));
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;
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);
}
// Write Internal Use area, if exists
if (iua)
ipmi_internal_use_area_write();
return 0;
}
void ipmi_set_internal_use_area(struct internal_use_area *d)
{
iua = d;
}
int ipmi_internal_use_area_write(void)
{
if (!iua || !f)
return -1;
fwrite(&iua->format, 1, 1, f);
fwrite(&iua->len, 1, 4, f);
fwrite(iua->data, 1, iua->len, f);
return 0;
}
unsigned char *ipmi_get_internal_use_data(char *data, int *l)
{
unsigned char *buf;
struct common_header *ch = (struct common_header *)data;
unsigned char *d = (unsigned char *)data + ch->internal_use_off*8;
int len = (int)d[1];
buf = malloc(sizeof(uint8_t) * (len + 1));
memcpy(buf, d+5, len);
buf[len] = 0;
*l = len;
return buf;
}
int ipmi_get_mfg_date(char *data)
{
int i;
int ret = 0;
struct common_header *ch = (struct common_header *)data;
unsigned char *date = (unsigned char *)data + ch->board_area_off*8 + 3;
for (i = 0; i < 3; i++)
ret |= (date[i] << (i*8));
return ret;
}
#ifndef IPMI_H
#define IPMI_H
#include <stdint.h>
#include <stdio.h>
/* 8 bytes */
struct common_header {
uint8_t format;
uint8_t internal_use_off;
uint8_t chassis_info_off;
uint8_t board_area_off;
uint8_t product_area_off;
uint8_t multirecord_off;
uint8_t pad;
uint8_t checksum;
};
struct board_info_area {
uint8_t format;
uint8_t area_len;
uint8_t language;
uint8_t mfg_date0;
uint8_t mfg_date1;
uint8_t mfg_date2;
uint8_t mfgr_typelen;
uint8_t *mfgr_data;
uint8_t product_typelen;
uint8_t *product_data;
uint8_t serial_typelen;
uint8_t *serial_data;
uint8_t partnum_typelen;
uint8_t *partnum_data;
uint8_t fru_fid_typelen;
uint8_t *fru_fid_data;
/* uint8_t *custom; */
uint8_t typelen_end;
uint8_t pad_len;
uint8_t checksum;
};
/* 5 bytes */
struct multirecord_header {
uint8_t record_typeid;
uint8_t extra;
uint8_t record_len;
uint8_t record_checksum;
uint8_t header_checksum;
};
struct dc_output_list {
struct dc_output_record *rec;
struct dc_output_list *next;
};
/* 13 bytes */
struct dc_load_record {
uint8_t voltage_required;
uint16_t nominal_voltage;
uint16_t min_voltage;
uint16_t max_voltage;
uint16_t spec_ripple;
uint16_t min_current;
uint16_t max_current;
};
struct dc_load_list {
struct dc_load_record *rec;
struct dc_load_list *next;
};
/* 13 bytes */
struct dc_output_record {
uint8_t output_info;
uint16_t nominal_voltage;
uint16_t max_neg_voltage_dev;
uint16_t max_pos_voltage_dev;
uint16_t ripple;
uint16_t min_current_draw;
uint16_t max_current_draw;
};
struct fmc_oem_data {
uint8_t subtype_version;
uint8_t other;
uint8_t p1_a_nsig;
uint8_t p1_b_nsig;
uint8_t p2_a_nsig;
uint8_t p2_b_nsig;
uint8_t p1_p2_gbt_ntran;
uint8_t max_clock;
};
/* 12 bytes */
struct oem_record {
uint8_t mfg_id0;
uint8_t mfg_id1;
uint8_t mfg_id2;
struct fmc_oem_data data;
};
struct internal_use_area {
uint8_t format;
int len;
char *data;
};
int ipmi_file_open(const char *name);
void ipmi_file_close(void);
int ipmi_write(void);
int ipmi_common_header_write(void);
void ipmi_set_board_info_area(struct board_info_area *);
int ipmi_board_info_area_write(void);
void ipmi_set_internal_use_area(struct internal_use_area *);
int ipmi_internal_use_area_write(void);
void ipmi_add_dc_load_record(struct dc_load_record *);
int ipmi_dc_load_record_write(int);
void ipmi_add_dc_output_record(struct dc_output_record *);
int ipmi_dc_output_record_write(int);
void ipmi_set_oem_record(struct oem_record *);
int ipmi_oem_record_write(int);
unsigned char *ipmi_get_internal_use_data(char *data, int *l);
int ipmi_get_mfg_date(char *data);
#endif
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