[PATCH v2 12/16] PM / hibernate: Forward signature verifying result and key to image kernel

Lee, Chun-Yi
2015-08-11T06:19:09Z

Due to the memory space of boot kernel will be overwritten after restoring
snapshot image and switching to image kernel. There have some informations
should be forwarded from boot kernel to image kernel: the result of
signature verifying, flag of enforce verifying signature and hibernation key.
That because those informations did not include in hibernate image or
produced when restoring.

The significant reason is image kernel needs hibernation key to sign image for
next hibernate cycle, otherwise the signature generating will be failed in
next cycle. In additional, it's also useful to expose the verification
result in dmesg after recovery.

The codes in hibernate key handler allocates an empty page as the buffer
of forward information that will be included in snapshot iamge, then keeps
page frame number in image header. When restoring snapshot image to memory
space of boot kernel, snapshot codes will asking key handler to fill forward
informations to buffer page. Then restoring hibernation key data to key page,
and cleaning this page buffer for next cycle.

Reviewed-by: Jiri Kosina <jkosina@suse.com>
Tested-by: Jiri Kosina <jkosina@suse.com>
Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
---
arch/x86/power/hibernate_keys.c | 63 +++++++++++++++++++++++++++++++++++++++++
kernel/power/hibernate.c | 1 +
kernel/power/power.h | 6 ++++
kernel/power/snapshot.c | 27 +++++++++++++++++-
kernel/power/user.c | 1 +
5 files changed, 97 insertions(+), 1 deletion(-)

diff --git a/arch/x86/power/hibernate_keys.c b/arch/x86/power/hibernate_keys.c
index f44823e..5e92101 100644
--- a/arch/x86/power/hibernate_keys.c
+++ b/arch/x86/power/hibernate_keys.c
@@ -19,6 +19,15 @@ static u64 keys_phys_addr;
/* A page used to keep hibernation keys */
static struct hibernation_keys *hibernation_keys;

+/* Forward information and keys from boot kernel to image kernel */
+struct forward_info {
+ bool sig_enforce;
+ int sig_verify_ret;
+ struct hibernation_keys hibernation_keys;
+};
+
+static struct forward_info *forward_buff;
+
void __init parse_hibernation_keys(u64 phys_addr, u32 data_len)
{
struct setup_data *hibernation_setup_data;
@@ -62,6 +71,55 @@ bool swsusp_page_is_keys(struct page *page)
return ret;
}

