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