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