alternative.c (3272B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * alternative runtime patching 4 * inspired by the ARM64 and x86 version 5 * 6 * Copyright (C) 2021 Sifive. 7 */ 8 9#include <linux/init.h> 10#include <linux/module.h> 11#include <linux/cpu.h> 12#include <linux/uaccess.h> 13#include <asm/alternative.h> 14#include <asm/sections.h> 15#include <asm/vendorid_list.h> 16#include <asm/sbi.h> 17#include <asm/csr.h> 18 19struct cpu_manufacturer_info_t { 20 unsigned long vendor_id; 21 unsigned long arch_id; 22 unsigned long imp_id; 23 void (*vendor_patch_func)(struct alt_entry *begin, struct alt_entry *end, 24 unsigned long archid, unsigned long impid, 25 unsigned int stage); 26}; 27 28static void __init_or_module riscv_fill_cpu_mfr_info(struct cpu_manufacturer_info_t *cpu_mfr_info) 29{ 30#ifdef CONFIG_RISCV_M_MODE 31 cpu_mfr_info->vendor_id = csr_read(CSR_MVENDORID); 32 cpu_mfr_info->arch_id = csr_read(CSR_MARCHID); 33 cpu_mfr_info->imp_id = csr_read(CSR_MIMPID); 34#else 35 cpu_mfr_info->vendor_id = sbi_get_mvendorid(); 36 cpu_mfr_info->arch_id = sbi_get_marchid(); 37 cpu_mfr_info->imp_id = sbi_get_mimpid(); 38#endif 39 40 switch (cpu_mfr_info->vendor_id) { 41#ifdef CONFIG_ERRATA_SIFIVE 42 case SIFIVE_VENDOR_ID: 43 cpu_mfr_info->vendor_patch_func = sifive_errata_patch_func; 44 break; 45#endif 46#ifdef CONFIG_ERRATA_THEAD 47 case THEAD_VENDOR_ID: 48 cpu_mfr_info->vendor_patch_func = thead_errata_patch_func; 49 break; 50#endif 51 default: 52 cpu_mfr_info->vendor_patch_func = NULL; 53 } 54} 55 56/* 57 * This is called very early in the boot process (directly after we run 58 * a feature detect on the boot CPU). No need to worry about other CPUs 59 * here. 60 */ 61static void __init_or_module _apply_alternatives(struct alt_entry *begin, 62 struct alt_entry *end, 63 unsigned int stage) 64{ 65 struct cpu_manufacturer_info_t cpu_mfr_info; 66 67 riscv_fill_cpu_mfr_info(&cpu_mfr_info); 68 69 riscv_cpufeature_patch_func(begin, end, stage); 70 71 if (!cpu_mfr_info.vendor_patch_func) 72 return; 73 74 cpu_mfr_info.vendor_patch_func(begin, end, 75 cpu_mfr_info.arch_id, 76 cpu_mfr_info.imp_id, 77 stage); 78} 79 80void __init apply_boot_alternatives(void) 81{ 82 /* If called on non-boot cpu things could go wrong */ 83 WARN_ON(smp_processor_id() != 0); 84 85 _apply_alternatives((struct alt_entry *)__alt_start, 86 (struct alt_entry *)__alt_end, 87 RISCV_ALTERNATIVES_BOOT); 88} 89 90/* 91 * apply_early_boot_alternatives() is called from setup_vm() with MMU-off. 92 * 93 * Following requirements should be honoured for it to work correctly: 94 * 1) It should use PC-relative addressing for accessing kernel symbols. 95 * To achieve this we always use GCC cmodel=medany. 96 * 2) The compiler instrumentation for FTRACE will not work for setup_vm() 97 * so disable compiler instrumentation when FTRACE is enabled. 98 * 99 * Currently, the above requirements are honoured by using custom CFLAGS 100 * for alternative.o in kernel/Makefile. 101 */ 102void __init apply_early_boot_alternatives(void) 103{ 104#ifdef CONFIG_RISCV_ALTERNATIVE_EARLY 105 _apply_alternatives((struct alt_entry *)__alt_start, 106 (struct alt_entry *)__alt_end, 107 RISCV_ALTERNATIVES_EARLY_BOOT); 108#endif 109} 110 111#ifdef CONFIG_MODULES 112void apply_module_alternatives(void *start, size_t length) 113{ 114 _apply_alternatives((struct alt_entry *)start, 115 (struct alt_entry *)(start + length), 116 RISCV_ALTERNATIVES_MODULE); 117} 118#endif