xref: /openbmc/phosphor-bmc-code-mgmt/i2c-vr/mps/mp297x.cpp (revision 3f2f3e6a61777c16094c94c22734659a8d84bdad)
1 #include "mp297x.hpp"
2 
3 #include "common/include/utils.hpp"
4 
5 #include <phosphor-logging/lg2.hpp>
6 
7 #include <fstream>
8 
9 PHOSPHOR_LOG2_USING;
10 
11 namespace phosphor::software::VR
12 {
13 
14 static constexpr size_t statusByteLength = 1;
15 
16 static constexpr std::string_view crcUserRegName = "CRC_USER";
17 static constexpr std::string_view crcMultiRegName = "CRC_MULTI";
18 
19 enum class MP297XCmd : uint8_t
20 {
21     // Page 0 commands
22     storeDataIntoMTP = 0xF1,
23 
24     // Page 1 commands
25     writeProtectMode = 0x35,
26     enableMTPPageWR = 0x4F,
27 
28     // Page 2 commands
29     enableMultiConfigCRC = 0xF3,
30 
31     // Page 0x29 commands
32     readUserCodeCRC = 0xFF,
33 
34     // Page 0x2A commands
35     readMultiConfigCRC = 0xBF,
36 };
37 
parseDeviceConfiguration()38 sdbusplus::async::task<bool> MP297X::parseDeviceConfiguration()
39 {
40     if (!configuration)
41     {
42         error("Device configuration not initialized");
43         co_return false;
44     }
45 
46     configuration->vendorId = 0x0025;
47     configuration->productId = 0x0071;
48 
49     for (const auto& tokens : parser->lineTokens)
50     {
51         if (!parser->isValidDataTokens(tokens))
52         {
53             continue;
54         }
55 
56         auto regName = parser->getVal<std::string>(tokens, ATE::regName);
57 
58         if (regName == crcUserRegName)
59         {
60             configuration->configId =
61                 parser->getVal<uint32_t>(tokens, ATE::configId);
62             configuration->crcUser =
63                 parser->getVal<uint32_t>(tokens, ATE::regDataHex);
64         }
65         else if (regName == crcMultiRegName)
66         {
67             configuration->crcMulti =
68                 parser->getVal<uint32_t>(tokens, ATE::regDataHex);
69             break;
70         }
71     }
72 
73     co_return true;
74 }
75 
verifyImage(const uint8_t * image,size_t imageSize)76 sdbusplus::async::task<bool> MP297X::verifyImage(const uint8_t* image,
77                                                  size_t imageSize)
78 {
79     if (!co_await parseImage(image, imageSize))
80     {
81         error("Image verification failed: image parsing failed");
82         co_return false;
83     }
84 
85     if (configuration->registersData.empty())
86     {
87         error("Image verification failed - no data found");
88         co_return false;
89     }
90 
91     if (configuration->configId == 0)
92     {
93         error("Image verification failed - missing config ID");
94         co_return false;
95     }
96 
97     co_return true;
98 }
99 
checkId(PMBusCmd idCmd,uint32_t expected)100 sdbusplus::async::task<bool> MP297X::checkId(PMBusCmd idCmd, uint32_t expected)
101 {
102     constexpr size_t idLength = 2;
103 
104     std::vector<uint8_t> tbuf;
105     std::vector<uint8_t> rbuf;
106 
107     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
108     if (!i2cInterface.sendReceive(tbuf, rbuf))
109     {
110         error("Failed to set page 0 for ID check");
111         co_return false;
112     }
113 
114     tbuf = buildByteVector(idCmd);
115     rbuf.resize(statusByteLength + idLength);
116     if (!i2cInterface.sendReceive(tbuf, rbuf))
117     {
118         error("Failed to read ID, cmd={CMD}", "CMD", lg2::hex,
119               static_cast<uint8_t>(idCmd));
120         co_return false;
121     }
122 
123     auto idBytes = std::span(rbuf).subspan(statusByteLength);
124     auto id = bytesToInt<uint32_t>(idBytes);
125 
126     if (id != expected)
127     {
128         error("ID check failed for cmd {CMD}: got {ID}, expected {EXP}", "CMD",
129               lg2::hex, static_cast<uint8_t>(idCmd), "ID", lg2::hex, id, "EXP",
130               lg2::hex, expected);
131         co_return false;
132     }
133 
134     co_return true;
135 }
136 
isPasswordUnlock()137 sdbusplus::async::task<bool> MP297X::isPasswordUnlock()
138 {
139     constexpr uint8_t passwordMatchMask = 0x08;
140 
141     std::vector<uint8_t> tbuf;
142     std::vector<uint8_t> rbuf;
143 
144     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
145     if (!i2cInterface.sendReceive(tbuf, rbuf))
146     {
147         error("Failed to set page 0 for password unlock check");
148         co_return false;
149     }
150 
151     tbuf = buildByteVector(PMBusCmd::statusCML);
152     rbuf.resize(statusByteLength);
153 
154     if (!i2cInterface.sendReceive(tbuf, rbuf))
155     {
156         error("Failed to check password unlock status");
157         co_return false;
158     }
159 
160     auto unlocked = (rbuf[0] & passwordMatchMask) != 0;
161 
162     debug("Password unlock status: {STATUS}", "STATUS",
163           unlocked ? "Unlocked" : "Locked");
164 
165     co_return unlocked;
166 }
167 
unlockWriteProtect()168 sdbusplus::async::task<bool> MP297X::unlockWriteProtect()
169 {
170     constexpr uint8_t writeProtectModeMask = 0x04;
171     constexpr uint8_t unlockMemoryProtect = 0x00;
172     constexpr uint8_t unlockMTPProtect = 0x63;
173 
174     std::vector<uint8_t> tbuf;
175     std::vector<uint8_t> rbuf(statusByteLength);
176 
177     // Get write protection mode
178     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page1);
179     if (!i2cInterface.sendReceive(tbuf, rbuf))
180     {
181         error("Failed to set page 1 to check write protection mode");
182         co_return false;
183     }
184 
185     tbuf = buildByteVector(MP297XCmd::writeProtectMode);
186     if (!i2cInterface.sendReceive(tbuf, rbuf))
187     {
188         error("Failed to get write protect mode");
189         co_return false;
190     }
191 
192     auto isMTPMode = (rbuf[0] & writeProtectModeMask) == 0;
193     auto unlockData = isMTPMode ? unlockMTPProtect : unlockMemoryProtect;
194 
195     // Unlock write protection
196     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
197     if (!i2cInterface.sendReceive(tbuf, rbuf))
198     {
199         error("Failed to set page 0 to unlock write protection");
200         co_return false;
201     }
202 
203     tbuf = buildByteVector(PMBusCmd::writeProtect, unlockData);
204     if (!i2cInterface.sendReceive(tbuf, rbuf))
205     {
206         error("Failed to unlock write protection");
207         co_return false;
208     }
209 
210     debug("Unlocked {MODE} write protection", "MODE",
211           isMTPMode ? "MTP" : "memory");
212     co_return true;
213 }
214 
programPageRegisters(MPSPage page,const std::map<uint8_t,std::vector<MPSData>> & groupedData)215 sdbusplus::async::task<bool> MP297X::programPageRegisters(
216     MPSPage page, const std::map<uint8_t, std::vector<MPSData>>& groupedData)
217 {
218     auto pageNum = static_cast<uint8_t>(page);
219 
220     if (groupedData.find(pageNum) == groupedData.end())
221     {
222         debug("No data found for page {PAGE}", "PAGE", pageNum);
223         co_return true;
224     }
225 
226     const auto& data = groupedData.at(pageNum);
227 
228     // Page 2 is the multi-config page. Enter page2A to write config value
229     // to MTP space directly during multi-config page programming.
230     if (page == MPSPage::page2)
231     {
232         page = MPSPage::page2A;
233         pageNum = static_cast<uint8_t>(page);
234     }
235 
236     std::vector<uint8_t> tbuf;
237     std::vector<uint8_t> rbuf;
238 
239     tbuf = buildByteVector(PMBusCmd::page, page);
240     if (!i2cInterface.sendReceive(tbuf, rbuf))
241     {
242         error("Failed to set page {PAGE} to program registers", "PAGE",
243               pageNum);
244         co_return false;
245     }
246 
247     auto i2cWriteWithRetry =
248         [&](const std::vector<uint8_t>& tbuf) -> sdbusplus::async::task<bool> {
249         std::vector<uint8_t> rbuf;
250         constexpr size_t maxRetries = 3;
251         constexpr auto retryDelay = std::chrono::milliseconds(10);
252 
253         for (size_t i = 1; i <= maxRetries; ++i)
254         {
255             if (i2cInterface.sendReceive(tbuf, rbuf))
256             {
257                 co_return true;
258             }
259             error("I2C write failed, retry {RETRY}", "RETRY", i);
260             co_await sdbusplus::async::sleep_for(ctx, retryDelay);
261         }
262         co_return false;
263     };
264 
265     for (const auto& regData : data)
266     {
267         tbuf = {regData.addr};
268         tbuf.insert(tbuf.end(), regData.data.begin(),
269                     regData.data.begin() + regData.length);
270 
271         if (!(co_await i2cWriteWithRetry(tbuf)))
272         {
273             error(
274                 "Failed to write data {DATA} to register {REG} on page {PAGE}",
275                 "DATA", lg2::hex, bytesToInt<uint32_t>(regData.data), "REG",
276                 lg2::hex, regData.addr, "PAGE", pageNum);
277             co_return false;
278         }
279 
280         if (page == MPSPage::page2A)
281         {
282             // Page 2A requires a delay after each register write
283             co_await sdbusplus::async::sleep_for(ctx,
284                                                  std::chrono::milliseconds(2));
285         }
286     }
287 
288     debug("Programmed {N} registers on page {PAGE}", "N", data.size(), "PAGE",
289           pageNum);
290 
291     co_return true;
292 }
293 
storeDataIntoMTP()294 sdbusplus::async::task<bool> MP297X::storeDataIntoMTP()
295 {
296     std::vector<uint8_t> tbuf;
297     std::vector<uint8_t> rbuf;
298 
299     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page0);
300     if (!i2cInterface.sendReceive(tbuf, rbuf))
301     {
302         error("Failed to set page 0 for storing data into MTP");
303         co_return false;
304     }
305 
306     tbuf = buildByteVector(MP297XCmd::storeDataIntoMTP);
307     if (!i2cInterface.sendReceive(tbuf, rbuf))
308     {
309         error("Failed to store data into MTP");
310         co_return false;
311     }
312 
313     // Wait store data into MTP
314     co_await sdbusplus::async::sleep_for(ctx, std::chrono::milliseconds(500));
315 
316     debug("Stored data into MTP");
317 
318     co_return true;
319 }
320 
enableMTPPageWriteRead()321 sdbusplus::async::task<bool> MP297X::enableMTPPageWriteRead()
322 {
323     constexpr uint8_t mtpByteRWEnable = 0x20;
324 
325     std::vector<uint8_t> tbuf;
326     std::vector<uint8_t> rbuf;
327 
328     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page1);
329     if (!i2cInterface.sendReceive(tbuf, rbuf))
330     {
331         error("Failed to set page 1 to enable MTP page write/read");
332         co_return false;
333     }
334 
335     tbuf = buildByteVector(MP297XCmd::enableMTPPageWR);
336     rbuf.resize(statusByteLength);
337     if (!i2cInterface.sendReceive(tbuf, rbuf))
338     {
339         error("Failed to read MTP page write/read status");
340         co_return false;
341     }
342 
343     uint8_t enableMTPPageWRData = rbuf[0] | mtpByteRWEnable;
344     tbuf = buildByteVector(MP297XCmd::enableMTPPageWR, enableMTPPageWRData);
345     rbuf.resize(0);
346     if (!i2cInterface.sendReceive(tbuf, rbuf))
347     {
348         error("Failed to enable MTP page write/read");
349         co_return false;
350     }
351 
352     debug("Enabled MTP page write/read");
353     co_return true;
354 }
355 
enableMultiConfigCRC()356 sdbusplus::async::task<bool> MP297X::enableMultiConfigCRC()
357 {
358     std::vector<uint8_t> tbuf;
359     std::vector<uint8_t> rbuf;
360 
361     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page2);
362     if (!i2cInterface.sendReceive(tbuf, rbuf))
363     {
364         error("Failed to set page 2 to enable multi-config CRC");
365         co_return false;
366     }
367 
368     tbuf = buildByteVector(MP297XCmd::enableMultiConfigCRC);
369     if (!i2cInterface.sendReceive(tbuf, rbuf))
370     {
371         error("Failed to enable multi-config CRC");
372         co_return false;
373     }
374 
375     debug("Enabled multi-config CRC");
376     co_return true;
377 }
378 
getCRC(uint32_t * checksum)379 sdbusplus::async::task<bool> MP297X::getCRC(uint32_t* checksum)
380 {
381     if (checksum == nullptr)
382     {
383         error("getCRC() called with null checksum pointer");
384         co_return false;
385     }
386 
387     constexpr size_t crcLength = 2;
388 
389     std::vector<uint8_t> tbuf;
390     std::vector<uint8_t> rbuf;
391 
392     uint16_t userCodeCRC = 0;
393     uint16_t multiConfigCRC = 0;
394 
395     // Read User Code CRC
396     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page29);
397     rbuf.resize(0);
398     if (!i2cInterface.sendReceive(tbuf, rbuf))
399     {
400         error("Failed to set page 29 for User Code CRC read");
401         co_return false;
402     }
403 
404     tbuf = buildByteVector(MP297XCmd::readUserCodeCRC);
405     rbuf.resize(crcLength);
406     if (!i2cInterface.sendReceive(tbuf, rbuf))
407     {
408         error("Failed to read User Code CRC from device");
409         co_return false;
410     }
411 
412     userCodeCRC = bytesToInt<uint16_t>(rbuf);
413 
414     // Read Multi Config CRC
415     tbuf = buildByteVector(PMBusCmd::page, MPSPage::page2A);
416     rbuf.resize(0);
417     if (!i2cInterface.sendReceive(tbuf, rbuf))
418     {
419         error("Failed to set page 2A for Multi Config CRC read");
420         co_return false;
421     }
422 
423     tbuf = buildByteVector(MP297XCmd::readMultiConfigCRC);
424     rbuf.resize(crcLength);
425     if (!i2cInterface.sendReceive(tbuf, rbuf))
426     {
427         error("Failed to read Multi Config CRC from device");
428         co_return false;
429     }
430 
431     multiConfigCRC = bytesToInt<uint16_t>(rbuf);
432 
433     // Combine: [byte3 byte2] = userCodeCRC, [byte1 byte0] = multiConfigCRC
434     *checksum = (static_cast<uint32_t>(userCodeCRC) << 16) |
435                 static_cast<uint32_t>(multiConfigCRC);
436 
437     debug("Read CRC: {CRC}", "CRC", lg2::hex, *checksum);
438 
439     co_return true;
440 }
441 
checkMTPCRC()442 sdbusplus::async::task<bool> MP297X::checkMTPCRC()
443 {
444     uint32_t crc = 0;
445     auto expectedCRC = (configuration->crcUser << 16) | configuration->crcMulti;
446 
447     // NOLINTBEGIN(clang-analyzer-core.uninitialized.Branch)
448     if (!co_await getCRC(&crc))
449     // NOLINTEND(clang-analyzer-core.uninitialized.Branch)
450     {
451         error("Failed to get CRC for MTP check");
452         co_return false;
453     }
454 
455     debug("MTP CRC: {CRC}, Expected: {EXP}", "CRC", lg2::hex, crc, "EXP",
456           lg2::hex, expectedCRC);
457 
458     co_return crc == expectedCRC;
459 }
460 
forcedUpdateAllowed()461 bool MP297X::forcedUpdateAllowed()
462 {
463     return true;
464 }
465 
updateFirmware(bool force)466 sdbusplus::async::task<bool> MP297X::updateFirmware(bool force)
467 {
468     (void)force;
469 
470     auto groupedConfigData = getGroupedConfigData();
471 
472     if (!co_await checkId(PMBusCmd::mfrId, configuration->vendorId))
473     {
474         co_return false;
475     }
476 
477     if (!co_await checkId(PMBusCmd::mfrModel, configuration->productId))
478     {
479         co_return false;
480     }
481 
482     if (!co_await isPasswordUnlock())
483     {
484         co_return false;
485     }
486 
487     if (!co_await unlockWriteProtect())
488     {
489         co_return false;
490     }
491 
492     if (!co_await programPageRegisters(MPSPage::page0, groupedConfigData))
493     {
494         co_return false;
495     }
496 
497     if (!co_await programPageRegisters(MPSPage::page1, groupedConfigData))
498     {
499         co_return false;
500     }
501 
502     if (!co_await storeDataIntoMTP())
503     {
504         co_return false;
505     }
506 
507     if (!co_await enableMTPPageWriteRead())
508     {
509         co_return false;
510     }
511 
512     if (!co_await enableMultiConfigCRC())
513     {
514         co_return false;
515     }
516 
517     if (!co_await programPageRegisters(MPSPage::page2, groupedConfigData))
518     {
519         co_return false;
520     }
521 
522     if (!(co_await checkMTPCRC()))
523     {
524         co_return false;
525     }
526 
527     co_return true;
528 }
529 
530 } // namespace phosphor::software::VR
531