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 <host-ipmid/ipmid-api.h> 18 19 #include <array> 20 #include <iostream> 21 #include <oemcommands.hpp> 22 #include <phosphor-ipmi-host/utils.hpp> 23 #include <phosphor-logging/log.hpp> 24 #include <sdbusplus/bus.hpp> 25 #include <sstream> 26 #include <string> 27 #include <vector> 28 #include <xyz/openbmc_project/Common/error.hpp> 29 30 namespace ipmi 31 { 32 static void register_netfn_firmware_functions() __attribute__((constructor)); 33 sdbusplus::bus::bus _dbus(ipmid_get_sd_bus_connection()); // from ipmid-api.h 34 static constexpr size_t maxFruStringLength = 0x3F; 35 36 // return code: 0 successful 37 int8_t getChassisSerialNumber(sdbusplus::bus::bus& bus, std::string& serial) 38 { 39 std::string objpath = "/xyz/openbmc_project/FruDevice"; 40 std::string intf = "xyz.openbmc_project.FruDeviceManager"; 41 std::string service = getService(bus, intf, objpath); 42 ObjectValueTree valueTree = getManagedObjects(bus, service, "/"); 43 if (valueTree.empty()) 44 { 45 phosphor::logging::log<phosphor::logging::level::ERR>( 46 "No object implements interface", 47 phosphor::logging::entry("INTF=%s", intf.c_str())); 48 return -1; 49 } 50 51 for (auto& item : valueTree) 52 { 53 auto interface = item.second.find("xyz.openbmc_project.FruDevice"); 54 if (interface == item.second.end()) 55 { 56 continue; 57 } 58 59 auto property = interface->second.find("CHASSIS_SERIAL_NUMBER"); 60 if (property == interface->second.end()) 61 { 62 continue; 63 } 64 65 try 66 { 67 Value variant = property->second; 68 std::string& result = variant.get<std::string>(); 69 if (result.size() > maxFruStringLength) 70 { 71 phosphor::logging::log<phosphor::logging::level::ERR>( 72 "FRU serial number exceed maximum length"); 73 74 return -1; 75 } 76 else 77 { 78 serial = result; 79 } 80 return 0; 81 } 82 catch (mapbox::util::bad_variant_access& e) 83 { 84 std::cerr << e.what() << std::endl; 85 return -1; 86 } 87 } 88 return -1; 89 } 90 ipmi_ret_t ipmiOEMWildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 91 ipmi_request_t request, ipmi_response_t response, 92 ipmi_data_len_t data_len, ipmi_context_t context) 93 { 94 phosphor::logging::log<phosphor::logging::level::INFO>( 95 "Handling OEM WILDCARD", phosphor::logging::entry("NETFN=%x", netfn), 96 phosphor::logging::entry("CMD=%x", cmd)); 97 98 // Status code. 99 ipmi_ret_t rc = IPMI_CC_INVALID; 100 *data_len = 0; 101 return rc; 102 } 103 104 // Returns the Chassis Identifier (serial #) 105 ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 106 ipmi_request_t request, 107 ipmi_response_t response, 108 ipmi_data_len_t data_len, 109 ipmi_context_t context) 110 { 111 std::string serial; 112 if (*data_len != 0) // invalid request if there are extra parameters 113 { 114 return IPMI_CC_REQ_DATA_LEN_INVALID; 115 } 116 if (getChassisSerialNumber(_dbus, serial) == 0) 117 { 118 *data_len = serial.size(); // length will never exceed response length 119 // as it is checked in getChassisSerialNumber 120 char* resp = static_cast<char*>(response); 121 serial.copy(resp, *data_len); 122 return IPMI_CC_OK; 123 } 124 else 125 { 126 *data_len = 0; 127 return IPMI_CC_RESPONSE_ERROR; 128 } 129 } 130 131 ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 132 ipmi_request_t request, 133 ipmi_response_t response, 134 ipmi_data_len_t data_len, 135 ipmi_context_t context) 136 { 137 static constexpr size_t safeBufferLength = 50; 138 char buf[safeBufferLength] = {0}; 139 GUIDData* Data = reinterpret_cast<GUIDData*>(request); 140 141 if (*data_len != sizeof(GUIDData)) // 16bytes 142 { 143 *data_len = 0; 144 return IPMI_CC_REQ_DATA_LEN_INVALID; 145 } 146 147 *data_len = 0; 148 149 snprintf( 150 buf, safeBufferLength, 151 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", 152 Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1, 153 Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1, 154 Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4, 155 Data->node3, Data->node2, Data->node1); 156 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 157 std::string guid = buf; 158 phosphor::logging::log<phosphor::logging::level::INFO>( 159 "Set System GUID", phosphor::logging::entry("GUID=%s", guid.c_str())); 160 161 std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID"; 162 std::string intf = "xyz.openbmc_project.Common.UUID"; 163 std::string service = getService(_dbus, intf, objpath); 164 setDbusProperty(_dbus, service, objpath, intf, "UUID", guid); 165 return IPMI_CC_OK; 166 } 167 168 ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 169 ipmi_request_t request, ipmi_response_t response, 170 ipmi_data_len_t dataLen, ipmi_context_t context) 171 { 172 DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request); 173 174 if ((*dataLen < 2) || (*dataLen != (1 + data->biosIdLength))) 175 { 176 phosphor::logging::log<phosphor::logging::level::ERR>( 177 "Invalid parameter", phosphor::logging::entry("LEN=%d", *dataLen), 178 phosphor::logging::entry("BIOSIDLEN=%d", data->biosIdLength)); 179 180 *dataLen = 0; 181 return IPMI_CC_REQ_DATA_LEN_INVALID; 182 } 183 std::string idString((char*)data->biosId, data->biosIdLength); 184 185 std::string service = getService(_dbus, biosIntf, biosObjPath); 186 setDbusProperty(_dbus, service, biosObjPath, biosIntf, biosProp, idString); 187 uint8_t* bytesWritten = static_cast<uint8_t*>(response); 188 *bytesWritten = 189 data->biosIdLength; // how many bytes are written into storage 190 *dataLen = 1; 191 return IPMI_CC_OK; 192 } 193 194 ipmi_ret_t ipmiOEMGetDeviceInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 195 ipmi_request_t request, 196 ipmi_response_t response, 197 ipmi_data_len_t dataLen, ipmi_context_t context) 198 { 199 GetOemDeviceInfoReq* req = reinterpret_cast<GetOemDeviceInfoReq*>(request); 200 GetOemDeviceInfoRes* res = reinterpret_cast<GetOemDeviceInfoRes*>(response); 201 202 if (*dataLen == 0) 203 { 204 phosphor::logging::log<phosphor::logging::level::ERR>( 205 "Paramter length should be at least one byte"); 206 return IPMI_CC_REQ_DATA_LEN_INVALID; 207 } 208 209 size_t reqDataLen = *dataLen; 210 *dataLen = 0; 211 if (req->entityType > OEMDevEntityType::sdrVer) 212 { 213 phosphor::logging::log<phosphor::logging::level::ERR>( 214 "Out of range", 215 phosphor::logging::entry("TYPE=%x", req->entityType)); 216 217 return IPMI_CC_INVALID_FIELD_REQUEST; 218 } 219 220 // handle OEM command items 221 switch (req->entityType) 222 { 223 case OEMDevEntityType::biosId: 224 { 225 if (sizeof(GetOemDeviceInfoReq) != reqDataLen) 226 { 227 phosphor::logging::log<phosphor::logging::level::ERR>( 228 "Data length does not match request", 229 phosphor::logging::entry("REQLEN=%d", reqDataLen), 230 phosphor::logging::entry("LEN=%d", 231 sizeof(GetOemDeviceInfoReq))); 232 return IPMI_CC_REQ_DATA_LEN_INVALID; 233 } 234 235 std::string service = getService(_dbus, biosIntf, biosObjPath); 236 try 237 { 238 Value variant = getDbusProperty(_dbus, service, biosObjPath, 239 biosIntf, biosProp); 240 std::string& idString = variant.get<std::string>(); 241 if (req->offset >= idString.size()) 242 { 243 phosphor::logging::log<phosphor::logging::level::ERR>( 244 "offset exceed range", 245 phosphor::logging::entry("OFFSET=%d", req->offset), 246 phosphor::logging::entry("IDLEN=%d", idString.size())); 247 return IPMI_CC_PARM_OUT_OF_RANGE; 248 } 249 else 250 { 251 size_t length = 0; 252 if (req->countToRead > (idString.size() - req->offset)) 253 { 254 length = idString.size() - req->offset; 255 } 256 else 257 { 258 length = req->countToRead; 259 } 260 std::copy(idString.begin() + req->offset, idString.end(), 261 res->data); 262 res->resDatalen = length; 263 *dataLen = res->resDatalen + 1; 264 } 265 } 266 catch (mapbox::util::bad_variant_access& e) 267 { 268 std::cerr << e.what() << std::endl; 269 return IPMI_CC_UNSPECIFIED_ERROR; 270 } 271 } 272 break; 273 274 case OEMDevEntityType::devVer: 275 case OEMDevEntityType::sdrVer: 276 // TODO: 277 return IPMI_CC_ILLEGAL_COMMAND; 278 default: 279 return IPMI_CC_INVALID_FIELD_REQUEST; 280 } 281 return IPMI_CC_OK; 282 } 283 284 ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 285 ipmi_request_t request, ipmi_response_t response, 286 ipmi_data_len_t dataLen, ipmi_context_t context) 287 { 288 if (*dataLen != 0) 289 { 290 phosphor::logging::log<phosphor::logging::level::ERR>( 291 "This command should not have any paramter"); 292 return IPMI_CC_REQ_DATA_LEN_INVALID; 293 } 294 295 *dataLen = 1; 296 uint8_t* res = reinterpret_cast<uint8_t*>(response); 297 // temporary fix. We don't support AIC FRU now. Just tell BIOS that no 298 // AIC is available so that BIOS will not timeout repeatly which leads to 299 // slow booting. 300 *res = 0; // Byte1=Count of SlotPosition/FruID records. 301 return IPMI_CC_OK; 302 } 303 304 void ipmi_register(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_context_t context, 305 ipmid_callback_t handler, ipmi_cmd_privilege_t priv) 306 { 307 std::ostringstream os; 308 os << "Registering NetFn:[0x" << std::hex << std::uppercase << netfn 309 << "], Cmd:[0x" << cmd << "]\n"; 310 phosphor::logging::log<phosphor::logging::level::INFO>(os.str().c_str()); 311 ipmi_register_callback(netfn, cmd, context, handler, priv); 312 } 313 314 static void register_netfn_firmware_functions(void) 315 { 316 phosphor::logging::log<phosphor::logging::level::INFO>( 317 "Registering OEM commands"); 318 ipmi_register(netfunIntcOEMGeneral, IPMI_CMD_WILDCARD, NULL, 319 ipmiOEMWildcard, 320 PRIVILEGE_USER); // wildcard default handler 321 ipmi_register(netfunIntcOEMGeneral, cmdGetChassisIdentifier, NULL, 322 ipmiOEMGetChassisIdentifier, 323 PRIVILEGE_USER); // get chassis identifier 324 ipmi_register(netfunIntcOEMGeneral, cmdSetSystemGUID, NULL, 325 ipmiOEMSetSystemGUID, 326 PRIVILEGE_ADMIN); // set system guid 327 ipmi_register(netfunIntcOEMGeneral, cmdSetBIOSID, NULL, ipmiOEMSetBIOSID, 328 PRIVILEGE_ADMIN); 329 ipmi_register(netfunIntcOEMGeneral, cmdGetOEMDeviceInfo, NULL, 330 ipmiOEMGetDeviceInfo, PRIVILEGE_USER); 331 ipmi_register(netfunIntcOEMGeneral, cmdGetAICSlotFRUIDRecords, NULL, 332 ipmiOEMGetAICFRU, PRIVILEGE_USER); 333 return; 334 } 335 336 } // namespace ipmi 337