blob: 5bff6db927d1dadb80d583ebf7f918fa0c15bfc1 [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>
21#include <unistd.h>
22
Ed Tanous1f978632023-02-28 18:16:39 -080023#include <boost/asio/io_context.hpp>
James Feist38fb5982020-05-28 10:09:54 -070024#include <sdbusplus/asio/object_server.hpp>
25
Ed Tanous8a57ec02020-10-09 12:46:52 -070026#include <cerrno>
Qiang XUe28d1fa2019-02-27 13:50:56 +080027#include <chrono>
28#include <iostream>
Patrick Venture96e97db2019-10-31 13:44:38 -070029#include <memory>
Qiang XUe28d1fa2019-02-27 13:50:56 +080030#include <string>
31#include <thread>
Ed Tanous8a57ec02020-10-09 12:46:52 -070032#include <utility>
Qiang XUe28d1fa2019-02-27 13:50:56 +080033
James Feist38fb5982020-05-28 10:09:54 -070034extern "C"
35{
Qiang XUe28d1fa2019-02-27 13:50:56 +080036#include <i2c/smbus.h>
37#include <linux/i2c-dev.h>
38}
39
Ed Tanous8a57ec02020-10-09 12:46:52 -070040static constexpr bool debug = false;
Qiang XUe28d1fa2019-02-27 13:50:56 +080041
42static constexpr unsigned int intrusionSensorPollSec = 1;
43
44// SMLink Status Register
45const static constexpr size_t pchStatusRegIntrusion = 0x04;
46
47// Status bit field masks
48const static constexpr size_t pchRegMaskIntrusion = 0x01;
49
Ed Tanous8a57ec02020-10-09 12:46:52 -070050void ChassisIntrusionSensor::updateValue(const std::string& newValue)
Qiang XUe28d1fa2019-02-27 13:50:56 +080051{
Josh Lehan833661a2020-03-04 17:35:41 -080052 // Take no action if value already equal
53 // Same semantics as Sensor::updateValue(const double&)
54 if (newValue == mValue)
55 {
56 return;
57 }
58
Qiang XUe28d1fa2019-02-27 13:50:56 +080059 // indicate that it is internal set call
60 mInternalSet = true;
61 mIface->set_property("Status", newValue);
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +053062 mInternalSet = false;
Qiang XUe28d1fa2019-02-27 13:50:56 +080063
64 mValue = newValue;
65
66 if (mOldValue == "Normal" && mValue != "Normal")
67 {
68 std::cerr << "save to SEL for intrusion assert event \n";
69 // TODO: call add SEL log API, depends on patch #13956
70 mOldValue = mValue;
71 }
72 else if (mOldValue != "Normal" && mValue == "Normal")
73 {
74 std::cerr << "save to SEL for intrusion de-assert event \n";
75 // TODO: call add SEL log API, depends on patch #13956
76 mOldValue = mValue;
77 }
78}
79
80int ChassisIntrusionSensor::i2cReadFromPch(int busId, int slaveAddr)
81{
82 std::string i2cBus = "/dev/i2c-" + std::to_string(busId);
83
Ed Tanous99c44092022-01-14 09:59:09 -080084 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
Qiang XUe28d1fa2019-02-27 13:50:56 +080085 int fd = open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
86 if (fd < 0)
87 {
88 std::cerr << "unable to open i2c device \n";
89 return -1;
90 }
Ed Tanous99c44092022-01-14 09:59:09 -080091
92 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
Qiang XUe28d1fa2019-02-27 13:50:56 +080093 if (ioctl(fd, I2C_SLAVE_FORCE, slaveAddr) < 0)
94 {
95 std::cerr << "unable to set device address\n";
96 close(fd);
97 return -1;
98 }
99
100 unsigned long funcs = 0;
Ed Tanous99c44092022-01-14 09:59:09 -0800101
102 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800103 if (ioctl(fd, I2C_FUNCS, &funcs) < 0)
104 {
105 std::cerr << "not support I2C_FUNCS \n";
106 close(fd);
107 return -1;
108 }
109
Ed Tanous2049bd22022-07-09 07:20:26 -0700110 if ((funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA) == 0U)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800111 {
112 std::cerr << "not support I2C_FUNC_SMBUS_READ_BYTE_DATA \n";
113 close(fd);
114 return -1;
115 }
116
Ed Tanous8a57ec02020-10-09 12:46:52 -0700117 int32_t statusMask = pchRegMaskIntrusion;
118 int32_t statusReg = pchStatusRegIntrusion;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800119
Ed Tanous8a57ec02020-10-09 12:46:52 -0700120 int32_t statusValue = i2c_smbus_read_byte_data(fd, statusReg);
121 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800122 {
123 std::cout << "\nRead bus " << busId << " addr " << slaveAddr
124 << ", value = " << statusValue << "\n";
125 }
126
127 close(fd);
128
129 if (statusValue < 0)
130 {
131 std::cerr << "i2c_smbus_read_byte_data failed \n";
132 return -1;
133 }
134
135 // Get status value with mask
136 int newValue = statusValue & statusMask;
137
Ed Tanous8a57ec02020-10-09 12:46:52 -0700138 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800139 {
140 std::cout << "statusValue is " << statusValue << "\n";
141 std::cout << "Intrusion sensor value is " << newValue << "\n";
142 }
143
144 return newValue;
145}
146
147void ChassisIntrusionSensor::pollSensorStatusByPch()
148{
149 // setting a new experation implicitly cancels any pending async wait
Ed Tanous83db50c2023-03-01 10:20:24 -0800150 mPollTimer.expires_after(std::chrono::seconds(intrusionSensorPollSec));
Qiang XUe28d1fa2019-02-27 13:50:56 +0800151
152 mPollTimer.async_wait([&](const boost::system::error_code& ec) {
153 // case of timer expired
154 if (!ec)
155 {
156 int statusValue = i2cReadFromPch(mBusId, mSlaveAddr);
Ed Tanous2049bd22022-07-09 07:20:26 -0700157 std::string newValue =
158 statusValue != 0 ? "HardwareIntrusion" : "Normal";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800159
Qiang XUe28d1fa2019-02-27 13:50:56 +0800160 if (newValue != "unknown" && mValue != newValue)
161 {
162 std::cout << "update value from " << mValue << " to "
163 << newValue << "\n";
164 updateValue(newValue);
165 }
166
167 // trigger next polling
168 pollSensorStatusByPch();
169 }
170 // case of being canceled
171 else if (ec == boost::asio::error::operation_aborted)
172 {
173 std::cerr << "Timer of intrusion sensor is cancelled. Return \n";
174 return;
175 }
176 });
177}
178
179void ChassisIntrusionSensor::readGpio()
180{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800181 mGpioLine.event_read();
182 auto value = mGpioLine.get_value();
183
184 // set string defined in chassis redfish schema
Ed Tanous2049bd22022-07-09 07:20:26 -0700185 std::string newValue = value != 0 ? "HardwareIntrusion" : "Normal";
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800186
Ed Tanous8a57ec02020-10-09 12:46:52 -0700187 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800188 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800189 std::cout << "\nGPIO value is " << value << "\n";
190 std::cout << "Intrusion sensor value is " << newValue << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800191 }
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800192
193 if (newValue != "unknown" && mValue != newValue)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800194 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800195 std::cout << "update value from " << mValue << " to " << newValue
196 << "\n";
197 updateValue(newValue);
Qiang XUe28d1fa2019-02-27 13:50:56 +0800198 }
199}
200
201void ChassisIntrusionSensor::pollSensorStatusByGpio(void)
202{
Ed Tanousbb679322022-05-16 16:10:00 -0700203 mGpioFd.async_wait(boost::asio::posix::stream_descriptor::wait_read,
204 [this](const boost::system::error_code& ec) {
205 if (ec == boost::system::errc::bad_file_descriptor)
206 {
207 return; // we're being destroyed
208 }
209 if (ec)
210 {
211 std::cerr << "Error on GPIO based intrusion sensor wait event\n";
212 }
213 else
214 {
215 readGpio();
216 }
217 pollSensorStatusByGpio();
218 });
Qiang XUe28d1fa2019-02-27 13:50:56 +0800219}
220
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800221void ChassisIntrusionSensor::initGpioDeviceFile()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800222{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800223 mGpioLine = gpiod::find_line(mPinName);
224 if (!mGpioLine)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800225 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800226 std::cerr << "ChassisIntrusionSensor error finding gpio pin name: "
227 << mPinName << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800228 return;
229 }
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800230
231 try
232 {
233
234 mGpioLine.request(
235 {"ChassisIntrusionSensor", gpiod::line_request::EVENT_BOTH_EDGES,
236 mGpioInverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
237
238 // set string defined in chassis redfish schema
239 auto value = mGpioLine.get_value();
Ed Tanous2049bd22022-07-09 07:20:26 -0700240 std::string newValue = value != 0 ? "HardwareIntrusion" : "Normal";
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800241 updateValue(newValue);
242
243 auto gpioLineFd = mGpioLine.event_get_fd();
244 if (gpioLineFd < 0)
245 {
246 std::cerr << "ChassisIntrusionSensor failed to get " << mPinName
247 << " fd\n";
248 return;
249 }
250
251 mGpioFd.assign(gpioLineFd);
252 }
Patrick Williams26601e82021-10-06 12:43:25 -0500253 catch (const std::system_error&)
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800254 {
255 std::cerr << "ChassisInrtusionSensor error requesting gpio pin name: "
256 << mPinName << "\n";
257 return;
258 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800259}
260
261int ChassisIntrusionSensor::setSensorValue(const std::string& req,
262 std::string& propertyValue)
263{
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530264 if (!mInternalSet)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800265 {
Qiang XUe28d1fa2019-02-27 13:50:56 +0800266 propertyValue = req;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800267 mOverridenState = true;
268 }
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530269 else if (!mOverridenState)
270 {
271 propertyValue = req;
272 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800273 return 1;
274}
275
276void ChassisIntrusionSensor::start(IntrusionSensorType type, int busId,
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800277 int slaveAddr, bool gpioInverted)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800278{
Ed Tanous8a57ec02020-10-09 12:46:52 -0700279 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800280 {
281 std::cerr << "enter ChassisIntrusionSensor::start, type = " << type
282 << "\n";
283 if (type == IntrusionSensorType::pch)
284 {
285 std::cerr << "busId = " << busId << ", slaveAddr = " << slaveAddr
286 << "\n";
287 }
288 else if (type == IntrusionSensorType::gpio)
289 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800290 std::cerr << "gpio pinName = " << mPinName
Qiang XUe28d1fa2019-02-27 13:50:56 +0800291 << ", gpioInverted = " << gpioInverted << "\n";
292 }
293 }
294
295 if ((type == IntrusionSensorType::pch && busId == mBusId &&
296 slaveAddr == mSlaveAddr) ||
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800297 (type == IntrusionSensorType::gpio && gpioInverted == mGpioInverted &&
298 mInitialized))
Qiang XUe28d1fa2019-02-27 13:50:56 +0800299 {
300 return;
301 }
302
303 mType = type;
304 mBusId = busId;
305 mSlaveAddr = slaveAddr;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800306 mGpioInverted = gpioInverted;
307
308 if ((mType == IntrusionSensorType::pch && mBusId > 0 && mSlaveAddr > 0) ||
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800309 (mType == IntrusionSensorType::gpio))
Qiang XUe28d1fa2019-02-27 13:50:56 +0800310 {
311 // initialize first if not initialized before
312 if (!mInitialized)
313 {
314 mIface->register_property(
315 "Status", mValue,
316 [&](const std::string& req, std::string& propertyValue) {
Ed Tanousbb679322022-05-16 16:10:00 -0700317 return setSensorValue(req, propertyValue);
Qiang XUe28d1fa2019-02-27 13:50:56 +0800318 });
319 mIface->initialize();
320
321 if (mType == IntrusionSensorType::gpio)
322 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800323 initGpioDeviceFile();
Qiang XUe28d1fa2019-02-27 13:50:56 +0800324 }
325
326 mInitialized = true;
327 }
328
329 // start polling value
330 if (mType == IntrusionSensorType::pch)
331 {
332 pollSensorStatusByPch();
333 }
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800334 else if (mType == IntrusionSensorType::gpio && mGpioLine)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800335 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800336 std::cerr << "Start polling intrusion sensors\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800337 pollSensorStatusByGpio();
338 }
339 }
340
341 // invalid para, release resource
342 else
343 {
344 if (mInitialized)
345 {
346 if (mType == IntrusionSensorType::pch)
347 {
348 mPollTimer.cancel();
349 }
350 else if (mType == IntrusionSensorType::gpio)
351 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800352 mGpioFd.close();
353 if (mGpioLine)
354 {
355 mGpioLine.release();
356 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800357 }
358 mInitialized = false;
359 }
360 }
361}
362
363ChassisIntrusionSensor::ChassisIntrusionSensor(
Ed Tanous1f978632023-02-28 18:16:39 -0800364 boost::asio::io_context& io,
Qiang XUe28d1fa2019-02-27 13:50:56 +0800365 std::shared_ptr<sdbusplus::asio::dbus_interface> iface) :
Ed Tanous8a57ec02020-10-09 12:46:52 -0700366 mIface(std::move(iface)),
Ed Tanousb429f312022-06-27 16:09:53 -0700367 mValue("unknown"), mOldValue("unknown"), mPollTimer(io), mGpioFd(io)
James Feist38fb5982020-05-28 10:09:54 -0700368{}
Qiang XUe28d1fa2019-02-27 13:50:56 +0800369
370ChassisIntrusionSensor::~ChassisIntrusionSensor()
371{
372 if (mType == IntrusionSensorType::pch)
373 {
374 mPollTimer.cancel();
375 }
376 else if (mType == IntrusionSensorType::gpio)
377 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800378 mGpioFd.close();
379 if (mGpioLine)
380 {
381 mGpioLine.release();
382 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800383 }
384}