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 }