From: Ken Cox Date: Tue, 4 Mar 2014 13:58:09 +0000 (-0600) Subject: staging: visoruislib driver used to handle requests from virtpci X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=bac8a4d5d29b1475d06f319d87e1d608c0b9dbe8;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git staging: visoruislib driver used to handle requests from virtpci The visoruislib module is a support library, used to handle requests from virtpci. Signed-off-by: Ken Cox Cc: Ben Romer Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/staging/unisys/Kconfig b/drivers/staging/unisys/Kconfig index c9a10bdf73cf..0b1e0c52efbb 100644 --- a/drivers/staging/unisys/Kconfig +++ b/drivers/staging/unisys/Kconfig @@ -13,5 +13,6 @@ source "drivers/staging/unisys/visorutil/Kconfig" source "drivers/staging/unisys/visorchannel/Kconfig" source "drivers/staging/unisys/visorchipset/Kconfig" source "drivers/staging/unisys/channels/Kconfig" +source "drivers/staging/unisys/uislib/Kconfig" endif # UNISYSSPAR diff --git a/drivers/staging/unisys/Makefile b/drivers/staging/unisys/Makefile index e637f30cf21a..f9174f580ac3 100644 --- a/drivers/staging/unisys/Makefile +++ b/drivers/staging/unisys/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_UNISYS_VISORUTIL) += visorutil/ obj-$(CONFIG_UNISYS_VISORCHANNEL) += visorchannel/ obj-$(CONFIG_UNISYS_VISORCHIPSET) += visorchipset/ obj-$(CONFIG_UNISYS_CHANNELSTUB) += channels/ - +obj-$(CONFIG_UNISYS_UISLIB) += uislib/ diff --git a/drivers/staging/unisys/include/sparstop.h b/drivers/staging/unisys/include/sparstop.h new file mode 100644 index 000000000000..3603ac607643 --- /dev/null +++ b/drivers/staging/unisys/include/sparstop.h @@ -0,0 +1,30 @@ +/* sparstop.h + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __SPARSTOP_H__ +#define __SPARSTOP_H__ + +#include "timskmod.h" +#include "version.h" +#include + +typedef void (*SPARSTOP_COMPLETE_FUNC) (void *context, int status); + +int sp_stop(void *context, SPARSTOP_COMPLETE_FUNC get_complete_func); +void test_remove_stop_device(void); + +#endif diff --git a/drivers/staging/unisys/uislib/Kconfig b/drivers/staging/unisys/uislib/Kconfig new file mode 100644 index 000000000000..8d87d9c81c66 --- /dev/null +++ b/drivers/staging/unisys/uislib/Kconfig @@ -0,0 +1,10 @@ +# +# Unisys uislib configuration +# + +config UNISYS_UISLIB + tristate "Unisys uislib driver" + depends on UNISYSSPAR && UNISYS_VISORCHIPSET && UNISYS_CHANNELSTUB + ---help--- + If you say Y here, you will enable the Unisys uislib driver. + diff --git a/drivers/staging/unisys/uislib/Makefile b/drivers/staging/unisys/uislib/Makefile new file mode 100644 index 000000000000..6e44d49458f5 --- /dev/null +++ b/drivers/staging/unisys/uislib/Makefile @@ -0,0 +1,17 @@ +# +# Makefile for Unisys uislib +# + +obj-$(CONFIG_UNISYS_UISLIB) += visoruislib.o + +visoruislib-y := uislib.o uisqueue.o uisthread.o uisutils.o + +ccflags-y += -Idrivers/staging/unisys/include +ccflags-y += -Idrivers/staging/unisys/channels +ccflags-y += -Idrivers/staging/unisys/visorchipset +ccflags-y += -Idrivers/staging/unisys/sparstopdriver +ccflags-y += -Idrivers/staging/unisys/common-spar/include +ccflags-y += -Idrivers/staging/unisys/common-spar/include/channels + +ccflags-y += -DCONFIG_SPAR_GUEST -DGUESTDRIVERBUILD -DNOAUTOVERSION + diff --git a/drivers/staging/unisys/uislib/uislib.c b/drivers/staging/unisys/uislib/uislib.c new file mode 100644 index 000000000000..a004c9ddc118 --- /dev/null +++ b/drivers/staging/unisys/uislib/uislib.c @@ -0,0 +1,2536 @@ +/* uislib.c + * + * Copyright � 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* @ALL_INSPECTED */ +#define EXPORT_SYMTAB +#include +#include +#ifdef CONFIG_MODVERSIONS +#include +#endif +#include + +#include "commontypes.h" + +#include +#include "uniklog.h" +#include "diagnostics/appos_subsystems.h" +#include "uisutils.h" +#include "vbuschannel.h" + +#include +#include /* for copy_from_user */ +#include /* for toupper */ +#include + +#include "sparstop.h" +#include "visorchipset.h" +#include "chanstub.h" +#include "version.h" +#include "guestlinuxdebug.h" + +#define SET_PROC_OWNER(x, y) + +#define UISLIB_TEST_PROC +#define POLLJIFFIES_NORMAL 1 +/* Choose whether or not you want to wakeup the request-polling thread + * after an IO termination: + * this is shorter than using __FILE__ (full path name) in + * debug/info/error messages + */ +#define CURRENT_FILE_PC UISLIB_PC_uislib_c +#define __MYFILE__ "uislib.c" + +/* global function pointers that act as callback functions into virtpcimod */ +int (*VirtControlChanFunc)(struct guest_msgs *); + +static int ProcReadBufferValid; +static char *ProcReadBuffer; /* Note this MUST be global, + * because the contents must */ +static unsigned int chipset_inited; +int callback_count = 0; +#define WAIT_ON_CALLBACK(handle) \ + do { \ + if (handle) \ + break; \ + UIS_THREAD_WAIT; \ + } while (1) + +static struct bus_info *BusListHead; +static rwlock_t BusListLock; +static int BusListCount; /* number of buses in the list */ +static int MaxBusCount; /* maximum number of buses expected */ +static U64 PhysicalDataChan; +static int PlatformNumber; + +/* This is a list of controlvm messages which could not complete + * immediately, but instead must be occasionally retried until they + * ultimately succeed/fail. When this happens, + * msg->hdr.Flags.responseExpected determines whether or not we will + * send a controlvm response. + */ +struct controlvm_retry_entry { + CONTROLVM_MESSAGE msg; + struct io_msgs cmd; + void *obj; + int (*controlChanFunc)(struct io_msgs *); + struct list_head list_link; +}; +LIST_HEAD(ControlVmRetryQHead); + +static struct uisthread_info Incoming_ThreadInfo; +static BOOL Incoming_Thread_Started = FALSE; +LIST_HEAD(List_Polling_Device_Channels); +unsigned long long tot_moved_to_tail_cnt = 0; +unsigned long long tot_wait_cnt = 0; +unsigned long long tot_wakeup_cnt = 0; +unsigned long long tot_schedule_cnt = 0; +int en_smart_wakeup = 1; +static DEFINE_SEMAPHORE(Lock_Polling_Device_Channels); /* unlocked */ +DECLARE_WAIT_QUEUE_HEAD(Wakeup_Polling_Device_Channels); +static int Go_Polling_Device_Channels; + +static struct proc_dir_entry *uislib_proc_dir; +static struct proc_dir_entry *uislib_proc_vbus_dir; +static struct proc_dir_entry *vnic_proc_entry; /* Used to be "datachan" */ +static struct proc_dir_entry *ctrlchan_proc_entry; +static struct proc_dir_entry *pmem_proc_entry; +static struct proc_dir_entry *info_proc_entry; +static struct proc_dir_entry *switch_proc_entry; +static struct proc_dir_entry *extport_proc_entry; +static struct proc_dir_entry *platformnumber_proc_entry; +static struct proc_dir_entry *bus_proc_entry; +static struct proc_dir_entry *dev_proc_entry; +static struct proc_dir_entry *chipset_proc_entry; +static struct proc_dir_entry *cycles_before_wait_proc_entry; +static struct proc_dir_entry *reset_counts_proc_entry; +static struct proc_dir_entry *smart_wakeup_proc_entry; +static struct proc_dir_entry *disable_proc_entry; + +#define DIR_PROC_ENTRY "uislib" +#define DIR_VBUS_PROC_ENTRY "vbus" +#define VNIC_PROC_ENTRY_FN "vnic" /* Used to be "datachan" */ +#define CTRLCHAN_PROC_ENTRY_FN "ctrlchan" +#define PMEM_PROC_ENTRY_FN "phys_to_virt" +#define INFO_PROC_ENTRY_FN "info" +#define SWITCH_PROC_ENTRY_FN "switch" +#define SWITCH_COUNT_PROC_ENTRY_FN "switch_count" +#define EXTPORT_PROC_ENTRY_FN "extport" +#define PLATFORMNUMBER_PROC_ENTRY_FN "platform" +#define BUS_PROC_ENTRY_FN "bus" +#define DEV_PROC_ENTRY_FN "device" +#define CHIPSET_PROC_ENTRY_FN "chipset" +#define CYCLES_BEFORE_WAIT_PROC_ENTRY_FN "cycles_before_wait" +#define RESET_COUNTS_PROC_ENTRY_FN "reset_counts" +#define SMART_WAKEUP_PROC_ENTRY_FN "smart_wakeup" +#define CALLHOME_PROC_ENTRY_FN "callhome" +#define CALLHOME_THROTTLED_PROC_ENTRY_FN "callhome_throttled" +#define DISABLE_PROC_ENTRY_FN "switch_state" +#ifdef UISLIB_TEST_PROC +static struct proc_dir_entry *test_proc_entry; +#define TEST_PROC_ENTRY_FN "test" +#endif +unsigned long long cycles_before_wait, wait_cycles; + +/*****************************************************/ +/* local functions */ +/*****************************************************/ + +static int proc_info_vbus_show(struct seq_file *m, void *v); +static int +proc_info_vbus_open(struct inode *inode, struct file *filp) +{ + /* proc_info_vbus_show will grab this from seq_file.private: */ + struct bus_info *bus = PDE_DATA(inode); + return single_open(filp, proc_info_vbus_show, bus); +} + +static const struct file_operations proc_info_vbus_fops = { + .open = proc_info_vbus_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static ssize_t uislib_proc_read_writeonly(struct file *file, + char __user *buffer, + size_t count, loff_t *ppos); + +static ssize_t vnic_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos); + +static const struct file_operations proc_vnic_fops = { + .read = uislib_proc_read_writeonly, + .write = vnic_proc_write, +}; + +static ssize_t chipset_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos); + +static const struct file_operations proc_chipset_fops = { + .read = uislib_proc_read_writeonly, + .write = chipset_proc_write, +}; + +static ssize_t info_proc_read(struct file *file, char __user *buf, + size_t len, loff_t *offset); +static const struct file_operations proc_info_fops = { + .read = info_proc_read, +}; + +static ssize_t platformnumber_proc_read(struct file *file, char __user *buf, + size_t len, loff_t *offset); +static const struct file_operations proc_platformnumber_fops = { + .read = platformnumber_proc_read, +}; + +static ssize_t cycles_before_wait_proc_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *ppos); +static const struct file_operations proc_cycles_before_wait_fops = { + .read = uislib_proc_read_writeonly, + .write = cycles_before_wait_proc_write, +}; + +static ssize_t reset_counts_proc_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *ppos); +static const struct file_operations proc_reset_counts_fops = { + .read = uislib_proc_read_writeonly, + .write = reset_counts_proc_write, +}; + +static ssize_t smart_wakeup_proc_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *ppos); +static const struct file_operations proc_smart_wakeup_fops = { + .read = uislib_proc_read_writeonly, + .write = smart_wakeup_proc_write, +}; + +static ssize_t test_proc_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *ppos); +static const struct file_operations proc_test_fops = { + .read = uislib_proc_read_writeonly, + .write = test_proc_write, +}; + +static ssize_t bus_proc_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *ppos); +static const struct file_operations proc_bus_fops = { + .read = uislib_proc_read_writeonly, + .write = bus_proc_write, +}; + +static ssize_t dev_proc_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *ppos); +static const struct file_operations proc_dev_fops = { + .read = uislib_proc_read_writeonly, + .write = dev_proc_write, +}; + +static void +init_msg_header(CONTROLVM_MESSAGE *msg, U32 id, uint rsp, uint svr) +{ + memset(msg, 0, sizeof(CONTROLVM_MESSAGE)); + msg->hdr.Id = id; + msg->hdr.Flags.responseExpected = rsp; + msg->hdr.Flags.server = svr; +} + +static void +create_bus_proc_entries(struct bus_info *bus) +{ + bus->proc_dir = proc_mkdir(bus->name, uislib_proc_vbus_dir); + if (!bus->proc_dir) { + LOGERR("failed to create /proc/uislib/vbus/%s directory", + bus->name); + return; + } + bus->proc_info = proc_create_data("info", 0, bus->proc_dir, + &proc_info_vbus_fops, bus); + if (!bus->proc_info) { + LOGERR("failed to create /proc/uislib/vbus/%s/info", bus->name); + remove_proc_entry(bus->name, uislib_proc_vbus_dir); + bus->proc_dir = NULL; + return; + } + SET_PROC_OWNER(bus->proc_info, THIS_MODULE); + +} + +static void * +init_vbus_channel(U64 channelAddr, U32 channelBytes, int isServer) +{ + void *rc = NULL; + void *pChan = uislib_ioremap_cache(channelAddr, channelBytes); + if (!pChan) { + LOGERR("CONTROLVM_BUS_CREATE error: ioremap_cache of channelAddr:%Lx for channelBytes:%llu failed", + (unsigned long long) channelAddr, + (unsigned long long) channelBytes); + RETPTR(NULL); + } + if (isServer) { + memset(pChan, 0, channelBytes); + if (!ULTRA_VBUS_CHANNEL_OK_SERVER(channelBytes, NULL)) { + ERRDRV("%s channel cannot be used", __func__); + uislib_iounmap(pChan); + RETPTR(NULL); + } + ULTRA_VBUS_init_channel(pChan, channelBytes); + } else { + if (!ULTRA_VBUS_CHANNEL_OK_CLIENT(pChan, NULL)) { + ERRDRV("%s channel cannot be used", __func__); + uislib_iounmap(pChan); + RETPTR(NULL); + } + } + RETPTR(pChan); +Away: + return rc; +} + +static int +create_bus(CONTROLVM_MESSAGE *msg, char *buf) +{ + U32 busNo, deviceCount; + struct bus_info *tmp, *bus; + size_t size; + + if (MaxBusCount == BusListCount) { + LOGERR("CONTROLVM_BUS_CREATE Failed: max buses:%d already created\n", + MaxBusCount); + POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, MaxBusCount, + POSTCODE_SEVERITY_ERR); + return CONTROLVM_RESP_ERROR_MAX_BUSES; + } + + busNo = msg->cmd.createBus.busNo; + deviceCount = msg->cmd.createBus.deviceCount; + + POSTCODE_LINUX_4(BUS_CREATE_ENTRY_PC, busNo, deviceCount, + POSTCODE_SEVERITY_INFO); + + size = + sizeof(struct bus_info) + + (deviceCount * sizeof(struct device_info *)); + bus = UISMALLOC(size, GFP_ATOMIC); + if (!bus) { + LOGERR("CONTROLVM_BUS_CREATE Failed: kmalloc for bus failed.\n"); + POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, busNo, + POSTCODE_SEVERITY_ERR); + return CONTROLVM_RESP_ERROR_KMALLOC_FAILED; + } + + memset(bus, 0, size); + + /* Currently by default, the bus Number is the GuestHandle. + * Configure Bus message can override this. + */ + if (msg->hdr.Flags.testMessage) { + /* This implies we're the IOVM so set guest handle to 0... */ + bus->guestHandle = 0; + bus->busNo = busNo; + bus->localVnic = 1; + } else + bus->busNo = bus->guestHandle = busNo; + sprintf(bus->name, "%d", (int) bus->busNo); + bus->deviceCount = deviceCount; + bus->device = + (struct device_info **) ((char *) bus + sizeof(struct bus_info)); + bus->busInstGuid = msg->cmd.createBus.busInstGuid; + bus->busChannelBytes = 0; + bus->pBusChannel = NULL; + + /* add bus to our bus list - but check for duplicates first */ + read_lock(&BusListLock); + for (tmp = BusListHead; tmp; tmp = tmp->next) { + if (tmp->busNo == bus->busNo) + break; + } + read_unlock(&BusListLock); + if (tmp) { + /* found a bus already in the list with same busNo - + * reject add + */ + LOGERR("CONTROLVM_BUS_CREATE Failed: bus %d already exists.\n", + bus->busNo); + POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->busNo, + POSTCODE_SEVERITY_ERR); + UISFREE(bus, size); + return CONTROLVM_RESP_ERROR_ALREADY_DONE; + } + if ((msg->cmd.createBus.channelAddr != 0) + && (msg->cmd.createBus.channelBytes != 0)) { + bus->busChannelBytes = msg->cmd.createBus.channelBytes; + bus->pBusChannel = + init_vbus_channel(msg->cmd.createBus.channelAddr, + msg->cmd.createBus.channelBytes, + msg->hdr.Flags.server); + } + /* the msg is bound for virtpci; send guest_msgs struct to callback */ + if (!msg->hdr.Flags.server) { + struct guest_msgs cmd; + cmd.msgtype = GUEST_ADD_VBUS; + cmd.add_vbus.busNo = busNo; + cmd.add_vbus.chanptr = bus->pBusChannel; + cmd.add_vbus.deviceCount = deviceCount; + cmd.add_vbus.busTypeGuid = msg->cmd.createBus.busDataTypeGuid; + cmd.add_vbus.busInstGuid = msg->cmd.createBus.busInstGuid; + if (!VirtControlChanFunc) { + UISFREE(bus, size); + LOGERR("CONTROLVM_BUS_CREATE Failed: virtpci callback not registered."); + POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->busNo, + POSTCODE_SEVERITY_ERR); + return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE; + } + if (!VirtControlChanFunc(&cmd)) { + UISFREE(bus, size); + LOGERR("CONTROLVM_BUS_CREATE Failed: virtpci GUEST_ADD_VBUS returned error."); + POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->busNo, + POSTCODE_SEVERITY_ERR); + return + CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR; + } + } + create_bus_proc_entries(bus); + + /* add bus at the head of our list */ + write_lock(&BusListLock); + if (!BusListHead) + BusListHead = bus; + else { + bus->next = BusListHead; + BusListHead = bus; + } + BusListCount++; + write_unlock(&BusListLock); + + POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, bus->busNo, + POSTCODE_SEVERITY_INFO); + return CONTROLVM_RESP_SUCCESS; +} + +static int +destroy_bus(CONTROLVM_MESSAGE *msg, char *buf) +{ + int i; + struct bus_info *bus, *prev = NULL; + U32 busNo; + + busNo = msg->cmd.destroyBus.busNo; + + /* find and delete the bus */ + read_lock(&BusListLock); + for (bus = BusListHead; bus; prev = bus, bus = bus->next) { + if (bus->busNo == busNo) { + /* found the bus - ensure that all device + * slots are NULL + */ + for (i = 0; i < bus->deviceCount; i++) { + if (bus->device[i] != NULL) { + LOGERR("CONTROLVM_BUS_DESTROY Failed: device %i attached to bus %d.", + i, busNo); + read_unlock(&BusListLock); + return CONTROLVM_RESP_ERROR_BUS_DEVICE_ATTACHED; + } + } + read_unlock(&BusListLock); + /* the msg is bound for virtpci; send + * guest_msgs struct to callback + */ + if (!msg->hdr.Flags.server) { + struct guest_msgs cmd; + cmd.msgtype = GUEST_DEL_VBUS; + cmd.del_vbus.busNo = busNo; + if (!VirtControlChanFunc) { + LOGERR("CONTROLVM_BUS_DESTROY Failed: virtpci callback not registered."); + return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE; + } + if (!VirtControlChanFunc(&cmd)) { + LOGERR("CONTROLVM_BUS_DESTROY Failed: virtpci GUEST_DEL_VBUS returned error."); + return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR; + } + } + /* remove the bus from the list */ + write_lock(&BusListLock); + if (prev) /* not at head */ + prev->next = bus->next; + else + BusListHead = bus->next; + BusListCount--; + write_unlock(&BusListLock); + break; + } + } + + if (!bus) { + LOGERR("CONTROLVM_BUS_DESTROY Failed: failed to find bus %d.\n", + busNo); + read_unlock(&BusListLock); + return CONTROLVM_RESP_ERROR_ALREADY_DONE; + } + if (bus->proc_info) { + remove_proc_entry("info", bus->proc_dir); + bus->proc_info = NULL; + } + if (bus->proc_dir) { + remove_proc_entry(bus->name, uislib_proc_vbus_dir); + bus->proc_dir = NULL; + } + if (bus->pBusChannel) { + uislib_iounmap(bus->pBusChannel); + bus->pBusChannel = NULL; + } + + UISFREE(bus, + sizeof(struct bus_info) + + (bus->deviceCount * sizeof(struct device_info *))); + return CONTROLVM_RESP_SUCCESS; +} + +static int +create_device(CONTROLVM_MESSAGE *msg, char *buf) +{ + struct device_info *dev; + struct bus_info *bus; + U32 busNo, devNo; + int result = CONTROLVM_RESP_SUCCESS; + U64 minSize = MIN_IO_CHANNEL_SIZE; + ReqHandlerInfo_t *pReqHandler; + + busNo = msg->cmd.createDevice.busNo; + devNo = msg->cmd.createDevice.devNo; + + POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, devNo, busNo, + POSTCODE_SEVERITY_INFO); + + dev = UISMALLOC(sizeof(struct device_info), GFP_ATOMIC); + if (!dev) { + LOGERR("CONTROLVM_DEVICE_CREATE Failed: kmalloc for dev failed.\n"); + POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo, + POSTCODE_SEVERITY_ERR); + return CONTROLVM_RESP_ERROR_KMALLOC_FAILED; + } + + memset(dev, 0, sizeof(struct device_info)); + dev->channelTypeGuid = msg->cmd.createDevice.dataTypeGuid; + dev->intr = msg->cmd.createDevice.intr; + dev->channelAddr = msg->cmd.createDevice.channelAddr; + dev->busNo = busNo; + dev->devNo = devNo; + sema_init(&dev->interrupt_callback_lock, 1); /* unlocked */ + sprintf(dev->devid, "vbus%u:dev%u", (unsigned) busNo, (unsigned) devNo); + /* map the channel memory for the device. */ + if (msg->hdr.Flags.testMessage) + dev->chanptr = __va(dev->channelAddr); + else { + pReqHandler = ReqHandlerFind(dev->channelTypeGuid); + if (pReqHandler) + /* generic service handler registered for this + * channel + */ + minSize = pReqHandler->min_channel_bytes; + if (minSize > msg->cmd.createDevice.channelBytes) { + LOGERR("CONTROLVM_DEVICE_CREATE Failed: channel size is too small, channel size:0x%lx, required size:0x%lx", + (ulong) msg->cmd.createDevice.channelBytes, + (ulong) minSize); + POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo, + POSTCODE_SEVERITY_ERR); + result = CONTROLVM_RESP_ERROR_CHANNEL_SIZE_TOO_SMALL; + goto Away; + } + dev->chanptr = + uislib_ioremap_cache(dev->channelAddr, + msg->cmd.createDevice.channelBytes); + if (!dev->chanptr) { + LOGERR("CONTROLVM_DEVICE_CREATE Failed: ioremap_cache of channelAddr:%Lx for channelBytes:%llu failed", + dev->channelAddr, + msg->cmd.createDevice.channelBytes); + result = CONTROLVM_RESP_ERROR_IOREMAP_FAILED; + POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo, + POSTCODE_SEVERITY_ERR); + goto Away; + } + } + dev->devInstGuid = msg->cmd.createDevice.devInstGuid; + dev->channelBytes = msg->cmd.createDevice.channelBytes; + + read_lock(&BusListLock); + for (bus = BusListHead; bus; bus = bus->next) { + if (bus->busNo == busNo) { + /* make sure the device number is valid */ + if (devNo >= bus->deviceCount) { + LOGERR("CONTROLVM_DEVICE_CREATE Failed: device (%d) >= deviceCount (%d).", + devNo, bus->deviceCount); + result = CONTROLVM_RESP_ERROR_MAX_DEVICES; + POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, + devNo, busNo, + POSTCODE_SEVERITY_ERR); + read_unlock(&BusListLock); + goto Away; + } + /* make sure this device is not already set */ + if (bus->device[devNo]) { + LOGERR("CONTROLVM_DEVICE_CREATE Failed: device %d is already exists.", + devNo); + POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, + devNo, busNo, + POSTCODE_SEVERITY_ERR); + result = CONTROLVM_RESP_ERROR_ALREADY_DONE; + read_unlock(&BusListLock); + goto Away; + } + read_unlock(&BusListLock); + /* the msg is bound for virtpci; send + * guest_msgs struct to callback + */ + if (!msg->hdr.Flags.server) { + struct guest_msgs cmd; + if (!memcmp + (&dev->channelTypeGuid, + &UltraVhbaChannelProtocolGuid, + sizeof(GUID))) { + WAIT_FOR_VALID_GUID(((CHANNEL_HEADER + *) (dev-> + chanptr))-> + Type); + if (!ULTRA_VHBA_CHANNEL_OK_CLIENT + (dev->chanptr, NULL)) { + LOGERR("CONTROLVM_DEVICE_CREATE Failed:[CLIENT]VHBA dev %d chan invalid.", + devNo); + POSTCODE_LINUX_4 + (DEVICE_CREATE_FAILURE_PC, + devNo, busNo, + POSTCODE_SEVERITY_ERR); + result = CONTROLVM_RESP_ERROR_CHANNEL_INVALID; + goto Away; + } + cmd.msgtype = GUEST_ADD_VHBA; + cmd.add_vhba.chanptr = dev->chanptr; + cmd.add_vhba.busNo = busNo; + cmd.add_vhba.deviceNo = devNo; + cmd.add_vhba.devInstGuid = + dev->devInstGuid; + cmd.add_vhba.intr = dev->intr; + } else + if (!memcmp + (&dev->channelTypeGuid, + &UltraVnicChannelProtocolGuid, + sizeof(GUID))) { + WAIT_FOR_VALID_GUID(((CHANNEL_HEADER + *) (dev-> + chanptr))-> + Type); + if (!ULTRA_VNIC_CHANNEL_OK_CLIENT + (dev->chanptr, NULL)) { + LOGERR("CONTROLVM_DEVICE_CREATE Failed: VNIC[CLIENT] dev %d chan invalid.", + devNo); + POSTCODE_LINUX_4 + (DEVICE_CREATE_FAILURE_PC, + devNo, busNo, + POSTCODE_SEVERITY_ERR); + result = CONTROLVM_RESP_ERROR_CHANNEL_INVALID; + goto Away; + } + cmd.msgtype = GUEST_ADD_VNIC; + cmd.add_vnic.chanptr = dev->chanptr; + cmd.add_vnic.busNo = busNo; + cmd.add_vnic.deviceNo = devNo; + cmd.add_vnic.devInstGuid = + dev->devInstGuid; + cmd.add_vhba.intr = dev->intr; + } else { + LOGERR("CONTROLVM_DEVICE_CREATE Failed: unknown channelTypeGuid.\n"); + POSTCODE_LINUX_4 + (DEVICE_CREATE_FAILURE_PC, devNo, + busNo, POSTCODE_SEVERITY_ERR); + result = CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN; + goto Away; + } + + if (!VirtControlChanFunc) { + LOGERR("CONTROLVM_DEVICE_CREATE Failed: virtpci callback not registered."); + POSTCODE_LINUX_4 + (DEVICE_CREATE_FAILURE_PC, devNo, + busNo, POSTCODE_SEVERITY_ERR); + result = CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE; + goto Away; + } + + if (!VirtControlChanFunc(&cmd)) { + LOGERR("CONTROLVM_DEVICE_CREATE Failed: virtpci GUEST_ADD_[VHBA||VNIC] returned error."); + POSTCODE_LINUX_4 + (DEVICE_CREATE_FAILURE_PC, devNo, + busNo, POSTCODE_SEVERITY_ERR); + result = CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR; + goto Away; + } + } + bus->device[devNo] = dev; + POSTCODE_LINUX_4(DEVICE_CREATE_SUCCESS_PC, devNo, busNo, + POSTCODE_SEVERITY_INFO); + return CONTROLVM_RESP_SUCCESS; + } + } + read_unlock(&BusListLock); + + LOGERR("CONTROLVM_DEVICE_CREATE Failed: failed to find bus %d.", busNo); + POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo, + POSTCODE_SEVERITY_ERR); + result = CONTROLVM_RESP_ERROR_BUS_INVALID; + +Away: + if (!msg->hdr.Flags.testMessage) { + uislib_iounmap(dev->chanptr); + dev->chanptr = NULL; + } + + UISFREE(dev, sizeof(struct device_info)); + return result; +} + +static int +pause_device(CONTROLVM_MESSAGE *msg) +{ + U32 busNo, devNo; + struct bus_info *bus; + struct device_info *dev; + struct guest_msgs cmd; + + busNo = msg->cmd.deviceChangeState.busNo; + devNo = msg->cmd.deviceChangeState.devNo; + + read_lock(&BusListLock); + for (bus = BusListHead; bus; bus = bus->next) { + if (bus->busNo == busNo) { + /* make sure the device number is valid */ + if (devNo >= bus->deviceCount) { + LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: device(%d) >= deviceCount(%d).", + devNo, bus->deviceCount); + read_unlock(&BusListLock); + return CONTROLVM_RESP_ERROR_DEVICE_INVALID; + } + /* make sure this device exists */ + dev = bus->device[devNo]; + if (!dev) { + LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: device %d does not exist.", + devNo); + read_unlock(&BusListLock); + return CONTROLVM_RESP_ERROR_ALREADY_DONE; + } + read_unlock(&BusListLock); + /* the msg is bound for virtpci; send + * guest_msgs struct to callback + */ + if (!memcmp + (&dev->channelTypeGuid, + &UltraVhbaChannelProtocolGuid, sizeof(GUID))) { + cmd.msgtype = GUEST_PAUSE_VHBA; + cmd.pause_vhba.chanptr = dev->chanptr; + } else + if (!memcmp + (&dev->channelTypeGuid, + &UltraVnicChannelProtocolGuid, + sizeof(GUID))) { + cmd.msgtype = GUEST_PAUSE_VNIC; + cmd.pause_vnic.chanptr = dev->chanptr; + } else { + LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: unknown channelTypeGuid.\n"); + return + CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN; + } + + if (!VirtControlChanFunc) { + LOGERR("CONTROLVM_DEVICE_CHANGESTATE Failed: virtpci callback not registered."); + return + CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE; + } + + if (!VirtControlChanFunc(&cmd)) { + LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: virtpci GUEST_PAUSE_[VHBA||VNIC] returned error."); + return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR; + } + break; + } + } + + if (!bus) { + LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: bus %d does not exist", + busNo); + read_unlock(&BusListLock); + return CONTROLVM_RESP_ERROR_BUS_INVALID; + } + + return CONTROLVM_RESP_SUCCESS; +} + +static int +resume_device(CONTROLVM_MESSAGE *msg) +{ + U32 busNo, devNo; + struct bus_info *bus; + struct device_info *dev; + struct guest_msgs cmd; + + busNo = msg->cmd.deviceChangeState.busNo; + devNo = msg->cmd.deviceChangeState.devNo; + + read_lock(&BusListLock); + for (bus = BusListHead; bus; bus = bus->next) { + if (bus->busNo == busNo) { + /* make sure the device number is valid */ + if (devNo >= bus->deviceCount) { + LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: device(%d) >= deviceCount(%d).", + devNo, bus->deviceCount); + read_unlock(&BusListLock); + return CONTROLVM_RESP_ERROR_DEVICE_INVALID; + } + /* make sure this device exists */ + dev = bus->device[devNo]; + if (!dev) { + LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: device %d does not exist.", + devNo); + read_unlock(&BusListLock); + return CONTROLVM_RESP_ERROR_ALREADY_DONE; + } + read_unlock(&BusListLock); + /* the msg is bound for virtpci; send + * guest_msgs struct to callback + */ + if (!memcmp(&dev->channelTypeGuid, + &UltraVhbaChannelProtocolGuid, + sizeof(GUID))) { + cmd.msgtype = GUEST_RESUME_VHBA; + cmd.resume_vhba.chanptr = dev->chanptr; + } else + if (!memcmp(&dev->channelTypeGuid, + &UltraVnicChannelProtocolGuid, + sizeof(GUID))) { + cmd.msgtype = GUEST_RESUME_VNIC; + cmd.resume_vnic.chanptr = dev->chanptr; + } else { + LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: unknown channelTypeGuid.\n"); + return + CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN; + } + + if (!VirtControlChanFunc) { + LOGERR("CONTROLVM_DEVICE_CHANGESTATE Failed: virtpci callback not registered."); + return + CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE; + } + + if (!VirtControlChanFunc(&cmd)) { + LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: virtpci GUEST_RESUME_[VHBA||VNIC] returned error."); + return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR; + } + break; + } + } + + if (!bus) { + LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: bus %d does not exist", + busNo); + read_unlock(&BusListLock); + return CONTROLVM_RESP_ERROR_BUS_INVALID; + } + + return CONTROLVM_RESP_SUCCESS; +} + +static int +destroy_device(CONTROLVM_MESSAGE *msg, char *buf) +{ + U32 busNo, devNo; + struct bus_info *bus; + struct device_info *dev; + struct guest_msgs cmd; + + busNo = msg->cmd.destroyDevice.busNo; + devNo = msg->cmd.destroyDevice.devNo; + + read_lock(&BusListLock); + LOGINF("destroy_device called for busNo=%u, devNo=%u", busNo, devNo); + for (bus = BusListHead; bus; bus = bus->next) { + if (bus->busNo == busNo) { + /* make sure the device number is valid */ + if (devNo >= bus->deviceCount) { + LOGERR("CONTROLVM_DEVICE_DESTORY Failed: device(%d) >= deviceCount(%d).", + devNo, bus->deviceCount); + read_unlock(&BusListLock); + return CONTROLVM_RESP_ERROR_DEVICE_INVALID; + } + /* make sure this device exists */ + dev = bus->device[devNo]; + if (!dev) { + LOGERR("CONTROLVM_DEVICE_DESTROY Failed: device %d does not exist.", + devNo); + read_unlock(&BusListLock); + return CONTROLVM_RESP_ERROR_ALREADY_DONE; + } + read_unlock(&BusListLock); + /* the msg is bound for virtpci; send + * guest_msgs struct to callback + */ + if (!memcmp + (&dev->channelTypeGuid, + &UltraVhbaChannelProtocolGuid, sizeof(GUID))) { + cmd.msgtype = GUEST_DEL_VHBA; + cmd.del_vhba.chanptr = dev->chanptr; + } else + if (!memcmp + (&dev->channelTypeGuid, + &UltraVnicChannelProtocolGuid, + sizeof(GUID))) { + cmd.msgtype = GUEST_DEL_VNIC; + cmd.del_vnic.chanptr = dev->chanptr; + } else { + LOGERR("CONTROLVM_DEVICE_DESTROY Failed: unknown channelTypeGuid.\n"); + return + CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN; + } + + if (!VirtControlChanFunc) { + LOGERR("CONTROLVM_DEVICE_DESTORY Failed: virtpci callback not registered."); + return + CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE; + } + + if (!VirtControlChanFunc(&cmd)) { + LOGERR("CONTROLVM_DEVICE_DESTROY Failed: virtpci GUEST_DEL_[VHBA||VNIC] returned error."); + return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR; + } +/* you must disable channel interrupts BEFORE you unmap the channel, + * because if you unmap first, there may still be some activity going + * on which accesses the channel and you will get a "unable to handle + * kernel paging request" + */ + if (dev->polling) { + LOGINF("calling uislib_disable_channel_interrupts"); + uislib_disable_channel_interrupts(busNo, devNo); + } + /* unmap the channel memory for the device. */ + if (!msg->hdr.Flags.testMessage) { + LOGINF("destroy_device, doing iounmap"); + uislib_iounmap(dev->chanptr); + } + UISFREE(dev, sizeof(struct device_info)); + bus->device[devNo] = NULL; + break; + } + } + + if (!bus) { + LOGERR("CONTROLVM_DEVICE_DESTROY Failed: bus %d does not exist", + busNo); + read_unlock(&BusListLock); + return CONTROLVM_RESP_ERROR_BUS_INVALID; + } + + return CONTROLVM_RESP_SUCCESS; +} + +void +ULTRA_disp_channel_header(CHANNEL_HEADER *x) +{ + LOGINF("Sig=%llx, HdrSz=%lx, Sz=%llx, Feat=%llx, hPart=%llx, Hndl=%llx, ChSpace=%llx, Ver=%lx, PartIdx=%lx\n", + x->Signature, (long unsigned int) x->HeaderSize, x->Size, + x->Features, x->PartitionHandle, x->Handle, x->oChannelSpace, + (long unsigned int) x->VersionId, + (long unsigned int) x->PartitionIndex); + + LOGINF("ClientStr=%lx, CliStBoot=%lx, CmdStCli=%lx, CliStOS=%lx, ChCharistics=%lx, CmdStSrv=%lx, SrvSt=%lx\n", + (long unsigned int) x->oClientString, + (long unsigned int) x->CliStateBoot, + (long unsigned int) x->CmdStateCli, + (long unsigned int) x->CliStateOS, + (long unsigned int) x->ChannelCharacteristics, + (long unsigned int) x->CmdStateSrv, + (long unsigned int) x->SrvState); + +} + +void +ULTRA_disp_channel(ULTRA_IO_CHANNEL_PROTOCOL *x) +{ + ULTRA_disp_channel_header(&x->ChannelHeader); + LOGINF("cmdQ.Type=%lx\n", (long unsigned int) x->cmdQ.Type); + LOGINF("cmdQ.Size=%llx\n", x->cmdQ.Size); + LOGINF("cmdQ.oSignalBase=%llx\n", x->cmdQ.oSignalBase); + LOGINF("cmdQ.SignalSize=%lx\n", (long unsigned int) x->cmdQ.SignalSize); + LOGINF("cmdQ.MaxSignalSlots=%lx\n", + (long unsigned int) x->cmdQ.MaxSignalSlots); + LOGINF("cmdQ.MaxSignals=%lx\n", (long unsigned int) x->cmdQ.MaxSignals); + LOGINF("rspQ.Type=%lx\n", (long unsigned int) x->rspQ.Type); + LOGINF("rspQ.Size=%llx\n", x->rspQ.Size); + LOGINF("rspQ.oSignalBase=%llx\n", x->rspQ.oSignalBase); + LOGINF("rspQ.SignalSize=%lx\n", (long unsigned int) x->rspQ.SignalSize); + LOGINF("rspQ.MaxSignalSlots=%lx\n", + (long unsigned int) x->rspQ.MaxSignalSlots); + LOGINF("rspQ.MaxSignals=%lx\n", (long unsigned int) x->rspQ.MaxSignals); + LOGINF("SIZEOF_CMDRSP=%lx\n", SIZEOF_CMDRSP); + LOGINF("SIZEOF_PROTOCOL=%lx\n", SIZEOF_PROTOCOL); +} + +void +ULTRA_disp_vnic_channel(ULTRA_IO_CHANNEL_PROTOCOL *x) +{ + LOGINF("num_rcv_bufs=%lx\n", (long unsigned int) x->vnic.num_rcv_bufs); + LOGINF("mtu=%lx\n", (long unsigned int) x->vnic.mtu); +} + +static int +init_chipset(CONTROLVM_MESSAGE *msg, char *buf) +{ + POSTCODE_LINUX_2(CHIPSET_INIT_ENTRY_PC, POSTCODE_SEVERITY_INFO); + + MaxBusCount = msg->cmd.initChipset.busCount; + PlatformNumber = msg->cmd.initChipset.platformNumber; + PhysicalDataChan = 0; + + /* We need to make sure we have our functions registered + * before processing messages. If we are a test vehicle the + * testMessage for init_chipset will be set. We can ignore the + * waits for the callbacks, since this will be manually entered + * from a user. If no testMessage is set, we will wait for the + * functions. + */ + if (!msg->hdr.Flags.testMessage) + WAIT_ON_CALLBACK(VirtControlChanFunc); + + chipset_inited = 1; + POSTCODE_LINUX_2(CHIPSET_INIT_EXIT_PC, POSTCODE_SEVERITY_INFO); + + return CONTROLVM_RESP_SUCCESS; +} + +static int +stop_chipset(CONTROLVM_MESSAGE *msg, char *buf) +{ + /* Check that all buses and switches have been torn down and + * destroyed. + */ + if (BusListHead) { + /* Buses still exist. */ + LOGERR("CONTROLVM_CHIPSET_STOP: BusListHead is not NULL"); + return CONTROLVM_RESP_ERROR_CHIPSET_STOP_FAILED_BUS; + } + if (BusListCount) { + /* BusListHead is NULL, but BusListCount != 0 */ + LOGERR("CONTROLVM_CHIPSET_STOP: BusListCount != 0"); + return CONTROLVM_RESP_ERROR_CHIPSET_STOP_FAILED_BUS; + } + + /* Buses are shut down. */ + return visorchipset_chipset_notready(); +} + +static int +delete_bus_glue(U32 busNo) +{ + CONTROLVM_MESSAGE msg; + + init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, 0); + msg.cmd.destroyBus.busNo = busNo; + if (destroy_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { + LOGERR("destroy_bus failed. busNo=0x%x\n", busNo); + return 0; + } + return 1; +} + +static int +delete_device_glue(U32 busNo, U32 devNo) +{ + CONTROLVM_MESSAGE msg; + + init_msg_header(&msg, CONTROLVM_DEVICE_DESTROY, 0, 0); + msg.cmd.destroyDevice.busNo = busNo; + msg.cmd.destroyDevice.devNo = devNo; + if (destroy_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { + LOGERR("destroy_device failed. busNo=0x%x devNo=0x%x\n", busNo, + devNo); + return 0; + } + return 1; +} + +int +uislib_client_inject_add_bus(U32 busNo, GUID instGuid, + U64 channelAddr, ulong nChannelBytes) +{ + CONTROLVM_MESSAGE msg; + + LOGINF("enter busNo=0x%x\n", busNo); + /* step 0: init the chipset */ + POSTCODE_LINUX_3(CHIPSET_INIT_ENTRY_PC, busNo, POSTCODE_SEVERITY_INFO); + + if (!chipset_inited) { + /* step: initialize the chipset */ + init_msg_header(&msg, CONTROLVM_CHIPSET_INIT, 0, 0); + /* this change is needed so that console will come up + * OK even when the bus 0 create comes in late. If the + * bus 0 create is the first create, then the add_vnic + * will work fine, but if the bus 0 create arrives + * after number 4, then the add_vnic will fail, and the + * ultraboot will fail. + */ + msg.cmd.initChipset.busCount = 23; + msg.cmd.initChipset.switchCount = 0; + if (init_chipset(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { + LOGERR("init_chipset failed.\n"); + return 0; + } + LOGINF("chipset initialized\n"); + POSTCODE_LINUX_3(CHIPSET_INIT_EXIT_PC, busNo, + POSTCODE_SEVERITY_INFO); + } + + /* step 1: create a bus */ + POSTCODE_LINUX_3(BUS_CREATE_ENTRY_PC, busNo, POSTCODE_SEVERITY_WARNING); + init_msg_header(&msg, CONTROLVM_BUS_CREATE, 0, 0); + msg.cmd.createBus.busNo = busNo; + msg.cmd.createBus.deviceCount = 23; /* devNo+1; */ + msg.cmd.createBus.channelAddr = channelAddr; + msg.cmd.createBus.channelBytes = nChannelBytes; + if (create_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { + LOGERR("create_bus failed.\n"); + POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, busNo, + POSTCODE_SEVERITY_ERR); + return 0; + } + POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, busNo, POSTCODE_SEVERITY_INFO); + + return 1; +} +EXPORT_SYMBOL_GPL(uislib_client_inject_add_bus); + + +int +uislib_client_inject_del_bus(U32 busNo) +{ + return delete_bus_glue(busNo); +} +EXPORT_SYMBOL_GPL(uislib_client_inject_del_bus); + +int +uislib_client_inject_pause_vhba(U32 busNo, U32 devNo) +{ + CONTROLVM_MESSAGE msg; + int rc; + + init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0); + msg.cmd.deviceChangeState.busNo = busNo; + msg.cmd.deviceChangeState.devNo = devNo; + msg.cmd.deviceChangeState.state = SegmentStateStandby; + rc = pause_device(&msg); + if (rc != CONTROLVM_RESP_SUCCESS) { + LOGERR("VHBA pause_device failed. busNo=0x%x devNo=0x%x\n", + busNo, devNo); + return rc; + } + return 0; +} +EXPORT_SYMBOL_GPL(uislib_client_inject_pause_vhba); + +int +uislib_client_inject_resume_vhba(U32 busNo, U32 devNo) +{ + CONTROLVM_MESSAGE msg; + int rc; + + init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0); + msg.cmd.deviceChangeState.busNo = busNo; + msg.cmd.deviceChangeState.devNo = devNo; + msg.cmd.deviceChangeState.state = SegmentStateRunning; + rc = resume_device(&msg); + if (rc != CONTROLVM_RESP_SUCCESS) { + LOGERR("VHBA resume_device failed. busNo=0x%x devNo=0x%x\n", + busNo, devNo); + return rc; + } + return 0; + +} +EXPORT_SYMBOL_GPL(uislib_client_inject_resume_vhba); + +int +uislib_client_inject_add_vhba(U32 busNo, U32 devNo, + U64 phys_chan_addr, U32 chan_bytes, + int is_test_addr, GUID instGuid, + struct InterruptInfo *intr) +{ + CONTROLVM_MESSAGE msg; + + LOGINF(" enter busNo=0x%x devNo=0x%x\n", busNo, devNo); + /* chipset init'ed with bus bus has been previously created - + * Verify it still exists step 2: create the VHBA device on the + * bus + */ + POSTCODE_LINUX_4(VHBA_CREATE_ENTRY_PC, devNo, busNo, + POSTCODE_SEVERITY_INFO); + + init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, 0); + if (is_test_addr) + /* signify that the physical channel address does NOT + * need to be ioremap()ed + */ + msg.hdr.Flags.testMessage = 1; + msg.cmd.createDevice.busNo = busNo; + msg.cmd.createDevice.devNo = devNo; + msg.cmd.createDevice.devInstGuid = instGuid; + if (intr) + msg.cmd.createDevice.intr = *intr; + else + memset(&msg.cmd.createDevice.intr, 0, + sizeof(struct InterruptInfo)); + msg.cmd.createDevice.channelAddr = phys_chan_addr; + if (chan_bytes < MIN_IO_CHANNEL_SIZE) { + LOGERR("wrong channel size.chan_bytes = 0x%x IO_CHANNEL_SIZE= 0x%x\n", + chan_bytes, (unsigned int) MIN_IO_CHANNEL_SIZE); + POSTCODE_LINUX_4(VHBA_CREATE_FAILURE_PC, chan_bytes, + MIN_IO_CHANNEL_SIZE, POSTCODE_SEVERITY_ERR); + return 0; + } + msg.cmd.createDevice.channelBytes = chan_bytes; + msg.cmd.createDevice.dataTypeGuid = UltraVhbaChannelProtocolGuid; + if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { + LOGERR("VHBA create_device failed.\n"); + POSTCODE_LINUX_4(VHBA_CREATE_FAILURE_PC, devNo, busNo, + POSTCODE_SEVERITY_ERR); + return 0; + } + POSTCODE_LINUX_4(VHBA_CREATE_SUCCESS_PC, devNo, busNo, + POSTCODE_SEVERITY_INFO); + return 1; +} +EXPORT_SYMBOL_GPL(uislib_client_inject_add_vhba); + +int +uislib_client_inject_del_vhba(U32 busNo, U32 devNo) +{ + return delete_device_glue(busNo, devNo); +} +EXPORT_SYMBOL_GPL(uislib_client_inject_del_vhba); + +int +uislib_client_inject_add_vnic(U32 busNo, U32 devNo, + U64 phys_chan_addr, U32 chan_bytes, + int is_test_addr, GUID instGuid, + struct InterruptInfo *intr) +{ + CONTROLVM_MESSAGE msg; + + LOGINF(" enter busNo=0x%x devNo=0x%x\n", busNo, devNo); + /* chipset init'ed with bus bus has been previously created - + * Verify it still exists step 2: create the VNIC device on the + * bus + */ + POSTCODE_LINUX_4(VNIC_CREATE_ENTRY_PC, devNo, busNo, + POSTCODE_SEVERITY_INFO); + + init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, 0); + if (is_test_addr) + /* signify that the physical channel address does NOT + * need to be ioremap()ed + */ + msg.hdr.Flags.testMessage = 1; + msg.cmd.createDevice.busNo = busNo; + msg.cmd.createDevice.devNo = devNo; + msg.cmd.createDevice.devInstGuid = instGuid; + if (intr) + msg.cmd.createDevice.intr = *intr; + else + memset(&msg.cmd.createDevice.intr, 0, + sizeof(struct InterruptInfo)); + msg.cmd.createDevice.channelAddr = phys_chan_addr; + if (chan_bytes < MIN_IO_CHANNEL_SIZE) { + LOGERR("wrong channel size.chan_bytes = 0x%x IO_CHANNEL_SIZE= 0x%x\n", + chan_bytes, (unsigned int) MIN_IO_CHANNEL_SIZE); + POSTCODE_LINUX_4(VNIC_CREATE_FAILURE_PC, chan_bytes, + MIN_IO_CHANNEL_SIZE, POSTCODE_SEVERITY_ERR); + return 0; + } + msg.cmd.createDevice.channelBytes = chan_bytes; + msg.cmd.createDevice.dataTypeGuid = UltraVnicChannelProtocolGuid; + if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { + LOGERR("VNIC create_device failed.\n"); + POSTCODE_LINUX_4(VNIC_CREATE_FAILURE_PC, devNo, busNo, + POSTCODE_SEVERITY_ERR); + return 0; + } + + POSTCODE_LINUX_4(VNIC_CREATE_SUCCESS_PC, devNo, busNo, + POSTCODE_SEVERITY_INFO); + return 1; +} +EXPORT_SYMBOL_GPL(uislib_client_inject_add_vnic); + +int +uislib_client_inject_pause_vnic(U32 busNo, U32 devNo) +{ + CONTROLVM_MESSAGE msg; + int rc; + + init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0); + msg.cmd.deviceChangeState.busNo = busNo; + msg.cmd.deviceChangeState.devNo = devNo; + msg.cmd.deviceChangeState.state = SegmentStateStandby; + rc = pause_device(&msg); + if (rc != CONTROLVM_RESP_SUCCESS) { + LOGERR("VNIC pause_device failed. busNo=0x%x devNo=0x%x\n", + busNo, devNo); + return -1; + } + return 0; +} +EXPORT_SYMBOL_GPL(uislib_client_inject_pause_vnic); + +int +uislib_client_inject_resume_vnic(U32 busNo, U32 devNo) +{ + CONTROLVM_MESSAGE msg; + int rc; + + init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0); + msg.cmd.deviceChangeState.busNo = busNo; + msg.cmd.deviceChangeState.devNo = devNo; + msg.cmd.deviceChangeState.state = SegmentStateRunning; + rc = resume_device(&msg); + if (rc != CONTROLVM_RESP_SUCCESS) { + LOGERR("VNIC resume_device failed. busNo=0x%x devNo=0x%x\n", + busNo, devNo); + return -1; + } + return 0; + +} +EXPORT_SYMBOL_GPL(uislib_client_inject_resume_vnic); + +int +uislib_client_inject_del_vnic(U32 busNo, U32 devNo) +{ + return delete_device_glue(busNo, devNo); +} +EXPORT_SYMBOL_GPL(uislib_client_inject_del_vnic); + +int +uislib_client_add_vnic(U32 busNo) +{ + BOOL busCreated = FALSE; + int devNo = 0; /* Default to 0, since only one device + * will be created for this bus... */ + GUID dummyGuid = GUID0; + CONTROLVM_MESSAGE msg; + + init_msg_header(&msg, CONTROLVM_BUS_CREATE, 0, 0); + msg.hdr.Flags.testMessage = 1; + msg.cmd.createBus.busNo = busNo; + msg.cmd.createBus.deviceCount = 4; + msg.cmd.createBus.channelAddr = 0; + msg.cmd.createBus.channelBytes = 0; + if (create_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { + LOGERR("client create_bus failed"); + return 0; + } + busCreated = TRUE; + + init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, 0); + msg.hdr.Flags.testMessage = 1; + msg.cmd.createDevice.busNo = busNo; + msg.cmd.createDevice.devNo = devNo; + msg.cmd.createDevice.devInstGuid = dummyGuid; + memset(&msg.cmd.createDevice.intr, 0, sizeof(struct InterruptInfo)); + msg.cmd.createDevice.channelAddr = PhysicalDataChan; + msg.cmd.createDevice.channelBytes = MIN_IO_CHANNEL_SIZE; + msg.cmd.createDevice.dataTypeGuid = UltraVnicChannelProtocolGuid; + if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { + LOGERR("client create_device failed"); + goto AwayCleanup; + } + + return 1; + +AwayCleanup: + if (busCreated) { + init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, 0); + msg.hdr.Flags.testMessage = 1; + msg.cmd.destroyBus.busNo = busNo; + if (destroy_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) + LOGERR("client destroy_bus failed.\n"); + } + + return 0; +} /* end uislib_client_add_vnic */ +EXPORT_SYMBOL_GPL(uislib_client_add_vnic); + +int +uislib_client_delete_vnic(U32 busNo) +{ + int devNo = 0; /* Default to 0, since only one device + * will be created for this bus... */ + CONTROLVM_MESSAGE msg; + + init_msg_header(&msg, CONTROLVM_DEVICE_DESTROY, 0, 0); + msg.hdr.Flags.testMessage = 1; + msg.cmd.destroyDevice.busNo = busNo; + msg.cmd.destroyDevice.devNo = devNo; + if (destroy_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { + /* Don't error exit - try to see if bus can be destroyed... */ + LOGERR("client destroy_device failed.\n"); + } + + init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, 0); + msg.hdr.Flags.testMessage = 1; + msg.cmd.destroyBus.busNo = busNo; + if (destroy_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) + LOGERR("client destroy_bus failed.\n"); + + return 1; +} +EXPORT_SYMBOL_GPL(uislib_client_delete_vnic); + + /* end client_delete_vnic */ + +static atomic_t Malloc_BytesInUse = ATOMIC_INIT(0); +static atomic_t Malloc_BuffersInUse = ATOMIC_INIT(0); +static atomic_t Malloc_FailuresAlloc = ATOMIC_INIT(0); +static atomic_t Malloc_FailuresFree = ATOMIC_INIT(0); +static atomic_t Malloc_TotalMallocs = ATOMIC_INIT(0); +static atomic_t Malloc_TotalFrees = ATOMIC_INIT(0); + +void * +uislib_malloc(size_t siz, gfp_t gfp, U8 contiguous, char *fn, int ln) +{ + void *p = NULL; + + if (contiguous == 0) { + /* Allocate non-contiguous memory, such as in the + * add_vnic and add_vhba methods where we are rebooting + * the guest, for example. Otherwise the contiguous + * memory allocation attempt results in an + * out-of-memory crash in the IOVM... + */ + p = vmalloc(siz); + } else { + /* __GFP_NORETRY means "ok to fail", meaning kmalloc() + * can return NULL. If you do NOT specify + * __GFP_NORETRY, Linux will go to extreme measures to + * get memory for you (like, invoke oom killer), which + * will probably cripple the system. + */ + p = kmalloc(siz, gfp | __GFP_NORETRY); + } + if (p == NULL) { + LOGERR("uislib_malloc failed to alloc %d bytes @%s:%d", + (int) siz, fn, ln); + atomic_inc(&Malloc_FailuresAlloc); + return NULL; + } + atomic_add((int) (siz), &Malloc_BytesInUse); + atomic_inc(&Malloc_BuffersInUse); + atomic_inc(&Malloc_TotalMallocs); /* will eventually overflow */ + return p; +} +EXPORT_SYMBOL_GPL(uislib_malloc); + +void +uislib_free(void *p, size_t siz, U8 contiguous, char *fn, int ln) +{ + if (p == NULL) { + LOGERR("uislib_free NULL pointer @%s:%d", fn, ln); + atomic_inc(&Malloc_FailuresFree); + return; + } + + if (contiguous == 0) + vfree(p); + else + kfree(p); + atomic_sub((int) (siz), &Malloc_BytesInUse); + atomic_dec(&Malloc_BuffersInUse); + atomic_inc(&Malloc_TotalFrees); /* will eventually overflow */ +} +EXPORT_SYMBOL_GPL(uislib_free); + +void * +uislib_cache_alloc(struct kmem_cache *cur_pool, char *fn, int ln) +{ + /* __GFP_NORETRY means "ok to fail", meaning kmalloc() can + * return NULL. If you do NOT specify __GFP_NORETRY, Linux + * will go to extreme measures to get memory for you (like, + * invoke oom killer), which will probably cripple the system. + */ + void *p = kmem_cache_alloc(cur_pool, GFP_ATOMIC | __GFP_NORETRY); + if (p == NULL) { + LOGERR("uislib_malloc failed to alloc uiscmdrsp @%s:%d", + fn, ln); + return NULL; + } + return p; +} +EXPORT_SYMBOL_GPL(uislib_cache_alloc); + +void +uislib_cache_free(struct kmem_cache *cur_pool, void *p, char *fn, int ln) +{ + if (p == NULL) { + LOGERR("uislib_free NULL pointer @%s:%d", fn, ln); + return; + } + kmem_cache_free(cur_pool, p); +} +EXPORT_SYMBOL_GPL(uislib_cache_free); + +/*****************************************************/ +/* proc filesystem callback functions */ +/*****************************************************/ + +static ssize_t +vnic_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + int action = 0xffff, busNo = 0, i, result = 0; + char buf[count]; + char direction; +/* GUID guid; */ + if (copy_from_user(buf, buffer, count)) { + LOGERR("echo > /proc/uislib/vnic copy_from_user ****FAILED.\n"); + return -EFAULT; + } + + i = sscanf(buf, "%d%c", &action, &direction); + if (i != 2) { + LOGERR("unable to parse vnic proc parameters.\n"); + return -EFAULT; + } + + if ((direction != '-') && (direction != '+')) { + LOGERR("unable to determine whether to add or delete vnic\n"); + return -EFAULT; + } + + /* if (i < 1), i.e., if we didn't even read the action field, + * then action will default to 0xffff and the code below will + * fall through the switch and print usage. + */ + switch (action) { + case 0: + /* call client method... */ + busNo = 0; /* All client drivers use bus value of 0... */ + if (direction == '+') + result = uislib_client_add_vnic(busNo); + else + result = uislib_client_delete_vnic(busNo); + if (!result) { + LOGERR("echo 0%c > /proc/uislib/vnic failed (client end)", + direction); + return -EFAULT; + } + return count; + + default: + break; + } + + LOGERR("USAGE: echo > /proc/uislib/vnic"); + LOGERR(" "); + LOGERR("Client Syntax"); + LOGERR("-------------"); + LOGERR("0+ ==> add vnic"); + LOGERR("0- ==> delete vnic"); + LOGERR(" "); + return count; +} /* end vnic_proc_write */ + +static ssize_t +chipset_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + int i, action = 0xffff; + char buf[count]; + CONTROLVM_MESSAGE msg; + + memset(&msg, 0, sizeof(CONTROLVM_MESSAGE)); + + if (copy_from_user(buf, buffer, count)) { + LOGERR("copy_from_user ****FAILED.\n"); + return -EFAULT; + } + + if (chipset_inited) { + LOGINF("Chipset already initialized\n"); + return -EFAULT; + } + i = sscanf(buf, "%x", &action); + + /* if (i < 1), i.e., if we didn't even read the action field, + * then action will default to 0xffff and the code below will + * fall through the switch and print usage. + */ + switch (action) { + case 1: + /* GUEST */ + /* step: initialize the chipset */ + init_msg_header(&msg, CONTROLVM_CHIPSET_INIT, 0, 0); + msg.hdr.Flags.testMessage = 0; + msg.cmd.initChipset.busCount = 23; + msg.cmd.initChipset.switchCount = 23; + + if (init_chipset(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { + LOGERR("init_chipset failed.\n"); + return 0; + } + return 1; + case 2: + /* BOTH */ + init_msg_header(&msg, CONTROLVM_CHIPSET_INIT, 0, 0); + msg.hdr.Flags.testMessage = 1; + msg.cmd.initChipset.busCount = 23; + msg.cmd.initChipset.switchCount = 23; + + if (init_chipset(&msg, NULL) != CONTROLVM_RESP_SUCCESS) { + LOGERR("init_chipset failed.\n"); + return 0; + } + return 1; + + default: + break; + } + + LOGERR("usage: 1 ==> init_chipset client\n"); + LOGERR("usage: 2 ==> init_chipset test\n"); + return -EFAULT; +} + +#define PROCLINE(...) \ + do { \ + if (util_add_proc_line_ex(&tot, buff, \ + buff_len, __VA_ARGS__) < 0) { \ + goto err_done; \ + } \ + } while (0) + +static int +info_proc_read_helper(char **buff, int *buff_len) +{ + int i, tot = 0; + struct bus_info *bus; + + PROCLINE("\nBuses:\n"); + + read_lock(&BusListLock); + for (bus = BusListHead; bus; bus = bus->next) { + + PROCLINE(" bus=0x%p, busNo=%d, deviceCount=%d\n", + bus, bus->busNo, bus->deviceCount); + + PROCLINE(" Devices:\n"); + + for (i = 0; i < bus->deviceCount; i++) { + if (bus->device[i]) { + PROCLINE(" busNo %d, device[%i]: 0x%p, chanptr=0x%p, swtch=0x%p\n", + bus->busNo, i, bus->device[i], + bus->device[i]->chanptr, + bus->device[i]->swtch); + PROCLINE(" first_busy_cnt=%llu, moved_to_tail_cnt=%llu, last_on_list_cnt=%llu\n", + bus->device[i]->first_busy_cnt, + bus->device[i]->moved_to_tail_cnt, + bus->device[i]->last_on_list_cnt); + } + } + } + read_unlock(&BusListLock); + + PROCLINE("Malloc bytes in use: %d\n", atomic_read(&Malloc_BytesInUse)); + PROCLINE("Malloc buffers in use: %d\n", + atomic_read(&Malloc_BuffersInUse)); + PROCLINE("Malloc allocation failures: %d\n", + atomic_read(&Malloc_FailuresAlloc)); + PROCLINE("Malloc free failures: %d\n", + atomic_read(&Malloc_FailuresFree)); + PROCLINE("Malloc total mallocs: %u (may overflow)\n", + (unsigned) atomic_read(&Malloc_TotalMallocs)); + PROCLINE("Malloc total frees: %u (may overflow)\n", + (unsigned) atomic_read(&Malloc_TotalFrees)); + PROCLINE("UisUtils_Registered_Services: %d\n", + atomic_read(&UisUtils_Registered_Services)); + + PROCLINE("cycles_before_wait %llu wait_cycles:%llu\n", + cycles_before_wait, wait_cycles); + PROCLINE("tot_wakeup_cnt %llu:tot_wait_cnt %llu:tot_schedule_cnt %llu\n", + tot_wakeup_cnt, tot_wait_cnt, tot_schedule_cnt); + PROCLINE("en_smart_wakeup %d\n", en_smart_wakeup); + PROCLINE("tot_moved_to_tail_cnt %llu\n", tot_moved_to_tail_cnt); + + return tot; +err_done: + + return -1; +} + +static ssize_t +info_proc_read(struct file *file, char __user *buf, size_t len, loff_t *offset) +{ + char *temp; + int totalBytes = 0; + int remaining_bytes = PROC_READ_BUFFER_SIZE; + +/* *start = buf; */ + if (ProcReadBuffer == NULL) { + DBGINF("ProcReadBuffer == NULL; allocating buffer.\n."); + ProcReadBuffer = UISVMALLOC(PROC_READ_BUFFER_SIZE); + + if (ProcReadBuffer == NULL) { + LOGERR("failed to allocate buffer to provide proc data.\n"); + return -ENOMEM; + } + } + + temp = ProcReadBuffer; + + if ((*offset == 0) || (!ProcReadBufferValid)) { + DBGINF("calling info_proc_read_helper.\n"); + /* if the read fails, then -1 will be returned */ + totalBytes = info_proc_read_helper(&temp, &remaining_bytes); + ProcReadBufferValid = 1; + } else + totalBytes = strlen(ProcReadBuffer); + + return simple_read_from_buffer(buf, len, offset, + ProcReadBuffer, totalBytes); +} + +static ssize_t +platformnumber_proc_read(struct file *file, char __user *buf, + size_t len, loff_t *offset) +{ + int length = 0; + char *vbuf; + loff_t pos = *offset; + + if (pos < 0) + return -EINVAL; + + if (pos > 0 || !len) + return 0; + + vbuf = kzalloc(len, GFP_KERNEL); + if (!vbuf) + return -ENOMEM; + + length = sprintf(vbuf, "%d\n", PlatformNumber); + + if (copy_to_user(buf, vbuf, length)) { + kfree(vbuf); + return -EFAULT; + } + + kfree(vbuf); + *offset += length; + return length; +} + +#ifdef UISLIB_TEST_PROC + +/* proc/uislib/vbus//info */ +static int +proc_info_vbus_show(struct seq_file *m, void *v) +{ + struct bus_info *bus = m->private; + int i, devInfoCount, x; + char buf[999]; + + if (bus == NULL) + return 0; + seq_printf(m, "Client device / client driver info for %s partition (vbus #%d):\n", + bus->partitionName, bus->busNo); + if ((bus->busChannelBytes == 0) || (bus->pBusChannel == NULL)) + return 0; + devInfoCount = + (bus->busChannelBytes - + sizeof(ULTRA_VBUS_CHANNEL_PROTOCOL)) / + sizeof(ULTRA_VBUS_DEVICEINFO); + x = VBUSCHANNEL_devInfoToStringBuffer(bus->pBusChannel->ChpInfo, buf, + sizeof(buf) - 1, -1); + buf[x] = '\0'; + seq_printf(m, "%s", buf); + x = VBUSCHANNEL_devInfoToStringBuffer(bus->pBusChannel->BusInfo, + buf, sizeof(buf) - 1, -1); + buf[x] = '\0'; + seq_printf(m, "%s", buf); + for (i = 0; i < devInfoCount; i++) { + x = VBUSCHANNEL_devInfoToStringBuffer(bus->pBusChannel-> + DevInfo[i], buf, + sizeof(buf) - 1, i); + if (x > 0) { + buf[x] = '\0'; + seq_printf(m, "%s", buf); + } + } + return 0; +} + +static ssize_t +bus_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + int server_flag = 0; + int i, action = 0xffff, result; + char buf[count]; + CONTROLVM_MESSAGE msg; + U32 busNo, deviceCount; + + memset(&msg, 0, sizeof(CONTROLVM_MESSAGE)); + + if (copy_from_user(buf, buffer, count)) { + LOGERR("echo > /proc/uislib/bus: copy_from_user ****FAILED."); + return -EFAULT; + } + + i = sscanf(buf, "%x-%d-%d", &action, &busNo, &deviceCount); + + /* if (i < 1), i.e., if we didn't even read the action field, + * then action will default to 0xffff and the code below will + * fall through the switch and print usage. + */ + switch (action) { + case 0: + /* destroy a bus */ + if (i != 2) + break; + init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, server_flag); + msg.cmd.destroyBus.busNo = busNo; + + result = destroy_bus(&msg, NULL); + + if (result != CONTROLVM_RESP_SUCCESS) { + LOGERR("echo 0-%d > /proc/uislib/bus {CONTROLVM_BUS_DESTROY Failed} Result(%d)", + busNo, result); + return -EFAULT; + } + return count; + case 1: + /* create a bus */ + if (i != 3) + break; + init_msg_header(&msg, CONTROLVM_BUS_CREATE, 0, server_flag); + msg.cmd.createBus.busNo = busNo; + msg.cmd.createBus.deviceCount = deviceCount; + + result = create_bus(&msg, NULL); + + if (result != CONTROLVM_RESP_SUCCESS) { + LOGERR("echo 1-%d-%d > /proc/uislib/bus {CONTROLVM_BUS_CREATE Failed} Result(%d)", + busNo, deviceCount, result); + return -EFAULT; + } + + return count; + default: + break; + } + + LOGERR("USAGE: echo -... > /proc/uislib/bus"); + LOGERR(" "); + LOGERR("Destruct Syntax ControlVM Message Id"); + LOGERR("--------------- ---------------------"); + LOGERR("0- ==> CONTROLVM_BUS_DESTROY"); + LOGERR(" "); + LOGERR("Construct Syntax ControlVM Message Id"); + LOGERR("----------------------- -------------------- "); + LOGERR("1-- ==> CONTROLVM_BUS_CREATE"); + + return -EFAULT; +} + +static ssize_t +uislib_proc_read_writeonly(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + return 0; +} + +static ssize_t +dev_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + int server_flag = 0; + CONTROLVM_MESSAGE msg; + U32 busNo, devNo; + char buf[count]; + unsigned int chanptr; + int type, i, action = 0xffff, result; + + if (copy_from_user(buf, buffer, count)) { + LOGERR("echo > /proc/uislib/device: copy_from_user ****FAILED."); + return -EFAULT; + } + + i = sscanf(buf, "%x-%d-%d-%x-%d", + &action, &busNo, &devNo, &chanptr, &type); + + switch (action) { + case 0: + if (i != 3) + break; + + /* destroy a device */ + init_msg_header(&msg, CONTROLVM_DEVICE_DESTROY, 0, server_flag); + msg.cmd.destroyDevice.busNo = busNo; + msg.cmd.destroyDevice.devNo = devNo; + + result = destroy_device(&msg, NULL); + + if (result != CONTROLVM_RESP_SUCCESS) { + LOGERR("echo 0-%d-%d > /proc/uislib/device {CONTROLVM_DEVICE_DESTROY Failed} Result(%d)", + busNo, devNo, result); + return -EFAULT; + } + + return count; + + case 1: + if (i != 5) + break; + + /* create a device */ + init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, server_flag); + msg.cmd.createDevice.busNo = busNo; + msg.cmd.createDevice.devNo = devNo; + msg.cmd.createDevice.channelAddr = __pa(chanptr); + msg.cmd.createDevice.channelBytes = MIN_IO_CHANNEL_SIZE; + + if (type == 0) + msg.cmd.createDevice.dataTypeGuid = + UltraVhbaChannelProtocolGuid; + else if (type == 1) + msg.cmd.createDevice.dataTypeGuid = + UltraVnicChannelProtocolGuid; + else { + LOGERR("echo 1-%d-%d-%x- > /proc/uislib/devce failed: invalid device type %d.", + busNo, devNo, chanptr, type); + return -EFAULT; + } + + result = create_device(&msg, NULL); + + if (result != CONTROLVM_RESP_SUCCESS) { + if (type == 0) + LOGERR("echo 1-%d-%d-%x-0 > /proc/uislib/device {CONTROLVM_DEVICE_CREATE[vHBA] Failed} Result(%d)", + busNo, devNo, chanptr, result); + else + LOGERR("echo 1-%d-%d-%x-1 > /proc/uislib/device {CONTROLVM_DEVICE_CREATE[vNIC] Failed} Result(%d)", + busNo, devNo, chanptr, result); + return -EFAULT; + } + + default: + break; + } + + LOGERR("USAGE: echo --... > /proc/uislib/device"); + LOGERR(" "); + LOGERR("Destruct Syntax ControlVM Message Id"); + LOGERR("----------------- ------------------------"); + LOGERR("0-- ==> CONTROLVM_DEVICE_DESTROY"); + LOGERR(" "); + LOGERR("Construct Syntax ControlVM Message Id"); + LOGERR + ("---------------------------------- ----------------------- "); + LOGERR + ("1---- ==> CONTROLVM_DEVICE_CREATE"); + LOGERR(" : vHBA"); + LOGERR(" : vNIC"); + LOGERR(" "); + + return -EFAULT; +} + +static ssize_t +cycles_before_wait_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + char buf[count]; + +#define CYCLES_BEFORE_WAIT_USE_ERROR { \ + LOGERR("Incorrect Call Home Input.\n"); \ + pr_info("Please pass Call Home Event Parameters in the form:\n"); \ + pr_info("EventID Category Type[parameter1][parameter2][parameter3][parameter4][parameter5][parameter6]\n"); \ + return -EFAULT; \ +} + + if (count == 0) + CYCLES_BEFORE_WAIT_USE_ERROR; + + if (copy_from_user(buf, buffer, count)) { + LOGERR("copy_from_user failed.\n"); + return -EFAULT; + } + buf[count - 1] = '\0'; /* Replace the LF at the end of the + * input with a NULL */ + /* Pull out the cycles_before_wait must be decimal integer */ + if (sscanf(buf, "%lld", &cycles_before_wait) != 1) + CYCLES_BEFORE_WAIT_USE_ERROR; + + return count; +} + +static ssize_t +reset_counts_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + char buf[count]; + unsigned long long new_value; + struct bus_info *bus; + int i; + +#define RESET_COUNTS_USE_ERROR { \ + LOGERR("Incorrect reset_counts Input.\n"); \ + pr_info("Please pass the new value for the counters:\n"); \ + pr_info("e.g. echo 0 > reset_counts\n"); \ + return -EFAULT; \ + } + + if (count == 0) + RESET_COUNTS_USE_ERROR; + + if (copy_from_user(buf, buffer, count)) { + LOGERR("copy_from_user failed.\n"); + return -EFAULT; + } + buf[count - 1] = '\0'; /* Replace the LF at the end of the + * input with a NULL */ + /* Pull out the reset_counts must be decimal integer */ + if (sscanf(buf, "%llu", &new_value) != 1) + RESET_COUNTS_USE_ERROR; + read_lock(&BusListLock); + for (bus = BusListHead; bus; bus = bus->next) { + + for (i = 0; i < bus->deviceCount; i++) { + if (bus->device[i]) { + bus->device[i]->first_busy_cnt = new_value; + bus->device[i]->moved_to_tail_cnt = new_value; + bus->device[i]->last_on_list_cnt = new_value; + } + } + } + read_unlock(&BusListLock); + tot_moved_to_tail_cnt = new_value; + tot_wait_cnt = new_value; + tot_wakeup_cnt = new_value; + tot_schedule_cnt = new_value; + return count; +} + +static ssize_t +smart_wakeup_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + char buf[count]; + int new_value; + +#define SMART_WAKEUP_USE_ERROR { \ + LOGERR("Incorrect smart_wakeup Input 0 disables smart_wakeup, and 1 enables smart_wakeup.\n"); \ + pr_info("echo 0 > smart_wakeup\n"); \ + pr_info("echo 1 > smart_wakeup\n"); \ + return -EFAULT; \ + } + + if (count == 0) + SMART_WAKEUP_USE_ERROR; + + if (copy_from_user(buf, buffer, count)) { + LOGERR("copy_from_user failed.\n"); + return -EFAULT; + } + buf[count - 1] = '\0'; /* Replace the LF at the end of the + * input with a NULL */ + /* Pull out the smart_wakeup must be decimal integer */ + if (sscanf(buf, "%d", &new_value) != 1) + SMART_WAKEUP_USE_ERROR; + en_smart_wakeup = new_value; + return count; +} + +static ssize_t +test_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + int i, action = 0xffff; + char buf[count]; + CONTROLVM_MESSAGE msg; + S64 vrtc_offset; + + memset(&msg, 0, sizeof(CONTROLVM_MESSAGE)); + + if (copy_from_user(buf, buffer, count)) { + LOGERR("copy_from_user ****FAILED.\n"); + return -EFAULT; + } + + i = sscanf(buf, "%x", &action); + + /* if (i < 1), i.e., if we didn't even read the action field, + * then action will default to 0xffff and the code below will + * fall through the switch and print usage. */ + switch (action) { + case 6: + msg.hdr.Id = CONTROLVM_CHIPSET_STOP; + msg.hdr.Flags.responseExpected = 1; + stop_chipset(&msg, NULL); + break; + case 7: + vrtc_offset = 0; + LOGERR("about to issue QUERY vrtc_offset=%LX", vrtc_offset); + vrtc_offset = Issue_VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET(); + LOGERR("result is vrtc_offset=%LX", vrtc_offset); + break; + case 8: + vrtc_offset = 60; + LOGERR("about to increase physical time by 0x%LX seconds", + vrtc_offset); + vrtc_offset = Issue_VMCALL_UPDATE_PHYSICAL_TIME(vrtc_offset); + break; + case 9: + vrtc_offset = -60; + LOGERR("about to decrease physical time by 0x%LX seconds", + vrtc_offset); + vrtc_offset = Issue_VMCALL_UPDATE_PHYSICAL_TIME(vrtc_offset); + break; + default: + LOGERR("usage: 6 for CHIPSET_STOP\n"); + LOGERR(" 7 for VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET()\n"); + LOGERR(" 8 for VMCALL_UPDATE_PHYSICAL_TIME(60)\n"); + LOGERR(" 9 for VMCALL_UPDATE_PHYSICAL_TIME(-60)\n"); + return -EFAULT; + break; + } + return count; +} + +#endif /* UISLIB_TEST_PROC */ +static struct device_info * +find_dev(U32 busNo, U32 devNo) +{ + struct bus_info *bus; + struct device_info *dev = NULL; + + read_lock(&BusListLock); + for (bus = BusListHead; bus; bus = bus->next) { + if (bus->busNo == busNo) { + /* make sure the device number is valid */ + if (devNo >= bus->deviceCount) { + LOGERR("%s bad busNo, devNo=%d,%d", + __func__, + (int) (busNo), (int) (devNo)); + goto Away; + } + dev = bus->device[devNo]; + if (!dev) + LOGERR("%s bad busNo, devNo=%d,%d", + __func__, + (int) (busNo), (int) (devNo)); + goto Away; + } + } +Away: + read_unlock(&BusListLock); + return dev; +} + +/* This thread calls the "interrupt" function for each device that has + * enabled such using uislib_enable_channel_interrupts(). The "interrupt" + * function typically reads and processes the devices's channel input + * queue. This thread repeatedly does this, until the thread is told to stop + * (via uisthread_stop()). Sleeping rules: + * - If we have called the "interrupt" function for all devices, and all of + * them have reported "nothing processed" (returned 0), then we will go to + * sleep for a maximum of POLLJIFFIES_NORMAL jiffies. + * - If anyone calls uislib_force_channel_interrupt(), the above jiffy + * sleep will be interrupted, and we will resume calling the "interrupt" + * function for all devices. + * - The list of devices is dynamically re-ordered in order to + * attempt to preserve fairness. Whenever we spin thru the list of + * devices and call the dev->interrupt() function, if we find + * devices which report that there is still more work to do, the + * the first such device we find is moved to the end of the device + * list. This ensures that extremely busy devices don't starve out + * less-busy ones. + * + */ +static int +Process_Incoming(void *v) +{ + unsigned long long cur_cycles, old_cycles, idle_cycles, delta_cycles; + struct list_head *new_tail = NULL; + int i; + UIS_DAEMONIZE("dev_incoming"); + for (i = 0; i < 16; i++) { + old_cycles = get_cycles(); + wait_event_timeout(Wakeup_Polling_Device_Channels, + 0, POLLJIFFIES_NORMAL); + cur_cycles = get_cycles(); + if (wait_cycles == 0) { + wait_cycles = (cur_cycles - old_cycles); + } else { + if (wait_cycles < (cur_cycles - old_cycles)) + wait_cycles = (cur_cycles - old_cycles); + } + } + LOGINF("wait_cycles=%llu", wait_cycles); + cycles_before_wait = wait_cycles; + idle_cycles = 0; + Go_Polling_Device_Channels = 0; + while (1) { + struct list_head *lelt, *tmp; + struct device_info *dev = NULL; + + /* poll each channel for input */ + LOCKSEM_UNINTERRUPTIBLE(&Lock_Polling_Device_Channels); + new_tail = NULL; + list_for_each_safe(lelt, tmp, &List_Polling_Device_Channels) { + int rc = 0; + dev = list_entry(lelt, struct device_info, + list_polling_device_channels); + LOCKSEM_UNINTERRUPTIBLE(&dev->interrupt_callback_lock); + if (dev->interrupt) + rc = dev->interrupt(dev->interrupt_context); + else + continue; + UNLOCKSEM(&dev->interrupt_callback_lock); + if (rc) { + /* dev->interrupt returned, but there + * is still more work to do. + * Reschedule work to occur as soon as + * possible. */ + idle_cycles = 0; + if (new_tail == NULL) { + dev->first_busy_cnt++; + if (! + (list_is_last + (lelt, + &List_Polling_Device_Channels))) { + new_tail = lelt; + dev->moved_to_tail_cnt++; + } else + dev->last_on_list_cnt++; + } + + } + if (Incoming_ThreadInfo.should_stop) + break; + } + if (new_tail != NULL) { + tot_moved_to_tail_cnt++; + list_move_tail(new_tail, &List_Polling_Device_Channels); + } + UNLOCKSEM(&Lock_Polling_Device_Channels); + cur_cycles = get_cycles(); + delta_cycles = cur_cycles - old_cycles; + old_cycles = cur_cycles; + + /* At this point, we have scanned thru all of the + * channels, and at least one of the following is true: + * - there is no input waiting on any of the channels + * - we have received a signal to stop this thread + */ + if (Incoming_ThreadInfo.should_stop) + break; + if (en_smart_wakeup == 0xFF) { + LOGINF("en_smart_wakeup set to 0xff, to force exiting process_incoming"); + break; + } + /* wait for POLLJIFFIES_NORMAL jiffies, or until + * someone wakes up Wakeup_Polling_Device_Channels, + * whichever comes first only do a wait when we have + * been idle for cycles_before_wait cycles. + */ + if (idle_cycles > cycles_before_wait) { + Go_Polling_Device_Channels = 0; + tot_wait_cnt++; + wait_event_timeout(Wakeup_Polling_Device_Channels, + Go_Polling_Device_Channels, + POLLJIFFIES_NORMAL); + Go_Polling_Device_Channels = 1; + } else { + tot_schedule_cnt++; + schedule(); + idle_cycles = idle_cycles + delta_cycles; + } + } + DBGINF("exiting.\n"); + complete_and_exit(&Incoming_ThreadInfo.has_stopped, 0); +} + +static BOOL +Initialize_incoming_thread(void) +{ + if (Incoming_Thread_Started) + return TRUE; + if (!uisthread_start(&Incoming_ThreadInfo, + &Process_Incoming, NULL, "dev_incoming")) { + LOGERR("uisthread_start Initialize_incoming_thread ****FAILED"); + return FALSE; + } + Incoming_Thread_Started = TRUE; + return TRUE; +} + +/* Add a new device/channel to the list being processed by + * Process_Incoming(). + * - indicates the function to call periodically. + * - indicates the data to pass to the + * function. + */ +void +uislib_enable_channel_interrupts(U32 busNo, U32 devNo, + int (*interrupt)(void *), + void *interrupt_context) +{ + struct device_info *dev; + dev = find_dev(busNo, devNo); + if (!dev) { + LOGERR("%s busNo=%d, devNo=%d", __func__, (int) (busNo), + (int) (devNo)); + return; + } + LOCKSEM_UNINTERRUPTIBLE(&Lock_Polling_Device_Channels); + Initialize_incoming_thread(); + dev->interrupt = interrupt; + dev->interrupt_context = interrupt_context; + dev->polling = TRUE; + list_add_tail(&(dev->list_polling_device_channels), + &List_Polling_Device_Channels); + UNLOCKSEM(&Lock_Polling_Device_Channels); +} +EXPORT_SYMBOL_GPL(uislib_enable_channel_interrupts); + +/* Remove a device/channel from the list being processed by + * Process_Incoming(). + */ +void +uislib_disable_channel_interrupts(U32 busNo, U32 devNo) +{ + struct device_info *dev; + dev = find_dev(busNo, devNo); + if (!dev) { + LOGERR("%s busNo=%d, devNo=%d", __func__, (int) (busNo), + (int) (devNo)); + return; + } + LOCKSEM_UNINTERRUPTIBLE(&Lock_Polling_Device_Channels); + list_del(&dev->list_polling_device_channels); + dev->polling = FALSE; + dev->interrupt = NULL; + UNLOCKSEM(&Lock_Polling_Device_Channels); +} +EXPORT_SYMBOL_GPL(uislib_disable_channel_interrupts); + +static void +do_wakeup_polling_device_channels(struct work_struct *dummy) +{ + if (!Go_Polling_Device_Channels) { + Go_Polling_Device_Channels = 1; + wake_up(&Wakeup_Polling_Device_Channels); + } +} + +DECLARE_WORK(Work_wakeup_polling_device_channels, + do_wakeup_polling_device_channels); + +/* Call this function when you want to send a hint to Process_Incoming() that + * your device might have more requests. + */ +void +uislib_force_channel_interrupt(U32 busNo, U32 devNo) +{ + if (en_smart_wakeup == 0) + return; + if (Go_Polling_Device_Channels) + return; + /* The point of using schedule_work() instead of just doing + * the work inline is to force a slight delay before waking up + * the Process_Incoming() thread. + */ + tot_wakeup_cnt++; + schedule_work(&Work_wakeup_polling_device_channels); +} +EXPORT_SYMBOL_GPL(uislib_force_channel_interrupt); + +/*****************************************************/ +/* Module Init & Exit functions */ +/*****************************************************/ + +static int __init +uislib_mod_init(void) +{ + + LOGINF("MONITORAPIS"); + + LOGINF("sizeof(struct uiscmdrsp):%lu bytes\n", + (ulong) sizeof(struct uiscmdrsp)); + LOGINF("sizeof(struct phys_info):%lu\n", + (ulong) sizeof(struct phys_info)); + LOGINF("sizeof(uiscmdrsp_scsi):%lu\n", + (ulong) sizeof(struct uiscmdrsp_scsi)); + LOGINF("sizeof(uiscmdrsp_net):%lu\n", + (ulong) sizeof(struct uiscmdrsp_net)); + LOGINF("sizeof(CONTROLVM_MESSAGE):%lu bytes\n", + (ulong) sizeof(CONTROLVM_MESSAGE)); + LOGINF("sizeof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL):%lu bytes\n", + (ulong) sizeof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL)); + LOGINF("sizeof(CHANNEL_HEADER):%lu bytes\n", + (ulong) sizeof(CHANNEL_HEADER)); + LOGINF("sizeof(ULTRA_IO_CHANNEL_PROTOCOL):%lu bytes\n", + (ulong) sizeof(ULTRA_IO_CHANNEL_PROTOCOL)); + LOGINF("SIZEOF_CMDRSP:%lu bytes\n", SIZEOF_CMDRSP); + LOGINF("SIZEOF_PROTOCOL:%lu bytes\n", SIZEOF_PROTOCOL); + + /* initialize global pointers to NULL */ + BusListHead = NULL; + BusListCount = MaxBusCount = 0; + rwlock_init(&BusListLock); + VirtControlChanFunc = NULL; + + /* Issue VMCALL_GET_CONTROLVM_ADDR to get CtrlChanPhysAddr and + * then map this physical address to a virtual address. */ + POSTCODE_LINUX_2(DRIVER_ENTRY_PC, POSTCODE_SEVERITY_INFO); + + /* create the proc entries for the channels */ + uislib_proc_dir = proc_mkdir(DIR_PROC_ENTRY, NULL); + /* (e.g., for /proc/uislib/vbus//info) */ + uislib_proc_vbus_dir = proc_mkdir(DIR_VBUS_PROC_ENTRY, uislib_proc_dir); + + vnic_proc_entry = proc_create(VNIC_PROC_ENTRY_FN, 0, uislib_proc_dir, + &proc_vnic_fops); + SET_PROC_OWNER(vnic_proc_entry, THIS_MODULE); + + /* for testing purposes only, create the proc entries for + * enqueuing Control Channel messages */ + chipset_proc_entry = + proc_create(CHIPSET_PROC_ENTRY_FN, 0, uislib_proc_dir, + &proc_chipset_fops); + SET_PROC_OWNER(chipset_proc_entry, THIS_MODULE); + + info_proc_entry = proc_create(INFO_PROC_ENTRY_FN, 0, uislib_proc_dir, + &proc_info_fops); + SET_PROC_OWNER(info_proc_entry, THIS_MODULE); + + platformnumber_proc_entry = + proc_create(PLATFORMNUMBER_PROC_ENTRY_FN, 0, uislib_proc_dir, + &proc_platformnumber_fops); + SET_PROC_OWNER(platformnumberinfo_proc_entry, THIS_MODULE); + + cycles_before_wait_proc_entry = + proc_create(CYCLES_BEFORE_WAIT_PROC_ENTRY_FN, 0, uislib_proc_dir, + &proc_cycles_before_wait_fops); + SET_PROC_OWNER(cycles_before_wait_proc_entry, THIS_MODULE); + + reset_counts_proc_entry = + proc_create(RESET_COUNTS_PROC_ENTRY_FN, 0, uislib_proc_dir, + &proc_reset_counts_fops); + SET_PROC_OWNER(reset_counts_proc_entry, THIS_MODULE); + + smart_wakeup_proc_entry = + proc_create(SMART_WAKEUP_PROC_ENTRY_FN, 0, uislib_proc_dir, + &proc_smart_wakeup_fops); + SET_PROC_OWNER(smart_wakeup_proc_entry, THIS_MODULE); + +#ifdef UISLIB_TEST_PROC + test_proc_entry = proc_create(TEST_PROC_ENTRY_FN, 0, uislib_proc_dir, + &proc_test_fops); + SET_PROC_OWNER(test_proc_entry, THIS_MODULE); + + bus_proc_entry = proc_create(BUS_PROC_ENTRY_FN, 0, uislib_proc_dir, + &proc_bus_fops); + SET_PROC_OWNER(bus_proc_entry, THIS_MODULE); + + dev_proc_entry = proc_create(DEV_PROC_ENTRY_FN, 0, uislib_proc_dir, + &proc_dev_fops); + SET_PROC_OWNER(dev_proc_entry, THIS_MODULE); +#endif /* UISLIB_TEST_PROC */ + POSTCODE_LINUX_3(DRIVER_EXIT_PC, 0, POSTCODE_SEVERITY_INFO); + return 0; +} + +static void __exit +uislib_mod_exit(void) +{ + if (disable_proc_entry) + remove_proc_entry(DISABLE_PROC_ENTRY_FN, uislib_proc_dir); + if (cycles_before_wait_proc_entry) + remove_proc_entry(CYCLES_BEFORE_WAIT_PROC_ENTRY_FN, + uislib_proc_dir); + if (reset_counts_proc_entry) + remove_proc_entry(RESET_COUNTS_PROC_ENTRY_FN, uislib_proc_dir); + if (smart_wakeup_proc_entry) + remove_proc_entry(SMART_WAKEUP_PROC_ENTRY_FN, uislib_proc_dir); + if (ctrlchan_proc_entry) + remove_proc_entry(CTRLCHAN_PROC_ENTRY_FN, uislib_proc_dir); + if (pmem_proc_entry) + remove_proc_entry(PMEM_PROC_ENTRY_FN, uislib_proc_dir); + if (info_proc_entry) + remove_proc_entry(INFO_PROC_ENTRY_FN, uislib_proc_dir); + if (switch_proc_entry) + remove_proc_entry(SWITCH_PROC_ENTRY_FN, uislib_proc_dir); + if (extport_proc_entry) + remove_proc_entry(EXTPORT_PROC_ENTRY_FN, uislib_proc_dir); + if (platformnumber_proc_entry) + remove_proc_entry(PLATFORMNUMBER_PROC_ENTRY_FN, + uislib_proc_dir); + if (bus_proc_entry) + remove_proc_entry(BUS_PROC_ENTRY_FN, uislib_proc_dir); + if (dev_proc_entry) + remove_proc_entry(DEV_PROC_ENTRY_FN, uislib_proc_dir); + if (vnic_proc_entry) + remove_proc_entry(VNIC_PROC_ENTRY_FN, uislib_proc_dir); + if (chipset_proc_entry) + remove_proc_entry(CHIPSET_PROC_ENTRY_FN, uislib_proc_dir); + if (uislib_proc_vbus_dir) + remove_proc_entry(DIR_VBUS_PROC_ENTRY, uislib_proc_dir); + if (uislib_proc_dir) + remove_proc_entry(DIR_PROC_ENTRY, NULL); + + if (ProcReadBuffer) { + UISVFREE(ProcReadBuffer, PROC_READ_BUFFER_SIZE); + ProcReadBuffer = NULL; + } + + DBGINF("goodbye.\n"); + return; +} + +module_init(uislib_mod_init); +module_exit(uislib_mod_exit); + +int uis_mandatory_services = -1; + +module_param_named(mandatory_services, uis_mandatory_services, + int, S_IRUGO); +MODULE_PARM_DESC(uis_mandatory_services, + "number of server drivers we expect to register (default=-1 for legacy behavior)"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Usha Srinivasan"); +MODULE_ALIAS("uislib"); + /* this is extracted during depmod and kept in modules.dep */ diff --git a/drivers/staging/unisys/uislib/uisqueue.c b/drivers/staging/unisys/uislib/uisqueue.c new file mode 100644 index 000000000000..67e413f748f5 --- /dev/null +++ b/drivers/staging/unisys/uislib/uisqueue.c @@ -0,0 +1,166 @@ +/* uisqueue.c + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* @ALL_INSPECTED */ +#include +#include + +#include "uisutils.h" + +#include "chanstub.h" + +/* this is shorter than using __FILE__ (full path name) in + * debug/info/error messages */ +#define CURRENT_FILE_PC UISLIB_PC_uisqueue_c +#define __MYFILE__ "uisqueue.c" + +#define RETVOID do { goto Away; } while (0) +#define RETINT(x) do { rc = (x); goto Away; } while (0) +#define RETPTR(x) do { rc = (x); goto Away; } while (0) +#define RETBOOL(x) do { rc = (x); goto Away; } while (0) + +#define CHECK_CACHE_ALIGN 0 + +/*****************************************************/ +/* Exported functions */ +/*****************************************************/ +unsigned long long +uisqueue_InterlockedOr(volatile unsigned long long *Target, + unsigned long long Set) +{ + unsigned long long i; + unsigned long long j; + + j = *Target; + do { + i = j; + j = uislibcmpxchg64((unsigned long long *) Target, + i, i | Set, sizeof(*(Target))); + + } while (i != j); + + return j; +} +EXPORT_SYMBOL_GPL(uisqueue_InterlockedOr); + +unsigned long long +uisqueue_InterlockedAnd(volatile unsigned long long *Target, + unsigned long long Set) +{ + unsigned long long i; + unsigned long long j; + + j = *Target; + do { + i = j; + j = uislibcmpxchg64((unsigned long long *) Target, + i, i & Set, sizeof(*(Target))); + + } while (i != j); + + return j; +} +EXPORT_SYMBOL_GPL(uisqueue_InterlockedAnd); + +static U8 +do_locked_client_insert(struct uisqueue_info *queueinfo, + unsigned int whichqueue, + void *pSignal, + spinlock_t *lock, + unsigned char issueInterruptIfEmpty, + U64 interruptHandle, U8 *channelId) +{ + unsigned long flags; + unsigned char queueWasEmpty; + unsigned int locked = 0; + unsigned int acquired = 0; + U8 rc = 0; + + spin_lock_irqsave(lock, flags); + locked = 1; + + if (!ULTRA_CHANNEL_CLIENT_ACQUIRE_OS(queueinfo->chan, channelId, NULL)) + RETINT(0); + + acquired = 1; + + queueWasEmpty = SignalQueueIsEmpty(queueinfo->chan, whichqueue); + if (!SignalInsert(queueinfo->chan, whichqueue, pSignal)) + RETINT(0); + ULTRA_CHANNEL_CLIENT_RELEASE_OS(queueinfo->chan, channelId, NULL); + acquired = 0; + spin_unlock_irqrestore(lock, flags); + locked = 0; + + queueinfo->packets_sent++; + + RETINT(1); + +Away: + if (acquired) { + ULTRA_CHANNEL_CLIENT_RELEASE_OS(queueinfo->chan, channelId, + NULL); + acquired = 0; + } + if (locked) { + spin_unlock_irqrestore((spinlock_t *) lock, flags); + locked = 0; + } + + return rc; +} + +int +uisqueue_put_cmdrsp_with_lock_client(struct uisqueue_info *queueinfo, + struct uiscmdrsp *cmdrsp, + unsigned int whichqueue, + void *insertlock, + unsigned char issueInterruptIfEmpty, + U64 interruptHandle, + char oktowait, U8 *channelId) +{ + while (!do_locked_client_insert(queueinfo, whichqueue, cmdrsp, + (spinlock_t *) insertlock, + issueInterruptIfEmpty, + interruptHandle, channelId)) { + if (oktowait != OK_TO_WAIT) { + LOGERR("****FAILED SignalInsert failed; cannot wait; insert aborted\n"); + return 0; /* failed to queue */ + } + /* try again */ + LOGERR("****FAILED SignalInsert failed; waiting to try again\n"); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(10)); + } + return 1; +} +EXPORT_SYMBOL_GPL(uisqueue_put_cmdrsp_with_lock_client); + +/* uisqueue_get_cmdrsp gets the cmdrsp entry at the head of the queue + * returns NULL if queue is empty */ +int +uisqueue_get_cmdrsp(struct uisqueue_info *queueinfo, + void *cmdrsp, unsigned int whichqueue) +{ + if (!SignalRemove(queueinfo->chan, whichqueue, cmdrsp)) + return 0; + + queueinfo->packets_received++; + + return 1; /* Success */ +} +EXPORT_SYMBOL_GPL(uisqueue_get_cmdrsp); diff --git a/drivers/staging/unisys/uislib/uisthread.c b/drivers/staging/unisys/uislib/uisthread.c new file mode 100644 index 000000000000..ecf4bfcb34a6 --- /dev/null +++ b/drivers/staging/unisys/uislib/uisthread.c @@ -0,0 +1,85 @@ +/* uisthread.c + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* @ALL_INSPECTED */ +#include +#include +#include +#include +#include "uniklog.h" +#include "uisutils.h" +#include "uisthread.h" + +#define KILL(a, b, c) kill_pid(find_vpid(a), b, c) + +/* this is shorter than using __FILE__ (full path name) in + * debug/info/error messages + */ +#define CURRENT_FILE_PC UISLIB_PC_uisthread_c +#define __MYFILE__ "uisthread.c" + +/*****************************************************/ +/* Exported functions */ +/*****************************************************/ + +/* returns 0 for failure, 1 for success */ +int +uisthread_start(struct uisthread_info *thrinfo, + int (*threadfn)(void *), void *thrcontext, char *name) +{ + thrinfo->should_stop = 0; + /* used to stop the thread */ + init_completion(&thrinfo->has_stopped); + thrinfo->task = kthread_create(threadfn, thrcontext, name, NULL); + if (thrinfo->task == NULL) { + thrinfo->id = 0; + return 0; /* failure */ + } + thrinfo->id = thrinfo->task->pid; + wake_up_process(thrinfo->task); + LOGINF("started thread pid:%d\n", thrinfo->id); + return 1; + +} +EXPORT_SYMBOL_GPL(uisthread_start); + +void +uisthread_stop(struct uisthread_info *thrinfo) +{ + int ret; + int stopped = 0; + if (thrinfo->id == 0) + return; /* thread not running */ + + LOGINF("uisthread_stop stopping id:%d\n", thrinfo->id); + thrinfo->should_stop = 1; + ret = KILL(thrinfo->id, SIGHUP, 1); + if (ret) { + LOGERR("unable to signal thread %d\n", ret); + } else { + /* give up if the thread has NOT died in 1 minute */ + if (wait_for_completion_timeout(&thrinfo->has_stopped, 60 * HZ)) + stopped = 1; + else + LOGERR("timed out trying to signal thread\n"); + } + if (stopped) { + LOGINF("uisthread_stop stopped id:%d\n", thrinfo->id); + thrinfo->id = 0; + } +} +EXPORT_SYMBOL_GPL(uisthread_stop); diff --git a/drivers/staging/unisys/uislib/uisutils.c b/drivers/staging/unisys/uislib/uisutils.c new file mode 100644 index 000000000000..974f8bc8c68a --- /dev/null +++ b/drivers/staging/unisys/uislib/uisutils.c @@ -0,0 +1,349 @@ +/* uisutils.c + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#include +#include +#include +#include +#include +#include "uniklog.h" +#include "uisutils.h" +#include "version.h" +#include "vbushelper.h" +#include "guidutils.h" +#include +#ifdef CONFIG_HIGHMEM +#include +#endif + +/* this is shorter than using __FILE__ (full path name) in + * debug/info/error messages + */ +#define CURRENT_FILE_PC UISLIB_PC_uisutils_c +#define __MYFILE__ "uisutils.c" + +/* exports */ +atomic_t UisUtils_Registered_Services = ATOMIC_INIT(0); + /* num registrations via + * uisctrl_register_req_handler() or + * uisctrl_register_req_handler_ex() */ + + +/*****************************************************/ +/* Utility functions */ +/*****************************************************/ + +int +util_add_proc_line_ex(int *total, char **buffer, int *buffer_remaining, + char *format, ...) +{ + va_list args; + int len; + + DBGINF("buffer = 0x%p : *buffer = 0x%p.\n", buffer, *buffer); + va_start(args, format); + len = vsnprintf(*buffer, *buffer_remaining, format, args); + if (len >= *buffer_remaining) { + *buffer += *buffer_remaining; + *total += *buffer_remaining; + *buffer_remaining = 0; + LOGERR("bytes remaining is too small!\n"); + return -1; + } + *buffer_remaining -= len; + *buffer += len; + *total += len; + return len; +} +EXPORT_SYMBOL_GPL(util_add_proc_line_ex); + +int +uisctrl_register_req_handler(int type, void *fptr, + ULTRA_VBUS_DEVICEINFO *chipset_DriverInfo) +{ + LOGINF("type = %d, fptr = 0x%p.\n", type, fptr); + + switch (type) { + case 2: + if (fptr) { + if (!VirtControlChanFunc) + atomic_inc(&UisUtils_Registered_Services); + VirtControlChanFunc = fptr; + } else { + if (VirtControlChanFunc) + atomic_dec(&UisUtils_Registered_Services); + VirtControlChanFunc = NULL; + } + break; + + default: + LOGERR("invalid type %d.\n", type); + return 0; + } + if (chipset_DriverInfo) + BusDeviceInfo_Init(chipset_DriverInfo, + "chipset", "uislib", + VERSION, NULL, __DATE__, __TIME__); + + return 1; +} +EXPORT_SYMBOL_GPL(uisctrl_register_req_handler); + +int +uisctrl_register_req_handler_ex(GUID switchTypeGuid, + const char *switch_type_name, + int (*controlfunc)(struct io_msgs *), + unsigned long min_channel_bytes, + int (*Server_Channel_Ok)(unsigned long + channelBytes), + int (*Server_Channel_Init) + (void *x, unsigned char *clientStr, + U32 clientStrLen, U64 bytes), + ULTRA_VBUS_DEVICEINFO *chipset_DriverInfo) +{ + char s[99]; + ReqHandlerInfo_t *pReqHandlerInfo; + int rc = 0; /* assume failure */ + LOGINF("type=%s, controlfunc=0x%p.\n", + GUID_format1(&switchTypeGuid, s), controlfunc); + if (!controlfunc) { + LOGERR("%s: controlfunc must be supplied\n", + GUID_format1(&switchTypeGuid, s)); + goto Away; + } + if (!Server_Channel_Ok) { + LOGERR("%s: Server_Channel_Ok must be supplied\n", + GUID_format1(&switchTypeGuid, s)); + goto Away; + } + if (!Server_Channel_Init) { + LOGERR("%s: Server_Channel_Init must be supplied\n", + GUID_format1(&switchTypeGuid, s)); + goto Away; + } + pReqHandlerInfo = ReqHandlerAdd(switchTypeGuid, + switch_type_name, + controlfunc, + min_channel_bytes, + Server_Channel_Ok, Server_Channel_Init); + if (!pReqHandlerInfo) { + LOGERR("failed to add %s to server list\n", + GUID_format1(&switchTypeGuid, s)); + goto Away; + } + + atomic_inc(&UisUtils_Registered_Services); + rc = 1; /* success */ +Away: + if (rc) { + if (chipset_DriverInfo) + BusDeviceInfo_Init(chipset_DriverInfo, + "chipset", "uislib", + VERSION, NULL, + __DATE__, __TIME__); + } else + LOGERR("failed to register type %s.\n", + GUID_format1(&switchTypeGuid, s)); + + return rc; +} +EXPORT_SYMBOL_GPL(uisctrl_register_req_handler_ex); + +int +uisctrl_unregister_req_handler_ex(GUID switchTypeGuid) +{ + char s[99]; + int rc = 0; /* assume failure */ + LOGINF("type=%s.\n", GUID_format1(&switchTypeGuid, s)); + if (ReqHandlerDel(switchTypeGuid) < 0) { + LOGERR("failed to remove %s from server list\n", + GUID_format1(&switchTypeGuid, s)); + goto Away; + } + atomic_dec(&UisUtils_Registered_Services); + rc = 1; /* success */ +Away: + if (!rc) + LOGERR("failed to unregister type %s.\n", + GUID_format1(&switchTypeGuid, s)); + return rc; +} +EXPORT_SYMBOL_GPL(uisctrl_unregister_req_handler_ex); + +/* + * unsigned int util_copy_fragsinfo_from_skb(unsigned char *calling_ctx, + * void *skb_in, + * unsigned int firstfraglen, + * unsigned int frags_max, + * struct phys_info frags[]) + * + * calling_ctx - input - a string that is displayed to show + * who called * this func + * void *skb_in - skb whose frag info we're copying type is hidden so we + * don't need to include skbbuff in uisutils.h which is + * included in non-networking code. + * unsigned int firstfraglen - input - length of first fragment in skb + * unsigned int frags_max - input - max len of frags array + * struct phys_info frags[] - output - frags array filled in on output + * return value indicates number of + * entries filled in frags + */ +unsigned int +util_copy_fragsinfo_from_skb(unsigned char *calling_ctx, void *skb_in, + unsigned int firstfraglen, unsigned int frags_max, + struct phys_info frags[]) +{ + unsigned int count = 0, ii, size, offset = 0, numfrags; + struct sk_buff *skb = skb_in; + + numfrags = skb_shinfo(skb)->nr_frags; + + while (firstfraglen) { + if (count == frags_max) { + LOGERR("%s frags array too small: max:%d count:%d\n", + calling_ctx, frags_max, count); + return -1; /* failure */ + } + frags[count].pi_pfn = + page_to_pfn(virt_to_page(skb->data + offset)); + frags[count].pi_off = + (unsigned long) (skb->data + offset) & PI_PAGE_MASK; + size = + min(firstfraglen, + (unsigned int) (PI_PAGE_SIZE - frags[count].pi_off)); + /* can take smallest of firstfraglen(what's left) OR + * bytes left in the page + */ + frags[count].pi_len = size; + firstfraglen -= size; + offset += size; + count++; + } + if (numfrags) { + if ((count + numfrags) > frags_max) { + LOGERR("**** FAILED %s frags array too small: max:%d count+nr_frags:%d\n", + calling_ctx, frags_max, count + numfrags); + return -1; /* failure */ + } + + for (ii = 0; ii < numfrags; ii++) { + count = add_physinfo_entries(page_to_pfn(skb_frag_page(&skb_shinfo(skb)->frags[ii])), /* pfn */ + skb_shinfo(skb)->frags[ii]. + page_offset, + skb_shinfo(skb)->frags[ii]. + size, count, frags_max, + frags); + if (count == 0) { + LOGERR("**** FAILED to add physinfo entries\n"); + return -1; /* failure */ + } + } + } + if (skb_shinfo(skb)->frag_list) { + struct sk_buff *skbinlist; + int c; + for (skbinlist = skb_shinfo(skb)->frag_list; skbinlist; + skbinlist = skbinlist->next) { + + c = util_copy_fragsinfo_from_skb("recursive", skbinlist, + skbinlist->len - + skbinlist->data_len, + frags_max - count, + &frags[count]); + if (c == -1) { + LOGERR("**** FAILED recursive call failed\n"); + return -1; + } + count += c; + } + } + return count; +} +EXPORT_SYMBOL_GPL(util_copy_fragsinfo_from_skb); + +static LIST_HEAD(ReqHandlerInfo_list); /* list of ReqHandlerInfo_t */ +static DEFINE_SPINLOCK(ReqHandlerInfo_list_lock); + +ReqHandlerInfo_t * +ReqHandlerAdd(GUID switchTypeGuid, + const char *switch_type_name, + int (*controlfunc)(struct io_msgs *), + unsigned long min_channel_bytes, + int (*Server_Channel_Ok)(unsigned long channelBytes), + int (*Server_Channel_Init) + (void *x, unsigned char *clientStr, U32 clientStrLen, U64 bytes)) +{ + ReqHandlerInfo_t *rc = NULL; + + rc = UISMALLOC(sizeof(*rc), GFP_ATOMIC); + if (!rc) + return NULL; + memset(rc, 0, sizeof(*rc)); + rc->switchTypeGuid = switchTypeGuid; + rc->controlfunc = controlfunc; + rc->min_channel_bytes = min_channel_bytes; + rc->Server_Channel_Ok = Server_Channel_Ok; + rc->Server_Channel_Init = Server_Channel_Init; + if (switch_type_name) + strncpy(rc->switch_type_name, switch_type_name, + sizeof(rc->switch_type_name) - 1); + spin_lock(&ReqHandlerInfo_list_lock); + list_add_tail(&(rc->list_link), &ReqHandlerInfo_list); + spin_unlock(&ReqHandlerInfo_list_lock); + + return rc; +} + +ReqHandlerInfo_t * +ReqHandlerFind(GUID switchTypeGuid) +{ + struct list_head *lelt, *tmp; + ReqHandlerInfo_t *entry = NULL; + spin_lock(&ReqHandlerInfo_list_lock); + list_for_each_safe(lelt, tmp, &ReqHandlerInfo_list) { + entry = list_entry(lelt, ReqHandlerInfo_t, list_link); + if (memcmp + (&entry->switchTypeGuid, &switchTypeGuid, + sizeof(GUID)) == 0) { + spin_unlock(&ReqHandlerInfo_list_lock); + return entry; + } + } + spin_unlock(&ReqHandlerInfo_list_lock); + return NULL; +} + +int +ReqHandlerDel(GUID switchTypeGuid) +{ + struct list_head *lelt, *tmp; + ReqHandlerInfo_t *entry = NULL; + int rc = -1; + spin_lock(&ReqHandlerInfo_list_lock); + list_for_each_safe(lelt, tmp, &ReqHandlerInfo_list) { + entry = list_entry(lelt, ReqHandlerInfo_t, list_link); + if (memcmp + (&entry->switchTypeGuid, &switchTypeGuid, + sizeof(GUID)) == 0) { + list_del(lelt); + UISFREE(entry, sizeof(*entry)); + rc++; + } + } + spin_unlock(&ReqHandlerInfo_list_lock); + return rc; +}