blob: 30663593b3544259c5261dd83484b2e570cf664a [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 Ly95f49932023-04-19 09:44:55 +000057// Value to clear intrusion status hwmon file
58const static constexpr size_t intrusionStatusHwmonClearValue = 0;
59
Chau Lycebb28c2022-10-21 10:01:52 +000060void ChassisIntrusionSensor::updateValue(const size_t& value)
Qiang XUe28d1fa2019-02-27 13:50:56 +080061{
Chau Lycebb28c2022-10-21 10:01:52 +000062 std::string newValue = value != 0 ? hwIntrusionValStr : normalValStr;
63
Josh Lehan833661a2020-03-04 17:35:41 -080064 // Take no action if value already equal
65 // Same semantics as Sensor::updateValue(const double&)
66 if (newValue == mValue)
67 {
68 return;
69 }
70
Chau Lycebb28c2022-10-21 10:01:52 +000071 if constexpr (debug)
72 {
73 std::cout << "Update value from " << mValue << " to " << newValue
74 << "\n";
75 }
76
Qiang XUe28d1fa2019-02-27 13:50:56 +080077 // indicate that it is internal set call
78 mInternalSet = true;
79 mIface->set_property("Status", newValue);
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +053080 mInternalSet = false;
Qiang XUe28d1fa2019-02-27 13:50:56 +080081
82 mValue = newValue;
83
Chau Lycebb28c2022-10-21 10:01:52 +000084 if (mOldValue == normalValStr && mValue != normalValStr)
Qiang XUe28d1fa2019-02-27 13:50:56 +080085 {
Matt Simmering79a160b2022-11-14 16:50:48 -080086 sd_journal_send("MESSAGE=%s", "Chassis intrusion assert event",
87 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
88 "OpenBMC.0.1.ChassisIntrusionDetected", NULL);
Qiang XUe28d1fa2019-02-27 13:50:56 +080089 mOldValue = mValue;
90 }
Chau Lycebb28c2022-10-21 10:01:52 +000091 else if (mOldValue == hwIntrusionValStr && mValue == normalValStr)
Qiang XUe28d1fa2019-02-27 13:50:56 +080092 {
Matt Simmering79a160b2022-11-14 16:50:48 -080093 sd_journal_send("MESSAGE=%s", "Chassis intrusion de-assert event",
94 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
95 "OpenBMC.0.1.ChassisIntrusionReset", NULL);
Qiang XUe28d1fa2019-02-27 13:50:56 +080096 mOldValue = mValue;
97 }
98}
99
Chau Lycebb28c2022-10-21 10:01:52 +0000100int ChassisIntrusionPchSensor::readSensor()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800101{
Ed Tanous8a57ec02020-10-09 12:46:52 -0700102 int32_t statusMask = pchRegMaskIntrusion;
103 int32_t statusReg = pchStatusRegIntrusion;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800104
Chau Lycebb28c2022-10-21 10:01:52 +0000105 int32_t value = i2c_smbus_read_byte_data(mBusFd, statusReg);
106 if constexpr (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800107 {
Chau Lycebb28c2022-10-21 10:01:52 +0000108 std::cout << "Pch type: raw value is " << value << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800109 }
110
Chau Lycebb28c2022-10-21 10:01:52 +0000111 if (value < 0)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800112 {
113 std::cerr << "i2c_smbus_read_byte_data failed \n";
114 return -1;
115 }
116
117 // Get status value with mask
Chau Lycebb28c2022-10-21 10:01:52 +0000118 value &= statusMask;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800119
Chau Lycebb28c2022-10-21 10:01:52 +0000120 if constexpr (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800121 {
Chau Lycebb28c2022-10-21 10:01:52 +0000122 std::cout << "Pch type: masked raw value is " << value << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800123 }
Chau Lycebb28c2022-10-21 10:01:52 +0000124 return value;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800125}
126
Chau Lycebb28c2022-10-21 10:01:52 +0000127void ChassisIntrusionPchSensor::pollSensorStatus()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800128{
Chau Lycebb28c2022-10-21 10:01:52 +0000129 std::weak_ptr<ChassisIntrusionPchSensor> weakRef = weak_from_this();
Chau Ly95f49932023-04-19 09:44:55 +0000130
Qiang XUe28d1fa2019-02-27 13:50:56 +0800131 // setting a new experation implicitly cancels any pending async wait
Ed Tanous83db50c2023-03-01 10:20:24 -0800132 mPollTimer.expires_after(std::chrono::seconds(intrusionSensorPollSec));
Qiang XUe28d1fa2019-02-27 13:50:56 +0800133
Chau Lycebb28c2022-10-21 10:01:52 +0000134 mPollTimer.async_wait([weakRef](const boost::system::error_code& ec) {
Qiang XUe28d1fa2019-02-27 13:50:56 +0800135 // case of being canceled
Chau Lycebb28c2022-10-21 10:01:52 +0000136 if (ec == boost::asio::error::operation_aborted)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800137 {
Chau Lycebb28c2022-10-21 10:01:52 +0000138 std::cerr << "Timer of intrusion sensor is cancelled\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800139 return;
140 }
Chau Ly95f49932023-04-19 09:44:55 +0000141
Chau Lycebb28c2022-10-21 10:01:52 +0000142 std::shared_ptr<ChassisIntrusionPchSensor> self = weakRef.lock();
143 if (!self)
144 {
145 std::cerr << "ChassisIntrusionSensor no self\n";
146 return;
147 }
Chau Ly95f49932023-04-19 09:44:55 +0000148
Chau Lycebb28c2022-10-21 10:01:52 +0000149 int value = self->readSensor();
150 if (value < 0)
151 {
152 intrusionSensorPollSec = sensorFailedPollSec;
153 }
154 else
155 {
156 intrusionSensorPollSec = defaultPollSec;
157 self->updateValue(value);
158 }
Chau Ly95f49932023-04-19 09:44:55 +0000159
Chau Lycebb28c2022-10-21 10:01:52 +0000160 // trigger next polling
161 self->pollSensorStatus();
Qiang XUe28d1fa2019-02-27 13:50:56 +0800162 });
163}
164
Chau Lycebb28c2022-10-21 10:01:52 +0000165int ChassisIntrusionGpioSensor::readSensor()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800166{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800167 mGpioLine.event_read();
168 auto value = mGpioLine.get_value();
Chau Lycebb28c2022-10-21 10:01:52 +0000169 if constexpr (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800170 {
Chau Lycebb28c2022-10-21 10:01:52 +0000171 std::cout << "Gpio type: raw value is " << value << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800172 }
Chau Lycebb28c2022-10-21 10:01:52 +0000173 return value;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800174}
175
Chau Lycebb28c2022-10-21 10:01:52 +0000176void ChassisIntrusionGpioSensor::pollSensorStatus()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800177{
Ed Tanousbb679322022-05-16 16:10:00 -0700178 mGpioFd.async_wait(boost::asio::posix::stream_descriptor::wait_read,
179 [this](const boost::system::error_code& ec) {
180 if (ec == boost::system::errc::bad_file_descriptor)
181 {
182 return; // we're being destroyed
183 }
Chau Ly95f49932023-04-19 09:44:55 +0000184
Ed Tanousbb679322022-05-16 16:10:00 -0700185 if (ec)
186 {
187 std::cerr << "Error on GPIO based intrusion sensor wait event\n";
188 }
189 else
190 {
Chau Lycebb28c2022-10-21 10:01:52 +0000191 int value = readSensor();
192 if (value >= 0)
193 {
194 updateValue(value);
195 }
196 // trigger next polling
197 pollSensorStatus();
Ed Tanousbb679322022-05-16 16:10:00 -0700198 }
Ed Tanousbb679322022-05-16 16:10:00 -0700199 });
Qiang XUe28d1fa2019-02-27 13:50:56 +0800200}
201
Chau Ly95f49932023-04-19 09:44:55 +0000202int ChassisIntrusionHwmonSensor::readSensor()
203{
204 int value = 0;
205
206 std::fstream stream(mHwmonPath, std::ios::in | std::ios::out);
207 if (!stream.good())
208 {
209 std::cerr << "Error reading status at " << mHwmonPath << "\n";
210 return -1;
211 }
212
213 std::string line;
214 if (!std::getline(stream, line))
215 {
216 std::cerr << "Error reading status at " << mHwmonPath << "\n";
217 return -1;
218 }
219
220 try
221 {
222 value = std::stoi(line);
223 if constexpr (debug)
224 {
225 std::cout << "Hwmon type: raw value is " << value << "\n";
226 }
227 }
228 catch (const std::invalid_argument& e)
229 {
230 std::cerr << "Error reading status at " << mHwmonPath << " : "
231 << e.what() << "\n";
232 return -1;
233 }
234
235 // Reset chassis intrusion status after every reading
236 stream << intrusionStatusHwmonClearValue;
237
238 return value;
239}
240
241void ChassisIntrusionHwmonSensor::pollSensorStatus()
242{
243 std::weak_ptr<ChassisIntrusionHwmonSensor> weakRef = weak_from_this();
244
245 // setting a new experation implicitly cancels any pending async wait
246 mPollTimer.expires_after(std::chrono::seconds(intrusionSensorPollSec));
247
248 mPollTimer.async_wait([weakRef](const boost::system::error_code& ec) {
249 // case of being canceled
250 if (ec == boost::asio::error::operation_aborted)
251 {
252 std::cerr << "Timer of intrusion sensor is cancelled\n";
253 return;
254 }
255
256 std::shared_ptr<ChassisIntrusionHwmonSensor> self = weakRef.lock();
257 if (!self)
258 {
259 std::cerr << "ChassisIntrusionSensor no self\n";
260 return;
261 }
262
263 int value = self->readSensor();
264 if (value < 0)
265 {
266 intrusionSensorPollSec = sensorFailedPollSec;
267 }
268 else
269 {
270 intrusionSensorPollSec = defaultPollSec;
271 self->updateValue(value);
272 }
273
274 // trigger next polling
275 self->pollSensorStatus();
276 });
277}
278
Qiang XUe28d1fa2019-02-27 13:50:56 +0800279int ChassisIntrusionSensor::setSensorValue(const std::string& req,
280 std::string& propertyValue)
281{
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530282 if (!mInternalSet)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800283 {
Qiang XUe28d1fa2019-02-27 13:50:56 +0800284 propertyValue = req;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800285 mOverridenState = true;
286 }
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530287 else if (!mOverridenState)
288 {
289 propertyValue = req;
290 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800291 return 1;
292}
293
Chau Lycebb28c2022-10-21 10:01:52 +0000294void ChassisIntrusionSensor::start()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800295{
Chau Lycebb28c2022-10-21 10:01:52 +0000296 mIface->register_property(
297 "Status", mValue,
298 [&](const std::string& req, std::string& propertyValue) {
299 return setSensorValue(req, propertyValue);
300 });
301 mIface->initialize();
302 pollSensorStatus();
Qiang XUe28d1fa2019-02-27 13:50:56 +0800303}
304
305ChassisIntrusionSensor::ChassisIntrusionSensor(
Chau Lycebb28c2022-10-21 10:01:52 +0000306 sdbusplus::asio::object_server& objServer) :
307 mObjServer(objServer)
308{
309 mIface = mObjServer.add_interface("/xyz/openbmc_project/Chassis/Intrusion",
310 "xyz.openbmc_project.Chassis.Intrusion");
311}
312
313ChassisIntrusionPchSensor::ChassisIntrusionPchSensor(
314 boost::asio::io_context& io, sdbusplus::asio::object_server& objServer,
315 int busId, int slaveAddr) :
316 ChassisIntrusionSensor(objServer),
317 mPollTimer(io)
318{
319 if (busId < 0 || slaveAddr <= 0)
320 {
321 throw std::invalid_argument("Invalid i2c bus " + std::to_string(busId) +
322 " address " + std::to_string(slaveAddr) +
323 "\n");
324 }
Chau Ly95f49932023-04-19 09:44:55 +0000325
Chau Lycebb28c2022-10-21 10:01:52 +0000326 mSlaveAddr = slaveAddr;
Chau Ly95f49932023-04-19 09:44:55 +0000327
Chau Lycebb28c2022-10-21 10:01:52 +0000328 std::string devPath = "/dev/i2c-" + std::to_string(busId);
329 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
330 mBusFd = open(devPath.c_str(), O_RDWR | O_CLOEXEC);
331 if (mBusFd < 0)
332 {
333 throw std::invalid_argument("Unable to open " + devPath + "\n");
334 }
335
336 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
337 if (ioctl(mBusFd, I2C_SLAVE_FORCE, mSlaveAddr) < 0)
338 {
339 throw std::runtime_error("Unable to set device address\n");
340 }
341
342 unsigned long funcs = 0;
343
344 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
345 if (ioctl(mBusFd, I2C_FUNCS, &funcs) < 0)
346 {
347 throw std::runtime_error("Don't support I2C_FUNCS\n");
348 }
349
350 if ((funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA) == 0U)
351 {
352 throw std::runtime_error(
353 "Do not have I2C_FUNC_SMBUS_READ_BYTE_DATA \n");
354 }
355}
356
357ChassisIntrusionGpioSensor::ChassisIntrusionGpioSensor(
358 boost::asio::io_context& io, sdbusplus::asio::object_server& objServer,
359 bool gpioInverted) :
360 ChassisIntrusionSensor(objServer),
361 mGpioInverted(gpioInverted), mGpioFd(io)
362{
363 mGpioLine = gpiod::find_line(mPinName);
364 if (!mGpioLine)
365 {
366 throw std::invalid_argument("Error finding gpio pin name: " + mPinName +
367 "\n");
368 }
369 mGpioLine.request(
370 {"ChassisIntrusionSensor", gpiod::line_request::EVENT_BOTH_EDGES,
371 mGpioInverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
372
373 auto gpioLineFd = mGpioLine.event_get_fd();
374 if (gpioLineFd < 0)
375 {
376 throw std::invalid_argument("Failed to get " + mPinName + " fd\n");
377 }
Chau Ly95f49932023-04-19 09:44:55 +0000378
Chau Lycebb28c2022-10-21 10:01:52 +0000379 mGpioFd.assign(gpioLineFd);
380}
Qiang XUe28d1fa2019-02-27 13:50:56 +0800381
Chau Ly95f49932023-04-19 09:44:55 +0000382ChassisIntrusionHwmonSensor::ChassisIntrusionHwmonSensor(
383 boost::asio::io_context& io, sdbusplus::asio::object_server& objServer,
384 std::string hwmonName) :
385 ChassisIntrusionSensor(objServer),
386 mHwmonName(std::move(hwmonName)), mPollTimer(io)
387{
388 std::vector<fs::path> paths;
389
390 if (!findFiles(fs::path("/sys/class/hwmon"), mHwmonName, paths))
391 {
392 throw std::invalid_argument("Failed to find hwmon path in sysfs\n");
393 }
394
395 if (paths.empty())
396 {
397 throw std::invalid_argument("Hwmon file " + mHwmonName +
398 " can't be found in sysfs\n");
399 }
400
401 if (paths.size() > 1)
402 {
403 std::cerr << "Found more than 1 hwmon file to read chassis intrusion"
404 << " status. Taking the first one. \n";
405 }
406
407 // Expecting only one hwmon file for one given chassis
408 mHwmonPath = paths[0].string();
409
410 if constexpr (debug)
411 {
412 std::cout << "Found " << paths.size()
413 << " paths for intrusion status \n"
414 << " The first path is: " << mHwmonPath << "\n";
415 }
416}
417
Qiang XUe28d1fa2019-02-27 13:50:56 +0800418ChassisIntrusionSensor::~ChassisIntrusionSensor()
419{
Chau Lycebb28c2022-10-21 10:01:52 +0000420 mObjServer.remove_interface(mIface);
421}
422
423ChassisIntrusionPchSensor::~ChassisIntrusionPchSensor()
424{
425 mPollTimer.cancel();
426 if (close(mBusFd) < 0)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800427 {
Chau Lycebb28c2022-10-21 10:01:52 +0000428 std::cerr << "Failed to close fd " << std::to_string(mBusFd);
Qiang XUe28d1fa2019-02-27 13:50:56 +0800429 }
Chau Lycebb28c2022-10-21 10:01:52 +0000430}
431
432ChassisIntrusionGpioSensor::~ChassisIntrusionGpioSensor()
433{
434 mGpioFd.close();
435 if (mGpioLine)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800436 {
Chau Lycebb28c2022-10-21 10:01:52 +0000437 mGpioLine.release();
Qiang XUe28d1fa2019-02-27 13:50:56 +0800438 }
439}
Chau Ly95f49932023-04-19 09:44:55 +0000440
441ChassisIntrusionHwmonSensor::~ChassisIntrusionHwmonSensor()
442{
443 mPollTimer.cancel();
444}