1 #pragma once 2 3 #include <app.hpp> 4 #include <async_resp.hpp> 5 #include <dbus_utility.hpp> 6 #include <error_messages.hpp> 7 #include <nlohmann/json.hpp> 8 #include <utils/collection.hpp> 9 #include <utils/hex_utils.hpp> 10 #include <utils/json_utils.hpp> 11 12 #include <vector> 13 14 namespace crow 15 { 16 namespace google_api 17 { 18 constexpr const char* hothSearchPath = "/xyz/openbmc_project"; 19 constexpr const char* hothInterface = "xyz.openbmc_project.Control.Hoth"; 20 constexpr const char* rotCollectionPrefix = "/google/v1/RootOfTrustCollection"; 21 22 inline void 23 handleGoogleV1Get(const crow::Request& /*req*/, 24 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 25 { 26 asyncResp->res.jsonValue["@odata.type"] = 27 "#GoogleServiceRoot.v1_0_0.GoogleServiceRoot"; 28 asyncResp->res.jsonValue["@odata.id"] = "/google/v1"; 29 asyncResp->res.jsonValue["Id"] = "Google Rest RootService"; 30 asyncResp->res.jsonValue["Name"] = "Google Service Root"; 31 asyncResp->res.jsonValue["Version"] = "1.0.0"; 32 asyncResp->res.jsonValue["RootOfTrustCollection"]["@odata.id"] = 33 rotCollectionPrefix; 34 } 35 36 inline void handleRootOfTrustCollectionGet( 37 const crow::Request& /*req*/, 38 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 39 { 40 asyncResp->res.jsonValue["@odata.id"] = rotCollectionPrefix; 41 asyncResp->res.jsonValue["@odata.type"] = 42 "#RootOfTrustCollection.RootOfTrustCollection"; 43 redfish::collection_util::getCollectionMembers( 44 asyncResp, rotCollectionPrefix, std::vector<const char*>{hothInterface}, 45 hothSearchPath); 46 } 47 48 // Helper struct to identify a resolved D-Bus object interface 49 struct ResolvedEntity 50 { 51 std::string id; 52 std::string service; 53 std::string object; 54 std::string interface; 55 }; 56 57 using ResolvedEntityHandler = std::function<void( 58 const std::string&, const std::shared_ptr<bmcweb::AsyncResp>&, 59 const ResolvedEntity&)>; 60 61 inline void hothGetSubtreeCallback( 62 const std::string& command, 63 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 64 const std::string& rotId, const ResolvedEntityHandler& entityHandler, 65 const boost::system::error_code ec, 66 const dbus::utility::MapperGetSubTreeResponse& subtree) 67 { 68 if (ec) 69 { 70 redfish::messages::internalError(asyncResp->res); 71 return; 72 } 73 // Iterate over all retrieved ObjectPaths. 74 for (const std::pair< 75 std::string, 76 std::vector<std::pair<std::string, std::vector<std::string>>>>& 77 object : subtree) 78 { 79 sdbusplus::message::object_path objPath(object.first); 80 if (objPath.filename() != rotId || object.second.empty()) 81 { 82 continue; 83 } 84 85 ResolvedEntity resolvedEntity = {.id = rotId, 86 .service = object.second[0].first, 87 .object = object.first, 88 .interface = hothInterface}; 89 entityHandler(command, asyncResp, resolvedEntity); 90 return; 91 } 92 93 // Couldn't find an object with that name. return an error 94 redfish::messages::resourceNotFound( 95 asyncResp->res, "#RootOfTrust.v1_0_0.RootOfTrust", rotId); 96 } 97 98 inline void resolveRoT(const std::string& command, 99 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 100 const std::string& rotId, 101 ResolvedEntityHandler&& entityHandler) 102 { 103 104 std::array<std::string, 1> hothIfaces = {hothInterface}; 105 crow::connections::systemBus->async_method_call( 106 [command, asyncResp, rotId, 107 entityHandler{std::forward<ResolvedEntityHandler>(entityHandler)}]( 108 const boost::system::error_code ec, 109 const dbus::utility::MapperGetSubTreeResponse& subtree) { 110 hothGetSubtreeCallback(command, asyncResp, rotId, entityHandler, ec, 111 subtree); 112 }, 113 "xyz.openbmc_project.ObjectMapper", 114 "/xyz/openbmc_project/object_mapper", 115 "xyz.openbmc_project.ObjectMapper", "GetSubTree", hothSearchPath, 116 /*depth=*/0, hothIfaces); 117 } 118 119 inline void populateRootOfTrustEntity( 120 const std::string& /*unused*/, 121 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 122 const ResolvedEntity& resolvedEntity) 123 { 124 asyncResp->res.jsonValue["@odata.type"] = "#RootOfTrust.v1_0_0.RootOfTrust"; 125 asyncResp->res.jsonValue["@odata.id"] = 126 "/google/v1/RootOfTrustCollection/" + resolvedEntity.id; 127 128 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 129 asyncResp->res.jsonValue["Id"] = resolvedEntity.id; 130 // Need to fix this later to a stabler property. 131 asyncResp->res.jsonValue["Name"] = resolvedEntity.id; 132 asyncResp->res.jsonValue["Description"] = "Google Root Of Trust"; 133 asyncResp->res.jsonValue["Actions"]["#RootOfTrust.SendCommand"]["target"] = 134 "/google/v1/RootOfTrustCollection/" + resolvedEntity.id + 135 "/Actions/RootOfTrust.SendCommand"; 136 137 asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 138 resolvedEntity.id; 139 asyncResp->res.jsonValue["Location"]["PartLocation"]["LocationType"] = 140 "Embedded"; 141 } 142 143 inline void 144 handleRootOfTrustGet(const crow::Request& /*req*/, 145 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 146 const std::string& param) 147 { 148 std::string emptyCommand; 149 resolveRoT(emptyCommand, asyncResp, param, populateRootOfTrustEntity); 150 } 151 152 inline void 153 invocationCallback(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 154 const boost::system::error_code ec, 155 const std::vector<uint8_t>& responseBytes) 156 { 157 if (ec) 158 { 159 BMCWEB_LOG_ERROR << "RootOfTrust.Actions.SendCommand failed: " 160 << ec.message(); 161 redfish::messages::internalError(asyncResp->res); 162 return; 163 } 164 165 asyncResp->res.jsonValue["CommandResponse"] = 166 bytesToHexString(responseBytes); 167 } 168 169 inline void 170 invokeRoTCommand(const std::string& command, 171 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 172 const ResolvedEntity& resolvedEntity) 173 { 174 std::vector<uint8_t> bytes = hexStringToBytes(command); 175 if (bytes.empty()) 176 { 177 BMCWEB_LOG_DEBUG << "Invalid command: " << command; 178 redfish::messages::actionParameterValueTypeError(command, "Command", 179 "SendCommand"); 180 return; 181 } 182 183 crow::connections::systemBus->async_method_call( 184 [asyncResp{asyncResp}](const boost::system::error_code ec, 185 const std::vector<uint8_t>& responseBytes) { 186 invocationCallback(asyncResp, ec, responseBytes); 187 }, 188 resolvedEntity.service, resolvedEntity.object, resolvedEntity.interface, 189 "SendHostCommand", bytes); 190 } 191 192 inline void handleRoTSendCommandPost( 193 const crow::Request& request, 194 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 195 const std::string& rotId) 196 { 197 std::string command; 198 if (!redfish::json_util::readJsonAction(request, asyncResp->res, "Command", 199 command)) 200 { 201 BMCWEB_LOG_DEBUG << "Missing property Command."; 202 redfish::messages::actionParameterMissing(asyncResp->res, "SendCommand", 203 "Command"); 204 return; 205 } 206 207 resolveRoT(command, asyncResp, rotId, invokeRoTCommand); 208 } 209 210 inline void requestRoutes(App& app) 211 { 212 BMCWEB_ROUTE(app, "/google/v1/") 213 .methods(boost::beast::http::verb::get)(handleGoogleV1Get); 214 215 BMCWEB_ROUTE(app, "/google/v1/RootOfTrustCollection") 216 .privileges({{"ConfigureManager"}}) 217 .methods(boost::beast::http::verb::get)(handleRootOfTrustCollectionGet); 218 219 BMCWEB_ROUTE(app, "/google/v1/RootOfTrustCollection/<str>") 220 .privileges({{"ConfigureManager"}}) 221 .methods(boost::beast::http::verb::get)(handleRootOfTrustGet); 222 223 BMCWEB_ROUTE( 224 app, 225 "/google/v1/RootOfTrustCollection/<str>/Actions/RootOfTrust.SendCommand") 226 .privileges({{"ConfigureManager"}}) 227 .methods(boost::beast::http::verb::post)(handleRoTSendCommandPost); 228 } 229 230 } // namespace google_api 231 } // namespace crow 232