xref: /openbmc/smbios-mdr/src/mdrv2.cpp (revision 08e4a6dfe54a98171839dba482f87ed9005b516b)
1 /*
2 // Copyright (c) 2018 Intel 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 "mdrv2.hpp"
18 
19 #include "pcieslot.hpp"
20 
21 #include <sys/mman.h>
22 
23 #include <phosphor-logging/elog-errors.hpp>
24 #include <sdbusplus/exception.hpp>
25 #include <xyz/openbmc_project/Smbios/MDR_V2/error.hpp>
26 
27 #include <fstream>
28 
29 namespace phosphor
30 {
31 namespace smbios
32 {
33 
34 std::vector<uint8_t> MDR_V2::getDirectoryInformation(uint8_t dirIndex)
35 {
36     std::vector<uint8_t> responseDir;
37     if (dirIndex > smbiosDir.dirEntries)
38     {
39         responseDir.push_back(0);
40         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
41             InvalidParameter();
42     }
43     responseDir.push_back(mdr2Version);
44     responseDir.push_back(smbiosDir.dirVersion);
45     uint8_t returnedEntries = smbiosDir.dirEntries - dirIndex;
46     responseDir.push_back(returnedEntries);
47     if ((dirIndex + returnedEntries) >= smbiosDir.dirEntries)
48     {
49         responseDir.push_back(0);
50     }
51     else
52     {
53         responseDir.push_back(smbiosDir.dirEntries - dirIndex -
54                               returnedEntries);
55     }
56     for (uint8_t index = dirIndex; index < smbiosDir.dirEntries; index++)
57     {
58         for (uint8_t indexId = 0; indexId < sizeof(DataIdStruct); indexId++)
59         {
60             responseDir.push_back(
61                 smbiosDir.dir[index].common.id.dataInfo[indexId]);
62         }
63     }
64 
65     return responseDir;
66 }
67 
68 bool MDR_V2::smbiosIsAvailForUpdate(uint8_t index)
69 {
70     bool ret = false;
71     if (index >= maxDirEntries)
72     {
73         return ret;
74     }
75 
76     switch (smbiosDir.dir[index].stage)
77     {
78         case MDR2SMBIOSStatusEnum::mdr2Updating:
79             ret = false;
80             break;
81 
82         case MDR2SMBIOSStatusEnum::mdr2Init:
83             // This *looks* like there should be a break statement here,
84             // as the effects of the previous statement are a noop given
85             // the following code that this falls through to.
86             // We've elected not to change it, though, since it's been
87             // this way since the old generation, and it would affect
88             // the way the code functions.
89             // If it ain't broke, don't fix it.
90 
91         case MDR2SMBIOSStatusEnum::mdr2Loaded:
92         case MDR2SMBIOSStatusEnum::mdr2Updated:
93             if (smbiosDir.dir[index].lock == MDR2DirLockEnum::mdr2DirLock)
94             {
95                 ret = false;
96             }
97             else
98             {
99                 ret = true;
100             }
101             break;
102 
103         default:
104             break;
105     }
106 
107     return ret;
108 }
109 
110 std::vector<uint8_t> MDR_V2::getDataOffer()
111 {
112     std::vector<uint8_t> offer(sizeof(DataIdStruct));
113     if (smbiosIsAvailForUpdate(0))
114     {
115         std::copy(smbiosDir.dir[0].common.id.dataInfo,
116                   &smbiosDir.dir[0].common.id.dataInfo[16], offer.data());
117     }
118     else
119     {
120         phosphor::logging::log<phosphor::logging::level::ERR>(
121             "smbios is not ready for update");
122         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
123             UpdateInProgress();
124     }
125     return offer;
126 }
127 
128 inline uint8_t MDR_V2::smbiosValidFlag(uint8_t index)
129 {
130     FlagStatus ret = FlagStatus::flagIsInvalid;
131     MDR2SMBIOSStatusEnum stage = smbiosDir.dir[index].stage;
132     MDR2DirLockEnum lock = smbiosDir.dir[index].lock;
133 
134     switch (stage)
135     {
136         case MDR2SMBIOSStatusEnum::mdr2Loaded:
137         case MDR2SMBIOSStatusEnum::mdr2Updated:
138             if (lock == MDR2DirLockEnum::mdr2DirLock)
139             {
140                 ret = FlagStatus::flagIsLocked; // locked
141             }
142             else
143             {
144                 ret = FlagStatus::flagIsValid; // valid
145             }
146             break;
147 
148         case MDR2SMBIOSStatusEnum::mdr2Updating:
149         case MDR2SMBIOSStatusEnum::mdr2Init:
150             ret = FlagStatus::flagIsInvalid; // invalid
151             break;
152 
153         default:
154             break;
155     }
156 
157     return static_cast<uint8_t>(ret);
158 }
159 
160 // If source variable size is 4 bytes (uint32_t) and destination is Vector type
161 // is 1 byte (uint8_t), then by using this API can copy data byte by byte. For
162 // Example copying data from smbiosDir.dir[idIndex].common.size and
163 // smbiosDir.dir[idIndex].common.timestamp to vector variable responseInfo
164 template <typename T>
165 void appendReversed(std::vector<uint8_t>& vector, const T& value)
166 {
167     auto data = reinterpret_cast<const uint8_t*>(&value);
168     std::reverse_copy(data, data + sizeof(value), std::back_inserter(vector));
169 }
170 
171 std::vector<uint8_t> MDR_V2::getDataInformation(uint8_t idIndex)
172 {
173     std::vector<uint8_t> responseInfo;
174     responseInfo.push_back(mdr2Version);
175 
176     if (idIndex >= maxDirEntries)
177     {
178         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
179             InvalidParameter();
180     }
181 
182     for (uint8_t index = 0; index < sizeof(DataIdStruct); index++)
183     {
184         responseInfo.push_back(
185             smbiosDir.dir[idIndex].common.id.dataInfo[index]);
186     }
187 
188     responseInfo.push_back(smbiosValidFlag(idIndex));
189     appendReversed(responseInfo, smbiosDir.dir[idIndex].common.size);
190     responseInfo.push_back(smbiosDir.dir[idIndex].common.dataVersion);
191     appendReversed(responseInfo, smbiosDir.dir[idIndex].common.timestamp);
192 
193     return responseInfo;
194 }
195 
196 bool MDR_V2::readDataFromFlash(MDRSMBIOSHeader* mdrHdr, uint8_t* data)
197 {
198     if (mdrHdr == nullptr)
199     {
200         phosphor::logging::log<phosphor::logging::level::ERR>(
201             "Read data from flash error - Invalid mdr header");
202         return false;
203     }
204     if (data == nullptr)
205     {
206         phosphor::logging::log<phosphor::logging::level::ERR>(
207             "Read data from flash error - Invalid data point");
208         return false;
209     }
210     std::ifstream smbiosFile(mdrType2File, std::ios_base::binary);
211     if (!smbiosFile.good())
212     {
213         phosphor::logging::log<phosphor::logging::level::ERR>(
214             "Read data from flash error - Open MDRV2 table file failure");
215         return false;
216     }
217     smbiosFile.clear();
218     smbiosFile.seekg(0, std::ios_base::end);
219     int fileLength = smbiosFile.tellg();
220     smbiosFile.seekg(0, std::ios_base::beg);
221     if (fileLength < sizeof(MDRSMBIOSHeader))
222     {
223         phosphor::logging::log<phosphor::logging::level::ERR>(
224             "MDR V2 file size is smaller than mdr header");
225         return false;
226     }
227     smbiosFile.read(reinterpret_cast<char*>(mdrHdr), sizeof(MDRSMBIOSHeader));
228     if (mdrHdr->dataSize > smbiosTableStorageSize)
229     {
230         phosphor::logging::log<phosphor::logging::level::ERR>(
231             "Data size out of limitation");
232         smbiosFile.close();
233         return false;
234     }
235     fileLength -= sizeof(MDRSMBIOSHeader);
236     if (fileLength < mdrHdr->dataSize)
237     {
238         smbiosFile.read(reinterpret_cast<char*>(data), fileLength);
239     }
240     else
241     {
242         smbiosFile.read(reinterpret_cast<char*>(data), mdrHdr->dataSize);
243     }
244     smbiosFile.close();
245     return true;
246 }
247 
248 bool MDR_V2::sendDirectoryInformation(uint8_t dirVersion, uint8_t dirIndex,
249                                       uint8_t returnedEntries,
250                                       uint8_t remainingEntries,
251                                       std::vector<uint8_t> dirEntry)
252 {
253     bool teminate = false;
254     if ((dirIndex >= maxDirEntries) || (returnedEntries < 1))
255     {
256         phosphor::logging::log<phosphor::logging::level::ERR>(
257             "Send Dir info failed - input parameter invalid");
258         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
259             InvalidParameter();
260     }
261     if ((static_cast<size_t>(returnedEntries) * sizeof(DataIdStruct)) !=
262         dirEntry.size())
263     {
264         phosphor::logging::log<phosphor::logging::level::ERR>(
265             "Directory size invalid");
266         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
267             InvalidParameter();
268     }
269     if (dirVersion == smbiosDir.dirVersion)
270     {
271         teminate = true;
272     }
273     else
274     {
275         if (remainingEntries > 0)
276         {
277             teminate = false;
278         }
279         else
280         {
281             teminate = true;
282             smbiosDir.dirVersion = dirVersion;
283         }
284         uint8_t idIndex = dirIndex;
285         smbiosDir.dirEntries = returnedEntries;
286 
287         uint8_t* pData = dirEntry.data();
288         if (pData == nullptr)
289         {
290             return false;
291         }
292         for (uint8_t index = 0; index < returnedEntries; index++)
293         {
294             auto data = reinterpret_cast<const DataIdStruct*>(pData);
295             std::copy(data->dataInfo, data->dataInfo + sizeof(DataIdStruct),
296                       smbiosDir.dir[idIndex + index].common.id.dataInfo);
297             pData += sizeof(DataIdStruct);
298         }
299     }
300     return teminate;
301 }
302 
303 bool MDR_V2::sendDataInformation(uint8_t idIndex, uint8_t flag,
304                                  uint32_t dataLen, uint32_t dataVer,
305                                  uint32_t timeStamp)
306 {
307     if (idIndex >= maxDirEntries)
308     {
309         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
310             InvalidParameter();
311     }
312     int entryChanged = 0;
313     if (smbiosDir.dir[idIndex].common.dataSetSize != dataLen)
314     {
315         entryChanged++;
316         smbiosDir.dir[idIndex].common.dataSetSize = dataLen;
317     }
318 
319     if (smbiosDir.dir[idIndex].common.dataVersion != dataVer)
320     {
321         entryChanged++;
322         smbiosDir.dir[idIndex].common.dataVersion = dataVer;
323     }
324 
325     if (smbiosDir.dir[idIndex].common.timestamp != timeStamp)
326     {
327         entryChanged++;
328         smbiosDir.dir[idIndex].common.timestamp = timeStamp;
329     }
330     if (entryChanged == 0)
331     {
332         return false;
333     }
334     return true;
335 }
336 
337 int MDR_V2::findIdIndex(std::vector<uint8_t> dataInfo)
338 {
339     if (dataInfo.size() != sizeof(DataIdStruct))
340     {
341         phosphor::logging::log<phosphor::logging::level::ERR>(
342             "Length of dataInfo invalid");
343         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
344             InvalidId();
345     }
346     std::array<uint8_t, 16> arrayDataInfo;
347 
348     std::copy(dataInfo.begin(), dataInfo.end(), arrayDataInfo.begin());
349 
350     for (int index = 0; index < smbiosDir.dirEntries; index++)
351     {
352         int info = 0;
353         for (; info < arrayDataInfo.size(); info++)
354         {
355             if (arrayDataInfo[info] !=
356                 smbiosDir.dir[index].common.id.dataInfo[info])
357             {
358                 break;
359             }
360         }
361         if (info == arrayDataInfo.size())
362         {
363             return index;
364         }
365     }
366     throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::InvalidId();
367 }
368 
369 uint8_t MDR_V2::directoryEntries(uint8_t value)
370 {
371     value = smbiosDir.dirEntries;
372     return sdbusplus::xyz::openbmc_project::Smbios::server::MDR_V2::
373         directoryEntries(value);
374 }
375 
376 void MDR_V2::systemInfoUpdate()
377 {
378     cpus.clear();
379 
380     int num = getTotalCpuSlot();
381     if (num == -1)
382     {
383         phosphor::logging::log<phosphor::logging::level::ERR>(
384             "get cpu total slot failed");
385         return;
386     }
387 
388     for (int index = 0; index < num; index++)
389     {
390         std::string path = cpuPath + std::to_string(index);
391         cpus.emplace_back(std::make_unique<phosphor::smbios::Cpu>(
392             bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage));
393     }
394 
395 #ifdef DIMM_DBUS
396 
397     dimms.clear();
398 
399     num = getTotalDimmSlot();
400     if (num == -1)
401     {
402         phosphor::logging::log<phosphor::logging::level::ERR>(
403             "get dimm total slot failed");
404         return;
405     }
406 
407     for (int index = 0; index < num; index++)
408     {
409         std::string path = dimmPath + std::to_string(index);
410         dimms.emplace_back(std::make_unique<phosphor::smbios::Dimm>(
411             bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage));
412     }
413 
414 #endif
415 
416     pcies.clear();
417     num = getTotalPcieSlot();
418     if (num == -1)
419     {
420         phosphor::logging::log<phosphor::logging::level::ERR>(
421             "get pcie total slot failed");
422         return;
423     }
424 
425     for (int index = 0; index < num; index++)
426     {
427         std::string path = pciePath + std::to_string(index);
428         pcies.emplace_back(std::make_unique<phosphor::smbios::Pcie>(
429             bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage));
430     }
431 
432     system.reset();
433     system = std::make_unique<System>(
434         bus, systemPath, smbiosDir.dir[smbiosDirIndex].dataStorage);
435 }
436 
437 int MDR_V2::getTotalCpuSlot()
438 {
439     uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage;
440     int num = 0;
441 
442     if (dataIn == nullptr)
443     {
444         phosphor::logging::log<phosphor::logging::level::ERR>(
445             "get cpu total slot failed - no storage data");
446         return -1;
447     }
448 
449     while (1)
450     {
451         dataIn = getSMBIOSTypePtr(dataIn, processorsType);
452         if (dataIn == nullptr)
453         {
454             break;
455         }
456         num++;
457         dataIn = smbiosNextPtr(dataIn);
458         if (dataIn == nullptr)
459         {
460             break;
461         }
462         if (num >= limitEntryLen)
463         {
464             break;
465         }
466     }
467 
468     return num;
469 }
470 
471 int MDR_V2::getTotalDimmSlot()
472 {
473     uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage;
474     uint8_t num = 0;
475 
476     if (dataIn == nullptr)
477     {
478         phosphor::logging::log<phosphor::logging::level::ERR>(
479             "Fail to get dimm total slot - no storage data");
480         return -1;
481     }
482 
483     while (1)
484     {
485         dataIn = getSMBIOSTypePtr(dataIn, memoryDeviceType);
486         if (dataIn == nullptr)
487         {
488             break;
489         }
490         num++;
491         dataIn = smbiosNextPtr(dataIn);
492         if (dataIn == nullptr)
493         {
494             break;
495         }
496         if (num >= limitEntryLen)
497         {
498             break;
499         }
500     }
501 
502     return num;
503 }
504 
505 int MDR_V2::getTotalPcieSlot()
506 {
507     uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage;
508     int num = 0;
509 
510     if (dataIn == nullptr)
511     {
512         phosphor::logging::log<phosphor::logging::level::ERR>(
513             "Fail to get total system slot - no storage data");
514         return -1;
515     }
516 
517     while (1)
518     {
519         dataIn = getSMBIOSTypePtr(dataIn, systemSlots);
520         if (dataIn == nullptr)
521         {
522             break;
523         }
524 
525         /* System slot type offset. Check if the slot is a PCIE slots. All
526          * PCIE slot type are hardcoded in a table.
527          */
528         if (pcieSmbiosType.find(*(dataIn + 5)) != pcieSmbiosType.end())
529         {
530             num++;
531         }
532         dataIn = smbiosNextPtr(dataIn);
533         if (dataIn == nullptr)
534         {
535             break;
536         }
537         if (num >= limitEntryLen)
538         {
539             break;
540         }
541     }
542 
543     return num;
544 }
545 
546 bool MDR_V2::agentSynchronizeData()
547 {
548     struct MDRSMBIOSHeader mdr2SMBIOS;
549     bool status = readDataFromFlash(&mdr2SMBIOS,
550                                     smbiosDir.dir[smbiosDirIndex].dataStorage);
551     if (!status)
552     {
553         phosphor::logging::log<phosphor::logging::level::ERR>(
554             "agent data sync failed - read data from flash failed");
555         return false;
556     }
557     else
558     {
559         systemInfoUpdate();
560         smbiosDir.dir[smbiosDirIndex].common.dataVersion = mdr2SMBIOS.dirVer;
561         smbiosDir.dir[smbiosDirIndex].common.timestamp = mdr2SMBIOS.timestamp;
562         smbiosDir.dir[smbiosDirIndex].common.size = mdr2SMBIOS.dataSize;
563         smbiosDir.dir[smbiosDirIndex].stage = MDR2SMBIOSStatusEnum::mdr2Loaded;
564         smbiosDir.dir[smbiosDirIndex].lock = MDR2DirLockEnum::mdr2DirUnlock;
565     }
566     return true;
567 }
568 
569 std::vector<uint32_t> MDR_V2::synchronizeDirectoryCommonData(uint8_t idIndex,
570                                                              uint32_t size)
571 {
572     std::chrono::microseconds usec(
573         defaultTimeout); // default lock time out is 2s
574     std::vector<uint32_t> result;
575     smbiosDir.dir[idIndex].common.size = size;
576     result.push_back(smbiosDir.dir[idIndex].common.dataSetSize);
577     result.push_back(smbiosDir.dir[idIndex].common.dataVersion);
578     result.push_back(smbiosDir.dir[idIndex].common.timestamp);
579 
580     timer.expires_after(usec);
581     timer.async_wait([this](boost::system::error_code ec) {
582         if (ec || this == nullptr)
583         {
584             phosphor::logging::log<phosphor::logging::level::ERR>(
585                 "Timer Error!");
586             return;
587         }
588         agentSynchronizeData();
589     });
590     return result;
591 }
592 
593 std::vector<boost::container::flat_map<std::string, RecordVariant>>
594     MDR_V2::getRecordType(size_t type)
595 {
596 
597     std::vector<boost::container::flat_map<std::string, RecordVariant>> ret;
598     if (type == memoryDeviceType)
599     {
600 
601         uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage;
602 
603         if (dataIn == nullptr)
604         {
605             throw std::runtime_error("Data not populated");
606         }
607 
608         do
609         {
610             dataIn =
611                 getSMBIOSTypePtr(dataIn, memoryDeviceType, sizeof(MemoryInfo));
612             if (dataIn == nullptr)
613             {
614                 break;
615             }
616             boost::container::flat_map<std::string, RecordVariant>& record =
617                 ret.emplace_back();
618 
619             auto memoryInfo = reinterpret_cast<MemoryInfo*>(dataIn);
620 
621             record["Type"] = memoryInfo->type;
622             record["Length"] = memoryInfo->length;
623             record["Handle"] = uint16_t(memoryInfo->handle);
624             record["Physical Memory Array Handle"] =
625                 uint16_t(memoryInfo->phyArrayHandle);
626             record["Memory Error Information Handle"] =
627                 uint16_t(memoryInfo->errInfoHandle);
628             record["Total Width"] = uint16_t(memoryInfo->totalWidth);
629             record["Data Width"] = uint16_t(memoryInfo->dataWidth);
630             record["Size"] = uint16_t(memoryInfo->size);
631             record["Form Factor"] = memoryInfo->formFactor;
632             record["Device Set"] = memoryInfo->deviceSet;
633             record["Device Locator"] = positionToString(
634                 memoryInfo->deviceLocator, memoryInfo->length, dataIn);
635             record["Bank Locator"] = positionToString(
636                 memoryInfo->bankLocator, memoryInfo->length, dataIn);
637             record["Memory Type"] = memoryInfo->memoryType;
638             record["Type Detail"] = uint16_t(memoryInfo->typeDetail);
639             record["Speed"] = uint16_t(memoryInfo->speed);
640             record["Manufacturer"] = positionToString(
641                 memoryInfo->manufacturer, memoryInfo->length, dataIn);
642             record["Serial Number"] = positionToString(
643                 memoryInfo->serialNum, memoryInfo->length, dataIn);
644             record["Asset Tag"] = positionToString(memoryInfo->assetTag,
645                                                    memoryInfo->length, dataIn);
646             record["Part Number"] = positionToString(
647                 memoryInfo->partNum, memoryInfo->length, dataIn);
648             record["Attributes"] = memoryInfo->attributes;
649             record["Extended Size"] = uint32_t(memoryInfo->extendedSize);
650             record["Configured Memory Speed"] =
651                 uint32_t(memoryInfo->confClockSpeed);
652             record["Minimum voltage"] = uint16_t(memoryInfo->minimumVoltage);
653             record["Maximum voltage"] = uint16_t(memoryInfo->maximumVoltage);
654             record["Configured voltage"] =
655                 uint16_t(memoryInfo->configuredVoltage);
656             record["Memory Technology"] = memoryInfo->memoryTechnology;
657             record["Memory Operating Mode Capabilty"] =
658                 uint16_t(memoryInfo->memoryOperatingModeCap);
659             record["Firmare Version"] = memoryInfo->firwareVersion;
660             record["Module Manufacturer ID"] =
661                 uint16_t(memoryInfo->modelManufId);
662             record["Module Product ID"] = uint16_t(memoryInfo->modelProdId);
663             record["Memory Subsystem Controller Manufacturer ID"] =
664                 uint16_t(memoryInfo->memSubConManufId);
665             record["Memory Subsystem Controller Product Id"] =
666                 uint16_t(memoryInfo->memSubConProdId);
667             record["Non-volatile Size"] = uint64_t(memoryInfo->nvSize);
668             record["Volatile Size"] = uint64_t(memoryInfo->volatileSize);
669             record["Cache Size"] = uint64_t(memoryInfo->cacheSize);
670             record["Logical Size"] = uint64_t(memoryInfo->logicalSize);
671         } while ((dataIn = smbiosNextPtr(dataIn)) != nullptr);
672 
673         return ret;
674     }
675 
676     throw std::invalid_argument("Invalid record type");
677     return ret;
678 }
679 
680 } // namespace smbios
681 } // namespace phosphor
682