blob: ba4d11bb475afe6343eba6416bc009d8b3aab1f9 [file] [log] [blame]
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +09301// SPDX-License-Identifier: Apache-2.0
2// Copyright (C) 2018 IBM Corp.
3
4#include "config.h"
5
6#include "hiomap.hpp"
7
8#include <endian.h>
9#include <host-ipmid/ipmid-api.h>
Andrew Jeffery619207d2018-11-22 13:09:17 +110010#include <signal.h>
11#include <string.h>
12#include <systemd/sd-bus.h>
13#include <systemd/sd-event.h>
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +093014
Andrew Jeffery619207d2018-11-22 13:09:17 +110015#include <cassert>
Patrick Ventureee701962018-10-31 14:51:37 -070016#include <cstring>
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +093017#include <fstream>
Andrew Jeffery0a3358e2018-08-21 10:42:09 +093018#include <functional>
19#include <host-ipmid/ipmid-host-cmd-utils.hpp>
20#include <host-ipmid/ipmid-host-cmd.hpp>
21#include <iostream>
Patrick Ventureee701962018-10-31 14:51:37 -070022#include <map>
Andrew Jeffery0a3358e2018-08-21 10:42:09 +093023#include <phosphor-logging/log.hpp>
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +093024#include <sdbusplus/bus.hpp>
Andrew Jeffery0a3358e2018-08-21 10:42:09 +093025#include <sdbusplus/bus/match.hpp>
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +093026#include <sdbusplus/exception.hpp>
Patrick Ventureee701962018-10-31 14:51:37 -070027#include <string>
28#include <tuple>
29#include <utility>
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +093030
Andrew Jeffery619207d2018-11-22 13:09:17 +110031/*
32
33Design and integration notes
34============================
35
36The primary motivation of the Host I/O Mapping protocol (HIOMAP) is to mediate
37host access to a BMC-controlled flash chip housing the host's boot firmware.
38
39openpower-host-ipmi-flash facilitates the system design of transporting the
40HIOMAP protocol[1] over IPMI. This is somewhat abusive of IPMI, basically
41treating the BT interface as a mailbox with an interrupt each way between the
42BMC and the host.
43
44[1] https://github.com/openbmc/mboxbridge/blob/master/Documentation/protocol.md
45
46Using IPMI in this way has a number of challenges, a lot of them on the host
47side where we need to bring up the LPC and BT interfaces to enable IPMI before
48accessing the flash, and before any interrupts are enabled. There are also
49challenges on the BMC side with the design of the current implementation. We
50will cover those here.
51
52BMC-side System Design and Integration Issues
53---------------------------------------------
54
55The current design is that we have the HIOMAP daemon, mboxd (to be renamed),
56exposing a set of DBus interfaces. Whilst the spec defines the IPMI transport
57message packing, mboxd knows nothing of IPMI itself, instead relying on the
58DBus interface to receive messages from ipmid. ipmid in-turn knows nothing of
59the interfaces communicating with it, also relying on DBus to receive messages
60from interface-specific daemons, e.g. btbridged[2].
61
62[2] https://github.com/openbmc/btbridge
63
64For this design to function correctly we must ensure that the daemons are
65started and shut down in a reasonable order, however defining that order is
66somewhat tricky:
67
681. systemd uses Wants=/Before=/After= relationships in units to define both
69 start-up *and* shutdown order, in stack push / pop order respectively.
702. Clearly ipmid depends on btbridged to receive messages sent by signals and
71 replied to by method calls, so it needs a Wants=/After= relationship on
72 btbridged
733. mboxd depends on ipmid to receive messages sent by method call, and issues a
74 PropertiesChanged signal to notify of state changes.
75
76Point 3. suggests mboxd should have a Wants=/Before= relationship with ipmid to
77ensure ipmid can call into mboxd as messages arrive. However, this causes some
78grief with shutdown of the BMC, as mboxd needs to issue a state-change
79notification when it is shut down to inform the host that will not repsond to
80future requests and that the protocol state has been reset. If mboxd has a
81Wants=/Before= relationship with ipmid this message will never propagate to the
82host, as ipmid will be shut by systemd before mboxd.
83
84The above leads to mboxd having a Wants=/After= relationship with ipmid. This
85ensures that if mboxd is restarted on its own the correct state changes will be
86propagated to the host. The case where ipmid attempts to call into mboxd's DBus
87interface before mboxd is ready is mitigated by the ready bit in the protocol's
88BMC status, which will not yet be set, preventing a conforming host from
89attempting to contact mboxd.
90
91While this ordering prevents mboxd from being terminated before ipmid, there is
92no control over the *scheduling* of processes to ensure the PropertiesChanged
93signal emitted by mboxd before mboxd is terminated is seen by ipmid before
94*ipmid* is also terminated. This leads to our first implementation wart:
95
96 On the basis that mboxd has a Wants=/After= relationship with ipmid,
97 openpower-host-ipmi-flash will emit an HIOMAP BMC status event to the host
98 with the value BMC_EVENT_PROTOCOL_RESET upon receiving SIGTERM iff the BMC
99 state is not already set to BMC_EVENT_PROTOCOL_RESET.
100
101If ipmid has received SIGTERM the assumption is that it is systemd that is
102sending it, and that the Wants=/After= relationship requires that mboxd has
103been terminated before ipmid receives SIGTERM. By ensuring
104openpower-host-ipmi-flash emits the BMC event state we close the race where the
105host is not informed of the termination of mboxd due to scheduling ipmid (to
106deliver SIGTERM) prior to scheduling dbus-daemon, where the PropertiesChanged
107event would be delivered from mboxd to ipmid.
108
109Observations on the IPMI Specification and Design Details of ipmid
110------------------------------------------------------------------
111
112In addition to the system-level design problems with delivering
113PropertiesChanged signals during shutdown, IPMI specification and ipmid design
114issues exist that make it tedious to ensure that events will be correctly
115delivered to the host.
116
117The first necessary observation is that the mechanism for delivering BMC state
118change events from mboxd to the host over IPMI uses the SMS ATN bit to indicate
119a message is ready for delivery from the BMC to the host system. Retrieving the
120BMC state data involves the host recognising that the SMS ATN bit is set,
121performing Get Message Flags transaction with the BMC followed by a subsequent
122Get Message transaction. Thus, delivery of the HIOMAP protocol's BMC status is
123not an atomic event.
124
125The second necessary observation is that the kernel delivers signals
126asynchronously. This couples badly with IPMI's event delivery not being atomic:
127ipmid can win the race against SIGTERM to receive the PropertiesChanged event
128from mboxd, but lose the race to complete delivery to the host.
129
130 On this basis, we need to block the delivery of SIGTERM to ipmid until ipmid
131 has completed the set of `SMS ATN`/`Get Message Flags`/`Get Message`
132 transactions with the host
133
134One approach to this would be to configure a custom SIGTERM handler that sets
135some global application state to indicate that SIGTERM has been delivered. A
136better approach that avoids the need for global application state is to simply
137block the signal until we are ready to handle it, which we can do via
138sigprocmask(2).
139
140The existing design of ipmid makes it feasible to block and unblock
141asynchronous SIGTERM as we require. ipmid_send_cmd_to_host() takes a CallBack
142function as an argument, which is invoked by
143phosphor::host::command::Manager::getNextCommand(). The documentation for
144phosphor::host::command::Manager::getNextCommand() says:
145
146 @brief Extracts the next entry in the queue and returns
147 Command and data part of it.
148
149 @detail Also calls into the registered handlers so that they can now
150 send the CommandComplete signal since the interface contract
151 is that we emit this signal once the message has been
152 passed to the host (which is required when calling this)
153
154 Also, if the queue has more commands, then it will alert the
155 host
156
157However, its description is not entirely accurate. The callback function is
158invoked when ipmid *dequeues* the data to send to the host: Delivery of the
159data to the host occurs at some *after* the callback has been invoked.
160
161Invoking the callback before completion of delivery of the data to the host
162nullifies the approach of unblocking asynchronous SIGTERM in the callback
163associated with sending the HIOMAP BMC state event to the host, as the BMC
164kernel can asynchronously terminate the process between the callback being
165invoked and the host receiving the BMC state event data.
166
167Overcoming this issue hinges on a significant implementation detail of ipmid:
168
169 ipmid uses an sd_event loop in the main function to pump DBus events.
170
171This leads to a third necessary observation:
172
173 sd_event can be used to process UNIX signals as well as other events by way
174 of Linux's signalfd(2) interface.
175
176The fact that sd_event is used to pump DBus events means that ipmid can remain
177a single-threaded process. By remaining single-threaded we know that events
178processing is sequencial and no two events can be processed simultaneously. A
179corollary of this is that DBus events and UNIX signals are serialised with
180respect to eachother.
181
182The fourth necessary observation is that we do not need to pump sd_event in
183order to complete DBus method calls; sd_bus will handle the pumping independent
184of the main loop in order to complete the method invocation.
185
186Implementing Reliable HIOMAP BMC Status Event Delivery
187------------------------------------------------------
188
189We achieve reliable delivery of HIOMAP BMC status events in the following way:
190
1911. During plugin initialisation, mask SIGTERM using sigprocmask(2)
1922. Subsequent to masking SIGTERM, register
193 openpower::flash::hiomap_protocol_reset() as the SIGTERM handler using
194 sd_event_add_signal() to hook a signalfd(2) into sd_event
1953. openpower::flash::hiomap_protocol_reset() implements the logic to send the
196 BMC_EVENT_PROTOCOL_RESET state to the host if necessary, otherwise terminate
197 the sd_event loop.
1984. If it is necessary to send BMC_EVENT_PROTOCOL_RESET to the host in 3, assign
199 a callback handler that terminates the sd_event loop, which is only
200 processed after the current iteration is complete.
201
202This process and its use of signalfd integration in the sd_event loop
203eliminates the following three races:
204
2051. The scheduler race between mboxd, dbus-daemon and ipmid, by having
206 openpower-host-ipmi-flash conditionally deliver the protocol reset event if
207 no such message has been received from mboxd
2082. The race between delivering the BMC status event to the host and ipmid
209 receiving asynchronous SIGTERM after receiving the PropertiesChanged event
210 from mboxd
2113. The race to deliver the BMC status data to the host after unblocking
212 asynchronous SIGTERM in the host command callback and before receiving
213 asynchronous SIGTERM.
214
215Ultimately, ipmid could benefit from a redesign that fires the callback *after*
216delivering the associated data to the host, but brief inspection determined
217that this involved a non-trivial amount of effort.
218
219*/
220
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930221using namespace sdbusplus;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930222using namespace phosphor::host::command;
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930223
224static void register_openpower_hiomap_commands() __attribute__((constructor));
225
226namespace openpower
227{
228namespace flash
229{
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930230constexpr auto BMC_EVENT_DAEMON_READY = 1 << 7;
231constexpr auto BMC_EVENT_FLASH_CTRL_LOST = 1 << 6;
232constexpr auto BMC_EVENT_WINDOW_RESET = 1 << 1;
233constexpr auto BMC_EVENT_PROTOCOL_RESET = 1 << 0;
234
235constexpr auto IPMI_CMD_HIOMAP_EVENT = 0x0f;
236
237constexpr auto HIOMAPD_SERVICE = "xyz.openbmc_project.Hiomapd";
238constexpr auto HIOMAPD_OBJECT = "/xyz/openbmc_project/Hiomapd";
239constexpr auto HIOMAPD_IFACE = "xyz.openbmc_project.Hiomapd.Protocol";
240constexpr auto HIOMAPD_IFACE_V2 = "xyz.openbmc_project.Hiomapd.Protocol.V2";
241
242constexpr auto DBUS_IFACE_PROPERTIES = "org.freedesktop.DBus.Properties";
243
Andrew Jeffery619207d2018-11-22 13:09:17 +1100244/* XXX: ipmid is currently single-threaded, pumping dbus events in sequence
245 * via the main event loop. Thus the code is not forced to be re-entrant. We
246 * also know that the callback and DBus event handling will not be running
247 * concurrently.
248 *
249 * ipmid_send_cmd_to_host() takes a callback that doesn't define a context
250 * pointer, so instead use a global. active_event_updates gates manipulation of
251 * process state, so its definition as a global at least aligns with its use.
252 */
253static int active_event_updates;
254static struct sd_event_source* event_source;
255
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930256struct hiomap
257{
Patrick Venture5b355062018-10-31 14:42:42 -0700258 bus::bus* bus;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930259
260 /* Signals */
Patrick Venture5b355062018-10-31 14:42:42 -0700261 bus::match::match* properties;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930262
263 /* Protocol state */
264 std::map<std::string, int> event_lookup;
265 uint8_t bmc_events;
Andrew Jeffery04d75132018-09-26 00:58:52 +0930266 uint8_t seq;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930267};
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930268
269/* TODO: Replace get/put with packed structs and direct assignment */
Patrick Venture5b355062018-10-31 14:42:42 -0700270template <typename T>
271static inline T get(void* buf)
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930272{
273 T t;
Patrick Ventureee701962018-10-31 14:51:37 -0700274 std::memcpy(&t, buf, sizeof(t));
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930275 return t;
276}
277
Patrick Venture5b355062018-10-31 14:42:42 -0700278template <typename T>
279static inline void put(void* buf, T&& t)
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930280{
Patrick Ventureee701962018-10-31 14:51:37 -0700281 std::memcpy(buf, &t, sizeof(t));
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930282}
283
284typedef ipmi_ret_t (*hiomap_command)(ipmi_request_t req, ipmi_response_t resp,
285 ipmi_data_len_t data_len,
286 ipmi_context_t context);
287
288struct errno_cc_entry
289{
290 int err;
291 int cc;
292};
293
294static const errno_cc_entry errno_cc_map[] = {
295 {0, IPMI_CC_OK},
296 {EBUSY, IPMI_CC_BUSY},
297 {ENOTSUP, IPMI_CC_INVALID},
298 {ETIMEDOUT, 0xc3}, /* FIXME: Replace when defined in ipmid-api.h */
299 {ENOSPC, 0xc4}, /* FIXME: Replace when defined in ipmid-api.h */
300 {EINVAL, IPMI_CC_PARM_OUT_OF_RANGE},
301 {ENODEV, IPMI_CC_SENSOR_INVALID},
302 {EPERM, IPMI_CC_INSUFFICIENT_PRIVILEGE},
303 {EACCES, IPMI_CC_INSUFFICIENT_PRIVILEGE},
304 {-1, IPMI_CC_UNSPECIFIED_ERROR},
305};
306
307static int hiomap_xlate_errno(int err)
308{
Patrick Venture5b355062018-10-31 14:42:42 -0700309 const errno_cc_entry* entry = &errno_cc_map[0];
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930310
311 while (!(entry->err == err || entry->err == -1))
312 {
313 entry++;
314 }
315
316 return entry->cc;
317}
318
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930319static void ipmi_hiomap_event_response(IpmiCmdData cmd, bool status)
320{
321 using namespace phosphor::logging;
Andrew Jeffery619207d2018-11-22 13:09:17 +1100322 int rc;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930323
324 if (!status)
325 {
326 log<level::ERR>("Failed to deliver host command",
327 entry("SEL_COMMAND=%x:%x", cmd.first, cmd.second));
328 }
Andrew Jeffery619207d2018-11-22 13:09:17 +1100329
330 assert(active_event_updates);
331 active_event_updates--;
332 if (!active_event_updates)
333 {
334 rc = sd_event_source_set_enabled(event_source, SD_EVENT_ON);
335 if (rc < 0)
336 {
337 log<level::WARNING>("Failed to unblock SIGTERM delivery",
338 entry("RC=%d", rc));
339 }
340 else
341 {
342 log<level::DEBUG>("Unblocked SIGTERM");
343 }
344 }
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930345}
346
Patrick Venture5b355062018-10-31 14:42:42 -0700347static int hiomap_handle_property_update(struct hiomap* ctx,
348 sdbusplus::message::message& msg)
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930349{
Andrew Jeffery619207d2018-11-22 13:09:17 +1100350 using namespace phosphor::logging;
351
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930352 std::map<std::string, sdbusplus::message::variant<bool>> msgData;
Andrew Jeffery619207d2018-11-22 13:09:17 +1100353 int rc;
354
355 if (!active_event_updates)
356 {
357 rc = sd_event_source_set_enabled(event_source, SD_EVENT_OFF);
358 if (rc < 0)
359 {
360 log<level::WARNING>("Failed to block SIGTERM delivery",
361 entry("RC=%d", rc));
362 }
363 else
364 {
365 log<level::DEBUG>("Blocked SIGTERM");
366 }
367 }
368 active_event_updates++;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930369
370 std::string iface;
371 msg.read(iface, msgData);
372
Patrick Venture5b355062018-10-31 14:42:42 -0700373 for (auto const& x : msgData)
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930374 {
375 if (!ctx->event_lookup.count(x.first))
376 {
377 /* Unsupported event? */
378 continue;
379 }
380
381 uint8_t mask = ctx->event_lookup[x.first];
382 auto value = sdbusplus::message::variant_ns::get<bool>(x.second);
383
384 if (value)
385 {
386 ctx->bmc_events |= mask;
387 }
388 else
389 {
390 ctx->bmc_events &= ~mask;
391 }
392 }
393
394 auto cmd = std::make_pair(IPMI_CMD_HIOMAP_EVENT, ctx->bmc_events);
395
396 ipmid_send_cmd_to_host(std::make_tuple(cmd, ipmi_hiomap_event_response));
397
398 return 0;
399}
400
Andrew Jeffery619207d2018-11-22 13:09:17 +1100401static int hiomap_protocol_reset_response(IpmiCmdData cmd, bool status)
402{
403 return sd_event_exit(ipmid_get_sd_event_connection(), status ? 0 : EIO);
404}
405
406static int hiomap_protocol_reset(sd_event_source* source,
407 const struct signalfd_siginfo* si,
408 void* userdata)
409{
410 struct hiomap* ctx = static_cast<struct hiomap*>(userdata);
411
412 if (ctx->bmc_events == BMC_EVENT_PROTOCOL_RESET)
413 {
414 return sd_event_exit(ipmid_get_sd_event_connection(), 0);
415 }
416
417 /*
418 * Send an attention indicating the hiomapd has died
419 * (BMC_EVENT_DAEMON_READY cleared) and that the protocol has been reset
420 * (BMC_EVENT_PROTOCOL_RESET set) to indicate to the host that it needs to
421 * wait for the BMC to come back and renegotiate the protocol.
422 *
423 * We know this to be the case in systems that integrate
424 * openpower-host-ipmi-flash, as hiomapd's unit depends on
425 * phosphor-ipmi-host, and thus hiomapd has been terminated before ipmid
426 * receives SIGTERM.
427 */
428 auto cmd = std::make_pair(IPMI_CMD_HIOMAP_EVENT, BMC_EVENT_PROTOCOL_RESET);
429
430 auto cmdHandler = std::make_tuple(cmd, hiomap_protocol_reset_response);
431 ipmid_send_cmd_to_host(cmdHandler);
432
433 return 0;
434}
435
Patrick Venture5b355062018-10-31 14:42:42 -0700436static bus::match::match hiomap_match_properties(struct hiomap* ctx)
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930437{
438 auto properties =
439 bus::match::rules::propertiesChanged(HIOMAPD_OBJECT, HIOMAPD_IFACE_V2);
440
441 bus::match::match match(
442 *ctx->bus, properties,
443 std::bind(hiomap_handle_property_update, ctx, std::placeholders::_1));
444
445 return match;
446}
447
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930448static ipmi_ret_t hiomap_reset(ipmi_request_t request, ipmi_response_t response,
449 ipmi_data_len_t data_len, ipmi_context_t context)
450{
Patrick Venture5b355062018-10-31 14:42:42 -0700451 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930452
453 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
454 HIOMAPD_IFACE, "Reset");
455 try
456 {
457 ctx->bus->call(m);
458
459 *data_len = 0;
460 }
Patrick Venture5b355062018-10-31 14:42:42 -0700461 catch (const exception::SdBusError& e)
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930462 {
463 return hiomap_xlate_errno(e.get_errno());
464 }
465
466 return IPMI_CC_OK;
467}
468
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930469static ipmi_ret_t hiomap_get_info(ipmi_request_t request,
470 ipmi_response_t response,
471 ipmi_data_len_t data_len,
472 ipmi_context_t context)
473{
Patrick Venture5b355062018-10-31 14:42:42 -0700474 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930475
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930476 if (*data_len < 1)
477 {
478 return IPMI_CC_REQ_DATA_LEN_INVALID;
479 }
480
Patrick Venture5b355062018-10-31 14:42:42 -0700481 uint8_t* reqdata = (uint8_t*)request;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930482 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
483 HIOMAPD_IFACE, "GetInfo");
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930484 m.append(reqdata[0]);
485
486 try
487 {
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930488 auto reply = ctx->bus->call(m);
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930489
490 uint8_t version;
491 uint8_t blockSizeShift;
492 uint16_t timeout;
493 reply.read(version, blockSizeShift, timeout);
494
Patrick Venture5b355062018-10-31 14:42:42 -0700495 uint8_t* respdata = (uint8_t*)response;
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930496
497 /* FIXME: Assumes v2! */
498 put(&respdata[0], version);
499 put(&respdata[1], blockSizeShift);
500 put(&respdata[2], htole16(timeout));
501
502 *data_len = 4;
503 }
Patrick Venture5b355062018-10-31 14:42:42 -0700504 catch (const exception::SdBusError& e)
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930505 {
506 return hiomap_xlate_errno(e.get_errno());
507 }
508
509 return IPMI_CC_OK;
510}
511
Andrew Jefferydb688e92018-08-23 21:21:30 +0930512static ipmi_ret_t hiomap_get_flash_info(ipmi_request_t request,
513 ipmi_response_t response,
514 ipmi_data_len_t data_len,
515 ipmi_context_t context)
516{
Patrick Venture5b355062018-10-31 14:42:42 -0700517 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jefferydb688e92018-08-23 21:21:30 +0930518
519 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
520 HIOMAPD_IFACE_V2, "GetFlashInfo");
521 try
522 {
523 auto reply = ctx->bus->call(m);
524
525 uint16_t flashSize, eraseSize;
526 reply.read(flashSize, eraseSize);
527
Patrick Venture5b355062018-10-31 14:42:42 -0700528 uint8_t* respdata = (uint8_t*)response;
Andrew Jefferydb688e92018-08-23 21:21:30 +0930529 put(&respdata[0], htole16(flashSize));
530 put(&respdata[2], htole16(eraseSize));
531
532 *data_len = 4;
533 }
Patrick Venture5b355062018-10-31 14:42:42 -0700534 catch (const exception::SdBusError& e)
Andrew Jefferydb688e92018-08-23 21:21:30 +0930535 {
536 return hiomap_xlate_errno(e.get_errno());
537 }
538
539 return IPMI_CC_OK;
540}
541
Patrick Venture5b355062018-10-31 14:42:42 -0700542static ipmi_ret_t hiomap_create_window(struct hiomap* ctx, bool ro,
Andrew Jefferya00f59b2018-08-23 22:25:49 +0930543 ipmi_request_t request,
544 ipmi_response_t response,
545 ipmi_data_len_t data_len)
546{
547 if (*data_len < 4)
548 {
549 return IPMI_CC_REQ_DATA_LEN_INVALID;
550 }
551
Patrick Venture5b355062018-10-31 14:42:42 -0700552 uint8_t* reqdata = (uint8_t*)request;
Andrew Jefferya00f59b2018-08-23 22:25:49 +0930553 auto windowType = ro ? "CreateReadWindow" : "CreateWriteWindow";
554
555 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
556 HIOMAPD_IFACE_V2, windowType);
557 m.append(le16toh(get<uint16_t>(&reqdata[0])));
558 m.append(le16toh(get<uint16_t>(&reqdata[2])));
559
560 try
561 {
562 auto reply = ctx->bus->call(m);
563
564 uint16_t lpcAddress, size, offset;
565 reply.read(lpcAddress, size, offset);
566
Patrick Venture5b355062018-10-31 14:42:42 -0700567 uint8_t* respdata = (uint8_t*)response;
Andrew Jefferya00f59b2018-08-23 22:25:49 +0930568
569 /* FIXME: Assumes v2! */
570 put(&respdata[0], htole16(lpcAddress));
571 put(&respdata[2], htole16(size));
572 put(&respdata[4], htole16(offset));
573
574 *data_len = 6;
575 }
Patrick Venture5b355062018-10-31 14:42:42 -0700576 catch (const exception::SdBusError& e)
Andrew Jefferya00f59b2018-08-23 22:25:49 +0930577 {
578 return hiomap_xlate_errno(e.get_errno());
579 }
580
581 return IPMI_CC_OK;
582}
583
584static ipmi_ret_t hiomap_create_read_window(ipmi_request_t request,
585 ipmi_response_t response,
586 ipmi_data_len_t data_len,
587 ipmi_context_t context)
588{
Patrick Venture5b355062018-10-31 14:42:42 -0700589 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jefferya00f59b2018-08-23 22:25:49 +0930590
591 return hiomap_create_window(ctx, true, request, response, data_len);
592}
593
594static ipmi_ret_t hiomap_create_write_window(ipmi_request_t request,
595 ipmi_response_t response,
596 ipmi_data_len_t data_len,
597 ipmi_context_t context)
598{
Patrick Venture5b355062018-10-31 14:42:42 -0700599 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jefferya00f59b2018-08-23 22:25:49 +0930600
601 return hiomap_create_window(ctx, false, request, response, data_len);
602}
603
Andrew Jefferyb52822c2018-08-23 23:01:39 +0930604static ipmi_ret_t hiomap_close_window(ipmi_request_t request,
605 ipmi_response_t response,
606 ipmi_data_len_t data_len,
607 ipmi_context_t context)
608{
Patrick Venture5b355062018-10-31 14:42:42 -0700609 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jefferyb52822c2018-08-23 23:01:39 +0930610
611 if (*data_len < 1)
612 {
613 return IPMI_CC_REQ_DATA_LEN_INVALID;
614 }
615
Patrick Venture5b355062018-10-31 14:42:42 -0700616 uint8_t* reqdata = (uint8_t*)request;
Andrew Jefferyb52822c2018-08-23 23:01:39 +0930617 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
618 HIOMAPD_IFACE_V2, "CloseWindow");
619 m.append(reqdata[0]);
620
621 try
622 {
623 auto reply = ctx->bus->call(m);
624
625 *data_len = 0;
626 }
Patrick Venture5b355062018-10-31 14:42:42 -0700627 catch (const exception::SdBusError& e)
Andrew Jefferyb52822c2018-08-23 23:01:39 +0930628 {
629 return hiomap_xlate_errno(e.get_errno());
630 }
631
632 return IPMI_CC_OK;
633}
634
Andrew Jeffery9847f1c2018-08-24 09:13:40 +0930635static ipmi_ret_t hiomap_mark_dirty(ipmi_request_t request,
636 ipmi_response_t response,
637 ipmi_data_len_t data_len,
638 ipmi_context_t context)
639{
Patrick Venture5b355062018-10-31 14:42:42 -0700640 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jeffery9847f1c2018-08-24 09:13:40 +0930641
642 if (*data_len < 4)
643 {
644 return IPMI_CC_REQ_DATA_LEN_INVALID;
645 }
646
Patrick Venture5b355062018-10-31 14:42:42 -0700647 uint8_t* reqdata = (uint8_t*)request;
Andrew Jeffery9847f1c2018-08-24 09:13:40 +0930648 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
649 HIOMAPD_IFACE_V2, "MarkDirty");
650 /* FIXME: Assumes v2 */
651 m.append(le16toh(get<uint16_t>(&reqdata[0]))); /* offset */
652 m.append(le16toh(get<uint16_t>(&reqdata[2]))); /* size */
653
654 try
655 {
656 auto reply = ctx->bus->call(m);
657
658 *data_len = 0;
659 }
Patrick Venture5b355062018-10-31 14:42:42 -0700660 catch (const exception::SdBusError& e)
Andrew Jeffery9847f1c2018-08-24 09:13:40 +0930661 {
662 return hiomap_xlate_errno(e.get_errno());
663 }
664
665 return IPMI_CC_OK;
666}
667
Andrew Jeffery7b225fb2018-08-24 09:19:21 +0930668static ipmi_ret_t hiomap_flush(ipmi_request_t request, ipmi_response_t response,
669 ipmi_data_len_t data_len, ipmi_context_t context)
670{
Patrick Venture5b355062018-10-31 14:42:42 -0700671 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jeffery7b225fb2018-08-24 09:19:21 +0930672
673 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
674 HIOMAPD_IFACE_V2, "Flush");
675
676 try
677 {
678 /* FIXME: No argument call assumes v2 */
679 auto reply = ctx->bus->call(m);
680
681 *data_len = 0;
682 }
Patrick Venture5b355062018-10-31 14:42:42 -0700683 catch (const exception::SdBusError& e)
Andrew Jeffery7b225fb2018-08-24 09:19:21 +0930684 {
685 return hiomap_xlate_errno(e.get_errno());
686 }
687
688 return IPMI_CC_OK;
689}
690
Andrew Jeffery99f277a2018-08-24 09:24:04 +0930691static ipmi_ret_t hiomap_ack(ipmi_request_t request, ipmi_response_t response,
692 ipmi_data_len_t data_len, ipmi_context_t context)
693{
Patrick Venture5b355062018-10-31 14:42:42 -0700694 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jeffery99f277a2018-08-24 09:24:04 +0930695
696 if (*data_len < 1)
697 {
698 return IPMI_CC_REQ_DATA_LEN_INVALID;
699 }
700
Patrick Venture5b355062018-10-31 14:42:42 -0700701 uint8_t* reqdata = (uint8_t*)request;
Andrew Jeffery99f277a2018-08-24 09:24:04 +0930702 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
703 HIOMAPD_IFACE_V2, "Ack");
704 auto acked = reqdata[0];
705 m.append(acked);
706
707 try
708 {
709 auto reply = ctx->bus->call(m);
710
Andrew Jeffery99f277a2018-08-24 09:24:04 +0930711 *data_len = 0;
712 }
Patrick Venture5b355062018-10-31 14:42:42 -0700713 catch (const exception::SdBusError& e)
Andrew Jeffery99f277a2018-08-24 09:24:04 +0930714 {
715 return hiomap_xlate_errno(e.get_errno());
716 }
717
718 return IPMI_CC_OK;
719}
720
Andrew Jefferya1e35b82018-08-24 09:39:10 +0930721static ipmi_ret_t hiomap_erase(ipmi_request_t request, ipmi_response_t response,
722 ipmi_data_len_t data_len, ipmi_context_t context)
723{
Patrick Venture5b355062018-10-31 14:42:42 -0700724 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jefferya1e35b82018-08-24 09:39:10 +0930725
726 if (*data_len < 4)
727 {
728 return IPMI_CC_REQ_DATA_LEN_INVALID;
729 }
730
Patrick Venture5b355062018-10-31 14:42:42 -0700731 uint8_t* reqdata = (uint8_t*)request;
Andrew Jefferya1e35b82018-08-24 09:39:10 +0930732 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
733 HIOMAPD_IFACE_V2, "Erase");
734 /* FIXME: Assumes v2 */
735 m.append(le16toh(get<uint16_t>(&reqdata[0]))); /* offset */
736 m.append(le16toh(get<uint16_t>(&reqdata[2]))); /* size */
737
738 try
739 {
740 auto reply = ctx->bus->call(m);
741
742 *data_len = 0;
743 }
Patrick Venture5b355062018-10-31 14:42:42 -0700744 catch (const exception::SdBusError& e)
Andrew Jefferya1e35b82018-08-24 09:39:10 +0930745 {
746 return hiomap_xlate_errno(e.get_errno());
747 }
748
749 return IPMI_CC_OK;
750}
751
Andrew Jeffery04d75132018-09-26 00:58:52 +0930752#define HIOMAP_C_RESET 1
753#define HIOMAP_C_GET_INFO 2
754#define HIOMAP_C_GET_FLASH_INFO 3
755#define HIOMAP_C_CREATE_READ_WINDOW 4
756#define HIOMAP_C_CLOSE_WINDOW 5
757#define HIOMAP_C_CREATE_WRITE_WINDOW 6
758#define HIOMAP_C_MARK_DIRTY 7
759#define HIOMAP_C_FLUSH 8
760#define HIOMAP_C_ACK 9
761#define HIOMAP_C_ERASE 10
762
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930763static const hiomap_command hiomap_commands[] = {
Andrew Jeffery04d75132018-09-26 00:58:52 +0930764 [0] = NULL, /* Invalid command ID */
765 [HIOMAP_C_RESET] = hiomap_reset,
766 [HIOMAP_C_GET_INFO] = hiomap_get_info,
767 [HIOMAP_C_GET_FLASH_INFO] = hiomap_get_flash_info,
768 [HIOMAP_C_CREATE_READ_WINDOW] = hiomap_create_read_window,
769 [HIOMAP_C_CLOSE_WINDOW] = hiomap_close_window,
770 [HIOMAP_C_CREATE_WRITE_WINDOW] = hiomap_create_write_window,
771 [HIOMAP_C_MARK_DIRTY] = hiomap_mark_dirty,
772 [HIOMAP_C_FLUSH] = hiomap_flush,
773 [HIOMAP_C_ACK] = hiomap_ack,
774 [HIOMAP_C_ERASE] = hiomap_erase,
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930775};
776
777/* FIXME: Define this in the "right" place, wherever that is */
778/* FIXME: Double evaluation */
779#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
780
781static ipmi_ret_t hiomap_dispatch(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
782 ipmi_request_t request,
783 ipmi_response_t response,
784 ipmi_data_len_t data_len,
785 ipmi_context_t context)
786{
Patrick Venture5b355062018-10-31 14:42:42 -0700787 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930788
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930789 if (*data_len < 2)
790 {
791 *data_len = 0;
792 return IPMI_CC_REQ_DATA_LEN_INVALID;
793 }
794
Patrick Venture5b355062018-10-31 14:42:42 -0700795 uint8_t* ipmi_req = (uint8_t*)request;
796 uint8_t* ipmi_resp = (uint8_t*)response;
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930797 uint8_t hiomap_cmd = ipmi_req[0];
798
799 if (hiomap_cmd == 0 || hiomap_cmd > ARRAY_SIZE(hiomap_commands) - 1)
800 {
801 *data_len = 0;
802 return IPMI_CC_PARM_OUT_OF_RANGE;
803 }
Andrew Jeffery04d75132018-09-26 00:58:52 +0930804
805 bool is_unversioned =
806 (hiomap_cmd == HIOMAP_C_RESET || hiomap_cmd == HIOMAP_C_GET_INFO ||
807 hiomap_cmd == HIOMAP_C_ACK);
808 if (!is_unversioned && ctx->seq == ipmi_req[1])
809 {
810 *data_len = 0;
811 return IPMI_CC_INVALID_FIELD_REQUEST;
812 }
813
814 ctx->seq = ipmi_req[1];
815
Patrick Venture5b355062018-10-31 14:42:42 -0700816 uint8_t* flash_req = ipmi_req + 2;
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930817 size_t flash_len = *data_len - 2;
Patrick Venture5b355062018-10-31 14:42:42 -0700818 uint8_t* flash_resp = ipmi_resp + 2;
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930819
820 ipmi_ret_t cc =
821 hiomap_commands[hiomap_cmd](flash_req, flash_resp, &flash_len, context);
822 if (cc != IPMI_CC_OK)
823 {
824 *data_len = 0;
825 return cc;
826 }
827
828 /* Populate the response command and sequence */
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930829 ipmi_resp[0] = hiomap_cmd;
Andrew Jeffery04d75132018-09-26 00:58:52 +0930830 ipmi_resp[1] = ctx->seq;
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930831
832 *data_len = flash_len + 2;
833
834 return cc;
835}
836} // namespace flash
837} // namespace openpower
838
839static void register_openpower_hiomap_commands()
840{
Andrew Jeffery619207d2018-11-22 13:09:17 +1100841 using namespace phosphor::logging;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930842 using namespace openpower::flash;
843
Andrew Jeffery619207d2018-11-22 13:09:17 +1100844 sigset_t _sigset, *sigset = &_sigset;
Patrick Venture5b355062018-10-31 14:42:42 -0700845 struct hiomap* ctx = new hiomap();
Andrew Jeffery619207d2018-11-22 13:09:17 +1100846 struct sd_event* events;
847 int rc;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930848
849 /* Initialise mapping from signal and property names to status bit */
850 ctx->event_lookup["DaemonReady"] = BMC_EVENT_DAEMON_READY;
851 ctx->event_lookup["FlashControlLost"] = BMC_EVENT_FLASH_CTRL_LOST;
852 ctx->event_lookup["WindowReset"] = BMC_EVENT_WINDOW_RESET;
853 ctx->event_lookup["ProtocolReset"] = BMC_EVENT_PROTOCOL_RESET;
854
855 ctx->bus = new bus::bus(ipmid_get_sd_bus_connection());
856
857 /* Initialise signal handling */
858
859 /*
860 * Can't use temporaries here because that causes SEGFAULTs due to slot
861 * destruction (!?), so enjoy the weird wrapping.
862 */
863 ctx->properties =
864 new bus::match::match(std::move(hiomap_match_properties(ctx)));
Andrew Jeffery619207d2018-11-22 13:09:17 +1100865
866 rc = sigemptyset(sigset);
867 if (rc < 0)
868 {
869 log<level::ERR>("sigemptyset() failed", entry("RC=%d", rc));
870 return;
871 }
872
873 rc = sigaddset(sigset, SIGTERM);
874 if (rc < 0)
875 {
876 log<level::ERR>("sigaddset() failed", entry("RC=%d", rc));
877 return;
878 }
879
880 rc = sigprocmask(SIG_BLOCK, sigset, NULL);
881 if (rc < 0)
882 {
883 log<level::ERR>("sigprocmask() failed", entry("RC=%d", rc));
884 return;
885 }
886
887 events = ipmid_get_sd_event_connection();
888
889 rc = sd_event_add_signal(events, &event_source, SIGTERM,
890 openpower::flash::hiomap_protocol_reset, ctx);
891 if (rc < 0)
892 {
893 log<level::ERR>("sd_event_add_signal() failed", entry("RC=%d", rc));
894 return;
895 }
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930896
897 ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_HIOMAP, ctx,
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930898 openpower::flash::hiomap_dispatch, SYSTEM_INTERFACE);
899}