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 *
psm_modlinkage_alloc(struct psm_info * infop)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
psm_modlinkage_free(struct modlinkage * mlinkp)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
psm_mod_init(void ** handlepp,struct psm_info * infop)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
psm_mod_fini(void ** handlepp,struct psm_info * infop)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
psm_mod_info(void ** handlepp,struct psm_info * infop,struct modinfo * modinfop)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
psm_add_intr(int lvl,avfunc xxintr,char * name,int vect,caddr_t arg)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
psm_add_nmintr(int lvl,avfunc xxintr,char * name,caddr_t arg)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
psm_get_cpu_id(void)201 psm_get_cpu_id(void)
202 {
203 return (CPU->cpu_id);
204 }
205
206 caddr_t
psm_map_phys_new(paddr_t addr,size_t len,int prot)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
psm_unmap_phys(caddr_t addr,size_t len)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
psm_map_new(paddr_t addr,size_t len,int prot)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
psm_map_phys(uint32_t addr,size_t len,int prot)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
psm_map(uint32_t addr,size_t len,int prot)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
psm_unmap(caddr_t addr,size_t len)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
mod_installpsm(struct modlpsm * modl,struct modlinkage * modlp)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
mod_removepsm(struct modlpsm * modl,struct modlinkage * modlp)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
mod_infopsm(struct modlpsm * modl,struct modlinkage * modlp,int * p0)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 *
psm_get_impl_module(int first)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
psm_modload(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
psm_install(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
psm_debugger(void)441 psm_debugger(void)
442 {
443 return ((boothowto & RB_DEBUG) != 0);
444 }
445