summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--host_error_monitor/src/host_error_monitor.cpp932
-rw-r--r--libpeci/.clang-format98
-rw-r--r--libpeci/CMakeLists.txt20
-rw-r--r--libpeci/peci.c1081
-rw-r--r--libpeci/peci.h224
-rw-r--r--libpeci/peci_cmds.c370
-rw-r--r--psu-manager/src/cold_redundancy.cpp4
7 files changed, 2690 insertions, 39 deletions
diff --git a/host_error_monitor/src/host_error_monitor.cpp b/host_error_monitor/src/host_error_monitor.cpp
index 4c44007..51434f4 100644
--- a/host_error_monitor/src/host_error_monitor.cpp
+++ b/host_error_monitor/src/host_error_monitor.cpp
@@ -22,6 +22,7 @@
#include <gpiod.hpp>
#include <iostream>
#include <sdbusplus/asio/object_server.hpp>
+#include <variant>
namespace host_error_monitor
{
@@ -30,44 +31,134 @@ static std::shared_ptr<sdbusplus::asio::connection> conn;
static bool hostOff = true;
-const static constexpr int caterrTimeoutMs = 1000;
-const static constexpr int err2TimeoutMs = 90000;
+const static constexpr int caterrTimeoutMs = 2000;
+const static constexpr int errTimeoutMs = 90000;
+const static constexpr int smiTimeoutMs = 90000;
const static constexpr int crashdumpTimeoutS = 300;
// Timers
// Timer for CATERR asserted
static boost::asio::steady_timer caterrAssertTimer(io);
+// Timer for ERR0 asserted
+static boost::asio::steady_timer err0AssertTimer(io);
+// Timer for ERR1 asserted
+static boost::asio::steady_timer err1AssertTimer(io);
// Timer for ERR2 asserted
static boost::asio::steady_timer err2AssertTimer(io);
+// Timer for SMI asserted
+static boost::asio::steady_timer smiAssertTimer(io);
// GPIO Lines and Event Descriptors
static gpiod::line caterrLine;
static boost::asio::posix::stream_descriptor caterrEvent(io);
+static gpiod::line err0Line;
+static boost::asio::posix::stream_descriptor err0Event(io);
+static gpiod::line err1Line;
+static boost::asio::posix::stream_descriptor err1Event(io);
static gpiod::line err2Line;
static boost::asio::posix::stream_descriptor err2Event(io);
+static gpiod::line smiLine;
+static boost::asio::posix::stream_descriptor smiEvent(io);
+static gpiod::line cpu1ThermtripLine;
+static boost::asio::posix::stream_descriptor cpu1ThermtripEvent(io);
+static gpiod::line cpu2ThermtripLine;
+static boost::asio::posix::stream_descriptor cpu2ThermtripEvent(io);
+static gpiod::line cpu1VRHotLine;
+static boost::asio::posix::stream_descriptor cpu1VRHotEvent(io);
+static gpiod::line cpu2VRHotLine;
+static boost::asio::posix::stream_descriptor cpu1MemABCDVRHotEvent(io);
+static gpiod::line cpu1MemEFGHVRHotLine;
+static boost::asio::posix::stream_descriptor cpu1MemEFGHVRHotEvent(io);
+static gpiod::line cpu2MemABCDVRHotLine;
+static boost::asio::posix::stream_descriptor cpu2VRHotEvent(io);
+static gpiod::line cpu1MemABCDVRHotLine;
+static boost::asio::posix::stream_descriptor cpu2MemABCDVRHotEvent(io);
+static gpiod::line cpu2MemEFGHVRHotLine;
+static boost::asio::posix::stream_descriptor cpu2MemEFGHVRHotEvent(io);
//----------------------------------
// PCH_BMC_THERMTRIP function related definition
//----------------------------------
-// GPIO Lines and Event Descriptors
static gpiod::line pchThermtripLine;
static boost::asio::posix::stream_descriptor pchThermtripEvent(io);
-static void cpuERR2Log()
+static void cpuIERRLog()
{
- sd_journal_send("MESSAGE=HostError: ERR2 Timeout", "PRIORITY=%i", LOG_INFO,
+ sd_journal_send("MESSAGE=HostError: IERR", "PRIORITY=%i", LOG_INFO,
"REDFISH_MESSAGE_ID=%s", "OpenBMC.0.1.CPUError",
- "REDFISH_MESSAGE_ARGS=%s", "ERR2 Timeout", NULL);
+ "REDFISH_MESSAGE_ARGS=%s", "IERR", NULL);
+}
+
+static void cpuIERRLog(const int cpuNum)
+{
+ std::string msg = "IERR on CPU " + std::to_string(cpuNum + 1);
+
+ sd_journal_send("MESSAGE=HostError: %s", msg.c_str(), "PRIORITY=%i",
+ LOG_INFO, "REDFISH_MESSAGE_ID=%s", "OpenBMC.0.1.CPUError",
+ "REDFISH_MESSAGE_ARGS=%s", msg.c_str(), NULL);
+}
+
+static void cpuIERRLog(const int cpuNum, const std::string& type)
+{
+ std::string msg = type + " IERR on CPU " + std::to_string(cpuNum + 1);
+
+ sd_journal_send("MESSAGE=HostError: %s", msg.c_str(), "PRIORITY=%i",
+ LOG_INFO, "REDFISH_MESSAGE_ID=%s", "OpenBMC.0.1.CPUError",
+ "REDFISH_MESSAGE_ARGS=%s", msg.c_str(), NULL);
+}
+
+static void cpuERRXLog(const int errPin)
+{
+ std::string msg = "ERR" + std::to_string(errPin) + " Timeout";
+
+ sd_journal_send("MESSAGE=HostError: %s", msg.c_str(), "PRIORITY=%i",
+ LOG_INFO, "REDFISH_MESSAGE_ID=%s", "OpenBMC.0.1.CPUError",
+ "REDFISH_MESSAGE_ARGS=%s", msg.c_str(), NULL);
}
-static void cpuERR2Log(const int cpuNum)
+static void cpuERRXLog(const int errPin, const int cpuNum)
{
- std::string msg = "ERR2 Timeout on CPU " + std::to_string(cpuNum + 1);
+ std::string msg = "ERR" + std::to_string(errPin) + " Timeout on CPU " +
+ std::to_string(cpuNum + 1);
sd_journal_send("MESSAGE=HostError: %s", msg.c_str(), "PRIORITY=%i",
LOG_INFO, "REDFISH_MESSAGE_ID=%s", "OpenBMC.0.1.CPUError",
"REDFISH_MESSAGE_ARGS=%s", msg.c_str(), NULL);
}
+static void smiTimeoutLog()
+{
+ sd_journal_send("MESSAGE=HostError: SMI Timeout", "PRIORITY=%i", LOG_INFO,
+ "REDFISH_MESSAGE_ID=%s", "OpenBMC.0.1.CPUError",
+ "REDFISH_MESSAGE_ARGS=%s", "SMI Timeout", NULL);
+}
+
+static void cpuThermTripLog(const int cpuNum)
+{
+ std::string msg = "CPU " + std::to_string(cpuNum) + " thermal trip";
+
+ sd_journal_send("MESSAGE=HostError: %s", msg.c_str(), "PRIORITY=%i",
+ LOG_INFO, "REDFISH_MESSAGE_ID=%s",
+ "OpenBMC.0.1.CPUThermalTrip", "REDFISH_MESSAGE_ARGS=%d",
+ cpuNum, NULL);
+}
+
+static void cpuVRHotLog(const std::string& vr)
+{
+ std::string msg = vr + " Voltage Regulator Overheated.";
+
+ sd_journal_send("MESSAGE=HostError: %s", msg.c_str(), "PRIORITY=%i",
+ LOG_INFO, "REDFISH_MESSAGE_ID=%s",
+ "OpenBMC.0.1.VoltageRegulatorOverheated",
+ "REDFISH_MESSAGE_ARGS=%s", vr.c_str(), NULL);
+}
+
+static void ssbThermTripLog()
+{
+ sd_journal_send("MESSAGE=HostError: SSB thermal trip", "PRIORITY=%i",
+ LOG_INFO, "REDFISH_MESSAGE_ID=%s",
+ "OpenBMC.0.1.SsbThermalTrip", NULL);
+}
+
static void initializeErrorState();
static void initializeHostState()
{
@@ -126,7 +217,10 @@ static std::shared_ptr<sdbusplus::bus::match::match> startHostStateMonitor()
if (hostOff)
{
caterrAssertTimer.cancel();
+ err0AssertTimer.cancel();
+ err1AssertTimer.cancel();
err2AssertTimer.cancel();
+ smiAssertTimer.cancel();
}
});
}
@@ -246,9 +340,282 @@ static void startCrashdumpAndRecovery(bool recoverSystem)
"com.intel.crashdump.Stored", "GenerateStoredLog");
}
+static void incrementCPUErrorCount(int cpuNum)
+{
+ std::string propertyName = "ErrorCountCPU" + std::to_string(cpuNum + 1);
+
+ // Get the current count
+ conn->async_method_call(
+ [propertyName](boost::system::error_code ec,
+ const std::variant<uint8_t>& property) {
+ if (ec)
+ {
+ std::cerr << "Failed to read " << propertyName << ": "
+ << ec.message() << "\n";
+ return;
+ }
+ const uint8_t* errorCountVariant = std::get_if<uint8_t>(&property);
+ if (errorCountVariant == nullptr)
+ {
+ std::cerr << propertyName << " invalid\n";
+ return;
+ }
+ uint8_t errorCount = *errorCountVariant;
+ if (errorCount == std::numeric_limits<uint8_t>::max())
+ {
+ std::cerr << "Maximum error count reached\n";
+ return;
+ }
+ // Increment the count
+ errorCount++;
+ conn->async_method_call(
+ [propertyName](boost::system::error_code ec) {
+ if (ec)
+ {
+ std::cerr << "Failed to set " << propertyName << ": "
+ << ec.message() << "\n";
+ }
+ },
+ "xyz.openbmc_project.Settings",
+ "/xyz/openbmc_project/control/processor_error_config",
+ "org.freedesktop.DBus.Properties", "Set",
+ "xyz.openbmc_project.Control.Processor.ErrConfig", propertyName,
+ std::variant<uint8_t>{errorCount});
+ },
+ "xyz.openbmc_project.Settings",
+ "/xyz/openbmc_project/control/processor_error_config",
+ "org.freedesktop.DBus.Properties", "Get",
+ "xyz.openbmc_project.Control.Processor.ErrConfig", propertyName);
+}
+
+static bool checkIERRCPUs()
+{
+ bool cpuIERRFound = false;
+ for (int cpu = 0, addr = crashdump::minClientAddr;
+ addr <= crashdump::maxClientAddr; cpu++, addr++)
+ {
+ if (peci_Ping(addr) != PECI_CC_SUCCESS)
+ {
+ continue;
+ }
+ uint8_t cc = 0;
+ uint32_t cpuID = 0;
+ if (peci_RdPkgConfig(addr, PECI_MBX_INDEX_CPU_ID, PECI_PKG_ID_CPU_ID,
+ sizeof(uint32_t), (uint8_t*)&cpuID,
+ &cc) != PECI_CC_SUCCESS)
+ {
+ std::cerr << "Cannot get CPUID!\n";
+ continue;
+ }
+
+ crashdump::CPUModel model{};
+ bool modelFound = false;
+ for (int i = 0; i < crashdump::cpuIDMap.size(); i++)
+ {
+ if (cpuID == crashdump::cpuIDMap[i].cpuID)
+ {
+ model = crashdump::cpuIDMap[i].model;
+ modelFound = true;
+ break;
+ }
+ }
+ if (!modelFound)
+ {
+ std::cerr << "Cannot find Model for CPUID 0x" << std::hex << cpuID
+ << "\n";
+ continue;
+ }
+
+ switch (model)
+ {
+ case crashdump::CPUModel::skx_h0:
+ {
+ // First check the MCA_ERR_SRC_LOG to see if this is the CPU
+ // that caused the IERR
+ uint32_t mcaErrSrcLog = 0;
+ if (peci_RdPkgConfig(addr, 0, 5, 4, (uint8_t*)&mcaErrSrcLog,
+ &cc) != PECI_CC_SUCCESS)
+ {
+ continue;
+ }
+ // Check MSMI_INTERNAL (20) and IERR_INTERNAL (27)
+ if ((mcaErrSrcLog & (1 << 20)) || (mcaErrSrcLog & (1 << 27)))
+ {
+ // TODO: Light the CPU fault LED?
+ cpuIERRFound = true;
+ incrementCPUErrorCount(cpu);
+ // Next check if it's a CPU/VR mismatch by reading the
+ // IA32_MC4_STATUS MSR (0x411)
+ uint64_t mc4Status = 0;
+ if (peci_RdIAMSR(addr, 0, 0x411, &mc4Status, &cc) !=
+ PECI_CC_SUCCESS)
+ {
+ continue;
+ }
+ // Check MSEC bits 31:24 for
+ // MCA_SVID_VCCIN_VR_ICC_MAX_FAILURE (0x40),
+ // MCA_SVID_VCCIN_VR_VOUT_FAILURE (0x42), or
+ // MCA_SVID_CPU_VR_CAPABILITY_ERROR (0x43)
+ if ((mc4Status & (0x40 << 24)) ||
+ (mc4Status & (0x42 << 24)) ||
+ (mc4Status & (0x43 << 24)))
+ {
+ cpuIERRLog(cpu, "CPU/VR Mismatch");
+ continue;
+ }
+
+ // Next check if it's a Core FIVR fault by looking for a
+ // non-zero value of CORE_FIVR_ERR_LOG (B(1) D30 F2 offset
+ // 80h)
+ uint32_t coreFIVRErrLog = 0;
+ if (peci_RdPCIConfigLocal(
+ addr, 1, 30, 2, 0x80, sizeof(uint32_t),
+ (uint8_t*)&coreFIVRErrLog, &cc) != PECI_CC_SUCCESS)
+ {
+ continue;
+ }
+ if (coreFIVRErrLog)
+ {
+ cpuIERRLog(cpu, "Core FIVR Fault");
+ continue;
+ }
+
+ // Next check if it's an Uncore FIVR fault by looking for a
+ // non-zero value of UNCORE_FIVR_ERR_LOG (B(1) D30 F2 offset
+ // 84h)
+ uint32_t uncoreFIVRErrLog = 0;
+ if (peci_RdPCIConfigLocal(addr, 1, 30, 2, 0x84,
+ sizeof(uint32_t),
+ (uint8_t*)&uncoreFIVRErrLog,
+ &cc) != PECI_CC_SUCCESS)
+ {
+ continue;
+ }
+ if (uncoreFIVRErrLog)
+ {
+ cpuIERRLog(cpu, "Uncore FIVR Fault");
+ continue;
+ }
+
+ // Last if CORE_FIVR_ERR_LOG and UNCORE_FIVR_ERR_LOG are
+ // both zero, but MSEC bits 31:24 have either
+ // MCA_FIVR_CATAS_OVERVOL_FAULT (0x51) or
+ // MCA_FIVR_CATAS_OVERCUR_FAULT (0x52), then log it as an
+ // uncore FIVR fault
+ if (!coreFIVRErrLog && !uncoreFIVRErrLog &&
+ ((mc4Status & (0x51 << 24)) ||
+ (mc4Status & (0x52 << 24))))
+ {
+ cpuIERRLog(cpu, "Uncore FIVR Fault");
+ continue;
+ }
+ cpuIERRLog(cpu);
+ }
+ break;
+ }
+ case crashdump::CPUModel::icx_a0:
+ case crashdump::CPUModel::icx_b0:
+ {
+ // First check the MCA_ERR_SRC_LOG to see if this is the CPU
+ // that caused the IERR
+ uint32_t mcaErrSrcLog = 0;
+ if (peci_RdPkgConfig(addr, 0, 5, 4, (uint8_t*)&mcaErrSrcLog,
+ &cc) != PECI_CC_SUCCESS)
+ {
+ continue;
+ }
+ // Check MSMI_INTERNAL (20) and IERR_INTERNAL (27)
+ if ((mcaErrSrcLog & (1 << 20)) || (mcaErrSrcLog & (1 << 27)))
+ {
+ // TODO: Light the CPU fault LED?
+ cpuIERRFound = true;
+ incrementCPUErrorCount(cpu);
+ // Next check if it's a CPU/VR mismatch by reading the
+ // IA32_MC4_STATUS MSR (0x411)
+ uint64_t mc4Status = 0;
+ if (peci_RdIAMSR(addr, 0, 0x411, &mc4Status, &cc) !=
+ PECI_CC_SUCCESS)
+ {
+ continue;
+ }
+ // TODO: Update MSEC/MSCOD_31_24 check
+ // Check MSEC bits 31:24 for
+ // MCA_SVID_VCCIN_VR_ICC_MAX_FAILURE (0x40),
+ // MCA_SVID_VCCIN_VR_VOUT_FAILURE (0x42), or
+ // MCA_SVID_CPU_VR_CAPABILITY_ERROR (0x43)
+ if ((mc4Status & (0x40 << 24)) ||
+ (mc4Status & (0x42 << 24)) ||
+ (mc4Status & (0x43 << 24)))
+ {
+ cpuIERRLog(cpu, "CPU/VR Mismatch");
+ continue;
+ }
+
+ // Next check if it's a Core FIVR fault by looking for a
+ // non-zero value of CORE_FIVR_ERR_LOG (B(31) D30 F2 offsets
+ // C0h and C4h) (Note: Bus 31 is accessed on PECI as bus 14)
+ uint32_t coreFIVRErrLog0 = 0;
+ uint32_t coreFIVRErrLog1 = 0;
+ if (peci_RdEndPointConfigPciLocal(
+ addr, 0, 14, 30, 2, 0xC0, sizeof(uint32_t),
+ (uint8_t*)&coreFIVRErrLog0, &cc) != PECI_CC_SUCCESS)
+ {
+ continue;
+ }
+ if (peci_RdEndPointConfigPciLocal(
+ addr, 0, 14, 30, 2, 0xC4, sizeof(uint32_t),
+ (uint8_t*)&coreFIVRErrLog1, &cc) != PECI_CC_SUCCESS)
+ {
+ continue;
+ }
+ if (coreFIVRErrLog0 || coreFIVRErrLog1)
+ {
+ cpuIERRLog(cpu, "Core FIVR Fault");
+ continue;
+ }
+
+ // Next check if it's an Uncore FIVR fault by looking for a
+ // non-zero value of UNCORE_FIVR_ERR_LOG (B(31) D30 F2
+ // offset 84h) (Note: Bus 31 is accessed on PECI as bus 14)
+ uint32_t uncoreFIVRErrLog = 0;
+ if (peci_RdEndPointConfigPciLocal(
+ addr, 0, 14, 30, 2, 0x84, sizeof(uint32_t),
+ (uint8_t*)&uncoreFIVRErrLog,
+ &cc) != PECI_CC_SUCCESS)
+ {
+ continue;
+ }
+ if (uncoreFIVRErrLog)
+ {
+ cpuIERRLog(cpu, "Uncore FIVR Fault");
+ continue;
+ }
+
+ // TODO: Update MSEC/MSCOD_31_24 check
+ // Last if CORE_FIVR_ERR_LOG and UNCORE_FIVR_ERR_LOG are
+ // both zero, but MSEC bits 31:24 have either
+ // MCA_FIVR_CATAS_OVERVOL_FAULT (0x51) or
+ // MCA_FIVR_CATAS_OVERCUR_FAULT (0x52), then log it as an
+ // uncore FIVR fault
+ if (!coreFIVRErrLog0 && !coreFIVRErrLog1 &&
+ !uncoreFIVRErrLog &&
+ ((mc4Status & (0x51 << 24)) ||
+ (mc4Status & (0x52 << 24))))
+ {
+ cpuIERRLog(cpu, "Uncore FIVR Fault");
+ continue;
+ }
+ cpuIERRLog(cpu);
+ }
+ break;
+ }
+ }
+ }
+ return cpuIERRFound;
+}
+
static void caterrAssertHandler()
{
- std::cout << "CPU CATERR detected, starting timer\n";
caterrAssertTimer.expires_after(std::chrono::milliseconds(caterrTimeoutMs));
caterrAssertTimer.async_wait([](const boost::system::error_code ec) {
if (ec)
@@ -260,10 +627,14 @@ static void caterrAssertHandler()
std::cerr << "caterr timeout async_wait failed: "
<< ec.message() << "\n";
}
- std::cout << "CATERR assert timer canceled\n";
return;
}
- std::cout << "CATERR asset timer completed\n";
+ std::cerr << "CATERR asserted for " << std::to_string(caterrTimeoutMs)
+ << " ms\n";
+ if (!checkIERRCPUs())
+ {
+ cpuIERRLog();
+ }
conn->async_method_call(
[](boost::system::error_code ec,
const std::variant<bool>& property) {
@@ -314,6 +685,213 @@ static void caterrHandler()
caterrHandler();
});
}
+
+static void cpu1ThermtripHandler()
+{
+ if (!hostOff)
+ {
+ gpiod::line_event gpioLineEvent = cpu1ThermtripLine.event_read();
+
+ bool cpu1Thermtrip =
+ gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
+ if (cpu1Thermtrip)
+ {
+ cpuThermTripLog(1);
+ }
+ }
+ cpu1ThermtripEvent.async_wait(
+ boost::asio::posix::stream_descriptor::wait_read,
+ [](const boost::system::error_code ec) {
+ if (ec)
+ {
+ std::cerr << "CPU 1 Thermtrip handler error: " << ec.message()
+ << "\n";
+ return;
+ }
+ cpu1ThermtripHandler();
+ });
+}
+
+static void cpu2ThermtripHandler()
+{
+ if (!hostOff)
+ {
+ gpiod::line_event gpioLineEvent = cpu2ThermtripLine.event_read();
+
+ bool cpu2Thermtrip =
+ gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
+ if (cpu2Thermtrip)
+ {
+ cpuThermTripLog(2);
+ }
+ }
+ cpu2ThermtripEvent.async_wait(
+ boost::asio::posix::stream_descriptor::wait_read,
+ [](const boost::system::error_code ec) {
+ if (ec)
+ {
+ std::cerr << "CPU 2 Thermtrip handler error: " << ec.message()
+ << "\n";
+ return;
+ }
+ cpu2ThermtripHandler();
+ });
+}
+
+static void cpu1VRHotHandler()
+{
+ if (!hostOff)
+ {
+ gpiod::line_event gpioLineEvent = cpu1VRHotLine.event_read();
+
+ bool cpu1VRHot =
+ gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
+ if (cpu1VRHot)
+ {
+ cpuVRHotLog("CPU 1");
+ }
+ }
+ cpu1VRHotEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
+ [](const boost::system::error_code ec) {
+ if (ec)
+ {
+ std::cerr << "CPU 1 VRHot handler error: "
+ << ec.message() << "\n";
+ return;
+ }
+ cpu1VRHotHandler();
+ });
+}
+
+static void cpu1MemABCDVRHotHandler()
+{
+ if (!hostOff)
+ {
+ gpiod::line_event gpioLineEvent = cpu1MemABCDVRHotLine.event_read();
+
+ bool cpu1MemABCDVRHot =
+ gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
+ if (cpu1MemABCDVRHot)
+ {
+ cpuVRHotLog("CPU 1 Memory ABCD");
+ }
+ }
+ cpu1MemABCDVRHotEvent.async_wait(
+ boost::asio::posix::stream_descriptor::wait_read,
+ [](const boost::system::error_code ec) {
+ if (ec)
+ {
+ std::cerr << "CPU 1 Memory ABCD VRHot handler error: "
+ << ec.message() << "\n";
+ return;
+ }
+ cpu1MemABCDVRHotHandler();
+ });
+}
+
+static void cpu1MemEFGHVRHotHandler()
+{
+ if (!hostOff)
+ {
+ gpiod::line_event gpioLineEvent = cpu1MemEFGHVRHotLine.event_read();
+
+ bool cpu1MemEFGHVRHot =
+ gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
+ if (cpu1MemEFGHVRHot)
+ {
+ cpuVRHotLog("CPU 1 Memory EFGH");
+ }
+ }
+ cpu1MemEFGHVRHotEvent.async_wait(
+ boost::asio::posix::stream_descriptor::wait_read,
+ [](const boost::system::error_code ec) {
+ if (ec)
+ {
+ std::cerr << "CPU 1 Memory EFGH VRHot handler error: "
+ << ec.message() << "\n";
+ return;
+ }
+ cpu1MemEFGHVRHotHandler();
+ });
+}
+
+static void cpu2VRHotHandler()
+{
+ if (!hostOff)
+ {
+ gpiod::line_event gpioLineEvent = cpu2VRHotLine.event_read();
+
+ bool cpu2VRHot =
+ gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
+ if (cpu2VRHot)
+ {
+ cpuVRHotLog("CPU 2");
+ }
+ }
+ cpu2VRHotEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
+ [](const boost::system::error_code ec) {
+ if (ec)
+ {
+ std::cerr << "CPU 2 VRHot handler error: "
+ << ec.message() << "\n";
+ return;
+ }
+ cpu2VRHotHandler();
+ });
+}
+
+static void cpu2MemABCDVRHotHandler()
+{
+ if (!hostOff)
+ {
+ gpiod::line_event gpioLineEvent = cpu2MemABCDVRHotLine.event_read();
+
+ bool cpu2MemABCDVRHot =
+ gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
+ if (cpu2MemABCDVRHot)
+ {
+ cpuVRHotLog("CPU 2 Memory ABCD");
+ }
+ }
+ cpu2MemABCDVRHotEvent.async_wait(
+ boost::asio::posix::stream_descriptor::wait_read,
+ [](const boost::system::error_code ec) {
+ if (ec)
+ {
+ std::cerr << "CPU 2 Memory ABCD VRHot handler error: "
+ << ec.message() << "\n";
+ return;
+ }
+ cpu2MemABCDVRHotHandler();
+ });
+}
+
+static void cpu2MemEFGHVRHotHandler()
+{
+ if (!hostOff)
+ {
+ gpiod::line_event gpioLineEvent = cpu2MemEFGHVRHotLine.event_read();
+
+ bool cpu2MemEFGHVRHot =
+ gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
+ if (cpu2MemEFGHVRHot)
+ {
+ cpuVRHotLog("CPU 2 Memory EFGH");
+ }
+ }
+ cpu2MemEFGHVRHotEvent.async_wait(
+ boost::asio::posix::stream_descriptor::wait_read,
+ [](const boost::system::error_code ec) {
+ if (ec)
+ {
+ std::cerr << "CPU 2 Memory EFGH VRHot handler error: "
+ << ec.message() << "\n";
+ return;
+ }
+ cpu2MemEFGHVRHotHandler();
+ });
+}
+
static void pchThermtripHandler()
{
if (!hostOff)
@@ -324,11 +902,7 @@ static void pchThermtripHandler()
gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
if (pchThermtrip)
{
- std::cout << "PCH Thermal trip detected \n";
- // log to redfish, call API
- sd_journal_send("MESSAGE=SsbThermalTrip: SSB Thermal trip",
- "PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
- "OpenBMC.0.1.SsbThermalTrip", NULL);
+ ssbThermTripLog();
}
}
pchThermtripEvent.async_wait(
@@ -344,9 +918,10 @@ static void pchThermtripHandler()
});
}
-static std::bitset<crashdump::maxCPUs> checkERR2CPUs()
+static std::bitset<crashdump::maxCPUs> checkERRPinCPUs(const int errPin)
{
- std::bitset<crashdump::maxCPUs> err2CPUs = 0;
+ int errPinSts = (1 << errPin);
+ std::bitset<crashdump::maxCPUs> errPinCPUs = 0;
for (int cpu = 0, addr = crashdump::minClientAddr;
addr <= crashdump::maxClientAddr; cpu++, addr++)
{
@@ -380,19 +955,18 @@ static std::bitset<crashdump::maxCPUs> checkERR2CPUs()
continue;
}
- static constexpr int err2Sts = (1 << 2);
switch (model)
{
case crashdump::CPUModel::skx_h0:
{
// Check the ERRPINSTS to see if this is the CPU that caused
- // the ERR2 (B(0) D8 F0 offset 210h)
+ // the ERRx (B(0) D8 F0 offset 210h)
uint32_t errpinsts = 0;
if (peci_RdPCIConfigLocal(
addr, 0, 8, 0, 0x210, sizeof(uint32_t),
(uint8_t*)&errpinsts, &cc) == PECI_CC_SUCCESS)
{
- err2CPUs[cpu] = (errpinsts & err2Sts) != 0;
+ errPinCPUs[cpu] = (errpinsts & errPinSts) != 0;
}
break;
}
@@ -400,30 +974,32 @@ static std::bitset<crashdump::maxCPUs> checkERR2CPUs()
case crashdump::CPUModel::icx_b0:
{
// Check the ERRPINSTS to see if this is the CPU that caused
- // the ERR2 (B(30) D0 F3 offset 274h) (Note: Bus 30 is
+ // the ERRx (B(30) D0 F3 offset 274h) (Note: Bus 30 is
// accessed on PECI as bus 13)
uint32_t errpinsts = 0;
if (peci_RdEndPointConfigPciLocal(
addr, 0, 13, 0, 3, 0x274, sizeof(uint32_t),
(uint8_t*)&errpinsts, &cc) == PECI_CC_SUCCESS)
{
- err2CPUs[cpu] = (errpinsts & err2Sts) != 0;
+ errPinCPUs[cpu] = (errpinsts & errPinSts) != 0;
}
break;
}
}
}
}
- return err2CPUs;
+ return errPinCPUs;
}
-static void err2AssertHandler()
+static void errXAssertHandler(const int errPin,
+ boost::asio::steady_timer& errXAssertTimer)
{
- // ERR2 status is not guaranteed through the timeout, so save which
- // CPUs have asserted ERR2 now
- std::bitset<crashdump::maxCPUs> err2CPUs = checkERR2CPUs();
- err2AssertTimer.expires_after(std::chrono::milliseconds(err2TimeoutMs));
- err2AssertTimer.async_wait([err2CPUs](const boost::system::error_code ec) {
+ // ERRx status is not guaranteed through the timeout, so save which
+ // CPUs have it asserted
+ std::bitset<crashdump::maxCPUs> errPinCPUs = checkERRPinCPUs(errPin);
+ errXAssertTimer.expires_after(std::chrono::milliseconds(errTimeoutMs));
+ errXAssertTimer.async_wait([errPin, errPinCPUs](
+ const boost::system::error_code ec) {
if (ec)
{
// operation_aborted is expected if timer is canceled before
@@ -435,21 +1011,114 @@ static void err2AssertHandler()
}
return;
}
- std::cerr << "ERR2 asserted for " << std::to_string(err2TimeoutMs)
- << " ms\n";
- if (err2CPUs.count())
+ std::cerr << "ERR" << std::to_string(errPin) << " asserted for "
+ << std::to_string(errTimeoutMs) << " ms\n";
+ if (errPinCPUs.count())
{
- for (int i = 0; i < err2CPUs.size(); i++)
+ for (int i = 0; i < errPinCPUs.size(); i++)
{
- if (err2CPUs[i])
+ if (errPinCPUs[i])
{
- cpuERR2Log(i);
+ cpuERRXLog(errPin, i);
}
}
}
else
{
- cpuERR2Log();
+ cpuERRXLog(errPin);
+ }
+ });
+}
+
+static void err0AssertHandler()
+{
+ // Handle the standard ERR0 detection and logging
+ const static constexpr int err0 = 0;
+ errXAssertHandler(err0, err0AssertTimer);
+}
+
+static void err0Handler()
+{
+ if (!hostOff)
+ {
+ gpiod::line_event gpioLineEvent = err0Line.event_read();
+
+ bool err0 = gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
+ if (err0)
+ {
+ err0AssertHandler();
+ }
+ else
+ {
+ err0AssertTimer.cancel();
+ }
+ }
+ err0Event.async_wait(boost::asio::posix::stream_descriptor::wait_read,
+ [](const boost::system::error_code ec) {
+ if (ec)
+ {
+ std::cerr
+ << "err0 handler error: " << ec.message()
+ << "\n";
+ return;
+ }
+ err0Handler();
+ });
+}
+
+static void err1AssertHandler()
+{
+ // Handle the standard ERR1 detection and logging
+ const static constexpr int err1 = 1;
+ errXAssertHandler(err1, err1AssertTimer);
+}
+
+static void err1Handler()
+{
+ if (!hostOff)
+ {
+ gpiod::line_event gpioLineEvent = err1Line.event_read();
+
+ bool err1 = gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
+ if (err1)
+ {
+ err1AssertHandler();
+ }
+ else
+ {
+ err1AssertTimer.cancel();
+ }
+ }
+ err1Event.async_wait(boost::asio::posix::stream_descriptor::wait_read,
+ [](const boost::system::error_code ec) {
+ if (ec)
+ {
+ std::cerr
+ << "err1 handler error: " << ec.message()
+ << "\n";
+ return;
+ }
+ err1Handler();
+ });
+}
+
+static void err2AssertHandler()
+{
+ // Handle the standard ERR2 detection and logging
+ const static constexpr int err2 = 2;
+ errXAssertHandler(err2, err2AssertTimer);
+ // Also handle reset for ERR2
+ err2AssertTimer.async_wait([](const boost::system::error_code ec) {
+ if (ec)
+ {
+ // operation_aborted is expected if timer is canceled before
+ // completion.
+ if (ec != boost::asio::error::operation_aborted)
+ {
+ std::cerr << "err2 timeout async_wait failed: " << ec.message()
+ << "\n";
+ }
+ return;
}
conn->async_method_call(
[](boost::system::error_code ec,
@@ -502,6 +1171,75 @@ static void err2Handler()
});
}
+static void smiAssertHandler()
+{
+ smiAssertTimer.expires_after(std::chrono::milliseconds(smiTimeoutMs));
+ smiAssertTimer.async_wait([](const boost::system::error_code ec) {
+ if (ec)
+ {
+ // operation_aborted is expected if timer is canceled before
+ // completion.
+ if (ec != boost::asio::error::operation_aborted)
+ {
+ std::cerr << "smi timeout async_wait failed: " << ec.message()
+ << "\n";
+ }
+ return;
+ }
+ std::cerr << "SMI asserted for " << std::to_string(smiTimeoutMs)
+ << " ms\n";
+ smiTimeoutLog();
+ conn->async_method_call(
+ [](boost::system::error_code ec,
+ const std::variant<bool>& property) {
+ if (ec)
+ {
+ return;
+ }
+ const bool* reset = std::get_if<bool>(&property);
+ if (reset == nullptr)
+ {
+ std::cerr << "Unable to read reset on SMI value\n";
+ return;
+ }
+ startCrashdumpAndRecovery(*reset);
+ },
+ "xyz.openbmc_project.Settings",
+ "/xyz/openbmc_project/control/bmc_reset_disables",
+ "org.freedesktop.DBus.Properties", "Get",
+ "xyz.openbmc_project.Control.ResetDisables", "ResetOnSMI");
+ });
+}
+
+static void smiHandler()
+{
+ if (!hostOff)
+ {
+ gpiod::line_event gpioLineEvent = smiLine.event_read();
+
+ bool smi = gpioLineEvent.event_type == gpiod::line_event::FALLING_EDGE;
+ if (smi)
+ {
+ smiAssertHandler();
+ }
+ else
+ {
+ smiAssertTimer.cancel();
+ }
+ }
+ smiEvent.async_wait(boost::asio::posix::stream_descriptor::wait_read,
+ [](const boost::system::error_code ec) {
+ if (ec)
+ {
+ std::cerr
+ << "smi handler error: " << ec.message()
+ << "\n";
+ return;
+ }
+ smiHandler();
+ });
+}
+
static void initializeErrorState()
{
// Handle CPU_CATERR if it's asserted now
@@ -510,11 +1248,35 @@ static void initializeErrorState()
caterrAssertHandler();
}
+ // Handle CPU_ERR0 if it's asserted now
+ if (err0Line.get_value() == 0)
+ {
+ err0AssertHandler();
+ }
+
+ // Handle CPU_ERR1 if it's asserted now
+ if (err1Line.get_value() == 0)
+ {
+ err1AssertHandler();
+ }
+
// Handle CPU_ERR2 if it's asserted now
if (err2Line.get_value() == 0)
{
err2AssertHandler();
}
+
+ // Handle SMI if it's asserted now
+ if (smiLine.get_value() == 0)
+ {
+ smiAssertHandler();
+ }
+
+ // Handle PCH_BMC_THERMTRIP if it's asserted now
+ if (pchThermtripLine.get_value() == 0)
+ {
+ ssbThermTripLog();
+ }
}
} // namespace host_error_monitor
@@ -545,6 +1307,22 @@ int main(int argc, char* argv[])
return -1;
}
+ // Request CPU_ERR0 GPIO events
+ if (!host_error_monitor::requestGPIOEvents(
+ "CPU_ERR0", host_error_monitor::err0Handler,
+ host_error_monitor::err0Line, host_error_monitor::err0Event))
+ {
+ return -1;
+ }
+
+ // Request CPU_ERR1 GPIO events
+ if (!host_error_monitor::requestGPIOEvents(
+ "CPU_ERR1", host_error_monitor::err1Handler,
+ host_error_monitor::err1Line, host_error_monitor::err1Event))
+ {
+ return -1;
+ }
+
// Request CPU_ERR2 GPIO events
if (!host_error_monitor::requestGPIOEvents(
"CPU_ERR2", host_error_monitor::err2Handler,
@@ -553,6 +1331,86 @@ int main(int argc, char* argv[])
return -1;
}
+ // Request SMI GPIO events
+ if (!host_error_monitor::requestGPIOEvents(
+ "SMI", host_error_monitor::smiHandler, host_error_monitor::smiLine,
+ host_error_monitor::smiEvent))
+ {
+ return -1;
+ }
+
+ // Request CPU1_THERMTRIP GPIO events
+ if (!host_error_monitor::requestGPIOEvents(
+ "CPU1_THERMTRIP", host_error_monitor::cpu1ThermtripHandler,
+ host_error_monitor::cpu1ThermtripLine,
+ host_error_monitor::cpu1ThermtripEvent))
+ {
+ return -1;
+ }
+
+ // Request CPU2_THERMTRIP GPIO events
+ if (!host_error_monitor::requestGPIOEvents(
+ "CPU2_THERMTRIP", host_error_monitor::cpu2ThermtripHandler,
+ host_error_monitor::cpu2ThermtripLine,
+ host_error_monitor::cpu2ThermtripEvent))
+ {
+ return -1;
+ }
+
+ // Request CPU1_VRHOT GPIO events
+ if (!host_error_monitor::requestGPIOEvents(
+ "CPU1_VRHOT", host_error_monitor::cpu1VRHotHandler,
+ host_error_monitor::cpu1VRHotLine,
+ host_error_monitor::cpu1VRHotEvent))
+ {
+ return -1;
+ }
+
+ // Request CPU1_MEM_ABCD_VRHOT GPIO events
+ if (!host_error_monitor::requestGPIOEvents(
+ "CPU1_MEM_ABCD_VRHOT", host_error_monitor::cpu1MemABCDVRHotHandler,
+ host_error_monitor::cpu1MemABCDVRHotLine,
+ host_error_monitor::cpu1MemABCDVRHotEvent))
+ {
+ return -1;
+ }
+
+ // Request CPU1_MEM_EFGH_VRHOT GPIO events
+ if (!host_error_monitor::requestGPIOEvents(
+ "CPU1_MEM_EFGH_VRHOT", host_error_monitor::cpu1MemEFGHVRHotHandler,
+ host_error_monitor::cpu1MemEFGHVRHotLine,
+ host_error_monitor::cpu1MemEFGHVRHotEvent))
+ {
+ return -1;
+ }
+
+ // Request CPU2_VRHOT GPIO events
+ if (!host_error_monitor::requestGPIOEvents(
+ "CPU2_VRHOT", host_error_monitor::cpu2VRHotHandler,
+ host_error_monitor::cpu2VRHotLine,
+ host_error_monitor::cpu2VRHotEvent))
+ {
+ return -1;
+ }
+
+ // Request CPU2_MEM_ABCD_VRHOT GPIO events
+ if (!host_error_monitor::requestGPIOEvents(
+ "CPU2_MEM_ABCD_VRHOT", host_error_monitor::cpu2MemABCDVRHotHandler,
+ host_error_monitor::cpu2MemABCDVRHotLine,
+ host_error_monitor::cpu2MemABCDVRHotEvent))
+ {
+ return -1;
+ }
+
+ // Request CPU2_MEM_EFGH_VRHOT GPIO events
+ if (!host_error_monitor::requestGPIOEvents(
+ "CPU2_MEM_EFGH_VRHOT", host_error_monitor::cpu2MemEFGHVRHotHandler,
+ host_error_monitor::cpu2MemEFGHVRHotLine,
+ host_error_monitor::cpu2MemEFGHVRHotEvent))
+ {
+ return -1;
+ }
+
// Request PCH_BMC_THERMTRIP GPIO events
if (!host_error_monitor::requestGPIOEvents(
"PCH_BMC_THERMTRIP", host_error_monitor::pchThermtripHandler,
diff --git a/libpeci/.clang-format b/libpeci/.clang-format
new file mode 100644
index 0000000..ae9ad39
--- /dev/null
+++ b/libpeci/.clang-format
@@ -0,0 +1,98 @@
+---
+Language: Cpp
+# BasedOnStyle: LLVM
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlinesLeft: false
+AlignOperands: true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: true
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+ AfterClass: true
+ AfterControlStatement: true
+ AfterEnum: true
+ AfterFunction: true
+ AfterNamespace: true
+ AfterObjCDeclaration: true
+ AfterStruct: true
+ AfterUnion: true
+ BeforeCatch: true
+ BeforeElse: true
+ IndentBraces: false
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Custom
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: AfterColon
+ColumnLimit: 80
+CommentPragmas: '^ IWYU pragma:'
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: false
+PointerAlignment: Left
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
+IncludeBlocks: Regroup
+IncludeCategories:
+ - Regex: '^[<"](gtest|gmock)'
+ Priority: 5
+ - Regex: '^"config.h"'
+ Priority: -1
+ - Regex: '^".*\.hpp"'
+ Priority: 1
+ - Regex: '^<.*\.h>'
+ Priority: 2
+ - Regex: '^<.*'
+ Priority: 3
+ - Regex: '.*'
+ Priority: 4
+IndentCaseLabels: true
+IndentWidth: 4
+IndentWrappedFunctionNames: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBlockIndentWidth: 2
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+ReflowComments: true
+SortIncludes: true
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Cpp11
+TabWidth: 4
+UseTab: Never
+...
diff --git a/libpeci/CMakeLists.txt b/libpeci/CMakeLists.txt
new file mode 100644
index 0000000..227ed1d
--- /dev/null
+++ b/libpeci/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.6)
+project(libpeci)
+
+add_library(peci SHARED peci.c)
+
+set_property(TARGET peci PROPERTY C_STANDARD 99)
+target_include_directories(peci PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+set_target_properties(peci PROPERTIES VERSION "1.0" SOVERSION "1")
+
+install(TARGETS peci DESTINATION lib)
+install(FILES peci.h DESTINATION include)
+
+add_executable(peci_cmds peci_cmds.c)
+add_dependencies(peci_cmds peci)
+target_link_libraries(peci_cmds peci)
+
+install(TARGETS peci_cmds
+ RUNTIME DESTINATION bin
+ LIBRARY DESTINATION lib
+ ARCHIVE DESTINATION lib/static)
diff --git a/libpeci/peci.c b/libpeci/peci.c
new file mode 100644
index 0000000..400e2b5
--- /dev/null
+++ b/libpeci/peci.c
@@ -0,0 +1,1081 @@
+/*
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#include <fcntl.h>
+#include <peci.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+EPECIStatus peci_GetDIB_seq(uint8_t target, uint64_t* dib, int peci_fd);
+
+/*-------------------------------------------------------------------------
+ * This function unlocks the peci interface
+ *------------------------------------------------------------------------*/
+void peci_Unlock(int peci_fd)
+{
+ if (close(peci_fd) != 0)
+ {
+ syslog(LOG_ERR, "PECI device failed to unlock.\n");
+ }
+}
+
+#define PECI_DEVICE "/dev/peci-0"
+/*-------------------------------------------------------------------------
+ * This function attempts to lock the peci interface with the specified
+ * timeout and returns a file descriptor if successful.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_Lock(int* peci_fd, uint32_t timeout_ms)
+{
+ struct timespec sRequest;
+ sRequest.tv_sec = 0;
+ sRequest.tv_nsec = PECI_TIMEOUT_RESOLUTION_MS * 1000 * 1000;
+ uint32_t timeout_count = 0;
+
+ if (NULL == peci_fd)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ // Open the PECI driver with the specified timeout
+ *peci_fd = open(PECI_DEVICE, O_RDWR | O_CLOEXEC);
+ switch (timeout_ms)
+ {
+ case PECI_NO_WAIT:
+ break;
+ case PECI_WAIT_FOREVER:
+ while (-1 == *peci_fd)
+ {
+ nanosleep(&sRequest, NULL);
+ *peci_fd = open(PECI_DEVICE, O_RDWR | O_CLOEXEC);
+ }
+ default:
+ while (-1 == *peci_fd && timeout_count < timeout_ms)
+ {
+ nanosleep(&sRequest, NULL);
+ timeout_count += PECI_TIMEOUT_RESOLUTION_MS;
+ *peci_fd = open(PECI_DEVICE, O_RDWR | O_CLOEXEC);
+ }
+ }
+ if (-1 == *peci_fd)
+ {
+ syslog(LOG_ERR, "%s(%d): >>> PECI Device Busy <<< \n", __FUNCTION__,
+ __LINE__);
+ return PECI_CC_DRIVER_ERR;
+ }
+ return PECI_CC_SUCCESS;
+}
+
+/*-------------------------------------------------------------------------
+ * This function closes the peci interface
+ *------------------------------------------------------------------------*/
+static void peci_Close(int peci_fd)
+{
+ peci_Unlock(peci_fd);
+}
+
+/*-------------------------------------------------------------------------
+ * This function opens the peci interface and returns a file descriptor
+ *------------------------------------------------------------------------*/
+static EPECIStatus peci_Open(int* peci_fd)
+{
+ if (NULL == peci_fd)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ // Lock the PECI driver with a default timeout
+ return peci_Lock(peci_fd, PECI_TIMEOUT_MS);
+}
+
+/*-------------------------------------------------------------------------
+ * This function issues peci commands to peci driver
+ *------------------------------------------------------------------------*/
+static EPECIStatus HW_peci_issue_cmd(unsigned int cmd, char* cmdPtr,
+ int peci_fd)
+{
+ if (cmdPtr == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ if (ioctl(peci_fd, cmd, cmdPtr) != 0)
+ {
+ return PECI_CC_DRIVER_ERR;
+ }
+
+ return PECI_CC_SUCCESS;
+}
+
+/*-------------------------------------------------------------------------
+ * Find the specified PCI bus number value
+ *------------------------------------------------------------------------*/
+EPECIStatus FindBusNumber(uint8_t u8Bus, uint8_t u8Cpu, uint8_t* pu8BusValue)
+{
+ uint8_t u8CpuBus0[] = {
+ PECI_PCI_BUS0_CPU0,
+ PECI_PCI_BUS0_CPU1,
+ };
+ uint8_t u8Bus0 = 0;
+ uint8_t u8Offset = 0;
+ EPECIStatus ret;
+ uint8_t u8Reg[4];
+ uint8_t cc = 0;
+
+ // First check for valid inputs
+ // Check cpu and bus numbers, only support buses [5:0]
+ if ((u8Bus > 5) || (u8Cpu >= (sizeof(u8CpuBus0) / sizeof(uint8_t))) ||
+ (pu8BusValue == NULL))
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ // Get the Bus 0 value for the requested CPU
+ u8Bus0 = u8CpuBus0[u8Cpu];
+
+ // Next check that the bus numbers are valid
+ // CPUBUSNO_VALID register - Above registers valid? - B(0) D5 F0 offset
+ // D4h
+ ret = peci_RdPCIConfig(u8Cpu, u8Bus0, PECI_PCI_CPUBUSNO_DEV,
+ PECI_PCI_CPUBUSNO_FUNC, PECI_PCI_CPUBUSNO_VALID,
+ u8Reg, &cc);
+ if (ret != PECI_CC_SUCCESS)
+ {
+ return ret;
+ }
+ // BIOS will set bit 31 of CPUBUSNO_VALID when the bus numbers are valid
+ if ((u8Reg[3] & 0x80) == 0)
+ {
+ return PECI_CC_HW_ERR;
+ }
+
+ // Bus numbers are valid so read the correct offset for the requested
+ // bus CPUBUSNO register - CPU Internal Bus Numbers [3:0] - B(0) D5 F0
+ // offset CCh CPUBUSNO_1 register - CPU Internal Bus Numbers [5:4] -
+ // B(0) D5 F0 offset D0h
+ u8Offset = u8Bus <= 3 ? PECI_PCI_CPUBUSNO : PECI_PCI_CPUBUSNO_1;
+ ret = peci_RdPCIConfig(u8Cpu, u8Bus0, PECI_PCI_CPUBUSNO_DEV,
+ PECI_PCI_CPUBUSNO_FUNC, u8Offset, u8Reg, &cc);
+ if (ret != PECI_CC_SUCCESS)
+ {
+ return ret;
+ }
+
+ // Now return the bus value for the requested bus
+ *pu8BusValue = u8Reg[u8Bus % 4];
+
+ // Unused bus numbers are set to zero which is only valid for bus 0
+ // so, return an error for any other bus set to zero
+ if (*pu8BusValue == 0 && u8Bus != 0)
+ {
+ return PECI_CC_CPU_NOT_PRESENT;
+ }
+
+ return PECI_CC_SUCCESS;
+}
+
+/*-------------------------------------------------------------------------
+ * This function checks the CPU PECI interface
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_Ping(uint8_t target)
+{
+ int peci_fd = -1;
+ EPECIStatus ret;
+
+ if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
+ {
+ return PECI_CC_DRIVER_ERR;
+ }
+ ret = peci_Ping_seq(target, peci_fd);
+
+ peci_Close(peci_fd);
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function allows sequential Ping with the provided
+ * peci file descriptor.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_Ping_seq(uint8_t target, int peci_fd)
+{
+ EPECIStatus ret;
+ struct peci_ping_msg cmd;
+
+ cmd.addr = target;
+ ret = HW_peci_issue_cmd(PECI_IOC_PING, (char*)&cmd, peci_fd);
+
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function gets PECI device information
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_GetDIB(uint8_t target, uint64_t* dib)
+{
+ int peci_fd = -1;
+ EPECIStatus ret;
+
+ if (dib == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
+ {
+ return PECI_CC_DRIVER_ERR;
+ }
+ ret = peci_GetDIB_seq(target, dib, peci_fd);
+
+ peci_Close(peci_fd);
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function allows sequential GetDIB with the provided
+ * peci file descriptor.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_GetDIB_seq(uint8_t target, uint64_t* dib, int peci_fd)
+{
+ struct peci_get_dib_msg cmd;
+ EPECIStatus ret;
+ cmd.addr = target;
+
+ if (dib == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ ret = HW_peci_issue_cmd(PECI_IOC_GET_DIB, (char*)&cmd, peci_fd);
+
+ if (ret == PECI_CC_SUCCESS)
+ {
+ *dib = cmd.dib;
+ }
+
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function get PECI Thermal temperature
+ * Expressed in signed fixed point value of 1/64 degrees celsius
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_GetTemp(uint8_t target, int16_t* temperature)
+{
+ int peci_fd = -1;
+ struct peci_get_temp_msg cmd;
+
+ if (temperature == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
+ {
+ return PECI_CC_DRIVER_ERR;
+ }
+
+ cmd.addr = target;
+
+ EPECIStatus ret =
+ HW_peci_issue_cmd(PECI_IOC_GET_TEMP, (char*)&cmd, peci_fd);
+
+ if (ret == PECI_CC_SUCCESS)
+ {
+ *temperature = cmd.temp_raw;
+ }
+
+ peci_Close(peci_fd);
+
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function provides read access to the package configuration
+ * space within the processor.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_RdPkgConfig(uint8_t target, uint8_t u8Index, uint16_t u16Value,
+ uint8_t u8ReadLen, uint8_t* pPkgConfig,
+ uint8_t* cc)
+{
+ int peci_fd = -1;
+ EPECIStatus ret;
+
+ if (pPkgConfig == NULL || cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
+ {
+ return PECI_CC_DRIVER_ERR;
+ }
+ ret = peci_RdPkgConfig_seq(target, u8Index, u16Value, u8ReadLen, pPkgConfig,
+ peci_fd, cc);
+
+ peci_Close(peci_fd);
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function allows sequential RdPkgConfig with the provided
+ * peci file descriptor.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_RdPkgConfig_seq(uint8_t target, uint8_t u8Index,
+ uint16_t u16Value, uint8_t u8ReadLen,
+ uint8_t* pPkgConfig, int peci_fd, uint8_t* cc)
+{
+ struct peci_rd_pkg_cfg_msg cmd;
+ EPECIStatus ret;
+
+ if (pPkgConfig == NULL || cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ // Per the PECI spec, the write length must be a byte, word, or dword
+ if (u8ReadLen != 1 && u8ReadLen != 2 && u8ReadLen != 4)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ // The PECI buffer must be large enough to hold the requested data
+ if (sizeof(cmd.pkg_config) < u8ReadLen)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ cmd.addr = target;
+ cmd.index = u8Index; // RdPkgConfig index
+ cmd.param = u16Value; // Config parameter value
+ cmd.rx_len = u8ReadLen;
+
+ ret = HW_peci_issue_cmd(PECI_IOC_RD_PKG_CFG, (char*)&cmd, peci_fd);
+ *cc = cmd.cc;
+ if (ret == PECI_CC_SUCCESS)
+ {
+ memcpy(pPkgConfig, cmd.pkg_config, u8ReadLen);
+ }
+
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function provides write access to the package configuration
+ * space within the processor
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_WrPkgConfig(uint8_t target, uint8_t u8Index, uint16_t u16Param,
+ uint32_t u32Value, uint8_t u8WriteLen, uint8_t* cc)
+{
+ int peci_fd = -1;
+ EPECIStatus ret;
+
+ if (cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
+ {
+ return PECI_CC_DRIVER_ERR;
+ }
+ ret = peci_WrPkgConfig_seq(target, u8Index, u16Param, u32Value, u8WriteLen,
+ peci_fd, cc);
+
+ peci_Close(peci_fd);
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function allows sequential WrPkgConfig with the provided
+ * peci file descriptor.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_WrPkgConfig_seq(uint8_t target, uint8_t u8Index,
+ uint16_t u16Param, uint32_t u32Value,
+ uint8_t u8WriteLen, int peci_fd, uint8_t* cc)
+{
+ struct peci_wr_pkg_cfg_msg cmd;
+ EPECIStatus ret;
+
+ if (cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ // Per the PECI spec, the write length must be a byte, word, or dword
+ if ((u8WriteLen != 1) && (u8WriteLen != 2) && (u8WriteLen != 4))
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ cmd.addr = target;
+ cmd.index = u8Index; // RdPkgConfig index
+ cmd.param = u16Param; // parameter value
+ cmd.tx_len = u8WriteLen;
+ cmd.value = u32Value;
+
+ ret = HW_peci_issue_cmd(PECI_IOC_WR_PKG_CFG, (char*)&cmd, peci_fd);
+ *cc = cmd.cc;
+
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function provides read access to Model Specific Registers
+ * defined in the processor doc.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_RdIAMSR(uint8_t target, uint8_t threadID, uint16_t MSRAddress,
+ uint64_t* u64MsrVal, uint8_t* cc)
+{
+ int peci_fd = -1;
+ struct peci_rd_ia_msr_msg cmd;
+ EPECIStatus ret;
+
+ if (u64MsrVal == NULL || cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
+ {
+ return PECI_CC_DRIVER_ERR;
+ }
+
+ cmd.addr = target;
+ cmd.thread_id = threadID; // request byte for thread ID
+ cmd.address = MSRAddress; // MSR Address
+
+ ret = HW_peci_issue_cmd(PECI_IOC_RD_IA_MSR, (char*)&cmd, peci_fd);
+ *cc = cmd.cc;
+ if (ret == PECI_CC_SUCCESS)
+ {
+ *u64MsrVal = cmd.value;
+ }
+
+ peci_Close(peci_fd);
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function provides read access to the PCI configuration space at
+ * the requested PCI configuration address.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_RdPCIConfig(uint8_t target, uint8_t u8Bus, uint8_t u8Device,
+ uint8_t u8Fcn, uint16_t u16Reg, uint8_t* pPCIData,
+ uint8_t* cc)
+{
+ int peci_fd = -1;
+ EPECIStatus ret;
+
+ if (pPCIData == NULL || cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
+ {
+ return PECI_CC_DRIVER_ERR;
+ }
+ ret = peci_RdPCIConfig_seq(target, u8Bus, u8Device, u8Fcn, u16Reg, pPCIData,
+ peci_fd, cc);
+
+ peci_Close(peci_fd);
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function allows sequential RdPCIConfig with the provided
+ * peci file descriptor.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_RdPCIConfig_seq(uint8_t target, uint8_t u8Bus,
+ uint8_t u8Device, uint8_t u8Fcn,
+ uint16_t u16Reg, uint8_t* pPCIData,
+ int peci_fd, uint8_t* cc)
+{
+ struct peci_rd_pci_cfg_msg cmd;
+ EPECIStatus ret;
+
+ if (pPCIData == NULL || cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ // The PECI buffer must be large enough to hold the PCI data
+ if (sizeof(cmd.pci_config) < 4)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ cmd.addr = target;
+ cmd.bus = u8Bus;
+ cmd.device = u8Device;
+ cmd.function = u8Fcn;
+ cmd.reg = u16Reg;
+
+ ret = HW_peci_issue_cmd(PECI_IOC_RD_PCI_CFG, (char*)&cmd, peci_fd);
+ *cc = cmd.cc;
+
+ if (ret == PECI_CC_SUCCESS)
+ {
+ memcpy(pPCIData, cmd.pci_config, 4);
+ }
+
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function provides read access to the local PCI configuration space
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_RdPCIConfigLocal(uint8_t target, uint8_t u8Bus,
+ uint8_t u8Device, uint8_t u8Fcn,
+ uint16_t u16Reg, uint8_t u8ReadLen,
+ uint8_t* pPCIReg, uint8_t* cc)
+{
+ int peci_fd = -1;
+ EPECIStatus ret;
+
+ if (pPCIReg == NULL || cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
+ {
+ return PECI_CC_DRIVER_ERR;
+ }
+ ret = peci_RdPCIConfigLocal_seq(target, u8Bus, u8Device, u8Fcn, u16Reg,
+ u8ReadLen, pPCIReg, peci_fd, cc);
+
+ peci_Close(peci_fd);
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function allows sequential RdPCIConfigLocal with the provided
+ * peci file descriptor.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_RdPCIConfigLocal_seq(uint8_t target, uint8_t u8Bus,
+ uint8_t u8Device, uint8_t u8Fcn,
+ uint16_t u16Reg, uint8_t u8ReadLen,
+ uint8_t* pPCIReg, int peci_fd,
+ uint8_t* cc)
+{
+ struct peci_rd_pci_cfg_local_msg cmd;
+ EPECIStatus ret;
+
+ if (pPCIReg == NULL || cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ // Per the PECI spec, the read length must be a byte, word, or dword
+ if (u8ReadLen != 1 && u8ReadLen != 2 && u8ReadLen != 4)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ // The PECI buffer must be large enough to hold the requested data
+ if (sizeof(cmd.pci_config) < u8ReadLen)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ cmd.addr = target;
+ cmd.bus = u8Bus;
+ cmd.device = u8Device;
+ cmd.function = u8Fcn;
+ cmd.reg = u16Reg;
+ cmd.rx_len = u8ReadLen;
+
+ ret = HW_peci_issue_cmd(PECI_IOC_RD_PCI_CFG_LOCAL, (char*)&cmd, peci_fd);
+ *cc = cmd.cc;
+
+ if (ret == PECI_CC_SUCCESS)
+ {
+ memcpy(pPCIReg, cmd.pci_config, u8ReadLen);
+ }
+
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function provides write access to the local PCI configuration space
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_WrPCIConfigLocal(uint8_t target, uint8_t u8Bus,
+ uint8_t u8Device, uint8_t u8Fcn,
+ uint16_t u16Reg, uint8_t DataLen,
+ uint32_t DataVal, uint8_t* cc)
+{
+ int peci_fd = -1;
+ struct peci_wr_pci_cfg_local_msg cmd;
+ EPECIStatus ret;
+
+ if (cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
+ {
+ return PECI_CC_DRIVER_ERR;
+ }
+
+ // Per the PECI spec, the write length must be a byte, word, or dword
+ if (DataLen != 1 && DataLen != 2 && DataLen != 4)
+ {
+ peci_Close(peci_fd);
+ return PECI_CC_INVALID_REQ;
+ }
+
+ cmd.addr = target;
+ cmd.bus = u8Bus;
+ cmd.device = u8Device;
+ cmd.function = u8Fcn;
+ cmd.reg = u16Reg;
+ cmd.tx_len = DataLen;
+ cmd.value = DataVal;
+
+ ret = HW_peci_issue_cmd(PECI_IOC_WR_PCI_CFG_LOCAL, (char*)&cmd, peci_fd);
+ *cc = cmd.cc;
+
+ peci_Close(peci_fd);
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This internal function is the common interface for RdEndPointConfig to PCI
+ *------------------------------------------------------------------------*/
+static EPECIStatus peci_RdEndPointConfigPciCommon(
+ uint8_t target, uint8_t u8MsgType, uint8_t u8Seg, uint8_t u8Bus,
+ uint8_t u8Device, uint8_t u8Fcn, uint16_t u16Reg, uint8_t u8ReadLen,
+ uint8_t* pPCIData, int peci_fd, uint8_t* cc)
+{
+ struct peci_rd_end_pt_cfg_msg cmd;
+ EPECIStatus ret;
+
+ if (pPCIData == NULL || cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ // The PECI buffer must be large enough to hold the requested data
+ if (sizeof(cmd.data) < u8ReadLen)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ cmd.addr = target;
+ cmd.msg_type = u8MsgType;
+ cmd.params.pci_cfg.seg = u8Seg;
+ cmd.params.pci_cfg.bus = u8Bus;
+ cmd.params.pci_cfg.device = u8Device;
+ cmd.params.pci_cfg.function = u8Fcn;
+ cmd.params.pci_cfg.reg = u16Reg;
+ cmd.rx_len = u8ReadLen;
+
+ ret = HW_peci_issue_cmd(PECI_IOC_RD_END_PT_CFG, (char*)&cmd, peci_fd);
+ *cc = cmd.cc;
+
+ if (ret == PECI_CC_SUCCESS)
+ {
+ memcpy(pPCIData, cmd.data, u8ReadLen);
+ }
+ else
+ {
+ ret = PECI_CC_DRIVER_ERR;
+ }
+
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function provides read access to the PCI configuration space at
+ * the requested PCI configuration address.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_RdEndPointConfigPci(uint8_t target, uint8_t u8Seg,
+ uint8_t u8Bus, uint8_t u8Device,
+ uint8_t u8Fcn, uint16_t u16Reg,
+ uint8_t u8ReadLen, uint8_t* pPCIData,
+ uint8_t* cc)
+{
+ int peci_fd = -1;
+ EPECIStatus ret;
+
+ if (pPCIData == NULL || cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
+ {
+ return PECI_CC_DRIVER_ERR;
+ }
+ ret =
+ peci_RdEndPointConfigPci_seq(target, u8Seg, u8Bus, u8Device, u8Fcn,
+ u16Reg, u8ReadLen, pPCIData, peci_fd, cc);
+ peci_Close(peci_fd);
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function allows sequential RdEndPointConfig to PCI with the provided
+ * peci file descriptor.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_RdEndPointConfigPci_seq(uint8_t target, uint8_t u8Seg,
+ uint8_t u8Bus, uint8_t u8Device,
+ uint8_t u8Fcn, uint16_t u16Reg,
+ uint8_t u8ReadLen, uint8_t* pPCIData,
+ int peci_fd, uint8_t* cc)
+{
+ if (pPCIData == NULL || cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ // Per the PECI spec, the read length must be a byte, word, or dword
+ if (u8ReadLen != 1 && u8ReadLen != 2 && u8ReadLen != 4)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ return peci_RdEndPointConfigPciCommon(target, PECI_ENDPTCFG_TYPE_PCI, u8Seg,
+ u8Bus, u8Device, u8Fcn, u16Reg,
+ u8ReadLen, pPCIData, peci_fd, cc);
+}
+
+/*-------------------------------------------------------------------------
+ * This function provides read access to the Local PCI configuration space at
+ * the requested PCI configuration address.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_RdEndPointConfigPciLocal(uint8_t target, uint8_t u8Seg,
+ uint8_t u8Bus, uint8_t u8Device,
+ uint8_t u8Fcn, uint16_t u16Reg,
+ uint8_t u8ReadLen, uint8_t* pPCIData,
+ uint8_t* cc)
+{
+ int peci_fd = -1;
+ EPECIStatus ret;
+
+ if (pPCIData == NULL || cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
+ {
+ return PECI_CC_DRIVER_ERR;
+ }
+ ret = peci_RdEndPointConfigPciLocal_seq(target, u8Seg, u8Bus, u8Device,
+ u8Fcn, u16Reg, u8ReadLen, pPCIData,
+ peci_fd, cc);
+ peci_Close(peci_fd);
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function allows sequential RdEndPointConfig to PCI Local with the
+ *provided peci file descriptor.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_RdEndPointConfigPciLocal_seq(uint8_t target, uint8_t u8Seg,
+ uint8_t u8Bus, uint8_t u8Device,
+ uint8_t u8Fcn, uint16_t u16Reg,
+ uint8_t u8ReadLen,
+ uint8_t* pPCIData, int peci_fd,
+ uint8_t* cc)
+{
+ if (pPCIData == NULL || cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ // Per the PECI spec, the read length must be a byte, word, or dword
+ if (u8ReadLen != 1 && u8ReadLen != 2 && u8ReadLen != 4)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ return peci_RdEndPointConfigPciCommon(target, PECI_ENDPTCFG_TYPE_LOCAL_PCI,
+ u8Seg, u8Bus, u8Device, u8Fcn, u16Reg,
+ u8ReadLen, pPCIData, peci_fd, cc);
+}
+
+/*-------------------------------------------------------------------------
+ * This function provides read access to PCI MMIO space at
+ * the requested PCI configuration address.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_RdEndPointConfigMmio(uint8_t target, uint8_t u8Seg,
+ uint8_t u8Bus, uint8_t u8Device,
+ uint8_t u8Fcn, uint8_t u8Bar,
+ uint8_t u8AddrType, uint64_t u64Offset,
+ uint8_t u8ReadLen, uint8_t* pMmioData,
+ uint8_t* cc)
+{
+ int peci_fd = -1;
+ EPECIStatus ret;
+
+ if (pMmioData == NULL || cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
+ {
+ return PECI_CC_DRIVER_ERR;
+ }
+ ret = peci_RdEndPointConfigMmio_seq(target, u8Seg, u8Bus, u8Device, u8Fcn,
+ u8Bar, u8AddrType, u64Offset, u8ReadLen,
+ pMmioData, peci_fd, cc);
+ peci_Close(peci_fd);
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function allows sequential RdEndPointConfig to PCI MMIO with the
+ *provided peci file descriptor.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_RdEndPointConfigMmio_seq(
+ uint8_t target, uint8_t u8Seg, uint8_t u8Bus, uint8_t u8Device,
+ uint8_t u8Fcn, uint8_t u8Bar, uint8_t u8AddrType, uint64_t u64Offset,
+ uint8_t u8ReadLen, uint8_t* pMmioData, int peci_fd, uint8_t* cc)
+{
+ struct peci_rd_end_pt_cfg_msg cmd;
+ EPECIStatus ret;
+
+ if (pMmioData == NULL || cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ // Per the PECI spec, the read length must be a byte, word, dword, or qword
+ if (u8ReadLen != 1 && u8ReadLen != 2 && u8ReadLen != 4 && u8ReadLen != 8)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ // The PECI buffer must be large enough to hold the requested data
+ if (sizeof(cmd.data) < u8ReadLen)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ cmd.addr = target;
+ cmd.msg_type = PECI_ENDPTCFG_TYPE_MMIO;
+ cmd.params.mmio.seg = u8Seg;
+ cmd.params.mmio.bus = u8Bus;
+ cmd.params.mmio.device = u8Device;
+ cmd.params.mmio.function = u8Fcn;
+ cmd.params.mmio.bar = u8Bar;
+ cmd.params.mmio.addr_type = u8AddrType;
+ cmd.params.mmio.offset = u64Offset;
+ cmd.rx_len = u8ReadLen;
+
+ ret = HW_peci_issue_cmd(PECI_IOC_RD_END_PT_CFG, (char*)&cmd, peci_fd);
+ *cc = cmd.cc;
+
+ if (ret == PECI_CC_SUCCESS)
+ {
+ memcpy(pMmioData, cmd.data, u8ReadLen);
+ }
+ else
+ {
+ ret = PECI_CC_DRIVER_ERR;
+ }
+
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function provides crashdump discovery data over PECI
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_CrashDump_Discovery(uint8_t target, uint8_t subopcode,
+ uint8_t param0, uint16_t param1,
+ uint8_t param2, uint8_t u8ReadLen,
+ uint8_t* pData, uint8_t* cc)
+{
+ int peci_fd = -1;
+ struct peci_crashdump_disc_msg cmd;
+ EPECIStatus ret;
+
+ if (pData == NULL || cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ // Per the PECI spec, the read length must be a byte, word, or qword
+ if (u8ReadLen != 1 && u8ReadLen != 2 && u8ReadLen != 8)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ // The PECI buffer must be large enough to hold the requested data
+ if (sizeof(cmd.data) < u8ReadLen)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
+ {
+ return PECI_CC_DRIVER_ERR;
+ }
+
+ cmd.addr = target;
+ cmd.subopcode = subopcode;
+ cmd.param0 = param0;
+ cmd.param1 = param1;
+ cmd.param2 = param2;
+ cmd.rx_len = u8ReadLen;
+
+ ret = HW_peci_issue_cmd(PECI_IOC_CRASHDUMP_DISC, (char*)&cmd, peci_fd);
+ *cc = cmd.cc;
+ if (ret == PECI_CC_SUCCESS)
+ {
+ memcpy(pData, cmd.data, u8ReadLen);
+ }
+ else
+ {
+ ret = PECI_CC_DRIVER_ERR;
+ }
+
+ peci_Close(peci_fd);
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function provides crashdump GetFrame data over PECI
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_CrashDump_GetFrame(uint8_t target, uint16_t param0,
+ uint16_t param1, uint16_t param2,
+ uint8_t u8ReadLen, uint8_t* pData,
+ uint8_t* cc)
+{
+ int peci_fd = -1;
+ struct peci_crashdump_get_frame_msg cmd;
+ EPECIStatus ret;
+
+ if (pData == NULL || cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ // Per the PECI spec, the read length must be a qword or dqword
+ if (u8ReadLen != 8 && u8ReadLen != 16)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ // The PECI buffer must be large enough to hold the requested data
+ if (sizeof(cmd.data) < u8ReadLen)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
+ {
+ return PECI_CC_DRIVER_ERR;
+ }
+
+ cmd.addr = target;
+ cmd.param0 = param0;
+ cmd.param1 = param1;
+ cmd.param2 = param2;
+ cmd.rx_len = u8ReadLen;
+
+ ret = HW_peci_issue_cmd(PECI_IOC_CRASHDUMP_GET_FRAME, (char*)&cmd, peci_fd);
+ *cc = cmd.cc;
+ if (ret == PECI_CC_SUCCESS)
+ {
+ memcpy(pData, cmd.data, u8ReadLen);
+ }
+ else
+ {
+ ret = PECI_CC_DRIVER_ERR;
+ }
+
+ peci_Close(peci_fd);
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function provides raw PECI command access
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_raw(uint8_t target, uint8_t u8ReadLen, const uint8_t* pRawCmd,
+ const uint32_t cmdSize, uint8_t* pRawResp,
+ uint32_t respSize)
+{
+ int peci_fd = -1;
+ struct peci_xfer_msg cmd;
+ uint8_t u8TxBuf[PECI_BUFFER_SIZE];
+ uint8_t u8RxBuf[PECI_BUFFER_SIZE];
+ EPECIStatus ret;
+
+ if (pRawResp == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
+ {
+ return PECI_CC_DRIVER_ERR;
+ }
+
+ // Check for valid buffer sizes
+ if (cmdSize > PECI_BUFFER_SIZE || respSize < u8ReadLen ||
+ u8ReadLen >
+ (PECI_BUFFER_SIZE - 1)) // response buffer is data + 1 status byte
+ {
+ peci_Close(peci_fd);
+ return PECI_CC_INVALID_REQ;
+ }
+
+ cmd.addr = target;
+ cmd.tx_len = cmdSize;
+ cmd.rx_len = u8ReadLen;
+
+ memcpy(u8TxBuf, pRawCmd, cmdSize);
+
+ cmd.tx_buf = u8TxBuf;
+ cmd.rx_buf = u8RxBuf;
+ ret = HW_peci_issue_cmd(PECI_IOC_XFER, (char*)&cmd, peci_fd);
+
+ if (ret == PECI_CC_SUCCESS)
+ {
+ memcpy(pRawResp, u8RxBuf, u8ReadLen);
+ }
+
+ peci_Close(peci_fd);
+ return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function returns the CPUID for the given PECI client address
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_GetCPUID(const uint8_t clientAddr, CPUModel* cpuModel,
+ uint8_t* cc)
+{
+ if (cpuModel == NULL || cc == NULL)
+ {
+ return PECI_CC_INVALID_REQ;
+ }
+
+ if (peci_Ping(clientAddr) != PECI_CC_SUCCESS)
+ {
+ return PECI_CC_CPU_NOT_PRESENT;
+ }
+
+ return peci_RdPkgConfig(clientAddr, PECI_MBX_INDEX_CPU_ID,
+ PECI_PKG_ID_CPU_ID, sizeof(uint32_t),
+ (uint8_t*)cpuModel, cc);
+}
diff --git a/libpeci/peci.h b/libpeci/peci.h
new file mode 100644
index 0000000..05a60bd
--- /dev/null
+++ b/libpeci/peci.h
@@ -0,0 +1,224 @@
+/*
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#pragma once
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <inttypes.h>
+#include <linux/peci-ioctl.h>
+#include <stdbool.h>
+
+// PECI Client Address List
+#define MIN_CLIENT_ADDR 0x30
+#define MAX_CLIENT_ADDR 0x37
+#define MAX_CPUS (MAX_CLIENT_ADDR - MIN_CLIENT_ADDR + 1)
+
+typedef enum
+{
+ skx = 0x00050654,
+ clx = 0x00050656,
+ clx2 = 0x00050657,
+ cpx = 0x0005065A,
+ icx = 0x000606A0,
+} CPUModel;
+
+// PECI Status Codes
+typedef enum
+{
+ PECI_CC_SUCCESS = 0,
+ PECI_CC_INVALID_REQ,
+ PECI_CC_HW_ERR,
+ PECI_CC_DRIVER_ERR,
+ PECI_CC_CPU_NOT_PRESENT,
+ PECI_CC_MEM_ERR,
+} EPECIStatus;
+
+// PECI Timeout Options
+typedef enum
+{
+ PECI_WAIT_FOREVER = -1,
+ PECI_NO_WAIT = 0,
+} EPECITimeout;
+
+#define PECI_TIMEOUT_RESOLUTION_MS 10 // 10 ms
+#define PECI_TIMEOUT_MS 100 // 100 ms
+
+// VCU Index and Sequence Paramaters
+#define VCU_SET_PARAM 0x0001
+#define VCU_READ 0x0002
+#define VCU_OPEN_SEQ 0x0003
+#define VCU_CLOSE_SEQ 0x0004
+#define VCU_ABORT_SEQ 0x0005
+#define VCU_VERSION 0x0009
+
+typedef enum
+{
+ VCU_READ_LOCAL_CSR_SEQ = 0x2,
+ VCU_READ_LOCAL_MMIO_SEQ = 0x6,
+ VCU_EN_SECURE_DATA_SEQ = 0x14,
+ VCU_CORE_MCA_SEQ = 0x10000,
+ VCU_UNCORE_MCA_SEQ = 0x10000,
+ VCU_IOT_BRKPT_SEQ = 0x10010,
+ VCU_MBP_CONFIG_SEQ = 0x10026,
+ VCU_PWR_MGT_SEQ = 0x1002a,
+ VCU_CRASHDUMP_SEQ = 0x10038,
+ VCU_ARRAY_DUMP_SEQ = 0x20000,
+ VCU_SCAN_DUMP_SEQ = 0x20008,
+ VCU_TOR_DUMP_SEQ = 0x30002,
+ VCU_SQ_DUMP_SEQ = 0x30004,
+ VCU_UNCORE_CRASHDUMP_SEQ = 0x30006,
+} EPECISequence;
+
+#define MBX_INDEX_VCU 128 // VCU Index
+
+typedef enum
+{
+ MMIO_DWORD_OFFSET = 0x05,
+ MMIO_QWORD_OFFSET = 0x06,
+} EEndPtMmioAddrType;
+
+// Find the specified PCI bus number value
+EPECIStatus FindBusNumber(uint8_t u8Bus, uint8_t u8Cpu, uint8_t* pu8BusValue);
+
+// Gets the temperature from the target
+// Expressed in signed fixed point value of 1/64 degrees celsius
+EPECIStatus peci_GetTemp(uint8_t target, int16_t* temperature);
+
+// Provides read access to the package configuration space within the processor
+EPECIStatus peci_RdPkgConfig(uint8_t target, uint8_t u8Index, uint16_t u16Value,
+ uint8_t u8ReadLen, uint8_t* pPkgConfig,
+ uint8_t* cc);
+
+// Allows sequential RdPkgConfig with the provided peci file descriptor
+EPECIStatus peci_RdPkgConfig_seq(uint8_t target, uint8_t u8Index,
+ uint16_t u16Value, uint8_t u8ReadLen,
+ uint8_t* pPkgConfig, int peci_fd, uint8_t* cc);
+
+// Provides write access to the package configuration space within the processor
+EPECIStatus peci_WrPkgConfig(uint8_t target, uint8_t u8Index, uint16_t u16Param,
+ uint32_t u32Value, uint8_t u8WriteLen,
+ uint8_t* cc);
+
+// Allows sequential WrPkgConfig with the provided peci file descriptor
+EPECIStatus peci_WrPkgConfig_seq(uint8_t target, uint8_t u8Index,
+ uint16_t u16Param, uint32_t u32Value,
+ uint8_t u8WriteLen, int peci_fd, uint8_t* cc);
+
+// Provides read access to Model Specific Registers
+EPECIStatus peci_RdIAMSR(uint8_t target, uint8_t threadID, uint16_t MSRAddress,
+ uint64_t* u64MsrVal, uint8_t* cc);
+
+// Provides read access to PCI Configuration space
+EPECIStatus peci_RdPCIConfig(uint8_t target, uint8_t u8Bus, uint8_t u8Device,
+ uint8_t u8Fcn, uint16_t u16Reg, uint8_t* pPCIReg,
+ uint8_t* cc);
+
+// Allows sequential RdPCIConfig with the provided peci file descriptor
+EPECIStatus peci_RdPCIConfig_seq(uint8_t target, uint8_t u8Bus,
+ uint8_t u8Device, uint8_t u8Fcn,
+ uint16_t u16Reg, uint8_t* pPCIData,
+ int peci_fd, uint8_t* cc);
+
+// Provides read access to the local PCI Configuration space
+EPECIStatus peci_RdPCIConfigLocal(uint8_t target, uint8_t u8Bus,
+ uint8_t u8Device, uint8_t u8Fcn,
+ uint16_t u16Reg, uint8_t u8ReadLen,
+ uint8_t* pPCIReg, uint8_t* cc);
+
+// Allows sequential RdPCIConfigLocal with the provided peci file descriptor
+EPECIStatus peci_RdPCIConfigLocal_seq(uint8_t target, uint8_t u8Bus,
+ uint8_t u8Device, uint8_t u8Fcn,
+ uint16_t u16Reg, uint8_t u8ReadLen,
+ uint8_t* pPCIReg, int peci_fd,
+ uint8_t* cc);
+
+// Provides write access to the local PCI Configuration space
+EPECIStatus peci_WrPCIConfigLocal(uint8_t target, uint8_t u8Bus,
+ uint8_t u8Device, uint8_t u8Fcn,
+ uint16_t u16Reg, uint8_t DataLen,
+ uint32_t DataVal, uint8_t* cc);
+
+// Provides read access to PCI configuration space
+EPECIStatus peci_RdEndPointConfigPci(uint8_t target, uint8_t u8Seg,
+ uint8_t u8Bus, uint8_t u8Device,
+ uint8_t u8Fcn, uint16_t u16Reg,
+ uint8_t u8ReadLen, uint8_t* pPCIData,
+ uint8_t* cc);
+
+// Allows sequential RdEndPointConfig to PCI Configuration space
+EPECIStatus peci_RdEndPointConfigPci_seq(uint8_t target, uint8_t u8Seg,
+ uint8_t u8Bus, uint8_t u8Device,
+ uint8_t u8Fcn, uint16_t u16Reg,
+ uint8_t u8ReadLen, uint8_t* pPCIData,
+ int peci_fd, uint8_t* cc);
+
+// Provides read access to the local PCI configuration space
+EPECIStatus peci_RdEndPointConfigPciLocal(uint8_t target, uint8_t u8Seg,
+ uint8_t u8Bus, uint8_t u8Device,
+ uint8_t u8Fcn, uint16_t u16Reg,
+ uint8_t u8ReadLen, uint8_t* pPCIData,
+ uint8_t* cc);
+
+// Allows sequential RdEndPointConfig to the local PCI Configuration space
+EPECIStatus peci_RdEndPointConfigPciLocal_seq(uint8_t target, uint8_t u8Seg,
+ uint8_t u8Bus, uint8_t u8Device,
+ uint8_t u8Fcn, uint16_t u16Reg,
+ uint8_t u8ReadLen,
+ uint8_t* pPCIData, int peci_fd,
+ uint8_t* cc);
+
+// Provides read access to PCI MMIO space
+EPECIStatus peci_RdEndPointConfigMmio(uint8_t target, uint8_t u8Seg,
+ uint8_t u8Bus, uint8_t u8Device,
+ uint8_t u8Fcn, uint8_t u8Bar,
+ uint8_t u8AddrType, uint64_t u64Offset,
+ uint8_t u8ReadLen, uint8_t* pMmioData,
+ uint8_t* cc);
+
+// Allows sequential RdEndPointConfig to PCI MMIO space
+EPECIStatus peci_RdEndPointConfigMmio_seq(
+ uint8_t target, uint8_t u8Seg, uint8_t u8Bus, uint8_t u8Device,
+ uint8_t u8Fcn, uint8_t u8Bar, uint8_t u8AddrType, uint64_t u64Offset,
+ uint8_t u8ReadLen, uint8_t* pMmioData, int peci_fd, uint8_t* cc);
+
+// Provides access to the Crashdump Discovery API
+EPECIStatus peci_CrashDump_Discovery(uint8_t target, uint8_t subopcode,
+ uint8_t param0, uint16_t param1,
+ uint8_t param2, uint8_t u8ReadLen,
+ uint8_t* pData, uint8_t* cc);
+
+// Provides access to the Crashdump GetFrame API
+EPECIStatus peci_CrashDump_GetFrame(uint8_t target, uint16_t param0,
+ uint16_t param1, uint16_t param2,
+ uint8_t u8ReadLen, uint8_t* pData,
+ uint8_t* cc);
+
+// Provides raw PECI command access
+EPECIStatus peci_raw(uint8_t target, uint8_t u8ReadLen, const uint8_t* pRawCmd,
+ const uint32_t cmdSize, uint8_t* pRawResp,
+ uint32_t respSize);
+
+EPECIStatus peci_Lock(int* peci_fd, uint32_t timeout_ms);
+void peci_Unlock(int peci_fd);
+EPECIStatus peci_Ping(uint8_t target);
+EPECIStatus peci_Ping_seq(uint8_t target, int peci_fd);
+EPECIStatus peci_GetCPUID(const uint8_t clientAddr, CPUModel* cpuModel,
+ uint8_t* cc);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/libpeci/peci_cmds.c b/libpeci/peci_cmds.c
new file mode 100644
index 0000000..ba37810
--- /dev/null
+++ b/libpeci/peci_cmds.c
@@ -0,0 +1,370 @@
+/*
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#include <inttypes.h>
+#include <peci.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#ifndef ABS
+#define ABS(_v_) (((_v_) > 0) ? (_v_) : -(_v_))
+#endif
+
+extern EPECIStatus peci_GetDIB(uint8_t target, uint64_t* dib);
+
+void Usage(char* progname)
+{
+ printf("Usage :\n");
+ printf("\t%s [-a <addr>] [-s <size>] <command> [parameters]\n", progname);
+ printf("\t\t-a : Address of the target in decimal. Default is 48.\n");
+ printf("\t\t-s : Size of data to read or write in bytes. Default is 4.\n");
+ printf("\t\t-b : Display completion code.\n");
+ printf("\t\t-n : Ping the target.\n");
+ printf("\t\t-t : Get the temperature.\n");
+ printf("\t\t-d : Get the DIB.\n");
+ printf(
+ "\t\t-p : PCI Read for specific hex address <Bus Dev Func [Reg]>.\n");
+ printf("\t\t-pw : PCI Write for specific hex address <Bus Dev Func [Reg] "
+ "Data>.\n");
+ printf("\t\t-c : Read Package Config <Index Parameter>.\n");
+ printf("\t\t-cw : Write Package Config <Index Parameter Data>.\n");
+ printf("\t\t-m : MSR Read <Thread Address>.\n");
+ printf("\t\t-l : Local PCI Read for specific hex address <Bus Dev Func "
+ "[Reg]>.\n");
+ printf("\t\t-lw : Local PCI Write for specific hex address <Bus Dev Func "
+ "[Reg] Data>.\n");
+ printf("\n");
+}
+
+int main(int argc, char* argv[])
+{
+ int c;
+ EPECIStatus ret;
+ int cnt = 0;
+ uint8_t u8Cmd = PECI_CMD_MAX;
+ uint8_t address = 0x30; // use default address of 48d
+ uint8_t u8Size = 4; // default to a DWORD
+ uint32_t u32PciReadVal = 0;
+ uint8_t u8PciBus = 0;
+ uint8_t u8PciDev = 0;
+ uint8_t u8PciFunc = 0;
+ uint16_t u16PciReg = 0;
+ uint32_t u32PciWriteVal = 0;
+ uint8_t u8PkgIndex = 0;
+ uint16_t u16PkgParam = 0;
+ uint32_t u32PkgValue = 0;
+ uint8_t u8MsrThread = 0;
+ uint16_t u16MsrAddr = 0;
+ uint64_t u64MsrVal = 0;
+ short temperature;
+ uint64_t dib;
+ uint8_t u8Index = 0;
+ uint8_t cc = 0;
+ bool showCc = false;
+
+ //
+ // Parse arguments.
+ //
+ while (-1 != (c = getopt(argc, argv, "a:s:nbtdp::c::m::l::")))
+ {
+ switch (c)
+ {
+ case 'a':
+ if (optarg != NULL)
+ address = (unsigned char)atoi(optarg);
+ break;
+
+ case 's':
+ if (optarg != NULL)
+ u8Size = (unsigned char)atoi(optarg);
+ break;
+
+ case 'b':
+ showCc = true;
+ break;
+
+ case 'n':
+ cnt++;
+ u8Cmd = PECI_CMD_PING;
+ break;
+
+ case 't':
+ cnt++;
+ u8Cmd = PECI_CMD_GET_TEMP;
+ break;
+
+ case 'd':
+ cnt++;
+ u8Cmd = PECI_CMD_GET_DIB;
+ break;
+
+ case 'p':
+ cnt++;
+ u8Cmd = PECI_CMD_RD_PCI_CFG;
+ if (optarg != NULL && optarg[0] == 'w')
+ u8Cmd = PECI_CMD_WR_PCI_CFG;
+ break;
+
+ case 'c':
+ cnt++;
+ u8Cmd = PECI_CMD_RD_PKG_CFG;
+ if (optarg != NULL && optarg[0] == 'w')
+ u8Cmd = PECI_CMD_WR_PKG_CFG;
+ break;
+
+ case 'm':
+ cnt++;
+ u8Cmd = PECI_CMD_RD_IA_MSR;
+ break;
+
+ case 'l':
+ cnt++;
+ u8Cmd = PECI_CMD_RD_PCI_CFG_LOCAL;
+ if (optarg != NULL && optarg[0] == 'w')
+ u8Cmd = PECI_CMD_WR_PCI_CFG_LOCAL;
+ break;
+
+ default:
+ printf("ERROR: Unrecognized option \"-%c\".\n", optopt);
+ goto ErrorExit;
+ break;
+ }
+ }
+
+ if (1 != cnt)
+ {
+ printf("ERROR: Invalid options.\n");
+ goto ErrorExit;
+ }
+
+ //
+ // Execute the command
+ //
+ printf("PECI target[%u]: ", (int)address);
+ switch (u8Cmd)
+ {
+ case PECI_CMD_PING:
+ printf("Pinging ... ");
+ (0 == peci_Ping(address)) ? printf("Succeeded.\n")
+ : printf("Failed.\n");
+ break;
+
+ case PECI_CMD_GET_TEMP:
+ ret = peci_GetTemp(address, &temperature);
+ if (0 != ret)
+ {
+ printf("ERROR: Retrieving temperature failed.\n");
+ break;
+ }
+ printf("Temperature is %04xh (%c%d.%02dC).\n",
+ (int)(unsigned int)(unsigned short)temperature,
+ (0 > temperature) ? '-' : '+',
+ (int)((unsigned int)ABS(temperature) / 64),
+ (int)(((unsigned int)ABS(temperature) % 64) * 100) / 64);
+ break;
+
+ case PECI_CMD_GET_DIB:
+ ret = peci_GetDIB(address, &dib);
+ if (0 != ret)
+ {
+ printf("ERROR: Retrieving DIB failed.\n");
+ break;
+ }
+ printf("GetDIB Returned: 0x%" PRIx64 "\n", dib);
+ break;
+
+ case PECI_CMD_RD_PCI_CFG:
+ u8Index = argc;
+ switch (argc - optind)
+ {
+ case 4:
+ u16PciReg = strtoul(argv[--u8Index], NULL, 16);
+ case 3:
+ u8PciFunc = strtoul(argv[--u8Index], NULL, 16);
+ case 2:
+ u8PciDev = strtoul(argv[--u8Index], NULL, 16);
+ case 1:
+ u8PciBus = strtoul(argv[--u8Index], NULL, 16);
+ break;
+ default:
+ printf("ERROR: Unsupported arguments for PCI Write\n");
+ goto ErrorExit;
+ break;
+ }
+ ret = peci_RdPCIConfig(address, u8PciBus, u8PciDev, u8PciFunc,
+ u16PciReg, (uint8_t*)&u32PciReadVal, &cc);
+ if (showCc)
+ printf("PCI Read cc:0x%x\n", cc);
+ if (0 != ret)
+ {
+ printf("ERROR: PCI Read failed or is not supported.\n");
+ break;
+ }
+ printf("PCI Read of %02x:%02x:%02x Reg %02x: 0x%0*x\n", u8PciBus,
+ u8PciDev, u8PciFunc, u16PciReg, u8Size * 2, u32PciReadVal);
+ break;
+
+ case PECI_CMD_RD_PKG_CFG:
+ u8Index = argc;
+ switch (argc - optind)
+ {
+ case 2:
+ u16PkgParam = strtoul(argv[--u8Index], NULL, 16);
+ u8PkgIndex = strtoul(argv[--u8Index], NULL, 16);
+ break;
+ default:
+ printf("ERROR: Unsupported arguments for Pkg Read\n");
+ goto ErrorExit;
+ break;
+ }
+ ret = peci_RdPkgConfig(address, u8PkgIndex, u16PkgParam, u8Size,
+ (uint8_t*)&u32PkgValue, &cc);
+ if (showCc)
+ printf("Pkg Read cc:0x%x\n", cc);
+ if (0 != ret)
+ {
+ printf("ERROR: Read Package failed or is not supported.\n");
+ break;
+ }
+ printf("Pkg Read of Index %02x Param %04x: 0x%0*x\n", u8PkgIndex,
+ u16PkgParam, u8Size * 2, u32PkgValue);
+ break;
+
+ case PECI_CMD_WR_PKG_CFG:
+ u8Index = argc;
+ switch (argc - optind)
+ {
+ case 3:
+ u32PkgValue = strtoul(argv[--u8Index], NULL, 16);
+ u16PkgParam = strtoul(argv[--u8Index], NULL, 16);
+ u8PkgIndex = strtoul(argv[--u8Index], NULL, 16);
+ break;
+ default:
+ printf("ERROR: Unsupported arguments for Pkg Write\n");
+ goto ErrorExit;
+ break;
+ }
+ printf("Pkg Write of Index %02x Param %04x: 0x%0*x\n", u8PkgIndex,
+ u16PkgParam, u8Size * 2, u32PkgValue);
+ ret = peci_WrPkgConfig(address, u8PkgIndex, u16PkgParam,
+ u32PkgValue, u8Size, &cc);
+ if (showCc)
+ printf("Pkg Write cc:0x%x\n", cc);
+ (0 == ret) ? printf("Succeeded.\n")
+ : printf("Failed or not supported.\n");
+ break;
+
+ case PECI_CMD_RD_IA_MSR:
+ u8Index = argc;
+ switch (argc - optind)
+ {
+ case 2:
+ u16MsrAddr = strtoul(argv[--u8Index], NULL, 16);
+ u8MsrThread = strtoul(argv[--u8Index], NULL, 16);
+ break;
+ default:
+ printf("ERROR: Unsupported arguments for MSR Read\n");
+ goto ErrorExit;
+ break;
+ }
+ ret =
+ peci_RdIAMSR(address, u8MsrThread, u16MsrAddr, &u64MsrVal, &cc);
+ if (showCc)
+ printf("MSR Read cc:0x%x\n", cc);
+ if (0 != ret)
+ {
+ printf("ERROR: Read MSR failed or is not supported. %x\n", ret);
+ break;
+ }
+ printf("MSR Read of Thread %02x MSR %04x: 0x%0*" PRIx64 "\n",
+ u8MsrThread, u16MsrAddr, u8Size * 2, u64MsrVal);
+ break;
+
+ case PECI_CMD_RD_PCI_CFG_LOCAL:
+ u8Index = argc;
+ switch (argc - optind)
+ {
+ case 4:
+ u16PciReg = strtoul(argv[--u8Index], NULL, 16);
+ case 3:
+ u8PciFunc = strtoul(argv[--u8Index], NULL, 16);
+ case 2:
+ u8PciDev = strtoul(argv[--u8Index], NULL, 16);
+ case 1:
+ u8PciBus = strtoul(argv[--u8Index], NULL, 16);
+ break;
+ default:
+ printf(
+ "ERROR: Unsupported arguments for Local PCI Write\n");
+ goto ErrorExit;
+ break;
+ }
+ ret = peci_RdPCIConfigLocal(address, u8PciBus, u8PciDev, u8PciFunc,
+ u16PciReg, u8Size,
+ (uint8_t*)&u32PciReadVal, &cc);
+ if (showCc)
+ printf("Local PCI Read cc:0x%x\n", cc);
+ if (0 != ret)
+ {
+ printf("ERROR: Local PCI Read failed or is not supported.\n");
+ break;
+ }
+ printf("Local PCI Read of %02x:%02x:%02x Reg %02x: 0x%0*x\n",
+ u8PciBus, u8PciDev, u8PciFunc, u16PciReg, u8Size * 2,
+ u32PciReadVal);
+ break;
+
+ case PECI_CMD_WR_PCI_CFG_LOCAL:
+ u8Index = argc;
+ u32PciWriteVal = strtoul(argv[--u8Index], NULL, 16);
+ switch (argc - optind)
+ {
+ case 5:
+ u16PciReg = strtoul(argv[--u8Index], NULL, 16);
+ case 4:
+ u8PciFunc = strtoul(argv[--u8Index], NULL, 16);
+ case 3:
+ u8PciDev = strtoul(argv[--u8Index], NULL, 16);
+ case 2:
+ u8PciBus = strtoul(argv[--u8Index], NULL, 16);
+ break;
+ default:
+ printf(
+ "ERROR: Unsupported arguments for Local PCI Write\n");
+ goto ErrorExit;
+ break;
+ }
+ printf("Local PCI Write of %02x:%02x:%02x Reg %02x: 0x%0*x\n",
+ u8PciBus, u8PciDev, u8PciFunc, u16PciReg, u8Size * 2,
+ u32PciWriteVal);
+ ret = peci_WrPCIConfigLocal(address, u8PciBus, u8PciDev, u8PciFunc,
+ u16PciReg, u8Size, u32PciWriteVal, &cc);
+ if (showCc)
+ printf("Local PCI Write cc:0x%x\n", cc);
+ (0 == ret) ? printf("Succeeded.\n")
+ : printf("Failed or not supported.\n");
+ break;
+
+ default:
+ printf("ERROR: Unrecognized command\n");
+ goto ErrorExit;
+ break;
+ }
+ return 0;
+
+ErrorExit:
+ Usage(argv[0]);
+ return 1;
+}
diff --git a/psu-manager/src/cold_redundancy.cpp b/psu-manager/src/cold_redundancy.cpp
index 14fabca..fef4a99 100644
--- a/psu-manager/src/cold_redundancy.cpp
+++ b/psu-manager/src/cold_redundancy.cpp
@@ -366,8 +366,8 @@ void ColdRedundancy::createPSU(
conn->async_method_call(
[this, &conn,
- &interface](const boost::system::error_code ec,
- PropertyMapType propMap) {
+ interface](const boost::system::error_code ec,
+ PropertyMapType propMap) {
if (ec)
{
std::cerr