1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2005 Peter Grehan 5 * Copyright (c) 2009 Nathan Whitehorn 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 /* 35 * Dispatch platform calls to the appropriate platform implementation 36 * through a previously registered kernel object. 37 */ 38 39 #include <sys/param.h> 40 #include <sys/kernel.h> 41 #include <sys/lock.h> 42 #include <sys/ktr.h> 43 #include <sys/mutex.h> 44 #include <sys/proc.h> 45 #include <sys/systm.h> 46 #include <sys/smp.h> 47 #include <sys/sysctl.h> 48 #include <sys/types.h> 49 50 #include <vm/vm.h> 51 #include <vm/vm_param.h> 52 #include <vm/vm_page.h> 53 #include <vm/vm_phys.h> 54 55 #include <machine/cpu.h> 56 #include <machine/md_var.h> 57 #include <machine/platform.h> 58 #include <machine/platformvar.h> 59 #include <machine/smp.h> 60 #include <machine/vmparam.h> 61 62 #include "platform_if.h" 63 64 static platform_def_t *plat_def_impl; 65 static platform_t plat_obj; 66 static struct kobj_ops plat_kernel_kops; 67 static struct platform_kobj plat_kernel_obj; 68 69 static char plat_name[64] = ""; 70 SYSCTL_STRING(_hw, OID_AUTO, platform, CTLFLAG_RD | CTLFLAG_TUN, 71 plat_name, 0, "Platform currently in use"); 72 73 static struct mem_affinity mem_info[VM_PHYSSEG_MAX + 1]; 74 static int vm_locality_table[MAXMEMDOM * MAXMEMDOM]; 75 static struct mem_region pregions[PHYS_AVAIL_SZ]; 76 static struct numa_mem_region numa_pregions[PHYS_AVAIL_SZ]; 77 static struct mem_region aregions[PHYS_AVAIL_SZ]; 78 static int nnumapregions, npregions, naregions; 79 80 /* 81 * Memory region utilities: determine if two regions overlap, 82 * and merge two overlapping regions into one 83 */ 84 static int 85 memr_overlap(struct mem_region *r1, struct mem_region *r2) 86 { 87 if ((r1->mr_start + r1->mr_size) < r2->mr_start || 88 (r2->mr_start + r2->mr_size) < r1->mr_start) 89 return (FALSE); 90 91 return (TRUE); 92 } 93 94 static void 95 memr_merge(struct mem_region *from, struct mem_region *to) 96 { 97 vm_offset_t end; 98 end = uqmax(to->mr_start + to->mr_size, from->mr_start + from->mr_size); 99 to->mr_start = uqmin(from->mr_start, to->mr_start); 100 to->mr_size = end - to->mr_start; 101 } 102 103 /* 104 * Quick sort callout for comparing memory regions. 105 */ 106 static int 107 mr_cmp(const void *a, const void *b) 108 { 109 const struct mem_region *regiona, *regionb; 110 111 regiona = a; 112 regionb = b; 113 if (regiona->mr_start < regionb->mr_start) 114 return (-1); 115 else if (regiona->mr_start > regionb->mr_start) 116 return (1); 117 else 118 return (0); 119 } 120 121 void 122 numa_mem_regions(struct numa_mem_region **phys, int *physsz) 123 { 124 struct mem_affinity *mi; 125 int i, j, maxdom, ndomain, offset; 126 127 nnumapregions = 0; 128 PLATFORM_NUMA_MEM_REGIONS(plat_obj, numa_pregions, &nnumapregions); 129 130 if (physsz != NULL) 131 *physsz = nnumapregions; 132 if (phys != NULL) 133 *phys = numa_pregions; 134 if (physsz == NULL || phys == NULL) { 135 printf("unset value\n"); 136 return; 137 } 138 maxdom = 0; 139 for (i = 0; i < nnumapregions; i++) 140 if (numa_pregions[i].mr_domain > maxdom) 141 maxdom = numa_pregions[i].mr_domain; 142 143 mi = mem_info; 144 for (i = 0; i < nnumapregions; i++, mi++) { 145 mi->start = numa_pregions[i].mr_start; 146 mi->end = numa_pregions[i].mr_start + numa_pregions[i].mr_size; 147 mi->domain = numa_pregions[i].mr_domain; 148 } 149 offset = 0; 150 vm_locality_table[offset] = 10; 151 ndomain = maxdom + 1; 152 if (ndomain > 1) { 153 for (i = 0; i < ndomain; i++) { 154 for (j = 0; j < ndomain; j++) { 155 /* 156 * Not sure what these values should actually be 157 */ 158 if (i == j) 159 vm_locality_table[offset] = 10; 160 else 161 vm_locality_table[offset] = 21; 162 offset++; 163 } 164 } 165 } 166 vm_phys_register_domains(ndomain, mem_info, vm_locality_table); 167 } 168 169 void 170 mem_regions(struct mem_region **phys, int *physsz, struct mem_region **avail, 171 int *availsz) 172 { 173 int i, j, still_merging; 174 175 if (npregions == 0) { 176 PLATFORM_MEM_REGIONS(plat_obj, pregions, &npregions, 177 aregions, &naregions); 178 qsort(pregions, npregions, sizeof(*pregions), mr_cmp); 179 qsort(aregions, naregions, sizeof(*aregions), mr_cmp); 180 181 /* Remove overlapping available regions */ 182 do { 183 still_merging = FALSE; 184 for (i = 0; i < naregions; i++) { 185 if (aregions[i].mr_size == 0) 186 continue; 187 for (j = i+1; j < naregions; j++) { 188 if (aregions[j].mr_size == 0) 189 continue; 190 if (!memr_overlap(&aregions[j], 191 &aregions[i])) 192 continue; 193 194 memr_merge(&aregions[j], &aregions[i]); 195 /* mark inactive */ 196 aregions[j].mr_size = 0; 197 still_merging = TRUE; 198 } 199 } 200 } while (still_merging == TRUE); 201 202 /* Collapse zero-length available regions */ 203 for (i = 0; i < naregions; i++) { 204 if (aregions[i].mr_size == 0) { 205 memcpy(&aregions[i], &aregions[i+1], 206 (naregions - i - 1)*sizeof(*aregions)); 207 naregions--; 208 i--; 209 } 210 } 211 } 212 213 if (phys != NULL) 214 *phys = pregions; 215 if (avail != NULL) 216 *avail = aregions; 217 if (physsz != NULL) 218 *physsz = npregions; 219 if (availsz != NULL) 220 *availsz = naregions; 221 } 222 223 int 224 mem_valid(vm_offset_t addr, int len) 225 { 226 int i; 227 228 if (npregions == 0) { 229 struct mem_region *p, *a; 230 int na, np; 231 mem_regions(&p, &np, &a, &na); 232 } 233 234 for (i = 0; i < npregions; i++) 235 if ((addr >= pregions[i].mr_start) 236 && (addr + len <= pregions[i].mr_start + pregions[i].mr_size)) 237 return (0); 238 239 return (EFAULT); 240 } 241 242 vm_offset_t 243 platform_real_maxaddr(void) 244 { 245 return (PLATFORM_REAL_MAXADDR(plat_obj)); 246 } 247 248 const char * 249 installed_platform() 250 { 251 return (plat_def_impl->name); 252 } 253 254 u_long 255 platform_timebase_freq(struct cpuref *cpu) 256 { 257 return (PLATFORM_TIMEBASE_FREQ(plat_obj, cpu)); 258 } 259 260 /* 261 * Put the current CPU, as last step in suspend, to sleep 262 */ 263 void 264 platform_sleep() 265 { 266 PLATFORM_SLEEP(plat_obj); 267 } 268 269 int 270 platform_smp_first_cpu(struct cpuref *cpu) 271 { 272 return (PLATFORM_SMP_FIRST_CPU(plat_obj, cpu)); 273 } 274 275 int 276 platform_smp_next_cpu(struct cpuref *cpu) 277 { 278 return (PLATFORM_SMP_NEXT_CPU(plat_obj, cpu)); 279 } 280 281 int 282 platform_smp_get_bsp(struct cpuref *cpu) 283 { 284 return (PLATFORM_SMP_GET_BSP(plat_obj, cpu)); 285 } 286 287 int 288 platform_smp_start_cpu(struct pcpu *cpu) 289 { 290 return (PLATFORM_SMP_START_CPU(plat_obj, cpu)); 291 } 292 293 void 294 platform_smp_ap_init() 295 { 296 PLATFORM_SMP_AP_INIT(plat_obj); 297 } 298 299 void 300 platform_smp_probe_threads(void) 301 { 302 PLATFORM_SMP_PROBE_THREADS(plat_obj); 303 } 304 305 #ifdef SMP 306 struct cpu_group * 307 cpu_topo(void) 308 { 309 return (PLATFORM_SMP_TOPO(plat_obj)); 310 } 311 #endif 312 313 /* 314 * Reset back to firmware. 315 */ 316 void 317 cpu_reset() 318 { 319 PLATFORM_RESET(plat_obj); 320 } 321 322 void platform_smp_timebase_sync(u_long tb, int ap) 323 { 324 325 PLATFORM_SMP_TIMEBASE_SYNC(plat_obj, tb, ap); 326 } 327 328 /* 329 * Platform install routines. Highest priority wins, using the same 330 * algorithm as bus attachment. 331 */ 332 SET_DECLARE(platform_set, platform_def_t); 333 334 void 335 platform_probe_and_attach() 336 { 337 platform_def_t **platpp, *platp; 338 int prio, best_prio; 339 340 plat_obj = &plat_kernel_obj; 341 best_prio = 0; 342 343 /* 344 * Try to locate the best platform kobj 345 */ 346 SET_FOREACH(platpp, platform_set) { 347 platp = *platpp; 348 349 /* 350 * Take care of compiling the selected class, and 351 * then statically initialise the MMU object 352 */ 353 kobj_class_compile_static(platp, &plat_kernel_kops); 354 kobj_init_static((kobj_t)plat_obj, platp); 355 356 prio = PLATFORM_PROBE(plat_obj); 357 358 /* Check for errors */ 359 if (prio > 0) 360 continue; 361 362 /* 363 * Check if this module was specifically requested through 364 * the loader tunable we provide. 365 */ 366 if (strcmp(platp->name,plat_name) == 0) { 367 plat_def_impl = platp; 368 break; 369 } 370 371 /* Otherwise, see if it is better than our current best */ 372 if (plat_def_impl == NULL || prio > best_prio) { 373 best_prio = prio; 374 plat_def_impl = platp; 375 } 376 377 /* 378 * We can't free the KOBJ, since it is static. Reset the ops 379 * member of this class so that we can come back later. 380 */ 381 platp->ops = NULL; 382 } 383 384 if (plat_def_impl == NULL) 385 panic("No platform module found!"); 386 387 /* 388 * Recompile to make sure we ended with the 389 * correct one, and then attach. 390 */ 391 392 kobj_class_compile_static(plat_def_impl, &plat_kernel_kops); 393 kobj_init_static((kobj_t)plat_obj, plat_def_impl); 394 395 strlcpy(plat_name,plat_def_impl->name,sizeof(plat_name)); 396 397 PLATFORM_ATTACH(plat_obj); 398 } 399 400