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

Marcelo Henrique Cerri marcelo.cerri at canonical.com
Thu Jan 18 16:13:12 UTC 2018


From: Martin Schwidefsky <schwidefsky at de.ibm.com>

CVE-2017-5753
CVE-2017-5715

Signed-off-by: Martin Schwidefsky <schwidefsky at de.ibm.com>
Signed-off-by: Marcelo Henrique Cerri <marcelo.cerri at 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 --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 3a55f493c7da..1a24a3e96199 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -485,6 +485,23 @@ source kernel/Kconfig.preempt
 
 source kernel/Kconfig.hz
 
+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 b4f99d22ba04..f3e1313c263f 100644
--- a/arch/s390/include/asm/facility.h
+++ b/arch/s390/include/asm/facility.h
@@ -13,6 +13,24 @@
 
 #define MAX_FACILITY_BIT (256*8)	/* stfle_fac_list has 256 bytes */
 
+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 afe1cfebf1a4..2f8809671151 100644
--- a/arch/s390/include/asm/lowcore.h
+++ b/arch/s390/include/asm/lowcore.h
@@ -170,7 +170,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 vector register save area */
diff --git a/arch/s390/kernel/alternative.c b/arch/s390/kernel/alternative.c
index 2281f763a9ff..4e443fe369c9 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 = strtobool(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 f82bcab0b9c3..6a4ba353b8ad 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -279,6 +279,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 ad97f60b699d..d990ad021124 100644
--- a/arch/s390/kernel/entry.S
+++ b/arch/s390/kernel/entry.S
@@ -167,7 +167,7 @@ _PIF_WORK	= (_PIF_PER_TRAP)
 660:	.long	0xb2e8c000
 	.popsection
 661:	.long	0x47000000
-	.pushsection .altnobp, "a"
+	.pushsection .altinstructions, "a"
 	.long 661b - .
 	.long 660b - .
 	.word 82
@@ -181,7 +181,7 @@ _PIF_WORK	= (_PIF_PER_TRAP)
 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 136caf3128f0..e5627d4d910c 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -334,7 +334,9 @@ static void __init setup_lowcore(void)
 	lc->machine_flags = S390_lowcore.machine_flags;
 	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)
 		lc->vector_save_area_addr =
 			(unsigned long) &lc->vector_save_area;
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index 5c06014d3bb4..6de44b6bbb0d 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -250,7 +250,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 5b96276f7b10..bcaa816ade85 100644
--- a/arch/s390/kernel/vmlinux.lds.S
+++ b/arch/s390/kernel/vmlinux.lds.S
@@ -83,9 +83,6 @@ SECTIONS
 		__alt_instructions = .;
 		*(.altinstructions)
 		__alt_instructions_end = .;
-		__alt_nobp = .;
-		*(.altnobp)
-		__alt_nobp_end = .;
 	}
 
 	/*
-- 
2.7.4





More information about the kernel-team mailing list