+unsigned long get_forward_buff_pfn(void)
+{
+ if (!forward_buff)
+ return 0;
+
+ return page_to_pfn(virt_to_page(forward_buff));
+}
+
+void fill_forward_info(void *forward_buff_page, int verify_ret)
+{
+ struct forward_info *info;
+
+ if (!forward_buff_page)
+ return;
+
+ memset(forward_buff_page, 0, PAGE_SIZE);
+ info = (struct forward_info *)forward_buff_page;
+ info->sig_verify_ret = verify_ret;
+
+ if (hibernation_keys && !hibernation_keys->hkey_status) {
+ info->hibernation_keys = *hibernation_keys;
+ memset(hibernation_keys, 0, PAGE_SIZE);
+ } else
+ pr_info("PM: Fill hibernation keys failed\n");
+
+ pr_info("PM: Filled sign information to forward buffer\n");
+}
+
+void restore_sig_forward_info(void)
+{
+ if (!forward_buff) {
+ pr_warn("PM: Did not allocate forward buffer\n");
+ return;
+ }
+
+ if (forward_buff->sig_verify_ret)
+ pr_warn("PM: Signature verifying failed: %d\n",
+ forward_buff->sig_verify_ret);
+
+ if (hibernation_keys) {
+ memset(hibernation_keys, 0, PAGE_SIZE);
+ *hibernation_keys = forward_buff->hibernation_keys;
+ pr_info("PM: Restored hibernation keys\n");
+ }
+
+ /* clean forward information buffer for next round */
+ memset(forward_buff, 0, PAGE_SIZE);
+}
+
static int __init init_hibernation_keys(void)
{
struct hibernation_keys *keys;
@@ -76,6 +134,11 @@ static int __init init_hibernation_keys(void)
hibernation_keys = (struct hibernation_keys *)get_zeroed_page(GFP_KERNEL);
if (hibernation_keys) {
*hibernation_keys = *keys;
+ forward_buff = (struct forward_info *)get_zeroed_page(GFP_KERNEL);
+ if (!forward_buff) {
+ pr_err("PM: Allocate forward buffer failed\n");
+ ret = -ENOMEM;
+ }
} else {
pr_err("PM: Allocate hibernation keys page failed\n");
ret = -ENOMEM;
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index 690f78f..640ca8a 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -702,6 +702,7 @@ int hibernate(void)
pm_restore_gfp_mask();
} else {
pr_debug("PM: Image restored successfully.\n");
+ restore_sig_forward_info();
}

Free_bitmaps:
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 6d1d406..1ea3513 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -13,6 +13,7 @@ struct swsusp_info {
unsigned long image_pages;
unsigned long pages;
unsigned long size;
+ unsigned long forward_buff_pfn;
u8 signature[HIBERNATION_DIGEST_SIZE];
} __aligned(PAGE_SIZE);

@@ -20,8 +21,13 @@ struct swsusp_info {
/* arch/x86/power/hibernate_keys.c */
extern int get_hibernation_key(u8 **hkey);
extern bool swsusp_page_is_keys(struct page *page);
+extern unsigned long get_forward_buff_pfn(void);
+extern void fill_forward_info(void *forward_buff_page, int verify_ret);
+extern void restore_sig_forward_info(void);
#else
static inline bool swsusp_page_is_keys(struct page *page) { return false; }
+static inline unsigned long get_forward_buff_pfn(void) { return 0; }
+static inline void restore_sig_forward_info(void) {}
#endif

/* kernel/power/snapshot.c */
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index 5522028..3629249 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -1281,6 +1281,14 @@ static unsigned int nr_copy_pages;
*/
static u8 signature[HIBERNATION_DIGEST_SIZE];

+/*
+ * Keep the pfn of forwarding information buffer from resume target.
+ * Writing hibernation keys to this buffer in snapshot image before restoring.
+ */
+unsigned long forward_buff_pfn;
+
+void *forward_buff;
+
/* Buffer point array for collecting address of page buffers */
void **h_buf;

@@ -1383,13 +1391,24 @@ error_key:
return ret;
}

+static void snapshot_fill_sig_forward_info(int verify_ret)
+{
+ if (!forward_buff_pfn || !forward_buff) {
+ pr_err("PM: Did not find forward buffer\n");
+ return;
+ }
+
+ /* Fill hibernation keys to snapshot in memory for next round */
+ fill_forward_info(forward_buff, verify_ret);
+}
+
int snapshot_image_verify(void)
{
struct crypto_shash *tfm;
struct shash_desc *desc;
u8 *key, *digest;
size_t digest_size, desc_size;
- int ret, i;
+ int i, ret = 0;

if (!h_buf)
return 0;
@@ -1450,6 +1469,7 @@ error_digest:
forward_ret:
if (ret)
pr_warn("PM: Signature verifying failed: %d\n", ret);
+ snapshot_fill_sig_forward_info(ret);
return ret;
}

@@ -2126,6 +2146,7 @@ static int init_header(struct swsusp_info *info)
info->pages = snapshot_get_image_size();
info->size = info->pages;
info->size <<= PAGE_SHIFT;
+ info->forward_buff_pfn = get_forward_buff_pfn();
memcpy(info->signature, signature, HIBERNATION_DIGEST_SIZE);
return init_header_complete(info);
}
@@ -2289,6 +2310,7 @@ load_header(struct swsusp_info *info)
if (!error) {
nr_copy_pages = info->image_pages;
nr_meta_pages = info->pages - info->image_pages - 1;
+ forward_buff_pfn = info->forward_buff_pfn;
memset(signature, 0, HIBERNATION_DIGEST_SIZE);
memcpy(signature, info->signature, HIBERNATION_DIGEST_SIZE);
}
@@ -2757,6 +2779,9 @@ int snapshot_write_next(struct snapshot_handle *handle)
handle->sync_read = 0;
if (h_buf)
*(h_buf + (handle->cur - nr_meta_pages - 1)) = handle->buffer;
+ /* Keep the buffer of hibernation keys in snapshot */
+ if (forward_buff_pfn && pfn == forward_buff_pfn)
+ forward_buff = handle->buffer;
}
handle->cur++;
return PAGE_SIZE;
diff --git a/kernel/power/user.c b/kernel/power/user.c
index 9b891d5..ffd327a 100644
--- a/kernel/power/user.c
+++ b/kernel/power/user.c
@@ -241,6 +241,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
if (!data->frozen || data->ready)
break;
pm_restore_gfp_mask();
+ restore_sig_forward_info();
free_basic_memory_bitmaps();
data->free_bitmaps = false;
thaw_processes();
--
2.1.4