blob: a98a1d8c6355ff24a3a3221c238ceaf6abb6c93c [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"
Faisal Awada57fb6642025-02-21 16:52:54 -060025#include "utils.hpp"
Faisal Awada5a582d32024-11-15 14:11:44 -060026
27#include <phosphor-logging/lg2.hpp>
28
Faisal Awada8dca5072024-11-23 08:07:38 -060029#include <fstream>
Faisal Awada57fb6642025-02-21 16:52:54 -060030#include <system_error>
Faisal Awada8dca5072024-11-23 08:07:38 -060031
Faisal Awada5a582d32024-11-15 14:11:44 -060032namespace aeiUpdater
33{
Jayanth Othayothf0c0c472024-12-07 02:47:10 -060034
Faisal Awada5a582d32024-11-15 14:11:44 -060035constexpr uint8_t MAX_RETRIES = 0x02; // Constants for retry limits
36
37constexpr int ISP_STATUS_DELAY = 1200; // Delay for ISP status check (1.2s)
38constexpr int MEM_WRITE_DELAY = 5000; // Memory write delay (5s)
Faisal Awada5ace9fb2025-01-07 13:26:25 -060039constexpr int MEM_STRETCH_DELAY = 1; // Delay between writes (1ms)
Faisal Awada5a582d32024-11-15 14:11:44 -060040constexpr int MEM_COMPLETE_DELAY = 2000; // Delay before completion (2s)
41constexpr int REBOOT_DELAY = 8000; // Delay for reboot (8s)
42
Faisal Awada5ace9fb2025-01-07 13:26:25 -060043constexpr uint8_t I2C_SMBUS_BLOCK_MAX = 0x20; // Max Read bytes from PSU
44constexpr uint8_t FW_READ_BLOCK_SIZE = 0x20; // Read bytes from FW file
45constexpr uint8_t BLOCK_WRITE_SIZE = 0x25; // I2C block write size
46
47constexpr uint8_t START_SEQUENCE_INDEX = 0x1; // Starting sequence index
48constexpr uint8_t STATUS_CML_INDEX = 0x4; // Status CML read index
49constexpr uint8_t EXPECTED_MEM_READ_REPLY = 0x5; // Expected memory read reply
50 // size after write data
Faisal Awada5a582d32024-11-15 14:11:44 -060051
52// Register addresses for commands.
53constexpr uint8_t KEY_REGISTER = 0xF6; // Key register
54constexpr uint8_t STATUS_REGISTER = 0xF7; // Status register
55constexpr uint8_t ISP_MEMORY_REGISTER = 0xF9; // ISP memory register
56
57// Define AEI ISP status register commands
58constexpr uint8_t CMD_CLEAR_STATUS = 0x0; // Clear the status register
59constexpr uint8_t CMD_RESET_SEQ = 0x01; // This command will reset ISP OS for
60 // another attempt of a sequential
61 // programming operation.
62constexpr uint8_t CMD_BOOT_ISP = 0x02; // Boot the In-System Programming System.
63constexpr uint8_t CMD_BOOT_PWR = 0x03; // Attempt to boot the Power Management
64 // OS.
65
66// Define AEI ISP response status bit
Faisal Awada5ace9fb2025-01-07 13:26:25 -060067constexpr uint8_t B_ISP_MODE = 0x40; // ISP mode
Faisal Awada5a582d32024-11-15 14:11:44 -060068constexpr uint8_t B_ISP_MODE_CHKSUM_GOOD = 0x41; // ISP mode & good checksum.
Faisal Awada8dca5072024-11-23 08:07:38 -060069constexpr uint8_t SUCCESSFUL_ISP_REBOOT_STATUS = 0x0; // Successful ISP reboot
70 // status
Faisal Awada5a582d32024-11-15 14:11:44 -060071using namespace phosphor::logging;
72namespace util = phosphor::power::util;
73
74int AeiUpdater::doUpdate()
75{
76 i2cInterface = Updater::getI2C();
Faisal Awada57fb6642025-02-21 16:52:54 -060077 enableEventLogging();
Faisal Awada5a582d32024-11-15 14:11:44 -060078 if (i2cInterface == nullptr)
79 {
Faisal Awada57fb6642025-02-21 16:52:54 -060080 // Report serviceable error
81 std::map<std::string, std::string> additionalData = {
82 {"I2C_INTERFACE", "I2C interface is null pointer."}};
83 // Callout PSU & I2C
84 callOutI2CEventLog(additionalData);
85
Faisal Awada5a582d32024-11-15 14:11:44 -060086 throw std::runtime_error("I2C interface error");
87 }
Faisal Awadaf9b426b2025-01-31 11:44:48 -060088 if (!getFirmwarePath() || !isFirmwareFileValid())
89 {
90 return 1; // No firmware file abort download
91 }
Faisal Awada5ace9fb2025-01-07 13:26:25 -060092 bool downloadFwFailed = false; // Download Firmware status
93 int retryProcessTwo(0);
94 int retryProcessOne(0);
Faisal Awada57fb6642025-02-21 16:52:54 -060095 disableEventLogging();
Faisal Awada5ace9fb2025-01-07 13:26:25 -060096 while ((retryProcessTwo < MAX_RETRIES) && (retryProcessOne < MAX_RETRIES))
Faisal Awada5a582d32024-11-15 14:11:44 -060097 {
Faisal Awada5ace9fb2025-01-07 13:26:25 -060098 // Write AEI PSU ISP key
99 if (!writeIspKey())
100 {
101 lg2::error("Failed to set ISP Key");
102 downloadFwFailed = true; // Download Firmware status
Faisal Awada57fb6642025-02-21 16:52:54 -0600103 break;
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600104 }
Faisal Awada5a582d32024-11-15 14:11:44 -0600105
Faisal Awada57fb6642025-02-21 16:52:54 -0600106 if (retryProcessTwo == (MAX_RETRIES - 1))
107 {
108 enableEventLogging();
109 }
110 retryProcessTwo++;
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600111 while (retryProcessOne < MAX_RETRIES)
112 {
113 downloadFwFailed = false; // Download Firmware status
114 retryProcessOne++;
115 // Set ISP mode
116 if (!writeIspMode())
117 {
118 // Write ISP Mode failed MAX_RETRIES times
119 retryProcessTwo = MAX_RETRIES;
120 downloadFwFailed = true; // Download Firmware Failed
121 break;
122 }
123
124 // Reset ISP status
125 if (writeIspStatusReset())
126 {
Faisal Awada57fb6642025-02-21 16:52:54 -0600127 // Start PSU firmware download.
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600128 if (downloadPsuFirmware())
129 {
130 if (!verifyDownloadFWStatus())
131 {
132 downloadFwFailed = true;
133 continue;
134 }
135 }
136 else
137 {
Faisal Awada57fb6642025-02-21 16:52:54 -0600138 // One of the block write commands failed, retry download
139 // procedure one time starting with re-writing initial ISP
140 // mode. If it fails again, log serviceable error.
Faisal Awadaf9b426b2025-01-31 11:44:48 -0600141 if (retryProcessOne == MAX_RETRIES)
142 {
Faisal Awada57fb6642025-02-21 16:52:54 -0600143 // Callout PSU failed to update FW
144 std::map<std::string, std::string> additionalData = {
145 {"UPDATE_FAILED", "Download firmware failed"}};
146
147 callOutPsuEventLog(additionalData);
Faisal Awadaf9b426b2025-01-31 11:44:48 -0600148 ispReboot(); // Try to set PSU to normal mode
149 }
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600150 downloadFwFailed = true;
151 continue;
152 }
153 }
154 else
155 {
156 // ISP Status Reset failed MAX_RETRIES times
157 retryProcessTwo = MAX_RETRIES;
158 downloadFwFailed = true;
159 break;
160 }
161
162 ispReboot();
Faisal Awada57fb6642025-02-21 16:52:54 -0600163
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600164 if (ispReadRebootStatus() && !downloadFwFailed)
165 {
166 // Download completed successful
167 retryProcessTwo = MAX_RETRIES;
168 break;
169 }
170 else
171 {
Faisal Awada57fb6642025-02-21 16:52:54 -0600172 // Retry the whole download process starting with the key and
173 // if fails again then report event log
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600174 if ((retryProcessOne < (MAX_RETRIES - 1)) &&
175 (retryProcessTwo < (MAX_RETRIES - 1)))
176 {
177 downloadFwFailed = false;
178 break;
179 }
180 }
181 }
182 }
183 if (downloadFwFailed)
Faisal Awada5a582d32024-11-15 14:11:44 -0600184 {
185 return 1;
186 }
Faisal Awada57fb6642025-02-21 16:52:54 -0600187 enableEventLogging();
188 bindUnbind(true);
189 updater::internal::delay(100);
190 callOutGoodEventLog();
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600191 return 0; // Update successful
Faisal Awada5a582d32024-11-15 14:11:44 -0600192}
193
194bool AeiUpdater::writeIspKey()
195{
196 // ISP Key to unlock programming mode ( ASCII for "artY").
197 constexpr std::array<uint8_t, 4> unlockData = {0x61, 0x72, 0x74,
198 0x59}; // ISP Key "artY"
Faisal Awada57fb6642025-02-21 16:52:54 -0600199 for (int retry = 0; retry < MAX_RETRIES; ++retry)
Faisal Awada5a582d32024-11-15 14:11:44 -0600200 {
Faisal Awada57fb6642025-02-21 16:52:54 -0600201 try
202 {
203 // Send ISP Key to unlock device for firmware update
204 i2cInterface->write(KEY_REGISTER, unlockData.size(),
205 unlockData.data());
206 disableEventLogging();
207 return true;
208 }
209 catch (const i2c::I2CException& e)
210 {
211 // Log failure if I2C write fails.
212 lg2::error("I2C write failed: {ERROR}", "ERROR", e);
213 std::map<std::string, std::string> additionalData = {
214 {"I2C_ISP_KEY", "ISP key failed due to I2C exception"}};
215 callOutI2CEventLog(additionalData, e.what(), e.errorCode);
216 enableEventLogging(); // enable event logging if fail again call out
217 // PSU & I2C
218 }
219
220 catch (const std::exception& e)
221 {
222 lg2::error("Exception write failed: {ERROR}", "ERROR", e);
223 std::map<std::string, std::string> additionalData = {
224 {"ISP_KEY", "ISP key failed due to exception"},
225 {"EXCEPTION", e.what()}};
226 callOutPsuEventLog(additionalData);
227 enableEventLogging(); // enable Event Logging if fail again call out
228 // PSU
229 }
Faisal Awada5a582d32024-11-15 14:11:44 -0600230 }
Faisal Awada57fb6642025-02-21 16:52:54 -0600231 return false;
Faisal Awada5a582d32024-11-15 14:11:44 -0600232}
233
234bool AeiUpdater::writeIspMode()
235{
236 // Attempt to set device in ISP mode with retries.
237 uint8_t ispStatus = 0x0;
Faisal Awada57fb6642025-02-21 16:52:54 -0600238 uint8_t exceptionCount = 0;
Faisal Awada5a582d32024-11-15 14:11:44 -0600239 for (int retry = 0; retry < MAX_RETRIES; ++retry)
240 {
241 try
242 {
243 // Write command to enter ISP mode.
244 i2cInterface->write(STATUS_REGISTER, CMD_BOOT_ISP);
245 // Delay to allow status register update.
246 updater::internal::delay(ISP_STATUS_DELAY);
247 // Read back status register to confirm ISP mode is active.
248 i2cInterface->read(STATUS_REGISTER, ispStatus);
249
250 if (ispStatus & B_ISP_MODE)
251 {
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600252 lg2::info("Set ISP Mode");
Faisal Awada57fb6642025-02-21 16:52:54 -0600253 disableEventLogging();
Faisal Awada5a582d32024-11-15 14:11:44 -0600254 return true;
255 }
Faisal Awada57fb6642025-02-21 16:52:54 -0600256 enableEventLogging();
257 }
258 catch (const i2c::I2CException& e)
259 {
260 exceptionCount++;
261 // Log I2C error with each retry attempt.
262 lg2::error("I2C exception during ISP mode write/read: {ERROR}",
263 "ERROR", e);
264 if (exceptionCount == MAX_RETRIES)
265 {
266 enableEventLogging();
267 std::map<std::string, std::string> additionalData = {
268 {"I2C_FIRMWARE_STATUS",
269 "Download firmware failed during writeIspMode due to I2C exception"}};
270 // Callout PSU & I2C
271 callOutI2CEventLog(additionalData, e.what(), e.errorCode);
272 return false; // Failed to set ISP Mode
273 }
Faisal Awada5a582d32024-11-15 14:11:44 -0600274 }
275 catch (const std::exception& e)
276 {
Faisal Awada57fb6642025-02-21 16:52:54 -0600277 exceptionCount++;
278 // Log error with each retry attempt.
279 lg2::error("Exception during ISP mode write/read: {ERROR}", "ERROR",
Faisal Awada8dca5072024-11-23 08:07:38 -0600280 e);
Faisal Awada57fb6642025-02-21 16:52:54 -0600281 if (exceptionCount == MAX_RETRIES)
282 {
283 enableEventLogging();
284 std::map<std::string, std::string> additionalData = {
285 {"FIRMWARE_STATUS",
286 "Download firmware failed during writeIspMode due to exception"},
287 {"EXCEPTION", e.what()}};
288 // Callout PSU
289 callOutPsuEventLog(additionalData);
290 return false; // Failed to set ISP Mode
291 }
Faisal Awada5a582d32024-11-15 14:11:44 -0600292 }
293 }
Faisal Awada57fb6642025-02-21 16:52:54 -0600294
295 if (exceptionCount != MAX_RETRIES)
296 {
297 // Callout PSU
298 std::map<std::string, std::string> additionalData = {
299 {"FIRMWARE_STATUS",
300 "Download firmware failed during writeIspMode"}};
301 callOutPsuEventLog(additionalData);
302 }
303
Faisal Awada5a582d32024-11-15 14:11:44 -0600304 lg2::error("Failed to set ISP Mode");
305 return false; // Failed to set ISP Mode after retries
306}
307
308bool AeiUpdater::writeIspStatusReset()
309{
310 // Reset ISP status register before firmware download.
311 uint8_t ispStatus = 0;
Faisal Awada57fb6642025-02-21 16:52:54 -0600312 uint8_t exceptionCount = 0;
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600313 for (int retry = 0; retry < MAX_RETRIES; retry++)
Faisal Awada5a582d32024-11-15 14:11:44 -0600314 {
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600315 try
316 {
317 i2cInterface->write(STATUS_REGISTER,
318 CMD_RESET_SEQ); // Start reset sequence.
319 retry = MAX_RETRIES;
320 }
Faisal Awada57fb6642025-02-21 16:52:54 -0600321 catch (const i2c::I2CException& e)
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600322 {
Faisal Awada57fb6642025-02-21 16:52:54 -0600323 exceptionCount++;
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600324 // Log any errors encountered during reset sequence.
325 lg2::error("I2C Write ISP reset failed: {ERROR}", "ERROR", e);
Faisal Awada57fb6642025-02-21 16:52:54 -0600326 if (exceptionCount == MAX_RETRIES)
327 {
328 enableEventLogging();
329 std::map<std::string, std::string> additionalData = {
330 {"I2C_ISP_RESET", "I2C exception during ISP status reset"}};
331 // Callout PSU & I2C
332 callOutI2CEventLog(additionalData, e.what(), e.errorCode);
333 ispReboot();
334 return false;
335 }
336 }
337 catch (const std::exception& e)
338 {
339 exceptionCount++;
340 // Log any errors encountered during reset sequence.
341 lg2::error("Write ISP reset failed: {ERROR}", "ERROR", e);
342 if (exceptionCount == MAX_RETRIES)
343 {
344 enableEventLogging();
345 std::map<std::string, std::string> additionalData = {
346 {"ISP_RESET", "Exception during ISP status reset"},
347 {"EXCEPTION", e.what()}};
348 // Callout PSU
349 callOutPsuEventLog(additionalData);
350 ispReboot();
351 return false;
352 }
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600353 }
354 }
355
Faisal Awada57fb6642025-02-21 16:52:54 -0600356 exceptionCount = 0;
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600357 for (int retry = 0; retry < MAX_RETRIES; ++retry)
358 {
359 try
Faisal Awada5a582d32024-11-15 14:11:44 -0600360 {
361 i2cInterface->read(STATUS_REGISTER, ispStatus);
362 if (ispStatus == B_ISP_MODE)
363 {
Faisal Awada57fb6642025-02-21 16:52:54 -0600364 lg2::info("write/read ISP reset");
365 disableEventLogging();
Faisal Awada5a582d32024-11-15 14:11:44 -0600366 return true; // ISP status reset successfully.
367 }
368 i2cInterface->write(STATUS_REGISTER,
369 CMD_CLEAR_STATUS); // Clear status if
370 // not reset.
Faisal Awada57fb6642025-02-21 16:52:54 -0600371 lg2::error("Write ISP reset failed");
372 enableEventLogging();
373 }
374 catch (const i2c::I2CException& e)
375 {
376 exceptionCount++;
377 // Log any errors encountered during reset sequence.
378 lg2::error(
379 "I2C Write/Read or Write error during ISP reset: {ERROR}",
380 "ERROR", e);
381 if (exceptionCount == MAX_RETRIES)
382 {
383 enableEventLogging();
384 std::map<std::string, std::string> additionalData = {
385 {"I2C_ISP_READ_STATUS",
386 "I2C exception during read ISP status"}};
387 // Callout PSU & I2C
388 callOutI2CEventLog(additionalData, e.what(), e.errorCode);
389 }
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600390 }
391 catch (const std::exception& e)
392 {
Faisal Awada57fb6642025-02-21 16:52:54 -0600393 exceptionCount++;
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600394 // Log any errors encountered during reset sequence.
Faisal Awada57fb6642025-02-21 16:52:54 -0600395 lg2::error("Write/Read or Write error during ISP reset: {ERROR}",
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600396 "ERROR", e);
Faisal Awada57fb6642025-02-21 16:52:54 -0600397 if (exceptionCount == MAX_RETRIES)
398 {
399 enableEventLogging();
400 std::map<std::string, std::string> additionalData = {
401 {"ISP_READ_STATUS", "Exception during read ISP status"},
402 {"EXCEPTION", e.what()}};
403 // Callout PSU
404 callOutPsuEventLog(additionalData);
405 }
Faisal Awada5a582d32024-11-15 14:11:44 -0600406 }
407 }
Faisal Awada57fb6642025-02-21 16:52:54 -0600408 if (exceptionCount != MAX_RETRIES)
409 {
410 std::map<std::string, std::string> additionalData = {
411 {"ISP_REST_FAILED", "Failed to read ISP expected status"}};
412 // Callout PSU
413 callOutPsuEventLog(additionalData);
414 }
Faisal Awada5a582d32024-11-15 14:11:44 -0600415 lg2::error("Failed to reset ISP Status");
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600416 ispReboot();
Faisal Awada5a582d32024-11-15 14:11:44 -0600417 return false;
418}
419
Faisal Awadaf9b426b2025-01-31 11:44:48 -0600420bool AeiUpdater::getFirmwarePath()
Faisal Awada8dca5072024-11-23 08:07:38 -0600421{
Faisal Awadaf9b426b2025-01-31 11:44:48 -0600422 fspath = updater::internal::getFWFilenamePath(getImageDir());
Faisal Awada8dca5072024-11-23 08:07:38 -0600423 if (fspath.empty())
424 {
Faisal Awada57fb6642025-02-21 16:52:54 -0600425 std::map<std::string, std::string> additionalData = {
426 {"FILE_PATH", "Firmware file path is null"}};
427 // Callout BMC0001 procedure
428 callOutSWEventLog(additionalData);
Faisal Awada8dca5072024-11-23 08:07:38 -0600429 lg2::error("Firmware file path not found");
Faisal Awada8dca5072024-11-23 08:07:38 -0600430 return false;
431 }
432 return true;
433}
434
Faisal Awadaf9b426b2025-01-31 11:44:48 -0600435bool AeiUpdater::isFirmwareFileValid()
436{
437 if (!updater::internal::validateFWFile(fspath))
438 {
Faisal Awada57fb6642025-02-21 16:52:54 -0600439 std::map<std::string, std::string> additionalData = {
440 {"FIRMWARE_VALID",
441 "Firmware validation failed, FW file path = " + fspath}};
442 // Callout BMC0001 procedure
443 callOutSWEventLog(additionalData);
Faisal Awadaf9b426b2025-01-31 11:44:48 -0600444 lg2::error("Firmware validation failed, fspath={PATH}", "PATH", fspath);
445 return false;
446 }
447 return true;
448}
449
450std::unique_ptr<std::ifstream> AeiUpdater::openFirmwareFile()
Faisal Awada8dca5072024-11-23 08:07:38 -0600451{
452 auto inputFile = updater::internal::openFirmwareFile(fspath);
453 if (!inputFile)
454 {
Faisal Awada57fb6642025-02-21 16:52:54 -0600455 std::map<std::string, std::string> additionalData = {
456 {"FIRMWARE_OPEN",
457 "Firmware file failed to open, FW file path = " + fspath}};
458 // Callout BMC0001 procedure
459 callOutSWEventLog(additionalData);
Faisal Awada8dca5072024-11-23 08:07:38 -0600460 lg2::error("Failed to open firmware file");
461 }
462 return inputFile;
463}
464
465std::vector<uint8_t> AeiUpdater::readFirmwareBlock(std::ifstream& file,
466 const size_t& bytesToRead)
467{
468 auto block = updater::internal::readFirmwareBytes(file, bytesToRead);
469 return block;
470}
471
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600472void AeiUpdater::prepareCommandBlock(const std::vector<uint8_t>& dataBlockRead)
Faisal Awada8dca5072024-11-23 08:07:38 -0600473{
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600474 cmdBlockWrite.clear(); // Clear cmdBlockWrite before use
475 // Assign new values to cmdBlockWrite
476 cmdBlockWrite.push_back(ISP_MEMORY_REGISTER);
477 cmdBlockWrite.push_back(BLOCK_WRITE_SIZE);
Faisal Awada8dca5072024-11-23 08:07:38 -0600478 cmdBlockWrite.insert(cmdBlockWrite.end(), byteSwappedIndex.begin(),
479 byteSwappedIndex.end());
480 cmdBlockWrite.insert(cmdBlockWrite.end(), dataBlockRead.begin(),
481 dataBlockRead.end());
482
483 // Resize to ensure it matches BLOCK_WRITE_SIZE + 1 and append CRC
484 if (cmdBlockWrite.size() != BLOCK_WRITE_SIZE + 1)
485 {
486 cmdBlockWrite.resize(BLOCK_WRITE_SIZE + 1, 0xFF);
487 }
488 cmdBlockWrite.push_back(updater::internal::calculateCRC8(cmdBlockWrite));
489 // Remove the F9 and byte count
490 cmdBlockWrite.erase(cmdBlockWrite.begin(), cmdBlockWrite.begin() + 2);
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600491}
Faisal Awada8dca5072024-11-23 08:07:38 -0600492
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600493bool AeiUpdater::downloadPsuFirmware()
494{
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600495 // Open firmware file
Faisal Awadaf9b426b2025-01-31 11:44:48 -0600496 auto inputFile = openFirmwareFile();
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600497 if (!inputFile)
498 {
Faisal Awada57fb6642025-02-21 16:52:54 -0600499 if (isEventLogEnabled())
500 {
501 // Callout BMC0001 procedure
502 std::map<std::string, std::string> additionalData = {
503 {"FW_FAILED_TO_OPEN", "Firmware file failed to open"},
504 {"FW_FILE_PATH", fspath}};
505
506 callOutSWEventLog(additionalData);
507 ispReboot(); // Try to set PSU to normal mode
508 }
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600509 lg2::error("Unable to open firmware file {FILE}", "FILE", fspath);
510 return false;
511 }
512
513 // Read and process firmware file in blocks
514 size_t bytesRead = 0;
515 const auto fileSize = std::filesystem::file_size(fspath);
516 bool downloadFailed = false;
517 byteSwappedIndex =
518 updater::internal::bigEndianToLittleEndian(START_SEQUENCE_INDEX);
519 int writeBlockDelay = MEM_WRITE_DELAY;
520
521 while ((bytesRead < fileSize) && !downloadFailed)
522 {
523 // Read a block of firmware data
524 auto dataRead = readFirmwareBlock(*inputFile, FW_READ_BLOCK_SIZE);
525 bytesRead += dataRead.size();
526
527 // Prepare command block with the current index and data
528 prepareCommandBlock(dataRead);
529
530 // Perform I2C write/read with retries
531 uint8_t readData[I2C_SMBUS_BLOCK_MAX] = {};
532 downloadFailed = !performI2cWriteReadWithRetries(
533 ISP_MEMORY_REGISTER, EXPECTED_MEM_READ_REPLY, readData, MAX_RETRIES,
534 writeBlockDelay);
535
536 // Adjust delay after first write block
537 writeBlockDelay = MEM_STRETCH_DELAY;
538 }
539
540 inputFile->close();
541
542 // Log final download status
543 if (downloadFailed)
544 {
545 lg2::error(
546 "Firmware download failed after retries at FW block {BYTESREAD}",
547 "BYTESREAD", bytesRead);
548 return false; // Failed
549 }
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600550 return true;
551}
552
553bool AeiUpdater::performI2cWriteReadWithRetries(
554 uint8_t regAddr, const uint8_t expectedReadSize, uint8_t* readData,
555 const int retries, const int delayTime)
556{
Faisal Awada57fb6642025-02-21 16:52:54 -0600557 uint8_t exceptionCount = 0;
558 uint32_t bigEndianValue = 0;
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600559 for (int i = 0; i < retries; ++i)
560 {
561 uint8_t readReplySize = 0;
562 try
563 {
564 performI2cWriteRead(regAddr, readReplySize, readData, delayTime);
Faisal Awadaf9b426b2025-01-31 11:44:48 -0600565 if ((readData[STATUS_CML_INDEX] == 0 ||
566 // The first firmware data packet sent to the PSU have a
567 // response of 0x80 which indicates firmware update in
568 // progress. If retry to send the first packet again reply will
569 // be 0.
570 (readData[STATUS_CML_INDEX] == 0x80 &&
571 delayTime == MEM_WRITE_DELAY)) &&
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600572 (readReplySize == expectedReadSize) &&
573 !std::equal(readData, readData + 4, byteSwappedIndex.begin()))
574 {
575 std::copy(readData, readData + 4, byteSwappedIndex.begin());
576 return true;
577 }
578 else
579 {
Faisal Awada57fb6642025-02-21 16:52:54 -0600580 bigEndianValue = (readData[0] << 24) | (readData[1] << 16) |
581 (readData[2] << 8) | (readData[3]);
Faisal Awadaf9b426b2025-01-31 11:44:48 -0600582 lg2::error("Write/read block {NUM} failed", "NUM",
583 bigEndianValue);
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600584 }
585 }
Faisal Awada57fb6642025-02-21 16:52:54 -0600586 catch (const i2c::I2CException& e)
587 {
588 exceptionCount++;
589 if (exceptionCount == MAX_RETRIES)
590 {
591 std::map<std::string, std::string> additionalData = {
592 {"I2C_WRITE_READ",
593 "I2C exception while flashing the firmware."}};
594 // Callout PSU & I2C
595 callOutI2CEventLog(additionalData, e.what(), e.errorCode);
596 }
597 lg2::error("I2C exception write/read block failed: {ERROR}",
598 "ERROR", e.what());
599 }
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600600 catch (const std::exception& e)
601 {
Faisal Awada57fb6642025-02-21 16:52:54 -0600602 exceptionCount++;
603 if (exceptionCount == MAX_RETRIES)
604 {
605 std::map<std::string, std::string> additionalData = {
606 {"WRITE_READ", "Exception while flashing the firmware."},
607 {"EXCEPTION", e.what()}};
608 // Callout PSU
609 callOutPsuEventLog(additionalData);
610 }
611 lg2::error("Exception write/read block failed: {ERROR}", "ERROR",
612 e.what());
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600613 }
614 }
Faisal Awada57fb6642025-02-21 16:52:54 -0600615 std::map<std::string, std::string> additionalData = {
616 {"WRITE_READ",
617 "Download firmware failed block: " + std::to_string(bigEndianValue)}};
618 // Callout PSU
619 callOutPsuEventLog(additionalData);
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600620 return false;
621}
622
623void AeiUpdater::performI2cWriteRead(uint8_t regAddr, uint8_t& readReplySize,
624 uint8_t* readData, const int& delayTime)
625{
626 i2cInterface->processCall(regAddr, cmdBlockWrite.size(),
627 cmdBlockWrite.data(), readReplySize, readData);
628
629 if (delayTime != 0)
630 {
631 updater::internal::delay(delayTime);
632 }
633}
634
635bool AeiUpdater::verifyDownloadFWStatus()
636{
637 try
638 {
639 // Read and verify firmware download status.
640 uint8_t status = 0;
641 i2cInterface->read(STATUS_REGISTER, status);
642 if (status != B_ISP_MODE_CHKSUM_GOOD)
643 {
644 lg2::error("Firmware download failed - status: {ERR}", "ERR",
645 status);
646
647 return false; // Failed checksum
648 }
649 return true;
650 }
651 catch (const std::exception& e)
652 {
653 lg2::error("I2C read status register failed: {ERROR}", "ERROR", e);
654 }
655 return false; // Failed
Faisal Awada8dca5072024-11-23 08:07:38 -0600656}
657
658void AeiUpdater::ispReboot()
659{
660 updater::internal::delay(
661 MEM_COMPLETE_DELAY); // Delay before starting the reboot process
662
663 try
664 {
665 // Write reboot command to the status register
666 i2cInterface->write(STATUS_REGISTER, CMD_BOOT_PWR);
667
668 updater::internal::delay(
669 REBOOT_DELAY); // Add delay after writing reboot command
670 }
671 catch (const std::exception& e)
672 {
673 lg2::error("I2C write error during reboot: {ERROR}", "ERROR", e);
674 }
675}
676
677bool AeiUpdater::ispReadRebootStatus()
678{
Faisal Awada57fb6642025-02-21 16:52:54 -0600679 for (int retry = 0; retry < MAX_RETRIES; ++retry)
Faisal Awada8dca5072024-11-23 08:07:38 -0600680 {
Faisal Awada57fb6642025-02-21 16:52:54 -0600681 try
Faisal Awada8dca5072024-11-23 08:07:38 -0600682 {
Faisal Awada57fb6642025-02-21 16:52:54 -0600683 // Read from the status register to verify reboot
684 uint8_t data = 1; // Initialize data to a non-zero value
685 i2cInterface->read(STATUS_REGISTER, data);
686
687 // If the reboot was successful, the read data should be 0
688 if (data == SUCCESSFUL_ISP_REBOOT_STATUS)
689 {
690 lg2::info("ISP Status Reboot successful.");
691 return true;
692 }
Faisal Awada8dca5072024-11-23 08:07:38 -0600693 }
Faisal Awada57fb6642025-02-21 16:52:54 -0600694 catch (const i2c::I2CException& e)
695 {
696 if (isEventLogEnabled())
697 {
698 std::map<std::string, std::string> additionalData = {
699 {"I2C_READ_REBOOT",
700 "I2C exception while reading ISP reboot status"}};
701
702 // Callout PSU & I2C
703 callOutI2CEventLog(additionalData, e.what(), e.errorCode);
704 }
705 lg2::error("I2C read error during reboot attempt: {ERROR}", "ERROR",
706 e);
707 }
708 catch (const std::exception& e)
709 {
710 if (isEventLogEnabled())
711 {
712 std::map<std::string, std::string> additionalData = {
713 {"READ_REBOOT",
714 "Exception while reading ISP reboot status"},
715 {"EXCEPTION", e.what()}};
716
717 // Callout PSU
718 callOutPsuEventLog(additionalData);
719 }
720 lg2::error("Read exception during reboot attempt: {ERROR}", "ERROR",
721 e);
722 }
723 // Reboot the PSU
724 ispReboot(); // Try to set PSU to normal mode
Faisal Awada8dca5072024-11-23 08:07:38 -0600725 }
Faisal Awada5ace9fb2025-01-07 13:26:25 -0600726
727 // If we reach here, all retries have failed
728 lg2::error("Failed to reboot ISP status after max retries.");
Faisal Awada8dca5072024-11-23 08:07:38 -0600729 return false;
730}
Faisal Awada5a582d32024-11-15 14:11:44 -0600731} // namespace aeiUpdater