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