blob: d29a0cb11427bb094e23568318fb71c88e63b1d7 [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>
Vernon Maueryee3064b2019-03-18 10:19:58 -070022#include <ipmid/api.hpp>
Patrick Ventureee701962018-10-31 14:51:37 -070023#include <map>
Andrew Jeffery0a3358e2018-08-21 10:42:09 +093024#include <phosphor-logging/log.hpp>
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +093025#include <sdbusplus/bus.hpp>
Andrew Jeffery0a3358e2018-08-21 10:42:09 +093026#include <sdbusplus/bus/match.hpp>
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +093027#include <sdbusplus/exception.hpp>
Patrick Ventureee701962018-10-31 14:51:37 -070028#include <string>
29#include <tuple>
30#include <utility>
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +093031
Andrew Jeffery619207d2018-11-22 13:09:17 +110032/*
33
34Design and integration notes
35============================
36
37The primary motivation of the Host I/O Mapping protocol (HIOMAP) is to mediate
38host access to a BMC-controlled flash chip housing the host's boot firmware.
39
40openpower-host-ipmi-flash facilitates the system design of transporting the
41HIOMAP protocol[1] over IPMI. This is somewhat abusive of IPMI, basically
42treating the BT interface as a mailbox with an interrupt each way between the
43BMC and the host.
44
45[1] https://github.com/openbmc/mboxbridge/blob/master/Documentation/protocol.md
46
47Using IPMI in this way has a number of challenges, a lot of them on the host
48side where we need to bring up the LPC and BT interfaces to enable IPMI before
49accessing the flash, and before any interrupts are enabled. There are also
50challenges on the BMC side with the design of the current implementation. We
51will cover those here.
52
53BMC-side System Design and Integration Issues
54---------------------------------------------
55
56The current design is that we have the HIOMAP daemon, mboxd (to be renamed),
57exposing a set of DBus interfaces. Whilst the spec defines the IPMI transport
58message packing, mboxd knows nothing of IPMI itself, instead relying on the
59DBus interface to receive messages from ipmid. ipmid in-turn knows nothing of
60the interfaces communicating with it, also relying on DBus to receive messages
61from interface-specific daemons, e.g. btbridged[2].
62
63[2] https://github.com/openbmc/btbridge
64
65For this design to function correctly we must ensure that the daemons are
66started and shut down in a reasonable order, however defining that order is
67somewhat tricky:
68
691. systemd uses Wants=/Before=/After= relationships in units to define both
70 start-up *and* shutdown order, in stack push / pop order respectively.
712. Clearly ipmid depends on btbridged to receive messages sent by signals and
72 replied to by method calls, so it needs a Wants=/After= relationship on
73 btbridged
743. mboxd depends on ipmid to receive messages sent by method call, and issues a
75 PropertiesChanged signal to notify of state changes.
76
77Point 3. suggests mboxd should have a Wants=/Before= relationship with ipmid to
78ensure ipmid can call into mboxd as messages arrive. However, this causes some
79grief with shutdown of the BMC, as mboxd needs to issue a state-change
80notification when it is shut down to inform the host that will not repsond to
81future requests and that the protocol state has been reset. If mboxd has a
82Wants=/Before= relationship with ipmid this message will never propagate to the
83host, as ipmid will be shut by systemd before mboxd.
84
85The above leads to mboxd having a Wants=/After= relationship with ipmid. This
86ensures that if mboxd is restarted on its own the correct state changes will be
87propagated to the host. The case where ipmid attempts to call into mboxd's DBus
88interface before mboxd is ready is mitigated by the ready bit in the protocol's
89BMC status, which will not yet be set, preventing a conforming host from
90attempting to contact mboxd.
91
92While this ordering prevents mboxd from being terminated before ipmid, there is
93no control over the *scheduling* of processes to ensure the PropertiesChanged
94signal emitted by mboxd before mboxd is terminated is seen by ipmid before
95*ipmid* is also terminated. This leads to our first implementation wart:
96
97 On the basis that mboxd has a Wants=/After= relationship with ipmid,
98 openpower-host-ipmi-flash will emit an HIOMAP BMC status event to the host
99 with the value BMC_EVENT_PROTOCOL_RESET upon receiving SIGTERM iff the BMC
100 state is not already set to BMC_EVENT_PROTOCOL_RESET.
101
102If ipmid has received SIGTERM the assumption is that it is systemd that is
103sending it, and that the Wants=/After= relationship requires that mboxd has
104been terminated before ipmid receives SIGTERM. By ensuring
105openpower-host-ipmi-flash emits the BMC event state we close the race where the
106host is not informed of the termination of mboxd due to scheduling ipmid (to
107deliver SIGTERM) prior to scheduling dbus-daemon, where the PropertiesChanged
108event would be delivered from mboxd to ipmid.
109
110Observations on the IPMI Specification and Design Details of ipmid
111------------------------------------------------------------------
112
113In addition to the system-level design problems with delivering
114PropertiesChanged signals during shutdown, IPMI specification and ipmid design
115issues exist that make it tedious to ensure that events will be correctly
116delivered to the host.
117
118The first necessary observation is that the mechanism for delivering BMC state
119change events from mboxd to the host over IPMI uses the SMS ATN bit to indicate
120a message is ready for delivery from the BMC to the host system. Retrieving the
121BMC state data involves the host recognising that the SMS ATN bit is set,
122performing Get Message Flags transaction with the BMC followed by a subsequent
123Get Message transaction. Thus, delivery of the HIOMAP protocol's BMC status is
124not an atomic event.
125
126The second necessary observation is that the kernel delivers signals
127asynchronously. This couples badly with IPMI's event delivery not being atomic:
128ipmid can win the race against SIGTERM to receive the PropertiesChanged event
129from mboxd, but lose the race to complete delivery to the host.
130
131 On this basis, we need to block the delivery of SIGTERM to ipmid until ipmid
132 has completed the set of `SMS ATN`/`Get Message Flags`/`Get Message`
133 transactions with the host
134
135One approach to this would be to configure a custom SIGTERM handler that sets
136some global application state to indicate that SIGTERM has been delivered. A
137better approach that avoids the need for global application state is to simply
138block the signal until we are ready to handle it, which we can do via
139sigprocmask(2).
140
141The existing design of ipmid makes it feasible to block and unblock
142asynchronous SIGTERM as we require. ipmid_send_cmd_to_host() takes a CallBack
143function as an argument, which is invoked by
144phosphor::host::command::Manager::getNextCommand(). The documentation for
145phosphor::host::command::Manager::getNextCommand() says:
146
147 @brief Extracts the next entry in the queue and returns
148 Command and data part of it.
149
150 @detail Also calls into the registered handlers so that they can now
151 send the CommandComplete signal since the interface contract
152 is that we emit this signal once the message has been
153 passed to the host (which is required when calling this)
154
155 Also, if the queue has more commands, then it will alert the
156 host
157
158However, its description is not entirely accurate. The callback function is
159invoked when ipmid *dequeues* the data to send to the host: Delivery of the
160data to the host occurs at some *after* the callback has been invoked.
161
162Invoking the callback before completion of delivery of the data to the host
163nullifies the approach of unblocking asynchronous SIGTERM in the callback
164associated with sending the HIOMAP BMC state event to the host, as the BMC
165kernel can asynchronously terminate the process between the callback being
166invoked and the host receiving the BMC state event data.
167
168Overcoming this issue hinges on a significant implementation detail of ipmid:
169
170 ipmid uses an sd_event loop in the main function to pump DBus events.
171
172This leads to a third necessary observation:
173
174 sd_event can be used to process UNIX signals as well as other events by way
175 of Linux's signalfd(2) interface.
176
177The fact that sd_event is used to pump DBus events means that ipmid can remain
178a single-threaded process. By remaining single-threaded we know that events
179processing is sequencial and no two events can be processed simultaneously. A
180corollary of this is that DBus events and UNIX signals are serialised with
181respect to eachother.
182
183The fourth necessary observation is that we do not need to pump sd_event in
184order to complete DBus method calls; sd_bus will handle the pumping independent
185of the main loop in order to complete the method invocation.
186
187Implementing Reliable HIOMAP BMC Status Event Delivery
188------------------------------------------------------
189
190We achieve reliable delivery of HIOMAP BMC status events in the following way:
191
1921. During plugin initialisation, mask SIGTERM using sigprocmask(2)
1932. Subsequent to masking SIGTERM, register
194 openpower::flash::hiomap_protocol_reset() as the SIGTERM handler using
195 sd_event_add_signal() to hook a signalfd(2) into sd_event
1963. openpower::flash::hiomap_protocol_reset() implements the logic to send the
197 BMC_EVENT_PROTOCOL_RESET state to the host if necessary, otherwise terminate
198 the sd_event loop.
1994. If it is necessary to send BMC_EVENT_PROTOCOL_RESET to the host in 3, assign
200 a callback handler that terminates the sd_event loop, which is only
201 processed after the current iteration is complete.
202
203This process and its use of signalfd integration in the sd_event loop
204eliminates the following three races:
205
2061. The scheduler race between mboxd, dbus-daemon and ipmid, by having
207 openpower-host-ipmi-flash conditionally deliver the protocol reset event if
208 no such message has been received from mboxd
2092. The race between delivering the BMC status event to the host and ipmid
210 receiving asynchronous SIGTERM after receiving the PropertiesChanged event
211 from mboxd
2123. The race to deliver the BMC status data to the host after unblocking
213 asynchronous SIGTERM in the host command callback and before receiving
214 asynchronous SIGTERM.
215
216Ultimately, ipmid could benefit from a redesign that fires the callback *after*
217delivering the associated data to the host, but brief inspection determined
218that this involved a non-trivial amount of effort.
219
220*/
221
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930222using namespace sdbusplus;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930223using namespace phosphor::host::command;
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930224
225static void register_openpower_hiomap_commands() __attribute__((constructor));
226
227namespace openpower
228{
229namespace flash
230{
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930231constexpr auto BMC_EVENT_DAEMON_READY = 1 << 7;
232constexpr auto BMC_EVENT_FLASH_CTRL_LOST = 1 << 6;
233constexpr auto BMC_EVENT_WINDOW_RESET = 1 << 1;
234constexpr auto BMC_EVENT_PROTOCOL_RESET = 1 << 0;
235
236constexpr auto IPMI_CMD_HIOMAP_EVENT = 0x0f;
237
238constexpr auto HIOMAPD_SERVICE = "xyz.openbmc_project.Hiomapd";
239constexpr auto HIOMAPD_OBJECT = "/xyz/openbmc_project/Hiomapd";
240constexpr auto HIOMAPD_IFACE = "xyz.openbmc_project.Hiomapd.Protocol";
241constexpr auto HIOMAPD_IFACE_V2 = "xyz.openbmc_project.Hiomapd.Protocol.V2";
242
243constexpr auto DBUS_IFACE_PROPERTIES = "org.freedesktop.DBus.Properties";
244
Andrew Jeffery619207d2018-11-22 13:09:17 +1100245/* XXX: ipmid is currently single-threaded, pumping dbus events in sequence
246 * via the main event loop. Thus the code is not forced to be re-entrant. We
247 * also know that the callback and DBus event handling will not be running
248 * concurrently.
249 *
250 * ipmid_send_cmd_to_host() takes a callback that doesn't define a context
251 * pointer, so instead use a global. active_event_updates gates manipulation of
252 * process state, so its definition as a global at least aligns with its use.
253 */
254static int active_event_updates;
Andrew Jeffery619207d2018-11-22 13:09:17 +1100255
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
Vernon Maueryee3064b2019-03-18 10:19:58 -0700269SignalResponse sigtermResponse = SignalResponse::continueExecution;
270
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930271/* TODO: Replace get/put with packed structs and direct assignment */
Patrick Venture5b355062018-10-31 14:42:42 -0700272template <typename T>
273static inline T get(void* buf)
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930274{
275 T t;
Patrick Ventureee701962018-10-31 14:51:37 -0700276 std::memcpy(&t, buf, sizeof(t));
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930277 return t;
278}
279
Patrick Venture5b355062018-10-31 14:42:42 -0700280template <typename T>
281static inline void put(void* buf, T&& t)
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930282{
Patrick Ventureee701962018-10-31 14:51:37 -0700283 std::memcpy(buf, &t, sizeof(t));
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930284}
285
286typedef ipmi_ret_t (*hiomap_command)(ipmi_request_t req, ipmi_response_t resp,
287 ipmi_data_len_t data_len,
288 ipmi_context_t context);
289
290struct errno_cc_entry
291{
292 int err;
293 int cc;
294};
295
296static const errno_cc_entry errno_cc_map[] = {
297 {0, IPMI_CC_OK},
298 {EBUSY, IPMI_CC_BUSY},
299 {ENOTSUP, IPMI_CC_INVALID},
300 {ETIMEDOUT, 0xc3}, /* FIXME: Replace when defined in ipmid-api.h */
301 {ENOSPC, 0xc4}, /* FIXME: Replace when defined in ipmid-api.h */
302 {EINVAL, IPMI_CC_PARM_OUT_OF_RANGE},
303 {ENODEV, IPMI_CC_SENSOR_INVALID},
304 {EPERM, IPMI_CC_INSUFFICIENT_PRIVILEGE},
305 {EACCES, IPMI_CC_INSUFFICIENT_PRIVILEGE},
306 {-1, IPMI_CC_UNSPECIFIED_ERROR},
307};
308
309static int hiomap_xlate_errno(int err)
310{
Patrick Venture5b355062018-10-31 14:42:42 -0700311 const errno_cc_entry* entry = &errno_cc_map[0];
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930312
313 while (!(entry->err == err || entry->err == -1))
314 {
315 entry++;
316 }
317
318 return entry->cc;
319}
320
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930321static void ipmi_hiomap_event_response(IpmiCmdData cmd, bool status)
322{
323 using namespace phosphor::logging;
324
325 if (!status)
326 {
327 log<level::ERR>("Failed to deliver host command",
328 entry("SEL_COMMAND=%x:%x", cmd.first, cmd.second));
329 }
Andrew Jeffery619207d2018-11-22 13:09:17 +1100330
331 assert(active_event_updates);
332 active_event_updates--;
333 if (!active_event_updates)
334 {
Vernon Maueryee3064b2019-03-18 10:19:58 -0700335 sigtermResponse = SignalResponse::continueExecution;
336 log<level::DEBUG>("Unblocked SIGTERM");
Andrew Jeffery619207d2018-11-22 13:09:17 +1100337 }
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930338}
339
Patrick Venture5b355062018-10-31 14:42:42 -0700340static int hiomap_handle_property_update(struct hiomap* ctx,
341 sdbusplus::message::message& msg)
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930342{
Andrew Jeffery619207d2018-11-22 13:09:17 +1100343 using namespace phosphor::logging;
344
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930345 std::map<std::string, sdbusplus::message::variant<bool>> msgData;
Andrew Jeffery619207d2018-11-22 13:09:17 +1100346
Vernon Maueryee3064b2019-03-18 10:19:58 -0700347 sigtermResponse = SignalResponse::breakExecution;
Andrew Jeffery619207d2018-11-22 13:09:17 +1100348 if (!active_event_updates)
349 {
Vernon Maueryee3064b2019-03-18 10:19:58 -0700350 sigtermResponse = SignalResponse::breakExecution;
351 log<level::DEBUG>("Blocked SIGTERM");
Andrew Jeffery619207d2018-11-22 13:09:17 +1100352 }
353 active_event_updates++;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930354
355 std::string iface;
356 msg.read(iface, msgData);
357
Patrick Venture5b355062018-10-31 14:42:42 -0700358 for (auto const& x : msgData)
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930359 {
360 if (!ctx->event_lookup.count(x.first))
361 {
362 /* Unsupported event? */
363 continue;
364 }
365
366 uint8_t mask = ctx->event_lookup[x.first];
367 auto value = sdbusplus::message::variant_ns::get<bool>(x.second);
368
369 if (value)
370 {
371 ctx->bmc_events |= mask;
372 }
373 else
374 {
375 ctx->bmc_events &= ~mask;
376 }
377 }
378
379 auto cmd = std::make_pair(IPMI_CMD_HIOMAP_EVENT, ctx->bmc_events);
380
381 ipmid_send_cmd_to_host(std::make_tuple(cmd, ipmi_hiomap_event_response));
382
383 return 0;
384}
385
Andrew Jeffery619207d2018-11-22 13:09:17 +1100386static int hiomap_protocol_reset_response(IpmiCmdData cmd, bool status)
387{
Vernon Maueryee3064b2019-03-18 10:19:58 -0700388 // If this is running in signal context, ipmid will shutdown
389 // the event queue as the last signal handler
Adriana Kobylakd4b7f5e2019-04-16 15:40:18 -0500390 sigtermResponse = SignalResponse::continueExecution;
Vernon Maueryee3064b2019-03-18 10:19:58 -0700391 return 0;
Andrew Jeffery619207d2018-11-22 13:09:17 +1100392}
393
Vernon Maueryee3064b2019-03-18 10:19:58 -0700394static int hiomap_protocol_reset(struct hiomap* ctx)
Andrew Jeffery619207d2018-11-22 13:09:17 +1100395{
Andrew Jeffery619207d2018-11-22 13:09:17 +1100396 if (ctx->bmc_events == BMC_EVENT_PROTOCOL_RESET)
397 {
Vernon Maueryee3064b2019-03-18 10:19:58 -0700398 // If this is running in signal context, ipmid will shutdown
399 // the event queue as the last signal handler
Adriana Kobylakd4b7f5e2019-04-16 15:40:18 -0500400 sigtermResponse = SignalResponse::continueExecution;
Vernon Maueryee3064b2019-03-18 10:19:58 -0700401 return 0;
Andrew Jeffery619207d2018-11-22 13:09:17 +1100402 }
403
404 /*
405 * Send an attention indicating the hiomapd has died
406 * (BMC_EVENT_DAEMON_READY cleared) and that the protocol has been reset
407 * (BMC_EVENT_PROTOCOL_RESET set) to indicate to the host that it needs to
408 * wait for the BMC to come back and renegotiate the protocol.
409 *
410 * We know this to be the case in systems that integrate
411 * openpower-host-ipmi-flash, as hiomapd's unit depends on
412 * phosphor-ipmi-host, and thus hiomapd has been terminated before ipmid
413 * receives SIGTERM.
414 */
415 auto cmd = std::make_pair(IPMI_CMD_HIOMAP_EVENT, BMC_EVENT_PROTOCOL_RESET);
416
417 auto cmdHandler = std::make_tuple(cmd, hiomap_protocol_reset_response);
418 ipmid_send_cmd_to_host(cmdHandler);
419
420 return 0;
421}
422
Patrick Venture5b355062018-10-31 14:42:42 -0700423static bus::match::match hiomap_match_properties(struct hiomap* ctx)
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930424{
425 auto properties =
426 bus::match::rules::propertiesChanged(HIOMAPD_OBJECT, HIOMAPD_IFACE_V2);
427
428 bus::match::match match(
429 *ctx->bus, properties,
430 std::bind(hiomap_handle_property_update, ctx, std::placeholders::_1));
431
432 return match;
433}
434
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930435static ipmi_ret_t hiomap_reset(ipmi_request_t request, ipmi_response_t response,
436 ipmi_data_len_t data_len, ipmi_context_t context)
437{
Patrick Venture5b355062018-10-31 14:42:42 -0700438 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930439
440 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
441 HIOMAPD_IFACE, "Reset");
442 try
443 {
444 ctx->bus->call(m);
445
446 *data_len = 0;
447 }
Patrick Venture5b355062018-10-31 14:42:42 -0700448 catch (const exception::SdBusError& e)
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930449 {
450 return hiomap_xlate_errno(e.get_errno());
451 }
452
453 return IPMI_CC_OK;
454}
455
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930456static ipmi_ret_t hiomap_get_info(ipmi_request_t request,
457 ipmi_response_t response,
458 ipmi_data_len_t data_len,
459 ipmi_context_t context)
460{
Patrick Venture5b355062018-10-31 14:42:42 -0700461 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930462
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930463 if (*data_len < 1)
464 {
465 return IPMI_CC_REQ_DATA_LEN_INVALID;
466 }
467
Patrick Venture5b355062018-10-31 14:42:42 -0700468 uint8_t* reqdata = (uint8_t*)request;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930469 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
470 HIOMAPD_IFACE, "GetInfo");
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930471 m.append(reqdata[0]);
472
473 try
474 {
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930475 auto reply = ctx->bus->call(m);
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930476
477 uint8_t version;
478 uint8_t blockSizeShift;
479 uint16_t timeout;
480 reply.read(version, blockSizeShift, timeout);
481
Patrick Venture5b355062018-10-31 14:42:42 -0700482 uint8_t* respdata = (uint8_t*)response;
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930483
484 /* FIXME: Assumes v2! */
485 put(&respdata[0], version);
486 put(&respdata[1], blockSizeShift);
487 put(&respdata[2], htole16(timeout));
488
489 *data_len = 4;
490 }
Patrick Venture5b355062018-10-31 14:42:42 -0700491 catch (const exception::SdBusError& e)
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930492 {
493 return hiomap_xlate_errno(e.get_errno());
494 }
495
496 return IPMI_CC_OK;
497}
498
Andrew Jefferydb688e92018-08-23 21:21:30 +0930499static ipmi_ret_t hiomap_get_flash_info(ipmi_request_t request,
500 ipmi_response_t response,
501 ipmi_data_len_t data_len,
502 ipmi_context_t context)
503{
Patrick Venture5b355062018-10-31 14:42:42 -0700504 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jefferydb688e92018-08-23 21:21:30 +0930505
506 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
507 HIOMAPD_IFACE_V2, "GetFlashInfo");
508 try
509 {
510 auto reply = ctx->bus->call(m);
511
512 uint16_t flashSize, eraseSize;
513 reply.read(flashSize, eraseSize);
514
Patrick Venture5b355062018-10-31 14:42:42 -0700515 uint8_t* respdata = (uint8_t*)response;
Andrew Jefferydb688e92018-08-23 21:21:30 +0930516 put(&respdata[0], htole16(flashSize));
517 put(&respdata[2], htole16(eraseSize));
518
519 *data_len = 4;
520 }
Patrick Venture5b355062018-10-31 14:42:42 -0700521 catch (const exception::SdBusError& e)
Andrew Jefferydb688e92018-08-23 21:21:30 +0930522 {
523 return hiomap_xlate_errno(e.get_errno());
524 }
525
526 return IPMI_CC_OK;
527}
528
Patrick Venture5b355062018-10-31 14:42:42 -0700529static ipmi_ret_t hiomap_create_window(struct hiomap* ctx, bool ro,
Andrew Jefferya00f59b2018-08-23 22:25:49 +0930530 ipmi_request_t request,
531 ipmi_response_t response,
532 ipmi_data_len_t data_len)
533{
534 if (*data_len < 4)
535 {
536 return IPMI_CC_REQ_DATA_LEN_INVALID;
537 }
538
Patrick Venture5b355062018-10-31 14:42:42 -0700539 uint8_t* reqdata = (uint8_t*)request;
Andrew Jefferya00f59b2018-08-23 22:25:49 +0930540 auto windowType = ro ? "CreateReadWindow" : "CreateWriteWindow";
541
542 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
543 HIOMAPD_IFACE_V2, windowType);
544 m.append(le16toh(get<uint16_t>(&reqdata[0])));
545 m.append(le16toh(get<uint16_t>(&reqdata[2])));
546
547 try
548 {
549 auto reply = ctx->bus->call(m);
550
551 uint16_t lpcAddress, size, offset;
552 reply.read(lpcAddress, size, offset);
553
Patrick Venture5b355062018-10-31 14:42:42 -0700554 uint8_t* respdata = (uint8_t*)response;
Andrew Jefferya00f59b2018-08-23 22:25:49 +0930555
556 /* FIXME: Assumes v2! */
557 put(&respdata[0], htole16(lpcAddress));
558 put(&respdata[2], htole16(size));
559 put(&respdata[4], htole16(offset));
560
561 *data_len = 6;
562 }
Patrick Venture5b355062018-10-31 14:42:42 -0700563 catch (const exception::SdBusError& e)
Andrew Jefferya00f59b2018-08-23 22:25:49 +0930564 {
565 return hiomap_xlate_errno(e.get_errno());
566 }
567
568 return IPMI_CC_OK;
569}
570
571static ipmi_ret_t hiomap_create_read_window(ipmi_request_t request,
572 ipmi_response_t response,
573 ipmi_data_len_t data_len,
574 ipmi_context_t context)
575{
Patrick Venture5b355062018-10-31 14:42:42 -0700576 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jefferya00f59b2018-08-23 22:25:49 +0930577
578 return hiomap_create_window(ctx, true, request, response, data_len);
579}
580
581static ipmi_ret_t hiomap_create_write_window(ipmi_request_t request,
582 ipmi_response_t response,
583 ipmi_data_len_t data_len,
584 ipmi_context_t context)
585{
Patrick Venture5b355062018-10-31 14:42:42 -0700586 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jefferya00f59b2018-08-23 22:25:49 +0930587
588 return hiomap_create_window(ctx, false, request, response, data_len);
589}
590
Andrew Jefferyb52822c2018-08-23 23:01:39 +0930591static ipmi_ret_t hiomap_close_window(ipmi_request_t request,
592 ipmi_response_t response,
593 ipmi_data_len_t data_len,
594 ipmi_context_t context)
595{
Patrick Venture5b355062018-10-31 14:42:42 -0700596 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jefferyb52822c2018-08-23 23:01:39 +0930597
598 if (*data_len < 1)
599 {
600 return IPMI_CC_REQ_DATA_LEN_INVALID;
601 }
602
Patrick Venture5b355062018-10-31 14:42:42 -0700603 uint8_t* reqdata = (uint8_t*)request;
Andrew Jefferyb52822c2018-08-23 23:01:39 +0930604 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
605 HIOMAPD_IFACE_V2, "CloseWindow");
606 m.append(reqdata[0]);
607
608 try
609 {
610 auto reply = ctx->bus->call(m);
611
612 *data_len = 0;
613 }
Patrick Venture5b355062018-10-31 14:42:42 -0700614 catch (const exception::SdBusError& e)
Andrew Jefferyb52822c2018-08-23 23:01:39 +0930615 {
616 return hiomap_xlate_errno(e.get_errno());
617 }
618
619 return IPMI_CC_OK;
620}
621
Andrew Jeffery9847f1c2018-08-24 09:13:40 +0930622static ipmi_ret_t hiomap_mark_dirty(ipmi_request_t request,
623 ipmi_response_t response,
624 ipmi_data_len_t data_len,
625 ipmi_context_t context)
626{
Patrick Venture5b355062018-10-31 14:42:42 -0700627 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jeffery9847f1c2018-08-24 09:13:40 +0930628
629 if (*data_len < 4)
630 {
631 return IPMI_CC_REQ_DATA_LEN_INVALID;
632 }
633
Patrick Venture5b355062018-10-31 14:42:42 -0700634 uint8_t* reqdata = (uint8_t*)request;
Andrew Jeffery9847f1c2018-08-24 09:13:40 +0930635 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
636 HIOMAPD_IFACE_V2, "MarkDirty");
637 /* FIXME: Assumes v2 */
638 m.append(le16toh(get<uint16_t>(&reqdata[0]))); /* offset */
639 m.append(le16toh(get<uint16_t>(&reqdata[2]))); /* size */
640
641 try
642 {
643 auto reply = ctx->bus->call(m);
644
645 *data_len = 0;
646 }
Patrick Venture5b355062018-10-31 14:42:42 -0700647 catch (const exception::SdBusError& e)
Andrew Jeffery9847f1c2018-08-24 09:13:40 +0930648 {
649 return hiomap_xlate_errno(e.get_errno());
650 }
651
652 return IPMI_CC_OK;
653}
654
Andrew Jeffery7b225fb2018-08-24 09:19:21 +0930655static ipmi_ret_t hiomap_flush(ipmi_request_t request, ipmi_response_t response,
656 ipmi_data_len_t data_len, ipmi_context_t context)
657{
Patrick Venture5b355062018-10-31 14:42:42 -0700658 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jeffery7b225fb2018-08-24 09:19:21 +0930659
660 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
661 HIOMAPD_IFACE_V2, "Flush");
662
663 try
664 {
665 /* FIXME: No argument call assumes v2 */
666 auto reply = ctx->bus->call(m);
667
668 *data_len = 0;
669 }
Patrick Venture5b355062018-10-31 14:42:42 -0700670 catch (const exception::SdBusError& e)
Andrew Jeffery7b225fb2018-08-24 09:19:21 +0930671 {
672 return hiomap_xlate_errno(e.get_errno());
673 }
674
675 return IPMI_CC_OK;
676}
677
Andrew Jeffery99f277a2018-08-24 09:24:04 +0930678static ipmi_ret_t hiomap_ack(ipmi_request_t request, ipmi_response_t response,
679 ipmi_data_len_t data_len, ipmi_context_t context)
680{
Patrick Venture5b355062018-10-31 14:42:42 -0700681 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jeffery99f277a2018-08-24 09:24:04 +0930682
683 if (*data_len < 1)
684 {
685 return IPMI_CC_REQ_DATA_LEN_INVALID;
686 }
687
Patrick Venture5b355062018-10-31 14:42:42 -0700688 uint8_t* reqdata = (uint8_t*)request;
Andrew Jeffery99f277a2018-08-24 09:24:04 +0930689 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
690 HIOMAPD_IFACE_V2, "Ack");
691 auto acked = reqdata[0];
692 m.append(acked);
693
694 try
695 {
696 auto reply = ctx->bus->call(m);
697
Andrew Jeffery99f277a2018-08-24 09:24:04 +0930698 *data_len = 0;
699 }
Patrick Venture5b355062018-10-31 14:42:42 -0700700 catch (const exception::SdBusError& e)
Andrew Jeffery99f277a2018-08-24 09:24:04 +0930701 {
702 return hiomap_xlate_errno(e.get_errno());
703 }
704
705 return IPMI_CC_OK;
706}
707
Andrew Jefferya1e35b82018-08-24 09:39:10 +0930708static ipmi_ret_t hiomap_erase(ipmi_request_t request, ipmi_response_t response,
709 ipmi_data_len_t data_len, ipmi_context_t context)
710{
Patrick Venture5b355062018-10-31 14:42:42 -0700711 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jefferya1e35b82018-08-24 09:39:10 +0930712
713 if (*data_len < 4)
714 {
715 return IPMI_CC_REQ_DATA_LEN_INVALID;
716 }
717
Patrick Venture5b355062018-10-31 14:42:42 -0700718 uint8_t* reqdata = (uint8_t*)request;
Andrew Jefferya1e35b82018-08-24 09:39:10 +0930719 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
720 HIOMAPD_IFACE_V2, "Erase");
721 /* FIXME: Assumes v2 */
722 m.append(le16toh(get<uint16_t>(&reqdata[0]))); /* offset */
723 m.append(le16toh(get<uint16_t>(&reqdata[2]))); /* size */
724
725 try
726 {
727 auto reply = ctx->bus->call(m);
728
729 *data_len = 0;
730 }
Patrick Venture5b355062018-10-31 14:42:42 -0700731 catch (const exception::SdBusError& e)
Andrew Jefferya1e35b82018-08-24 09:39:10 +0930732 {
733 return hiomap_xlate_errno(e.get_errno());
734 }
735
736 return IPMI_CC_OK;
737}
738
Andrew Jeffery04d75132018-09-26 00:58:52 +0930739#define HIOMAP_C_RESET 1
740#define HIOMAP_C_GET_INFO 2
741#define HIOMAP_C_GET_FLASH_INFO 3
742#define HIOMAP_C_CREATE_READ_WINDOW 4
743#define HIOMAP_C_CLOSE_WINDOW 5
744#define HIOMAP_C_CREATE_WRITE_WINDOW 6
745#define HIOMAP_C_MARK_DIRTY 7
746#define HIOMAP_C_FLUSH 8
747#define HIOMAP_C_ACK 9
748#define HIOMAP_C_ERASE 10
749
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930750static const hiomap_command hiomap_commands[] = {
Andrew Jeffery04d75132018-09-26 00:58:52 +0930751 [0] = NULL, /* Invalid command ID */
752 [HIOMAP_C_RESET] = hiomap_reset,
753 [HIOMAP_C_GET_INFO] = hiomap_get_info,
754 [HIOMAP_C_GET_FLASH_INFO] = hiomap_get_flash_info,
755 [HIOMAP_C_CREATE_READ_WINDOW] = hiomap_create_read_window,
756 [HIOMAP_C_CLOSE_WINDOW] = hiomap_close_window,
757 [HIOMAP_C_CREATE_WRITE_WINDOW] = hiomap_create_write_window,
758 [HIOMAP_C_MARK_DIRTY] = hiomap_mark_dirty,
759 [HIOMAP_C_FLUSH] = hiomap_flush,
760 [HIOMAP_C_ACK] = hiomap_ack,
761 [HIOMAP_C_ERASE] = hiomap_erase,
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930762};
763
764/* FIXME: Define this in the "right" place, wherever that is */
765/* FIXME: Double evaluation */
766#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
767
768static ipmi_ret_t hiomap_dispatch(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
769 ipmi_request_t request,
770 ipmi_response_t response,
771 ipmi_data_len_t data_len,
772 ipmi_context_t context)
773{
Patrick Venture5b355062018-10-31 14:42:42 -0700774 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930775
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930776 if (*data_len < 2)
777 {
778 *data_len = 0;
779 return IPMI_CC_REQ_DATA_LEN_INVALID;
780 }
781
Patrick Venture5b355062018-10-31 14:42:42 -0700782 uint8_t* ipmi_req = (uint8_t*)request;
783 uint8_t* ipmi_resp = (uint8_t*)response;
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930784 uint8_t hiomap_cmd = ipmi_req[0];
785
786 if (hiomap_cmd == 0 || hiomap_cmd > ARRAY_SIZE(hiomap_commands) - 1)
787 {
788 *data_len = 0;
789 return IPMI_CC_PARM_OUT_OF_RANGE;
790 }
Andrew Jeffery04d75132018-09-26 00:58:52 +0930791
792 bool is_unversioned =
793 (hiomap_cmd == HIOMAP_C_RESET || hiomap_cmd == HIOMAP_C_GET_INFO ||
794 hiomap_cmd == HIOMAP_C_ACK);
795 if (!is_unversioned && ctx->seq == ipmi_req[1])
796 {
797 *data_len = 0;
798 return IPMI_CC_INVALID_FIELD_REQUEST;
799 }
800
801 ctx->seq = ipmi_req[1];
802
Patrick Venture5b355062018-10-31 14:42:42 -0700803 uint8_t* flash_req = ipmi_req + 2;
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930804 size_t flash_len = *data_len - 2;
Patrick Venture5b355062018-10-31 14:42:42 -0700805 uint8_t* flash_resp = ipmi_resp + 2;
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930806
807 ipmi_ret_t cc =
808 hiomap_commands[hiomap_cmd](flash_req, flash_resp, &flash_len, context);
809 if (cc != IPMI_CC_OK)
810 {
811 *data_len = 0;
812 return cc;
813 }
814
815 /* Populate the response command and sequence */
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930816 ipmi_resp[0] = hiomap_cmd;
Andrew Jeffery04d75132018-09-26 00:58:52 +0930817 ipmi_resp[1] = ctx->seq;
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930818
819 *data_len = flash_len + 2;
820
821 return cc;
822}
823} // namespace flash
824} // namespace openpower
825
826static void register_openpower_hiomap_commands()
827{
Andrew Jeffery619207d2018-11-22 13:09:17 +1100828 using namespace phosphor::logging;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930829 using namespace openpower::flash;
830
Patrick Venture5b355062018-10-31 14:42:42 -0700831 struct hiomap* ctx = new hiomap();
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930832
833 /* Initialise mapping from signal and property names to status bit */
834 ctx->event_lookup["DaemonReady"] = BMC_EVENT_DAEMON_READY;
835 ctx->event_lookup["FlashControlLost"] = BMC_EVENT_FLASH_CTRL_LOST;
836 ctx->event_lookup["WindowReset"] = BMC_EVENT_WINDOW_RESET;
837 ctx->event_lookup["ProtocolReset"] = BMC_EVENT_PROTOCOL_RESET;
838
839 ctx->bus = new bus::bus(ipmid_get_sd_bus_connection());
840
841 /* Initialise signal handling */
842
843 /*
844 * Can't use temporaries here because that causes SEGFAULTs due to slot
845 * destruction (!?), so enjoy the weird wrapping.
846 */
847 ctx->properties =
848 new bus::match::match(std::move(hiomap_match_properties(ctx)));
Andrew Jeffery619207d2018-11-22 13:09:17 +1100849
Vernon Maueryee3064b2019-03-18 10:19:58 -0700850 std::function<SignalResponse(int)> shutdownHandler =
851 [ctx](int signalNumber) {
852 hiomap_protocol_reset(ctx);
853 return sigtermResponse;
854 };
855 registerSignalHandler(ipmi::prioMax, SIGTERM, shutdownHandler);
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930856
857 ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_HIOMAP, ctx,
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930858 openpower::flash::hiomap_dispatch, SYSTEM_INTERFACE);
859}