| /* |
| // 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 <stdlib.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <syslog.h> |
| #include <time.h> |
| #include <unistd.h> |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wcpp" |
| #pragma GCC diagnostic ignored "-Wvariadic-macros" |
| #include <linux/peci-ioctl.h> |
| #pragma GCC diagnostic pop |
| |
| EPECIStatus peci_GetDIB_seq(uint8_t target, uint64_t* dib, int peci_fd); |
| |
| char* peci_device_list[2]; |
| #define DEV_NAME_SIZE 64 |
| /*------------------------------------------------------------------------- |
| * This function sets the name of the PECI device file to use. |
| * If the PECI device name is null try "/dev/peci-default", |
| * if "/dev/peci-default" does not exist, fall back to "/dev/peci-0" |
| *------------------------------------------------------------------------*/ |
| void peci_SetDevName(char* peci_dev) |
| { |
| static char peci_name_new[DEV_NAME_SIZE] = {0}; |
| |
| if (peci_dev) |
| { |
| strncpy(peci_name_new, peci_dev, sizeof(peci_name_new)); |
| peci_name_new[DEV_NAME_SIZE - 1] = '\0'; |
| peci_device_list[0] = peci_name_new; |
| peci_device_list[1] = NULL; |
| syslog(LOG_INFO, "PECI set dev name to %s\n", peci_device_list[0]); |
| } |
| else |
| { |
| peci_device_list[0] = "/dev/peci-default"; |
| peci_device_list[1] = "/dev/peci-0"; |
| syslog(LOG_INFO, "PECI set dev names to %s, %s\n", peci_device_list[0], |
| peci_device_list[1]); |
| } |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function initializes PECI device name when a shared library |
| * is loaded, typically during program startup. |
| *------------------------------------------------------------------------*/ |
| static void init() __attribute__((constructor)); |
| static void init() |
| { |
| // By default PECI_DEV is not defined in the environment, |
| // so this will call peci_SetDevName(NULL) and initialize |
| // PECI device name to defaults. |
| peci_SetDevName(getenv("PECI_DEV")); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * 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"); |
| } |
| } |
| |
| /*------------------------------------------------------------------------- |
| * 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 = {0}; |
| sRequest.tv_sec = 0; |
| sRequest.tv_nsec = PECI_TIMEOUT_RESOLUTION_MS * 1000 * 1000; |
| int timeout_count = 0; |
| char* peci_device = peci_device_list[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); |
| if (*peci_fd == -1 && errno == ENOENT && peci_device_list[1]) |
| { |
| peci_device = peci_device_list[1]; |
| *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); |
| } |
| break; |
| 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 = PECI_CC_SUCCESS; |
| uint8_t u8Reg[4] = {0}; |
| 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 = PECI_CC_SUCCESS; |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| 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 = PECI_CC_SUCCESS; |
| struct peci_ping_msg cmd = {0}; |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| 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 = PECI_CC_SUCCESS; |
| |
| if (dib == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| 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 = {0}; |
| EPECIStatus ret = PECI_CC_SUCCESS; |
| cmd.addr = target; |
| |
| if (dib == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| 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 = {0}; |
| |
| if (temperature == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| 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) |
| { |
| // Default to domain ID 0 |
| return peci_RdPkgConfig_dom(target, 0, u8Index, u16Value, u8ReadLen, |
| pPkgConfig, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function provides read access to the package configuration |
| * space within the processor in the specified domain. |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_RdPkgConfig_dom( |
| uint8_t target, uint8_t domainId, uint8_t u8Index, uint16_t u16Value, |
| uint8_t u8ReadLen, uint8_t* pPkgConfig, uint8_t* cc) |
| { |
| int peci_fd = -1; |
| EPECIStatus ret = PECI_CC_SUCCESS; |
| |
| if (pPkgConfig == NULL || cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| if (peci_Open(&peci_fd) != PECI_CC_SUCCESS) |
| { |
| return PECI_CC_DRIVER_ERR; |
| } |
| ret = peci_RdPkgConfig_seq_dom(target, domainId, 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) |
| { |
| // Default to domain ID 0 |
| return peci_RdPkgConfig_seq_dom(target, 0, u8Index, u16Value, u8ReadLen, |
| pPkgConfig, peci_fd, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function allows sequential RdPkgConfig with the provided |
| * peci file descriptor in the specified domain. |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_RdPkgConfig_seq_dom( |
| uint8_t target, uint8_t domainId, 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 = {0}; |
| EPECIStatus ret = PECI_CC_SUCCESS; |
| |
| if (pPkgConfig == NULL || cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| 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; |
| cmd.domain_id = domainId; |
| |
| 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) |
| { |
| // Default to domain ID 0 |
| return peci_WrPkgConfig_dom(target, 0, u8Index, u16Param, u32Value, |
| u8WriteLen, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function provides write access to the package configuration |
| * space within the processor in the specified domain |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_WrPkgConfig_dom( |
| uint8_t target, uint8_t domainId, uint8_t u8Index, uint16_t u16Param, |
| uint32_t u32Value, uint8_t u8WriteLen, uint8_t* cc) |
| { |
| int peci_fd = -1; |
| EPECIStatus ret = PECI_CC_SUCCESS; |
| |
| if (cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| if (peci_Open(&peci_fd) != PECI_CC_SUCCESS) |
| { |
| return PECI_CC_DRIVER_ERR; |
| } |
| ret = peci_WrPkgConfig_seq_dom(target, domainId, 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) |
| { |
| // Default to domain ID 0 |
| return peci_WrPkgConfig_seq_dom(target, 0, u8Index, u16Param, u32Value, |
| u8WriteLen, peci_fd, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function allows sequential WrPkgConfig with the provided |
| * peci file descriptor in the specified domain. |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_WrPkgConfig_seq_dom( |
| uint8_t target, uint8_t domainId, 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 = {0}; |
| EPECIStatus ret = PECI_CC_SUCCESS; |
| |
| if (cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| 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; |
| cmd.domain_id = domainId; |
| |
| 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) |
| { |
| // Default to domain ID 0 |
| return peci_RdIAMSR_dom(target, 0, threadID, MSRAddress, u64MsrVal, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function provides read access to Model Specific Registers |
| * defined in the processor doc in the specified domain. |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_RdIAMSR_dom(uint8_t target, uint8_t domainId, uint8_t threadID, |
| uint16_t MSRAddress, uint64_t* u64MsrVal, |
| uint8_t* cc) |
| { |
| int peci_fd = -1; |
| struct peci_rd_ia_msr_msg cmd = {0}; |
| EPECIStatus ret = PECI_CC_SUCCESS; |
| |
| if (u64MsrVal == NULL || cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| 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 |
| cmd.domain_id = domainId; |
| |
| 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) |
| { |
| // Default to domain ID 0 |
| return peci_RdPCIConfig_dom(target, 0, u8Bus, u8Device, u8Fcn, u16Reg, |
| pPCIData, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function provides read access to the PCI configuration space at |
| * the requested PCI configuration address in the specified domain. |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_RdPCIConfig_dom( |
| uint8_t target, uint8_t domainId, uint8_t u8Bus, uint8_t u8Device, |
| uint8_t u8Fcn, uint16_t u16Reg, uint8_t* pPCIData, uint8_t* cc) |
| { |
| int peci_fd = -1; |
| EPECIStatus ret = PECI_CC_SUCCESS; |
| |
| if (pPCIData == NULL || cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| if (peci_Open(&peci_fd) != PECI_CC_SUCCESS) |
| { |
| return PECI_CC_DRIVER_ERR; |
| } |
| ret = peci_RdPCIConfig_seq_dom(target, domainId, 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) |
| { |
| // Default to domain ID 0 |
| return peci_RdPCIConfig_seq_dom(target, 0, u8Bus, u8Device, u8Fcn, u16Reg, |
| pPCIData, peci_fd, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function allows sequential RdPCIConfig with the provided |
| * peci file descriptor in the specified domain. |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_RdPCIConfig_seq_dom( |
| uint8_t target, uint8_t domainId, 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 = {0}; |
| EPECIStatus ret = PECI_CC_SUCCESS; |
| |
| if (pPCIData == NULL || cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| 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; |
| cmd.domain_id = domainId; |
| |
| 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) |
| { |
| // Default to domain ID 0 |
| return peci_RdPCIConfigLocal_dom(target, 0, u8Bus, u8Device, u8Fcn, u16Reg, |
| u8ReadLen, pPCIReg, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function provides read access to the local PCI configuration space in |
| * the specified domain |
| *------------------------------------------------------------------------*/ |
| EPECIStatus |
| peci_RdPCIConfigLocal_dom(uint8_t target, uint8_t domainId, 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 = PECI_CC_SUCCESS; |
| |
| if (pPCIReg == NULL || cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| if (peci_Open(&peci_fd) != PECI_CC_SUCCESS) |
| { |
| return PECI_CC_DRIVER_ERR; |
| } |
| ret = |
| peci_RdPCIConfigLocal_seq_dom(target, domainId, 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) |
| { |
| // Default to domain ID 0 |
| return peci_RdPCIConfigLocal_seq_dom( |
| target, 0, u8Bus, u8Device, u8Fcn, u16Reg, u8ReadLen, pPCIReg, peci_fd, |
| cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function allows sequential RdPCIConfigLocal with the provided |
| * peci file descriptor in the specified domain. |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_RdPCIConfigLocal_seq_dom( |
| uint8_t target, uint8_t domainId, 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 = {0}; |
| EPECIStatus ret = PECI_CC_SUCCESS; |
| |
| if (pPCIReg == NULL || cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| 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; |
| cmd.domain_id = domainId; |
| |
| 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) |
| { |
| // Default to domain ID 0 |
| return peci_WrPCIConfigLocal_dom(target, 0, u8Bus, u8Device, u8Fcn, u16Reg, |
| DataLen, DataVal, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function provides write access to the local PCI configuration space in |
| * the specified domain |
| *------------------------------------------------------------------------*/ |
| EPECIStatus |
| peci_WrPCIConfigLocal_dom(uint8_t target, uint8_t domainId, 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 = {0}; |
| EPECIStatus ret = PECI_CC_SUCCESS; |
| |
| if (cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| 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; |
| cmd.domain_id = domainId; |
| |
| 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 in |
| * the specified domain |
| *------------------------------------------------------------------------*/ |
| static EPECIStatus peci_RdEndPointConfigPciCommon_dom( |
| uint8_t target, uint8_t domainId, 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 = {0}; |
| EPECIStatus ret = PECI_CC_SUCCESS; |
| |
| if (pPCIData == NULL || cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| 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; |
| cmd.domain_id = domainId; |
| |
| 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) |
| { |
| // Default to domain ID 0 |
| return peci_RdEndPointConfigPci_dom(target, 0, u8Seg, u8Bus, u8Device, |
| u8Fcn, u16Reg, u8ReadLen, pPCIData, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function provides read access to the PCI configuration space at |
| * the requested PCI configuration address in the specified domain. |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_RdEndPointConfigPci_dom( |
| uint8_t target, uint8_t domainId, 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 = PECI_CC_SUCCESS; |
| |
| if (pPCIData == NULL || cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| if (peci_Open(&peci_fd) != PECI_CC_SUCCESS) |
| { |
| return PECI_CC_DRIVER_ERR; |
| } |
| ret = peci_RdEndPointConfigPci_seq_dom( |
| target, domainId, 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) |
| { |
| // Default to domain ID 0 |
| return peci_RdEndPointConfigPci_seq_dom( |
| target, 0, u8Seg, u8Bus, u8Device, u8Fcn, u16Reg, u8ReadLen, pPCIData, |
| peci_fd, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function allows sequential RdEndPointConfig to PCI with the provided |
| * peci file descriptor in the specified domain. |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_RdEndPointConfigPci_seq_dom( |
| uint8_t target, uint8_t domainId, 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; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| 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_dom( |
| target, domainId, 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) |
| { |
| // Default to domain ID 0 |
| return peci_RdEndPointConfigPciLocal_dom( |
| target, 0, u8Seg, u8Bus, u8Device, u8Fcn, u16Reg, u8ReadLen, pPCIData, |
| cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function provides read access to the Local PCI configuration space at |
| * the requested PCI configuration address in the specified domain. |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_RdEndPointConfigPciLocal_dom( |
| uint8_t target, uint8_t domainId, 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 = PECI_CC_SUCCESS; |
| |
| if (pPCIData == NULL || cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| if (peci_Open(&peci_fd) != PECI_CC_SUCCESS) |
| { |
| return PECI_CC_DRIVER_ERR; |
| } |
| ret = peci_RdEndPointConfigPciLocal_seq_dom( |
| target, domainId, 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) |
| { |
| // Default to domain ID 0 |
| return peci_RdEndPointConfigPciLocal_seq_dom( |
| target, 0, u8Seg, u8Bus, u8Device, u8Fcn, u16Reg, u8ReadLen, pPCIData, |
| peci_fd, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function allows sequential RdEndPointConfig to PCI Local with the |
| * provided peci file descriptor in the specified domain. |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_RdEndPointConfigPciLocal_seq_dom( |
| uint8_t target, uint8_t domainId, 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; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| 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_dom( |
| target, domainId, 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) |
| { |
| // Default to domain ID 0 |
| return peci_RdEndPointConfigMmio_dom( |
| target, 0, u8Seg, u8Bus, u8Device, u8Fcn, u8Bar, u8AddrType, u64Offset, |
| u8ReadLen, pMmioData, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function provides read access to PCI MMIO space at |
| * the requested PCI configuration address in the specified domain. |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_RdEndPointConfigMmio_dom( |
| uint8_t target, uint8_t domainId, 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 = PECI_CC_SUCCESS; |
| |
| if (pMmioData == NULL || cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| if (peci_Open(&peci_fd) != PECI_CC_SUCCESS) |
| { |
| return PECI_CC_DRIVER_ERR; |
| } |
| ret = peci_RdEndPointConfigMmio_seq_dom( |
| target, domainId, 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) |
| { |
| // Default to domain ID 0 |
| return peci_RdEndPointConfigMmio_seq_dom( |
| target, 0, u8Seg, u8Bus, u8Device, u8Fcn, u8Bar, u8AddrType, u64Offset, |
| u8ReadLen, pMmioData, peci_fd, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function allows sequential RdEndPointConfig to PCI MMIO with the |
| * provided peci file descriptor in the specified domain. |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_RdEndPointConfigMmio_seq_dom( |
| uint8_t target, uint8_t domainId, 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 = {0}; |
| EPECIStatus ret = PECI_CC_SUCCESS; |
| |
| if (pMmioData == NULL || cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| 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; |
| cmd.domain_id = domainId; |
| |
| 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) |
| { |
| // Default to domain ID 0 |
| return peci_WrEndPointConfig_seq_dom( |
| target, 0, u8MsgType, u8Seg, u8Bus, u8Device, u8Fcn, u16Reg, DataLen, |
| DataVal, peci_fd, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function allows sequential peci_WrEndPointConfig to PCI EndPoint with |
| * the provided peci file descriptor in the specified domain. |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_WrEndPointConfig_seq_dom( |
| uint8_t target, uint8_t domainId, 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 = {0}; |
| EPECIStatus ret = PECI_CC_SUCCESS; |
| |
| if (cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| 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; |
| cmd.domain_id = domainId; |
| |
| 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) |
| { |
| // Default to domain ID 0 |
| return peci_WrEndPointPCIConfigLocal_dom( |
| target, 0, u8Seg, u8Bus, u8Device, u8Fcn, u16Reg, DataLen, DataVal, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function provides write access to the EP local PCI configuration space |
| * in the specified domain |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_WrEndPointPCIConfigLocal_dom( |
| uint8_t target, uint8_t domainId, 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 = PECI_CC_SUCCESS; |
| |
| if (peci_Open(&peci_fd) != PECI_CC_SUCCESS) |
| { |
| return PECI_CC_DRIVER_ERR; |
| } |
| |
| ret = peci_WrEndPointConfig_seq_dom( |
| target, domainId, 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) |
| { |
| // Default to domain ID 0 |
| return peci_WrEndPointPCIConfig_dom(target, 0, u8Seg, u8Bus, u8Device, |
| u8Fcn, u16Reg, DataLen, DataVal, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function provides write access to the EP local PCI configuration space |
| * in the specified domain |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_WrEndPointPCIConfig_dom( |
| uint8_t target, uint8_t domainId, 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 = PECI_CC_SUCCESS; |
| |
| if (peci_Open(&peci_fd) != PECI_CC_SUCCESS) |
| { |
| return PECI_CC_DRIVER_ERR; |
| } |
| ret = peci_WrEndPointConfig_seq_dom( |
| target, domainId, 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) |
| { |
| // Default to domain ID 0 |
| return peci_WrEndPointConfigMmio_dom( |
| target, 0, u8Seg, u8Bus, u8Device, u8Fcn, u8Bar, u8AddrType, u64Offset, |
| u8DataLen, u64DataVal, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function provides write access to PCI MMIO space at |
| * the requested PCI configuration address in the specified domain. |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_WrEndPointConfigMmio_dom( |
| uint8_t target, uint8_t domainId, 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 = PECI_CC_SUCCESS; |
| |
| if (cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| if (peci_Open(&peci_fd) != PECI_CC_SUCCESS) |
| { |
| return PECI_CC_DRIVER_ERR; |
| } |
| ret = peci_WrEndPointConfigMmio_seq_dom( |
| target, domainId, 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) |
| { |
| // Default to domain ID 0 |
| return peci_WrEndPointConfigMmio_seq_dom( |
| target, 0, u8Seg, u8Bus, u8Device, u8Fcn, u8Bar, u8AddrType, u64Offset, |
| u8DataLen, u64DataVal, peci_fd, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function allows sequential WrEndPointConfig to PCI MMIO with the |
| * provided peci file descriptor in the specified domain. |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_WrEndPointConfigMmio_seq_dom( |
| uint8_t target, uint8_t domainId, 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 = {0}; |
| EPECIStatus ret = PECI_CC_SUCCESS; |
| |
| if (cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| 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; |
| cmd.domain_id = domainId; |
| |
| 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) |
| { |
| // Default to domain ID 0 |
| return peci_CrashDump_Discovery_dom(target, 0, subopcode, param0, param1, |
| param2, u8ReadLen, pData, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function provides crashdump discovery data over PECI in the specified |
| * domain |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_CrashDump_Discovery_dom( |
| uint8_t target, uint8_t domainId, 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 = {0}; |
| EPECIStatus ret = PECI_CC_SUCCESS; |
| |
| if (pData == NULL || cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| 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; |
| cmd.domain_id = domainId; |
| |
| 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) |
| { |
| // Default to domain ID 0 |
| return peci_CrashDump_GetFrame_dom(target, 0, param0, param1, param2, |
| u8ReadLen, pData, cc); |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function provides crashdump GetFrame data over PECI in the specified |
| * domain |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_CrashDump_GetFrame_dom( |
| uint8_t target, uint8_t domainId, 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 = {0}; |
| EPECIStatus ret = PECI_CC_SUCCESS; |
| |
| if (pData == NULL || cc == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| 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; |
| cmd.domain_id = domainId; |
| |
| 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; |
| if (u8ReadLen && pRawResp == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| if (peci_Open(&peci_fd) != PECI_CC_SUCCESS) |
| { |
| return PECI_CC_DRIVER_ERR; |
| } |
| |
| EPECIStatus ret = peci_raw_seq(target, u8ReadLen, pRawCmd, cmdSize, |
| pRawResp, respSize, peci_fd); |
| peci_Close(peci_fd); |
| return ret; |
| } |
| |
| /*------------------------------------------------------------------------- |
| * This function provides sequential raw PECI command access |
| *------------------------------------------------------------------------*/ |
| EPECIStatus peci_raw_seq(uint8_t target, uint8_t u8ReadLen, |
| const uint8_t* pRawCmd, const uint32_t cmdSize, |
| uint8_t* pRawResp, uint32_t respSize, int peci_fd) |
| { |
| struct peci_xfer_msg cmd = {0}; |
| uint8_t u8TxBuf[PECI_BUFFER_SIZE] = {0}; |
| uint8_t u8RxBuf[PECI_BUFFER_SIZE] = {0}; |
| EPECIStatus ret = PECI_CC_SUCCESS; |
| |
| if (u8ReadLen && pRawResp == NULL) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // The target address must be in the valid range |
| if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR) |
| { |
| return PECI_CC_INVALID_REQ; |
| } |
| |
| // Check for valid buffer sizes |
| if (cmdSize > PECI_BUFFER_SIZE || respSize < u8ReadLen || |
| u8ReadLen > |
| (PECI_BUFFER_SIZE - 1)) // response buffer is data + 1 status byte |
| { |
| 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); |
| } |
| |
| 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; |
| } |
| |
| // The client address must be in the valid range |
| if (clientAddr < MIN_CLIENT_ADDR || clientAddr > MAX_CLIENT_ADDR) |
| { |
| 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; |
| } |