blob: 4b2327e73cd08e6226e63bc352edae84e578ed5f [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
Patrick Ventureca44b2f2019-10-31 11:02:26 -070017#include "ChassisIntrusionSensor.hpp"
18
Qiang XUe28d1fa2019-02-27 13:50:56 +080019#include <errno.h>
20#include <fcntl.h>
21#include <sys/ioctl.h>
22#include <unistd.h>
23
Qiang XUe28d1fa2019-02-27 13:50:56 +080024#include <boost/asio.hpp>
25#include <boost/bind.hpp>
26#include <chrono>
27#include <iostream>
Patrick Venture96e97db2019-10-31 13:44:38 -070028#include <memory>
Qiang XUe28d1fa2019-02-27 13:50:56 +080029#include <sdbusplus/asio/object_server.hpp>
30#include <string>
31#include <thread>
32
33extern "C" {
34#include <i2c/smbus.h>
35#include <linux/i2c-dev.h>
36}
37
38static constexpr bool DEBUG = false;
39
40static constexpr unsigned int intrusionSensorPollSec = 1;
41
42// SMLink Status Register
43const static constexpr size_t pchStatusRegIntrusion = 0x04;
44
45// Status bit field masks
46const static constexpr size_t pchRegMaskIntrusion = 0x01;
47
Qiang XUe28d1fa2019-02-27 13:50:56 +080048void ChassisIntrusionSensor::updateValue(const std::string newValue)
49{
50 // indicate that it is internal set call
51 mInternalSet = true;
52 mIface->set_property("Status", newValue);
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +053053 mInternalSet = false;
Qiang XUe28d1fa2019-02-27 13:50:56 +080054
55 mValue = newValue;
56
57 if (mOldValue == "Normal" && mValue != "Normal")
58 {
59 std::cerr << "save to SEL for intrusion assert event \n";
60 // TODO: call add SEL log API, depends on patch #13956
61 mOldValue = mValue;
62 }
63 else if (mOldValue != "Normal" && mValue == "Normal")
64 {
65 std::cerr << "save to SEL for intrusion de-assert event \n";
66 // TODO: call add SEL log API, depends on patch #13956
67 mOldValue = mValue;
68 }
69}
70
71int ChassisIntrusionSensor::i2cReadFromPch(int busId, int slaveAddr)
72{
73 std::string i2cBus = "/dev/i2c-" + std::to_string(busId);
74
75 int fd = open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
76 if (fd < 0)
77 {
78 std::cerr << "unable to open i2c device \n";
79 return -1;
80 }
81 if (ioctl(fd, I2C_SLAVE_FORCE, slaveAddr) < 0)
82 {
83 std::cerr << "unable to set device address\n";
84 close(fd);
85 return -1;
86 }
87
88 unsigned long funcs = 0;
89 if (ioctl(fd, I2C_FUNCS, &funcs) < 0)
90 {
91 std::cerr << "not support I2C_FUNCS \n";
92 close(fd);
93 return -1;
94 }
95
96 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
97 {
98 std::cerr << "not support I2C_FUNC_SMBUS_READ_BYTE_DATA \n";
99 close(fd);
100 return -1;
101 }
102
James Feistb6c0b912019-07-09 12:21:44 -0700103 int statusValue;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800104 unsigned int statusMask = pchRegMaskIntrusion;
105 unsigned int statusReg = pchStatusRegIntrusion;
106
107 statusValue = i2c_smbus_read_byte_data(fd, statusReg);
108 if (DEBUG)
109 {
110 std::cout << "\nRead bus " << busId << " addr " << slaveAddr
111 << ", value = " << statusValue << "\n";
112 }
113
114 close(fd);
115
116 if (statusValue < 0)
117 {
118 std::cerr << "i2c_smbus_read_byte_data failed \n";
119 return -1;
120 }
121
122 // Get status value with mask
123 int newValue = statusValue & statusMask;
124
125 if (DEBUG)
126 {
127 std::cout << "statusValue is " << statusValue << "\n";
128 std::cout << "Intrusion sensor value is " << newValue << "\n";
129 }
130
131 return newValue;
132}
133
134void ChassisIntrusionSensor::pollSensorStatusByPch()
135{
136 // setting a new experation implicitly cancels any pending async wait
137 mPollTimer.expires_from_now(
138 boost::posix_time::seconds(intrusionSensorPollSec));
139
140 mPollTimer.async_wait([&](const boost::system::error_code& ec) {
141 // case of timer expired
142 if (!ec)
143 {
144 int statusValue = i2cReadFromPch(mBusId, mSlaveAddr);
145 std::string newValue = statusValue ? "HardwareIntrusion" : "Normal";
146
Qiang XUe28d1fa2019-02-27 13:50:56 +0800147 if (newValue != "unknown" && mValue != newValue)
148 {
149 std::cout << "update value from " << mValue << " to "
150 << newValue << "\n";
151 updateValue(newValue);
152 }
153
154 // trigger next polling
155 pollSensorStatusByPch();
156 }
157 // case of being canceled
158 else if (ec == boost::asio::error::operation_aborted)
159 {
160 std::cerr << "Timer of intrusion sensor is cancelled. Return \n";
161 return;
162 }
163 });
164}
165
166void ChassisIntrusionSensor::readGpio()
167{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800168 mGpioLine.event_read();
169 auto value = mGpioLine.get_value();
170
171 // set string defined in chassis redfish schema
172 std::string newValue = value ? "HardwareIntrusion" : "Normal";
173
174 if (DEBUG)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800175 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800176 std::cout << "\nGPIO value is " << value << "\n";
177 std::cout << "Intrusion sensor value is " << newValue << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800178 }
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800179
180 if (newValue != "unknown" && mValue != newValue)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800181 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800182 std::cout << "update value from " << mValue << " to " << newValue
183 << "\n";
184 updateValue(newValue);
Qiang XUe28d1fa2019-02-27 13:50:56 +0800185 }
186}
187
188void ChassisIntrusionSensor::pollSensorStatusByGpio(void)
189{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800190 mGpioFd.async_wait(
191 boost::asio::posix::stream_descriptor::wait_read,
Qiang XUe28d1fa2019-02-27 13:50:56 +0800192 [this](const boost::system::error_code& ec) {
193 if (ec == boost::system::errc::bad_file_descriptor)
194 {
195 return; // we're being destroyed
196 }
197 else if (ec)
198 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800199 std::cerr
200 << "Error on GPIO based intrusion sensor wait event\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800201 }
202 else
203 {
204 readGpio();
205 }
206 pollSensorStatusByGpio();
207 });
208}
209
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800210void ChassisIntrusionSensor::initGpioDeviceFile()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800211{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800212 mGpioLine = gpiod::find_line(mPinName);
213 if (!mGpioLine)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800214 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800215 std::cerr << "ChassisIntrusionSensor error finding gpio pin name: "
216 << mPinName << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800217 return;
218 }
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800219
220 try
221 {
222
223 mGpioLine.request(
224 {"ChassisIntrusionSensor", gpiod::line_request::EVENT_BOTH_EDGES,
225 mGpioInverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
226
227 // set string defined in chassis redfish schema
228 auto value = mGpioLine.get_value();
229 std::string newValue = value ? "HardwareIntrusion" : "Normal";
230 updateValue(newValue);
231
232 auto gpioLineFd = mGpioLine.event_get_fd();
233 if (gpioLineFd < 0)
234 {
235 std::cerr << "ChassisIntrusionSensor failed to get " << mPinName
236 << " fd\n";
237 return;
238 }
239
240 mGpioFd.assign(gpioLineFd);
241 }
242 catch (std::system_error&)
243 {
244 std::cerr << "ChassisInrtusionSensor error requesting gpio pin name: "
245 << mPinName << "\n";
246 return;
247 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800248}
249
250int ChassisIntrusionSensor::setSensorValue(const std::string& req,
251 std::string& propertyValue)
252{
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530253 if (!mInternalSet)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800254 {
Qiang XUe28d1fa2019-02-27 13:50:56 +0800255 propertyValue = req;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800256 mOverridenState = true;
257 }
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530258 else if (!mOverridenState)
259 {
260 propertyValue = req;
261 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800262 return 1;
263}
264
265void ChassisIntrusionSensor::start(IntrusionSensorType type, int busId,
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800266 int slaveAddr, bool gpioInverted)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800267{
268 if (DEBUG)
269 {
270 std::cerr << "enter ChassisIntrusionSensor::start, type = " << type
271 << "\n";
272 if (type == IntrusionSensorType::pch)
273 {
274 std::cerr << "busId = " << busId << ", slaveAddr = " << slaveAddr
275 << "\n";
276 }
277 else if (type == IntrusionSensorType::gpio)
278 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800279 std::cerr << "gpio pinName = " << mPinName
Qiang XUe28d1fa2019-02-27 13:50:56 +0800280 << ", gpioInverted = " << gpioInverted << "\n";
281 }
282 }
283
284 if ((type == IntrusionSensorType::pch && busId == mBusId &&
285 slaveAddr == mSlaveAddr) ||
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800286 (type == IntrusionSensorType::gpio && gpioInverted == mGpioInverted &&
287 mInitialized))
Qiang XUe28d1fa2019-02-27 13:50:56 +0800288 {
289 return;
290 }
291
292 mType = type;
293 mBusId = busId;
294 mSlaveAddr = slaveAddr;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800295 mGpioInverted = gpioInverted;
296
297 if ((mType == IntrusionSensorType::pch && mBusId > 0 && mSlaveAddr > 0) ||
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800298 (mType == IntrusionSensorType::gpio))
Qiang XUe28d1fa2019-02-27 13:50:56 +0800299 {
300 // initialize first if not initialized before
301 if (!mInitialized)
302 {
303 mIface->register_property(
304 "Status", mValue,
305 [&](const std::string& req, std::string& propertyValue) {
306 return setSensorValue(req, propertyValue);
307 });
308 mIface->initialize();
309
310 if (mType == IntrusionSensorType::gpio)
311 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800312 initGpioDeviceFile();
Qiang XUe28d1fa2019-02-27 13:50:56 +0800313 }
314
315 mInitialized = true;
316 }
317
318 // start polling value
319 if (mType == IntrusionSensorType::pch)
320 {
321 pollSensorStatusByPch();
322 }
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800323 else if (mType == IntrusionSensorType::gpio && mGpioLine)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800324 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800325 std::cerr << "Start polling intrusion sensors\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800326 pollSensorStatusByGpio();
327 }
328 }
329
330 // invalid para, release resource
331 else
332 {
333 if (mInitialized)
334 {
335 if (mType == IntrusionSensorType::pch)
336 {
337 mPollTimer.cancel();
338 }
339 else if (mType == IntrusionSensorType::gpio)
340 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800341 mGpioFd.close();
342 if (mGpioLine)
343 {
344 mGpioLine.release();
345 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800346 }
347 mInitialized = false;
348 }
349 }
350}
351
352ChassisIntrusionSensor::ChassisIntrusionSensor(
353 boost::asio::io_service& io,
354 std::shared_ptr<sdbusplus::asio::dbus_interface> iface) :
Brad Bishopfbb44ad2019-11-08 09:42:37 -0500355 mIface(iface),
356 mType(IntrusionSensorType::gpio), mValue("unknown"), mOldValue("unknown"),
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800357 mBusId(-1), mSlaveAddr(-1), mPollTimer(io), mGpioInverted(false),
358 mGpioFd(io)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800359{
360}
361
362ChassisIntrusionSensor::~ChassisIntrusionSensor()
363{
364 if (mType == IntrusionSensorType::pch)
365 {
366 mPollTimer.cancel();
367 }
368 else if (mType == IntrusionSensorType::gpio)
369 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800370 mGpioFd.close();
371 if (mGpioLine)
372 {
373 mGpioLine.release();
374 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800375 }
376}