From: Benjamin Romer <benjamin.romer@xxxxxxxxxx> This patch adds the visorclientbus driver to the Unisys s-Par driver set. This driver is responsible for helping manage virtual devices like keyboards, mice, serial ports, displays, and any customized drivers for special-purpose guests. Signed-off-by: Benjamin Romer <benjamin.romer@xxxxxxxxxx> Signed-off-by: Ken Cox <jkc@xxxxxxxxxx> --- drivers/staging/unisys/Kconfig | 1 + drivers/staging/unisys/Makefile | 1 + drivers/staging/unisys/visorclientbus/Kconfig | 9 + drivers/staging/unisys/visorclientbus/Makefile | 13 + .../unisys/visorclientbus/visorclientbus_main.c | 378 +++++++++++++++++++++ 5 files changed, 402 insertions(+) create mode 100644 drivers/staging/unisys/visorclientbus/Kconfig create mode 100644 drivers/staging/unisys/visorclientbus/Makefile create mode 100644 drivers/staging/unisys/visorclientbus/visorclientbus_main.c diff --git a/drivers/staging/unisys/Kconfig b/drivers/staging/unisys/Kconfig index ac080c9..d63b035 100644 --- a/drivers/staging/unisys/Kconfig +++ b/drivers/staging/unisys/Kconfig @@ -16,5 +16,6 @@ source "drivers/staging/unisys/channels/Kconfig" source "drivers/staging/unisys/uislib/Kconfig" source "drivers/staging/unisys/virtpci/Kconfig" source "drivers/staging/unisys/virthba/Kconfig" +source "drivers/staging/unisys/visorclientbus/Kconfig" endif # UNISYSSPAR diff --git a/drivers/staging/unisys/Makefile b/drivers/staging/unisys/Makefile index b988d69..794177b 100644 --- a/drivers/staging/unisys/Makefile +++ b/drivers/staging/unisys/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_UNISYS_CHANNELSTUB) += channels/ obj-$(CONFIG_UNISYS_UISLIB) += uislib/ obj-$(CONFIG_UNISYS_VIRTPCI) += virtpci/ obj-$(CONFIG_UNISYS_VIRTHBA) += virthba/ +obj-$(CONFIG_UNISYS_VISORCLIENTBUS) += visorclientbus/ diff --git a/drivers/staging/unisys/visorclientbus/Kconfig b/drivers/staging/unisys/visorclientbus/Kconfig new file mode 100644 index 0000000..62bdbb9 --- /dev/null +++ b/drivers/staging/unisys/visorclientbus/Kconfig @@ -0,0 +1,9 @@ +# +# Unisys visorclientbus configuration +# + +config UNISYS_VISORCLIENTBUS + tristate "Unisys visorclientbus driver" + depends on UNISYSSPAR && UNISYS_VISORCHIPSET && UNISYS_UISLIB + ---help--- + If you say Y here, you will enable the Unisys visorclientbus driver. diff --git a/drivers/staging/unisys/visorclientbus/Makefile b/drivers/staging/unisys/visorclientbus/Makefile new file mode 100644 index 0000000..bfacc0d --- /dev/null +++ b/drivers/staging/unisys/visorclientbus/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for Unisys visorclientbus +# + +obj-$(CONFIG_UNISYS_VISORCLIENTBUS) += visorclientbus.o + +visorclientbus-y := visorclientbus_main.o + +ccflags-y += -Idrivers/staging/unisys/include +ccflags-y += -Idrivers/staging/unisys/uislib +ccflags-y += -Idrivers/staging/unisys/visorchipset +ccflags-y += -Idrivers/staging/unisys/common-spar/include +ccflags-y += -Idrivers/staging/unisys/common-spar/include/channels diff --git a/drivers/staging/unisys/visorclientbus/visorclientbus_main.c b/drivers/staging/unisys/visorclientbus/visorclientbus_main.c new file mode 100644 index 0000000..0c560a1 --- /dev/null +++ b/drivers/staging/unisys/visorclientbus/visorclientbus_main.c @@ -0,0 +1,378 @@ +/* visorclientbus_main.c + * + * Copyright (C) 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. + */ + +/* + * This module processes bus+device messages received for devices which we are + * to act as a client for. Currently the only device for which we can act + * as a client is VNIC. + */ + +#include "timskmod.h" +#include "visorchipset.h" +#include "uisutils.h" +#include "iochannel.h" +#include "version.h" +#include "guestlinuxdebug.h" +#include <linux/mm.h> + +#define CURRENT_FILE_PC VISOR_CLIENT_BUS_PC_visorclientbus_main_c +#define MYDRVNAME "visorclientbus" + +static int dump_vhba_bus = -1; + +static void chipset_bus_create(ulong bus_no); +static void chipset_bus_destroy(ulong bus_no); + +static void chipset_device_create(ulong bus_no, ulong dev_no); +static void chipset_device_destroy(ulong bus_no, ulong dev_no); +static void chipset_device_pause(ulong bus_no, ulong dev_no); +static void chipset_device_resume(ulong bus_no, ulong dev_no); + +/* These functions are implemented herein, and are called by the chipset + * driver to notify us about specific events. + */ +static struct visorchipset_busdev_notifiers chipset_notifiers = { + .bus_create = chipset_bus_create, + .bus_destroy = chipset_bus_destroy, + .device_create = chipset_device_create, + .device_destroy = chipset_device_destroy, + .device_pause = chipset_device_pause, + .device_resume = chipset_device_resume, + .get_channel_info = NULL, +}; + +/* These functions are implemented in the chipset driver, and we call them + * herein when we want to acknowledge a specific event. + */ +static struct visorchipset_busdev_responders chipset_responders; + +/* filled in with info about parent chipset driver when we register with it */ +static struct ultra_vbus_deviceinfo chipset_driver_info; + +static void __iomem * +get_virt(u64 phys_addr, u32 bytes, enum visorchipset_addresstype addr_type) +{ + struct resource *tmp, **p; + struct resource *root = NULL; + void __iomem *pcpy = NULL; + + if (addr_type == ADDRTYPE_LOCALTEST) { + if (phys_addr > virt_to_phys(high_memory - 1)) { + ERRDRV("%s - bad localTest address for channel (0x%-16.16Lx for %lu bytes)", + __func__, + (unsigned long long)phys_addr, (ulong)bytes); + return NULL; + } + return (void __iomem *)__va(phys_addr); + } else if (addr_type == ADDRTYPE_LOCALPHYSICAL) { + /* walk through the "iomem_resource" tables, check the requested + * channel addresses is in RESERVED or UNDEFINED/AVAILABLE or + * greater than HIGH_MEMORY. If channel addresses is TRUE with + * the above mentioned scenario, then use ioremap_cache to get a + * valid pointer otherwise return NULL. */ + root = &iomem_resource; /* Root node, Global var */ + p = &root->child; + for (;;) { + tmp = *p; + if (!tmp || tmp->start > (phys_addr + bytes - 1)) { + /* Memory region is undefined */ + break; + } + p = &tmp->sibling; + if (tmp->end < phys_addr) /* start */ + continue; + + if (phys_addr <= virt_to_phys(high_memory - 1)) { + /* Memory is reserved and within HIGH_MEMORY */ + ERRDRV("%s - localPhysical address overlaps memory our OS is currently using! (0x%-16.16Lx for %lu bytes)", + __func__, + (unsigned long long)phys_addr, + (ulong)bytes); + return NULL; + } + break; /* greater then HIGH_MEMORY */ + } + /* come out, if Memory is undefined or greater then + * HIGM_MEMORY + */ + if (phys_addr > (u64)ULONG_MAX) { + ERRDRV("%s - localPhysical address is too large to be be mapped (0x%-16.16Lx for %lu bytes)", + __func__, + (unsigned long long)phys_addr, (ulong)bytes); + return NULL; + } + pcpy = ioremap_cache((ulong)phys_addr, (ulong)bytes); + if (pcpy == NULL) { + ERRDRV("%s - ioremap_cache(0x%lx, %lu) failed", + __func__, (ulong)phys_addr, (ulong)bytes); + return NULL; + } + return pcpy; + } + return NULL; +} + +static void __iomem *chipset_preamble(ulong bus_no, ulong dev_no, + struct visorchipset_device_info *devinfo) +{ + if (!visorchipset_get_device_info(bus_no, dev_no, devinfo)) { + ERRDRV("%s - visorchipset_get_device_info returned false", + __func__); + return NULL; + } + if ((uuid_le_cmp(devinfo->chan_info.channel_type_uuid, + spar_vnic_channel_protocol_uuid) != 0) && + (uuid_le_cmp(devinfo->chan_info.channel_type_uuid, + spar_vhba_channel_protocol_uuid) != 0)) { + ERRDRV("%s - I only know how to handle VNIC or VHBA client channels", + __func__); + return NULL; + } + return get_virt(devinfo->chan_info.channel_addr, + devinfo->chan_info.n_channel_bytes, + devinfo->chan_info.addr_type); +} + +static void +chipset_bus_create(ulong bus_no) +{ + int rc = 0; + u64 channeladdr = 0; + ulong nchannelbytes = 0; + struct visorchipset_bus_info businfo; + struct controlvm_message msg; + + POSTCODE_LINUX_3(BUS_CREATE_ENTRY_PC, bus_no, POSTCODE_SEVERITY_INFO); + if ((visorchipset_get_bus_info(bus_no, &businfo)) && + (businfo.chan_info.channel_addr > 0) && + (businfo.chan_info.n_channel_bytes > 0)) { + channeladdr = businfo.chan_info.channel_addr; + nchannelbytes = (ulong)businfo.chan_info.n_channel_bytes; + } + /* Save off message with IOVM bus info in case of crash */ + if ((uuid_le_cmp(businfo.chan_info.channel_inst_uuid, + spar_siovm_uuid) == 0)) { + msg.hdr.id = CONTROLVM_BUS_CREATE; + msg.hdr.flags.response_expected = 0; + msg.hdr.flags.server = 0; + msg.cmd.create_bus.bus_no = bus_no; + msg.cmd.create_bus.dev_count = businfo.dev_no; + msg.cmd.create_bus.channel_addr = channeladdr; + msg.cmd.create_bus.channel_bytes = nchannelbytes; + dump_vhba_bus = bus_no; + visorchipset_save_message(&msg, CRASH_BUS); + } + + if (!uislib_client_inject_add_bus(bus_no, + spar_vbus_channel_protocol_uuid, + channeladdr, nchannelbytes)) { + rc = -1; + } + + if (chipset_responders.bus_create) + (*chipset_responders.bus_create) (bus_no, rc); +} + +static void +chipset_bus_destroy(ulong bus_no) +{ + int rc = 0; + + if (!uislib_client_inject_del_bus(bus_no)) + rc = -1; + + if (chipset_responders.bus_destroy) + (*chipset_responders.bus_destroy) (bus_no, rc); +} + +static void +chipset_device_create(ulong bus_no, ulong dev_no) +{ + void __iomem *paddr = NULL; + int rc = 0; + struct visorchipset_device_info dev_info; + struct controlvm_message msg; + + paddr = chipset_preamble(bus_no, dev_no, &dev_info); + POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, dev_no, bus_no, + POSTCODE_SEVERITY_INFO); + + if (!paddr) { + rc = -1; + goto cleanup; + } + if (uuid_le_cmp(dev_info.chan_info.channel_type_uuid, + spar_vnic_channel_protocol_uuid) == 0) { + if (!uislib_client_inject_add_vnic + (bus_no, dev_no, + dev_info.chan_info.channel_addr, + dev_info.chan_info.n_channel_bytes, + dev_info.chan_info.addr_type == ADDRTYPE_LOCALTEST, + dev_info.dev_inst_uuid, &dev_info.chan_info.intr)) { + rc = -2; + goto cleanup; + } + goto cleanup; + } else if (uuid_le_cmp(dev_info.chan_info.channel_type_uuid, + spar_vhba_channel_protocol_uuid) == 0) { + /* Save off message with hba info in case of crash */ + if (bus_no == dump_vhba_bus) { + msg.hdr.id = CONTROLVM_DEVICE_CREATE; + msg.hdr.flags.response_expected = 0; + msg.hdr.flags.server = 0; + msg.cmd.create_device.bus_no = bus_no; + msg.cmd.create_device.dev_no = dev_no; + msg.cmd.create_device.dev_inst_uuid = + dev_info.dev_inst_uuid; + msg.cmd.create_device.intr = dev_info.chan_info.intr; + msg.cmd.create_device.channel_addr = + dev_info.chan_info.channel_addr; + msg.cmd.create_device.channel_bytes = + dev_info.chan_info.n_channel_bytes; + msg.cmd.create_device.data_type_uuid = + spar_vhba_channel_protocol_uuid; + visorchipset_save_message(&msg, CRASH_DEV); + } + + if (!uislib_client_inject_add_vhba + (bus_no, dev_no, + dev_info.chan_info.channel_addr, + dev_info.chan_info.n_channel_bytes, + dev_info.chan_info.addr_type == ADDRTYPE_LOCALTEST, + dev_info.dev_inst_uuid, &dev_info.chan_info.intr)) { + rc = -3; + goto cleanup; + } + goto cleanup; + } + + rc = -4; /* unsupported GUID */ +cleanup: + if (chipset_responders.device_create) + (*chipset_responders.device_create) (bus_no, dev_no, rc); +} + +static void +chipset_device_destroy(ulong bus_no, ulong dev_no) +{ + void __iomem *paddr = NULL; + int rc = 0; + struct visorchipset_device_info dev_info; + + paddr = chipset_preamble(bus_no, dev_no, &dev_info); + if (!paddr) { + rc = -1; + goto cleanup; + } + if (uuid_le_cmp(dev_info.chan_info.channel_type_uuid, + spar_vnic_channel_protocol_uuid) == 0) { + uislib_client_inject_del_vnic(bus_no, dev_no); + goto cleanup; + } else if (uuid_le_cmp(dev_info.chan_info.channel_type_uuid, + spar_vhba_channel_protocol_uuid) == 0) { + uislib_client_inject_del_vhba(bus_no, dev_no); + goto cleanup; + } + rc = -1; /* no match on GUID */ +cleanup: + if (chipset_responders.device_destroy) + (*chipset_responders.device_destroy) (bus_no, dev_no, rc); +} + +static void +chipset_device_pause(ulong bus_no, ulong dev_no) +{ + void __iomem *paddr = NULL; + int rc = 0; + struct visorchipset_device_info dev_info; + + paddr = chipset_preamble(bus_no, dev_no, &dev_info); + if (!paddr) { + rc = -1; + goto cleanup; + } + if (uuid_le_cmp(dev_info.chan_info.channel_type_uuid, + spar_vnic_channel_protocol_uuid) == 0) { + rc = uislib_client_inject_pause_vnic(bus_no, dev_no); + goto cleanup; + } else if (uuid_le_cmp(dev_info.chan_info.channel_type_uuid, + spar_vhba_channel_protocol_uuid) == 0) { + rc = uislib_client_inject_pause_vhba(bus_no, dev_no); + goto cleanup; + } + rc = -1; /* no match on GUID */ +cleanup: + if ((rc != CONTROLVM_RESP_SUCCESS) && chipset_responders.device_pause) + (*chipset_responders.device_pause)(bus_no, dev_no, rc); +} + +static void +chipset_device_resume(ulong bus_no, ulong dev_no) +{ + void __iomem *paddr = NULL; + int rc = 0; + struct visorchipset_device_info dev_info; + + paddr = chipset_preamble(bus_no, dev_no, &dev_info); + if (!paddr) { + rc = -1; + goto cleanup; + } + if (uuid_le_cmp(dev_info.chan_info.channel_type_uuid, + spar_vnic_channel_protocol_uuid) == 0) { + rc = uislib_client_inject_resume_vnic(bus_no, dev_no); + goto cleanup; + } else if (uuid_le_cmp(dev_info.chan_info.channel_type_uuid, + spar_vhba_channel_protocol_uuid) == 0) { + rc = uislib_client_inject_resume_vhba(bus_no, dev_no); + goto cleanup; + } + rc = -1; /* no match on GUID */ +cleanup: + if (chipset_responders.device_resume) + (*chipset_responders.device_resume) (bus_no, dev_no, rc); +} + +static int __init +visorclientbus_init(void) +{ + POSTCODE_LINUX_2(CHIPSET_INIT_ENTRY_PC, POSTCODE_SEVERITY_INFO); + /* This enables us to receive notifications when devices appear for + * which this service partition is to be a client for. + */ + visorchipset_register_busdev_client(&chipset_notifiers, + &chipset_responders, + &chipset_driver_info); + + POSTCODE_LINUX_2(CHIPSET_INIT_EXIT_PC, POSTCODE_SEVERITY_INFO); + + return 0; +} + +static void +visorclientbus_exit(void) +{ + visorchipset_register_busdev_client(NULL, NULL, NULL); +} + +module_init(visorclientbus_init); +module_exit(visorclientbus_exit); + +MODULE_AUTHOR("Unisys"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("client device bus driver for service partition"); +MODULE_VERSION(VERSION); -- 1.9.3 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel