diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 9baee3e..855bda4 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -368,6 +368,91 @@ free_handle:
 	return status;
 }
 
+#ifdef CONFIG_SNAPSHOT_VERIFICATION
+static efi_status_t setup_s4_keys(struct boot_params *params)
+{
+	struct setup_data *data;
+	unsigned long datasize;
+	u32 attr;
+	struct efi_s4_key *s4key;
+	efi_status_t status;
+
+	data = (struct setup_data *)(unsigned long)params->hdr.setup_data;
+
+	while (data && data->next)
+		data = (struct setup_data *)(unsigned long)data->next;
+
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA, sizeof(*s4key), &s4key);
+	if (status != EFI_SUCCESS) {
+		efi_printk("Failed to alloc memory for efi_s4_key\n");
+		goto error_setup;
+	}
+
+	s4key->data.type = SETUP_S4_KEY;
+	s4key->data.len = sizeof(struct efi_s4_key) -
+		sizeof(struct setup_data);
+	s4key->data.next = 0;
+	s4key->skey_dsize = 0;
+	s4key->err_status = 0;
+
+	if (data)
+		data->next = (unsigned long)s4key;
+	else
+		params->hdr.setup_data = (unsigned long)s4key;
+
+	/* obtain the size of key data */
+	datasize = 0;
+	status = efi_call_phys5(sys_table->runtime->get_variable,
+				EFI_S4_SIGN_KEY_NAME, &EFI_HIBERNATE_GUID,
+				NULL, &datasize, NULL);
+	if (status != EFI_BUFFER_TOO_SMALL) {
+		efi_printk("Couldn't get S4 key data size\n");
+		goto error_size;
+	}
+	if (datasize > PAGE_SIZE - sizeof(datasize)) {
+		efi_printk("The size of S4 sign key is too large\n");
+		status = EFI_UNSUPPORTED;
+		goto error_size;
+	}
+
+	s4key->skey_dsize = datasize;
+	status = efi_call_phys3(sys_table->boottime->allocate_pool,
+				EFI_LOADER_DATA, s4key->skey_dsize,
+				&s4key->skey_data_addr);
+	if (status != EFI_SUCCESS) {
+		efi_printk("Failed to alloc page for S4 key data\n");
+		goto error_s4key;
+	}
+
+	attr = 0;
+	memset((void *)s4key->skey_data_addr, 0, s4key->skey_dsize);
+	status = efi_call_phys5(sys_table->runtime->get_variable,
+				EFI_S4_SIGN_KEY_NAME, &EFI_HIBERNATE_GUID, &attr,
+				&(s4key->skey_dsize), s4key->skey_data_addr);
+	if (status) {
+		efi_printk("Couldn't get S4 key data\n");
+		goto error_gets4key;
+	}
+	if (attr & EFI_VARIABLE_RUNTIME_ACCESS) {
+		efi_printk("S4 sign key can not be a runtime variable\n");
+		memset((void *)s4key->skey_data_addr, 0, s4key->skey_dsize);
+		status = EFI_UNSUPPORTED;
+		goto error_gets4key;
+	}
+
+	return 0;
+
+error_gets4key:
+	efi_call_phys1(sys_table->boottime->free_pool, s4key->skey_data_addr);
+error_s4key:
+error_size:
+	s4key->err_status = status;
+error_setup:
+	return status;
+}
+#endif /* CONFIG_SNAPSHOT_VERIFICATION */
+
 /*
  * See if we have Graphics Output Protocol
  */
@@ -1205,6 +1290,10 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table,
 
 	setup_efi_pci(boot_params);
 
+#ifdef CONFIG_SNAPSHOT_VERIFICATION
+	setup_s4_keys(boot_params);
+#endif
+
 	status = efi_call_phys3(sys_table->boottime->allocate_pool,
 				EFI_LOADER_DATA, sizeof(*gdt),
 				(void **)&gdt);
diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
index 0062a01..56ececa 100644
--- a/arch/x86/include/asm/efi.h
+++ b/arch/x86/include/asm/efi.h
@@ -102,6 +102,15 @@ extern void efi_call_phys_epilog(void);
 extern void efi_unmap_memmap(void);
 extern void efi_memory_uc(u64 addr, unsigned long size);
 
+#ifdef CONFIG_SNAPSHOT_VERIFICATION
+struct efi_s4_key {
+	struct setup_data data;
+	unsigned long err_status;
+	unsigned long skey_dsize;
+	void *skey_data_addr;
+};
+#endif
+
 #ifdef CONFIG_EFI
 
 static inline bool efi_is_native(void)
diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h
index 85d7685..79398ff 100644
--- a/arch/x86/include/uapi/asm/bootparam.h
+++ b/arch/x86/include/uapi/asm/bootparam.h
@@ -6,6 +6,7 @@
 #define SETUP_E820_EXT			1
 #define SETUP_DTB			2
 #define SETUP_PCI			3
+#define SETUP_S4_KEY			4
 
 /* ram_size flags */
 #define RAMDISK_IMAGE_START_MASK	0x07FF
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 90f6ed1..c8a4fca 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -704,6 +704,69 @@ static int __init efi_memmap_init(void)
 	return 0;
 }
 
