blob: eb325fc5c1c78e2ce3434f8a4b29da5f2d70ad69 [file] [log] [blame]
Qiang XUe28d1fa2019-02-27 13:50:56 +08001/*
2// Copyright (c) 2018 Intel Corporation
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
Andrew Jefferye73bd0a2023-01-25 10:39:57 +103017#include "ChassisIntrusionSensor.hpp"
18
Qiang XUe28d1fa2019-02-27 13:50:56 +080019#include <fcntl.h>
20#include <sys/ioctl.h>
Matt Simmering79a160b2022-11-14 16:50:48 -080021#include <systemd/sd-journal.h>
Qiang XUe28d1fa2019-02-27 13:50:56 +080022#include <unistd.h>
23
Ed Tanous1f978632023-02-28 18:16:39 -080024#include <boost/asio/io_context.hpp>
James Feist38fb5982020-05-28 10:09:54 -070025#include <sdbusplus/asio/object_server.hpp>
26
Ed Tanous8a57ec02020-10-09 12:46:52 -070027#include <cerrno>
Qiang XUe28d1fa2019-02-27 13:50:56 +080028#include <chrono>
29#include <iostream>
Patrick Venture96e97db2019-10-31 13:44:38 -070030#include <memory>
Qiang XUe28d1fa2019-02-27 13:50:56 +080031#include <string>
32#include <thread>
Ed Tanous8a57ec02020-10-09 12:46:52 -070033#include <utility>
Qiang XUe28d1fa2019-02-27 13:50:56 +080034
James Feist38fb5982020-05-28 10:09:54 -070035extern "C"
36{
Qiang XUe28d1fa2019-02-27 13:50:56 +080037#include <i2c/smbus.h>
38#include <linux/i2c-dev.h>
39}
40
Ed Tanous8a57ec02020-10-09 12:46:52 -070041static constexpr bool debug = false;
Qiang XUe28d1fa2019-02-27 13:50:56 +080042
43static constexpr unsigned int intrusionSensorPollSec = 1;
44
45// SMLink Status Register
46const static constexpr size_t pchStatusRegIntrusion = 0x04;
47
48// Status bit field masks
49const static constexpr size_t pchRegMaskIntrusion = 0x01;
50
Ed Tanous8a57ec02020-10-09 12:46:52 -070051void ChassisIntrusionSensor::updateValue(const std::string& newValue)
Qiang XUe28d1fa2019-02-27 13:50:56 +080052{
Josh Lehan833661a2020-03-04 17:35:41 -080053 // Take no action if value already equal
54 // Same semantics as Sensor::updateValue(const double&)
55 if (newValue == mValue)
56 {
57 return;
58 }
59
Qiang XUe28d1fa2019-02-27 13:50:56 +080060 // indicate that it is internal set call
61 mInternalSet = true;
62 mIface->set_property("Status", newValue);
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +053063 mInternalSet = false;
Qiang XUe28d1fa2019-02-27 13:50:56 +080064
65 mValue = newValue;
66
67 if (mOldValue == "Normal" && mValue != "Normal")
68 {
Matt Simmering79a160b2022-11-14 16:50:48 -080069 sd_journal_send("MESSAGE=%s", "Chassis intrusion assert event",
70 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
71 "OpenBMC.0.1.ChassisIntrusionDetected", NULL);
Qiang XUe28d1fa2019-02-27 13:50:56 +080072 mOldValue = mValue;
73 }
74 else if (mOldValue != "Normal" && mValue == "Normal")
75 {
Matt Simmering79a160b2022-11-14 16:50:48 -080076 sd_journal_send("MESSAGE=%s", "Chassis intrusion de-assert event",
77 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
78 "OpenBMC.0.1.ChassisIntrusionReset", NULL);
Qiang XUe28d1fa2019-02-27 13:50:56 +080079 mOldValue = mValue;
80 }
81}
82
83int ChassisIntrusionSensor::i2cReadFromPch(int busId, int slaveAddr)
84{
85 std::string i2cBus = "/dev/i2c-" + std::to_string(busId);
86
Ed Tanous99c44092022-01-14 09:59:09 -080087 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
Qiang XUe28d1fa2019-02-27 13:50:56 +080088 int fd = open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
89 if (fd < 0)
90 {
91 std::cerr << "unable to open i2c device \n";
92 return -1;
93 }
Ed Tanous99c44092022-01-14 09:59:09 -080094
95 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
Qiang XUe28d1fa2019-02-27 13:50:56 +080096 if (ioctl(fd, I2C_SLAVE_FORCE, slaveAddr) < 0)
97 {
98 std::cerr << "unable to set device address\n";
99 close(fd);
100 return -1;
101 }
102
103 unsigned long funcs = 0;
Ed Tanous99c44092022-01-14 09:59:09 -0800104
105 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800106 if (ioctl(fd, I2C_FUNCS, &funcs) < 0)
107 {
108 std::cerr << "not support I2C_FUNCS \n";
109 close(fd);
110 return -1;
111 }
112
Ed Tanous2049bd22022-07-09 07:20:26 -0700113 if ((funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA) == 0U)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800114 {
115 std::cerr << "not support I2C_FUNC_SMBUS_READ_BYTE_DATA \n";
116 close(fd);
117 return -1;
118 }
119
Ed Tanous8a57ec02020-10-09 12:46:52 -0700120 int32_t statusMask = pchRegMaskIntrusion;
121 int32_t statusReg = pchStatusRegIntrusion;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800122
Ed Tanous8a57ec02020-10-09 12:46:52 -0700123 int32_t statusValue = i2c_smbus_read_byte_data(fd, statusReg);
124 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800125 {
126 std::cout << "\nRead bus " << busId << " addr " << slaveAddr
127 << ", value = " << statusValue << "\n";
128 }
129
130 close(fd);
131
132 if (statusValue < 0)
133 {
134 std::cerr << "i2c_smbus_read_byte_data failed \n";
135 return -1;
136 }
137
138 // Get status value with mask
139 int newValue = statusValue & statusMask;
140
Ed Tanous8a57ec02020-10-09 12:46:52 -0700141 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800142 {
143 std::cout << "statusValue is " << statusValue << "\n";
144 std::cout << "Intrusion sensor value is " << newValue << "\n";
145 }
146
147 return newValue;
148}
149
150void ChassisIntrusionSensor::pollSensorStatusByPch()
151{
152 // setting a new experation implicitly cancels any pending async wait
Ed Tanous83db50c2023-03-01 10:20:24 -0800153 mPollTimer.expires_after(std::chrono::seconds(intrusionSensorPollSec));
Qiang XUe28d1fa2019-02-27 13:50:56 +0800154
155 mPollTimer.async_wait([&](const boost::system::error_code& ec) {
156 // case of timer expired
157 if (!ec)
158 {
159 int statusValue = i2cReadFromPch(mBusId, mSlaveAddr);
Ed Tanous2049bd22022-07-09 07:20:26 -0700160 std::string newValue =
161 statusValue != 0 ? "HardwareIntrusion" : "Normal";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800162
Qiang XUe28d1fa2019-02-27 13:50:56 +0800163 if (newValue != "unknown" && mValue != newValue)
164 {
165 std::cout << "update value from " << mValue << " to "
166 << newValue << "\n";
167 updateValue(newValue);
168 }
169
170 // trigger next polling
171 pollSensorStatusByPch();
172 }
173 // case of being canceled
174 else if (ec == boost::asio::error::operation_aborted)
175 {
176 std::cerr << "Timer of intrusion sensor is cancelled. Return \n";
177 return;
178 }
179 });
180}
181
182void ChassisIntrusionSensor::readGpio()
183{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800184 mGpioLine.event_read();
185 auto value = mGpioLine.get_value();
186
187 // set string defined in chassis redfish schema
Ed Tanous2049bd22022-07-09 07:20:26 -0700188 std::string newValue = value != 0 ? "HardwareIntrusion" : "Normal";
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800189
Ed Tanous8a57ec02020-10-09 12:46:52 -0700190 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800191 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800192 std::cout << "\nGPIO value is " << value << "\n";
193 std::cout << "Intrusion sensor value is " << newValue << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800194 }
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800195
196 if (newValue != "unknown" && mValue != newValue)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800197 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800198 std::cout << "update value from " << mValue << " to " << newValue
199 << "\n";
200 updateValue(newValue);
Qiang XUe28d1fa2019-02-27 13:50:56 +0800201 }
202}
203
204void ChassisIntrusionSensor::pollSensorStatusByGpio(void)
205{
Ed Tanousbb679322022-05-16 16:10:00 -0700206 mGpioFd.async_wait(boost::asio::posix::stream_descriptor::wait_read,
207 [this](const boost::system::error_code& ec) {
208 if (ec == boost::system::errc::bad_file_descriptor)
209 {
210 return; // we're being destroyed
211 }
212 if (ec)
213 {
214 std::cerr << "Error on GPIO based intrusion sensor wait event\n";
215 }
216 else
217 {
218 readGpio();
219 }
220 pollSensorStatusByGpio();
221 });
Qiang XUe28d1fa2019-02-27 13:50:56 +0800222}
223
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800224void ChassisIntrusionSensor::initGpioDeviceFile()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800225{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800226 mGpioLine = gpiod::find_line(mPinName);
227 if (!mGpioLine)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800228 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800229 std::cerr << "ChassisIntrusionSensor error finding gpio pin name: "
230 << mPinName << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800231 return;
232 }
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800233
234 try
235 {
236
237 mGpioLine.request(
238 {"ChassisIntrusionSensor", gpiod::line_request::EVENT_BOTH_EDGES,
239 mGpioInverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
240
241 // set string defined in chassis redfish schema
242 auto value = mGpioLine.get_value();
Ed Tanous2049bd22022-07-09 07:20:26 -0700243 std::string newValue = value != 0 ? "HardwareIntrusion" : "Normal";
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800244 updateValue(newValue);
245
246 auto gpioLineFd = mGpioLine.event_get_fd();
247 if (gpioLineFd < 0)
248 {
249 std::cerr << "ChassisIntrusionSensor failed to get " << mPinName
250 << " fd\n";
251 return;
252 }
253
254 mGpioFd.assign(gpioLineFd);
255 }
Patrick Williams26601e82021-10-06 12:43:25 -0500256 catch (const std::system_error&)
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800257 {
258 std::cerr << "ChassisInrtusionSensor error requesting gpio pin name: "
259 << mPinName << "\n";
260 return;
261 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800262}
263
264int ChassisIntrusionSensor::setSensorValue(const std::string& req,
265 std::string& propertyValue)
266{
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530267 if (!mInternalSet)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800268 {
Qiang XUe28d1fa2019-02-27 13:50:56 +0800269 propertyValue = req;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800270 mOverridenState = true;
271 }
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530272 else if (!mOverridenState)
273 {
274 propertyValue = req;
275 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800276 return 1;
277}
278
279void ChassisIntrusionSensor::start(IntrusionSensorType type, int busId,
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800280 int slaveAddr, bool gpioInverted)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800281{
Ed Tanous8a57ec02020-10-09 12:46:52 -0700282 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800283 {
284 std::cerr << "enter ChassisIntrusionSensor::start, type = " << type
285 << "\n";
286 if (type == IntrusionSensorType::pch)
287 {
288 std::cerr << "busId = " << busId << ", slaveAddr = " << slaveAddr
289 << "\n";
290 }
291 else if (type == IntrusionSensorType::gpio)
292 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800293 std::cerr << "gpio pinName = " << mPinName
Qiang XUe28d1fa2019-02-27 13:50:56 +0800294 << ", gpioInverted = " << gpioInverted << "\n";
295 }
296 }
297
298 if ((type == IntrusionSensorType::pch && busId == mBusId &&
299 slaveAddr == mSlaveAddr) ||
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800300 (type == IntrusionSensorType::gpio && gpioInverted == mGpioInverted &&
301 mInitialized))
Qiang XUe28d1fa2019-02-27 13:50:56 +0800302 {
303 return;
304 }
305
306 mType = type;
307 mBusId = busId;
308 mSlaveAddr = slaveAddr;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800309 mGpioInverted = gpioInverted;
310
311 if ((mType == IntrusionSensorType::pch && mBusId > 0 && mSlaveAddr > 0) ||
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800312 (mType == IntrusionSensorType::gpio))
Qiang XUe28d1fa2019-02-27 13:50:56 +0800313 {
314 // initialize first if not initialized before
315 if (!mInitialized)
316 {
317 mIface->register_property(
318 "Status", mValue,
319 [&](const std::string& req, std::string& propertyValue) {
Ed Tanousbb679322022-05-16 16:10:00 -0700320 return setSensorValue(req, propertyValue);
Qiang XUe28d1fa2019-02-27 13:50:56 +0800321 });
322 mIface->initialize();
323
324 if (mType == IntrusionSensorType::gpio)
325 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800326 initGpioDeviceFile();
Qiang XUe28d1fa2019-02-27 13:50:56 +0800327 }
328
329 mInitialized = true;
330 }
331
332 // start polling value
333 if (mType == IntrusionSensorType::pch)
334 {
335 pollSensorStatusByPch();
336 }
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800337 else if (mType == IntrusionSensorType::gpio && mGpioLine)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800338 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800339 std::cerr << "Start polling intrusion sensors\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800340 pollSensorStatusByGpio();
341 }
342 }
343
344 // invalid para, release resource
345 else
346 {
347 if (mInitialized)
348 {
349 if (mType == IntrusionSensorType::pch)
350 {
351 mPollTimer.cancel();
352 }
353 else if (mType == IntrusionSensorType::gpio)
354 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800355 mGpioFd.close();
356 if (mGpioLine)
357 {
358 mGpioLine.release();
359 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800360 }
361 mInitialized = false;
362 }
363 }
364}
365
366ChassisIntrusionSensor::ChassisIntrusionSensor(
Ed Tanous1f978632023-02-28 18:16:39 -0800367 boost::asio::io_context& io,
Qiang XUe28d1fa2019-02-27 13:50:56 +0800368 std::shared_ptr<sdbusplus::asio::dbus_interface> iface) :
Ed Tanous8a57ec02020-10-09 12:46:52 -0700369 mIface(std::move(iface)),
Ed Tanousb429f312022-06-27 16:09:53 -0700370 mValue("unknown"), mOldValue("unknown"), mPollTimer(io), mGpioFd(io)
James Feist38fb5982020-05-28 10:09:54 -0700371{}
Qiang XUe28d1fa2019-02-27 13:50:56 +0800372
373ChassisIntrusionSensor::~ChassisIntrusionSensor()
374{
375 if (mType == IntrusionSensorType::pch)
376 {
377 mPollTimer.cancel();
378 }
379 else if (mType == IntrusionSensorType::gpio)
380 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800381 mGpioFd.close();
382 if (mGpioLine)
383 {
384 mGpioLine.release();
385 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800386 }
387}