Add i2c voltage regulator updater
- /i2c-vr: Classes following the design in [1]
- /i2c-vr/vr.cpp, vr.hpp: General representation of a voltage regulators
communication interface
- /vr-i2c/xdpe1x2xx: Support for Infineon XDPE1x2xx class of voltage
regulators
- /common/i2c/: Basic I2C communication interface
The configuration of a voltage regulator relies on the EM-Schema in [2]
1: https://github.com/openbmc/docs/blob/master/designs/code-update.md
2: https://gerrit.openbmc.org/c/openbmc/entity-manager/+/77463
Tested on QEMU/Yosemite4:
1. Display the firmware inventory
```
curl --insecure --user root:0penBmc \
https://127.0.0.1:2443/redfish/v1/UpdateService/FirmwareInventory
```
Output:
```
{
"@odata.id": "/redfish/v1/UpdateService/FirmwareInventory",
"@odata.type": "#SoftwareInventoryCollection.SoftwareInventoryCollection",
"Members": [
{
"@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/16bae6fd"
},
{
"@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/DummyDeviceFirmwareName_6637"
},
{
"@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/Management_Board_cpld"
},
{...}
```
2. Query voltage regulator version
The dummy device sets the default version to 0xBEEF (Decimal: 48879)
```
curl --insecure --user root:0penBmc \
https://127.0.0.1:2443/redfish/v1/UpdateService/FirmwareInventory/
DummyDeviceFirmwareName_4390
```
Output:
```
{
"@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/DummyDeviceFirmwareName_4390",
"@odata.type": "#SoftwareInventory.v1_1_0.SoftwareInventory",
"Description": "Other image",
"Id": "DummyDeviceFirmwareName_4390",
"Name": "Software Inventory",
"Status": {
"Health": "Warning",
"HealthRollup": "OK",
"State": "Disabled"
},
"Updateable": false,
"Version": "48879"
}
```
3. Trigger the update
```
curl -k --insecure --user root:0penBmc \
-H "Content-Type:multipart/form-data" \
-X POST \
-F UpdateParameters="{\"Targets\":[\"/redfish/v1/UpdateService/FirmwareInventory/DummyDeviceFirmwareName_4390\"],\"@Redfish.OperationApplyTime\":\"Immediate\"};type=application/json" \
-F "UpdateFile=@fw_vr_update.bin;type=application/octet-stream" \
https://127.0.0.1:2443/redfish/v1/UpdateService/update
```
4. Task is returned
```
{
"@odata.id": "/redfish/v1/TaskService/Tasks/0",
"@odata.type": "#Task.v1_4_3.Task",
"Id": "0",
"TaskState": "Running",
"TaskStatus": "OK"
}
```
5. Query the task
```
curl --insecure --user root:0penBmc \
https://127.0.0.1:2443/redfish/v1/TaskService/Tasks/0
```
Output:
```
{
"@odata.id": "/redfish/v1/TaskService/Tasks/0",
"@odata.type": "#Task.v1_4_3.Task",
"EndTime": "2025-03-10T13:47:34+00:00",
"HidePayload": false,
"Id": "0",
"Messages": [
{
"@odata.type": "#Message.v1_1_1.Message",
"Message": "The task with Id '0' has started.",
"MessageArgs": [
"0"
],
"MessageId": "TaskEvent.1.0.TaskStarted",
"MessageSeverity": "OK",
"Resolution": "None."
},
{
"@odata.type": "#Message.v1_1_1.Message",
"Message": "The task with Id '0' has completed.",
"MessageArgs": [
"0"
],
"MessageId": "TaskEvent.1.0.TaskCompletedOK",
"MessageSeverity": "OK",
"Resolution": "None."
}
],
"Name": "Task 0",
"Payload": {
"HttpHeaders": [],
"HttpOperation": "POST",
"JsonBody": "null",
"TargetUri": "/redfish/v1/UpdateService/update"
},
"PercentComplete": 0,
"StartTime": "2025-03-10T13:47:34+00:00",
"TaskMonitor": "/redfish/v1/TaskService/TaskMonitors/0",
"TaskState": "Completed",
"TaskStatus": "OK"
}
```
6. Display fw inventory after update
```
curl --insecure --user root:0penBmc \
https://127.0.0.1:2443/redfish/v1/UpdateService/FirmwareInventory
```
Output:
```
{
"@odata.id": "/redfish/v1/UpdateService/FirmwareInventory",
"@odata.type": "#SoftwareInventoryCollection.SoftwareInventoryCollection",
"Members": [
{
"@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/16bae6fd"
},
{
"@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/DummyDeviceFirmwareName_282"
},
{...}
}
```
7. Query the new fw version.
The version is 'mycompversion' since that's what has been set in the
pldm fw update package for testing.
```
curl --insecure --user root:0penBmc \
https://127.0.0.1:2443/redfish/v1/UpdateService/FirmwareInventory/
DummyDeviceFirmwareName_282
```
Output:
```
{
"@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/DummyDeviceFirmwareName_282",
"@odata.type": "#SoftwareInventory.v1_1_0.SoftwareInventory",
"Description": "Other image",
"Id": "DummyDeviceFirmwareName_282",
"Name": "Software Inventory",
"Status": {
"Health": "OK",
"HealthRollup": "OK",
"State": "Enabled"
},
"Updateable": false,
"Version": "mycompversion"
}
```
8. Update in progress error
```
curl -k --insecure --user root:0penBmc \
-H "Content-Type:multipart/form-data" \
-X POST \
-F UpdateParameters="{\"Targets\":[\"/redfish/v1/UpdateService/FirmwareInventory/${TARGET}\"],\"@Redfish.OperationApplyTime\":\"Immediate\"};type=application/json" \
-F "UpdateFile=@${FWPATH};type=application/octet-stream" \
https://127.0.0.1:2443/redfish/v1/UpdateService/update
```
Output:
```
{
"error": {
"@Message.ExtendedInfo": [
{
"@odata.type": "#Message.v1_1_1.Message",
"Message": "The request failed due to an internal service error. The service is still operational.",
"MessageArgs": [],
"MessageId": "Base.1.19.InternalError",
"MessageSeverity": "Critical",
"Resolution": "Resubmit the request. If the problem persists, consider resetting the service."
}
],
"code": "Base.1.19.InternalError",
"message": "The request failed due to an internal service error. The service is still operational."
}
```
Change-Id: I2e11a6c10ae40ed7719ceb86dfd6a38dd5b27017
Signed-off-by: Christopher Meis <christopher.meis@9elements.com>
diff --git a/i2c-vr/xdpe1x2xx/xdpe1x2xx.cpp b/i2c-vr/xdpe1x2xx/xdpe1x2xx.cpp
new file mode 100644
index 0000000..28894e0
--- /dev/null
+++ b/i2c-vr/xdpe1x2xx/xdpe1x2xx.cpp
@@ -0,0 +1,733 @@
+#include "xdpe1x2xx.hpp"
+
+#include "common/include/i2c/i2c.hpp"
+
+#include <unistd.h>
+
+#include <phosphor-logging/lg2.hpp>
+
+#include <cstdio>
+
+#define REMAINING_TIMES(x, y) (((((x)[1]) << 8) | ((x)[0])) / (y))
+
+PHOSPHOR_LOG2_USING;
+
+namespace phosphor::software::VR
+{
+
+const uint32_t CRC32Poly = 0xEDB88320;
+const int VRResetDelay = 500000;
+
+enum RevisionCode
+{
+ REV_A = 0x00,
+ REV_B,
+ REV_C,
+ REV_D,
+};
+
+enum ProductID
+{
+ ProductIDXDPE15284 = 0x8A,
+ ProductIDXDPE19283 = 0x95,
+};
+
+const uint32_t PMBusICDeviceID = 0xAD;
+const uint32_t PMBusSTLCml = 0x7E;
+const uint32_t IFXICDeviceIDLen = 2;
+const uint32_t IFXMFRAHBAddr = 0xCE;
+const uint32_t IFXMFRRegWrite = 0xDE;
+const uint32_t IFXMFRFwCmdData = 0xFD;
+const uint32_t IFXMFRFwCmd = 0xFE;
+const uint32_t MFRFwCmdReset = 0x0e;
+const uint32_t MFRFwCmdRmng = 0x10;
+const uint32_t MFRFwCmdOTPConfSTO = 0x11;
+const uint32_t MFRFwCmdOTPFileInvd = 0x12;
+const uint32_t MFRFwCmdGetCRC = 0x2D;
+const int XDPE15284CConfSize = 1344;
+const int XDPE19283BConfSize = 1416;
+const uint32_t VRWarnRemaining = 3;
+const uint32_t SectTrim = 0x02;
+
+const char* const AddressField = "PMBus Address :";
+const char* const ChecksumField = "Checksum :";
+const char* const DataStartTag = "Configuration Data]";
+const char* const DataEndTag = "[End Configuration Data]";
+const char* const DataComment = "//";
+const char* const DataXV = "XV";
+
+XDPE1X2XX::XDPE1X2XX(sdbusplus::async::context& ctx, uint16_t bus,
+ uint16_t address) :
+ VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address))
+{}
+
+// NOLINTBEGIN(readability-static-accessed-through-instance)
+sdbusplus::async::task<bool> XDPE1X2XX::getDeviceId(uint8_t* deviceID)
+// NOLINTEND(readability-static-accessed-through-instance)
+{
+ bool ret = false;
+ uint8_t tbuf[16] = {0};
+ tbuf[0] = PMBusICDeviceID;
+ tbuf[1] = 2;
+ uint8_t tSize = 1;
+ uint8_t rbuf[16] = {0};
+ uint8_t rSize = IFXICDeviceIDLen + 1;
+
+ ret = co_await this->i2cInterface.sendReceive(tbuf, tSize, rbuf, rSize);
+ if (!ret)
+ {
+ error("Failed to get device ID");
+ co_return false;
+ }
+
+ std::memcpy(deviceID, &rbuf[1], IFXICDeviceIDLen);
+
+ co_return true;
+}
+
+// NOLINTBEGIN(readability-static-accessed-through-instance)
+sdbusplus::async::task<bool> XDPE1X2XX::mfrFWcmd(uint8_t cmd, uint8_t* data,
+ uint8_t* resp)
+// NOLINTEND(readability-static-accessed-through-instance)
+{
+ bool ret = false;
+ uint8_t tBuf[16] = {0};
+ uint8_t rBuf[16] = {0};
+ uint8_t tSize = 0;
+ uint8_t rSize = 0;
+
+ if (data)
+ {
+ tBuf[0] = IFXMFRFwCmdData;
+ tBuf[1] = 4; // Block write 4 bytes
+ tSize = 6;
+ std::memcpy(&tBuf[2], data, 4);
+ ret = co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf, rSize);
+ if (!ret)
+ {
+ error("Failed to send MFR command: {CMD}", "CMD",
+ std::string("IFXMFRFwCmdDAta"));
+ co_return false;
+ }
+ }
+
+ co_await sdbusplus::async::sleep_for(ctx, std::chrono::microseconds(300));
+
+ tBuf[0] = IFXMFRFwCmd;
+ tBuf[1] = cmd;
+ tSize = 2;
+ rSize = 0;
+ ret = co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf, rSize);
+ if (!ret)
+ {
+ error("Failed to send MFR command: {CMD}", "CMD",
+ std::string("IFXMFRFwCmd"));
+ co_return false;
+ }
+
+ co_await sdbusplus::async::sleep_for(ctx, std::chrono::microseconds(20000));
+
+ if (resp)
+ {
+ tBuf[0] = IFXMFRFwCmdData;
+ tSize = 1;
+ rSize = 6;
+ ret = co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf, rSize);
+ if (!ret)
+ {
+ error("Failed to send MFR command: {CMD}", "CMD",
+ std::string("IFXMFRFwCmdData"));
+ co_return false;
+ }
+ if (rBuf[0] != 4)
+ {
+ error(
+ "Failed to receive MFR response with unexpected response size");
+ co_return false;
+ }
+ std::memcpy(resp, rBuf + 1, 4);
+ }
+
+ co_return true;
+}
+
+// NOLINTBEGIN(readability-static-accessed-through-instance)
+sdbusplus::async::task<bool> XDPE1X2XX::getRemainingWrites(uint8_t* remain)
+// NOLINTEND(readability-static-accessed-through-instance)
+{
+ bool ret = false;
+ uint8_t tBuf[16] = {0};
+ uint8_t rBuf[16] = {0};
+ uint8_t devId[2] = {0};
+
+ ret = co_await this->mfrFWcmd(MFRFwCmdRmng, tBuf, rBuf);
+ if (!ret)
+ {
+ error("Failed to request remaining writes");
+ co_return false;
+ }
+
+ ret = co_await this->getDeviceId(devId);
+ if (!ret)
+ {
+ error("Failed to request device ID for remaining writes");
+ co_return false;
+ }
+
+ int configSize = getConfigSize(devId[1], devId[0]);
+ if (configSize < 0)
+ {
+ error("Failed to request valid configuration size");
+ co_return false;
+ }
+
+ *remain = REMAINING_TIMES(rBuf, configSize);
+
+ co_return 0;
+}
+
+int XDPE1X2XX::getConfigSize(uint8_t deviceId, uint8_t revision)
+{
+ int size = -1;
+
+ switch (deviceId)
+ {
+ case ProductIDXDPE19283:
+ if (revision == REV_B)
+ {
+ size = XDPE19283BConfSize;
+ }
+ break;
+ case ProductIDXDPE15284:
+ size = XDPE15284CConfSize;
+ break;
+ default:
+ error(
+ "Failed to get configuration size of {DEVID} with revision {REV}",
+ "DEVID", deviceId, "REV", revision);
+ return -1;
+ }
+
+ return size;
+}
+
+// NOLINTBEGIN(readability-static-accessed-through-instance)
+sdbusplus::async::task<bool> XDPE1X2XX::getCRC(uint32_t* checksum)
+// NOLINTEND(readability-static-accessed-through-instance)
+{
+ uint8_t tBuf[16] = {0};
+ uint8_t rBuf[16] = {0};
+
+ bool ret = co_await this->mfrFWcmd(MFRFwCmdGetCRC, tBuf, rBuf);
+ if (!ret)
+ {
+ error("Failed to get CRC value");
+ co_return false;
+ }
+
+ *checksum = (static_cast<uint32_t>(rBuf[3]) << 24) |
+ (static_cast<uint32_t>(rBuf[2]) << 16) |
+ (static_cast<uint32_t>(rBuf[1]) << 8) |
+ (static_cast<uint32_t>(rBuf[0]));
+
+ co_return true;
+}
+
+// NOLINTBEGIN(readability-static-accessed-through-instance)
+sdbusplus::async::task<bool> XDPE1X2XX::program(bool force)
+// NOLINTEND(readability-static-accessed-through-instance)
+
+{
+ bool ret = false;
+ uint8_t tBuf[16] = {0};
+ uint8_t rBuf[16] = {0};
+ uint8_t remain = 0;
+ uint32_t sum = 0;
+ int size = 0;
+
+ // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
+ ret = co_await getCRC(&sum);
+ // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
+ if (!ret)
+ {
+ error("Failed to program the VR");
+ co_return -1;
+ }
+
+ if (!force && (sum == configuration.sumExp))
+ {
+ error("Failed to program the VR - CRC value are equal with no force");
+ co_return -1;
+ }
+
+ // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
+ ret = co_await this->getRemainingWrites(&remain);
+ // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
+ if (!ret)
+ {
+ error("Failed to program the VR - unable to obtain remaing writes");
+ co_return -1;
+ }
+
+ if (!remain)
+ {
+ error("Failed to program the VR - no remaining write cycles left");
+ co_return -1;
+ }
+
+ if (!force && (remain <= VRWarnRemaining))
+ {
+ error(
+ "Failed to program the VR - {REMAIN} remaining writes left and not force",
+ "REMAIN", remain);
+ co_return -1;
+ }
+
+ // Added reprogramming of the entire configuration file.
+ // Except for the trim section, all other data will be replaced.
+ // 0xfe 0xfe 0x00 0x00 instructs the command to reprogram all header codes
+ // and XVcode. If the old sections are not invalidated in OTP, they can
+ // affect the CRC calculation.
+
+ tBuf[0] = 0xfe;
+ tBuf[1] = 0xfe;
+ tBuf[2] = 0x00;
+ tBuf[3] = 0x00;
+
+ ret = co_await this->mfrFWcmd(MFRFwCmdOTPFileInvd, tBuf, NULL);
+ if (!ret)
+ {
+ error("Failed to program the VR - Invalidation of currect FW");
+ co_return ret;
+ }
+
+ co_await sdbusplus::async::sleep_for(ctx,
+ std::chrono::microseconds(500000));
+
+ for (int i = 0; i < configuration.sectCnt; i++)
+ {
+ struct configSect* sect = &configuration.section[i];
+ if (sect == NULL)
+ {
+ error(
+ "Failed to program the VR - unexpected NULL section in config");
+ ret = -1;
+ break;
+ }
+
+ if ((i <= 0) || (sect->type != configuration.section[i - 1].type))
+ {
+ tBuf[0] = PMBusSTLCml;
+ tBuf[1] = 0x1;
+ uint8_t tSize = 2;
+ uint8_t rSize = 0;
+ ret = co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
+ rSize);
+ if (!ret)
+ {
+ error("Failed to program the VR on sendReceive {CMD}", "CMD",
+ std::string("PMBusSTLCml"));
+ break;
+ }
+
+ tBuf[0] = sect->type;
+ tBuf[1] = 0x00;
+ tBuf[2] = 0x00;
+ tBuf[3] = 0x00;
+
+ ret = co_await this->mfrFWcmd(MFRFwCmdOTPFileInvd, tBuf, NULL);
+ if (!ret)
+ {
+ error("Failed to program VR on mfrFWCmd on {CMD}", "CMD",
+ std::string("MFRFwCmdOTPFileInvd"));
+ break;
+ }
+
+ co_await sdbusplus::async::sleep_for(
+ ctx, std::chrono::microseconds(10000)); // Write delay
+
+ tBuf[0] = IFXMFRAHBAddr;
+ tBuf[1] = 4;
+ tBuf[2] = 0x00;
+ tBuf[3] = 0xe0;
+ tBuf[4] = 0x05;
+ tBuf[5] = 0x20;
+ tSize = 6;
+ rSize = 0;
+
+ ret = co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
+ rSize);
+ if (!ret)
+ {
+ error("Failed to program VR on sendReceive on {CMD}", "CMD",
+ std::string("IFXMFRAHBAddr"));
+ break;
+ }
+
+ co_await sdbusplus::async::sleep_for(
+ ctx, std::chrono::microseconds(10000));
+ size = 0;
+ }
+
+ // programm into scratchpad
+ for (int j = 0; j < sect->dataCnt; j++)
+ {
+ tBuf[0] = IFXMFRRegWrite;
+ tBuf[1] = 4;
+ uint8_t tSize = 6;
+ uint8_t rSize = 0;
+ memcpy(&tBuf[2], §->data[j], 4);
+ ret = co_await this->i2cInterface.sendReceive(tBuf, tSize, rBuf,
+ rSize);
+ if (!ret)
+ {
+ error("Failed to program the VR on sendReceive {CMD}", "CMD",
+ std::string("IFXMFRRegWrite"));
+ break;
+ }
+ co_await sdbusplus::async::sleep_for(
+ ctx, std::chrono::microseconds(10000));
+ }
+ if (ret)
+ {
+ break;
+ }
+
+ size += sect->dataCnt * 4;
+ if ((i + 1 >= configuration.sectCnt) ||
+ (sect->type != configuration.section[i + 1].type))
+ {
+ // Upload to scratchpad
+ std::memcpy(tBuf, &size, 2);
+ tBuf[2] = 0x00;
+ tBuf[3] = 0x00;
+ bool ret = co_await this->mfrFWcmd(MFRFwCmdOTPConfSTO, tBuf, NULL);
+ if (ret)
+ {
+ error("Failed to program the VR on mfrFWcmd {CMD}", "CMD",
+ std::string("MFRFwCmdOTPConfSTO"));
+ break;
+ }
+
+ // wait for programming soak (2ms/byte, at least 200ms)
+ // ex: Config (604 bytes): (604 / 50) + 2 = 14 (1400 ms)
+ size = (size / 50) + 2;
+ for (int j = 0; j < size; j++)
+ {
+ co_await sdbusplus::async::sleep_for(
+ ctx, std::chrono::microseconds(100000));
+ }
+
+ tBuf[0] = PMBusSTLCml;
+ uint8_t tSize = 1;
+ uint8_t rSize = 1;
+ ret = co_await this->i2cInterface.sendReceive(rBuf, tSize, tBuf,
+ rSize);
+ if (!ret)
+ {
+ error("Failed to program VR on sendReceive {CMD}", "CMD",
+ std::string("PMBusSTLCml"));
+ break;
+ }
+ if (rBuf[0] & 0x01)
+ {
+ error("Failed to program VR - response code invalid");
+ break;
+ }
+ }
+ }
+
+ if (!ret)
+ {
+ co_return false;
+ }
+
+ co_return true;
+}
+
+int XDPE1X2XX::lineSplit(char** dest, char* src, char* delim)
+{
+ char* s = strtok(src, delim);
+ int size = 0;
+ int maxSz = 5;
+
+ while (s)
+ {
+ *dest++ = s;
+ if ((++size) >= maxSz)
+ {
+ break;
+ }
+ s = strtok(NULL, delim);
+ }
+
+ return size;
+}
+
+int XDPE1X2XX::parseImage(const uint8_t* image, size_t image_size)
+{
+ size_t lenEndTag = strlen(DataEndTag);
+ size_t lenStartTag = strlen(DataStartTag);
+ size_t lenComment = strlen(DataComment);
+ size_t lenXV = strlen(DataXV);
+ size_t start = 0;
+ const int maxLineLength = 40;
+ char line[maxLineLength];
+ char* token = NULL;
+ bool isData = false;
+ char delim = ' ';
+ uint16_t offset;
+ uint8_t sectType = 0x0;
+ uint32_t dWord;
+ int dataCnt = 0;
+ int sectIndex = -1;
+
+ for (size_t i = 0; i < image_size; i++)
+ {
+ if (image[i] == '\n')
+ {
+ std::memcpy(line, image + start, i - start);
+ if (!strncmp(line, DataComment, lenComment))
+ {
+ token = line + lenComment;
+ if (!strncmp(token, DataXV, lenXV))
+ {
+ debug("Parsing: {OBJ}", "OBJ",
+ reinterpret_cast<const char*>(line));
+ }
+ start = i + 1;
+ continue;
+ }
+ if (!strncmp(line, DataEndTag, lenEndTag))
+ {
+ debug("Parsing: {OBJ}", "OBJ",
+ reinterpret_cast<const char*>(line));
+ break;
+ }
+ else if (isData)
+ {
+ char* tokenList[8] = {0};
+ int tokenSize = lineSplit(tokenList, line, &delim);
+ if (tokenSize < 1)
+ {
+ start = i + 1;
+ continue;
+ }
+
+ offset = (uint16_t)strtol(tokenList[0], NULL, 16);
+ if (sectType == SectTrim && offset != 0x0)
+ {
+ continue;
+ }
+
+ for (int i = 1; i < tokenSize; i++)
+ {
+ dWord = (uint32_t)strtol(tokenList[i], NULL, 16);
+ if ((offset == 0x0) && (i == 1))
+ {
+ sectType = (uint8_t)dWord;
+ if (sectType == SectTrim)
+ {
+ break;
+ }
+ if ((++sectIndex) >= MaxSectCnt)
+ {
+ return -1;
+ }
+
+ configuration.section[sectIndex].type = sectType;
+ configuration.sectCnt = sectIndex + 1;
+ dataCnt = 0;
+ }
+
+ if (dataCnt >= MaxSectDataCnt)
+ {
+ return -1;
+ }
+
+ configuration.section[sectIndex].data[dataCnt++] = dWord;
+ configuration.section[sectIndex].dataCnt = dataCnt;
+ configuration.totalCnt++;
+ }
+ }
+ else
+ {
+ if ((token = strstr(line, AddressField)) != NULL)
+ {
+ if ((token = strstr(token, "0x")) != NULL)
+ {
+ configuration.addr =
+ (uint8_t)(strtoul(token, NULL, 16) << 1);
+ }
+ }
+ else if ((token = strstr(line, ChecksumField)) != NULL)
+ {
+ if ((token = strstr(token, "0x")) != NULL)
+ {
+ configuration.sumExp =
+ (uint32_t)strtoul(token, NULL, 16);
+ }
+ }
+ else if (!strncmp(line, DataStartTag, lenStartTag))
+ {
+ isData = true;
+ start = i + 1;
+ continue;
+ }
+ else
+ {
+ start = i + 1;
+ continue;
+ }
+ }
+ start = i + 1;
+ }
+ }
+
+ return 0;
+}
+
+int XDPE1X2XX::checkImage()
+{
+ uint8_t i;
+ uint32_t crc;
+ uint32_t sum = 0;
+
+ for (i = 0; i < configuration.sectCnt; i++)
+ {
+ struct configSect* sect = &configuration.section[i];
+ if (sect == NULL)
+ {
+ error("Failed to check image - unexpected NULL section");
+ return -1;
+ }
+
+ crc = calcCRC32(§->data[2], 2);
+ if (crc != sect->data[2])
+ {
+ error("Failed to check image - first CRC value mismatch");
+ return -1;
+ }
+ sum += crc;
+
+ // check CRC of section data
+ crc = calcCRC32(§->data[3], sect->dataCnt - 4);
+ if (crc != sect->data[sect->dataCnt - 1])
+ {
+ error("Failed to check image - second CRC value mismatch");
+ return -1;
+ }
+ sum += crc;
+ }
+
+ if (sum != configuration.sumExp)
+ {
+ error("Failed to check image - third CRC value mismatch");
+ return -1;
+ }
+
+ return 0;
+}
+
+// NOLINTBEGIN(readability-static-accessed-through-instance)
+sdbusplus::async::task<bool> XDPE1X2XX::verifyImage(const uint8_t* image,
+ size_t imageSize)
+// NOLINTEND(readability-static-accessed-through-instance)
+{
+ if (parseImage(image, imageSize) < 0)
+ {
+ error("Failed to update firmware on parsing Image");
+ co_return false;
+ }
+
+ if (checkImage() < 0)
+ {
+ error("Failed to update firmware on check image");
+ co_return false;
+ }
+
+ co_return true;
+}
+
+// NOLINTBEGIN(readability-static-accessed-through-instance)
+sdbusplus::async::task<bool> XDPE1X2XX::updateFirmware(bool force)
+// NOLINTEND(readability-static-accessed-through-instance)
+{
+ // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
+ bool ret = co_await program(force);
+ // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
+ if (!ret)
+ {
+ error("Failed to update firmware on program");
+ co_return false;
+ }
+
+ // Reset the configuration
+ configuration.addr = 0;
+ configuration.totalCnt = 0;
+ configuration.sumExp = 0;
+ configuration.sectCnt = 0;
+ for (int i = 0; i <= MaxSectCnt - 1; i++)
+ {
+ configuration.section[i].type = 0;
+ configuration.section[i].dataCnt = 0;
+ for (int j = 0; j <= MaxSectDataCnt; j++)
+ {
+ configuration.section[i].data[j] = 0;
+ }
+ }
+
+ co_return true;
+}
+
+// NOLINTBEGIN(readability-static-accessed-through-instance)
+sdbusplus::async::task<bool> XDPE1X2XX::reset()
+// NOLINTEND(readability-static-accessed-through-instance)
+{
+ bool ret = co_await mfrFWcmd(MFRFwCmdReset, NULL, NULL);
+ if (!ret)
+ {
+ error("Failed to reset the VR");
+ co_return false;
+ }
+
+ co_await sdbusplus::async::sleep_for(
+ ctx, std::chrono::microseconds(VRResetDelay));
+
+ co_return true;
+}
+
+uint32_t XDPE1X2XX::calcCRC32(const uint32_t* data, int len)
+{
+ if (data == NULL)
+ {
+ return 0;
+ }
+
+ uint32_t crc = 0xFFFFFFFF;
+ for (int i = 0; i < len; i++)
+ {
+ crc ^= data[i];
+
+ for (int b = 0; b < 32; b++)
+ {
+ if (crc & 0x1)
+ {
+ crc = (crc >> 1) ^ CRC32Poly; // lsb-first
+ }
+ else
+ {
+ crc >>= 1;
+ }
+ }
+ }
+
+ return ~crc;
+}
+
+bool XDPE1X2XX::forcedUpdateAllowed()
+{
+ return true;
+}
+
+} // namespace phosphor::software::VR