diff options
| author | Huang Ying <ying.huang@intel.com> | 2011-12-07 22:25:49 -0500 |
|---|---|---|
| committer | Len Brown <len.brown@intel.com> | 2012-01-17 03:54:44 -0500 |
| commit | b54ac6d2a25084667da781c7ca2cebef52a2bcdd (patch) | |
| tree | b4dbaa790dcecff6b5b0772846d43b360f6389d7 | |
| parent | b4e008dc53a31cb4bf6a12d9dbaf1d5c6070a838 (diff) | |
ACPI, Record ACPI NVS regions
Some firmware will access memory in ACPI NVS region via APEI. That
is, instructions in APEI ERST/EINJ table will read/write ACPI NVS
region. The original resource conflict checking in APEI code will
check memory/ioport accessed by APEI via general resource management
mechanism. But ACPI NVS region is marked as busy already, so that the
false resource conflict will prevent APEI ERST/EINJ to work.
To fix this, this patch record ACPI NVS regions, so that we can avoid
request resources for memory region inside it.
Signed-off-by: Huang Ying <ying.huang@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
| -rw-r--r-- | arch/x86/kernel/e820.c | 4 | ||||
| -rw-r--r-- | drivers/acpi/Makefile | 3 | ||||
| -rw-r--r-- | drivers/acpi/nvs.c | 53 | ||||
| -rw-r--r-- | include/linux/acpi.h | 20 |
4 files changed, 70 insertions, 10 deletions
diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index 303a0e48f076..51c3b186e5b9 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c | |||
| @@ -714,7 +714,7 @@ void __init e820_mark_nosave_regions(unsigned long limit_pfn) | |||
| 714 | } | 714 | } |
| 715 | #endif | 715 | #endif |
| 716 | 716 | ||
| 717 | #ifdef CONFIG_HIBERNATION | 717 | #ifdef CONFIG_ACPI |
| 718 | /** | 718 | /** |
| 719 | * Mark ACPI NVS memory region, so that we can save/restore it during | 719 | * Mark ACPI NVS memory region, so that we can save/restore it during |
| 720 | * hibernation and the subsequent resume. | 720 | * hibernation and the subsequent resume. |
| @@ -727,7 +727,7 @@ static int __init e820_mark_nvs_memory(void) | |||
| 727 | struct e820entry *ei = &e820.map[i]; | 727 | struct e820entry *ei = &e820.map[i]; |
| 728 | 728 | ||
| 729 | if (ei->type == E820_NVS) | 729 | if (ei->type == E820_NVS) |
| 730 | suspend_nvs_register(ei->addr, ei->size); | 730 | acpi_nvs_register(ei->addr, ei->size); |
| 731 | } | 731 | } |
| 732 | 732 | ||
| 733 | return 0; | 733 | return 0; |
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index ecb26b4f29a0..c07f44f05f9d 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile | |||
| @@ -20,11 +20,12 @@ obj-y += acpi.o \ | |||
| 20 | # All the builtin files are in the "acpi." module_param namespace. | 20 | # All the builtin files are in the "acpi." module_param namespace. |
| 21 | acpi-y += osl.o utils.o reboot.o | 21 | acpi-y += osl.o utils.o reboot.o |
| 22 | acpi-y += atomicio.o | 22 | acpi-y += atomicio.o |
| 23 | acpi-y += nvs.o | ||
| 23 | 24 | ||
| 24 | # sleep related files | 25 | # sleep related files |
| 25 | acpi-y += wakeup.o | 26 | acpi-y += wakeup.o |
| 26 | acpi-y += sleep.o | 27 | acpi-y += sleep.o |
| 27 | acpi-$(CONFIG_ACPI_SLEEP) += proc.o nvs.o | 28 | acpi-$(CONFIG_ACPI_SLEEP) += proc.o |
| 28 | 29 | ||
| 29 | 30 | ||
| 30 | # | 31 | # |
diff --git a/drivers/acpi/nvs.c b/drivers/acpi/nvs.c index 096787b43c96..7a2035fa8c71 100644 --- a/drivers/acpi/nvs.c +++ b/drivers/acpi/nvs.c | |||
| @@ -15,6 +15,56 @@ | |||
| 15 | #include <linux/acpi_io.h> | 15 | #include <linux/acpi_io.h> |
| 16 | #include <acpi/acpiosxf.h> | 16 | #include <acpi/acpiosxf.h> |
| 17 | 17 | ||
| 18 | /* ACPI NVS regions, APEI may use it */ | ||
| 19 | |||
| 20 | struct nvs_region { | ||
| 21 | __u64 phys_start; | ||
| 22 | __u64 size; | ||
| 23 | struct list_head node; | ||
| 24 | }; | ||
| 25 | |||
| 26 | static LIST_HEAD(nvs_region_list); | ||
| 27 | |||
| 28 | #ifdef CONFIG_ACPI_SLEEP | ||
| 29 | static int suspend_nvs_register(unsigned long start, unsigned long size); | ||
| 30 | #else | ||
| 31 | static inline int suspend_nvs_register(unsigned long a, unsigned long b) | ||
| 32 | { | ||
| 33 | return 0; | ||
| 34 | } | ||
| 35 | #endif | ||
| 36 | |||
| 37 | int acpi_nvs_register(__u64 start, __u64 size) | ||
| 38 | { | ||
| 39 | struct nvs_region *region; | ||
| 40 | |||
| 41 | region = kmalloc(sizeof(*region), GFP_KERNEL); | ||
| 42 | if (!region) | ||
| 43 | return -ENOMEM; | ||
| 44 | region->phys_start = start; | ||
| 45 | region->size = size; | ||
| 46 | list_add_tail(®ion->node, &nvs_region_list); | ||
| 47 | |||
| 48 | return suspend_nvs_register(start, size); | ||
| 49 | } | ||
| 50 | |||
| 51 | int acpi_nvs_for_each_region(int (*func)(__u64 start, __u64 size, void *data), | ||
| 52 | void *data) | ||
| 53 | { | ||
| 54 | int rc; | ||
| 55 | struct nvs_region *region; | ||
| 56 | |||
| 57 | list_for_each_entry(region, &nvs_region_list, node) { | ||
| 58 | rc = func(region->phys_start, region->size, data); | ||
| 59 | if (rc) | ||
| 60 | return rc; | ||
| 61 | } | ||
| 62 | |||
| 63 | return 0; | ||
| 64 | } | ||
| 65 | |||
| 66 | |||
| 67 | #ifdef CONFIG_ACPI_SLEEP | ||
| 18 | /* | 68 | /* |
| 19 | * Platforms, like ACPI, may want us to save some memory used by them during | 69 | * Platforms, like ACPI, may want us to save some memory used by them during |
| 20 | * suspend and to restore the contents of this memory during the subsequent | 70 | * suspend and to restore the contents of this memory during the subsequent |
| @@ -41,7 +91,7 @@ static LIST_HEAD(nvs_list); | |||
| 41 | * things so that the data from page-aligned addresses in this region will | 91 | * things so that the data from page-aligned addresses in this region will |
| 42 | * be copied into separate RAM pages. | 92 | * be copied into separate RAM pages. |
| 43 | */ | 93 | */ |
| 44 | int suspend_nvs_register(unsigned long start, unsigned long size) | 94 | static int suspend_nvs_register(unsigned long start, unsigned long size) |
| 45 | { | 95 | { |
| 46 | struct nvs_page *entry, *next; | 96 | struct nvs_page *entry, *next; |
| 47 | 97 | ||
| @@ -159,3 +209,4 @@ void suspend_nvs_restore(void) | |||
| 159 | if (entry->data) | 209 | if (entry->data) |
| 160 | memcpy(entry->kaddr, entry->data, entry->size); | 210 | memcpy(entry->kaddr, entry->data, entry->size); |
| 161 | } | 211 | } |
| 212 | #endif | ||
diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 6001b4da39dd..26b75442ff7a 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h | |||
| @@ -306,6 +306,11 @@ extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, | |||
| 306 | u32 *mask, u32 req); | 306 | u32 *mask, u32 req); |
| 307 | extern void acpi_early_init(void); | 307 | extern void acpi_early_init(void); |
| 308 | 308 | ||
| 309 | extern int acpi_nvs_register(__u64 start, __u64 size); | ||
| 310 | |||
| 311 | extern int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *), | ||
| 312 | void *data); | ||
| 313 | |||
| 309 | #else /* !CONFIG_ACPI */ | 314 | #else /* !CONFIG_ACPI */ |
| 310 | 315 | ||
| 311 | #define acpi_disabled 1 | 316 | #define acpi_disabled 1 |
| @@ -348,15 +353,18 @@ static inline int acpi_table_parse(char *id, | |||
| 348 | { | 353 | { |
| 349 | return -1; | 354 | return -1; |
| 350 | } | 355 | } |
| 351 | #endif /* !CONFIG_ACPI */ | ||
| 352 | 356 | ||
| 353 | #ifdef CONFIG_ACPI_SLEEP | 357 | static inline int acpi_nvs_register(__u64 start, __u64 size) |
| 354 | int suspend_nvs_register(unsigned long start, unsigned long size); | ||
| 355 | #else | ||
| 356 | static inline int suspend_nvs_register(unsigned long a, unsigned long b) | ||
| 357 | { | 358 | { |
| 358 | return 0; | 359 | return 0; |
| 359 | } | 360 | } |
| 360 | #endif | 361 | |
| 362 | static inline int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *), | ||
| 363 | void *data) | ||
| 364 | { | ||
| 365 | return 0; | ||
| 366 | } | ||
| 367 | |||
| 368 | #endif /* !CONFIG_ACPI */ | ||
| 361 | 369 | ||
| 362 | #endif /*_LINUX_ACPI_H*/ | 370 | #endif /*_LINUX_ACPI_H*/ |
