1 /* 2 * Copyright (c) 2018 Intel Corporation. 3 * Copyright (c) 2018-present Facebook. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 #include <ipmid/api.hpp> 19 #include <ipmid/api-types.hpp> 20 21 #include <commandutils.hpp> 22 #include <biccommands.hpp> 23 #include <phosphor-logging/log.hpp> 24 25 #include <vector> 26 #include <variant> 27 #include <iostream> 28 29 namespace ipmi 30 { 31 32 int sendBicCmd(uint8_t, uint8_t, uint8_t, std::vector<uint8_t>&, 33 std::vector<uint8_t>&); 34 35 using namespace phosphor::logging; 36 37 #ifdef BIC_ENABLED 38 static void registerBICFunctions() __attribute__((constructor)); 39 #endif 40 41 extern message::Response::ptr executeIpmiCommand(message::Request::ptr); 42 43 int sendBicCmd(uint8_t, uint8_t, uint8_t, std::vector<uint8_t>&, 44 std::vector<uint8_t>&); 45 46 //---------------------------------------------------------------------- 47 // ipmiOemBicHandler (IPMI/Section - ) (CMD_OEM_BIC_INFO) 48 // This Function will handle BIC request for netfn=0x38 and cmd=1 49 // send the response back to the sender. 50 //---------------------------------------------------------------------- 51 52 ipmi::RspType<IanaType, uint8_t, uint2_t, uint6_t, uint8_t, uint8_t, 53 ipmi::message::Payload> 54 ipmiOemBicHandler(ipmi::Context::ptr ctx, IanaType reqIana, 55 uint8_t interface, uint2_t lun, uint6_t netFnReq, 56 uint8_t cmdReq, SecureBuffer data) 57 { 58 59 ipmi::message::Response::ptr res; 60 61 // Updating the correct netfn and cmd in the ipmi Context 62 ctx->netFn = ((uint8_t)netFnReq); 63 ctx->cmd = cmdReq; 64 65 // creating ipmi message request for calling executeIpmiCommand function 66 auto req = std::make_shared<ipmi::message::Request>(ctx, std::move(data)); 67 68 // Calling executeIpmiCommand request function 69 res = ipmi::executeIpmiCommand(req); 70 71 // sending the response with headers and payload 72 return ipmi::responseSuccess(reqIana, interface, lun, ++netFnReq, cmdReq, 73 res->cc, res->payload); 74 } 75 76 //---------------------------------------------------------------------- 77 // ipmiOemPostCodeHandler (CMD_OEM_BIC_POST_BUFFER_INFO) 78 // This Function will handle BIC incomming postcode from multi-host for 79 // netfn=0x38 and cmd=0x08 send the response back to the sender. 80 //---------------------------------------------------------------------- 81 82 ipmi::RspType<IanaType> ipmiOemPostCodeHandler(ipmi::Context::ptr ctx, 83 IanaType reqIana, 84 uint8_t dataLen, 85 std::vector<uint8_t> data) 86 { 87 // creating bus connection 88 auto conn = getSdBus(); 89 90 using postcode_t = std::tuple<uint64_t, std::vector<uint8_t>>; 91 92 std::string dbusObjStr = dbusObj + std::to_string((ctx->hostIdx + 1)); 93 94 for (unsigned int index = 0; index < dataLen; index++) 95 { 96 uint64_t primaryPostCode = static_cast<uint64_t>(data[index]); 97 auto postCode = postcode_t(primaryPostCode, {}); 98 99 try 100 { 101 auto method = conn->new_method_call( 102 "xyz.openbmc_project.State.Boot.Raw", dbusObjStr.c_str(), 103 "org.freedesktop.DBus.Properties", "Set"); 104 105 // Adding paramters to method call 106 method.append(dbusService, "Value", 107 std::variant<postcode_t>(postCode)); 108 109 // Invoke method call function 110 auto reply = conn->call(method); 111 } 112 113 catch (std::exception& e) 114 { 115 phosphor::logging::log<phosphor::logging::level::ERR>( 116 "post code handler error\n"); 117 118 // sending the Error response 119 return ipmi::responseResponseError(); 120 } 121 } 122 123 return ipmi::responseSuccess(reqIana); 124 } 125 126 //---------------------------------------------------------------------- 127 // ipmiOemGetBicGpioState (CMD_OEM_GET_BIC_GPIO_STATE) 128 // This Function will handle BIC GPIO stats for 129 // netfn=0x38 and cmd=0x03 send the response back to the sender. 130 //---------------------------------------------------------------------- 131 132 ipmi::RspType<IanaType, std::vector<uint8_t>> 133 ipmiOemGetBicGpioState(ipmi::Context::ptr ctx, std::vector<uint8_t> reqIana) 134 { 135 std::vector<uint8_t> respData; 136 137 if (std::equal(reqIana.begin(), reqIana.end(), iana.begin()) == false) 138 { 139 phosphor::logging::log<phosphor::logging::level::ERR>( 140 "Invalid IANA number"); 141 return ipmi::responseInvalidFieldRequest(); 142 } 143 144 uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2; 145 146 if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqIana, respData)) 147 { 148 return ipmi::responseUnspecifiedError(); 149 } 150 151 std::vector<uint8_t> gpioState; 152 IanaType respIana; 153 154 auto r = 155 std::ranges::copy_n(respData.begin(), iana.size(), respIana.begin()).in; 156 std::copy(r, respData.end(), std::back_inserter(gpioState)); 157 158 return ipmi::responseSuccess(respIana, gpioState); 159 } 160 161 //---------------------------------------------------------------------- 162 // ipmiOemSetHostPowerState (CMD_OEM_SET_HOST_POWER_STATE) 163 // This Function will handle BIC incomming IPMI request for 164 // setting host current state for netfn=0x38 and cmd=0x0C 165 // send the response back to the sender. 166 //---------------------------------------------------------------------- 167 168 ipmi::RspType<IanaType> ipmiOemSetHostPowerState(ipmi::Context::ptr ctx, 169 IanaType reqIana, 170 uint8_t status) 171 { 172 std::string targetUnit; 173 174 switch (static_cast<HostPowerState>(status)) 175 { 176 case HostPowerState::HOST_POWER_ON: 177 targetUnit = "obmc-host-startmin@.target"; 178 break; 179 case HostPowerState::HOST_POWER_OFF: 180 targetUnit = "obmc-host-stop@.target"; 181 break; 182 default: 183 phosphor::logging::log<phosphor::logging::level::ERR>( 184 "IPMI ipmiOemHostPowerStatus power status error"); 185 return ipmi::responseUnspecifiedError(); 186 } 187 188 int mousePos = targetUnit.find('@'); 189 targetUnit.insert(mousePos + 1, std::to_string(ctx->hostIdx + 1)); 190 191 auto conn = getSdBus(); 192 auto method = conn->new_method_call(systemdService, systemdObjPath, 193 systemdInterface, "StartUnit"); 194 method.append(targetUnit); 195 method.append("replace"); 196 197 try 198 { 199 conn->call_noreply(method); 200 } 201 catch (const sdbusplus::exception::SdBusError& e) 202 { 203 phosphor::logging::log<phosphor::logging::level::ERR>( 204 "IPMI ipmiOemHostPowerStatus Failed in call method", 205 phosphor::logging::entry("ERROR=%s", e.what())); 206 return ipmi::responseUnspecifiedError(); 207 } 208 209 return ipmi::responseSuccess(reqIana); 210 } 211 212 //---------------------------------------------------------------------- 213 // ipmiOemGetBiosFlashSize (CMD_OEM_GET_FLASH_SIZE) 214 // This Function will return the bios flash size 215 // netfn=0x38 and cmd=0x19 send the response back to the sender. 216 //---------------------------------------------------------------------- 217 218 ipmi::RspType<IanaType, flashSize> 219 ipmiOemGetBiosFlashSize(ipmi::Context::ptr ctx, IanaType ianaReq, 220 uint8_t target) 221 { 222 if (iana != ianaReq) 223 { 224 phosphor::logging::log<phosphor::logging::level::ERR>( 225 "Invalid IANA ID length received"); 226 return ipmi::responseReqDataLenInvalid(); 227 } 228 229 std::vector<uint8_t> respData; 230 uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2; 231 std::vector<uint8_t> reqData(ianaReq.begin(), ianaReq.end()); 232 reqData.emplace_back(target); 233 234 if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqData, respData)) 235 { 236 return ipmi::responseUnspecifiedError(); 237 } 238 239 if (respData.size() != flashSizeRespLen) 240 { 241 phosphor::logging::log<phosphor::logging::level::ERR>( 242 "Invalid Response Data length received"); 243 return ipmi::responseReqDataLenInvalid(); 244 } 245 246 IanaType ianaResp; 247 std::copy_n(respData.begin(), ianaResp.size(), ianaResp.begin()); 248 249 if (iana != ianaResp) 250 { 251 phosphor::logging::log<phosphor::logging::level::ERR>( 252 "Invalid IANA ID received"); 253 return ipmi::responseInvalidCommand(); 254 } 255 256 flashSize flashResp; 257 std::vector<uint8_t>::iterator respDataIter = respData.begin(); 258 std::advance(respDataIter, ianaResp.size()); 259 std::copy_n(respDataIter, flashResp.size(), flashResp.begin()); 260 261 // sending the success response. 262 return ipmi::responseSuccess(ianaResp, flashResp); 263 } 264 265 [[maybe_unused]] static void registerBICFunctions(void) 266 { 267 268 phosphor::logging::log<phosphor::logging::level::INFO>( 269 "Registering BIC commands"); 270 271 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFive, 272 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_BIC_INFO), 273 ipmi::Privilege::User, ipmiOemBicHandler); 274 ipmi::registerHandler( 275 ipmi::prioOpenBmcBase, ipmi::netFnOemFive, 276 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_SEND_POST_BUFFER_TO_BMC), 277 ipmi::Privilege::User, ipmiOemPostCodeHandler); 278 ipmi::registerHandler( 279 ipmi::prioOemBase, ipmi::netFnOemFive, 280 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_GET_BIC_GPIO_STATE), 281 ipmi::Privilege::User, ipmiOemGetBicGpioState); 282 ipmi::registerHandler( 283 ipmi::prioOpenBmcBase, ipmi::netFnOemFive, 284 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_SET_HOST_POWER_STATE), 285 ipmi::Privilege::User, ipmiOemSetHostPowerState); 286 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFive, 287 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_GET_FLASH_SIZE), 288 ipmi::Privilege::User, ipmiOemGetBiosFlashSize); 289 return; 290 } 291 292 } // namespace ipmi 293