blob: 064258e316d5068a2866c623eda12083620999eb [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{
32constexpr uint8_t MAX_RETRIES = 0x02; // Constants for retry limits
33
34constexpr int ISP_STATUS_DELAY = 1200; // Delay for ISP status check (1.2s)
35constexpr int MEM_WRITE_DELAY = 5000; // Memory write delay (5s)
Faisal Awada8dca5072024-11-23 08:07:38 -060036constexpr int MEM_STRETCH_DELAY = 10; // Delay between writes (10ms)
Faisal Awada5a582d32024-11-15 14:11:44 -060037constexpr int MEM_COMPLETE_DELAY = 2000; // Delay before completion (2s)
38constexpr int REBOOT_DELAY = 8000; // Delay for reboot (8s)
39
40constexpr uint8_t I2C_SMBUS_BLOCK_MAX = 0x20; // Max Read bytes from PSU
41constexpr uint8_t FW_READ_BLOCK_SIZE = 0x20; // Read bytes from FW file
42constexpr uint8_t BLOCK_WRITE_SIZE = 0x25; // I2C block write size
43constexpr uint8_t READ_SEQ_ST_CML_SIZE = 0x6; // Read sequence and status CML
44 // size
45constexpr uint8_t START_SEQUENCE_INDEX = 0x1; // Starting sequence index
46constexpr uint8_t STATUS_CML_INDEX = 0x5; // Status CML read index
47
48// Register addresses for commands.
49constexpr uint8_t KEY_REGISTER = 0xF6; // Key register
50constexpr uint8_t STATUS_REGISTER = 0xF7; // Status register
51constexpr uint8_t ISP_MEMORY_REGISTER = 0xF9; // ISP memory register
52
53// Define AEI ISP status register commands
54constexpr uint8_t CMD_CLEAR_STATUS = 0x0; // Clear the status register
55constexpr uint8_t CMD_RESET_SEQ = 0x01; // This command will reset ISP OS for
56 // another attempt of a sequential
57 // programming operation.
58constexpr uint8_t CMD_BOOT_ISP = 0x02; // Boot the In-System Programming System.
59constexpr uint8_t CMD_BOOT_PWR = 0x03; // Attempt to boot the Power Management
60 // OS.
61
62// Define AEI ISP response status bit
63constexpr uint8_t B_CHKSUM_ERR = 0x0; // The checksum verification unsuccessful
64constexpr uint8_t B_CHKSUM_SUCCESS = 0x1; // The checksum verification
65 // successful.
66constexpr uint8_t B_MEM_ERR = 0x2; // Memory boundry error indication.
67constexpr uint8_t B_ALIGN_ERR = 0x4; // Address error indication.
68constexpr uint8_t B_KEY_ERR = 0x8; // Invalid Key
69constexpr uint8_t B_START_ERR = 0x10; // Error indicator set at startup.
70constexpr uint8_t B_IMG_MISSMATCH_ERR = 0x20; // Firmware image does not match
71 // PSU
72constexpr uint8_t B_ISP_MODE = 0x40; // ISP mode
73constexpr uint8_t B_ISP_MODE_CHKSUM_GOOD = 0x41; // ISP mode & good checksum.
74constexpr uint8_t B_PRGM_BUSY = 0x80; // Write operation in progress.
Faisal Awada8dca5072024-11-23 08:07:38 -060075constexpr uint8_t SUCCESSFUL_ISP_REBOOT_STATUS = 0x0; // Successful ISP reboot
76 // status
Faisal Awada5a582d32024-11-15 14:11:44 -060077
78using namespace phosphor::logging;
79namespace util = phosphor::power::util;
80
81int AeiUpdater::doUpdate()
82{
83 i2cInterface = Updater::getI2C();
84 if (i2cInterface == nullptr)
85 {
86 throw std::runtime_error("I2C interface error");
87 }
88 bool cleanFailedIspMode = false; // Flag to prevent download and continue to
89 // restore the PSU to it's original state.
90
91 // Set ISP mode by writing necessary keys and resetting ISP status
92 if (!writeIspKey() || !writeIspMode() || !writeIspStatusReset())
93 {
94 lg2::error("Failed to set ISP key or mode or reset ISP status");
95 cleanFailedIspMode = true;
96 }
97
98 if (cleanFailedIspMode)
99 {
100 return 1;
101 }
102 return 0; // Update successful.
103}
104
105bool AeiUpdater::writeIspKey()
106{
107 // ISP Key to unlock programming mode ( ASCII for "artY").
108 constexpr std::array<uint8_t, 4> unlockData = {0x61, 0x72, 0x74,
109 0x59}; // ISP Key "artY"
110 try
111 {
112 // Send ISP Key to unlock device for firmware update
113 i2cInterface->write(KEY_REGISTER, unlockData.size(), unlockData.data());
114 return true;
115 }
116 catch (const std::exception& e)
117 {
118 // Log failure if I2C write fails.
Faisal Awada8dca5072024-11-23 08:07:38 -0600119 lg2::error("I2C write failed: {ERROR}", "ERROR", e);
Faisal Awada5a582d32024-11-15 14:11:44 -0600120 return false;
121 }
122}
123
124bool AeiUpdater::writeIspMode()
125{
126 // Attempt to set device in ISP mode with retries.
127 uint8_t ispStatus = 0x0;
128 for (int retry = 0; retry < MAX_RETRIES; ++retry)
129 {
130 try
131 {
132 // Write command to enter ISP mode.
133 i2cInterface->write(STATUS_REGISTER, CMD_BOOT_ISP);
134 // Delay to allow status register update.
135 updater::internal::delay(ISP_STATUS_DELAY);
136 // Read back status register to confirm ISP mode is active.
137 i2cInterface->read(STATUS_REGISTER, ispStatus);
138
139 if (ispStatus & B_ISP_MODE)
140 {
141 return true;
142 }
143 }
144 catch (const std::exception& e)
145 {
146 // Log I2C error with each retry attempt.
147 lg2::error("I2C error during ISP mode write/read: {ERROR}", "ERROR",
Faisal Awada8dca5072024-11-23 08:07:38 -0600148 e);
Faisal Awada5a582d32024-11-15 14:11:44 -0600149 }
150 }
151 lg2::error("Failed to set ISP Mode");
152 return false; // Failed to set ISP Mode after retries
153}
154
155bool AeiUpdater::writeIspStatusReset()
156{
157 // Reset ISP status register before firmware download.
158 uint8_t ispStatus = 0;
159 try
160 {
161 i2cInterface->write(STATUS_REGISTER,
162 CMD_RESET_SEQ); // Start reset sequence.
163 for (int retry = 0; retry < MAX_RETRIES; ++retry)
164 {
165 i2cInterface->read(STATUS_REGISTER, ispStatus);
166 if (ispStatus == B_ISP_MODE)
167 {
168 return true; // ISP status reset successfully.
169 }
170 i2cInterface->write(STATUS_REGISTER,
171 CMD_CLEAR_STATUS); // Clear status if
172 // not reset.
173 }
174 }
175 catch (const std::exception& e)
176 {
177 // Log any errors encountered during reset sequence.
178 lg2::error("I2C Read/Write error during ISP reset: {ERROR}", "ERROR",
Faisal Awada8dca5072024-11-23 08:07:38 -0600179 e);
Faisal Awada5a582d32024-11-15 14:11:44 -0600180 }
181 lg2::error("Failed to reset ISP Status");
182 return false;
183}
184
Faisal Awada8dca5072024-11-23 08:07:38 -0600185std::string AeiUpdater::getFirmwarePath()
186{
187 const std::string fspath =
188 updater::internal::getFWFilenamePath(getImageDir());
189 if (fspath.empty())
190 {
191 lg2::error("Firmware file path not found");
192 }
193 return fspath;
194}
195
196bool AeiUpdater::isFirmwareFileValid(const std::string& fspath)
197{
198 if (!updater::internal::validateFWFile(fspath))
199 {
200 lg2::error("Firmware validation failed");
201 return false;
202 }
203 return true;
204}
205
206std::unique_ptr<std::ifstream>
207 AeiUpdater::openFirmwareFile(const std::string& fspath)
208{
209 auto inputFile = updater::internal::openFirmwareFile(fspath);
210 if (!inputFile)
211 {
212 lg2::error("Failed to open firmware file");
213 }
214 return inputFile;
215}
216
217std::vector<uint8_t> AeiUpdater::readFirmwareBlock(std::ifstream& file,
218 const size_t& bytesToRead)
219{
220 auto block = updater::internal::readFirmwareBytes(file, bytesToRead);
221 return block;
222}
223
224std::vector<uint8_t>
225 AeiUpdater::prepareCommandBlock(const std::vector<uint8_t>& dataBlockRead)
226{
227 std::vector<uint8_t> cmdBlockWrite = {ISP_MEMORY_REGISTER,
228 BLOCK_WRITE_SIZE};
229
230 cmdBlockWrite.insert(cmdBlockWrite.end(), byteSwappedIndex.begin(),
231 byteSwappedIndex.end());
232 cmdBlockWrite.insert(cmdBlockWrite.end(), dataBlockRead.begin(),
233 dataBlockRead.end());
234
235 // Resize to ensure it matches BLOCK_WRITE_SIZE + 1 and append CRC
236 if (cmdBlockWrite.size() != BLOCK_WRITE_SIZE + 1)
237 {
238 cmdBlockWrite.resize(BLOCK_WRITE_SIZE + 1, 0xFF);
239 }
240 cmdBlockWrite.push_back(updater::internal::calculateCRC8(cmdBlockWrite));
241 // Remove the F9 and byte count
242 cmdBlockWrite.erase(cmdBlockWrite.begin(), cmdBlockWrite.begin() + 2);
243
244 return cmdBlockWrite;
245}
246
247void AeiUpdater::ispReboot()
248{
249 updater::internal::delay(
250 MEM_COMPLETE_DELAY); // Delay before starting the reboot process
251
252 try
253 {
254 // Write reboot command to the status register
255 i2cInterface->write(STATUS_REGISTER, CMD_BOOT_PWR);
256
257 updater::internal::delay(
258 REBOOT_DELAY); // Add delay after writing reboot command
259 }
260 catch (const std::exception& e)
261 {
262 lg2::error("I2C write error during reboot: {ERROR}", "ERROR", e);
263 }
264}
265
266bool AeiUpdater::ispReadRebootStatus()
267{
268 try
269 {
270 // Read from the status register to verify reboot
271 uint8_t data = 1; // Initialize data to a non-zero value
272 i2cInterface->read(STATUS_REGISTER, data);
273
274 uint8_t status = SUCCESSFUL_ISP_REBOOT_STATUS;
275 // If the reboot was successful, the read data should be 0
276 if (data == status)
277 {
278 lg2::info("ISP Status Reboot successful.");
279 return true;
280 }
281 }
282 catch (const std::exception& e)
283 {
284 lg2::error("I2C read error during reboot attempt: {ERROR}", "ERROR", e);
285 }
286 return false;
287}
288
Faisal Awada5a582d32024-11-15 14:11:44 -0600289} // namespace aeiUpdater