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