blob: 75104939b1ce5cec509001c052ad50766dfe1883 [file] [log] [blame]
Vernon Mauerya3702c12019-05-22 13:20:59 -07001/*
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 <ipmid/api.h>
18
19#include <commandutils.hpp>
20#include <cstdint>
21#include <iostream>
22#include <ipmid/utils.hpp>
23#include <phosphor-logging/elog-errors.hpp>
24#include <phosphor-logging/log.hpp>
25#include <sdbusplus/bus.hpp>
26#include <smbioshandler.hpp>
27#include <string>
28#include <vector>
29#include <xyz/openbmc_project/Common/error.hpp>
30
31using InternalFailure =
32 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
33
34using level = phosphor::logging::level;
35
36constexpr const char* DBUS_PROPERTIES = "org.freedesktop.DBus.Properties";
37constexpr const char* MDRV1_PATH = "/xyz/openbmc_project/Smbios/MDR_V1";
38constexpr const char* MDRV1_INTERFACE = "xyz.openbmc_project.Smbios.MDR_V1";
39
40static void register_netfn_smbios_functions() __attribute__((constructor));
41static sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
42
43ipmi_ret_t cmd_region_status(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
44 ipmi_request_t request, ipmi_response_t response,
45 ipmi_data_len_t data_len, ipmi_context_t context)
46{
47 auto requestData = reinterpret_cast<const RegionStatusRequest*>(request);
48 std::vector<uint8_t> status;
49
50 if (*data_len != sizeof(RegionStatusRequest))
51 {
52 *data_len = 0;
53 return IPMI_CC_REQ_DATA_LEN_INVALID;
54 }
55
56 uint8_t regionId = requestData->regionId - 1;
57 *data_len = 0;
58
59 if (regionId >= maxMDRId)
60 {
61 phosphor::logging::log<level::ERR>("Invalid region");
62 return IPMI_CC_PARM_OUT_OF_RANGE;
63 }
64
65 std::string service = ipmi::getService(bus, MDRV1_INTERFACE, MDRV1_PATH);
66
67 auto method = bus.new_method_call(service.c_str(), MDRV1_PATH,
68 MDRV1_INTERFACE, "RegionStatus");
69 method.append(regionId);
70 auto reply = bus.call(method);
71 if (reply.is_method_error())
72 {
73 phosphor::logging::log<level::ERR>(
74 "Error get region status",
75 phosphor::logging::entry("SERVICE=%s", service.c_str()),
76 phosphor::logging::entry("PATH=%s", MDRV1_PATH));
77 return IPMI_CC_UNSPECIFIED_ERROR;
78 }
79 reply.read(status);
80
81 if (status.size() != sizeof(MDRState))
82 {
83 phosphor::logging::log<level::ERR>(
84 "Error get region status, return length invalid");
85 return IPMI_CC_UNSPECIFIED_ERROR;
86 }
87 *data_len = static_cast<size_t>(status.size());
88 auto dataOut = reinterpret_cast<uint8_t*>(response);
89 std::copy(&status[0], &status[*data_len], dataOut);
90 return IPMI_CC_OK;
91}
92
93int sdplus_mdrv1_get_property(
94 const std::string& name,
95 sdbusplus::message::variant<uint8_t, uint16_t>& value, std::string& service)
96{
97 auto method = bus.new_method_call(service.c_str(), MDRV1_PATH,
98 DBUS_PROPERTIES, "Get");
99 method.append(MDRV1_INTERFACE, name);
100 auto reply = bus.call(method);
101 if (reply.is_method_error())
102 {
103 phosphor::logging::log<level::ERR>(
104 "Error getting property, sdbusplus call failed");
105 return -1;
106 }
107 reply.read(value);
108
109 return 0;
110}
111
112static int set_regionId(uint8_t regionId, std::string& service)
113{
114 auto method = bus.new_method_call(service.c_str(), MDRV1_PATH,
115 DBUS_PROPERTIES, "Set");
116 sdbusplus::message::variant<uint8_t> value{regionId};
117 method.append(MDRV1_INTERFACE, "RegionId", value);
118 auto region = bus.call(method);
119 if (region.is_method_error())
120 {
121 phosphor::logging::log<level::ERR>(
122 "Error setting regionID, sdbusplus call failed");
123 return -1;
124 }
125 return 0;
126}
127
128ipmi_ret_t cmd_region_complete(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
129 ipmi_request_t request, ipmi_response_t response,
130 ipmi_data_len_t data_len, ipmi_context_t context)
131{
132 auto requestData = reinterpret_cast<const RegionCompleteRequest*>(request);
133 uint8_t status;
134
135 sdbusplus::message::variant<uint8_t, uint16_t> value;
136
137 if (*data_len != sizeof(RegionCompleteRequest))
138 {
139 *data_len = 0;
140 return IPMI_CC_REQ_DATA_LEN_INVALID;
141 }
142
143 uint8_t regionId = requestData->regionId - 1;
144 *data_len = 0;
145
146 if (regionId >= maxMDRId)
147 {
148 phosphor::logging::log<level::ERR>("Invalid region");
149 return IPMI_CC_PARM_OUT_OF_RANGE;
150 }
151
152 std::string service = ipmi::getService(bus, MDRV1_INTERFACE, MDRV1_PATH);
153
154 if (set_regionId(regionId, service) < 0)
155 {
156 phosphor::logging::log<level::ERR>("Error setting regionId");
157 return IPMI_CC_UNSPECIFIED_ERROR;
158 }
159
160 if (0 > sdplus_mdrv1_get_property("LockPolicy", value, service))
161 {
162 phosphor::logging::log<level::ERR>("Error getting lockPolicy");
163 return IPMI_CC_UNSPECIFIED_ERROR;
164 }
165 if (regionLockUnlocked ==
166 sdbusplus::message::variant_ns::get<uint8_t>(value))
167 {
168 return IPMI_CC_PARAMETER_NOT_SUPPORT_IN_PRESENT_STATE;
169 }
170
171 if (0 > sdplus_mdrv1_get_property("SessionId", value, service))
172 {
173 phosphor::logging::log<level::ERR>("Error getting sessionId");
174 return IPMI_CC_UNSPECIFIED_ERROR;
175 }
176 if (requestData->sessionId !=
177 sdbusplus::message::variant_ns::get<uint8_t>(value))
178 {
179 return IPMI_CC_OEM_SET_IN_PROCESS;
180 }
181
182 auto method = bus.new_method_call(service.c_str(), MDRV1_PATH,
183 MDRV1_INTERFACE, "RegionComplete");
184
185 method.append(regionId);
186
187 auto reply = bus.call(method);
188 if (reply.is_method_error())
189 {
190 phosphor::logging::log<level::ERR>(
191 "Error set region complete",
192 phosphor::logging::entry("SERVICE=%s", service.c_str()),
193 phosphor::logging::entry("PATH=%s", MDRV1_PATH));
194 return IPMI_CC_UNSPECIFIED_ERROR;
195 }
196 reply.read(status);
197
198 if (status != 0)
199 phosphor::logging::log<level::ERR>(
200 "Error set region complete, unexpected error");
201 return IPMI_CC_UNSPECIFIED_ERROR;
202
203 return IPMI_CC_OK;
204}
205
206ipmi_ret_t cmd_region_read(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
207 ipmi_request_t request, ipmi_response_t response,
208 ipmi_data_len_t data_len, ipmi_context_t context)
209{
210 auto requestData = reinterpret_cast<const RegionReadRequest*>(request);
211 auto responseData = reinterpret_cast<RegionReadResponse*>(response);
212 sdbusplus::message::variant<uint8_t, uint16_t> regUsedVal;
213 sdbusplus::message::variant<uint8_t, uint16_t> lockPolicyVal;
214 std::vector<uint8_t> res;
215
216 if (*data_len < sizeof(RegionReadRequest))
217 {
218 *data_len = 0;
219 return IPMI_CC_REQ_DATA_LEN_INVALID;
220 }
221
222 uint8_t regionId = requestData->regionId - 1;
223
224 *data_len = 0;
225
226 if (regionId >= maxMDRId)
227 {
228 phosphor::logging::log<level::ERR>("Invalid region");
229 return IPMI_CC_PARM_OUT_OF_RANGE;
230 }
231
232 std::string service = ipmi::getService(bus, MDRV1_INTERFACE, MDRV1_PATH);
233 // TODO to make sure the interface can get correct LockPolicy even
234 // regionId changed by another task.
235 if (set_regionId(regionId, service) < 0)
236 {
237 phosphor::logging::log<level::ERR>("Error setting regionId");
238 return IPMI_CC_UNSPECIFIED_ERROR;
239 }
240 if (0 > sdplus_mdrv1_get_property("RegionUsed", regUsedVal, service))
241 {
242 phosphor::logging::log<level::ERR>("Error getting regionUsed");
243 return IPMI_CC_UNSPECIFIED_ERROR;
244 }
245 if (requestData->offset + requestData->length >
246 sdbusplus::message::variant_ns::get<uint16_t>(regUsedVal))
247 {
248 return IPMI_CC_REQ_DATA_LEN_INVALID;
249 }
250
251 if (0 > sdplus_mdrv1_get_property("LockPolicy", lockPolicyVal, service))
252 {
253 phosphor::logging::log<level::ERR>("Error getting lockPolicy");
254 return IPMI_CC_UNSPECIFIED_ERROR;
255 }
256 if (regionLockUnlocked !=
257 sdbusplus::message::variant_ns::get<uint8_t>(lockPolicyVal))
258 {
259 return IPMI_CC_PARAMETER_NOT_SUPPORT_IN_PRESENT_STATE;
260 }
261
262 auto method = bus.new_method_call(service.c_str(), MDRV1_PATH,
263 MDRV1_INTERFACE, "RegionRead");
264
265 method.append(regionId, requestData->length, requestData->offset);
266
267 auto reply = bus.call(method);
268 if (reply.is_method_error())
269 {
270 phosphor::logging::log<level::ERR>(
271 "Error read region data",
272 phosphor::logging::entry("SERVICE=%s", service.c_str()),
273 phosphor::logging::entry("PATH=%s", MDRV1_PATH));
274 return IPMI_CC_UNSPECIFIED_ERROR;
275 }
276 reply.read(res);
277
278 *data_len = responseData->length = res[0];
279 responseData->updateCount = res[1];
280
281 if ((*data_len == 0) || (*data_len >= 254))
282 {
283 phosphor::logging::log<level::ERR>(
284 "Data length send from service is invalid");
285 *data_len = 0;
286 return IPMI_CC_RESPONSE_ERROR;
287 }
288
289 *data_len += 2 * sizeof(uint8_t);
290 std::copy(&res[2], &res[*data_len], responseData->data);
291 return IPMI_CC_OK;
292}
293
294ipmi_ret_t cmd_region_write(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
295 ipmi_request_t request, ipmi_response_t response,
296 ipmi_data_len_t data_len, ipmi_context_t context)
297{
298 auto requestData = reinterpret_cast<const RegionWriteRequest*>(request);
299 uint8_t regionId = requestData->regionId - 1;
300 std::string res;
301 std::vector<uint8_t> writeData;
302 uint16_t index;
303 uint8_t tmp[255];
304
305 size_t minInputLen = &requestData->data[0] - &requestData->sessionId + 1;
306 if (*data_len < minInputLen)
307 { // this command need at least 6 bytes input
308 *data_len = 0;
309 return IPMI_CC_REQ_DATA_LEN_INVALID;
310 }
311
312 sdbusplus::message::variant<uint8_t, uint16_t> value;
313
314 *data_len = 0;
315
316 if (regionId >= maxMDRId)
317 {
318 phosphor::logging::log<level::ERR>("Invalid region");
319 return IPMI_CC_PARM_OUT_OF_RANGE;
320 }
321
322 std::string service = ipmi::getService(bus, MDRV1_INTERFACE, MDRV1_PATH);
323
324 if (set_regionId(regionId, service) < 0)
325 {
326 phosphor::logging::log<level::ERR>("Error setting regionId");
327 return IPMI_CC_UNSPECIFIED_ERROR;
328 }
329
330 if (0 > sdplus_mdrv1_get_property("LockPolicy", value, service))
331 {
332 phosphor::logging::log<level::ERR>("Error getting lockPolicy");
333 return IPMI_CC_UNSPECIFIED_ERROR;
334 }
335 if (regionLockUnlocked ==
336 sdbusplus::message::variant_ns::get<uint8_t>(value))
337 {
338 return IPMI_CC_PARAMETER_NOT_SUPPORT_IN_PRESENT_STATE;
339 }
340
341 if (0 > sdplus_mdrv1_get_property("SessionId", value, service))
342 {
343 phosphor::logging::log<level::ERR>("Error getting sessionId");
344 return IPMI_CC_UNSPECIFIED_ERROR;
345 }
346 if (requestData->sessionId !=
347 sdbusplus::message::variant_ns::get<uint8_t>(value))
348 {
349 return IPMI_CC_OEM_SET_IN_PROCESS;
350 }
351
352 std::copy(&(requestData->length), &(requestData->data[requestData->length]),
353 tmp);
354 writeData.push_back(regionId);
355 for (index = 0; index < minInputLen + requestData->length - 2; index++)
356 {
357 writeData.push_back(tmp[index]);
358 }
359
360 auto method = bus.new_method_call(service.c_str(), MDRV1_PATH,
361 MDRV1_INTERFACE, "RegionWrite");
362
363 method.append(writeData);
364
365 auto reply = bus.call(method);
366 if (reply.is_method_error())
367 {
368 phosphor::logging::log<level::ERR>(
369 "Error write region data",
370 phosphor::logging::entry("SERVICE=%s", service.c_str()),
371 phosphor::logging::entry("PATH=%s", MDRV1_PATH));
372 return IPMI_CC_UNSPECIFIED_ERROR;
373 }
374 reply.read(res);
375
376 if (res == "NoData")
377 {
378 return IPMI_CC_PARM_OUT_OF_RANGE;
379 }
380 else if (res != "Success")
381 {
382 phosphor::logging::log<level::ERR>(
383 "Error write region data, unexpected error");
384 return IPMI_CC_UNSPECIFIED_ERROR;
385 }
386
387 return IPMI_CC_OK;
388}
389
390ipmi_ret_t cmd_region_lock(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
391 ipmi_request_t request, ipmi_response_t response,
392 ipmi_data_len_t data_len, ipmi_context_t context)
393{
394 auto requestData = reinterpret_cast<const RegionLockRequest*>(request);
395 uint8_t regionId = requestData->regionId - 1;
396 sdbusplus::message::variant<uint8_t, uint16_t> value;
397 auto res = reinterpret_cast<uint8_t*>(response);
398 uint8_t lockResponse;
399
400 if (*data_len != sizeof(RegionLockRequest))
401 {
402 *data_len = 0;
403 return IPMI_CC_REQ_DATA_LEN_INVALID;
404 }
405
406 *data_len = 0;
407
408 if (regionId >= maxMDRId)
409 {
410 phosphor::logging::log<level::ERR>("Invalid region");
411 return IPMI_CC_PARM_OUT_OF_RANGE;
412 }
413
414 std::string service = ipmi::getService(bus, MDRV1_INTERFACE, MDRV1_PATH);
415
416 if (set_regionId(regionId, service) < 0)
417 {
418 phosphor::logging::log<level::ERR>("Error setting regionId");
419 return IPMI_CC_UNSPECIFIED_ERROR;
420 }
421
422 if (0 > sdplus_mdrv1_get_property("LockPolicy", value, service))
423 {
424 phosphor::logging::log<level::ERR>("Error getting lockPolicy");
425 return IPMI_CC_UNSPECIFIED_ERROR;
426 }
427 if (requestData->lockPolicy == regionLockUnlocked)
428 {
429 if (regionLockUnlocked ==
430 sdbusplus::message::variant_ns::get<uint8_t>(value))
431 {
432 return IPMI_CC_PARAMETER_NOT_SUPPORT_IN_PRESENT_STATE;
433 }
434 }
435 if (regionLockUnlocked !=
436 sdbusplus::message::variant_ns::get<uint8_t>(value))
437 {
438 if (0 > sdplus_mdrv1_get_property("SessionId", value, service))
439 {
440 phosphor::logging::log<level::ERR>("Error getting sessionId");
441 return IPMI_CC_UNSPECIFIED_ERROR;
442 }
443 if (requestData->sessionId !=
444 sdbusplus::message::variant_ns::get<uint8_t>(value))
445 {
446 if (requestData->lockPolicy != regionLockStrict)
447 {
448 return IPMI_CC_OEM_SET_IN_PROCESS;
449 }
450 }
451 }
452 auto method = bus.new_method_call(service.c_str(), MDRV1_PATH,
453 MDRV1_INTERFACE, "RegionLock");
454
455 method.append(requestData->sessionId, regionId, requestData->lockPolicy,
456 requestData->msTimeout);
457
458 auto reply = bus.call(method);
459 if (reply.is_method_error())
460 {
461 phosphor::logging::log<level::ERR>(
462 "Error lock region ",
463 phosphor::logging::entry("SERVICE=%s", service.c_str()),
464 phosphor::logging::entry("PATH=%s", MDRV1_PATH));
465 return IPMI_CC_UNSPECIFIED_ERROR;
466 }
467 reply.read(lockResponse);
468
469 *data_len = sizeof(lockResponse);
470 *res = lockResponse;
471 return IPMI_CC_OK;
472}
473
474static void register_netfn_smbios_functions(void)
475{
476 // MDR V1 Command
477 // <Get MDR Status Command>
478 ipmi_register_callback(NETFUN_INTEL_APP_OEM,
479 IPMI_NETFN_INTEL_OEM_APP_CMD::MDR_STATUS, NULL,
480 cmd_region_status, PRIVILEGE_OPERATOR);
481
482 // <Update Complete Status Command>
483 ipmi_register_callback(NETFUN_INTEL_APP_OEM,
484 IPMI_NETFN_INTEL_OEM_APP_CMD::MDR_COMPLETE, NULL,
485 cmd_region_complete, PRIVILEGE_OPERATOR);
486
487 // <Read MDR Command>
488 ipmi_register_callback(NETFUN_INTEL_APP_OEM,
489 IPMI_NETFN_INTEL_OEM_APP_CMD::MDR_READ, NULL,
490 cmd_region_read, PRIVILEGE_OPERATOR);
491
492 // <Write MDR Command>
493 ipmi_register_callback(NETFUN_INTEL_APP_OEM,
494 IPMI_NETFN_INTEL_OEM_APP_CMD::MDR_WRITE, NULL,
495 cmd_region_write, PRIVILEGE_OPERATOR);
496
497 // <Lock MDR Command>
498 ipmi_register_callback(NETFUN_INTEL_APP_OEM,
499 IPMI_NETFN_INTEL_OEM_APP_CMD::MDR_LOCK, NULL,
500 cmd_region_lock, PRIVILEGE_OPERATOR);
501}