+#ifdef CONFIG_SNAPSHOT_VERIFICATION
+static unsigned long skey_dsize;
+static u64 skey_data_addr;
+static unsigned long skey_err_status;
+
+bool efi_s4_key_available(void)
+{
+	return skey_dsize && skey_data_addr && !skey_err_status;
+}
+
+unsigned long __init efi_copy_skey_data(void *page_addr)
+{
+	void *key_addr;
+
+	if (efi_s4_key_available()) {
+		key_addr = early_ioremap(skey_data_addr, skey_dsize);
+		memcpy(page_addr, key_addr, skey_dsize);
+		early_iounmap(key_addr, skey_dsize);
+	}
+
+	return skey_dsize;
+}
+
+void __init efi_erase_s4_skey_data(void)
+{
+	void *key_addr;
+
+	key_addr = early_ioremap(skey_data_addr, skey_dsize);
+	memset(key_addr, 0, skey_dsize);
+	early_iounmap(key_addr, skey_dsize);
+	memblock_free(skey_data_addr, skey_dsize);
+	skey_data_addr = 0;
+	skey_dsize = 0;
+}
+
+static void __init efi_reserve_s4_skey_data(void)
+{
+	u64 pa_data;
+	struct setup_data *data;
+	struct efi_s4_key *s4key;
+
+	skey_err_status = 0;
+	pa_data = boot_params.hdr.setup_data;
+	while (pa_data) {
+		data = early_ioremap(pa_data, sizeof(*s4key));
+		if (data->type == SETUP_S4_KEY) {
+			s4key = (struct efi_s4_key *)data;
+			if (!s4key->err_status) {
+				skey_dsize = s4key->skey_dsize;
+				skey_data_addr = (u64) s4key->skey_data_addr;
+				memblock_reserve(skey_data_addr, skey_dsize);
+			} else {
+				skey_err_status = s4key->err_status;
+				pr_err("Get S4 sign key from EFI fail: 0x%lx\n",
+					skey_err_status);
+			}
+		}
+		pa_data = data->next;
+		early_iounmap(data, sizeof(*s4key));
+	}
+}
+#endif /* CONFIG_SNAPSHOT_VERIFICATION */
+
 void __init efi_init(void)
 {
 	efi_char16_t *c16;
@@ -729,6 +792,11 @@ void __init efi_init(void)
 
 	set_bit(EFI_SYSTEM_TABLES, &x86_efi_facility);
 
+#ifdef CONFIG_SNAPSHOT_VERIFICATION
+	/* keep s4 key from setup_data */
+	efi_reserve_s4_skey_data();
+#endif
+
 	/*
 	 * Show what we know for posterity
 	 */
diff --git a/include/linux/efi.h b/include/linux/efi.h
index febce85..55f80be 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -389,6 +389,18 @@ typedef efi_status_t efi_query_variable_store_t(u32 attributes, unsigned long si
 #define EFI_FILE_SYSTEM_GUID \
     EFI_GUID(  0x964e5b22, 0x6459, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b )
 
+#ifdef CONFIG_SNAPSHOT_VERIFICATION
+#define EFI_HIBERNATE_GUID \
+    EFI_GUID(0xfe141863, 0xc070, 0x478e, 0xb8, 0xa3, 0x87, 0x8a, 0x5d, 0xc9, 0xef, 0x21)
+/*
+ * The UEFI variable names of the key-pair to verify S4 snapshot image:
+ * S4SignKey-EFI_HIBERNATE_GUID: The private key is used to sign snapshot
+ * S4WakeKey-EFI_HIBERNATE_GUID: The public key is used to verify snapshot
+ */
+#define EFI_S4_SIGN_KEY_NAME    ((efi_char16_t [10]) { 'S', '4', 'S', 'i', 'g', 'n', 'K', 'e', 'y', 0 })
+#define EFI_S4_WAKE_KEY_NAME    ((efi_char16_t [10]) { 'S', '4', 'W', 'a', 'k', 'e', 'K', 'e', 'y', 0 })
+#endif /* CONFIG_SNAPSHOT_VERIFICATION */
+
 typedef struct {
 	efi_guid_t guid;
 	u64 table;
@@ -577,6 +589,11 @@ extern void efi_enter_virtual_mode (void);	/* switch EFI to virtual mode, if pos
 extern void efi_late_init(void);
 extern void efi_free_boot_services(void);
 extern efi_status_t efi_query_variable_store(u32 attributes, unsigned long size);
+#ifdef CONFIG_SNAPSHOT_VERIFICATION
+extern bool efi_s4_key_available(void);
+extern unsigned long efi_copy_skey_data(void *page_addr);
+extern void efi_erase_s4_skey_data(void);
+#endif /* CONFIG_SNAPSHOT_VERIFICATION */
 #else
 static inline void efi_late_init(void) {}
 static inline void efi_free_boot_services(void) {}
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index d444c4e..b592d88 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -66,8 +66,17 @@ config HIBERNATION
 
 	  For more information take a look at <file:Documentation/power/swsusp.txt>.
 
-config ARCH_SAVE_PAGE_KEYS
-	bool
+config SNAPSHOT_VERIFICATION
+	bool "Hibernate snapshot verification"
+	depends on HIBERNATION
+	depends on EFI_STUB
+	depends on X86
+	select PKCS8_PRIVATE_KEY_INFO_PARSER
+	help
+	  This option provides support for generate anad verify the signautre by
+	  RSA key-pair against hibernate snapshot image. Current mechanism
+	  dependent on UEFI environment. EFI bootloader should generate the
+	  key-pair.
 
 config PM_STD_PARTITION
 	string "Default resume partition"
@@ -91,6 +100,9 @@ config PM_STD_PARTITION
 	  suspended image to. It will simply pick the first available swap 
 	  device.
 
+config ARCH_SAVE_PAGE_KEYS
+	bool
+
 config PM_SLEEP
 	def_bool y
 	depends on SUSPEND || HIBERNATE_CALLBACKS
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 29472bf..46b6422 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_VT_CONSOLE_SLEEP)	+= console.o
 obj-$(CONFIG_FREEZER)		+= process.o
 obj-$(CONFIG_SUSPEND)		+= suspend.o
 obj-$(CONFIG_PM_TEST_SUSPEND)	+= suspend_test.o
+obj-$(CONFIG_SNAPSHOT_VERIFICATION)    += hibernate_keys.o
 obj-$(CONFIG_HIBERNATION)	+= hibernate.o snapshot.o swap.o user.o \
 				   block_io.o
 obj-$(CONFIG_PM_AUTOSLEEP)	+= autosleep.o
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index b26f5f1..c545b15 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -28,6 +28,7 @@
 #include <linux/syscore_ops.h>
 #include <linux/ctype.h>
 #include <linux/genhd.h>
+#include <linux/key.h>
 
 #include "power.h"
 
@@ -631,6 +632,7 @@ static void power_down(void)
 int hibernate(void)
 {
 	int error;
+	int skey_error;
 
 	lock_system_sleep();
 	/* The snapshot device should not be opened while we're running */
@@ -680,6 +682,7 @@ int hibernate(void)
 		pm_restore_gfp_mask();
 	} else {
 		pr_debug("PM: Image restored successfully.\n");
+		restore_sign_key_data();
 	}
 
  Thaw:
diff --git a/kernel/power/hibernate_keys.c b/kernel/power/hibernate_keys.c
new file mode 100644
index 0000000..1bc5976
--- /dev/null
+++ b/kernel/power/hibernate_keys.c
@@ -0,0 +1,290 @@
+#include <linux/sched.h>
+#include <linux/efi.h>
+#include <linux/mpi.h>
+#include <linux/asn1.h>
+#include <crypto/public_key.h>
+#include <keys/asymmetric-type.h>
+
+#include "power.h"
+
+static void *skey_data;
+static void *skey_data_buf;
+static unsigned long skey_dsize;
+
+static int efi_status_to_err(efi_status_t status)
+{
+	int err;
+
+	switch (status) {
+	case EFI_INVALID_PARAMETER:
+		err = -EINVAL;
+		break;
+	case EFI_OUT_OF_RESOURCES:
+		err = -ENOSPC;
+		break;
+	case EFI_DEVICE_ERROR:
+		err = -EIO;
+		break;
+	case EFI_WRITE_PROTECTED:
+		err = -EROFS;
+		break;
+	case EFI_SECURITY_VIOLATION:
+		err = -EACCES;
+		break;
+	case EFI_NOT_FOUND:
+		err = -ENODATA;
+		break;
+	default:
+		err = -EINVAL;
+	}
+
+	return err;
+}
+
+bool swsusp_page_is_sign_key(struct page *page)
+{
+	unsigned long skey_data_pfn;
+	bool ret;
+
+	if (!skey_data || IS_ERR(skey_data))
+		return false;
+
+	skey_data_pfn = page_to_pfn(virt_to_page(skey_data));
+	ret = (page_to_pfn(page) == skey_data_pfn) ? true : false;
+	if (ret)
+		pr_info("PM: Avoid snapshot the page of S4 sign key.\n");
+
+	return ret;
+}
+
+unsigned long get_skey_data_buf_pfn(void)
+{
+	if (!skey_data_buf || IS_ERR(skey_data_buf))
+		return 0;
+
+	return page_to_pfn(virt_to_page(skey_data_buf));
+}
+
+void clone_skey_data(void *page)
+{
+	if (!page)
+		return;
+
+	if (skey_data && !IS_ERR(skey_data)) {
+		memcpy(page, &skey_dsize, sizeof(skey_dsize));
+		memcpy(page + sizeof(skey_dsize), skey_data, PAGE_SIZE - sizeof(skey_dsize));
+	}
+}
+
+void restore_sign_key_data(void)
+{
+	memset(skey_data, 0, PAGE_SIZE);
+	if (skey_data_buf && !IS_ERR(skey_data_buf)) {
+		/* restore sign key size and data from buffer */
+		memcpy(&skey_dsize, skey_data_buf, sizeof(skey_dsize));
+		memcpy(skey_data, skey_data_buf + sizeof(skey_dsize),
+				PAGE_SIZE - sizeof(skey_dsize));
+		/* reset skey page buffer */
+		memset(skey_data_buf, 0, PAGE_SIZE);
+		pr_info("PM: Restore S4 sign key from buffer\n");
+	} else
+		pr_err("PM: Restore S4 sign key fail\n");
+}
+
+bool skey_data_available(void)
+{
+	static unsigned char const_seq = (ASN1_SEQ | (ASN1_CONS << 5));
+	bool ret = false;
+
+	/* Sign key is PKCS#8 format that must be a Constructed SEQUENCE */
+	ret = skey_data && !IS_ERR(skey_data) &&
+		(skey_dsize != 0) &&
+		((unsigned char *)skey_data)[0] == const_seq;
+
+	return ret;
+}
+
+struct key *get_sign_key(void)
+{
+	const struct cred *cred = current_cred();
+	struct key *skey;
+	int err;
+
+	if (!skey_data || IS_ERR(skey_data))
+		return ERR_PTR(-EBADMSG);
+
+	skey = key_alloc(&key_type_asymmetric, "s4_sign_key",
+			GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
+			cred, 0, KEY_ALLOC_NOT_IN_QUOTA);
+	if (IS_ERR(skey)) {
+		pr_err("PM: Allocate s4 sign key error: %ld\n", PTR_ERR(skey));
+		goto error_keyalloc;
+	}
+
+	err = key_instantiate_and_link(skey, skey_data, skey_dsize, NULL, NULL);
+	if (err < 0) {
+		pr_err("PM: S4 sign key instantiate error: %d\n", err);
+		if (skey)
+			key_put(skey);
+		skey = ERR_PTR(err);
+		goto error_keyinit;
+	}
+
+	return skey;
+
+error_keyinit:
+error_keyalloc:
+	return skey;
+}
+
+void erase_skey_data(void)
+{
+	if (!skey_data || IS_ERR(skey_data))
+		return;
+
+	memset(skey_data, 0, PAGE_SIZE);
+}
+
+void destroy_sign_key(struct key *skey)
+{
+	erase_skey_data();
+	if (skey)
+		key_put(skey);
+}
+
+static void *load_wake_key_data(unsigned long *datasize)
+{
+	u32 attr;
+	void *wkey_data;
+	efi_status_t status;
+
+	if (!efi_enabled(EFI_RUNTIME_SERVICES))
+		return ERR_PTR(-EPERM);
+
+	/* obtain the size */
+	*datasize = 0;
+	status = efi.get_variable(EFI_S4_WAKE_KEY_NAME, &EFI_HIBERNATE_GUID,
+				  NULL, datasize, NULL);
+	if (status != EFI_BUFFER_TOO_SMALL) {
+		wkey_data = ERR_PTR(efi_status_to_err(status));
+		pr_err("PM: Couldn't get wake key data size: 0x%lx\n", status);
+		goto error;
+	}
+
+	/* check attributes */
+	wkey_data = kzalloc(*datasize, GFP_KERNEL);
+	if (!wkey_data) {
+		wkey_data = ERR_PTR(-ENOMEM);
+		goto error;
+	}
+
+	status = efi.get_variable(EFI_S4_WAKE_KEY_NAME, &EFI_HIBERNATE_GUID,
+				&attr, datasize, wkey_data);
+	if (status) {
+		kfree(wkey_data);
+		*datasize = 0;
+		wkey_data = ERR_PTR(efi_status_to_err(status));
+		pr_err("PM: Get wake key data error: 0x%lx\n", status);
+		goto error;
+	}
+	if (attr & EFI_VARIABLE_NON_VOLATILE) {
+		memset(wkey_data, 0, *datasize);
+		kfree(wkey_data);
+		*datasize = 0;
+		wkey_data = ERR_PTR(-EBADMSG);
+		pr_err("PM: Wake key has wrong attributes: 0x%x\n", attr);
+		goto error;
+	}
+
+error:
+	return wkey_data;
+}
+
+bool wkey_data_available(void)
+{
+	static int ret = 1;
+	unsigned long datasize;
+	void *wkey_data;
+
+	if (ret > 0) {
+		wkey_data = load_wake_key_data(&datasize);
+		if (wkey_data && IS_ERR(wkey_data)) {
+			ret = PTR_ERR(wkey_data);
+			goto error;
+		} else {
+			if (wkey_data) {
+				memset(wkey_data, 0, datasize);
+				kfree(wkey_data);
+			}
+			ret = 0;
+		}
+	}
+
+error:
+	return !ret;
+}
+
+struct key *get_wake_key(void)
+{
+	const struct cred *cred = current_cred();
+	void *wkey_data;
+	unsigned long datasize = 0;
+	struct key *wkey;
+	int err;
+
+	wkey_data = load_wake_key_data(&datasize);
+	if (IS_ERR(wkey_data)) {
+		wkey = (struct key *)wkey_data;
+		goto error_data;
+	}
+
+	wkey = key_alloc(&key_type_asymmetric, "s4_wake_key",
+			GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
+			cred, 0, KEY_ALLOC_NOT_IN_QUOTA);
+	if (IS_ERR(wkey)) {
+		pr_err("PM: Allocate s4 wake key error: %ld\n", PTR_ERR(wkey));
+		goto error_keyalloc;
+	}
+	err = key_instantiate_and_link(wkey, wkey_data, datasize, NULL, NULL);
+	if (err < 0) {
+		pr_err("PM: S4 wake key instantiate error: %d\n", err);
+		if (wkey)
+			key_put(wkey);
+		wkey = ERR_PTR(err);
+	}
+
+error_keyalloc:
+	if (wkey_data && !IS_ERR(wkey_data))
+		kfree(wkey_data);
+error_data:
+	return wkey;
+}
+
+size_t get_key_length(const struct key *key)
+{
+	const struct public_key *pk = key->payload.data;
+	size_t len;
+
+	/* TODO: better check the RSA type */
+
+	len = mpi_get_nbits(pk->rsa.n);
+	len = (len + 7) / 8;
+
+	return len;
+}
+
+static int __init init_sign_key_data(void)
+{
+	skey_data = (void *)get_zeroed_page(GFP_KERNEL);
+	skey_data_buf = (void *)get_zeroed_page(GFP_KERNEL);
+
+	if (skey_data && efi_s4_key_available()) {
+		skey_dsize = efi_copy_skey_data(skey_data);
+		efi_erase_s4_skey_data();
+		pr_info("PM: Load s4 sign key from EFI\n");
+	}
+
+	return 0;
+}
+
+late_initcall(init_sign_key_data);
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 7d4b7ff..69a81d8 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -160,6 +160,33 @@ extern void swsusp_close(fmode_t);
 extern int swsusp_unmark(void);
 #endif
 
+/* kernel/power/hibernate_key.c */
+extern bool skey_data_available(void);
+extern struct key *get_sign_key(void);
+extern void erase_skey_data(void);
+extern void snapshot_fill_s4_skey(void);
+extern void destroy_sign_key(struct key *key);
+extern bool wkey_data_available(void);
+extern struct key *get_wake_key(void);
+extern size_t get_key_length(const struct key *key);
+
+#ifdef CONFIG_SNAPSHOT_VERIFICATION
+extern void restore_sign_key_data(void);
+extern bool swsusp_page_is_sign_key(struct page *page);
+extern unsigned long get_skey_data_buf_pfn(void);
+extern void clone_skey_data(void *page_addr);
+#else /* !CONFIG_SUSPEND */
+static inline void restore_sign_key_data(void) {}
+static inline bool swsusp_page_is_sign_key(struct page *page)
+{
+	return false;
+}
+static inline unsigned long get_skey_data_buf_pfn(void)
+{
+	return 0;
+}
+#endif /* !CONFIG_SNAPSHOT_VERIFICATION */
+
 /* kernel/power/block_io.c */
 extern struct block_device *hib_resume_bdev;
 
