blob: 99081d7a00220b8b25b8f658f50f2a738acef830 [file] [log] [blame]
Matthew Barth70dbc582016-09-20 10:16:52 -05001#include "oemhandler.hpp"
Vishwanatha Subbanna07655062017-07-14 20:31:57 +05302#include "config.h"
Tom Joseph246bc0d2017-10-17 16:08:38 +05303#include "elog-errors.hpp"
Matt Spinler34aca012018-09-25 11:21:06 -05004#include "local_users.hpp"
Chris Austen4d98c1e2015-10-13 14:33:50 -05005#include <host-ipmid/ipmid-api.h>
Vishwanatha Subbanna07655062017-07-14 20:31:57 +05306#include <host-ipmid/ipmid-host-cmd.hpp>
Adriana Kobylak187bfce2016-03-04 11:55:43 -06007#include <fstream>
Tom Joseph246bc0d2017-10-17 16:08:38 +05308#include <memory>
Chris Austen4d98c1e2015-10-13 14:33:50 -05009#include <stdio.h>
10#include <string.h>
Nan Li8a0807a2016-05-24 16:59:24 +080011#include <endian.h>
Vishwanatha Subbanna07655062017-07-14 20:31:57 +053012#include <functional>
13#include <systemd/sd-bus.h>
14#include <sdbusplus/bus.hpp>
15#include <host-interface.hpp>
Tom Joseph246bc0d2017-10-17 16:08:38 +053016#include <org/open_power/OCC/Metrics/error.hpp>
Chris Austen4d98c1e2015-10-13 14:33:50 -050017
Andrew Jeffery8fb3f032018-07-27 16:45:44 +093018void register_netfn_ibm_oem_commands() __attribute__((constructor));
Chris Austen4d98c1e2015-10-13 14:33:50 -050019
Chris Austenfcafccf2016-02-19 23:15:57 -060020const char *g_esel_path = "/tmp/esel";
Chris Austen29a8e0f2015-10-30 11:44:38 -050021uint16_t g_record_id = 0x0001;
Tom Joseph246bc0d2017-10-17 16:08:38 +053022using namespace phosphor::logging;
23constexpr auto occMetricsType = 0xDD;
24
25std::string readESEL(const char* fileName)
26{
27 std::string content {};
28
29 std::ifstream handle(fileName);
30
31 if (handle.fail())
32 {
33 log<level::ERR>("Failed to open eSEL",
34 entry("FILENAME=%s", fileName));
35 return content;
36 }
37
38 handle.seekg(0, std::ios::end);
39 content.resize(handle.tellg());
40 handle.seekg(0, std::ios::beg);
41 handle.read(&content[0], content.size());
42 handle.close();
43
44 return content;
45}
46
47void createOCCLogEntry(const std::string& eSELData)
48{
49 // Each byte in eSEL is formatted as %02x with a space between bytes and
50 // insert '/0' at the end of the character array.
51 constexpr auto byteSeperator = 3;
52
53 std::unique_ptr<char[]> data(new char[
54 (eSELData.size() * byteSeperator) + 1]());
55
56 for (size_t i = 0; i < eSELData.size(); i++)
57 {
58 sprintf(&data[i * byteSeperator], "%02x ", eSELData[i]);
59 }
60 data[eSELData.size() * byteSeperator] = '\0';
61
62 using error = sdbusplus::org::open_power::OCC::Metrics::Error::Event;
63 using metadata = org::open_power::OCC::Metrics::Event;
64
65 report<error>(metadata::ESEL(data.get()));
66}
67
Chris Austen4d98c1e2015-10-13 14:33:50 -050068
Chris Austen4d98c1e2015-10-13 14:33:50 -050069///////////////////////////////////////////////////////////////////////////////
Patrick Williams24fa5a92015-10-30 14:53:57 -050070// For the First partial add eSEL the SEL Record ID and offset
71// value should be 0x0000. The extended data needs to be in
72// the form of an IPMI SEL Event Record, with Event sensor type
73// of 0xDF and Event Message format of 0x04. The returned
Chris Austen4d98c1e2015-10-13 14:33:50 -050074// Record ID should be used for all partial eSEL adds.
75//
Chris Austenba54afb2016-02-19 23:18:42 -060076// This function creates a /tmp/esel file to store the
Chris Austen4d98c1e2015-10-13 14:33:50 -050077// incoming partial esel. It is the role of some other
Patrick Williams24fa5a92015-10-30 14:53:57 -050078// function to commit the error log in to long term
79// storage. Likely via the ipmi add_sel command.
Chris Austen4d98c1e2015-10-13 14:33:50 -050080///////////////////////////////////////////////////////////////////////////////
Patrick Williams24fa5a92015-10-30 14:53:57 -050081ipmi_ret_t ipmi_ibm_oem_partial_esel(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Chris Austen17160ce2015-10-30 11:44:38 -050082 ipmi_request_t request, ipmi_response_t response,
83 ipmi_data_len_t data_len, ipmi_context_t context)
Chris Austen4d98c1e2015-10-13 14:33:50 -050084{
Nan Li8a0807a2016-05-24 16:59:24 +080085 uint8_t *reqptr = (uint8_t *) request;
86 esel_request_t esel_req;
Chris Austenba54afb2016-02-19 23:18:42 -060087 FILE *fp;
Nan Lib6c4c562016-03-30 17:06:13 +080088 int r = 0;
Chris Austenba54afb2016-02-19 23:18:42 -060089 uint8_t rlen;
90 ipmi_ret_t rc = IPMI_CC_OK;
91 const char *pio;
Chris Austen4d98c1e2015-10-13 14:33:50 -050092
Nan Li8a0807a2016-05-24 16:59:24 +080093 esel_req.resid = le16toh((((uint16_t) reqptr[1]) << 8) + reqptr[0]);
94 esel_req.selrecord = le16toh((((uint16_t) reqptr[3]) << 8) + reqptr[2]);
95 esel_req.offset = le16toh((((uint16_t) reqptr[5]) << 8) + reqptr[4]);
96 esel_req.progress = reqptr[6];
Nan Lib6c4c562016-03-30 17:06:13 +080097
Nan Li8a0807a2016-05-24 16:59:24 +080098 uint16_t used_res_id = get_sel_reserve_id();
Nan Lib6c4c562016-03-30 17:06:13 +080099
100 // According to IPMI spec, Reservation ID must be checked.
Nan Li8a0807a2016-05-24 16:59:24 +0800101 if ( used_res_id != esel_req.resid ) {
Nan Lib6c4c562016-03-30 17:06:13 +0800102 // 0xc5 means Reservation Cancelled or Invalid Reservation ID.
103 printf("Used Reservation ID = %d\n", used_res_id);
104 rc = IPMI_CC_INVALID_RESERVATION_ID;
105
Andrew Geisslerd9296052017-06-15 14:57:25 -0500106 // clean g_esel_path.
Nan Lib6c4c562016-03-30 17:06:13 +0800107 r = remove(g_esel_path);
108 if(r < 0)
109 fprintf(stderr, "Error deleting %s\n", g_esel_path);
110
111 return rc;
112 }
113
114 // OpenPOWER Host Interface spec says if RecordID and Offset are
Chris Austenba54afb2016-02-19 23:18:42 -0600115 // 0 then then this is a new request
Nan Li8a0807a2016-05-24 16:59:24 +0800116 if (!esel_req.selrecord && !esel_req.offset)
Chris Austenba54afb2016-02-19 23:18:42 -0600117 pio = "wb";
118 else
119 pio = "rb+";
Chris Austen4d98c1e2015-10-13 14:33:50 -0500120
Chris Austenba54afb2016-02-19 23:18:42 -0600121 rlen = (*data_len) - (uint8_t) (sizeof(esel_request_t));
Chris Austen4d98c1e2015-10-13 14:33:50 -0500122
Chris Austenba54afb2016-02-19 23:18:42 -0600123 if ((fp = fopen(g_esel_path, pio)) != NULL) {
Nan Li8a0807a2016-05-24 16:59:24 +0800124 fseek(fp, esel_req.offset, SEEK_SET);
125 fwrite(reqptr+(uint8_t) (sizeof(esel_request_t)), rlen, 1, fp);
Chris Austenba54afb2016-02-19 23:18:42 -0600126 fclose(fp);
Chris Austen4d98c1e2015-10-13 14:33:50 -0500127
Chris Austenba54afb2016-02-19 23:18:42 -0600128 *data_len = sizeof(g_record_id);
129 memcpy(response, &g_record_id, *data_len);
130 } else {
131 fprintf(stderr, "Error trying to perform %s for esel%s\n",pio, g_esel_path);
132 rc = IPMI_CC_INVALID;
133 *data_len = 0;
134 }
Chris Austen4d98c1e2015-10-13 14:33:50 -0500135
Tom Joseph246bc0d2017-10-17 16:08:38 +0530136 // The first bit presents that this is the last partial packet
Chris Austenba54afb2016-02-19 23:18:42 -0600137 // coming down. If that is the case advance the record id so we
138 // don't overlap logs. This allows anyone to establish a log
139 // directory system.
Tom Joseph246bc0d2017-10-17 16:08:38 +0530140 if (esel_req.progress & 1 )
141 {
142 g_record_id++;
Chris Austen4d98c1e2015-10-13 14:33:50 -0500143
Tom Joseph246bc0d2017-10-17 16:08:38 +0530144 auto eSELData = readESEL(g_esel_path);
145
146 if (eSELData.empty())
147 {
148 return IPMI_CC_UNSPECIFIED_ERROR;
149 }
150
151 // If the eSEL record type is OCC metrics, then create the OCC log
152 // entry.
153 if (eSELData[2] == occMetricsType)
154 {
155 createOCCLogEntry(eSELData);
156 }
157 }
158
159 return rc;
Chris Austen4d98c1e2015-10-13 14:33:50 -0500160}
161
Adriana Kobylak187bfce2016-03-04 11:55:43 -0600162// Prepare for FW Update.
163// Execute needed commands to prepare the system for a fw update from the host.
164ipmi_ret_t ipmi_ibm_oem_prep_fw_update(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
165 ipmi_request_t request, ipmi_response_t response,
166 ipmi_data_len_t data_len, ipmi_context_t context)
167{
168 ipmi_ret_t ipmi_rc = IPMI_CC_OK;
169 *data_len = 0;
170
171 int rc = 0;
172 std::ofstream rwfs_file;
173 const char *busname = "org.openbmc.control.Bmc";
174 const char *objname = "/org/openbmc/control/bmc0";
175 const char *iface = "org.openbmc.control.Bmc";
176 sd_bus *bus = ipmid_get_sd_bus_connection();
177 sd_bus_message *reply = NULL;
178 sd_bus_error error = SD_BUS_ERROR_NULL;
179 int r = 0;
180
181 // Set one time flag
182 rc = system("fw_setenv openbmconce copy-files-to-ram copy-base-filesystem-to-ram");
183 rc = WEXITSTATUS(rc);
184 if (rc != 0) {
185 fprintf(stderr, "fw_setenv openbmconce failed with rc=%d\n", rc);
Andrew Geisslerd9296052017-06-15 14:57:25 -0500186 return IPMI_CC_UNSPECIFIED_ERROR;
Adriana Kobylak187bfce2016-03-04 11:55:43 -0600187 }
188
189 // Touch the image-rwfs file to perform an empty update to force the save
190 // in case we're already in ram and the flash is the same causing the ram files
191 // to not be copied back to flash
192 rwfs_file.open("/run/initramfs/image-rwfs", std::ofstream::out | std::ofstream::app);
193 rwfs_file.close();
194
195 // Reboot the BMC for settings to take effect
196 r = sd_bus_call_method(bus, busname, objname, iface,
197 "warmReset", &error, &reply, NULL);
198 if (r < 0) {
199 fprintf(stderr, "Failed to reset BMC: %s\n", strerror(-r));
200 return -1;
201 }
202 printf("Warning: BMC is going down for reboot!\n");
203 sd_bus_error_free(&error);
204 reply = sd_bus_message_unref(reply);
205
206 return ipmi_rc;
207}
Chris Austen4d98c1e2015-10-13 14:33:50 -0500208
Matt Spinler32884712018-09-28 13:56:20 -0500209ipmi_ret_t ipmi_ibm_oem_reset_bmc_auth(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
210 ipmi_request_t request,
211 ipmi_response_t response,
212 ipmi_data_len_t data_len,
213 ipmi_context_t context)
Adriana Kobylak81e23102018-08-09 14:44:19 -0500214{
Matt Spinler34aca012018-09-25 11:21:06 -0500215 ipmi_ret_t rc;
216
217 rc = local::users::enableUsers();
218
219 return rc;
Adriana Kobylak81e23102018-08-09 14:44:19 -0500220}
221
Vishwanatha Subbanna07655062017-07-14 20:31:57 +0530222namespace {
223// Storage to keep the object alive during process life
224std::unique_ptr<open_power::host::command::Host> opHost
225 __attribute__((init_priority(101)));
226std::unique_ptr<sdbusplus::server::manager::manager> objManager
227 __attribute__((init_priority(101)));
228}
229
Andrew Jeffery8fb3f032018-07-27 16:45:44 +0930230void register_netfn_ibm_oem_commands()
Chris Austen4d98c1e2015-10-13 14:33:50 -0500231{
Tom Joseph64994ea2017-10-25 09:05:43 +0530232 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_IBM_OEM, IPMI_CMD_PESEL);
233 ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_PESEL, NULL, ipmi_ibm_oem_partial_esel,
Tomcbfd6ec2016-09-14 17:45:55 +0530234 SYSTEM_INTERFACE);
Adriana Kobylak187bfce2016-03-04 11:55:43 -0600235
236 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", NETFUN_OEM, IPMI_CMD_PREP_FW_UPDATE);
Tomcbfd6ec2016-09-14 17:45:55 +0530237 ipmi_register_callback(NETFUN_OEM, IPMI_CMD_PREP_FW_UPDATE, NULL, ipmi_ibm_oem_prep_fw_update,
238 SYSTEM_INTERFACE);
Adriana Kobylak187bfce2016-03-04 11:55:43 -0600239
Matt Spinler32884712018-09-28 13:56:20 -0500240 ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_RESET_BMC_AUTH, NULL,
241 ipmi_ibm_oem_reset_bmc_auth, SYSTEM_INTERFACE);
Adriana Kobylak81e23102018-08-09 14:44:19 -0500242
Vishwanatha Subbanna07655062017-07-14 20:31:57 +0530243 // Create new object on the bus
244 auto objPath = std::string{CONTROL_HOST_OBJ_MGR} + '/' + HOST_NAME + '0';
245
246 // Add sdbusplus ObjectManager.
247 auto& sdbusPlusHandler = ipmid_get_sdbus_plus_handler();
248 objManager = std::make_unique<sdbusplus::server::manager::manager>(
249 *sdbusPlusHandler,
250 CONTROL_HOST_OBJ_MGR);
251
252 opHost = std::make_unique<open_power::host::command::Host>(
253 *sdbusPlusHandler, objPath.c_str());
254
255 // Service for this is provided by phosphor layer systemcmdintf
256 // and this will be as part of that.
Tomcbfd6ec2016-09-14 17:45:55 +0530257 return;
Chris Austen4d98c1e2015-10-13 14:33:50 -0500258}