xref: /openbmc/phosphor-host-postd/main.cpp (revision afff574e)
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 <fcntl.h>
18 #include <getopt.h>
19 #include <unistd.h>
20 
21 #include <array>
22 #include <cstdint>
23 #include <iostream>
24 #include <memory>
25 #include <sys/epoll.h>
26 #include <systemd/sd-event.h>
27 #include <thread>
28 
29 #include "lpcsnoop/snoop.hpp"
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     static const struct option long_options[] = {
133         {"bytes", required_argument, NULL, 'b'},
134         {"device", required_argument, NULL, 'd'},
135         {0, 0, 0, 0}};
136 
137     while ((opt = getopt_long(argc, argv, "b:d:", long_options, NULL)) != -1)
138     {
139         switch (opt)
140         {
141             case 0:
142                 break;
143             case 'b':
144                 codeSize = atoi(optarg);
145 
146                 if (codeSize < 1 || codeSize > 8)
147                 {
148                     fprintf(stderr,
149                             "Invalid POST code size '%s'. Must be "
150                             "an integer from 1 to 8.\n",
151                             optarg);
152                     exit(EXIT_FAILURE);
153                 }
154                 break;
155             case 'd':
156                 snoopFilename = optarg;
157                 break;
158             default:
159                 usage(argv[0]);
160                 exit(EXIT_FAILURE);
161         }
162     }
163 
164     postFd = open(snoopFilename, 0);
165     if (postFd < 0)
166     {
167         fprintf(stderr, "Unable to open: %s\n", snoopFilename);
168         return -1;
169     }
170 
171     auto bus = sdbusplus::bus::new_default();
172 
173     // Add systemd object manager.
174     sdbusplus::server::manager::manager(bus, snoopObject);
175 
176     PostReporter reporter(bus, snoopObject, deferSignals);
177 
178     // Create sdevent and add IO source
179     // TODO(kunyi): the current interface is really C-style. Move to a C++
180     // wrapper when there is a SdEventPlus or some sort of that is ready.
181     rc = sd_event_default(&event);
182 
183     if (rc < 0)
184     {
185         fprintf(stderr, "Failed to allocate event loop:%s\n", strerror(-rc));
186         goto finish;
187     }
188 
189     sd_event_source* source;
190     rc = sd_event_add_io(event, &source, postFd, EPOLLIN, PostCodeEventHandler,
191                          &reporter);
192     if (rc < 0)
193     {
194         fprintf(stderr, "Failed to add sdevent io source:%s\n", strerror(-rc));
195         goto finish;
196     }
197 
198     // Enable bus to handle incoming IO and bus events
199     reporter.emit_object_added();
200     bus.request_name(snoopDbus);
201     bus.attach_event(event, SD_EVENT_PRIORITY_NORMAL);
202 
203     rc = sd_event_loop(event);
204 
205 finish:
206     if (postFd > -1)
207     {
208         close(postFd);
209     }
210 
211     return rc;
212 }
213