mp_implfuncs.c revision 5f890c5ea34c9c865176a1fbac1b73191b061c1c
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/vmem.h>
#include <vm/hat.h>
#include <sys/modctl.h>
#include <vm/seg_kmem.h>
#define PSMI_1_6
#include <sys/psm.h>
#include <sys/psm_modctl.h>
#include <sys/smp_impldefs.h>
#include <sys/reboot.h>
#if defined(__xpv)
#include <sys/hypervisor.h>
#include <vm/kboot_mmu.h>
#include <vm/hat_pte.h>
#endif
/*
* External reference functions
*/
extern void *get_next_mach(void *, char *);
extern void close_mach_list(void);
extern void open_mach_list(void);
/*
* from startup.c - kernel VA range allocator for device mappings
*/
extern void *device_arena_alloc(size_t size, int vm_flag);
extern void device_arena_free(void * vaddr, size_t size);
void psm_modloadonly(void);
void psm_install(void);
/*
* Local Function Prototypes
*/
static struct modlinkage *psm_modlinkage_alloc(struct psm_info *infop);
static void psm_modlinkage_free(struct modlinkage *mlinkp);
static char *psm_get_impl_module(int first);
static int mod_installpsm(struct modlpsm *modl, struct modlinkage *modlp);
static int mod_removepsm(struct modlpsm *modl, struct modlinkage *modlp);
static int mod_infopsm(struct modlpsm *modl, struct modlinkage *modlp, int *p0);
struct mod_ops mod_psmops = {
mod_installpsm, mod_removepsm, mod_infopsm
};
static struct psm_sw psm_swtab = {
&psm_swtab, &psm_swtab, NULL, NULL
};
kmutex_t psmsw_lock; /* lock accesses to psmsw */
struct psm_sw *psmsw = &psm_swtab; /* start of all psm_sw */
static struct modlinkage *
psm_modlinkage_alloc(struct psm_info *infop)
{
int memsz;
struct modlinkage *mlinkp;
struct modlpsm *mlpsmp;
struct psm_sw *swp;
memsz = sizeof (struct modlinkage) + sizeof (struct modlpsm) +
sizeof (struct psm_sw);
mlinkp = (struct modlinkage *)kmem_zalloc(memsz, KM_NOSLEEP);
if (!mlinkp) {
cmn_err(CE_WARN, "!psm_mod_init: Cannot install %s",
infop->p_mach_idstring);
return (NULL);
}
mlpsmp = (struct modlpsm *)(mlinkp + 1);
swp = (struct psm_sw *)(mlpsmp + 1);
mlinkp->ml_rev = MODREV_1;
mlinkp->ml_linkage[0] = (void *)mlpsmp;
mlinkp->ml_linkage[1] = (void *)NULL;
mlpsmp->psm_modops = &mod_psmops;
mlpsmp->psm_linkinfo = infop->p_mach_desc;
mlpsmp->psm_swp = swp;
swp->psw_infop = infop;
return (mlinkp);
}
static void
psm_modlinkage_free(struct modlinkage *mlinkp)
{
if (!mlinkp)
return;
(void) kmem_free(mlinkp, (sizeof (struct modlinkage) +
sizeof (struct modlpsm) + sizeof (struct psm_sw)));
}
int
psm_mod_init(void **handlepp, struct psm_info *infop)
{
struct modlinkage **modlpp = (struct modlinkage **)handlepp;
int status;
struct modlinkage *mlinkp;
if (!*modlpp) {
mlinkp = psm_modlinkage_alloc(infop);
if (!mlinkp)
return (ENOSPC);
} else
mlinkp = *modlpp;
status = mod_install(mlinkp);
if (status) {
psm_modlinkage_free(mlinkp);
*modlpp = NULL;
} else
*modlpp = mlinkp;
return (status);
}
/*ARGSUSED1*/
int
psm_mod_fini(void **handlepp, struct psm_info *infop)
{
struct modlinkage **modlpp = (struct modlinkage **)handlepp;
int status;
status = mod_remove(*modlpp);
if (status == 0) {
psm_modlinkage_free(*modlpp);
*modlpp = NULL;
}
return (status);
}
int
psm_mod_info(void **handlepp, struct psm_info *infop, struct modinfo *modinfop)
{
struct modlinkage **modlpp = (struct modlinkage **)handlepp;
int status;
struct modlinkage *mlinkp;
if (!*modlpp) {
mlinkp = psm_modlinkage_alloc(infop);
if (!mlinkp)
return ((int)NULL);
} else
mlinkp = *modlpp;
status = mod_info(mlinkp, modinfop);
if (!status) {
psm_modlinkage_free(mlinkp);
*modlpp = NULL;
} else
*modlpp = mlinkp;
return (status);
}
int
psm_add_intr(int lvl, avfunc xxintr, char *name, int vect, caddr_t arg)
{
return (add_avintr((void *)NULL, lvl, xxintr, name, vect,
arg, NULL, NULL, NULL));
}
int
psm_add_nmintr(int lvl, avfunc xxintr, char *name, caddr_t arg)
{
return (add_nmintr(lvl, xxintr, name, arg));
}
processorid_t
psm_get_cpu_id(void)
{
return (CPU->cpu_id);
}
caddr_t
psm_map_phys_new(paddr_t addr, size_t len, int prot)
{
uint_t pgoffset;
paddr_t base;
pgcnt_t npages;
caddr_t cvaddr;
if (len == 0)
return (0);
pgoffset = addr & MMU_PAGEOFFSET;
#ifdef __xpv
/*
* If we're dom0, we're starting from a MA. translate that to a PA
* XXPV - what about driver domains???
*/
if (DOMAIN_IS_INITDOMAIN(xen_info)) {
base = pfn_to_pa(xen_assign_pfn(mmu_btop(addr))) |
(addr & MMU_PAGEOFFSET);
} else {
base = addr;
}
#else
base = addr;
#endif
npages = mmu_btopr(len + pgoffset);
cvaddr = device_arena_alloc(ptob(npages), VM_NOSLEEP);
if (cvaddr == NULL)
return (0);
hat_devload(kas.a_hat, cvaddr, mmu_ptob(npages), mmu_btop(base),
prot, HAT_LOAD_LOCK);
return (cvaddr + pgoffset);
}
void
psm_unmap_phys(caddr_t addr, size_t len)
{
uint_t pgoffset;
caddr_t base;
pgcnt_t npages;
if (len == 0)
return;
pgoffset = (uintptr_t)addr & MMU_PAGEOFFSET;
base = addr - pgoffset;
npages = mmu_btopr(len + pgoffset);
hat_unload(kas.a_hat, base, ptob(npages), HAT_UNLOAD_UNLOCK);
device_arena_free(base, ptob(npages));
}
caddr_t
psm_map_new(paddr_t addr, size_t len, int prot)
{
int phys_prot = PROT_READ;
ASSERT(prot == (prot & (PSM_PROT_WRITE | PSM_PROT_READ)));
if (prot & PSM_PROT_WRITE)
phys_prot |= PROT_WRITE;
return (psm_map_phys(addr, len, phys_prot));
}
#undef psm_map_phys
#undef psm_map
caddr_t
psm_map_phys(uint32_t addr, size_t len, int prot)
{
return (psm_map_phys_new((paddr_t)(addr & 0xffffffff), len, prot));
}
caddr_t
psm_map(uint32_t addr, size_t len, int prot)
{
return (psm_map_new((paddr_t)(addr & 0xffffffff), len, prot));
}
void
psm_unmap(caddr_t addr, size_t len)
{
uint_t pgoffset;
caddr_t base;
pgcnt_t npages;
if (len == 0)
return;
pgoffset = (uintptr_t)addr & MMU_PAGEOFFSET;
base = addr - pgoffset;
npages = mmu_btopr(len + pgoffset);
hat_unload(kas.a_hat, base, ptob(npages), HAT_UNLOAD_UNLOCK);
device_arena_free(base, ptob(npages));
}
/*ARGSUSED1*/
static int
mod_installpsm(struct modlpsm *modl, struct modlinkage *modlp)
{
struct psm_sw *swp;
swp = modl->psm_swp;
mutex_enter(&psmsw_lock);
psmsw->psw_back->psw_forw = swp;
swp->psw_back = psmsw->psw_back;
swp->psw_forw = psmsw;
psmsw->psw_back = swp;
swp->psw_flag |= PSM_MOD_INSTALL;
mutex_exit(&psmsw_lock);
return (0);
}
/*ARGSUSED1*/
static int
mod_removepsm(struct modlpsm *modl, struct modlinkage *modlp)
{
struct psm_sw *swp;
swp = modl->psm_swp;
mutex_enter(&psmsw_lock);
if (swp->psw_flag & PSM_MOD_IDENTIFY) {
mutex_exit(&psmsw_lock);
return (EBUSY);
}
if (!(swp->psw_flag & PSM_MOD_INSTALL)) {
mutex_exit(&psmsw_lock);
return (0);
}
swp->psw_back->psw_forw = swp->psw_forw;
swp->psw_forw->psw_back = swp->psw_back;
mutex_exit(&psmsw_lock);
return (0);
}
/*ARGSUSED1*/
static int
mod_infopsm(struct modlpsm *modl, struct modlinkage *modlp, int *p0)
{
*p0 = (int)modl->psm_swp->psw_infop->p_owner;
return (0);
}
#if defined(__xpv)
#define DEFAULT_PSM_MODULE "xpv_uppc"
#else
#define DEFAULT_PSM_MODULE "uppc"
#endif
static char *
psm_get_impl_module(int first)
{
static char **pnamep;
static char *psm_impl_module_list[] = {
DEFAULT_PSM_MODULE,
(char *)0
};
static void *mhdl = NULL;
static char machname[MAXNAMELEN];
if (first)
pnamep = psm_impl_module_list;
if (*pnamep != (char *)0)
return (*pnamep++);
mhdl = get_next_mach(mhdl, machname);
if (mhdl)
return (machname);
return ((char *)0);
}
void
psm_modload(void)
{
char *this;
mutex_init(&psmsw_lock, NULL, MUTEX_DEFAULT, NULL);
open_mach_list();
for (this = psm_get_impl_module(1); this != (char *)NULL;
this = psm_get_impl_module(0)) {
if (modload("mach", this) == -1)
cmn_err(CE_CONT, "!Skipping psm: %s\n", this);
}
close_mach_list();
}
#if defined(__xpv)
#define NOTSUP_MSG "This version of Solaris xVM does not support this hardware"
#else
#define NOTSUP_MSG "This version of Solaris does not support this hardware"
#endif /* __xpv */
void
psm_install(void)
{
struct psm_sw *swp, *cswp;
struct psm_ops *opsp;
char machstring[15];
int err, psmcnt = 0;
mutex_enter(&psmsw_lock);
for (swp = psmsw->psw_forw; swp != psmsw; ) {
opsp = swp->psw_infop->p_ops;
if (opsp->psm_probe) {
if ((*opsp->psm_probe)() == PSM_SUCCESS) {
psmcnt++;
swp->psw_flag |= PSM_MOD_IDENTIFY;
swp = swp->psw_forw;
continue;
}
}
/* remove the unsuccessful psm modules */
cswp = swp;
swp = swp->psw_forw;
mutex_exit(&psmsw_lock);
(void) strcpy(&machstring[0], cswp->psw_infop->p_mach_idstring);
err = mod_remove_by_name(cswp->psw_infop->p_mach_idstring);
if (err)
cmn_err(CE_WARN, "%s: mod_remove_by_name failed %d",
&machstring[0], err);
mutex_enter(&psmsw_lock);
}
mutex_exit(&psmsw_lock);
if (psmcnt == 0)
halt(NOTSUP_MSG);
(*psminitf)();
}
/*
* Return 1 if kernel debugger is present, and 0 if not.
*/
int
psm_debugger(void)
{
return ((boothowto & RB_DEBUG) != 0);
}