Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Huang Ying and committed by
Len Brown
b54ac6d2 b4e008dc

+70 -10
+2 -2
arch/x86/kernel/e820.c
··· 714 714 } 715 715 #endif 716 716 717 - #ifdef CONFIG_HIBERNATION 717 + #ifdef CONFIG_ACPI 718 718 /** 719 719 * Mark ACPI NVS memory region, so that we can save/restore it during 720 720 * hibernation and the subsequent resume. ··· 727 727 struct e820entry *ei = &e820.map[i]; 728 728 729 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 733 return 0;
+2 -1
drivers/acpi/Makefile
··· 20 20 # All the builtin files are in the "acpi." module_param namespace. 21 21 acpi-y += osl.o utils.o reboot.o 22 22 acpi-y += atomicio.o 23 + acpi-y += nvs.o 23 24 24 25 # sleep related files 25 26 acpi-y += wakeup.o 26 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 #
+52 -1
drivers/acpi/nvs.c
··· 15 15 #include <linux/acpi_io.h> 16 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(&region->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 69 * Platforms, like ACPI, may want us to save some memory used by them during 20 70 * suspend and to restore the contents of this memory during the subsequent ··· 91 41 * things so that the data from page-aligned addresses in this region will 92 42 * be copied into separate RAM pages. 93 43 */ 94 - int suspend_nvs_register(unsigned long start, unsigned long size) 44 + static int suspend_nvs_register(unsigned long start, unsigned long size) 95 45 { 96 46 struct nvs_page *entry, *next; 97 47 ··· 209 159 if (entry->data) 210 160 memcpy(entry->kaddr, entry->data, entry->size); 211 161 } 162 + #endif
+14 -6
include/linux/acpi.h
··· 306 306 u32 *mask, u32 req); 307 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 314 #else /* !CONFIG_ACPI */ 310 315 311 316 #define acpi_disabled 1 ··· 353 348 { 354 349 return -1; 355 350 } 356 - #endif /* !CONFIG_ACPI */ 357 351 358 - #ifdef CONFIG_ACPI_SLEEP 359 - int suspend_nvs_register(unsigned long start, unsigned long size); 360 - #else 361 - static inline int suspend_nvs_register(unsigned long a, unsigned long b) 352 + static inline int acpi_nvs_register(__u64 start, __u64 size) 362 353 { 363 354 return 0; 364 355 } 365 - #endif 356 + 357 + static inline int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *), 358 + void *data) 359 + { 360 + return 0; 361 + } 362 + 363 + #endif /* !CONFIG_ACPI */ 366 364 367 365 #endif /*_LINUX_ACPI_H*/