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 }