xref: /openbmc/phosphor-host-ipmid/oem/nvidia/bootstrap-credentials-oem-cmds.cpp (revision c1e7b5c3f0a2bec3bb7b5c34d7a90c8bdc6e7aac)
1bffaa110SPrithvi Pai /*
2bffaa110SPrithvi Pai  * SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION &
3bffaa110SPrithvi Pai  * AFFILIATES. All rights reserved.
4bffaa110SPrithvi Pai  * SPDX-License-Identifier: Apache-2.0
5bffaa110SPrithvi Pai  */
6bffaa110SPrithvi Pai 
7bffaa110SPrithvi Pai #include "oemcommands.hpp"
8bffaa110SPrithvi Pai 
9bffaa110SPrithvi Pai #include <ipmid/api.hpp>
10bffaa110SPrithvi Pai #include <ipmid/types.hpp>
116823fd46SPrithvi Pai #include <ipmid/utils.hpp>
12*c1e7b5c3SPrithvi Pai #include <nlohmann/json.hpp>
136823fd46SPrithvi Pai #include <phosphor-logging/lg2.hpp>
14bffaa110SPrithvi Pai 
15*c1e7b5c3SPrithvi Pai #include <array>
16bffaa110SPrithvi Pai #include <cstdint>
17*c1e7b5c3SPrithvi Pai #include <fstream>
18bffaa110SPrithvi Pai 
19bffaa110SPrithvi Pai void registerBootstrapCredentialsOemCommands() __attribute__((constructor));
20bffaa110SPrithvi Pai 
21bffaa110SPrithvi Pai namespace ipmi
22bffaa110SPrithvi Pai {
23bffaa110SPrithvi Pai ipmi::RspType<uint8_t, uint8_t> ipmiGetUsbVendorIdProductId(uint8_t type)
24bffaa110SPrithvi Pai {
25bffaa110SPrithvi Pai     constexpr uint8_t descriptorVendorId = 1;
26bffaa110SPrithvi Pai     constexpr uint8_t descriptorProductId = 2;
27bffaa110SPrithvi Pai 
28bffaa110SPrithvi Pai     // IPMI OEM USB Linux Gadget info
29bffaa110SPrithvi Pai     constexpr uint16_t usbVendorId = 0x0525;
30bffaa110SPrithvi Pai     constexpr uint16_t usbProductId = 0xA4A2;
31bffaa110SPrithvi Pai 
32bffaa110SPrithvi Pai     if (type == descriptorVendorId)
33bffaa110SPrithvi Pai     {
34bffaa110SPrithvi Pai         return ipmi::responseSuccess(static_cast<uint8_t>(usbVendorId >> 8),
35bffaa110SPrithvi Pai                                      static_cast<uint8_t>(usbVendorId & 0xFF));
36bffaa110SPrithvi Pai     }
37bffaa110SPrithvi Pai     else if (type == descriptorProductId)
38bffaa110SPrithvi Pai     {
39bffaa110SPrithvi Pai         return ipmi::responseSuccess(static_cast<uint8_t>(usbProductId >> 8),
40bffaa110SPrithvi Pai                                      static_cast<uint8_t>(usbProductId & 0xFF));
41bffaa110SPrithvi Pai     }
42bffaa110SPrithvi Pai     return ipmi::responseInvalidFieldRequest();
43bffaa110SPrithvi Pai }
44bffaa110SPrithvi Pai 
456bf35ee6SPrithvi Pai ipmi::RspType<ipmi::message::Payload> ipmiGetUsbSerialNumber()
466bf35ee6SPrithvi Pai {
476bf35ee6SPrithvi Pai     static constexpr uint8_t usbSerialNumber = 0x00;
486bf35ee6SPrithvi Pai     ipmi::message::Payload usbSerialNumberPayload;
496bf35ee6SPrithvi Pai     usbSerialNumberPayload.pack(usbSerialNumber);
506bf35ee6SPrithvi Pai     return ipmi::responseSuccess(usbSerialNumberPayload);
516bf35ee6SPrithvi Pai }
526bf35ee6SPrithvi Pai 
536823fd46SPrithvi Pai ipmi::RspType<ipmi::message::Payload> ipmiGetRedfishHostName(
546823fd46SPrithvi Pai     ipmi::Context::ptr ctx)
556823fd46SPrithvi Pai {
566823fd46SPrithvi Pai     std::string service{};
576823fd46SPrithvi Pai     constexpr auto networkConfigObj = "/xyz/openbmc_project/network/config";
586823fd46SPrithvi Pai     constexpr auto networkConfigIface =
596823fd46SPrithvi Pai         "xyz.openbmc_project.Network.SystemConfiguration";
606823fd46SPrithvi Pai     boost::system::error_code ec =
616823fd46SPrithvi Pai         ipmi::getService(ctx, networkConfigIface, networkConfigObj, service);
626823fd46SPrithvi Pai     if (ec)
636823fd46SPrithvi Pai     {
646823fd46SPrithvi Pai         lg2::error("ipmiGetRedfishHostName failed to get Network SystemConfig "
656823fd46SPrithvi Pai                    "object: {STATUS}",
666823fd46SPrithvi Pai                    "STATUS", ec.message());
676823fd46SPrithvi Pai         return ipmi::responseResponseError();
686823fd46SPrithvi Pai     }
696823fd46SPrithvi Pai 
706823fd46SPrithvi Pai     std::string hostName{};
716823fd46SPrithvi Pai     ec = ipmi::getDbusProperty<std::string>(
726823fd46SPrithvi Pai         ctx, service, networkConfigObj, networkConfigIface, "HostName",
736823fd46SPrithvi Pai         hostName);
746823fd46SPrithvi Pai     if (ec)
756823fd46SPrithvi Pai     {
766823fd46SPrithvi Pai         lg2::error("ipmiGetRedfishHostName failed to get HostName from Network "
776823fd46SPrithvi Pai                    "SystemConfig service: {STATUS}",
786823fd46SPrithvi Pai                    "STATUS", ec.message());
796823fd46SPrithvi Pai         return ipmi::responseResponseError();
806823fd46SPrithvi Pai     }
816823fd46SPrithvi Pai     ipmi::message::Payload hostNamePayload;
826823fd46SPrithvi Pai     hostNamePayload.pack(
836823fd46SPrithvi Pai         std::vector<uint8_t>(hostName.begin(), hostName.end()));
846823fd46SPrithvi Pai     return ipmi::responseSuccess(hostNamePayload);
856823fd46SPrithvi Pai }
86529d31c7SPrithvi Pai 
87529d31c7SPrithvi Pai ipmi::RspType<uint8_t> ipmiGetIpmiChannelRfHi()
88529d31c7SPrithvi Pai {
89529d31c7SPrithvi Pai     constexpr auto redfishHostInterfaceChannel = "usb0";
90529d31c7SPrithvi Pai     uint8_t chNum = ipmi::getChannelByName(redfishHostInterfaceChannel);
91529d31c7SPrithvi Pai     ChannelInfo chInfo{};
92529d31c7SPrithvi Pai     Cc compCode = ipmi::getChannelInfo(chNum, chInfo);
93529d31c7SPrithvi Pai     if (compCode != ipmi::ccSuccess)
94529d31c7SPrithvi Pai     {
95529d31c7SPrithvi Pai         lg2::error(
96529d31c7SPrithvi Pai             "ipmiGetIpmiChannelRfHi failed for channel {CHANNEL} with error {ERROR}",
97529d31c7SPrithvi Pai             "CHANNEL", chNum, "ERROR", compCode);
98529d31c7SPrithvi Pai         return ipmi::responseUnspecifiedError();
99529d31c7SPrithvi Pai     }
100529d31c7SPrithvi Pai 
101529d31c7SPrithvi Pai     if (chInfo.mediumType !=
102529d31c7SPrithvi Pai             static_cast<uint8_t>(EChannelMediumType::lan8032) ||
103529d31c7SPrithvi Pai         chInfo.protocolType !=
104529d31c7SPrithvi Pai             static_cast<uint8_t>(EChannelProtocolType::ipmbV10) ||
105529d31c7SPrithvi Pai         chInfo.sessionSupported !=
106529d31c7SPrithvi Pai             static_cast<uint8_t>(EChannelSessSupported::multi) ||
107529d31c7SPrithvi Pai         chInfo.isIpmi != true)
108529d31c7SPrithvi Pai     {
109529d31c7SPrithvi Pai         lg2::error(
110529d31c7SPrithvi Pai             "ipmiGetIpmiChannelRfHi: channel {CHANNEL} lacks required config",
111529d31c7SPrithvi Pai             "CHANNEL", chNum);
112529d31c7SPrithvi Pai         return responseSensorInvalid();
113529d31c7SPrithvi Pai     }
114529d31c7SPrithvi Pai     return ipmi::responseSuccess(static_cast<uint8_t>(chNum));
115529d31c7SPrithvi Pai }
116529d31c7SPrithvi Pai 
117*c1e7b5c3SPrithvi Pai bool getRfUuid(std::string& rfUuid)
118*c1e7b5c3SPrithvi Pai {
119*c1e7b5c3SPrithvi Pai     constexpr const char* bmcwebPersistentDataFile =
120*c1e7b5c3SPrithvi Pai         "/home/root/bmcweb_persistent_data.json";
121*c1e7b5c3SPrithvi Pai     std::ifstream f(bmcwebPersistentDataFile);
122*c1e7b5c3SPrithvi Pai     if (!f.is_open())
123*c1e7b5c3SPrithvi Pai     {
124*c1e7b5c3SPrithvi Pai         lg2::error("Failed to open {FILE}", "FILE", bmcwebPersistentDataFile);
125*c1e7b5c3SPrithvi Pai         return false;
126*c1e7b5c3SPrithvi Pai     }
127*c1e7b5c3SPrithvi Pai     auto data = nlohmann::json::parse(f, nullptr, false);
128*c1e7b5c3SPrithvi Pai     if (data.is_discarded())
129*c1e7b5c3SPrithvi Pai     {
130*c1e7b5c3SPrithvi Pai         lg2::error("Failed to parse {FILE}", "FILE", bmcwebPersistentDataFile);
131*c1e7b5c3SPrithvi Pai         return false;
132*c1e7b5c3SPrithvi Pai     }
133*c1e7b5c3SPrithvi Pai 
134*c1e7b5c3SPrithvi Pai     if (auto it = data.find("system_uuid"); it != data.end() && it->is_string())
135*c1e7b5c3SPrithvi Pai     {
136*c1e7b5c3SPrithvi Pai         rfUuid = *it;
137*c1e7b5c3SPrithvi Pai         return true;
138*c1e7b5c3SPrithvi Pai     }
139*c1e7b5c3SPrithvi Pai 
140*c1e7b5c3SPrithvi Pai     lg2::error("system_uuid missing in {FILE}", "FILE",
141*c1e7b5c3SPrithvi Pai                bmcwebPersistentDataFile);
142*c1e7b5c3SPrithvi Pai     return false;
143*c1e7b5c3SPrithvi Pai }
144*c1e7b5c3SPrithvi Pai 
145*c1e7b5c3SPrithvi Pai ipmi::RspType<std::vector<uint8_t>> ipmiGetRedfishServiceUUID()
146*c1e7b5c3SPrithvi Pai {
147*c1e7b5c3SPrithvi Pai     std::string rfUuid;
148*c1e7b5c3SPrithvi Pai     bool ret = getRfUuid(rfUuid);
149*c1e7b5c3SPrithvi Pai     if (!ret)
150*c1e7b5c3SPrithvi Pai     {
151*c1e7b5c3SPrithvi Pai         lg2::error(
152*c1e7b5c3SPrithvi Pai             "ipmiGetRedfishServiceUUID: Error reading Redfish Service UUID File.");
153*c1e7b5c3SPrithvi Pai         return ipmi::responseResponseError();
154*c1e7b5c3SPrithvi Pai     }
155*c1e7b5c3SPrithvi Pai 
156*c1e7b5c3SPrithvi Pai     // As per Redfish Host Interface Spec v1.3.0
157*c1e7b5c3SPrithvi Pai     // The Redfish UUID is 16byte and should be represented as below:
158*c1e7b5c3SPrithvi Pai     // Ex: {00112233-4455-6677-8899-AABBCCDDEEFF}
159*c1e7b5c3SPrithvi Pai     // 0x33 0x22 0x11 0x00 0x55 0x44 0x77 0x66 0x88 0x99 0xAA 0xBB 0xCC 0xDD
160*c1e7b5c3SPrithvi Pai     // 0xEE 0xFF
161*c1e7b5c3SPrithvi Pai 
162*c1e7b5c3SPrithvi Pai     std::vector<uint8_t> resBuf;
163*c1e7b5c3SPrithvi Pai     std::vector<std::string> groups = ipmi::split(rfUuid, '-');
164*c1e7b5c3SPrithvi Pai 
165*c1e7b5c3SPrithvi Pai     for (size_t i = 0; i < groups.size(); ++i)
166*c1e7b5c3SPrithvi Pai     {
167*c1e7b5c3SPrithvi Pai         auto group = groups[i];
168*c1e7b5c3SPrithvi Pai         if (i < 3)
169*c1e7b5c3SPrithvi Pai         {
170*c1e7b5c3SPrithvi Pai             std::reverse(group.begin(), group.end());
171*c1e7b5c3SPrithvi Pai         }
172*c1e7b5c3SPrithvi Pai 
173*c1e7b5c3SPrithvi Pai         for (size_t j = 0; j < group.size(); j += 2)
174*c1e7b5c3SPrithvi Pai         {
175*c1e7b5c3SPrithvi Pai             if (i < 3)
176*c1e7b5c3SPrithvi Pai             {
177*c1e7b5c3SPrithvi Pai                 std::swap(group[j], group[j + 1]);
178*c1e7b5c3SPrithvi Pai             }
179*c1e7b5c3SPrithvi Pai             resBuf.push_back(static_cast<uint8_t>(
180*c1e7b5c3SPrithvi Pai                 std::stoi(group.substr(j, 2), nullptr, 16)));
181*c1e7b5c3SPrithvi Pai         }
182*c1e7b5c3SPrithvi Pai     }
183*c1e7b5c3SPrithvi Pai     return ipmi::responseSuccess(resBuf);
184*c1e7b5c3SPrithvi Pai }
185*c1e7b5c3SPrithvi Pai 
186bffaa110SPrithvi Pai } // namespace ipmi
187bffaa110SPrithvi Pai 
188bffaa110SPrithvi Pai void registerBootstrapCredentialsOemCommands()
189bffaa110SPrithvi Pai {
190bffaa110SPrithvi Pai     ipmi::registerHandler(
191bffaa110SPrithvi Pai         ipmi::prioOemBase, ipmi::groupNvidia,
192bffaa110SPrithvi Pai         ipmi::bootstrap_credentials_oem::cmdGetUsbVendorIdProductId,
193bffaa110SPrithvi Pai         ipmi::Privilege::Admin, ipmi::ipmiGetUsbVendorIdProductId);
1946bf35ee6SPrithvi Pai 
1956bf35ee6SPrithvi Pai     ipmi::registerHandler(
1966bf35ee6SPrithvi Pai         ipmi::prioOemBase, ipmi::groupNvidia,
1976bf35ee6SPrithvi Pai         ipmi::bootstrap_credentials_oem::cmdGetUsbSerialNumber,
1986bf35ee6SPrithvi Pai         ipmi::Privilege::Admin, ipmi::ipmiGetUsbSerialNumber);
1996823fd46SPrithvi Pai 
2006823fd46SPrithvi Pai     ipmi::registerHandler(
2016823fd46SPrithvi Pai         ipmi::prioOemBase, ipmi::groupNvidia,
2026823fd46SPrithvi Pai         ipmi::bootstrap_credentials_oem::cmdGetRedfishHostName,
2036823fd46SPrithvi Pai         ipmi::Privilege::Admin, ipmi::ipmiGetRedfishHostName);
204529d31c7SPrithvi Pai 
205529d31c7SPrithvi Pai     ipmi::registerHandler(
206529d31c7SPrithvi Pai         ipmi::prioOemBase, ipmi::groupNvidia,
207529d31c7SPrithvi Pai         ipmi::bootstrap_credentials_oem::cmdGetIpmiChannelRfHi,
208529d31c7SPrithvi Pai         ipmi::Privilege::Admin, ipmi::ipmiGetIpmiChannelRfHi);
209*c1e7b5c3SPrithvi Pai 
210*c1e7b5c3SPrithvi Pai     ipmi::registerHandler(
211*c1e7b5c3SPrithvi Pai         ipmi::prioOemBase, ipmi::groupNvidia,
212*c1e7b5c3SPrithvi Pai         ipmi::bootstrap_credentials_oem::cmdGetRedfishServiceUUID,
213*c1e7b5c3SPrithvi Pai         ipmi::Privilege::Admin, ipmi::ipmiGetRedfishServiceUUID);
214bffaa110SPrithvi Pai }
215