blob: ffe2d1d29d1bdb7910fa327736fe3e053b9f8c00 [file] [log] [blame]
Faisal Awada5a582d32024-11-15 14:11:44 -06001/**
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
Faisal Awada8dca5072024-11-23 08:07:38 -060028#include <fstream>
29
Faisal Awada5a582d32024-11-15 14:11:44 -060030namespace aeiUpdater
31{
Jayanth Othayothf0c0c472024-12-07 02:47:10 -060032
Faisal Awada5a582d32024-11-15 14:11:44 -060033constexpr uint8_t MAX_RETRIES = 0x02; // Constants for retry limits
34
35constexpr int ISP_STATUS_DELAY = 1200; // Delay for ISP status check (1.2s)
36constexpr int MEM_WRITE_DELAY = 5000; // Memory write delay (5s)
Faisal Awada5ace9fb2025-01-07 13:26:25 -060037constexpr int MEM_STRETCH_DELAY = 1; // Delay between writes (1ms)
Faisal Awada5a582d32024-11-15 14:11:44 -060038constexpr int MEM_COMPLETE_DELAY = 2000; // Delay before completion (2s)
39constexpr int REBOOT_DELAY = 8000; // Delay for reboot (8s)
40
Faisal Awada5ace9fb2025-01-07 13:26:25 -060041constexpr uint8_t I2C_SMBUS_BLOCK_MAX = 0x20; // Max Read bytes from PSU
42constexpr uint8_t FW_READ_BLOCK_SIZE = 0x20; // Read bytes from FW file
43constexpr uint8_t BLOCK_WRITE_SIZE = 0x25; // I2C block write size
44
45constexpr uint8_t START_SEQUENCE_INDEX = 0x1; // Starting sequence index
46constexpr uint8_t STATUS_CML_INDEX = 0x4; // Status CML read index
47constexpr uint8_t EXPECTED_MEM_READ_REPLY = 0x5; // Expected memory read reply
48 // size after write data
Faisal Awada5a582d32024-11-15 14:11:44 -060049
50// Register addresses for commands.
51constexpr uint8_t KEY_REGISTER = 0xF6; // Key register
52constexpr uint8_t STATUS_REGISTER = 0xF7; // Status register
53constexpr uint8_t ISP_MEMORY_REGISTER = 0xF9; // ISP memory register
54
55// Define AEI ISP status register commands
56constexpr uint8_t CMD_CLEAR_STATUS = 0x0; // Clear the status register
57constexpr uint8_t CMD_RESET_SEQ = 0x01; // This command will reset ISP OS for
58 // another attempt of a sequential
59 // programming operation.
60constexpr uint8_t CMD_BOOT_ISP = 0x02; // Boot the In-System Programming System.
61constexpr uint8_t CMD_BOOT_PWR = 0x03; // Attempt to boot the Power Management
62 // OS.
63
64// Define AEI ISP response status bit
Faisal Awada5ace9fb2025-01-07 13:26:25 -060065constexpr uint8_t B_ISP_MODE = 0x40; // ISP mode
Faisal Awada5a582d32024-11-15 14:11:44 -060066constexpr uint8_t B_ISP_MODE_CHKSUM_GOOD = 0x41; // ISP mode & good checksum.
Faisal Awada8dca5072024-11-23 08:07:38 -060067constexpr uint8_t SUCCESSFUL_ISP_REBOOT_STATUS = 0x0; // Successful ISP reboot
68 // status
Faisal Awada5a582d32024-11-15 14:11:44 -060069using namespace phosphor::logging;
70namespace util = phosphor::power::util;
71
72int AeiUpdater::doUpdate()
73{
74 i2cInterface = Updater::getI2C();
75 if (i2cInterface == nullptr)
76 {
77 throw std::runtime_error("I2C interface error");
78 }
Faisal Awada5ace9fb2025-01-07 13:26:25 -060079 bool downloadFwFailed = false; // Download Firmware status
80 int retryProcessTwo(0);
81 int retryProcessOne(0);
82 while ((retryProcessTwo < MAX_RETRIES) && (retryProcessOne < MAX_RETRIES))
Faisal Awada5a582d32024-11-15 14:11:44 -060083 {
Faisal Awada5ace9fb2025-01-07 13:26:25 -060084 retryProcessTwo++;
85 // Write AEI PSU ISP key
86 if (!writeIspKey())
87 {
88 lg2::error("Failed to set ISP Key");
89 downloadFwFailed = true; // Download Firmware status
90 continue;
91 }
Faisal Awada5a582d32024-11-15 14:11:44 -060092
Faisal Awada5ace9fb2025-01-07 13:26:25 -060093 while (retryProcessOne < MAX_RETRIES)
94 {
95 downloadFwFailed = false; // Download Firmware status
96 retryProcessOne++;
97 // Set ISP mode
98 if (!writeIspMode())
99 {
100 // Write ISP Mode failed MAX_RETRIES times
101 retryProcessTwo = MAX_RETRIES;
102 downloadFwFailed = true; // Download Firmware Failed
103 break;
104 }
105
106 // Reset ISP status
107 if (writeIspStatusReset())
108 {
109 // Start PSU frimware download.
110 if (downloadPsuFirmware())
111 {
112 if (!verifyDownloadFWStatus())
113 {
114 downloadFwFailed = true;
115 continue;
116 }
117 }
118 else
119 {
120 downloadFwFailed = true;
121 continue;
122 }
123 }
124 else
125 {
126 // ISP Status Reset failed MAX_RETRIES times
127 retryProcessTwo = MAX_RETRIES;
128 downloadFwFailed = true;
129 break;
130 }
131
132 ispReboot();
133 if (ispReadRebootStatus() && !downloadFwFailed)
134 {
135 // Download completed successful
136 retryProcessTwo = MAX_RETRIES;
137 break;
138 }
139 else
140 {
141 if ((retryProcessOne < (MAX_RETRIES - 1)) &&
142 (retryProcessTwo < (MAX_RETRIES - 1)))
143 {
144 downloadFwFailed = false;
145 break;
146 }
147 }
148 }
149 }
150 if (downloadFwFailed)
Faisal Awada5a582d32024-11-15 14:11:44 -0600151 {
152 return 1;
153 }
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600154 return 0; // Update successful
Faisal Awada5a582d32024-11-15 14:11:44 -0600155}
156
157bool AeiUpdater::writeIspKey()
158{
159 // ISP Key to unlock programming mode ( ASCII for "artY").
160 constexpr std::array<uint8_t, 4> unlockData = {0x61, 0x72, 0x74,
161 0x59}; // ISP Key "artY"
162 try
163 {
164 // Send ISP Key to unlock device for firmware update
165 i2cInterface->write(KEY_REGISTER, unlockData.size(), unlockData.data());
166 return true;
167 }
168 catch (const std::exception& e)
169 {
170 // Log failure if I2C write fails.
Faisal Awada8dca5072024-11-23 08:07:38 -0600171 lg2::error("I2C write failed: {ERROR}", "ERROR", e);
Faisal Awada5a582d32024-11-15 14:11:44 -0600172 return false;
173 }
174}
175
176bool AeiUpdater::writeIspMode()
177{
178 // Attempt to set device in ISP mode with retries.
179 uint8_t ispStatus = 0x0;
180 for (int retry = 0; retry < MAX_RETRIES; ++retry)
181 {
182 try
183 {
184 // Write command to enter ISP mode.
185 i2cInterface->write(STATUS_REGISTER, CMD_BOOT_ISP);
186 // Delay to allow status register update.
187 updater::internal::delay(ISP_STATUS_DELAY);
188 // Read back status register to confirm ISP mode is active.
189 i2cInterface->read(STATUS_REGISTER, ispStatus);
190
191 if (ispStatus & B_ISP_MODE)
192 {
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600193 lg2::info("Set ISP Mode");
Faisal Awada5a582d32024-11-15 14:11:44 -0600194 return true;
195 }
196 }
197 catch (const std::exception& e)
198 {
199 // Log I2C error with each retry attempt.
200 lg2::error("I2C error during ISP mode write/read: {ERROR}", "ERROR",
Faisal Awada8dca5072024-11-23 08:07:38 -0600201 e);
Faisal Awada5a582d32024-11-15 14:11:44 -0600202 }
203 }
204 lg2::error("Failed to set ISP Mode");
205 return false; // Failed to set ISP Mode after retries
206}
207
208bool AeiUpdater::writeIspStatusReset()
209{
210 // Reset ISP status register before firmware download.
211 uint8_t ispStatus = 0;
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600212 for (int retry = 0; retry < MAX_RETRIES; retry++)
Faisal Awada5a582d32024-11-15 14:11:44 -0600213 {
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600214 try
215 {
216 i2cInterface->write(STATUS_REGISTER,
217 CMD_RESET_SEQ); // Start reset sequence.
218 retry = MAX_RETRIES;
219 }
220 catch (const std::exception& e)
221 {
222 // Log any errors encountered during reset sequence.
223 lg2::error("I2C Write ISP reset failed: {ERROR}", "ERROR", e);
224 }
225 }
226
227 for (int retry = 0; retry < MAX_RETRIES; ++retry)
228 {
229 try
Faisal Awada5a582d32024-11-15 14:11:44 -0600230 {
231 i2cInterface->read(STATUS_REGISTER, ispStatus);
232 if (ispStatus == B_ISP_MODE)
233 {
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600234 lg2::info("Read/Write ISP reset");
Faisal Awada5a582d32024-11-15 14:11:44 -0600235 return true; // ISP status reset successfully.
236 }
237 i2cInterface->write(STATUS_REGISTER,
238 CMD_CLEAR_STATUS); // Clear status if
239 // not reset.
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600240 lg2::error("Read/Write ISP reset failed");
241 }
242 catch (const std::exception& e)
243 {
244 // Log any errors encountered during reset sequence.
245 lg2::error("I2C Read/Write error during ISP reset: {ERROR}",
246 "ERROR", e);
Faisal Awada5a582d32024-11-15 14:11:44 -0600247 }
248 }
Faisal Awada5a582d32024-11-15 14:11:44 -0600249 lg2::error("Failed to reset ISP Status");
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600250 ispReboot();
Faisal Awada5a582d32024-11-15 14:11:44 -0600251 return false;
252}
253
Faisal Awada8dca5072024-11-23 08:07:38 -0600254std::string AeiUpdater::getFirmwarePath()
255{
256 const std::string fspath =
257 updater::internal::getFWFilenamePath(getImageDir());
258 if (fspath.empty())
259 {
260 lg2::error("Firmware file path not found");
261 }
262 return fspath;
263}
264
265bool AeiUpdater::isFirmwareFileValid(const std::string& fspath)
266{
267 if (!updater::internal::validateFWFile(fspath))
268 {
269 lg2::error("Firmware validation failed");
270 return false;
271 }
272 return true;
273}
274
Patrick Williams92261f82025-02-01 08:22:34 -0500275std::unique_ptr<std::ifstream> AeiUpdater::openFirmwareFile(
276 const std::string& fspath)
Faisal Awada8dca5072024-11-23 08:07:38 -0600277{
278 auto inputFile = updater::internal::openFirmwareFile(fspath);
279 if (!inputFile)
280 {
281 lg2::error("Failed to open firmware file");
282 }
283 return inputFile;
284}
285
286std::vector<uint8_t> AeiUpdater::readFirmwareBlock(std::ifstream& file,
287 const size_t& bytesToRead)
288{
289 auto block = updater::internal::readFirmwareBytes(file, bytesToRead);
290 return block;
291}
292
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600293void AeiUpdater::prepareCommandBlock(const std::vector<uint8_t>& dataBlockRead)
Faisal Awada8dca5072024-11-23 08:07:38 -0600294{
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600295 cmdBlockWrite.clear(); // Clear cmdBlockWrite before use
296 // Assign new values to cmdBlockWrite
297 cmdBlockWrite.push_back(ISP_MEMORY_REGISTER);
298 cmdBlockWrite.push_back(BLOCK_WRITE_SIZE);
Faisal Awada8dca5072024-11-23 08:07:38 -0600299 cmdBlockWrite.insert(cmdBlockWrite.end(), byteSwappedIndex.begin(),
300 byteSwappedIndex.end());
301 cmdBlockWrite.insert(cmdBlockWrite.end(), dataBlockRead.begin(),
302 dataBlockRead.end());
303
304 // Resize to ensure it matches BLOCK_WRITE_SIZE + 1 and append CRC
305 if (cmdBlockWrite.size() != BLOCK_WRITE_SIZE + 1)
306 {
307 cmdBlockWrite.resize(BLOCK_WRITE_SIZE + 1, 0xFF);
308 }
309 cmdBlockWrite.push_back(updater::internal::calculateCRC8(cmdBlockWrite));
310 // Remove the F9 and byte count
311 cmdBlockWrite.erase(cmdBlockWrite.begin(), cmdBlockWrite.begin() + 2);
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600312}
Faisal Awada8dca5072024-11-23 08:07:38 -0600313
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600314bool AeiUpdater::downloadPsuFirmware()
315{
316 // Get firmware path
317 const std::string fspath = getFirmwarePath();
318 if (fspath.empty())
319 {
320 lg2::error("Unable to find path");
321 return false;
322 }
323 // Validate firmware file
324 if (!isFirmwareFileValid(fspath))
325 {
326 lg2::error("Invalid file path");
327 return false;
328 }
329
330 // Open firmware file
331 auto inputFile = openFirmwareFile(fspath);
332 if (!inputFile)
333 {
334 lg2::error("Unable to open firmware file {FILE}", "FILE", fspath);
335 return false;
336 }
337
338 // Read and process firmware file in blocks
339 size_t bytesRead = 0;
340 const auto fileSize = std::filesystem::file_size(fspath);
341 bool downloadFailed = false;
342 byteSwappedIndex =
343 updater::internal::bigEndianToLittleEndian(START_SEQUENCE_INDEX);
344 int writeBlockDelay = MEM_WRITE_DELAY;
345
346 while ((bytesRead < fileSize) && !downloadFailed)
347 {
348 // Read a block of firmware data
349 auto dataRead = readFirmwareBlock(*inputFile, FW_READ_BLOCK_SIZE);
350 bytesRead += dataRead.size();
351
352 // Prepare command block with the current index and data
353 prepareCommandBlock(dataRead);
354
355 // Perform I2C write/read with retries
356 uint8_t readData[I2C_SMBUS_BLOCK_MAX] = {};
357 downloadFailed = !performI2cWriteReadWithRetries(
358 ISP_MEMORY_REGISTER, EXPECTED_MEM_READ_REPLY, readData, MAX_RETRIES,
359 writeBlockDelay);
360
361 // Adjust delay after first write block
362 writeBlockDelay = MEM_STRETCH_DELAY;
363 }
364
365 inputFile->close();
366
367 // Log final download status
368 if (downloadFailed)
369 {
370 lg2::error(
371 "Firmware download failed after retries at FW block {BYTESREAD}",
372 "BYTESREAD", bytesRead);
373 return false; // Failed
374 }
375
376 return true;
377}
378
379bool AeiUpdater::performI2cWriteReadWithRetries(
380 uint8_t regAddr, const uint8_t expectedReadSize, uint8_t* readData,
381 const int retries, const int delayTime)
382{
383 for (int i = 0; i < retries; ++i)
384 {
385 uint8_t readReplySize = 0;
386 try
387 {
388 performI2cWriteRead(regAddr, readReplySize, readData, delayTime);
389 if ((readData[STATUS_CML_INDEX] == 0) &&
390 (readReplySize == expectedReadSize) &&
391 !std::equal(readData, readData + 4, byteSwappedIndex.begin()))
392 {
393 std::copy(readData, readData + 4, byteSwappedIndex.begin());
394 return true;
395 }
396 else
397 {
398 lg2::error("I2C write/read block failed");
399 }
400 }
401 catch (const std::exception& e)
402 {
403 lg2::error("I2C write/read block failed: {ERROR}", "ERROR", e);
404 }
405 }
406 return false;
407}
408
409void AeiUpdater::performI2cWriteRead(uint8_t regAddr, uint8_t& readReplySize,
410 uint8_t* readData, const int& delayTime)
411{
412 i2cInterface->processCall(regAddr, cmdBlockWrite.size(),
413 cmdBlockWrite.data(), readReplySize, readData);
414
415 if (delayTime != 0)
416 {
417 updater::internal::delay(delayTime);
418 }
419}
420
421bool AeiUpdater::verifyDownloadFWStatus()
422{
423 try
424 {
425 // Read and verify firmware download status.
426 uint8_t status = 0;
427 i2cInterface->read(STATUS_REGISTER, status);
428 if (status != B_ISP_MODE_CHKSUM_GOOD)
429 {
430 lg2::error("Firmware download failed - status: {ERR}", "ERR",
431 status);
432
433 return false; // Failed checksum
434 }
435 return true;
436 }
437 catch (const std::exception& e)
438 {
439 lg2::error("I2C read status register failed: {ERROR}", "ERROR", e);
440 }
441 return false; // Failed
Faisal Awada8dca5072024-11-23 08:07:38 -0600442}
443
444void AeiUpdater::ispReboot()
445{
446 updater::internal::delay(
447 MEM_COMPLETE_DELAY); // Delay before starting the reboot process
448
449 try
450 {
451 // Write reboot command to the status register
452 i2cInterface->write(STATUS_REGISTER, CMD_BOOT_PWR);
453
454 updater::internal::delay(
455 REBOOT_DELAY); // Add delay after writing reboot command
456 }
457 catch (const std::exception& e)
458 {
459 lg2::error("I2C write error during reboot: {ERROR}", "ERROR", e);
460 }
461}
462
463bool AeiUpdater::ispReadRebootStatus()
464{
465 try
466 {
467 // Read from the status register to verify reboot
468 uint8_t data = 1; // Initialize data to a non-zero value
469 i2cInterface->read(STATUS_REGISTER, data);
470
Faisal Awada8dca5072024-11-23 08:07:38 -0600471 // If the reboot was successful, the read data should be 0
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600472 if (data == SUCCESSFUL_ISP_REBOOT_STATUS)
Faisal Awada8dca5072024-11-23 08:07:38 -0600473 {
474 lg2::info("ISP Status Reboot successful.");
475 return true;
476 }
477 }
478 catch (const std::exception& e)
479 {
480 lg2::error("I2C read error during reboot attempt: {ERROR}", "ERROR", e);
481 }
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600482
483 // If we reach here, all retries have failed
484 lg2::error("Failed to reboot ISP status after max retries.");
Faisal Awada8dca5072024-11-23 08:07:38 -0600485 return false;
486}
487
Faisal Awada5a582d32024-11-15 14:11:44 -0600488} // namespace aeiUpdater