1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com> 4 */ 5 #include <libcflat.h> 6 #include <bitops.h> 7 #include <devicetree.h> 8 #include <string.h> 9 #include <asm/isa.h> 10 #include <asm/setup.h> 11 12 typedef void (*isa_func_t)(const char *, int, void *); 13 14 struct isa_info { 15 unsigned long hartid; 16 isa_func_t func; 17 void *data; 18 }; 19 20 static bool isa_match(const char *ext, const char *name, int len) 21 { 22 return len == strlen(ext) && !strncasecmp(name, ext, len); 23 } 24 25 struct isa_check { 26 const char *ext; 27 bool found; 28 }; 29 30 static void isa_name(const char *name, int len, void *data) 31 { 32 struct isa_check *check = (struct isa_check *)data; 33 34 if (isa_match(check->ext, name, len)) 35 check->found = true; 36 } 37 38 static void isa_bit(const char *name, int len, void *data) 39 { 40 struct thread_info *info = (struct thread_info *)data; 41 42 if (isa_match("sstc", name, len)) 43 set_bit(ISA_SSTC, info->isa); 44 } 45 46 static void isa_parse(const char *isa_string, int len, struct isa_info *info) 47 { 48 assert(isa_string[0] == 'r' && isa_string[1] == 'v'); 49 #if __riscv_xlen == 32 50 assert(isa_string[2] == '3' && isa_string[3] == '2'); 51 #else 52 assert(isa_string[2] == '6' && isa_string[3] == '4'); 53 #endif 54 55 for (int i = 4; i < len; ++i) { 56 if (isa_string[i] == '_') { 57 const char *multi = &isa_string[++i]; 58 int start = i; 59 60 while (i < len - 1 && isa_string[i] != '_') 61 ++i; 62 info->func(multi, i - start, info->data); 63 if (i < len - 1) 64 --i; 65 } else { 66 info->func(&isa_string[i], 1, info->data); 67 } 68 } 69 } 70 71 static void isa_parse_fdt(int cpu_node, u64 hartid, void *data) 72 { 73 struct isa_info *info = (struct isa_info *)data; 74 const struct fdt_property *prop; 75 int len; 76 77 if (hartid != info->hartid) 78 return; 79 80 prop = fdt_get_property(dt_fdt(), cpu_node, "riscv,isa", &len); 81 assert(prop); 82 83 isa_parse(prop->data, len, info); 84 } 85 86 static void isa_init_acpi(void) 87 { 88 assert_msg(false, "ACPI not available"); 89 } 90 91 void isa_init(struct thread_info *ti) 92 { 93 struct isa_info info = { 94 .hartid = ti->hartid, 95 .func = isa_bit, 96 .data = ti, 97 }; 98 int ret; 99 100 if (dt_available()) { 101 ret = dt_for_each_cpu_node(isa_parse_fdt, &info); 102 assert(ret == 0); 103 } else { 104 isa_init_acpi(); 105 } 106 } 107 108 bool cpu_has_extension_name(int cpu, const char *ext) 109 { 110 struct isa_info info = { 111 .hartid = cpus[cpu].hartid, 112 .func = isa_name, 113 .data = &(struct isa_check){ .ext = ext, }, 114 }; 115 struct isa_check *check = info.data; 116 int ret; 117 118 if (dt_available()) { 119 ret = dt_for_each_cpu_node(isa_parse_fdt, &info); 120 assert(ret == 0); 121 } else { 122 assert_msg(false, "ACPI not available"); 123 } 124 125 return check->found; 126 } 127