/*
* linux/kernel/power/snapshot.c
*
* This file provide system snapshot/restore functionality.
*
* Copyright (C) 1998-2005 Pavel Machek <pavel@suse.cz>
*
* This file is released under the GPLv2, and is based on swsusp.c.
*
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/suspend.h>
#include <linux/smp_lock.h>
#include <linux/delay.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/kernel.h>
#include <linux/pm.h>
#include <linux/device.h>
#include <linux/bootmem.h>
#include <linux/syscalls.h>
#include <linux/console.h>
#include <linux/highmem.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
#include <asm/pgtable.h>
#include <asm/tlbflush.h>
#include <asm/io.h>
#include "power.h"
struct pbe *pagedir_nosave;
static unsigned int nr_copy_pages;
static unsigned int nr_meta_pages;
static unsigned long *buffer;
struct arch_saveable_page {
unsigned long start;
unsigned long end;
char *data;
struct arch_saveable_page *next;
};
static struct arch_saveable_page *arch_pages;
int swsusp_add_arch_pages(unsigned long start, unsigned long end)
{
struct arch_saveable_page *tmp;
while (start < end) {
tmp = kzalloc(sizeof(struct arch_saveable_page), GFP_KERNEL);
if (!tmp)
return -ENOMEM;
tmp->start = start;
tmp->end = ((start >> PAGE_SHIFT) + 1) << PAGE_SHIFT;
if (tmp->end > end)
tmp->end = end;
tmp->next = arch_pages;
start = tmp->end;
arch_pages = tmp;
}
return 0;
}
static unsigned int count_arch_pages(void)
{
unsigned int count = 0;
struct arch_saveable_page *tmp = arch_pages;
while (tmp) {
count++;
tmp = tmp->next;
}
return count;
}
static int save_arch_mem(void)
{
char *kaddr;
struct arch_saveable_page *tmp = arch_pages;
int offset;
pr_debug("swsusp: Saving arch specific memory");
while (tmp) {
tmp->data = (char *)__get_free_page(GFP_ATOMIC);
if (!tmp->data)
return -ENOMEM;
offset = tmp->start - (tmp->start & PAGE_MASK);
/* arch pages might haven't a 'struct page' */
kaddr = kmap_atomic_pfn(tmp->start >> PAGE_SHIFT, KM_USER0);
memcpy(tmp->data + offset, kaddr + offset,
tmp->end - tmp->start);
kunmap_atomic(kaddr, KM_USER0);
tmp = tmp->next;
}
return 0;
}
static int restore_arch_mem(void)
{
char *kaddr;
struct arch_saveable_page *tmp = arch_pages;
int offset;
while (tmp) {
if (!tmp->data)
continue;
offset = tmp->start - (tmp->start & PAGE_MASK);
kaddr = kmap_atomic_pfn(tmp->start >> PAGE_SHIFT, KM_USER0);
memcpy(kaddr + offset, tmp->data + offset,
tmp->end - tmp->start);
kunmap_atomic(kaddr, KM_USER0);
free_page((long)tmp->data);
tmp->data = NULL;
tmp = tmp->next;
}
return 0;
}
#ifdef CONFIG_HIGHMEM
static unsigned int count_highmem_pages(void)
{
struct zone *zone;
unsigned long zone_pfn;
unsigned int n = 0;
for_each_zone (zone)
if (is_highmem(zone)) {
mark_free_pages(zone);
for (zone_pfn = 0; zone_pfn < zone->spanned_pages; zone_pfn++) {
struct page *page;
unsigned long pfn = zone_pfn + zone->zone_start_pfn;
if (!pfn_valid(pfn))
continue;
page = pfn_to_page(pfn);
if (PageReserved(page))
continue;
if (PageNosaveFree(page))
continue;
n++;
}
}
return n;
}
struct highmem_page {
char *data;
struct page *page;
struct highmem_page *next;
};
static struct highmem_page *highmem_copy;
static int save_highmem_zone(struct zone *zone)
{
unsigned long zone_pfn;
mark_free_pages(zone);
for (<