122 lines · 3.3 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* *    Alternative live-patching for parisc. *    Copyright (C) 2018 Helge Deller <deller@gmx.de> * */ #include <asm/processor.h>#include <asm/sections.h>#include <asm/alternative.h>#include <asm/cacheflush.h> #include <linux/module.h> static int no_alternatives;static int __init setup_no_alternatives(char *str){	no_alternatives = 1;	return 1;}__setup("no-alternatives", setup_no_alternatives); void __init_or_module apply_alternatives(struct alt_instr *start,		 struct alt_instr *end, const char *module_name){	struct alt_instr *entry;	int index = 0, applied = 0;	int num_cpus = num_present_cpus();	u16 cond_check; 	cond_check = ALT_COND_ALWAYS |		((num_cpus == 1) ? ALT_COND_NO_SMP : 0) |		((cache_info.dc_size == 0) ? ALT_COND_NO_DCACHE : 0) |		((cache_info.ic_size == 0) ? ALT_COND_NO_ICACHE : 0) |		(running_on_qemu ? ALT_COND_RUN_ON_QEMU : 0) |		((split_tlb == 0) ? ALT_COND_NO_SPLIT_TLB : 0) |		/*		 * If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit		 * set (bit #61, big endian), we have to flush and sync every		 * time IO-PDIR is changed in Ike/Astro.		 */		(((boot_cpu_data.cpu_type > pcxw_) &&		  ((boot_cpu_data.pdc.capabilities & PDC_MODEL_IOPDIR_FDC) == 0))			? ALT_COND_NO_IOC_FDC : 0); 	for (entry = start; entry < end; entry++, index++) { 		u32 *from, replacement;		u16 cond;		s16 len; 		from = (u32 *)((ulong)&entry->orig_offset + entry->orig_offset);		len = entry->len;		cond = entry->cond;		replacement = entry->replacement; 		WARN_ON(!cond); 		if ((cond & ALT_COND_ALWAYS) == 0 && no_alternatives)			continue; 		pr_debug("Check %d: Cond 0x%x, Replace %02d instructions @ 0x%px with 0x%08x\n",			index, cond, len, from, replacement); 		/* Bounce out if none of the conditions are true. */		if ((cond & cond_check) == 0)			continue; 		/* Want to replace pdtlb by a pdtlb,l instruction? */		if (replacement == INSN_PxTLB) {			replacement = *from;			if (boot_cpu_data.cpu_type >= pcxu) /* >= pa2.0 ? */				replacement |= (1 << 10); /* set el bit */		} 		/*		 * Replace instruction with NOPs?		 * For long distance insert a branch instruction instead.		 */		if (replacement == INSN_NOP && len > 1)			replacement = 0xe8000002 + (len-2)*8; /* "b,n .+8" */ 		pr_debug("ALTERNATIVE %3d: Cond %2x, Replace %2d instructions to 0x%08x @ 0x%px (%pS)\n",			index, cond, len, replacement, from, from); 		if (len < 0) {			/* Replace multiple instruction by new code */			u32 *source;			len = -len;			source = (u32 *)((ulong)&entry->replacement + entry->replacement);			memcpy(from, source, 4 * len);		} else {			/* Replace by one instruction */			*from = replacement;		}		applied++;	} 	pr_info("%s%salternatives: applied %d out of %d patches\n",		module_name ? : "", module_name ? " " : "",		applied, index);}  void __init apply_alternatives_all(void){	set_kernel_text_rw(1); 	apply_alternatives((struct alt_instr *) &__alt_instructions,		(struct alt_instr *) &__alt_instructions_end, NULL); 	if (cache_info.dc_size == 0 && cache_info.ic_size == 0) {		pr_info("alternatives: optimizing cache-flushes.\n");		static_branch_disable(&parisc_has_cache);	}	if (cache_info.dc_size == 0)		static_branch_disable(&parisc_has_dcache);	if (cache_info.ic_size == 0)		static_branch_disable(&parisc_has_icache); 	set_kernel_text_rw(0);}