xref: /illumos-gate/usr/src/uts/i86pc/os/mp_implfuncs.c (revision 797f979d1fe26bfb1cdeb3e7a86ed24c0b654200)
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, 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