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