1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2021 Heiko Stuebner <heiko@sntech.de> 4 */ 5 6 #include <linux/bug.h> 7 #include <linux/kernel.h> 8 #include <linux/module.h> 9 #include <linux/string.h> 10 #include <linux/uaccess.h> 11 #include <asm/alternative.h> 12 #include <asm/cacheflush.h> 13 #include <asm/errata_list.h> 14 #include <asm/patch.h> 15 #include <asm/vendorid_list.h> 16 17 struct errata_info { 18 char name[ERRATA_STRING_LENGTH_MAX]; 19 bool (*check_func)(unsigned long arch_id, unsigned long impid); 20 unsigned int stage; 21 }; 22 23 static bool errata_mt_check_func(unsigned long arch_id, unsigned long impid) 24 { 25 if (arch_id != 0 || impid != 0) 26 return false; 27 return true; 28 } 29 30 static const struct errata_info errata_list[ERRATA_THEAD_NUMBER] = { 31 { 32 .name = "memory-types", 33 .stage = RISCV_ALTERNATIVES_EARLY_BOOT, 34 .check_func = errata_mt_check_func 35 }, 36 }; 37 38 static u32 thead_errata_probe(unsigned int stage, unsigned long archid, unsigned long impid) 39 { 40 const struct errata_info *info; 41 u32 cpu_req_errata = 0; 42 int idx; 43 44 for (idx = 0; idx < ERRATA_THEAD_NUMBER; idx++) { 45 info = &errata_list[idx]; 46 47 if ((stage == RISCV_ALTERNATIVES_MODULE || 48 info->stage == stage) && info->check_func(archid, impid)) 49 cpu_req_errata |= (1U << idx); 50 } 51 52 return cpu_req_errata; 53 } 54 55 void __init_or_module thead_errata_patch_func(struct alt_entry *begin, struct alt_entry *end, 56 unsigned long archid, unsigned long impid, 57 unsigned int stage) 58 { 59 struct alt_entry *alt; 60 u32 cpu_req_errata = thead_errata_probe(stage, archid, impid); 61 u32 tmp; 62 63 for (alt = begin; alt < end; alt++) { 64 if (alt->vendor_id != THEAD_VENDOR_ID) 65 continue; 66 if (alt->errata_id >= ERRATA_THEAD_NUMBER) 67 continue; 68 69 tmp = (1U << alt->errata_id); 70 if (cpu_req_errata & tmp) { 71 /* On vm-alternatives, the mmu isn't running yet */ 72 if (stage == RISCV_ALTERNATIVES_EARLY_BOOT) 73 memcpy((void *)__pa_symbol(alt->old_ptr), 74 (void *)__pa_symbol(alt->alt_ptr), alt->alt_len); 75 else 76 patch_text_nosync(alt->old_ptr, alt->alt_ptr, alt->alt_len); 77 } 78 } 79 80 if (stage == RISCV_ALTERNATIVES_EARLY_BOOT) 81 local_flush_icache_all(); 82 } 83