xref: /openbmc/phosphor-host-postd/main.cpp (revision b5754fd4)
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 <fcntl.h>
20 #include <getopt.h>
21 #include <sys/epoll.h>
22 #include <systemd/sd-event.h>
23 #include <unistd.h>
24 
25 #include <array>
26 #include <cstdint>
27 #include <iostream>
28 #include <memory>
29 #include <thread>
30 
31 static const char* snoopFilename = "/dev/aspeed-lpc-snoop0";
32 static size_t codeSize = 1; /* Size of each POST code in bytes */
33 
34 /*
35  * 256 bytes is a nice amount.  It's improbable we'd need this many, but its
36  * gives us leg room in the event the driver poll doesn't return in a timely
37  * fashion.  So, mostly arbitrarily chosen.
38  */
39 static constexpr size_t BUFFER_SIZE = 256;
40 
41 static void usage(const char* name)
42 {
43     fprintf(stderr,
44             "Usage: %s [-d <DEVICE>]\n"
45             "  -b, --bytes <SIZE>     set POST code length to <SIZE> bytes. "
46             "Default is %zu\n"
47             "  -d, --device <DEVICE>  use <DEVICE> file. Default is '%s'\n\n",
48             name, codeSize, snoopFilename);
49 }
50 
51 static uint64_t assembleBytes(std::array<uint8_t, BUFFER_SIZE> buf, int start,
52                               int size)
53 {
54     uint64_t result = 0;
55 
56     for (int i = start + size - 1; i >= start; i--)
57     {
58         result <<= 8;
59         result |= buf[i];
60     }
61 
62     return result;
63 }
64 
65 /*
66  * Callback handling IO event from the POST code fd. i.e. there is new
67  * POST code available to read.
68  */
69 int PostCodeEventHandler(sd_event_source* s, int postFd, uint32_t revents,
70                          void* userdata)
71 {
72     PostReporter* reporter = static_cast<PostReporter*>(userdata);
73     std::array<uint8_t, BUFFER_SIZE> buffer;
74     int readb;
75 
76     // TODO(kunyi): more error handling for EPOLLPRI/EPOLLERR.
77     if (revents & EPOLLIN)
78     {
79         readb = read(postFd, buffer.data(), buffer.size());
80         if (readb < 0)
81         {
82             /* Read failure. */
83             return readb;
84         }
85 
86         if (readb % codeSize != 0)
87         {
88             fprintf(stderr,
89                     "Warning: read size %d not a multiple of "
90                     "POST code length %zu. Some codes may be "
91                     "corrupt or missing\n",
92                     readb, codeSize);
93             readb -= (readb % codeSize);
94         }
95 
96         /* Broadcast the values read. */
97         for (int i = 0; i < readb; i += codeSize)
98         {
99             reporter->value(assembleBytes(buffer, i, codeSize));
100         }
101     }
102 
103     return 0;
104 }
105 
106 /*
107  * TODO(venture): this only listens one of the possible snoop ports, but
108  * doesn't share the namespace.
109  *
110  * This polls() the lpc snoop character device and it owns the dbus object
111  * whose value is the latest port 80h value.
112  */
113 int main(int argc, char* argv[])
114 {
115     int rc = 0;
116     int opt;
117     int postFd = -1;
118     sd_event* event = NULL;
119 
120     /*
121      * These string constants are only used in this method within this object
122      * and this object is the only object feeding into the final binary.
123      *
124      * If however, another object is added to this binary it would be proper
125      * to move these declarations to be global and extern to the other object.
126      */
127     const char* snoopObject = SNOOP_OBJECTPATH;
128     const char* snoopDbus = SNOOP_BUSNAME;
129 
130     bool deferSignals = true;
131 
132     // clang-format off
133     static const struct option long_options[] = {
134         {"bytes",  required_argument, NULL, 'b'},
135         {"device", required_argument, NULL, 'd'},
136         {0, 0, 0, 0}
137     };
138     // clang-format on
139 
140     while ((opt = getopt_long(argc, argv, "b:d:", long_options, NULL)) != -1)
141     {
142         switch (opt)
143         {
144             case 0:
145                 break;
146             case 'b':
147                 codeSize = atoi(optarg);
148 
149                 if (codeSize < 1 || codeSize > 8)
150                 {
151                     fprintf(stderr,
152                             "Invalid POST code size '%s'. Must be "
153                             "an integer from 1 to 8.\n",
154                             optarg);
155                     exit(EXIT_FAILURE);
156                 }
157                 break;
158             case 'd':
159                 snoopFilename = optarg;
160                 break;
161             default:
162                 usage(argv[0]);
163                 exit(EXIT_FAILURE);
164         }
165     }
166 
167     postFd = open(snoopFilename, 0);
168     if (postFd < 0)
169     {
170         fprintf(stderr, "Unable to open: %s\n", snoopFilename);
171         return -1;
172     }
173 
174     auto bus = sdbusplus::bus::new_default();
175 
176     // Add systemd object manager.
177     sdbusplus::server::manager::manager(bus, snoopObject);
178 
179     PostReporter reporter(bus, snoopObject, deferSignals);
180 
181     // Create sdevent and add IO source
182     // TODO(kunyi): the current interface is really C-style. Move to a C++
183     // wrapper when there is a SdEventPlus or some sort of that is ready.
184     rc = sd_event_default(&event);
185 
186     if (rc < 0)
187     {
188         fprintf(stderr, "Failed to allocate event loop:%s\n", strerror(-rc));
189         goto finish;
190     }
191 
192     sd_event_source* source;
193     rc = sd_event_add_io(event, &source, postFd, EPOLLIN, PostCodeEventHandler,
194                          &reporter);
195     if (rc < 0)
196     {
197         fprintf(stderr, "Failed to add sdevent io source:%s\n", strerror(-rc));
198         goto finish;
199     }
200 
201     // Enable bus to handle incoming IO and bus events
202     reporter.emit_object_added();
203     bus.request_name(snoopDbus);
204     bus.attach_event(event, SD_EVENT_PRIORITY_NORMAL);
205 
206     rc = sd_event_loop(event);
207 
208 finish:
209     if (postFd > -1)
210     {
211         close(postFd);
212     }
213 
214     return rc;
215 }
216