/* * 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 */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../biostest.h" #include #include #include #include /* Defined in PCI Firmware Specification 3.0 */ struct mcfg_entry { unsigned int low_address; unsigned int high_address; unsigned short segment; unsigned char start_bus; unsigned char end_bus; unsigned char reserved[4]; } __attribute__ ((packed)); static void compare_config_space(int segment, int device, char *space) { char filename[PATH_MAX]; char *compare_line = strdup(""); char line[4096]; char *lspci_cmd; FILE *file; int i; for (i = 0; i < 16 ; i++) { compare_line = scatprintf(compare_line,"%02x", (unsigned char)space[i]); if (i!=15) compare_line = scatprintf(compare_line," "); } lspci_cmd = get_relative_command ("lspci", NULL); sprintf(filename, "%s -vxxx -s %i:%i", lspci_cmd, segment, device); file = popen(filename, "r"); if (!file) return; while (!feof(file)) { memset(line, 0, 4096); fgets(line, 4095, file); chop_newline(line); if (strncmp(line, "00: ",4)==0) { if (strcmp(&line[4], compare_line)) { compare_line = scatprintf(compare_line," is read from MMCONFIG, but traditional gives :\n-%s-\n", &line[4]); report_result("mcfg", FAIL, "PCI config space appears to not work", compare_line, NULL); return; } else { report_result("mcfg", PASS, "PCI config space verified", compare_line, NULL); return; } } } fclose(file); report_result("mcfg", WARN, "Config space defect??", NULL, NULL); } void do_manual_mcfg_test(void) { unsigned long address; unsigned long size; int fd; int nr, i; char *table_ptr, *table_page; struct mcfg_entry *table, firstentry; char *buffer = strdup(""); start_test("mcfg", "MCFG PCI Express* memory mapped config space", "This test tries to validate the MCFG table by comparing the first " "16 bytes in the MMIO mapped config space with the 'traditional' config " "space of the first PCI device (root bridge). The MCFG data is only " "trusted if it is marked reserved in the E820 table."); if (locate_acpi_table("MCFG", &address, &size)) { report_result("mcfg", WARN, "No MCFG ACPI table found. This table is required for PCI Express*.", NULL, NULL); goto out; } if (address == 0) { report_result("mcfg", WARN, "No MCFG ACPI table found. This table is required for PCI Express*.", NULL, NULL); goto out; } size -= 36; /* general ACPI header */ size -= 8; /* 8 bytes of padding */ if ((int)size<0) { report_result("mcfg", FAIL, "Invalid MCFG ACPI table size", NULL, NULL); goto out; } nr = size / sizeof(struct mcfg_entry); if (nr==0) { report_result("mcfg", FAIL, "No MCFG ACPI table entries", NULL, NULL); goto out; } if (nr * sizeof(struct mcfg_entry) != size) report_result("mcfg", WARN, "MCFG table is not a multiple of record size\n", NULL, NULL); buffer = scatprintf(buffer, "MCFG table found, size is %i bytes (%i entries)\n", size, nr); table_page = table_ptr = address; if (table_page==NULL) { report_result("mcfg", FAIL, "Invalid MCFG ACPI table size", NULL, NULL); goto out; } table_ptr += 36 + 8; /* 36 for the acpi header, 8 for padding */ table = (struct mcfg_entry *) table_ptr; firstentry = *table; for (i = 0; ilow_address); if (!e820_is_reserved(table->low_address)) { char buf[4095]; sprintf(buf, "E820: MCFG mmio config space at 0x%x is not reserved in the E820 table", table->low_address); report_result("mcfg", FAIL, buf, NULL, "e820://"); goto out; } buffer = scatprintf(buffer, "High address : %x \n", table->high_address); buffer = scatprintf(buffer, "Segment : %i \n", table->segment); buffer = scatprintf(buffer, "Start bus : %i \n", table->start_bus); buffer = scatprintf(buffer, "End bus : %i \n", table->end_bus); table++; } for (i = 0; i < (int)size ; i++) { buffer = scatprintf(buffer,"%02x ", (unsigned char)table_ptr[i]); if ( (i%16)==15) buffer = scatprintf(buffer,"\n"); } buffer = scatprintf(buffer,"\n"); free(table_page); fd = open("/dev/mem", O_RDONLY); if (fd<=0) goto out2; table_page = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd, firstentry.low_address); if (!table_page) goto out2; if (table_page==(void*)-1) { non_fatal_bug(strerror(errno)); goto out2; } for (i = 0; i < 64 ; i++) { buffer = scatprintf(buffer,"%02x ", (unsigned char)table_page[i]); if ( (i%16)==15) buffer = scatprintf(buffer,"\n"); } compare_config_space(firstentry.segment, 0, table_page); buffer = scatprintf(buffer,"\n"); munmap(table_page, getpagesize()); out2: report_result("mcfg", PASS, buffer, buffer, NULL); out: finish_test("mcfg"); } void run_test(void) { register_interactive_test("MCFG test", do_manual_mcfg_test); }