xref: /openbmc/smbios-mdr/src/mdrv2.cpp (revision a4c0870faaf18083dfb6c90d5a3168d2862bdbb8)
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         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
39             InvalidParameter();
40     }
41     responseDir.push_back(mdr2Version);
42     responseDir.push_back(smbiosDir.dirVersion);
43     uint8_t returnedEntries = smbiosDir.dirEntries - dirIndex;
44     responseDir.push_back(returnedEntries);
45     if ((dirIndex + returnedEntries) >= smbiosDir.dirEntries)
46     {
47         responseDir.push_back(0);
48     }
49     else
50     {
51         responseDir.push_back(smbiosDir.dirEntries - dirIndex -
52                               returnedEntries);
53     }
54     for (uint8_t index = dirIndex; index < smbiosDir.dirEntries; index++)
55     {
56         for (uint8_t indexId = 0; indexId < sizeof(DataIdStruct); indexId++)
57         {
58             responseDir.push_back(
59                 smbiosDir.dir[index].common.id.dataInfo[indexId]);
60         }
61     }
62 
63     return responseDir;
64 }
65 
66 bool MDR_V2::smbiosIsAvailForUpdate(uint8_t index)
67 {
68     bool ret = false;
69     if (index > maxDirEntries)
70     {
71         return ret;
72     }
73 
74     switch (smbiosDir.dir[index].stage)
75     {
76         case MDR2SMBIOSStatusEnum::mdr2Updating:
77             ret = false;
78             break;
79 
80         case MDR2SMBIOSStatusEnum::mdr2Init:
81             // This *looks* like there should be a break statement here,
82             // as the effects of the previous statement are a noop given
83             // the following code that this falls through to.
84             // We've elected not to change it, though, since it's been
85             // this way since the old generation, and it would affect
86             // the way the code functions.
87             // If it ain't broke, don't fix it.
88 
89         case MDR2SMBIOSStatusEnum::mdr2Loaded:
90         case MDR2SMBIOSStatusEnum::mdr2Updated:
91             if (smbiosDir.dir[index].lock == MDR2DirLockEnum::mdr2DirLock)
92             {
93                 ret = false;
94             }
95             else
96             {
97                 ret = true;
98             }
99             break;
100 
101         default:
102             break;
103     }
104 
105     return ret;
106 }
107 
108 std::vector<uint8_t> MDR_V2::getDataOffer()
109 {
110     std::vector<uint8_t> offer(sizeof(DataIdStruct));
111     if (smbiosIsAvailForUpdate(0))
112     {
113         std::copy(smbiosDir.dir[0].common.id.dataInfo,
114                   &smbiosDir.dir[0].common.id.dataInfo[16], offer.data());
115     }
116     else
117     {
118         phosphor::logging::log<phosphor::logging::level::ERR>(
119             "smbios is not ready for update");
120         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
121             UpdateInProgress();
122     }
123     return offer;
124 }
125 
126 inline uint8_t MDR_V2::smbiosValidFlag(uint8_t index)
127 {
128     FlagStatus ret = FlagStatus::flagIsInvalid;
129     MDR2SMBIOSStatusEnum stage = smbiosDir.dir[index].stage;
130     MDR2DirLockEnum lock = smbiosDir.dir[index].lock;
131 
132     switch (stage)
133     {
134         case MDR2SMBIOSStatusEnum::mdr2Loaded:
135         case MDR2SMBIOSStatusEnum::mdr2Updated:
136             if (lock == MDR2DirLockEnum::mdr2DirLock)
137             {
138                 ret = FlagStatus::flagIsLocked; // locked
139             }
140             else
141             {
142                 ret = FlagStatus::flagIsValid; // valid
143             }
144             break;
145 
146         case MDR2SMBIOSStatusEnum::mdr2Updating:
147         case MDR2SMBIOSStatusEnum::mdr2Init:
148             ret = FlagStatus::flagIsInvalid; // invalid
149             break;
150 
151         default:
152             break;
153     }
154 
155     return static_cast<uint8_t>(ret);
156 }
157 
158 // If source variable size is 4 bytes (uint32_t) and destination is Vector type
159 // is 1 byte (uint8_t), then by using this API can copy data byte by byte. For
160 // Example copying data from smbiosDir.dir[idIndex].common.size and
161 // smbiosDir.dir[idIndex].common.timestamp to vector variable responseInfo
162 template <typename T>
163 void appendReversed(std::vector<uint8_t>& vector, const T& value)
164 {
165     auto data = reinterpret_cast<const uint8_t*>(&value);
166     std::reverse_copy(data, data + sizeof(value), std::back_inserter(vector));
167 }
168 
169 std::vector<uint8_t> MDR_V2::getDataInformation(uint8_t idIndex)
170 {
171     std::vector<uint8_t> responseInfo;
172     responseInfo.push_back(mdr2Version);
173 
174     if (idIndex >= maxDirEntries)
175     {
176         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
177             InvalidParameter();
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         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
257             InvalidParameter();
258     }
259     if (dirEntry.size() < sizeof(Mdr2DirEntry))
260     {
261         phosphor::logging::log<phosphor::logging::level::ERR>(
262             "Directory size invalid");
263         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
264             InvalidParameter();
265     }
266     if (dirVersion == smbiosDir.dirVersion)
267     {
268         teminate = true;
269     }
270     else
271     {
272         if (remainingEntries > 0)
273         {
274             teminate = false;
275         }
276         else
277         {
278             teminate = true;
279             smbiosDir.dirVersion = dirVersion;
280         }
281         uint8_t idIndex = dirIndex;
282 
283         uint8_t* pData = dirEntry.data();
284         if (pData == nullptr)
285         {
286             return false;
287         }
288         for (uint8_t index = 0; index < returnedEntries; index++)
289         {
290             auto data = reinterpret_cast<const Mdr2DirEntry*>(pData);
291             smbiosDir.dir[idIndex + index].common.dataVersion =
292                 data->dataVersion;
293             std::copy(data->id.dataInfo,
294                       data->id.dataInfo + sizeof(DataIdStruct),
295                       smbiosDir.dir[idIndex + index].common.id.dataInfo);
296             smbiosDir.dir[idIndex + index].common.dataSetSize = data->size;
297             smbiosDir.dir[idIndex + index].common.timestamp = data->timestamp;
298             pData += sizeof(returnedEntries);
299         }
300     }
301     return teminate;
302 }
303 
304 bool MDR_V2::sendDataInformation(uint8_t idIndex, uint8_t flag,
305                                  uint32_t dataLen, uint32_t dataVer,
306                                  uint32_t timeStamp)
307 {
308     if (idIndex >= maxDirEntries)
309     {
310         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
311             InvalidParameter();
312     }
313     int entryChanged = 0;
314     if (smbiosDir.dir[idIndex].common.dataSetSize != dataLen)
315     {
316         entryChanged++;
317         smbiosDir.dir[idIndex].common.dataSetSize = dataLen;
318     }
319 
320     if (smbiosDir.dir[idIndex].common.dataVersion != dataVer)
321     {
322         entryChanged++;
323         smbiosDir.dir[idIndex].common.dataVersion = dataVer;
324     }
325 
326     if (smbiosDir.dir[idIndex].common.timestamp != timeStamp)
327     {
328         entryChanged++;
329         smbiosDir.dir[idIndex].common.timestamp = timeStamp;
330     }
331     if (entryChanged == 0)
332     {
333         return false;
334     }
335     return true;
336 }
337 
338 int MDR_V2::findIdIndex(std::vector<uint8_t> dataInfo)
339 {
340     if (dataInfo.size() != sizeof(DataIdStruct))
341     {
342         phosphor::logging::log<phosphor::logging::level::ERR>(
343             "Length of dataInfo invalid");
344         throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::
345             InvalidId();
346     }
347     std::array<uint8_t, 16> arrayDataInfo;
348 
349     std::copy(dataInfo.begin(), dataInfo.end(), arrayDataInfo.begin());
350 
351     for (int index = 0; index < smbiosDir.dirEntries; index++)
352     {
353         int info = 0;
354         for (; info < arrayDataInfo.size(); info++)
355         {
356             if (arrayDataInfo[info] !=
357                 smbiosDir.dir[index].common.id.dataInfo[info])
358             {
359                 break;
360             }
361         }
362         if (info == arrayDataInfo.size())
363         {
364             return index;
365         }
366     }
367     throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::InvalidId();
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             throw std::runtime_error("Data not populated");
550         }
551 
552         do
553         {
554             dataIn =
555                 getSMBIOSTypePtr(dataIn, memoryDeviceType, sizeof(MemoryInfo));
556             if (dataIn == nullptr)
557             {
558                 break;
559             }
560             boost::container::flat_map<std::string, RecordVariant>& record =
561                 ret.emplace_back();
562 
563             auto memoryInfo = reinterpret_cast<MemoryInfo*>(dataIn);
564 
565             record["Type"] = memoryInfo->type;
566             record["Length"] = memoryInfo->length;
567             record["Handle"] = uint16_t(memoryInfo->handle);
568             record["Physical Memory Array Handle"] =
569                 uint16_t(memoryInfo->phyArrayHandle);
570             record["Memory Error Information Handle"] =
571                 uint16_t(memoryInfo->errInfoHandle);
572             record["Total Width"] = uint16_t(memoryInfo->totalWidth);
573             record["Data Width"] = uint16_t(memoryInfo->dataWidth);
574             record["Size"] = uint16_t(memoryInfo->size);
575             record["Form Factor"] = memoryInfo->formFactor;
576             record["Device Set"] = memoryInfo->deviceSet;
577             record["Device Locator"] = positionToString(
578                 memoryInfo->deviceLocator, memoryInfo->length, dataIn);
579             record["Bank Locator"] = positionToString(
580                 memoryInfo->bankLocator, memoryInfo->length, dataIn);
581             record["Memory Type"] = memoryInfo->memoryType;
582             record["Type Detail"] = uint16_t(memoryInfo->typeDetail);
583             record["Speed"] = uint16_t(memoryInfo->speed);
584             record["Manufacturer"] = positionToString(
585                 memoryInfo->manufacturer, memoryInfo->length, dataIn);
586             record["Serial Number"] = positionToString(
587                 memoryInfo->serialNum, memoryInfo->length, dataIn);
588             record["Asset Tag"] = positionToString(memoryInfo->assetTag,
589                                                    memoryInfo->length, dataIn);
590             record["Part Number"] = positionToString(
591                 memoryInfo->partNum, memoryInfo->length, dataIn);
592             record["Attributes"] = memoryInfo->attributes;
593             record["Extended Size"] = uint32_t(memoryInfo->extendedSize);
594             record["Configured Memory Speed"] =
595                 uint32_t(memoryInfo->confClockSpeed);
596             record["Minimum voltage"] = uint16_t(memoryInfo->minimumVoltage);
597             record["Maximum voltage"] = uint16_t(memoryInfo->maximumVoltage);
598             record["Configured voltage"] =
599                 uint16_t(memoryInfo->configuredVoltage);
600             record["Memory Technology"] = memoryInfo->memoryTechnology;
601             record["Memory Operating Mode Capabilty"] =
602                 uint16_t(memoryInfo->memoryOperatingModeCap);
603             record["Firmare Version"] = memoryInfo->firwareVersion;
604             record["Module Manufacturer ID"] =
605                 uint16_t(memoryInfo->modelManufId);
606             record["Module Product ID"] = uint16_t(memoryInfo->modelProdId);
607             record["Memory Subsystem Controller Manufacturer ID"] =
608                 uint16_t(memoryInfo->memSubConManufId);
609             record["Memory Subsystem Controller Product Id"] =
610                 uint16_t(memoryInfo->memSubConProdId);
611             record["Non-volatile Size"] = uint64_t(memoryInfo->nvSize);
612             record["Volatile Size"] = uint64_t(memoryInfo->volatileSize);
613             record["Cache Size"] = uint64_t(memoryInfo->cacheSize);
614             record["Logical Size"] = uint64_t(memoryInfo->logicalSize);
615         } while ((dataIn = smbiosNextPtr(dataIn)) != nullptr);
616 
617         return ret;
618     }
619 
620     throw std::invalid_argument("Invalid record type");
621     return ret;
622 }
623 
624 } // namespace smbios
625 } // namespace phosphor
626