diff --git a/grub-core/commands/efi/acpi.c b/grub-core/commands/efi/acpi.c deleted file mode 100644 index 74f8cd1..0000000 --- a/grub-core/commands/efi/acpi.c +++ /dev/null @@ -1,59 +0,0 @@ -/* acpi.c - get acpi tables. */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2009 Free Software Foundation, Inc. - * - * GRUB is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * GRUB 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GRUB. If not, see . - */ - -#include -#include -#include -#include - -struct grub_acpi_rsdp_v10 * -grub_machine_acpi_get_rsdpv1 (void) -{ - unsigned i; - static grub_efi_packed_guid_t acpi_guid = GRUB_EFI_ACPI_TABLE_GUID; - - for (i = 0; i < grub_efi_system_table->num_table_entries; i++) - { - grub_efi_packed_guid_t *guid = - &grub_efi_system_table->configuration_table[i].vendor_guid; - - if (! grub_memcmp (guid, &acpi_guid, sizeof (grub_efi_packed_guid_t))) - return (struct grub_acpi_rsdp_v10 *) - grub_efi_system_table->configuration_table[i].vendor_table; - } - return 0; -} - -struct grub_acpi_rsdp_v20 * -grub_machine_acpi_get_rsdpv2 (void) -{ - unsigned i; - static grub_efi_packed_guid_t acpi20_guid = GRUB_EFI_ACPI_20_TABLE_GUID; - - for (i = 0; i < grub_efi_system_table->num_table_entries; i++) - { - grub_efi_packed_guid_t *guid = - &grub_efi_system_table->configuration_table[i].vendor_guid; - - if (! grub_memcmp (guid, &acpi20_guid, sizeof (grub_efi_packed_guid_t))) - return (struct grub_acpi_rsdp_v20 *) - grub_efi_system_table->configuration_table[i].vendor_table; - } - return 0; -} diff --git a/grub-core/kern/acpi.c b/grub-core/kern/acpi.c new file mode 100644 index 0000000..5292597 --- /dev/null +++ b/grub-core/kern/acpi.c @@ -0,0 +1,119 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2012 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + +static void * +grub_acpi_rsdt_find_table (struct grub_acpi_table_header *rsdt, const char *sig) +{ + grub_size_t s; + grub_uint32_t *ptr; + + if (!rsdt) + return 0; + + if (grub_memcmp (rsdt->signature, "RSDT", 4) != 0) + return 0; + + ptr = (grub_uint32_t *) (rsdt + 1); + s = (rsdt->length - sizeof (*rsdt)) / sizeof (grub_uint32_t); + for (; s; s--, ptr++) + { + struct grub_acpi_table_header *tbl; + tbl = (struct grub_acpi_table_header *) (grub_addr_t) *ptr; + if (grub_memcmp (tbl->signature, sig, 4) == 0) + return tbl; + } + return 0; +} + +static void * +grub_acpi_xsdt_find_table (struct grub_acpi_table_header *xsdt, const char *sig) +{ + grub_size_t s; + grub_uint64_t *ptr; + + if (!xsdt) + return 0; + + if (grub_memcmp (xsdt->signature, "XSDT", 4) != 0) + return 0; + + ptr = (grub_uint64_t *) (xsdt + 1); + s = (xsdt->length - sizeof (*xsdt)) / sizeof (grub_uint32_t); + for (; s; s--, ptr++) + { + struct grub_acpi_table_header *tbl; +#if GRUB_CPU_SIZEOF_VOID_P != 8 + if (*ptr >> 32) + continue; +#endif + tbl = (struct grub_acpi_table_header *) (grub_addr_t) *ptr; + if (grub_memcmp (tbl->signature, sig, 4) == 0) + return tbl; + } + return 0; +} + +struct grub_acpi_fadt * +grub_acpi_find_fadt (void) +{ + struct grub_acpi_fadt *fadt = 0; + struct grub_acpi_rsdp_v10 *rsdpv1; + struct grub_acpi_rsdp_v20 *rsdpv2; + rsdpv1 = grub_machine_acpi_get_rsdpv1 (); + if (rsdpv1) + fadt = grub_acpi_rsdt_find_table ((struct grub_acpi_table_header *) + (grub_addr_t) rsdpv1->rsdt_addr, + GRUB_ACPI_FADT_SIGNATURE); + if (fadt) + return fadt; + rsdpv2 = grub_machine_acpi_get_rsdpv2 (); + if (rsdpv2) + fadt = grub_acpi_rsdt_find_table ((struct grub_acpi_table_header *) + (grub_addr_t) rsdpv2->rsdpv1.rsdt_addr, + GRUB_ACPI_FADT_SIGNATURE); + if (fadt) + return fadt; + if (rsdpv2 +#if GRUB_CPU_SIZEOF_VOID_P != 8 + && !(rsdpv2->xsdt_addr >> 32) +#endif + ) + fadt = grub_acpi_xsdt_find_table ((struct grub_acpi_table_header *) + (grub_addr_t) rsdpv2->xsdt_addr, + GRUB_ACPI_FADT_SIGNATURE); + if (fadt) + return fadt; + return 0; +} + +/* Simple checksum by summing all bytes. Used by ACPI and SMBIOS. */ +grub_uint8_t +grub_byte_checksum (void *base, grub_size_t size) +{ + grub_uint8_t *ptr; + grub_uint8_t ret = 0; + for (ptr = (grub_uint8_t *) base; ptr < ((grub_uint8_t *) base) + size; + ptr++) + ret += *ptr; + return ret; +} diff --git a/grub-core/kern/efi/acpi.c b/grub-core/kern/efi/acpi.c new file mode 100644 index 0000000..74f8cd1 --- /dev/null +++ b/grub-core/kern/efi/acpi.c @@ -0,0 +1,59 @@ +/* acpi.c - get acpi tables. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + +struct grub_acpi_rsdp_v10 * +grub_machine_acpi_get_rsdpv1 (void) +{ + unsigned i; + static grub_efi_packed_guid_t acpi_guid = GRUB_EFI_ACPI_TABLE_GUID; + + for (i = 0; i < grub_efi_system_table->num_table_entries; i++) + { + grub_efi_packed_guid_t *guid = + &grub_efi_system_table->configuration_table[i].vendor_guid; + + if (! grub_memcmp (guid, &acpi_guid, sizeof (grub_efi_packed_guid_t))) + return (struct grub_acpi_rsdp_v10 *) + grub_efi_system_table->configuration_table[i].vendor_table; + } + return 0; +} + +struct grub_acpi_rsdp_v20 * +grub_machine_acpi_get_rsdpv2 (void) +{ + unsigned i; + static grub_efi_packed_guid_t acpi20_guid = GRUB_EFI_ACPI_20_TABLE_GUID; + + for (i = 0; i < grub_efi_system_table->num_table_entries; i++) + { + grub_efi_packed_guid_t *guid = + &grub_efi_system_table->configuration_table[i].vendor_guid; + + if (! grub_memcmp (guid, &acpi20_guid, sizeof (grub_efi_packed_guid_t))) + return (struct grub_acpi_rsdp_v20 *) + grub_efi_system_table->configuration_table[i].vendor_table; + } + return 0; +} diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index af0c7f4..7a1ffdc 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -101,6 +101,7 @@ if COND_i386_efi KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h endif @@ -112,6 +113,7 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/video_fb.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/gfxterm.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/font.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/bufio.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h endif if COND_i386_multiboot @@ -154,6 +156,7 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h endif if COND_ia64_efi diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index d9fa0e3..cbef91f 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -171,6 +171,12 @@ kernel = { efi = kern/efi/init.c; efi = kern/efi/mm.c; efi = term/efi/console.c; + efi = kern/acpi.c; + efi = kern/efi/acpi.c; + i386_coreboot = kern/i386/pc/acpi.c; + i386_multiboot = kern/i386/pc/acpi.c; + i386_coreboot = kern/acpi.c; + i386_multiboot = kern/acpi.c; x86 = kern/i386/tsc.c; @@ -665,10 +671,6 @@ module = { name = acpi; common = commands/acpi.c; - efi = commands/efi/acpi.c; - i386_pc = commands/i386/pc/acpi.c; - i386_coreboot = commands/i386/pc/acpi.c; - i386_multiboot = commands/i386/pc/acpi.c; enable = efi; enable = i386_pc; diff --git a/grub-core/commands/acpi.c b/grub-core/commands/acpi.c index c3861f5..ece49b4 100644 --- a/grub-core/commands/acpi.c +++ b/grub-core/commands/acpi.c @@ -61,18 +61,6 @@ static const struct grub_arg_option options[] = { {0, 0, 0, 0, 0, 0} }; -/* Simple checksum by summing all bytes. Used by ACPI and SMBIOS. */ -grub_uint8_t -grub_byte_checksum (void *base, grub_size_t size) -{ - grub_uint8_t *ptr; - grub_uint8_t ret = 0; - for (ptr = (grub_uint8_t *) base; ptr < ((grub_uint8_t *) base) + size; - ptr++) - ret += *ptr; - return ret; -} - /* rev1 is 1 if ACPIv1 is to be generated, 0 otherwise. rev2 contains the revision of ACPIv2+ to generate or 0 if none. */ static int rev1, rev2; diff --git a/grub-core/kern/i386/tsc.c b/grub-core/kern/i386/tsc.c index bc441d0..3b4786f 100644 --- a/grub-core/kern/i386/tsc.c +++ b/grub-core/kern/i386/tsc.c @@ -1,6 +1,6 @@ /* kern/i386/tsc.c - x86 TSC time source implementation * Requires Pentium or better x86 CPU that supports the RDTSC instruction. - * This module uses the RTC (via grub_get_rtc()) to calibrate the TSC to + * This module uses the PIT to calibrate the TSC to * real time. * * GRUB -- GRand Unified Bootloader @@ -30,7 +30,12 @@ #else #include #endif +#ifdef GRUB_MACHINE_EFI +#include +#include +#endif #include +#include /* This defines the value TSC had at the epoch (that is, when we calibrated it). */ static grub_uint64_t tsc_boot_time; @@ -57,11 +62,20 @@ grub_get_tsc (void) return (((grub_uint64_t) hi) << 32) | lo; } -#ifndef GRUB_MACHINE_XEN +static grub_uint64_t +grub_tsc_get_time_ms (void) +{ + grub_uint64_t a = grub_get_tsc () - tsc_boot_time; + grub_uint64_t ah = a >> 32; + grub_uint64_t al = a & 0xffffffff; + + return ((al * grub_tsc_rate) >> 32) + ah * grub_tsc_rate; +} static __inline int grub_cpu_is_tsc_supported (void) { +#ifndef GRUB_MACHINE_XEN grub_uint32_t a,b,c,d; if (! grub_cpu_is_cpuid_supported ()) return 0; @@ -69,11 +83,18 @@ grub_cpu_is_tsc_supported (void) grub_cpuid(1,a,b,c,d); return (d & (1 << 4)) != 0; +#else + return 1; +#endif } -static void +#ifndef GRUB_MACHINE_XEN + +static int grub_pit_wait (grub_uint16_t tics) { + int ret = 0; + /* Disable timer2 gate and speaker. */ grub_outb (grub_inb (GRUB_PIT_SPEAKER_PORT) & ~ (GRUB_PIT_SPK_DATA | GRUB_PIT_SPK_TMR2), @@ -90,63 +111,147 @@ grub_pit_wait (grub_uint16_t tics) | GRUB_PIT_SPK_TMR2, GRUB_PIT_SPEAKER_PORT); - /* Wait. */ - while ((grub_inb (GRUB_PIT_SPEAKER_PORT) & GRUB_PIT_SPK_TMR2_LATCH) == 0x00); + if ((grub_inb (GRUB_PIT_SPEAKER_PORT) & GRUB_PIT_SPK_TMR2_LATCH)) { + ret = 1; + /* Wait. */ + while ((grub_inb (GRUB_PIT_SPEAKER_PORT) & GRUB_PIT_SPK_TMR2_LATCH) == 0x00); + } /* Disable timer2 gate and speaker. */ grub_outb (grub_inb (GRUB_PIT_SPEAKER_PORT) & ~ (GRUB_PIT_SPK_DATA | GRUB_PIT_SPK_TMR2), GRUB_PIT_SPEAKER_PORT); -} -#endif -static grub_uint64_t -grub_tsc_get_time_ms (void) -{ - grub_uint64_t a = grub_get_tsc () - tsc_boot_time; - grub_uint64_t ah = a >> 32; - grub_uint64_t al = a & 0xffffffff; - - return ((al * grub_tsc_rate) >> 32) + ah * grub_tsc_rate; + return ret; } -#ifndef GRUB_MACHINE_XEN /* Calibrate the TSC based on the RTC. */ -static void -calibrate_tsc (void) +static int +calibrate_tsc_pit (void) { /* First calibrate the TSC rate (relative, not absolute time). */ - grub_uint64_t end_tsc; + grub_uint64_t start_tsc, end_tsc; - tsc_boot_time = grub_get_tsc (); - grub_pit_wait (0xffff); + start_tsc = grub_get_tsc (); + if (!grub_pit_wait (0xffff)) + return 0; end_tsc = grub_get_tsc (); grub_tsc_rate = 0; if (end_tsc > tsc_boot_time) - grub_tsc_rate = grub_divmod64 ((55ULL << 32), end_tsc - tsc_boot_time, 0); + grub_tsc_rate = grub_divmod64 ((55ULL << 32), end_tsc - start_tsc, 0); if (grub_tsc_rate == 0) - grub_tsc_rate = 5368;/* 800 MHz */ + return 0; + return 1; } + #endif -void -grub_tsc_init (void) +#if defined (GRUB_MACHINE_EFI) +static int +calibrate_tsc_efi (void) +{ + grub_uint64_t start_tsc, end_tsc; + /* Use EFI Time Service to calibrate TSC */ + start_tsc = grub_get_tsc (); + efi_call_1 (grub_efi_system_table->boot_services->stall, 1000); + end_tsc = grub_get_tsc (); + grub_tsc_rate = grub_divmod64 ((1ULL << 32), end_tsc - start_tsc, 0); + return 1; +} +#endif + +#if defined (GRUB_MACHINE_EFI) || defined (GRUB_MACHINE_COREBOOT) + +static int +calibrate_tsc_pmtimer (void) { + grub_uint32_t start; + grub_uint32_t last; + grub_uint32_t cur, end; + struct grub_acpi_fadt *fadt; + grub_port_t p; + grub_uint64_t start_tsc; + grub_uint64_t end_tsc; + int num_iter = 0; + + fadt = grub_acpi_find_fadt (); + if (!fadt) + return 0; + p = fadt->pmtimer; + if (!p) + return 0; + + start = grub_inl (p) & 0xffffff; + last = start; + /* It's 3.579545 MHz clock. Wait 1 ms. */ + end = start + 3580; + start_tsc = grub_get_tsc (); + while (1) + { + cur = grub_inl (p) & 0xffffff; + if (cur < last) + cur |= 0x1000000; + num_iter++; + if (cur >= end) + { + end_tsc = grub_get_tsc (); + grub_tsc_rate = grub_divmod64 ((1ULL << 32), end_tsc - start_tsc, 0); + return 1; + } + /* Check for broken PM timer. + 50000000 TSCs is between 5 ms (10GHz) and 200 ms (250 MHz) + if after this time we still don't have 1 ms on pmtimer, then + pmtimer is broken. + */ + if ((num_iter & 0xffffff) == 0 && grub_get_tsc () - start_tsc > 5000000) { + return 0; + } + } +} + +#endif + #ifdef GRUB_MACHINE_XEN + +static int +calibrate_tsc_xen (void) +{ grub_uint64_t t; - tsc_boot_time = grub_get_tsc (); t = grub_xen_shared_info->vcpu_info[0].time.tsc_to_system_mul; if (grub_xen_shared_info->vcpu_info[0].time.tsc_shift > 0) t <<= grub_xen_shared_info->vcpu_info[0].time.tsc_shift; else t >>= -grub_xen_shared_info->vcpu_info[0].time.tsc_shift; grub_tsc_rate = grub_divmod64 (t, 1000000, 0); - grub_install_get_time_ms (grub_tsc_get_time_ms); -#else + return 1; +} + +#endif + +static int +calibrate_tsc_hardcode (void) +{ + grub_tsc_rate = 5368;/* 800 MHz */ + return 1; +} + +void +grub_tsc_init (void) +{ + tsc_boot_time = grub_get_tsc (); + if (grub_cpu_is_tsc_supported ()) { - calibrate_tsc (); +#ifdef GRUB_MACHINE_XEN + calibrate_tsc_xen (); +#elif defined (GRUB_MACHINE_EFI) + (void) (calibrate_tsc_pit () || calibrate_tsc_pmtimer() || calibrate_tsc_efi() || calibrate_tsc_hardcode()); +#elif defined (GRUB_MACHINE_COREBOOT) + (void) (calibrate_tsc_pmtimer() || calibrate_tsc_pit () || calibrate_tsc_hardcode()); +#else + (void) (calibrate_tsc_pit () || calibrate_tsc_hardcode()); +#endif grub_install_get_time_ms (grub_tsc_get_time_ms); } else @@ -157,5 +262,4 @@ grub_tsc_init (void) grub_fatal ("no TSC found"); #endif } -#endif } diff --git a/include/grub/acpi.h b/include/grub/acpi.h index f6e6a11..af98776 100644 --- a/include/grub/acpi.h +++ b/include/grub/acpi.h @@ -67,10 +67,14 @@ struct grub_acpi_fadt grub_uint32_t dsdt_addr; grub_uint8_t somefields1[20]; grub_uint32_t pm1a; - grub_uint8_t somefields2[64]; + grub_uint8_t somefields2[8]; + grub_uint32_t pmtimer; + grub_uint8_t somefields3[32]; + grub_uint32_t flags; + grub_uint8_t somefields4[16]; grub_uint64_t facs_xaddr; grub_uint64_t dsdt_xaddr; - grub_uint8_t somefields3[96]; + grub_uint8_t somefields5[96]; } GRUB_PACKED; #define GRUB_ACPI_MADT_SIGNATURE "APIC" @@ -176,9 +180,9 @@ enum #ifndef GRUB_DSDT_TEST struct grub_acpi_rsdp_v10 *grub_acpi_get_rsdpv1 (void); struct grub_acpi_rsdp_v20 *grub_acpi_get_rsdpv2 (void); -struct grub_acpi_rsdp_v10 *grub_machine_acpi_get_rsdpv1 (void); -struct grub_acpi_rsdp_v20 *grub_machine_acpi_get_rsdpv2 (void); -grub_uint8_t grub_byte_checksum (void *base, grub_size_t size); +struct grub_acpi_rsdp_v10 *EXPORT_FUNC(grub_machine_acpi_get_rsdpv1) (void); +struct grub_acpi_rsdp_v20 *EXPORT_FUNC(grub_machine_acpi_get_rsdpv2) (void); +grub_uint8_t EXPORT_FUNC(grub_byte_checksum) (void *base, grub_size_t size); grub_err_t grub_acpi_create_ebda (void); @@ -234,4 +238,7 @@ enum GRUB_ACPI_EXTOPCODE_BANK_FIELD_OP = 0x87, }; +struct grub_acpi_fadt * +EXPORT_FUNC(grub_acpi_find_fadt) (void); + #endif /* ! GRUB_ACPI_HEADER */