Commit 00dcbda8 authored by Nicholas Piggin's avatar Nicholas Piggin Committed by Juerg Haefliger

UBUNTU: SAUCE: powerpc/64s: Add support for a store forwarding barrier at kernel entry/exit

CVE-2018-3639 (powerpc)
Signed-off-by: default avatarNicholas Piggin <npiggin@gmail.com>
[mauricio: backport to Ubuntu 16.04 linux_4.4.0-124.148]
Signed-off-by: default avatarMauricio Faria de Oliveira <mauricfo@linux.vnet.ibm.com>
Signed-off-by: default avatarJuerg Haefliger <juergh@canonical.com>
parent b3aaed30
......@@ -50,6 +50,27 @@
#define EX_PPR 88 /* SMT thread status register (priority) */
#define EX_CTR 96
#define STF_ENTRY_BARRIER_SLOT \
STF_ENTRY_BARRIER_FIXUP_SECTION; \
mflr r10; \
bl stf_barrier_fallback; \
mtlr r10
#define STF_EXIT_BARRIER_SLOT \
STF_EXIT_BARRIER_FIXUP_SECTION; \
nop; \
nop; \
nop; \
nop; \
nop; \
nop
/*
* r10 must be free to use, r13 must be paca
*/
#define INTERRUPT_TO_KERNEL \
STF_ENTRY_BARRIER_SLOT
/*
* Macros for annotating the expected destination of (h)rfid
*
......@@ -66,16 +87,19 @@
rfid
#define RFI_TO_USER \
STF_EXIT_BARRIER_SLOT; \
RFI_FLUSH_SLOT; \
rfid; \
b rfi_flush_fallback
#define RFI_TO_USER_OR_KERNEL \
STF_EXIT_BARRIER_SLOT; \
RFI_FLUSH_SLOT; \
rfid; \
b rfi_flush_fallback
#define RFI_TO_GUEST \
STF_EXIT_BARRIER_SLOT; \
RFI_FLUSH_SLOT; \
rfid; \
b rfi_flush_fallback
......@@ -84,21 +108,25 @@
hrfid
#define HRFI_TO_USER \
STF_EXIT_BARRIER_SLOT; \
RFI_FLUSH_SLOT; \
hrfid; \
b hrfi_flush_fallback
#define HRFI_TO_USER_OR_KERNEL \
STF_EXIT_BARRIER_SLOT; \
RFI_FLUSH_SLOT; \
hrfid; \
b hrfi_flush_fallback
#define HRFI_TO_GUEST \
STF_EXIT_BARRIER_SLOT; \
RFI_FLUSH_SLOT; \
hrfid; \
b hrfi_flush_fallback
#define HRFI_TO_UNKNOWN \
STF_EXIT_BARRIER_SLOT; \
RFI_FLUSH_SLOT; \
hrfid; \
b hrfi_flush_fallback
......@@ -226,6 +254,7 @@ END_FTR_SECTION_NESTED(ftr,ftr,943)
#define __EXCEPTION_PROLOG_1(area, extra, vec) \
OPT_SAVE_REG_TO_PACA(area+EX_PPR, r9, CPU_FTR_HAS_PPR); \
OPT_SAVE_REG_TO_PACA(area+EX_CFAR, r10, CPU_FTR_CFAR); \
INTERRUPT_TO_KERNEL; \
SAVE_CTR(r10, area); \
mfcr r9; \
extra(vec); \
......@@ -503,6 +532,13 @@ label##_relon_hv: \
#define SOFTEN_NOTEST_PR(vec) _SOFTEN_TEST(EXC_STD, vec)
#define SOFTEN_NOTEST_HV(vec) _SOFTEN_TEST(EXC_HV, vec)
#define __MASKABLE_EXCEPTION_PSERIES_OOL(vec, label, h, extra) \
__EXCEPTION_PROLOG_1(PACA_EXGEN, extra, vec); \
EXCEPTION_PROLOG_PSERIES_1(label##_common, h);
#define _MASKABLE_EXCEPTION_PSERIES_OOL(vec, label, h, extra) \
__MASKABLE_EXCEPTION_PSERIES_OOL(vec, label, h, extra)
#define __MASKABLE_EXCEPTION_PSERIES(vec, label, h, extra) \
SET_SCRATCH0(r13); /* save r13 */ \
EXCEPTION_PROLOG_0(PACA_EXGEN); \
......
......@@ -184,6 +184,22 @@ label##3: \
FTR_ENTRY_OFFSET label##1b-label##3b; \
.popsection;
#define STF_ENTRY_BARRIER_FIXUP_SECTION \
953: \
.pushsection __stf_entry_barrier_fixup,"a"; \
.align 2; \
954: \
FTR_ENTRY_OFFSET 953b-954b; \
.popsection;
#define STF_EXIT_BARRIER_FIXUP_SECTION \
955: \
.pushsection __stf_exit_barrier_fixup,"a"; \
.align 2; \
956: \
FTR_ENTRY_OFFSET 955b-956b; \
.popsection;
#define RFI_FLUSH_FIXUP_SECTION \
951: \
.pushsection __rfi_flush_fixup,"a"; \
......@@ -195,6 +211,9 @@ label##3: \
#ifndef __ASSEMBLY__
extern long stf_barrier_fallback;
extern long __start___stf_entry_barrier_fixup, __stop___stf_entry_barrier_fixup;
extern long __start___stf_exit_barrier_fixup, __stop___stf_exit_barrier_fixup;
extern long __start___rfi_flush_fixup, __stop___rfi_flush_fixup;
#endif
......
......@@ -25,9 +25,17 @@ void check_for_initrd(void);
void initmem_init(void);
#define ARCH_PANIC_TIMEOUT 180
void rfi_flush_enable(bool enable);
/* These are bit flags */
enum stf_barrier_type {
STF_BARRIER_NONE = 0x1,
STF_BARRIER_FALLBACK = 0x2,
STF_BARRIER_EIEIO = 0x4,
STF_BARRIER_SYNC_ORI = 0x8,
};
void setup_stf_barrier(void);
void do_stf_barrier_fixups(enum stf_barrier_type types);
enum l1d_flush_type {
L1D_FLUSH_NONE = 0x1,
L1D_FLUSH_FALLBACK = 0x2,
......
......@@ -292,9 +292,17 @@ hardware_interrupt_hv:
. = 0x900
.globl decrementer_pSeries
decrementer_pSeries:
_MASKABLE_EXCEPTION_PSERIES(0x900, decrementer, EXC_STD, SOFTEN_TEST_PR)
SET_SCRATCH0(r13)
EXCEPTION_PROLOG_0(PACA_EXGEN)
b decrementer_pSeries_OOL
STD_EXCEPTION_HV(0x980, 0x982, hdecrementer)
. = 0x980
.globl hv_hdecrementer_trampoline
hv_hdecrementer_trampoline:
HMT_MEDIUM
SET_SCRATCH0(r13)
EXCEPTION_PROLOG_0(PACA_EXGEN)
b hdecrementer_hv
MASKABLE_EXCEPTION_PSERIES(0xa00, 0xa00, doorbell_super)
KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xa00)
......@@ -321,10 +329,15 @@ system_call_pSeries:
std r10,PACA_EXGEN+EX_R10(r13)
OPT_SAVE_REG_TO_PACA(PACA_EXGEN+EX_PPR, r9, CPU_FTR_HAS_PPR);
mfcr r9
INTERRUPT_TO_KERNEL
KVMTEST(0xc00)
GET_SCRATCH0(r13)
#else
HMT_MEDIUM;
mr r12,r13
GET_PACA(r13)
INTERRUPT_TO_KERNEL
mr r13,r12
#endif
SYSCALL_PSERIES_1
SYSCALL_PSERIES_2_RFID
......@@ -788,7 +801,11 @@ kvmppc_skip_Hinterrupt:
STD_EXCEPTION_COMMON_ASYNC(0x500, hardware_interrupt, do_IRQ)
STD_EXCEPTION_COMMON_ASYNC(0x900, decrementer, timer_interrupt)
.globl decrementer_pSeries_OOL
decrementer_pSeries_OOL:
_MASKABLE_EXCEPTION_PSERIES_OOL(0x900, decrementer, EXC_STD, SOFTEN_TEST_PR)
STD_EXCEPTION_COMMON(0x980, hdecrementer, hdec_interrupt)
STD_EXCEPTION_HV_OOL(0x982, hdecrementer)
#ifdef CONFIG_PPC_DOORBELL
STD_EXCEPTION_COMMON_ASYNC(0xa00, doorbell_super, doorbell_exception)
#else
......@@ -900,6 +917,10 @@ hardware_interrupt_relon_hv:
.globl system_call_relon_pSeries
system_call_relon_pSeries:
HMT_MEDIUM
mr r12,r13
GET_PACA(r13)
INTERRUPT_TO_KERNEL
mr r13,r12
SYSCALL_PSERIES_1
SYSCALL_PSERIES_2_DIRECT
SYSCALL_PSERIES_3
......@@ -1566,6 +1587,20 @@ power4_fixup_nap:
blr
#endif
.globl stf_barrier_fallback
stf_barrier_fallback:
std r9,PACA_EXRFI+EX_R9(r13)
std r10,PACA_EXRFI+EX_R10(r13)
sync
ld r9,PACA_EXRFI+EX_R9(r13)
ld r10,PACA_EXRFI+EX_R10(r13)
ori 31,31,0
.rept 14
b 1f
1:
.endr
blr
.globl rfi_flush_fallback
rfi_flush_fallback:
SET_SCRATCH0(r13);
......
......@@ -70,6 +70,7 @@
#include <asm/kvm_ppc.h>
#include <asm/hugetlb.h>
#include <asm/epapr_hcalls.h>
#include <asm/security_features.h>
#ifdef DEBUG
#define DBG(fmt...) udbg_printf(fmt)
......@@ -835,11 +836,48 @@ early_initcall(disable_hardlockup_detector);
#endif
#ifdef CONFIG_PPC_BOOK3S_64
static enum l1d_flush_type enabled_flush_types;
static enum stf_barrier_type stf_enabled_flush_types;
static bool no_stf_barrier;
bool stf_barrier;
static enum l1d_flush_type rfi_enabled_flush_types;
static void *l1d_flush_fallback_area;
static bool no_rfi_flush;
bool rfi_flush;
static int __init handle_no_stf_barrier(char *p)
{
pr_info("stf-barrier: disabled on command line.");
no_stf_barrier = true;
return 0;
}
early_param("no_stf_barrier", handle_no_stf_barrier);
/* This is the generic flag used by other architectures */
static int __init handle_ssbd(char *p)
{
if (!p || strncmp(p, "off", 3) == 0) {
handle_no_stf_barrier(NULL);
return 0;
} else if (strncmp(p, "auto", 5) == 0 || strncmp(p, "on", 2) == 0 )
/* Until firmware tells us, we have the barrier with auto */
return 0;
else
return 1;
return 0;
}
early_param("spec_store_bypass_disable", handle_ssbd);
/* This is the generic flag used by other architectures */
static int __init handle_no_ssbd(char *p)
{
handle_no_stf_barrier(NULL);
return 0;
}
early_param("nospec_store_bypass_disable", handle_no_ssbd);
static int __init handle_no_rfi_flush(char *p)
{
pr_info("rfi-flush: disabled on command line.");
......@@ -868,10 +906,21 @@ static void do_nothing(void *unused)
*/
}
void rfi_flush_enable(bool enable)
static void stf_barrier_enable(bool enable)
{
if (enable) {
do_rfi_flush_fixups(enabled_flush_types);
do_stf_barrier_fixups(stf_enabled_flush_types);
on_each_cpu(do_nothing, NULL, 1);
} else
do_stf_barrier_fixups(STF_BARRIER_NONE);
stf_barrier = enable;
}
static void rfi_flush_enable(bool enable)
{
if (enable) {
do_rfi_flush_fixups(rfi_enabled_flush_types);
on_each_cpu(do_nothing, NULL, 1);
} else
do_rfi_flush_fixups(L1D_FLUSH_NONE);
......@@ -879,6 +928,39 @@ void rfi_flush_enable(bool enable)
rfi_flush = enable;
}
void setup_stf_barrier(void)
{
enum stf_barrier_type type;
bool enable, hv;
hv = cpu_has_feature(CPU_FTR_HVMODE);
/* Default to fallback in case fw-features are not available */
if (cpu_has_feature(CPU_FTR_ARCH_207S))
type = STF_BARRIER_SYNC_ORI;
else if (cpu_has_feature(CPU_FTR_ARCH_206))
type = STF_BARRIER_FALLBACK;
else
type = STF_BARRIER_NONE;
enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) &&
(security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR) ||
(security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && hv));
if (type == STF_BARRIER_FALLBACK) {
pr_info("stf-barrier: fallback barrier available\n");
} else if (type == STF_BARRIER_SYNC_ORI) {
pr_info("stf-barrier: hwsync barrier available\n");
} else if (type == STF_BARRIER_EIEIO) {
pr_info("stf-barrier: eieio barrier available\n");
}
stf_enabled_flush_types = type;
if (!no_stf_barrier)
stf_barrier_enable(enable);
}
static void init_fallback_flush(void)
{
u64 l1d_size, limit;
......@@ -929,13 +1011,39 @@ void setup_rfi_flush(enum l1d_flush_type types, bool enable)
if (types & L1D_FLUSH_MTTRIG)
pr_info("rfi-flush: mttrig type flush available\n");
enabled_flush_types = types;
rfi_enabled_flush_types = types;
if (!no_rfi_flush)
rfi_flush_enable(enable);
}
#ifdef CONFIG_DEBUG_FS
static int stf_barrier_set(void *data, u64 val)
{
bool enable;
if (val == 1)
enable = true;
else if (val == 0)
enable = false;
else
return -EINVAL;
/* Only do anything if we're changing state */
if (enable != stf_barrier)
stf_barrier_enable(enable);
return 0;
}
static int stf_barrier_get(void *data, u64 *val)
{
*val = stf_barrier ? 1 : 0;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(fops_stf_barrier, stf_barrier_get, stf_barrier_set, "%llu\n");
static int rfi_flush_set(void *data, u64 val)
{
bool enable;
......@@ -965,6 +1073,7 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_rfi_flush, rfi_flush_get, rfi_flush_set, "%llu\n");
static __init int rfi_flush_debugfs_init(void)
{
debugfs_create_file("rfi_flush", 0600, powerpc_debugfs_root, NULL, &fops_rfi_flush);
debugfs_create_file("stf_barrier", 0600, powerpc_debugfs_root, NULL, &fops_stf_barrier);
return 0;
}
device_initcall(rfi_flush_debugfs_init);
......
......@@ -73,6 +73,20 @@ SECTIONS
RODATA
#ifdef CONFIG_PPC64
. = ALIGN(8);
__stf_entry_barrier_fixup : AT(ADDR(__stf_entry_barrier_fixup) - LOAD_OFFSET) {
__start___stf_entry_barrier_fixup = .;
*(__stf_entry_barrier_fixup)
__stop___stf_entry_barrier_fixup = .;
}
. = ALIGN(8);
__stf_exit_barrier_fixup : AT(ADDR(__stf_exit_barrier_fixup) - LOAD_OFFSET) {
__start___stf_exit_barrier_fixup = .;
*(__stf_exit_barrier_fixup)
__stop___stf_exit_barrier_fixup = .;
}
. = ALIGN(8);
__rfi_flush_fixup : AT(ADDR(__rfi_flush_fixup) - LOAD_OFFSET) {
__start___rfi_flush_fixup = .;
......
......@@ -115,6 +115,120 @@ void do_feature_fixups(unsigned long value, void *fixup_start, void *fixup_end)
}
#ifdef CONFIG_PPC_BOOK3S_64
void do_stf_entry_barrier_fixups(enum stf_barrier_type types)
{
unsigned int instrs[3], *dest;
long *start, *end;
int i;
start = PTRRELOC(&__start___stf_entry_barrier_fixup),
end = PTRRELOC(&__stop___stf_entry_barrier_fixup);
instrs[0] = 0x60000000; /* nop */
instrs[1] = 0x60000000; /* nop */
instrs[2] = 0x60000000; /* nop */
i = 0;
if (types & STF_BARRIER_FALLBACK) {
instrs[i++] = 0x7d4802a6; /* mflr r10 */
instrs[i++] = 0x60000000; /* branch patched below */
instrs[i++] = 0x7d4803a6; /* mtlr r10 */
} else if (types & STF_BARRIER_EIEIO) {
instrs[i++] = 0x7e0006ac; /* eieio + bit 6 hint */
} else if (types & STF_BARRIER_SYNC_ORI) {
instrs[i++] = 0x7c0004ac; /* hwsync */
instrs[i++] = 0xe94d0000; /* ld r10,0(r13) */
instrs[i++] = 0x63ff0000; /* ori 31,31,0 speculation barrier */
}
for (i = 0; start < end; start++, i++) {
dest = (void *)start + *start;
pr_devel("patching dest %lx\n", (unsigned long)dest);
patch_instruction(dest, instrs[0]);
if (types & STF_BARRIER_FALLBACK)
patch_branch(dest + 1, (unsigned long)&stf_barrier_fallback,
BRANCH_SET_LINK);
else
patch_instruction(dest + 1, instrs[1]);
patch_instruction(dest + 2, instrs[2]);
}
printk(KERN_DEBUG "stf-barrier: patched %d entry locations (%s barrier)\n", i,
(types == STF_BARRIER_NONE) ? "no" :
(types == STF_BARRIER_FALLBACK) ? "fallback" :
(types == STF_BARRIER_EIEIO) ? "eieio" :
(types == (STF_BARRIER_SYNC_ORI)) ? "hwsync"
: "unknown");
}
void do_stf_exit_barrier_fixups(enum stf_barrier_type types)
{
unsigned int instrs[6], *dest;
long *start, *end;
int i;
start = PTRRELOC(&__start___stf_exit_barrier_fixup),
end = PTRRELOC(&__stop___stf_exit_barrier_fixup);
instrs[0] = 0x60000000; /* nop */
instrs[1] = 0x60000000; /* nop */
instrs[2] = 0x60000000; /* nop */
instrs[3] = 0x60000000; /* nop */
instrs[4] = 0x60000000; /* nop */
instrs[5] = 0x60000000; /* nop */
i = 0;
if (types & STF_BARRIER_FALLBACK || types & STF_BARRIER_SYNC_ORI) {
if (cpu_has_feature(CPU_FTR_HVMODE)) {
instrs[i++] = 0x7db14ba6; /* mtspr 0x131, r13 (HSPRG1) */
instrs[i++] = 0x7db04aa6; /* mfspr r13, 0x130 (HSPRG0) */
} else {
instrs[i++] = 0x7db243a6; /* mtsprg 2,r13 */
instrs[i++] = 0x7db142a6; /* mfsprg r13,1 */
}
instrs[i++] = 0x7c0004ac; /* hwsync */
instrs[i++] = 0xe9ad0000; /* ld r13,0(r13) */
instrs[i++] = 0x63ff0000; /* ori 31,31,0 speculation barrier */
if (cpu_has_feature(CPU_FTR_HVMODE)) {
instrs[i++] = 0x7db14aa6; /* mfspr r13, 0x131 (HSPRG1) */
} else {
instrs[i++] = 0x7db242a6; /* mfsprg r13,2 */
}
} else if (types & STF_BARRIER_EIEIO) {
instrs[i++] = 0x7e0006ac; /* eieio + bit 6 hint */
}
for (i = 0; start < end; start++, i++) {
dest = (void *)start + *start;
pr_devel("patching dest %lx\n", (unsigned long)dest);
patch_instruction(dest, instrs[0]);
patch_instruction(dest + 1, instrs[1]);
patch_instruction(dest + 2, instrs[2]);
patch_instruction(dest + 3, instrs[3]);
patch_instruction(dest + 4, instrs[4]);
patch_instruction(dest + 5, instrs[5]);
}
printk(KERN_DEBUG "stf-barrier: patched %d exit locations (%s barrier)\n", i,
(types == STF_BARRIER_NONE) ? "no" :
(types == STF_BARRIER_FALLBACK) ? "fallback" :
(types == STF_BARRIER_EIEIO) ? "eieio" :
(types == (STF_BARRIER_SYNC_ORI)) ? "hwsync"
: "unknown");
}
void do_stf_barrier_fixups(enum stf_barrier_type types)
{
do_stf_entry_barrier_fixups(types);
do_stf_exit_barrier_fixups(types);
}
void do_rfi_flush_fixups(enum l1d_flush_type types)
{
unsigned int instrs[3], *dest;
......
......@@ -129,6 +129,7 @@ static void __init pnv_setup_arch(void)
{
set_arch_panic_timeout(10, ARCH_PANIC_TIMEOUT);
setup_stf_barrier();
pnv_setup_rfi_flush();
/* Initialize SMP */
......
......@@ -593,6 +593,7 @@ static void __init pSeries_setup_arch(void)
fwnmi_init();
pseries_setup_rfi_flush();
setup_stf_barrier();
/* By default, only probe PCI (can be overridden by rtas_pci) */
pci_add_flags(PCI_PROBE_ONLY);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment