1*3f7c5e40SJason M. Bills /* 2*3f7c5e40SJason M. Bills // Copyright (c) 2017 2018 Intel Corporation 3*3f7c5e40SJason M. Bills // 4*3f7c5e40SJason M. Bills // Licensed under the Apache License, Version 2.0 (the "License"); 5*3f7c5e40SJason M. Bills // you may not use this file except in compliance with the License. 6*3f7c5e40SJason M. Bills // You may obtain a copy of the License at 7*3f7c5e40SJason M. Bills // 8*3f7c5e40SJason M. Bills // http://www.apache.org/licenses/LICENSE-2.0 9*3f7c5e40SJason M. Bills // 10*3f7c5e40SJason M. Bills // Unless required by applicable law or agreed to in writing, software 11*3f7c5e40SJason M. Bills // distributed under the License is distributed on an "AS IS" BASIS, 12*3f7c5e40SJason M. Bills // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*3f7c5e40SJason M. Bills // See the License for the specific language governing permissions and 14*3f7c5e40SJason M. Bills // limitations under the License. 15*3f7c5e40SJason M. Bills */ 16*3f7c5e40SJason M. Bills 17*3f7c5e40SJason M. Bills #include <host-ipmid/ipmid-api.h> 18*3f7c5e40SJason M. Bills 19*3f7c5e40SJason M. Bills #include <boost/container/flat_map.hpp> 20*3f7c5e40SJason M. Bills #include <commandutils.hpp> 21*3f7c5e40SJason M. Bills #include <iostream> 22*3f7c5e40SJason M. Bills #include <phosphor-logging/log.hpp> 23*3f7c5e40SJason M. Bills #include <sdbusplus/message/types.hpp> 24*3f7c5e40SJason M. Bills #include <sdbusplus/timer.hpp> 25*3f7c5e40SJason M. Bills #include <storagecommands.hpp> 26*3f7c5e40SJason M. Bills 27*3f7c5e40SJason M. Bills namespace ipmi 28*3f7c5e40SJason M. Bills { 29*3f7c5e40SJason M. Bills 30*3f7c5e40SJason M. Bills namespace storage 31*3f7c5e40SJason M. Bills { 32*3f7c5e40SJason M. Bills 33*3f7c5e40SJason M. Bills constexpr static const size_t maxFruSdrNameSize = 16; 34*3f7c5e40SJason M. Bills using ManagedObjectType = boost::container::flat_map< 35*3f7c5e40SJason M. Bills sdbusplus::message::object_path, 36*3f7c5e40SJason M. Bills boost::container::flat_map< 37*3f7c5e40SJason M. Bills std::string, boost::container::flat_map<std::string, DbusVariant>>>; 38*3f7c5e40SJason M. Bills using ManagedEntry = std::pair< 39*3f7c5e40SJason M. Bills sdbusplus::message::object_path, 40*3f7c5e40SJason M. Bills boost::container::flat_map< 41*3f7c5e40SJason M. Bills std::string, boost::container::flat_map<std::string, DbusVariant>>>; 42*3f7c5e40SJason M. Bills 43*3f7c5e40SJason M. Bills constexpr static const char* fruDeviceServiceName = "com.intel.FruDevice"; 44*3f7c5e40SJason M. Bills 45*3f7c5e40SJason M. Bills static std::vector<uint8_t> fruCache; 46*3f7c5e40SJason M. Bills static uint8_t cacheBus = 0xFF; 47*3f7c5e40SJason M. Bills static uint8_t cacheAddr = 0XFF; 48*3f7c5e40SJason M. Bills 49*3f7c5e40SJason M. Bills std::unique_ptr<phosphor::Timer> cacheTimer = nullptr; 50*3f7c5e40SJason M. Bills 51*3f7c5e40SJason M. Bills // we unfortunately have to build a map of hashes in case there is a 52*3f7c5e40SJason M. Bills // collision to verify our dev-id 53*3f7c5e40SJason M. Bills boost::container::flat_map<uint8_t, std::pair<uint8_t, uint8_t>> deviceHashes; 54*3f7c5e40SJason M. Bills 55*3f7c5e40SJason M. Bills static sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); 56*3f7c5e40SJason M. Bills 57*3f7c5e40SJason M. Bills bool writeFru() 58*3f7c5e40SJason M. Bills { 59*3f7c5e40SJason M. Bills sdbusplus::message::message writeFru = dbus.new_method_call( 60*3f7c5e40SJason M. Bills fruDeviceServiceName, "/xyz/openbmc_project/FruDevice", 61*3f7c5e40SJason M. Bills "xyz.openbmc_project.FruDeviceManager", "WriteFru"); 62*3f7c5e40SJason M. Bills writeFru.append(cacheBus, cacheAddr, fruCache); 63*3f7c5e40SJason M. Bills try 64*3f7c5e40SJason M. Bills { 65*3f7c5e40SJason M. Bills sdbusplus::message::message writeFruResp = dbus.call(writeFru); 66*3f7c5e40SJason M. Bills } 67*3f7c5e40SJason M. Bills catch (sdbusplus::exception_t&) 68*3f7c5e40SJason M. Bills { 69*3f7c5e40SJason M. Bills // todo: log sel? 70*3f7c5e40SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>( 71*3f7c5e40SJason M. Bills "error writing fru"); 72*3f7c5e40SJason M. Bills return false; 73*3f7c5e40SJason M. Bills } 74*3f7c5e40SJason M. Bills return true; 75*3f7c5e40SJason M. Bills } 76*3f7c5e40SJason M. Bills 77*3f7c5e40SJason M. Bills ipmi_ret_t replaceCacheFru(uint8_t devId) 78*3f7c5e40SJason M. Bills { 79*3f7c5e40SJason M. Bills static uint8_t lastDevId = 0xFF; 80*3f7c5e40SJason M. Bills 81*3f7c5e40SJason M. Bills bool timerRunning = (cacheTimer != nullptr) && !cacheTimer->isExpired(); 82*3f7c5e40SJason M. Bills if (lastDevId == devId && timerRunning) 83*3f7c5e40SJason M. Bills { 84*3f7c5e40SJason M. Bills return IPMI_CC_OK; // cache already up to date 85*3f7c5e40SJason M. Bills } 86*3f7c5e40SJason M. Bills // if timer is running, stop it and writeFru manually 87*3f7c5e40SJason M. Bills else if (timerRunning) 88*3f7c5e40SJason M. Bills { 89*3f7c5e40SJason M. Bills cacheTimer->stop(); 90*3f7c5e40SJason M. Bills writeFru(); 91*3f7c5e40SJason M. Bills } 92*3f7c5e40SJason M. Bills 93*3f7c5e40SJason M. Bills sdbusplus::message::message getObjects = dbus.new_method_call( 94*3f7c5e40SJason M. Bills fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager", 95*3f7c5e40SJason M. Bills "GetManagedObjects"); 96*3f7c5e40SJason M. Bills ManagedObjectType frus; 97*3f7c5e40SJason M. Bills try 98*3f7c5e40SJason M. Bills { 99*3f7c5e40SJason M. Bills sdbusplus::message::message resp = dbus.call(getObjects); 100*3f7c5e40SJason M. Bills resp.read(frus); 101*3f7c5e40SJason M. Bills } 102*3f7c5e40SJason M. Bills catch (sdbusplus::exception_t&) 103*3f7c5e40SJason M. Bills { 104*3f7c5e40SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>( 105*3f7c5e40SJason M. Bills "replaceCacheFru: error getting managed objects"); 106*3f7c5e40SJason M. Bills return IPMI_CC_RESPONSE_ERROR; 107*3f7c5e40SJason M. Bills } 108*3f7c5e40SJason M. Bills 109*3f7c5e40SJason M. Bills deviceHashes.clear(); 110*3f7c5e40SJason M. Bills 111*3f7c5e40SJason M. Bills // hash the object paths to create unique device id's. increment on 112*3f7c5e40SJason M. Bills // collision 113*3f7c5e40SJason M. Bills std::hash<std::string> hasher; 114*3f7c5e40SJason M. Bills for (const auto& fru : frus) 115*3f7c5e40SJason M. Bills { 116*3f7c5e40SJason M. Bills auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice"); 117*3f7c5e40SJason M. Bills if (fruIface == fru.second.end()) 118*3f7c5e40SJason M. Bills { 119*3f7c5e40SJason M. Bills continue; 120*3f7c5e40SJason M. Bills } 121*3f7c5e40SJason M. Bills 122*3f7c5e40SJason M. Bills auto busFind = fruIface->second.find("BUS"); 123*3f7c5e40SJason M. Bills auto addrFind = fruIface->second.find("ADDRESS"); 124*3f7c5e40SJason M. Bills if (busFind == fruIface->second.end() || 125*3f7c5e40SJason M. Bills addrFind == fruIface->second.end()) 126*3f7c5e40SJason M. Bills { 127*3f7c5e40SJason M. Bills phosphor::logging::log<phosphor::logging::level::INFO>( 128*3f7c5e40SJason M. Bills "fru device missing Bus or Address", 129*3f7c5e40SJason M. Bills phosphor::logging::entry("FRU=%s", fru.first.str.c_str())); 130*3f7c5e40SJason M. Bills continue; 131*3f7c5e40SJason M. Bills } 132*3f7c5e40SJason M. Bills 133*3f7c5e40SJason M. Bills uint8_t fruBus = 134*3f7c5e40SJason M. Bills sdbusplus::message::variant_ns::get<uint32_t>(busFind->second); 135*3f7c5e40SJason M. Bills uint8_t fruAddr = 136*3f7c5e40SJason M. Bills sdbusplus::message::variant_ns::get<uint32_t>(addrFind->second); 137*3f7c5e40SJason M. Bills 138*3f7c5e40SJason M. Bills uint8_t fruHash = 0; 139*3f7c5e40SJason M. Bills if (fruBus != 0 || fruAddr != 0) 140*3f7c5e40SJason M. Bills { 141*3f7c5e40SJason M. Bills fruHash = hasher(fru.first.str); 142*3f7c5e40SJason M. Bills // can't be 0xFF based on spec, and 0 is reserved for baseboard 143*3f7c5e40SJason M. Bills if (fruHash == 0 || fruHash == 0xFF) 144*3f7c5e40SJason M. Bills { 145*3f7c5e40SJason M. Bills fruHash = 1; 146*3f7c5e40SJason M. Bills } 147*3f7c5e40SJason M. Bills } 148*3f7c5e40SJason M. Bills std::pair<uint8_t, uint8_t> newDev(fruBus, fruAddr); 149*3f7c5e40SJason M. Bills 150*3f7c5e40SJason M. Bills bool emplacePassed = false; 151*3f7c5e40SJason M. Bills while (!emplacePassed) 152*3f7c5e40SJason M. Bills { 153*3f7c5e40SJason M. Bills auto resp = deviceHashes.emplace(fruHash, newDev); 154*3f7c5e40SJason M. Bills emplacePassed = resp.second; 155*3f7c5e40SJason M. Bills if (!emplacePassed) 156*3f7c5e40SJason M. Bills { 157*3f7c5e40SJason M. Bills fruHash++; 158*3f7c5e40SJason M. Bills // can't be 0xFF based on spec, and 0 is reserved for 159*3f7c5e40SJason M. Bills // baseboard 160*3f7c5e40SJason M. Bills if (fruHash == 0XFF) 161*3f7c5e40SJason M. Bills { 162*3f7c5e40SJason M. Bills fruHash = 0x1; 163*3f7c5e40SJason M. Bills } 164*3f7c5e40SJason M. Bills } 165*3f7c5e40SJason M. Bills } 166*3f7c5e40SJason M. Bills } 167*3f7c5e40SJason M. Bills auto deviceFind = deviceHashes.find(devId); 168*3f7c5e40SJason M. Bills if (deviceFind == deviceHashes.end()) 169*3f7c5e40SJason M. Bills { 170*3f7c5e40SJason M. Bills return IPMI_CC_SENSOR_INVALID; 171*3f7c5e40SJason M. Bills } 172*3f7c5e40SJason M. Bills 173*3f7c5e40SJason M. Bills fruCache.clear(); 174*3f7c5e40SJason M. Bills sdbusplus::message::message getRawFru = dbus.new_method_call( 175*3f7c5e40SJason M. Bills fruDeviceServiceName, "/xyz/openbmc_project/FruDevice", 176*3f7c5e40SJason M. Bills "xyz.openbmc_project.FruDeviceManager", "GetRawFru"); 177*3f7c5e40SJason M. Bills cacheBus = deviceFind->second.first; 178*3f7c5e40SJason M. Bills cacheAddr = deviceFind->second.second; 179*3f7c5e40SJason M. Bills getRawFru.append(cacheBus, cacheAddr); 180*3f7c5e40SJason M. Bills try 181*3f7c5e40SJason M. Bills { 182*3f7c5e40SJason M. Bills sdbusplus::message::message getRawResp = dbus.call(getRawFru); 183*3f7c5e40SJason M. Bills getRawResp.read(fruCache); 184*3f7c5e40SJason M. Bills } 185*3f7c5e40SJason M. Bills catch (sdbusplus::exception_t&) 186*3f7c5e40SJason M. Bills { 187*3f7c5e40SJason M. Bills lastDevId = 0xFF; 188*3f7c5e40SJason M. Bills cacheBus = 0xFF; 189*3f7c5e40SJason M. Bills cacheAddr = 0xFF; 190*3f7c5e40SJason M. Bills return IPMI_CC_RESPONSE_ERROR; 191*3f7c5e40SJason M. Bills } 192*3f7c5e40SJason M. Bills 193*3f7c5e40SJason M. Bills lastDevId = devId; 194*3f7c5e40SJason M. Bills return IPMI_CC_OK; 195*3f7c5e40SJason M. Bills } 196*3f7c5e40SJason M. Bills 197*3f7c5e40SJason M. Bills ipmi_ret_t getFruSdrCount(size_t& count) 198*3f7c5e40SJason M. Bills { 199*3f7c5e40SJason M. Bills ipmi_ret_t ret = replaceCacheFru(0); 200*3f7c5e40SJason M. Bills if (ret != IPMI_CC_OK) 201*3f7c5e40SJason M. Bills { 202*3f7c5e40SJason M. Bills return ret; 203*3f7c5e40SJason M. Bills } 204*3f7c5e40SJason M. Bills count = deviceHashes.size(); 205*3f7c5e40SJason M. Bills return IPMI_CC_OK; 206*3f7c5e40SJason M. Bills } 207*3f7c5e40SJason M. Bills 208*3f7c5e40SJason M. Bills ipmi_ret_t getFruSdrs(size_t index, get_sdr::SensorDataFruRecord& resp) 209*3f7c5e40SJason M. Bills { 210*3f7c5e40SJason M. Bills ipmi_ret_t ret = replaceCacheFru(0); // this will update the hash list 211*3f7c5e40SJason M. Bills if (ret != IPMI_CC_OK) 212*3f7c5e40SJason M. Bills { 213*3f7c5e40SJason M. Bills return ret; 214*3f7c5e40SJason M. Bills } 215*3f7c5e40SJason M. Bills if (deviceHashes.size() < index) 216*3f7c5e40SJason M. Bills { 217*3f7c5e40SJason M. Bills return IPMI_CC_INVALID_FIELD_REQUEST; 218*3f7c5e40SJason M. Bills } 219*3f7c5e40SJason M. Bills auto device = deviceHashes.begin() + index; 220*3f7c5e40SJason M. Bills uint8_t& bus = device->second.first; 221*3f7c5e40SJason M. Bills uint8_t& address = device->second.second; 222*3f7c5e40SJason M. Bills 223*3f7c5e40SJason M. Bills ManagedObjectType frus; 224*3f7c5e40SJason M. Bills 225*3f7c5e40SJason M. Bills sdbusplus::message::message getObjects = dbus.new_method_call( 226*3f7c5e40SJason M. Bills fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager", 227*3f7c5e40SJason M. Bills "GetManagedObjects"); 228*3f7c5e40SJason M. Bills try 229*3f7c5e40SJason M. Bills { 230*3f7c5e40SJason M. Bills sdbusplus::message::message resp = dbus.call(getObjects); 231*3f7c5e40SJason M. Bills resp.read(frus); 232*3f7c5e40SJason M. Bills } 233*3f7c5e40SJason M. Bills catch (sdbusplus::exception_t&) 234*3f7c5e40SJason M. Bills { 235*3f7c5e40SJason M. Bills return IPMI_CC_RESPONSE_ERROR; 236*3f7c5e40SJason M. Bills } 237*3f7c5e40SJason M. Bills boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr; 238*3f7c5e40SJason M. Bills auto fru = 239*3f7c5e40SJason M. Bills std::find_if(frus.begin(), frus.end(), 240*3f7c5e40SJason M. Bills [bus, address, &fruData](ManagedEntry& entry) { 241*3f7c5e40SJason M. Bills auto findFruDevice = 242*3f7c5e40SJason M. Bills entry.second.find("xyz.openbmc_project.FruDevice"); 243*3f7c5e40SJason M. Bills if (findFruDevice == entry.second.end()) 244*3f7c5e40SJason M. Bills { 245*3f7c5e40SJason M. Bills return false; 246*3f7c5e40SJason M. Bills } 247*3f7c5e40SJason M. Bills fruData = &(findFruDevice->second); 248*3f7c5e40SJason M. Bills auto findBus = findFruDevice->second.find("BUS"); 249*3f7c5e40SJason M. Bills auto findAddress = 250*3f7c5e40SJason M. Bills findFruDevice->second.find("ADDRESS"); 251*3f7c5e40SJason M. Bills if (findBus == findFruDevice->second.end() || 252*3f7c5e40SJason M. Bills findAddress == findFruDevice->second.end()) 253*3f7c5e40SJason M. Bills { 254*3f7c5e40SJason M. Bills return false; 255*3f7c5e40SJason M. Bills } 256*3f7c5e40SJason M. Bills if (sdbusplus::message::variant_ns::get<uint32_t>( 257*3f7c5e40SJason M. Bills findBus->second) != bus) 258*3f7c5e40SJason M. Bills { 259*3f7c5e40SJason M. Bills return false; 260*3f7c5e40SJason M. Bills } 261*3f7c5e40SJason M. Bills if (sdbusplus::message::variant_ns::get<uint32_t>( 262*3f7c5e40SJason M. Bills findAddress->second) != address) 263*3f7c5e40SJason M. Bills { 264*3f7c5e40SJason M. Bills return false; 265*3f7c5e40SJason M. Bills } 266*3f7c5e40SJason M. Bills return true; 267*3f7c5e40SJason M. Bills }); 268*3f7c5e40SJason M. Bills if (fru == frus.end()) 269*3f7c5e40SJason M. Bills { 270*3f7c5e40SJason M. Bills return IPMI_CC_RESPONSE_ERROR; 271*3f7c5e40SJason M. Bills } 272*3f7c5e40SJason M. Bills std::string name; 273*3f7c5e40SJason M. Bills auto findProductName = fruData->find("BOARD_PRODUCT_NAME"); 274*3f7c5e40SJason M. Bills auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME"); 275*3f7c5e40SJason M. Bills if (findProductName != fruData->end()) 276*3f7c5e40SJason M. Bills { 277*3f7c5e40SJason M. Bills name = sdbusplus::message::variant_ns::get<std::string>( 278*3f7c5e40SJason M. Bills findProductName->second); 279*3f7c5e40SJason M. Bills } 280*3f7c5e40SJason M. Bills else if (findBoardName != fruData->end()) 281*3f7c5e40SJason M. Bills { 282*3f7c5e40SJason M. Bills name = sdbusplus::message::variant_ns::get<std::string>( 283*3f7c5e40SJason M. Bills findBoardName->second); 284*3f7c5e40SJason M. Bills } 285*3f7c5e40SJason M. Bills else 286*3f7c5e40SJason M. Bills { 287*3f7c5e40SJason M. Bills name = "UNKNOWN"; 288*3f7c5e40SJason M. Bills } 289*3f7c5e40SJason M. Bills if (name.size() > maxFruSdrNameSize) 290*3f7c5e40SJason M. Bills { 291*3f7c5e40SJason M. Bills name = name.substr(0, maxFruSdrNameSize); 292*3f7c5e40SJason M. Bills } 293*3f7c5e40SJason M. Bills size_t sizeDiff = maxFruSdrNameSize - name.size(); 294*3f7c5e40SJason M. Bills 295*3f7c5e40SJason M. Bills resp.header.record_id_lsb = 0x0; // calling code is to implement these 296*3f7c5e40SJason M. Bills resp.header.record_id_msb = 0x0; 297*3f7c5e40SJason M. Bills resp.header.sdr_version = ipmiSdrVersion; 298*3f7c5e40SJason M. Bills resp.header.record_type = 0x11; // FRU Device Locator 299*3f7c5e40SJason M. Bills resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff; 300*3f7c5e40SJason M. Bills resp.key.deviceAddress = 0x20; 301*3f7c5e40SJason M. Bills resp.key.fruID = device->first; 302*3f7c5e40SJason M. Bills resp.key.accessLun = 0x80; // logical / physical fru device 303*3f7c5e40SJason M. Bills resp.key.channelNumber = 0x0; 304*3f7c5e40SJason M. Bills resp.body.reserved = 0x0; 305*3f7c5e40SJason M. Bills resp.body.deviceType = 0x10; 306*3f7c5e40SJason M. Bills resp.body.entityID = 0x0; 307*3f7c5e40SJason M. Bills resp.body.entityInstance = 0x1; 308*3f7c5e40SJason M. Bills resp.body.oem = 0x0; 309*3f7c5e40SJason M. Bills resp.body.deviceIDLen = name.size(); 310*3f7c5e40SJason M. Bills name.copy(resp.body.deviceID, name.size()); 311*3f7c5e40SJason M. Bills 312*3f7c5e40SJason M. Bills return IPMI_CC_OK; 313*3f7c5e40SJason M. Bills } 314*3f7c5e40SJason M. Bills } // namespace storage 315*3f7c5e40SJason M. Bills } // namespace ipmi