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