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