xref: /openbmc/phosphor-power/tools/power-utils/aei_updater.cpp (revision f9b426b49d50b83cd8d5c67391497fd2dbeb9937)
1 /**
2  * Copyright © 2024 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "config.h"
18 
19 #include "aei_updater.hpp"
20 
21 #include "pmbus.hpp"
22 #include "types.hpp"
23 #include "updater.hpp"
24 #include "utility.hpp"
25 
26 #include <phosphor-logging/lg2.hpp>
27 
28 #include <fstream>
29 
30 namespace aeiUpdater
31 {
32 
33 constexpr uint8_t MAX_RETRIES = 0x02;    // Constants for retry limits
34 
35 constexpr int ISP_STATUS_DELAY = 1200;   // Delay for ISP status check (1.2s)
36 constexpr int MEM_WRITE_DELAY = 5000;    // Memory write delay (5s)
37 constexpr int MEM_STRETCH_DELAY = 1;     // Delay between writes (1ms)
38 constexpr int MEM_COMPLETE_DELAY = 2000; // Delay before completion (2s)
39 constexpr int REBOOT_DELAY = 8000;       // Delay for reboot (8s)
40 
41 constexpr uint8_t I2C_SMBUS_BLOCK_MAX = 0x20;    // Max Read bytes from PSU
42 constexpr uint8_t FW_READ_BLOCK_SIZE = 0x20;     // Read bytes from FW file
43 constexpr uint8_t BLOCK_WRITE_SIZE = 0x25;       // I2C block write size
44 
45 constexpr uint8_t START_SEQUENCE_INDEX = 0x1;    // Starting sequence index
46 constexpr uint8_t STATUS_CML_INDEX = 0x4;        // Status CML read index
47 constexpr uint8_t EXPECTED_MEM_READ_REPLY = 0x5; //  Expected memory read reply
48                                                  //  size after write data
49 
50 // Register addresses for commands.
51 constexpr uint8_t KEY_REGISTER = 0xF6;        // Key register
52 constexpr uint8_t STATUS_REGISTER = 0xF7;     // Status register
53 constexpr uint8_t ISP_MEMORY_REGISTER = 0xF9; // ISP memory register
54 
55 // Define AEI ISP status register commands
56 constexpr uint8_t CMD_CLEAR_STATUS = 0x0; // Clear the status register
57 constexpr uint8_t CMD_RESET_SEQ = 0x01;   // This command will reset ISP OS for
58                                           // another attempt of a sequential
59                                           // programming operation.
60 constexpr uint8_t CMD_BOOT_ISP = 0x02; // Boot the In-System Programming System.
61 constexpr uint8_t CMD_BOOT_PWR = 0x03; // Attempt to boot the Power Management
62                                        // OS.
63 
64 // Define AEI ISP response status bit
65 constexpr uint8_t B_ISP_MODE = 0x40;             // ISP mode
66 constexpr uint8_t B_ISP_MODE_CHKSUM_GOOD = 0x41; // ISP mode  & good checksum.
67 constexpr uint8_t SUCCESSFUL_ISP_REBOOT_STATUS = 0x0; // Successful ISP reboot
68                                                       // status
69 using namespace phosphor::logging;
70 namespace util = phosphor::power::util;
71 
doUpdate()72 int AeiUpdater::doUpdate()
73 {
74     i2cInterface = Updater::getI2C();
75     if (i2cInterface == nullptr)
76     {
77         throw std::runtime_error("I2C interface error");
78     }
79     if (!getFirmwarePath() || !isFirmwareFileValid())
80     {
81         return 1;                  // No firmware file abort download
82     }
83     bool downloadFwFailed = false; // Download Firmware status
84     int retryProcessTwo(0);
85     int retryProcessOne(0);
86     int serviceableEvent(0);
87     while ((retryProcessTwo < MAX_RETRIES) && (retryProcessOne < MAX_RETRIES))
88     {
89         retryProcessTwo++;
90         // Write AEI PSU ISP key
91         if (!writeIspKey())
92         {
93             serviceableEvent++;
94             if (serviceableEvent == MAX_RETRIES)
95             {
96                 createServiceableError(
97                     "createServiceableError: ISP key failed");
98             }
99             lg2::error("Failed to set ISP Key");
100             downloadFwFailed = true; // Download Firmware status
101             continue;
102         }
103 
104         while (retryProcessOne < MAX_RETRIES)
105         {
106             downloadFwFailed = false; // Download Firmware status
107             retryProcessOne++;
108             // Set ISP mode
109             if (!writeIspMode())
110             {
111                 // Write ISP Mode failed MAX_RETRIES times
112                 retryProcessTwo = MAX_RETRIES;
113                 downloadFwFailed = true; // Download Firmware Failed
114                 break;
115             }
116 
117             // Reset ISP status
118             if (writeIspStatusReset())
119             {
120                 // Start PSU frimware download.
121                 if (downloadPsuFirmware())
122                 {
123                     if (!verifyDownloadFWStatus())
124                     {
125                         downloadFwFailed = true;
126                         continue;
127                     }
128                 }
129                 else
130                 {
131                     if (retryProcessOne == MAX_RETRIES)
132                     {
133                         // no more retries report serviceable error
134                         createServiceableError(
135                             "serviceableError: Download firmware failed");
136                         ispReboot(); // Try to set PSU to normal mode
137                     }
138                     downloadFwFailed = true;
139                     continue;
140                 }
141             }
142             else
143             {
144                 // ISP Status Reset failed MAX_RETRIES times
145                 retryProcessTwo = MAX_RETRIES;
146                 downloadFwFailed = true;
147                 break;
148             }
149 
150             ispReboot();
151             if (ispReadRebootStatus() && !downloadFwFailed)
152             {
153                 // Download completed successful
154                 retryProcessTwo = MAX_RETRIES;
155                 break;
156             }
157             else
158             {
159                 if ((retryProcessOne < (MAX_RETRIES - 1)) &&
160                     (retryProcessTwo < (MAX_RETRIES - 1)))
161                 {
162                     downloadFwFailed = false;
163                     break;
164                 }
165             }
166         }
167     }
168     if (downloadFwFailed)
169     {
170         return 1;
171     }
172     return 0; // Update successful
173 }
174 
writeIspKey()175 bool AeiUpdater::writeIspKey()
176 {
177     // ISP Key to unlock programming mode ( ASCII for "artY").
178     constexpr std::array<uint8_t, 4> unlockData = {0x61, 0x72, 0x74,
179                                                    0x59}; // ISP Key "artY"
180     try
181     {
182         // Send ISP Key to unlock device for firmware update
183         i2cInterface->write(KEY_REGISTER, unlockData.size(), unlockData.data());
184         return true;
185     }
186     catch (const std::exception& e)
187     {
188         // Log failure if I2C write fails.
189         lg2::error("I2C write failed: {ERROR}", "ERROR", e);
190         return false;
191     }
192 }
193 
writeIspMode()194 bool AeiUpdater::writeIspMode()
195 {
196     // Attempt to set device in ISP mode with retries.
197     uint8_t ispStatus = 0x0;
198     for (int retry = 0; retry < MAX_RETRIES; ++retry)
199     {
200         try
201         {
202             // Write command to enter ISP mode.
203             i2cInterface->write(STATUS_REGISTER, CMD_BOOT_ISP);
204             // Delay to allow status register update.
205             updater::internal::delay(ISP_STATUS_DELAY);
206             // Read back status register to confirm ISP mode is active.
207             i2cInterface->read(STATUS_REGISTER, ispStatus);
208 
209             if (ispStatus & B_ISP_MODE)
210             {
211                 lg2::info("Set ISP Mode");
212                 return true;
213             }
214         }
215         catch (const std::exception& e)
216         {
217             // Log I2C error with each retry attempt.
218             lg2::error("I2C error during ISP mode write/read: {ERROR}", "ERROR",
219                        e);
220         }
221     }
222     createServiceableError("createServiceableError: ISP Status failed");
223     lg2::error("Failed to set ISP Mode");
224     return false; // Failed to set ISP Mode after retries
225 }
226 
writeIspStatusReset()227 bool AeiUpdater::writeIspStatusReset()
228 {
229     // Reset ISP status register before firmware download.
230     uint8_t ispStatus = 0;
231     for (int retry = 0; retry < MAX_RETRIES; retry++)
232     {
233         try
234         {
235             i2cInterface->write(STATUS_REGISTER,
236                                 CMD_RESET_SEQ); // Start reset sequence.
237             retry = MAX_RETRIES;
238         }
239         catch (const std::exception& e)
240         {
241             // Log any errors encountered during reset sequence.
242             lg2::error("I2C Write ISP reset failed: {ERROR}", "ERROR", e);
243         }
244     }
245 
246     for (int retry = 0; retry < MAX_RETRIES; ++retry)
247     {
248         try
249         {
250             i2cInterface->read(STATUS_REGISTER, ispStatus);
251             if (ispStatus == B_ISP_MODE)
252             {
253                 lg2::info("Read/Write ISP reset");
254                 return true; // ISP status reset successfully.
255             }
256             i2cInterface->write(STATUS_REGISTER,
257                                 CMD_CLEAR_STATUS); // Clear status if
258                                                    // not reset.
259             lg2::error("Read/Write ISP reset failed");
260         }
261         catch (const std::exception& e)
262         {
263             // Log any errors encountered during reset sequence.
264             lg2::error("I2C Read/Write error during ISP reset: {ERROR}",
265                        "ERROR", e);
266         }
267     }
268     createServiceableError("createServiceableError: Failed to reset ISP");
269     lg2::error("Failed to reset ISP Status");
270     ispReboot();
271     return false;
272 }
273 
getFirmwarePath()274 bool AeiUpdater::getFirmwarePath()
275 {
276     fspath = updater::internal::getFWFilenamePath(getImageDir());
277     if (fspath.empty())
278     {
279         lg2::error("Firmware file path not found");
280         return false;
281     }
282     return true;
283 }
284 
isFirmwareFileValid()285 bool AeiUpdater::isFirmwareFileValid()
286 {
287     if (!updater::internal::validateFWFile(fspath))
288     {
289         lg2::error("Firmware validation failed, fspath={PATH}", "PATH", fspath);
290         return false;
291     }
292     return true;
293 }
294 
openFirmwareFile()295 std::unique_ptr<std::ifstream> AeiUpdater::openFirmwareFile()
296 {
297     auto inputFile = updater::internal::openFirmwareFile(fspath);
298     if (!inputFile)
299     {
300         lg2::error("Failed to open firmware file");
301     }
302     return inputFile;
303 }
304 
readFirmwareBlock(std::ifstream & file,const size_t & bytesToRead)305 std::vector<uint8_t> AeiUpdater::readFirmwareBlock(std::ifstream& file,
306                                                    const size_t& bytesToRead)
307 {
308     auto block = updater::internal::readFirmwareBytes(file, bytesToRead);
309     return block;
310 }
311 
prepareCommandBlock(const std::vector<uint8_t> & dataBlockRead)312 void AeiUpdater::prepareCommandBlock(const std::vector<uint8_t>& dataBlockRead)
313 {
314     cmdBlockWrite.clear(); // Clear cmdBlockWrite before use
315     // Assign new values to cmdBlockWrite
316     cmdBlockWrite.push_back(ISP_MEMORY_REGISTER);
317     cmdBlockWrite.push_back(BLOCK_WRITE_SIZE);
318     cmdBlockWrite.insert(cmdBlockWrite.end(), byteSwappedIndex.begin(),
319                          byteSwappedIndex.end());
320     cmdBlockWrite.insert(cmdBlockWrite.end(), dataBlockRead.begin(),
321                          dataBlockRead.end());
322 
323     // Resize to ensure it matches BLOCK_WRITE_SIZE + 1 and append CRC
324     if (cmdBlockWrite.size() != BLOCK_WRITE_SIZE + 1)
325     {
326         cmdBlockWrite.resize(BLOCK_WRITE_SIZE + 1, 0xFF);
327     }
328     cmdBlockWrite.push_back(updater::internal::calculateCRC8(cmdBlockWrite));
329     // Remove the F9 and byte count
330     cmdBlockWrite.erase(cmdBlockWrite.begin(), cmdBlockWrite.begin() + 2);
331 }
332 
downloadPsuFirmware()333 bool AeiUpdater::downloadPsuFirmware()
334 {
335     // Open firmware file
336     auto inputFile = openFirmwareFile();
337     if (!inputFile)
338     {
339         lg2::error("Unable to open firmware file {FILE}", "FILE", fspath);
340         return false;
341     }
342 
343     // Read and process firmware file in blocks
344     size_t bytesRead = 0;
345     const auto fileSize = std::filesystem::file_size(fspath);
346     bool downloadFailed = false;
347     byteSwappedIndex =
348         updater::internal::bigEndianToLittleEndian(START_SEQUENCE_INDEX);
349     int writeBlockDelay = MEM_WRITE_DELAY;
350 
351     while ((bytesRead < fileSize) && !downloadFailed)
352     {
353         // Read a block of firmware data
354         auto dataRead = readFirmwareBlock(*inputFile, FW_READ_BLOCK_SIZE);
355         bytesRead += dataRead.size();
356 
357         // Prepare command block with the current index and data
358         prepareCommandBlock(dataRead);
359 
360         // Perform I2C write/read with retries
361         uint8_t readData[I2C_SMBUS_BLOCK_MAX] = {};
362         downloadFailed = !performI2cWriteReadWithRetries(
363             ISP_MEMORY_REGISTER, EXPECTED_MEM_READ_REPLY, readData, MAX_RETRIES,
364             writeBlockDelay);
365 
366         // Adjust delay after first write block
367         writeBlockDelay = MEM_STRETCH_DELAY;
368     }
369 
370     inputFile->close();
371 
372     // Log final download status
373     if (downloadFailed)
374     {
375         lg2::error(
376             "Firmware download failed after retries at FW block {BYTESREAD}",
377             "BYTESREAD", bytesRead);
378         return false; // Failed
379     }
380 
381     return true;
382 }
383 
performI2cWriteReadWithRetries(uint8_t regAddr,const uint8_t expectedReadSize,uint8_t * readData,const int retries,const int delayTime)384 bool AeiUpdater::performI2cWriteReadWithRetries(
385     uint8_t regAddr, const uint8_t expectedReadSize, uint8_t* readData,
386     const int retries, const int delayTime)
387 {
388     for (int i = 0; i < retries; ++i)
389     {
390         uint8_t readReplySize = 0;
391         try
392         {
393             performI2cWriteRead(regAddr, readReplySize, readData, delayTime);
394             if ((readData[STATUS_CML_INDEX] == 0 ||
395                  // The first firmware data packet sent to the PSU have a
396                  // response of 0x80 which indicates firmware update in
397                  // progress. If retry to send the first packet again reply will
398                  // be 0.
399                  (readData[STATUS_CML_INDEX] == 0x80 &&
400                   delayTime == MEM_WRITE_DELAY)) &&
401                 (readReplySize == expectedReadSize) &&
402                 !std::equal(readData, readData + 4, byteSwappedIndex.begin()))
403             {
404                 std::copy(readData, readData + 4, byteSwappedIndex.begin());
405                 return true;
406             }
407             else
408             {
409                 uint32_t littleEndianValue =
410                     *reinterpret_cast<uint32_t*>(readData);
411                 uint32_t bigEndianValue =
412                     ((littleEndianValue & 0x000000FF) << 24) |
413                     ((littleEndianValue & 0x0000FF00) << 8) |
414                     ((littleEndianValue & 0x00FF0000) >> 8) |
415                     ((littleEndianValue & 0xFF000000) >> 24);
416                 lg2::error("Write/read block {NUM} failed", "NUM",
417                            bigEndianValue);
418             }
419         }
420         catch (const std::exception& e)
421         {
422             lg2::error("I2C write/read block failed: {ERROR}", "ERROR", e);
423         }
424     }
425     return false;
426 }
427 
performI2cWriteRead(uint8_t regAddr,uint8_t & readReplySize,uint8_t * readData,const int & delayTime)428 void AeiUpdater::performI2cWriteRead(uint8_t regAddr, uint8_t& readReplySize,
429                                      uint8_t* readData, const int& delayTime)
430 {
431     i2cInterface->processCall(regAddr, cmdBlockWrite.size(),
432                               cmdBlockWrite.data(), readReplySize, readData);
433 
434     if (delayTime != 0)
435     {
436         updater::internal::delay(delayTime);
437     }
438 }
439 
verifyDownloadFWStatus()440 bool AeiUpdater::verifyDownloadFWStatus()
441 {
442     try
443     {
444         // Read and verify firmware download status.
445         uint8_t status = 0;
446         i2cInterface->read(STATUS_REGISTER, status);
447         if (status != B_ISP_MODE_CHKSUM_GOOD)
448         {
449             lg2::error("Firmware download failed - status: {ERR}", "ERR",
450                        status);
451 
452             return false; // Failed checksum
453         }
454         return true;
455     }
456     catch (const std::exception& e)
457     {
458         lg2::error("I2C read status register failed: {ERROR}", "ERROR", e);
459     }
460     return false; // Failed
461 }
462 
ispReboot()463 void AeiUpdater::ispReboot()
464 {
465     updater::internal::delay(
466         MEM_COMPLETE_DELAY); // Delay before starting the reboot process
467 
468     try
469     {
470         // Write reboot command to the status register
471         i2cInterface->write(STATUS_REGISTER, CMD_BOOT_PWR);
472 
473         updater::internal::delay(
474             REBOOT_DELAY); // Add delay after writing reboot command
475     }
476     catch (const std::exception& e)
477     {
478         lg2::error("I2C write error during reboot: {ERROR}", "ERROR", e);
479     }
480 }
481 
ispReadRebootStatus()482 bool AeiUpdater::ispReadRebootStatus()
483 {
484     try
485     {
486         // Read from the status register to verify reboot
487         uint8_t data = 1; // Initialize data to a non-zero value
488         i2cInterface->read(STATUS_REGISTER, data);
489 
490         // If the reboot was successful, the read data should be 0
491         if (data == SUCCESSFUL_ISP_REBOOT_STATUS)
492         {
493             lg2::info("ISP Status Reboot successful.");
494             return true;
495         }
496     }
497     catch (const std::exception& e)
498     {
499         lg2::error("I2C read error during reboot attempt: {ERROR}", "ERROR", e);
500     }
501 
502     // If we reach here, all retries have failed
503     lg2::error("Failed to reboot ISP status after max retries.");
504     return false;
505 }
506 
507 } // namespace aeiUpdater
508