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 */