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