1 /*
2 * SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #include "oemcommands.hpp"
7
8 #include <ipmid/api.hpp>
9 #include <ipmid/types.hpp>
10 #include <ipmid/utils.hpp>
11 #include <nlohmann/json.hpp>
12 #include <phosphor-logging/lg2.hpp>
13
14 #include <array>
15 #include <cstdint>
16 #include <fstream>
17
18 void registerBootstrapCredentialsOemCommands() __attribute__((constructor));
19
20 namespace ipmi
21 {
ipmiGetUsbVendorIdProductId(uint8_t type)22 ipmi::RspType<uint8_t, uint8_t> ipmiGetUsbVendorIdProductId(uint8_t type)
23 {
24 constexpr uint8_t descriptorVendorId = 1;
25 constexpr uint8_t descriptorProductId = 2;
26
27 // IPMI OEM USB Linux Gadget info
28 constexpr uint16_t usbVendorId = 0x0525;
29 constexpr uint16_t usbProductId = 0xA4A2;
30
31 if (type == descriptorVendorId)
32 {
33 return ipmi::responseSuccess(static_cast<uint8_t>(usbVendorId >> 8),
34 static_cast<uint8_t>(usbVendorId & 0xFF));
35 }
36 else if (type == descriptorProductId)
37 {
38 return ipmi::responseSuccess(static_cast<uint8_t>(usbProductId >> 8),
39 static_cast<uint8_t>(usbProductId & 0xFF));
40 }
41 return ipmi::responseInvalidFieldRequest();
42 }
43
ipmiGetUsbSerialNumber()44 ipmi::RspType<ipmi::message::Payload> ipmiGetUsbSerialNumber()
45 {
46 static constexpr uint8_t usbSerialNumber = 0x00;
47 ipmi::message::Payload usbSerialNumberPayload;
48 usbSerialNumberPayload.pack(usbSerialNumber);
49 return ipmi::responseSuccess(usbSerialNumberPayload);
50 }
51
ipmiGetRedfishHostName(ipmi::Context::ptr ctx)52 ipmi::RspType<ipmi::message::Payload> ipmiGetRedfishHostName(
53 ipmi::Context::ptr ctx)
54 {
55 std::string service{};
56 constexpr auto networkConfigObj = "/xyz/openbmc_project/network/config";
57 constexpr auto networkConfigIface =
58 "xyz.openbmc_project.Network.SystemConfiguration";
59 boost::system::error_code ec =
60 ipmi::getService(ctx, networkConfigIface, networkConfigObj, service);
61 if (ec)
62 {
63 lg2::error("ipmiGetRedfishHostName failed to get Network SystemConfig "
64 "object: {STATUS}",
65 "STATUS", ec.message());
66 return ipmi::responseResponseError();
67 }
68
69 std::string hostName{};
70 ec = ipmi::getDbusProperty<std::string>(
71 ctx, service, networkConfigObj, networkConfigIface, "HostName",
72 hostName);
73 if (ec)
74 {
75 lg2::error("ipmiGetRedfishHostName failed to get HostName from Network "
76 "SystemConfig service: {STATUS}",
77 "STATUS", ec.message());
78 return ipmi::responseResponseError();
79 }
80 ipmi::message::Payload hostNamePayload;
81 hostNamePayload.pack(
82 std::vector<uint8_t>(hostName.begin(), hostName.end()));
83 return ipmi::responseSuccess(hostNamePayload);
84 }
85
ipmiGetIpmiChannelRfHi()86 ipmi::RspType<uint8_t> ipmiGetIpmiChannelRfHi()
87 {
88 constexpr auto redfishHostInterfaceChannel = "usb0";
89 uint8_t chNum = ipmi::getChannelByName(redfishHostInterfaceChannel);
90 ChannelInfo chInfo{};
91 Cc compCode = ipmi::getChannelInfo(chNum, chInfo);
92 if (compCode != ipmi::ccSuccess)
93 {
94 lg2::error(
95 "ipmiGetIpmiChannelRfHi failed for channel {CHANNEL} with error {ERROR}",
96 "CHANNEL", chNum, "ERROR", compCode);
97 return ipmi::responseUnspecifiedError();
98 }
99
100 if (chInfo.mediumType !=
101 static_cast<uint8_t>(EChannelMediumType::lan8032) ||
102 chInfo.protocolType !=
103 static_cast<uint8_t>(EChannelProtocolType::ipmbV10) ||
104 chInfo.sessionSupported !=
105 static_cast<uint8_t>(EChannelSessSupported::multi) ||
106 chInfo.isIpmi != true)
107 {
108 lg2::error(
109 "ipmiGetIpmiChannelRfHi: channel {CHANNEL} lacks required config",
110 "CHANNEL", chNum);
111 return responseSensorInvalid();
112 }
113 return ipmi::responseSuccess(static_cast<uint8_t>(chNum));
114 }
115
getRfUuid(std::string & rfUuid)116 bool getRfUuid(std::string& rfUuid)
117 {
118 constexpr const char* bmcwebPersistentDataFile =
119 "/home/root/bmcweb_persistent_data.json";
120 std::ifstream f(bmcwebPersistentDataFile);
121 if (!f.is_open())
122 {
123 lg2::error("Failed to open {FILE}", "FILE", bmcwebPersistentDataFile);
124 return false;
125 }
126 auto data = nlohmann::json::parse(f, nullptr, false);
127 if (data.is_discarded())
128 {
129 lg2::error("Failed to parse {FILE}", "FILE", bmcwebPersistentDataFile);
130 return false;
131 }
132
133 if (auto it = data.find("system_uuid"); it != data.end() && it->is_string())
134 {
135 rfUuid = *it;
136 return true;
137 }
138
139 lg2::error("system_uuid missing in {FILE}", "FILE",
140 bmcwebPersistentDataFile);
141 return false;
142 }
143
ipmiGetRedfishServiceUUID()144 ipmi::RspType<std::vector<uint8_t>> ipmiGetRedfishServiceUUID()
145 {
146 std::string rfUuid;
147 bool ret = getRfUuid(rfUuid);
148 if (!ret)
149 {
150 lg2::error(
151 "ipmiGetRedfishServiceUUID: Error reading Redfish Service UUID File.");
152 return ipmi::responseResponseError();
153 }
154
155 // As per Redfish Host Interface Spec v1.3.0
156 // The Redfish UUID is 16byte and should be represented as below:
157 // Ex: {00112233-4455-6677-8899-AABBCCDDEEFF}
158 // 0x33 0x22 0x11 0x00 0x55 0x44 0x77 0x66 0x88 0x99 0xAA 0xBB 0xCC 0xDD
159 // 0xEE 0xFF
160
161 std::vector<uint8_t> resBuf;
162 std::vector<std::string> groups = ipmi::split(rfUuid, '-');
163
164 for (size_t i = 0; i < groups.size(); ++i)
165 {
166 auto group = groups[i];
167 if (i < 3)
168 {
169 std::reverse(group.begin(), group.end());
170 }
171
172 for (size_t j = 0; j < group.size(); j += 2)
173 {
174 if (i < 3)
175 {
176 std::swap(group[j], group[j + 1]);
177 }
178 resBuf.push_back(static_cast<uint8_t>(
179 std::stoi(group.substr(j, 2), nullptr, 16)));
180 }
181 }
182 return ipmi::responseSuccess(resBuf);
183 }
184
ipmiGetRedfishServicePort()185 ipmi::RspType<uint16_t> ipmiGetRedfishServicePort()
186 {
187 constexpr uint16_t redfishPort = 443;
188 return ipmi::responseSuccess(htons(redfishPort));
189 }
190
191 } // namespace ipmi
192
registerBootstrapCredentialsOemCommands()193 void registerBootstrapCredentialsOemCommands()
194 {
195 ipmi::registerHandler(
196 ipmi::prioOemBase, ipmi::groupNvidia,
197 ipmi::bootstrap_credentials_oem::cmdGetUsbVendorIdProductId,
198 ipmi::Privilege::Admin, ipmi::ipmiGetUsbVendorIdProductId);
199
200 ipmi::registerHandler(
201 ipmi::prioOemBase, ipmi::groupNvidia,
202 ipmi::bootstrap_credentials_oem::cmdGetUsbSerialNumber,
203 ipmi::Privilege::Admin, ipmi::ipmiGetUsbSerialNumber);
204
205 ipmi::registerHandler(
206 ipmi::prioOemBase, ipmi::groupNvidia,
207 ipmi::bootstrap_credentials_oem::cmdGetRedfishHostName,
208 ipmi::Privilege::Admin, ipmi::ipmiGetRedfishHostName);
209
210 ipmi::registerHandler(
211 ipmi::prioOemBase, ipmi::groupNvidia,
212 ipmi::bootstrap_credentials_oem::cmdGetIpmiChannelRfHi,
213 ipmi::Privilege::Admin, ipmi::ipmiGetIpmiChannelRfHi);
214
215 ipmi::registerHandler(
216 ipmi::prioOemBase, ipmi::groupNvidia,
217 ipmi::bootstrap_credentials_oem::cmdGetRedfishServiceUUID,
218 ipmi::Privilege::Admin, ipmi::ipmiGetRedfishServiceUUID);
219
220 ipmi::registerHandler(
221 ipmi::prioOemBase, ipmi::groupNvidia,
222 ipmi::bootstrap_credentials_oem::cmdGetRedfishServicePort,
223 ipmi::Privilege::Admin, ipmi::ipmiGetRedfishServicePort);
224 }
225