/* * 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 #define UNCACHED 1 #define WRITEBACK 2 #define WRITECOMBINING 4 #define WRITETHROUGH 8 #define DEFAULT 16 extern void load_boot_dmesg_buffer(); extern GList *boot_demsg; struct mtrr_entry { uint64_t start; uint64_t end; int type; }; static GList *mtrr_list; char *mtrr_resource = NULL; static char *cache_to_string(int type) { char str[1024]; memset(str, 0, 1024); if (type & UNCACHED || type==0) strcat(str,"uncached "); if (type & WRITECOMBINING) strcat(str,"write-combining "); if (type & WRITEBACK) strcat(str,"write-back "); if (type & WRITETHROUGH) strcat(str,"write-through "); if (type & DEFAULT) strcat(str,"default "); return strdup(str); } static void get_mtrrs(void) { struct mtrr_entry *entry; FILE *file; char line[4096]; memset(line, 0, 4096); file = fopen("/proc/mtrr", "r"); if (!file) return; while (!feof(file)) { char *c, *c2; if (fgets(line, 4095, file)==NULL) break; entry = malloc(sizeof(struct mtrr_entry)); if (!entry) break; memset(entry, 0, sizeof(struct mtrr_entry)); c = strstr(line, "base=0x"); if (c==NULL) continue; c+=5; entry->start = strtoull(c, NULL, 16); c = strstr(line, "size="); if (!c) continue; c+=5; entry->end = strtoull(c, &c2, 10); if (c2 && *c2=='M') entry->end = entry->end * 1024 * 1024; if (c2 && *c2=='K') entry->end = entry->end * 1024; if (c2 && *c2=='m') entry->end = entry->end * 1024 * 1024; if (c2 && *c2=='k') entry->end = entry->end * 1024; entry->end += entry->start; if (strstr(line, "write-back")) entry->type = WRITEBACK; if (strstr(line, "uncachable")) entry->type = UNCACHED; if (strstr(line, "write-through")) entry->type = WRITETHROUGH; if (strstr(line, "write-combining")) entry->type = WRITECOMBINING; mtrr_list = g_list_append(mtrr_list, entry); } fclose(file); } static int cache_types(uint64_t start, uint64_t end) { GList *list; struct mtrr_entry *entry; int type = 0; list = mtrr_list; while (list) { entry = (struct mtrr_entry*)list->data; if (entry->end > start && entry->start < end) type |= entry->type; list = g_list_next(list); } /* now to see if there is any part of the range that isn't covered by an mtrr, since it's UNCACHED if so */ restart: list = mtrr_list; while (list) { entry = (struct mtrr_entry*)list->data; if (entry->end >= end && entry->start < end) { end = entry->start; if (end> %s", LFDK_ERRLOG); system(line); } return pref; } static void guess_cache_type(char *string, int *must, int *mustnot, uint64_t address) { *must = 0; *mustnot = 0; if (strstr(string, "System RAM")) { *must = WRITEBACK; *mustnot = ~*must; return; } /* if it's PCI mmio -> uncached typically except for video */ if (strstr(string, "0000:")) { if (is_prefetchable(string, address)) { *must = 0; *mustnot = WRITEBACK | UNCACHED; } else { *must = UNCACHED; *mustnot = (~*must) & ~DEFAULT; } } } static void validate_iomem(void) { FILE *file; char buffer[4096]; int pcidepth = 0; memset(buffer, 0, 4096); file = fopen("/proc/iomem", "r"); if (!file) return; while (!feof(file)) { uint64_t start, end; int type, type_must, type_mustnot; char *c, *c2; int i; int skiperror = 0; if (fgets(buffer, 4095, file)==NULL) break; chop_newline(buffer); /* for pci bridges, we note the increased depth and otherwise skip the entry */ if (strstr(buffer, ": PCI Bus #")) { pcidepth++; continue; } /* then: check the pci depth */ for (i=0; i skip */ if (*c==' ') continue; start = strtoull(c, NULL, 16); c2 = strchr(c, '-'); if (!c2) continue; c2++; end = strtoull(c2, NULL, 16); c2 = strstr(c, " : "); if (!c2) continue; c2+=3; /* exception: 640K - 1Mb range we ignore */ if (start >= 640*1024 && end <= 1024*1024) continue; type = cache_types(start, end); guess_cache_type(c2, &type_must, &type_mustnot, start); if ((type & type_mustnot)!=0) { char out[4096]; char uri[4096]; char parent[4096]; sprintf(out, "Memory range 0x%llx to 0x%llx (%s) has incorrect attribute %s", start, end, c2, cache_to_string(type & type_mustnot)); sprintf(uri, "mtrr://%s", c2); parent[0]=0; if (strstr(c2,"0000:")) sprintf(parent, "pci://%s", c2); announce_resource(uri, mtrr_resource, parent); report_result("mtrr", FAIL, out, out, uri); if (type_must == UNCACHED) skiperror = 1; } if (type & DEFAULT) { type |= UNCACHED; type &= ~DEFAULT; } if ((type & type_must)!=type_must && skiperror==0) { char out[4096]; sprintf(out, "Memory range 0x%llx to 0x%llx (%s) is lacking attribute %s", start, end, c2, cache_to_string( (type & type_must) ^ type_must)); report_result("mtrr", FAIL, out, out, "mtrr://"); } } fclose(file); } static void do_mtrr_resource(void) { GList *list; struct mtrr_entry *entry; list = mtrr_list; mtrr_resource = scatprintf(mtrr_resource,"MTRR overview\n-------------\n"); while (list) { entry = (struct mtrr_entry*)list->data; mtrr_resource = scatprintf(mtrr_resource, "0x%08llx - 0x%08llx %s \n", entry->start, entry->end, cache_to_string(entry->type)); list = g_list_next(list); } announce_resource("mtrr://", mtrr_resource, NULL); } static void check_line(gpointer data, gpointer user_data) { char *line = (char *)data; if (strstr(line, "mtrr: probably your BIOS does not setup all CPUs.")) report_result("mtrr", FAIL, "Not all processors have the MTRR set up", line, NULL); } static void do_manual_mtrr_test(void) { start_test("mtrr", "MTRR validation", "This test validates the MTRR setup against the memory map to " "detect any inconsistencies in cachability."); get_mtrrs(); do_mtrr_resource(); validate_iomem(); load_boot_dmesg_buffer(); if(boot_dmesg != NULL) g_list_foreach(boot_dmesg, check_line, NULL); else fprintf(stderr, "WARN: No boot dmesg found.\n"); finish_test("mtrr"); } int main(int argc, char **argv) { if (access("/proc/mtrr", R_OK)) return 0; do_manual_mtrr_test(); return 0; }