@@ -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;
@@ -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:
@@ -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 */
@@ -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;
@@ -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();