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); > -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html