diff mbox series

[artful,1/3] UBUNTU: SAUCE: s390: improve cpu alternative handling for gmb and nobp

Message ID 1516291975-11518-2-git-send-email-marcelo.cerri@canonical.com
State New
Headers show
Series Improve cpu alternative handling for gmb and nobp | expand

Commit Message

Marcelo Henrique Cerri Jan. 18, 2018, 4:12 p.m. UTC
From: Martin Schwidefsky <schwidefsky@de.ibm.com>

CVE-2017-5753
CVE-2017-5715

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Marcelo Henrique Cerri <marcelo.cerri@canonical.com>
---
 arch/s390/Kconfig                | 17 +++++++++++++++++
 arch/s390/include/asm/facility.h | 18 ++++++++++++++++++
 arch/s390/include/asm/lowcore.h  |  3 ++-
 arch/s390/kernel/alternative.c   | 32 +++++++++++++++++++++++++-------
 arch/s390/kernel/early.c         |  5 +++++
 arch/s390/kernel/entry.S         |  4 ++--
 arch/s390/kernel/setup.c         |  4 +++-
 arch/s390/kernel/smp.c           |  4 +++-
 arch/s390/kernel/vmlinux.lds.S   |  3 ---
 9 files changed, 75 insertions(+), 15 deletions(-)
diff mbox series

Patch

diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 3797b5e44c34..3d652609096a 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -519,6 +519,23 @@  config ARCH_RANDOM
 
 	  If unsure, say Y.
 
+config KERNEL_NOBP
+       def_bool n
+       prompt "Enable modified branch prediction for the kernel by default"
+       help
+         If this option is selected the kernel will switch to a modified
+	 branch prediction mode if the firmware interface is available.
+	 The modified branch prediction mode improves the behaviour in
+	 regard to speculative execution.
+
+	 With the option enabled the kernel parameter "nobp=0" or "nospec"
+	 can be used to run the kernel in the normal branch prediction mode.
+
+	 With the option disabled the modified branch prediction mode is
+	 enabled with the "nobp=1" kernel parameter.
+
+	 If unsure, say N.
+
 endmenu
 
 menu "Memory setup"
diff --git a/arch/s390/include/asm/facility.h b/arch/s390/include/asm/facility.h
index cb60d5c5755d..0645b550cf0c 100644
--- a/arch/s390/include/asm/facility.h
+++ b/arch/s390/include/asm/facility.h
@@ -14,6 +14,24 @@ 
 
 #define MAX_FACILITY_BIT (sizeof(((struct lowcore *)0)->stfle_fac_list) * 8)
 
