xref: /openbmc/phosphor-host-postd/main.cpp (revision 2ae207c2)
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 <poll.h>
19 #include <unistd.h>
20 
21 #include <array>
22 #include <cstdint>
23 #include <iostream>
24 #include <memory>
25 #include <thread>
26 
27 #include <sdbusplus/bus.hpp>
28 #include <sdbusplus/server.hpp>
29 #include "xyz/openbmc_project/State/Boot/Raw/server.hpp"
30 
31 #include "lpcsnoop/snoop.hpp"
32 
33 template <typename... T>
34 using ServerObject = typename sdbusplus::server::object::object<T...>;
35 using PostInterface = sdbusplus::xyz::openbmc_project::State::Boot::server::Raw;
36 using PostObject = ServerObject<PostInterface>;
37 
38 class PostReporter : public PostObject
39 {
40   public:
41     PostReporter(sdbusplus::bus::bus& bus, const char* objPath, bool defer) :
42         PostObject(bus, objPath, defer)
43     {
44     }
45 };
46 
47 /*
48  * 256 bytes is a nice amount.  It's improbable we'd need this many, but its
49  * gives us leg room in the event the driver poll doesn't return in a timely
50  * fashion.  So, mostly arbitrarily chosen.
51  */
52 static constexpr size_t BUFFER_SIZE = 256;
53 
54 /*
55  * Process any incoming dbus inquiries, which include introspection etc.
56  */
57 void ProcessDbus(sdbusplus::bus::bus& bus)
58 {
59     while (true)
60     {
61         bus.process_discard();
62         bus.wait(); // wait indefinitely
63     }
64 
65     return;
66 }
67 
68 /*
69  * TODO(venture): this only listens one of the possible snoop ports, but
70  * doesn't share the namespace.
71  *
72  * This polls() the lpc snoop character device and it owns the dbus object
73  * whose value is the latest port 80h value.
74  */
75 int main(int argc, char* argv[])
76 {
77     int rc = 0;
78     struct pollfd pollset;
79     int pollr;
80     int readb;
81     int postFd = -1;
82     std::array<uint8_t, BUFFER_SIZE> buffer;
83 
84     /*
85      * These string constants are only used in this method within this object
86      * and this object is the only object feeding into the final binary.
87      *
88      * If however, another object is added to this binary it would be proper
89      * to move these declarations to be global and extern to the other object.
90      */
91     const char* snoopObject = SNOOP_OBJECTPATH;
92     const char* snoopDbus = SNOOP_BUSNAME;
93     /*
94      * The following string would be promoted if used in another file under the
95      * same header.
96      */
97     auto snoopFilename = "/dev/aspeed-lpc-snoop0";
98     bool deferSignals = true;
99 
100     postFd = open(snoopFilename, 0);
101     if (postFd < 0)
102     {
103         fprintf(stderr, "Unable to open: %s\n", snoopFilename);
104         return -1;
105     }
106 
107     pollset.fd = postFd;
108     pollset.events |= POLLIN;
109 
110     auto bus = sdbusplus::bus::new_default();
111 
112     // Add systemd object manager.
113     sdbusplus::server::manager::manager(bus, snoopObject);
114 
115     PostReporter reporter(bus, snoopObject, deferSignals);
116     reporter.emit_object_added();
117 
118     bus.request_name(snoopDbus);
119 
120     /*
121      * I don't see a public interface for getting the underlying sd_bus*
122      * so instead of poll(bus, driver), I'll just create a separate thread.
123      *
124      * TODO(venture): There may be a way to use sdevent to poll both the file
125      * and the dbus in the same event loop.  If I could get the sdbus pointer
126      * from bus directly, I'd grab a file handler from it, and then just poll on
127      * both in one loop.  From a cursory look at sdevent, I should be able to do
128      * something similar with that at some point.
129      */
130     std::thread lt(ProcessDbus, std::ref(bus));
131 
132     /* infinitely listen for POST codes and broadcast. */
133     while (true)
134     {
135         pollr = poll(&pollset, 1, -1); /* polls indefinitely. */
136         if (pollr < 0)
137         {
138             /* poll returned error. */
139             rc = -errno;
140             goto exit;
141         }
142 
143         if (pollr > 0)
144         {
145             if (pollset.revents & POLLIN)
146             {
147                 readb = read(postFd, buffer.data(), buffer.size());
148                 if (readb < 0)
149                 {
150                     /* Read failure. */
151                     rc = readb;
152                     goto exit;
153                 }
154                 else
155                 {
156                     /* Broadcast the bytes read. */
157                     for (int i = 0; i < readb; i++)
158                     {
159                         reporter.value(buffer[i]);
160                     }
161                 }
162             }
163         }
164     }
165 
166 exit:
167     if (postFd > -1)
168     {
169         close(postFd);
170     }
171 
172     lt.join();
173 
174     return rc;
175 }
176