blob: ee49e6ea594bd7739933e933d0f4f4c37563136e [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;
Chau Lyb318dca2022-10-26 04:12:52 +000048static constexpr const char* hwIntrusionValStr =
49 "xyz.openbmc_project.Chassis.Intrusion.Status.HardwareIntrusion";
50static constexpr const char* normalValStr =
51 "xyz.openbmc_project.Chassis.Intrusion.Status.Normal";
52static constexpr const char* manualRearmStr =
53 "xyz.openbmc_project.Chassis.Intrusion.RearmMode.Manual";
54static constexpr const char* autoRearmStr =
55 "xyz.openbmc_project.Chassis.Intrusion.RearmMode.Automatic";
Qiang XUe28d1fa2019-02-27 13:50:56 +080056
57// SMLink Status Register
58const static constexpr size_t pchStatusRegIntrusion = 0x04;
59
60// Status bit field masks
61const static constexpr size_t pchRegMaskIntrusion = 0x01;
62
Chau Ly95f49932023-04-19 09:44:55 +000063// Value to clear intrusion status hwmon file
64const static constexpr size_t intrusionStatusHwmonClearValue = 0;
65
Chau Lycebb28c2022-10-21 10:01:52 +000066void ChassisIntrusionSensor::updateValue(const size_t& value)
Qiang XUe28d1fa2019-02-27 13:50:56 +080067{
Chau Lycebb28c2022-10-21 10:01:52 +000068 std::string newValue = value != 0 ? hwIntrusionValStr : normalValStr;
69
Chau Lyb318dca2022-10-26 04:12:52 +000070 // Take no action if the hardware status does not change
Josh Lehan833661a2020-03-04 17:35:41 -080071 // Same semantics as Sensor::updateValue(const double&)
72 if (newValue == mValue)
73 {
74 return;
75 }
76
Chau Lycebb28c2022-10-21 10:01:52 +000077 if constexpr (debug)
78 {
79 std::cout << "Update value from " << mValue << " to " << newValue
80 << "\n";
81 }
82
Chau Lyb318dca2022-10-26 04:12:52 +000083 // Automatic Rearm mode allows direct update
84 // Manual Rearm mode requires a rearm action to clear the intrusion
85 // status
86 if (!mAutoRearm)
87 {
88 if (newValue == normalValStr)
89 {
90 // Chassis is first closed from being open. If it has been
91 // rearmed externally, reset the flag, update mValue and
92 // return, without having to write "Normal" to DBus property
93 // (because the rearm action already did).
94 // Otherwise, return with no more action.
95 if (mRearmFlag)
96 {
97 mRearmFlag = false;
98 mValue = newValue;
99 }
100 return;
101 }
102 }
103
104 // Flush the rearm flag everytime it allows an update to Dbus
105 mRearmFlag = false;
106
Qiang XUe28d1fa2019-02-27 13:50:56 +0800107 // indicate that it is internal set call
Chau Lyb318dca2022-10-26 04:12:52 +0000108 mOverridenState = false;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800109 mInternalSet = true;
110 mIface->set_property("Status", newValue);
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530111 mInternalSet = false;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800112
113 mValue = newValue;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800114}
115
Chau Lycebb28c2022-10-21 10:01:52 +0000116int ChassisIntrusionPchSensor::readSensor()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800117{
Ed Tanous8a57ec02020-10-09 12:46:52 -0700118 int32_t statusMask = pchRegMaskIntrusion;
119 int32_t statusReg = pchStatusRegIntrusion;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800120
Chau Lycebb28c2022-10-21 10:01:52 +0000121 int32_t value = i2c_smbus_read_byte_data(mBusFd, statusReg);
122 if constexpr (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800123 {
Chau Lycebb28c2022-10-21 10:01:52 +0000124 std::cout << "Pch type: raw value is " << value << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800125 }
126
Chau Lycebb28c2022-10-21 10:01:52 +0000127 if (value < 0)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800128 {
129 std::cerr << "i2c_smbus_read_byte_data failed \n";
130 return -1;
131 }
132
133 // Get status value with mask
Chau Lycebb28c2022-10-21 10:01:52 +0000134 value &= statusMask;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800135
Chau Lycebb28c2022-10-21 10:01:52 +0000136 if constexpr (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800137 {
Chau Lycebb28c2022-10-21 10:01:52 +0000138 std::cout << "Pch type: masked raw value is " << value << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800139 }
Chau Lycebb28c2022-10-21 10:01:52 +0000140 return value;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800141}
142
Chau Lycebb28c2022-10-21 10:01:52 +0000143void ChassisIntrusionPchSensor::pollSensorStatus()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800144{
Chau Lycebb28c2022-10-21 10:01:52 +0000145 std::weak_ptr<ChassisIntrusionPchSensor> weakRef = weak_from_this();
Chau Ly95f49932023-04-19 09:44:55 +0000146
Qiang XUe28d1fa2019-02-27 13:50:56 +0800147 // setting a new experation implicitly cancels any pending async wait
Ed Tanous83db50c2023-03-01 10:20:24 -0800148 mPollTimer.expires_after(std::chrono::seconds(intrusionSensorPollSec));
Qiang XUe28d1fa2019-02-27 13:50:56 +0800149
Chau Lycebb28c2022-10-21 10:01:52 +0000150 mPollTimer.async_wait([weakRef](const boost::system::error_code& ec) {
Qiang XUe28d1fa2019-02-27 13:50:56 +0800151 // case of being canceled
Chau Lycebb28c2022-10-21 10:01:52 +0000152 if (ec == boost::asio::error::operation_aborted)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800153 {
Chau Lycebb28c2022-10-21 10:01:52 +0000154 std::cerr << "Timer of intrusion sensor is cancelled\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800155 return;
156 }
Chau Ly95f49932023-04-19 09:44:55 +0000157
Chau Lycebb28c2022-10-21 10:01:52 +0000158 std::shared_ptr<ChassisIntrusionPchSensor> self = weakRef.lock();
159 if (!self)
160 {
161 std::cerr << "ChassisIntrusionSensor no self\n";
162 return;
163 }
Chau Ly95f49932023-04-19 09:44:55 +0000164
Chau Lycebb28c2022-10-21 10:01:52 +0000165 int value = self->readSensor();
166 if (value < 0)
167 {
168 intrusionSensorPollSec = sensorFailedPollSec;
169 }
170 else
171 {
172 intrusionSensorPollSec = defaultPollSec;
173 self->updateValue(value);
174 }
Chau Ly95f49932023-04-19 09:44:55 +0000175
Chau Lycebb28c2022-10-21 10:01:52 +0000176 // trigger next polling
177 self->pollSensorStatus();
Qiang XUe28d1fa2019-02-27 13:50:56 +0800178 });
179}
180
Chau Lycebb28c2022-10-21 10:01:52 +0000181int ChassisIntrusionGpioSensor::readSensor()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800182{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800183 mGpioLine.event_read();
184 auto value = mGpioLine.get_value();
Chau Lycebb28c2022-10-21 10:01:52 +0000185 if constexpr (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800186 {
Chau Lycebb28c2022-10-21 10:01:52 +0000187 std::cout << "Gpio type: raw value is " << value << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800188 }
Chau Lycebb28c2022-10-21 10:01:52 +0000189 return value;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800190}
191
Chau Lycebb28c2022-10-21 10:01:52 +0000192void ChassisIntrusionGpioSensor::pollSensorStatus()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800193{
Ed Tanousbb679322022-05-16 16:10:00 -0700194 mGpioFd.async_wait(boost::asio::posix::stream_descriptor::wait_read,
195 [this](const boost::system::error_code& ec) {
196 if (ec == boost::system::errc::bad_file_descriptor)
197 {
198 return; // we're being destroyed
199 }
Chau Ly95f49932023-04-19 09:44:55 +0000200
Ed Tanousbb679322022-05-16 16:10:00 -0700201 if (ec)
202 {
203 std::cerr << "Error on GPIO based intrusion sensor wait event\n";
204 }
205 else
206 {
Chau Lycebb28c2022-10-21 10:01:52 +0000207 int value = readSensor();
208 if (value >= 0)
209 {
210 updateValue(value);
211 }
212 // trigger next polling
213 pollSensorStatus();
Ed Tanousbb679322022-05-16 16:10:00 -0700214 }
Ed Tanousbb679322022-05-16 16:10:00 -0700215 });
Qiang XUe28d1fa2019-02-27 13:50:56 +0800216}
217
Chau Ly95f49932023-04-19 09:44:55 +0000218int ChassisIntrusionHwmonSensor::readSensor()
219{
220 int value = 0;
221
222 std::fstream stream(mHwmonPath, std::ios::in | std::ios::out);
223 if (!stream.good())
224 {
225 std::cerr << "Error reading status at " << mHwmonPath << "\n";
226 return -1;
227 }
228
229 std::string line;
230 if (!std::getline(stream, line))
231 {
232 std::cerr << "Error reading status at " << mHwmonPath << "\n";
233 return -1;
234 }
235
236 try
237 {
238 value = std::stoi(line);
239 if constexpr (debug)
240 {
241 std::cout << "Hwmon type: raw value is " << value << "\n";
242 }
243 }
244 catch (const std::invalid_argument& e)
245 {
246 std::cerr << "Error reading status at " << mHwmonPath << " : "
247 << e.what() << "\n";
248 return -1;
249 }
250
251 // Reset chassis intrusion status after every reading
252 stream << intrusionStatusHwmonClearValue;
253
254 return value;
255}
256
257void ChassisIntrusionHwmonSensor::pollSensorStatus()
258{
259 std::weak_ptr<ChassisIntrusionHwmonSensor> weakRef = weak_from_this();
260
261 // setting a new experation implicitly cancels any pending async wait
262 mPollTimer.expires_after(std::chrono::seconds(intrusionSensorPollSec));
263
264 mPollTimer.async_wait([weakRef](const boost::system::error_code& ec) {
265 // case of being canceled
266 if (ec == boost::asio::error::operation_aborted)
267 {
268 std::cerr << "Timer of intrusion sensor is cancelled\n";
269 return;
270 }
271
272 std::shared_ptr<ChassisIntrusionHwmonSensor> self = weakRef.lock();
273 if (!self)
274 {
275 std::cerr << "ChassisIntrusionSensor no self\n";
276 return;
277 }
278
279 int value = self->readSensor();
280 if (value < 0)
281 {
282 intrusionSensorPollSec = sensorFailedPollSec;
283 }
284 else
285 {
286 intrusionSensorPollSec = defaultPollSec;
287 self->updateValue(value);
288 }
289
290 // trigger next polling
291 self->pollSensorStatus();
292 });
293}
294
Qiang XUe28d1fa2019-02-27 13:50:56 +0800295int ChassisIntrusionSensor::setSensorValue(const std::string& req,
296 std::string& propertyValue)
297{
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530298 if (!mInternalSet)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800299 {
Chau Lyb318dca2022-10-26 04:12:52 +0000300 /*
301 1. Assuming that setting property in Automatic mode causes
302 no effect but only event logs and propertiesChanged signal
303 (because the property will be updated continuously to the
304 current hardware status anyway), only update Status property
305 and raise rearm flag in Manual rearm mode.
306 2. Only accept Normal value from an external call.
307 */
308 if (!mAutoRearm && req == normalValStr)
309 {
310 mRearmFlag = true;
311 propertyValue = req;
312 mOverridenState = true;
313 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800314 }
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530315 else if (!mOverridenState)
316 {
317 propertyValue = req;
318 }
Chau Lyb318dca2022-10-26 04:12:52 +0000319 else
320 {
321 return 1;
322 }
323 // Send intrusion event to Redfish
324 if (mValue == normalValStr && propertyValue != normalValStr)
325 {
326 sd_journal_send("MESSAGE=%s", "Chassis intrusion assert event",
327 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
328 "OpenBMC.0.1.ChassisIntrusionDetected", NULL);
329 }
330 else if (mValue == hwIntrusionValStr && propertyValue == normalValStr)
331 {
332 sd_journal_send("MESSAGE=%s", "Chassis intrusion de-assert event",
333 "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
334 "OpenBMC.0.1.ChassisIntrusionReset", NULL);
335 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800336 return 1;
337}
338
Chau Lycebb28c2022-10-21 10:01:52 +0000339void ChassisIntrusionSensor::start()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800340{
Chau Lycebb28c2022-10-21 10:01:52 +0000341 mIface->register_property(
342 "Status", mValue,
343 [&](const std::string& req, std::string& propertyValue) {
344 return setSensorValue(req, propertyValue);
Patrick Williams597e8422023-10-20 11:19:01 -0500345 });
Chau Lyb318dca2022-10-26 04:12:52 +0000346 std::string rearmStr = mAutoRearm ? autoRearmStr : manualRearmStr;
347 mIface->register_property("Rearm", rearmStr);
Chau Lycebb28c2022-10-21 10:01:52 +0000348 mIface->initialize();
349 pollSensorStatus();
Qiang XUe28d1fa2019-02-27 13:50:56 +0800350}
351
352ChassisIntrusionSensor::ChassisIntrusionSensor(
Chau Lyb318dca2022-10-26 04:12:52 +0000353 bool autoRearm, sdbusplus::asio::object_server& objServer) :
354 mValue(normalValStr),
355 mAutoRearm(autoRearm), mObjServer(objServer)
Chau Lycebb28c2022-10-21 10:01:52 +0000356{
357 mIface = mObjServer.add_interface("/xyz/openbmc_project/Chassis/Intrusion",
358 "xyz.openbmc_project.Chassis.Intrusion");
359}
360
361ChassisIntrusionPchSensor::ChassisIntrusionPchSensor(
Chau Lyb318dca2022-10-26 04:12:52 +0000362 bool autoRearm, boost::asio::io_context& io,
363 sdbusplus::asio::object_server& objServer, int busId, int slaveAddr) :
364 ChassisIntrusionSensor(autoRearm, objServer),
Chau Lycebb28c2022-10-21 10:01:52 +0000365 mPollTimer(io)
366{
367 if (busId < 0 || slaveAddr <= 0)
368 {
369 throw std::invalid_argument("Invalid i2c bus " + std::to_string(busId) +
370 " address " + std::to_string(slaveAddr) +
371 "\n");
372 }
Chau Ly95f49932023-04-19 09:44:55 +0000373
Chau Lycebb28c2022-10-21 10:01:52 +0000374 mSlaveAddr = slaveAddr;
Chau Ly95f49932023-04-19 09:44:55 +0000375
Chau Lycebb28c2022-10-21 10:01:52 +0000376 std::string devPath = "/dev/i2c-" + std::to_string(busId);
377 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
378 mBusFd = open(devPath.c_str(), O_RDWR | O_CLOEXEC);
379 if (mBusFd < 0)
380 {
381 throw std::invalid_argument("Unable to open " + devPath + "\n");
382 }
383
384 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
385 if (ioctl(mBusFd, I2C_SLAVE_FORCE, mSlaveAddr) < 0)
386 {
387 throw std::runtime_error("Unable to set device address\n");
388 }
389
390 unsigned long funcs = 0;
391
392 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
393 if (ioctl(mBusFd, I2C_FUNCS, &funcs) < 0)
394 {
395 throw std::runtime_error("Don't support I2C_FUNCS\n");
396 }
397
398 if ((funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA) == 0U)
399 {
400 throw std::runtime_error(
401 "Do not have I2C_FUNC_SMBUS_READ_BYTE_DATA \n");
402 }
403}
404
405ChassisIntrusionGpioSensor::ChassisIntrusionGpioSensor(
Chau Lyb318dca2022-10-26 04:12:52 +0000406 bool autoRearm, boost::asio::io_context& io,
407 sdbusplus::asio::object_server& objServer, bool gpioInverted) :
408 ChassisIntrusionSensor(autoRearm, objServer),
Chau Lycebb28c2022-10-21 10:01:52 +0000409 mGpioInverted(gpioInverted), mGpioFd(io)
410{
411 mGpioLine = gpiod::find_line(mPinName);
412 if (!mGpioLine)
413 {
414 throw std::invalid_argument("Error finding gpio pin name: " + mPinName +
415 "\n");
416 }
417 mGpioLine.request(
418 {"ChassisIntrusionSensor", gpiod::line_request::EVENT_BOTH_EDGES,
419 mGpioInverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
420
421 auto gpioLineFd = mGpioLine.event_get_fd();
422 if (gpioLineFd < 0)
423 {
424 throw std::invalid_argument("Failed to get " + mPinName + " fd\n");
425 }
Chau Ly95f49932023-04-19 09:44:55 +0000426
Chau Lycebb28c2022-10-21 10:01:52 +0000427 mGpioFd.assign(gpioLineFd);
428}
Qiang XUe28d1fa2019-02-27 13:50:56 +0800429
Chau Ly95f49932023-04-19 09:44:55 +0000430ChassisIntrusionHwmonSensor::ChassisIntrusionHwmonSensor(
Chau Lyb318dca2022-10-26 04:12:52 +0000431 bool autoRearm, boost::asio::io_context& io,
432 sdbusplus::asio::object_server& objServer, std::string hwmonName) :
433 ChassisIntrusionSensor(autoRearm, objServer),
Chau Ly95f49932023-04-19 09:44:55 +0000434 mHwmonName(std::move(hwmonName)), mPollTimer(io)
435{
436 std::vector<fs::path> paths;
437
438 if (!findFiles(fs::path("/sys/class/hwmon"), mHwmonName, paths))
439 {
440 throw std::invalid_argument("Failed to find hwmon path in sysfs\n");
441 }
442
443 if (paths.empty())
444 {
445 throw std::invalid_argument("Hwmon file " + mHwmonName +
446 " can't be found in sysfs\n");
447 }
448
449 if (paths.size() > 1)
450 {
451 std::cerr << "Found more than 1 hwmon file to read chassis intrusion"
452 << " status. Taking the first one. \n";
453 }
454
455 // Expecting only one hwmon file for one given chassis
456 mHwmonPath = paths[0].string();
457
458 if constexpr (debug)
459 {
460 std::cout << "Found " << paths.size()
461 << " paths for intrusion status \n"
462 << " The first path is: " << mHwmonPath << "\n";
463 }
464}
465
Qiang XUe28d1fa2019-02-27 13:50:56 +0800466ChassisIntrusionSensor::~ChassisIntrusionSensor()
467{
Chau Lycebb28c2022-10-21 10:01:52 +0000468 mObjServer.remove_interface(mIface);
469}
470
471ChassisIntrusionPchSensor::~ChassisIntrusionPchSensor()
472{
473 mPollTimer.cancel();
474 if (close(mBusFd) < 0)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800475 {
Chau Lycebb28c2022-10-21 10:01:52 +0000476 std::cerr << "Failed to close fd " << std::to_string(mBusFd);
Qiang XUe28d1fa2019-02-27 13:50:56 +0800477 }
Chau Lycebb28c2022-10-21 10:01:52 +0000478}
479
480ChassisIntrusionGpioSensor::~ChassisIntrusionGpioSensor()
481{
482 mGpioFd.close();
483 if (mGpioLine)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800484 {
Chau Lycebb28c2022-10-21 10:01:52 +0000485 mGpioLine.release();
Qiang XUe28d1fa2019-02-27 13:50:56 +0800486 }
487}
Chau Ly95f49932023-04-19 09:44:55 +0000488
489ChassisIntrusionHwmonSensor::~ChassisIntrusionHwmonSensor()
490{
491 mPollTimer.cancel();
492}