@@ -1394,10 +1394,12 @@ static void setup_hibernation_keys(struct boot_params *params)
{
unsigned long key_size;
unsigned long attributes;
+ unsigned long ignore;
struct setup_data *setup_data, *hibernation_setup_data;
struct hibernation_keys *keys;
+ bool regen_key = false;
unsigned long size = 0;
- efi_status_t status;
+ efi_status_t status, reg_status;
/* Allocate setup_data to carry keys */
size = sizeof(struct setup_data) + sizeof(struct hibernation_keys);
@@ -1427,12 +1429,16 @@ static void setup_hibernation_keys(struct boot_params *params)
goto clean_fail;
}
- if (status != EFI_SUCCESS) {
- efi_printk(sys_table, "Failed to get existing hibernation key\n");
+ reg_status = efi_call_early(get_variable, HIBERNATION_KEY_REGEN_FLAG,
+ &EFI_HIBERNATION_GUID, NULL, &ignore, ®en_key);
+ if ((status != EFI_SUCCESS) ||
+ (reg_status == EFI_SUCCESS && regen_key)) {
+ efi_printk(sys_table, "Regenerating hibernation key\n");
efi_get_random_key(sys_table, params, keys->hibernation_key,
HIBERNATION_DIGEST_SIZE);
+ /* Set new hibernation key to bootservice non-volatile variable */
status = efi_call_early(set_variable, HIBERNATION_KEY,
&EFI_HIBERNATION_GUID,
HIBERNATION_KEY_ATTRIBUTE,
@@ -1440,6 +1446,13 @@ static void setup_hibernation_keys(struct boot_params *params)
keys->hibernation_key);
if (status != EFI_SUCCESS)
efi_printk(sys_table, "Failed to set hibernation key\n");
+
+ efi_call_early(get_variable, HIBERNATION_KEY, &EFI_HIBERNATION_GUID,
+ NULL, &key_size, keys->hibernation_key);
+
+ /* Clean key regenerate flag */
+ efi_call_early(set_variable, HIBERNATION_KEY_REGEN_FLAG,
+ &EFI_HIBERNATION_GUID, 0, 0, NULL);
}
clean_fail:
@@ -165,6 +165,8 @@ static int __init init_hibernation_keys(void)
memblock_free(keys_phys_addr, sizeof(struct hibernation_keys));
keys_phys_addr = 0;
+ set_hibernation_key_regen_flag = false;
+
return ret;
}
@@ -19,3 +19,4 @@ obj-y += broadcom/
obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
obj-$(CONFIG_EFI) += efi/
obj-$(CONFIG_UEFI_CPER) += efi/
+obj-$(CONFIG_EFI_HIBERNATION_KEYS) += efi/
@@ -69,3 +69,7 @@ endmenu
config UEFI_CPER
bool
+
+config EFI_HIBERNATION_KEYS
+ bool
+ select EFI_VARS
@@ -5,6 +5,7 @@ obj-$(CONFIG_EFI) += efi.o vars.o reboot.o
obj-$(CONFIG_EFI_VARS) += efivars.o
obj-$(CONFIG_EFI_ESRT) += esrt.o
obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o
+obj-$(CONFIG_EFI_HIBERNATION_KEYS) += efi-hibernate_keys.o
obj-$(CONFIG_UEFI_CPER) += cper.o
obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o
obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o
new file mode 100644
@@ -0,0 +1,39 @@
+/* EFI variable handler of hibernation key regen flag
+ *
+ * Copyright (C) 2015 Lee, Chun-Yi <jlee@suse.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/efi.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+
+void create_hibernation_key_regen_flag(void)
+{
+ struct efivar_entry *entry = NULL;
+ int err = 0;
+
+ if (!set_hibernation_key_regen_flag)
+ return;
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return;
+
+ memcpy(entry->var.VariableName,
+ HIBERNATION_KEY_REGEN_FLAG, sizeof(HIBERNATION_KEY_REGEN_FLAG));
+ memcpy(&(entry->var.VendorGuid),
+ &EFI_HIBERNATION_GUID, sizeof(efi_guid_t));
+
+ err = efivar_entry_set(entry, HIBERNATION_KEY_SEED_ATTRIBUTE,
+ sizeof(bool), &set_hibernation_key_regen_flag, NULL);
+ if (err)
+ pr_warn("PM: Set flag of regenerating hibernation key failed: %d\n", err);
+
+ kfree(entry);
+}
+EXPORT_SYMBOL_GPL(create_hibernation_key_regen_flag);
@@ -330,6 +330,11 @@ struct platform_hibernation_ops {
#define EFI_HIBERNATION_GUID \
EFI_GUID(0xfe141863, 0xc070, 0x478e, 0xb8, 0xa3, 0x87, 0x8a, 0x5d, 0xc9, 0xef, 0x21)
+#define HIBERNATION_KEY_REGEN_FLAG \
+ ((efi_char16_t [20]) { 'H', 'I', 'B', 'E', 'R', 'N', 'A', 'T', 'I', 'O', 'N', 'K', 'e', 'y', 'R', 'e', 'g', 'e', 'n', 0 })
+#define HIBERNATION_KEY_SEED_ATTRIBUTE (EFI_VARIABLE_NON_VOLATILE | \
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | \
+ EFI_VARIABLE_RUNTIME_ACCESS)
/* HMAC Algorithm of Hibernate Signature */
#define HIBERNATION_HMAC "hmac(sha1)"
@@ -338,6 +343,16 @@ struct platform_hibernation_ops {
/* kernel/power/hibernate.c */
extern int sigenforce;
+/* kernel/power/user.c */
+extern bool set_hibernation_key_regen_flag;
+
+#ifdef CONFIG_HIBERNATE_VERIFICATION
+/* drivers/firmware/efi/efi-hibernate_keys.c */
+extern void create_hibernation_key_regen_flag(void);
+#else
+static inline void create_hibernation_key_regen_flag(void) {}
+#endif
+
/* kernel/power/snapshot.c */
extern void __register_nosave_region(unsigned long b, unsigned long e, int km);
static inline void __init register_nosave_region(unsigned long b, unsigned long e)
@@ -370,6 +385,7 @@ static inline void hibernation_set_ops(const struct platform_hibernation_ops *op
static inline int hibernate(void) { return -ENOSYS; }
static inline bool system_entering_hibernation(void) { return false; }
static inline bool hibernation_available(void) { return false; }
+static inline void create_hibernation_key_regen_flag(void) {}
#endif /* CONFIG_HIBERNATION */
/* Hibernation and suspend events */
@@ -28,6 +28,7 @@ struct resume_swap_area {
#define SNAPSHOT_PREF_IMAGE_SIZE _IO(SNAPSHOT_IOC_MAGIC, 18)
#define SNAPSHOT_AVAIL_SWAP_SIZE _IOR(SNAPSHOT_IOC_MAGIC, 19, __kernel_loff_t)
#define SNAPSHOT_ALLOC_SWAP_PAGE _IOR(SNAPSHOT_IOC_MAGIC, 20, __kernel_loff_t)
-#define SNAPSHOT_IOC_MAXNR 20
+#define SNAPSHOT_REGENERATE_KEY _IO(SNAPSHOT_IOC_MAGIC, 21)
+#define SNAPSHOT_IOC_MAXNR 21
#endif /* _LINUX_SUSPEND_IOCTLS_H */
@@ -71,6 +71,7 @@ config HIBERNATE_VERIFICATION
depends on HIBERNATION
depends on EFI_STUB
depends on X86
+ select EFI_HIBERNATION_KEYS
select CRYPTO_HMAC
select CRYPTO_SHA1
help
@@ -653,6 +653,8 @@ int hibernate(void)
{
int error;
+ set_hibernation_key_regen_flag = false;
+
if (!hibernation_available()) {
pr_debug("PM: Hibernation not available.\n");
return -EPERM;
@@ -32,6 +32,9 @@
#define SNAPSHOT_MINOR 231
+/* Handler will create HIBERNATIONKeyRegen EFI variable when flag raised */
+bool set_hibernation_key_regen_flag;
+
static struct snapshot_data {
struct snapshot_handle handle;
int swap;
@@ -338,6 +341,8 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
error = -EPERM;
break;
}
+ /* clean flag to avoid hibernation key regenerated */
+ set_hibernation_key_regen_flag = false;
/*
* Tasks are frozen and the notifiers have been called with
* PM_HIBERNATION_PREPARE
@@ -351,6 +356,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
break;
case SNAPSHOT_POWER_OFF:
+ set_hibernation_key_regen_flag = false;
if (data->platform_support)
error = hibernation_platform_enter();
break;
@@ -386,6 +392,10 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
}
break;
+ case SNAPSHOT_REGENERATE_KEY:
+ set_hibernation_key_regen_flag = !!arg;
+ break;
+
default:
error = -ENOTTY;
@@ -213,6 +213,7 @@ void migrate_to_reboot_cpu(void)
*/
void kernel_restart(char *cmd)
{
+ create_hibernation_key_regen_flag();
kernel_restart_prepare(cmd);
migrate_to_reboot_cpu();
syscore_shutdown();
@@ -240,6 +241,7 @@ static void kernel_shutdown_prepare(enum system_states state)
*/
void kernel_halt(void)
{
+ create_hibernation_key_regen_flag();
kernel_shutdown_prepare(SYSTEM_HALT);
migrate_to_reboot_cpu();
syscore_shutdown();
@@ -256,6 +258,7 @@ EXPORT_SYMBOL_GPL(kernel_halt);
*/
void kernel_power_off(void)
{
+ create_hibernation_key_regen_flag();
kernel_shutdown_prepare(SYSTEM_POWER_OFF);
if (pm_power_off_prepare)
pm_power_off_prepare();