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 <poll.h> 20 #include <unistd.h> 21 22 #include <array> 23 #include <cstdint> 24 #include <iostream> 25 #include <memory> 26 #include <thread> 27 28 #include <sdbusplus/bus.hpp> 29 #include <sdbusplus/server.hpp> 30 #include "xyz/openbmc_project/State/Boot/Raw/server.hpp" 31 32 #include "lpcsnoop/snoop.hpp" 33 34 template <typename... T> 35 using ServerObject = typename sdbusplus::server::object::object<T...>; 36 using PostInterface = sdbusplus::xyz::openbmc_project::State::Boot::server::Raw; 37 using PostObject = ServerObject<PostInterface>; 38 39 class PostReporter : public PostObject 40 { 41 public: 42 PostReporter(sdbusplus::bus::bus& bus, const char* objPath, bool defer) : 43 PostObject(bus, objPath, defer) 44 { 45 } 46 }; 47 48 static const char* snoopFilename = "/dev/aspeed-lpc-snoop0"; 49 static size_t codeSize = 1; /* Size of each POST code in bytes */ 50 51 /* 52 * 256 bytes is a nice amount. It's improbable we'd need this many, but its 53 * gives us leg room in the event the driver poll doesn't return in a timely 54 * fashion. So, mostly arbitrarily chosen. 55 */ 56 static constexpr size_t BUFFER_SIZE = 256; 57 58 /* 59 * Process any incoming dbus inquiries, which include introspection etc. 60 */ 61 void ProcessDbus(sdbusplus::bus::bus& bus) 62 { 63 while (true) 64 { 65 bus.process_discard(); 66 bus.wait(); // wait indefinitely 67 } 68 69 return; 70 } 71 72 static void usage(const char* name) 73 { 74 fprintf(stderr, 75 "Usage: %s [-d <DEVICE>]\n" 76 " -b, --bytes <SIZE> set POST code length to <SIZE> bytes. " 77 "Default is %zu\n" 78 " -d, --device <DEVICE> use <DEVICE> file. Default is '%s'\n\n", 79 name, codeSize, snoopFilename); 80 } 81 82 static uint64_t assembleBytes(std::array<uint8_t, BUFFER_SIZE> buf, int start, 83 int size) 84 { 85 uint64_t result = 0; 86 87 for (int i = start + size - 1; i >= start; i--) 88 { 89 result <<= 8; 90 result |= buf[i]; 91 } 92 93 return result; 94 } 95 96 /* 97 * TODO(venture): this only listens one of the possible snoop ports, but 98 * doesn't share the namespace. 99 * 100 * This polls() the lpc snoop character device and it owns the dbus object 101 * whose value is the latest port 80h value. 102 */ 103 int main(int argc, char* argv[]) 104 { 105 int rc = 0; 106 int opt; 107 struct pollfd pollset; 108 int pollr; 109 int readb; 110 int postFd = -1; 111 std::array<uint8_t, BUFFER_SIZE> buffer; 112 113 /* 114 * These string constants are only used in this method within this object 115 * and this object is the only object feeding into the final binary. 116 * 117 * If however, another object is added to this binary it would be proper 118 * to move these declarations to be global and extern to the other object. 119 */ 120 const char* snoopObject = SNOOP_OBJECTPATH; 121 const char* snoopDbus = SNOOP_BUSNAME; 122 123 bool deferSignals = true; 124 125 static const struct option long_options[] = { 126 {"bytes", required_argument, NULL, 'b'}, 127 {"device", required_argument, NULL, 'd'}, 128 {0, 0, 0, 0}}; 129 130 while ((opt = getopt_long(argc, argv, "b:d:", long_options, NULL)) != -1) 131 { 132 switch (opt) 133 { 134 case 0: 135 break; 136 case 'b': 137 codeSize = atoi(optarg); 138 139 if (codeSize < 1 || codeSize > 8) 140 { 141 fprintf(stderr, 142 "Invalid POST code size '%s'. Must be " 143 "an integer from 1 to 8.\n", 144 optarg); 145 exit(EXIT_FAILURE); 146 } 147 break; 148 case 'd': 149 snoopFilename = optarg; 150 break; 151 default: 152 usage(argv[0]); 153 exit(EXIT_FAILURE); 154 } 155 } 156 157 postFd = open(snoopFilename, 0); 158 if (postFd < 0) 159 { 160 fprintf(stderr, "Unable to open: %s\n", snoopFilename); 161 return -1; 162 } 163 164 pollset.fd = postFd; 165 pollset.events |= POLLIN; 166 167 auto bus = sdbusplus::bus::new_default(); 168 169 // Add systemd object manager. 170 sdbusplus::server::manager::manager(bus, snoopObject); 171 172 PostReporter reporter(bus, snoopObject, deferSignals); 173 reporter.emit_object_added(); 174 175 bus.request_name(snoopDbus); 176 177 /* 178 * I don't see a public interface for getting the underlying sd_bus* 179 * so instead of poll(bus, driver), I'll just create a separate thread. 180 * 181 * TODO(venture): There may be a way to use sdevent to poll both the file 182 * and the dbus in the same event loop. If I could get the sdbus pointer 183 * from bus directly, I'd grab a file handler from it, and then just poll on 184 * both in one loop. From a cursory look at sdevent, I should be able to do 185 * something similar with that at some point. 186 */ 187 std::thread lt(ProcessDbus, std::ref(bus)); 188 189 /* infinitely listen for POST codes and broadcast. */ 190 while (true) 191 { 192 pollr = poll(&pollset, 1, -1); /* polls indefinitely. */ 193 if (pollr < 0) 194 { 195 /* poll returned error. */ 196 rc = -errno; 197 goto exit; 198 } 199 200 if (pollr > 0) 201 { 202 if (pollset.revents & POLLIN) 203 { 204 readb = read(postFd, buffer.data(), buffer.size()); 205 if (readb < 0) 206 { 207 /* Read failure. */ 208 rc = readb; 209 goto exit; 210 } 211 else 212 { 213 if (readb % codeSize != 0) 214 { 215 fprintf(stderr, 216 "Warning: read size %d not a multiple of " 217 "POST code length %zu. Some codes may be " 218 "corrupt or missing\n", 219 readb, codeSize); 220 readb -= (readb % codeSize); 221 } 222 223 /* Broadcast the values read. */ 224 for (int i = 0; i < readb; i += codeSize) 225 { 226 reporter.value(assembleBytes(buffer, i, codeSize)); 227 } 228 } 229 } 230 } 231 } 232 233 exit: 234 if (postFd > -1) 235 { 236 close(postFd); 237 } 238 239 lt.join(); 240 241 return rc; 242 } 243