diff -ruNp 801-checksumming.patch-old/kernel/power/atomic_copy.c 801-checksumming.patch-new/kernel/power/atomic_copy.c
--- 801-checksumming.patch-old/kernel/power/atomic_copy.c	2005-07-30 21:06:30.000000000 +1000
+++ 801-checksumming.patch-new/kernel/power/atomic_copy.c	2005-07-30 20:27:58.000000000 +1000
@@ -277,6 +277,8 @@ void do_suspend2_lowlevel(int resume)
 	suspend2_arch_restore_processor_context();
 	suspend2_arch_flush_caches();
 
+	suspend2_checksum_print_differences();
+
 	/* Now we are running with our old stack, and with registers copied
 	 * from suspend time. Let's copy back those remaining highmem pages. */
 	copyback_high();
diff -ruNp 801-checksumming.patch-old/kernel/power/atomic_copy.h 801-checksumming.patch-new/kernel/power/atomic_copy.h
--- 801-checksumming.patch-old/kernel/power/atomic_copy.h	2005-07-30 21:06:30.000000000 +1000
+++ 801-checksumming.patch-new/kernel/power/atomic_copy.h	2005-07-30 20:27:58.000000000 +1000
@@ -1,3 +1,4 @@
 extern inline void move_stack_to_nonconflicing_area(void);
 extern int save_image_part1(void);
 extern void suspend_atomic_restore(void);
+
diff -ruNp 801-checksumming.patch-old/kernel/power/checksum.h 801-checksumming.patch-new/kernel/power/checksum.h
--- 801-checksumming.patch-old/kernel/power/checksum.h	1970-01-01 10:00:00.000000000 +1000
+++ 801-checksumming.patch-new/kernel/power/checksum.h	2005-07-30 20:27:58.000000000 +1000
@@ -0,0 +1,11 @@
+#ifdef CONFIG_SUSPEND2_CHECKSUMS
+extern void suspend2_verify_checksums(void);
+extern void suspend2_checksum_calculate_checksums(void);
+extern void suspend2_checksum_print_differences(void);
+extern int suspend2_allocate_checksum_pages(void);
+#else
+static inline void suspend2_verify_checksums(void) { };
+static inline void suspend2_checksum_calculate_checksums(void) { };
+static inline void suspend2_checksum_print_differences(void) { };
+static inline int suspend2_allocate_checksum_pages(void) { return 0; };
+#endif
diff -ruNp 801-checksumming.patch-old/kernel/power/Kconfig 801-checksumming.patch-new/kernel/power/Kconfig
--- 801-checksumming.patch-old/kernel/power/Kconfig	2005-07-30 21:06:30.000000000 +1000
+++ 801-checksumming.patch-new/kernel/power/Kconfig	2005-07-30 21:06:36.000000000 +1000
@@ -133,3 +133,10 @@ menuconfig SUSPEND2
 		  bit. Keep image mode is a little less user friendly on purpose - it
 		  should not be used without thought!
 
+	config SUSPEND2_CHECKSUMMING
+		bool '  Checksum the image'
+		depends on SUSPEND2
+		default n
+		---help---
+		  This option enables checksumming support, for (limited) 
+		  verification of the integrity of the image.
diff -ruNp 801-checksumming.patch-old/kernel/power/Makefile 801-checksumming.patch-new/kernel/power/Makefile
--- 801-checksumming.patch-old/kernel/power/Makefile	2005-07-30 21:06:30.000000000 +1000
+++ 801-checksumming.patch-new/kernel/power/Makefile	2005-07-30 21:06:36.000000000 +1000
@@ -25,6 +25,8 @@ obj-$(CONFIG_CRYPTO)			+= compression.o 
 obj-$(CONFIG_SUSPEND2_SWAPWRITER)	+= suspend_block_io.o suspend_swap.o
 obj-$(CONFIG_SUSPEND2_FILEWRITER)	+= suspend_block_io.o suspend_file.o
 
+obj-$(CONFIG_SUSPEND2_CHECKSUMMING)	+= suspend_checksums.o
+
 obj-$(CONFIG_SOFTWARE_SUSPEND)	+= swsusp.o disk.o
 
 obj-$(CONFIG_MAGIC_SYSRQ)	+= poweroff.o
