Oops, send to the right linux-kernel mail list, :) thanks,rui On Thu, 2008-07-03 at 10:24 +0800, Zhang Rui wrote:> According to the ACPI spec, ACPI NVS memory region is required to be> saved/restored by OS during hibernation.> > Section 15.3.2 ACPI Spec 3.0b,> "OSPM will call the _PTS control method some time before entering a sleeping state,> to allow the platform’s AML code to update this memory image before entering the> sleeping state. After the system awakes from an S4 state, OSPM will restore this memory> area and call the _WAK control method to enable the BIOS to reclaim its memory image."> > Add the mechanism to save/restore ACPI NVS memory during hibernation.> > Note: now Linux save ACPI NVS memory in acpi_hibernation_pre_snapshot, and restore it in> acpi_hibernation_leave. Both of these functions will be invoked only once during> the hibernate and resume.> Note: in Section 14.3 ACPI spec 3.0b, I only get> "EfiACPIMemoryNVS: The OS and loader must preserve this memory range in> the working and ACPI S1–S3 states."> whether we should save/restore this piece of memory is not cleared.> > Signed-off-by: Zhang Rui <rui.zhang@xxxxxxxxx>> ---> drivers/acpi/sleep/main.c | 103 +++++++++++++++++++++++++++++++++++++++++++++-> include/linux/acpi.h | 3 +> 2 files changed, 104 insertions(+), 2 deletions(-)> > Index: linux-2.6/drivers/acpi/sleep/main.c> ===================================================================> --- linux-2.6.orig/drivers/acpi/sleep/main.c 2008-06-30 16:28:56.000000000 +0800> +++ linux-2.6/drivers/acpi/sleep/main.c 2008-07-01 09:30:36.000000000 +0800> @@ -15,6 +15,7 @@> #include <linux/dmi.h>> #include <linux/device.h>> #include <linux/suspend.h>> +#include <linux/highmem.h>> > #include <asm/io.h>> > @@ -255,11 +256,91 @@> #endif /* CONFIG_SUSPEND */> > #ifdef CONFIG_HIBERNATION> +> +/* ACPI NVS memory need to be saved/stored during hibernation */> +struct nvs_page {> + unsigned long pfn;> + void *data;> + struct list_head node;> +};> +static LIST_HEAD(nvs_pages_list);> +> +int acpi_mark_nvs_region(unsigned long start, unsigned long end)> +{> + struct nvs_page *pos;> +> + while (start <= end) {> + pos = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);> + if (!pos)> + return -ENOMEM;> + pos->pfn = start;> + start++;> + list_add_tail(&pos->node, &nvs_pages_list);> + }> + return 0;> +}> +> +static int acpi_free_nvs_pages(void)> +{> + struct nvs_page *pos;> +> + list_for_each_entry(pos, &nvs_pages_list, node) {> + if (!pos->data)> + break;> + free_page((long)pos->data);> + pos->data = NULL;> + }> + return 0;> +}> +> +static int acpi_allocate_nvs_pages(void)> +{> + struct nvs_page *pos;> +> + list_for_each_entry(pos, &nvs_pages_list, node) {> + pos->data = (void *)__get_free_page(GFP_KERNEL);> + if (!pos->data) {> + acpi_free_nvs_pages();> + return -ENOMEM;> + }> + }> + return 0;> +}> +> +static void acpi_save_nvs_memory(void)> +{> + void *kaddr;> + struct nvs_page *pos;> +> + pr_debug("ACPI: Saving ACPI NVS memory\n");> + list_for_each_entry(pos, &nvs_pages_list, node) {> + /* nvs page might not have a 'struct page' */> + kaddr = kmap_atomic_pfn(pos->pfn, KM_USER0);> + copy_page(pos->data, kaddr);> + kunmap_atomic(kaddr, KM_USER0);> + }> + return;> +}> +> +static void acpi_restore_nvs_memory(void)> +{> + void *kaddr;> + struct nvs_page *pos;> +> + pr_debug("ACPI: Restoring ACPI NVS memory\n");> + list_for_each_entry(pos, &nvs_pages_list, node) {> + kaddr = kmap_atomic_pfn(pos->pfn, KM_USER0);> + copy_page(kaddr, pos->data);> + kunmap_atomic(kaddr, KM_USER0);> + }> +}> +> static int acpi_hibernation_begin(void)> {> acpi_target_sleep_state = ACPI_STATE_S4;> > - return 0;> + /* allocate pages for ACPI NVS memory before swsusp_shrink_memory */> + return acpi_allocate_nvs_pages();> }> > static int acpi_hibernation_prepare(void)> @@ -274,6 +355,20 @@> return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT;> }> > +static int acpi_hibernation_pre_snapshot(void)> +{> + int error = acpi_sleep_prepare(ACPI_STATE_S4);> +> + if (error) {> + acpi_target_sleep_state = ACPI_STATE_S0;> + return error;> + }> +> + acpi_save_nvs_memory();> +> + return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT;> +}> +> static int acpi_hibernation_enter(void)> {> acpi_status status = AE_OK;> @@ -301,6 +396,7 @@> acpi_enable();> /* Reprogram control registers and execute _BFS */> acpi_leave_sleep_state_prep(ACPI_STATE_S4);> + acpi_restore_nvs_memory();> }> > static void acpi_hibernation_finish(void)> @@ -321,6 +417,9 @@> * during a failing transition to the sleep state.> */> acpi_target_sleep_state = ACPI_STATE_S0;> +> + /* free pages allocated for ACPI NVS memory */> + acpi_free_nvs_pages();> }> > static int acpi_hibernation_pre_restore(void)> @@ -340,7 +439,7 @@> static struct platform_hibernation_ops acpi_hibernation_ops = {> .begin = acpi_hibernation_begin,> .end = acpi_hibernation_end,> - .pre_snapshot = acpi_hibernation_prepare,> + .pre_snapshot = acpi_hibernation_pre_snapshot,> .finish = acpi_hibernation_finish,> .prepare = acpi_hibernation_prepare,> .enter = acpi_hibernation_enter,> Index: linux-2.6/include/linux/acpi.h> ===================================================================> --- linux-2.6.orig/include/linux/acpi.h 2008-06-30 16:28:56.000000000 +0800> +++ linux-2.6/include/linux/acpi.h 2008-06-30 16:29:26.000000000 +0800> @@ -106,6 +106,9 @@> void acpi_numa_arch_fixup(void);> #endif> > +#ifdef CONFIG_HIBERNATION> +int acpi_mark_nvs_region(unsigned long, unsigned long);> +#endif> #ifdef CONFIG_ACPI_HOTPLUG_CPU> /* Arch dependent functions for cpu hotplug support */> int acpi_map_lsapic(acpi_handle handle, int *pcpu);> _______________________________________________linux-pm mailing listlinux-pm@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx://lists.linux-foundation.org/mailman/listinfo/linux-pm