Add PECI library and peci_cmds utility source

This adds the PECI library and peci_cmds utility source ported
from Intel-BMC.

Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..ae9ad39
--- /dev/null
+++ b/.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/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..441b320
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,40 @@
+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")
+
+set(
+  CMAKE_C_FLAGS
+  "${CMAKE_C_FLAGS} \
+-Wall \
+-Wextra \
+-Wcast-align \
+-Wunused \
+-Wconversion \
+-Wsign-conversion \
+-Wnull-dereference \
+-Wdouble-promotion \
+-Wformat=2 \
+-Wno-unused-parameter \
+-Werror \
+-Wduplicated-cond \
+-Wduplicated-branches \
+-Wlogical-op \
+"
+  )
+
+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/peci.c b/peci.c
new file mode 100644
index 0000000..6aef7a8
--- /dev/null
+++ b/peci.c
@@ -0,0 +1,1253 @@
+/*
+// 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 <errno.h>
+#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, int timeout_ms)
+{
+    struct timespec sRequest;
+    sRequest.tv_sec = 0;
+    sRequest.tv_nsec = PECI_TIMEOUT_RESOLUTION_MS * 1000 * 1000;
+    int 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, " >>> PECI Device Busy <<< \n");
+        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)
+    {
+        if (errno == ETIMEDOUT)
+        {
+            return PECI_CC_TIMEOUT;
+        }
+        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 allows sequential peci_WrEndPointConfig to PCI EndPoint with
+ *the provided peci file descriptor.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_WrEndPointConfig_seq(uint8_t target, uint8_t u8MsgType,
+                                      uint8_t u8Seg, uint8_t u8Bus,
+                                      uint8_t u8Device, uint8_t u8Fcn,
+                                      uint16_t u16Reg, uint8_t DataLen,
+                                      uint32_t DataVal, int peci_fd,
+                                      uint8_t* cc)
+{
+    struct peci_wr_end_pt_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 (DataLen != 1 && DataLen != 2 && DataLen != 4)
+    {
+        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.tx_len = DataLen;
+    cmd.value = DataVal;
+
+    ret = HW_peci_issue_cmd(PECI_IOC_WR_END_PT_CFG, (char*)&cmd, peci_fd);
+    *cc = cmd.cc;
+
+    return ret;
+}
+
+/*-------------------------------------------------------------------------
+ *  This function provides write access to the EP local PCI configuration space
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_WrEndPointPCIConfigLocal(uint8_t target, uint8_t u8Seg,
+                                          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;
+    EPECIStatus ret;
+
+    if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
+    {
+        return PECI_CC_DRIVER_ERR;
+    }
+
+    ret = peci_WrEndPointConfig_seq(target, PECI_ENDPTCFG_TYPE_LOCAL_PCI, u8Seg,
+                                    u8Bus, u8Device, u8Fcn, u16Reg, DataLen,
+                                    DataVal, peci_fd, cc);
+    peci_Close(peci_fd);
+    return ret;
+}
+
+/*-------------------------------------------------------------------------
+ *  This function provides write access to the EP local PCI configuration space
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_WrEndPointPCIConfig(uint8_t target, uint8_t u8Seg,
+                                     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;
+    EPECIStatus ret;
+
+    if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
+    {
+        return PECI_CC_DRIVER_ERR;
+    }
+    ret = peci_WrEndPointConfig_seq(target, PECI_ENDPTCFG_TYPE_PCI, u8Seg,
+                                    u8Bus, u8Device, u8Fcn, u16Reg, DataLen,
+                                    DataVal, peci_fd, cc);
+    peci_Close(peci_fd);
+    return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function provides write access to PCI MMIO space at
+ * the requested PCI configuration address.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_WrEndPointConfigMmio(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 u8DataLen, uint64_t u64DataVal,
+                                      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_WrEndPointConfigMmio_seq(target, u8Seg, u8Bus, u8Device, u8Fcn,
+                                        u8Bar, u8AddrType, u64Offset, u8DataLen,
+                                        u64DataVal, peci_fd, cc);
+    peci_Close(peci_fd);
+    return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function allows sequential WrEndPointConfig to PCI MMIO with the
+ * provided peci file descriptor.
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_WrEndPointConfigMmio_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 u8DataLen, uint64_t u64DataVal, int peci_fd, uint8_t* cc)
+{
+    struct peci_wr_end_pt_cfg_msg cmd;
+    EPECIStatus ret;
+
+    if (cc == NULL)
+    {
+        return PECI_CC_INVALID_REQ;
+    }
+
+    // Per the PECI spec, the read length must be a byte, word, dword, or qword
+    if (u8DataLen != 1 && u8DataLen != 2 && u8DataLen != 4 && u8DataLen != 8)
+    {
+        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.tx_len = u8DataLen;
+    cmd.value = u64DataVal;
+
+    ret = HW_peci_issue_cmd(PECI_IOC_WR_END_PT_CFG, (char*)&cmd, peci_fd);
+    *cc = cmd.cc;
+
+    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 (u8ReadLen && 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 = (uint8_t)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 || ret == PECI_CC_TIMEOUT)
+    {
+        memcpy(pRawResp, u8RxBuf, u8ReadLen);
+    }
+
+    peci_Close(peci_fd);
+    return ret;
+}
+
+/*-------------------------------------------------------------------------
+ * This function returns the CPUID (Model and stepping) for the given PECI
+ * client address
+ *------------------------------------------------------------------------*/
+EPECIStatus peci_GetCPUID(const uint8_t clientAddr, CPUModel* cpuModel,
+                          uint8_t* stepping, uint8_t* cc)
+{
+    EPECIStatus ret = PECI_CC_SUCCESS;
+    uint32_t cpuid = 0;
+
+    if (cpuModel == NULL || stepping == NULL || cc == NULL)
+    {
+        return PECI_CC_INVALID_REQ;
+    }
+
+    if (peci_Ping(clientAddr) != PECI_CC_SUCCESS)
+    {
+        return PECI_CC_CPU_NOT_PRESENT;
+    }
+
+    ret =
+        peci_RdPkgConfig(clientAddr, PECI_MBX_INDEX_CPU_ID, PECI_PKG_ID_CPU_ID,
+                         sizeof(uint32_t), (uint8_t*)&cpuid, cc);
+
+    // Separate out the model and stepping (bits 3:0) from the CPUID
+    *cpuModel = cpuid & 0xFFFFFFF0;
+    *stepping = (uint8_t)(cpuid & 0x0000000F);
+    return ret;
+}
diff --git a/peci.h b/peci.h
new file mode 100644
index 0000000..70ffb11
--- /dev/null
+++ b/peci.h
@@ -0,0 +1,261 @@
+/*
+// 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
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcpp"
+#pragma GCC diagnostic ignored "-Wvariadic-macros"
+#include <linux/peci-ioctl.h>
+#pragma GCC diagnostic pop
+#include <inttypes.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 = 0x00050650,
+    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,
+    PECI_CC_TIMEOUT,
+} 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 write access to the EP local PCI Configuration space
+EPECIStatus peci_WrEndPointPCIConfigLocal(uint8_t target, uint8_t u8Seg,
+                                          uint8_t u8Bus, uint8_t u8Device,
+                                          uint8_t u8Fcn, uint16_t u16Reg,
+                                          uint8_t DataLen, uint32_t DataVal,
+                                          uint8_t* cc);
+
+// Provides write access to the EP PCI Configuration space
+EPECIStatus peci_WrEndPointPCIConfig(uint8_t target, uint8_t u8Seg,
+                                     uint8_t u8Bus, uint8_t u8Device,
+                                     uint8_t u8Fcn, uint16_t u16Reg,
+                                     uint8_t DataLen, uint32_t DataVal,
+                                     uint8_t* cc);
+
+// Allows sequential write access to the EP PCI Configuration space
+EPECIStatus peci_WrEndPointConfig_seq(uint8_t target, uint8_t u8MsgType,
+                                      uint8_t u8Seg, uint8_t u8Bus,
+                                      uint8_t u8Device, uint8_t u8Fcn,
+                                      uint16_t u16Reg, uint8_t DataLen,
+                                      uint32_t DataVal, int peci_fd,
+                                      uint8_t* cc);
+
+// Provides write access to the EP PCI MMIO space
+EPECIStatus peci_WrEndPointConfigMmio(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 u8DataLen, uint64_t u64DataVal,
+                                      uint8_t* cc);
+
+// Allows sequential write access to the EP PCI MMIO space
+EPECIStatus peci_WrEndPointConfigMmio_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 u8DataLen, uint64_t u64DataVal, 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, int 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* stepping, uint8_t* cc);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/peci_cmds.c b/peci_cmds.c
new file mode 100644
index 0000000..ee23cca
--- /dev/null
+++ b/peci_cmds.c
@@ -0,0 +1,712 @@
+/*
+// 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 <ctype.h>
+#include <inttypes.h>
+#include <peci.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.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("%s [-a <addr>] [-s <size>] <command> [parameters]\n", progname);
+    printf("Options:\n");
+    printf("\t%-6s%s\n", "-h", "Display this help information");
+    printf("\t%-6s%s\n", "-v",
+           "Display additional information about the command");
+    printf("\t%-6s%s\n", "-a",
+           "Address of the target. Accepted values are 48-55 (0x30-0x37). "
+           "Default is 48 (0x30)");
+    printf("\t%-6s%s\n", "-s",
+           "Size of data to read or write in bytes. Accepted values are 1, 2, "
+           "4, 8, and 16. Default is 4");
+    printf("Commands:\n");
+    printf("\t%-28s%s\n", "Ping", "Ping the target");
+    printf("\t%-28s%s\n", "GetTemp", "Get the temperature");
+    printf("\t%-28s%s\n", "GetDIB", "Get the DIB");
+    printf("\t%-28s%s\n", "RdPkgConfig",
+           "Read Package Config <Index Parameter>");
+    printf("\t%-28s%s\n", "WrPkgConfig",
+           "Write Package Config <Index Parameter Data>");
+    printf("\t%-28s%s\n", "RdIAMSR", "MSR Read <Thread Address>");
+    printf("\t%-28s%s\n", "RdPCIConfig", "PCI Read <Bus Dev Func [Reg]>");
+    printf("\t%-28s%s\n", "RdPCIConfigLocal",
+           "Local PCI Read <Bus Dev Func [Reg]>");
+    printf("\t%-28s%s\n", "WrPCIConfigLocal",
+           "Local PCI Write <Bus Dev Func Reg Data>");
+    printf("\t%-28s%s\n", "RdEndpointConfigPCILocal",
+           "Endpoint Local PCI Config Read <Seg Bus Dev Func Reg>");
+    printf("\t%-28s%s\n", "WrEndpointConfigPCILocal",
+           "Endpoint Local PCI Config Write <Seg Bus Dev Func Reg Data>");
+    printf("\t%-28s%s\n", "RdEndpointConfigPCI",
+           "Endpoint PCI Config Read <Seg Bus Dev Func Reg>");
+    printf("\t%-28s%s\n", "WrEndpointConfigPCI",
+           "Endpoint PCI Config Write <Seg Bus Dev Func Reg Data>");
+    printf("\t%-28s%s\n", "RdEndpointConfigMMIO",
+           "Endpoint MMIO Read <AType Bar Seg Bus Dev Func Reg>");
+    printf("\t%-28s%s\n", "WrEndpointConfigMMIO",
+           "Endpoint MMIO Write <AType Bar Seg Bus Dev Func Reg Data>");
+    printf("\t%-28s%s\n", "raw", "Raw PECI command in bytes");
+    printf("\n");
+}
+
+int main(int argc, char* argv[])
+{
+    int c;
+    int i = 0;
+    char* cmd = NULL;
+    EPECIStatus ret;
+    uint8_t address = 0x30; // use default address of 48d
+    uint8_t u8Size = 4;     // default to a DWORD
+    uint32_t u32PciReadVal = 0;
+    uint8_t u8Seg = 0;
+    uint8_t u8Bar = 0;
+    uint8_t u8AddrType = 0;
+    uint8_t u8PciBus = 0;
+    uint8_t u8PciDev = 0;
+    uint8_t u8PciFunc = 0;
+    uint16_t u16PciReg = 0;
+    uint64_t u64Offset = 0;
+    uint32_t u32PciWriteVal = 0;
+    uint64_t u64MmioWriteVal = 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;
+    int index = 0;
+    uint8_t cc = 0;
+    bool verbose = false;
+
+    //
+    // Parse arguments.
+    //
+    while (-1 != (c = getopt(argc, argv, "hva:s:")))
+    {
+        switch (c)
+        {
+            case 'h':
+                Usage(argv[0]);
+                return 0;
+                break;
+
+            case 'v':
+                verbose = true;
+                break;
+
+            case 'a':
+                if (optarg != NULL)
+                    address = (uint8_t)strtoul(optarg, NULL, 0);
+                if (address < MIN_CLIENT_ADDR || address > MAX_CLIENT_ADDR)
+                {
+                    printf("ERROR: Invalid address \"0x%x\"\n", address);
+                    goto ErrorExit;
+                }
+
+                break;
+
+            case 's':
+                if (optarg != NULL)
+                    u8Size = (uint8_t)strtoul(optarg, NULL, 0);
+                if (u8Size != 1 && u8Size != 2 && u8Size != 4 && u8Size != 8 &&
+                    u8Size != 16)
+                {
+                    printf("ERROR: Invalid size \"%d\"\n", u8Size);
+                    goto ErrorExit;
+                }
+                break;
+
+            default:
+                printf("ERROR: Unrecognized option \"-%c\"\n", optopt);
+                goto ErrorExit;
+                break;
+        }
+    }
+
+    // Get the command from the first parameter
+    cmd = argv[optind++];
+    if (cmd == NULL)
+    {
+        Usage(argv[0]);
+        return 0;
+    }
+
+    // Allow any case
+    while (cmd[i])
+    {
+        cmd[i] = (char)tolower((int)cmd[i]);
+        i++;
+    }
+
+    //
+    // Execute the command
+    //
+    if (verbose)
+    {
+        printf("PECI target[0x%x]: ", address);
+    }
+    if (strcmp(cmd, "ping") == 0)
+    {
+        if (verbose)
+        {
+            printf("Pinging ... ");
+        }
+        (0 == peci_Ping(address)) ? printf("Succeeded\n") : printf("Failed\n");
+    }
+    else if (strcmp(cmd, "getdib") == 0)
+    {
+        if (verbose)
+        {
+            printf("GetDIB\n");
+        }
+        ret = peci_GetDIB(address, &dib);
+        if (0 != ret)
+        {
+            printf("ERROR %d: Retrieving DIB failed\n", ret);
+            return 1;
+        }
+        printf("   0x%" PRIx64 "\n", dib);
+    }
+
+    else if (strcmp(cmd, "gettemp") == 0)
+    {
+        if (verbose)
+        {
+            printf("GetTemp\n");
+        }
+        ret = peci_GetTemp(address, &temperature);
+        if (0 != ret)
+        {
+            printf("ERROR %d: Retrieving temperature failed\n", ret);
+            return 1;
+        }
+        printf("   %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);
+    }
+
+    else if (strcmp(cmd, "rdpkgconfig") == 0)
+    {
+        index = argc;
+        switch (argc - optind)
+        {
+            case 2:
+                u16PkgParam = (uint16_t)strtoul(argv[--index], NULL, 0);
+                u8PkgIndex = (uint8_t)strtoul(argv[--index], NULL, 0);
+                break;
+            default:
+                printf("ERROR: Unsupported arguments for Pkg Read\n");
+                goto ErrorExit;
+                break;
+        }
+        if (verbose)
+        {
+            printf("Pkg Read of Index %02x Param %04x\n", u8PkgIndex,
+                   u16PkgParam);
+        }
+        ret = peci_RdPkgConfig(address, u8PkgIndex, u16PkgParam, u8Size,
+                               (uint8_t*)&u32PkgValue, &cc);
+        if (0 != ret)
+        {
+            printf("ERROR %d: command failed\n", ret);
+            printf("   cc:0x%02x\n", cc);
+            return 1;
+        }
+        printf("   cc:0x%02x 0x%0*x\n", cc, u8Size * 2, u32PkgValue);
+    }
+    else if (strcmp(cmd, "wrpkgconfig") == 0)
+    {
+        index = argc;
+        switch (argc - optind)
+        {
+            case 3:
+                u32PkgValue = strtoul(argv[--index], NULL, 0);
+                u16PkgParam = (uint16_t)strtoul(argv[--index], NULL, 0);
+                u8PkgIndex = (uint8_t)strtoul(argv[--index], NULL, 0);
+                break;
+            default:
+                printf("ERROR: Unsupported arguments for Pkg Write\n");
+                goto ErrorExit;
+                break;
+        }
+        if (verbose)
+        {
+            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 (0 != ret)
+        {
+            printf("ERROR %d: command failed\n", ret);
+            printf("   cc:0x%02x\n", cc);
+            return 1;
+        }
+        printf("   cc:0x%02x\n", cc);
+    }
+    else if (strcmp(cmd, "rdiamsr") == 0)
+    {
+        index = argc;
+        switch (argc - optind)
+        {
+            case 2:
+                u16MsrAddr = (uint16_t)strtoul(argv[--index], NULL, 0);
+                u8MsrThread = (uint8_t)strtoul(argv[--index], NULL, 0);
+                break;
+            default:
+                printf("ERROR: Unsupported arguments for MSR Read\n");
+                goto ErrorExit;
+                break;
+        }
+        if (verbose)
+        {
+            printf("MSR Read of Thread %02x MSR %04x\n", u8MsrThread,
+                   u16MsrAddr);
+        }
+        ret = peci_RdIAMSR(address, u8MsrThread, u16MsrAddr, &u64MsrVal, &cc);
+        if (0 != ret)
+        {
+            printf("ERROR %d: command failed\n", ret);
+            printf("   cc:0x%02x\n", cc);
+            return 1;
+        }
+        printf("   cc:0x%02x 0x%0*llx\n", cc, u8Size * 2, u64MsrVal);
+    }
+    else if (strcmp(cmd, "rdpciconfig") == 0)
+    {
+        index = argc;
+        switch (argc - optind)
+        {
+            case 4:
+                u16PciReg = (uint16_t)strtoul(argv[--index], NULL, 0);
+                /* FALLTHROUGH */
+            case 3:
+                u8PciFunc = (uint8_t)strtoul(argv[--index], NULL, 0);
+                /* FALLTHROUGH */
+            case 2:
+                u8PciDev = (uint8_t)strtoul(argv[--index], NULL, 0);
+                /* FALLTHROUGH */
+            case 1:
+                u8PciBus = (uint8_t)strtoul(argv[--index], NULL, 0);
+                break;
+            default:
+                printf("ERROR: Unsupported arguments for PCI Read\n");
+                goto ErrorExit;
+                break;
+        }
+        if (verbose)
+        {
+            printf("PCI Read of %02x:%02x:%02x Reg %02x\n", u8PciBus, u8PciDev,
+                   u8PciFunc, u16PciReg);
+        }
+        ret = peci_RdPCIConfig(address, u8PciBus, u8PciDev, u8PciFunc,
+                               u16PciReg, (uint8_t*)&u32PciReadVal, &cc);
+        if (0 != ret)
+        {
+            printf("ERROR %d: command failed\n", ret);
+            printf("   cc:0x%02x\n", cc);
+            return 1;
+        }
+        printf("   cc:0x%02x 0x%0*x\n", cc, u8Size * 2, u32PciReadVal);
+    }
+    else if (strcmp(cmd, "rdpciconfiglocal") == 0)
+    {
+        index = argc;
+        switch (argc - optind)
+        {
+            case 4:
+                u16PciReg = (uint16_t)strtoul(argv[--index], NULL, 0);
+                /* FALLTHROUGH */
+            case 3:
+                u8PciFunc = (uint8_t)strtoul(argv[--index], NULL, 0);
+                /* FALLTHROUGH */
+            case 2:
+                u8PciDev = (uint8_t)strtoul(argv[--index], NULL, 0);
+                /* FALLTHROUGH */
+            case 1:
+                u8PciBus = (uint8_t)strtoul(argv[--index], NULL, 0);
+                break;
+            default:
+                printf("ERROR: Unsupported arguments for Local PCI Read\n");
+                goto ErrorExit;
+                break;
+        }
+        if (verbose)
+        {
+            printf("Local PCI Read of %02x:%02x:%02x Reg %02x\n", u8PciBus,
+                   u8PciDev, u8PciFunc, u16PciReg);
+        }
+        ret = peci_RdPCIConfigLocal(address, u8PciBus, u8PciDev, u8PciFunc,
+                                    u16PciReg, u8Size, (uint8_t*)&u32PciReadVal,
+                                    &cc);
+        if (0 != ret)
+        {
+            printf("ERROR %d: command failed\n", ret);
+            printf("   cc:0x%02x\n", cc);
+            return 1;
+        }
+        printf("   cc:0x%02x 0x%0*x\n", cc, u8Size * 2, u32PciReadVal);
+    }
+    else if (strcmp(cmd, "wrpciconfiglocal") == 0)
+    {
+        index = argc;
+        u32PciWriteVal = strtoul(argv[--index], NULL, 0);
+        switch (argc - optind)
+        {
+            case 5:
+                u16PciReg = (uint16_t)strtoul(argv[--index], NULL, 0);
+                /* FALLTHROUGH */
+            case 4:
+                u8PciFunc = (uint8_t)strtoul(argv[--index], NULL, 0);
+                /* FALLTHROUGH */
+            case 3:
+                u8PciDev = (uint8_t)strtoul(argv[--index], NULL, 0);
+                /* FALLTHROUGH */
+            case 2:
+                u8PciBus = (uint8_t)strtoul(argv[--index], NULL, 0);
+                break;
+            default:
+                printf("ERROR: Unsupported arguments for Local PCI Write\n");
+                goto ErrorExit;
+                break;
+        }
+        if (verbose)
+        {
+            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 (0 != ret)
+        {
+            printf("ERROR %d: command failed\n", ret);
+            printf("   cc:0x%02x\n", cc);
+            return 1;
+        }
+        printf("   cc:0x%02x\n", cc);
+    }
+    else if (strcmp(cmd, "rdendpointconfigpcilocal") == 0)
+    {
+        index = argc;
+        switch (argc - optind)
+        {
+            case 5:
+                u16PciReg = (uint16_t)strtoul(argv[--index], NULL, 0);
+                u8PciFunc = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8PciDev = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8PciBus = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8Seg = (uint8_t)strtoul(argv[--index], NULL, 0);
+                break;
+
+            default:
+                printf("ERROR: Unsupported arguments for Endpoint Local PCI "
+                       "Read\n");
+                goto ErrorExit;
+        }
+        if (verbose)
+        {
+            printf(
+                "Endpoint Local PCI Read of Seg:%02x %02x:%02x:%02x Reg %02x\n",
+                u8Seg, u8PciBus, u8PciDev, u8PciFunc, u16PciReg);
+        }
+        ret = peci_RdEndPointConfigPciLocal(address, u8Seg, u8PciBus, u8PciDev,
+                                            u8PciFunc, u16PciReg, u8Size,
+                                            (uint8_t*)&u32PciReadVal, &cc);
+        if (0 != ret)
+        {
+            printf("ERROR %d: command failed\n", ret);
+            printf("   cc:0x%02x\n", cc);
+            return 1;
+        }
+        printf("   cc:0x%02x 0x%0*x\n", cc, u8Size * 2, u32PciReadVal);
+    }
+    else if (strcmp(cmd, "wrendpointconfigpcilocal") == 0)
+    {
+        index = argc;
+        switch (argc - optind)
+        {
+            case 6:
+                u32PciWriteVal = strtoul(argv[--index], NULL, 0);
+                u16PciReg = (uint16_t)strtoul(argv[--index], NULL, 0);
+                u8PciFunc = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8PciDev = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8PciBus = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8Seg = (uint8_t)strtoul(argv[--index], NULL, 0);
+                break;
+
+            default:
+                printf("ERROR: Unsupported arguments for Endpoint Local PCI "
+                       "Write\n");
+                goto ErrorExit;
+        }
+        if (verbose)
+        {
+            printf("Endpoint Local PCI Write of Seg:%02x %02x:%02x:%02x Reg "
+                   "%02x: 0x%0*x\n",
+                   u8Seg, u8PciBus, u8PciDev, u8PciFunc, u16PciReg, u8Size * 2,
+                   u32PciWriteVal);
+        }
+        ret = peci_WrEndPointPCIConfigLocal(address, u8Seg, u8PciBus, u8PciDev,
+                                            u8PciFunc, u16PciReg, u8Size,
+                                            u32PciWriteVal, &cc);
+        if (0 != ret)
+        {
+            printf("ERROR %d: command failed\n", ret);
+            printf("   cc:0x%02x\n", cc);
+            return 1;
+        }
+        printf("   cc:0x%02x\n", cc);
+    }
+    else if (strcmp(cmd, "rdendpointconfigpci") == 0)
+    {
+        index = argc;
+        switch (argc - optind)
+        {
+            case 5:
+                u16PciReg = (uint16_t)strtoul(argv[--index], NULL, 0);
+                u8PciFunc = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8PciDev = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8PciBus = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8Seg = (uint8_t)strtoul(argv[--index], NULL, 0);
+                break;
+
+            default:
+                printf("ERROR: Unsupported arguments for Endpoint PCI Read\n");
+                goto ErrorExit;
+        }
+        if (verbose)
+        {
+            printf("Endpoint PCI Read of Seg:%02x %02x:%02x:%02x Reg %02x\n",
+                   u8Seg, u8PciBus, u8PciDev, u8PciFunc, u16PciReg);
+        }
+        ret = peci_RdEndPointConfigPci(address, u8Seg, u8PciBus, u8PciDev,
+                                       u8PciFunc, u16PciReg, u8Size,
+                                       (uint8_t*)&u32PciReadVal, &cc);
+        if (0 != ret)
+        {
+            printf("ERROR %d: command failed\n", ret);
+            printf("   cc:0x%02x\n", cc);
+            return 1;
+        }
+        printf("   cc:0x%02x 0x%0*x\n", cc, u8Size * 2, u32PciReadVal);
+    }
+    else if (strcmp(cmd, "wrendpointconfigpci") == 0)
+    {
+        index = argc;
+        switch (argc - optind)
+        {
+            case 6:
+                u32PciWriteVal = strtoul(argv[--index], NULL, 0);
+                u16PciReg = (uint16_t)strtoul(argv[--index], NULL, 0);
+                u8PciFunc = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8PciDev = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8PciBus = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8Seg = (uint8_t)strtoul(argv[--index], NULL, 0);
+                break;
+
+            default:
+                printf("ERROR: Unsupported arguments for Endpoint PCI Write\n");
+                goto ErrorExit;
+        }
+        if (verbose)
+        {
+            printf("Endpoint PCI Write of Seg:%02x %02x:%02x:%02x Reg %02x: "
+                   "0x%0*x\n",
+                   u8Seg, u8PciBus, u8PciDev, u8PciFunc, u16PciReg, u8Size * 2,
+                   u32PciWriteVal);
+        }
+        ret = peci_WrEndPointPCIConfig(address, u8Seg, u8PciBus, u8PciDev,
+                                       u8PciFunc, u16PciReg, u8Size,
+                                       u32PciWriteVal, &cc);
+        if (0 != ret)
+        {
+            printf("ERROR %d: command failed\n", ret);
+            printf("   cc:0x%02x\n", cc);
+            return 1;
+        }
+        printf("   cc:0x%02x\n", cc);
+    }
+    else if (strcmp(cmd, "rdendpointconfigmmio") == 0)
+    {
+        index = argc;
+        switch (argc - optind)
+        {
+            case 7:
+                u64Offset = strtoull(argv[--index], NULL, 0);
+                u8PciFunc = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8PciDev = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8PciBus = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8Seg = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8Bar = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8AddrType = (uint8_t)strtoul(argv[--index], NULL, 0);
+                break;
+
+            default:
+                printf("ERROR: Unsupported arguments for Endpoint MMIO Read\n");
+                goto ErrorExit;
+        }
+        if (verbose)
+        {
+            printf("Endpoint MMIO Read of Seg:%02x %02x:%02x:%02x AType:%02x "
+                   "Bar:%02x Offset:0x%" PRIx64 "\n",
+                   u8Seg, u8PciBus, u8PciDev, u8PciFunc, u8AddrType, u8Bar,
+                   u64Offset);
+        }
+        ret = peci_RdEndPointConfigMmio(address, u8Seg, u8PciBus, u8PciDev,
+                                        u8PciFunc, u8Bar, u8AddrType, u64Offset,
+                                        u8Size, (uint8_t*)&u32PciReadVal, &cc);
+        if (0 != ret)
+        {
+            printf("ERROR %d: command failed\n", ret);
+            printf("   cc:0x%02x\n", cc);
+            return 1;
+        }
+        printf("   cc:0x%02x 0x%0*x\n", cc, u8Size * 2, u32PciReadVal);
+    }
+    else if (strcmp(cmd, "wrendpointconfigmmio") == 0)
+    {
+        index = argc;
+        switch (argc - optind)
+        {
+            case 8:
+                u64MmioWriteVal = strtoull(argv[--index], NULL, 0);
+                u64Offset = strtoull(argv[--index], NULL, 0);
+                u8PciFunc = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8PciDev = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8PciBus = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8Seg = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8Bar = (uint8_t)strtoul(argv[--index], NULL, 0);
+                u8AddrType = (uint8_t)strtoul(argv[--index], NULL, 0);
+                break;
+
+            default:
+                printf(
+                    "ERROR: Unsupported arguments for Endpoint MMIO Write\n");
+                goto ErrorExit;
+        }
+        if (verbose)
+        {
+            printf("Endpoint MMIO Write of Seg:%02x %02x:%02x:%02x AType:%02x "
+                   "Bar:%02x Offset:0x%" PRIx64 ": 0x%0*" PRIx64 "\n",
+                   u8Seg, u8PciBus, u8PciDev, u8PciFunc, u8AddrType, u8Bar,
+                   u64Offset, u8Size * 2, u64MmioWriteVal);
+        }
+        ret = peci_WrEndPointConfigMmio(address, u8Seg, u8PciBus, u8PciDev,
+                                        u8PciFunc, u8Bar, u8AddrType, u64Offset,
+                                        u8Size, u64MmioWriteVal, &cc);
+        if (0 != ret)
+        {
+            printf("ERROR %d: command failed\n", ret);
+            printf("   cc:0x%02x\n", cc);
+            return 1;
+        }
+        printf("   cc:0x%02x\n", cc);
+    }
+    else if (strcmp(cmd, "raw") == 0)
+    {
+        if ((argc - optind) < 3)
+        {
+            printf("ERROR: Unsupported arguments for raw command\n");
+            goto ErrorExit;
+        }
+
+        // Address is provided in the first byte of the PECI command
+        uint8_t rawAddr = (uint8_t)strtoul(argv[optind++], NULL, 0);
+        // Write length is provided in the second byte of the PECI command
+        uint8_t writeLength = (uint8_t)strtoul(argv[optind++], NULL, 0);
+        // Read length is provided in the third byte of the PECI command
+        uint8_t readLength = (uint8_t)strtoul(argv[optind++], NULL, 0);
+
+        // remaining parameters should match write length
+        if ((argc - optind) != writeLength)
+        {
+            printf("ERROR: Incorrect write length for raw command\n");
+            goto ErrorExit;
+        }
+        uint8_t* rawCmd = (uint8_t*)calloc(writeLength, sizeof(uint8_t));
+        if (rawCmd == NULL)
+        {
+            // calloc failed, abort the sequence
+            printf("Raw command memory allocation failed\n");
+            return 1;
+        }
+        for (i = 0; i < writeLength; i++)
+        {
+            rawCmd[i] = (uint8_t)strtoul(argv[i + optind], NULL, 0);
+        }
+        if (verbose)
+        {
+            printf("Raw command: %02x %02x %02x ", rawAddr, writeLength,
+                   readLength);
+            for (i = 0; i < writeLength; i++)
+            {
+                printf("0x%02x ", rawCmd[i]);
+            }
+            printf("\n");
+        }
+
+        uint8_t* rawResp = (uint8_t*)calloc(readLength, sizeof(uint8_t));
+        if (rawResp == NULL)
+        {
+            // calloc failed, abort the sequence
+            printf("Raw command memory allocation failed\n");
+            free(rawCmd);
+            return 1;
+        }
+        ret = peci_raw(rawAddr, readLength, rawCmd, writeLength, rawResp,
+                       readLength);
+        if (0 != ret)
+        {
+            printf("ERROR %d: command failed\n", ret);
+            free(rawCmd);
+            free(rawResp);
+            return 1;
+        }
+        printf("   ");
+        for (i = 0; i < readLength; i++)
+        {
+            printf("0x%02x ", rawResp[i]);
+        }
+        printf("\n");
+
+        free(rawCmd);
+        free(rawResp);
+    }
+    else
+    {
+        printf("ERROR: Unrecognized command\n");
+        goto ErrorExit;
+    }
+
+    return 0;
+
+ErrorExit:
+    Usage(argv[0]);
+    return 1;
+}