+static inline void __set_facility(unsigned long nr, void *facilities)
+{
+	unsigned char *ptr = (unsigned char *) facilities;
+
+	if (nr >= MAX_FACILITY_BIT)
+		return;
+	ptr[nr >> 3] |= 0x80 >> (nr & 7);
+}
+
+static inline void __clear_facility(unsigned long nr, void *facilities)
+{
+	unsigned char *ptr = (unsigned char *) facilities;
+
+	if (nr >= MAX_FACILITY_BIT)
+		return;
+	ptr[nr >> 3] &= ~(0x80 >> (nr & 7));
+}
+
 static inline int __test_facility(unsigned long nr, void *facilities)
 {
 	unsigned char *ptr;
diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h
index 8a5b082797f8..bee2907d2019 100644
--- a/arch/s390/include/asm/lowcore.h
+++ b/arch/s390/include/asm/lowcore.h
@@ -154,7 +154,8 @@  struct lowcore {
 	__u8	pad_0x0e20[0x0f00-0x0e20];	/* 0x0e20 */
 
 	/* Extended facility list */
-	__u64	stfle_fac_list[32];		/* 0x0f00 */
+	__u64	stfle_fac_list[16];		/* 0x0f00 */
+	__u64	alt_stfle_fac_list[16];		/* 0x0f80 */
 	__u8	pad_0x1000[0x11b0-0x1000];	/* 0x1000 */
 
 	/* Pointer to the machine check extended save area */
diff --git a/arch/s390/kernel/alternative.c b/arch/s390/kernel/alternative.c
index 39a530f1f816..4e2ddfc7364d 100644
--- a/arch/s390/kernel/alternative.c
+++ b/arch/s390/kernel/alternative.c
@@ -14,18 +14,35 @@  static int __init disable_alternative_instructions(char *str)
 
 early_param("noaltinstr", disable_alternative_instructions);
 
-extern struct alt_instr __alt_nobp[], __alt_nobp_end[];
-static int __init nobp_setup(char *str)
+static int __init nobp_setup_early(char *str)
 {
 	bool enabled;
 	int rc;
 
 	rc = kstrtobool(str, &enabled);
-	if (!rc && enabled)
-		apply_alternatives(__alt_nobp, __alt_nobp_end);
-	return rc;
+	if (rc)
+		return rc;
+	if (enabled && test_facility(82))
+		__set_facility(82, S390_lowcore.alt_stfle_fac_list);
+	else
+		__clear_facility(82, S390_lowcore.alt_stfle_fac_list);
+	return 0;
+}
+early_param("nobp", nobp_setup_early);
+
+static int __init nospec_setup_early(char *str)
+{
+	__clear_facility(82, S390_lowcore.alt_stfle_fac_list);
+	return 0;
+}
+early_param("nospec", nospec_setup_early);
+
+static int __init nogmb_setup_early(char *str)
+{
+	__clear_facility(81, S390_lowcore.alt_stfle_fac_list);
+	return 0;
 }
-__setup("nobp=", nobp_setup);
+early_param("nogmb", nogmb_setup_early);
 
 struct brcl_insn {
 	u16 opc;
@@ -87,7 +104,8 @@  static void __init_or_module __apply_alternatives(struct alt_instr *start,
 		instr = (u8 *)&a->instr_offset + a->instr_offset;
 		replacement = (u8 *)&a->repl_offset + a->repl_offset;
 
-		if (!test_facility(a->facility))
+		if (!__test_facility(a->facility,
+				     S390_lowcore.alt_stfle_fac_list))
 			continue;
 
 		if (unlikely(a->instrlen % 2 || a->replacementlen % 2)) {
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index 5d20182ee8ae..4ade955b929d 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -327,6 +327,11 @@  static noinline __init void setup_facility_list(void)
 {
 	stfle(S390_lowcore.stfle_fac_list,
 	      ARRAY_SIZE(S390_lowcore.stfle_fac_list));
+	memcpy(S390_lowcore.alt_stfle_fac_list,
+	       S390_lowcore.stfle_fac_list,
+	       sizeof(S390_lowcore.alt_stfle_fac_list));
+	if (!IS_ENABLED(CONFIG_KERNEL_NOBP))
+		__clear_facility(82, S390_lowcore.alt_stfle_fac_list);
 }
 
 static __init void detect_diag9c(void)
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
index cdcdab92e22e..15f623904cbc 100644
--- a/arch/s390/kernel/entry.S
+++ b/arch/s390/kernel/entry.S
@@ -162,7 +162,7 @@  _PIF_WORK	= (_PIF_PER_TRAP | _PIF_SYSCALL_RESTART)
 660:	.long	0xb2e8c000
 	.popsection
 661:	.long	0x47000000
-	.pushsection .altnobp, "a"
+	.pushsection .altinstructions, "a"
 	.long 661b - .
 	.long 660b - .
 	.word 82
@@ -176,7 +176,7 @@  _PIF_WORK	= (_PIF_PER_TRAP | _PIF_SYSCALL_RESTART)
 662:	.long	0xb2e8d000
 	.popsection
 663:	.long	0x47000000
-	.pushsection .altnobp, "a"
+	.pushsection .altinstructions, "a"
 	.long 663b - .
 	.long 662b - .
 	.word 82
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index 5a1b299cfd81..99b003f7c488 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -339,7 +339,9 @@  static void __init setup_lowcore(void)
 	lc->preempt_count = S390_lowcore.preempt_count;
 	lc->stfl_fac_list = S390_lowcore.stfl_fac_list;
 	memcpy(lc->stfle_fac_list, S390_lowcore.stfle_fac_list,
-	       MAX_FACILITY_BIT/8);
+	       sizeof(lc->stfle_fac_list));
+	memcpy(lc->alt_stfle_fac_list, S390_lowcore.alt_stfle_fac_list,
+	       sizeof(lc->alt_stfle_fac_list));
 	if (MACHINE_HAS_VX || MACHINE_HAS_GS) {
 		unsigned long bits, size;
 
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index f19c90c7fec5..9dea18d93950 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -280,7 +280,9 @@  static void pcpu_prepare_secondary(struct pcpu *pcpu, int cpu)
 	__ctl_store(lc->cregs_save_area, 0, 15);
 	save_access_regs((unsigned int *) lc->access_regs_save_area);
 	memcpy(lc->stfle_fac_list, S390_lowcore.stfle_fac_list,
-	       MAX_FACILITY_BIT/8);
+	       sizeof(lc->stfle_fac_list));
+	memcpy(lc->alt_stfle_fac_list, S390_lowcore.alt_stfle_fac_list,
+	       sizeof(lc->alt_stfle_fac_list));
 }
 
 static void pcpu_attach_task(struct pcpu *pcpu, struct task_struct *tsk)
diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S
index c5248fb38782..7c9fcf7cb43d 100644
--- a/arch/s390/kernel/vmlinux.lds.S
+++ b/arch/s390/kernel/vmlinux.lds.S
@@ -115,9 +115,6 @@  SECTIONS
 		__alt_instructions = .;
 		*(.altinstructions)
 		__alt_instructions_end = .;
-		__alt_nobp = .;
-		*(.altnobp)
-		__alt_nobp_end = .;
 	}
 
 	/*