blob: 925324352b35603aa8b071a12c63b807147c97af [file] [log] [blame]
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +09301// SPDX-License-Identifier: Apache-2.0
2// Copyright (C) 2018 IBM Corp.
3
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +09304#include "hiomap.hpp"
5
6#include <endian.h>
Willy Tu89707262022-06-15 13:27:37 -07007#include <ipmid/api.h>
Andrew Jeffery619207d2018-11-22 13:09:17 +11008#include <signal.h>
9#include <string.h>
10#include <systemd/sd-bus.h>
11#include <systemd/sd-event.h>
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +093012
Andrew Jeffery619207d2018-11-22 13:09:17 +110013#include <cassert>
Patrick Ventureee701962018-10-31 14:51:37 -070014#include <cstring>
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +093015#include <fstream>
Andrew Jeffery0a3358e2018-08-21 10:42:09 +093016#include <functional>
Andrew Jeffery0a3358e2018-08-21 10:42:09 +093017#include <iostream>
Willy Tu89707262022-06-15 13:27:37 -070018#include <ipmid-host/cmd-utils.hpp>
19#include <ipmid-host/cmd.hpp>
Vernon Maueryee3064b2019-03-18 10:19:58 -070020#include <ipmid/api.hpp>
Patrick Ventureee701962018-10-31 14:51:37 -070021#include <map>
Andrew Jeffery0a3358e2018-08-21 10:42:09 +093022#include <phosphor-logging/log.hpp>
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +093023#include <sdbusplus/bus.hpp>
Andrew Jeffery0a3358e2018-08-21 10:42:09 +093024#include <sdbusplus/bus/match.hpp>
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +093025#include <sdbusplus/exception.hpp>
Patrick Ventureee701962018-10-31 14:51:37 -070026#include <string>
27#include <tuple>
Willy Tu89707262022-06-15 13:27:37 -070028#include <unordered_map>
Patrick Ventureee701962018-10-31 14:51:37 -070029#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;
Andrew Jeffery619207d2018-11-22 13:09:17 +1100254
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930255struct hiomap
256{
Patrick Venture5b355062018-10-31 14:42:42 -0700257 bus::bus* bus;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930258
259 /* Signals */
Patrick Venture5b355062018-10-31 14:42:42 -0700260 bus::match::match* properties;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930261
262 /* Protocol state */
263 std::map<std::string, int> event_lookup;
264 uint8_t bmc_events;
Andrew Jeffery04d75132018-09-26 00:58:52 +0930265 uint8_t seq;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930266};
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930267
Vernon Maueryee3064b2019-03-18 10:19:58 -0700268SignalResponse sigtermResponse = SignalResponse::continueExecution;
269
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930270/* TODO: Replace get/put with packed structs and direct assignment */
Patrick Venture5b355062018-10-31 14:42:42 -0700271template <typename T>
272static inline T get(void* buf)
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930273{
274 T t;
Patrick Ventureee701962018-10-31 14:51:37 -0700275 std::memcpy(&t, buf, sizeof(t));
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930276 return t;
277}
278
Patrick Venture5b355062018-10-31 14:42:42 -0700279template <typename T>
280static inline void put(void* buf, T&& t)
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930281{
Patrick Ventureee701962018-10-31 14:51:37 -0700282 std::memcpy(buf, &t, sizeof(t));
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930283}
284
Willy Tu89707262022-06-15 13:27:37 -0700285using hiomap_command =
286 std::function<ipmi_ret_t(ipmi_request_t req, ipmi_response_t resp,
287 ipmi_data_len_t data_len, ipmi_context_t context)>;
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930288struct 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;
322
323 if (!status)
324 {
325 log<level::ERR>("Failed to deliver host command",
326 entry("SEL_COMMAND=%x:%x", cmd.first, cmd.second));
327 }
Andrew Jeffery619207d2018-11-22 13:09:17 +1100328
329 assert(active_event_updates);
330 active_event_updates--;
331 if (!active_event_updates)
332 {
Vernon Maueryee3064b2019-03-18 10:19:58 -0700333 sigtermResponse = SignalResponse::continueExecution;
334 log<level::DEBUG>("Unblocked SIGTERM");
Andrew Jeffery619207d2018-11-22 13:09:17 +1100335 }
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930336}
337
Patrick Venture5b355062018-10-31 14:42:42 -0700338static int hiomap_handle_property_update(struct hiomap* ctx,
339 sdbusplus::message::message& msg)
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930340{
Andrew Jeffery619207d2018-11-22 13:09:17 +1100341 using namespace phosphor::logging;
342
Patrick Williams80d5bca2020-05-13 17:49:26 -0500343 std::map<std::string, std::variant<bool>> msgData;
Andrew Jeffery619207d2018-11-22 13:09:17 +1100344
Vernon Maueryee3064b2019-03-18 10:19:58 -0700345 sigtermResponse = SignalResponse::breakExecution;
Andrew Jeffery619207d2018-11-22 13:09:17 +1100346 if (!active_event_updates)
347 {
Vernon Maueryee3064b2019-03-18 10:19:58 -0700348 sigtermResponse = SignalResponse::breakExecution;
349 log<level::DEBUG>("Blocked SIGTERM");
Andrew Jeffery619207d2018-11-22 13:09:17 +1100350 }
351 active_event_updates++;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930352
353 std::string iface;
354 msg.read(iface, msgData);
355
Patrick Venture5b355062018-10-31 14:42:42 -0700356 for (auto const& x : msgData)
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930357 {
358 if (!ctx->event_lookup.count(x.first))
359 {
360 /* Unsupported event? */
361 continue;
362 }
363
364 uint8_t mask = ctx->event_lookup[x.first];
Patrick Williams3d420922020-05-13 12:26:25 -0500365 auto value = std::get<bool>(x.second);
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930366
367 if (value)
368 {
369 ctx->bmc_events |= mask;
370 }
371 else
372 {
373 ctx->bmc_events &= ~mask;
374 }
375 }
376
377 auto cmd = std::make_pair(IPMI_CMD_HIOMAP_EVENT, ctx->bmc_events);
378
379 ipmid_send_cmd_to_host(std::make_tuple(cmd, ipmi_hiomap_event_response));
380
381 return 0;
382}
383
Willy Tu89707262022-06-15 13:27:37 -0700384static int hiomap_protocol_reset_response([[maybe_unused]] IpmiCmdData cmd,
385 [[maybe_unused]] bool status)
Andrew Jeffery619207d2018-11-22 13:09:17 +1100386{
Vernon Maueryee3064b2019-03-18 10:19:58 -0700387 // If this is running in signal context, ipmid will shutdown
388 // the event queue as the last signal handler
Adriana Kobylakd4b7f5e2019-04-16 15:40:18 -0500389 sigtermResponse = SignalResponse::continueExecution;
Vernon Maueryee3064b2019-03-18 10:19:58 -0700390 return 0;
Andrew Jeffery619207d2018-11-22 13:09:17 +1100391}
392
Vernon Maueryee3064b2019-03-18 10:19:58 -0700393static int hiomap_protocol_reset(struct hiomap* ctx)
Andrew Jeffery619207d2018-11-22 13:09:17 +1100394{
Andrew Jeffery619207d2018-11-22 13:09:17 +1100395 if (ctx->bmc_events == BMC_EVENT_PROTOCOL_RESET)
396 {
Vernon Maueryee3064b2019-03-18 10:19:58 -0700397 // If this is running in signal context, ipmid will shutdown
398 // the event queue as the last signal handler
Adriana Kobylakd4b7f5e2019-04-16 15:40:18 -0500399 sigtermResponse = SignalResponse::continueExecution;
Vernon Maueryee3064b2019-03-18 10:19:58 -0700400 return 0;
Andrew Jeffery619207d2018-11-22 13:09:17 +1100401 }
402
403 /*
404 * Send an attention indicating the hiomapd has died
405 * (BMC_EVENT_DAEMON_READY cleared) and that the protocol has been reset
406 * (BMC_EVENT_PROTOCOL_RESET set) to indicate to the host that it needs to
407 * wait for the BMC to come back and renegotiate the protocol.
408 *
409 * We know this to be the case in systems that integrate
410 * openpower-host-ipmi-flash, as hiomapd's unit depends on
411 * phosphor-ipmi-host, and thus hiomapd has been terminated before ipmid
412 * receives SIGTERM.
413 */
414 auto cmd = std::make_pair(IPMI_CMD_HIOMAP_EVENT, BMC_EVENT_PROTOCOL_RESET);
415
416 auto cmdHandler = std::make_tuple(cmd, hiomap_protocol_reset_response);
417 ipmid_send_cmd_to_host(cmdHandler);
418
419 return 0;
420}
421
Patrick Venture5b355062018-10-31 14:42:42 -0700422static bus::match::match hiomap_match_properties(struct hiomap* ctx)
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930423{
424 auto properties =
425 bus::match::rules::propertiesChanged(HIOMAPD_OBJECT, HIOMAPD_IFACE_V2);
426
427 bus::match::match match(
428 *ctx->bus, properties,
429 std::bind(hiomap_handle_property_update, ctx, std::placeholders::_1));
430
431 return match;
432}
433
Willy Tu89707262022-06-15 13:27:37 -0700434static ipmi_ret_t hiomap_reset([[maybe_unused]] ipmi_request_t request,
435 [[maybe_unused]] ipmi_response_t response,
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930436 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 Williams95510382021-09-02 09:26:42 -0500448 catch (const exception::exception& 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 Williams95510382021-09-02 09:26:42 -0500491 catch (const exception::exception& 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
Willy Tu89707262022-06-15 13:27:37 -0700499static ipmi_ret_t hiomap_get_flash_info([[maybe_unused]] ipmi_request_t request,
Andrew Jefferydb688e92018-08-23 21:21:30 +0930500 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 Williams95510382021-09-02 09:26:42 -0500521 catch (const exception::exception& 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 Williams95510382021-09-02 09:26:42 -0500563 catch (const exception::exception& 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,
Willy Tu89707262022-06-15 13:27:37 -0700592 [[maybe_unused]] ipmi_response_t response,
Andrew Jefferyb52822c2018-08-23 23:01:39 +0930593 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 Williams95510382021-09-02 09:26:42 -0500614 catch (const exception::exception& 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,
Willy Tu89707262022-06-15 13:27:37 -0700623 [[maybe_unused]] ipmi_response_t response,
Andrew Jeffery9847f1c2018-08-24 09:13:40 +0930624 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 Williams95510382021-09-02 09:26:42 -0500647 catch (const exception::exception& 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
Willy Tu89707262022-06-15 13:27:37 -0700655static ipmi_ret_t hiomap_flush([[maybe_unused]] ipmi_request_t request,
656 [[maybe_unused]] ipmi_response_t response,
Andrew Jeffery7b225fb2018-08-24 09:19:21 +0930657 ipmi_data_len_t data_len, ipmi_context_t context)
658{
Patrick Venture5b355062018-10-31 14:42:42 -0700659 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jeffery7b225fb2018-08-24 09:19:21 +0930660
661 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
662 HIOMAPD_IFACE_V2, "Flush");
663
664 try
665 {
666 /* FIXME: No argument call assumes v2 */
667 auto reply = ctx->bus->call(m);
668
669 *data_len = 0;
670 }
Patrick Williams95510382021-09-02 09:26:42 -0500671 catch (const exception::exception& e)
Andrew Jeffery7b225fb2018-08-24 09:19:21 +0930672 {
673 return hiomap_xlate_errno(e.get_errno());
674 }
675
676 return IPMI_CC_OK;
677}
678
Willy Tu89707262022-06-15 13:27:37 -0700679static ipmi_ret_t hiomap_ack(ipmi_request_t request,
680 [[maybe_unused]] ipmi_response_t response,
Andrew Jeffery99f277a2018-08-24 09:24:04 +0930681 ipmi_data_len_t data_len, ipmi_context_t context)
682{
Patrick Venture5b355062018-10-31 14:42:42 -0700683 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jeffery99f277a2018-08-24 09:24:04 +0930684
685 if (*data_len < 1)
686 {
687 return IPMI_CC_REQ_DATA_LEN_INVALID;
688 }
689
Patrick Venture5b355062018-10-31 14:42:42 -0700690 uint8_t* reqdata = (uint8_t*)request;
Andrew Jeffery99f277a2018-08-24 09:24:04 +0930691 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
692 HIOMAPD_IFACE_V2, "Ack");
693 auto acked = reqdata[0];
694 m.append(acked);
695
696 try
697 {
698 auto reply = ctx->bus->call(m);
699
Andrew Jeffery99f277a2018-08-24 09:24:04 +0930700 *data_len = 0;
701 }
Patrick Williams95510382021-09-02 09:26:42 -0500702 catch (const exception::exception& e)
Andrew Jeffery99f277a2018-08-24 09:24:04 +0930703 {
704 return hiomap_xlate_errno(e.get_errno());
705 }
706
707 return IPMI_CC_OK;
708}
709
Willy Tu89707262022-06-15 13:27:37 -0700710static ipmi_ret_t hiomap_erase(ipmi_request_t request,
711 [[maybe_unused]] ipmi_response_t response,
Andrew Jefferya1e35b82018-08-24 09:39:10 +0930712 ipmi_data_len_t data_len, ipmi_context_t context)
713{
Patrick Venture5b355062018-10-31 14:42:42 -0700714 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jefferya1e35b82018-08-24 09:39:10 +0930715
716 if (*data_len < 4)
717 {
718 return IPMI_CC_REQ_DATA_LEN_INVALID;
719 }
720
Patrick Venture5b355062018-10-31 14:42:42 -0700721 uint8_t* reqdata = (uint8_t*)request;
Andrew Jefferya1e35b82018-08-24 09:39:10 +0930722 auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
723 HIOMAPD_IFACE_V2, "Erase");
724 /* FIXME: Assumes v2 */
725 m.append(le16toh(get<uint16_t>(&reqdata[0]))); /* offset */
726 m.append(le16toh(get<uint16_t>(&reqdata[2]))); /* size */
727
728 try
729 {
730 auto reply = ctx->bus->call(m);
731
732 *data_len = 0;
733 }
Patrick Williams95510382021-09-02 09:26:42 -0500734 catch (const exception::exception& e)
Andrew Jefferya1e35b82018-08-24 09:39:10 +0930735 {
736 return hiomap_xlate_errno(e.get_errno());
737 }
738
739 return IPMI_CC_OK;
740}
741
Andrew Jeffery04d75132018-09-26 00:58:52 +0930742#define HIOMAP_C_RESET 1
743#define HIOMAP_C_GET_INFO 2
744#define HIOMAP_C_GET_FLASH_INFO 3
745#define HIOMAP_C_CREATE_READ_WINDOW 4
746#define HIOMAP_C_CLOSE_WINDOW 5
747#define HIOMAP_C_CREATE_WRITE_WINDOW 6
748#define HIOMAP_C_MARK_DIRTY 7
749#define HIOMAP_C_FLUSH 8
750#define HIOMAP_C_ACK 9
751#define HIOMAP_C_ERASE 10
752
Willy Tu89707262022-06-15 13:27:37 -0700753static const std::unordered_map<uint8_t, hiomap_command> hiomap_commands = {
754 {0, nullptr}, /* Invalid command ID */
755 {HIOMAP_C_RESET, hiomap_reset},
756 {HIOMAP_C_GET_INFO, hiomap_get_info},
757 {HIOMAP_C_GET_FLASH_INFO, hiomap_get_flash_info},
758 {HIOMAP_C_CREATE_READ_WINDOW, hiomap_create_read_window},
759 {HIOMAP_C_CLOSE_WINDOW, hiomap_close_window},
760 {HIOMAP_C_CREATE_WRITE_WINDOW, hiomap_create_write_window},
761 {HIOMAP_C_MARK_DIRTY, hiomap_mark_dirty},
762 {HIOMAP_C_FLUSH, hiomap_flush},
763 {HIOMAP_C_ACK, hiomap_ack},
764 {HIOMAP_C_ERASE, hiomap_erase},
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930765};
766
767/* FIXME: Define this in the "right" place, wherever that is */
768/* FIXME: Double evaluation */
769#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
770
Willy Tu89707262022-06-15 13:27:37 -0700771static ipmi_ret_t hiomap_dispatch([[maybe_unused]] ipmi_netfn_t netfn,
772 [[maybe_unused]] ipmi_cmd_t cmd,
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930773 ipmi_request_t request,
774 ipmi_response_t response,
775 ipmi_data_len_t data_len,
776 ipmi_context_t context)
777{
Patrick Venture5b355062018-10-31 14:42:42 -0700778 struct hiomap* ctx = static_cast<struct hiomap*>(context);
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930779
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930780 if (*data_len < 2)
781 {
782 *data_len = 0;
783 return IPMI_CC_REQ_DATA_LEN_INVALID;
784 }
785
Patrick Venture5b355062018-10-31 14:42:42 -0700786 uint8_t* ipmi_req = (uint8_t*)request;
787 uint8_t* ipmi_resp = (uint8_t*)response;
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930788 uint8_t hiomap_cmd = ipmi_req[0];
789
Willy Tu89707262022-06-15 13:27:37 -0700790 if (hiomap_cmd == 0 || hiomap_cmd > hiomap_commands.size() - 1)
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930791 {
792 *data_len = 0;
793 return IPMI_CC_PARM_OUT_OF_RANGE;
794 }
Andrew Jeffery04d75132018-09-26 00:58:52 +0930795
796 bool is_unversioned =
797 (hiomap_cmd == HIOMAP_C_RESET || hiomap_cmd == HIOMAP_C_GET_INFO ||
798 hiomap_cmd == HIOMAP_C_ACK);
799 if (!is_unversioned && ctx->seq == ipmi_req[1])
800 {
801 *data_len = 0;
802 return IPMI_CC_INVALID_FIELD_REQUEST;
803 }
804
805 ctx->seq = ipmi_req[1];
806
Patrick Venture5b355062018-10-31 14:42:42 -0700807 uint8_t* flash_req = ipmi_req + 2;
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930808 size_t flash_len = *data_len - 2;
Patrick Venture5b355062018-10-31 14:42:42 -0700809 uint8_t* flash_resp = ipmi_resp + 2;
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930810
Willy Tu89707262022-06-15 13:27:37 -0700811 auto command = hiomap_commands.find(hiomap_cmd);
812 if (command == hiomap_commands.end())
813 {
814 *data_len = 0;
815 return IPMI_CC_INVALID;
816 }
817 ipmi_ret_t cc = command->second(flash_req, flash_resp, &flash_len, context);
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930818 if (cc != IPMI_CC_OK)
819 {
820 *data_len = 0;
821 return cc;
822 }
823
824 /* Populate the response command and sequence */
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930825 ipmi_resp[0] = hiomap_cmd;
Andrew Jeffery04d75132018-09-26 00:58:52 +0930826 ipmi_resp[1] = ctx->seq;
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930827
828 *data_len = flash_len + 2;
829
830 return cc;
831}
832} // namespace flash
833} // namespace openpower
834
835static void register_openpower_hiomap_commands()
836{
Andrew Jeffery619207d2018-11-22 13:09:17 +1100837 using namespace phosphor::logging;
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930838 using namespace openpower::flash;
839
Patrick Venture5b355062018-10-31 14:42:42 -0700840 struct hiomap* ctx = new hiomap();
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930841
842 /* Initialise mapping from signal and property names to status bit */
843 ctx->event_lookup["DaemonReady"] = BMC_EVENT_DAEMON_READY;
844 ctx->event_lookup["FlashControlLost"] = BMC_EVENT_FLASH_CTRL_LOST;
845 ctx->event_lookup["WindowReset"] = BMC_EVENT_WINDOW_RESET;
846 ctx->event_lookup["ProtocolReset"] = BMC_EVENT_PROTOCOL_RESET;
847
848 ctx->bus = new bus::bus(ipmid_get_sd_bus_connection());
849
850 /* Initialise signal handling */
851
852 /*
853 * Can't use temporaries here because that causes SEGFAULTs due to slot
854 * destruction (!?), so enjoy the weird wrapping.
855 */
856 ctx->properties =
857 new bus::match::match(std::move(hiomap_match_properties(ctx)));
Andrew Jeffery619207d2018-11-22 13:09:17 +1100858
Vernon Maueryee3064b2019-03-18 10:19:58 -0700859 std::function<SignalResponse(int)> shutdownHandler =
Willy Tu89707262022-06-15 13:27:37 -0700860 [ctx]([[maybe_unused]] int signalNumber) {
Vernon Maueryee3064b2019-03-18 10:19:58 -0700861 hiomap_protocol_reset(ctx);
862 return sigtermResponse;
863 };
864 registerSignalHandler(ipmi::prioMax, SIGTERM, shutdownHandler);
Andrew Jeffery0a3358e2018-08-21 10:42:09 +0930865
866 ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_HIOMAP, ctx,
Andrew Jeffery2c07f6f2018-08-10 16:24:32 +0930867 openpower::flash::hiomap_dispatch, SYSTEM_INTERFACE);
868}