12c07f6f0SAndrew Jeffery // SPDX-License-Identifier: Apache-2.0
22c07f6f0SAndrew Jeffery // Copyright (C) 2018 IBM Corp.
32c07f6f0SAndrew Jeffery 
42c07f6f0SAndrew Jeffery #include "hiomap.hpp"
52c07f6f0SAndrew Jeffery 
62c07f6f0SAndrew Jeffery #include <endian.h>
789707266SWilly Tu #include <ipmid/api.h>
8619207dcSAndrew Jeffery #include <signal.h>
9619207dcSAndrew Jeffery #include <string.h>
10619207dcSAndrew Jeffery #include <systemd/sd-bus.h>
11619207dcSAndrew Jeffery #include <systemd/sd-event.h>
122c07f6f0SAndrew Jeffery 
13*0d5b076eSPatrick Williams #include <ipmid-host/cmd-utils.hpp>
14*0d5b076eSPatrick Williams #include <ipmid-host/cmd.hpp>
15*0d5b076eSPatrick Williams #include <ipmid/api.hpp>
16*0d5b076eSPatrick Williams #include <phosphor-logging/log.hpp>
17*0d5b076eSPatrick Williams #include <sdbusplus/bus.hpp>
18*0d5b076eSPatrick Williams #include <sdbusplus/bus/match.hpp>
19*0d5b076eSPatrick Williams #include <sdbusplus/exception.hpp>
20*0d5b076eSPatrick Williams 
21619207dcSAndrew Jeffery #include <cassert>
22ee70196bSPatrick Venture #include <cstring>
232c07f6f0SAndrew Jeffery #include <fstream>
240a3358e7SAndrew Jeffery #include <functional>
250a3358e7SAndrew Jeffery #include <iostream>
26ee70196bSPatrick Venture #include <map>
27ee70196bSPatrick Venture #include <string>
28ee70196bSPatrick Venture #include <tuple>
2989707266SWilly Tu #include <unordered_map>
30ee70196bSPatrick Venture #include <utility>
312c07f6f0SAndrew Jeffery 
32619207dcSAndrew Jeffery /*
33619207dcSAndrew Jeffery 
34619207dcSAndrew Jeffery Design and integration notes
35619207dcSAndrew Jeffery ============================
36619207dcSAndrew Jeffery 
37619207dcSAndrew Jeffery The primary motivation of the Host I/O Mapping protocol (HIOMAP) is to mediate
38619207dcSAndrew Jeffery host access to a BMC-controlled flash chip housing the host's boot firmware.
39619207dcSAndrew Jeffery 
40619207dcSAndrew Jeffery openpower-host-ipmi-flash facilitates the system design of transporting the
41619207dcSAndrew Jeffery HIOMAP protocol[1] over IPMI. This is somewhat abusive of IPMI, basically
42619207dcSAndrew Jeffery treating the BT interface as a mailbox with an interrupt each way between the
43619207dcSAndrew Jeffery BMC and the host.
44619207dcSAndrew Jeffery 
45619207dcSAndrew Jeffery [1] https://github.com/openbmc/mboxbridge/blob/master/Documentation/protocol.md
46619207dcSAndrew Jeffery 
47619207dcSAndrew Jeffery Using IPMI in this way has a number of challenges, a lot of them on the host
48619207dcSAndrew Jeffery side where we need to bring up the LPC and BT interfaces to enable IPMI before
49619207dcSAndrew Jeffery accessing the flash, and before any interrupts are enabled. There are also
50619207dcSAndrew Jeffery challenges on the BMC side with the design of the current implementation. We
51619207dcSAndrew Jeffery will cover those here.
52619207dcSAndrew Jeffery 
53619207dcSAndrew Jeffery BMC-side System Design and Integration Issues
54619207dcSAndrew Jeffery ---------------------------------------------
55619207dcSAndrew Jeffery 
56619207dcSAndrew Jeffery The current design is that we have the HIOMAP daemon, mboxd (to be renamed),
57619207dcSAndrew Jeffery exposing a set of DBus interfaces. Whilst the spec defines the IPMI transport
58619207dcSAndrew Jeffery message packing, mboxd knows nothing of IPMI itself, instead relying on the
59619207dcSAndrew Jeffery DBus interface to receive messages from ipmid. ipmid in-turn knows nothing of
60619207dcSAndrew Jeffery the interfaces communicating with it, also relying on DBus to receive messages
61619207dcSAndrew Jeffery from interface-specific daemons, e.g. btbridged[2].
62619207dcSAndrew Jeffery 
63619207dcSAndrew Jeffery [2] https://github.com/openbmc/btbridge
64619207dcSAndrew Jeffery 
65619207dcSAndrew Jeffery For this design to function correctly we must ensure that the daemons are
66619207dcSAndrew Jeffery started and shut down in a reasonable order, however defining that order is
67619207dcSAndrew Jeffery somewhat tricky:
68619207dcSAndrew Jeffery 
69619207dcSAndrew Jeffery 1. systemd uses Wants=/Before=/After= relationships in units to define both
70619207dcSAndrew Jeffery    start-up *and* shutdown order, in stack push / pop order respectively.
71619207dcSAndrew Jeffery 2. Clearly ipmid depends on btbridged to receive messages sent by signals and
72619207dcSAndrew Jeffery    replied to by method calls, so it needs a Wants=/After= relationship on
73619207dcSAndrew Jeffery    btbridged
74619207dcSAndrew Jeffery 3. mboxd depends on ipmid to receive messages sent by method call, and issues a
75619207dcSAndrew Jeffery    PropertiesChanged signal to notify of state changes.
76619207dcSAndrew Jeffery 
77619207dcSAndrew Jeffery Point 3. suggests mboxd should have a Wants=/Before= relationship with ipmid to
78619207dcSAndrew Jeffery ensure ipmid can call into mboxd as messages arrive. However, this causes some
79619207dcSAndrew Jeffery grief with shutdown of the BMC, as mboxd needs to issue a state-change
80619207dcSAndrew Jeffery notification when it is shut down to inform the host that will not repsond to
81619207dcSAndrew Jeffery future requests and that the protocol state has been reset. If mboxd has a
82619207dcSAndrew Jeffery Wants=/Before= relationship with ipmid this message will never propagate to the
83619207dcSAndrew Jeffery host, as ipmid will be shut by systemd before mboxd.
84619207dcSAndrew Jeffery 
85619207dcSAndrew Jeffery The above leads to mboxd having a Wants=/After= relationship with ipmid. This
86619207dcSAndrew Jeffery ensures that if mboxd is restarted on its own the correct state changes will be
87619207dcSAndrew Jeffery propagated to the host. The case where ipmid attempts to call into mboxd's DBus
88619207dcSAndrew Jeffery interface before mboxd is ready is mitigated by the ready bit in the protocol's
89619207dcSAndrew Jeffery BMC status, which will not yet be set, preventing a conforming host from
90619207dcSAndrew Jeffery attempting to contact mboxd.
91619207dcSAndrew Jeffery 
92619207dcSAndrew Jeffery While this ordering prevents mboxd from being terminated before ipmid, there is
93619207dcSAndrew Jeffery no control over the *scheduling* of processes to ensure the PropertiesChanged
94619207dcSAndrew Jeffery signal emitted by mboxd before mboxd is terminated is seen by ipmid before
95619207dcSAndrew Jeffery *ipmid* is also terminated. This leads to our first implementation wart:
96619207dcSAndrew Jeffery 
97619207dcSAndrew Jeffery   On the basis that mboxd has a Wants=/After= relationship with ipmid,
98619207dcSAndrew Jeffery   openpower-host-ipmi-flash will emit an HIOMAP BMC status event to the host
99619207dcSAndrew Jeffery   with the value BMC_EVENT_PROTOCOL_RESET upon receiving SIGTERM iff the BMC
100619207dcSAndrew Jeffery   state is not already set to BMC_EVENT_PROTOCOL_RESET.
101619207dcSAndrew Jeffery 
102619207dcSAndrew Jeffery If ipmid has received SIGTERM the assumption is that it is systemd that is
103619207dcSAndrew Jeffery sending it, and that the Wants=/After= relationship requires that mboxd has
104619207dcSAndrew Jeffery been terminated before ipmid receives SIGTERM. By ensuring
105619207dcSAndrew Jeffery openpower-host-ipmi-flash emits the BMC event state we close the race where the
106619207dcSAndrew Jeffery host is not informed of the termination of mboxd due to scheduling ipmid (to
107619207dcSAndrew Jeffery deliver SIGTERM) prior to scheduling dbus-daemon, where the PropertiesChanged
108619207dcSAndrew Jeffery event would be delivered from mboxd to ipmid.
109619207dcSAndrew Jeffery 
110619207dcSAndrew Jeffery Observations on the IPMI Specification and Design Details of ipmid
111619207dcSAndrew Jeffery ------------------------------------------------------------------
112619207dcSAndrew Jeffery 
113619207dcSAndrew Jeffery In addition to the system-level design problems with delivering
114619207dcSAndrew Jeffery PropertiesChanged signals during shutdown, IPMI specification and ipmid design
115619207dcSAndrew Jeffery issues exist that make it tedious to ensure that events will be correctly
116619207dcSAndrew Jeffery delivered to the host.
117619207dcSAndrew Jeffery 
118619207dcSAndrew Jeffery The first necessary observation is that the mechanism for delivering BMC state
119619207dcSAndrew Jeffery change events from mboxd to the host over IPMI uses the SMS ATN bit to indicate
120619207dcSAndrew Jeffery a message is ready for delivery from the BMC to the host system. Retrieving the
121619207dcSAndrew Jeffery BMC state data involves the host recognising that the SMS ATN bit is set,
122619207dcSAndrew Jeffery performing Get Message Flags transaction with the BMC followed by a subsequent
123619207dcSAndrew Jeffery Get Message transaction. Thus, delivery of the HIOMAP protocol's BMC status is
124619207dcSAndrew Jeffery not an atomic event.
125619207dcSAndrew Jeffery 
126619207dcSAndrew Jeffery The second necessary observation is that the kernel delivers signals
127619207dcSAndrew Jeffery asynchronously. This couples badly with IPMI's event delivery not being atomic:
128619207dcSAndrew Jeffery ipmid can win the race against SIGTERM to receive the PropertiesChanged event
129619207dcSAndrew Jeffery from mboxd, but lose the race to complete delivery to the host.
130619207dcSAndrew Jeffery 
131619207dcSAndrew Jeffery   On this basis, we need to block the delivery of SIGTERM to ipmid until ipmid
132619207dcSAndrew Jeffery   has completed the set of `SMS ATN`/`Get Message Flags`/`Get Message`
133619207dcSAndrew Jeffery   transactions with the host
134619207dcSAndrew Jeffery 
135619207dcSAndrew Jeffery One approach to this would be to configure a custom SIGTERM handler that sets
136619207dcSAndrew Jeffery some global application state to indicate that SIGTERM has been delivered. A
137619207dcSAndrew Jeffery better approach that avoids the need for global application state is to simply
138619207dcSAndrew Jeffery block the signal until we are ready to handle it, which we can do via
139619207dcSAndrew Jeffery sigprocmask(2).
140619207dcSAndrew Jeffery 
141619207dcSAndrew Jeffery The existing design of ipmid makes it feasible to block and unblock
142619207dcSAndrew Jeffery asynchronous SIGTERM as we require. ipmid_send_cmd_to_host() takes a CallBack
143619207dcSAndrew Jeffery function as an argument, which is invoked by
144619207dcSAndrew Jeffery phosphor::host::command::Manager::getNextCommand(). The documentation for
145619207dcSAndrew Jeffery phosphor::host::command::Manager::getNextCommand() says:
146619207dcSAndrew Jeffery 
147619207dcSAndrew Jeffery   @brief  Extracts the next entry in the queue and returns
148619207dcSAndrew Jeffery           Command and data part of it.
149619207dcSAndrew Jeffery 
150619207dcSAndrew Jeffery   @detail Also calls into the registered handlers so that they can now
151619207dcSAndrew Jeffery           send the CommandComplete signal since the interface contract
152619207dcSAndrew Jeffery           is that we emit this signal once the message has been
153619207dcSAndrew Jeffery           passed to the host (which is required when calling this)
154619207dcSAndrew Jeffery 
155619207dcSAndrew Jeffery           Also, if the queue has more commands, then it will alert the
156619207dcSAndrew Jeffery           host
157619207dcSAndrew Jeffery 
158619207dcSAndrew Jeffery However, its description is not entirely accurate. The callback function is
159619207dcSAndrew Jeffery invoked when ipmid *dequeues* the data to send to the host: Delivery of the
160619207dcSAndrew Jeffery data to the host occurs at some *after* the callback has been invoked.
161619207dcSAndrew Jeffery 
162619207dcSAndrew Jeffery Invoking the callback before completion of delivery of the data to the host
163619207dcSAndrew Jeffery nullifies the approach of unblocking asynchronous SIGTERM in the callback
164619207dcSAndrew Jeffery associated with sending the HIOMAP BMC state event to the host, as the BMC
165619207dcSAndrew Jeffery kernel can asynchronously terminate the process between the callback being
166619207dcSAndrew Jeffery invoked and the host receiving the BMC state event data.
167619207dcSAndrew Jeffery 
168619207dcSAndrew Jeffery Overcoming this issue hinges on a significant implementation detail of ipmid:
169619207dcSAndrew Jeffery 
170619207dcSAndrew Jeffery   ipmid uses an sd_event loop in the main function to pump DBus events.
171619207dcSAndrew Jeffery 
172619207dcSAndrew Jeffery This leads to a third necessary observation:
173619207dcSAndrew Jeffery 
174619207dcSAndrew Jeffery   sd_event can be used to process UNIX signals as well as other events by way
175619207dcSAndrew Jeffery   of Linux's signalfd(2) interface.
176619207dcSAndrew Jeffery 
177619207dcSAndrew Jeffery The fact that sd_event is used to pump DBus events means that ipmid can remain
178619207dcSAndrew Jeffery a single-threaded process. By remaining single-threaded we know that events
179619207dcSAndrew Jeffery processing is sequencial and no two events can be processed simultaneously. A
180619207dcSAndrew Jeffery corollary of this is that DBus events and UNIX signals are serialised with
181619207dcSAndrew Jeffery respect to eachother.
182619207dcSAndrew Jeffery 
183619207dcSAndrew Jeffery The fourth necessary observation is that we do not need to pump sd_event in
184619207dcSAndrew Jeffery order to complete DBus method calls; sd_bus will handle the pumping independent
185619207dcSAndrew Jeffery of the main loop in order to complete the method invocation.
186619207dcSAndrew Jeffery 
187619207dcSAndrew Jeffery Implementing Reliable HIOMAP BMC Status Event Delivery
188619207dcSAndrew Jeffery ------------------------------------------------------
189619207dcSAndrew Jeffery 
190619207dcSAndrew Jeffery We achieve reliable delivery of HIOMAP BMC status events in the following way:
191619207dcSAndrew Jeffery 
192619207dcSAndrew Jeffery 1. During plugin initialisation, mask SIGTERM using sigprocmask(2)
193619207dcSAndrew Jeffery 2. Subsequent to masking SIGTERM, register
194619207dcSAndrew Jeffery    openpower::flash::hiomap_protocol_reset() as the SIGTERM handler using
195619207dcSAndrew Jeffery    sd_event_add_signal() to hook a signalfd(2) into sd_event
196619207dcSAndrew Jeffery 3. openpower::flash::hiomap_protocol_reset() implements the logic to send the
197619207dcSAndrew Jeffery    BMC_EVENT_PROTOCOL_RESET state to the host if necessary, otherwise terminate
198619207dcSAndrew Jeffery    the sd_event loop.
199619207dcSAndrew Jeffery 4. If it is necessary to send BMC_EVENT_PROTOCOL_RESET to the host in 3, assign
200619207dcSAndrew Jeffery    a callback handler that terminates the sd_event loop, which is only
201619207dcSAndrew Jeffery    processed after the current iteration is complete.
202619207dcSAndrew Jeffery 
203619207dcSAndrew Jeffery This process and its use of signalfd integration in the sd_event loop
204619207dcSAndrew Jeffery eliminates the following three races:
205619207dcSAndrew Jeffery 
206619207dcSAndrew Jeffery 1. The scheduler race between mboxd, dbus-daemon and ipmid, by having
207619207dcSAndrew Jeffery    openpower-host-ipmi-flash conditionally deliver the protocol reset event if
208619207dcSAndrew Jeffery    no such message has been received from mboxd
209619207dcSAndrew Jeffery 2. The race between delivering the BMC status event to the host and ipmid
210619207dcSAndrew Jeffery    receiving asynchronous SIGTERM after receiving the PropertiesChanged event
211619207dcSAndrew Jeffery    from mboxd
212619207dcSAndrew Jeffery 3. The race to deliver the BMC status data to the host after unblocking
213619207dcSAndrew Jeffery    asynchronous SIGTERM in the host command callback and before receiving
214619207dcSAndrew Jeffery    asynchronous SIGTERM.
215619207dcSAndrew Jeffery 
216619207dcSAndrew Jeffery Ultimately, ipmid could benefit from a redesign that fires the callback *after*
217619207dcSAndrew Jeffery delivering the associated data to the host, but brief inspection determined
218619207dcSAndrew Jeffery that this involved a non-trivial amount of effort.
219619207dcSAndrew Jeffery 
220619207dcSAndrew Jeffery */
221619207dcSAndrew Jeffery 
2222c07f6f0SAndrew Jeffery using namespace sdbusplus;
2230a3358e7SAndrew Jeffery using namespace phosphor::host::command;
2242c07f6f0SAndrew Jeffery 
2252c07f6f0SAndrew Jeffery static void register_openpower_hiomap_commands() __attribute__((constructor));
2262c07f6f0SAndrew Jeffery 
2272c07f6f0SAndrew Jeffery namespace openpower
2282c07f6f0SAndrew Jeffery {
2292c07f6f0SAndrew Jeffery namespace flash
2302c07f6f0SAndrew Jeffery {
2310a3358e7SAndrew Jeffery constexpr auto BMC_EVENT_DAEMON_READY = 1 << 7;
2320a3358e7SAndrew Jeffery constexpr auto BMC_EVENT_FLASH_CTRL_LOST = 1 << 6;
2330a3358e7SAndrew Jeffery constexpr auto BMC_EVENT_WINDOW_RESET = 1 << 1;
2340a3358e7SAndrew Jeffery constexpr auto BMC_EVENT_PROTOCOL_RESET = 1 << 0;
2350a3358e7SAndrew Jeffery 
2360a3358e7SAndrew Jeffery constexpr auto IPMI_CMD_HIOMAP_EVENT = 0x0f;
2370a3358e7SAndrew Jeffery 
2380a3358e7SAndrew Jeffery constexpr auto HIOMAPD_SERVICE = "xyz.openbmc_project.Hiomapd";
2390a3358e7SAndrew Jeffery constexpr auto HIOMAPD_OBJECT = "/xyz/openbmc_project/Hiomapd";
2400a3358e7SAndrew Jeffery constexpr auto HIOMAPD_IFACE = "xyz.openbmc_project.Hiomapd.Protocol";
2410a3358e7SAndrew Jeffery constexpr auto HIOMAPD_IFACE_V2 = "xyz.openbmc_project.Hiomapd.Protocol.V2";
2420a3358e7SAndrew Jeffery 
2430a3358e7SAndrew Jeffery constexpr auto DBUS_IFACE_PROPERTIES = "org.freedesktop.DBus.Properties";
2440a3358e7SAndrew Jeffery 
245619207dcSAndrew Jeffery /* XXX: ipmid is currently single-threaded, pumping dbus events in sequence
246619207dcSAndrew Jeffery  * via the main event loop. Thus the code is not forced to be re-entrant. We
247619207dcSAndrew Jeffery  * also know that the callback and DBus event handling will not be running
248619207dcSAndrew Jeffery  * concurrently.
249619207dcSAndrew Jeffery  *
250619207dcSAndrew Jeffery  * ipmid_send_cmd_to_host() takes a callback that doesn't define a context
251619207dcSAndrew Jeffery  * pointer, so instead use a global. active_event_updates gates manipulation of
252619207dcSAndrew Jeffery  * process state, so its definition as a global at least aligns with its use.
253619207dcSAndrew Jeffery  */
254619207dcSAndrew Jeffery static int active_event_updates;
255619207dcSAndrew Jeffery 
2560a3358e7SAndrew Jeffery struct hiomap
2570a3358e7SAndrew Jeffery {
2586aa9236cSPatrick Williams     bus_t* bus;
2590a3358e7SAndrew Jeffery 
2600a3358e7SAndrew Jeffery     /* Signals */
2616aa9236cSPatrick Williams     bus::match_t* properties;
2620a3358e7SAndrew Jeffery 
2630a3358e7SAndrew Jeffery     /* Protocol state */
2640a3358e7SAndrew Jeffery     std::map<std::string, int> event_lookup;
2650a3358e7SAndrew Jeffery     uint8_t bmc_events;
26604d75136SAndrew Jeffery     uint8_t seq;
2670a3358e7SAndrew Jeffery };
2682c07f6f0SAndrew Jeffery 
269ee3064baSVernon Mauery SignalResponse sigtermResponse = SignalResponse::continueExecution;
270ee3064baSVernon Mauery 
2712c07f6f0SAndrew Jeffery /* TODO: Replace get/put with packed structs and direct assignment */
2725b355068SPatrick Venture template <typename T>
get(void * buf)2735b355068SPatrick Venture static inline T get(void* buf)
2742c07f6f0SAndrew Jeffery {
2752c07f6f0SAndrew Jeffery     T t;
276ee70196bSPatrick Venture     std::memcpy(&t, buf, sizeof(t));
2772c07f6f0SAndrew Jeffery     return t;
2782c07f6f0SAndrew Jeffery }
2792c07f6f0SAndrew Jeffery 
2805b355068SPatrick Venture template <typename T>
put(void * buf,T && t)2815b355068SPatrick Venture static inline void put(void* buf, T&& t)
2822c07f6f0SAndrew Jeffery {
283ee70196bSPatrick Venture     std::memcpy(buf, &t, sizeof(t));
2842c07f6f0SAndrew Jeffery }
2852c07f6f0SAndrew Jeffery 
28689707266SWilly Tu using hiomap_command =
28789707266SWilly Tu     std::function<ipmi_ret_t(ipmi_request_t req, ipmi_response_t resp,
28889707266SWilly Tu                              ipmi_data_len_t data_len, ipmi_context_t context)>;
2892c07f6f0SAndrew Jeffery struct errno_cc_entry
2902c07f6f0SAndrew Jeffery {
2912c07f6f0SAndrew Jeffery     int err;
2922c07f6f0SAndrew Jeffery     int cc;
2932c07f6f0SAndrew Jeffery };
2942c07f6f0SAndrew Jeffery 
2952c07f6f0SAndrew Jeffery static const errno_cc_entry errno_cc_map[] = {
2962c07f6f0SAndrew Jeffery     {0, IPMI_CC_OK},
2972c07f6f0SAndrew Jeffery     {EBUSY, IPMI_CC_BUSY},
2982c07f6f0SAndrew Jeffery     {ENOTSUP, IPMI_CC_INVALID},
2992c07f6f0SAndrew Jeffery     {ETIMEDOUT, 0xc3}, /* FIXME: Replace when defined in ipmid-api.h */
3002c07f6f0SAndrew Jeffery     {ENOSPC, 0xc4},    /* FIXME: Replace when defined in ipmid-api.h */
3012c07f6f0SAndrew Jeffery     {EINVAL, IPMI_CC_PARM_OUT_OF_RANGE},
3022c07f6f0SAndrew Jeffery     {ENODEV, IPMI_CC_SENSOR_INVALID},
3032c07f6f0SAndrew Jeffery     {EPERM, IPMI_CC_INSUFFICIENT_PRIVILEGE},
3042c07f6f0SAndrew Jeffery     {EACCES, IPMI_CC_INSUFFICIENT_PRIVILEGE},
3052c07f6f0SAndrew Jeffery     {-1, IPMI_CC_UNSPECIFIED_ERROR},
3062c07f6f0SAndrew Jeffery };
3072c07f6f0SAndrew Jeffery 
hiomap_xlate_errno(int err)3082c07f6f0SAndrew Jeffery static int hiomap_xlate_errno(int err)
3092c07f6f0SAndrew Jeffery {
3102c07f6f0SAndrew Jeffery     const errno_cc_entry* entry = &errno_cc_map[0];
3112c07f6f0SAndrew Jeffery 
3122c07f6f0SAndrew Jeffery     while (!(entry->err == err || entry->err == -1))
3132c07f6f0SAndrew Jeffery     {
3142c07f6f0SAndrew Jeffery         entry++;
3152c07f6f0SAndrew Jeffery     }
3162c07f6f0SAndrew Jeffery 
3172c07f6f0SAndrew Jeffery     return entry->cc;
3182c07f6f0SAndrew Jeffery }
3192c07f6f0SAndrew Jeffery 
ipmi_hiomap_event_response(IpmiCmdData cmd,bool status)3200a3358e7SAndrew Jeffery static void ipmi_hiomap_event_response(IpmiCmdData cmd, bool status)
3210a3358e7SAndrew Jeffery {
3220a3358e7SAndrew Jeffery     using namespace phosphor::logging;
3230a3358e7SAndrew Jeffery 
3240a3358e7SAndrew Jeffery     if (!status)
3250a3358e7SAndrew Jeffery     {
3260a3358e7SAndrew Jeffery         log<level::ERR>("Failed to deliver host command",
3270a3358e7SAndrew Jeffery                         entry("SEL_COMMAND=%x:%x", cmd.first, cmd.second));
3280a3358e7SAndrew Jeffery     }
329619207dcSAndrew Jeffery 
330619207dcSAndrew Jeffery     assert(active_event_updates);
331619207dcSAndrew Jeffery     active_event_updates--;
332619207dcSAndrew Jeffery     if (!active_event_updates)
333619207dcSAndrew Jeffery     {
334ee3064baSVernon Mauery         sigtermResponse = SignalResponse::continueExecution;
335619207dcSAndrew Jeffery         log<level::DEBUG>("Unblocked SIGTERM");
336619207dcSAndrew Jeffery     }
337619207dcSAndrew Jeffery }
3380a3358e7SAndrew Jeffery 
hiomap_handle_property_update(struct hiomap * ctx,sdbusplus::message_t & msg)3390a3358e7SAndrew Jeffery static int hiomap_handle_property_update(struct hiomap* ctx,
3406aa9236cSPatrick Williams                                          sdbusplus::message_t& msg)
3410a3358e7SAndrew Jeffery {
342619207dcSAndrew Jeffery     using namespace phosphor::logging;
343619207dcSAndrew Jeffery 
34480d5bcafSPatrick Williams     std::map<std::string, std::variant<bool>> msgData;
345619207dcSAndrew Jeffery 
346ee3064baSVernon Mauery     sigtermResponse = SignalResponse::breakExecution;
347619207dcSAndrew Jeffery     if (!active_event_updates)
348619207dcSAndrew Jeffery     {
349ee3064baSVernon Mauery         sigtermResponse = SignalResponse::breakExecution;
350619207dcSAndrew Jeffery         log<level::DEBUG>("Blocked SIGTERM");
351619207dcSAndrew Jeffery     }
352619207dcSAndrew Jeffery     active_event_updates++;
3530a3358e7SAndrew Jeffery 
3540a3358e7SAndrew Jeffery     std::string iface;
3550a3358e7SAndrew Jeffery     msg.read(iface, msgData);
3560a3358e7SAndrew Jeffery 
357*0d5b076eSPatrick Williams     for (const auto& x : msgData)
3580a3358e7SAndrew Jeffery     {
3590a3358e7SAndrew Jeffery         if (!ctx->event_lookup.count(x.first))
3600a3358e7SAndrew Jeffery         {
3610a3358e7SAndrew Jeffery             /* Unsupported event? */
3620a3358e7SAndrew Jeffery             continue;
3630a3358e7SAndrew Jeffery         }
3640a3358e7SAndrew Jeffery 
3650a3358e7SAndrew Jeffery         uint8_t mask = ctx->event_lookup[x.first];
3663d420921SPatrick Williams         auto value = std::get<bool>(x.second);
3670a3358e7SAndrew Jeffery 
3680a3358e7SAndrew Jeffery         if (value)
3690a3358e7SAndrew Jeffery         {
3700a3358e7SAndrew Jeffery             ctx->bmc_events |= mask;
3710a3358e7SAndrew Jeffery         }
3720a3358e7SAndrew Jeffery         else
3730a3358e7SAndrew Jeffery         {
3740a3358e7SAndrew Jeffery             ctx->bmc_events &= ~mask;
3750a3358e7SAndrew Jeffery         }
3760a3358e7SAndrew Jeffery     }
3770a3358e7SAndrew Jeffery 
3780a3358e7SAndrew Jeffery     auto cmd = std::make_pair(IPMI_CMD_HIOMAP_EVENT, ctx->bmc_events);
3790a3358e7SAndrew Jeffery 
3800a3358e7SAndrew Jeffery     ipmid_send_cmd_to_host(std::make_tuple(cmd, ipmi_hiomap_event_response));
3810a3358e7SAndrew Jeffery 
3820a3358e7SAndrew Jeffery     return 0;
3830a3358e7SAndrew Jeffery }
3840a3358e7SAndrew Jeffery 
hiomap_protocol_reset_response(IpmiCmdData cmd,bool status)38589707266SWilly Tu static int hiomap_protocol_reset_response([[maybe_unused]] IpmiCmdData cmd,
38689707266SWilly Tu                                           [[maybe_unused]] bool status)
387619207dcSAndrew Jeffery {
388ee3064baSVernon Mauery     // If this is running in signal context, ipmid will shutdown
389ee3064baSVernon Mauery     // the event queue as the last signal handler
390d4b7f5e4SAdriana Kobylak     sigtermResponse = SignalResponse::continueExecution;
391ee3064baSVernon Mauery     return 0;
392619207dcSAndrew Jeffery }
393619207dcSAndrew Jeffery 
hiomap_protocol_reset(struct hiomap * ctx)394ee3064baSVernon Mauery static int hiomap_protocol_reset(struct hiomap* ctx)
395619207dcSAndrew Jeffery {
396619207dcSAndrew Jeffery     if (ctx->bmc_events == BMC_EVENT_PROTOCOL_RESET)
397619207dcSAndrew Jeffery     {
398ee3064baSVernon Mauery         // If this is running in signal context, ipmid will shutdown
399ee3064baSVernon Mauery         // the event queue as the last signal handler
400d4b7f5e4SAdriana Kobylak         sigtermResponse = SignalResponse::continueExecution;
401ee3064baSVernon Mauery         return 0;
402619207dcSAndrew Jeffery     }
403619207dcSAndrew Jeffery 
404619207dcSAndrew Jeffery     /*
405619207dcSAndrew Jeffery      * Send an attention indicating the hiomapd has died
406619207dcSAndrew Jeffery      * (BMC_EVENT_DAEMON_READY cleared) and that the protocol has been reset
407619207dcSAndrew Jeffery      * (BMC_EVENT_PROTOCOL_RESET set) to indicate to the host that it needs to
408619207dcSAndrew Jeffery      * wait for the BMC to come back and renegotiate the protocol.
409619207dcSAndrew Jeffery      *
410619207dcSAndrew Jeffery      * We know this to be the case in systems that integrate
411619207dcSAndrew Jeffery      * openpower-host-ipmi-flash, as hiomapd's unit depends on
412619207dcSAndrew Jeffery      * phosphor-ipmi-host, and thus hiomapd has been terminated before ipmid
413619207dcSAndrew Jeffery      * receives SIGTERM.
414619207dcSAndrew Jeffery      */
415619207dcSAndrew Jeffery     auto cmd = std::make_pair(IPMI_CMD_HIOMAP_EVENT, BMC_EVENT_PROTOCOL_RESET);
416619207dcSAndrew Jeffery 
417619207dcSAndrew Jeffery     auto cmdHandler = std::make_tuple(cmd, hiomap_protocol_reset_response);
418619207dcSAndrew Jeffery     ipmid_send_cmd_to_host(cmdHandler);
419619207dcSAndrew Jeffery 
420619207dcSAndrew Jeffery     return 0;
421619207dcSAndrew Jeffery }
422619207dcSAndrew Jeffery 
hiomap_match_properties(struct hiomap * ctx)4236aa9236cSPatrick Williams static bus::match_t hiomap_match_properties(struct hiomap* ctx)
4240a3358e7SAndrew Jeffery {
425*0d5b076eSPatrick Williams     auto properties = bus::match::rules::propertiesChanged(HIOMAPD_OBJECT,
426*0d5b076eSPatrick Williams                                                            HIOMAPD_IFACE_V2);
4270a3358e7SAndrew Jeffery 
4286aa9236cSPatrick Williams     bus::match_t match(
4290a3358e7SAndrew Jeffery         *ctx->bus, properties,
4300a3358e7SAndrew Jeffery         std::bind(hiomap_handle_property_update, ctx, std::placeholders::_1));
4310a3358e7SAndrew Jeffery 
4320a3358e7SAndrew Jeffery     return match;
4330a3358e7SAndrew Jeffery }
4340a3358e7SAndrew Jeffery 
hiomap_reset(ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t context)43589707266SWilly Tu static ipmi_ret_t hiomap_reset([[maybe_unused]] ipmi_request_t request,
43689707266SWilly Tu                                [[maybe_unused]] ipmi_response_t response,
4370a3358e7SAndrew Jeffery                                ipmi_data_len_t data_len, ipmi_context_t context)
4380a3358e7SAndrew Jeffery {
4390a3358e7SAndrew Jeffery     struct hiomap* ctx = static_cast<struct hiomap*>(context);
4400a3358e7SAndrew Jeffery 
4410a3358e7SAndrew Jeffery     auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
4420a3358e7SAndrew Jeffery                                        HIOMAPD_IFACE, "Reset");
4430a3358e7SAndrew Jeffery     try
4440a3358e7SAndrew Jeffery     {
4450a3358e7SAndrew Jeffery         ctx->bus->call(m);
4460a3358e7SAndrew Jeffery 
4470a3358e7SAndrew Jeffery         *data_len = 0;
4480a3358e7SAndrew Jeffery     }
4496aa9236cSPatrick Williams     catch (const exception_t& e)
4500a3358e7SAndrew Jeffery     {
4510a3358e7SAndrew Jeffery         return hiomap_xlate_errno(e.get_errno());
4520a3358e7SAndrew Jeffery     }
4530a3358e7SAndrew Jeffery 
4540a3358e7SAndrew Jeffery     return IPMI_CC_OK;
4550a3358e7SAndrew Jeffery }
4560a3358e7SAndrew Jeffery 
hiomap_get_info(ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t context)4572c07f6f0SAndrew Jeffery static ipmi_ret_t hiomap_get_info(ipmi_request_t request,
4582c07f6f0SAndrew Jeffery                                   ipmi_response_t response,
4592c07f6f0SAndrew Jeffery                                   ipmi_data_len_t data_len,
4602c07f6f0SAndrew Jeffery                                   ipmi_context_t context)
4612c07f6f0SAndrew Jeffery {
4620a3358e7SAndrew Jeffery     struct hiomap* ctx = static_cast<struct hiomap*>(context);
4630a3358e7SAndrew Jeffery 
4642c07f6f0SAndrew Jeffery     if (*data_len < 1)
4652c07f6f0SAndrew Jeffery     {
4662c07f6f0SAndrew Jeffery         return IPMI_CC_REQ_DATA_LEN_INVALID;
4672c07f6f0SAndrew Jeffery     }
4682c07f6f0SAndrew Jeffery 
4692c07f6f0SAndrew Jeffery     uint8_t* reqdata = (uint8_t*)request;
4700a3358e7SAndrew Jeffery     auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
4710a3358e7SAndrew Jeffery                                        HIOMAPD_IFACE, "GetInfo");
4722c07f6f0SAndrew Jeffery     m.append(reqdata[0]);
4732c07f6f0SAndrew Jeffery 
4742c07f6f0SAndrew Jeffery     try
4752c07f6f0SAndrew Jeffery     {
4760a3358e7SAndrew Jeffery         auto reply = ctx->bus->call(m);
4772c07f6f0SAndrew Jeffery 
4782c07f6f0SAndrew Jeffery         uint8_t version;
4792c07f6f0SAndrew Jeffery         uint8_t blockSizeShift;
4802c07f6f0SAndrew Jeffery         uint16_t timeout;
4812c07f6f0SAndrew Jeffery         reply.read(version, blockSizeShift, timeout);
4822c07f6f0SAndrew Jeffery 
4832c07f6f0SAndrew Jeffery         uint8_t* respdata = (uint8_t*)response;
4842c07f6f0SAndrew Jeffery 
4852c07f6f0SAndrew Jeffery         /* FIXME: Assumes v2! */
4862c07f6f0SAndrew Jeffery         put(&respdata[0], version);
4872c07f6f0SAndrew Jeffery         put(&respdata[1], blockSizeShift);
4882c07f6f0SAndrew Jeffery         put(&respdata[2], htole16(timeout));
4892c07f6f0SAndrew Jeffery 
4902c07f6f0SAndrew Jeffery         *data_len = 4;
4912c07f6f0SAndrew Jeffery     }
4926aa9236cSPatrick Williams     catch (const exception_t& e)
4932c07f6f0SAndrew Jeffery     {
4942c07f6f0SAndrew Jeffery         return hiomap_xlate_errno(e.get_errno());
4952c07f6f0SAndrew Jeffery     }
4962c07f6f0SAndrew Jeffery 
4972c07f6f0SAndrew Jeffery     return IPMI_CC_OK;
4982c07f6f0SAndrew Jeffery }
4992c07f6f0SAndrew Jeffery 
hiomap_get_flash_info(ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t context)50089707266SWilly Tu static ipmi_ret_t hiomap_get_flash_info([[maybe_unused]] ipmi_request_t request,
501db688e9fSAndrew Jeffery                                         ipmi_response_t response,
502db688e9fSAndrew Jeffery                                         ipmi_data_len_t data_len,
503db688e9fSAndrew Jeffery                                         ipmi_context_t context)
504db688e9fSAndrew Jeffery {
505db688e9fSAndrew Jeffery     struct hiomap* ctx = static_cast<struct hiomap*>(context);
506db688e9fSAndrew Jeffery 
507db688e9fSAndrew Jeffery     auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
508db688e9fSAndrew Jeffery                                        HIOMAPD_IFACE_V2, "GetFlashInfo");
509db688e9fSAndrew Jeffery     try
510db688e9fSAndrew Jeffery     {
511db688e9fSAndrew Jeffery         auto reply = ctx->bus->call(m);
512db688e9fSAndrew Jeffery 
513db688e9fSAndrew Jeffery         uint16_t flashSize, eraseSize;
514db688e9fSAndrew Jeffery         reply.read(flashSize, eraseSize);
515db688e9fSAndrew Jeffery 
516db688e9fSAndrew Jeffery         uint8_t* respdata = (uint8_t*)response;
517db688e9fSAndrew Jeffery         put(&respdata[0], htole16(flashSize));
518db688e9fSAndrew Jeffery         put(&respdata[2], htole16(eraseSize));
519db688e9fSAndrew Jeffery 
520db688e9fSAndrew Jeffery         *data_len = 4;
521db688e9fSAndrew Jeffery     }
5226aa9236cSPatrick Williams     catch (const exception_t& e)
523db688e9fSAndrew Jeffery     {
524db688e9fSAndrew Jeffery         return hiomap_xlate_errno(e.get_errno());
525db688e9fSAndrew Jeffery     }
526db688e9fSAndrew Jeffery 
527db688e9fSAndrew Jeffery     return IPMI_CC_OK;
528db688e9fSAndrew Jeffery }
529db688e9fSAndrew Jeffery 
hiomap_create_window(struct hiomap * ctx,bool ro,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len)530a00f59baSAndrew Jeffery static ipmi_ret_t hiomap_create_window(struct hiomap* ctx, bool ro,
531a00f59baSAndrew Jeffery                                        ipmi_request_t request,
532a00f59baSAndrew Jeffery                                        ipmi_response_t response,
533a00f59baSAndrew Jeffery                                        ipmi_data_len_t data_len)
534a00f59baSAndrew Jeffery {
535a00f59baSAndrew Jeffery     if (*data_len < 4)
536a00f59baSAndrew Jeffery     {
537a00f59baSAndrew Jeffery         return IPMI_CC_REQ_DATA_LEN_INVALID;
538a00f59baSAndrew Jeffery     }
539a00f59baSAndrew Jeffery 
540a00f59baSAndrew Jeffery     uint8_t* reqdata = (uint8_t*)request;
541a00f59baSAndrew Jeffery     auto windowType = ro ? "CreateReadWindow" : "CreateWriteWindow";
542a00f59baSAndrew Jeffery 
543a00f59baSAndrew Jeffery     auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
544a00f59baSAndrew Jeffery                                        HIOMAPD_IFACE_V2, windowType);
545a00f59baSAndrew Jeffery     m.append(le16toh(get<uint16_t>(&reqdata[0])));
546a00f59baSAndrew Jeffery     m.append(le16toh(get<uint16_t>(&reqdata[2])));
547a00f59baSAndrew Jeffery 
548a00f59baSAndrew Jeffery     try
549a00f59baSAndrew Jeffery     {
550a00f59baSAndrew Jeffery         auto reply = ctx->bus->call(m);
551a00f59baSAndrew Jeffery 
552a00f59baSAndrew Jeffery         uint16_t lpcAddress, size, offset;
553a00f59baSAndrew Jeffery         reply.read(lpcAddress, size, offset);
554a00f59baSAndrew Jeffery 
555a00f59baSAndrew Jeffery         uint8_t* respdata = (uint8_t*)response;
556a00f59baSAndrew Jeffery 
557a00f59baSAndrew Jeffery         /* FIXME: Assumes v2! */
558a00f59baSAndrew Jeffery         put(&respdata[0], htole16(lpcAddress));
559a00f59baSAndrew Jeffery         put(&respdata[2], htole16(size));
560a00f59baSAndrew Jeffery         put(&respdata[4], htole16(offset));
561a00f59baSAndrew Jeffery 
562a00f59baSAndrew Jeffery         *data_len = 6;
563a00f59baSAndrew Jeffery     }
5646aa9236cSPatrick Williams     catch (const exception_t& e)
565a00f59baSAndrew Jeffery     {
566a00f59baSAndrew Jeffery         return hiomap_xlate_errno(e.get_errno());
567a00f59baSAndrew Jeffery     }
568a00f59baSAndrew Jeffery 
569a00f59baSAndrew Jeffery     return IPMI_CC_OK;
570a00f59baSAndrew Jeffery }
571a00f59baSAndrew Jeffery 
hiomap_create_read_window(ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t context)572a00f59baSAndrew Jeffery static ipmi_ret_t hiomap_create_read_window(ipmi_request_t request,
573a00f59baSAndrew Jeffery                                             ipmi_response_t response,
574a00f59baSAndrew Jeffery                                             ipmi_data_len_t data_len,
575a00f59baSAndrew Jeffery                                             ipmi_context_t context)
576a00f59baSAndrew Jeffery {
577a00f59baSAndrew Jeffery     struct hiomap* ctx = static_cast<struct hiomap*>(context);
578a00f59baSAndrew Jeffery 
579a00f59baSAndrew Jeffery     return hiomap_create_window(ctx, true, request, response, data_len);
580a00f59baSAndrew Jeffery }
581a00f59baSAndrew Jeffery 
hiomap_create_write_window(ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t context)582a00f59baSAndrew Jeffery static ipmi_ret_t hiomap_create_write_window(ipmi_request_t request,
583a00f59baSAndrew Jeffery                                              ipmi_response_t response,
584a00f59baSAndrew Jeffery                                              ipmi_data_len_t data_len,
585a00f59baSAndrew Jeffery                                              ipmi_context_t context)
586a00f59baSAndrew Jeffery {
587a00f59baSAndrew Jeffery     struct hiomap* ctx = static_cast<struct hiomap*>(context);
588a00f59baSAndrew Jeffery 
589a00f59baSAndrew Jeffery     return hiomap_create_window(ctx, false, request, response, data_len);
590a00f59baSAndrew Jeffery }
591a00f59baSAndrew Jeffery 
hiomap_close_window(ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t context)592b52822cdSAndrew Jeffery static ipmi_ret_t hiomap_close_window(ipmi_request_t request,
59389707266SWilly Tu                                       [[maybe_unused]] ipmi_response_t response,
594b52822cdSAndrew Jeffery                                       ipmi_data_len_t data_len,
595b52822cdSAndrew Jeffery                                       ipmi_context_t context)
596b52822cdSAndrew Jeffery {
597b52822cdSAndrew Jeffery     struct hiomap* ctx = static_cast<struct hiomap*>(context);
598b52822cdSAndrew Jeffery 
599b52822cdSAndrew Jeffery     if (*data_len < 1)
600b52822cdSAndrew Jeffery     {
601b52822cdSAndrew Jeffery         return IPMI_CC_REQ_DATA_LEN_INVALID;
602b52822cdSAndrew Jeffery     }
603b52822cdSAndrew Jeffery 
604b52822cdSAndrew Jeffery     uint8_t* reqdata = (uint8_t*)request;
605b52822cdSAndrew Jeffery     auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
606b52822cdSAndrew Jeffery                                        HIOMAPD_IFACE_V2, "CloseWindow");
607b52822cdSAndrew Jeffery     m.append(reqdata[0]);
608b52822cdSAndrew Jeffery 
609b52822cdSAndrew Jeffery     try
610b52822cdSAndrew Jeffery     {
611b52822cdSAndrew Jeffery         auto reply = ctx->bus->call(m);
612b52822cdSAndrew Jeffery 
613b52822cdSAndrew Jeffery         *data_len = 0;
614b52822cdSAndrew Jeffery     }
6156aa9236cSPatrick Williams     catch (const exception_t& e)
616b52822cdSAndrew Jeffery     {
617b52822cdSAndrew Jeffery         return hiomap_xlate_errno(e.get_errno());
618b52822cdSAndrew Jeffery     }
619b52822cdSAndrew Jeffery 
620b52822cdSAndrew Jeffery     return IPMI_CC_OK;
621b52822cdSAndrew Jeffery }
622b52822cdSAndrew Jeffery 
hiomap_mark_dirty(ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t context)6239847f1c2SAndrew Jeffery static ipmi_ret_t hiomap_mark_dirty(ipmi_request_t request,
62489707266SWilly Tu                                     [[maybe_unused]] ipmi_response_t response,
6259847f1c2SAndrew Jeffery                                     ipmi_data_len_t data_len,
6269847f1c2SAndrew Jeffery                                     ipmi_context_t context)
6279847f1c2SAndrew Jeffery {
6289847f1c2SAndrew Jeffery     struct hiomap* ctx = static_cast<struct hiomap*>(context);
6299847f1c2SAndrew Jeffery 
6309847f1c2SAndrew Jeffery     if (*data_len < 4)
6319847f1c2SAndrew Jeffery     {
6329847f1c2SAndrew Jeffery         return IPMI_CC_REQ_DATA_LEN_INVALID;
6339847f1c2SAndrew Jeffery     }
6349847f1c2SAndrew Jeffery 
6359847f1c2SAndrew Jeffery     uint8_t* reqdata = (uint8_t*)request;
6369847f1c2SAndrew Jeffery     auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
6379847f1c2SAndrew Jeffery                                        HIOMAPD_IFACE_V2, "MarkDirty");
6389847f1c2SAndrew Jeffery     /* FIXME: Assumes v2 */
6399847f1c2SAndrew Jeffery     m.append(le16toh(get<uint16_t>(&reqdata[0]))); /* offset */
6409847f1c2SAndrew Jeffery     m.append(le16toh(get<uint16_t>(&reqdata[2]))); /* size */
6419847f1c2SAndrew Jeffery 
6429847f1c2SAndrew Jeffery     try
6439847f1c2SAndrew Jeffery     {
6449847f1c2SAndrew Jeffery         auto reply = ctx->bus->call(m);
6459847f1c2SAndrew Jeffery 
6469847f1c2SAndrew Jeffery         *data_len = 0;
6479847f1c2SAndrew Jeffery     }
6486aa9236cSPatrick Williams     catch (const exception_t& e)
6499847f1c2SAndrew Jeffery     {
6509847f1c2SAndrew Jeffery         return hiomap_xlate_errno(e.get_errno());
6519847f1c2SAndrew Jeffery     }
6529847f1c2SAndrew Jeffery 
6539847f1c2SAndrew Jeffery     return IPMI_CC_OK;
6549847f1c2SAndrew Jeffery }
6559847f1c2SAndrew Jeffery 
hiomap_flush(ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t context)65689707266SWilly Tu static ipmi_ret_t hiomap_flush([[maybe_unused]] ipmi_request_t request,
65789707266SWilly Tu                                [[maybe_unused]] ipmi_response_t response,
6587b225fb2SAndrew Jeffery                                ipmi_data_len_t data_len, ipmi_context_t context)
6597b225fb2SAndrew Jeffery {
6607b225fb2SAndrew Jeffery     struct hiomap* ctx = static_cast<struct hiomap*>(context);
6617b225fb2SAndrew Jeffery 
6627b225fb2SAndrew Jeffery     auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
6637b225fb2SAndrew Jeffery                                        HIOMAPD_IFACE_V2, "Flush");
6647b225fb2SAndrew Jeffery 
6657b225fb2SAndrew Jeffery     try
6667b225fb2SAndrew Jeffery     {
6677b225fb2SAndrew Jeffery         /* FIXME: No argument call assumes v2 */
6687b225fb2SAndrew Jeffery         auto reply = ctx->bus->call(m);
6697b225fb2SAndrew Jeffery 
6707b225fb2SAndrew Jeffery         *data_len = 0;
6717b225fb2SAndrew Jeffery     }
6726aa9236cSPatrick Williams     catch (const exception_t& e)
6737b225fb2SAndrew Jeffery     {
6747b225fb2SAndrew Jeffery         return hiomap_xlate_errno(e.get_errno());
6757b225fb2SAndrew Jeffery     }
6767b225fb2SAndrew Jeffery 
6777b225fb2SAndrew Jeffery     return IPMI_CC_OK;
6787b225fb2SAndrew Jeffery }
6797b225fb2SAndrew Jeffery 
hiomap_ack(ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t context)68089707266SWilly Tu static ipmi_ret_t hiomap_ack(ipmi_request_t request,
68189707266SWilly Tu                              [[maybe_unused]] ipmi_response_t response,
68299f277a1SAndrew Jeffery                              ipmi_data_len_t data_len, ipmi_context_t context)
68399f277a1SAndrew Jeffery {
68499f277a1SAndrew Jeffery     struct hiomap* ctx = static_cast<struct hiomap*>(context);
68599f277a1SAndrew Jeffery 
68699f277a1SAndrew Jeffery     if (*data_len < 1)
68799f277a1SAndrew Jeffery     {
68899f277a1SAndrew Jeffery         return IPMI_CC_REQ_DATA_LEN_INVALID;
68999f277a1SAndrew Jeffery     }
69099f277a1SAndrew Jeffery 
69199f277a1SAndrew Jeffery     uint8_t* reqdata = (uint8_t*)request;
69299f277a1SAndrew Jeffery     auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
69399f277a1SAndrew Jeffery                                        HIOMAPD_IFACE_V2, "Ack");
69499f277a1SAndrew Jeffery     auto acked = reqdata[0];
69599f277a1SAndrew Jeffery     m.append(acked);
69699f277a1SAndrew Jeffery 
69799f277a1SAndrew Jeffery     try
69899f277a1SAndrew Jeffery     {
69999f277a1SAndrew Jeffery         auto reply = ctx->bus->call(m);
70099f277a1SAndrew Jeffery 
70199f277a1SAndrew Jeffery         *data_len = 0;
70299f277a1SAndrew Jeffery     }
7036aa9236cSPatrick Williams     catch (const exception_t& e)
70499f277a1SAndrew Jeffery     {
70599f277a1SAndrew Jeffery         return hiomap_xlate_errno(e.get_errno());
70699f277a1SAndrew Jeffery     }
70799f277a1SAndrew Jeffery 
70899f277a1SAndrew Jeffery     return IPMI_CC_OK;
70999f277a1SAndrew Jeffery }
71099f277a1SAndrew Jeffery 
hiomap_erase(ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t context)71189707266SWilly Tu static ipmi_ret_t hiomap_erase(ipmi_request_t request,
71289707266SWilly Tu                                [[maybe_unused]] ipmi_response_t response,
713a1e35b85SAndrew Jeffery                                ipmi_data_len_t data_len, ipmi_context_t context)
714a1e35b85SAndrew Jeffery {
715a1e35b85SAndrew Jeffery     struct hiomap* ctx = static_cast<struct hiomap*>(context);
716a1e35b85SAndrew Jeffery 
717a1e35b85SAndrew Jeffery     if (*data_len < 4)
718a1e35b85SAndrew Jeffery     {
719a1e35b85SAndrew Jeffery         return IPMI_CC_REQ_DATA_LEN_INVALID;
720a1e35b85SAndrew Jeffery     }
721a1e35b85SAndrew Jeffery 
722a1e35b85SAndrew Jeffery     uint8_t* reqdata = (uint8_t*)request;
723a1e35b85SAndrew Jeffery     auto m = ctx->bus->new_method_call(HIOMAPD_SERVICE, HIOMAPD_OBJECT,
724a1e35b85SAndrew Jeffery                                        HIOMAPD_IFACE_V2, "Erase");
725a1e35b85SAndrew Jeffery     /* FIXME: Assumes v2 */
726a1e35b85SAndrew Jeffery     m.append(le16toh(get<uint16_t>(&reqdata[0]))); /* offset */
727a1e35b85SAndrew Jeffery     m.append(le16toh(get<uint16_t>(&reqdata[2]))); /* size */
728a1e35b85SAndrew Jeffery 
729a1e35b85SAndrew Jeffery     try
730a1e35b85SAndrew Jeffery     {
731a1e35b85SAndrew Jeffery         auto reply = ctx->bus->call(m);
732a1e35b85SAndrew Jeffery 
733a1e35b85SAndrew Jeffery         *data_len = 0;
734a1e35b85SAndrew Jeffery     }
7356aa9236cSPatrick Williams     catch (const exception_t& e)
736a1e35b85SAndrew Jeffery     {
737a1e35b85SAndrew Jeffery         return hiomap_xlate_errno(e.get_errno());
738a1e35b85SAndrew Jeffery     }
739a1e35b85SAndrew Jeffery 
740a1e35b85SAndrew Jeffery     return IPMI_CC_OK;
741a1e35b85SAndrew Jeffery }
742a1e35b85SAndrew Jeffery 
74304d75136SAndrew Jeffery #define HIOMAP_C_RESET 1
74404d75136SAndrew Jeffery #define HIOMAP_C_GET_INFO 2
74504d75136SAndrew Jeffery #define HIOMAP_C_GET_FLASH_INFO 3
74604d75136SAndrew Jeffery #define HIOMAP_C_CREATE_READ_WINDOW 4
74704d75136SAndrew Jeffery #define HIOMAP_C_CLOSE_WINDOW 5
74804d75136SAndrew Jeffery #define HIOMAP_C_CREATE_WRITE_WINDOW 6
74904d75136SAndrew Jeffery #define HIOMAP_C_MARK_DIRTY 7
75004d75136SAndrew Jeffery #define HIOMAP_C_FLUSH 8
75104d75136SAndrew Jeffery #define HIOMAP_C_ACK 9
75204d75136SAndrew Jeffery #define HIOMAP_C_ERASE 10
75304d75136SAndrew Jeffery 
75489707266SWilly Tu static const std::unordered_map<uint8_t, hiomap_command> hiomap_commands = {
75589707266SWilly Tu     {0, nullptr}, /* Invalid command ID */
75689707266SWilly Tu     {HIOMAP_C_RESET, hiomap_reset},
75789707266SWilly Tu     {HIOMAP_C_GET_INFO, hiomap_get_info},
75889707266SWilly Tu     {HIOMAP_C_GET_FLASH_INFO, hiomap_get_flash_info},
75989707266SWilly Tu     {HIOMAP_C_CREATE_READ_WINDOW, hiomap_create_read_window},
76089707266SWilly Tu     {HIOMAP_C_CLOSE_WINDOW, hiomap_close_window},
76189707266SWilly Tu     {HIOMAP_C_CREATE_WRITE_WINDOW, hiomap_create_write_window},
76289707266SWilly Tu     {HIOMAP_C_MARK_DIRTY, hiomap_mark_dirty},
76389707266SWilly Tu     {HIOMAP_C_FLUSH, hiomap_flush},
76489707266SWilly Tu     {HIOMAP_C_ACK, hiomap_ack},
76589707266SWilly Tu     {HIOMAP_C_ERASE, hiomap_erase},
7662c07f6f0SAndrew Jeffery };
7672c07f6f0SAndrew Jeffery 
7682c07f6f0SAndrew Jeffery /* FIXME: Define this in the "right" place, wherever that is */
7692c07f6f0SAndrew Jeffery /* FIXME: Double evaluation */
7702c07f6f0SAndrew Jeffery #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
7712c07f6f0SAndrew Jeffery 
hiomap_dispatch(ipmi_netfn_t netfn,ipmi_cmd_t cmd,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t context)77289707266SWilly Tu static ipmi_ret_t hiomap_dispatch([[maybe_unused]] ipmi_netfn_t netfn,
77389707266SWilly Tu                                   [[maybe_unused]] ipmi_cmd_t cmd,
7742c07f6f0SAndrew Jeffery                                   ipmi_request_t request,
7752c07f6f0SAndrew Jeffery                                   ipmi_response_t response,
7762c07f6f0SAndrew Jeffery                                   ipmi_data_len_t data_len,
7772c07f6f0SAndrew Jeffery                                   ipmi_context_t context)
7782c07f6f0SAndrew Jeffery {
7790a3358e7SAndrew Jeffery     struct hiomap* ctx = static_cast<struct hiomap*>(context);
7800a3358e7SAndrew Jeffery 
7812c07f6f0SAndrew Jeffery     if (*data_len < 2)
7822c07f6f0SAndrew Jeffery     {
7832c07f6f0SAndrew Jeffery         *data_len = 0;
7842c07f6f0SAndrew Jeffery         return IPMI_CC_REQ_DATA_LEN_INVALID;
7852c07f6f0SAndrew Jeffery     }
7862c07f6f0SAndrew Jeffery 
7872c07f6f0SAndrew Jeffery     uint8_t* ipmi_req = (uint8_t*)request;
7882c07f6f0SAndrew Jeffery     uint8_t* ipmi_resp = (uint8_t*)response;
7892c07f6f0SAndrew Jeffery     uint8_t hiomap_cmd = ipmi_req[0];
7902c07f6f0SAndrew Jeffery 
79189707266SWilly Tu     if (hiomap_cmd == 0 || hiomap_cmd > hiomap_commands.size() - 1)
7922c07f6f0SAndrew Jeffery     {
7932c07f6f0SAndrew Jeffery         *data_len = 0;
7942c07f6f0SAndrew Jeffery         return IPMI_CC_PARM_OUT_OF_RANGE;
7952c07f6f0SAndrew Jeffery     }
79604d75136SAndrew Jeffery 
797*0d5b076eSPatrick Williams     bool is_unversioned = (hiomap_cmd == HIOMAP_C_RESET ||
798*0d5b076eSPatrick Williams                            hiomap_cmd == HIOMAP_C_GET_INFO ||
79904d75136SAndrew Jeffery                            hiomap_cmd == HIOMAP_C_ACK);
80004d75136SAndrew Jeffery     if (!is_unversioned && ctx->seq == ipmi_req[1])
80104d75136SAndrew Jeffery     {
80204d75136SAndrew Jeffery         *data_len = 0;
80304d75136SAndrew Jeffery         return IPMI_CC_INVALID_FIELD_REQUEST;
80404d75136SAndrew Jeffery     }
80504d75136SAndrew Jeffery 
80604d75136SAndrew Jeffery     ctx->seq = ipmi_req[1];
80704d75136SAndrew Jeffery 
8082c07f6f0SAndrew Jeffery     uint8_t* flash_req = ipmi_req + 2;
8092c07f6f0SAndrew Jeffery     size_t flash_len = *data_len - 2;
8102c07f6f0SAndrew Jeffery     uint8_t* flash_resp = ipmi_resp + 2;
8112c07f6f0SAndrew Jeffery 
81289707266SWilly Tu     auto command = hiomap_commands.find(hiomap_cmd);
81389707266SWilly Tu     if (command == hiomap_commands.end())
81489707266SWilly Tu     {
81589707266SWilly Tu         *data_len = 0;
81689707266SWilly Tu         return IPMI_CC_INVALID;
81789707266SWilly Tu     }
81889707266SWilly Tu     ipmi_ret_t cc = command->second(flash_req, flash_resp, &flash_len, context);
8192c07f6f0SAndrew Jeffery     if (cc != IPMI_CC_OK)
8202c07f6f0SAndrew Jeffery     {
8212c07f6f0SAndrew Jeffery         *data_len = 0;
8222c07f6f0SAndrew Jeffery         return cc;
8232c07f6f0SAndrew Jeffery     }
8242c07f6f0SAndrew Jeffery 
8252c07f6f0SAndrew Jeffery     /* Populate the response command and sequence */
8260a3358e7SAndrew Jeffery     ipmi_resp[0] = hiomap_cmd;
82704d75136SAndrew Jeffery     ipmi_resp[1] = ctx->seq;
8282c07f6f0SAndrew Jeffery 
8292c07f6f0SAndrew Jeffery     *data_len = flash_len + 2;
8302c07f6f0SAndrew Jeffery 
8312c07f6f0SAndrew Jeffery     return cc;
8322c07f6f0SAndrew Jeffery }
8332c07f6f0SAndrew Jeffery } // namespace flash
8342c07f6f0SAndrew Jeffery } // namespace openpower
8352c07f6f0SAndrew Jeffery 
register_openpower_hiomap_commands()8362c07f6f0SAndrew Jeffery static void register_openpower_hiomap_commands()
8372c07f6f0SAndrew Jeffery {
838619207dcSAndrew Jeffery     using namespace phosphor::logging;
8390a3358e7SAndrew Jeffery     using namespace openpower::flash;
8400a3358e7SAndrew Jeffery 
8410a3358e7SAndrew Jeffery     struct hiomap* ctx = new hiomap();
8420a3358e7SAndrew Jeffery 
8430a3358e7SAndrew Jeffery     /* Initialise mapping from signal and property names to status bit */
8440a3358e7SAndrew Jeffery     ctx->event_lookup["DaemonReady"] = BMC_EVENT_DAEMON_READY;
8450a3358e7SAndrew Jeffery     ctx->event_lookup["FlashControlLost"] = BMC_EVENT_FLASH_CTRL_LOST;
8460a3358e7SAndrew Jeffery     ctx->event_lookup["WindowReset"] = BMC_EVENT_WINDOW_RESET;
8470a3358e7SAndrew Jeffery     ctx->event_lookup["ProtocolReset"] = BMC_EVENT_PROTOCOL_RESET;
8480a3358e7SAndrew Jeffery 
8496aa9236cSPatrick Williams     ctx->bus = new bus_t(ipmid_get_sd_bus_connection());
8500a3358e7SAndrew Jeffery 
8510a3358e7SAndrew Jeffery     /* Initialise signal handling */
8520a3358e7SAndrew Jeffery 
8530a3358e7SAndrew Jeffery     /*
8540a3358e7SAndrew Jeffery      * Can't use temporaries here because that causes SEGFAULTs due to slot
8550a3358e7SAndrew Jeffery      * destruction (!?), so enjoy the weird wrapping.
8560a3358e7SAndrew Jeffery      */
8576aa9236cSPatrick Williams     ctx->properties = new bus::match_t(std::move(hiomap_match_properties(ctx)));
858619207dcSAndrew Jeffery 
859ee3064baSVernon Mauery     std::function<SignalResponse(int)> shutdownHandler =
86089707266SWilly Tu         [ctx]([[maybe_unused]] int signalNumber) {
861ee3064baSVernon Mauery         hiomap_protocol_reset(ctx);
862ee3064baSVernon Mauery         return sigtermResponse;
863ee3064baSVernon Mauery     };
864ee3064baSVernon Mauery     registerSignalHandler(ipmi::prioMax, SIGTERM, shutdownHandler);
8650a3358e7SAndrew Jeffery 
8660a3358e7SAndrew Jeffery     ipmi_register_callback(NETFUN_IBM_OEM, IPMI_CMD_HIOMAP, ctx,
8672c07f6f0SAndrew Jeffery                            openpower::flash::hiomap_dispatch, SYSTEM_INTERFACE);
8682c07f6f0SAndrew Jeffery }
869