xref: /openbmc/phosphor-host-postd/main.cpp (revision 0d14a59e)
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 #include "lpcsnoop/snoop.hpp"
18 
19 #include <endian.h>
20 #include <fcntl.h>
21 #include <getopt.h>
22 #include <sys/epoll.h>
23 #include <systemd/sd-event.h>
24 #include <unistd.h>
25 
26 #include <cstdint>
27 #include <exception>
28 #include <iostream>
29 #include <memory>
30 #include <sdeventplus/event.hpp>
31 #include <sdeventplus/source/event.hpp>
32 #include <sdeventplus/source/io.hpp>
33 #include <thread>
34 
35 static const char* snoopFilename = "/dev/aspeed-lpc-snoop0";
36 static size_t codeSize = 1; /* Size of each POST code in bytes */
37 
38 static void usage(const char* name)
39 {
40     fprintf(stderr,
41             "Usage: %s [-d <DEVICE>]\n"
42             "  -b, --bytes <SIZE>     set POST code length to <SIZE> bytes. "
43             "Default is %zu\n"
44             "  -d, --device <DEVICE>  use <DEVICE> file. Default is '%s'\n"
45             "  -v, --verbose  Prints verbose information while running\n\n",
46             name, codeSize, snoopFilename);
47 }
48 
49 /*
50  * Callback handling IO event from the POST code fd. i.e. there is new
51  * POST code available to read.
52  */
53 void PostCodeEventHandler(sdeventplus::source::IO& s, int postFd, uint32_t,
54                           PostReporter* reporter, bool verbose)
55 {
56     uint64_t code = 0;
57     ssize_t readb;
58     while ((readb = read(postFd, &code, codeSize)) > 0)
59     {
60         code = le64toh(code);
61         if (verbose)
62         {
63             fprintf(stderr, "Code: 0x%" PRIx64 "\n", code);
64         }
65         // HACK: Always send property changed signal even for the same code
66         // since we are single threaded, external users will never see the
67         // first value.
68         reporter->value(~code, true);
69         reporter->value(code);
70 
71         // read depends on old data being cleared since it doens't always read
72         // the full code size
73         code = 0;
74     }
75 
76     if (readb < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
77     {
78         return;
79     }
80 
81     /* Read failure. */
82     if (readb == 0)
83     {
84         fprintf(stderr, "Unexpected EOF reading postcode\n");
85     }
86     else
87     {
88         fprintf(stderr, "Failed to read postcode: %s\n", strerror(errno));
89     }
90     s.get_event().exit(1);
91 }
92 
93 /*
94  * TODO(venture): this only listens one of the possible snoop ports, but
95  * doesn't share the namespace.
96  *
97  * This polls() the lpc snoop character device and it owns the dbus object
98  * whose value is the latest port 80h value.
99  */
100 int main(int argc, char* argv[])
101 {
102     int rc = 0;
103     int opt;
104     int postFd = -1;
105 
106     /*
107      * These string constants are only used in this method within this object
108      * and this object is the only object feeding into the final binary.
109      *
110      * If however, another object is added to this binary it would be proper
111      * to move these declarations to be global and extern to the other object.
112      */
113     const char* snoopObject = SNOOP_OBJECTPATH;
114     const char* snoopDbus = SNOOP_BUSNAME;
115 
116     bool deferSignals = true;
117     bool verbose = false;
118 
119     // clang-format off
120     static const struct option long_options[] = {
121         {"bytes",  required_argument, NULL, 'b'},
122         {"device", required_argument, NULL, 'd'},
123         {"verbose", no_argument, NULL, 'v'},
124         {0, 0, 0, 0}
125     };
126     // clang-format on
127 
128     while ((opt = getopt_long(argc, argv, "b:d:v", long_options, NULL)) != -1)
129     {
130         switch (opt)
131         {
132             case 0:
133                 break;
134             case 'b':
135                 codeSize = atoi(optarg);
136 
137                 if (codeSize < 1 || codeSize > 8)
138                 {
139                     fprintf(stderr,
140                             "Invalid POST code size '%s'. Must be "
141                             "an integer from 1 to 8.\n",
142                             optarg);
143                     exit(EXIT_FAILURE);
144                 }
145                 break;
146             case 'd':
147                 snoopFilename = optarg;
148                 break;
149             case 'v':
150                 verbose = true;
151                 break;
152             default:
153                 usage(argv[0]);
154                 exit(EXIT_FAILURE);
155         }
156     }
157 
158     postFd = open(snoopFilename, O_NONBLOCK);
159     if (postFd < 0)
160     {
161         fprintf(stderr, "Unable to open: %s\n", snoopFilename);
162         return -1;
163     }
164 
165     auto bus = sdbusplus::bus::new_default();
166 
167     // Add systemd object manager.
168     sdbusplus::server::manager::manager(bus, snoopObject);
169 
170     PostReporter reporter(bus, snoopObject, deferSignals);
171     reporter.emit_object_added();
172     bus.request_name(snoopDbus);
173 
174     // Create sdevent and add IO source
175     try
176     {
177         sdeventplus::Event event = sdeventplus::Event::get_default();
178         sdeventplus::source::IO reporterSource(
179             event, postFd, EPOLLIN | EPOLLET,
180             std::bind(PostCodeEventHandler, std::placeholders::_1,
181                       std::placeholders::_2, std::placeholders::_3, &reporter,
182                       verbose));
183         // Enable bus to handle incoming IO and bus events
184         bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
185         rc = event.loop();
186     }
187     catch (const std::exception& e)
188     {
189         fprintf(stderr, "%s\n", e.what());
190     }
191 
192     if (postFd > -1)
193     {
194         close(postFd);
195     }
196 
197     return rc;
198 }
199