blob: 841259fae94cb2d276535eee57a1ce1a0bfcb332 [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
Chau Lycebb28c2022-10-21 10:01:52 +000024#include <Utils.hpp>
Ed Tanous1f978632023-02-28 18:16:39 -080025#include <boost/asio/io_context.hpp>
James Feist38fb5982020-05-28 10:09:54 -070026#include <sdbusplus/asio/object_server.hpp>
27
Ed Tanous8a57ec02020-10-09 12:46:52 -070028#include <cerrno>
Qiang XUe28d1fa2019-02-27 13:50:56 +080029#include <chrono>
Chau Lycebb28c2022-10-21 10:01:52 +000030#include <fstream>
Qiang XUe28d1fa2019-02-27 13:50:56 +080031#include <iostream>
Patrick Venture96e97db2019-10-31 13:44:38 -070032#include <memory>
Qiang XUe28d1fa2019-02-27 13:50:56 +080033#include <string>
34#include <thread>
Ed Tanous8a57ec02020-10-09 12:46:52 -070035#include <utility>
Qiang XUe28d1fa2019-02-27 13:50:56 +080036
James Feist38fb5982020-05-28 10:09:54 -070037extern "C"
38{
Qiang XUe28d1fa2019-02-27 13:50:56 +080039#include <i2c/smbus.h>
40#include <linux/i2c-dev.h>
41}
42
Ed Tanous8a57ec02020-10-09 12:46:52 -070043static constexpr bool debug = false;
Qiang XUe28d1fa2019-02-27 13:50:56 +080044
Chau Lycebb28c2022-10-21 10:01:52 +000045static constexpr unsigned int defaultPollSec = 1;
46static constexpr unsigned int sensorFailedPollSec = 5;
47static unsigned int intrusionSensorPollSec = defaultPollSec;
48static constexpr const char* hwIntrusionValStr = "HardwareIntrusion";
49static constexpr const char* normalValStr = "Normal";
Qiang XUe28d1fa2019-02-27 13:50:56 +080050
51// SMLink Status Register
52const static constexpr size_t pchStatusRegIntrusion = 0x04;
53
54// Status bit field masks
55const static constexpr size_t pchRegMaskIntrusion = 0x01;
56
Chau Lycebb28c2022-10-21 10:01:52 +000057void ChassisIntrusionSensor::updateValue(const size_t& value)
Qiang XUe28d1fa2019-02-27 13:50:56 +080058{
Chau Lycebb28c2022-10-21 10:01:52 +000059 std::string newValue = value != 0 ? hwIntrusionValStr : normalValStr;
60
Josh Lehan833661a2020-03-04 17:35:41 -080061 // Take no action if value already equal
62 // Same semantics as Sensor::updateValue(const double&)
63 if (newValue == mValue)
64 {
65 return;
66 }
67
Chau Lycebb28c2022-10-21 10:01:52 +000068 if constexpr (debug)
69 {
70 std::cout << "Update value from " << mValue << " to " << newValue
71 << "\n";
72 }
73
Qiang XUe28d1fa2019-02-27 13:50:56 +080074 // indicate that it is internal set call
75 mInternalSet = true;
76 mIface->set_property("Status", newValue);
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +053077 mInternalSet = false;
Qiang XUe28d1fa2019-02-27 13:50:56 +080078
79 mValue = newValue;
80
Chau Lycebb28c2022-10-21 10:01:52 +000081 if (mOldValue == normalValStr && mValue != normalValStr)
Qiang XUe28d1fa2019-02-27 13:50:56 +080082 {
Matt Simmering79a160b2022-11-14 16:50:48 -080083 sd_journal_send("MESSAGE=%s", "Chassis intrusion assert event",
84 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
85 "OpenBMC.0.1.ChassisIntrusionDetected", NULL);
Qiang XUe28d1fa2019-02-27 13:50:56 +080086 mOldValue = mValue;
87 }
Chau Lycebb28c2022-10-21 10:01:52 +000088 else if (mOldValue == hwIntrusionValStr && mValue == normalValStr)
Qiang XUe28d1fa2019-02-27 13:50:56 +080089 {
Matt Simmering79a160b2022-11-14 16:50:48 -080090 sd_journal_send("MESSAGE=%s", "Chassis intrusion de-assert event",
91 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
92 "OpenBMC.0.1.ChassisIntrusionReset", NULL);
Qiang XUe28d1fa2019-02-27 13:50:56 +080093 mOldValue = mValue;
94 }
95}
96
Chau Lycebb28c2022-10-21 10:01:52 +000097int ChassisIntrusionPchSensor::readSensor()
Qiang XUe28d1fa2019-02-27 13:50:56 +080098{
Ed Tanous8a57ec02020-10-09 12:46:52 -070099 int32_t statusMask = pchRegMaskIntrusion;
100 int32_t statusReg = pchStatusRegIntrusion;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800101
Chau Lycebb28c2022-10-21 10:01:52 +0000102 int32_t value = i2c_smbus_read_byte_data(mBusFd, statusReg);
103 if constexpr (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800104 {
Chau Lycebb28c2022-10-21 10:01:52 +0000105 std::cout << "Pch type: raw value is " << value << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800106 }
107
Chau Lycebb28c2022-10-21 10:01:52 +0000108 if (value < 0)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800109 {
110 std::cerr << "i2c_smbus_read_byte_data failed \n";
111 return -1;
112 }
113
114 // Get status value with mask
Chau Lycebb28c2022-10-21 10:01:52 +0000115 value &= statusMask;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800116
Chau Lycebb28c2022-10-21 10:01:52 +0000117 if constexpr (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800118 {
Chau Lycebb28c2022-10-21 10:01:52 +0000119 std::cout << "Pch type: masked raw value is " << value << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800120 }
Chau Lycebb28c2022-10-21 10:01:52 +0000121 return value;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800122}
123
Chau Lycebb28c2022-10-21 10:01:52 +0000124void ChassisIntrusionPchSensor::pollSensorStatus()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800125{
Chau Lycebb28c2022-10-21 10:01:52 +0000126 std::weak_ptr<ChassisIntrusionPchSensor> weakRef = weak_from_this();
Qiang XUe28d1fa2019-02-27 13:50:56 +0800127 // setting a new experation implicitly cancels any pending async wait
Ed Tanous83db50c2023-03-01 10:20:24 -0800128 mPollTimer.expires_after(std::chrono::seconds(intrusionSensorPollSec));
Qiang XUe28d1fa2019-02-27 13:50:56 +0800129
Chau Lycebb28c2022-10-21 10:01:52 +0000130 mPollTimer.async_wait([weakRef](const boost::system::error_code& ec) {
Qiang XUe28d1fa2019-02-27 13:50:56 +0800131 // case of being canceled
Chau Lycebb28c2022-10-21 10:01:52 +0000132 if (ec == boost::asio::error::operation_aborted)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800133 {
Chau Lycebb28c2022-10-21 10:01:52 +0000134 std::cerr << "Timer of intrusion sensor is cancelled\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800135 return;
136 }
Chau Lycebb28c2022-10-21 10:01:52 +0000137 std::shared_ptr<ChassisIntrusionPchSensor> self = weakRef.lock();
138 if (!self)
139 {
140 std::cerr << "ChassisIntrusionSensor no self\n";
141 return;
142 }
143 int value = self->readSensor();
144 if (value < 0)
145 {
146 intrusionSensorPollSec = sensorFailedPollSec;
147 }
148 else
149 {
150 intrusionSensorPollSec = defaultPollSec;
151 self->updateValue(value);
152 }
153 // trigger next polling
154 self->pollSensorStatus();
Qiang XUe28d1fa2019-02-27 13:50:56 +0800155 });
156}
157
Chau Lycebb28c2022-10-21 10:01:52 +0000158int ChassisIntrusionGpioSensor::readSensor()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800159{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800160 mGpioLine.event_read();
161 auto value = mGpioLine.get_value();
Chau Lycebb28c2022-10-21 10:01:52 +0000162 if constexpr (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800163 {
Chau Lycebb28c2022-10-21 10:01:52 +0000164 std::cout << "Gpio type: raw value is " << value << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800165 }
Chau Lycebb28c2022-10-21 10:01:52 +0000166 return value;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800167}
168
Chau Lycebb28c2022-10-21 10:01:52 +0000169void ChassisIntrusionGpioSensor::pollSensorStatus()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800170{
Ed Tanousbb679322022-05-16 16:10:00 -0700171 mGpioFd.async_wait(boost::asio::posix::stream_descriptor::wait_read,
172 [this](const boost::system::error_code& ec) {
173 if (ec == boost::system::errc::bad_file_descriptor)
174 {
175 return; // we're being destroyed
176 }
177 if (ec)
178 {
179 std::cerr << "Error on GPIO based intrusion sensor wait event\n";
180 }
181 else
182 {
Chau Lycebb28c2022-10-21 10:01:52 +0000183 int value = readSensor();
184 if (value >= 0)
185 {
186 updateValue(value);
187 }
188 // trigger next polling
189 pollSensorStatus();
Ed Tanousbb679322022-05-16 16:10:00 -0700190 }
Ed Tanousbb679322022-05-16 16:10:00 -0700191 });
Qiang XUe28d1fa2019-02-27 13:50:56 +0800192}
193
Qiang XUe28d1fa2019-02-27 13:50:56 +0800194int ChassisIntrusionSensor::setSensorValue(const std::string& req,
195 std::string& propertyValue)
196{
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530197 if (!mInternalSet)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800198 {
Qiang XUe28d1fa2019-02-27 13:50:56 +0800199 propertyValue = req;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800200 mOverridenState = true;
201 }
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530202 else if (!mOverridenState)
203 {
204 propertyValue = req;
205 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800206 return 1;
207}
208
Chau Lycebb28c2022-10-21 10:01:52 +0000209void ChassisIntrusionSensor::start()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800210{
Chau Lycebb28c2022-10-21 10:01:52 +0000211 mIface->register_property(
212 "Status", mValue,
213 [&](const std::string& req, std::string& propertyValue) {
214 return setSensorValue(req, propertyValue);
215 });
216 mIface->initialize();
217 pollSensorStatus();
Qiang XUe28d1fa2019-02-27 13:50:56 +0800218}
219
220ChassisIntrusionSensor::ChassisIntrusionSensor(
Chau Lycebb28c2022-10-21 10:01:52 +0000221 sdbusplus::asio::object_server& objServer) :
222 mObjServer(objServer)
223{
224 mIface = mObjServer.add_interface("/xyz/openbmc_project/Chassis/Intrusion",
225 "xyz.openbmc_project.Chassis.Intrusion");
226}
227
228ChassisIntrusionPchSensor::ChassisIntrusionPchSensor(
229 boost::asio::io_context& io, sdbusplus::asio::object_server& objServer,
230 int busId, int slaveAddr) :
231 ChassisIntrusionSensor(objServer),
232 mPollTimer(io)
233{
234 if (busId < 0 || slaveAddr <= 0)
235 {
236 throw std::invalid_argument("Invalid i2c bus " + std::to_string(busId) +
237 " address " + std::to_string(slaveAddr) +
238 "\n");
239 }
240 mSlaveAddr = slaveAddr;
241 std::string devPath = "/dev/i2c-" + std::to_string(busId);
242 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
243 mBusFd = open(devPath.c_str(), O_RDWR | O_CLOEXEC);
244 if (mBusFd < 0)
245 {
246 throw std::invalid_argument("Unable to open " + devPath + "\n");
247 }
248
249 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
250 if (ioctl(mBusFd, I2C_SLAVE_FORCE, mSlaveAddr) < 0)
251 {
252 throw std::runtime_error("Unable to set device address\n");
253 }
254
255 unsigned long funcs = 0;
256
257 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
258 if (ioctl(mBusFd, I2C_FUNCS, &funcs) < 0)
259 {
260 throw std::runtime_error("Don't support I2C_FUNCS\n");
261 }
262
263 if ((funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA) == 0U)
264 {
265 throw std::runtime_error(
266 "Do not have I2C_FUNC_SMBUS_READ_BYTE_DATA \n");
267 }
268}
269
270ChassisIntrusionGpioSensor::ChassisIntrusionGpioSensor(
271 boost::asio::io_context& io, sdbusplus::asio::object_server& objServer,
272 bool gpioInverted) :
273 ChassisIntrusionSensor(objServer),
274 mGpioInverted(gpioInverted), mGpioFd(io)
275{
276 mGpioLine = gpiod::find_line(mPinName);
277 if (!mGpioLine)
278 {
279 throw std::invalid_argument("Error finding gpio pin name: " + mPinName +
280 "\n");
281 }
282 mGpioLine.request(
283 {"ChassisIntrusionSensor", gpiod::line_request::EVENT_BOTH_EDGES,
284 mGpioInverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
285
286 auto gpioLineFd = mGpioLine.event_get_fd();
287 if (gpioLineFd < 0)
288 {
289 throw std::invalid_argument("Failed to get " + mPinName + " fd\n");
290 }
291 mGpioFd.assign(gpioLineFd);
292}
Qiang XUe28d1fa2019-02-27 13:50:56 +0800293
294ChassisIntrusionSensor::~ChassisIntrusionSensor()
295{
Chau Lycebb28c2022-10-21 10:01:52 +0000296 mObjServer.remove_interface(mIface);
297}
298
299ChassisIntrusionPchSensor::~ChassisIntrusionPchSensor()
300{
301 mPollTimer.cancel();
302 if (close(mBusFd) < 0)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800303 {
Chau Lycebb28c2022-10-21 10:01:52 +0000304 std::cerr << "Failed to close fd " << std::to_string(mBusFd);
Qiang XUe28d1fa2019-02-27 13:50:56 +0800305 }
Chau Lycebb28c2022-10-21 10:01:52 +0000306}
307
308ChassisIntrusionGpioSensor::~ChassisIntrusionGpioSensor()
309{
310 mGpioFd.close();
311 if (mGpioLine)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800312 {
Chau Lycebb28c2022-10-21 10:01:52 +0000313 mGpioLine.release();
Qiang XUe28d1fa2019-02-27 13:50:56 +0800314 }
315}