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