diff options
author | Jason M. Bills <jason.m.bills@linux.intel.com> | 2019-08-28 02:58:07 +0300 |
---|---|---|
committer | Jason M. Bills <jason.m.bills@linux.intel.com> | 2019-09-13 22:00:03 +0300 |
commit | 4ee8ea263091e636e69c7d094ac9b688f78078e4 (patch) | |
tree | 9f4dfdd1eb9ace1761d5d49f300698388da4e4a6 /libpeci | |
parent | b1343f99d63781ee3b9ba87575183bbbff1d5476 (diff) | |
download | provingground-4ee8ea263091e636e69c7d094ac9b688f78078e4.tar.xz |
Add libpeci as a standalone library
Tested:
Verified that PECI functions and the peci_cmds application are
available and functional from the standalone library.
Change-Id: I9c0d3ba73db0f7e4bd7495dbeaae9cb60e3cda63
Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
Diffstat (limited to 'libpeci')
-rw-r--r-- | libpeci/.clang-format | 98 | ||||
-rw-r--r-- | libpeci/CMakeLists.txt | 20 | ||||
-rw-r--r-- | libpeci/peci.c | 1081 | ||||
-rw-r--r-- | libpeci/peci.h | 224 | ||||
-rw-r--r-- | libpeci/peci_cmds.c | 370 |
5 files changed, 1793 insertions, 0 deletions
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; +} |