/* // Copyright (c) 2018 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. */ #include "mdrv2.hpp" #include "pcieslot.hpp" #include #include #include #include #include namespace phosphor { namespace smbios { std::vector MDRV2::getDirectoryInformation(uint8_t dirIndex) { std::vector responseDir; std::ifstream smbiosFile(smbiosFilePath, std::ios_base::binary); if (!smbiosFile.good()) { phosphor::logging::log( "Read data from flash error - Open MDRV2 table file failure"); throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error:: InvalidParameter(); } if (dirIndex > smbiosDir.dirEntries) { responseDir.push_back(0); throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error:: InvalidParameter(); } responseDir.push_back(mdr2Version); responseDir.push_back(smbiosDir.dirVersion); uint8_t returnedEntries = smbiosDir.dirEntries - dirIndex; responseDir.push_back(returnedEntries); if ((dirIndex + returnedEntries) >= smbiosDir.dirEntries) { responseDir.push_back(0); } else { responseDir.push_back(smbiosDir.dirEntries - dirIndex - returnedEntries); } for (uint8_t index = dirIndex; index < smbiosDir.dirEntries; index++) { for (uint8_t indexId = 0; indexId < sizeof(DataIdStruct); indexId++) { responseDir.push_back( smbiosDir.dir[index].common.id.dataInfo[indexId]); } } return responseDir; } bool MDRV2::smbiosIsAvailForUpdate(uint8_t index) { bool ret = false; if (index >= maxDirEntries) { return ret; } switch (smbiosDir.dir[index].stage) { case MDR2SMBIOSStatusEnum::mdr2Updating: ret = false; break; case MDR2SMBIOSStatusEnum::mdr2Init: // This *looks* like there should be a break statement here, // as the effects of the previous statement are a noop given // the following code that this falls through to. // We've elected not to change it, though, since it's been // this way since the old generation, and it would affect // the way the code functions. // If it ain't broke, don't fix it. case MDR2SMBIOSStatusEnum::mdr2Loaded: case MDR2SMBIOSStatusEnum::mdr2Updated: if (smbiosDir.dir[index].lock == MDR2DirLockEnum::mdr2DirLock) { ret = false; } else { ret = true; } break; default: break; } return ret; } std::vector MDRV2::getDataOffer() { std::vector offer(sizeof(DataIdStruct)); if (smbiosIsAvailForUpdate(0)) { std::copy(smbiosDir.dir[0].common.id.dataInfo, &smbiosDir.dir[0].common.id.dataInfo[16], offer.data()); } else { phosphor::logging::log( "smbios is not ready for update"); throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error:: UpdateInProgress(); } return offer; } inline uint8_t MDRV2::smbiosValidFlag(uint8_t index) { FlagStatus ret = FlagStatus::flagIsInvalid; MDR2SMBIOSStatusEnum stage = smbiosDir.dir[index].stage; MDR2DirLockEnum lock = smbiosDir.dir[index].lock; switch (stage) { case MDR2SMBIOSStatusEnum::mdr2Loaded: case MDR2SMBIOSStatusEnum::mdr2Updated: if (lock == MDR2DirLockEnum::mdr2DirLock) { ret = FlagStatus::flagIsLocked; // locked } else { ret = FlagStatus::flagIsValid; // valid } break; case MDR2SMBIOSStatusEnum::mdr2Updating: case MDR2SMBIOSStatusEnum::mdr2Init: ret = FlagStatus::flagIsInvalid; // invalid break; default: break; } return static_cast(ret); } // If source variable size is 4 bytes (uint32_t) and destination is Vector type // is 1 byte (uint8_t), then by using this API can copy data byte by byte. For // Example copying data from smbiosDir.dir[idIndex].common.size and // smbiosDir.dir[idIndex].common.timestamp to vector variable responseInfo template void appendReversed(std::vector& vector, const T& value) { auto data = reinterpret_cast(&value); std::reverse_copy(data, data + sizeof(value), std::back_inserter(vector)); } std::vector MDRV2::getDataInformation(uint8_t idIndex) { std::vector responseInfo; responseInfo.push_back(mdr2Version); if (idIndex >= maxDirEntries) { throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error:: InvalidParameter(); } for (uint8_t index = 0; index < sizeof(DataIdStruct); index++) { responseInfo.push_back( smbiosDir.dir[idIndex].common.id.dataInfo[index]); } responseInfo.push_back(smbiosValidFlag(idIndex)); appendReversed(responseInfo, smbiosDir.dir[idIndex].common.size); responseInfo.push_back(smbiosDir.dir[idIndex].common.dataVersion); appendReversed(responseInfo, smbiosDir.dir[idIndex].common.timestamp); return responseInfo; } bool MDRV2::readDataFromFlash(MDRSMBIOSHeader* mdrHdr, uint8_t* data) { if (mdrHdr == nullptr) { phosphor::logging::log( "Read data from flash error - Invalid mdr header"); return false; } if (data == nullptr) { phosphor::logging::log( "Read data from flash error - Invalid data point"); return false; } std::ifstream smbiosFile(smbiosFilePath, std::ios_base::binary); if (!smbiosFile.good()) { phosphor::logging::log( "Read data from flash error - Open MDRV2 table file failure"); return false; } smbiosFile.clear(); smbiosFile.seekg(0, std::ios_base::end); size_t fileLength = smbiosFile.tellg(); smbiosFile.seekg(0, std::ios_base::beg); if (fileLength < sizeof(MDRSMBIOSHeader)) { phosphor::logging::log( "MDR V2 file size is smaller than mdr header"); return false; } smbiosFile.read(reinterpret_cast(mdrHdr), sizeof(MDRSMBIOSHeader)); if (mdrHdr->dataSize > smbiosTableStorageSize) { phosphor::logging::log( "Data size out of limitation"); smbiosFile.close(); return false; } fileLength -= sizeof(MDRSMBIOSHeader); if (fileLength < mdrHdr->dataSize) { smbiosFile.read(reinterpret_cast(data), fileLength); } else { smbiosFile.read(reinterpret_cast(data), mdrHdr->dataSize); } smbiosFile.close(); return true; } bool MDRV2::sendDirectoryInformation(uint8_t dirVersion, uint8_t dirIndex, uint8_t returnedEntries, uint8_t remainingEntries, std::vector dirEntry) { bool terminate = false; if ((dirIndex >= maxDirEntries) || (returnedEntries < 1)) { phosphor::logging::log( "Send Dir info failed - input parameter invalid"); throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error:: InvalidParameter(); } if ((static_cast(returnedEntries) * sizeof(DataIdStruct)) != dirEntry.size()) { phosphor::logging::log( "Directory size invalid"); throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error:: InvalidParameter(); } if (dirVersion == smbiosDir.dirVersion) { terminate = true; } else { if (remainingEntries > 0) { terminate = false; } else { terminate = true; smbiosDir.dirVersion = dirVersion; } uint8_t idIndex = dirIndex; smbiosDir.dirEntries = returnedEntries; uint8_t* pData = dirEntry.data(); if (pData == nullptr) { return false; } for (uint8_t index = 0; index < returnedEntries; index++) { auto data = reinterpret_cast(pData); std::copy(data->dataInfo, data->dataInfo + sizeof(DataIdStruct), smbiosDir.dir[idIndex + index].common.id.dataInfo); pData += sizeof(DataIdStruct); } } return terminate; } bool MDRV2::sendDataInformation(uint8_t idIndex, uint8_t /* flag */, uint32_t dataLen, uint32_t dataVer, uint32_t timeStamp) { if (idIndex >= maxDirEntries) { throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error:: InvalidParameter(); } int entryChanged = 0; if (smbiosDir.dir[idIndex].common.dataSetSize != dataLen) { entryChanged++; smbiosDir.dir[idIndex].common.dataSetSize = dataLen; } if (smbiosDir.dir[idIndex].common.dataVersion != dataVer) { entryChanged++; smbiosDir.dir[idIndex].common.dataVersion = dataVer; } if (smbiosDir.dir[idIndex].common.timestamp != timeStamp) { entryChanged++; smbiosDir.dir[idIndex].common.timestamp = timeStamp; } if (entryChanged == 0) { return false; } return true; } int MDRV2::findIdIndex(std::vector dataInfo) { if (dataInfo.size() != sizeof(DataIdStruct)) { phosphor::logging::log( "Length of dataInfo invalid"); throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error:: InvalidId(); } std::array arrayDataInfo; std::copy(dataInfo.begin(), dataInfo.end(), arrayDataInfo.begin()); for (int index = 0; index < smbiosDir.dirEntries; index++) { size_t info = 0; for (; info < arrayDataInfo.size(); info++) { if (arrayDataInfo[info] != smbiosDir.dir[index].common.id.dataInfo[info]) { break; } } if (info == arrayDataInfo.size()) { return index; } } throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::InvalidId(); } uint8_t MDRV2::directoryEntries(uint8_t value) { std::ifstream smbiosFile(smbiosFilePath, std::ios_base::binary); if (!smbiosFile.good()) { phosphor::logging::log( "Read data from flash error - Open MDRV2 table file failure"); value = 0; } else { value = smbiosDir.dirEntries; } return sdbusplus::server::xyz::openbmc_project::smbios::MDRV2:: directoryEntries(value); } void MDRV2::systemInfoUpdate() { // By default, look for System interface on any system/board/* object std::string mapperAncestorPath = smbiosInventoryPath; std::string matchParentPath = smbiosInventoryPath + "/board/"; bool requireExactMatch = false; // If customized, look for System on only that custom object if (smbiosInventoryPath != defaultInventoryPath) { std::filesystem::path path(smbiosInventoryPath); // Search under parent to find exact match for self mapperAncestorPath = path.parent_path().string(); matchParentPath = mapperAncestorPath; requireExactMatch = true; } std::string motherboardPath; auto method = bus->new_method_call(mapperBusName, mapperPath, mapperInterface, "GetSubTreePaths"); method.append(mapperAncestorPath); method.append(0); // If customized, also accept Board as anchor, not just System std::vector desiredInterfaces{systemInterface}; if (requireExactMatch) { desiredInterfaces.emplace_back(boardInterface); } method.append(desiredInterfaces); try { std::vector paths; sdbusplus::message_t reply = bus->call(method); reply.read(paths); size_t pathsCount = paths.size(); for (size_t i = 0; i < pathsCount; ++i) { if (requireExactMatch && (paths[i] != smbiosInventoryPath)) { continue; } motherboardPath = std::move(paths[i]); break; } } catch (const sdbusplus::exception_t& e) { lg2::error( "Exception while trying to find Inventory anchor object for SMBIOS content {I}: {E}", "I", smbiosInventoryPath, "E", e.what()); phosphor::logging::log( "Failed to query system motherboard", phosphor::logging::entry("ERROR=%s", e.what())); } if (motherboardPath.empty()) { phosphor::logging::log( "Failed to get system motherboard dbus path. Setting up a " "match rule"); if (motherboardConfigMatch) { lg2::info("Motherboard match rule already exists"); } else { motherboardConfigMatch = std::make_unique( *bus, sdbusplus::bus::match::rules::interfacesAdded() + sdbusplus::bus::match::rules::argNpath(0, matchParentPath), [this, requireExactMatch](sdbusplus::message_t& msg) { sdbusplus::message::object_path objectName; boost::container::flat_map< std::string, boost::container::flat_map< std::string, std::variant>> msgData; msg.read(objectName, msgData); bool gotMatch = false; if (msgData.contains(systemInterface)) { lg2::info("Successful match on system interface"); gotMatch = true; } // If customized, also accept Board as anchor, not just System if (requireExactMatch && msgData.contains(boardInterface)) { lg2::info("Successful match on board interface"); gotMatch = true; } if (gotMatch) { // There is a race condition here: our desired interface // has just been created, triggering the D-Bus callback, // but Object Mapper has not been told of it yet. The // mapper must also add it. Stall for time, so it can. sleep(2); systemInfoUpdate(); } }); } } else { #ifdef ASSOC_TRIM_PATH // When enabled, chop off last component of motherboardPath, to trim one // layer, so that associations are built to the underlying chassis // itself, not the system boards in the chassis. This is for // compatibility with traditional systems which only have one // motherboard per chassis. std::filesystem::path foundPath(motherboardPath); motherboardPath = foundPath.parent_path().string(); #endif lg2::info("Found Inventory anchor object for SMBIOS content {I}: {M}", "I", smbiosInventoryPath, "M", motherboardPath); } lg2::info("Using Inventory anchor object for SMBIOS content {I}: {M}", "I", smbiosInventoryPath, "M", motherboardPath); std::optional num = getTotalCpuSlot(); if (!num) { phosphor::logging::log( "get cpu total slot failed"); return; } // In case the new size is smaller than old, trim the vector if (*num < cpus.size()) { cpus.resize(*num); } for (unsigned int index = 0; index < *num; index++) { std::string path = smbiosInventoryPath + cpuSuffix + std::to_string(index); if (index + 1 > cpus.size()) { cpus.emplace_back(std::make_unique( *bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage, motherboardPath)); } else { cpus[index]->infoUpdate(smbiosDir.dir[smbiosDirIndex].dataStorage, motherboardPath); } } #ifdef DIMM_DBUS num = getTotalDimmSlot(); if (!num) { phosphor::logging::log( "get dimm total slot failed"); return; } // In case the new size is smaller than old, trim the vector if (*num < dimms.size()) { dimms.resize(*num); } for (unsigned int index = 0; index < *num; index++) { std::string path = smbiosInventoryPath + dimmSuffix + std::to_string(index); if (index + 1 > dimms.size()) { dimms.emplace_back(std::make_unique( *bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage, motherboardPath)); } else { dimms[index]->memoryInfoUpdate( smbiosDir.dir[smbiosDirIndex].dataStorage, motherboardPath); } } #endif num = getTotalPcieSlot(); if (!num) { phosphor::logging::log( "get pcie total slot failed"); return; } // In case the new size is smaller than old, trim the vector if (*num < pcies.size()) { pcies.resize(*num); } for (unsigned int index = 0; index < *num; index++) { std::string path = smbiosInventoryPath + pcieSuffix + std::to_string(index); if (index + 1 > pcies.size()) { pcies.emplace_back(std::make_unique( *bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage, motherboardPath)); } else { pcies[index]->pcieInfoUpdate( smbiosDir.dir[smbiosDirIndex].dataStorage, motherboardPath); } } system.reset(); system = std::make_unique(bus, smbiosInventoryPath + systemSuffix, smbiosDir.dir[smbiosDirIndex].dataStorage, smbiosFilePath); } std::optional MDRV2::getTotalCpuSlot() { uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage; size_t num = 0; if (dataIn == nullptr) { phosphor::logging::log( "get cpu total slot failed - no storage data"); return std::nullopt; } while (1) { dataIn = getSMBIOSTypePtr(dataIn, processorsType); if (dataIn == nullptr) { break; } num++; dataIn = smbiosNextPtr(dataIn); if (dataIn == nullptr) { break; } if (num >= limitEntryLen) { break; } } return num; } std::optional MDRV2::getTotalDimmSlot() { uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage; size_t num = 0; if (dataIn == nullptr) { phosphor::logging::log( "Fail to get dimm total slot - no storage data"); return std::nullopt; } while (1) { dataIn = getSMBIOSTypePtr(dataIn, memoryDeviceType); if (dataIn == nullptr) { break; } num++; dataIn = smbiosNextPtr(dataIn); if (dataIn == nullptr) { break; } if (num >= limitEntryLen) { break; } } return num; } std::optional MDRV2::getTotalPcieSlot() { uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage; size_t num = 0; if (dataIn == nullptr) { phosphor::logging::log( "Fail to get total system slot - no storage data"); return std::nullopt; } while (1) { dataIn = getSMBIOSTypePtr(dataIn, systemSlots); if (dataIn == nullptr) { break; } /* System slot type offset. Check if the slot is a PCIE slots. All * PCIE slot type are hardcoded in a table. */ if (pcieSmbiosType.find(*(dataIn + 5)) != pcieSmbiosType.end()) { num++; } dataIn = smbiosNextPtr(dataIn); if (dataIn == nullptr) { break; } if (num >= limitEntryLen) { break; } } return num; } bool MDRV2::checkSMBIOSVersion(uint8_t* dataIn) { const std::string anchorString21 = "_SM_"; const std::string anchorString30 = "_SM3_"; std::string buffer(reinterpret_cast(dataIn), smbiosTableStorageSize); auto it = std::search(std::begin(buffer), std::end(buffer), std::begin(anchorString21), std::end(anchorString21)); bool smbios21Found = it != std::end(buffer); if (!smbios21Found) { phosphor::logging::log( "SMBIOS 2.1 Anchor String not found. Looking for SMBIOS 3.0"); it = std::search(std::begin(buffer), std::end(buffer), std::begin(anchorString30), std::end(anchorString30)); if (it == std::end(buffer)) { phosphor::logging::log( "SMBIOS 2.1 and 3.0 Anchor Strings not found"); return false; } } auto pos = std::distance(std::begin(buffer), it); size_t length = smbiosTableStorageSize - pos; uint8_t foundMajorVersion; uint8_t foundMinorVersion; if (smbios21Found) { if (length < sizeof(EntryPointStructure21)) { phosphor::logging::log( "Invalid entry point structure for SMBIOS 2.1"); return false; } auto epStructure = reinterpret_cast(&dataIn[pos]); foundMajorVersion = epStructure->smbiosVersion.majorVersion; foundMinorVersion = epStructure->smbiosVersion.minorVersion; } else { if (length < sizeof(EntryPointStructure30)) { phosphor::logging::log( "Invalid entry point structure for SMBIOS 3.0"); return false; } auto epStructure = reinterpret_cast(&dataIn[pos]); foundMajorVersion = epStructure->smbiosVersion.majorVersion; foundMinorVersion = epStructure->smbiosVersion.minorVersion; } lg2::info("SMBIOS VERSION - {MAJOR}.{MINOR}", "MAJOR", foundMajorVersion, "MINOR", foundMinorVersion); auto itr = std::find_if(std::begin(supportedSMBIOSVersions), std::end(supportedSMBIOSVersions), [&](SMBIOSVersion versionItr) { return versionItr.majorVersion == foundMajorVersion && versionItr.minorVersion == foundMinorVersion; }); if (itr == std::end(supportedSMBIOSVersions)) { return false; } return true; } bool MDRV2::agentSynchronizeData() { struct MDRSMBIOSHeader mdr2SMBIOS; bool status = readDataFromFlash(&mdr2SMBIOS, smbiosDir.dir[smbiosDirIndex].dataStorage); if (!status) { phosphor::logging::log( "agent data sync failed - read data from flash failed"); return false; } if (!checkSMBIOSVersion(smbiosDir.dir[smbiosDirIndex].dataStorage)) { phosphor::logging::log( "Unsupported SMBIOS table version"); return false; } systemInfoUpdate(); smbiosDir.dir[smbiosDirIndex].common.dataVersion = mdr2SMBIOS.dirVer; smbiosDir.dir[smbiosDirIndex].common.timestamp = mdr2SMBIOS.timestamp; smbiosDir.dir[smbiosDirIndex].common.size = mdr2SMBIOS.dataSize; smbiosDir.dir[smbiosDirIndex].stage = MDR2SMBIOSStatusEnum::mdr2Loaded; smbiosDir.dir[smbiosDirIndex].lock = MDR2DirLockEnum::mdr2DirUnlock; return true; } std::vector MDRV2::synchronizeDirectoryCommonData(uint8_t idIndex, uint32_t size) { std::chrono::microseconds usec( defaultTimeout); // default lock time out is 2s std::vector result; smbiosDir.dir[idIndex].common.size = size; result.push_back(smbiosDir.dir[idIndex].common.dataSetSize); result.push_back(smbiosDir.dir[idIndex].common.dataVersion); result.push_back(smbiosDir.dir[idIndex].common.timestamp); timer.expires_after(usec); timer.async_wait([this](boost::system::error_code ec) { if (ec) { phosphor::logging::log( "Timer Error!"); return; } agentSynchronizeData(); }); return result; } std::vector> MDRV2::getRecordType(size_t type) { std::vector> ret; if (type == memoryDeviceType) { uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage; if (dataIn == nullptr) { throw std::runtime_error("Data not populated"); } do { dataIn = getSMBIOSTypePtr(dataIn, memoryDeviceType, sizeof(MemoryInfo)); if (dataIn == nullptr) { break; } boost::container::flat_map& record = ret.emplace_back(); auto memoryInfo = reinterpret_cast(dataIn); record["Type"] = memoryInfo->type; record["Length"] = memoryInfo->length; record["Handle"] = uint16_t(memoryInfo->handle); record["Physical Memory Array Handle"] = uint16_t(memoryInfo->phyArrayHandle); record["Memory Error Information Handle"] = uint16_t(memoryInfo->errInfoHandle); record["Total Width"] = uint16_t(memoryInfo->totalWidth); record["Data Width"] = uint16_t(memoryInfo->dataWidth); record["Size"] = uint16_t(memoryInfo->size); record["Form Factor"] = memoryInfo->formFactor; record["Device Set"] = memoryInfo->deviceSet; record["Device Locator"] = positionToString( memoryInfo->deviceLocator, memoryInfo->length, dataIn); record["Bank Locator"] = positionToString( memoryInfo->bankLocator, memoryInfo->length, dataIn); record["Memory Type"] = memoryInfo->memoryType; record["Type Detail"] = uint16_t(memoryInfo->typeDetail); record["Speed"] = uint16_t(memoryInfo->speed); record["Manufacturer"] = positionToString( memoryInfo->manufacturer, memoryInfo->length, dataIn); record["Serial Number"] = positionToString( memoryInfo->serialNum, memoryInfo->length, dataIn); record["Asset Tag"] = positionToString(memoryInfo->assetTag, memoryInfo->length, dataIn); record["Part Number"] = positionToString( memoryInfo->partNum, memoryInfo->length, dataIn); record["Attributes"] = uint32_t(memoryInfo->attributes); record["Extended Size"] = uint32_t(memoryInfo->extendedSize); record["Configured Memory Speed"] = uint32_t(memoryInfo->confClockSpeed); record["Minimum voltage"] = uint16_t(memoryInfo->minimumVoltage); record["Maximum voltage"] = uint16_t(memoryInfo->maximumVoltage); record["Configured voltage"] = uint16_t(memoryInfo->configuredVoltage); record["Memory Technology"] = memoryInfo->memoryTechnology; record["Memory Operating Mode Capability"] = uint16_t(memoryInfo->memoryOperatingModeCap); record["Firmware Version"] = memoryInfo->firwareVersion; record["Module Manufacturer ID"] = uint16_t(memoryInfo->modelManufId); record["Module Product ID"] = uint16_t(memoryInfo->modelProdId); record["Memory Subsystem Controller Manufacturer ID"] = uint16_t(memoryInfo->memSubConManufId); record["Memory Subsystem Controller Product Id"] = uint16_t(memoryInfo->memSubConProdId); record["Non-volatile Size"] = uint64_t(memoryInfo->nvSize); record["Volatile Size"] = uint64_t(memoryInfo->volatileSize); record["Cache Size"] = uint64_t(memoryInfo->cacheSize); record["Logical Size"] = uint64_t(memoryInfo->logicalSize); } while ((dataIn = smbiosNextPtr(dataIn)) != nullptr); return ret; } throw std::invalid_argument("Invalid record type"); return ret; } } // namespace smbios } // namespace phosphor