blob: e6eddcc6418e3d7df8f60c1e9958ac2a2c4b0335 [file] [log] [blame]
Jia, Chunhuia835eaa2018-09-05 09:00:41 +08001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
17#include <host-ipmid/ipmid-api.h>
18
19#include <array>
20#include <host-ipmid/utils.hpp>
21#include <iostream>
22#include <oemcommands.hpp>
23#include <phosphor-logging/log.hpp>
24#include <sdbusplus/bus.hpp>
25#include <sstream>
26#include <string>
27#include <vector>
28#include <xyz/openbmc_project/Common/error.hpp>
29
30namespace ipmi
31{
32static void register_netfn_firmware_functions() __attribute__((constructor));
33sdbusplus::bus::bus _dbus(ipmid_get_sd_bus_connection()); // from ipmid-api.h
34static constexpr size_t maxFruStringLength = 0x3F;
35
36// return code: 0 successful
37int8_t getChassisSerialNumber(sdbusplus::bus::bus& bus, std::string& serial)
38{
39 std::string objpath = "/xyz/openbmc_project/FruDevice";
40 std::string intf = "xyz.openbmc_project.FruDeviceManager";
41 std::string service = getService(bus, intf, objpath);
42 ObjectValueTree valueTree = getManagedObjects(bus, service, "/");
43 if (valueTree.empty())
44 {
45 phosphor::logging::log<phosphor::logging::level::ERR>(
46 "No object implements interface",
47 phosphor::logging::entry("INTF=%s", intf.c_str()));
48 return -1;
49 }
50
51 for (auto& item : valueTree)
52 {
53 auto interface = item.second.find("xyz.openbmc_project.FruDevice");
54 if (interface == item.second.end())
55 {
56 continue;
57 }
58
59 auto property = interface->second.find("CHASSIS_SERIAL_NUMBER");
60 if (property == interface->second.end())
61 {
62 continue;
63 }
64
65 try
66 {
67 Value variant = property->second;
68 std::string& result = variant.get<std::string>();
69 if (result.size() > maxFruStringLength)
70 {
71 phosphor::logging::log<phosphor::logging::level::ERR>(
72 "FRU serial number exceed maximum length");
73
74 return -1;
75 }
76 else
77 {
78 serial = result;
79 }
80 return 0;
81 }
82 catch (mapbox::util::bad_variant_access& e)
83 {
84 std::cerr << e.what() << std::endl;
85 return -1;
86 }
87 }
88 return -1;
89}
90ipmi_ret_t ipmiOEMWildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
91 ipmi_request_t request, ipmi_response_t response,
92 ipmi_data_len_t data_len, ipmi_context_t context)
93{
94 phosphor::logging::log<phosphor::logging::level::INFO>(
95 "Handling OEM WILDCARD", phosphor::logging::entry("NETFN=%x", netfn),
96 phosphor::logging::entry("CMD=%x", cmd));
97
98 // Status code.
99 ipmi_ret_t rc = IPMI_CC_INVALID;
100 *data_len = 0;
101 return rc;
102}
103
104// Returns the Chassis Identifier (serial #)
105ipmi_ret_t ipmiOEMGetChassisIdentifier(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
106 ipmi_request_t request,
107 ipmi_response_t response,
108 ipmi_data_len_t data_len,
109 ipmi_context_t context)
110{
111 std::string serial;
112 if (*data_len != 0) // invalid request if there are extra parameters
113 {
114 return IPMI_CC_REQ_DATA_LEN_INVALID;
115 }
116 if (getChassisSerialNumber(_dbus, serial) == 0)
117 {
118 *data_len = serial.size(); // length will never exceed response length
119 // as it is checked in getChassisSerialNumber
120 char* resp = static_cast<char*>(response);
121 serial.copy(resp, *data_len);
122 return IPMI_CC_OK;
123 }
124 else
125 {
126 *data_len = 0;
127 return IPMI_CC_RESPONSE_ERROR;
128 }
129}
130
131ipmi_ret_t ipmiOEMSetSystemGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
132 ipmi_request_t request,
133 ipmi_response_t response,
134 ipmi_data_len_t data_len,
135 ipmi_context_t context)
136{
137 static constexpr size_t safeBufferLength = 50;
138 char buf[safeBufferLength] = {0};
139 GUIDData* Data = reinterpret_cast<GUIDData*>(request);
140
141 if (*data_len != sizeof(GUIDData)) // 16bytes
142 {
143 *data_len = 0;
144 return IPMI_CC_REQ_DATA_LEN_INVALID;
145 }
146
147 *data_len = 0;
148
149 snprintf(
150 buf, safeBufferLength,
151 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
152 Data->timeLow4, Data->timeLow3, Data->timeLow2, Data->timeLow1,
153 Data->timeMid2, Data->timeMid1, Data->timeHigh2, Data->timeHigh1,
154 Data->clock2, Data->clock1, Data->node6, Data->node5, Data->node4,
155 Data->node3, Data->node2, Data->node1);
156 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
157 std::string guid = buf;
158 phosphor::logging::log<phosphor::logging::level::INFO>(
159 "Set System GUID", phosphor::logging::entry("GUID=%s", guid.c_str()));
160
161 std::string objpath = "/xyz/openbmc_project/control/host0/systemGUID";
162 std::string intf = "xyz.openbmc_project.Common.UUID";
163 std::string service = getService(_dbus, intf, objpath);
164 setDbusProperty(_dbus, service, objpath, intf, "UUID", guid);
165 return IPMI_CC_OK;
166}
167
168ipmi_ret_t ipmiOEMSetBIOSID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
169 ipmi_request_t request, ipmi_response_t response,
170 ipmi_data_len_t dataLen, ipmi_context_t context)
171{
172 DeviceInfo* data = reinterpret_cast<DeviceInfo*>(request);
173
174 if ((*dataLen < 2) || (*dataLen != (1 + data->biosIdLength)))
175 {
176 phosphor::logging::log<phosphor::logging::level::ERR>(
177 "Invalid parameter", phosphor::logging::entry("LEN=%d", *dataLen),
178 phosphor::logging::entry("BIOSIDLEN=%d", data->biosIdLength));
179
180 *dataLen = 0;
181 return IPMI_CC_REQ_DATA_LEN_INVALID;
182 }
183 std::string idString((char*)data->biosId, data->biosIdLength);
184
185 std::string service = getService(_dbus, biosIntf, biosObjPath);
186 setDbusProperty(_dbus, service, biosObjPath, biosIntf, biosProp, idString);
187 uint8_t* bytesWritten = static_cast<uint8_t*>(response);
188 *bytesWritten =
189 data->biosIdLength; // how many bytes are written into storage
190 *dataLen = 1;
191 return IPMI_CC_OK;
192}
193
194ipmi_ret_t ipmiOEMGetDeviceInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
195 ipmi_request_t request,
196 ipmi_response_t response,
197 ipmi_data_len_t dataLen, ipmi_context_t context)
198{
199 GetOemDeviceInfoReq* req = reinterpret_cast<GetOemDeviceInfoReq*>(request);
200 GetOemDeviceInfoRes* res = reinterpret_cast<GetOemDeviceInfoRes*>(response);
201
202 if (*dataLen == 0)
203 {
204 phosphor::logging::log<phosphor::logging::level::ERR>(
205 "Paramter length should be at least one byte");
206 return IPMI_CC_REQ_DATA_LEN_INVALID;
207 }
208
209 size_t reqDataLen = *dataLen;
210 *dataLen = 0;
211 if (req->entityType > OEMDevEntityType::sdrVer)
212 {
213 phosphor::logging::log<phosphor::logging::level::ERR>(
214 "Out of range",
215 phosphor::logging::entry("TYPE=%x", req->entityType));
216
217 return IPMI_CC_INVALID_FIELD_REQUEST;
218 }
219
220 // handle OEM command items
221 switch (req->entityType)
222 {
223 case OEMDevEntityType::biosId:
224 {
225 if (sizeof(GetOemDeviceInfoReq) != reqDataLen)
226 {
227 phosphor::logging::log<phosphor::logging::level::ERR>(
228 "Data length does not match request",
229 phosphor::logging::entry("REQLEN=%d", reqDataLen),
230 phosphor::logging::entry("LEN=%d",
231 sizeof(GetOemDeviceInfoReq)));
232 return IPMI_CC_REQ_DATA_LEN_INVALID;
233 }
234
235 std::string service = getService(_dbus, biosIntf, biosObjPath);
236 try
237 {
238 Value variant = getDbusProperty(_dbus, service, biosObjPath,
239 biosIntf, biosProp);
240 std::string& idString = variant.get<std::string>();
241 if (req->offset >= idString.size())
242 {
243 phosphor::logging::log<phosphor::logging::level::ERR>(
244 "offset exceed range",
245 phosphor::logging::entry("OFFSET=%d", req->offset),
246 phosphor::logging::entry("IDLEN=%d", idString.size()));
247 return IPMI_CC_PARM_OUT_OF_RANGE;
248 }
249 else
250 {
251 size_t length = 0;
252 if (req->countToRead > (idString.size() - req->offset))
253 {
254 length = idString.size() - req->offset;
255 }
256 else
257 {
258 length = req->countToRead;
259 }
260 std::copy(idString.begin() + req->offset, idString.end(),
261 res->data);
262 res->resDatalen = length;
263 *dataLen = res->resDatalen + 1;
264 }
265 }
266 catch (mapbox::util::bad_variant_access& e)
267 {
268 std::cerr << e.what() << std::endl;
269 return IPMI_CC_UNSPECIFIED_ERROR;
270 }
271 }
272 break;
273
274 case OEMDevEntityType::devVer:
275 case OEMDevEntityType::sdrVer:
276 // TODO:
277 return IPMI_CC_ILLEGAL_COMMAND;
278 default:
279 return IPMI_CC_INVALID_FIELD_REQUEST;
280 }
281 return IPMI_CC_OK;
282}
283
284ipmi_ret_t ipmiOEMGetAICFRU(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
285 ipmi_request_t request, ipmi_response_t response,
286 ipmi_data_len_t dataLen, ipmi_context_t context)
287{
288 if (*dataLen != 0)
289 {
290 phosphor::logging::log<phosphor::logging::level::ERR>(
291 "This command should not have any paramter");
292 return IPMI_CC_REQ_DATA_LEN_INVALID;
293 }
294
295 *dataLen = 1;
296 uint8_t* res = reinterpret_cast<uint8_t*>(response);
297 // temporary fix. We don't support AIC FRU now. Just tell BIOS that no
298 // AIC is available so that BIOS will not timeout repeatly which leads to
299 // slow booting.
300 *res = 0; // Byte1=Count of SlotPosition/FruID records.
301 return IPMI_CC_OK;
302}
303
304void ipmi_register(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_context_t context,
305 ipmid_callback_t handler, ipmi_cmd_privilege_t priv)
306{
307 std::ostringstream os;
308 os << "Registering NetFn:[0x" << std::hex << std::uppercase << netfn
309 << "], Cmd:[0x" << cmd << "]\n";
310 phosphor::logging::log<phosphor::logging::level::INFO>(os.str().c_str());
311 ipmi_register_callback(netfn, cmd, context, handler, priv);
312}
313
314static void register_netfn_firmware_functions(void)
315{
316 phosphor::logging::log<phosphor::logging::level::INFO>(
317 "Registering OEM commands");
318 ipmi_register(netfunIntcOEMGeneral, IPMI_CMD_WILDCARD, NULL,
319 ipmiOEMWildcard,
320 PRIVILEGE_USER); // wildcard default handler
321 ipmi_register(netfunIntcOEMGeneral, cmdGetChassisIdentifier, NULL,
322 ipmiOEMGetChassisIdentifier,
323 PRIVILEGE_USER); // get chassis identifier
324 ipmi_register(netfunIntcOEMGeneral, cmdSetSystemGUID, NULL,
325 ipmiOEMSetSystemGUID,
326 PRIVILEGE_ADMIN); // set system guid
327 ipmi_register(netfunIntcOEMGeneral, cmdSetBIOSID, NULL, ipmiOEMSetBIOSID,
328 PRIVILEGE_ADMIN);
329 ipmi_register(netfunIntcOEMGeneral, cmdGetOEMDeviceInfo, NULL,
330 ipmiOEMGetDeviceInfo, PRIVILEGE_USER);
331 ipmi_register(netfunIntcOEMGeneral, cmdGetAICSlotFRUIDRecords, NULL,
332 ipmiOEMGetAICFRU, PRIVILEGE_USER);
333 return;
334}
335
336} // namespace ipmi