blob: 2bb04ce11eb0cfc6bd074bf84115fd024becf6c4 [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
Qiang XUe28d1fa2019-02-27 13:50:56 +080017#include <fcntl.h>
18#include <sys/ioctl.h>
19#include <unistd.h>
20
Ed Tanous8a57ec02020-10-09 12:46:52 -070021#include <ChassisIntrusionSensor.hpp>
22#include <boost/asio/io_service.hpp>
James Feist38fb5982020-05-28 10:09:54 -070023#include <sdbusplus/asio/object_server.hpp>
24
Ed Tanous8a57ec02020-10-09 12:46:52 -070025#include <cerrno>
Qiang XUe28d1fa2019-02-27 13:50:56 +080026#include <chrono>
27#include <iostream>
Patrick Venture96e97db2019-10-31 13:44:38 -070028#include <memory>
Qiang XUe28d1fa2019-02-27 13:50:56 +080029#include <string>
30#include <thread>
Ed Tanous8a57ec02020-10-09 12:46:52 -070031#include <utility>
Qiang XUe28d1fa2019-02-27 13:50:56 +080032
James Feist38fb5982020-05-28 10:09:54 -070033extern "C"
34{
Qiang XUe28d1fa2019-02-27 13:50:56 +080035#include <i2c/smbus.h>
36#include <linux/i2c-dev.h>
37}
38
Ed Tanous8a57ec02020-10-09 12:46:52 -070039static constexpr bool debug = false;
Qiang XUe28d1fa2019-02-27 13:50:56 +080040
41static constexpr unsigned int intrusionSensorPollSec = 1;
42
43// SMLink Status Register
44const static constexpr size_t pchStatusRegIntrusion = 0x04;
45
46// Status bit field masks
47const static constexpr size_t pchRegMaskIntrusion = 0x01;
48
Ed Tanous8a57ec02020-10-09 12:46:52 -070049void ChassisIntrusionSensor::updateValue(const std::string& newValue)
Qiang XUe28d1fa2019-02-27 13:50:56 +080050{
Josh Lehan833661a2020-03-04 17:35:41 -080051 // Take no action if value already equal
52 // Same semantics as Sensor::updateValue(const double&)
53 if (newValue == mValue)
54 {
55 return;
56 }
57
Qiang XUe28d1fa2019-02-27 13:50:56 +080058 // indicate that it is internal set call
59 mInternalSet = true;
60 mIface->set_property("Status", newValue);
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +053061 mInternalSet = false;
Qiang XUe28d1fa2019-02-27 13:50:56 +080062
63 mValue = newValue;
64
65 if (mOldValue == "Normal" && mValue != "Normal")
66 {
67 std::cerr << "save to SEL for intrusion assert event \n";
68 // TODO: call add SEL log API, depends on patch #13956
69 mOldValue = mValue;
70 }
71 else if (mOldValue != "Normal" && mValue == "Normal")
72 {
73 std::cerr << "save to SEL for intrusion de-assert event \n";
74 // TODO: call add SEL log API, depends on patch #13956
75 mOldValue = mValue;
76 }
77}
78
79int ChassisIntrusionSensor::i2cReadFromPch(int busId, int slaveAddr)
80{
81 std::string i2cBus = "/dev/i2c-" + std::to_string(busId);
82
Ed Tanous99c44092022-01-14 09:59:09 -080083 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
Qiang XUe28d1fa2019-02-27 13:50:56 +080084 int fd = open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
85 if (fd < 0)
86 {
87 std::cerr << "unable to open i2c device \n";
88 return -1;
89 }
Ed Tanous99c44092022-01-14 09:59:09 -080090
91 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
Qiang XUe28d1fa2019-02-27 13:50:56 +080092 if (ioctl(fd, I2C_SLAVE_FORCE, slaveAddr) < 0)
93 {
94 std::cerr << "unable to set device address\n";
95 close(fd);
96 return -1;
97 }
98
99 unsigned long funcs = 0;
Ed Tanous99c44092022-01-14 09:59:09 -0800100
101 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800102 if (ioctl(fd, I2C_FUNCS, &funcs) < 0)
103 {
104 std::cerr << "not support I2C_FUNCS \n";
105 close(fd);
106 return -1;
107 }
108
Ed Tanous2049bd22022-07-09 07:20:26 -0700109 if ((funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA) == 0U)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800110 {
111 std::cerr << "not support I2C_FUNC_SMBUS_READ_BYTE_DATA \n";
112 close(fd);
113 return -1;
114 }
115
Ed Tanous8a57ec02020-10-09 12:46:52 -0700116 int32_t statusMask = pchRegMaskIntrusion;
117 int32_t statusReg = pchStatusRegIntrusion;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800118
Ed Tanous8a57ec02020-10-09 12:46:52 -0700119 int32_t statusValue = i2c_smbus_read_byte_data(fd, statusReg);
120 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800121 {
122 std::cout << "\nRead bus " << busId << " addr " << slaveAddr
123 << ", value = " << statusValue << "\n";
124 }
125
126 close(fd);
127
128 if (statusValue < 0)
129 {
130 std::cerr << "i2c_smbus_read_byte_data failed \n";
131 return -1;
132 }
133
134 // Get status value with mask
135 int newValue = statusValue & statusMask;
136
Ed Tanous8a57ec02020-10-09 12:46:52 -0700137 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800138 {
139 std::cout << "statusValue is " << statusValue << "\n";
140 std::cout << "Intrusion sensor value is " << newValue << "\n";
141 }
142
143 return newValue;
144}
145
146void ChassisIntrusionSensor::pollSensorStatusByPch()
147{
148 // setting a new experation implicitly cancels any pending async wait
Ed Tanous9b4a20e2022-09-06 08:47:11 -0700149 mPollTimer.expires_from_now(std::chrono::seconds(intrusionSensorPollSec));
Qiang XUe28d1fa2019-02-27 13:50:56 +0800150
151 mPollTimer.async_wait([&](const boost::system::error_code& ec) {
152 // case of timer expired
153 if (!ec)
154 {
155 int statusValue = i2cReadFromPch(mBusId, mSlaveAddr);
Ed Tanous2049bd22022-07-09 07:20:26 -0700156 std::string newValue =
157 statusValue != 0 ? "HardwareIntrusion" : "Normal";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800158
Qiang XUe28d1fa2019-02-27 13:50:56 +0800159 if (newValue != "unknown" && mValue != newValue)
160 {
161 std::cout << "update value from " << mValue << " to "
162 << newValue << "\n";
163 updateValue(newValue);
164 }
165
166 // trigger next polling
167 pollSensorStatusByPch();
168 }
169 // case of being canceled
170 else if (ec == boost::asio::error::operation_aborted)
171 {
172 std::cerr << "Timer of intrusion sensor is cancelled. Return \n";
173 return;
174 }
175 });
176}
177
178void ChassisIntrusionSensor::readGpio()
179{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800180 mGpioLine.event_read();
181 auto value = mGpioLine.get_value();
182
183 // set string defined in chassis redfish schema
Ed Tanous2049bd22022-07-09 07:20:26 -0700184 std::string newValue = value != 0 ? "HardwareIntrusion" : "Normal";
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800185
Ed Tanous8a57ec02020-10-09 12:46:52 -0700186 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800187 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800188 std::cout << "\nGPIO value is " << value << "\n";
189 std::cout << "Intrusion sensor value is " << newValue << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800190 }
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800191
192 if (newValue != "unknown" && mValue != newValue)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800193 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800194 std::cout << "update value from " << mValue << " to " << newValue
195 << "\n";
196 updateValue(newValue);
Qiang XUe28d1fa2019-02-27 13:50:56 +0800197 }
198}
199
200void ChassisIntrusionSensor::pollSensorStatusByGpio(void)
201{
Ed Tanousbb679322022-05-16 16:10:00 -0700202 mGpioFd.async_wait(boost::asio::posix::stream_descriptor::wait_read,
203 [this](const boost::system::error_code& ec) {
204 if (ec == boost::system::errc::bad_file_descriptor)
205 {
206 return; // we're being destroyed
207 }
208 if (ec)
209 {
210 std::cerr << "Error on GPIO based intrusion sensor wait event\n";
211 }
212 else
213 {
214 readGpio();
215 }
216 pollSensorStatusByGpio();
217 });
Qiang XUe28d1fa2019-02-27 13:50:56 +0800218}
219
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800220void ChassisIntrusionSensor::initGpioDeviceFile()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800221{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800222 mGpioLine = gpiod::find_line(mPinName);
223 if (!mGpioLine)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800224 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800225 std::cerr << "ChassisIntrusionSensor error finding gpio pin name: "
226 << mPinName << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800227 return;
228 }
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800229
230 try
231 {
232
233 mGpioLine.request(
234 {"ChassisIntrusionSensor", gpiod::line_request::EVENT_BOTH_EDGES,
235 mGpioInverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
236
237 // set string defined in chassis redfish schema
238 auto value = mGpioLine.get_value();
Ed Tanous2049bd22022-07-09 07:20:26 -0700239 std::string newValue = value != 0 ? "HardwareIntrusion" : "Normal";
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800240 updateValue(newValue);
241
242 auto gpioLineFd = mGpioLine.event_get_fd();
243 if (gpioLineFd < 0)
244 {
245 std::cerr << "ChassisIntrusionSensor failed to get " << mPinName
246 << " fd\n";
247 return;
248 }
249
250 mGpioFd.assign(gpioLineFd);
251 }
Patrick Williams26601e82021-10-06 12:43:25 -0500252 catch (const std::system_error&)
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800253 {
254 std::cerr << "ChassisInrtusionSensor error requesting gpio pin name: "
255 << mPinName << "\n";
256 return;
257 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800258}
259
260int ChassisIntrusionSensor::setSensorValue(const std::string& req,
261 std::string& propertyValue)
262{
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530263 if (!mInternalSet)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800264 {
Qiang XUe28d1fa2019-02-27 13:50:56 +0800265 propertyValue = req;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800266 mOverridenState = true;
267 }
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530268 else if (!mOverridenState)
269 {
270 propertyValue = req;
271 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800272 return 1;
273}
274
275void ChassisIntrusionSensor::start(IntrusionSensorType type, int busId,
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800276 int slaveAddr, bool gpioInverted)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800277{
Ed Tanous8a57ec02020-10-09 12:46:52 -0700278 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800279 {
280 std::cerr << "enter ChassisIntrusionSensor::start, type = " << type
281 << "\n";
282 if (type == IntrusionSensorType::pch)
283 {
284 std::cerr << "busId = " << busId << ", slaveAddr = " << slaveAddr
285 << "\n";
286 }
287 else if (type == IntrusionSensorType::gpio)
288 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800289 std::cerr << "gpio pinName = " << mPinName
Qiang XUe28d1fa2019-02-27 13:50:56 +0800290 << ", gpioInverted = " << gpioInverted << "\n";
291 }
292 }
293
294 if ((type == IntrusionSensorType::pch && busId == mBusId &&
295 slaveAddr == mSlaveAddr) ||
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800296 (type == IntrusionSensorType::gpio && gpioInverted == mGpioInverted &&
297 mInitialized))
Qiang XUe28d1fa2019-02-27 13:50:56 +0800298 {
299 return;
300 }
301
302 mType = type;
303 mBusId = busId;
304 mSlaveAddr = slaveAddr;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800305 mGpioInverted = gpioInverted;
306
307 if ((mType == IntrusionSensorType::pch && mBusId > 0 && mSlaveAddr > 0) ||
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800308 (mType == IntrusionSensorType::gpio))
Qiang XUe28d1fa2019-02-27 13:50:56 +0800309 {
310 // initialize first if not initialized before
311 if (!mInitialized)
312 {
313 mIface->register_property(
314 "Status", mValue,
315 [&](const std::string& req, std::string& propertyValue) {
Ed Tanousbb679322022-05-16 16:10:00 -0700316 return setSensorValue(req, propertyValue);
Qiang XUe28d1fa2019-02-27 13:50:56 +0800317 });
318 mIface->initialize();
319
320 if (mType == IntrusionSensorType::gpio)
321 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800322 initGpioDeviceFile();
Qiang XUe28d1fa2019-02-27 13:50:56 +0800323 }
324
325 mInitialized = true;
326 }
327
328 // start polling value
329 if (mType == IntrusionSensorType::pch)
330 {
331 pollSensorStatusByPch();
332 }
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800333 else if (mType == IntrusionSensorType::gpio && mGpioLine)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800334 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800335 std::cerr << "Start polling intrusion sensors\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800336 pollSensorStatusByGpio();
337 }
338 }
339
340 // invalid para, release resource
341 else
342 {
343 if (mInitialized)
344 {
345 if (mType == IntrusionSensorType::pch)
346 {
347 mPollTimer.cancel();
348 }
349 else if (mType == IntrusionSensorType::gpio)
350 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800351 mGpioFd.close();
352 if (mGpioLine)
353 {
354 mGpioLine.release();
355 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800356 }
357 mInitialized = false;
358 }
359 }
360}
361
362ChassisIntrusionSensor::ChassisIntrusionSensor(
363 boost::asio::io_service& io,
364 std::shared_ptr<sdbusplus::asio::dbus_interface> iface) :
Ed Tanous8a57ec02020-10-09 12:46:52 -0700365 mIface(std::move(iface)),
Ed Tanousb429f312022-06-27 16:09:53 -0700366 mValue("unknown"), mOldValue("unknown"), mPollTimer(io), mGpioFd(io)
James Feist38fb5982020-05-28 10:09:54 -0700367{}
Qiang XUe28d1fa2019-02-27 13:50:56 +0800368
369ChassisIntrusionSensor::~ChassisIntrusionSensor()
370{
371 if (mType == IntrusionSensorType::pch)
372 {
373 mPollTimer.cancel();
374 }
375 else if (mType == IntrusionSensorType::gpio)
376 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800377 mGpioFd.close();
378 if (mGpioLine)
379 {
380 mGpioLine.release();
381 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800382 }
383}