Blob Blame History Raw
From e795d31f0f0a228c881924a7ce9878431e3559ab Mon Sep 17 00:00:00 2001
From: "Lee, Chun-Yi" <jlee@suse.com>
Date: Tue, 7 Jul 2015 10:20:54 +0800
Subject: [PATCH v2 04/16] x86/efi: Generating random number in EFI stub

Patch-mainline: Not yet, reviewing on linux-efi
References: fate#316350

This patch adds the codes for generating random number array as the
HMAC key that will used by later EFI stub codes.

The original codes in efi_random copied from aslr and add the codes
to accept input entropy and EFI debugging. In later patch will add
the codes to get random number by EFI protocol. The separate codes
can avoid impacting aslr function.

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/boot/compressed/Makefile     |    1 
 arch/x86/boot/compressed/eboot.h      |    5 ++
 arch/x86/boot/compressed/efi_random.c |   80 ++++++++++++++++++++++++++++++++++
 arch/x86/boot/compressed/misc.c       |    4 -
 arch/x86/boot/compressed/misc.h       |    2 
 5 files changed, 89 insertions(+), 3 deletions(-)
 create mode 100644 arch/x86/boot/compressed/efi_random.c

--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -61,6 +61,7 @@ vmlinux-objs-$(CONFIG_RANDOMIZE_BASE) +=
 
 $(obj)/eboot.o: KBUILD_CFLAGS += -fshort-wchar -mno-red-zone
 
+vmlinux-objs-$(CONFIG_HIBERNATE_VERIFICATION) += $(obj)/efi_random.o
 vmlinux-objs-$(CONFIG_EFI_STUB) += $(obj)/eboot.o $(obj)/efi_stub_$(BITS).o \
 	$(objtree)/drivers/firmware/efi/libstub/lib.a
 vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o
--- a/arch/x86/boot/compressed/eboot.h
+++ b/arch/x86/boot/compressed/eboot.h
@@ -103,4 +103,9 @@ struct efi_uga_draw_protocol {
 	void *blt;
 };
 
+#if CONFIG_HIBERNATE_VERIFICATION
+extern void efi_get_random_key(efi_system_table_t *sys_table,
+		struct boot_params *params, u8 key[], unsigned int size);
+#endif
+
 #endif /* BOOT_COMPRESSED_EBOOT_H */
--- /dev/null
+++ b/arch/x86/boot/compressed/efi_random.c
@@ -0,0 +1,80 @@
+#include "misc.h"
+
+#include <linux/efi.h>
+#include <asm/archrandom.h>
+
+#define EDX_TSC		(1 << 4)
+#define ECX_RDRAND	(1 << 30)
+
+static unsigned int cpuid_0x1_ecx, cpuid_0x1_edx;
+
+static void cpuid_ecx_edx(void)
+{
+	unsigned int eax, ebx;
+
+	cpuid(0x1, &eax, &ebx, &cpuid_0x1_ecx, &cpuid_0x1_edx);
+}
+
+static unsigned long get_random_long(unsigned long entropy,
+				     struct boot_params *boot_params,
+				     efi_system_table_t *sys_table)
+{
+#ifdef CONFIG_X86_64
+	const unsigned long mix_const = 0x5d6008cbf3848dd3UL;
+#else
+	const unsigned long mix_const = 0x3f39e593UL;
+#endif
+	unsigned long raw, random;
+	bool use_i8254 = true;
+
+	if (entropy)
+		random = entropy;
+	else
+		random = get_random_boot(boot_params);
+
+	if (cpuid_0x1_ecx & ECX_RDRAND) {
+		if (rdrand_long(&raw)) {
+			random ^= raw;
+			use_i8254 = false;
+		}
+	}
+
+	if (cpuid_0x1_edx & EDX_TSC) {
+		rdtscll(raw);
+
+		random ^= raw;
+		use_i8254 = false;
+	}
+
+	if (use_i8254)
+		random ^= read_i8254();
+
+	/* Circular multiply for better bit diffusion */
+	asm("mul %3"
+	    : "=a" (random), "=d" (raw)
+	    : "a" (random), "rm" (mix_const));
+	random += raw;
+
+	return random;
+}
+
+void efi_get_random_key(efi_system_table_t *sys_table,
+			struct boot_params *params, u8 key[], unsigned int size)
+{
+	unsigned long entropy = 0;
+	unsigned int bfill = size;
+
+	if (key == NULL || !size)
+		return;
+
+	cpuid_ecx_edx();
+
+	memset(key, 0, size);
+	while (bfill > 0) {
+		unsigned int copy_len = 0;
+		entropy = get_random_long(entropy, params, sys_table);
+		copy_len = (bfill < sizeof(entropy)) ? bfill : sizeof(entropy);
+		memcpy((void *)(key + size - bfill), &entropy, copy_len);
+		bfill -= copy_len;
+	}
+}
--- a/arch/x86/boot/compressed/misc.c
+++ b/arch/x86/boot/compressed/misc.c
@@ -464,7 +464,7 @@ asmlinkage __visible void *decompress_ke
 	return output;
 }
 
-#if CONFIG_RANDOMIZE_BASE
+#if CONFIG_HIBERNATE_VERIFICATION || CONFIG_RANDOMIZE_BASE
 #define I8254_PORT_CONTROL     0x43
 #define I8254_PORT_COUNTER0    0x40
 #define I8254_CMD_READBACK     0xC0
@@ -513,4 +513,4 @@ unsigned long get_random_boot(struct boo
 
 	return hash;
 }
-#endif /* CONFIG_RANDOMIZE_BASE */
+#endif /* CONFIG_HIBERNATE_VERIFICATION || CONFIG_RANDOMIZE_BASE */
--- a/arch/x86/boot/compressed/misc.h
+++ b/arch/x86/boot/compressed/misc.h
@@ -96,7 +96,7 @@ static inline void console_init(void)
 { }
 #endif
 
-#if CONFIG_RANDOMIZE_BASE
+#if CONFIG_HIBERNATE_VERIFICATION || CONFIG_RANDOMIZE_BASE
 extern u16 read_i8254(void);
 extern unsigned long get_random_boot(struct boot_params *boot_params);
 #endif