Home | History | Annotate | Download | only in os
      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 
     26 #include <sys/vmem.h>
     27 #include <vm/hat.h>
     28 #include <sys/modctl.h>
     29 #include <vm/seg_kmem.h>
     30 #define	PSMI_1_6
     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, NULL
     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 ((int)NULL);
    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