Skip to content

Commit

Permalink
powerpc/security: Fix link stack flush instruction
Browse files Browse the repository at this point in the history
The inline execution path for the hardware assisted branch flush
instruction failed to set CTR to the correct value before bcctr,
causing a crash when the feature is enabled.

Fixes: 4d24e21 ("powerpc/security: Allow for processors that flush the link stack using the special bcctr")
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20201007080605.64423-1-npiggin@gmail.com
  • Loading branch information
npiggin authored and mpe committed Oct 8, 2020
1 parent 09b791d commit 792254a
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 13 deletions.
4 changes: 3 additions & 1 deletion arch/powerpc/include/asm/asm-prototypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,9 @@ void _kvmppc_restore_tm_pr(struct kvm_vcpu *vcpu, u64 guest_msr);
void _kvmppc_save_tm_pr(struct kvm_vcpu *vcpu, u64 guest_msr);

/* Patch sites */
extern s32 patch__call_flush_branch_caches;
extern s32 patch__call_flush_branch_caches1;
extern s32 patch__call_flush_branch_caches2;
extern s32 patch__call_flush_branch_caches3;
extern s32 patch__flush_count_cache_return;
extern s32 patch__flush_link_stack_return;
extern s32 patch__call_kvm_flush_link_stack;
Expand Down
8 changes: 6 additions & 2 deletions arch/powerpc/kernel/entry_64.S
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,11 @@ _ASM_NOKPROBE_SYMBOL(save_nvgprs);

#define FLUSH_COUNT_CACHE \
1: nop; \
patch_site 1b, patch__call_flush_branch_caches
patch_site 1b, patch__call_flush_branch_caches1; \
1: nop; \
patch_site 1b, patch__call_flush_branch_caches2; \
1: nop; \
patch_site 1b, patch__call_flush_branch_caches3

.macro nops number
.rept \number
Expand Down Expand Up @@ -512,7 +516,7 @@ _GLOBAL(_switch)

kuap_check_amr r9, r10

FLUSH_COUNT_CACHE
FLUSH_COUNT_CACHE /* Clobbers r9, ctr */

/*
* On SMP kernels, care must be taken because a task may be
Expand Down
34 changes: 24 additions & 10 deletions arch/powerpc/kernel/security.c
Original file line number Diff line number Diff line change
Expand Up @@ -430,30 +430,44 @@ device_initcall(stf_barrier_debugfs_init);

static void update_branch_cache_flush(void)
{
u32 *site;

#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
site = &patch__call_kvm_flush_link_stack;
// This controls the branch from guest_exit_cont to kvm_flush_link_stack
if (link_stack_flush_type == BRANCH_CACHE_FLUSH_NONE) {
patch_instruction_site(&patch__call_kvm_flush_link_stack,
ppc_inst(PPC_INST_NOP));
patch_instruction_site(site, ppc_inst(PPC_INST_NOP));
} else {
// Could use HW flush, but that could also flush count cache
patch_branch_site(&patch__call_kvm_flush_link_stack,
(u64)&kvm_flush_link_stack, BRANCH_SET_LINK);
patch_branch_site(site, (u64)&kvm_flush_link_stack, BRANCH_SET_LINK);
}
#endif

// Patch out the bcctr first, then nop the rest
site = &patch__call_flush_branch_caches3;
patch_instruction_site(site, ppc_inst(PPC_INST_NOP));
site = &patch__call_flush_branch_caches2;
patch_instruction_site(site, ppc_inst(PPC_INST_NOP));
site = &patch__call_flush_branch_caches1;
patch_instruction_site(site, ppc_inst(PPC_INST_NOP));

// This controls the branch from _switch to flush_branch_caches
if (count_cache_flush_type == BRANCH_CACHE_FLUSH_NONE &&
link_stack_flush_type == BRANCH_CACHE_FLUSH_NONE) {
patch_instruction_site(&patch__call_flush_branch_caches,
ppc_inst(PPC_INST_NOP));
// Nothing to be done

} else if (count_cache_flush_type == BRANCH_CACHE_FLUSH_HW &&
link_stack_flush_type == BRANCH_CACHE_FLUSH_HW) {
patch_instruction_site(&patch__call_flush_branch_caches,
ppc_inst(PPC_INST_BCCTR_FLUSH));
// Patch in the bcctr last
site = &patch__call_flush_branch_caches1;
patch_instruction_site(site, ppc_inst(0x39207fff)); // li r9,0x7fff
site = &patch__call_flush_branch_caches2;
patch_instruction_site(site, ppc_inst(0x7d2903a6)); // mtctr r9
site = &patch__call_flush_branch_caches3;
patch_instruction_site(site, ppc_inst(PPC_INST_BCCTR_FLUSH));

} else {
patch_branch_site(&patch__call_flush_branch_caches,
(u64)&flush_branch_caches, BRANCH_SET_LINK);
patch_branch_site(site, (u64)&flush_branch_caches, BRANCH_SET_LINK);

// If we just need to flush the link stack, early return
if (count_cache_flush_type == BRANCH_CACHE_FLUSH_NONE) {
Expand Down

0 comments on commit 792254a

Please sign in to comment.