blob: 413a3d3f3aa64aa1d7937c05bf5939fb511408b7 [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"
Chris Austen4d98c1e2015-10-13 14:33:50 -05004#include <host-ipmid/ipmid-api.h>
Vishwanatha Subbanna07655062017-07-14 20:31:57 +05305#include <host-ipmid/ipmid-host-cmd.hpp>
Adriana Kobylak187bfce2016-03-04 11:55:43 -06006#include <fstream>
Tom Joseph246bc0d2017-10-17 16:08:38 +05307#include <memory>
Chris Austen4d98c1e2015-10-13 14:33:50 -05008#include <stdio.h>
9#include <string.h>
Nan Li8a0807a2016-05-24 16:59:24 +080010#include <endian.h>
Vishwanatha Subbanna07655062017-07-14 20:31:57 +053011#include <functional>
12#include <systemd/sd-bus.h>
13#include <sdbusplus/bus.hpp>
14#include <host-interface.hpp>
Tom Joseph246bc0d2017-10-17 16:08:38 +053015#include <org/open_power/OCC/Metrics/error.hpp>
Chris Austen4d98c1e2015-10-13 14:33:50 -050016
17void register_netfn_oem_partial_esel() __attribute__((constructor));
18
Chris Austenfcafccf2016-02-19 23:15:57 -060019const char *g_esel_path = "/tmp/esel";
Chris Austen29a8e0f2015-10-30 11:44:38 -050020uint16_t g_record_id = 0x0001;
Tom Joseph246bc0d2017-10-17 16:08:38 +053021using namespace phosphor::logging;
22constexpr auto occMetricsType = 0xDD;
23
24std::string readESEL(const char* fileName)
25{
26 std::string content {};
27
28 std::ifstream handle(fileName);
29
30 if (handle.fail())
31 {
32 log<level::ERR>("Failed to open eSEL",
33 entry("FILENAME=%s", fileName));
34 return content;
35 }
36
37 handle.seekg(0, std::ios::end);
38 content.resize(handle.tellg());
39 handle.seekg(0, std::ios::beg);
40 handle.read(&content[0], content.size());
41 handle.close();
42
43 return content;
44}
45
46void createOCCLogEntry(const std::string& eSELData)
47{
48 // Each byte in eSEL is formatted as %02x with a space between bytes and
49 // insert '/0' at the end of the character array.
50 constexpr auto byteSeperator = 3;
51
52 std::unique_ptr<char[]> data(new char[
53 (eSELData.size() * byteSeperator) + 1]());
54
55 for (size_t i = 0; i < eSELData.size(); i++)
56 {
57 sprintf(&data[i * byteSeperator], "%02x ", eSELData[i]);
58 }
59 data[eSELData.size() * byteSeperator] = '\0';
60
61 using error = sdbusplus::org::open_power::OCC::Metrics::Error::Event;
62 using metadata = org::open_power::OCC::Metrics::Event;
63
64 report<error>(metadata::ESEL(data.get()));
65}
66
Chris Austen4d98c1e2015-10-13 14:33:50 -050067
Chris Austen4d98c1e2015-10-13 14:33:50 -050068///////////////////////////////////////////////////////////////////////////////
Patrick Williams24fa5a92015-10-30 14:53:57 -050069// For the First partial add eSEL the SEL Record ID and offset
70// value should be 0x0000. The extended data needs to be in
71// the form of an IPMI SEL Event Record, with Event sensor type
72// of 0xDF and Event Message format of 0x04. The returned
Chris Austen4d98c1e2015-10-13 14:33:50 -050073// Record ID should be used for all partial eSEL adds.
74//
Chris Austenba54afb2016-02-19 23:18:42 -060075// This function creates a /tmp/esel file to store the
Chris Austen4d98c1e2015-10-13 14:33:50 -050076// incoming partial esel. It is the role of some other
Patrick Williams24fa5a92015-10-30 14:53:57 -050077// function to commit the error log in to long term
78// storage. Likely via the ipmi add_sel command.
Chris Austen4d98c1e2015-10-13 14:33:50 -050079///////////////////////////////////////////////////////////////////////////////
Patrick Williams24fa5a92015-10-30 14:53:57 -050080ipmi_ret_t ipmi_ibm_oem_partial_esel(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Chris Austen17160ce2015-10-30 11:44:38 -050081 ipmi_request_t request, ipmi_response_t response,
82 ipmi_data_len_t data_len, ipmi_context_t context)
Chris Austen4d98c1e2015-10-13 14:33:50 -050083{
Nan Li8a0807a2016-05-24 16:59:24 +080084 uint8_t *reqptr = (uint8_t *) request;
85 esel_request_t esel_req;
Chris Austenba54afb2016-02-19 23:18:42 -060086 FILE *fp;
Nan Lib6c4c562016-03-30 17:06:13 +080087 int r = 0;
Chris Austenba54afb2016-02-19 23:18:42 -060088 uint8_t rlen;
89 ipmi_ret_t rc = IPMI_CC_OK;
90 const char *pio;
Chris Austen4d98c1e2015-10-13 14:33:50 -050091
Nan Li8a0807a2016-05-24 16:59:24 +080092 esel_req.resid = le16toh((((uint16_t) reqptr[1]) << 8) + reqptr[0]);
93 esel_req.selrecord = le16toh((((uint16_t) reqptr[3]) << 8) + reqptr[2]);
94 esel_req.offset = le16toh((((uint16_t) reqptr[5]) << 8) + reqptr[4]);
95 esel_req.progress = reqptr[6];
Nan Lib6c4c562016-03-30 17:06:13 +080096
Nan Li8a0807a2016-05-24 16:59:24 +080097 uint16_t used_res_id = get_sel_reserve_id();
Nan Lib6c4c562016-03-30 17:06:13 +080098
99 // According to IPMI spec, Reservation ID must be checked.
Nan Li8a0807a2016-05-24 16:59:24 +0800100 if ( used_res_id != esel_req.resid ) {
Nan Lib6c4c562016-03-30 17:06:13 +0800101 // 0xc5 means Reservation Cancelled or Invalid Reservation ID.
102 printf("Used Reservation ID = %d\n", used_res_id);
103 rc = IPMI_CC_INVALID_RESERVATION_ID;
104
Andrew Geisslerd9296052017-06-15 14:57:25 -0500105 // clean g_esel_path.
Nan Lib6c4c562016-03-30 17:06:13 +0800106 r = remove(g_esel_path);
107 if(r < 0)
108 fprintf(stderr, "Error deleting %s\n", g_esel_path);
109
110 return rc;
111 }
112
113 // OpenPOWER Host Interface spec says if RecordID and Offset are
Chris Austenba54afb2016-02-19 23:18:42 -0600114 // 0 then then this is a new request
Nan Li8a0807a2016-05-24 16:59:24 +0800115 if (!esel_req.selrecord && !esel_req.offset)
Chris Austenba54afb2016-02-19 23:18:42 -0600116 pio = "wb";
117 else
118 pio = "rb+";
Chris Austen4d98c1e2015-10-13 14:33:50 -0500119
Chris Austenba54afb2016-02-19 23:18:42 -0600120 rlen = (*data_len) - (uint8_t) (sizeof(esel_request_t));
Chris Austen4d98c1e2015-10-13 14:33:50 -0500121
Chris Austenba54afb2016-02-19 23:18:42 -0600122 if ((fp = fopen(g_esel_path, pio)) != NULL) {
Nan Li8a0807a2016-05-24 16:59:24 +0800123 fseek(fp, esel_req.offset, SEEK_SET);
124 fwrite(reqptr+(uint8_t) (sizeof(esel_request_t)), rlen, 1, fp);
Chris Austenba54afb2016-02-19 23:18:42 -0600125 fclose(fp);
Chris Austen4d98c1e2015-10-13 14:33:50 -0500126
Chris Austenba54afb2016-02-19 23:18:42 -0600127 *data_len = sizeof(g_record_id);
128 memcpy(response, &g_record_id, *data_len);
129 } else {
130 fprintf(stderr, "Error trying to perform %s for esel%s\n",pio, g_esel_path);
131 rc = IPMI_CC_INVALID;
132 *data_len = 0;
133 }
Chris Austen4d98c1e2015-10-13 14:33:50 -0500134
Tom Joseph246bc0d2017-10-17 16:08:38 +0530135 // The first bit presents that this is the last partial packet
Chris Austenba54afb2016-02-19 23:18:42 -0600136 // coming down. If that is the case advance the record id so we
137 // don't overlap logs. This allows anyone to establish a log
138 // directory system.
Tom Joseph246bc0d2017-10-17 16:08:38 +0530139 if (esel_req.progress & 1 )
140 {
141 g_record_id++;
Chris Austen4d98c1e2015-10-13 14:33:50 -0500142
Tom Joseph246bc0d2017-10-17 16:08:38 +0530143 auto eSELData = readESEL(g_esel_path);
144
145 if (eSELData.empty())
146 {
147 return IPMI_CC_UNSPECIFIED_ERROR;
148 }
149
150 // If the eSEL record type is OCC metrics, then create the OCC log
151 // entry.
152 if (eSELData[2] == occMetricsType)
153 {
154 createOCCLogEntry(eSELData);
155 }
156 }
157
158 return rc;
Chris Austen4d98c1e2015-10-13 14:33:50 -0500159}
160
Adriana Kobylak187bfce2016-03-04 11:55:43 -0600161// Prepare for FW Update.
162// Execute needed commands to prepare the system for a fw update from the host.
163ipmi_ret_t ipmi_ibm_oem_prep_fw_update(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
164 ipmi_request_t request, ipmi_response_t response,
165 ipmi_data_len_t data_len, ipmi_context_t context)
166{
167 ipmi_ret_t ipmi_rc = IPMI_CC_OK;
168 *data_len = 0;
169
170 int rc = 0;
171 std::ofstream rwfs_file;
172 const char *busname = "org.openbmc.control.Bmc";
173 const char *objname = "/org/openbmc/control/bmc0";
174 const char *iface = "org.openbmc.control.Bmc";
175 sd_bus *bus = ipmid_get_sd_bus_connection();
176 sd_bus_message *reply = NULL;
177 sd_bus_error error = SD_BUS_ERROR_NULL;
178 int r = 0;
179
180 // Set one time flag
181 rc = system("fw_setenv openbmconce copy-files-to-ram copy-base-filesystem-to-ram");
182 rc = WEXITSTATUS(rc);
183 if (rc != 0) {
184 fprintf(stderr, "fw_setenv openbmconce failed with rc=%d\n", rc);
Andrew Geisslerd9296052017-06-15 14:57:25 -0500185 return IPMI_CC_UNSPECIFIED_ERROR;
Adriana Kobylak187bfce2016-03-04 11:55:43 -0600186 }
187
188 // Touch the image-rwfs file to perform an empty update to force the save
189 // in case we're already in ram and the flash is the same causing the ram files
190 // to not be copied back to flash
191 rwfs_file.open("/run/initramfs/image-rwfs", std::ofstream::out | std::ofstream::app);
192 rwfs_file.close();
193
194 // Reboot the BMC for settings to take effect
195 r = sd_bus_call_method(bus, busname, objname, iface,
196 "warmReset", &error, &reply, NULL);
197 if (r < 0) {
198 fprintf(stderr, "Failed to reset BMC: %s\n", strerror(-r));
199 return -1;
200 }
201 printf("Warning: BMC is going down for reboot!\n");
202 sd_bus_error_free(&error);
203 reply = sd_bus_message_unref(reply);
204
205 return ipmi_rc;
206}
Chris Austen4d98c1e2015-10-13 14:33:50 -0500207
Vishwanatha Subbanna07655062017-07-14 20:31:57 +0530208namespace {
209// Storage to keep the object alive during process life
210std::unique_ptr<open_power::host::command::Host> opHost
211 __attribute__((init_priority(101)));
212std::unique_ptr<sdbusplus::server::manager::manager> objManager
213 __attribute__((init_priority(101)));
214}
215
Chris Austen4d98c1e2015-10-13 14:33:50 -0500216void register_netfn_oem_partial_esel()
217{
Tom Joseph64994ea2017-10-25 09:05:43 +0530218 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_IBM_OEM, IPMI_CMD_PESEL);
219 ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_PESEL, NULL, ipmi_ibm_oem_partial_esel,
Tomcbfd6ec2016-09-14 17:45:55 +0530220 SYSTEM_INTERFACE);
Adriana Kobylak187bfce2016-03-04 11:55:43 -0600221
222 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", NETFUN_OEM, IPMI_CMD_PREP_FW_UPDATE);
Tomcbfd6ec2016-09-14 17:45:55 +0530223 ipmi_register_callback(NETFUN_OEM, IPMI_CMD_PREP_FW_UPDATE, NULL, ipmi_ibm_oem_prep_fw_update,
224 SYSTEM_INTERFACE);
Adriana Kobylak187bfce2016-03-04 11:55:43 -0600225
Vishwanatha Subbanna07655062017-07-14 20:31:57 +0530226 // Create new object on the bus
227 auto objPath = std::string{CONTROL_HOST_OBJ_MGR} + '/' + HOST_NAME + '0';
228
229 // Add sdbusplus ObjectManager.
230 auto& sdbusPlusHandler = ipmid_get_sdbus_plus_handler();
231 objManager = std::make_unique<sdbusplus::server::manager::manager>(
232 *sdbusPlusHandler,
233 CONTROL_HOST_OBJ_MGR);
234
235 opHost = std::make_unique<open_power::host::command::Host>(
236 *sdbusPlusHandler, objPath.c_str());
237
238 // Service for this is provided by phosphor layer systemcmdintf
239 // and this will be as part of that.
Tomcbfd6ec2016-09-14 17:45:55 +0530240 return;
Chris Austen4d98c1e2015-10-13 14:33:50 -0500241}