diff mbox

pcie: aspm: add aspm configuration check on PCIe root ports and PCIe devices.

Message ID 1325833364-15933-1-git-send-email-alex.hung@canonical.com
State Rejected
Headers show

Commit Message

Alex Hung Jan. 6, 2012, 7:02 a.m. UTC
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(-)
diff mbox

Patch

diff --git a/src/lib/include/fwts_aspm.h b/src/lib/include/fwts_aspm.h
index 91437fa..389ebee 100644
--- a/src/lib/include/fwts_aspm.h
+++ b/src/lib/include/fwts_aspm.h
@@ -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);
 
diff --git a/src/lib/src/fwts_aspm.c b/src/lib/src/fwts_aspm.c
index a6ee1b7..f0d7f8e 100644
--- a/src/lib/src/fwts_aspm.c
+++ b/src/lib/src/fwts_aspm.c
@@ -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;
 }
 
-