1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 #define PSMI_1_7 26 27 #include <sys/vmem.h> 28 #include <vm/hat.h> 29 #include <sys/modctl.h> 30 #include <vm/seg_kmem.h> 31 #include <sys/psm.h> 32 #include <sys/psm_modctl.h> 33 #include <sys/smp_impldefs.h> 34 #include <sys/reboot.h> 35 36 /* 37 * External reference functions 38 */ 39 extern void *get_next_mach(void *, char *); 40 extern void close_mach_list(void); 41 extern void open_mach_list(void); 42 43 /* 44 * from startup.c - kernel VA range allocator for device mappings 45 */ 46 extern void *device_arena_alloc(size_t size, int vm_flag); 47 extern void device_arena_free(void * vaddr, size_t size); 48 49 void psm_modloadonly(void); 50 void psm_install(void); 51 52 /* 53 * Local Function Prototypes 54 */ 55 static struct modlinkage *psm_modlinkage_alloc(struct psm_info *infop); 56 static void psm_modlinkage_free(struct modlinkage *mlinkp); 57 58 static char *psm_get_impl_module(int first); 59 60 static int mod_installpsm(struct modlpsm *modl, struct modlinkage *modlp); 61 static int mod_removepsm(struct modlpsm *modl, struct modlinkage *modlp); 62 static int mod_infopsm(struct modlpsm *modl, struct modlinkage *modlp, int *p0); 63 struct mod_ops mod_psmops = { 64 mod_installpsm, mod_removepsm, mod_infopsm 65 }; 66 67 static struct psm_sw psm_swtab = { 68 &psm_swtab, &psm_swtab, NULL, NULL 69 }; 70 71 kmutex_t psmsw_lock; /* lock accesses to psmsw */ 72 struct psm_sw *psmsw = &psm_swtab; /* start of all psm_sw */ 73 74 static struct modlinkage * 75 psm_modlinkage_alloc(struct psm_info *infop) 76 { 77 int memsz; 78 struct modlinkage *mlinkp; 79 struct modlpsm *mlpsmp; 80 struct psm_sw *swp; 81 82 memsz = sizeof (struct modlinkage) + sizeof (struct modlpsm) + 83 sizeof (struct psm_sw); 84 mlinkp = (struct modlinkage *)kmem_zalloc(memsz, KM_NOSLEEP); 85 if (!mlinkp) { 86 cmn_err(CE_WARN, "!psm_mod_init: Cannot install %s", 87 infop->p_mach_idstring); 88 return (NULL); 89 } 90 mlpsmp = (struct modlpsm *)(mlinkp + 1); 91 swp = (struct psm_sw *)(mlpsmp + 1); 92 93 mlinkp->ml_rev = MODREV_1; 94 mlinkp->ml_linkage[0] = (void *)mlpsmp; 95 mlinkp->ml_linkage[1] = (void *)NULL; 96 97 mlpsmp->psm_modops = &mod_psmops; 98 mlpsmp->psm_linkinfo = infop->p_mach_desc; 99 mlpsmp->psm_swp = swp; 100 101 swp->psw_infop = infop; 102 103 return (mlinkp); 104 } 105 106 static void 107 psm_modlinkage_free(struct modlinkage *mlinkp) 108 { 109 if (!mlinkp) 110 return; 111 112 (void) kmem_free(mlinkp, (sizeof (struct modlinkage) + 113 sizeof (struct modlpsm) + sizeof (struct psm_sw))); 114 } 115 116 int 117 psm_mod_init(void **handlepp, struct psm_info *infop) 118 { 119 struct modlinkage **modlpp = (struct modlinkage **)handlepp; 120 int status; 121 struct modlinkage *mlinkp; 122 123 if (!*modlpp) { 124 mlinkp = psm_modlinkage_alloc(infop); 125 if (!mlinkp) 126 return (ENOSPC); 127 } else 128 mlinkp = *modlpp; 129 130 status = mod_install(mlinkp); 131 if (status) { 132 psm_modlinkage_free(mlinkp); 133 *modlpp = NULL; 134 } else 135 *modlpp = mlinkp; 136 137 return (status); 138 } 139 140 /*ARGSUSED1*/ 141 int 142 psm_mod_fini(void **handlepp, struct psm_info *infop) 143 { 144 struct modlinkage **modlpp = (struct modlinkage **)handlepp; 145 int status; 146 147 status = mod_remove(*modlpp); 148 if (status == 0) { 149 psm_modlinkage_free(*modlpp); 150 *modlpp = NULL; 151 } 152 return (status); 153 } 154 155 int 156 psm_mod_info(void **handlepp, struct psm_info *infop, struct modinfo *modinfop) 157 { 158 struct modlinkage **modlpp = (struct modlinkage **)handlepp; 159 int status; 160 struct modlinkage *mlinkp; 161 162 if (!*modlpp) { 163 mlinkp = psm_modlinkage_alloc(infop); 164 if (!mlinkp) 165 return ((int)NULL); 166 } else 167 mlinkp = *modlpp; 168 169 status = mod_info(mlinkp, modinfop); 170 171 if (!status) { 172 psm_modlinkage_free(mlinkp); 173 *modlpp = NULL; 174 } else 175 *modlpp = mlinkp; 176 177 return (status); 178 } 179 180 int 181 psm_add_intr(int lvl, avfunc xxintr, char *name, int vect, caddr_t arg) 182 { 183 return (add_avintr((void *)NULL, lvl, xxintr, name, vect, 184 arg, NULL, NULL, NULL)); 185 } 186 187 int 188 psm_add_nmintr(int lvl, avfunc xxintr, char *name, caddr_t arg) 189 { 190 return (add_nmintr(lvl, xxintr, name, arg)); 191 } 192 193 processorid_t 194 psm_get_cpu_id(void) 195 { 196 return (CPU->cpu_id); 197 } 198 199 caddr_t 200 psm_map_phys_new(paddr_t addr, size_t len, int prot) 201 { 202 uint_t pgoffset; 203 paddr_t base; 204 pgcnt_t npages; 205 caddr_t cvaddr; 206 207 if (len == 0) 208 return (0); 209 210 pgoffset = addr & MMU_PAGEOFFSET; 211 base = addr; 212 npages = mmu_btopr(len + pgoffset); 213 cvaddr = device_arena_alloc(ptob(npages), VM_NOSLEEP); 214 if (cvaddr == NULL) 215 return (0); 216 hat_devload(kas.a_hat, cvaddr, mmu_ptob(npages), mmu_btop(base), 217 prot, HAT_LOAD_LOCK); 218 return (cvaddr + pgoffset); 219 } 220 221 void 222 psm_unmap_phys(caddr_t addr, size_t len) 223 { 224 uint_t pgoffset; 225 caddr_t base; 226 pgcnt_t npages; 227 228 if (len == 0) 229 return; 230 231 pgoffset = (uintptr_t)addr & MMU_PAGEOFFSET; 232 base = addr - pgoffset; 233 npages = mmu_btopr(len + pgoffset); 234 hat_unload(kas.a_hat, base, ptob(npages), HAT_UNLOAD_UNLOCK); 235 device_arena_free(base, ptob(npages)); 236 } 237 238 caddr_t 239 psm_map_new(paddr_t addr, size_t len, int prot) 240 { 241 int phys_prot = PROT_READ; 242 243 ASSERT(prot == (prot & (PSM_PROT_WRITE | PSM_PROT_READ))); 244 if (prot & PSM_PROT_WRITE) 245 phys_prot |= PROT_WRITE; 246 247 return (psm_map_phys(addr, len, phys_prot)); 248 } 249 250 #undef psm_map_phys 251 #undef psm_map 252 253 caddr_t 254 psm_map_phys(uint32_t addr, size_t len, int prot) 255 { 256 return (psm_map_phys_new((paddr_t)(addr & 0xffffffff), len, prot)); 257 } 258 259 caddr_t 260 psm_map(uint32_t addr, size_t len, int prot) 261 { 262 return (psm_map_new((paddr_t)(addr & 0xffffffff), len, prot)); 263 } 264 265 void 266 psm_unmap(caddr_t addr, size_t len) 267 { 268 uint_t pgoffset; 269 caddr_t base; 270 pgcnt_t npages; 271 272 if (len == 0) 273 return; 274 275 pgoffset = (uintptr_t)addr & MMU_PAGEOFFSET; 276 base = addr - pgoffset; 277 npages = mmu_btopr(len + pgoffset); 278 hat_unload(kas.a_hat, base, ptob(npages), HAT_UNLOAD_UNLOCK); 279 device_arena_free(base, ptob(npages)); 280 } 281 282 /*ARGSUSED1*/ 283 static int 284 mod_installpsm(struct modlpsm *modl, struct modlinkage *modlp) 285 { 286 struct psm_sw *swp; 287 288 swp = modl->psm_swp; 289 mutex_enter(&psmsw_lock); 290 psmsw->psw_back->psw_forw = swp; 291 swp->psw_back = psmsw->psw_back; 292 swp->psw_forw = psmsw; 293 psmsw->psw_back = swp; 294 swp->psw_flag |= PSM_MOD_INSTALL; 295 mutex_exit(&psmsw_lock); 296 return (0); 297 } 298 299 /*ARGSUSED1*/ 300 static int 301 mod_removepsm(struct modlpsm *modl, struct modlinkage *modlp) 302 { 303 struct psm_sw *swp; 304 305 swp = modl->psm_swp; 306 mutex_enter(&psmsw_lock); 307 if (swp->psw_flag & PSM_MOD_IDENTIFY) { 308 mutex_exit(&psmsw_lock); 309 return (EBUSY); 310 } 311 if (!(swp->psw_flag & PSM_MOD_INSTALL)) { 312 mutex_exit(&psmsw_lock); 313 return (0); 314 } 315 316 swp->psw_back->psw_forw = swp->psw_forw; 317 swp->psw_forw->psw_back = swp->psw_back; 318 mutex_exit(&psmsw_lock); 319 return (0); 320 } 321 322 /*ARGSUSED1*/ 323 static int 324 mod_infopsm(struct modlpsm *modl, struct modlinkage *modlp, int *p0) 325 { 326 *p0 = (int)modl->psm_swp->psw_infop->p_owner; 327 return (0); 328 } 329 330 #define DEFAULT_PSM_MODULE "uppc" 331 332 static char * 333 psm_get_impl_module(int first) 334 { 335 static char **pnamep; 336 static char *psm_impl_module_list[] = { 337 DEFAULT_PSM_MODULE, 338 (char *)0 339 }; 340 static void *mhdl = NULL; 341 static char machname[MAXNAMELEN]; 342 343 if (first) 344 pnamep = psm_impl_module_list; 345 346 if (*pnamep != (char *)0) 347 return (*pnamep++); 348 349 mhdl = get_next_mach(mhdl, machname); 350 if (mhdl) 351 return (machname); 352 return ((char *)0); 353 } 354 355 void 356 psm_modload(void) 357 { 358 char *this; 359 360 mutex_init(&psmsw_lock, NULL, MUTEX_DEFAULT, NULL); 361 open_mach_list(); 362 363 for (this = psm_get_impl_module(1); this != (char *)NULL; 364 this = psm_get_impl_module(0)) { 365 if (modload("mach", this) == -1) 366 cmn_err(CE_CONT, "!Skipping psm: %s\n", this); 367 } 368 close_mach_list(); 369 } 370 371 void 372 psm_install(void) 373 { 374 struct psm_sw *swp, *cswp; 375 struct psm_ops *opsp; 376 char machstring[15]; 377 int err, psmcnt = 0; 378 379 mutex_enter(&psmsw_lock); 380 for (swp = psmsw->psw_forw; swp != psmsw; ) { 381 opsp = swp->psw_infop->p_ops; 382 if (opsp->psm_probe) { 383 if ((*opsp->psm_probe)() == PSM_SUCCESS) { 384 psmcnt++; 385 swp->psw_flag |= PSM_MOD_IDENTIFY; 386 swp = swp->psw_forw; 387 continue; 388 } 389 } 390 /* remove the unsuccessful psm modules */ 391 cswp = swp; 392 swp = swp->psw_forw; 393 394 mutex_exit(&psmsw_lock); 395 (void) strcpy(&machstring[0], cswp->psw_infop->p_mach_idstring); 396 err = mod_remove_by_name(cswp->psw_infop->p_mach_idstring); 397 if (err) 398 cmn_err(CE_WARN, "!%s: mod_remove_by_name failed %d", 399 &machstring[0], err); 400 mutex_enter(&psmsw_lock); 401 } 402 mutex_exit(&psmsw_lock); 403 if (psmcnt == 0) 404 halt("This hardware is not supported by current OS version"); 405 (*psminitf)(); 406 } 407 408 /* 409 * Return 1 if kernel debugger is present, and 0 if not. 410 */ 411 int 412 psm_debugger(void) 413 { 414 return ((boothowto & RB_DEBUG) != 0); 415 }