common/recipes-lib/pldm/files/pldm.c (788 lines of code) (raw):
/*
*
* Copyright 2018-present Facebook. All Rights Reserved.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <string.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>
#include <stddef.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <openbmc/ncsi.h>
#include "pldm_base.h"
#include "pldm_fw_update.h"
#include "pldm.h"
#include "pldm_pmc.h"
#define DEBUG_PLDM
#ifdef DEBUG_PLDM
#define DBG_PRINT(fmt, args...) printf(fmt, ##args)
#else
#define DBG_PRINT(fmt, args...)
#endif
// global PLDM control & configuration variables
static uint8_t gPldm_iid = 0; // technically only 5 bits as per DSP0240 v1.0.0
static uint32_t gPldm_transfer_size = PLDM_MAX_XFER_SIZE;
static uint8_t gPldm_max_transfer_cnt = PLDM_MAX_XFER_CNT;
static uint64_t gPldm_supported_types = 0;
/* PLDM Firmware Update command name*/
const char *pldm_fw_cmd_string[NUM_PLDM_UPDATE_CDMS] = {
"CMD_REQUEST_UPDATE",
"CMD_GET_PACKAGE_DATA",
"CMD_GET_DEVICE_METADATA",
"CMD_PASS_COMPONENT_TABLE",
"CMD_UPDATE_COMPONENT",
"CMD_REQUEST_FIRMWARE_DATA",
"CMD_TRANSFER_COMPLETE",
"CMD_VERIFY_COMPLETE",
"CMD_APPLY_COMPLETE",
"CMD_GET_META_DATA",
"CMD_ACTIVATE_FIRMWARE",
"CMD_GET_STATUS",
"CMD_CANCEL_UPDATE_COMPONENT",
"CMD_CANCEL_UPDATE",
};
/* PLDM base completion code */
const char *pldm_base_cc_string[6] = {
"SUCCESS",
"ERROR",
"ERROR_INVALID_DATA",
"ERROR_INVALID_LENGTH",
"ERROR_NOT_READY",
"ERROR_UNSUPPORTED_PLDM_CMD",
};
/* PLDM Firmware completion code*/
const char *pldm_update_cmd_cc_string[NUM_FW_UPDATE_CC] = {
"NOT_IN_UPDATE_MODE",
"ALREADY_IN_UPDATE_MODE",
"DATA_OUT_OF_RANGE",
"INVALID_TRANSFER_LENTH",
"INVALID_STATE_FOR_CMD",
"INCOMPLETE_UPDATE",
"BUSY_IN_BACKGROUND",
"CANCEL_PENDING",
"COMMAND_NOT_EXPECTED",
"RETRY_REQUEST_FW_DATA",
"UNABLE_TO_INITIATE_UPDATE",
"ACTIVATION_NOT_REQUIRED",
"SELF_CONTAINED_ACTIVATION_NOT_PEMITTED",
"NO_DEVICE_METADATA",
"RETRY_REQUEST_UPDATE",
"NO_PACKAGE_DATA",
"INVALID_DATA_TRANSFER_HANDLE",
"INVALID_TRANSFER_OPERATION_FLAG",
};
/* PLDM component class name*/
const char *pldm_comp_class_name[NUM_COMP_CLASS] = {
"C_CLASS_UNKNOWN",
"C_CLASS_OTHER",
"C_CLASS_DRIVER",
"C_CLASS_CONFIG_SW",
"C_CLASS_APPLICATION_SW",
"C_CLASS_INSTRUMENTATION",
"C_CLASS_FIRMWARE_BIOS",
"C_CLASS_DIAG_SW",
"C_CLASS_OS",
"C_CLASS_MIDDLEWARE",
"C_CLASS_FIRMWARE",
"C_CLASS_BIOS_FCODE",
"C_CLASS_SUPPORT_SERVICE_PACK",
"C_CLASS_SOFTWARE_BUNDLE",
};
/* PLDM firmware string type*/
const char *pldm_str_type_name[NUM_PLDM_FW_STR_TYPES] = {
"UNKNOWN",
"ASCII",
"UTF8",
"UTF16",
"UTF16LE",
"UTF16BE",
};
// UUID signature specifying package supports PLDM FW Update
const char PLDM_FW_UUID[PLDM_FW_UUID_LEN] = {
0xf0, 0x18, 0x87, 0x8c, 0xcb, 0x7d, 0x49, 0x43,
0x98, 0x00, 0xa0, 0x2f, 0x05, 0x9a, 0xca, 0x02
};
const char *
pldm_str_type_to_name(fwStrType strType)
{
if (strType < 0 || strType >= NUM_PLDM_FW_STR_TYPES ||
pldm_str_type_name[strType] == NULL) {
return "unknown_str_type";
}
return pldm_str_type_name[strType];
}
const char *
pldm_fw_cmd_to_name(PldmFWCmds cmd)
{
if (cmd < CMD_REQUEST_UPDATE ||
cmd > CMD_CANCEL_UPDATE ||
pldm_fw_cmd_string[cmd - CMD_REQUEST_UPDATE] == NULL) {
return "unknown_pldm_fw_cmd";
}
return pldm_fw_cmd_string[cmd - CMD_REQUEST_UPDATE];
}
const char *
pldm_fw_cmd_cc_to_name(uint8_t comp_code)
{
if ((comp_code <= CC_ERR_UNSUPPORTED_PLDM_CMD) &&
pldm_base_cc_string[comp_code] != NULL) {
return pldm_base_cc_string[comp_code];
} else if (comp_code == CC_INVALID_PLDM_TYPE) {
return "ERROR_INVALID_PLDM_TYPE";
} else if (comp_code < CC_FW_UPDATE_BASE ||
comp_code > CC_INVALID_TRANSFER_OPERATION_FLAG ||
pldm_update_cmd_cc_string[comp_code - CC_FW_UPDATE_BASE] == NULL) {
return "unknown_completion code";
}
return pldm_update_cmd_cc_string[comp_code - CC_FW_UPDATE_BASE];
}
const char *
pldm_comp_class_to_name(PldmComponentClass compClass)
{
if (compClass < 0 || compClass >= NUM_COMP_CLASS ||
pldm_comp_class_name[compClass] == NULL) {
return "unknown_classification";
}
return pldm_comp_class_name[compClass];
}
void dbgPrintCdb(pldm_cmd_req *cdb)
{
int i = 0;
DBG_PRINT("%s\n common: ", __FUNCTION__);
for (i=0; i<3; ++i)
DBG_PRINT("%02x", cdb->common[i]);
DBG_PRINT("\n payload: ");
for (i=0; i<cdb->payload_size; ++i) {
DBG_PRINT("%02x", cdb->payload[i]);
if ((i%4 == 3) && (i%15 != 0))
DBG_PRINT(" ");
if (i%16 == 15)
DBG_PRINT("\n ");
}
DBG_PRINT("\n");
DBG_PRINT("\n iid=%d, cmd=0x%x (%s)\n\n",
(cdb->common[PLDM_IID_OFFSET]) & 0x1f,
cdb->common[PLDM_CMD_OFFSET],
pldm_fw_cmd_to_name(cdb->common[PLDM_CMD_OFFSET]));
}
void genReqCommonFields(PldmType type, PldmFWCmds cmd, uint8_t *common)
{
uint8_t iid = 0;
gPldm_iid = (gPldm_iid + 1) & 0x1f;;
iid = gPldm_iid;
common[PLDM_IID_OFFSET] = 0x80 | (iid & 0x1f);
common[PLDM_TYPE_OFFSET] = type;
common[PLDM_CMD_OFFSET] = cmd;
}
// Given a PLDM package pointed by pFwPkgHdr
// Generate command cdb for PLDM RequestUpdate command (Type 0x05, cmd 0x10)
void pldmCreateReqUpdateCmd(pldm_fw_pkg_hdr_t *pFwPkgHdr, pldm_cmd_req *pPldmCdb,
int pldm_bufsize)
{
PLDM_RequestUpdate_t *pCmdPayload = (PLDM_RequestUpdate_t *)&(pPldmCdb->payload);
printf("\n\nCMD_REQUEST_UPDATE\n");
genReqCommonFields(PLDM_TYPE_FIRMWARE_UPDATE, CMD_REQUEST_UPDATE, &(pPldmCdb->common[0]));
if ((pldm_bufsize == 0) || (pldm_bufsize > gPldm_transfer_size)) {
pldm_bufsize = gPldm_transfer_size;
}
printf(" Request_update buffer size=%d\n", pldm_bufsize);
pCmdPayload->maxTransferSize = pldm_bufsize;
pCmdPayload->numComponents = pFwPkgHdr->componentImageCnt;
pCmdPayload->maxOutstandingTransferRequests = gPldm_max_transfer_cnt;
pCmdPayload->packageDataLength =
pFwPkgHdr->pDevIdRecs[0]->pRecords->fwDevPkgDataLength;
pCmdPayload->componentImageSetVersionStringType =
pFwPkgHdr->pDevIdRecs[0]->pRecords->compImgSetVersionStringType;
pCmdPayload->componentImageSetVersionStringLength =
pFwPkgHdr->pDevIdRecs[0]->pRecords->compImgSetVersionStringLength;
if (pCmdPayload->componentImageSetVersionStringLength > MAX_VERSION_STRING_LEN)
{
pCmdPayload->componentImageSetVersionStringLength = MAX_VERSION_STRING_LEN;
syslog(LOG_ERR, "%s version length(%d) exceeds max (%d)",
__FUNCTION__, pCmdPayload->componentImageSetVersionStringLength,
MAX_VERSION_STRING_LEN);
}
memcpy(pCmdPayload->componentImageSetVersionString,
pFwPkgHdr->pDevIdRecs[0]->versionString,
pCmdPayload->componentImageSetVersionStringLength);
pPldmCdb->payload_size = PLDM_COMMON_REQ_LEN +
offsetof(PLDM_RequestUpdate_t, componentImageSetVersionString) +
pCmdPayload->componentImageSetVersionStringLength;
dbgPrintCdb(pPldmCdb);
return;
}
// Given:
// 1. a PLDM package pointed by pFwPkgHdr
// 2. component index within PLDM package
// Generate command cdb for PLDM Pass Component Table command (Type 0x05, cmd 0x11)
void pldmCreatePassComponentTblCmd(pldm_fw_pkg_hdr_t *pFwPkgHdr, uint8_t compIdx,
pldm_cmd_req *pPldmCdb)
{
PLDM_PassComponentTable_t *pCmdPayload =
(PLDM_PassComponentTable_t *)&(pPldmCdb->payload);
pldm_component_img_info_t *pCompImgInfo = pFwPkgHdr->pCompImgInfo[compIdx];
printf("\n\nCMD_PASS_COMPONENT_TABLE\n");
genReqCommonFields(PLDM_TYPE_FIRMWARE_UPDATE, CMD_PASS_COMPONENT_TABLE, &(pPldmCdb->common[0]));
pCmdPayload->transferFlag = TFLAG_STATRT_END; // only support 1 pass for now
pCmdPayload->_class = pCompImgInfo->_class;
pCmdPayload->id = pCompImgInfo->id;
pCmdPayload->classIndex = 0; // TBD - needs to query for this from NIC
pCmdPayload->compStamp = pCompImgInfo->compStamp;
pCmdPayload->versionStringType = pCompImgInfo->versionStringType;
pCmdPayload->versionStringLength = pCompImgInfo->versionStringLength;
if (pCmdPayload->versionStringLength > MAX_VERSION_STRING_LEN) {
pCmdPayload->versionStringLength = MAX_VERSION_STRING_LEN;
syslog(LOG_ERR, "%s version length(%d) exceeds max (%d)",
__FUNCTION__, pCmdPayload->versionStringLength, MAX_VERSION_STRING_LEN);
}
memcpy(pCmdPayload->versionString, pCompImgInfo->versionString,
pCmdPayload->versionStringLength);
pPldmCdb->payload_size = PLDM_COMMON_REQ_LEN +
offsetof(PLDM_PassComponentTable_t, versionString) +
pCmdPayload->versionStringLength;
dbgPrintCdb(pPldmCdb);
}
void pldmCreateUpdateComponentCmd(pldm_fw_pkg_hdr_t *pFwPkgHdr, uint8_t compIdx,
pldm_cmd_req *pPldmCdb)
{
PLDM_UpdateComponent_t *pCmdPayload =
(PLDM_UpdateComponent_t *)&(pPldmCdb->payload);
pldm_component_img_info_t *pCompImgInfo = pFwPkgHdr->pCompImgInfo[compIdx];
printf("\n\nCMD_UPDATE_COMPONENT\n");
genReqCommonFields(PLDM_TYPE_FIRMWARE_UPDATE, CMD_UPDATE_COMPONENT, &(pPldmCdb->common[0]));
pCmdPayload->_class = pCompImgInfo->_class;
pCmdPayload->id = pCompImgInfo->id;
pCmdPayload->classIndex = 0; // TBD - needs to query for this from NIC
pCmdPayload->compStamp = pCompImgInfo->compStamp;
pCmdPayload->compSize = pCompImgInfo->size;
pCmdPayload->updateOptions = pCompImgInfo->options;
pCmdPayload->versionStringType = pCompImgInfo->versionStringType;
pCmdPayload->versionStringLength = pCompImgInfo->versionStringLength;
if (pCmdPayload->versionStringLength > MAX_VERSION_STRING_LEN) {
pCmdPayload->versionStringLength = MAX_VERSION_STRING_LEN;
syslog(LOG_ERR, "%s version length(%d) exceeds max (%d)",
__FUNCTION__, pCmdPayload->versionStringLength, MAX_VERSION_STRING_LEN);
}
memcpy(pCmdPayload->versionString, pCompImgInfo->versionString,
pCmdPayload->versionStringLength);
pPldmCdb->payload_size = PLDM_COMMON_REQ_LEN +
offsetof(PLDM_UpdateComponent_t, versionString) +
pCmdPayload->versionStringLength;
dbgPrintCdb(pPldmCdb);
}
void pldmCreateActivateFirmwareCmd(pldm_cmd_req *pPldmCdb)
{
PLDM_ActivateFirmware_t *pCmdPayload =
(PLDM_ActivateFirmware_t *)&(pPldmCdb->payload);
printf("\n\nCMD_ACTIVATE_FIRMWARE\n");
genReqCommonFields(PLDM_TYPE_FIRMWARE_UPDATE, CMD_ACTIVATE_FIRMWARE, &(pPldmCdb->common[0]));
pCmdPayload->selfContainedActivationRequest = 0; // no self activation
pPldmCdb->payload_size = PLDM_COMMON_REQ_LEN +
sizeof(PLDM_ActivateFirmware_t);
}
void pldmCreateGetStatusCmd(pldm_cmd_req *pPldmCdb)
{
printf("\n\nCMD_GET_STATUS\n");
genReqCommonFields(PLDM_TYPE_FIRMWARE_UPDATE, CMD_GET_STATUS, &(pPldmCdb->common[0]));
pPldmCdb->payload_size = PLDM_COMMON_REQ_LEN;
}
void pldmCreateCancelUpdateCmd(pldm_cmd_req *pPldmCdb)
{
printf("\n\nCMD_CANCEL_UPDATE\n");
genReqCommonFields(PLDM_TYPE_FIRMWARE_UPDATE, CMD_CANCEL_UPDATE, &(pPldmCdb->common[0]));
pPldmCdb->payload_size = PLDM_COMMON_REQ_LEN;
}
// helper function to check if a given UUID is a PLDM FW Package
int isPldmFwUuid(char *uuid)
{
return (!strncmp(uuid, PLDM_FW_UUID, PLDM_FW_UUID_LEN));
}
void printHdrInfo(pldm_fw_pkg_hdr_info_t *hdr_info, int printVersion)
{
int i;
printf("\n\nPLDM Firmware Package Header Info\n");
printf(" UUID: ");
for (i=0; i<16; ++i)
printf("%x", hdr_info->uuid[i]);
printf(" PLDM UUID Y/N? (%C)\n", isPldmFwUuid((char *)(hdr_info->uuid))? 'Y':'N');
printf(" headerRevision: 0x%x\n", hdr_info->headerRevision);
printf(" headerSize: 0x%x\n", hdr_info->headerSize);
printf(" componentBitmapBitLength: 0x%x\n", hdr_info->componentBitmapBitLength);
printf(" versionStringType: 0x%x (%s)\n", hdr_info->versionStringType,
pldm_str_type_to_name(hdr_info->versionStringType));
printf(" versionStringLength: 0x%x\n", hdr_info->versionStringLength);
if (printVersion) {
printf(" version string: ");
for (i=0; i<hdr_info->versionStringLength; ++i)
printf("%c", hdr_info->versionString[i]);
printf(" (");
for (i=0; i<hdr_info->versionStringLength; ++i)
printf("0x%x ", hdr_info->versionString[i]);
printf(")\n");
}
}
void printDevIdRec(pldm_fw_pkg_hdr_t *pFwPkgHdr, int index)
{
int i;
uint8_t compBitFieldLen = pFwPkgHdr->phdrInfo->componentBitmapBitLength/8;
pldm_fw_dev_id_records_t *pDevRc = pFwPkgHdr->pDevIdRecs[index];
uint8_t versionLen = pDevRc->pRecords->compImgSetVersionStringLength;
printf("\n\nDevice ID[%d] Record Info\n", index);
printf(" devRec[%d].RecordLength: %d\n", index,
pDevRc->pRecords->recordLength);
printf(" devRec[%d].descriptorCnt: 0x%x\n", index,
pDevRc->pRecords->descriptorCnt);
printf(" devRec[%d].deviceUpdateOptionFlags: 0x%x\n", index,
pDevRc->pRecords->devUpdateOptFlags);
printf(" devRec[%d].compImgSetVersionStringType: 0x%x (%s)\n", index,
pDevRc->pRecords->compImgSetVersionStringType,
pldm_str_type_to_name(pDevRc->pRecords->compImgSetVersionStringType));
printf(" devRec[%d].compImgSetVersionStringLength: 0x%x\n", index,
pDevRc->pRecords->compImgSetVersionStringLength);
printf(" devRec[%d].fwDevPkgDataLength: 0x%x\n", index,
pDevRc->pRecords->fwDevPkgDataLength);
printf(" devRec[%d].applicableComponents: ", index);
for (i=0; i<compBitFieldLen; ++i)
printf("%d\n", pDevRc->pApplicableComponents[i]);
printf(" devRec[%d].versionString: ", index);
for (i=0; i<versionLen; ++i)
printf("%c", pDevRc->versionString[i]);
printf(" (");
for (i=0; i<versionLen; ++i)
printf("0x%x ", pDevRc->versionString[i]);
printf(")\n");
// print each record descriptors
printf(" Record Descriptors\n");
for (i=0; i<pDevRc->pRecords->descriptorCnt; ++i) {
printf(" Record Descriptor[%d]\n", i);
printf(" Type=0x%x", pDevRc->pRecordDes[i]->type);
printf(" length=0x%x", pDevRc->pRecordDes[i]->length);
printf(" data=[");
for (int j=0; j<pDevRc->pRecordDes[i]->length; ++j) {
printf("%x",pDevRc->pRecordDes[i]->data[j]);
}
printf("]\n");
}
}
void printComponentImgInfo(pldm_fw_pkg_hdr_t *pFwPkgHdr, int index)
{
int i;
pldm_component_img_info_t *pComp = pFwPkgHdr->pCompImgInfo[index];
printf("\n\nComponent[%d] Info\n", index);
printf(" Component[%d].classification: %d (%s)\n", index,
pComp->_class, pldm_comp_class_to_name(pComp->_class));
printf(" Component[%d].identifier: 0x%x\n", index,
pComp->id);
printf(" Component[%d].comparison stamp: 0x%x\n", index,
pComp->compStamp);
printf(" Component[%d].component options: 0x%x\n", index,
pComp->options);
printf(" Component[%d].request update activation method: 0x%x\n", index,
pComp->requestedActivationMethod);
printf(" Component[%d].location offset: 0x%x\n", index,
pComp->locationOffset);
printf(" Component[%d].component size: 0x%x\n", index,
pComp->size);
printf(" Component[%d].versionStringType: 0x%x (%s)\n", index,
pComp->versionStringType,
pldm_str_type_to_name(pComp->versionStringType));
printf(" Component[%d].versionStringLength: 0x%x\n", index,
pComp->versionStringLength);
printf(" Component[%d].versionString: ", index);
for (i=0; i<pComp->versionStringLength; ++i)
printf("%c", pComp->versionString[i]);
printf(" (");
for (i=0; i<pComp->versionStringLength; ++i)
printf("0x%x ", pComp->versionString[i]);
printf(")\n");
}
// Given a PLDM Firmware package, this function will
// 1. allocate a pldm_fw_pkg_hdr_t structure representing this package,
// 2. read PLDM firmware package to RAM
// 3. initialize header info area of pldm_fw_pkg_hdr_t
// 4. returns
// 1. pointer to the struct,
// 2. number of bytes consumed in the header info area
//
int
init_pkg_hdr_info(char *path, pldm_fw_pkg_hdr_t** pFwPkgHdr, int *pOffset)
{
FILE *fp;
int rcnt, size;
struct stat buf;
// Open the file exclusively for read
fp = fopen(path, "r");
if (!fp) {
printf("ERROR: invalid file path :%s!\n", path);
return -1;
}
stat(path, &buf);
size = buf.st_size;
printf("size of file is %d bytes\n", size);
// allocate pointer structure to access fw pkg header
*pFwPkgHdr = (pldm_fw_pkg_hdr_t *)calloc(1, sizeof(pldm_fw_pkg_hdr_t));
if (!(*pFwPkgHdr)) {
printf("ERROR: pFwPkgHdr malloc failed, size %zu\n", sizeof(pldm_fw_pkg_hdr_t));
fclose(fp);
return -1;
}
// allocate actual buffer to store fw package header
(*pFwPkgHdr)->rawHdrBuf = (unsigned char *)calloc(1, size);
if (!(*pFwPkgHdr)->rawHdrBuf) {
printf("ERROR: rawHdrBuf malloc failed, size %d\n", size);
fclose(fp);
return -1;
}
// Move the file pointer to the start and read the entire package header
fseek(fp, 0, SEEK_SET);
rcnt = fread((*pFwPkgHdr)->rawHdrBuf, 1, size, fp);
if (rcnt < size) {
printf("ERROR: rawHdrBuf read, rcnt(%d) < %d", rcnt,
size);
fclose(fp);
return -1;
}
fclose(fp);
(*pFwPkgHdr)->phdrInfo = (pldm_fw_pkg_hdr_info_t *)(*pFwPkgHdr)->rawHdrBuf;
printHdrInfo((*pFwPkgHdr)->phdrInfo, 1);
*pOffset += offsetof(pldm_fw_pkg_hdr_info_t, versionString) +
(*pFwPkgHdr)->phdrInfo->versionStringLength;
if (!isPldmFwUuid((char *)((*pFwPkgHdr)->phdrInfo->uuid))) {
printf("Not a valid PLDM package, exiting\n");
return -1;
}
return 0;
}
// initialize "firmware device area" of pldm_fw_pkg_hdr_t structure,
// i.e. devIdRecordCnt
// allocate array of device records and initialize corresponding pointers
//
// input: pFwPkgHdr with header area preinitialized
// pOffset : pointer to starting offset of "firmware device area"
// of the package
// output: pFwPkgHdr with firmware device area initialized
// pOffset: updated to pointing to starting offset of Component area
//
int
init_device_id_records(pldm_fw_pkg_hdr_t* pFwPkgHdr, int *pOffset)
{
int i;
pFwPkgHdr->devIdRecordCnt = pFwPkgHdr->rawHdrBuf[*pOffset];
*pOffset += sizeof(pFwPkgHdr->devIdRecordCnt);
DBG_PRINT("\n\n Number of Device ID Record in package (devIdRecordCnt) =%d\n",
pFwPkgHdr->devIdRecordCnt);
// allocate a look up table of pointers to each devIdRecord
pFwPkgHdr->pDevIdRecs =
(pldm_fw_dev_id_records_t **) calloc(pFwPkgHdr->devIdRecordCnt,
sizeof(pldm_fw_dev_id_records_t *));
if (!pFwPkgHdr->pDevIdRecs)
{
printf("ERROR: pFwPkgHdr->pDevIdRecs malloc failed, size %zu\n",
sizeof(pldm_fw_dev_id_records_t *) * pFwPkgHdr->devIdRecordCnt);
return -1;
}
for (i=0; i<pFwPkgHdr->devIdRecordCnt; ++i)
{
// pointer to current DeviceRecord we're working on
pFwPkgHdr->pDevIdRecs[i] = calloc(1, sizeof(pldm_fw_dev_id_records_t));
if (!(pFwPkgHdr->pDevIdRecs[i])) {
printf("ERROR: pFwPkgHdr->pDevIdRecs[%d] malloc failed, size %zu\n", i,
sizeof(pldm_fw_dev_id_records_t));
return -1;
}
pldm_fw_dev_id_records_t *pDevIdRec = pFwPkgHdr->pDevIdRecs[i];
// "offset" will be updated to track sizeof(current deviceRecord)
// "suboffset" is used to initialize subfields within current deviceRecord
// that are variable length
int subOffset = *pOffset;
pDevIdRec->pRecords = (pldm_fw_dev_id_records_fixed_len_t *)(pFwPkgHdr->rawHdrBuf + subOffset);
subOffset += sizeof(pldm_fw_dev_id_records_fixed_len_t);
pDevIdRec->pApplicableComponents = (uint8_t *)(pFwPkgHdr->rawHdrBuf + subOffset);
// length of pApplicableComponents field is defined in hdr info,
subOffset += (pFwPkgHdr->phdrInfo->componentBitmapBitLength/8);
pDevIdRec->versionString = (uint8_t *)(pFwPkgHdr->rawHdrBuf + subOffset);
subOffset += pDevIdRec->pRecords->compImgSetVersionStringLength;
// allocate a look up table of pointers to each Record Descriptor
pDevIdRec->pRecordDes =
(record_descriptors_t **) calloc(pDevIdRec->pRecords->descriptorCnt,
sizeof(record_descriptors_t *));
if (!pDevIdRec->pRecordDes)
{
printf("ERROR: pDevIdRec->pRecordDes malloc failed, size %zu\n",
sizeof(record_descriptors_t *) * pDevIdRec->pRecords->descriptorCnt);
return -1;
}
for (int j=0; j<pDevIdRec->pRecords->descriptorCnt; ++j)
{
pDevIdRec->pRecordDes[j] = (record_descriptors_t *)(pFwPkgHdr->rawHdrBuf + subOffset);
subOffset += (offsetof(record_descriptors_t, data) + pDevIdRec->pRecordDes[j]->length);
}
DBG_PRINT("DevRec[%d].length=%d, processed=%d\n", i,
pDevIdRec->pRecords->recordLength,
subOffset-*pOffset);
printDevIdRec(pFwPkgHdr, i);
// update offset for next deviceRecord
*pOffset += pDevIdRec->pRecords->recordLength;
}
return 0;
}
// initialize "Component Image area" of pldm_fw_pkg_hdr_t structure,
// i.e. componentImageCnt
// allocate array of pointers to each Component Image
//
// input: pFwPkgHdr with header and firmware device ID area preinitialized
// pOffset : pointer to starting offset of "Component Image area"
// of the package
// output: pFwPkgHdr with component image area initialized
// pOffset: updated to pointing to starting offset of header checksum
//
int
init_component_img_info(pldm_fw_pkg_hdr_t* pFwPkgHdr, int *pOffset)
{
int i;
pFwPkgHdr->componentImageCnt = pFwPkgHdr->rawHdrBuf[*pOffset];
*pOffset += sizeof(pFwPkgHdr->componentImageCnt);
DBG_PRINT("\n\n Number of Component in package (componentImageCnt) =%d\n",
pFwPkgHdr->componentImageCnt);
// allocate a look up table of pointers to each component image
pFwPkgHdr->pCompImgInfo =
(pldm_component_img_info_t **) calloc(pFwPkgHdr->componentImageCnt,
sizeof(pldm_component_img_info_t *));
if (!pFwPkgHdr->pCompImgInfo)
{
printf("ERROR: pFwPkgHdr->pCompImgInfo malloc failed, size %zu\n",
sizeof(pldm_component_img_info_t **) * pFwPkgHdr->componentImageCnt);
return -1;
}
for (i=0; i<pFwPkgHdr->componentImageCnt; ++i)
{
pFwPkgHdr->pCompImgInfo[i] = (pldm_component_img_info_t *)(pFwPkgHdr->rawHdrBuf + *pOffset);
printComponentImgInfo(pFwPkgHdr, i);
// move pointer to next Component image, taking int account of variable
// version size
*pOffset += offsetof(pldm_component_img_info_t, versionString) +
pFwPkgHdr->pCompImgInfo[i]->versionStringLength;
}
return 0;
}
// free up all pointers allocated in pldm_fw_pkg_hdr_t *pFwPkgHdr
void
free_pldm_pkg_data(pldm_fw_pkg_hdr_t **pFwPkgHdr)
{
if ((!pFwPkgHdr) || (!(*pFwPkgHdr)))
return;
if ((*pFwPkgHdr)->pDevIdRecs) {
for (int i=0; i<(*pFwPkgHdr)->devIdRecordCnt; ++i) {
if ( ((*pFwPkgHdr)->pDevIdRecs[i]) &&
((*pFwPkgHdr)->pDevIdRecs[i]->pRecordDes)) {
free((*pFwPkgHdr)->pDevIdRecs[i]->pRecordDes);
}
if ((*pFwPkgHdr)->pDevIdRecs[i]) {
free((*pFwPkgHdr)->pDevIdRecs[i]);
}
}
free((*pFwPkgHdr)->pDevIdRecs);
}
if ((*pFwPkgHdr)->pCompImgInfo) {
free((*pFwPkgHdr)->pCompImgInfo);
}
if ((*pFwPkgHdr)->rawHdrBuf) {
free((*pFwPkgHdr)->rawHdrBuf);
}
free(*pFwPkgHdr);
return;
}
pldm_fw_pkg_hdr_t *
pldm_parse_fw_pkg(char *path) {
int ret = 0;
int offset = 0;
// firmware package header
pldm_fw_pkg_hdr_t *pFwPkgHdr;
// initialize pFwPkgHdr access pointer as fw package header contains
// multiple variable size fields
// header info area
offset = 0;
ret = init_pkg_hdr_info(path, &pFwPkgHdr, &offset);
if (ret < 0) {
goto error_exit;
}
// firmware device id area
ret = init_device_id_records(pFwPkgHdr, &offset);
if (ret < 0) {
goto error_exit;
}
// component image info area
ret = init_component_img_info(pFwPkgHdr, &offset);
if (ret < 0) {
goto error_exit;
}
// pkg header checksum
pFwPkgHdr->pkgHdrChksum = *(uint32_t *)(pFwPkgHdr->rawHdrBuf+ offset);
offset += sizeof(uint32_t);
printf("\n\nPDLM Firmware Package Checksum=0x%x\n", pFwPkgHdr->pkgHdrChksum);
if (pFwPkgHdr->phdrInfo->headerSize != offset) {
printf("ERROR: header size(0x%x) and processed data (0x%x) mismatch\n",
pFwPkgHdr->phdrInfo->headerSize, offset);
}
return pFwPkgHdr;
error_exit:
if (pFwPkgHdr)
free_pldm_pkg_data(&pFwPkgHdr);
return 0;
}
static
int handlePldmReqFwData(pldm_fw_pkg_hdr_t *pkgHdr, pldm_cmd_req *pCmd, pldm_response *pldmRes)
{
PLDM_RequestFWData_t *pReqDataCmd = (PLDM_RequestFWData_t *)pCmd->payload;
// for now assumes it's always component 0
unsigned char *pComponent =
(unsigned char*)(pkgHdr->rawHdrBuf + pkgHdr->pCompImgInfo[0]->locationOffset);
uint32_t componentSize = pkgHdr->pCompImgInfo[0]->size;
// calculate how much FW data is left to transfer and if any padding is needed
int compBytesLeft = componentSize - pReqDataCmd->offset;
int numPaddingNeeded = pReqDataCmd->length > compBytesLeft ?
(pReqDataCmd->length - compBytesLeft) : 0;
printf("\r%s offset = 0x%x, length = 0x%x, compBytesLeft=%d, numPadding=%d",
__FUNCTION__, pReqDataCmd->offset, pReqDataCmd->length, compBytesLeft,
numPaddingNeeded);
fflush(stdout);
memcpy(pldmRes->common, pCmd->common, PLDM_COMMON_REQ_LEN);
// clear Req bit in PLDM response header
pldmRes->common[PLDM_IID_OFFSET] &= PLDM_RESP_MASK;
// hard code success for now, need to check length in the future
pldmRes->common[PLDM_CC_OFFSET] = CC_SUCCESS;
if (numPaddingNeeded > 0) {
printf("%s %d bytes padding added\n", __FUNCTION__, numPaddingNeeded);
memcpy(pldmRes->response, pComponent + pReqDataCmd->offset, compBytesLeft);
memset(pldmRes->response + compBytesLeft, 0, numPaddingNeeded);
} else {
memcpy(pldmRes->response, pComponent + pReqDataCmd->offset, pReqDataCmd->length);
}
pldmRes->resp_size = PLDM_COMMON_RES_LEN + pReqDataCmd->length;
return 0;
}
static
int handlePldmFwTransferComplete(pldm_cmd_req *pCmd, pldm_response *pRes)
{
PLDM_TransferComplete_t *pReqDataCmd = (PLDM_TransferComplete_t *)pCmd->payload;
int ret = 0;
if (pReqDataCmd->transferResult != 0) {
printf("Error, transfer failed, err=%d\n", pReqDataCmd->transferResult);
ret = -1;
}
memcpy(pRes->common, pCmd->common, PLDM_COMMON_REQ_LEN);
// clear Req bit in PLDM response header
pRes->common[PLDM_IID_OFFSET] &= PLDM_RESP_MASK;
pRes->common[PLDM_CC_OFFSET] = CC_SUCCESS;
pRes->resp_size = PLDM_COMMON_RES_LEN;
return ret;
}
static
int handlePldmVerifyComplete(pldm_cmd_req *pCmd, pldm_response *pRes)
{
PLDM_VerifyComplete_t *pReqDataCmd = (PLDM_VerifyComplete_t *)pCmd->payload;
int ret = 0;
if (pReqDataCmd->verifyResult != 0) {
printf("Error, firmware verify failed, err=0x%x\n", pReqDataCmd->verifyResult);
ret = -1;
}
memcpy(pRes->common, pCmd->common, PLDM_COMMON_REQ_LEN);
// clear Req bit in PLDM response header
pRes->common[PLDM_IID_OFFSET] &= PLDM_RESP_MASK;
pRes->common[PLDM_CC_OFFSET] = CC_SUCCESS;
pRes->resp_size = PLDM_COMMON_RES_LEN;
return ret;
}
static
int handlePldmFwApplyComplete(pldm_cmd_req *pCmd, pldm_response *pRes)
{
PLDM_ApplyComplete_t *pReqDataCmd = (PLDM_ApplyComplete_t *)pCmd->payload;
int ret = 0;
if (pReqDataCmd->applyResult != 0) {
printf("Error, firmware apply failed, err=%d\n", pReqDataCmd->applyResult);
ret = -1;
}
printf("Apply result = 0x%x, compActivationMethodsModification=0x%x\n",
pReqDataCmd->applyResult, pReqDataCmd->compActivationMethodsModification);
memcpy(pRes->common, pCmd->common, PLDM_COMMON_REQ_LEN);
// clear Req bit in PLDM response header
pRes->common[PLDM_IID_OFFSET] &= PLDM_RESP_MASK;
pRes->common[PLDM_CC_OFFSET] = CC_SUCCESS;
pRes->resp_size = PLDM_COMMON_RES_LEN;
return ret;
}
int pldmFwUpdateCmdHandler(pldm_fw_pkg_hdr_t *pkgHdr, pldm_cmd_req *pCmd, pldm_response *pRes)
{
int cmd = pCmd->common[PLDM_CMD_OFFSET];
int ret = 0;
// need to check cmd validity
switch (cmd) {
case CMD_REQUEST_FIRMWARE_DATA:
ret = handlePldmReqFwData(pkgHdr, pCmd, pRes);
break;
case CMD_TRANSFER_COMPLETE:
printf("handle CMD_TRANSFER_COMPLETE\n");
dbgPrintCdb(pCmd);
ret = handlePldmFwTransferComplete(pCmd, pRes);
break;
case CMD_VERIFY_COMPLETE:
printf("handle CMD_VERIFY_COMPLETE\n");
dbgPrintCdb(pCmd);
ret = handlePldmVerifyComplete(pCmd, pRes);
break;
case CMD_APPLY_COMPLETE:
printf("handle CMD_APPLY_COMPLETE\n");
dbgPrintCdb(pCmd);
ret = handlePldmFwApplyComplete(pCmd, pRes);
break;
default:
printf("unknown cmd %d\n",cmd);
dbgPrintCdb(pCmd);
ret = -1;
break;
}
return ret;
}
void setPldmTimeout(int pldmCmd, int *timeout_sec)
{
if (pldmCmd == CMD_REQUEST_FIRMWARE_DATA) {
// special timeout value (UA_T2) for Request FW data as specified in DSP0267
*timeout_sec = UA_T2;
} else {
// default time out is State Change Timeout (in DSP0267, tbl 2 timing spec)
*timeout_sec = UA_T3;
}
}
// PLDM Base commands
// Cmd 0x 03 - get version
void pldmCreateGetVersionCmd(uint8_t pldmType, pldm_cmd_req *pPldmCdb)
{
#define GET_NEXT_PART 0
#define GET_FIRST_PART 1
PLDM_GetPldmVersion_t *pGetVersionCmd = (PLDM_GetPldmVersion_t *)pPldmCdb->payload;
memset(pPldmCdb, 0, sizeof(pldm_cmd_req));
genReqCommonFields(PLDM_TYPE_MSG_CTRL_AND_DISCOVERY, CMD_GET_PLDM_VERSION, &(pPldmCdb->common[0]));
pPldmCdb->payload_size = sizeof(PLDM_GetPldmVersion_t);
pGetVersionCmd->dataTransferHandle = 0;
pGetVersionCmd->transferOperationFlag = GET_FIRST_PART;
pGetVersionCmd->pldmType = pldmType;
dbgPrintCdb(pPldmCdb);
return;
}
// handle Cmd 0x04 (Get Supported Types) response
void pldmHandleGetVersionResp(PLDM_GetPldmVersion_Response_t *pPldmResp,
pldm_ver_t *version)
{
if (pPldmResp->completionCode == CC_SUCCESS)
{
// version string encoding is 0xFaFbFcdd
// where
// major version number: a
// minor version number: b
// update version number: c
// alpha verision : dd
version->major = (int)(pPldmResp->version[3]) - 0xf0;
version->minor = (int)(pPldmResp->version[2]) - 0xf0;
version->update = (int)(pPldmResp->version[1]) - 0xf0;
version->alpha = (int)(pPldmResp->version[0]);
}
return;
}
// Cmd 0x04 - check supported types
void pldmCreateGetPldmTypesCmd(pldm_cmd_req *pPldmCdb)
{
memset(pPldmCdb, 0, sizeof(pldm_cmd_req));
genReqCommonFields(PLDM_TYPE_MSG_CTRL_AND_DISCOVERY, CMD_GET_PLDM_TYPES, &(pPldmCdb->common[0]));
pPldmCdb->payload_size = 0;
dbgPrintCdb(pPldmCdb);
return;
}
// handle Cmd 0x04 (Get Supported Types) response
uint64_t pldmHandleGetPldmTypesResp(PLDM_GetPldmTypes_Response_t *pPldmResp)
{
uint64_t ret = 0;
if (pPldmResp->completionCode == CC_SUCCESS)
{
gPldm_supported_types = pPldmResp->supported_types;
ret = gPldm_supported_types;
}
return ret;
}
int pldmRespHandler(pldm_response *pResp)
{
int pldmType = pResp->common[PLDM_TYPE_OFFSET] & 0x3f;
int compCode = pResp->common[PLDM_CC_OFFSET];
int ret = 0;
// if error occurred, do not decode response payload
if (compCode != 0)
return ret;
printf("pldmRespHandler, pldmType=%d\n", pldmType);
switch (pldmType) {
case PLDM_MONITORING:
return pldmHandlePmcResp(pResp);
default:
ret = -1;
break;
}
return ret;
}
// takes NCSI cmd 0x56 response and returns
// PLDM opcode, as well as the entire cmd field in pldmReq
int ncsiGetPldmCmd(NCSI_NL_RSP_T *nl_resp, pldm_cmd_req *pldmReq)
{
// NCSI response contains at least 4 bytes of Reason code and
// Response code.
if (nl_resp->hdr.payload_length <= 4) {
// empty response, no PLDM payload
return -1;
}
pldmReq->payload_size = nl_resp->hdr.payload_length - 4; // account for response and reason code
memcpy((char *)&(pldmReq->common[0]), (char *)&(nl_resp->msg_payload[4]),
pldmReq->payload_size);
// PLDM cmd byte is the 3rd byte of PLDM header
return pldmReq->common[PLDM_CMD_OFFSET];
}
// take an NCSI response carry PLDM response, extract PLDM Completion code
int ncsiDecodePldmCompCode(NCSI_NL_RSP_T *nl_resp)
{
// NCSI response contains at least 4 bytes of Reason code and
// Response code. PLDM response contains at least 4 bytes header
if (nl_resp->hdr.payload_length < 8) {
// empty response, no PLDM payload/or payload incomplete
return -1;
}
// Completion Code is in 4th byte of PLDM header
return nl_resp->msg_payload[7];
}
// takes an NCSI response carrying PLDM payload, and extract PLDM IID
int ncsiDecodePldmIID(NCSI_Response_Packet *ncsi_resp)
{
// if NCSI command failed, do not proceed to extract payload
if (ncsi_resp->Response_Code != REASON_NO_ERROR) {
return -1;
}
return (ncsi_resp->Payload_Data[PLDM_IID_OFFSET] & PLDM_CM_IID_MASK);
}
// takes an NCSI response carrying PLDM response, and extract PLDM Type
int ncsiDecodePldmType(NCSI_Response_Packet *ncsi_resp)
{
// if NCSI command failed, do not proceed to extract payload
if (ncsi_resp->Response_Code != REASON_NO_ERROR) {
return -1;
}
return (ncsi_resp->Payload_Data[PLDM_TYPE_OFFSET] & PLDM_CM_PLDM_TYPE_MASK);
}
// takes an NCSI response carrying PLDM response, and extract PLDM cmd
int ncsiDecodePldmCmd(NCSI_Response_Packet *ncsi_resp)
{
// if NCSI command failed, do not proceed to extract payload
if (ncsi_resp->Response_Code != REASON_NO_ERROR) {
return -1;
}
// PLDM CMD is the first byte of PLDM header
return (ncsi_resp->Payload_Data[PLDM_CMD_OFFSET]);
}
// takes a PLDM response (table 6.1 of DSP0240)
// https://www.dmtf.org/sites/default/files/standards/documents/DSP0240_1.0.0.pdf
// Returns the response payload, start with completion code
unsigned char *
get_pldm_response_payload(unsigned char *buf)
{
return (buf + 3);
}