- Add support for new PICMG 3.0 R3.0 (March 24, 2008) requirements:

** REQ 3.410: Any IPM Controller may write protect data in the FRU storage
              area and return a "Write protected offset (80h)" Completion
              Code for a command that attempts to change such data.

-- To do so, add a function to build block region in the fru and write
   data using block offset.  If block if found to be write-protected,
   jump over the protected block.

-- Should not change previous behaviour.

-- Have leave the previous functions in comments in reference.

-- Any questions, please send e-mail to jean-michel.audet@ca.kontron.com
This commit is contained in:
Jean-Michel Audet 2008-09-29 17:13:50 +00:00
parent 185b23a5c4
commit c81563434b

View File

@ -56,6 +56,7 @@
* TODO: make this a command line option
*/
#define LIMIT_ALL_REQUEST_SIZE 1
#define FRU_MULTIREC_CHUNK_SIZE (255 + sizeof(struct fru_multirec_header))
static char fileName[512];
@ -74,7 +75,12 @@ static void ipmi_fru_get_adjust_size_from_buffer(uint8_t * pBufArea, uint32_t *p
static void ipmi_fru_picmg_ext_print(uint8_t * fru_data, int off, int length);
static int ipmi_fru_set_field_string(struct ipmi_intf * intf, unsigned
char fruId, uint8_t f_type, uint8_t f_index, char *f_string);
static void
fru_area_print_multirec_bloc(struct ipmi_intf * intf, struct fru_info * fru,
uint8_t id, uint32_t offset);
int
read_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id,
uint32_t offset, uint32_t length, uint8_t *frubuf);
/* get_fru_area_str - Parse FRU area string from raw data
*
@ -179,7 +185,11 @@ get_fru_area_str(uint8_t * data, uint32_t * offset)
return str;
}
/* write_fru_area - write fru data
/* build_fru_bloc - build fru bloc for write protection
*
* @intf: ipmi interface
* @fru_info: information about FRU device
@ -192,6 +202,419 @@ get_fru_area_str(uint8_t * data, uint32_t * offset)
* returns 0 on success
* returns -1 on error
*/
#define FRU_NUM_BLOC_COMMON_HEADER 6
typedef struct ipmi_fru_bloc
{
uint16_t start;
uint16_t size;
uint8_t blocId[32];
}t_ipmi_fru_bloc;
t_ipmi_fru_bloc *
build_fru_bloc(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id,
/* OUT */uint16_t * ptr_number_bloc)
{
t_ipmi_fru_bloc * p_bloc;
struct ipmi_rs * rsp;
struct ipmi_rq req;
struct fru_header header;
uint8_t * fru_data = NULL;
uint8_t msg_data[4];
uint16_t num_bloc;
uint16_t bloc_count;
(* ptr_number_bloc) = 0;
/*memset(&fru, 0, sizeof(struct fru_info));*/
memset(&header, 0, sizeof(struct fru_header));
/*
* get COMMON Header format
*/
msg_data[0] = id;
msg_data[1] = 0;
msg_data[2] = 0;
msg_data[3] = 8;
memset(&req, 0, sizeof(req));
req.msg.netfn = IPMI_NETFN_STORAGE;
req.msg.cmd = GET_FRU_DATA;
req.msg.data = msg_data;
req.msg.data_len = 4;
rsp = intf->sendrecv(intf, &req);
if (rsp == NULL) {
lprintf(LOG_ERR, " Device not present (No Response)\n");
return NULL;
}
if (rsp->ccode > 0) {
lprintf(LOG_ERR," Device not present (%s)\n",
val2str(rsp->ccode, completion_code_vals));
return NULL;
}
if (verbose > 1)
printbuf(rsp->data, rsp->data_len, "FRU DATA");
memcpy(&header, rsp->data + 1, 8);
if (header.version != 1) {
lprintf(LOG_ERR, " Unknown FRU header version 0x%02x",
header.version);
return NULL;
}
/******************************************
Count the number of bloc
*******************************************/
// Common header
num_bloc = 1;
// Internal
if( header.offset.internal )
num_bloc ++;
// Chassis
if( header.offset.chassis )
num_bloc ++;
// Board
if( header.offset.board )
num_bloc ++;
// Product
if( header.offset.product )
num_bloc ++;
// Multi
if( header.offset.multi )
{
uint32_t i;
struct fru_multirec_header * h;
uint32_t last_off, len;
i = last_off = (header.offset.multi*8);
//fru_len = 0;
fru_data = malloc(fru->size + 1);
if (fru_data == NULL) {
lprintf(LOG_ERR, " Out of memory!");
return;
}
memset(fru_data, 0, fru->size + 1);
do {
h = (struct fru_multirec_header *) (fru_data + i);
// read area in (at most) FRU_MULTIREC_CHUNK_SIZE bytes at a time
if ((last_off < (i + sizeof(*h))) || (last_off < (i + h->len)))
{
len = fru->size - last_off;
if (len > FRU_MULTIREC_CHUNK_SIZE)
len = FRU_MULTIREC_CHUNK_SIZE;
if (read_fru_area(intf, fru, id, last_off, len, fru_data) < 0)
break;
last_off += len;
}
num_bloc++;
//printf("Bloc Numb : %i\n", counter);
//printf("Bloc Start: %i\n", i);
//printf("Bloc Size : %i\n", h->len);
//printf("\n");
i += h->len + sizeof (struct fru_multirec_header);
} while (!(h->format & 0x80));
lprintf(LOG_DEBUG ,"Multi-Record area ends at: %i (%xh)",i,i);
if(fru->size > i)
{
// Bloc for remaining space
num_bloc ++;
}
}
else
{
/* Since there is no multi-rec area and no end delimiter, the remaining
space will be added to the last bloc */
}
/******************************************
Malloc and fill up the bloc contents
*******************************************/
p_bloc = malloc( sizeof( t_ipmi_fru_bloc ) * num_bloc );
if(!p_bloc)
{
lprintf(LOG_ERR, " Unable to get memory to build Fru bloc");
return NULL;
}
// Common header
bloc_count = 0;
p_bloc[bloc_count].start= 0;
p_bloc[bloc_count].size = 8;
strcpy(p_bloc[bloc_count].blocId, "Common Header Section");
bloc_count ++;
// Internal
if( header.offset.internal )
{
p_bloc[bloc_count].start = (header.offset.internal * 8);
p_bloc[bloc_count].size = 0; // Will be fillup later
strcpy(p_bloc[bloc_count].blocId, "Internal Use Section");
bloc_count ++;
}
// Chassis
if( header.offset.chassis )
{
p_bloc[bloc_count].start = (header.offset.chassis * 8);
p_bloc[bloc_count].size = 0; // Will be fillup later
strcpy(p_bloc[bloc_count].blocId, "Chassis Section");
bloc_count ++;
}
// Board
if( header.offset.board )
{
p_bloc[bloc_count].start = (header.offset.board * 8);
p_bloc[bloc_count].size = 0; // Will be fillup later
strcpy(p_bloc[bloc_count].blocId, "Board Section");
bloc_count ++;
}
// Product
if( header.offset.product )
{
p_bloc[bloc_count].start = (header.offset.product * 8);
p_bloc[bloc_count].size = 0; // Will be fillup later
strcpy(p_bloc[bloc_count].blocId, "Product Section");
bloc_count ++;
}
// Multi-Record Area
if(
( header.offset.multi )
&&
( fru_data )
)
{
uint32_t i = (header.offset.multi*8);
struct fru_multirec_header * h;
do {
h = (struct fru_multirec_header *) (fru_data + i);
p_bloc[bloc_count].start = i;
p_bloc[bloc_count].size = h->len + sizeof (struct fru_multirec_header);
sprintf(p_bloc[bloc_count].blocId, "Multi-Rec Aread: Type %i", h->type);
bloc_count ++;
/*printf("Bloc Start: %i\n", i);
printf("Bloc Size : %i\n", h->len);
printf("\n");*/
i += h->len + sizeof (struct fru_multirec_header);
} while (!(h->format & 0x80));
lprintf(LOG_DEBUG ,"Multi-Record area ends at: %i (%xh)",i,i);
/* If last bloc size was defined and is not until the end, create a
last bloc with the remaining unused space */
if(fru->size > i)
{
// Bloc for remaining space
p_bloc[bloc_count].start = i;
p_bloc[bloc_count].size = (fru->size - i);
sprintf(p_bloc[bloc_count].blocId, "Unused space");
bloc_count ++;
}
free(fru_data);
}
/* Fill up size for first bloc */
{
unsigned short counter;
lprintf(LOG_DEBUG ,"\nNumber Bloc : %i\n", num_bloc);
for(counter = 0; counter < (num_bloc); counter ++)
{
/* If size where not initialized, do it. */
if( p_bloc[counter].size == 0)
{
/* If not the last bloc, use the next bloc to determine the end */
if((counter+1) < num_bloc)
{
p_bloc[counter].size = (p_bloc[counter+1].start - p_bloc[counter].start);
}
else
{
p_bloc[counter].size = (fru->size - p_bloc[counter].start);
}
}
lprintf(LOG_DEBUG ,"Bloc Numb : %i\n", counter);
lprintf(LOG_DEBUG ,"Bloc Id : %s\n", p_bloc[counter].blocId);
lprintf(LOG_DEBUG ,"Bloc Start: %i\n", p_bloc[counter].start);
lprintf(LOG_DEBUG ,"Bloc Size : %i\n", p_bloc[counter].size);
lprintf(LOG_DEBUG ,"\n");
}
}
(* ptr_number_bloc) = num_bloc;
return p_bloc;
}
int
write_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id,
uint16_t soffset, uint16_t doffset,
uint16_t length, uint8_t *pFrubuf)
{ /*
// fill in frubuf[offset:length] from the FRU[offset:length]
// rc=1 on success
*/
static uint16_t fru_data_rqst_size = 32;
uint16_t off=0, tmp, finish;
struct ipmi_rs * rsp;
struct ipmi_rq req;
uint8_t msg_data[25];
uint8_t writeLength;
uint16_t num_bloc;
finish = doffset + length; /* destination offset */
if (finish > fru->size)
{
lprintf(LOG_ERROR, "Return error\n");
return -1;
}
t_ipmi_fru_bloc * fru_bloc = build_fru_bloc(intf, fru, id, &num_bloc);
memset(&req, 0, sizeof(req));
req.msg.netfn = IPMI_NETFN_STORAGE;
req.msg.cmd = SET_FRU_DATA;
req.msg.data = msg_data;
#ifdef LIMIT_ALL_REQUEST_SIZE
if (fru_data_rqst_size > 16)
#else
if (fru->access && fru_data_rqst_size > 16)
#endif
fru_data_rqst_size = 16;
do {
/* Temp init end_bloc to the end, if not found */
uint16_t end_bloc = finish;
uint8_t protected_bloc = 0;
uint16_t found_bloc = 0xffff;
/* real destination offset */
tmp = fru->access ? (doffset+off) >> 1 : (doffset+off);
msg_data[0] = id;
msg_data[1] = (uint8_t)tmp;
msg_data[2] = (uint8_t)(tmp >> 8);
/* Write per bloc, try to find the end of a bloc*/
{
uint16_t counter;
for(counter = 0; counter < (num_bloc); counter ++)
{
if(
(tmp >= fru_bloc[counter].start)
&&
(tmp < (fru_bloc[counter].start + fru_bloc[counter].size))
)
{
found_bloc = counter;
end_bloc = (fru_bloc[counter].start + fru_bloc[counter].size);
counter = num_bloc;
}
}
}
tmp = end_bloc - (doffset+off); /* bytes remaining for the bloc */
if (tmp > 16) {
memcpy(&msg_data[3], pFrubuf + soffset + off, 16);
req.msg.data_len = 16 + 3;
}
else {
memcpy(&msg_data[3], pFrubuf + soffset + off, (uint8_t)tmp);
req.msg.data_len = tmp + 3;
}
if(found_bloc == 0)
{
lprintf(LOG_INFO,"Writing %d bytes", (req.msg.data_len-3));
}
else
{
lprintf(LOG_INFO,"Writing %d bytes (Bloc #%i: %s)",
(req.msg.data_len-3),
found_bloc, fru_bloc[found_bloc].blocId);
}
writeLength = req.msg.data_len-3;
rsp = intf->sendrecv(intf, &req);
if (!rsp) {
break;
}
if(rsp->ccode==0x80) // Write protected section
{
protected_bloc = 1;
}
else if ((rsp->ccode==0xc7 || rsp->ccode==0xc8 || rsp->ccode==0xca ) &&
--fru_data_rqst_size > 8) {
lprintf(LOG_NOTICE,"Bad CC -> %x\n", rsp->ccode);
break; /*continue;*/
}
else if (rsp->ccode > 0)
break;
if(protected_bloc == 0)
{
off += writeLength; // Write OK, bloc not protected, continue
}
else
{
if(found_bloc != 0xffff)
{
// Bloc protected, advise user and jump over protected bloc
lprintf(LOG_INFO,"Bloc [%s] protected at offset: %i (size %i bytes)",
fru_bloc[found_bloc].blocId,
fru_bloc[found_bloc].start,
fru_bloc[found_bloc].size);
lprintf(LOG_INFO,"Jumping over this bloc");
}
else
{
lprintf(LOG_INFO,"Remaining FRU is protected following offset: %i",
off);
}
off = end_bloc;
}
} while ((doffset+off) < finish);
free(fru_bloc);
return ((doffset+off) >= finish);
}
#if 0
int
write_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id,
uint16_t soffset, uint16_t doffset,
@ -262,6 +685,7 @@ write_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id,
return ((doffset+off) >= finish);
}
#endif
/* read_fru_area - fill in frubuf[offset:length] from the FRU[offset:length]
*
@ -455,6 +879,68 @@ read_fru_area_section(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id,
return 0;
}
static void
fru_area_print_multirec_bloc(struct ipmi_intf * intf, struct fru_info * fru,
uint8_t id, uint32_t offset)
{
uint8_t * fru_data;
uint32_t fru_len, i;
struct fru_multirec_header * h;
uint32_t last_off, len;
i = last_off = offset;
fru_len = 0;
fru_data = malloc(fru->size + 1);
if (fru_data == NULL) {
lprintf(LOG_ERR, " Out of memory!");
return;
}
memset(fru_data, 0, fru->size + 1);
do {
h = (struct fru_multirec_header *) (fru_data + i);
// read area in (at most) FRU_MULTIREC_CHUNK_SIZE bytes at a time
if ((last_off < (i + sizeof(*h))) || (last_off < (i + h->len)))
{
len = fru->size - last_off;
if (len > FRU_MULTIREC_CHUNK_SIZE)
len = FRU_MULTIREC_CHUNK_SIZE;
if (read_fru_area(intf, fru, id, last_off, len, fru_data) < 0)
break;
last_off += len;
}
//printf("Bloc Numb : %i\n", counter);
printf("Bloc Start: %i\n", i);
printf("Bloc Size : %i\n", h->len);
printf("\n");
i += h->len + sizeof (struct fru_multirec_header);
} while (!(h->format & 0x80));
i = offset;
do {
h = (struct fru_multirec_header *) (fru_data + i);
printf("Bloc Start: %i\n", i);
printf("Bloc Size : %i\n", h->len);
printf("\n");
i += h->len + sizeof (struct fru_multirec_header);
} while (!(h->format & 0x80));
lprintf(LOG_DEBUG ,"Multi-Record area ends at: %i (%xh)",i,i);
free(fru_data);
}
/* fru_area_print_chassis - Print FRU Chassis Area
*
* @intf: ipmi interface
@ -726,7 +1212,7 @@ fru_area_print_product(struct ipmi_intf * intf, struct fru_info * fru,
free(fru_data);
}
#define FRU_MULTIREC_CHUNK_SIZE (255 + sizeof(struct fru_multirec_header))
/* fru_area_print_multirec - Print FRU Multi Record Area
*