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 #if defined(__xpv) 36 #include <sys/hypervisor.h> 37 #include <vm/kboot_mmu.h> 38 #include <vm/hat_pte.h> 39 #endif 40 41 /* 42 * External reference functions 43 */ 44 extern void *get_next_mach(void *, char *); 45 extern void close_mach_list(void); 46 extern void open_mach_list(void); 47 48 /* 49 * from startup.c - kernel VA range allocator for device mappings 50 */ 51 extern void *device_arena_alloc(size_t size, int vm_flag); 52 extern void device_arena_free(void * vaddr, size_t size); 53 54 void psm_modloadonly(void); 55 void psm_install(void); 56 57 /* 58 * Local Function Prototypes 59 */ 60 static struct modlinkage *psm_modlinkage_alloc(struct psm_info *infop); 61 static void psm_modlinkage_free(struct modlinkage *mlinkp); 62 63 static char *psm_get_impl_module(int first); 64 65 static int mod_installpsm(struct modlpsm *modl, struct modlinkage *modlp); 66 static int mod_removepsm(struct modlpsm *modl, struct modlinkage *modlp); 67 static int mod_infopsm(struct modlpsm *modl, struct modlinkage *modlp, int *p0); 68 struct mod_ops mod_psmops = { 69 mod_installpsm, mod_removepsm, mod_infopsm 70 }; 71 72 static struct psm_sw psm_swtab = { 73 &psm_swtab, &psm_swtab, NULL, 0 74 }; 75 76 kmutex_t psmsw_lock; /* lock accesses to psmsw */ 77 struct psm_sw *psmsw = &psm_swtab; /* start of all psm_sw */ 78 79 static struct modlinkage * 80 psm_modlinkage_alloc(struct psm_info *infop) 81 { 82 int memsz; 83 struct modlinkage *mlinkp; 84 struct modlpsm *mlpsmp; 85 struct psm_sw *swp; 86 87 memsz = sizeof (struct modlinkage) + sizeof (struct modlpsm) + 88 sizeof (struct psm_sw); 89 mlinkp = (struct modlinkage *)kmem_zalloc(memsz, KM_NOSLEEP); 90 if (!mlinkp) { 91 cmn_err(CE_WARN, "!psm_mod_init: Cannot install %s", 92 infop->p_mach_idstring); 93 return (NULL); 94 } 95 mlpsmp = (struct modlpsm *)(mlinkp + 1); 96 swp = (struct psm_sw *)(mlpsmp + 1); 97 98 mlinkp->ml_rev = MODREV_1; 99 mlinkp->ml_linkage[0] = (void *)mlpsmp; 100 mlinkp->ml_linkage[1] = (void *)NULL; 101 102 mlpsmp->psm_modops = &mod_psmops; 103 mlpsmp->psm_linkinfo = infop->p_mach_desc; 104 mlpsmp->psm_swp = swp; 105 106 swp->psw_infop = infop; 107 108 return (mlinkp); 109 } 110 111 static void 112 psm_modlinkage_free(struct modlinkage *mlinkp) 113 { 114 if (!mlinkp) 115 return; 116 117 (void) kmem_free(mlinkp, (sizeof (struct modlinkage) + 118 sizeof (struct modlpsm) + sizeof (struct psm_sw))); 119 } 120 121 int 122 psm_mod_init(void **handlepp, struct psm_info *infop) 123 { 124 struct modlinkage **modlpp = (struct modlinkage **)handlepp; 125 int status; 126 struct modlinkage *mlinkp; 127 128 if (!*modlpp) { 129 mlinkp = psm_modlinkage_alloc(infop); 130 if (!mlinkp) 131 return (ENOSPC); 132 } else 133 mlinkp = *modlpp; 134 135 status = mod_install(mlinkp); 136 if (status) { 137 psm_modlinkage_free(mlinkp); 138 *modlpp = NULL; 139 } else 140 *modlpp = mlinkp; 141 142 return (status); 143 } 144 145 /*ARGSUSED1*/ 146 int 147 psm_mod_fini(void **handlepp, struct psm_info *infop) 148 { 149 struct modlinkage **modlpp = (struct modlinkage **)handlepp; 150 int status; 151 152 status = mod_remove(*modlpp); 153 if (status == 0) { 154 psm_modlinkage_free(*modlpp); 155 *modlpp = NULL; 156 } 157 return (status); 158 } 159 160 int 161 psm_mod_info(void **handlepp, struct psm_info *infop, struct modinfo *modinfop) 162 { 163 struct modlinkage **modlpp = (struct modlinkage **)handlepp; 164 int status; 165 struct modlinkage *mlinkp; 166 167 if (!*modlpp) { 168 mlinkp = psm_modlinkage_alloc(infop); 169 if (!mlinkp) 170 return (0); 171 } else 172 mlinkp = *modlpp; 173 174 status = mod_info(mlinkp, modinfop); 175 176 if (!status) { 177 psm_modlinkage_free(mlinkp); 178 *modlpp = NULL; 179 } else 180 *modlpp = mlinkp; 181 182 return (status); 183 } 184 185 int 186 psm_add_intr(int lvl, avfunc xxintr, char *name, int vect, caddr_t arg) 187 { 188 return (add_avintr((void *)NULL, lvl, xxintr, name, vect, 189 arg, NULL, NULL, NULL)); 190 } 191 192 int 193 psm_add_nmintr(int lvl, avfunc xxintr, char *name, caddr_t arg) 194 { 195 return (add_nmintr(lvl, xxintr, name, arg)); 196 } 197 198 processorid_t 199 psm_get_cpu_id(void) 200 { 201 return (CPU->cpu_id); 202 } 203 204 caddr_t 205 psm_map_phys_new(paddr_t addr, size_t len, int prot) 206 { 207 uint_t pgoffset; 208 paddr_t base; 209 pgcnt_t npages; 210 caddr_t cvaddr; 211 212 if (len == 0) 213 return (0); 214 215 pgoffset = addr & MMU_PAGEOFFSET; 216 #ifdef __xpv 217 /* 218 * If we're dom0, we're starting from a MA. translate that to a PA 219 * XXPV - what about driver domains??? 220 */ 221 if (DOMAIN_IS_INITDOMAIN(xen_info)) { 222 base = pfn_to_pa(xen_assign_pfn(mmu_btop(addr))) | 223 (addr & MMU_PAGEOFFSET); 224 } else { 225 base = addr; 226 } 227 #else 228 base = addr; 229 #endif 230 npages = mmu_btopr(len + pgoffset); 231 cvaddr = device_arena_alloc(ptob(npages), VM_NOSLEEP); 232 if (cvaddr == NULL) 233 return (0); 234 hat_devload(kas.a_hat, cvaddr, mmu_ptob(npages), mmu_btop(base), 235 prot, HAT_LOAD_LOCK); 236 return (cvaddr + pgoffset); 237 } 238 239 void 240 psm_unmap_phys(caddr_t addr, size_t len) 241 { 242 uint_t pgoffset; 243 caddr_t base; 244 pgcnt_t npages; 245 246 if (len == 0) 247 return; 248 249 pgoffset = (uintptr_t)addr & MMU_PAGEOFFSET; 250 base = addr - pgoffset; 251 npages = mmu_btopr(len + pgoffset); 252 hat_unload(kas.a_hat, base, ptob(npages), HAT_UNLOAD_UNLOCK); 253 device_arena_free(base, ptob(npages)); 254 } 255 256 caddr_t 257 psm_map_new(paddr_t addr, size_t len, int prot) 258 { 259 int phys_prot = PROT_READ; 260 261 ASSERT(prot == (prot & (PSM_PROT_WRITE | PSM_PROT_READ))); 262 if (prot & PSM_PROT_WRITE) 263 phys_prot |= PROT_WRITE; 264 265 return (psm_map_phys(addr, len, phys_prot)); 266 } 267 268 #undef psm_map_phys 269 #undef psm_map 270 271 caddr_t 272 psm_map_phys(uint32_t addr, size_t len, int prot) 273 { 274 return (psm_map_phys_new((paddr_t)(addr & 0xffffffff), len, prot)); 275 } 276 277 caddr_t 278 psm_map(uint32_t addr, size_t len, int prot) 279 { 280 return (psm_map_new((paddr_t)(addr & 0xffffffff), len, prot)); 281 } 282 283 void 284 psm_unmap(caddr_t addr, size_t len) 285 { 286 uint_t pgoffset; 287 caddr_t base; 288 pgcnt_t npages; 289 290 if (len == 0) 291 return; 292 293 pgoffset = (uintptr_t)addr & MMU_PAGEOFFSET; 294 base = addr - pgoffset; 295 npages = mmu_btopr(len + pgoffset); 296 hat_unload(kas.a_hat, base, ptob(npages), HAT_UNLOAD_UNLOCK); 297 device_arena_free(base, ptob(npages)); 298 } 299 300 /*ARGSUSED1*/ 301 static int 302 mod_installpsm(struct modlpsm *modl, struct modlinkage *modlp) 303 { 304 struct psm_sw *swp; 305 306 swp = modl->psm_swp; 307 mutex_enter(&psmsw_lock); 308 psmsw->psw_back->psw_forw = swp; 309 swp->psw_back = psmsw->psw_back; 310 swp->psw_forw = psmsw; 311 psmsw->psw_back = swp; 312 swp->psw_flag |= PSM_MOD_INSTALL; 313 mutex_exit(&psmsw_lock); 314 return (0); 315 } 316 317 /*ARGSUSED1*/ 318 static int 319 mod_removepsm(struct modlpsm *modl, struct modlinkage *modlp) 320 { 321 struct psm_sw *swp; 322 323 swp = modl->psm_swp; 324 mutex_enter(&psmsw_lock); 325 if (swp->psw_flag & PSM_MOD_IDENTIFY) { 326 mutex_exit(&psmsw_lock); 327 return (EBUSY); 328 } 329 if (!(swp->psw_flag & PSM_MOD_INSTALL)) { 330 mutex_exit(&psmsw_lock); 331 return (0); 332 } 333 334 swp->psw_back->psw_forw = swp->psw_forw; 335 swp->psw_forw->psw_back = swp->psw_back; 336 mutex_exit(&psmsw_lock); 337 return (0); 338 } 339 340 /*ARGSUSED1*/ 341 static int 342 mod_infopsm(struct modlpsm *modl, struct modlinkage *modlp, int *p0) 343 { 344 *p0 = (int)modl->psm_swp->psw_infop->p_owner; 345 return (0); 346 } 347 348 #if defined(__xpv) 349 #define DEFAULT_PSM_MODULE "xpv_uppc" 350 #else 351 #define DEFAULT_PSM_MODULE "uppc" 352 #endif 353 354 static char * 355 psm_get_impl_module(int first) 356 { 357 static char **pnamep; 358 static char *psm_impl_module_list[] = { 359 DEFAULT_PSM_MODULE, 360 (char *)0 361 }; 362 static void *mhdl = NULL; 363 static char machname[MAXNAMELEN]; 364 365 if (first) 366 pnamep = psm_impl_module_list; 367 368 if (*pnamep != (char *)0) 369 return (*pnamep++); 370 371 mhdl = get_next_mach(mhdl, machname); 372 if (mhdl) 373 return (machname); 374 return ((char *)0); 375 } 376 377 void 378 psm_modload(void) 379 { 380 char *this; 381 382 mutex_init(&psmsw_lock, NULL, MUTEX_DEFAULT, NULL); 383 open_mach_list(); 384 385 for (this = psm_get_impl_module(1); this != (char *)NULL; 386 this = psm_get_impl_module(0)) { 387 if (modload("mach", this) == -1) 388 cmn_err(CE_CONT, "!Skipping psm: %s\n", this); 389 } 390 close_mach_list(); 391 } 392 393 #if defined(__xpv) 394 #define NOTSUP_MSG "This version of Solaris xVM does not support this hardware" 395 #else 396 #define NOTSUP_MSG "This version of Solaris does not support this hardware" 397 #endif /* __xpv */ 398 399 void 400 psm_install(void) 401 { 402 struct psm_sw *swp, *cswp; 403 struct psm_ops *opsp; 404 char machstring[15]; 405 int err, psmcnt = 0; 406 407 mutex_enter(&psmsw_lock); 408 for (swp = psmsw->psw_forw; swp != psmsw; ) { 409 opsp = swp->psw_infop->p_ops; 410 if (opsp->psm_probe) { 411 if ((*opsp->psm_probe)() == PSM_SUCCESS) { 412 psmcnt++; 413 swp->psw_flag |= PSM_MOD_IDENTIFY; 414 swp = swp->psw_forw; 415 continue; 416 } 417 } 418 /* remove the unsuccessful psm modules */ 419 cswp = swp; 420 swp = swp->psw_forw; 421 422 mutex_exit(&psmsw_lock); 423 (void) strcpy(&machstring[0], cswp->psw_infop->p_mach_idstring); 424 err = mod_remove_by_name(cswp->psw_infop->p_mach_idstring); 425 if (err) 426 cmn_err(CE_WARN, "!%s: mod_remove_by_name failed %d", 427 &machstring[0], err); 428 mutex_enter(&psmsw_lock); 429 } 430 mutex_exit(&psmsw_lock); 431 if (psmcnt == 0) 432 halt(NOTSUP_MSG); 433 (*psminitf)(); 434 } 435 436 /* 437 * Return 1 if kernel debugger is present, and 0 if not. 438 */ 439 int 440 psm_debugger(void) 441 { 442 return ((boothowto & RB_DEBUG) != 0); 443 } 444