1 // SPDX-License-Identifier: GPL-2.0-only 2 #include <linux/types.h> 3 #include <linux/init.h> 4 #include <linux/libfdt.h> 5 #include <linux/ctype.h> 6 #include <asm/csr.h> 7 8 #include "pi.h" 9 10 u64 get_kaslr_seed(uintptr_t dtb_pa) 11 { 12 int node, len; 13 fdt64_t *prop; 14 u64 ret; 15 16 node = fdt_path_offset((void *)dtb_pa, "/chosen"); 17 if (node < 0) 18 return 0; 19 20 prop = fdt_getprop_w((void *)dtb_pa, node, "kaslr-seed", &len); 21 if (!prop || len != sizeof(u64)) 22 return 0; 23 24 ret = fdt64_to_cpu(*prop); 25 *prop = 0; 26 return ret; 27 } 28 29 /** 30 * fdt_device_is_available - check if a device is available for use 31 * 32 * @fdt: pointer to the device tree blob 33 * @node: offset of the node whose property to find 34 * 35 * Returns true if the status property is absent or set to "okay" or "ok", 36 * false otherwise 37 */ 38 static bool fdt_device_is_available(const void *fdt, int node) 39 { 40 const char *status; 41 int statlen; 42 43 status = fdt_getprop(fdt, node, "status", &statlen); 44 if (!status) 45 return true; 46 47 if (statlen > 0) { 48 if (!strcmp(status, "okay") || !strcmp(status, "ok")) 49 return true; 50 } 51 52 return false; 53 } 54 55 /* Copy of fdt_nodename_eq_ */ 56 static int fdt_node_name_eq(const void *fdt, int offset, 57 const char *s) 58 { 59 int olen; 60 int len = strlen(s); 61 const char *p = fdt_get_name(fdt, offset, &olen); 62 63 if (!p || olen < len) 64 /* short match */ 65 return 0; 66 67 if (memcmp(p, s, len) != 0) 68 return 0; 69 70 if (p[len] == '\0') 71 return 1; 72 else if (!memchr(s, '@', len) && (p[len] == '@')) 73 return 1; 74 else 75 return 0; 76 } 77 78 /** 79 * isa_string_contains - check if isa string contains an extension 80 * 81 * @isa_str: isa string to search 82 * @ext_name: the extension to search for 83 * 84 * Returns true if the extension is in the given isa string, 85 * false otherwise 86 */ 87 static bool isa_string_contains(const char *isa_str, const char *ext_name) 88 { 89 size_t i, single_end, len = strlen(ext_name); 90 char ext_end; 91 92 /* Error must contain rv32/64 */ 93 if (strlen(isa_str) < 4) 94 return false; 95 96 if (len == 1) { 97 single_end = strcspn(isa_str, "sSxXzZ"); 98 /* Search for single chars between rv32/64 and multi-letter extensions */ 99 for (i = 4; i < single_end; i++) { 100 if (tolower(isa_str[i]) == ext_name[0]) 101 return true; 102 } 103 return false; 104 } 105 106 /* Skip to start of multi-letter extensions */ 107 isa_str = strpbrk(isa_str, "sSxXzZ"); 108 while (isa_str) { 109 if (strncasecmp(isa_str, ext_name, len) == 0) { 110 ext_end = isa_str[len]; 111 /* Check if matches the whole extension. */ 112 if (ext_end == '\0' || ext_end == '_') 113 return true; 114 } 115 /* Multi-letter extensions must be split from other multi-letter 116 * extensions with an "_", the end of a multi-letter extension will 117 * either be the null character or the "_" at the start of the next 118 * multi-letter extension. 119 */ 120 isa_str = strchr(isa_str, '_'); 121 if (isa_str) 122 isa_str++; 123 } 124 125 return false; 126 } 127 128 /** 129 * early_cpu_isa_ext_available - check if cpu node has an extension 130 * 131 * @fdt: pointer to the device tree blob 132 * @node: offset of the cpu node 133 * @ext_name: the extension to search for 134 * 135 * Returns true if the cpu node has the extension, 136 * false otherwise 137 */ 138 static bool early_cpu_isa_ext_available(const void *fdt, int node, const char *ext_name) 139 { 140 const void *prop; 141 int len; 142 143 prop = fdt_getprop(fdt, node, "riscv,isa-extensions", &len); 144 if (prop && fdt_stringlist_contains(prop, len, ext_name)) 145 return true; 146 147 prop = fdt_getprop(fdt, node, "riscv,isa", &len); 148 if (prop && isa_string_contains(prop, ext_name)) 149 return true; 150 151 return false; 152 } 153 154 /** 155 * fdt_early_match_extension_isa - check if all cpu nodes have an extension 156 * 157 * @fdt: pointer to the device tree blob 158 * @ext_name: the extension to search for 159 * 160 * Returns true if the all available the cpu nodes have the extension, 161 * false otherwise 162 */ 163 bool fdt_early_match_extension_isa(const void *fdt, const char *ext_name) 164 { 165 int node, parent; 166 bool ret = false; 167 168 parent = fdt_path_offset(fdt, "/cpus"); 169 if (parent < 0) 170 return false; 171 172 fdt_for_each_subnode(node, fdt, parent) { 173 if (!fdt_node_name_eq(fdt, node, "cpu")) 174 continue; 175 176 if (!fdt_device_is_available(fdt, node)) 177 continue; 178 179 if (!early_cpu_isa_ext_available(fdt, node, ext_name)) 180 return false; 181 182 ret = true; 183 } 184 185 return ret; 186 } 187 188 /** 189 * set_satp_mode_from_fdt - determine SATP mode based on the MMU type in fdt 190 * 191 * @dtb_pa: physical address of the device tree blob 192 * 193 * Returns the SATP mode corresponding to the MMU type of the first enabled CPU, 194 * 0 otherwise 195 */ 196 u64 set_satp_mode_from_fdt(uintptr_t dtb_pa) 197 { 198 const void *fdt = (const void *)dtb_pa; 199 const char *mmu_type; 200 int node, parent; 201 202 parent = fdt_path_offset(fdt, "/cpus"); 203 if (parent < 0) 204 return 0; 205 206 fdt_for_each_subnode(node, fdt, parent) { 207 if (!fdt_node_name_eq(fdt, node, "cpu")) 208 continue; 209 210 if (!fdt_device_is_available(fdt, node)) 211 continue; 212 213 mmu_type = fdt_getprop(fdt, node, "mmu-type", NULL); 214 if (!mmu_type) 215 break; 216 217 if (!strcmp(mmu_type, "riscv,sv39")) 218 return SATP_MODE_39; 219 else if (!strcmp(mmu_type, "riscv,sv48")) 220 return SATP_MODE_48; 221 break; 222 } 223 224 return 0; 225 } 226