/* * 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 */ /* TODO: check irq routing under differnt usage */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "../biostest.h" #include #include #include #include #include #include #include #define HPET_REG_SIZE (0x400) #define MAX_CLK_PERIOD (100000000) extern GList *boot_dmesg; extern void load_boot_dmesg_buffer(); extern GList *ssdt[]; extern void load_acpi_tables(); static uint64_t hpet_base_p = 0; static void *hpet_base_v = 0; typedef enum { DMESG_BASE = 1, DSDT_BASE } HPET_STATUS; /* check_hpet_base_hpet() -- used to parse the HPET Table for HPET base info */ static void check_hpet_base_hpet(void) { unsigned long address = 0; unsigned long size = 0; struct hpet_table *table; char *table_ptr; if(locate_acpi_table("HPET", &address, &size)) return; if (address == 0 || address == -1) return; table = (struct hpet_table *) address; hpet_base_p = table->base_address.address; free((void *) address); } /* check_hpet_base_dsdt() -- used to parse the DSDT for HPET base info */ static void check_hpet_base_dsdt(gpointer data, gpointer user_data) { char *line = (char *) data; char *val, *idx; static int hpet_found = 0; if (!hpet_found) { val = strstr(line, "Device (HPET)"); if (val != NULL) { hpet_found = 1; } } else { /* HPET section is found, looking for base */ val = strstr(line, "0x"); if (val != NULL) { idx = index(val, ','); if (idx) *idx = '\0'; if ((hpet_base_p != 0) && (hpet_base_p != (uint64_t) strtoul(val, NULL, 0x10))) { char details[4096]; sprintf(details, "Mismatched HPET base between DSDT (%lx) and the kernel %lx)", (unsigned long)hpet_base_p, (unsigned long)strtoul(val, NULL, 0x10)); report_result("chk_hpet", FAIL, "Mismatched HPET base between DSDT and the kernel", details, NULL); return; } hpet_base_p = strtoul(val, NULL, 0x10); // fprintf(stderr, "DSDT HPET base = 0x%08X\n", // (unsigned int) hpet_base_p); hpet_found = 0; } } } /* check_hpet_base() -- used to parse the kernel dmesg for HPET base info */ static void check_hpet_base(gpointer data, gpointer user_data) { char *line = (char *) data; char *val, *c; if ((val = strstr(line, "ACPI: HPET id:")) != NULL) { c= strstr(line, "base: "); if (c) hpet_base_p = strtoul(c+6, NULL, 0x10); // fprintf(stderr, "HPET base = 0x%08X\n", // (unsigned int) hpet_base_p); *(unsigned int *) user_data = DMESG_BASE; return; } } /* hpet_check_ids() -- checks the validatity of the HPET id * and the clock period. */ static void hpet_check_ids() { unsigned long long hpet_id; unsigned long clk_period; char buffer[4096]; hpet_id = *(unsigned long long *) hpet_base_v; sprintf(buffer, "HPET found, VendorID is: %04X", ((hpet_id & 0xffff0000) >> 16)); report_result("chk_hpet", INFO, buffer, NULL, NULL); clk_period = hpet_id >> 32; if ((clk_period > MAX_CLK_PERIOD) || (clk_period == 0)) { sprintf(buffer, "Invalid clock period %li, must be non-zero and less than 10^8 fs", clk_period); report_result("chk_hpet", WARN, buffer, NULL, NULL); return; } hpet_id = *(unsigned long long *) (hpet_base_v + 0x10); } /* main() -- This plugin checks the DSDT and kernel dmesg for * HPET base. It then checks the validity of * the HPET id and the clock period. * * Parameters * argc & argv * Returns * EXIT_SUCCESS upon success of running this plugin * EXIT_FAILURE upon failure of running this plugin */ int main(int argc, char ** argv) { int fd, ret; int hpet_status; char uri[1024]; start_test("chk_hpet", "HPET configuration test", "This test checks the HPET PCI BAR for each timer block in the timer." "The base address is passed by the firmware via an ACPI table." "IRQ routing and initialization is also verified by the test."); ret = EXIT_SUCCESS; memset(uri, 0, 1024); load_boot_dmesg_buffer(); if (boot_dmesg != NULL) g_list_foreach(boot_dmesg, check_hpet_base, &hpet_status); else fprintf(stderr, "WARN: No boot dmesg found.\n"); if (hpet_status == DMESG_BASE) { report_result("chk_hpet", WARN, "HPET driver in the kernel is enabled, inaccurate results follow.", NULL, NULL); }; report_testrun_progress(30); load_acpi_tables(); if (ssdt[0] != NULL) g_list_foreach(ssdt[0], check_hpet_base_dsdt, &hpet_status); else { fprintf(stderr, "WARN: No DSDT found.\n"); } if (!hpet_base_p) check_hpet_base_hpet(); report_testrun_progress(60); if (!hpet_base_p) { report_result("chk_hpet", FAIL, "Failed to locate HPET base", "The firmware AML code doesn't advertize the HPET base.\n" "HPET might be disabled in the bios settings, or not supported by the platform.", NULL); finish_test("chk_hpet"); return EXIT_SUCCESS; } fd = open("/dev/mem", O_RDONLY); if (fd <= 0) { fprintf(stderr, "ERR: Failed to open /dev/mem\n"); return EXIT_SUCCESS; } hpet_base_v = mmap(NULL, HPET_REG_SIZE, PROT_READ, MAP_SHARED, fd, hpet_base_p); report_testrun_progress(60); hpet_check_ids(); finish_test("chk_hpet"); return EXIT_SUCCESS; }