@@ -22,6 +22,42 @@
#include "fwts_acpi.h"
+typedef struct pci_device {
+ uint8_t segment;
+ uint8_t bus;
+ uint8_t dev;
+ uint8_t func;
+ uint8_t configuration[256];
+ struct pci_device *next;
+} pci_device;
+
+
+typedef struct pcie_capability {
+ uint8_t pcie_cap_id;
+ uint8_t next_cap_point;
+ uint16_t pcie_cap_reg;
+ uint32_t device_cap;
+ uint16_t device_contrl;
+ uint16_t device_status;
+ uint32_t link_cap;
+ uint16_t link_contrl;
+ uint16_t link_status;
+ uint32_t slot_cap;
+ uint16_t slot_contrl;
+ uint16_t slot_status;
+ uint16_t root_contrl;
+ uint16_t root_cap;
+ uint32_t root_status;
+ uint32_t device_cap2;
+ uint16_t device_contrl2;
+ uint16_t device_status2;
+ uint32_t link_cap2;
+ uint16_t link_contrl2;
+ uint16_t link_status2;
+ uint32_t slot_cap2;
+ uint16_t slot_contrl2;
+ uint16_t slot_status2;
+} pcie_capability;
int fwts_aspm_check_configuration(fwts_framework *fw);
@@ -26,7 +26,6 @@
#include <string.h>
#include <fcntl.h>
#include <limits.h>
-#include <linux/pci.h>
#include "fwts.h"
@@ -51,6 +50,154 @@ int fwts_facp_get_aspm_control(fwts_framework *fw, int *aspm)
return FWTS_OK;
}
+int fwts_pcie_check_aspm_registers(fwts_framework *fw, const fwts_log_level level)
+{
+ fwts_list *lspci_output;
+ fwts_list_link *item;
+ pci_device *root = NULL, *cur = NULL, *device = NULL;
+ char command[PATH_MAX];
+
+ // Get list of PCIe devices
+ snprintf(command, sizeof(command), "%s", fw->lspci);
+
+ if (fwts_pipe_exec(command, &lspci_output) == FWTS_EXEC_ERROR) {
+ fwts_log_warning(fw, "Could not execute %s", command);
+ return FWTS_EXEC_ERROR;
+ }
+
+ fwts_list_foreach(item, lspci_output) {
+ device = (pci_device*) malloc (sizeof(pci_device));
+ char *line = fwts_text_list_text(item);
+ char *pEnd;
+
+ device->bus = strtol(line, &pEnd, 16);
+ device->dev = strtol(pEnd + 1, &pEnd, 16);
+ device->func = strtol(pEnd + 1, &pEnd, 16);
+
+ if (device->bus == 0 && device->dev == 0 && device->func == 0) {
+ root = device;
+ } else {
+ cur->next = device;
+ }
+ cur = device;
+ }
+
+ // Get configuration space for each PCIe device
+ cur = root;
+ while (cur != NULL) {
+ int reg_loc = 0, reg_val = 0;
+ int i;
+
+ snprintf(command, sizeof(command), "%s -s %02X:%02X.%02X -xxx", fw->lspci, cur->bus, cur->dev, cur->func);
+
+ if (fwts_pipe_exec(command, &lspci_output) == FWTS_EXEC_ERROR) {
+ fwts_log_warning(fw, "Could not execute %s", command);
+ return FWTS_EXEC_ERROR;
+ }
+
+ fwts_list_foreach(item, lspci_output) {
+ char *line = fwts_text_list_text(item);
+ char *pEnd;
+
+ if (line[3] == ' ') {
+ reg_val = strtol(line, &pEnd, 16);
+ for (i = 0; i < 16; i++){
+ reg_val = strtol(pEnd + 1, &pEnd, 16);
+ cur->configuration[reg_loc] = (uint8_t) reg_val;
+ reg_loc++;
+ }
+ }
+ }
+ cur = cur->next;
+ }
+
+ // Check aspm settings
+ cur = root;
+ while (cur != NULL) {
+ pci_device *target;
+ pcie_capability *rp_cap, *device_cap;
+
+ if (cur->configuration[0x0E] & 0x01) {
+ uint8_t rp_aspm_support, rp_aspm_cntrl, device_aspm_support, device_aspm_cntrl;
+
+ target = root;
+ while (target != NULL) {
+ if (target->bus == cur->configuration[0x19])
+ break;
+ target = target->next;
+ }
+
+ if (target == NULL) {
+ cur = cur->next;
+ continue;
+ }
+
+ rp_cap = (pcie_capability*) &cur->configuration[cur->configuration[0x34]];
+ while (rp_cap->pcie_cap_id != 0x10) {
+ if (rp_cap->next_cap_point == 0x00)
+ break;
+ rp_cap = (pcie_capability*) &cur->configuration[rp_cap->next_cap_point];
+ }
+
+ device_cap = (pcie_capability*) &target->configuration[target->configuration[0x34]];
+ while (device_cap->pcie_cap_id != 0x10) {
+ if (device_cap->next_cap_point == 0x00)
+ break;
+ device_cap = (pcie_capability*) &target->configuration[device_cap->next_cap_point];
+ }
+
+ // Compare aspm settings for the root port (rp_cap) vs. the device (device_cap)
+ rp_aspm_support = (rp_cap->link_cap & 0x0c00) >> 10;
+ rp_aspm_cntrl = (rp_cap->link_contrl & 0x03);
+ device_aspm_support = (device_cap->link_cap & 0x0c00) >> 10;
+ device_aspm_cntrl = (device_cap->link_contrl & 0x03);
+
+ if ((rp_aspm_support & 0x01) != (rp_aspm_cntrl & 0x01)){
+ fwts_log_info(fw, "PCIe root port (B%02Xh, D%02Xh, F%02Xh) supports L0s, but not enabled.",
+ cur->bus, cur->dev, cur->func);
+ }
+
+ if ((rp_aspm_support & 0x02) != (rp_aspm_cntrl & 0x02)){
+ fwts_log_info(fw, "PCIe root port (B%02Xh, D%02Xh, F%02Xh) supports L1, but not enabled.",
+ cur->bus, cur->dev, cur->func);
+ }
+
+
+ if ((device_aspm_support & 0x01) != (device_aspm_cntrl & 0x01)){
+ fwts_log_info(fw, "PCIe device (B%02Xh, D%02Xh, F%02Xh) supports L0s, but not enabled.",
+ target->bus, target->dev, target->func);
+ }
+
+ if ((device_aspm_support & 0x02) != (device_aspm_cntrl & 0x02)){
+ fwts_log_info(fw, "PCIe device (B%02Xh, D%02Xh, F%02Xh) supports L1, but not enabled.",
+ target->bus, target->dev, target->func);
+ }
+
+ if (rp_aspm_cntrl != device_aspm_cntrl){
+ fwts_failed(fw, level, "ASPMInconsistent",
+ "PCIe root port has different aspm setting from PCIe device.");
+ fwts_log_info(fw, "PCIe root port (B%02Xh, D%02Xh, F%02Xh) has aspm = %02Xh.",
+ cur->bus, cur->dev, cur->func, rp_aspm_cntrl);
+ fwts_log_info(fw, "PCIe device (B%02Xh, D%02Xh, F%02Xh) has aspm = %02Xh.",
+ target->bus, target->dev, target->func, device_aspm_cntrl);
+ }
+ }
+ cur = cur->next;
+ }
+
+ // Free memory
+ cur = root;
+ while (cur != NULL){
+ device = cur->next;
+ free(cur);
+ cur = device;
+ }
+
+ fwts_text_list_free(lspci_output);
+
+ return FWTS_OK;
+}
+
int fwts_aspm_check_configuration(fwts_framework *fw)
{
int ret;
@@ -62,7 +209,8 @@ int fwts_aspm_check_configuration(fwts_framework *fw)
return FWTS_ERROR;
}
+ ret = fwts_pcie_check_aspm_registers(fw, LOG_LEVEL_HIGH);
+
return ret;
}
-
Signed-off-by: Alex Hung <alex.hung@canonical.com> --- src/lib/include/fwts_aspm.h | 36 ++++++++++ src/lib/src/fwts_aspm.c | 152 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 186 insertions(+), 2 deletions(-)