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