/* * Copyright (C) 2006, Intel Corporation * * This file is part of the Linux-ready Firmware Developer Kit * * This program file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation;version 2.1 of the License. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program in a file named COPYING; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "../biostest.h" struct pci_resource { uint64_t start, end; char device[140]; }; GList *pci_resources; extern void check_resource_size_against_db(void); static void find_overlapping_resources(void) { GList *item1, *item2; struct pci_resource *res1, *res2; item1 = g_list_first(pci_resources); while (item1) { res1 = (struct pci_resource *) item1->data; item1 = g_list_next(item1); item2=item1; while (item2) { char line[4096]; char detail[4096]; res2 = (struct pci_resource *) item2->data; item2 = g_list_next(item2); if (res2->end <= res1->start) continue; if (res2->start >= res1->end) continue; sprintf(line, "PCI devices %s and %s have an overlapping PCI memory resource!", res1->device, res2->device); sprintf(detail, "Device %s has a resource from %llx to %llx, device %s has a resource from %llx to %llx", res1->device, res1->start, res1->end, res2->device, res2->start, res2->end); report_result("pciresource", FAIL, line, detail, res1->device); } } } static void process_failure(char *line) { char *pci; char summary[1024]; char uri[1024]; pci = strstr(line, " of "); if (pci==NULL) return; /* malformed */ pci+=4; sprintf(summary, "Device %s has incorrect resources", pci); sprintf(uri,"pci://%s", pci); report_result("pciresource", FAIL, summary, line, uri); } static void process_alloc_region_failure(char *line) { char *pci; char summary[1024]; char uri[1024]; pci = strstr(line, " of device "); if (pci==NULL) { pci = strstr(line, " of bridge "); if (pci==NULL) return; /* malformed */ } pci+=11; chop_newline(pci); sprintf(summary, "Device %s has incorrect resources", pci); sprintf(uri,"pci://%s", pci); report_result("pciresource", FAIL, summary, line, uri); } static void process_alloc_mem_failure(char *line) { char *pci; char summary[1024]; char uri[1024]; pci = strstr(line, " for "); if (pci==NULL) return; /* malformed */ pci+=5; chop_newline(pci); sprintf(summary, "Device %s has incorrect resources", pci); sprintf(uri,"pci://%s", pci); report_result("pciresource", FAIL, summary, line, uri); } static void process_unable_reserve(char *line) { char *pci; char summary[1024]; char uri[1024]; pci = strstr(line, " for device "); if (pci==NULL) return; /* malformed */ pci+=12; sprintf(summary, "Memory resource for device %s couldn't be reserved.", pci); sprintf(uri,"pci://%s", pci); report_result("pciresource", FAIL, summary, line, uri); } /* Checks the dmesg output for pci errors */ static void check_line(gpointer data, gpointer user_data) { char *line = (char *)data; if (strstr(line, "PCI: Ignore bogus resource")!=NULL) process_failure(line); if (strstr(line, "PCI: Cannot allocate resource region")!=NULL) process_alloc_region_failure(line); if (strstr(line, "hpet_resources:") && strstr(line,"is busy")) report_result("pciresource", FAIL, "HPET resources incorrect", line, NULL); if (strstr(line, "PCI: Unable to reserve mem region") && strstr(line,"for device")) process_unable_reserve(line); if (strstr(line, "PCI: Failed to allocate mem resource")!=NULL) process_alloc_mem_failure(line); } extern void gather_pci_info(void); /* Go through 'lspci -v' output and check for invalid pci device resource size. Also, add all the pci devices to our GList pci_resources list. */ static void check_resource_size(void) { char current_device[4096]; char line[4096]; char uri[4095]; char *c; char *lspci_cmd; FILE *file; memset(current_device, 0, 4096); lspci_cmd = get_relative_command ("lspci", " -v"); if(!lspci_cmd) { sprintf(line,"echo lspci not found >> %s", LFDK_ERRLOG); system(line); return; } file = popen(lspci_cmd, "r"); if (!file) return; while (!feof(file)) { char *c2, *c3; char hexaddress[20]; int maybehex=0; /* Get current device */ if (fgets(line, 4095, file)==NULL) break; if (line[0] != ' ' && line[0] != '\t') { strcpy(current_device, line); c2=strchr(current_device, ' '); if (c2) *c2=0; } memset(hexaddress, 0, 20); strcpy(hexaddress, "0x"); c = strstr(line, "Memory at "); if (c) { strncat(hexaddress, c+10, 16); c = strchr(hexaddress, ' '); if (c) *c=0; } c = strstr(line, "[size="); if (c) { uint64_t value; struct pci_resource *res; c+=6; c2=strchr(c, ']'); if (c2) *c2 = 0; value = strtoul(c, &c3, 10); /* weird things get presented as hex */ while (strlen(c3)>0) { if (*c3 == 'K') value = value * 1024; else if (*c3 == 'M') value = value * 1024 * 1024; else if (*c3 == 'G') value = value * 1024 * 1024 * 1024; else maybehex = 1; c3++; } if (maybehex) value = strtoul(c, &c3, 16); if (value > 512*1024*1024) { char output[4096]; current_device[8] = 0; sprintf(uri, "pci://0000:%s", current_device); sprintf("Device %s has invalid resource size %i", current_device, value); report_result("pciresource", FAIL, output, NULL, uri); } /* Add this pci device to our glist of resources */ res = malloc(sizeof(struct pci_resource)); if (res && strlen(hexaddress)>2) { memset(res, 0, sizeof(struct pci_resource)); res->start = strtoul(hexaddress, NULL, 16); res->end = res->start + value; sprintf(res->device, "pci://0000:%s", current_device); pci_resources = g_list_append(pci_resources, res); } } } fclose(file); } void run_test(void) { start_test("pciresource", "Validate assigned PCI resources", "This test is currently a placeholder and just checks the kernel log " "for complaints about PCI resource errors. In the future the idea is " "to actually perform a validation step on all PCI resources against a " "certain rule-set."); g_list_foreach(boot_dmesg, check_line, NULL); check_resource_size(); check_resource_size_against_db(); find_overlapping_resources(); gather_pci_info(); finish_test("pciresource"); }