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