diff -ruNp 801-checksumming.patch-old/kernel/power/pagedir.c 801-checksumming.patch-new/kernel/power/pagedir.c
--- 801-checksumming.patch-old/kernel/power/pagedir.c	2005-07-30 21:06:30.000000000 +1000
+++ 801-checksumming.patch-new/kernel/power/pagedir.c	2005-07-30 20:27:58.000000000 +1000
@@ -281,7 +281,7 @@ void suspend2_release_conflicting_pages(
 void suspend2_relocate_page_if_required(void ** page_pointer_addr)
 {
 	void * current_value = *page_pointer_addr;
-	if PagePageset1(virt_to_page(current_value)) {
+	if (PagePageset1(virt_to_page(current_value))) {
 		unsigned long * new_page = (unsigned long *) suspend2_get_nonconflicting_pages(0);
 		memcpy(new_page, current_value, PAGE_SIZE);
 		free_page((unsigned long) current_value);
diff -ruNp 801-checksumming.patch-old/kernel/power/prepare_image.c 801-checksumming.patch-new/kernel/power/prepare_image.c
--- 801-checksumming.patch-old/kernel/power/prepare_image.c	2005-07-30 21:06:30.000000000 +1000
+++ 801-checksumming.patch-new/kernel/power/prepare_image.c	2005-07-30 20:27:58.000000000 +1000
@@ -31,6 +31,7 @@
 #include "ui.h"
 #include "extent.h"
 #include "prepare_image.h"
+#include "checksum.h"
 
 static int are_frozen = 0, num_nosave = 0;
 static int header_space_allocated = 0;
@@ -292,6 +293,12 @@ static int update_image(void) 
 
 	result = suspend2_recalculate_stats();
 
+	if (suspend2_allocate_checksum_pages()) {
+		suspend_message(SUSPEND_ANY_SECTION, SUSPEND_LOW, 1,
+			"Still need to get more pages for checksum pages.\n");
+		return 1;
+	}
+
 	/* Include allowance for growth in pagedir1 while writing pagedir 2 */
 	if (suspend2_allocate_extra_pagedir_memory(&pagedir1,
 				pagedir1.pageset_size + EXTRA_PD1_PAGES_ALLOWANCE,
diff -ruNp 801-checksumming.patch-old/kernel/power/suspend_checksums.c 801-checksumming.patch-new/kernel/power/suspend_checksums.c
--- 801-checksumming.patch-old/kernel/power/suspend_checksums.c	1970-01-01 10:00:00.000000000 +1000
+++ 801-checksumming.patch-new/kernel/power/suspend_checksums.c	2005-07-30 20:27:58.000000000 +1000
@@ -0,0 +1,543 @@
+#include <linux/suspend.h>
+#include <linux/highmem.h>
+#ifdef CONFIG_KDB
+#include <linux/kdb.h>
+#include <linux/kdbprivate.h>
+#endif
+#include <linux/module.h>
+
+#include "suspend.h"
+#include "plugins.h"
+#include "pageflags.h"
+#include "proc.h"
+#include "pagedir.h"
+#include "ui.h"
+
+#define CHECKSUMS_PER_PAGE ((PAGE_SIZE - sizeof(void *)) / sizeof(unsigned long))
+#define NEXT_CHECKSUM_PAGE(page) *((unsigned long *) (((char *) (page)) + PAGE_SIZE - sizeof(void *)))
+
+static int checksum_pages;
+static unsigned long * first_checksum_page, *last_checksum_page;
+static int num_reload_pages = 0;
+
+struct reload_data
+{
+	int pageset;
+	int pagenumber;
+	struct page * page_address;
+	char * base_version;
+	char * compared_version;
+	struct reload_data * next;
+};
+
+static struct reload_data * first_reload_data, * last_reload_data;
+dyn_pageflags_t checksum_map;
+
+static inline int PageChecksumIgnore(struct page * page)
+{
+	return checksum_map ?
+		test_bit(PAGEBIT(page), PAGE_UL_PTR(checksum_map, page)) :
+		0;
+}
+
+static inline void SetPageChecksumIgnore(struct page * page)
+{
+	if (checksum_map)
+		set_bit(PAGEBIT(page), PAGE_UL_PTR(checksum_map,page));
+};
+
+static inline void ClearPageChecksumIgnore(struct page * page)
+{
+	if (checksum_map)
+		clear_bit(PAGEBIT(page), PAGE_UL_PTR(checksum_map,page));
+};
+
+unsigned long suspend_page_checksum(struct page * page)
+{
+	unsigned long * virt;
+	int i;
+	unsigned long value = 0;
+
+	virt = (unsigned long *) kmap_atomic(page, KM_USER0);
+	for (i = 0; i < (PAGE_SIZE / sizeof(unsigned long)); i++)
+		value += *(virt + i);
+	kunmap_atomic(virt, KM_USER0);
+	return value;
+}
+
+extern void get_first_pbe(struct pbe * pbe, struct pagedir * pagedir);
+extern void get_next_pbe(struct pbe * pbe);
+
+void __suspend_calculate_checksums(dyn_pageflags_t map, unsigned long **current_checksum_page,
+		int *page_index)
+{
+	int page_number;
+	
+	BITMAP_FOR_EACH_SET(map, page_number) {
+		//printk("Page number %d... Orig address %p.", i, pbe.origaddress);
+		*(*current_checksum_page + *page_index) =
+			suspend_page_checksum(pfn_to_page(page_number));
+		//printk("Checksum calculated as %lx.\n", *(current_checksum_page + page_index));
+		*page_index++;
+		if (*page_index == CHECKSUMS_PER_PAGE) {
+			*page_index = 0;
+			*current_checksum_page = (unsigned long *)
+				NEXT_CHECKSUM_PAGE(*current_checksum_page);
+			//printk("Moving to new checksum page %p.\n", current_checksum_page);
+		}
+	};
+}
+
+void suspend_calculate_checksums(void)
+{
+	int page_index = 0;
+	unsigned long * current_checksum_page = first_checksum_page;
+	
+	if (!first_checksum_page) {
+		suspend2_prepare_status(1, 0, "Unable to checksum at this point.");
+		return;
+	}
+
+	suspend2_prepare_status(1, 0, "Calculating checksums... ");
+	//printk("First checksum page is %p.\n", current_checksum_page);
+
+	__suspend_calculate_checksums(pageset1_map, &current_checksum_page,
+			&page_index);
+	
+	__suspend_calculate_checksums(pageset2_map, &current_checksum_page,
+			&page_index);
+
+	suspend2_prepare_status(1, 0, "Checksums done.");
+}
+
+int __suspend_check_checksums(int whichpagedir, unsigned long **current_checksum_page,
+		int *page_index, struct reload_data ** next_reload_data)
+{
+	int page_number, num_differences = 0;
+	unsigned long sum_now;
+	dyn_pageflags_t map;
+
+	if (whichpagedir == 1)
+		map = pageset1_map;
+	else
+		map = pageset2_map;
+
+	BITMAP_FOR_EACH_SET(map, page_number) {
+		/* Also ignore the page containing our variables */
+		if (!PageChecksumIgnore(pfn_to_page(page_number))) {
+			/* Also ignore the page containing our variables */
+			sum_now = suspend_page_checksum(pfn_to_page(page_number));
+			if (sum_now != *(*current_checksum_page + *page_index)) {
+				num_differences++;
+				if (next_reload_data) {
+					char * virt;
+					struct reload_data * this = *next_reload_data;
+					this->pageset = whichpagedir;
+					this->pagenumber = page_number;
+					this->page_address = pfn_to_page(page_number);
+					virt = kmap_atomic(pfn_to_page(page_number), KM_USER0);
+					memcpy(this->compared_version,
+						virt, PAGE_SIZE);
+					kunmap_atomic(virt, KM_USER0);
+					*next_reload_data = this->next;
+				}
+			}
+		}
+
+		*page_index++;
+		if (*page_index == CHECKSUMS_PER_PAGE) {
+			*page_index = 0;
+			*current_checksum_page = (unsigned long *) 
+				NEXT_CHECKSUM_PAGE(*current_checksum_page);
+		}
+	}
+
+	return num_differences;
+}
+
+void suspend_check_checksums(void)
+{
+	int page_index = 0, num_differences = 0;
+	unsigned long * current_checksum_page = first_checksum_page;
+	struct reload_data * next_reload_data = first_reload_data;
+
+	if (!first_checksum_page) {
+		suspend2_prepare_status(1, 0, "Unable to checksum at this point.");
+		return;
+	}
+
+	//suspend2_prepare_status(1, 0, "Checking checksums... ");
+	
+	num_differences += __suspend_check_checksums(1, &current_checksum_page,
+			&page_index, &next_reload_data);
+
+	num_differences += __suspend_check_checksums(2, &current_checksum_page,
+			&page_index, &next_reload_data);
+}
+
+/*
+ * free_reload_data.
+ *
+ * Reload data begins on a page boundary.
+ */
+void suspend_free_reload_data(void)
+{
+	struct reload_data * this_data = first_reload_data;
+	struct reload_data *prev_reload_data = this_data;
+	
+	while (this_data) {
+		if (this_data->compared_version) {
+		  ClearPageNosave(virt_to_page(this_data->compared_version));
+		  free_pages((unsigned long) this_data->compared_version, 0);
+		}
+
+		if (this_data->base_version) {
+			ClearPageNosave(virt_to_page(this_data->base_version));
+			free_pages((unsigned long) this_data->base_version, 0);
+		}
+
+		this_data = this_data->next;
+
+		if (!(((unsigned long) this_data) & ~PAGE_MASK)) {
+			//printk("Linking %p to %p.\n", prev_reload_data, this_data);
+			prev_reload_data->next = this_data;
+			prev_reload_data = this_data;
+		}
+	}
+
+	this_data = first_reload_data;
+	while (this_data) {
+		prev_reload_data = this_data;
+		this_data = this_data->next;
+		//printk("Freeing reload page %p.\n", prev_reload_data);
+		free_pages((unsigned long) prev_reload_data, 0);
+		num_reload_pages--;
+	}
+
+	first_reload_data = last_reload_data = NULL;
+
+}
+
+/* suspend_reread_pages()
+ *
+ * Description:	Reread pages from an image for diagnosing differences.
+ * Arguments:	page_list:	A list containing information on pages
+ *                              to be reloaded, sorted by pageset and
+ *                              page index.
+ * Returns:	Zero on success or -1 on failure.
+ */
+
+int suspend_reread_pages(struct reload_data * page_list)
+{
+	int result = 0, whichtoread, pageset_offset = -1;
+	long i = 0;
+	struct suspend_plugin_ops * this_filter, * first_filter = get_next_filter(NULL);
+	dyn_pageflags_t *pageflags = &pageset1_map;
+
+	if (!page_list)
+		return 0;
+
+	for (whichtoread = page_list->pageset; whichtoread <= 2; whichtoread++) {
+		struct pagedir * pagedir;
+
+		switch (whichtoread) {
+			case 1:
+				pagedir = &pagedir1;
+				break;
+			case 2:
+				pagedir = &pagedir2;
+				pageflags = &pageset2_map;
+				pageset_offset = -1;
+				i = -1;
+				break;
+			default:
+				goto out;
+		}
+
+		suspend_message(SUSPEND_IO, SUSPEND_LOW, 0,
+			"Reread pages from pagedir %d.\n", whichtoread);
+
+		/* Initialise page transformers */
+		list_for_each_entry(this_filter, &suspend_filters, ops.filter.filter_list) {
+			if (this_filter->disabled)
+				continue;
+			if (this_filter->read_init && 
+				this_filter->read_init(whichtoread)) {
+				abort_suspend("Failed to initialise a filter.");
+				return 1;
+			}
+		}
+
+		/* Initialise writer */
+		if (active_writer->read_init(whichtoread)) {
+			abort_suspend("Failed to initialise the writer."); 
+			result = 1;
+			goto reread_free_buffers;
+		}
+
+		/* Read the pages */
+		while(i <= page_list->pagenumber) {
+			/* Read */
+			result = first_filter->ops.filter.read_chunk(
+					virt_to_page(page_list->base_version),
+					SUSPEND_SYNC);
+
+			if (result) {
+				abort_suspend("Failed to read a chunk of the image.");
+				goto reread_free_buffers;
+			}
+
+			/* Interactivity*/
+			check_shift_keys(0, NULL);
+
+			/* Prepare next */
+			pageset_offset = __get_next_bit_on(*pageflags, pageset_offset);
+
+			/* Got the one we're after? */
+			i++;
+
+			if (i == page_list->pagenumber)
+				page_list = page_list->next;
+
+			if (page_list->pageset != whichtoread)
+				break;
+		}
+		
+reread_free_buffers:
+
+		/* Cleanup reads from this pageset. */
+		list_for_each_entry(this_filter, &suspend_plugins, plugin_list) {
+			if (this_filter->disabled)
+				continue;
+			if (this_filter->read_cleanup &&
+				this_filter->read_cleanup()) {
+				abort_suspend("Failed to cleanup a filter.");
+				result = 1;
+			}
+		}
+
+		if (active_writer->read_cleanup()) {
+			abort_suspend("Failed to cleanup the writer.");
+			result = 1;
+		}
+	}
+out:
+	printk("\n");
+
+	return result;
+}
+void suspend_free_checksum_pages(void)
+{
+	unsigned long * next_checksum_page;
+
+	while(first_checksum_page) {
+		next_checksum_page =
+		  (unsigned long *) NEXT_CHECKSUM_PAGE(first_checksum_page);
+		free_pages((unsigned long) first_checksum_page, 0);
+		first_checksum_page = next_checksum_page;
+	}
+	last_checksum_page = NULL;
+	checksum_pages = 0;
+}
+
+#define PRINTABLE(a) (((a) < 32 || (a) > 122) ? '.' : (a))
+static void local_print_location(
+		unsigned char * real,
+		unsigned char * original,
+		unsigned char * resumetime)
+{
+	int i;
+
+	for (i = 0; i < 8; i++)
+		if (*(original + i) != *(resumetime + i))
+			break;
+	if (i == 8)
+		return;
+	
+	suspend_message(SUSPEND_INTEGRITY, SUSPEND_HIGH, 1, "%p", real);
+	if (PageNosave(virt_to_page(real)))
+		suspend_message(SUSPEND_INTEGRITY, SUSPEND_HIGH, 1,
+			" [NoSave]");
+	if (PageSlab(virt_to_page(real)))
+		suspend_message(SUSPEND_INTEGRITY, SUSPEND_HIGH, 1,
+			" [Slab]");
+	suspend_message(SUSPEND_INTEGRITY, SUSPEND_HIGH, 1, "\n");
+
+#ifdef CONFIG_KDB
+	for (i = 0; i < 8; i++) {
+		static const char *last_sym = NULL;
+		if (*(original + i) != *(resumetime + i)) {
+			kdb_symtab_t symtab;
+
+			kdbnearsym((unsigned long) real + i,
+					&symtab);
+
+			if ((!symtab.sym_name) ||
+					(symtab.sym_name == last_sym))
+				continue;
+
+			last_sym = symtab.sym_name;
+
+			suspend_message(SUSPEND_INTEGRITY, SUSPEND_LOW, 1,
+				"%p = %s\n",
+				symtab.sym_start,
+				symtab.sym_name);
+		}
+	}
+#endif
+
+	for (i = 0; i < 8; i++)
+		suspend_message(SUSPEND_INTEGRITY, SUSPEND_HIGH, 1,
+			"%2x ", *(original + i));
+	suspend_message(SUSPEND_INTEGRITY, SUSPEND_HIGH, 1, "    ");
+	for (i = 0; i < 8; i++)
+		suspend_message(SUSPEND_INTEGRITY, SUSPEND_HIGH, 1,
+			"%c", PRINTABLE(*(original + i)));
+	suspend_message(SUSPEND_INTEGRITY, SUSPEND_HIGH, 1, "    ");
+	
+	for (i = 0; i < 8; i++)
+		suspend_message(SUSPEND_INTEGRITY, SUSPEND_HIGH, 1,
+			"%2x ", *(resumetime + i));
+	suspend_message(SUSPEND_INTEGRITY, SUSPEND_HIGH, 1, "    ");
+	for (i = 0; i < 8; i++)
+		suspend_message(SUSPEND_INTEGRITY, SUSPEND_HIGH, 1,
+			"%c", PRINTABLE(*(resumetime + i)));
+	suspend_message(SUSPEND_INTEGRITY, SUSPEND_HIGH, 1, "\n\n");
+}
+
+int suspend_allocate_reload_data(int pages)
+{
+	struct reload_data * this_data;
+	unsigned long data_start;
+	int i;
+
+	if (num_reload_pages >= pages)
+		return 0;
+
+	for (i = 1; i <= pages; i++) {
+		data_start = get_zeroed_page(GFP_ATOMIC);
+
+		if (!data_start)
+			return -ENOMEM;
+
+		SetPageChecksumIgnore(virt_to_page(data_start));
+		this_data = (struct reload_data *) data_start; 
+		num_reload_pages++;
+
+		while (data_start == 
+		  ((((unsigned long) (this_data + 1)) - 1) & PAGE_MASK)) {
+			struct page * page;
+			unsigned long virt;
+
+			virt = get_zeroed_page(GFP_ATOMIC);
+			if (!virt) {
+				printk("Couldn't get a page in which to store "
+					"a changed page.\n");
+				return -ENOMEM;
+			}
+			page = virt_to_page(virt);
+
+			this_data->compared_version = (char *) virt;
+			SetPageNosave(page);
+			SetPageChecksumIgnore(page);
+			
+			virt = get_zeroed_page(GFP_ATOMIC);
+			if (!virt) {
+				printk("Couldn't get a page in which to store "
+					"a baseline page.\n");
+				return -ENOMEM;
+			}
+			page = virt_to_page(virt);
+
+			this_data->base_version = (char *) virt;
+			SetPageNosave(page);
+			SetPageChecksumIgnore(page);
+
+			if (last_reload_data)
+				last_reload_data->next = this_data;
+			else
+				first_reload_data = this_data;
+			
+			last_reload_data = this_data;
+
+			this_data++;
+		}
+
+		check_shift_keys(0, NULL);
+	}
+
+	return 0;
+}
+
+void suspend_print_differences(void)
+{
+	struct reload_data * this_data = first_reload_data;
+	int i;
+
+	suspend_reread_pages(first_reload_data);
+	
+	while (this_data) {
+		if (this_data->pageset && 
+		    this_data->pagenumber) {
+			suspend_message(SUSPEND_INTEGRITY, SUSPEND_MEDIUM, 1,
+				"Pagedir %d. Page %d. Address %p."
+				" Base %p. Copy %p.\n",
+				this_data->pageset,
+				this_data->pagenumber,
+				page_address(this_data->page_address),
+				this_data->base_version,
+				this_data->compared_version);
+			for (i= 0; i < (PAGE_SIZE / 8); i++) {
+				local_print_location(
+					page_address(this_data->page_address) + i * 8,
+					this_data->base_version + i * 8,
+					this_data->compared_version + i * 8);
+				check_shift_keys(0, NULL);
+			}
+			check_shift_keys(1, NULL);
+		} else
+			return;
+		this_data = this_data->next;
+	}
+}
+
+int __suspend_allocate_checksum_pages(void)
+{
+	int pages_required =
+		(pagedir1.pageset_size + pagedir2.pageset_size) / CHECKSUMS_PER_PAGE;
+	unsigned long this_page;
+
+	while (checksum_pages <= pages_required) {
+		this_page = get_zeroed_page(GFP_ATOMIC);
+		if (!this_page)
+			return -ENOMEM;
+
+		if (!first_checksum_page)
+			first_checksum_page = 
+				(unsigned long *) this_page;
+		else
+			NEXT_CHECKSUM_PAGE(last_checksum_page) = this_page;
+		
+		last_checksum_page = (unsigned long *) this_page;
+		SetPageChecksumIgnore(virt_to_page(this_page));
+		checksum_pages++;
+	}
+
+	return suspend_allocate_reload_data(2);
+}
+
+int suspend_checksum_init(void)
+{
+	if (suspend_allocate_dyn_pageflags(&checksum_map))
+		return 1;
+	return 0;
+}
+
+
+void suspend_checksum_cleanup(void)
+{
+	suspend_free_reload_data();
+	suspend_free_checksum_pages();
+	
+	free_dyn_pageflags(&checksum_map);
+}

