xref: /openbmc/phosphor-host-postd/main.cpp (revision f8d0e0bf)
1 /**
2  * Copyright 2017 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifdef ENABLE_IPMI_SNOOP
18 #include "ipmisnoop/ipmisnoop.hpp"
19 #endif
20 
21 #include "lpcsnoop/snoop.hpp"
22 
23 #include <endian.h>
24 #include <fcntl.h>
25 #include <getopt.h>
26 #include <sys/epoll.h>
27 #include <systemd/sd-event.h>
28 #include <unistd.h>
29 
30 #include <cstdint>
31 #include <exception>
32 #include <functional>
33 #include <iostream>
34 #include <optional>
35 #include <sdeventplus/event.hpp>
36 #include <sdeventplus/source/event.hpp>
37 #include <sdeventplus/source/io.hpp>
38 #include <sdeventplus/source/signal.hpp>
39 #include <span>
40 #include <stdplus/signal.hpp>
41 #include <thread>
42 
43 #ifdef ENABLE_IPMI_SNOOP
44 #include <xyz/openbmc_project/State/Boot/Raw/server.hpp>
45 #endif
46 
47 static size_t codeSize = 1; /* Size of each POST code in bytes */
48 const char* defaultHostInstances = "0";
49 #ifdef ENABLE_IPMI_SNOOP
50 const uint8_t minPositionVal = 0;
51 const uint8_t maxPositionVal = 5;
52 #endif
53 
54 #ifdef ENABLE_IPMI_SNOOP
55 std::vector<std::unique_ptr<IpmiPostReporter>> reporters;
56 #endif
57 
58 #ifdef ENABLE_IPMI_SNOOP
59 void IpmiPostReporter::getSelectorPositionSignal(sdbusplus::bus::bus& bus)
60 {
61     size_t posVal = 0;
62 
63     matchSignal = std::make_unique<sdbusplus::bus::match_t>(
64         bus,
65         sdbusplus::bus::match::rules::propertiesChanged(selectorObject,
66                                                         selectorIface),
67         [&](sdbusplus::message::message& msg) {
68             std::string objectName;
69             std::map<std::string, Selector::PropertiesVariant> msgData;
70             msg.read(objectName, msgData);
71 
72             auto valPropMap = msgData.find("Position");
73             {
74                 if (valPropMap == msgData.end())
75                 {
76                     std::cerr << "Position property not found " << std::endl;
77                     return;
78                 }
79 
80                 posVal = std::get<size_t>(valPropMap->second);
81 
82                 if (posVal > minPositionVal && posVal < maxPositionVal)
83                 {
84                     std::tuple<uint64_t, secondary_post_code_t> postcodes =
85                         reporters[posVal - 1]->value();
86                     uint64_t postcode = std::get<uint64_t>(postcodes);
87 
88                     // write postcode into seven segment display
89                     if (postCodeDisplay(postcode) < 0)
90                     {
91                         fprintf(stderr, "Error in display the postcode\n");
92                     }
93                 }
94             }
95         });
96 }
97 #endif
98 
99 static void usage(const char* name)
100 {
101     fprintf(stderr,
102             "Usage: %s [-d <DEVICE>]\n"
103             "  -b, --bytes <SIZE>     set POST code length to <SIZE> bytes. "
104             "Default is %zu\n"
105             "  -d, --device <DEVICE>  use <DEVICE> file.\n"
106             "  -h, --host <host instances>  . Default is '%s'\n"
107             "  -v, --verbose  Prints verbose information while running\n\n",
108             name, codeSize, defaultHostInstances);
109 }
110 
111 /*
112  * Callback handling IO event from the POST code fd. i.e. there is new
113  * POST code available to read.
114  */
115 void PostCodeEventHandler(PostReporter* reporter, bool verbose,
116                           sdeventplus::source::IO& s, int postFd, uint32_t)
117 {
118     uint64_t code = 0;
119     ssize_t readb;
120     while ((readb = read(postFd, &code, codeSize)) > 0)
121     {
122         code = le64toh(code);
123         if (verbose)
124         {
125             fprintf(stderr, "Code: 0x%" PRIx64 "\n", code);
126         }
127         // HACK: Always send property changed signal even for the same code
128         // since we are single threaded, external users will never see the
129         // first value.
130         reporter->value(std::make_tuple(~code, secondary_post_code_t{}), true);
131         reporter->value(std::make_tuple(code, secondary_post_code_t{}));
132 
133         // read depends on old data being cleared since it doens't always read
134         // the full code size
135         code = 0;
136     }
137 
138     if (readb < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
139     {
140         return;
141     }
142 
143     /* Read failure. */
144     if (readb == 0)
145     {
146         fprintf(stderr, "Unexpected EOF reading postcode\n");
147     }
148     else
149     {
150         fprintf(stderr, "Failed to read postcode: %s\n", strerror(errno));
151     }
152     s.get_event().exit(1);
153 }
154 
155 #ifdef ENABLE_IPMI_SNOOP
156 // handle muti-host D-bus
157 int postCodeIpmiHandler(const std::string& snoopObject,
158                         const std::string& snoopDbus, sdbusplus::bus::bus& bus,
159                         std::span<std::string> host)
160 {
161     int ret = 0;
162 
163     try
164     {
165         for (size_t iteration = 0; iteration < host.size(); iteration++)
166         {
167             std::string objPathInst = snoopObject + host[iteration];
168 
169             sdbusplus::server::manager_t m{bus, objPathInst.c_str()};
170 
171             /* Create a monitor object and let it do all the rest */
172             reporters.emplace_back(
173                 std::make_unique<IpmiPostReporter>(bus, objPathInst.c_str()));
174 
175             reporters[iteration]->emit_object_added();
176         }
177 
178         bus.request_name(snoopDbus.c_str());
179         reporters[0]->getSelectorPositionSignal(bus);
180     }
181     catch (const std::exception& e)
182     {
183         fprintf(stderr, "%s\n", e.what());
184     }
185 
186     // Configure seven segment dsiplay connected to GPIOs as output
187     ret = configGPIODirOutput();
188     if (ret < 0)
189     {
190         fprintf(stderr, "Failed find the gpio line\n");
191     }
192 
193     while (true)
194     {
195         bus.process_discard();
196         bus.wait();
197     }
198     exit(EXIT_SUCCESS);
199 }
200 #endif
201 
202 /*
203  * TODO(venture): this only listens one of the possible snoop ports, but
204  * doesn't share the namespace.
205  *
206  * This polls() the lpc snoop character device and it owns the dbus object
207  * whose value is the latest port 80h value.
208  */
209 int main(int argc, char* argv[])
210 {
211 
212 #ifndef ENABLE_IPMI_SNOOP
213     int postFd = -1;
214 #endif
215 
216     int opt;
217     bool verbose = false;
218 
219 #ifdef ENABLE_IPMI_SNOOP
220     std::vector<std::string> host;
221 #endif
222     /*
223      * These string constants are only used in this method within this object
224      * and this object is the only object feeding into the final binary.
225      *
226      * If however, another object is added to this binary it would be proper
227      * to move these declarations to be global and extern to the other object.
228      */
229 
230     // clang-format off
231     static const struct option long_options[] = {
232         #ifdef ENABLE_IPMI_SNOOP
233         {"host", optional_argument, NULL, 'h'},
234         #endif
235         {"bytes",  required_argument, NULL, 'b'},
236         #ifndef ENABLE_IPMI_SNOOP
237         {"device", optional_argument, NULL, 'd'},
238         #endif
239         {"verbose", no_argument, NULL, 'v'},
240         {0, 0, 0, 0}
241     };
242     // clang-format on
243 
244     while ((opt = getopt_long(argc, argv, "h:b:d:v", long_options, NULL)) != -1)
245     {
246         switch (opt)
247         {
248             case 0:
249                 break;
250 #ifdef ENABLE_IPMI_SNOOP
251             case 'h': {
252                 std::string_view instances = optarg;
253                 size_t pos = 0;
254 
255                 while ((pos = instances.find(" ")) != std::string::npos)
256                 {
257                     host.emplace_back(instances.substr(0, pos));
258                     instances.remove_prefix(pos + 1);
259                 }
260                 host.emplace_back(instances);
261                 break;
262             }
263 #endif
264             case 'b': {
265                 codeSize = atoi(optarg);
266 
267                 if (codeSize < 1 || codeSize > 8)
268                 {
269                     fprintf(stderr,
270                             "Invalid POST code size '%s'. Must be "
271                             "an integer from 1 to 8.\n",
272                             optarg);
273                     exit(EXIT_FAILURE);
274                 }
275                 break;
276             }
277 #ifndef ENABLE_IPMI_SNOOP
278             case 'd':
279 
280                 postFd = open(optarg, O_NONBLOCK);
281                 if (postFd < 0)
282                 {
283                     fprintf(stderr, "Unable to open: %s\n", optarg);
284                     return -1;
285                 }
286                 break;
287 #endif
288             case 'v':
289                 verbose = true;
290                 break;
291             default:
292                 usage(argv[0]);
293         }
294     }
295 
296     auto bus = sdbusplus::bus::new_default();
297 
298 #ifdef ENABLE_IPMI_SNOOP
299     std::cout << "Verbose = " << verbose << std::endl;
300     int ret = postCodeIpmiHandler(ipmiSnoopObject, snoopDbus, bus, host);
301     if (ret < 0)
302     {
303         fprintf(stderr, "Error in postCodeIpmiHandler\n");
304         return ret;
305     }
306     return 0;
307 #endif
308 
309 #ifndef ENABLE_IPMI_SNOOP
310     int rc = 0;
311 
312     bool deferSignals = true;
313 
314     // Add systemd object manager.
315     sdbusplus::server::manager::manager snoopdManager(bus, snoopObject);
316 
317     PostReporter reporter(bus, snoopObject, deferSignals);
318     reporter.emit_object_added();
319     bus.request_name(snoopDbus);
320 
321     // Create sdevent and add IO source
322     try
323     {
324         sdeventplus::Event event = sdeventplus::Event::get_default();
325         std::optional<sdeventplus::source::IO> reporterSource;
326         if (postFd > 0)
327         {
328             reporterSource.emplace(
329                 event, postFd, EPOLLIN | EPOLLET,
330                 std::bind_front(PostCodeEventHandler, &reporter, verbose));
331         }
332         // Enable bus to handle incoming IO and bus events
333         bool done = false;
334         bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
335         auto intCb = [&done](sdeventplus::source::Signal& source,
336                              const struct signalfd_siginfo*) {
337             source.get_event().exit(0);
338             done = true;
339         };
340         stdplus::signal::block(SIGINT);
341         sdeventplus::source::Signal(event, SIGINT, intCb).set_floating(true);
342         stdplus::signal::block(SIGTERM);
343         sdeventplus::source::Signal(event, SIGTERM, std::move(intCb))
344             .set_floating(true);
345 
346         while (!done)
347         {
348             // Process all outstanding bus events before running the loop.
349             // This prevents the sd-bus handling logic from leaking memory.
350             // TODO: Remove when upstream fixes this bug
351             while (bus.process_discard() > 0)
352                 ;
353 
354             // Run and never timeout
355             rc = event.run(std::nullopt);
356         }
357     }
358     catch (const std::exception& e)
359     {
360         fprintf(stderr, "%s\n", e.what());
361     }
362 
363     if (postFd > -1)
364     {
365         close(postFd);
366     }
367 
368     return rc;
369 #endif
370 }
371