blob: cded6cf5739450dbe1ed6b726d232a74b394c49e [file] [log] [blame]
Patrick Venture33569752018-03-12 18:56:14 -07001/**
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
Kumar Thangavel0269eaf2021-08-11 15:45:18 +053017#ifdef ENABLE_IPMI_SNOOP
18#include "ipmisnoop/ipmisnoop.hpp"
19#endif
Patrick Ventureb5754fd2018-09-10 13:13:58 -070020#include "lpcsnoop/snoop.hpp"
21
William A. Kennington III6dac4c52019-12-13 15:05:00 -080022#include <endian.h>
Patrick Venture33569752018-03-12 18:56:14 -070023#include <fcntl.h>
Benjamin Faire7b07f02018-07-02 09:51:37 -070024#include <getopt.h>
Patrick Ventureb5754fd2018-09-10 13:13:58 -070025#include <sys/epoll.h>
26#include <systemd/sd-event.h>
Patrick Venture33569752018-03-12 18:56:14 -070027#include <unistd.h>
28
Kun Yi1c16ad82018-09-12 10:01:49 -070029#include <sdeventplus/event.hpp>
30#include <sdeventplus/source/event.hpp>
31#include <sdeventplus/source/io.hpp>
William A. Kennington III6a5e0a12021-12-19 20:47:34 -080032#include <sdeventplus/source/signal.hpp>
Jonathan Doman38b09462023-04-26 11:45:39 -070033#include <sdeventplus/source/time.hpp>
William A. Kennington III30751ec2022-11-21 18:38:36 -080034#include <sdeventplus/utility/sdbus.hpp>
William A. Kennington III6a5e0a12021-12-19 20:47:34 -080035#include <stdplus/signal.hpp>
Patrick Williams0ea73572023-05-10 07:50:44 -050036
37#include <chrono>
38#include <cstdint>
39#include <exception>
40#include <functional>
41#include <iostream>
42#include <optional>
Patrick Venture33569752018-03-12 18:56:14 -070043#include <thread>
44
Benjamin Fairf69ad7e2018-07-13 13:41:10 -070045static size_t codeSize = 1; /* Size of each POST code in bytes */
Jonathan Doman38b09462023-04-26 11:45:39 -070046static bool verbose = false;
cchoux72d55072023-12-05 14:05:23 +080047static std::function<bool(uint64_t&, ssize_t)> procPostCode;
Benjamin Faire7b07f02018-07-02 09:51:37 -070048
Benjamin Faire7b07f02018-07-02 09:51:37 -070049static void usage(const char* name)
50{
51 fprintf(stderr,
Jonathan Domande7a6dd2023-05-03 10:54:08 -070052 "Usage: %s\n"
53#ifdef ENABLE_IPMI_SNOOP
54 " -h, --host <host instances> Default is '0'\n"
55#else
Manojkiran Edaaade4ad2021-02-19 11:20:33 +053056 " -d, --device <DEVICE> use <DEVICE> file.\n"
Jonathan Domanfd2430d2023-05-03 11:44:01 -070057 " -r, --rate-limit=<N> Only process N POST codes from the "
58 "device per second.\n"
Jonathan Domande7a6dd2023-05-03 10:54:08 -070059 " -b, --bytes <SIZE> set POST code length to <SIZE> bytes. "
60 "Default is 1\n"
61#endif
William A. Kennington III66efa632020-04-16 18:46:20 -070062 " -v, --verbose Prints verbose information while running\n\n",
Jonathan Domande7a6dd2023-05-03 10:54:08 -070063 name);
Benjamin Fairf69ad7e2018-07-13 13:41:10 -070064}
65
Jonathan Doman38b09462023-04-26 11:45:39 -070066/**
67 * Call once for each POST code received. If the number of POST codes exceeds
68 * the configured rate limit, this function will disable the snoop device IO
69 * source until the end of the 1 second interval, then re-enable it.
70 *
71 * @return Whether the rate limit is exceeded.
72 */
73bool rateLimit(PostReporter& reporter, sdeventplus::source::IO& ioSource)
74{
75 if (reporter.rateLimit == 0)
76 {
77 // Rate limiting is disabled.
78 return false;
79 }
80
81 using Clock = sdeventplus::Clock<sdeventplus::ClockId::Monotonic>;
82
83 static constexpr std::chrono::seconds rateLimitInterval(1);
84 static unsigned int rateLimitCount = 0;
85 static Clock::time_point rateLimitEndTime;
86
87 const sdeventplus::Event& event = ioSource.get_event();
88
89 if (rateLimitCount == 0)
90 {
91 // Initialize the end time when we start a new interval
92 rateLimitEndTime = Clock(event).now() + rateLimitInterval;
93 }
94
95 if (++rateLimitCount < reporter.rateLimit)
96 {
97 return false;
98 }
99
100 rateLimitCount = 0;
101
102 if (rateLimitEndTime < Clock(event).now())
103 {
104 return false;
105 }
106
107 if (verbose)
108 {
109 fprintf(stderr, "Hit POST code rate limit - disabling temporarily\n");
110 }
111
112 ioSource.set_enabled(sdeventplus::source::Enabled::Off);
113 sdeventplus::source::Time<sdeventplus::ClockId::Monotonic>(
114 event, rateLimitEndTime, std::chrono::milliseconds(100),
115 [&ioSource](auto&, auto) {
Patrick Williams0ea73572023-05-10 07:50:44 -0500116 if (verbose)
117 {
118 fprintf(stderr, "Reenabling POST code handler\n");
119 }
120 ioSource.set_enabled(sdeventplus::source::Enabled::On);
Patrick Williamsbebbda92023-10-20 11:19:03 -0500121 }).set_floating(true);
Jonathan Doman38b09462023-04-26 11:45:39 -0700122 return true;
123}
124
Patrick Venture33569752018-03-12 18:56:14 -0700125/*
cchoux72d55072023-12-05 14:05:23 +0800126 * Split input code into multiple 2 bytes PCC code, If the PCC code prefix
127 * matches the check code, store each PCC code in aspeedPCCBuffer, or clear
128 * aspeedPCCBuffer if the prefix does not match.
129 *
130 * Each PCC code contains one byte of port number (MSB) and another byte of
131 * partial postcode (LSB). To get a complete postcode, the PCC code should
132 * followed the sequence of 0x40AA, 0x41BB, 0x42CC & 0x43DD. When
133 * aspeedPCCBuffer contains enough PCC codes, the postcode will be assigned as
134 * 0xDDCCBBAA.
135 */
136bool aspeedPCC(uint64_t& code, ssize_t readb)
137{
138 // Size of data coming from the PCC hardware
139 constexpr size_t pccSize = sizeof(uint16_t);
140 // Required PCC count of a full postcode, if codeSize is 8 bytes, it means
141 // it require 4 PCC codes in correct sequence to get a complete postcode.
142 const size_t fullPostPCCCount = codeSize / pccSize;
143 // A PCC buffer for storing PCC code in sequence.
144 static std::vector<uint16_t> aspeedPCCBuffer;
145 constexpr uint16_t firstPCCPortNumber = 0x4000;
146 constexpr uint16_t pccPortNumberMask = 0xFF00;
147 constexpr uint16_t pccPostCodeMask = 0x00FF;
148 constexpr uint8_t byteShift = 8;
149
150 uint16_t* codePtr = reinterpret_cast<uint16_t*>(&code);
151
152 for (size_t i = 0; i < (readb / pccSize); i++)
153 {
154 uint16_t checkCode = firstPCCPortNumber +
155 ((aspeedPCCBuffer.size() % fullPostPCCCount)
156 << byteShift);
157
158 if (checkCode == (codePtr[i] & pccPortNumberMask))
159 {
160 aspeedPCCBuffer.emplace_back(codePtr[i]);
161 }
162 else
163 {
164 aspeedPCCBuffer.clear();
165
166 // keep the PCC code if codePtr[i] matches with 0x40XX as first PCC
167 // code in buffer.
168 if ((codePtr[i] & pccPortNumberMask) == firstPCCPortNumber)
169 {
170 aspeedPCCBuffer.emplace_back(codePtr[i]);
171 }
172 }
173 }
174
175 if (aspeedPCCBuffer.size() < fullPostPCCCount)
176 {
177 // not receive full postcode yet.
178 return false;
179 }
180
181 // Remove the prefix bytes and combine the partial postcodes together.
182 code = 0;
183 for (size_t i = 0; i < fullPostPCCCount; i++)
184 {
185 code |= static_cast<uint64_t>(aspeedPCCBuffer[i] & pccPostCodeMask)
186 << (byteShift * i);
187 }
188 aspeedPCCBuffer.erase(aspeedPCCBuffer.begin(),
189 aspeedPCCBuffer.begin() + fullPostPCCCount);
190
191 return true;
192}
193
194/*
Kun Yieb312312018-06-13 09:20:50 -0700195 * Callback handling IO event from the POST code fd. i.e. there is new
196 * POST code available to read.
197 */
Jonathan Doman38b09462023-04-26 11:45:39 -0700198void PostCodeEventHandler(PostReporter* reporter, sdeventplus::source::IO& s,
199 int postFd, uint32_t)
Kun Yieb312312018-06-13 09:20:50 -0700200{
William A. Kennington III6dac4c52019-12-13 15:05:00 -0800201 uint64_t code = 0;
William A. Kennington III0f964b42020-04-16 18:46:03 -0700202 ssize_t readb;
Jonathan Doman38b09462023-04-26 11:45:39 -0700203
William A. Kennington III0f964b42020-04-16 18:46:03 -0700204 while ((readb = read(postFd, &code, codeSize)) > 0)
Kun Yi1c16ad82018-09-12 10:01:49 -0700205 {
cchoux72d55072023-12-05 14:05:23 +0800206 if (procPostCode && procPostCode(code, readb) == false)
207 {
208 return;
209 }
210
William A. Kennington IIIf21475a2019-12-13 17:21:24 -0800211 code = le64toh(code);
William A. Kennington III66efa632020-04-16 18:46:20 -0700212 if (verbose)
213 {
214 fprintf(stderr, "Code: 0x%" PRIx64 "\n", code);
215 }
William A. Kennington IIIf21475a2019-12-13 17:21:24 -0800216 // HACK: Always send property changed signal even for the same code
217 // since we are single threaded, external users will never see the
218 // first value.
Manojkiran Edaba5258f2021-02-25 13:23:33 +0530219 reporter->value(std::make_tuple(~code, secondary_post_code_t{}), true);
220 reporter->value(std::make_tuple(code, secondary_post_code_t{}));
William A. Kennington III0f964b42020-04-16 18:46:03 -0700221
222 // read depends on old data being cleared since it doens't always read
223 // the full code size
224 code = 0;
Jonathan Doman38b09462023-04-26 11:45:39 -0700225
226 if (rateLimit(*reporter, s))
227 {
228 return;
229 }
Kun Yi1c16ad82018-09-12 10:01:49 -0700230 }
William A. Kennington III0f964b42020-04-16 18:46:03 -0700231
232 if (readb < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
233 {
234 return;
235 }
236
237 /* Read failure. */
238 if (readb == 0)
239 {
240 fprintf(stderr, "Unexpected EOF reading postcode\n");
241 }
242 else
243 {
244 fprintf(stderr, "Failed to read postcode: %s\n", strerror(errno));
245 }
246 s.get_event().exit(1);
Kun Yieb312312018-06-13 09:20:50 -0700247}
248
249/*
Patrick Venture33569752018-03-12 18:56:14 -0700250 * TODO(venture): this only listens one of the possible snoop ports, but
251 * doesn't share the namespace.
252 *
253 * This polls() the lpc snoop character device and it owns the dbus object
254 * whose value is the latest port 80h value.
255 */
256int main(int argc, char* argv[])
257{
Kumar Thangavel0269eaf2021-08-11 15:45:18 +0530258 int postFd = -1;
Jonathan Doman38b09462023-04-26 11:45:39 -0700259 unsigned int rateLimit = 0;
Kumar Thangavel0269eaf2021-08-11 15:45:18 +0530260
261 int opt;
Kumar Thangavel0269eaf2021-08-11 15:45:18 +0530262
Kumar Thangavel0269eaf2021-08-11 15:45:18 +0530263 std::vector<std::string> host;
Patrick Venture33569752018-03-12 18:56:14 -0700264
Patrick Venture1ceb21b2018-08-08 11:29:36 -0700265 // clang-format off
Benjamin Faire7b07f02018-07-02 09:51:37 -0700266 static const struct option long_options[] = {
Jonathan Domande7a6dd2023-05-03 10:54:08 -0700267#ifdef ENABLE_IPMI_SNOOP
Kumar Thangavel0269eaf2021-08-11 15:45:18 +0530268 {"host", optional_argument, NULL, 'h'},
Jonathan Domande7a6dd2023-05-03 10:54:08 -0700269#else
Manojkiran Edaaade4ad2021-02-19 11:20:33 +0530270 {"device", optional_argument, NULL, 'd'},
Jonathan Doman38b09462023-04-26 11:45:39 -0700271 {"rate-limit", optional_argument, NULL, 'r'},
Jonathan Domande7a6dd2023-05-03 10:54:08 -0700272 {"bytes", required_argument, NULL, 'b'},
273#endif
William A. Kennington III66efa632020-04-16 18:46:20 -0700274 {"verbose", no_argument, NULL, 'v'},
Patrick Venture1ceb21b2018-08-08 11:29:36 -0700275 {0, 0, 0, 0}
276 };
277 // clang-format on
Benjamin Faire7b07f02018-07-02 09:51:37 -0700278
Jonathan Domande7a6dd2023-05-03 10:54:08 -0700279 constexpr const char* optstring =
280#ifdef ENABLE_IPMI_SNOOP
281 "h:"
282#else
283 "d:r:b:"
284#endif
285 "v";
286
287 while ((opt = getopt_long(argc, argv, optstring, long_options, NULL)) != -1)
Benjamin Faire7b07f02018-07-02 09:51:37 -0700288 {
289 switch (opt)
290 {
291 case 0:
292 break;
Patrick Williams0ea73572023-05-10 07:50:44 -0500293 case 'h':
294 {
Kumar Thangavel0269eaf2021-08-11 15:45:18 +0530295 std::string_view instances = optarg;
296 size_t pos = 0;
297
298 while ((pos = instances.find(" ")) != std::string::npos)
299 {
300 host.emplace_back(instances.substr(0, pos));
301 instances.remove_prefix(pos + 1);
302 }
303 host.emplace_back(instances);
304 break;
305 }
Patrick Williams0ea73572023-05-10 07:50:44 -0500306 case 'b':
307 {
Benjamin Fairf69ad7e2018-07-13 13:41:10 -0700308 codeSize = atoi(optarg);
309
310 if (codeSize < 1 || codeSize > 8)
311 {
312 fprintf(stderr,
313 "Invalid POST code size '%s'. Must be "
314 "an integer from 1 to 8.\n",
315 optarg);
316 exit(EXIT_FAILURE);
317 }
318 break;
Kumar Thangavel0269eaf2021-08-11 15:45:18 +0530319 }
Benjamin Faire7b07f02018-07-02 09:51:37 -0700320 case 'd':
Rajaganesh Rathinasabapathi95aa4442024-04-08 01:25:20 -0500321 if (std::string(optarg).starts_with("/dev/aspeed-lpc-pcc"))
cchoux72d55072023-12-05 14:05:23 +0800322 {
323 procPostCode = aspeedPCC;
324 }
Kumar Thangavel0269eaf2021-08-11 15:45:18 +0530325
Manojkiran Edaaade4ad2021-02-19 11:20:33 +0530326 postFd = open(optarg, O_NONBLOCK);
327 if (postFd < 0)
328 {
329 fprintf(stderr, "Unable to open: %s\n", optarg);
330 return -1;
331 }
Benjamin Faire7b07f02018-07-02 09:51:37 -0700332 break;
Patrick Williams0ea73572023-05-10 07:50:44 -0500333 case 'r':
334 {
Jonathan Doman38b09462023-04-26 11:45:39 -0700335 int argVal = -1;
336 try
337 {
338 argVal = std::stoi(optarg);
339 }
340 catch (...)
Patrick Williams0ea73572023-05-10 07:50:44 -0500341 {}
Jonathan Doman38b09462023-04-26 11:45:39 -0700342
343 if (argVal < 1)
344 {
345 fprintf(stderr, "Invalid rate limit '%s'. Must be >= 1.\n",
346 optarg);
347 return EXIT_FAILURE;
348 }
349
350 rateLimit = static_cast<unsigned int>(argVal);
351 fprintf(stderr, "Rate limiting to %d POST codes per second.\n",
352 argVal);
353 break;
354 }
William A. Kennington III66efa632020-04-16 18:46:20 -0700355 case 'v':
356 verbose = true;
357 break;
Benjamin Faire7b07f02018-07-02 09:51:37 -0700358 default:
359 usage(argv[0]);
Jonathan Doman38b09462023-04-26 11:45:39 -0700360 return EXIT_FAILURE;
Benjamin Faire7b07f02018-07-02 09:51:37 -0700361 }
362 }
363
Patrick Venture33569752018-03-12 18:56:14 -0700364 auto bus = sdbusplus::bus::new_default();
365
Kumar Thangavel0269eaf2021-08-11 15:45:18 +0530366#ifdef ENABLE_IPMI_SNOOP
367 std::cout << "Verbose = " << verbose << std::endl;
368 int ret = postCodeIpmiHandler(ipmiSnoopObject, snoopDbus, bus, host);
369 if (ret < 0)
370 {
371 fprintf(stderr, "Error in postCodeIpmiHandler\n");
372 return ret;
373 }
374 return 0;
375#endif
376
Kumar Thangavel0269eaf2021-08-11 15:45:18 +0530377 bool deferSignals = true;
378
Patrick Venture33569752018-03-12 18:56:14 -0700379 // Add systemd object manager.
Patrick Williamsaebf87c2022-07-22 19:26:54 -0500380 sdbusplus::server::manager_t snoopdManager(bus, snoopObject);
Patrick Venture33569752018-03-12 18:56:14 -0700381
382 PostReporter reporter(bus, snoopObject, deferSignals);
Kun Yieb312312018-06-13 09:20:50 -0700383 reporter.emit_object_added();
384 bus.request_name(snoopDbus);
Kun Yieb312312018-06-13 09:20:50 -0700385
Kun Yi1c16ad82018-09-12 10:01:49 -0700386 // Create sdevent and add IO source
387 try
388 {
Kumar Thangavel0269eaf2021-08-11 15:45:18 +0530389 sdeventplus::Event event = sdeventplus::Event::get_default();
Harvey.Wu4f26b3e2022-05-12 08:54:30 +0800390 std::optional<sdeventplus::source::IO> reporterSource;
Manojkiran Edaaade4ad2021-02-19 11:20:33 +0530391 if (postFd > 0)
392 {
Jonathan Doman38b09462023-04-26 11:45:39 -0700393 reporter.rateLimit = rateLimit;
Harvey.Wu4f26b3e2022-05-12 08:54:30 +0800394 reporterSource.emplace(
Jonathan Doman38b09462023-04-26 11:45:39 -0700395 event, postFd, EPOLLIN,
396 std::bind_front(PostCodeEventHandler, &reporter));
Manojkiran Edaaade4ad2021-02-19 11:20:33 +0530397 }
Kun Yi1c16ad82018-09-12 10:01:49 -0700398 // Enable bus to handle incoming IO and bus events
William A. Kennington III30751ec2022-11-21 18:38:36 -0800399 auto intCb = [](sdeventplus::source::Signal& source,
400 const struct signalfd_siginfo*) {
William A. Kennington III6a5e0a12021-12-19 20:47:34 -0800401 source.get_event().exit(0);
402 };
403 stdplus::signal::block(SIGINT);
404 sdeventplus::source::Signal(event, SIGINT, intCb).set_floating(true);
405 stdplus::signal::block(SIGTERM);
406 sdeventplus::source::Signal(event, SIGTERM, std::move(intCb))
407 .set_floating(true);
William A. Kennington III30751ec2022-11-21 18:38:36 -0800408 return sdeventplus::utility::loopWithBus(event, bus);
Kun Yi1c16ad82018-09-12 10:01:49 -0700409 }
410 catch (const std::exception& e)
411 {
412 fprintf(stderr, "%s\n", e.what());
413 }
Kun Yieb312312018-06-13 09:20:50 -0700414
Patrick Venture33569752018-03-12 18:56:14 -0700415 if (postFd > -1)
416 {
417 close(postFd);
418 }
419
Sunita Kumari56127ef2022-10-12 05:01:20 +0000420 return 0;
Patrick Venture33569752018-03-12 18:56:14 -0700421}