blob: c793941a05792d5c33270943d01561b8a36bd11d [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 Lib6c4c562016-03-30 17:06:13 +080098 // According to IPMI spec, Reservation ID must be checked.
Jason M. Billsa9915b72018-10-09 09:23:49 -070099 if ( !checkSELReservation(esel_req.resid) ) {
Nan Lib6c4c562016-03-30 17:06:13 +0800100 // 0xc5 means Reservation Cancelled or Invalid Reservation ID.
Jason M. Billsa9915b72018-10-09 09:23:49 -0700101 printf("Used Reservation ID = %d\n", esel_req.resid);
Nan Lib6c4c562016-03-30 17:06:13 +0800102 rc = IPMI_CC_INVALID_RESERVATION_ID;
103
Andrew Geisslerd9296052017-06-15 14:57:25 -0500104 // clean g_esel_path.
Nan Lib6c4c562016-03-30 17:06:13 +0800105 r = remove(g_esel_path);
106 if(r < 0)
107 fprintf(stderr, "Error deleting %s\n", g_esel_path);
108
109 return rc;
110 }
111
112 // OpenPOWER Host Interface spec says if RecordID and Offset are
Chris Austenba54afb2016-02-19 23:18:42 -0600113 // 0 then then this is a new request
Nan Li8a0807a2016-05-24 16:59:24 +0800114 if (!esel_req.selrecord && !esel_req.offset)
Chris Austenba54afb2016-02-19 23:18:42 -0600115 pio = "wb";
116 else
117 pio = "rb+";
Chris Austen4d98c1e2015-10-13 14:33:50 -0500118
Chris Austenba54afb2016-02-19 23:18:42 -0600119 rlen = (*data_len) - (uint8_t) (sizeof(esel_request_t));
Chris Austen4d98c1e2015-10-13 14:33:50 -0500120
Chris Austenba54afb2016-02-19 23:18:42 -0600121 if ((fp = fopen(g_esel_path, pio)) != NULL) {
Nan Li8a0807a2016-05-24 16:59:24 +0800122 fseek(fp, esel_req.offset, SEEK_SET);
123 fwrite(reqptr+(uint8_t) (sizeof(esel_request_t)), rlen, 1, fp);
Chris Austenba54afb2016-02-19 23:18:42 -0600124 fclose(fp);
Chris Austen4d98c1e2015-10-13 14:33:50 -0500125
Chris Austenba54afb2016-02-19 23:18:42 -0600126 *data_len = sizeof(g_record_id);
127 memcpy(response, &g_record_id, *data_len);
128 } else {
129 fprintf(stderr, "Error trying to perform %s for esel%s\n",pio, g_esel_path);
130 rc = IPMI_CC_INVALID;
131 *data_len = 0;
132 }
Chris Austen4d98c1e2015-10-13 14:33:50 -0500133
Tom Joseph246bc0d2017-10-17 16:08:38 +0530134 // The first bit presents that this is the last partial packet
Chris Austenba54afb2016-02-19 23:18:42 -0600135 // coming down. If that is the case advance the record id so we
136 // don't overlap logs. This allows anyone to establish a log
137 // directory system.
Tom Joseph246bc0d2017-10-17 16:08:38 +0530138 if (esel_req.progress & 1 )
139 {
140 g_record_id++;
Chris Austen4d98c1e2015-10-13 14:33:50 -0500141
Tom Joseph246bc0d2017-10-17 16:08:38 +0530142 auto eSELData = readESEL(g_esel_path);
143
144 if (eSELData.empty())
145 {
146 return IPMI_CC_UNSPECIFIED_ERROR;
147 }
148
149 // If the eSEL record type is OCC metrics, then create the OCC log
150 // entry.
151 if (eSELData[2] == occMetricsType)
152 {
153 createOCCLogEntry(eSELData);
154 }
155 }
156
157 return rc;
Chris Austen4d98c1e2015-10-13 14:33:50 -0500158}
159
Adriana Kobylak187bfce2016-03-04 11:55:43 -0600160// Prepare for FW Update.
161// Execute needed commands to prepare the system for a fw update from the host.
162ipmi_ret_t ipmi_ibm_oem_prep_fw_update(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
163 ipmi_request_t request, ipmi_response_t response,
164 ipmi_data_len_t data_len, ipmi_context_t context)
165{
166 ipmi_ret_t ipmi_rc = IPMI_CC_OK;
167 *data_len = 0;
168
169 int rc = 0;
170 std::ofstream rwfs_file;
171 const char *busname = "org.openbmc.control.Bmc";
172 const char *objname = "/org/openbmc/control/bmc0";
173 const char *iface = "org.openbmc.control.Bmc";
174 sd_bus *bus = ipmid_get_sd_bus_connection();
175 sd_bus_message *reply = NULL;
176 sd_bus_error error = SD_BUS_ERROR_NULL;
177 int r = 0;
178
179 // Set one time flag
180 rc = system("fw_setenv openbmconce copy-files-to-ram copy-base-filesystem-to-ram");
181 rc = WEXITSTATUS(rc);
182 if (rc != 0) {
183 fprintf(stderr, "fw_setenv openbmconce failed with rc=%d\n", rc);
Andrew Geisslerd9296052017-06-15 14:57:25 -0500184 return IPMI_CC_UNSPECIFIED_ERROR;
Adriana Kobylak187bfce2016-03-04 11:55:43 -0600185 }
186
187 // Touch the image-rwfs file to perform an empty update to force the save
188 // in case we're already in ram and the flash is the same causing the ram files
189 // to not be copied back to flash
190 rwfs_file.open("/run/initramfs/image-rwfs", std::ofstream::out | std::ofstream::app);
191 rwfs_file.close();
192
193 // Reboot the BMC for settings to take effect
194 r = sd_bus_call_method(bus, busname, objname, iface,
195 "warmReset", &error, &reply, NULL);
196 if (r < 0) {
197 fprintf(stderr, "Failed to reset BMC: %s\n", strerror(-r));
198 return -1;
199 }
200 printf("Warning: BMC is going down for reboot!\n");
201 sd_bus_error_free(&error);
202 reply = sd_bus_message_unref(reply);
203
204 return ipmi_rc;
205}
Chris Austen4d98c1e2015-10-13 14:33:50 -0500206
Matt Spinler32884712018-09-28 13:56:20 -0500207ipmi_ret_t ipmi_ibm_oem_reset_bmc_auth(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
208 ipmi_request_t request,
209 ipmi_response_t response,
210 ipmi_data_len_t data_len,
211 ipmi_context_t context)
Adriana Kobylak81e23102018-08-09 14:44:19 -0500212{
Matt Spinler34aca012018-09-25 11:21:06 -0500213 ipmi_ret_t rc;
214
215 rc = local::users::enableUsers();
216
217 return rc;
Adriana Kobylak81e23102018-08-09 14:44:19 -0500218}
219
Vishwanatha Subbanna07655062017-07-14 20:31:57 +0530220namespace {
221// Storage to keep the object alive during process life
222std::unique_ptr<open_power::host::command::Host> opHost
223 __attribute__((init_priority(101)));
224std::unique_ptr<sdbusplus::server::manager::manager> objManager
225 __attribute__((init_priority(101)));
226}
227
Andrew Jeffery8fb3f032018-07-27 16:45:44 +0930228void register_netfn_ibm_oem_commands()
Chris Austen4d98c1e2015-10-13 14:33:50 -0500229{
Tom Joseph64994ea2017-10-25 09:05:43 +0530230 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_IBM_OEM, IPMI_CMD_PESEL);
231 ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_PESEL, NULL, ipmi_ibm_oem_partial_esel,
Tomcbfd6ec2016-09-14 17:45:55 +0530232 SYSTEM_INTERFACE);
Adriana Kobylak187bfce2016-03-04 11:55:43 -0600233
234 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", NETFUN_OEM, IPMI_CMD_PREP_FW_UPDATE);
Tomcbfd6ec2016-09-14 17:45:55 +0530235 ipmi_register_callback(NETFUN_OEM, IPMI_CMD_PREP_FW_UPDATE, NULL, ipmi_ibm_oem_prep_fw_update,
236 SYSTEM_INTERFACE);
Adriana Kobylak187bfce2016-03-04 11:55:43 -0600237
Matt Spinler32884712018-09-28 13:56:20 -0500238 ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_RESET_BMC_AUTH, NULL,
239 ipmi_ibm_oem_reset_bmc_auth, SYSTEM_INTERFACE);
Adriana Kobylak81e23102018-08-09 14:44:19 -0500240
Vishwanatha Subbanna07655062017-07-14 20:31:57 +0530241 // Create new object on the bus
242 auto objPath = std::string{CONTROL_HOST_OBJ_MGR} + '/' + HOST_NAME + '0';
243
244 // Add sdbusplus ObjectManager.
245 auto& sdbusPlusHandler = ipmid_get_sdbus_plus_handler();
246 objManager = std::make_unique<sdbusplus::server::manager::manager>(
247 *sdbusPlusHandler,
248 CONTROL_HOST_OBJ_MGR);
249
250 opHost = std::make_unique<open_power::host::command::Host>(
251 *sdbusPlusHandler, objPath.c_str());
252
253 // Service for this is provided by phosphor layer systemcmdintf
254 // and this will be as part of that.
Tomcbfd6ec2016-09-14 17:45:55 +0530255 return;
Chris Austen4d98c1e2015-10-13 14:33:50 -0500256}