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