#include <sys/types.h>
#include <sys/stat.h>
#include <sys/cpuvar.h>
#include <sys/conf.h>
#include <sys/kmem.h>
#include <sys/async.h>
#include <sys/sysmacros.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/ddi_impldefs.h>
#include <sys/open.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/policy.h>
#include <sys/pci_tools.h>
#include <sys/pci_impl.h>
#include <sys/hypervisor_api.h>
#include <sys/hotplug/pci/pcihp.h>
#include "niumx_var.h"
static int niumx_open(dev_t *devp, int flags, int otyp, cred_t *credp);
static int niumx_close(dev_t dev, int flags, int otyp, cred_t *credp);
static int niumx_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
cred_t *credp, int *rvalp);
static int niumx_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
int flags, char *name, caddr_t valuep, int *lengthp);
struct cb_ops niumx_cb_ops = {
niumx_open,
niumx_close,
nodev,
nodev,
nodev,
nodev,
nodev,
niumx_ioctl,
nodev,
nodev,
nodev,
nochpoll,
niumx_prop_op,
NULL,
D_NEW | D_MP | D_HOTPLUG,
CB_REV,
nodev,
nodev
};
static void niumxtool_fill_in_intr_devs(pcitool_intr_dev_t *dev,
char *driver_name, char *path_name, int instance);
static int niumxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode);
int niumx_set_intr_target(niumx_devstate_t *niumxds_p, niudevino_t ino,
niucpuid_t cpu_id);
extern void *niumx_state;
static int
niumx_open(dev_t *devp, int flags, int otyp, cred_t *credp)
{
niumx_devstate_t *niumxds_p;
minor_t minor = getminor(*devp);
if (otyp != OTYP_CHR)
return (EINVAL);
niumxds_p = (niumx_devstate_t *)ddi_get_soft_state(niumx_state,
PCI_MINOR_NUM_TO_INSTANCE(minor));
if (niumxds_p == NULL)
return (ENXIO);
mutex_enter(&niumxds_p->niumx_mutex);
if (flags & FEXCL) {
if (niumxds_p->niumx_soft_state != NIUMX_SOFT_STATE_CLOSED) {
mutex_exit(&niumxds_p->niumx_mutex);
return (EBUSY);
}
niumxds_p->niumx_soft_state = NIUMX_SOFT_STATE_OPEN_EXCL;
} else {
if (niumxds_p->niumx_soft_state == NIUMX_SOFT_STATE_OPEN_EXCL) {
mutex_exit(&niumxds_p->niumx_mutex);
return (EBUSY);
}
niumxds_p->niumx_soft_state = NIUMX_SOFT_STATE_OPEN;
}
niumxds_p->niumx_open_count++;
mutex_exit(&niumxds_p->niumx_mutex);
return (0);
}
static int
niumx_close(dev_t dev, int flags, int otyp, cred_t *credp)
{
niumx_devstate_t *niumxds_p;
minor_t minor = getminor(dev);
if (otyp != OTYP_CHR)
return (EINVAL);
niumxds_p = (niumx_devstate_t *)ddi_get_soft_state(niumx_state,
PCI_MINOR_NUM_TO_INSTANCE(minor));
if (niumxds_p == NULL)
return (ENXIO);
mutex_enter(&niumxds_p->niumx_mutex);
niumxds_p->niumx_soft_state = NIUMX_SOFT_STATE_CLOSED;
niumxds_p->niumx_open_count = 0;
mutex_exit(&niumxds_p->niumx_mutex);
return (0);
}
int
niumx_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
int *rvalp)
{
niumx_devstate_t *niumxds_p;
dev_info_t *dip;
int rv = DDI_SUCCESS;
int minor = getminor(dev);
niumxds_p = (niumx_devstate_t *)ddi_get_soft_state(niumx_state,
PCI_MINOR_NUM_TO_INSTANCE(minor));
if (niumxds_p == NULL) {
return (ENXIO);
}
dip = niumxds_p->dip;
switch (minor & 0xff) {
case PCI_TOOL_INTR_MINOR_NUM:
switch (cmd) {
case PCITOOL_DEVICE_SET_INTR:
if (secpolicy_kmdb(credp)) {
rv = EPERM;
break;
}
case PCITOOL_DEVICE_GET_INTR:
case PCITOOL_SYSTEM_INTR_INFO:
rv = niumxtool_intr(dip, (void *)arg, cmd, mode);
break;
default:
rv = ENOTTY;
}
return (rv);
default:
break;
}
return (rv);
}
static int niumx_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
int flags, char *name, caddr_t valuep, int *lengthp)
{
return (ddi_prop_op(dev, dip, prop_op, flags, name, valuep, lengthp));
}
int
niumxtool_init(dev_info_t *dip)
{
int instance = ddi_get_instance(dip);
if (ddi_create_minor_node(dip, PCI_MINOR_INTR, S_IFCHR,
PCI_MINOR_NUM(instance, PCI_TOOL_INTR_MINOR_NUM),
DDI_NT_INTRCTL, 0) != DDI_SUCCESS) {
ddi_remove_minor_node(dip, PCI_MINOR_REG);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
void
niumxtool_uninit(dev_info_t *dip)
{
ddi_remove_minor_node(dip, PCI_MINOR_INTR);
}
static void
niumxtool_fill_in_intr_devs(pcitool_intr_dev_t *dev, char *driver_name,
char *path_name, int instance)
{
(void) strlcpy(dev->driver_name, driver_name, MAXMODCONFNAME);
(void) strlcpy(dev->path, path_name, MAXPATHLEN);
dev->dev_inst = instance;
}
static int
niumxtool_intr_info(dev_info_t *dip, void *arg, int mode)
{
pcitool_intr_info_t intr_info;
int rval = DDI_SUCCESS;
if (ddi_copyin(arg, &intr_info, sizeof (pcitool_intr_info_t), mode) !=
DDI_SUCCESS) {
return (EFAULT);
}
intr_info.ctlr_version = 0;
intr_info.ctlr_type = PCITOOL_CTLR_TYPE_RISC;
if (intr_info.flags & PCITOOL_INTR_FLAG_GET_MSI)
intr_info.num_intr = 0;
else
intr_info.num_intr = NIUMX_MAX_INTRS;
intr_info.drvr_version = PCITOOL_VERSION;
if (ddi_copyout(&intr_info, arg, sizeof (pcitool_intr_info_t), mode) !=
DDI_SUCCESS) {
rval = EFAULT;
}
return (rval);
}
static int
niumxtool_get_intr(dev_info_t *dip, void *arg, int mode)
{
pcitool_intr_get_t partial_iget;
pcitool_intr_get_t *iget_p = &partial_iget;
int copyout_rval;
niusysino_t sysino;
niucpuid_t cpu_id;
niumx_devstate_t *niumxds_p;
dev_info_t *ih_dip;
size_t iget_kmem_alloc_size = 0;
char pathname[MAXPATHLEN];
int rval = EIO;
niumxds_p = (niumx_devstate_t *)
ddi_get_soft_state(niumx_state, ddi_get_instance(dip));
if (ddi_copyin(arg, &partial_iget, PCITOOL_IGET_SIZE(0), mode) !=
DDI_SUCCESS)
return (EFAULT);
iget_p->status = PCITOOL_IO_ERROR;
iget_p->msi = (uint32_t)-1;
if (iget_p->flags & PCITOOL_INTR_FLAG_GET_MSI) {
iget_p->status = PCITOOL_INVALID_MSI;
rval = EINVAL;
goto done_get_intr;
}
if (iget_p->ino > NIUMX_MAX_INTRS) {
iget_p->status = PCITOOL_INVALID_INO;
rval = EINVAL;
goto done_get_intr;
}
if (iget_p->num_devs_ret > 0) {
iget_kmem_alloc_size = PCITOOL_IGET_SIZE(iget_p->num_devs_ret);
iget_p = kmem_zalloc(iget_kmem_alloc_size, KM_SLEEP);
if (ddi_copyin(arg, iget_p, iget_kmem_alloc_size, mode) !=
DDI_SUCCESS) {
kmem_free(iget_p, iget_kmem_alloc_size);
return (EFAULT);
}
}
sysino = niumxds_p->niumx_ihtable[iget_p->ino].ih_sysino;
if (sysino == 0) {
iget_p->status = PCITOOL_IO_ERROR;
rval = EIO;
goto done_get_intr;
}
ih_dip = niumxds_p->niumx_ihtable[iget_p->ino].ih_dip;
ddi_pathname(ih_dip, pathname);
niumxtool_fill_in_intr_devs(&iget_p->dev[0],
(char *)ddi_driver_name(ih_dip), pathname,
ddi_get_instance(ih_dip));
if (hvio_intr_gettarget(sysino, &cpu_id) != H_EOK) {
iget_p->status = PCITOOL_IO_ERROR;
rval = EIO;
goto done_get_intr;
}
if (niumxds_p->niumx_ihtable[iget_p->ino].ih_cpuid != cpu_id) {
cmn_err(CE_WARN, "CPU Does not match %x %x", cpu_id,
niumxds_p->niumx_ihtable[iget_p->ino].ih_cpuid);
iget_p->status = PCITOOL_IO_ERROR;
rval = EIO;
goto done_get_intr;
}
iget_p->num_devs = 1;
iget_p->cpu_id = niumxds_p->niumx_ihtable[iget_p->ino].ih_cpuid;
iget_p->status = PCITOOL_SUCCESS;
rval = DDI_SUCCESS;
done_get_intr:
iget_p->drvr_version = PCITOOL_VERSION;
copyout_rval =
ddi_copyout(iget_p, arg, PCITOOL_IGET_SIZE(iget_p->num_devs_ret),
mode);
if (iget_kmem_alloc_size > 0)
kmem_free(iget_p, iget_kmem_alloc_size);
if (copyout_rval != DDI_SUCCESS)
rval = EFAULT;
return (rval);
}
static int
niumxtool_set_intr(dev_info_t *dip, void *arg, int mode)
{
pcitool_intr_set_t iset;
niucpuid_t old_cpu_id;
int rval = EIO;
int ret = DDI_SUCCESS;
size_t copyinout_size;
niumx_devstate_t *niumxds_p;
niumxds_p = (niumx_devstate_t *)
ddi_get_soft_state(niumx_state, ddi_get_instance(dip));
bzero(&iset, sizeof (pcitool_intr_set_t));
copyinout_size = (size_t)&iset.flags - (size_t)&iset;
if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
return (EFAULT);
switch (iset.user_version) {
case PCITOOL_V1:
break;
case PCITOOL_V2:
copyinout_size = sizeof (pcitool_intr_set_t);
if (ddi_copyin(arg, &iset, copyinout_size, mode) != DDI_SUCCESS)
return (EFAULT);
break;
default:
iset.status = PCITOOL_OUT_OF_RANGE;
rval = ENOTSUP;
goto done_set_intr;
}
if (iset.flags & PCITOOL_INTR_FLAG_SET_GROUP) {
iset.status = PCITOOL_IO_ERROR;
rval = ENOTSUP;
goto done_set_intr;
}
iset.status = PCITOOL_IO_ERROR;
iset.msi = (uint32_t)-1;
if (iset.ino > NIUMX_MAX_INTRS) {
iset.status = PCITOOL_INVALID_INO;
rval = EINVAL;
goto done_set_intr;
}
old_cpu_id = niumxds_p->niumx_ihtable[iset.ino].ih_cpuid;
if ((ret = niumx_set_intr_target(niumxds_p, iset.ino,
iset.cpu_id)) == DDI_SUCCESS) {
iset.cpu_id = old_cpu_id;
iset.status = PCITOOL_SUCCESS;
rval = DDI_SUCCESS;
goto done_set_intr;
}
switch (ret) {
case DDI_EPENDING:
iset.status = PCITOOL_PENDING_INTRTIMEOUT;
rval = ETIME;
break;
case DDI_EINVAL:
iset.status = PCITOOL_INVALID_CPUID;
rval = EINVAL;
break;
default:
iset.status = PCITOOL_IO_ERROR;
rval = EIO;
break;
}
done_set_intr:
iset.drvr_version = PCITOOL_VERSION;
if (ddi_copyout(&iset, arg, copyinout_size, mode) != DDI_SUCCESS)
rval = EFAULT;
return (rval);
}
static int
niumxtool_intr(dev_info_t *dip, void *arg, int cmd, int mode)
{
int rval = DDI_SUCCESS;
switch (cmd) {
case PCITOOL_SYSTEM_INTR_INFO:
rval = niumxtool_intr_info(dip, arg, mode);
break;
case PCITOOL_DEVICE_GET_INTR:
rval = niumxtool_get_intr(dip, arg, mode);
break;
case PCITOOL_DEVICE_SET_INTR:
rval = niumxtool_set_intr(dip, arg, mode);
break;
default:
rval = ENOTTY;
}
return (rval);
}