blob: 9eebf7f52db9990f977f10ef6512b84e12ce0d2f [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
Vernon Mauerya3702c12019-05-22 13:20:59 -070017#include <commandutils.hpp>
18#include <cstdint>
19#include <iostream>
Vernon Mauery15419dd2019-05-24 09:40:30 -070020#include <ipmid/api.hpp>
Vernon Mauerya3702c12019-05-22 13:20:59 -070021#include <ipmid/utils.hpp>
22#include <phosphor-logging/elog-errors.hpp>
23#include <phosphor-logging/log.hpp>
Vernon Mauerya3702c12019-05-22 13:20:59 -070024#include <smbioshandler.hpp>
25#include <string>
26#include <vector>
27#include <xyz/openbmc_project/Common/error.hpp>
28
29using InternalFailure =
30 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
31
32using level = phosphor::logging::level;
33
34constexpr const char* DBUS_PROPERTIES = "org.freedesktop.DBus.Properties";
35constexpr const char* MDRV1_PATH = "/xyz/openbmc_project/Smbios/MDR_V1";
36constexpr const char* MDRV1_INTERFACE = "xyz.openbmc_project.Smbios.MDR_V1";
37
38static void register_netfn_smbios_functions() __attribute__((constructor));
Vernon Mauerya3702c12019-05-22 13:20:59 -070039
40ipmi_ret_t cmd_region_status(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
41 ipmi_request_t request, ipmi_response_t response,
42 ipmi_data_len_t data_len, ipmi_context_t context)
43{
44 auto requestData = reinterpret_cast<const RegionStatusRequest*>(request);
45 std::vector<uint8_t> status;
46
47 if (*data_len != sizeof(RegionStatusRequest))
48 {
49 *data_len = 0;
50 return IPMI_CC_REQ_DATA_LEN_INVALID;
51 }
52
53 uint8_t regionId = requestData->regionId - 1;
54 *data_len = 0;
55
56 if (regionId >= maxMDRId)
57 {
58 phosphor::logging::log<level::ERR>("Invalid region");
59 return IPMI_CC_PARM_OUT_OF_RANGE;
60 }
61
Vernon Mauery15419dd2019-05-24 09:40:30 -070062 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
63 std::string service = ipmi::getService(*bus, MDRV1_INTERFACE, MDRV1_PATH);
Vernon Mauerya3702c12019-05-22 13:20:59 -070064
Vernon Mauery15419dd2019-05-24 09:40:30 -070065 auto method = bus->new_method_call(service.c_str(), MDRV1_PATH,
66 MDRV1_INTERFACE, "RegionStatus");
Vernon Mauerya3702c12019-05-22 13:20:59 -070067 method.append(regionId);
Vernon Mauery15419dd2019-05-24 09:40:30 -070068 auto reply = bus->call(method);
Vernon Mauerya3702c12019-05-22 13:20:59 -070069 if (reply.is_method_error())
70 {
71 phosphor::logging::log<level::ERR>(
72 "Error get region status",
73 phosphor::logging::entry("SERVICE=%s", service.c_str()),
74 phosphor::logging::entry("PATH=%s", MDRV1_PATH));
75 return IPMI_CC_UNSPECIFIED_ERROR;
76 }
77 reply.read(status);
78
79 if (status.size() != sizeof(MDRState))
80 {
81 phosphor::logging::log<level::ERR>(
82 "Error get region status, return length invalid");
83 return IPMI_CC_UNSPECIFIED_ERROR;
84 }
85 *data_len = static_cast<size_t>(status.size());
86 auto dataOut = reinterpret_cast<uint8_t*>(response);
87 std::copy(&status[0], &status[*data_len], dataOut);
88 return IPMI_CC_OK;
89}
90
91int sdplus_mdrv1_get_property(
92 const std::string& name,
93 sdbusplus::message::variant<uint8_t, uint16_t>& value, std::string& service)
94{
Vernon Mauery15419dd2019-05-24 09:40:30 -070095 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
96 auto method = bus->new_method_call(service.c_str(), MDRV1_PATH,
97 DBUS_PROPERTIES, "Get");
Vernon Mauerya3702c12019-05-22 13:20:59 -070098 method.append(MDRV1_INTERFACE, name);
Vernon Mauery15419dd2019-05-24 09:40:30 -070099 auto reply = bus->call(method);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700100 if (reply.is_method_error())
101 {
102 phosphor::logging::log<level::ERR>(
103 "Error getting property, sdbusplus call failed");
104 return -1;
105 }
106 reply.read(value);
107
108 return 0;
109}
110
111static int set_regionId(uint8_t regionId, std::string& service)
112{
Vernon Mauery15419dd2019-05-24 09:40:30 -0700113 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
114 auto method = bus->new_method_call(service.c_str(), MDRV1_PATH,
115 DBUS_PROPERTIES, "Set");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700116 sdbusplus::message::variant<uint8_t> value{regionId};
117 method.append(MDRV1_INTERFACE, "RegionId", value);
Vernon Mauery15419dd2019-05-24 09:40:30 -0700118 auto region = bus->call(method);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700119 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
Vernon Mauery15419dd2019-05-24 09:40:30 -0700152 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
153 std::string service = ipmi::getService(*bus, MDRV1_INTERFACE, MDRV1_PATH);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700154
155 if (set_regionId(regionId, service) < 0)
156 {
157 phosphor::logging::log<level::ERR>("Error setting regionId");
158 return IPMI_CC_UNSPECIFIED_ERROR;
159 }
160
161 if (0 > sdplus_mdrv1_get_property("LockPolicy", value, service))
162 {
163 phosphor::logging::log<level::ERR>("Error getting lockPolicy");
164 return IPMI_CC_UNSPECIFIED_ERROR;
165 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700166 if (regionLockUnlocked == std::get<uint8_t>(value))
Vernon Mauerya3702c12019-05-22 13:20:59 -0700167 {
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 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700176 if (requestData->sessionId != std::get<uint8_t>(value))
Vernon Mauerya3702c12019-05-22 13:20:59 -0700177 {
178 return IPMI_CC_OEM_SET_IN_PROCESS;
179 }
180
Vernon Mauery15419dd2019-05-24 09:40:30 -0700181 auto method = bus->new_method_call(service.c_str(), MDRV1_PATH,
182 MDRV1_INTERFACE, "RegionComplete");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700183
184 method.append(regionId);
185
Vernon Mauery15419dd2019-05-24 09:40:30 -0700186 auto reply = bus->call(method);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700187 if (reply.is_method_error())
188 {
189 phosphor::logging::log<level::ERR>(
190 "Error set region complete",
191 phosphor::logging::entry("SERVICE=%s", service.c_str()),
192 phosphor::logging::entry("PATH=%s", MDRV1_PATH));
193 return IPMI_CC_UNSPECIFIED_ERROR;
194 }
195 reply.read(status);
196
197 if (status != 0)
198 phosphor::logging::log<level::ERR>(
199 "Error set region complete, unexpected error");
200 return IPMI_CC_UNSPECIFIED_ERROR;
201
202 return IPMI_CC_OK;
203}
204
205ipmi_ret_t cmd_region_read(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
206 ipmi_request_t request, ipmi_response_t response,
207 ipmi_data_len_t data_len, ipmi_context_t context)
208{
209 auto requestData = reinterpret_cast<const RegionReadRequest*>(request);
210 auto responseData = reinterpret_cast<RegionReadResponse*>(response);
211 sdbusplus::message::variant<uint8_t, uint16_t> regUsedVal;
212 sdbusplus::message::variant<uint8_t, uint16_t> lockPolicyVal;
213 std::vector<uint8_t> res;
214
215 if (*data_len < sizeof(RegionReadRequest))
216 {
217 *data_len = 0;
218 return IPMI_CC_REQ_DATA_LEN_INVALID;
219 }
220
221 uint8_t regionId = requestData->regionId - 1;
222
223 *data_len = 0;
224
225 if (regionId >= maxMDRId)
226 {
227 phosphor::logging::log<level::ERR>("Invalid region");
228 return IPMI_CC_PARM_OUT_OF_RANGE;
229 }
230
Vernon Mauery15419dd2019-05-24 09:40:30 -0700231 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
232 std::string service = ipmi::getService(*bus, MDRV1_INTERFACE, MDRV1_PATH);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700233 // 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 >
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700246 std::get<uint16_t>(regUsedVal))
Vernon Mauerya3702c12019-05-22 13:20:59 -0700247 {
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 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700256 if (regionLockUnlocked != std::get<uint8_t>(lockPolicyVal))
Vernon Mauerya3702c12019-05-22 13:20:59 -0700257 {
258 return IPMI_CC_PARAMETER_NOT_SUPPORT_IN_PRESENT_STATE;
259 }
260
Vernon Mauery15419dd2019-05-24 09:40:30 -0700261 auto method = bus->new_method_call(service.c_str(), MDRV1_PATH,
262 MDRV1_INTERFACE, "RegionRead");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700263
264 method.append(regionId, requestData->length, requestData->offset);
265
Vernon Mauery15419dd2019-05-24 09:40:30 -0700266 auto reply = bus->call(method);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700267 if (reply.is_method_error())
268 {
269 phosphor::logging::log<level::ERR>(
270 "Error read region data",
271 phosphor::logging::entry("SERVICE=%s", service.c_str()),
272 phosphor::logging::entry("PATH=%s", MDRV1_PATH));
273 return IPMI_CC_UNSPECIFIED_ERROR;
274 }
275 reply.read(res);
276
277 *data_len = responseData->length = res[0];
278 responseData->updateCount = res[1];
279
280 if ((*data_len == 0) || (*data_len >= 254))
281 {
282 phosphor::logging::log<level::ERR>(
283 "Data length send from service is invalid");
284 *data_len = 0;
285 return IPMI_CC_RESPONSE_ERROR;
286 }
287
288 *data_len += 2 * sizeof(uint8_t);
289 std::copy(&res[2], &res[*data_len], responseData->data);
290 return IPMI_CC_OK;
291}
292
293ipmi_ret_t cmd_region_write(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
294 ipmi_request_t request, ipmi_response_t response,
295 ipmi_data_len_t data_len, ipmi_context_t context)
296{
297 auto requestData = reinterpret_cast<const RegionWriteRequest*>(request);
298 uint8_t regionId = requestData->regionId - 1;
299 std::string res;
300 std::vector<uint8_t> writeData;
301 uint16_t index;
302 uint8_t tmp[255];
303
304 size_t minInputLen = &requestData->data[0] - &requestData->sessionId + 1;
305 if (*data_len < minInputLen)
306 { // this command need at least 6 bytes input
307 *data_len = 0;
308 return IPMI_CC_REQ_DATA_LEN_INVALID;
309 }
310
311 sdbusplus::message::variant<uint8_t, uint16_t> value;
312
313 *data_len = 0;
314
315 if (regionId >= maxMDRId)
316 {
317 phosphor::logging::log<level::ERR>("Invalid region");
318 return IPMI_CC_PARM_OUT_OF_RANGE;
319 }
320
Vernon Mauery15419dd2019-05-24 09:40:30 -0700321 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
322 std::string service = ipmi::getService(*bus, MDRV1_INTERFACE, MDRV1_PATH);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700323
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 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700335 if (regionLockUnlocked == std::get<uint8_t>(value))
Vernon Mauerya3702c12019-05-22 13:20:59 -0700336 {
337 return IPMI_CC_PARAMETER_NOT_SUPPORT_IN_PRESENT_STATE;
338 }
339
340 if (0 > sdplus_mdrv1_get_property("SessionId", value, service))
341 {
342 phosphor::logging::log<level::ERR>("Error getting sessionId");
343 return IPMI_CC_UNSPECIFIED_ERROR;
344 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700345 if (requestData->sessionId != std::get<uint8_t>(value))
Vernon Mauerya3702c12019-05-22 13:20:59 -0700346 {
347 return IPMI_CC_OEM_SET_IN_PROCESS;
348 }
349
350 std::copy(&(requestData->length), &(requestData->data[requestData->length]),
351 tmp);
352 writeData.push_back(regionId);
353 for (index = 0; index < minInputLen + requestData->length - 2; index++)
354 {
355 writeData.push_back(tmp[index]);
356 }
357
Vernon Mauery15419dd2019-05-24 09:40:30 -0700358 auto method = bus->new_method_call(service.c_str(), MDRV1_PATH,
359 MDRV1_INTERFACE, "RegionWrite");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700360
361 method.append(writeData);
362
Vernon Mauery15419dd2019-05-24 09:40:30 -0700363 auto reply = bus->call(method);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700364 if (reply.is_method_error())
365 {
366 phosphor::logging::log<level::ERR>(
367 "Error write region data",
368 phosphor::logging::entry("SERVICE=%s", service.c_str()),
369 phosphor::logging::entry("PATH=%s", MDRV1_PATH));
370 return IPMI_CC_UNSPECIFIED_ERROR;
371 }
372 reply.read(res);
373
374 if (res == "NoData")
375 {
376 return IPMI_CC_PARM_OUT_OF_RANGE;
377 }
378 else if (res != "Success")
379 {
380 phosphor::logging::log<level::ERR>(
381 "Error write region data, unexpected error");
382 return IPMI_CC_UNSPECIFIED_ERROR;
383 }
384
385 return IPMI_CC_OK;
386}
387
388ipmi_ret_t cmd_region_lock(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
389 ipmi_request_t request, ipmi_response_t response,
390 ipmi_data_len_t data_len, ipmi_context_t context)
391{
392 auto requestData = reinterpret_cast<const RegionLockRequest*>(request);
393 uint8_t regionId = requestData->regionId - 1;
394 sdbusplus::message::variant<uint8_t, uint16_t> value;
395 auto res = reinterpret_cast<uint8_t*>(response);
396 uint8_t lockResponse;
397
398 if (*data_len != sizeof(RegionLockRequest))
399 {
400 *data_len = 0;
401 return IPMI_CC_REQ_DATA_LEN_INVALID;
402 }
403
404 *data_len = 0;
405
406 if (regionId >= maxMDRId)
407 {
408 phosphor::logging::log<level::ERR>("Invalid region");
409 return IPMI_CC_PARM_OUT_OF_RANGE;
410 }
411
Vernon Mauery15419dd2019-05-24 09:40:30 -0700412 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
413 std::string service = ipmi::getService(*bus, MDRV1_INTERFACE, MDRV1_PATH);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700414
415 if (set_regionId(regionId, service) < 0)
416 {
417 phosphor::logging::log<level::ERR>("Error setting regionId");
418 return IPMI_CC_UNSPECIFIED_ERROR;
419 }
420
421 if (0 > sdplus_mdrv1_get_property("LockPolicy", value, service))
422 {
423 phosphor::logging::log<level::ERR>("Error getting lockPolicy");
424 return IPMI_CC_UNSPECIFIED_ERROR;
425 }
426 if (requestData->lockPolicy == regionLockUnlocked)
427 {
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700428 if (regionLockUnlocked == std::get<uint8_t>(value))
Vernon Mauerya3702c12019-05-22 13:20:59 -0700429 {
430 return IPMI_CC_PARAMETER_NOT_SUPPORT_IN_PRESENT_STATE;
431 }
432 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700433 if (regionLockUnlocked != std::get<uint8_t>(value))
Vernon Mauerya3702c12019-05-22 13:20:59 -0700434 {
435 if (0 > sdplus_mdrv1_get_property("SessionId", value, service))
436 {
437 phosphor::logging::log<level::ERR>("Error getting sessionId");
438 return IPMI_CC_UNSPECIFIED_ERROR;
439 }
Vernon Mauery8166c8d2019-05-23 11:22:30 -0700440 if (requestData->sessionId != std::get<uint8_t>(value))
Vernon Mauerya3702c12019-05-22 13:20:59 -0700441 {
442 if (requestData->lockPolicy != regionLockStrict)
443 {
444 return IPMI_CC_OEM_SET_IN_PROCESS;
445 }
446 }
447 }
Vernon Mauery15419dd2019-05-24 09:40:30 -0700448 auto method = bus->new_method_call(service.c_str(), MDRV1_PATH,
449 MDRV1_INTERFACE, "RegionLock");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700450
451 method.append(requestData->sessionId, regionId, requestData->lockPolicy,
452 requestData->msTimeout);
453
Vernon Mauery15419dd2019-05-24 09:40:30 -0700454 auto reply = bus->call(method);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700455 if (reply.is_method_error())
456 {
457 phosphor::logging::log<level::ERR>(
458 "Error lock region ",
459 phosphor::logging::entry("SERVICE=%s", service.c_str()),
460 phosphor::logging::entry("PATH=%s", MDRV1_PATH));
461 return IPMI_CC_UNSPECIFIED_ERROR;
462 }
463 reply.read(lockResponse);
464
465 *data_len = sizeof(lockResponse);
466 *res = lockResponse;
467 return IPMI_CC_OK;
468}
469
470static void register_netfn_smbios_functions(void)
471{
472 // MDR V1 Command
473 // <Get MDR Status Command>
474 ipmi_register_callback(NETFUN_INTEL_APP_OEM,
475 IPMI_NETFN_INTEL_OEM_APP_CMD::MDR_STATUS, NULL,
476 cmd_region_status, PRIVILEGE_OPERATOR);
477
478 // <Update Complete Status Command>
479 ipmi_register_callback(NETFUN_INTEL_APP_OEM,
480 IPMI_NETFN_INTEL_OEM_APP_CMD::MDR_COMPLETE, NULL,
481 cmd_region_complete, PRIVILEGE_OPERATOR);
482
483 // <Read MDR Command>
484 ipmi_register_callback(NETFUN_INTEL_APP_OEM,
485 IPMI_NETFN_INTEL_OEM_APP_CMD::MDR_READ, NULL,
486 cmd_region_read, PRIVILEGE_OPERATOR);
487
488 // <Write MDR Command>
489 ipmi_register_callback(NETFUN_INTEL_APP_OEM,
490 IPMI_NETFN_INTEL_OEM_APP_CMD::MDR_WRITE, NULL,
491 cmd_region_write, PRIVILEGE_OPERATOR);
492
493 // <Lock MDR Command>
494 ipmi_register_callback(NETFUN_INTEL_APP_OEM,
495 IPMI_NETFN_INTEL_OEM_APP_CMD::MDR_LOCK, NULL,
496 cmd_region_lock, PRIVILEGE_OPERATOR);
497}