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_page.h> 52 53 #include <machine/cpu.h> 54 #include <machine/md_var.h> 55 #include <machine/platform.h> 56 #include <machine/platformvar.h> 57 #include <machine/smp.h> 58 59 #include "platform_if.h" 60 61 static platform_def_t *plat_def_impl; 62 static platform_t plat_obj; 63 static struct kobj_ops plat_kernel_kops; 64 static struct platform_kobj plat_kernel_obj; 65 66 static char plat_name[64] = ""; 67 SYSCTL_STRING(_hw, OID_AUTO, platform, CTLFLAG_RD | CTLFLAG_TUN, 68 plat_name, 0, "Platform currently in use"); 69 70 static struct mem_region pregions[PHYS_AVAIL_SZ]; 71 static struct mem_region aregions[PHYS_AVAIL_SZ]; 72 static int npregions, naregions; 73 74 /* 75 * Memory region utilities: determine if two regions overlap, 76 * and merge two overlapping regions into one 77 */ 78 static int 79 memr_overlap(struct mem_region *r1, struct mem_region *r2) 80 { 81 if ((r1->mr_start + r1->mr_size) < r2->mr_start || 82 (r2->mr_start + r2->mr_size) < r1->mr_start) 83 return (FALSE); 84 85 return (TRUE); 86 } 87 88 static void 89 memr_merge(struct mem_region *from, struct mem_region *to) 90 { 91 vm_offset_t end; 92 end = uqmax(to->mr_start + to->mr_size, from->mr_start + from->mr_size); 93 to->mr_start = uqmin(from->mr_start, to->mr_start); 94 to->mr_size = end - to->mr_start; 95 } 96 97 /* 98 * Quick sort callout for comparing memory regions. 99 */ 100 static int 101 mr_cmp(const void *a, const void *b) 102 { 103 const struct mem_region *regiona, *regionb; 104 105 regiona = a; 106 regionb = b; 107 if (regiona->mr_start < regionb->mr_start) 108 return (-1); 109 else if (regiona->mr_start > regionb->mr_start) 110 return (1); 111 else 112 return (0); 113 } 114 115 void 116 mem_regions(struct mem_region **phys, int *physsz, struct mem_region **avail, 117 int *availsz) 118 { 119 int i, j, still_merging; 120 121 if (npregions == 0) { 122 PLATFORM_MEM_REGIONS(plat_obj, pregions, &npregions, 123 aregions, &naregions); 124 qsort(pregions, npregions, sizeof(*pregions), mr_cmp); 125 qsort(aregions, naregions, sizeof(*aregions), mr_cmp); 126 127 /* Remove overlapping available regions */ 128 do { 129 still_merging = FALSE; 130 for (i = 0; i < naregions; i++) { 131 if (aregions[i].mr_size == 0) 132 continue; 133 for (j = i+1; j < naregions; j++) { 134 if (aregions[j].mr_size == 0) 135 continue; 136 if (!memr_overlap(&aregions[j], 137 &aregions[i])) 138 continue; 139 140 memr_merge(&aregions[j], &aregions[i]); 141 /* mark inactive */ 142 aregions[j].mr_size = 0; 143 still_merging = TRUE; 144 } 145 } 146 } while (still_merging == TRUE); 147 148 /* Collapse zero-length available regions */ 149 for (i = 0; i < naregions; i++) { 150 if (aregions[i].mr_size == 0) { 151 memcpy(&aregions[i], &aregions[i+1], 152 (naregions - i - 1)*sizeof(*aregions)); 153 naregions--; 154 i--; 155 } 156 } 157 } 158 159 if (phys != NULL) 160 *phys = pregions; 161 if (avail != NULL) 162 *avail = aregions; 163 if (physsz != NULL) 164 *physsz = npregions; 165 if (availsz != NULL) 166 *availsz = naregions; 167 } 168 169 int 170 mem_valid(vm_offset_t addr, int len) 171 { 172 int i; 173 174 if (npregions == 0) { 175 struct mem_region *p, *a; 176 int na, np; 177 mem_regions(&p, &np, &a, &na); 178 } 179 180 for (i = 0; i < npregions; i++) 181 if ((addr >= pregions[i].mr_start) 182 && (addr + len <= pregions[i].mr_start + pregions[i].mr_size)) 183 return (0); 184 185 return (EFAULT); 186 } 187 188 vm_offset_t 189 platform_real_maxaddr(void) 190 { 191 return (PLATFORM_REAL_MAXADDR(plat_obj)); 192 } 193 194 const char * 195 installed_platform() 196 { 197 return (plat_def_impl->name); 198 } 199 200 u_long 201 platform_timebase_freq(struct cpuref *cpu) 202 { 203 return (PLATFORM_TIMEBASE_FREQ(plat_obj, cpu)); 204 } 205 206 /* 207 * Put the current CPU, as last step in suspend, to sleep 208 */ 209 void 210 platform_sleep() 211 { 212 PLATFORM_SLEEP(plat_obj); 213 } 214 215 int 216 platform_smp_first_cpu(struct cpuref *cpu) 217 { 218 return (PLATFORM_SMP_FIRST_CPU(plat_obj, cpu)); 219 } 220 221 int 222 platform_smp_next_cpu(struct cpuref *cpu) 223 { 224 return (PLATFORM_SMP_NEXT_CPU(plat_obj, cpu)); 225 } 226 227 int 228 platform_smp_get_bsp(struct cpuref *cpu) 229 { 230 return (PLATFORM_SMP_GET_BSP(plat_obj, cpu)); 231 } 232 233 int 234 platform_smp_start_cpu(struct pcpu *cpu) 235 { 236 return (PLATFORM_SMP_START_CPU(plat_obj, cpu)); 237 } 238 239 void 240 platform_smp_ap_init() 241 { 242 PLATFORM_SMP_AP_INIT(plat_obj); 243 } 244 245 #ifdef SMP 246 struct cpu_group * 247 cpu_topo(void) 248 { 249 return (PLATFORM_SMP_TOPO(plat_obj)); 250 } 251 #endif 252 253 /* 254 * Reset back to firmware. 255 */ 256 void 257 cpu_reset() 258 { 259 PLATFORM_RESET(plat_obj); 260 } 261 262 void platform_smp_timebase_sync(u_long tb, int ap) 263 { 264 265 PLATFORM_SMP_TIMEBASE_SYNC(plat_obj, tb, ap); 266 } 267 268 /* 269 * Platform install routines. Highest priority wins, using the same 270 * algorithm as bus attachment. 271 */ 272 SET_DECLARE(platform_set, platform_def_t); 273 274 void 275 platform_probe_and_attach() 276 { 277 platform_def_t **platpp, *platp; 278 int prio, best_prio; 279 280 plat_obj = &plat_kernel_obj; 281 best_prio = 0; 282 283 /* 284 * Try to locate the best platform kobj 285 */ 286 SET_FOREACH(platpp, platform_set) { 287 platp = *platpp; 288 289 /* 290 * Take care of compiling the selected class, and 291 * then statically initialise the MMU object 292 */ 293 kobj_class_compile_static(platp, &plat_kernel_kops); 294 kobj_init_static((kobj_t)plat_obj, platp); 295 296 prio = PLATFORM_PROBE(plat_obj); 297 298 /* Check for errors */ 299 if (prio > 0) 300 continue; 301 302 /* 303 * Check if this module was specifically requested through 304 * the loader tunable we provide. 305 */ 306 if (strcmp(platp->name,plat_name) == 0) { 307 plat_def_impl = platp; 308 break; 309 } 310 311 /* Otherwise, see if it is better than our current best */ 312 if (plat_def_impl == NULL || prio > best_prio) { 313 best_prio = prio; 314 plat_def_impl = platp; 315 } 316 317 /* 318 * We can't free the KOBJ, since it is static. Reset the ops 319 * member of this class so that we can come back later. 320 */ 321 platp->ops = NULL; 322 } 323 324 if (plat_def_impl == NULL) 325 panic("No platform module found!"); 326 327 /* 328 * Recompile to make sure we ended with the 329 * correct one, and then attach. 330 */ 331 332 kobj_class_compile_static(plat_def_impl, &plat_kernel_kops); 333 kobj_init_static((kobj_t)plat_obj, plat_def_impl); 334 335 strlcpy(plat_name,plat_def_impl->name,sizeof(plat_name)); 336 337 PLATFORM_ATTACH(plat_obj); 338 } 339 340