162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2001 Ben. Herrenschmidt (benh@kernel.crashing.org) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Modifications for ppc64: 662306a36Sopenharmony_ci * Copyright (C) 2003 Dave Engebretsen <engebret@us.ibm.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright 2008 Michael Ellerman, IBM Corporation. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/jump_label.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/string.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/sched/mm.h> 1762306a36Sopenharmony_ci#include <linux/stop_machine.h> 1862306a36Sopenharmony_ci#include <asm/cputable.h> 1962306a36Sopenharmony_ci#include <asm/code-patching.h> 2062306a36Sopenharmony_ci#include <asm/interrupt.h> 2162306a36Sopenharmony_ci#include <asm/page.h> 2262306a36Sopenharmony_ci#include <asm/sections.h> 2362306a36Sopenharmony_ci#include <asm/setup.h> 2462306a36Sopenharmony_ci#include <asm/security_features.h> 2562306a36Sopenharmony_ci#include <asm/firmware.h> 2662306a36Sopenharmony_ci#include <asm/inst.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct fixup_entry { 2962306a36Sopenharmony_ci unsigned long mask; 3062306a36Sopenharmony_ci unsigned long value; 3162306a36Sopenharmony_ci long start_off; 3262306a36Sopenharmony_ci long end_off; 3362306a36Sopenharmony_ci long alt_start_off; 3462306a36Sopenharmony_ci long alt_end_off; 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic u32 *calc_addr(struct fixup_entry *fcur, long offset) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci /* 4062306a36Sopenharmony_ci * We store the offset to the code as a negative offset from 4162306a36Sopenharmony_ci * the start of the alt_entry, to support the VDSO. This 4262306a36Sopenharmony_ci * routine converts that back into an actual address. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci return (u32 *)((unsigned long)fcur + offset); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int patch_alt_instruction(u32 *src, u32 *dest, u32 *alt_start, u32 *alt_end) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci int err; 5062306a36Sopenharmony_ci ppc_inst_t instr; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci instr = ppc_inst_read(src); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (instr_is_relative_branch(ppc_inst_read(src))) { 5562306a36Sopenharmony_ci u32 *target = (u32 *)branch_target(src); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* Branch within the section doesn't need translating */ 5862306a36Sopenharmony_ci if (target < alt_start || target > alt_end) { 5962306a36Sopenharmony_ci err = translate_branch(&instr, dest, src); 6062306a36Sopenharmony_ci if (err) 6162306a36Sopenharmony_ci return 1; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci raw_patch_instruction(dest, instr); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic int patch_feature_section_mask(unsigned long value, unsigned long mask, 7162306a36Sopenharmony_ci struct fixup_entry *fcur) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci u32 *start, *end, *alt_start, *alt_end, *src, *dest; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci start = calc_addr(fcur, fcur->start_off); 7662306a36Sopenharmony_ci end = calc_addr(fcur, fcur->end_off); 7762306a36Sopenharmony_ci alt_start = calc_addr(fcur, fcur->alt_start_off); 7862306a36Sopenharmony_ci alt_end = calc_addr(fcur, fcur->alt_end_off); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if ((alt_end - alt_start) > (end - start)) 8162306a36Sopenharmony_ci return 1; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if ((value & fcur->mask & mask) == (fcur->value & mask)) 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci src = alt_start; 8762306a36Sopenharmony_ci dest = start; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci for (; src < alt_end; src = ppc_inst_next(src, src), 9062306a36Sopenharmony_ci dest = ppc_inst_next(dest, dest)) { 9162306a36Sopenharmony_ci if (patch_alt_instruction(src, dest, alt_start, alt_end)) 9262306a36Sopenharmony_ci return 1; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci for (; dest < end; dest++) 9662306a36Sopenharmony_ci raw_patch_instruction(dest, ppc_inst(PPC_RAW_NOP())); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic void do_feature_fixups_mask(unsigned long value, unsigned long mask, 10262306a36Sopenharmony_ci void *fixup_start, void *fixup_end) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct fixup_entry *fcur, *fend; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci fcur = fixup_start; 10762306a36Sopenharmony_ci fend = fixup_end; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci for (; fcur < fend; fcur++) { 11062306a36Sopenharmony_ci if (patch_feature_section_mask(value, mask, fcur)) { 11162306a36Sopenharmony_ci WARN_ON(1); 11262306a36Sopenharmony_ci printk("Unable to patch feature section at %p - %p" \ 11362306a36Sopenharmony_ci " with %p - %p\n", 11462306a36Sopenharmony_ci calc_addr(fcur, fcur->start_off), 11562306a36Sopenharmony_ci calc_addr(fcur, fcur->end_off), 11662306a36Sopenharmony_ci calc_addr(fcur, fcur->alt_start_off), 11762306a36Sopenharmony_ci calc_addr(fcur, fcur->alt_end_off)); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_civoid do_feature_fixups(unsigned long value, void *fixup_start, void *fixup_end) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci do_feature_fixups_mask(value, ~0, fixup_start, fixup_end); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#ifdef CONFIG_PPC_BARRIER_NOSPEC 12862306a36Sopenharmony_cistatic bool is_fixup_addr_valid(void *dest, size_t size) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci return system_state < SYSTEM_FREEING_INITMEM || 13162306a36Sopenharmony_ci !init_section_contains(dest, size); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic int do_patch_fixups(long *start, long *end, unsigned int *instrs, int num) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci int i; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci for (i = 0; start < end; start++, i++) { 13962306a36Sopenharmony_ci int j; 14062306a36Sopenharmony_ci unsigned int *dest = (void *)start + *start; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (!is_fixup_addr_valid(dest, sizeof(*instrs) * num)) 14362306a36Sopenharmony_ci continue; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci pr_devel("patching dest %lx\n", (unsigned long)dest); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci for (j = 0; j < num; j++) 14862306a36Sopenharmony_ci patch_instruction(dest + j, ppc_inst(instrs[j])); 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci return i; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci#endif 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S_64 15562306a36Sopenharmony_cistatic int do_patch_entry_fixups(long *start, long *end, unsigned int *instrs, 15662306a36Sopenharmony_ci bool do_fallback, void *fallback) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci int i; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci for (i = 0; start < end; start++, i++) { 16162306a36Sopenharmony_ci unsigned int *dest = (void *)start + *start; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (!is_fixup_addr_valid(dest, sizeof(*instrs) * 3)) 16462306a36Sopenharmony_ci continue; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci pr_devel("patching dest %lx\n", (unsigned long)dest); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci // See comment in do_entry_flush_fixups() RE order of patching 16962306a36Sopenharmony_ci if (do_fallback) { 17062306a36Sopenharmony_ci patch_instruction(dest, ppc_inst(instrs[0])); 17162306a36Sopenharmony_ci patch_instruction(dest + 2, ppc_inst(instrs[2])); 17262306a36Sopenharmony_ci patch_branch(dest + 1, (unsigned long)fallback, BRANCH_SET_LINK); 17362306a36Sopenharmony_ci } else { 17462306a36Sopenharmony_ci patch_instruction(dest + 1, ppc_inst(instrs[1])); 17562306a36Sopenharmony_ci patch_instruction(dest + 2, ppc_inst(instrs[2])); 17662306a36Sopenharmony_ci patch_instruction(dest, ppc_inst(instrs[0])); 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci return i; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic void do_stf_entry_barrier_fixups(enum stf_barrier_type types) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci unsigned int instrs[3]; 18562306a36Sopenharmony_ci long *start, *end; 18662306a36Sopenharmony_ci int i; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci start = PTRRELOC(&__start___stf_entry_barrier_fixup); 18962306a36Sopenharmony_ci end = PTRRELOC(&__stop___stf_entry_barrier_fixup); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci instrs[0] = PPC_RAW_NOP(); 19262306a36Sopenharmony_ci instrs[1] = PPC_RAW_NOP(); 19362306a36Sopenharmony_ci instrs[2] = PPC_RAW_NOP(); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci i = 0; 19662306a36Sopenharmony_ci if (types & STF_BARRIER_FALLBACK) { 19762306a36Sopenharmony_ci instrs[i++] = PPC_RAW_MFLR(_R10); 19862306a36Sopenharmony_ci instrs[i++] = PPC_RAW_NOP(); /* branch patched below */ 19962306a36Sopenharmony_ci instrs[i++] = PPC_RAW_MTLR(_R10); 20062306a36Sopenharmony_ci } else if (types & STF_BARRIER_EIEIO) { 20162306a36Sopenharmony_ci instrs[i++] = PPC_RAW_EIEIO() | 0x02000000; /* eieio + bit 6 hint */ 20262306a36Sopenharmony_ci } else if (types & STF_BARRIER_SYNC_ORI) { 20362306a36Sopenharmony_ci instrs[i++] = PPC_RAW_SYNC(); 20462306a36Sopenharmony_ci instrs[i++] = PPC_RAW_LD(_R10, _R13, 0); 20562306a36Sopenharmony_ci instrs[i++] = PPC_RAW_ORI(_R31, _R31, 0); /* speculation barrier */ 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci i = do_patch_entry_fixups(start, end, instrs, types & STF_BARRIER_FALLBACK, 20962306a36Sopenharmony_ci &stf_barrier_fallback); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci printk(KERN_DEBUG "stf-barrier: patched %d entry locations (%s barrier)\n", i, 21262306a36Sopenharmony_ci (types == STF_BARRIER_NONE) ? "no" : 21362306a36Sopenharmony_ci (types == STF_BARRIER_FALLBACK) ? "fallback" : 21462306a36Sopenharmony_ci (types == STF_BARRIER_EIEIO) ? "eieio" : 21562306a36Sopenharmony_ci (types == (STF_BARRIER_SYNC_ORI)) ? "hwsync" 21662306a36Sopenharmony_ci : "unknown"); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void do_stf_exit_barrier_fixups(enum stf_barrier_type types) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci unsigned int instrs[6]; 22262306a36Sopenharmony_ci long *start, *end; 22362306a36Sopenharmony_ci int i; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci start = PTRRELOC(&__start___stf_exit_barrier_fixup); 22662306a36Sopenharmony_ci end = PTRRELOC(&__stop___stf_exit_barrier_fixup); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci instrs[0] = PPC_RAW_NOP(); 22962306a36Sopenharmony_ci instrs[1] = PPC_RAW_NOP(); 23062306a36Sopenharmony_ci instrs[2] = PPC_RAW_NOP(); 23162306a36Sopenharmony_ci instrs[3] = PPC_RAW_NOP(); 23262306a36Sopenharmony_ci instrs[4] = PPC_RAW_NOP(); 23362306a36Sopenharmony_ci instrs[5] = PPC_RAW_NOP(); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci i = 0; 23662306a36Sopenharmony_ci if (types & STF_BARRIER_FALLBACK || types & STF_BARRIER_SYNC_ORI) { 23762306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_HVMODE)) { 23862306a36Sopenharmony_ci instrs[i++] = PPC_RAW_MTSPR(SPRN_HSPRG1, _R13); 23962306a36Sopenharmony_ci instrs[i++] = PPC_RAW_MFSPR(_R13, SPRN_HSPRG0); 24062306a36Sopenharmony_ci } else { 24162306a36Sopenharmony_ci instrs[i++] = PPC_RAW_MTSPR(SPRN_SPRG2, _R13); 24262306a36Sopenharmony_ci instrs[i++] = PPC_RAW_MFSPR(_R13, SPRN_SPRG1); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci instrs[i++] = PPC_RAW_SYNC(); 24562306a36Sopenharmony_ci instrs[i++] = PPC_RAW_LD(_R13, _R13, 0); 24662306a36Sopenharmony_ci instrs[i++] = PPC_RAW_ORI(_R31, _R31, 0); /* speculation barrier */ 24762306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_HVMODE)) 24862306a36Sopenharmony_ci instrs[i++] = PPC_RAW_MFSPR(_R13, SPRN_HSPRG1); 24962306a36Sopenharmony_ci else 25062306a36Sopenharmony_ci instrs[i++] = PPC_RAW_MFSPR(_R13, SPRN_SPRG2); 25162306a36Sopenharmony_ci } else if (types & STF_BARRIER_EIEIO) { 25262306a36Sopenharmony_ci instrs[i++] = PPC_RAW_EIEIO() | 0x02000000; /* eieio + bit 6 hint */ 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci i = do_patch_fixups(start, end, instrs, ARRAY_SIZE(instrs)); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci printk(KERN_DEBUG "stf-barrier: patched %d exit locations (%s barrier)\n", i, 25862306a36Sopenharmony_ci (types == STF_BARRIER_NONE) ? "no" : 25962306a36Sopenharmony_ci (types == STF_BARRIER_FALLBACK) ? "fallback" : 26062306a36Sopenharmony_ci (types == STF_BARRIER_EIEIO) ? "eieio" : 26162306a36Sopenharmony_ci (types == (STF_BARRIER_SYNC_ORI)) ? "hwsync" 26262306a36Sopenharmony_ci : "unknown"); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic bool stf_exit_reentrant = false; 26662306a36Sopenharmony_cistatic bool rfi_exit_reentrant = false; 26762306a36Sopenharmony_cistatic DEFINE_MUTEX(exit_flush_lock); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int __do_stf_barrier_fixups(void *data) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci enum stf_barrier_type *types = data; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci do_stf_entry_barrier_fixups(*types); 27462306a36Sopenharmony_ci do_stf_exit_barrier_fixups(*types); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return 0; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_civoid do_stf_barrier_fixups(enum stf_barrier_type types) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci /* 28262306a36Sopenharmony_ci * The call to the fallback entry flush, and the fallback/sync-ori exit 28362306a36Sopenharmony_ci * flush can not be safely patched in/out while other CPUs are 28462306a36Sopenharmony_ci * executing them. So call __do_stf_barrier_fixups() on one CPU while 28562306a36Sopenharmony_ci * all other CPUs spin in the stop machine core with interrupts hard 28662306a36Sopenharmony_ci * disabled. 28762306a36Sopenharmony_ci * 28862306a36Sopenharmony_ci * The branch to mark interrupt exits non-reentrant is enabled first, 28962306a36Sopenharmony_ci * then stop_machine runs which will ensure all CPUs are out of the 29062306a36Sopenharmony_ci * low level interrupt exit code before patching. After the patching, 29162306a36Sopenharmony_ci * if allowed, then flip the branch to allow fast exits. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci // Prevent static key update races with do_rfi_flush_fixups() 29562306a36Sopenharmony_ci mutex_lock(&exit_flush_lock); 29662306a36Sopenharmony_ci static_branch_enable(&interrupt_exit_not_reentrant); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci stop_machine(__do_stf_barrier_fixups, &types, NULL); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if ((types & STF_BARRIER_FALLBACK) || (types & STF_BARRIER_SYNC_ORI)) 30162306a36Sopenharmony_ci stf_exit_reentrant = false; 30262306a36Sopenharmony_ci else 30362306a36Sopenharmony_ci stf_exit_reentrant = true; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (stf_exit_reentrant && rfi_exit_reentrant) 30662306a36Sopenharmony_ci static_branch_disable(&interrupt_exit_not_reentrant); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci mutex_unlock(&exit_flush_lock); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_civoid do_uaccess_flush_fixups(enum l1d_flush_type types) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci unsigned int instrs[4]; 31462306a36Sopenharmony_ci long *start, *end; 31562306a36Sopenharmony_ci int i; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci start = PTRRELOC(&__start___uaccess_flush_fixup); 31862306a36Sopenharmony_ci end = PTRRELOC(&__stop___uaccess_flush_fixup); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci instrs[0] = PPC_RAW_NOP(); 32162306a36Sopenharmony_ci instrs[1] = PPC_RAW_NOP(); 32262306a36Sopenharmony_ci instrs[2] = PPC_RAW_NOP(); 32362306a36Sopenharmony_ci instrs[3] = PPC_RAW_BLR(); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci i = 0; 32662306a36Sopenharmony_ci if (types == L1D_FLUSH_FALLBACK) { 32762306a36Sopenharmony_ci instrs[3] = PPC_RAW_NOP(); 32862306a36Sopenharmony_ci /* fallthrough to fallback flush */ 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (types & L1D_FLUSH_ORI) { 33262306a36Sopenharmony_ci instrs[i++] = PPC_RAW_ORI(_R31, _R31, 0); /* speculation barrier */ 33362306a36Sopenharmony_ci instrs[i++] = PPC_RAW_ORI(_R30, _R30, 0); /* L1d flush */ 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (types & L1D_FLUSH_MTTRIG) 33762306a36Sopenharmony_ci instrs[i++] = PPC_RAW_MTSPR(SPRN_TRIG2, _R0); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci i = do_patch_fixups(start, end, instrs, ARRAY_SIZE(instrs)); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci printk(KERN_DEBUG "uaccess-flush: patched %d locations (%s flush)\n", i, 34262306a36Sopenharmony_ci (types == L1D_FLUSH_NONE) ? "no" : 34362306a36Sopenharmony_ci (types == L1D_FLUSH_FALLBACK) ? "fallback displacement" : 34462306a36Sopenharmony_ci (types & L1D_FLUSH_ORI) ? (types & L1D_FLUSH_MTTRIG) 34562306a36Sopenharmony_ci ? "ori+mttrig type" 34662306a36Sopenharmony_ci : "ori type" : 34762306a36Sopenharmony_ci (types & L1D_FLUSH_MTTRIG) ? "mttrig type" 34862306a36Sopenharmony_ci : "unknown"); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic int __do_entry_flush_fixups(void *data) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci enum l1d_flush_type types = *(enum l1d_flush_type *)data; 35462306a36Sopenharmony_ci unsigned int instrs[3]; 35562306a36Sopenharmony_ci long *start, *end; 35662306a36Sopenharmony_ci int i; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci instrs[0] = PPC_RAW_NOP(); 35962306a36Sopenharmony_ci instrs[1] = PPC_RAW_NOP(); 36062306a36Sopenharmony_ci instrs[2] = PPC_RAW_NOP(); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci i = 0; 36362306a36Sopenharmony_ci if (types == L1D_FLUSH_FALLBACK) { 36462306a36Sopenharmony_ci instrs[i++] = PPC_RAW_MFLR(_R10); 36562306a36Sopenharmony_ci instrs[i++] = PPC_RAW_NOP(); /* branch patched below */ 36662306a36Sopenharmony_ci instrs[i++] = PPC_RAW_MTLR(_R10); 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (types & L1D_FLUSH_ORI) { 37062306a36Sopenharmony_ci instrs[i++] = PPC_RAW_ORI(_R31, _R31, 0); /* speculation barrier */ 37162306a36Sopenharmony_ci instrs[i++] = PPC_RAW_ORI(_R30, _R30, 0); /* L1d flush */ 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (types & L1D_FLUSH_MTTRIG) 37562306a36Sopenharmony_ci instrs[i++] = PPC_RAW_MTSPR(SPRN_TRIG2, _R0); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* 37862306a36Sopenharmony_ci * If we're patching in or out the fallback flush we need to be careful about the 37962306a36Sopenharmony_ci * order in which we patch instructions. That's because it's possible we could 38062306a36Sopenharmony_ci * take a page fault after patching one instruction, so the sequence of 38162306a36Sopenharmony_ci * instructions must be safe even in a half patched state. 38262306a36Sopenharmony_ci * 38362306a36Sopenharmony_ci * To make that work, when patching in the fallback flush we patch in this order: 38462306a36Sopenharmony_ci * - the mflr (dest) 38562306a36Sopenharmony_ci * - the mtlr (dest + 2) 38662306a36Sopenharmony_ci * - the branch (dest + 1) 38762306a36Sopenharmony_ci * 38862306a36Sopenharmony_ci * That ensures the sequence is safe to execute at any point. In contrast if we 38962306a36Sopenharmony_ci * patch the mtlr last, it's possible we could return from the branch and not 39062306a36Sopenharmony_ci * restore LR, leading to a crash later. 39162306a36Sopenharmony_ci * 39262306a36Sopenharmony_ci * When patching out the fallback flush (either with nops or another flush type), 39362306a36Sopenharmony_ci * we patch in this order: 39462306a36Sopenharmony_ci * - the branch (dest + 1) 39562306a36Sopenharmony_ci * - the mtlr (dest + 2) 39662306a36Sopenharmony_ci * - the mflr (dest) 39762306a36Sopenharmony_ci * 39862306a36Sopenharmony_ci * Note we are protected by stop_machine() from other CPUs executing the code in a 39962306a36Sopenharmony_ci * semi-patched state. 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci start = PTRRELOC(&__start___entry_flush_fixup); 40362306a36Sopenharmony_ci end = PTRRELOC(&__stop___entry_flush_fixup); 40462306a36Sopenharmony_ci i = do_patch_entry_fixups(start, end, instrs, types == L1D_FLUSH_FALLBACK, 40562306a36Sopenharmony_ci &entry_flush_fallback); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci start = PTRRELOC(&__start___scv_entry_flush_fixup); 40862306a36Sopenharmony_ci end = PTRRELOC(&__stop___scv_entry_flush_fixup); 40962306a36Sopenharmony_ci i += do_patch_entry_fixups(start, end, instrs, types == L1D_FLUSH_FALLBACK, 41062306a36Sopenharmony_ci &scv_entry_flush_fallback); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci printk(KERN_DEBUG "entry-flush: patched %d locations (%s flush)\n", i, 41362306a36Sopenharmony_ci (types == L1D_FLUSH_NONE) ? "no" : 41462306a36Sopenharmony_ci (types == L1D_FLUSH_FALLBACK) ? "fallback displacement" : 41562306a36Sopenharmony_ci (types & L1D_FLUSH_ORI) ? (types & L1D_FLUSH_MTTRIG) 41662306a36Sopenharmony_ci ? "ori+mttrig type" 41762306a36Sopenharmony_ci : "ori type" : 41862306a36Sopenharmony_ci (types & L1D_FLUSH_MTTRIG) ? "mttrig type" 41962306a36Sopenharmony_ci : "unknown"); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return 0; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_civoid do_entry_flush_fixups(enum l1d_flush_type types) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci /* 42762306a36Sopenharmony_ci * The call to the fallback flush can not be safely patched in/out while 42862306a36Sopenharmony_ci * other CPUs are executing it. So call __do_entry_flush_fixups() on one 42962306a36Sopenharmony_ci * CPU while all other CPUs spin in the stop machine core with interrupts 43062306a36Sopenharmony_ci * hard disabled. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_ci stop_machine(__do_entry_flush_fixups, &types, NULL); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int __do_rfi_flush_fixups(void *data) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci enum l1d_flush_type types = *(enum l1d_flush_type *)data; 43862306a36Sopenharmony_ci unsigned int instrs[3]; 43962306a36Sopenharmony_ci long *start, *end; 44062306a36Sopenharmony_ci int i; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci start = PTRRELOC(&__start___rfi_flush_fixup); 44362306a36Sopenharmony_ci end = PTRRELOC(&__stop___rfi_flush_fixup); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci instrs[0] = PPC_RAW_NOP(); 44662306a36Sopenharmony_ci instrs[1] = PPC_RAW_NOP(); 44762306a36Sopenharmony_ci instrs[2] = PPC_RAW_NOP(); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (types & L1D_FLUSH_FALLBACK) 45062306a36Sopenharmony_ci /* b .+16 to fallback flush */ 45162306a36Sopenharmony_ci instrs[0] = PPC_RAW_BRANCH(16); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci i = 0; 45462306a36Sopenharmony_ci if (types & L1D_FLUSH_ORI) { 45562306a36Sopenharmony_ci instrs[i++] = PPC_RAW_ORI(_R31, _R31, 0); /* speculation barrier */ 45662306a36Sopenharmony_ci instrs[i++] = PPC_RAW_ORI(_R30, _R30, 0); /* L1d flush */ 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (types & L1D_FLUSH_MTTRIG) 46062306a36Sopenharmony_ci instrs[i++] = PPC_RAW_MTSPR(SPRN_TRIG2, _R0); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci i = do_patch_fixups(start, end, instrs, ARRAY_SIZE(instrs)); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci printk(KERN_DEBUG "rfi-flush: patched %d locations (%s flush)\n", i, 46562306a36Sopenharmony_ci (types == L1D_FLUSH_NONE) ? "no" : 46662306a36Sopenharmony_ci (types == L1D_FLUSH_FALLBACK) ? "fallback displacement" : 46762306a36Sopenharmony_ci (types & L1D_FLUSH_ORI) ? (types & L1D_FLUSH_MTTRIG) 46862306a36Sopenharmony_ci ? "ori+mttrig type" 46962306a36Sopenharmony_ci : "ori type" : 47062306a36Sopenharmony_ci (types & L1D_FLUSH_MTTRIG) ? "mttrig type" 47162306a36Sopenharmony_ci : "unknown"); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_civoid do_rfi_flush_fixups(enum l1d_flush_type types) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci /* 47962306a36Sopenharmony_ci * stop_machine gets all CPUs out of the interrupt exit handler same 48062306a36Sopenharmony_ci * as do_stf_barrier_fixups. do_rfi_flush_fixups patching can run 48162306a36Sopenharmony_ci * without stop_machine, so this could be achieved with a broadcast 48262306a36Sopenharmony_ci * IPI instead, but this matches the stf sequence. 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci // Prevent static key update races with do_stf_barrier_fixups() 48662306a36Sopenharmony_ci mutex_lock(&exit_flush_lock); 48762306a36Sopenharmony_ci static_branch_enable(&interrupt_exit_not_reentrant); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci stop_machine(__do_rfi_flush_fixups, &types, NULL); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (types & L1D_FLUSH_FALLBACK) 49262306a36Sopenharmony_ci rfi_exit_reentrant = false; 49362306a36Sopenharmony_ci else 49462306a36Sopenharmony_ci rfi_exit_reentrant = true; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (stf_exit_reentrant && rfi_exit_reentrant) 49762306a36Sopenharmony_ci static_branch_disable(&interrupt_exit_not_reentrant); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci mutex_unlock(&exit_flush_lock); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_civoid do_barrier_nospec_fixups_range(bool enable, void *fixup_start, void *fixup_end) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci unsigned int instr; 50562306a36Sopenharmony_ci long *start, *end; 50662306a36Sopenharmony_ci int i; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci start = fixup_start; 50962306a36Sopenharmony_ci end = fixup_end; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci instr = PPC_RAW_NOP(); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (enable) { 51462306a36Sopenharmony_ci pr_info("barrier-nospec: using ORI speculation barrier\n"); 51562306a36Sopenharmony_ci instr = PPC_RAW_ORI(_R31, _R31, 0); /* speculation barrier */ 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci i = do_patch_fixups(start, end, &instr, 1); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci printk(KERN_DEBUG "barrier-nospec: patched %d locations\n", i); 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci#endif /* CONFIG_PPC_BOOK3S_64 */ 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci#ifdef CONFIG_PPC_BARRIER_NOSPEC 52662306a36Sopenharmony_civoid do_barrier_nospec_fixups(bool enable) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci void *start, *end; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci start = PTRRELOC(&__start___barrier_nospec_fixup); 53162306a36Sopenharmony_ci end = PTRRELOC(&__stop___barrier_nospec_fixup); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci do_barrier_nospec_fixups_range(enable, start, end); 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci#endif /* CONFIG_PPC_BARRIER_NOSPEC */ 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci#ifdef CONFIG_PPC_E500 53862306a36Sopenharmony_civoid do_barrier_nospec_fixups_range(bool enable, void *fixup_start, void *fixup_end) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci unsigned int instr[2]; 54162306a36Sopenharmony_ci long *start, *end; 54262306a36Sopenharmony_ci int i; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci start = fixup_start; 54562306a36Sopenharmony_ci end = fixup_end; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci instr[0] = PPC_RAW_NOP(); 54862306a36Sopenharmony_ci instr[1] = PPC_RAW_NOP(); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (enable) { 55162306a36Sopenharmony_ci pr_info("barrier-nospec: using isync; sync as speculation barrier\n"); 55262306a36Sopenharmony_ci instr[0] = PPC_RAW_ISYNC(); 55362306a36Sopenharmony_ci instr[1] = PPC_RAW_SYNC(); 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci i = do_patch_fixups(start, end, instr, ARRAY_SIZE(instr)); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci printk(KERN_DEBUG "barrier-nospec: patched %d locations\n", i); 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic void __init patch_btb_flush_section(long *curr) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci unsigned int *start, *end; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci start = (void *)curr + *curr; 56662306a36Sopenharmony_ci end = (void *)curr + *(curr + 1); 56762306a36Sopenharmony_ci for (; start < end; start++) { 56862306a36Sopenharmony_ci pr_devel("patching dest %lx\n", (unsigned long)start); 56962306a36Sopenharmony_ci patch_instruction(start, ppc_inst(PPC_RAW_NOP())); 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_civoid __init do_btb_flush_fixups(void) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci long *start, *end; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci start = PTRRELOC(&__start__btb_flush_fixup); 57862306a36Sopenharmony_ci end = PTRRELOC(&__stop__btb_flush_fixup); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci for (; start < end; start += 2) 58162306a36Sopenharmony_ci patch_btb_flush_section(start); 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci#endif /* CONFIG_PPC_E500 */ 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_civoid do_lwsync_fixups(unsigned long value, void *fixup_start, void *fixup_end) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci long *start, *end; 58862306a36Sopenharmony_ci u32 *dest; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (!(value & CPU_FTR_LWSYNC)) 59162306a36Sopenharmony_ci return ; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci start = fixup_start; 59462306a36Sopenharmony_ci end = fixup_end; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci for (; start < end; start++) { 59762306a36Sopenharmony_ci dest = (void *)start + *start; 59862306a36Sopenharmony_ci raw_patch_instruction(dest, ppc_inst(PPC_INST_LWSYNC)); 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic void __init do_final_fixups(void) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci#if defined(CONFIG_PPC64) && defined(CONFIG_RELOCATABLE) 60562306a36Sopenharmony_ci ppc_inst_t inst; 60662306a36Sopenharmony_ci u32 *src, *dest, *end; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (PHYSICAL_START == 0) 60962306a36Sopenharmony_ci return; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci src = (u32 *)(KERNELBASE + PHYSICAL_START); 61262306a36Sopenharmony_ci dest = (u32 *)KERNELBASE; 61362306a36Sopenharmony_ci end = (void *)src + (__end_interrupts - _stext); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci while (src < end) { 61662306a36Sopenharmony_ci inst = ppc_inst_read(src); 61762306a36Sopenharmony_ci raw_patch_instruction(dest, inst); 61862306a36Sopenharmony_ci src = ppc_inst_next(src, src); 61962306a36Sopenharmony_ci dest = ppc_inst_next(dest, dest); 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci#endif 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic unsigned long __initdata saved_cpu_features; 62562306a36Sopenharmony_cistatic unsigned int __initdata saved_mmu_features; 62662306a36Sopenharmony_ci#ifdef CONFIG_PPC64 62762306a36Sopenharmony_cistatic unsigned long __initdata saved_firmware_features; 62862306a36Sopenharmony_ci#endif 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_civoid __init apply_feature_fixups(void) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct cpu_spec *spec = PTRRELOC(*PTRRELOC(&cur_cpu_spec)); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci *PTRRELOC(&saved_cpu_features) = spec->cpu_features; 63562306a36Sopenharmony_ci *PTRRELOC(&saved_mmu_features) = spec->mmu_features; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* 63862306a36Sopenharmony_ci * Apply the CPU-specific and firmware specific fixups to kernel text 63962306a36Sopenharmony_ci * (nop out sections not relevant to this CPU or this firmware). 64062306a36Sopenharmony_ci */ 64162306a36Sopenharmony_ci do_feature_fixups(spec->cpu_features, 64262306a36Sopenharmony_ci PTRRELOC(&__start___ftr_fixup), 64362306a36Sopenharmony_ci PTRRELOC(&__stop___ftr_fixup)); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci do_feature_fixups(spec->mmu_features, 64662306a36Sopenharmony_ci PTRRELOC(&__start___mmu_ftr_fixup), 64762306a36Sopenharmony_ci PTRRELOC(&__stop___mmu_ftr_fixup)); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci do_lwsync_fixups(spec->cpu_features, 65062306a36Sopenharmony_ci PTRRELOC(&__start___lwsync_fixup), 65162306a36Sopenharmony_ci PTRRELOC(&__stop___lwsync_fixup)); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci#ifdef CONFIG_PPC64 65462306a36Sopenharmony_ci saved_firmware_features = powerpc_firmware_features; 65562306a36Sopenharmony_ci do_feature_fixups(powerpc_firmware_features, 65662306a36Sopenharmony_ci &__start___fw_ftr_fixup, &__stop___fw_ftr_fixup); 65762306a36Sopenharmony_ci#endif 65862306a36Sopenharmony_ci do_final_fixups(); 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_civoid __init update_mmu_feature_fixups(unsigned long mask) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci saved_mmu_features &= ~mask; 66462306a36Sopenharmony_ci saved_mmu_features |= cur_cpu_spec->mmu_features & mask; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci do_feature_fixups_mask(cur_cpu_spec->mmu_features, mask, 66762306a36Sopenharmony_ci PTRRELOC(&__start___mmu_ftr_fixup), 66862306a36Sopenharmony_ci PTRRELOC(&__stop___mmu_ftr_fixup)); 66962306a36Sopenharmony_ci mmu_feature_keys_init(); 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_civoid __init setup_feature_keys(void) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci /* 67562306a36Sopenharmony_ci * Initialise jump label. This causes all the cpu/mmu_has_feature() 67662306a36Sopenharmony_ci * checks to take on their correct polarity based on the current set of 67762306a36Sopenharmony_ci * CPU/MMU features. 67862306a36Sopenharmony_ci */ 67962306a36Sopenharmony_ci jump_label_init(); 68062306a36Sopenharmony_ci cpu_feature_keys_init(); 68162306a36Sopenharmony_ci mmu_feature_keys_init(); 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic int __init check_features(void) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci WARN(saved_cpu_features != cur_cpu_spec->cpu_features, 68762306a36Sopenharmony_ci "CPU features changed after feature patching!\n"); 68862306a36Sopenharmony_ci WARN(saved_mmu_features != cur_cpu_spec->mmu_features, 68962306a36Sopenharmony_ci "MMU features changed after feature patching!\n"); 69062306a36Sopenharmony_ci#ifdef CONFIG_PPC64 69162306a36Sopenharmony_ci WARN(saved_firmware_features != powerpc_firmware_features, 69262306a36Sopenharmony_ci "Firmware features changed after feature patching!\n"); 69362306a36Sopenharmony_ci#endif 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci return 0; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_cilate_initcall(check_features); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci#ifdef CONFIG_FTR_FIXUP_SELFTEST 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci#define check(x) \ 70262306a36Sopenharmony_ci if (!(x)) printk("feature-fixups: test failed at line %d\n", __LINE__); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic int patch_feature_section(unsigned long value, struct fixup_entry *fcur) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci return patch_feature_section_mask(value, ~0, fcur); 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci/* This must be after the text it fixes up, vmlinux.lds.S enforces that atm */ 71062306a36Sopenharmony_cistatic struct fixup_entry fixup; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic long __init calc_offset(struct fixup_entry *entry, unsigned int *p) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci return (unsigned long)p - (unsigned long)entry; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic void __init test_basic_patching(void) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci extern unsigned int ftr_fixup_test1[]; 72062306a36Sopenharmony_ci extern unsigned int end_ftr_fixup_test1[]; 72162306a36Sopenharmony_ci extern unsigned int ftr_fixup_test1_orig[]; 72262306a36Sopenharmony_ci extern unsigned int ftr_fixup_test1_expected[]; 72362306a36Sopenharmony_ci int size = 4 * (end_ftr_fixup_test1 - ftr_fixup_test1); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci fixup.value = fixup.mask = 8; 72662306a36Sopenharmony_ci fixup.start_off = calc_offset(&fixup, ftr_fixup_test1 + 1); 72762306a36Sopenharmony_ci fixup.end_off = calc_offset(&fixup, ftr_fixup_test1 + 2); 72862306a36Sopenharmony_ci fixup.alt_start_off = fixup.alt_end_off = 0; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* Sanity check */ 73162306a36Sopenharmony_ci check(memcmp(ftr_fixup_test1, ftr_fixup_test1_orig, size) == 0); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* Check we don't patch if the value matches */ 73462306a36Sopenharmony_ci patch_feature_section(8, &fixup); 73562306a36Sopenharmony_ci check(memcmp(ftr_fixup_test1, ftr_fixup_test1_orig, size) == 0); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci /* Check we do patch if the value doesn't match */ 73862306a36Sopenharmony_ci patch_feature_section(0, &fixup); 73962306a36Sopenharmony_ci check(memcmp(ftr_fixup_test1, ftr_fixup_test1_expected, size) == 0); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* Check we do patch if the mask doesn't match */ 74262306a36Sopenharmony_ci memcpy(ftr_fixup_test1, ftr_fixup_test1_orig, size); 74362306a36Sopenharmony_ci check(memcmp(ftr_fixup_test1, ftr_fixup_test1_orig, size) == 0); 74462306a36Sopenharmony_ci patch_feature_section(~8, &fixup); 74562306a36Sopenharmony_ci check(memcmp(ftr_fixup_test1, ftr_fixup_test1_expected, size) == 0); 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic void __init test_alternative_patching(void) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci extern unsigned int ftr_fixup_test2[]; 75162306a36Sopenharmony_ci extern unsigned int end_ftr_fixup_test2[]; 75262306a36Sopenharmony_ci extern unsigned int ftr_fixup_test2_orig[]; 75362306a36Sopenharmony_ci extern unsigned int ftr_fixup_test2_alt[]; 75462306a36Sopenharmony_ci extern unsigned int ftr_fixup_test2_expected[]; 75562306a36Sopenharmony_ci int size = 4 * (end_ftr_fixup_test2 - ftr_fixup_test2); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci fixup.value = fixup.mask = 0xF; 75862306a36Sopenharmony_ci fixup.start_off = calc_offset(&fixup, ftr_fixup_test2 + 1); 75962306a36Sopenharmony_ci fixup.end_off = calc_offset(&fixup, ftr_fixup_test2 + 2); 76062306a36Sopenharmony_ci fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_test2_alt); 76162306a36Sopenharmony_ci fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_test2_alt + 1); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* Sanity check */ 76462306a36Sopenharmony_ci check(memcmp(ftr_fixup_test2, ftr_fixup_test2_orig, size) == 0); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci /* Check we don't patch if the value matches */ 76762306a36Sopenharmony_ci patch_feature_section(0xF, &fixup); 76862306a36Sopenharmony_ci check(memcmp(ftr_fixup_test2, ftr_fixup_test2_orig, size) == 0); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* Check we do patch if the value doesn't match */ 77162306a36Sopenharmony_ci patch_feature_section(0, &fixup); 77262306a36Sopenharmony_ci check(memcmp(ftr_fixup_test2, ftr_fixup_test2_expected, size) == 0); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci /* Check we do patch if the mask doesn't match */ 77562306a36Sopenharmony_ci memcpy(ftr_fixup_test2, ftr_fixup_test2_orig, size); 77662306a36Sopenharmony_ci check(memcmp(ftr_fixup_test2, ftr_fixup_test2_orig, size) == 0); 77762306a36Sopenharmony_ci patch_feature_section(~0xF, &fixup); 77862306a36Sopenharmony_ci check(memcmp(ftr_fixup_test2, ftr_fixup_test2_expected, size) == 0); 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic void __init test_alternative_case_too_big(void) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci extern unsigned int ftr_fixup_test3[]; 78462306a36Sopenharmony_ci extern unsigned int end_ftr_fixup_test3[]; 78562306a36Sopenharmony_ci extern unsigned int ftr_fixup_test3_orig[]; 78662306a36Sopenharmony_ci extern unsigned int ftr_fixup_test3_alt[]; 78762306a36Sopenharmony_ci int size = 4 * (end_ftr_fixup_test3 - ftr_fixup_test3); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci fixup.value = fixup.mask = 0xC; 79062306a36Sopenharmony_ci fixup.start_off = calc_offset(&fixup, ftr_fixup_test3 + 1); 79162306a36Sopenharmony_ci fixup.end_off = calc_offset(&fixup, ftr_fixup_test3 + 2); 79262306a36Sopenharmony_ci fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_test3_alt); 79362306a36Sopenharmony_ci fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_test3_alt + 2); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci /* Sanity check */ 79662306a36Sopenharmony_ci check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci /* Expect nothing to be patched, and the error returned to us */ 79962306a36Sopenharmony_ci check(patch_feature_section(0xF, &fixup) == 1); 80062306a36Sopenharmony_ci check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0); 80162306a36Sopenharmony_ci check(patch_feature_section(0, &fixup) == 1); 80262306a36Sopenharmony_ci check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0); 80362306a36Sopenharmony_ci check(patch_feature_section(~0xF, &fixup) == 1); 80462306a36Sopenharmony_ci check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0); 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistatic void __init test_alternative_case_too_small(void) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci extern unsigned int ftr_fixup_test4[]; 81062306a36Sopenharmony_ci extern unsigned int end_ftr_fixup_test4[]; 81162306a36Sopenharmony_ci extern unsigned int ftr_fixup_test4_orig[]; 81262306a36Sopenharmony_ci extern unsigned int ftr_fixup_test4_alt[]; 81362306a36Sopenharmony_ci extern unsigned int ftr_fixup_test4_expected[]; 81462306a36Sopenharmony_ci int size = 4 * (end_ftr_fixup_test4 - ftr_fixup_test4); 81562306a36Sopenharmony_ci unsigned long flag; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci /* Check a high-bit flag */ 81862306a36Sopenharmony_ci flag = 1UL << ((sizeof(unsigned long) - 1) * 8); 81962306a36Sopenharmony_ci fixup.value = fixup.mask = flag; 82062306a36Sopenharmony_ci fixup.start_off = calc_offset(&fixup, ftr_fixup_test4 + 1); 82162306a36Sopenharmony_ci fixup.end_off = calc_offset(&fixup, ftr_fixup_test4 + 5); 82262306a36Sopenharmony_ci fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_test4_alt); 82362306a36Sopenharmony_ci fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_test4_alt + 2); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* Sanity check */ 82662306a36Sopenharmony_ci check(memcmp(ftr_fixup_test4, ftr_fixup_test4_orig, size) == 0); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci /* Check we don't patch if the value matches */ 82962306a36Sopenharmony_ci patch_feature_section(flag, &fixup); 83062306a36Sopenharmony_ci check(memcmp(ftr_fixup_test4, ftr_fixup_test4_orig, size) == 0); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci /* Check we do patch if the value doesn't match */ 83362306a36Sopenharmony_ci patch_feature_section(0, &fixup); 83462306a36Sopenharmony_ci check(memcmp(ftr_fixup_test4, ftr_fixup_test4_expected, size) == 0); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci /* Check we do patch if the mask doesn't match */ 83762306a36Sopenharmony_ci memcpy(ftr_fixup_test4, ftr_fixup_test4_orig, size); 83862306a36Sopenharmony_ci check(memcmp(ftr_fixup_test4, ftr_fixup_test4_orig, size) == 0); 83962306a36Sopenharmony_ci patch_feature_section(~flag, &fixup); 84062306a36Sopenharmony_ci check(memcmp(ftr_fixup_test4, ftr_fixup_test4_expected, size) == 0); 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic void test_alternative_case_with_branch(void) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci extern unsigned int ftr_fixup_test5[]; 84662306a36Sopenharmony_ci extern unsigned int end_ftr_fixup_test5[]; 84762306a36Sopenharmony_ci extern unsigned int ftr_fixup_test5_expected[]; 84862306a36Sopenharmony_ci int size = 4 * (end_ftr_fixup_test5 - ftr_fixup_test5); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci check(memcmp(ftr_fixup_test5, ftr_fixup_test5_expected, size) == 0); 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_cistatic void __init test_alternative_case_with_external_branch(void) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci extern unsigned int ftr_fixup_test6[]; 85662306a36Sopenharmony_ci extern unsigned int end_ftr_fixup_test6[]; 85762306a36Sopenharmony_ci extern unsigned int ftr_fixup_test6_expected[]; 85862306a36Sopenharmony_ci int size = 4 * (end_ftr_fixup_test6 - ftr_fixup_test6); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci check(memcmp(ftr_fixup_test6, ftr_fixup_test6_expected, size) == 0); 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_cistatic void __init test_alternative_case_with_branch_to_end(void) 86462306a36Sopenharmony_ci{ 86562306a36Sopenharmony_ci extern unsigned int ftr_fixup_test7[]; 86662306a36Sopenharmony_ci extern unsigned int end_ftr_fixup_test7[]; 86762306a36Sopenharmony_ci extern unsigned int ftr_fixup_test7_expected[]; 86862306a36Sopenharmony_ci int size = 4 * (end_ftr_fixup_test7 - ftr_fixup_test7); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci check(memcmp(ftr_fixup_test7, ftr_fixup_test7_expected, size) == 0); 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic void __init test_cpu_macros(void) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci extern u8 ftr_fixup_test_FTR_macros[]; 87662306a36Sopenharmony_ci extern u8 ftr_fixup_test_FTR_macros_expected[]; 87762306a36Sopenharmony_ci unsigned long size = ftr_fixup_test_FTR_macros_expected - 87862306a36Sopenharmony_ci ftr_fixup_test_FTR_macros; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* The fixups have already been done for us during boot */ 88162306a36Sopenharmony_ci check(memcmp(ftr_fixup_test_FTR_macros, 88262306a36Sopenharmony_ci ftr_fixup_test_FTR_macros_expected, size) == 0); 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cistatic void __init test_fw_macros(void) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci#ifdef CONFIG_PPC64 88862306a36Sopenharmony_ci extern u8 ftr_fixup_test_FW_FTR_macros[]; 88962306a36Sopenharmony_ci extern u8 ftr_fixup_test_FW_FTR_macros_expected[]; 89062306a36Sopenharmony_ci unsigned long size = ftr_fixup_test_FW_FTR_macros_expected - 89162306a36Sopenharmony_ci ftr_fixup_test_FW_FTR_macros; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci /* The fixups have already been done for us during boot */ 89462306a36Sopenharmony_ci check(memcmp(ftr_fixup_test_FW_FTR_macros, 89562306a36Sopenharmony_ci ftr_fixup_test_FW_FTR_macros_expected, size) == 0); 89662306a36Sopenharmony_ci#endif 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_cistatic void __init test_lwsync_macros(void) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci extern u8 lwsync_fixup_test[]; 90262306a36Sopenharmony_ci extern u8 end_lwsync_fixup_test[]; 90362306a36Sopenharmony_ci extern u8 lwsync_fixup_test_expected_LWSYNC[]; 90462306a36Sopenharmony_ci extern u8 lwsync_fixup_test_expected_SYNC[]; 90562306a36Sopenharmony_ci unsigned long size = end_lwsync_fixup_test - 90662306a36Sopenharmony_ci lwsync_fixup_test; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci /* The fixups have already been done for us during boot */ 90962306a36Sopenharmony_ci if (cur_cpu_spec->cpu_features & CPU_FTR_LWSYNC) { 91062306a36Sopenharmony_ci check(memcmp(lwsync_fixup_test, 91162306a36Sopenharmony_ci lwsync_fixup_test_expected_LWSYNC, size) == 0); 91262306a36Sopenharmony_ci } else { 91362306a36Sopenharmony_ci check(memcmp(lwsync_fixup_test, 91462306a36Sopenharmony_ci lwsync_fixup_test_expected_SYNC, size) == 0); 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci#ifdef CONFIG_PPC64 91962306a36Sopenharmony_cistatic void __init test_prefix_patching(void) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci extern unsigned int ftr_fixup_prefix1[]; 92262306a36Sopenharmony_ci extern unsigned int end_ftr_fixup_prefix1[]; 92362306a36Sopenharmony_ci extern unsigned int ftr_fixup_prefix1_orig[]; 92462306a36Sopenharmony_ci extern unsigned int ftr_fixup_prefix1_expected[]; 92562306a36Sopenharmony_ci int size = sizeof(unsigned int) * (end_ftr_fixup_prefix1 - ftr_fixup_prefix1); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci fixup.value = fixup.mask = 8; 92862306a36Sopenharmony_ci fixup.start_off = calc_offset(&fixup, ftr_fixup_prefix1 + 1); 92962306a36Sopenharmony_ci fixup.end_off = calc_offset(&fixup, ftr_fixup_prefix1 + 3); 93062306a36Sopenharmony_ci fixup.alt_start_off = fixup.alt_end_off = 0; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci /* Sanity check */ 93362306a36Sopenharmony_ci check(memcmp(ftr_fixup_prefix1, ftr_fixup_prefix1_orig, size) == 0); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci patch_feature_section(0, &fixup); 93662306a36Sopenharmony_ci check(memcmp(ftr_fixup_prefix1, ftr_fixup_prefix1_expected, size) == 0); 93762306a36Sopenharmony_ci check(memcmp(ftr_fixup_prefix1, ftr_fixup_prefix1_orig, size) != 0); 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_cistatic void __init test_prefix_alt_patching(void) 94162306a36Sopenharmony_ci{ 94262306a36Sopenharmony_ci extern unsigned int ftr_fixup_prefix2[]; 94362306a36Sopenharmony_ci extern unsigned int end_ftr_fixup_prefix2[]; 94462306a36Sopenharmony_ci extern unsigned int ftr_fixup_prefix2_orig[]; 94562306a36Sopenharmony_ci extern unsigned int ftr_fixup_prefix2_expected[]; 94662306a36Sopenharmony_ci extern unsigned int ftr_fixup_prefix2_alt[]; 94762306a36Sopenharmony_ci int size = sizeof(unsigned int) * (end_ftr_fixup_prefix2 - ftr_fixup_prefix2); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci fixup.value = fixup.mask = 8; 95062306a36Sopenharmony_ci fixup.start_off = calc_offset(&fixup, ftr_fixup_prefix2 + 1); 95162306a36Sopenharmony_ci fixup.end_off = calc_offset(&fixup, ftr_fixup_prefix2 + 3); 95262306a36Sopenharmony_ci fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_prefix2_alt); 95362306a36Sopenharmony_ci fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_prefix2_alt + 2); 95462306a36Sopenharmony_ci /* Sanity check */ 95562306a36Sopenharmony_ci check(memcmp(ftr_fixup_prefix2, ftr_fixup_prefix2_orig, size) == 0); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci patch_feature_section(0, &fixup); 95862306a36Sopenharmony_ci check(memcmp(ftr_fixup_prefix2, ftr_fixup_prefix2_expected, size) == 0); 95962306a36Sopenharmony_ci check(memcmp(ftr_fixup_prefix2, ftr_fixup_prefix2_orig, size) != 0); 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_cistatic void __init test_prefix_word_alt_patching(void) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci extern unsigned int ftr_fixup_prefix3[]; 96562306a36Sopenharmony_ci extern unsigned int end_ftr_fixup_prefix3[]; 96662306a36Sopenharmony_ci extern unsigned int ftr_fixup_prefix3_orig[]; 96762306a36Sopenharmony_ci extern unsigned int ftr_fixup_prefix3_expected[]; 96862306a36Sopenharmony_ci extern unsigned int ftr_fixup_prefix3_alt[]; 96962306a36Sopenharmony_ci int size = sizeof(unsigned int) * (end_ftr_fixup_prefix3 - ftr_fixup_prefix3); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci fixup.value = fixup.mask = 8; 97262306a36Sopenharmony_ci fixup.start_off = calc_offset(&fixup, ftr_fixup_prefix3 + 1); 97362306a36Sopenharmony_ci fixup.end_off = calc_offset(&fixup, ftr_fixup_prefix3 + 4); 97462306a36Sopenharmony_ci fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_prefix3_alt); 97562306a36Sopenharmony_ci fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_prefix3_alt + 3); 97662306a36Sopenharmony_ci /* Sanity check */ 97762306a36Sopenharmony_ci check(memcmp(ftr_fixup_prefix3, ftr_fixup_prefix3_orig, size) == 0); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci patch_feature_section(0, &fixup); 98062306a36Sopenharmony_ci check(memcmp(ftr_fixup_prefix3, ftr_fixup_prefix3_expected, size) == 0); 98162306a36Sopenharmony_ci patch_feature_section(0, &fixup); 98262306a36Sopenharmony_ci check(memcmp(ftr_fixup_prefix3, ftr_fixup_prefix3_orig, size) != 0); 98362306a36Sopenharmony_ci} 98462306a36Sopenharmony_ci#else 98562306a36Sopenharmony_cistatic inline void test_prefix_patching(void) {} 98662306a36Sopenharmony_cistatic inline void test_prefix_alt_patching(void) {} 98762306a36Sopenharmony_cistatic inline void test_prefix_word_alt_patching(void) {} 98862306a36Sopenharmony_ci#endif /* CONFIG_PPC64 */ 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic int __init test_feature_fixups(void) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci printk(KERN_DEBUG "Running feature fixup self-tests ...\n"); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci test_basic_patching(); 99562306a36Sopenharmony_ci test_alternative_patching(); 99662306a36Sopenharmony_ci test_alternative_case_too_big(); 99762306a36Sopenharmony_ci test_alternative_case_too_small(); 99862306a36Sopenharmony_ci test_alternative_case_with_branch(); 99962306a36Sopenharmony_ci test_alternative_case_with_external_branch(); 100062306a36Sopenharmony_ci test_alternative_case_with_branch_to_end(); 100162306a36Sopenharmony_ci test_cpu_macros(); 100262306a36Sopenharmony_ci test_fw_macros(); 100362306a36Sopenharmony_ci test_lwsync_macros(); 100462306a36Sopenharmony_ci test_prefix_patching(); 100562306a36Sopenharmony_ci test_prefix_alt_patching(); 100662306a36Sopenharmony_ci test_prefix_word_alt_patching(); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci return 0; 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_cilate_initcall(test_feature_fixups); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci#endif /* CONFIG_FTR_FIXUP_SELFTEST */ 1013