1 #include "guid.hpp" 2 3 #include <ipmid/api.h> 4 #include <mapper.h> 5 6 #include <ipmid/utils.hpp> 7 #include <phosphor-logging/elog-errors.hpp> 8 #include <phosphor-logging/lg2.hpp> 9 #include <xyz/openbmc_project/Common/error.hpp> 10 11 #include <sstream> 12 #include <string> 13 14 using namespace phosphor::logging; 15 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 16 17 static std::optional<command::Guid> guid; 18 19 namespace command 20 { 21 22 std::unique_ptr<sdbusplus::bus::match_t> matchPtr(nullptr); 23 24 static constexpr auto propInterface = "xyz.openbmc_project.Common.UUID"; 25 static constexpr auto uuidProperty = "UUID"; 26 static constexpr auto subtreePath = "/xyz/openbmc_project/inventory"; 27 28 static void rfcToGuid(std::string rfc4122, Guid& uuid) 29 { 30 using Argument = xyz::openbmc_project::Common::InvalidArgument; 31 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 32 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte 33 // order 34 // Ex: 0x2332fc2c40e66298e511f2782395a361 35 constexpr size_t uuidHexLength = (2 * BMC_GUID_LEN); 36 constexpr size_t uuidRfc4122Length = (uuidHexLength + 4); 37 38 if (rfc4122.size() == uuidRfc4122Length) 39 { 40 rfc4122.erase(std::remove(rfc4122.begin(), rfc4122.end(), '-'), 41 rfc4122.end()); 42 } 43 if (rfc4122.size() != uuidHexLength) 44 { 45 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"), 46 Argument::ARGUMENT_VALUE(rfc4122.c_str())); 47 } 48 for (size_t ind = 0; ind < uuidHexLength; ind += 2) 49 { 50 long b; 51 try 52 { 53 b = std::stoul(rfc4122.substr(ind, 2), nullptr, 16); 54 } 55 catch (const std::exception& e) 56 { 57 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"), 58 Argument::ARGUMENT_VALUE(rfc4122.c_str())); 59 } 60 61 uuid[BMC_GUID_LEN - (ind / 2) - 1] = static_cast<uint8_t>(b); 62 } 63 return; 64 } 65 66 // Canned System GUID for when the Chassis DBUS object is not populated 67 static constexpr Guid fakeGuid = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 68 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 69 0x0D, 0x0E, 0x0F, 0x10}; 70 const Guid& getSystemGUID() 71 { 72 if (guid.has_value()) 73 { 74 return guid.value(); 75 } 76 77 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 78 79 ipmi::Value propValue; 80 try 81 { 82 const auto& [objPath, service] = ipmi::getDbusObject(bus, propInterface, 83 subtreePath); 84 // Read UUID property value from bmcObject 85 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 86 propValue = ipmi::getDbusProperty(bus, service, objPath, propInterface, 87 uuidProperty); 88 } 89 catch (const sdbusplus::exception_t& e) 90 { 91 lg2::error("Failed in reading BMC UUID property: {ERROR}", "ERROR", e); 92 return fakeGuid; 93 } 94 95 std::string rfc4122Uuid = std::get<std::string>(propValue); 96 try 97 { 98 // convert to IPMI format 99 Guid tmpGuid{}; 100 rfcToGuid(rfc4122Uuid, tmpGuid); 101 guid = tmpGuid; 102 } 103 catch (const InvalidArgument& e) 104 { 105 lg2::error("Failed in parsing BMC UUID property: {VALUE}", "VALUE", 106 rfc4122Uuid.c_str()); 107 return fakeGuid; 108 } 109 return guid.value(); 110 } 111 112 void registerGUIDChangeCallback() 113 { 114 if (matchPtr == nullptr) 115 { 116 using namespace sdbusplus::bus::match::rules; 117 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 118 119 try 120 { 121 matchPtr = std::make_unique<sdbusplus::bus::match_t>( 122 bus, propertiesChangedNamespace(subtreePath, propInterface), 123 [](sdbusplus::message_t& m) { 124 try 125 { 126 std::string iface{}; 127 std::map<std::string, ipmi::Value> pdict{}; 128 m.read(iface, pdict); 129 if (iface != propInterface) 130 { 131 return; 132 } 133 auto guidStr = std::get<std::string>(pdict.at("UUID")); 134 Guid tmpGuid{}; 135 rfcToGuid(guidStr, tmpGuid); 136 guid = tmpGuid; 137 } 138 catch (const std::exception& e) 139 { 140 // signal contained invalid guid; ignore it 141 lg2::error( 142 "Failed to parse propertiesChanged signal: {ERROR}", 143 "ERROR", e); 144 } 145 }); 146 } 147 catch (const std::exception& e) 148 { 149 lg2::error("Failed to create dbus match: {ERROR}", "ERROR", e); 150 } 151 } 152 } 153 154 } // namespace command 155