From 93d3ad90c2d470804b16f79e7e872408747d3e77 Mon Sep 17 00:00:00 2001 From: David Kershner Date: Thu, 7 Dec 2017 12:11:07 -0500 Subject: drivers: visorbus: move driver out of staging Move the visorbus driver out of staging (drivers/staging/unisys/visorbus) and to drivers/visorbus. Modify the configuration and makefiles so they now reference the new location. The s-Par header file visorbus.h that is referenced by all s-Par drivers, is being moved into include/linux. Signed-off-by: David Kershner Reviewed-by: Tim Sell Signed-off-by: Greg Kroah-Hartman --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/staging/unisys/Kconfig | 1 - drivers/staging/unisys/Makefile | 1 - drivers/staging/unisys/include/iochannel.h | 3 +- drivers/staging/unisys/include/visorbus.h | 344 ---- drivers/staging/unisys/visorbus/Kconfig | 15 - drivers/staging/unisys/visorbus/Makefile | 12 - drivers/staging/unisys/visorbus/controlvmchannel.h | 651 -------- drivers/staging/unisys/visorbus/vbuschannel.h | 95 -- drivers/staging/unisys/visorbus/visorbus_main.c | 1234 -------------- drivers/staging/unisys/visorbus/visorbus_private.h | 48 - drivers/staging/unisys/visorbus/visorchannel.c | 434 ----- drivers/staging/unisys/visorbus/visorchipset.c | 1686 -------------------- drivers/staging/unisys/visorhba/visorhba_main.c | 2 +- drivers/staging/unisys/visorinput/visorinput.c | 2 +- drivers/staging/unisys/visornic/visornic_main.c | 2 +- drivers/visorbus/Kconfig | 14 + drivers/visorbus/Makefile | 10 + drivers/visorbus/controlvmchannel.h | 650 ++++++++ drivers/visorbus/vbuschannel.h | 95 ++ drivers/visorbus/visorbus_main.c | 1234 ++++++++++++++ drivers/visorbus/visorbus_private.h | 48 + drivers/visorbus/visorchannel.c | 434 +++++ drivers/visorbus/visorchipset.c | 1686 ++++++++++++++++++++ 25 files changed, 4178 insertions(+), 4526 deletions(-) delete mode 100644 drivers/staging/unisys/include/visorbus.h delete mode 100644 drivers/staging/unisys/visorbus/Kconfig delete mode 100644 drivers/staging/unisys/visorbus/Makefile delete mode 100644 drivers/staging/unisys/visorbus/controlvmchannel.h delete mode 100644 drivers/staging/unisys/visorbus/vbuschannel.h delete mode 100644 drivers/staging/unisys/visorbus/visorbus_main.c delete mode 100644 drivers/staging/unisys/visorbus/visorbus_private.h delete mode 100644 drivers/staging/unisys/visorbus/visorchannel.c delete mode 100644 drivers/staging/unisys/visorbus/visorchipset.c create mode 100644 drivers/visorbus/Kconfig create mode 100644 drivers/visorbus/Makefile create mode 100644 drivers/visorbus/controlvmchannel.h create mode 100644 drivers/visorbus/vbuschannel.h create mode 100644 drivers/visorbus/visorbus_main.c create mode 100644 drivers/visorbus/visorbus_private.h create mode 100644 drivers/visorbus/visorchannel.c create mode 100644 drivers/visorbus/visorchipset.c (limited to 'drivers') diff --git a/drivers/Kconfig b/drivers/Kconfig index 152744c5ef0f..ef5fb8395d76 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -211,4 +211,6 @@ source "drivers/mux/Kconfig" source "drivers/opp/Kconfig" +source "drivers/visorbus/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index e06f7f633f73..7a2330077e47 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -184,3 +184,4 @@ obj-$(CONFIG_FPGA) += fpga/ obj-$(CONFIG_FSI) += fsi/ obj-$(CONFIG_TEE) += tee/ obj-$(CONFIG_MULTIPLEXER) += mux/ +obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ diff --git a/drivers/staging/unisys/Kconfig b/drivers/staging/unisys/Kconfig index 4d190389aa6c..c27dab3b610f 100644 --- a/drivers/staging/unisys/Kconfig +++ b/drivers/staging/unisys/Kconfig @@ -8,7 +8,6 @@ menuconfig UNISYSSPAR if UNISYSSPAR -source "drivers/staging/unisys/visorbus/Kconfig" source "drivers/staging/unisys/visornic/Kconfig" source "drivers/staging/unisys/visorinput/Kconfig" source "drivers/staging/unisys/visorhba/Kconfig" diff --git a/drivers/staging/unisys/Makefile b/drivers/staging/unisys/Makefile index 20eb098538d3..e45f44b64202 100644 --- a/drivers/staging/unisys/Makefile +++ b/drivers/staging/unisys/Makefile @@ -1,7 +1,6 @@ # # Makefile for Unisys SPAR drivers # -obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ obj-$(CONFIG_UNISYS_VISORNIC) += visornic/ obj-$(CONFIG_UNISYS_VISORINPUT) += visorinput/ obj-$(CONFIG_UNISYS_VISORHBA) += visorhba/ diff --git a/drivers/staging/unisys/include/iochannel.h b/drivers/staging/unisys/include/iochannel.h index 9023cf56625d..45c785d80ce4 100644 --- a/drivers/staging/unisys/include/iochannel.h +++ b/drivers/staging/unisys/include/iochannel.h @@ -33,8 +33,7 @@ #include #include - -#include "visorbus.h" +#include /* * Must increment these whenever you insert or delete fields within this channel diff --git a/drivers/staging/unisys/include/visorbus.h b/drivers/staging/unisys/include/visorbus.h deleted file mode 100644 index 0d8bd6769b13..000000000000 --- a/drivers/staging/unisys/include/visorbus.h +++ /dev/null @@ -1,344 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2010 - 2013 UNISYS CORPORATION - * All rights reserved. - */ - -/* - * This header file is to be included by other kernel mode components that - * implement a particular kind of visor_device. Each of these other kernel - * mode components is called a visor device driver. Refer to visortemplate - * for a minimal sample visor device driver. - * - * There should be nothing in this file that is private to the visorbus - * bus implementation itself. - */ - -#ifndef __VISORBUS_H__ -#define __VISORBUS_H__ - -#include - -#define VISOR_CHANNEL_SIGNATURE ('L' << 24 | 'N' << 16 | 'C' << 8 | 'E') - -/* - * enum channel_serverstate - * @CHANNELSRV_UNINITIALIZED: Channel is in an undefined state. - * @CHANNELSRV_READY: Channel has been initialized by server. - */ -enum channel_serverstate { - CHANNELSRV_UNINITIALIZED = 0, - CHANNELSRV_READY = 1 -}; - -/* - * enum channel_clientstate - * @CHANNELCLI_DETACHED: - * @CHANNELCLI_DISABLED: Client can see channel but is NOT allowed to use it - * unless given TBD* explicit request - * (should actually be < DETACHED). - * @CHANNELCLI_ATTACHING: Legacy EFI client request for EFI server to attach. - * @CHANNELCLI_ATTACHED: Idle, but client may want to use channel any time. - * @CHANNELCLI_BUSY: Client either wants to use or is using channel. - * @CHANNELCLI_OWNED: "No worries" state - client can access channel - * anytime. - */ -enum channel_clientstate { - CHANNELCLI_DETACHED = 0, - CHANNELCLI_DISABLED = 1, - CHANNELCLI_ATTACHING = 2, - CHANNELCLI_ATTACHED = 3, - CHANNELCLI_BUSY = 4, - CHANNELCLI_OWNED = 5 -}; - -/* - * Values for VISOR_CHANNEL_PROTOCOL.Features: This define exists so that - * a guest can look at the FeatureFlags in the io channel, and configure the - * driver to use interrupts or not based on this setting. All feature bits for - * all channels should be defined here. The io channel feature bits are defined - * below. - */ -#define VISOR_DRIVER_ENABLES_INTS (0x1ULL << 1) -#define VISOR_CHANNEL_IS_POLLING (0x1ULL << 3) -#define VISOR_IOVM_OK_DRIVER_DISABLING_INTS (0x1ULL << 4) -#define VISOR_DRIVER_DISABLES_INTS (0x1ULL << 5) -#define VISOR_DRIVER_ENHANCED_RCVBUF_CHECKING (0x1ULL << 6) - -/* - * struct channel_header - Common Channel Header - * @signature: Signature. - * @legacy_state: DEPRECATED - being replaced by. - * @header_size: sizeof(struct channel_header). - * @size: Total size of this channel in bytes. - * @features: Flags to modify behavior. - * @chtype: Channel type: data, bus, control, etc.. - * @partition_handle: ID of guest partition. - * @handle: Device number of this channel in client. - * @ch_space_offset: Offset in bytes to channel specific area. - * @version_id: Struct channel_header Version ID. - * @partition_index: Index of guest partition. - * @zone_uuid: Guid of Channel's zone. - * @cli_str_offset: Offset from channel header to null-terminated - * ClientString (0 if ClientString not present). - * @cli_state_boot: CHANNEL_CLIENTSTATE of pre-boot EFI client of this - * channel. - * @cmd_state_cli: CHANNEL_COMMANDSTATE (overloaded in Windows drivers, see - * ServerStateUp, ServerStateDown, etc). - * @cli_state_os: CHANNEL_CLIENTSTATE of Guest OS client of this channel. - * @ch_characteristic: CHANNEL_CHARACTERISTIC_. - * @cmd_state_srv: CHANNEL_COMMANDSTATE (overloaded in Windows drivers, see - * ServerStateUp, ServerStateDown, etc). - * @srv_state: CHANNEL_SERVERSTATE. - * @cli_error_boot: Bits to indicate err states for boot clients, so err - * messages can be throttled. - * @cli_error_os: Bits to indicate err states for OS clients, so err - * messages can be throttled. - * @filler: Pad out to 128 byte cacheline. - * @recover_channel: Please add all new single-byte values below here. - */ -struct channel_header { - u64 signature; - u32 legacy_state; - /* SrvState, CliStateBoot, and CliStateOS below */ - u32 header_size; - u64 size; - u64 features; - guid_t chtype; - u64 partition_handle; - u64 handle; - u64 ch_space_offset; - u32 version_id; - u32 partition_index; - guid_t zone_guid; - u32 cli_str_offset; - u32 cli_state_boot; - u32 cmd_state_cli; - u32 cli_state_os; - u32 ch_characteristic; - u32 cmd_state_srv; - u32 srv_state; - u8 cli_error_boot; - u8 cli_error_os; - u8 filler[1]; - u8 recover_channel; -} __packed; - -#define VISOR_CHANNEL_ENABLE_INTS (0x1ULL << 0) - -/* - * struct signal_queue_header - Subheader for the Signal Type variation of the - * Common Channel. - * @version: SIGNAL_QUEUE_HEADER Version ID. - * @chtype: Queue type: storage, network. - * @size: Total size of this queue in bytes. - * @sig_base_offset: Offset to signal queue area. - * @features: Flags to modify behavior. - * @num_sent: Total # of signals placed in this queue. - * @num_overflows: Total # of inserts failed due to full queue. - * @signal_size: Total size of a signal for this queue. - * @max_slots: Max # of slots in queue, 1 slot is always empty. - * @max_signals: Max # of signals in queue (MaxSignalSlots-1). - * @head: Queue head signal #. - * @num_received: Total # of signals removed from this queue. - * @tail: Queue tail signal. - * @reserved1: Reserved field. - * @reserved2: Reserved field. - * @client_queue: - * @num_irq_received: Total # of Interrupts received. This is incremented by the - * ISR in the guest windows driver. - * @num_empty: Number of times that visor_signal_remove is called and - * returned Empty Status. - * @errorflags: Error bits set during SignalReinit to denote trouble with - * client's fields. - * @filler: Pad out to 64 byte cacheline. - */ -struct signal_queue_header { - /* 1st cache line */ - u32 version; - u32 chtype; - u64 size; - u64 sig_base_offset; - u64 features; - u64 num_sent; - u64 num_overflows; - u32 signal_size; - u32 max_slots; - u32 max_signals; - u32 head; - /* 2nd cache line */ - u64 num_received; - u32 tail; - u32 reserved1; - u64 reserved2; - u64 client_queue; - u64 num_irq_received; - u64 num_empty; - u32 errorflags; - u8 filler[12]; -} __packed; - -/* VISORCHANNEL Guids */ -/* {414815ed-c58c-11da-95a9-00e08161165f} */ -#define VISOR_VHBA_CHANNEL_GUID \ - GUID_INIT(0x414815ed, 0xc58c, 0x11da, \ - 0x95, 0xa9, 0x0, 0xe0, 0x81, 0x61, 0x16, 0x5f) -#define VISOR_VHBA_CHANNEL_GUID_STR \ - "414815ed-c58c-11da-95a9-00e08161165f" -struct visorchipset_state { - u32 created:1; - u32 attached:1; - u32 configured:1; - u32 running:1; - /* Remaining bits in this 32-bit word are reserved. */ -}; - -/** - * struct visor_device - A device type for things "plugged" into the visorbus - * bus - * @visorchannel: Points to the channel that the device is - * associated with. - * @channel_type_guid: Identifies the channel type to the bus driver. - * @device: Device struct meant for use by the bus driver - * only. - * @list_all: Used by the bus driver to enumerate devices. - * @timer: Timer fired periodically to do interrupt-type - * activity. - * @being_removed: Indicates that the device is being removed from - * the bus. Private bus driver use only. - * @visordriver_callback_lock: Used by the bus driver to lock when adding and - * removing devices. - * @pausing: Indicates that a change towards a paused state. - * is in progress. Only modified by the bus driver. - * @resuming: Indicates that a change towards a running state - * is in progress. Only modified by the bus driver. - * @chipset_bus_no: Private field used by the bus driver. - * @chipset_dev_no: Private field used the bus driver. - * @state: Used to indicate the current state of the - * device. - * @inst: Unique GUID for this instance of the device. - * @name: Name of the device. - * @pending_msg_hdr: For private use by bus driver to respond to - * hypervisor requests. - * @vbus_hdr_info: A pointer to header info. Private use by bus - * driver. - * @partition_guid: Indicates client partion id. This should be the - * same across all visor_devices in the current - * guest. Private use by bus driver only. - */ -struct visor_device { - struct visorchannel *visorchannel; - guid_t channel_type_guid; - /* These fields are for private use by the bus driver only. */ - struct device device; - struct list_head list_all; - struct timer_list timer; - bool timer_active; - bool being_removed; - struct mutex visordriver_callback_lock; /* synchronize probe/remove */ - bool pausing; - bool resuming; - u32 chipset_bus_no; - u32 chipset_dev_no; - struct visorchipset_state state; - guid_t inst; - u8 *name; - struct controlvm_message_header *pending_msg_hdr; - void *vbus_hdr_info; - guid_t partition_guid; - struct dentry *debugfs_dir; - struct dentry *debugfs_bus_info; -}; - -#define to_visor_device(x) container_of(x, struct visor_device, device) - -typedef void (*visorbus_state_complete_func) (struct visor_device *dev, - int status); - -/* - * This struct describes a specific visor channel, by providing its GUID, name, - * and sizes. - */ -struct visor_channeltype_descriptor { - const guid_t guid; - const char *name; - u64 min_bytes; - u32 version; -}; - -/** - * struct visor_driver - Information provided by each visor driver when it - * registers with the visorbus driver - * @name: Name of the visor driver. - * @owner: The module owner. - * @channel_types: Types of channels handled by this driver, ending with - * a zero GUID. Our specialized BUS.match() method knows - * about this list, and uses it to determine whether this - * driver will in fact handle a new device that it has - * detected. - * @probe: Called when a new device comes online, by our probe() - * function specified by driver.probe() (triggered - * ultimately by some call to driver_register(), - * bus_add_driver(), or driver_attach()). - * @remove: Called when a new device is removed, by our remove() - * function specified by driver.remove() (triggered - * ultimately by some call to device_release_driver()). - * @channel_interrupt: Called periodically, whenever there is a possiblity - * that "something interesting" may have happened to the - * channel. - * @pause: Called to initiate a change of the device's state. If - * the return valu`e is < 0, there was an error and the - * state transition will NOT occur. If the return value - * is >= 0, then the state transition was INITIATED - * successfully, and complete_func() will be called (or - * was just called) with the final status when either the - * state transition fails or completes successfully. - * @resume: Behaves similar to pause. - * @driver: Private reference to the device driver. For use by bus - * driver only. - */ -struct visor_driver { - const char *name; - struct module *owner; - struct visor_channeltype_descriptor *channel_types; - int (*probe)(struct visor_device *dev); - void (*remove)(struct visor_device *dev); - void (*channel_interrupt)(struct visor_device *dev); - int (*pause)(struct visor_device *dev, - visorbus_state_complete_func complete_func); - int (*resume)(struct visor_device *dev, - visorbus_state_complete_func complete_func); - - /* These fields are for private use by the bus driver only. */ - struct device_driver driver; -}; - -#define to_visor_driver(x) (container_of(x, struct visor_driver, driver)) - -int visor_check_channel(struct channel_header *ch, struct device *dev, - const guid_t *expected_uuid, char *chname, - u64 expected_min_bytes, u32 expected_version, - u64 expected_signature); - -int visorbus_register_visor_driver(struct visor_driver *drv); -void visorbus_unregister_visor_driver(struct visor_driver *drv); -int visorbus_read_channel(struct visor_device *dev, - unsigned long offset, void *dest, - unsigned long nbytes); -int visorbus_write_channel(struct visor_device *dev, - unsigned long offset, void *src, - unsigned long nbytes); -int visorbus_enable_channel_interrupts(struct visor_device *dev); -void visorbus_disable_channel_interrupts(struct visor_device *dev); - -int visorchannel_signalremove(struct visorchannel *channel, u32 queue, - void *msg); -int visorchannel_signalinsert(struct visorchannel *channel, u32 queue, - void *msg); -bool visorchannel_signalempty(struct visorchannel *channel, u32 queue); -const guid_t *visorchannel_get_guid(struct visorchannel *channel); - -#define BUS_ROOT_DEVICE UINT_MAX -struct visor_device *visorbus_get_device_by_id(u32 bus_no, u32 dev_no, - struct visor_device *from); -#endif diff --git a/drivers/staging/unisys/visorbus/Kconfig b/drivers/staging/unisys/visorbus/Kconfig deleted file mode 100644 index 3866804ed918..000000000000 --- a/drivers/staging/unisys/visorbus/Kconfig +++ /dev/null @@ -1,15 +0,0 @@ -# -# Unisys visorbus configuration -# - -config UNISYS_VISORBUS - tristate "Unisys visorbus driver" - depends on UNISYSSPAR - depends on X86_64 && ACPI - ---help--- - The visorbus driver is a virtualized bus for the Unisys s-Par firmware. - Virtualized devices allow Linux guests on a system to share disks and - network cards that do not have SR-IOV support, and to be accessed using - the partition desktop application. The visorbus driver is required to - discover devices on an s-Par guest, and must be present for any other - s-Par guest driver to function correctly. diff --git a/drivers/staging/unisys/visorbus/Makefile b/drivers/staging/unisys/visorbus/Makefile deleted file mode 100644 index 784cdc1f9d6a..000000000000 --- a/drivers/staging/unisys/visorbus/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for Unisys visorbus -# - -obj-$(CONFIG_UNISYS_VISORBUS) += visorbus.o - -visorbus-y := visorbus_main.o -visorbus-y += visorchannel.o -visorbus-y += visorchipset.o - -ccflags-y += -Idrivers/staging/unisys/include diff --git a/drivers/staging/unisys/visorbus/controlvmchannel.h b/drivers/staging/unisys/visorbus/controlvmchannel.h deleted file mode 100644 index b0ae29e505ff..000000000000 --- a/drivers/staging/unisys/visorbus/controlvmchannel.h +++ /dev/null @@ -1,651 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2010 - 2015 UNISYS CORPORATION - * All rights reserved. - */ - -#ifndef __CONTROLVMCHANNEL_H__ -#define __CONTROLVMCHANNEL_H__ - -#include - -#include "visorbus.h" - -/* {2B3C2D10-7EF5-4ad8-B966-3448B7386B3D} */ -#define VISOR_CONTROLVM_CHANNEL_GUID \ - GUID_INIT(0x2b3c2d10, 0x7ef5, 0x4ad8, \ - 0xb9, 0x66, 0x34, 0x48, 0xb7, 0x38, 0x6b, 0x3d) - -#define CONTROLVM_MESSAGE_MAX 64 - -/* - * Must increment this whenever you insert or delete fields within this channel - * struct. Also increment whenever you change the meaning of fields within this - * channel struct so as to break pre-existing software. Note that you can - * usually add fields to the END of the channel struct withOUT needing to - * increment this. - */ -#define VISOR_CONTROLVM_CHANNEL_VERSIONID 1 - -/* Defines for various channel queues */ -#define CONTROLVM_QUEUE_REQUEST 0 -#define CONTROLVM_QUEUE_RESPONSE 1 -#define CONTROLVM_QUEUE_EVENT 2 -#define CONTROLVM_QUEUE_ACK 3 - -/* Max num of messages stored during IOVM creation to be reused after crash */ -#define CONTROLVM_CRASHMSG_MAX 2 - -/* - * struct visor_segment_state - * @enabled: May enter other states. - * @active: Assigned to active partition. - * @alive: Configure message sent to service/server. - * @revoked: Similar to partition state ShuttingDown. - * @allocated: Memory (device/port number) has been selected by Command. - * @known: Has been introduced to the service/guest partition. - * @ready: Service/Guest partition has responded to introduction. - * @operating: Resource is configured and operating. - * @reserved: Natural alignment. - * - * Note: Don't use high bit unless we need to switch to ushort which is - * non-compliant. - */ -struct visor_segment_state { - u16 enabled:1; - u16 active:1; - u16 alive:1; - u16 revoked:1; - u16 allocated:1; - u16 known:1; - u16 ready:1; - u16 operating:1; - u16 reserved:8; -} __packed; - -static const struct visor_segment_state segment_state_running = { - 1, 1, 1, 0, 1, 1, 1, 1 -}; - -static const struct visor_segment_state segment_state_paused = { - 1, 1, 1, 0, 1, 1, 1, 0 -}; - -static const struct visor_segment_state segment_state_standby = { - 1, 1, 0, 0, 1, 1, 1, 0 -}; - -/* - * enum controlvm_id - * @CONTROLVM_INVALID: - * @CONTROLVM_BUS_CREATE: CP --> SP, GP. - * @CONTROLVM_BUS_DESTROY: CP --> SP, GP. - * @CONTROLVM_BUS_CONFIGURE: CP --> SP. - * @CONTROLVM_BUS_CHANGESTATE: CP --> SP, GP. - * @CONTROLVM_BUS_CHANGESTATE_EVENT: SP, GP --> CP. - * @CONTROLVM_DEVICE_CREATE: CP --> SP, GP. - * @CONTROLVM_DEVICE_DESTROY: CP --> SP, GP. - * @CONTROLVM_DEVICE_CONFIGURE: CP --> SP. - * @CONTROLVM_DEVICE_CHANGESTATE: CP --> SP, GP. - * @CONTROLVM_DEVICE_CHANGESTATE_EVENT: SP, GP --> CP. - * @CONTROLVM_DEVICE_RECONFIGURE: CP --> Boot. - * @CONTROLVM_CHIPSET_INIT: CP --> SP, GP. - * @CONTROLVM_CHIPSET_STOP: CP --> SP, GP. - * @CONTROLVM_CHIPSET_READY: CP --> SP. - * @CONTROLVM_CHIPSET_SELFTEST: CP --> SP. - * - * Ids for commands that may appear in either queue of a ControlVm channel. - * - * Commands that are initiated by the command partition (CP), by an IO or - * console service partition (SP), or by a guest partition (GP) are: - * - issued on the RequestQueue queue (q #0) in the ControlVm channel - * - responded to on the ResponseQueue queue (q #1) in the ControlVm channel - * - * Events that are initiated by an IO or console service partition (SP) or - * by a guest partition (GP) are: - * - issued on the EventQueue queue (q #2) in the ControlVm channel - * - responded to on the EventAckQueue queue (q #3) in the ControlVm channel - */ -enum controlvm_id { - CONTROLVM_INVALID = 0, - /* - * SWITCH commands required Parameter: SwitchNumber. - * BUS commands required Parameter: BusNumber - */ - CONTROLVM_BUS_CREATE = 0x101, - CONTROLVM_BUS_DESTROY = 0x102, - CONTROLVM_BUS_CONFIGURE = 0x104, - CONTROLVM_BUS_CHANGESTATE = 0x105, - CONTROLVM_BUS_CHANGESTATE_EVENT = 0x106, - /* DEVICE commands required Parameter: BusNumber, DeviceNumber */ - CONTROLVM_DEVICE_CREATE = 0x201, - CONTROLVM_DEVICE_DESTROY = 0x202, - CONTROLVM_DEVICE_CONFIGURE = 0x203, - CONTROLVM_DEVICE_CHANGESTATE = 0x204, - CONTROLVM_DEVICE_CHANGESTATE_EVENT = 0x205, - CONTROLVM_DEVICE_RECONFIGURE = 0x206, - /* CHIPSET commands */ - CONTROLVM_CHIPSET_INIT = 0x301, - CONTROLVM_CHIPSET_STOP = 0x302, - CONTROLVM_CHIPSET_READY = 0x304, - CONTROLVM_CHIPSET_SELFTEST = 0x305, -}; - -/* - * struct irq_info - * @reserved1: Natural alignment purposes - * @recv_irq_handle: Specifies interrupt handle. It is used to retrieve the - * corresponding interrupt pin from Monitor; and the interrupt - * pin is used to connect to the corresponding interrupt. - * Used by IOPart-GP only. - * @recv_irq_vector: Specifies interrupt vector. It, interrupt pin, and shared - * are used to connect to the corresponding interrupt. - * Used by IOPart-GP only. - * @recv_irq_shared: Specifies if the recvInterrupt is shared. It, interrupt - * pin and vector are used to connect to 0 = not shared; - * 1 = shared the corresponding interrupt. - * Used by IOPart-GP only. - * @reserved: Natural alignment purposes - */ -struct irq_info { - u64 reserved1; - u64 recv_irq_handle; - u32 recv_irq_vector; - u8 recv_irq_shared; - u8 reserved[3]; -} __packed; - -/* - * struct efi_visor_indication - * @boot_to_fw_ui: Stop in UEFI UI - * @clear_nvram: Clear NVRAM - * @clear_cmos: Clear CMOS - * @boot_to_tool: Run install tool - * @reserved: Natural alignment - */ -struct efi_visor_indication { - u64 boot_to_fw_ui:1; - u64 clear_nvram:1; - u64 clear_cmos:1; - u64 boot_to_tool:1; - /* Remaining bits are available */ - u64 reserved:60; -} __packed; - -enum visor_chipset_feature { - VISOR_CHIPSET_FEATURE_REPLY = 0x00000001, - VISOR_CHIPSET_FEATURE_PARA_HOTPLUG = 0x00000002, -}; - -/* - * struct controlvm_message_header - * @id: See CONTROLVM_ID. - * @message_size: Includes size of this struct + size of message. - * @segment_index: Index of segment containing Vm message/information. - * @completion_status: Error status code or result of message completion. - * @struct flags: - * @failed: =1 in a response to signify failure. - * @response_expected: =1 in all messages that expect a response. - * @server: =1 in all bus & device-related messages where the - * message receiver is to act as the bus or device - * server. - * @test_message: =1 for testing use only (Control and Command - * ignore this). - * @partial_completion: =1 if there are forthcoming responses/acks - * associated with this message. - * @preserve: =1 this is to let us know to preserve channel - * contents. - * @writer_in_diag: =1 the DiagWriter is active in the Diagnostic - * Partition. - * @reserve: Natural alignment. - * @reserved: Natural alignment. - * @message_handle: Identifies the particular message instance. - * @payload_vm_offset: Offset of payload area from start of this instance. - * @payload_max_bytes: Maximum bytes allocated in payload area of ControlVm - * segment. - * @payload_bytes: Actual number of bytes of payload area to copy between - * IO/Command. If non-zero, there is a payload to copy. - * - * This is the common structure that is at the beginning of every - * ControlVm message (both commands and responses) in any ControlVm - * queue. Commands are easily distinguished from responses by - * looking at the flags.response field. - */ -struct controlvm_message_header { - u32 id; - /* - * For requests, indicates the message type. For responses, indicates - * the type of message we are responding to. - */ - u32 message_size; - u32 segment_index; - u32 completion_status; - struct { - u32 failed:1; - u32 response_expected:1; - u32 server:1; - u32 test_message:1; - u32 partial_completion:1; - u32 preserve:1; - u32 writer_in_diag:1; - u32 reserve:25; - } __packed flags; - u32 reserved; - u64 message_handle; - u64 payload_vm_offset; - u32 payload_max_bytes; - u32 payload_bytes; -} __packed; - -/* - * struct controlvm_packet_device_create - For CONTROLVM_DEVICE_CREATE - * @bus_no: Bus # (0..n-1) from the msg receiver's end. - * @dev_no: Bus-relative (0..n-1) device number. - * @channel_addr: Guest physical address of the channel, which can be - * dereferenced by the receiver of this ControlVm command. - * @channel_bytes: Specifies size of the channel in bytes. - * @data_type_uuid: Specifies format of data in channel. - * @dev_inst_uuid: Instance guid for the device. - * @irq_info intr: Specifies interrupt information. - */ -struct controlvm_packet_device_create { - u32 bus_no; - u32 dev_no; - u64 channel_addr; - u64 channel_bytes; - guid_t data_type_guid; - guid_t dev_inst_guid; - struct irq_info intr; -} __packed; - -/* - * struct controlvm_packet_device_configure - For CONTROLVM_DEVICE_CONFIGURE - * @bus_no: Bus number (0..n-1) from the msg receiver's perspective. - * @dev_no: Bus-relative (0..n-1) device number. - */ -struct controlvm_packet_device_configure { - u32 bus_no; - u32 dev_no; -} __packed; - -/* Total 128 bytes */ -struct controlvm_message_device_create { - struct controlvm_message_header header; - struct controlvm_packet_device_create packet; -} __packed; - -/* Total 56 bytes */ -struct controlvm_message_device_configure { - struct controlvm_message_header header; - struct controlvm_packet_device_configure packet; -} __packed; - -/* - * struct controlvm_message_packet - This is the format for a message in any - * ControlVm queue. - * @struct create_bus: For CONTROLVM_BUS_CREATE. - * @bus_no: Bus # (0..n-1) from the msg receiver's perspective. - * @dev_count: Indicates the max number of devices on this bus. - * @channel_addr: Guest physical address of the channel, which can be - * dereferenced by the receiver of this ControlVM - * command. - * @channel_bytes: Size of the channel. - * @bus_data_type_uuid: Indicates format of data in bus channel. - * @bus_inst_uuid: Instance uuid for the bus. - * - * @struct destroy_bus: For CONTROLVM_BUS_DESTROY. - * @bus_no: Bus # (0..n-1) from the msg receiver's perspective. - * @reserved: Natural alignment purposes. - * - * @struct configure_bus: For CONTROLVM_BUS_CONFIGURE. - * @bus_no: Bus # (0..n-1) from the receiver's perspective. - * @reserved1: For alignment purposes. - * @guest_handle: This is used to convert guest physical address to - * physical address. - * @recv_bus_irq_handle: Specifies interrupt info. It is used by SP to - * register to receive interrupts from the CP. This - * interrupt is used for bus level notifications. - * The corresponding sendBusInterruptHandle is kept - * in CP. - * - * @struct create_device: For CONTROLVM_DEVICE_CREATE. - * - * @struct destroy_device: For CONTROLVM_DEVICE_DESTROY. - * @bus_no: Bus # (0..n-1) from the msg receiver's perspective. - * @dev_no: Bus-relative (0..n-1) device number. - * - * @struct configure_device: For CONTROLVM_DEVICE_CONFIGURE. - * - * @struct reconfigure_device: For CONTROLVM_DEVICE_RECONFIGURE. - * @bus_no: Bus # (0..n-1) from the msg receiver's perspective. - * @dev_no: Bus-relative (0..n-1) device number. - * - * @struct bus_change_state: For CONTROLVM_BUS_CHANGESTATE. - * @bus_no: - * @struct state: - * @reserved: Natural alignment purposes. - * - * @struct device_change_state: For CONTROLVM_DEVICE_CHANGESTATE. - * @bus_no: - * @dev_no: - * @struct state: - * @struct flags: - * @phys_device: =1 if message is for a physical device. - * @reserved: Natural alignment. - * @reserved1: Natural alignment. - * @reserved: Natural alignment purposes. - * - * @struct device_change_state_event: For CONTROLVM_DEVICE_CHANGESTATE_EVENT. - * @bus_no: - * @dev_no: - * @struct state: - * @reserved: Natural alignment purposes. - * - * @struct init_chipset: For CONTROLVM_CHIPSET_INIT. - * @bus_count: Indicates the max number of busses. - * @switch_count: Indicates the max number of switches. - * @enum features: - * @platform_number: - * - * @struct chipset_selftest: For CONTROLVM_CHIPSET_SELFTEST. - * @options: Reserved. - * @test: Bit 0 set to run embedded selftest. - * - * @addr: A physical address of something, that can be dereferenced by the - * receiver of this ControlVm command. - * - * @handle: A handle of something (depends on command id). - */ -struct controlvm_message_packet { - union { - struct { - u32 bus_no; - u32 dev_count; - u64 channel_addr; - u64 channel_bytes; - guid_t bus_data_type_guid; - guid_t bus_inst_guid; - } __packed create_bus; - struct { - u32 bus_no; - u32 reserved; - } __packed destroy_bus; - struct { - u32 bus_no; - u32 reserved1; - u64 guest_handle; - u64 recv_bus_irq_handle; - } __packed configure_bus; - struct controlvm_packet_device_create create_device; - struct { - u32 bus_no; - u32 dev_no; - } __packed destroy_device; - struct controlvm_packet_device_configure configure_device; - struct { - u32 bus_no; - u32 dev_no; - } __packed reconfigure_device; - struct { - u32 bus_no; - struct visor_segment_state state; - u8 reserved[2]; - } __packed bus_change_state; - struct { - u32 bus_no; - u32 dev_no; - struct visor_segment_state state; - struct { - u32 phys_device:1; - u32 reserved:31; - u32 reserved1; - } __packed flags; - u8 reserved[2]; - } __packed device_change_state; - struct { - u32 bus_no; - u32 dev_no; - struct visor_segment_state state; - u8 reserved[6]; - } __packed device_change_state_event; - struct { - u32 bus_count; - u32 switch_count; - enum visor_chipset_feature features; - u32 platform_number; - } __packed init_chipset; - struct { - u32 options; - u32 test; - } __packed chipset_selftest; - u64 addr; - u64 handle; - }; -} __packed; - -/* All messages in any ControlVm queue have this layout. */ -struct controlvm_message { - struct controlvm_message_header hdr; - struct controlvm_message_packet cmd; -} __packed; - -/* - * struct visor_controlvm_channel - * @struct header: - * @gp_controlvm: Guest phys addr of this channel. - * @gp_partition_tables: Guest phys addr of partition tables. - * @gp_diag_guest: Guest phys addr of diagnostic channel. - * @gp_boot_romdisk: Guest phys addr of (read* only) Boot - * ROM disk. - * @gp_boot_ramdisk: Guest phys addr of writable Boot RAM - * disk. - * @gp_acpi_table: Guest phys addr of acpi table. - * @gp_control_channel: Guest phys addr of control channel. - * @gp_diag_romdisk: Guest phys addr of diagnostic ROM disk. - * @gp_nvram: Guest phys addr of NVRAM channel. - * @request_payload_offset: Offset to request payload area. - * @event_payload_offset: Offset to event payload area. - * @request_payload_bytes: Bytes available in request payload area. - * @event_payload_bytes: Bytes available in event payload area. - * @control_channel_bytes: - * @nvram_channel_bytes: Bytes in PartitionNvram segment. - * @message_bytes: sizeof(CONTROLVM_MESSAGE). - * @message_count: CONTROLVM_MESSAGE_MAX. - * @gp_smbios_table: Guest phys addr of SMBIOS tables. - * @gp_physical_smbios_table: Guest phys addr of SMBIOS table. - * @gp_reserved: VISOR_MAX_GUESTS_PER_SERVICE. - * @virtual_guest_firmware_image_base: Guest physical address of EFI firmware - * image base. - * @virtual_guest_firmware_entry_point: Guest physical address of EFI firmware - * entry point. - * @virtual_guest_firmware_image_size: Guest EFI firmware image size. - * @virtual_guest_firmware_boot_base: GPA = 1MB where EFI firmware image is - * copied to. - * @virtual_guest_image_base: - * @virtual_guest_image_size: - * @prototype_control_channel_offset: - * @virtual_guest_partition_handle: - * @restore_action: Restore Action field to restore the - * guest partition. - * @dump_action: For Windows guests it shows if the - * visordisk is in dump mode. - * @nvram_fail_count: - * @saved_crash_message_count: = CONTROLVM_CRASHMSG_MAX. - * @saved_crash_message_offset: Offset to request payload area needed - * for crash dump. - * @installation_error: Type of error encountered during - * installation. - * @installation_text_id: Id of string to display. - * @installation_remaining_steps: Number of remaining installation steps - * (for progress bars). - * @tool_action: VISOR_TOOL_ACTIONS Installation Action - * field. - * @reserved: Alignment. - * @struct efi_visor_ind: - * @sp_reserved: - * @reserved2: Force signals to begin on 128-byte - * cache line. - * @struct request_queue: Guest partition uses this queue to send - * requests to Control. - * @struct response_queue: Control uses this queue to respond to - * service or guest partition request. - * @struct event_queue: Control uses this queue to send events - * to guest partition. - * @struct event_ack_queue: Service or guest partition uses this - * queue to ack Control events. - * @struct request_msg: Request fixed-size message pool - - * does not include payload. - * @struct response_msg: Response fixed-size message pool - - * does not include payload. - * @struct event_msg: Event fixed-size message pool - - * does not include payload. - * @struct event_ack_msg: Ack fixed-size message pool - - * does not include payload. - * @struct saved_crash_msg: Message stored during IOVM creation to - * be reused after crash. - */ -struct visor_controlvm_channel { - struct channel_header header; - u64 gp_controlvm; - u64 gp_partition_tables; - u64 gp_diag_guest; - u64 gp_boot_romdisk; - u64 gp_boot_ramdisk; - u64 gp_acpi_table; - u64 gp_control_channel; - u64 gp_diag_romdisk; - u64 gp_nvram; - u64 request_payload_offset; - u64 event_payload_offset; - u32 request_payload_bytes; - u32 event_payload_bytes; - u32 control_channel_bytes; - u32 nvram_channel_bytes; - u32 message_bytes; - u32 message_count; - u64 gp_smbios_table; - u64 gp_physical_smbios_table; - char gp_reserved[2688]; - u64 virtual_guest_firmware_image_base; - u64 virtual_guest_firmware_entry_point; - u64 virtual_guest_firmware_image_size; - u64 virtual_guest_firmware_boot_base; - u64 virtual_guest_image_base; - u64 virtual_guest_image_size; - u64 prototype_control_channel_offset; - u64 virtual_guest_partition_handle; - u16 restore_action; - u16 dump_action; - u16 nvram_fail_count; - u16 saved_crash_message_count; - u32 saved_crash_message_offset; - u32 installation_error; - u32 installation_text_id; - u16 installation_remaining_steps; - u8 tool_action; - u8 reserved; - struct efi_visor_indication efi_visor_ind; - u32 sp_reserved; - u8 reserved2[28]; - struct signal_queue_header request_queue; - struct signal_queue_header response_queue; - struct signal_queue_header event_queue; - struct signal_queue_header event_ack_queue; - struct controlvm_message request_msg[CONTROLVM_MESSAGE_MAX]; - struct controlvm_message response_msg[CONTROLVM_MESSAGE_MAX]; - struct controlvm_message event_msg[CONTROLVM_MESSAGE_MAX]; - struct controlvm_message event_ack_msg[CONTROLVM_MESSAGE_MAX]; - struct controlvm_message saved_crash_msg[CONTROLVM_CRASHMSG_MAX]; -} __packed; - -/* - * struct visor_controlvm_parameters_header - * - * The following header will be located at the beginning of PayloadVmOffset for - * various ControlVm commands. The receiver of a ControlVm command with a - * PayloadVmOffset will dereference this address and then use connection_offset, - * initiator_offset, and target_offset to get the location of UTF-8 formatted - * strings that can be parsed to obtain command-specific information. The value - * of total_length should equal PayloadBytes. The format of the strings at - * PayloadVmOffset will take different forms depending on the message. - */ -struct visor_controlvm_parameters_header { - u32 total_length; - u32 header_length; - u32 connection_offset; - u32 connection_length; - u32 initiator_offset; - u32 initiator_length; - u32 target_offset; - u32 target_length; - u32 client_offset; - u32 client_length; - u32 name_offset; - u32 name_length; - guid_t id; - u32 revision; - /* Natural alignment */ - u32 reserved; -} __packed; - -/* General Errors------------------------------------------------------[0-99] */ -#define CONTROLVM_RESP_SUCCESS 0 -#define CONTROLVM_RESP_ALREADY_DONE 1 -#define CONTROLVM_RESP_IOREMAP_FAILED 2 -#define CONTROLVM_RESP_KMALLOC_FAILED 3 -#define CONTROLVM_RESP_ID_UNKNOWN 4 -#define CONTROLVM_RESP_ID_INVALID_FOR_CLIENT 5 -/* CONTROLVM_INIT_CHIPSET-------------------------------------------[100-199] */ -#define CONTROLVM_RESP_CLIENT_SWITCHCOUNT_NONZERO 100 -#define CONTROLVM_RESP_EXPECTED_CHIPSET_INIT 101 -/* Maximum Limit----------------------------------------------------[200-299] */ -/* BUS_CREATE */ -#define CONTROLVM_RESP_ERROR_MAX_BUSES 201 -/* DEVICE_CREATE */ -#define CONTROLVM_RESP_ERROR_MAX_DEVICES 202 -/* Payload and Parameter Related------------------------------------[400-499] */ -/* SWITCH_ATTACHEXTPORT, DEVICE_CONFIGURE */ -#define CONTROLVM_RESP_PAYLOAD_INVALID 400 -/* Multiple */ -#define CONTROLVM_RESP_INITIATOR_PARAMETER_INVALID 401 -/* DEVICE_CONFIGURE */ -#define CONTROLVM_RESP_TARGET_PARAMETER_INVALID 402 -/* DEVICE_CONFIGURE */ -#define CONTROLVM_RESP_CLIENT_PARAMETER_INVALID 403 -/* Specified[Packet Structure] Value--------------------------------[500-599] */ -/* SWITCH_ATTACHINTPORT */ -/* BUS_CONFIGURE, DEVICE_CREATE, DEVICE_CONFIG, DEVICE_DESTROY */ -#define CONTROLVM_RESP_BUS_INVALID 500 -/* SWITCH_ATTACHINTPORT*/ -/* DEVICE_CREATE, DEVICE_CONFIGURE, DEVICE_DESTROY */ -#define CONTROLVM_RESP_DEVICE_INVALID 501 -/* DEVICE_CREATE, DEVICE_CONFIGURE */ -#define CONTROLVM_RESP_CHANNEL_INVALID 502 -/* Partition Driver Callback Interface------------------------------[600-699] */ -/* BUS_CREATE, BUS_DESTROY, DEVICE_CREATE, DEVICE_DESTROY */ -#define CONTROLVM_RESP_VIRTPCI_DRIVER_FAILURE 604 -/* Unable to invoke VIRTPCI callback. VIRTPCI Callback returned error. */ -/* BUS_CREATE, BUS_DESTROY, DEVICE_CREATE, DEVICE_DESTROY */ -#define CONTROLVM_RESP_VIRTPCI_DRIVER_CALLBACK_ERROR 605 -/* Generic device callback returned error. */ -/* SWITCH_ATTACHEXTPORT, SWITCH_DETACHEXTPORT, DEVICE_CONFIGURE */ -#define CONTROLVM_RESP_GENERIC_DRIVER_CALLBACK_ERROR 606 -/* Bus Related------------------------------------------------------[700-799] */ -/* BUS_DESTROY */ -#define CONTROLVM_RESP_ERROR_BUS_DEVICE_ATTACHED 700 -/* Channel Related--------------------------------------------------[800-899] */ -/* GET_CHANNELINFO, DEVICE_DESTROY */ -#define CONTROLVM_RESP_CHANNEL_TYPE_UNKNOWN 800 -/* DEVICE_CREATE */ -#define CONTROLVM_RESP_CHANNEL_SIZE_TOO_SMALL 801 -/* Chipset Shutdown Related---------------------------------------[1000-1099] */ -#define CONTROLVM_RESP_CHIPSET_SHUTDOWN_FAILED 1000 -#define CONTROLVM_RESP_CHIPSET_SHUTDOWN_ALREADY_ACTIVE 1001 -/* Chipset Stop Related-------------------------------------------[1100-1199] */ -#define CONTROLVM_RESP_CHIPSET_STOP_FAILED_BUS 1100 -#define CONTROLVM_RESP_CHIPSET_STOP_FAILED_SWITCH 1101 -/* Device Related-------------------------------------------------[1400-1499] */ -#define CONTROLVM_RESP_DEVICE_UDEV_TIMEOUT 1400 - -/* __CONTROLVMCHANNEL_H__ */ -#endif diff --git a/drivers/staging/unisys/visorbus/vbuschannel.h b/drivers/staging/unisys/visorbus/vbuschannel.h deleted file mode 100644 index 622c9d71a04a..000000000000 --- a/drivers/staging/unisys/visorbus/vbuschannel.h +++ /dev/null @@ -1,95 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2010 - 2015 UNISYS CORPORATION - * All rights reserved. - */ - -#ifndef __VBUSCHANNEL_H__ -#define __VBUSCHANNEL_H__ - -/* - * The vbus channel is the channel area provided via the BUS_CREATE controlvm - * message for each virtual bus. This channel area is provided to both server - * and client ends of the bus. The channel header area is initialized by - * the server, and the remaining information is filled in by the client. - * We currently use this for the client to provide various information about - * the client devices and client drivers for the server end to see. - */ - -#include -#include "visorbus.h" - -/* {193b331b-c58f-11da-95a9-00e08161165f} */ -#define VISOR_VBUS_CHANNEL_GUID \ - GUID_INIT(0x193b331b, 0xc58f, 0x11da, \ - 0x95, 0xa9, 0x0, 0xe0, 0x81, 0x61, 0x16, 0x5f) - -/* - * Must increment this whenever you insert or delete fields within this channel - * struct. Also increment whenever you change the meaning of fields within this - * channel struct so as to break pre-existing software. Note that you can - * usually add fields to the END of the channel struct withOUT needing to - * increment this. - */ -#define VISOR_VBUS_CHANNEL_VERSIONID 1 - -/* - * struct visor_vbus_deviceinfo - * @devtype: Short string identifying the device type. - * @drvname: Driver .sys file name. - * @infostrs: Kernel vversion. - * @reserved: Pad size to 256 bytes. - * - * An array of this struct is present in the channel area for each vbus. It is - * filled in by the client side to provide info about the device and driver from - * the client's perspective. - */ -struct visor_vbus_deviceinfo { - u8 devtype[16]; - u8 drvname[16]; - u8 infostrs[96]; - u8 reserved[128]; -} __packed; - -/* - * struct visor_vbus_headerinfo - * @struct_bytes: Size of this struct in bytes. - * @device_info_struct_bytes: Size of VISOR_VBUS_DEVICEINFO. - * @dev_info_count: Num of items in DevInfo member. This is the - * allocated size. - * @chp_info_offset: Byte offset from beginning of this struct to the - * ChpInfo struct. - * @bus_info_offset: Byte offset from beginning of this struct to the - * BusInfo struct. - * @dev_info_offset: Byte offset from beginning of this struct to the - * DevInfo array. - * @reserved: Natural alignment. - */ -struct visor_vbus_headerinfo { - u32 struct_bytes; - u32 device_info_struct_bytes; - u32 dev_info_count; - u32 chp_info_offset; - u32 bus_info_offset; - u32 dev_info_offset; - u8 reserved[104]; -} __packed; - -/* - * struct visor_vbus_channel - * @channel_header: Initialized by server. - * @hdr_info: Initialized by server. - * @chp_info: Describes client chipset device and driver. - * @bus_info: Describes client bus device and driver. - * @dev_info: Describes client device and driver for each device on the - * bus. - */ -struct visor_vbus_channel { - struct channel_header channel_header; - struct visor_vbus_headerinfo hdr_info; - struct visor_vbus_deviceinfo chp_info; - struct visor_vbus_deviceinfo bus_info; - struct visor_vbus_deviceinfo dev_info[0]; -} __packed; - -#endif diff --git a/drivers/staging/unisys/visorbus/visorbus_main.c b/drivers/staging/unisys/visorbus/visorbus_main.c deleted file mode 100644 index a16715525dc9..000000000000 --- a/drivers/staging/unisys/visorbus/visorbus_main.c +++ /dev/null @@ -1,1234 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright � 2010 - 2015 UNISYS CORPORATION - * All rights reserved. - */ - -#include -#include -#include -#include -#include - -#include "visorbus.h" -#include "visorbus_private.h" - -static const guid_t visor_vbus_channel_guid = VISOR_VBUS_CHANNEL_GUID; - -/* Display string that is guaranteed to be no longer the 99 characters */ -#define LINESIZE 99 -#define POLLJIFFIES_NORMALCHANNEL 10 - -/* stores whether bus_registration was successful */ -static bool initialized; -static struct dentry *visorbus_debugfs_dir; - -/* - * DEVICE type attributes - * - * The modalias file will contain the guid of the device. - */ -static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct visor_device *vdev; - const guid_t *guid; - - vdev = to_visor_device(dev); - guid = visorchannel_get_guid(vdev->visorchannel); - return sprintf(buf, "visorbus:%pUl\n", guid); -} -static DEVICE_ATTR_RO(modalias); - -static struct attribute *visorbus_dev_attrs[] = { - &dev_attr_modalias.attr, - NULL, -}; - -ATTRIBUTE_GROUPS(visorbus_dev); - -/* filled in with info about parent chipset driver when we register with it */ -static struct visor_vbus_deviceinfo chipset_driverinfo; -/* filled in with info about this driver, wrt it servicing client busses */ -static struct visor_vbus_deviceinfo clientbus_driverinfo; - -/* list of visor_device structs, linked via .list_all */ -static LIST_HEAD(list_all_bus_instances); -/* list of visor_device structs, linked via .list_all */ -static LIST_HEAD(list_all_device_instances); - -/* - * Generic function useful for validating any type of channel when it is - * received by the client that will be accessing the channel. - * Note that is only needed for callers in the EFI environment, and - * is used to pass the EFI_DIAG_CAPTURE_PROTOCOL needed to log messages. - */ -int visor_check_channel(struct channel_header *ch, struct device *dev, - const guid_t *expected_guid, char *chname, - u64 expected_min_bytes, u32 expected_version, - u64 expected_signature) -{ - if (!guid_is_null(expected_guid)) { - /* caller wants us to verify type GUID */ - if (!guid_equal(&ch->chtype, expected_guid)) { - dev_err(dev, "Channel mismatch on channel=%s(%pUL) field=type expected=%pUL actual=%pUL\n", - chname, expected_guid, expected_guid, - &ch->chtype); - return 0; - } - } - /* verify channel size */ - if (expected_min_bytes > 0) { - if (ch->size < expected_min_bytes) { - dev_err(dev, "Channel mismatch on channel=%s(%pUL) field=size expected=0x%-8.8Lx actual=0x%-8.8Lx\n", - chname, expected_guid, - (unsigned long long)expected_min_bytes, - ch->size); - return 0; - } - } - /* verify channel version */ - if (expected_version > 0) { - if (ch->version_id != expected_version) { - dev_err(dev, "Channel mismatch on channel=%s(%pUL) field=version expected=0x%-8.8lx actual=0x%-8.8x\n", - chname, expected_guid, - (unsigned long)expected_version, - ch->version_id); - return 0; - } - } - /* verify channel signature */ - if (expected_signature > 0) { - if (ch->signature != expected_signature) { - dev_err(dev, "Channel mismatch on channel=%s(%pUL) field=signature expected=0x%-8.8Lx actual=0x%-8.8Lx\n", - chname, expected_guid, expected_signature, - ch->signature); - return 0; - } - } - return 1; -} - -static int visorbus_uevent(struct device *xdev, struct kobj_uevent_env *env) -{ - struct visor_device *dev; - const guid_t *guid; - - dev = to_visor_device(xdev); - guid = visorchannel_get_guid(dev->visorchannel); - return add_uevent_var(env, "MODALIAS=visorbus:%pUl", guid); -} - -/* - * visorbus_match() - called automatically upon adding a visor_device - * (device_add), or adding a visor_driver - * (visorbus_register_visor_driver) - * @xdev: struct device for the device being matched - * @xdrv: struct device_driver for driver to match device against - * - * Return: 1 iff the provided driver can control the specified device - */ -static int visorbus_match(struct device *xdev, struct device_driver *xdrv) -{ - const guid_t *channel_type; - int i; - struct visor_device *dev; - struct visor_driver *drv; - struct visorchannel *chan; - - dev = to_visor_device(xdev); - channel_type = visorchannel_get_guid(dev->visorchannel); - drv = to_visor_driver(xdrv); - chan = dev->visorchannel; - if (!drv->channel_types) - return 0; - for (i = 0; !guid_is_null(&drv->channel_types[i].guid); i++) - if (guid_equal(&drv->channel_types[i].guid, channel_type) && - visor_check_channel(visorchannel_get_header(chan), - xdev, - &drv->channel_types[i].guid, - (char *)drv->channel_types[i].name, - drv->channel_types[i].min_bytes, - drv->channel_types[i].version, - VISOR_CHANNEL_SIGNATURE)) - return i + 1; - return 0; -} - -/* - * This describes the TYPE of bus. - * (Don't confuse this with an INSTANCE of the bus.) - */ -static struct bus_type visorbus_type = { - .name = "visorbus", - .match = visorbus_match, - .uevent = visorbus_uevent, - .dev_groups = visorbus_dev_groups, -}; - -struct visor_busdev { - u32 bus_no; - u32 dev_no; -}; - -static int match_visorbus_dev_by_id(struct device *dev, void *data) -{ - struct visor_device *vdev = to_visor_device(dev); - struct visor_busdev *id = data; - - if (vdev->chipset_bus_no == id->bus_no && - vdev->chipset_dev_no == id->dev_no) - return 1; - return 0; -} - -struct visor_device *visorbus_get_device_by_id(u32 bus_no, u32 dev_no, - struct visor_device *from) -{ - struct device *dev; - struct device *dev_start = NULL; - struct visor_busdev id = { - .bus_no = bus_no, - .dev_no = dev_no - }; - - if (from) - dev_start = &from->device; - dev = bus_find_device(&visorbus_type, dev_start, (void *)&id, - match_visorbus_dev_by_id); - if (!dev) - return NULL; - return to_visor_device(dev); -} - -/* - * visorbus_release_busdevice() - called when device_unregister() is called for - * the bus device instance, after all other tasks - * involved with destroying the dev are complete - * @xdev: struct device for the bus being released - */ -static void visorbus_release_busdevice(struct device *xdev) -{ - struct visor_device *dev = dev_get_drvdata(xdev); - - debugfs_remove(dev->debugfs_bus_info); - debugfs_remove_recursive(dev->debugfs_dir); - visorchannel_destroy(dev->visorchannel); - kfree(dev); -} - -/* - * visorbus_release_device() - called when device_unregister() is called for - * each child device instance - * @xdev: struct device for the visor device being released - */ -static void visorbus_release_device(struct device *xdev) -{ - struct visor_device *dev = to_visor_device(xdev); - - visorchannel_destroy(dev->visorchannel); - kfree(dev); -} - -/* - * BUS specific channel attributes to appear under - * /sys/bus/visorbus/dev/channel - */ - -static ssize_t physaddr_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct visor_device *vdev = to_visor_device(dev); - - return sprintf(buf, "0x%llx\n", - visorchannel_get_physaddr(vdev->visorchannel)); -} -static DEVICE_ATTR_RO(physaddr); - -static ssize_t nbytes_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct visor_device *vdev = to_visor_device(dev); - - return sprintf(buf, "0x%lx\n", - visorchannel_get_nbytes(vdev->visorchannel)); -} -static DEVICE_ATTR_RO(nbytes); - -static ssize_t clientpartition_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct visor_device *vdev = to_visor_device(dev); - - return sprintf(buf, "0x%llx\n", - visorchannel_get_clientpartition(vdev->visorchannel)); -} -static DEVICE_ATTR_RO(clientpartition); - -static ssize_t typeguid_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct visor_device *vdev = to_visor_device(dev); - char typeid[LINESIZE]; - - return sprintf(buf, "%s\n", - visorchannel_id(vdev->visorchannel, typeid)); -} -static DEVICE_ATTR_RO(typeguid); - -static ssize_t zoneguid_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct visor_device *vdev = to_visor_device(dev); - char zoneid[LINESIZE]; - - return sprintf(buf, "%s\n", - visorchannel_zoneid(vdev->visorchannel, zoneid)); -} -static DEVICE_ATTR_RO(zoneguid); - -static ssize_t typename_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int i = 0; - struct bus_type *xbus = dev->bus; - struct device_driver *xdrv = dev->driver; - struct visor_driver *drv = NULL; - - if (!xdrv) - return 0; - i = xbus->match(dev, xdrv); - if (!i) - return 0; - drv = to_visor_driver(xdrv); - return sprintf(buf, "%s\n", drv->channel_types[i - 1].name); -} -static DEVICE_ATTR_RO(typename); - -static struct attribute *channel_attrs[] = { - &dev_attr_physaddr.attr, - &dev_attr_nbytes.attr, - &dev_attr_clientpartition.attr, - &dev_attr_typeguid.attr, - &dev_attr_zoneguid.attr, - &dev_attr_typename.attr, - NULL -}; - -ATTRIBUTE_GROUPS(channel); - -/* - * BUS instance attributes - * - * define & implement display of bus attributes under - * /sys/bus/visorbus/devices/visorbus. - */ -static ssize_t partition_handle_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct visor_device *vdev = to_visor_device(dev); - u64 handle = visorchannel_get_clientpartition(vdev->visorchannel); - - return sprintf(buf, "0x%llx\n", handle); -} -static DEVICE_ATTR_RO(partition_handle); - -static ssize_t partition_guid_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct visor_device *vdev = to_visor_device(dev); - - return sprintf(buf, "{%pUb}\n", &vdev->partition_guid); -} -static DEVICE_ATTR_RO(partition_guid); - -static ssize_t partition_name_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct visor_device *vdev = to_visor_device(dev); - - return sprintf(buf, "%s\n", vdev->name); -} -static DEVICE_ATTR_RO(partition_name); - -static ssize_t channel_addr_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct visor_device *vdev = to_visor_device(dev); - u64 addr = visorchannel_get_physaddr(vdev->visorchannel); - - return sprintf(buf, "0x%llx\n", addr); -} -static DEVICE_ATTR_RO(channel_addr); - -static ssize_t channel_bytes_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct visor_device *vdev = to_visor_device(dev); - u64 nbytes = visorchannel_get_nbytes(vdev->visorchannel); - - return sprintf(buf, "0x%llx\n", nbytes); -} -static DEVICE_ATTR_RO(channel_bytes); - -static ssize_t channel_id_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct visor_device *vdev = to_visor_device(dev); - int len = 0; - - visorchannel_id(vdev->visorchannel, buf); - len = strlen(buf); - buf[len++] = '\n'; - return len; -} -static DEVICE_ATTR_RO(channel_id); - -static struct attribute *visorbus_attrs[] = { - &dev_attr_partition_handle.attr, - &dev_attr_partition_guid.attr, - &dev_attr_partition_name.attr, - &dev_attr_channel_addr.attr, - &dev_attr_channel_bytes.attr, - &dev_attr_channel_id.attr, - NULL -}; - -ATTRIBUTE_GROUPS(visorbus); - -/* - * BUS debugfs entries - * - * define & implement display of debugfs attributes under - * /sys/kernel/debug/visorbus/visorbus. - */ - -/* - * vbuschannel_print_devinfo() - format a struct visor_vbus_deviceinfo - * and write it to a seq_file - * @devinfo: the struct visor_vbus_deviceinfo to format - * @seq: seq_file to write to - * @devix: the device index to be included in the output data, or -1 if no - * device index is to be included - * - * Reads @devInfo, and writes it in human-readable notation to @seq. - */ -static void vbuschannel_print_devinfo(struct visor_vbus_deviceinfo *devinfo, - struct seq_file *seq, int devix) -{ - /* uninitialized vbus device entry */ - if (!isprint(devinfo->devtype[0])) - return; - if (devix >= 0) - seq_printf(seq, "[%d]", devix); - else - /* vbus device entry is for bus or chipset */ - seq_puts(seq, " "); - /* - * Note: because the s-Par back-end is free to scribble in this area, - * we never assume '\0'-termination. - */ - seq_printf(seq, "%-*.*s ", (int)sizeof(devinfo->devtype), - (int)sizeof(devinfo->devtype), devinfo->devtype); - seq_printf(seq, "%-*.*s ", (int)sizeof(devinfo->drvname), - (int)sizeof(devinfo->drvname), devinfo->drvname); - seq_printf(seq, "%.*s\n", (int)sizeof(devinfo->infostrs), - devinfo->infostrs); -} - -static int bus_info_debugfs_show(struct seq_file *seq, void *v) -{ - int i = 0; - unsigned long off; - struct visor_vbus_deviceinfo dev_info; - struct visor_device *vdev = seq->private; - struct visorchannel *channel = vdev->visorchannel; - - if (!channel) - return 0; - - seq_printf(seq, - "Client device/driver info for %s partition (vbus #%u):\n", - ((vdev->name) ? (char *)(vdev->name) : ""), - vdev->chipset_bus_no); - if (visorchannel_read(channel, - offsetof(struct visor_vbus_channel, chp_info), - &dev_info, sizeof(dev_info)) >= 0) - vbuschannel_print_devinfo(&dev_info, seq, -1); - if (visorchannel_read(channel, - offsetof(struct visor_vbus_channel, bus_info), - &dev_info, sizeof(dev_info)) >= 0) - vbuschannel_print_devinfo(&dev_info, seq, -1); - - off = offsetof(struct visor_vbus_channel, dev_info); - while (off + sizeof(dev_info) <= visorchannel_get_nbytes(channel)) { - if (visorchannel_read(channel, off, &dev_info, - sizeof(dev_info)) >= 0) - vbuschannel_print_devinfo(&dev_info, seq, i); - off += sizeof(dev_info); - i++; - } - return 0; -} - -static int bus_info_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, bus_info_debugfs_show, inode->i_private); -} - -static const struct file_operations bus_info_debugfs_fops = { - .owner = THIS_MODULE, - .open = bus_info_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static void dev_periodic_work(struct timer_list *t) -{ - struct visor_device *dev = from_timer(dev, t, timer); - struct visor_driver *drv = to_visor_driver(dev->device.driver); - - drv->channel_interrupt(dev); - mod_timer(&dev->timer, jiffies + POLLJIFFIES_NORMALCHANNEL); -} - -static int dev_start_periodic_work(struct visor_device *dev) -{ - if (dev->being_removed || dev->timer_active) - return -EINVAL; - - /* now up by at least 2 */ - get_device(&dev->device); - dev->timer.expires = jiffies + POLLJIFFIES_NORMALCHANNEL; - add_timer(&dev->timer); - dev->timer_active = true; - return 0; -} - -static void dev_stop_periodic_work(struct visor_device *dev) -{ - if (!dev->timer_active) - return; - - del_timer_sync(&dev->timer); - dev->timer_active = false; - put_device(&dev->device); -} - -/* - * visordriver_remove_device() - handle visor device going away - * @xdev: struct device for the visor device being removed - * - * This is called when device_unregister() is called for each child device - * instance, to notify the appropriate visorbus function driver that the device - * is going away, and to decrease the reference count of the device. - * - * Return: 0 iff successful - */ -static int visordriver_remove_device(struct device *xdev) -{ - struct visor_device *dev = to_visor_device(xdev); - struct visor_driver *drv = to_visor_driver(xdev->driver); - - mutex_lock(&dev->visordriver_callback_lock); - dev->being_removed = true; - drv->remove(dev); - mutex_unlock(&dev->visordriver_callback_lock); - dev_stop_periodic_work(dev); - put_device(&dev->device); - return 0; -} - -/* - * visorbus_unregister_visor_driver() - unregisters the provided driver - * @drv: the driver to unregister - * - * A visor function driver calls this function to unregister the driver, - * i.e., within its module_exit function. - */ -void visorbus_unregister_visor_driver(struct visor_driver *drv) -{ - driver_unregister(&drv->driver); -} -EXPORT_SYMBOL_GPL(visorbus_unregister_visor_driver); - -/* - * visorbus_read_channel() - reads from the designated channel into - * the provided buffer - * @dev: the device whose channel is read from - * @offset: the offset into the channel at which reading starts - * @dest: the destination buffer that is written into from the channel - * @nbytes: the number of bytes to read from the channel - * - * If receiving a message, use the visorchannel_signalremove() function instead. - * - * Return: integer indicating success (zero) or failure (non-zero) - */ -int visorbus_read_channel(struct visor_device *dev, unsigned long offset, - void *dest, unsigned long nbytes) -{ - return visorchannel_read(dev->visorchannel, offset, dest, nbytes); -} -EXPORT_SYMBOL_GPL(visorbus_read_channel); - -/* - * visorbus_write_channel() - writes the provided buffer into the designated - * channel - * @dev: the device whose channel is written to - * @offset: the offset into the channel at which writing starts - * @src: the source buffer that is written into the channel - * @nbytes: the number of bytes to write into the channel - * - * If sending a message, use the visorchannel_signalinsert() function instead. - * - * Return: integer indicating success (zero) or failure (non-zero) - */ -int visorbus_write_channel(struct visor_device *dev, unsigned long offset, - void *src, unsigned long nbytes) -{ - return visorchannel_write(dev->visorchannel, offset, src, nbytes); -} -EXPORT_SYMBOL_GPL(visorbus_write_channel); - -/* - * visorbus_enable_channel_interrupts() - enables interrupts on the - * designated device - * @dev: the device on which to enable interrupts - * - * Currently we don't yet have a real interrupt, so for now we just call the - * interrupt function periodically via a timer. - */ -int visorbus_enable_channel_interrupts(struct visor_device *dev) -{ - struct visor_driver *drv = to_visor_driver(dev->device.driver); - - if (!drv->channel_interrupt) { - dev_err(&dev->device, "%s no interrupt function!\n", __func__); - return -ENOENT; - } - - return dev_start_periodic_work(dev); -} -EXPORT_SYMBOL_GPL(visorbus_enable_channel_interrupts); - -/* - * visorbus_disable_channel_interrupts() - disables interrupts on the - * designated device - * @dev: the device on which to disable interrupts - */ -void visorbus_disable_channel_interrupts(struct visor_device *dev) -{ - dev_stop_periodic_work(dev); -} -EXPORT_SYMBOL_GPL(visorbus_disable_channel_interrupts); - -/* - * create_visor_device() - create visor device as a result of receiving the - * controlvm device_create message for a new device - * @dev: a freshly-zeroed struct visor_device, containing only filled-in values - * for chipset_bus_no and chipset_dev_no, that will be initialized - * - * This is how everything starts from the device end. - * This function is called when a channel first appears via a ControlVM - * message. In response, this function allocates a visor_device to correspond - * to the new channel, and attempts to connect it the appropriate * driver. If - * the appropriate driver is found, the visor_driver.probe() function for that - * driver will be called, and will be passed the new * visor_device that we - * just created. - * - * It's ok if the appropriate driver is not yet loaded, because in that case - * the new device struct will just stick around in the bus' list of devices. - * When the appropriate driver calls visorbus_register_visor_driver(), the - * visor_driver.probe() for the new driver will be called with the new device. - * - * Return: 0 if successful, otherwise the negative value returned by - * device_add() indicating the reason for failure - */ -int create_visor_device(struct visor_device *dev) -{ - int err; - u32 chipset_bus_no = dev->chipset_bus_no; - u32 chipset_dev_no = dev->chipset_dev_no; - - mutex_init(&dev->visordriver_callback_lock); - dev->device.bus = &visorbus_type; - dev->device.groups = channel_groups; - device_initialize(&dev->device); - dev->device.release = visorbus_release_device; - /* keep a reference just for us (now 2) */ - get_device(&dev->device); - timer_setup(&dev->timer, dev_periodic_work, 0); - /* - * bus_id must be a unique name with respect to this bus TYPE (NOT bus - * instance). That's why we need to include the bus number within the - * name. - */ - err = dev_set_name(&dev->device, "vbus%u:dev%u", - chipset_bus_no, chipset_dev_no); - if (err) - goto err_put; - /* - * device_add does this: - * bus_add_device(dev) - * ->device_attach(dev) - * ->for each driver drv registered on the bus that dev is on - * if (dev.drv) ** device already has a driver ** - * ** not sure we could ever get here... ** - * else - * if (bus.match(dev,drv)) [visorbus_match] - * dev.drv = drv - * if (!drv.probe(dev)) [visordriver_probe_device] - * dev.drv = NULL - * - * Note that device_add does NOT fail if no driver failed to claim the - * device. The device will be linked onto bus_type.klist_devices - * regardless (use bus_for_each_dev). - */ - err = device_add(&dev->device); - if (err < 0) - goto err_put; - list_add_tail(&dev->list_all, &list_all_device_instances); - dev->state.created = 1; - visorbus_response(dev, err, CONTROLVM_DEVICE_CREATE); - /* success: reference kept via unmatched get_device() */ - return 0; - -err_put: - put_device(&dev->device); - dev_err(&dev->device, "Creating visor device failed. %d\n", err); - return err; -} - -void remove_visor_device(struct visor_device *dev) -{ - list_del(&dev->list_all); - put_device(&dev->device); - if (dev->pending_msg_hdr) - visorbus_response(dev, 0, CONTROLVM_DEVICE_DESTROY); - device_unregister(&dev->device); -} - -static int get_vbus_header_info(struct visorchannel *chan, - struct device *dev, - struct visor_vbus_headerinfo *hdr_info) -{ - int err; - - if (!visor_check_channel(visorchannel_get_header(chan), - dev, - &visor_vbus_channel_guid, - "vbus", - sizeof(struct visor_vbus_channel), - VISOR_VBUS_CHANNEL_VERSIONID, - VISOR_CHANNEL_SIGNATURE)) - return -EINVAL; - - err = visorchannel_read(chan, sizeof(struct channel_header), hdr_info, - sizeof(*hdr_info)); - if (err < 0) - return err; - if (hdr_info->struct_bytes < sizeof(struct visor_vbus_headerinfo)) - return -EINVAL; - if (hdr_info->device_info_struct_bytes < - sizeof(struct visor_vbus_deviceinfo)) - return -EINVAL; - return 0; -} - -/* - * write_vbus_chp_info() - write the contents of to the struct - * visor_vbus_channel.chp_info - * @chan: indentifies the s-Par channel that will be updated - * @hdr_info: used to find appropriate channel offset to write data - * @info: contains the information to write - * - * Writes chipset info into the channel memory to be used for diagnostic - * purposes. - * - * Returns no value since this is debug information and not needed for - * device functionality. - */ -static void write_vbus_chp_info(struct visorchannel *chan, - struct visor_vbus_headerinfo *hdr_info, - struct visor_vbus_deviceinfo *info) -{ - int off; - - if (hdr_info->chp_info_offset == 0) - return; - - off = sizeof(struct channel_header) + hdr_info->chp_info_offset; - visorchannel_write(chan, off, info, sizeof(*info)); -} - -/* - * write_vbus_bus_info() - write the contents of to the struct - * visor_vbus_channel.bus_info - * @chan: indentifies the s-Par channel that will be updated - * @hdr_info: used to find appropriate channel offset to write data - * @info: contains the information to write - * - * Writes bus info into the channel memory to be used for diagnostic - * purposes. - * - * Returns no value since this is debug information and not needed for - * device functionality. - */ -static void write_vbus_bus_info(struct visorchannel *chan, - struct visor_vbus_headerinfo *hdr_info, - struct visor_vbus_deviceinfo *info) -{ - int off; - - if (hdr_info->bus_info_offset == 0) - return; - - off = sizeof(struct channel_header) + hdr_info->bus_info_offset; - visorchannel_write(chan, off, info, sizeof(*info)); -} - -/* - * write_vbus_dev_info() - write the contents of to the struct - * visor_vbus_channel.dev_info[] - * @chan: indentifies the s-Par channel that will be updated - * @hdr_info: used to find appropriate channel offset to write data - * @info: contains the information to write - * @devix: the relative device number (0..n-1) of the device on the bus - * - * Writes device info into the channel memory to be used for diagnostic - * purposes. - * - * Returns no value since this is debug information and not needed for - * device functionality. - */ -static void write_vbus_dev_info(struct visorchannel *chan, - struct visor_vbus_headerinfo *hdr_info, - struct visor_vbus_deviceinfo *info, - unsigned int devix) -{ - int off; - - if (hdr_info->dev_info_offset == 0) - return; - off = (sizeof(struct channel_header) + hdr_info->dev_info_offset) + - (hdr_info->device_info_struct_bytes * devix); - visorchannel_write(chan, off, info, sizeof(*info)); -} - -static void bus_device_info_init( - struct visor_vbus_deviceinfo *bus_device_info_ptr, - const char *dev_type, const char *drv_name) -{ - memset(bus_device_info_ptr, 0, sizeof(struct visor_vbus_deviceinfo)); - snprintf(bus_device_info_ptr->devtype, - sizeof(bus_device_info_ptr->devtype), - "%s", (dev_type) ? dev_type : "unknownType"); - snprintf(bus_device_info_ptr->drvname, - sizeof(bus_device_info_ptr->drvname), - "%s", (drv_name) ? drv_name : "unknownDriver"); - snprintf(bus_device_info_ptr->infostrs, - sizeof(bus_device_info_ptr->infostrs), "kernel ver. %s", - utsname()->release); -} - -/* - * publish_vbus_dev_info() - for a child device just created on a client bus, - * fill in information about the driver that is - * controlling this device into the appropriate slot - * within the vbus channel of the bus instance - * @visordev: struct visor_device for the desired device - */ -static void publish_vbus_dev_info(struct visor_device *visordev) -{ - int i; - struct visor_device *bdev; - struct visor_driver *visordrv; - u32 bus_no = visordev->chipset_bus_no; - u32 dev_no = visordev->chipset_dev_no; - struct visor_vbus_deviceinfo dev_info; - const char *chan_type_name = NULL; - struct visor_vbus_headerinfo *hdr_info; - - if (!visordev->device.driver) - return; - bdev = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); - if (!bdev) - return; - hdr_info = (struct visor_vbus_headerinfo *)bdev->vbus_hdr_info; - if (!hdr_info) - return; - visordrv = to_visor_driver(visordev->device.driver); - - /* - * Within the list of device types (by GUID) that the driver - * says it supports, find out which one of those types matches - * the type of this device, so that we can include the device - * type name - */ - for (i = 0; visordrv->channel_types[i].name; i++) { - if (guid_equal(&visordrv->channel_types[i].guid, - &visordev->channel_type_guid)) { - chan_type_name = visordrv->channel_types[i].name; - break; - } - } - bus_device_info_init(&dev_info, chan_type_name, visordrv->name); - write_vbus_dev_info(bdev->visorchannel, hdr_info, &dev_info, dev_no); - write_vbus_chp_info(bdev->visorchannel, hdr_info, &chipset_driverinfo); - write_vbus_bus_info(bdev->visorchannel, hdr_info, - &clientbus_driverinfo); -} - -/* - * visordriver_probe_device() - handle new visor device coming online - * @xdev: struct device for the visor device being probed - * - * This is called automatically upon adding a visor_device (device_add), or - * adding a visor_driver (visorbus_register_visor_driver), but only after - * visorbus_match() has returned 1 to indicate a successful match between - * driver and device. - * - * If successful, a reference to the device will be held onto via get_device(). - * - * Return: 0 if successful, meaning the function driver's probe() function - * was successful with this device, otherwise a negative errno - * value indicating failure reason - */ -static int visordriver_probe_device(struct device *xdev) -{ - int err; - struct visor_driver *drv = to_visor_driver(xdev->driver); - struct visor_device *dev = to_visor_device(xdev); - - mutex_lock(&dev->visordriver_callback_lock); - dev->being_removed = false; - err = drv->probe(dev); - if (err) { - mutex_unlock(&dev->visordriver_callback_lock); - return err; - } - /* success: reference kept via unmatched get_device() */ - get_device(&dev->device); - publish_vbus_dev_info(dev); - mutex_unlock(&dev->visordriver_callback_lock); - return 0; -} - -/* - * visorbus_register_visor_driver() - registers the provided visor driver for - * handling one or more visor device - * types (channel_types) - * @drv: the driver to register - * - * A visor function driver calls this function to register the driver. The - * caller MUST fill in the following fields within the #drv structure: - * name, version, owner, channel_types, probe, remove - * - * Here's how the whole Linux bus / driver / device model works. - * - * At system start-up, the visorbus kernel module is loaded, which registers - * visorbus_type as a bus type, using bus_register(). - * - * All kernel modules that support particular device types on a - * visorbus bus are loaded. Each of these kernel modules calls - * visorbus_register_visor_driver() in their init functions, passing a - * visor_driver struct. visorbus_register_visor_driver() in turn calls - * register_driver(&visor_driver.driver). This .driver member is - * initialized with generic methods (like probe), whose sole responsibility - * is to act as a broker for the real methods, which are within the - * visor_driver struct. (This is the way the subclass behavior is - * implemented, since visor_driver is essentially a subclass of the - * generic driver.) Whenever a driver_register() happens, core bus code in - * the kernel does (see device_attach() in drivers/base/dd.c): - * - * for each dev associated with the bus (the bus that driver is on) that - * does not yet have a driver - * if bus.match(dev,newdriver) == yes_matched ** .match specified - * ** during bus_register(). - * newdriver.probe(dev) ** for visor drivers, this will call - * ** the generic driver.probe implemented in visorbus.c, - * ** which in turn calls the probe specified within the - * ** struct visor_driver (which was specified by the - * ** actual device driver as part of - * ** visorbus_register_visor_driver()). - * - * The above dance also happens when a new device appears. - * So the question is, how are devices created within the system? - * Basically, just call device_add(dev). See pci_bus_add_devices(). - * pci_scan_device() shows an example of how to build a device struct. It - * returns the newly-created struct to pci_scan_single_device(), who adds it - * to the list of devices at PCIBUS.devices. That list of devices is what - * is traversed by pci_bus_add_devices(). - * - * Return: integer indicating success (zero) or failure (non-zero) - */ -int visorbus_register_visor_driver(struct visor_driver *drv) -{ - /* can't register on a nonexistent bus */ - if (!initialized) - return -ENODEV; - if (!drv->probe) - return -EINVAL; - if (!drv->remove) - return -EINVAL; - if (!drv->pause) - return -EINVAL; - if (!drv->resume) - return -EINVAL; - - drv->driver.name = drv->name; - drv->driver.bus = &visorbus_type; - drv->driver.probe = visordriver_probe_device; - drv->driver.remove = visordriver_remove_device; - drv->driver.owner = drv->owner; - /* - * driver_register does this: - * bus_add_driver(drv) - * ->if (drv.bus) ** (bus_type) ** - * driver_attach(drv) - * for each dev with bus type of drv.bus - * if (!dev.drv) ** no driver assigned yet ** - * if (bus.match(dev,drv)) [visorbus_match] - * dev.drv = drv - * if (!drv.probe(dev)) [visordriver_probe_device] - * dev.drv = NULL - */ - return driver_register(&drv->driver); -} -EXPORT_SYMBOL_GPL(visorbus_register_visor_driver); - -/* - * visorbus_create_instance() - create a device instance for the visorbus itself - * @dev: struct visor_device indicating the bus instance - * - * Return: 0 for success, otherwise negative errno value indicating reason for - * failure - */ -int visorbus_create_instance(struct visor_device *dev) -{ - int id = dev->chipset_bus_no; - int err; - struct visor_vbus_headerinfo *hdr_info; - - hdr_info = kzalloc(sizeof(*hdr_info), GFP_KERNEL); - if (!hdr_info) - return -ENOMEM; - dev_set_name(&dev->device, "visorbus%d", id); - dev->device.bus = &visorbus_type; - dev->device.groups = visorbus_groups; - dev->device.release = visorbus_release_busdevice; - dev->debugfs_dir = debugfs_create_dir(dev_name(&dev->device), - visorbus_debugfs_dir); - dev->debugfs_bus_info = debugfs_create_file("client_bus_info", 0440, - dev->debugfs_dir, dev, - &bus_info_debugfs_fops); - dev_set_drvdata(&dev->device, dev); - err = get_vbus_header_info(dev->visorchannel, &dev->device, hdr_info); - if (err < 0) - goto err_debugfs_dir; - err = device_register(&dev->device); - if (err < 0) - goto err_debugfs_dir; - list_add_tail(&dev->list_all, &list_all_bus_instances); - dev->state.created = 1; - dev->vbus_hdr_info = (void *)hdr_info; - write_vbus_chp_info(dev->visorchannel, hdr_info, &chipset_driverinfo); - write_vbus_bus_info(dev->visorchannel, hdr_info, &clientbus_driverinfo); - visorbus_response(dev, err, CONTROLVM_BUS_CREATE); - return 0; - -err_debugfs_dir: - debugfs_remove_recursive(dev->debugfs_dir); - kfree(hdr_info); - dev_err(&dev->device, "%s failed: %d\n", __func__, err); - return err; -} - -/* - * visorbus_remove_instance() - remove a device instance for the visorbus itself - * @dev: struct visor_device indentifying the bus to remove - */ -void visorbus_remove_instance(struct visor_device *dev) -{ - /* - * Note that this will result in the release method for - * dev->dev being called, which will call - * visorbus_release_busdevice(). This has something to do with - * the put_device() done in device_unregister(), but I have never - * successfully been able to trace thru the code to see where/how - * release() gets called. But I know it does. - */ - kfree(dev->vbus_hdr_info); - list_del(&dev->list_all); - if (dev->pending_msg_hdr) - visorbus_response(dev, 0, CONTROLVM_BUS_DESTROY); - device_unregister(&dev->device); -} - -/* - * remove_all_visor_devices() - remove all child visorbus device instances - */ -static void remove_all_visor_devices(void) -{ - struct list_head *listentry, *listtmp; - - list_for_each_safe(listentry, listtmp, &list_all_device_instances) { - struct visor_device *dev; - - dev = list_entry(listentry, struct visor_device, list_all); - remove_visor_device(dev); - } -} - -/* - * pause_state_change_complete() - the callback function to be called by a - * visorbus function driver when a - * pending "pause device" operation has - * completed - * @dev: struct visor_device identifying the paused device - * @status: 0 iff the pause state change completed successfully, otherwise - * a negative errno value indicating the reason for failure - */ -static void pause_state_change_complete(struct visor_device *dev, int status) -{ - if (!dev->pausing) - return; - - dev->pausing = false; - visorbus_device_changestate_response(dev, status, - segment_state_standby); -} - -/* - * resume_state_change_complete() - the callback function to be called by a - * visorbus function driver when a - * pending "resume device" operation has - * completed - * @dev: struct visor_device identifying the resumed device - * @status: 0 iff the resume state change completed successfully, otherwise - * a negative errno value indicating the reason for failure - */ -static void resume_state_change_complete(struct visor_device *dev, int status) -{ - if (!dev->resuming) - return; - - dev->resuming = false; - /* - * Notify the chipset driver that the resume is complete, - * which will presumably want to send some sort of response to - * the initiator. - */ - visorbus_device_changestate_response(dev, status, - segment_state_running); -} - -/* - * visorchipset_initiate_device_pause_resume() - start a pause or resume - * operation for a visor device - * @dev: struct visor_device identifying the device being paused or resumed - * @is_pause: true to indicate pause operation, false to indicate resume - * - * Tell the subordinate function driver for a specific device to pause - * or resume that device. Success/failure result is returned asynchronously - * via a callback function; see pause_state_change_complete() and - * resume_state_change_complete(). - */ -static int visorchipset_initiate_device_pause_resume(struct visor_device *dev, - bool is_pause) -{ - int err; - struct visor_driver *drv; - - /* If no driver associated with the device nothing to pause/resume */ - if (!dev->device.driver) - return 0; - if (dev->pausing || dev->resuming) - return -EBUSY; - - drv = to_visor_driver(dev->device.driver); - if (is_pause) { - dev->pausing = true; - err = drv->pause(dev, pause_state_change_complete); - } else { - /* - * The vbus_dev_info structure in the channel was been cleared, - * make sure it is valid. - */ - publish_vbus_dev_info(dev); - dev->resuming = true; - err = drv->resume(dev, resume_state_change_complete); - } - return err; -} - -/* - * visorchipset_device_pause() - start a pause operation for a visor device - * @dev_info: struct visor_device identifying the device being paused - * - * Tell the subordinate function driver for a specific device to pause - * that device. Success/failure result is returned asynchronously - * via a callback function; see pause_state_change_complete(). - */ -int visorchipset_device_pause(struct visor_device *dev_info) -{ - int err; - - err = visorchipset_initiate_device_pause_resume(dev_info, true); - if (err < 0) { - dev_info->pausing = false; - return err; - } - return 0; -} - -/* - * visorchipset_device_resume() - start a resume operation for a visor device - * @dev_info: struct visor_device identifying the device being resumed - * - * Tell the subordinate function driver for a specific device to resume - * that device. Success/failure result is returned asynchronously - * via a callback function; see resume_state_change_complete(). - */ -int visorchipset_device_resume(struct visor_device *dev_info) -{ - int err; - - err = visorchipset_initiate_device_pause_resume(dev_info, false); - if (err < 0) { - dev_info->resuming = false; - return err; - } - return 0; -} - -int visorbus_init(void) -{ - int err; - - visorbus_debugfs_dir = debugfs_create_dir("visorbus", NULL); - bus_device_info_init(&clientbus_driverinfo, "clientbus", "visorbus"); - err = bus_register(&visorbus_type); - if (err < 0) - return err; - initialized = true; - bus_device_info_init(&chipset_driverinfo, "chipset", "visorchipset"); - return 0; -} - -void visorbus_exit(void) -{ - struct list_head *listentry, *listtmp; - - remove_all_visor_devices(); - list_for_each_safe(listentry, listtmp, &list_all_bus_instances) { - struct visor_device *dev; - - dev = list_entry(listentry, struct visor_device, list_all); - visorbus_remove_instance(dev); - } - bus_unregister(&visorbus_type); - initialized = false; - debugfs_remove_recursive(visorbus_debugfs_dir); -} diff --git a/drivers/staging/unisys/visorbus/visorbus_private.h b/drivers/staging/unisys/visorbus/visorbus_private.h deleted file mode 100644 index e48498fedc49..000000000000 --- a/drivers/staging/unisys/visorbus/visorbus_private.h +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2010 - 2015 UNISYS CORPORATION - * All rights reserved. - */ - -#ifndef __VISORBUS_PRIVATE_H__ -#define __VISORBUS_PRIVATE_H__ - -#include -#include - -#include "controlvmchannel.h" -#include "vbuschannel.h" -#include "visorbus.h" - -struct visor_device *visorbus_get_device_by_id(u32 bus_no, u32 dev_no, - struct visor_device *from); -int visorbus_create_instance(struct visor_device *dev); -void visorbus_remove_instance(struct visor_device *bus_info); -int create_visor_device(struct visor_device *dev_info); -void remove_visor_device(struct visor_device *dev_info); -int visorchipset_device_pause(struct visor_device *dev_info); -int visorchipset_device_resume(struct visor_device *dev_info); -void visorbus_response(struct visor_device *p, int response, int controlvm_id); -void visorbus_device_changestate_response(struct visor_device *p, int response, - struct visor_segment_state state); -int visorbus_init(void); -void visorbus_exit(void); - -/* visorchannel access functions */ -struct visorchannel *visorchannel_create(u64 physaddr, gfp_t gfp, - const guid_t *guid, bool needs_lock); -void visorchannel_destroy(struct visorchannel *channel); -int visorchannel_read(struct visorchannel *channel, ulong offset, - void *dest, ulong nbytes); -int visorchannel_write(struct visorchannel *channel, ulong offset, - void *dest, ulong nbytes); -u64 visorchannel_get_physaddr(struct visorchannel *channel); -ulong visorchannel_get_nbytes(struct visorchannel *channel); -char *visorchannel_id(struct visorchannel *channel, char *s); -char *visorchannel_zoneid(struct visorchannel *channel, char *s); -u64 visorchannel_get_clientpartition(struct visorchannel *channel); -int visorchannel_set_clientpartition(struct visorchannel *channel, - u64 partition_handle); -char *visorchannel_guid_id(const guid_t *guid, char *s); -void *visorchannel_get_header(struct visorchannel *channel); -#endif diff --git a/drivers/staging/unisys/visorbus/visorchannel.c b/drivers/staging/unisys/visorbus/visorchannel.c deleted file mode 100644 index f3996a750c3b..000000000000 --- a/drivers/staging/unisys/visorbus/visorchannel.c +++ /dev/null @@ -1,434 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2010 - 2015 UNISYS CORPORATION - * All rights reserved. - */ - -/* - * This provides s-Par channel communication primitives, which are - * independent of the mechanism used to access the channel data. - */ - -#include -#include -#include - -#include "visorbus.h" -#include "visorbus_private.h" -#include "controlvmchannel.h" - -#define VISOR_DRV_NAME "visorchannel" - -#define VISOR_CONSOLEVIDEO_CHANNEL_GUID \ - GUID_INIT(0x3cd6e705, 0xd6a2, 0x4aa5, \ - 0xad, 0x5c, 0x7b, 0x8, 0x88, 0x9d, 0xff, 0xe2) - -static const guid_t visor_video_guid = VISOR_CONSOLEVIDEO_CHANNEL_GUID; - -struct visorchannel { - u64 physaddr; - ulong nbytes; - void *mapped; - bool requested; - struct channel_header chan_hdr; - guid_t guid; - /* - * channel creator knows if more than one thread will be inserting or - * removing - */ - bool needs_lock; - /* protect head writes in chan_hdr */ - spinlock_t insert_lock; - /* protect tail writes in chan_hdr */ - spinlock_t remove_lock; - guid_t type; - guid_t inst; -}; - -void visorchannel_destroy(struct visorchannel *channel) -{ - if (!channel) - return; - - if (channel->mapped) { - memunmap(channel->mapped); - if (channel->requested) - release_mem_region(channel->physaddr, channel->nbytes); - } - kfree(channel); -} - -u64 visorchannel_get_physaddr(struct visorchannel *channel) -{ - return channel->physaddr; -} - -ulong visorchannel_get_nbytes(struct visorchannel *channel) -{ - return channel->nbytes; -} - -char *visorchannel_guid_id(const guid_t *guid, char *s) -{ - sprintf(s, "%pUL", guid); - return s; -} - -char *visorchannel_id(struct visorchannel *channel, char *s) -{ - return visorchannel_guid_id(&channel->guid, s); -} - -char *visorchannel_zoneid(struct visorchannel *channel, char *s) -{ - return visorchannel_guid_id(&channel->chan_hdr.zone_guid, s); -} - -u64 visorchannel_get_clientpartition(struct visorchannel *channel) -{ - return channel->chan_hdr.partition_handle; -} - -int visorchannel_set_clientpartition(struct visorchannel *channel, - u64 partition_handle) -{ - channel->chan_hdr.partition_handle = partition_handle; - return 0; -} - -/** - * visorchannel_get_guid() - queries the GUID of the designated channel - * @channel: the channel to query - * - * Return: the GUID of the provided channel - */ -const guid_t *visorchannel_get_guid(struct visorchannel *channel) -{ - return &channel->guid; -} -EXPORT_SYMBOL_GPL(visorchannel_get_guid); - -int visorchannel_read(struct visorchannel *channel, ulong offset, void *dest, - ulong nbytes) -{ - if (offset + nbytes > channel->nbytes) - return -EIO; - - memcpy(dest, channel->mapped + offset, nbytes); - return 0; -} - -int visorchannel_write(struct visorchannel *channel, ulong offset, void *dest, - ulong nbytes) -{ - size_t chdr_size = sizeof(struct channel_header); - size_t copy_size; - - if (offset + nbytes > channel->nbytes) - return -EIO; - - if (offset < chdr_size) { - copy_size = min(chdr_size - offset, nbytes); - memcpy(((char *)(&channel->chan_hdr)) + offset, - dest, copy_size); - } - memcpy(channel->mapped + offset, dest, nbytes); - return 0; -} - -void *visorchannel_get_header(struct visorchannel *channel) -{ - return &channel->chan_hdr; -} - -/* - * Return offset of a specific SIGNAL_QUEUE_HEADER from the beginning of a - * channel header - */ -static int sig_queue_offset(struct channel_header *chan_hdr, int q) -{ - return ((chan_hdr)->ch_space_offset + - ((q) * sizeof(struct signal_queue_header))); -} - -/* - * Return offset of a specific queue entry (data) from the beginning of a - * channel header - */ -static int sig_data_offset(struct channel_header *chan_hdr, int q, - struct signal_queue_header *sig_hdr, int slot) -{ - return (sig_queue_offset(chan_hdr, q) + sig_hdr->sig_base_offset + - (slot * sig_hdr->signal_size)); -} - -/* - * Write the contents of a specific field within a SIGNAL_QUEUE_HEADER back into - * host memory - */ -#define SIG_WRITE_FIELD(channel, queue, sig_hdr, FIELD) \ - visorchannel_write(channel, \ - sig_queue_offset(&channel->chan_hdr, queue) + \ - offsetof(struct signal_queue_header, FIELD), \ - &((sig_hdr)->FIELD), \ - sizeof((sig_hdr)->FIELD)) - -static int sig_read_header(struct visorchannel *channel, u32 queue, - struct signal_queue_header *sig_hdr) -{ - if (channel->chan_hdr.ch_space_offset < sizeof(struct channel_header)) - return -EINVAL; - - /* Read the appropriate SIGNAL_QUEUE_HEADER into local memory. */ - return visorchannel_read(channel, - sig_queue_offset(&channel->chan_hdr, queue), - sig_hdr, sizeof(struct signal_queue_header)); -} - -static int sig_read_data(struct visorchannel *channel, u32 queue, - struct signal_queue_header *sig_hdr, u32 slot, - void *data) -{ - int signal_data_offset = sig_data_offset(&channel->chan_hdr, queue, - sig_hdr, slot); - - return visorchannel_read(channel, signal_data_offset, - data, sig_hdr->signal_size); -} - -static int sig_write_data(struct visorchannel *channel, u32 queue, - struct signal_queue_header *sig_hdr, u32 slot, - void *data) -{ - int signal_data_offset = sig_data_offset(&channel->chan_hdr, queue, - sig_hdr, slot); - - return visorchannel_write(channel, signal_data_offset, - data, sig_hdr->signal_size); -} - -static int signalremove_inner(struct visorchannel *channel, u32 queue, - void *msg) -{ - struct signal_queue_header sig_hdr; - int error; - - error = sig_read_header(channel, queue, &sig_hdr); - if (error) - return error; - /* No signals to remove; have caller try again. */ - if (sig_hdr.head == sig_hdr.tail) - return -EAGAIN; - sig_hdr.tail = (sig_hdr.tail + 1) % sig_hdr.max_slots; - error = sig_read_data(channel, queue, &sig_hdr, sig_hdr.tail, msg); - if (error) - return error; - sig_hdr.num_received++; - /* - * For each data field in SIGNAL_QUEUE_HEADER that was modified, update - * host memory. Required for channel sync. - */ - mb(); - error = SIG_WRITE_FIELD(channel, queue, &sig_hdr, tail); - if (error) - return error; - error = SIG_WRITE_FIELD(channel, queue, &sig_hdr, num_received); - if (error) - return error; - return 0; -} - -/** - * visorchannel_signalremove() - removes a message from the designated - * channel/queue - * @channel: the channel the message will be removed from - * @queue: the queue the message will be removed from - * @msg: the message to remove - * - * Return: integer error code indicating the status of the removal - */ -int visorchannel_signalremove(struct visorchannel *channel, u32 queue, - void *msg) -{ - int rc; - unsigned long flags; - - if (channel->needs_lock) { - spin_lock_irqsave(&channel->remove_lock, flags); - rc = signalremove_inner(channel, queue, msg); - spin_unlock_irqrestore(&channel->remove_lock, flags); - } else { - rc = signalremove_inner(channel, queue, msg); - } - - return rc; -} -EXPORT_SYMBOL_GPL(visorchannel_signalremove); - -static bool queue_empty(struct visorchannel *channel, u32 queue) -{ - struct signal_queue_header sig_hdr; - - if (sig_read_header(channel, queue, &sig_hdr)) - return true; - return (sig_hdr.head == sig_hdr.tail); -} - -/** - * visorchannel_signalempty() - checks if the designated channel/queue contains - * any messages - * @channel: the channel to query - * @queue: the queue in the channel to query - * - * Return: boolean indicating whether any messages in the designated - * channel/queue are present - */ -bool visorchannel_signalempty(struct visorchannel *channel, u32 queue) -{ - bool rc; - unsigned long flags; - - if (!channel->needs_lock) - return queue_empty(channel, queue); - spin_lock_irqsave(&channel->remove_lock, flags); - rc = queue_empty(channel, queue); - spin_unlock_irqrestore(&channel->remove_lock, flags); - return rc; -} -EXPORT_SYMBOL_GPL(visorchannel_signalempty); - -static int signalinsert_inner(struct visorchannel *channel, u32 queue, - void *msg) -{ - struct signal_queue_header sig_hdr; - int err; - - err = sig_read_header(channel, queue, &sig_hdr); - if (err) - return err; - sig_hdr.head = (sig_hdr.head + 1) % sig_hdr.max_slots; - if (sig_hdr.head == sig_hdr.tail) { - sig_hdr.num_overflows++; - err = SIG_WRITE_FIELD(channel, queue, &sig_hdr, num_overflows); - if (err) - return err; - return -EIO; - } - err = sig_write_data(channel, queue, &sig_hdr, sig_hdr.head, msg); - if (err) - return err; - sig_hdr.num_sent++; - /* - * For each data field in SIGNAL_QUEUE_HEADER that was modified, update - * host memory. Required for channel sync. - */ - mb(); - err = SIG_WRITE_FIELD(channel, queue, &sig_hdr, head); - if (err) - return err; - err = SIG_WRITE_FIELD(channel, queue, &sig_hdr, num_sent); - if (err) - return err; - return 0; -} - -/* - * visorchannel_create() - creates the struct visorchannel abstraction for a - * data area in memory, but does NOT modify this data - * area - * @physaddr: physical address of start of channel - * @gfp: gfp_t to use when allocating memory for the data struct - * @guid: GUID that identifies channel type; - * @needs_lock: must specify true if you have multiple threads of execution - * that will be calling visorchannel methods of this - * visorchannel at the same time - * - * Return: pointer to visorchannel that was created if successful, - * otherwise NULL - */ -struct visorchannel *visorchannel_create(u64 physaddr, gfp_t gfp, - const guid_t *guid, bool needs_lock) -{ - struct visorchannel *channel; - int err; - size_t size = sizeof(struct channel_header); - - if (physaddr == 0) - return NULL; - - channel = kzalloc(sizeof(*channel), gfp); - if (!channel) - return NULL; - channel->needs_lock = needs_lock; - spin_lock_init(&channel->insert_lock); - spin_lock_init(&channel->remove_lock); - /* - * Video driver constains the efi framebuffer so it will get a conflict - * resource when requesting its full mem region. Since we are only - * using the efi framebuffer for video we can ignore this. Remember that - * we haven't requested it so we don't try to release later on. - */ - channel->requested = request_mem_region(physaddr, size, VISOR_DRV_NAME); - if (!channel->requested && !guid_equal(guid, &visor_video_guid)) - /* we only care about errors if this is not the video channel */ - goto err_destroy_channel; - channel->mapped = memremap(physaddr, size, MEMREMAP_WB); - if (!channel->mapped) { - release_mem_region(physaddr, size); - goto err_destroy_channel; - } - channel->physaddr = physaddr; - channel->nbytes = size; - err = visorchannel_read(channel, 0, &channel->chan_hdr, size); - if (err) - goto err_destroy_channel; - size = (ulong)channel->chan_hdr.size; - memunmap(channel->mapped); - if (channel->requested) - release_mem_region(channel->physaddr, channel->nbytes); - channel->mapped = NULL; - channel->requested = request_mem_region(channel->physaddr, size, - VISOR_DRV_NAME); - if (!channel->requested && !guid_equal(guid, &visor_video_guid)) - /* we only care about errors if this is not the video channel */ - goto err_destroy_channel; - channel->mapped = memremap(channel->physaddr, size, MEMREMAP_WB); - if (!channel->mapped) { - release_mem_region(channel->physaddr, size); - goto err_destroy_channel; - } - channel->nbytes = size; - guid_copy(&channel->guid, guid); - return channel; - -err_destroy_channel: - visorchannel_destroy(channel); - return NULL; -} - -/** - * visorchannel_signalinsert() - inserts a message into the designated - * channel/queue - * @channel: the channel the message will be added to - * @queue: the queue the message will be added to - * @msg: the message to insert - * - * Return: integer error code indicating the status of the insertion - */ -int visorchannel_signalinsert(struct visorchannel *channel, u32 queue, - void *msg) -{ - int rc; - unsigned long flags; - - if (channel->needs_lock) { - spin_lock_irqsave(&channel->insert_lock, flags); - rc = signalinsert_inner(channel, queue, msg); - spin_unlock_irqrestore(&channel->insert_lock, flags); - } else { - rc = signalinsert_inner(channel, queue, msg); - } - - return rc; -} -EXPORT_SYMBOL_GPL(visorchannel_signalinsert); diff --git a/drivers/staging/unisys/visorbus/visorchipset.c b/drivers/staging/unisys/visorbus/visorchipset.c deleted file mode 100644 index daff44d7a8c0..000000000000 --- a/drivers/staging/unisys/visorbus/visorchipset.c +++ /dev/null @@ -1,1686 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2010 - 2015 UNISYS CORPORATION - * All rights reserved. - */ - -#include -#include - -#include "visorbus.h" -#include "visorbus_private.h" - -/* {72120008-4AAB-11DC-8530-444553544200} */ -#define VISOR_SIOVM_GUID GUID_INIT(0x72120008, 0x4AAB, 0x11DC, 0x85, 0x30, \ - 0x44, 0x45, 0x53, 0x54, 0x42, 0x00) - -static const guid_t visor_vhba_channel_guid = VISOR_VHBA_CHANNEL_GUID; -static const guid_t visor_siovm_guid = VISOR_SIOVM_GUID; -static const guid_t visor_controlvm_channel_guid = VISOR_CONTROLVM_CHANNEL_GUID; - -#define POLLJIFFIES_CONTROLVM_FAST 1 -#define POLLJIFFIES_CONTROLVM_SLOW 100 - -#define MAX_CONTROLVM_PAYLOAD_BYTES (1024 * 128) - -#define UNISYS_VISOR_LEAF_ID 0x40000000 - -/* The s-Par leaf ID returns "UnisysSpar64" encoded across ebx, ecx, edx */ -#define UNISYS_VISOR_ID_EBX 0x73696e55 -#define UNISYS_VISOR_ID_ECX 0x70537379 -#define UNISYS_VISOR_ID_EDX 0x34367261 - -/* - * When the controlvm channel is idle for at least MIN_IDLE_SECONDS, we switch - * to slow polling mode. As soon as we get a controlvm message, we switch back - * to fast polling mode. - */ -#define MIN_IDLE_SECONDS 10 - -struct parser_context { - unsigned long allocbytes; - unsigned long param_bytes; - u8 *curr; - unsigned long bytes_remaining; - bool byte_stream; - struct visor_controlvm_parameters_header data; -}; - -/* VMCALL_CONTROLVM_ADDR: Used by all guests, not just IO. */ -#define VMCALL_CONTROLVM_ADDR 0x0501 - -enum vmcall_result { - VMCALL_RESULT_SUCCESS = 0, - VMCALL_RESULT_INVALID_PARAM = 1, - VMCALL_RESULT_DATA_UNAVAILABLE = 2, - VMCALL_RESULT_FAILURE_UNAVAILABLE = 3, - VMCALL_RESULT_DEVICE_ERROR = 4, - VMCALL_RESULT_DEVICE_NOT_READY = 5 -}; - -/* - * struct vmcall_io_controlvm_addr_params - Structure for IO VMCALLS. Has - * parameters to VMCALL_CONTROLVM_ADDR - * interface. - * @address: The Guest-relative physical address of the ControlVm channel. - * This VMCall fills this in with the appropriate address. - * Contents provided by this VMCALL (OUT). - * @channel_bytes: The size of the ControlVm channel in bytes This VMCall fills - * this in with the appropriate address. Contents provided by - * this VMCALL (OUT). - * @unused: Unused Bytes in the 64-Bit Aligned Struct. - */ -struct vmcall_io_controlvm_addr_params { - u64 address; - u32 channel_bytes; - u8 unused[4]; -} __packed; - -struct visorchipset_device { - struct acpi_device *acpi_device; - unsigned long poll_jiffies; - /* when we got our last controlvm message */ - unsigned long most_recent_message_jiffies; - struct delayed_work periodic_controlvm_work; - struct visorchannel *controlvm_channel; - unsigned long controlvm_payload_bytes_buffered; - /* - * The following variables are used to handle the scenario where we are - * unable to offload the payload from a controlvm message due to memory - * requirements. In this scenario, we simply stash the controlvm - * message, then attempt to process it again the next time - * controlvm_periodic_work() runs. - */ - struct controlvm_message controlvm_pending_msg; - bool controlvm_pending_msg_valid; - struct vmcall_io_controlvm_addr_params controlvm_params; -}; - -static struct visorchipset_device *chipset_dev; - -struct parahotplug_request { - struct list_head list; - int id; - unsigned long expiration; - struct controlvm_message msg; -}; - -/* prototypes for attributes */ -static ssize_t toolaction_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 tool_action = 0; - int err; - - err = visorchannel_read(chipset_dev->controlvm_channel, - offsetof(struct visor_controlvm_channel, - tool_action), - &tool_action, sizeof(u8)); - if (err) - return err; - return sprintf(buf, "%u\n", tool_action); -} - -static ssize_t toolaction_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - u8 tool_action; - int err; - - if (kstrtou8(buf, 10, &tool_action)) - return -EINVAL; - err = visorchannel_write(chipset_dev->controlvm_channel, - offsetof(struct visor_controlvm_channel, - tool_action), - &tool_action, sizeof(u8)); - if (err) - return err; - return count; -} -static DEVICE_ATTR_RW(toolaction); - -static ssize_t boottotool_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct efi_visor_indication efi_visor_indication; - int err; - - err = visorchannel_read(chipset_dev->controlvm_channel, - offsetof(struct visor_controlvm_channel, - efi_visor_ind), - &efi_visor_indication, - sizeof(struct efi_visor_indication)); - if (err) - return err; - return sprintf(buf, "%u\n", efi_visor_indication.boot_to_tool); -} - -static ssize_t boottotool_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int val, err; - struct efi_visor_indication efi_visor_indication; - - if (kstrtoint(buf, 10, &val)) - return -EINVAL; - efi_visor_indication.boot_to_tool = val; - err = visorchannel_write(chipset_dev->controlvm_channel, - offsetof(struct visor_controlvm_channel, - efi_visor_ind), - &(efi_visor_indication), - sizeof(struct efi_visor_indication)); - if (err) - return err; - return count; -} -static DEVICE_ATTR_RW(boottotool); - -static ssize_t error_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - u32 error = 0; - int err; - - err = visorchannel_read(chipset_dev->controlvm_channel, - offsetof(struct visor_controlvm_channel, - installation_error), - &error, sizeof(u32)); - if (err) - return err; - return sprintf(buf, "%u\n", error); -} - -static ssize_t error_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - u32 error; - int err; - - if (kstrtou32(buf, 10, &error)) - return -EINVAL; - err = visorchannel_write(chipset_dev->controlvm_channel, - offsetof(struct visor_controlvm_channel, - installation_error), - &error, sizeof(u32)); - if (err) - return err; - return count; -} -static DEVICE_ATTR_RW(error); - -static ssize_t textid_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - u32 text_id = 0; - int err; - - err = visorchannel_read(chipset_dev->controlvm_channel, - offsetof(struct visor_controlvm_channel, - installation_text_id), - &text_id, sizeof(u32)); - if (err) - return err; - return sprintf(buf, "%u\n", text_id); -} - -static ssize_t textid_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - u32 text_id; - int err; - - if (kstrtou32(buf, 10, &text_id)) - return -EINVAL; - err = visorchannel_write(chipset_dev->controlvm_channel, - offsetof(struct visor_controlvm_channel, - installation_text_id), - &text_id, sizeof(u32)); - if (err) - return err; - return count; -} -static DEVICE_ATTR_RW(textid); - -static ssize_t remaining_steps_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u16 remaining_steps = 0; - int err; - - err = visorchannel_read(chipset_dev->controlvm_channel, - offsetof(struct visor_controlvm_channel, - installation_remaining_steps), - &remaining_steps, sizeof(u16)); - if (err) - return err; - return sprintf(buf, "%hu\n", remaining_steps); -} - -static ssize_t remaining_steps_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - u16 remaining_steps; - int err; - - if (kstrtou16(buf, 10, &remaining_steps)) - return -EINVAL; - err = visorchannel_write(chipset_dev->controlvm_channel, - offsetof(struct visor_controlvm_channel, - installation_remaining_steps), - &remaining_steps, sizeof(u16)); - if (err) - return err; - return count; -} -static DEVICE_ATTR_RW(remaining_steps); - -static void controlvm_init_response(struct controlvm_message *msg, - struct controlvm_message_header *msg_hdr, - int response) -{ - memset(msg, 0, sizeof(struct controlvm_message)); - memcpy(&msg->hdr, msg_hdr, sizeof(struct controlvm_message_header)); - msg->hdr.payload_bytes = 0; - msg->hdr.payload_vm_offset = 0; - msg->hdr.payload_max_bytes = 0; - if (response < 0) { - msg->hdr.flags.failed = 1; - msg->hdr.completion_status = (u32)(-response); - } -} - -static int controlvm_respond_chipset_init( - struct controlvm_message_header *msg_hdr, - int response, - enum visor_chipset_feature features) -{ - struct controlvm_message outmsg; - - controlvm_init_response(&outmsg, msg_hdr, response); - outmsg.cmd.init_chipset.features = features; - return visorchannel_signalinsert(chipset_dev->controlvm_channel, - CONTROLVM_QUEUE_REQUEST, &outmsg); -} - -static int chipset_init(struct controlvm_message *inmsg) -{ - static int chipset_inited; - enum visor_chipset_feature features = 0; - int rc = CONTROLVM_RESP_SUCCESS; - int res = 0; - - if (chipset_inited) { - rc = -CONTROLVM_RESP_ALREADY_DONE; - res = -EIO; - goto out_respond; - } - chipset_inited = 1; - /* - * Set features to indicate we support parahotplug (if Command also - * supports it). Set the "reply" bit so Command knows this is a - * features-aware driver. - */ - features = inmsg->cmd.init_chipset.features & - VISOR_CHIPSET_FEATURE_PARA_HOTPLUG; - features |= VISOR_CHIPSET_FEATURE_REPLY; - -out_respond: - if (inmsg->hdr.flags.response_expected) - res = controlvm_respond_chipset_init(&inmsg->hdr, rc, features); - - return res; -} - -static int controlvm_respond(struct controlvm_message_header *msg_hdr, - int response, struct visor_segment_state *state) -{ - struct controlvm_message outmsg; - - controlvm_init_response(&outmsg, msg_hdr, response); - if (outmsg.hdr.flags.test_message == 1) - return -EINVAL; - if (state) { - outmsg.cmd.device_change_state.state = *state; - outmsg.cmd.device_change_state.flags.phys_device = 1; - } - return visorchannel_signalinsert(chipset_dev->controlvm_channel, - CONTROLVM_QUEUE_REQUEST, &outmsg); -} - -enum crash_obj_type { - CRASH_DEV, - CRASH_BUS, -}; - -static int save_crash_message(struct controlvm_message *msg, - enum crash_obj_type cr_type) -{ - u32 local_crash_msg_offset; - u16 local_crash_msg_count; - int err; - - err = visorchannel_read(chipset_dev->controlvm_channel, - offsetof(struct visor_controlvm_channel, - saved_crash_message_count), - &local_crash_msg_count, sizeof(u16)); - if (err) { - dev_err(&chipset_dev->acpi_device->dev, - "failed to read message count\n"); - return err; - } - if (local_crash_msg_count != CONTROLVM_CRASHMSG_MAX) { - dev_err(&chipset_dev->acpi_device->dev, - "invalid number of messages\n"); - return -EIO; - } - err = visorchannel_read(chipset_dev->controlvm_channel, - offsetof(struct visor_controlvm_channel, - saved_crash_message_offset), - &local_crash_msg_offset, sizeof(u32)); - if (err) { - dev_err(&chipset_dev->acpi_device->dev, - "failed to read offset\n"); - return err; - } - switch (cr_type) { - case CRASH_DEV: - local_crash_msg_offset += sizeof(struct controlvm_message); - err = visorchannel_write(chipset_dev->controlvm_channel, - local_crash_msg_offset, msg, - sizeof(struct controlvm_message)); - if (err) { - dev_err(&chipset_dev->acpi_device->dev, - "failed to write dev msg\n"); - return err; - } - break; - case CRASH_BUS: - err = visorchannel_write(chipset_dev->controlvm_channel, - local_crash_msg_offset, msg, - sizeof(struct controlvm_message)); - if (err) { - dev_err(&chipset_dev->acpi_device->dev, - "failed to write bus msg\n"); - return err; - } - break; - default: - dev_err(&chipset_dev->acpi_device->dev, - "Invalid crash_obj_type\n"); - break; - } - return 0; -} - -static int controlvm_responder(enum controlvm_id cmd_id, - struct controlvm_message_header *pending_msg_hdr, - int response) -{ - if (pending_msg_hdr->id != (u32)cmd_id) - return -EINVAL; - - return controlvm_respond(pending_msg_hdr, response, NULL); -} - -static int device_changestate_responder(enum controlvm_id cmd_id, - struct visor_device *p, int response, - struct visor_segment_state state) -{ - struct controlvm_message outmsg; - - if (p->pending_msg_hdr->id != cmd_id) - return -EINVAL; - - controlvm_init_response(&outmsg, p->pending_msg_hdr, response); - outmsg.cmd.device_change_state.bus_no = p->chipset_bus_no; - outmsg.cmd.device_change_state.dev_no = p->chipset_dev_no; - outmsg.cmd.device_change_state.state = state; - return visorchannel_signalinsert(chipset_dev->controlvm_channel, - CONTROLVM_QUEUE_REQUEST, &outmsg); -} - -static int visorbus_create(struct controlvm_message *inmsg) -{ - struct controlvm_message_packet *cmd = &inmsg->cmd; - struct controlvm_message_header *pmsg_hdr; - u32 bus_no = cmd->create_bus.bus_no; - struct visor_device *bus_info; - struct visorchannel *visorchannel; - int err; - - bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); - if (bus_info && bus_info->state.created == 1) { - dev_err(&chipset_dev->acpi_device->dev, - "failed %s: already exists\n", __func__); - err = -EEXIST; - goto err_respond; - } - bus_info = kzalloc(sizeof(*bus_info), GFP_KERNEL); - if (!bus_info) { - err = -ENOMEM; - goto err_respond; - } - INIT_LIST_HEAD(&bus_info->list_all); - bus_info->chipset_bus_no = bus_no; - bus_info->chipset_dev_no = BUS_ROOT_DEVICE; - if (guid_equal(&cmd->create_bus.bus_inst_guid, &visor_siovm_guid)) { - err = save_crash_message(inmsg, CRASH_BUS); - if (err) - goto err_free_bus_info; - } - if (inmsg->hdr.flags.response_expected == 1) { - pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL); - if (!pmsg_hdr) { - err = -ENOMEM; - goto err_free_bus_info; - } - memcpy(pmsg_hdr, &inmsg->hdr, - sizeof(struct controlvm_message_header)); - bus_info->pending_msg_hdr = pmsg_hdr; - } - visorchannel = visorchannel_create(cmd->create_bus.channel_addr, - GFP_KERNEL, - &cmd->create_bus.bus_data_type_guid, - false); - if (!visorchannel) { - err = -ENOMEM; - goto err_free_pending_msg; - } - bus_info->visorchannel = visorchannel; - /* Response will be handled by visorbus_create_instance on success */ - err = visorbus_create_instance(bus_info); - if (err) - goto err_destroy_channel; - return 0; - -err_destroy_channel: - visorchannel_destroy(visorchannel); - -err_free_pending_msg: - kfree(bus_info->pending_msg_hdr); - -err_free_bus_info: - kfree(bus_info); - -err_respond: - if (inmsg->hdr.flags.response_expected == 1) - controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err); - return err; -} - -static int visorbus_destroy(struct controlvm_message *inmsg) -{ - struct controlvm_message_header *pmsg_hdr; - u32 bus_no = inmsg->cmd.destroy_bus.bus_no; - struct visor_device *bus_info; - int err; - - bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); - if (!bus_info) { - err = -ENODEV; - goto err_respond; - } - if (bus_info->state.created == 0) { - err = -ENOENT; - goto err_respond; - } - if (bus_info->pending_msg_hdr) { - /* only non-NULL if dev is still waiting on a response */ - err = -EEXIST; - goto err_respond; - } - if (inmsg->hdr.flags.response_expected == 1) { - pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL); - if (!pmsg_hdr) { - err = -ENOMEM; - goto err_respond; - } - memcpy(pmsg_hdr, &inmsg->hdr, - sizeof(struct controlvm_message_header)); - bus_info->pending_msg_hdr = pmsg_hdr; - } - /* Response will be handled by visorbus_remove_instance */ - visorbus_remove_instance(bus_info); - return 0; - -err_respond: - if (inmsg->hdr.flags.response_expected == 1) - controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err); - return err; -} - -static const guid_t *parser_id_get(struct parser_context *ctx) -{ - return &ctx->data.id; -} - -static void *parser_string_get(u8 *pscan, int nscan) -{ - int value_length; - void *value; - - if (nscan == 0) - return NULL; - - value_length = strnlen(pscan, nscan); - value = kzalloc(value_length + 1, GFP_KERNEL); - if (!value) - return NULL; - if (value_length > 0) - memcpy(value, pscan, value_length); - return value; -} - -static void *parser_name_get(struct parser_context *ctx) -{ - struct visor_controlvm_parameters_header *phdr; - - phdr = &ctx->data; - if ((unsigned long)phdr->name_offset + - (unsigned long)phdr->name_length > ctx->param_bytes) - return NULL; - ctx->curr = (char *)&phdr + phdr->name_offset; - ctx->bytes_remaining = phdr->name_length; - return parser_string_get(ctx->curr, phdr->name_length); -} - -static int visorbus_configure(struct controlvm_message *inmsg, - struct parser_context *parser_ctx) -{ - struct controlvm_message_packet *cmd = &inmsg->cmd; - u32 bus_no; - struct visor_device *bus_info; - int err = 0; - - bus_no = cmd->configure_bus.bus_no; - bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); - if (!bus_info) { - err = -EINVAL; - goto err_respond; - } - if (bus_info->state.created == 0) { - err = -EINVAL; - goto err_respond; - } - if (bus_info->pending_msg_hdr) { - err = -EIO; - goto err_respond; - } - err = visorchannel_set_clientpartition(bus_info->visorchannel, - cmd->configure_bus.guest_handle); - if (err) - goto err_respond; - if (parser_ctx) { - const guid_t *partition_guid = parser_id_get(parser_ctx); - - guid_copy(&bus_info->partition_guid, partition_guid); - bus_info->name = parser_name_get(parser_ctx); - } - if (inmsg->hdr.flags.response_expected == 1) - controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err); - return 0; - -err_respond: - dev_err(&chipset_dev->acpi_device->dev, - "%s exited with err: %d\n", __func__, err); - if (inmsg->hdr.flags.response_expected == 1) - controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err); - return err; -} - -static int visorbus_device_create(struct controlvm_message *inmsg) -{ - struct controlvm_message_packet *cmd = &inmsg->cmd; - struct controlvm_message_header *pmsg_hdr; - u32 bus_no = cmd->create_device.bus_no; - u32 dev_no = cmd->create_device.dev_no; - struct visor_device *dev_info; - struct visor_device *bus_info; - struct visorchannel *visorchannel; - int err; - - bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); - if (!bus_info) { - dev_err(&chipset_dev->acpi_device->dev, - "failed to get bus by id: %d\n", bus_no); - err = -ENODEV; - goto err_respond; - } - if (bus_info->state.created == 0) { - dev_err(&chipset_dev->acpi_device->dev, - "bus not created, id: %d\n", bus_no); - err = -EINVAL; - goto err_respond; - } - dev_info = visorbus_get_device_by_id(bus_no, dev_no, NULL); - if (dev_info && dev_info->state.created == 1) { - dev_err(&chipset_dev->acpi_device->dev, - "failed to get bus by id: %d/%d\n", bus_no, dev_no); - err = -EEXIST; - goto err_respond; - } - - dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL); - if (!dev_info) { - err = -ENOMEM; - goto err_respond; - } - dev_info->chipset_bus_no = bus_no; - dev_info->chipset_dev_no = dev_no; - guid_copy(&dev_info->inst, &cmd->create_device.dev_inst_guid); - dev_info->device.parent = &bus_info->device; - visorchannel = visorchannel_create(cmd->create_device.channel_addr, - GFP_KERNEL, - &cmd->create_device.data_type_guid, - true); - if (!visorchannel) { - dev_err(&chipset_dev->acpi_device->dev, - "failed to create visorchannel: %d/%d\n", - bus_no, dev_no); - err = -ENOMEM; - goto err_free_dev_info; - } - dev_info->visorchannel = visorchannel; - guid_copy(&dev_info->channel_type_guid, - &cmd->create_device.data_type_guid); - if (guid_equal(&cmd->create_device.data_type_guid, - &visor_vhba_channel_guid)) { - err = save_crash_message(inmsg, CRASH_DEV); - if (err) - goto err_destroy_visorchannel; - } - if (inmsg->hdr.flags.response_expected == 1) { - pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL); - if (!pmsg_hdr) { - err = -ENOMEM; - goto err_destroy_visorchannel; - } - memcpy(pmsg_hdr, &inmsg->hdr, - sizeof(struct controlvm_message_header)); - dev_info->pending_msg_hdr = pmsg_hdr; - } - /* create_visor_device will send response */ - err = create_visor_device(dev_info); - if (err) - goto err_destroy_visorchannel; - - return 0; - -err_destroy_visorchannel: - visorchannel_destroy(visorchannel); - -err_free_dev_info: - kfree(dev_info); - -err_respond: - if (inmsg->hdr.flags.response_expected == 1) - controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err); - return err; -} - -static int visorbus_device_changestate(struct controlvm_message *inmsg) -{ - struct controlvm_message_packet *cmd = &inmsg->cmd; - struct controlvm_message_header *pmsg_hdr; - u32 bus_no = cmd->device_change_state.bus_no; - u32 dev_no = cmd->device_change_state.dev_no; - struct visor_segment_state state = cmd->device_change_state.state; - struct visor_device *dev_info; - int err = 0; - - dev_info = visorbus_get_device_by_id(bus_no, dev_no, NULL); - if (!dev_info) { - err = -ENODEV; - goto err_respond; - } - if (dev_info->state.created == 0) { - err = -EINVAL; - goto err_respond; - } - if (dev_info->pending_msg_hdr) { - /* only non-NULL if dev is still waiting on a response */ - err = -EIO; - goto err_respond; - } - - if (inmsg->hdr.flags.response_expected == 1) { - pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL); - if (!pmsg_hdr) { - err = -ENOMEM; - goto err_respond; - } - memcpy(pmsg_hdr, &inmsg->hdr, - sizeof(struct controlvm_message_header)); - dev_info->pending_msg_hdr = pmsg_hdr; - } - if (state.alive == segment_state_running.alive && - state.operating == segment_state_running.operating) - /* Response will be sent from visorchipset_device_resume */ - err = visorchipset_device_resume(dev_info); - /* ServerNotReady / ServerLost / SegmentStateStandby */ - else if (state.alive == segment_state_standby.alive && - state.operating == segment_state_standby.operating) - /* - * technically this is standby case where server is lost. - * Response will be sent from visorchipset_device_pause. - */ - err = visorchipset_device_pause(dev_info); - if (err) - goto err_respond; - return 0; - -err_respond: - dev_err(&chipset_dev->acpi_device->dev, "failed: %d\n", err); - if (inmsg->hdr.flags.response_expected == 1) - controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err); - return err; -} - -static int visorbus_device_destroy(struct controlvm_message *inmsg) -{ - struct controlvm_message_packet *cmd = &inmsg->cmd; - struct controlvm_message_header *pmsg_hdr; - u32 bus_no = cmd->destroy_device.bus_no; - u32 dev_no = cmd->destroy_device.dev_no; - struct visor_device *dev_info; - int err; - - dev_info = visorbus_get_device_by_id(bus_no, dev_no, NULL); - if (!dev_info) { - err = -ENODEV; - goto err_respond; - } - if (dev_info->state.created == 0) { - err = -EINVAL; - goto err_respond; - } - if (dev_info->pending_msg_hdr) { - /* only non-NULL if dev is still waiting on a response */ - err = -EIO; - goto err_respond; - } - if (inmsg->hdr.flags.response_expected == 1) { - pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL); - if (!pmsg_hdr) { - err = -ENOMEM; - goto err_respond; - } - - memcpy(pmsg_hdr, &inmsg->hdr, - sizeof(struct controlvm_message_header)); - dev_info->pending_msg_hdr = pmsg_hdr; - } - kfree(dev_info->name); - remove_visor_device(dev_info); - return 0; - -err_respond: - if (inmsg->hdr.flags.response_expected == 1) - controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err); - return err; -} - -/* - * The general parahotplug flow works as follows. The visorchipset receives - * a DEVICE_CHANGESTATE message from Command specifying a physical device - * to enable or disable. The CONTROLVM message handler calls - * parahotplug_process_message, which then adds the message to a global list - * and kicks off a udev event which causes a user level script to enable or - * disable the specified device. The udev script then writes to - * /sys/devices/platform/visorchipset/parahotplug, which causes the - * parahotplug store functions to get called, at which point the - * appropriate CONTROLVM message is retrieved from the list and responded to. - */ - -#define PARAHOTPLUG_TIMEOUT_MS 2000 - -/* - * parahotplug_next_id() - generate unique int to match an outstanding - * CONTROLVM message with a udev script /sys - * response - * - * Return: a unique integer value - */ -static int parahotplug_next_id(void) -{ - static atomic_t id = ATOMIC_INIT(0); - - return atomic_inc_return(&id); -} - -/* - * parahotplug_next_expiration() - returns the time (in jiffies) when a - * CONTROLVM message on the list should expire - * -- PARAHOTPLUG_TIMEOUT_MS in the future - * - * Return: expected expiration time (in jiffies) - */ -static unsigned long parahotplug_next_expiration(void) -{ - return jiffies + msecs_to_jiffies(PARAHOTPLUG_TIMEOUT_MS); -} - -/* - * parahotplug_request_create() - create a parahotplug_request, which is - * basically a wrapper for a CONTROLVM_MESSAGE - * that we can stick on a list - * @msg: the message to insert in the request - * - * Return: the request containing the provided message - */ -static struct parahotplug_request *parahotplug_request_create( - struct controlvm_message *msg) -{ - struct parahotplug_request *req; - - req = kmalloc(sizeof(*req), GFP_KERNEL); - if (!req) - return NULL; - req->id = parahotplug_next_id(); - req->expiration = parahotplug_next_expiration(); - req->msg = *msg; - return req; -} - -/* - * parahotplug_request_destroy() - free a parahotplug_request - * @req: the request to deallocate - */ -static void parahotplug_request_destroy(struct parahotplug_request *req) -{ - kfree(req); -} - -static LIST_HEAD(parahotplug_request_list); -/* lock for above */ -static DEFINE_SPINLOCK(parahotplug_request_list_lock); - -/* - * parahotplug_request_complete() - mark request as complete - * @id: the id of the request - * @active: indicates whether the request is assigned to active partition - * - * Called from the /sys handler, which means the user script has - * finished the enable/disable. Find the matching identifier, and - * respond to the CONTROLVM message with success. - * - * Return: 0 on success or -EINVAL on failure - */ -static int parahotplug_request_complete(int id, u16 active) -{ - struct list_head *pos; - struct list_head *tmp; - struct parahotplug_request *req; - - spin_lock(¶hotplug_request_list_lock); - /* Look for a request matching "id". */ - list_for_each_safe(pos, tmp, ¶hotplug_request_list) { - req = list_entry(pos, struct parahotplug_request, list); - if (req->id == id) { - /* - * Found a match. Remove it from the list and - * respond. - */ - list_del(pos); - spin_unlock(¶hotplug_request_list_lock); - req->msg.cmd.device_change_state.state.active = active; - if (req->msg.hdr.flags.response_expected) - controlvm_respond( - &req->msg.hdr, CONTROLVM_RESP_SUCCESS, - &req->msg.cmd.device_change_state.state); - parahotplug_request_destroy(req); - return 0; - } - } - spin_unlock(¶hotplug_request_list_lock); - return -EINVAL; -} - -/* - * devicedisabled_store() - disables the hotplug device - * @dev: sysfs interface variable not utilized in this function - * @attr: sysfs interface variable not utilized in this function - * @buf: buffer containing the device id - * @count: the size of the buffer - * - * The parahotplug/devicedisabled interface gets called by our support script - * when an SR-IOV device has been shut down. The ID is passed to the script - * and then passed back when the device has been removed. - * - * Return: the size of the buffer for success or negative for error - */ -static ssize_t devicedisabled_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - unsigned int id; - int err; - - if (kstrtouint(buf, 10, &id)) - return -EINVAL; - err = parahotplug_request_complete(id, 0); - if (err < 0) - return err; - return count; -} -static DEVICE_ATTR_WO(devicedisabled); - -/* - * deviceenabled_store() - enables the hotplug device - * @dev: sysfs interface variable not utilized in this function - * @attr: sysfs interface variable not utilized in this function - * @buf: buffer containing the device id - * @count: the size of the buffer - * - * The parahotplug/deviceenabled interface gets called by our support script - * when an SR-IOV device has been recovered. The ID is passed to the script - * and then passed back when the device has been brought back up. - * - * Return: the size of the buffer for success or negative for error - */ -static ssize_t deviceenabled_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - unsigned int id; - - if (kstrtouint(buf, 10, &id)) - return -EINVAL; - parahotplug_request_complete(id, 1); - return count; -} -static DEVICE_ATTR_WO(deviceenabled); - -static struct attribute *visorchipset_install_attrs[] = { - &dev_attr_toolaction.attr, - &dev_attr_boottotool.attr, - &dev_attr_error.attr, - &dev_attr_textid.attr, - &dev_attr_remaining_steps.attr, - NULL -}; - -static const struct attribute_group visorchipset_install_group = { - .name = "install", - .attrs = visorchipset_install_attrs -}; - -static struct attribute *visorchipset_parahotplug_attrs[] = { - &dev_attr_devicedisabled.attr, - &dev_attr_deviceenabled.attr, - NULL -}; - -static const struct attribute_group visorchipset_parahotplug_group = { - .name = "parahotplug", - .attrs = visorchipset_parahotplug_attrs -}; - -static const struct attribute_group *visorchipset_dev_groups[] = { - &visorchipset_install_group, - &visorchipset_parahotplug_group, - NULL -}; - -/* - * parahotplug_request_kickoff() - initiate parahotplug request - * @req: the request to initiate - * - * Cause uevent to run the user level script to do the disable/enable specified - * in the parahotplug_request. - */ -static int parahotplug_request_kickoff(struct parahotplug_request *req) -{ - struct controlvm_message_packet *cmd = &req->msg.cmd; - char env_cmd[40], env_id[40], env_state[40], env_bus[40], env_dev[40], - env_func[40]; - char *envp[] = { env_cmd, env_id, env_state, env_bus, env_dev, - env_func, NULL - }; - - sprintf(env_cmd, "VISOR_PARAHOTPLUG=1"); - sprintf(env_id, "VISOR_PARAHOTPLUG_ID=%d", req->id); - sprintf(env_state, "VISOR_PARAHOTPLUG_STATE=%d", - cmd->device_change_state.state.active); - sprintf(env_bus, "VISOR_PARAHOTPLUG_BUS=%d", - cmd->device_change_state.bus_no); - sprintf(env_dev, "VISOR_PARAHOTPLUG_DEVICE=%d", - cmd->device_change_state.dev_no >> 3); - sprintf(env_func, "VISOR_PARAHOTPLUG_FUNCTION=%d", - cmd->device_change_state.dev_no & 0x7); - return kobject_uevent_env(&chipset_dev->acpi_device->dev.kobj, - KOBJ_CHANGE, envp); -} - -/* - * parahotplug_process_message() - enables or disables a PCI device by kicking - * off a udev script - * @inmsg: the message indicating whether to enable or disable - */ -static int parahotplug_process_message(struct controlvm_message *inmsg) -{ - struct parahotplug_request *req; - int err; - - req = parahotplug_request_create(inmsg); - if (!req) - return -ENOMEM; - /* - * For enable messages, just respond with success right away, we don't - * need to wait to see if the enable was successful. - */ - if (inmsg->cmd.device_change_state.state.active) { - err = parahotplug_request_kickoff(req); - if (err) - goto err_respond; - controlvm_respond(&inmsg->hdr, CONTROLVM_RESP_SUCCESS, - &inmsg->cmd.device_change_state.state); - parahotplug_request_destroy(req); - return 0; - } - /* - * For disable messages, add the request to the request list before - * kicking off the udev script. It won't get responded to until the - * script has indicated it's done. - */ - spin_lock(¶hotplug_request_list_lock); - list_add_tail(&req->list, ¶hotplug_request_list); - spin_unlock(¶hotplug_request_list_lock); - err = parahotplug_request_kickoff(req); - if (err) - goto err_respond; - return 0; - -err_respond: - controlvm_respond(&inmsg->hdr, err, - &inmsg->cmd.device_change_state.state); - return err; -} - -/* - * chipset_ready_uevent() - sends chipset_ready action - * - * Send ACTION=online for DEVPATH=/sys/devices/platform/visorchipset. - * - * Return: 0 on success, negative on failure - */ -static int chipset_ready_uevent(struct controlvm_message_header *msg_hdr) -{ - int res; - - res = kobject_uevent(&chipset_dev->acpi_device->dev.kobj, KOBJ_ONLINE); - if (msg_hdr->flags.response_expected) - controlvm_respond(msg_hdr, res, NULL); - return res; -} - -/* - * chipset_selftest_uevent() - sends chipset_selftest action - * - * Send ACTION=online for DEVPATH=/sys/devices/platform/visorchipset. - * - * Return: 0 on success, negative on failure - */ -static int chipset_selftest_uevent(struct controlvm_message_header *msg_hdr) -{ - char env_selftest[20]; - char *envp[] = { env_selftest, NULL }; - int res; - - sprintf(env_selftest, "SPARSP_SELFTEST=%d", 1); - res = kobject_uevent_env(&chipset_dev->acpi_device->dev.kobj, - KOBJ_CHANGE, envp); - if (msg_hdr->flags.response_expected) - controlvm_respond(msg_hdr, res, NULL); - return res; -} - -/* - * chipset_notready_uevent() - sends chipset_notready action - * - * Send ACTION=offline for DEVPATH=/sys/devices/platform/visorchipset. - * - * Return: 0 on success, negative on failure - */ -static int chipset_notready_uevent(struct controlvm_message_header *msg_hdr) -{ - int res = kobject_uevent(&chipset_dev->acpi_device->dev.kobj, - KOBJ_OFFLINE); - - if (msg_hdr->flags.response_expected) - controlvm_respond(msg_hdr, res, NULL); - return res; -} - -static int unisys_vmcall(unsigned long tuple, unsigned long param) -{ - int result = 0; - unsigned int cpuid_eax, cpuid_ebx, cpuid_ecx, cpuid_edx; - unsigned long reg_ebx; - unsigned long reg_ecx; - - reg_ebx = param & 0xFFFFFFFF; - reg_ecx = param >> 32; - cpuid(0x00000001, &cpuid_eax, &cpuid_ebx, &cpuid_ecx, &cpuid_edx); - if (!(cpuid_ecx & 0x80000000)) - return -EPERM; - __asm__ __volatile__(".byte 0x00f, 0x001, 0x0c1" : "=a"(result) : - "a"(tuple), "b"(reg_ebx), "c"(reg_ecx)); - if (result) - goto error; - return 0; - -/* Need to convert from VMCALL error codes to Linux */ -error: - switch (result) { - case VMCALL_RESULT_INVALID_PARAM: - return -EINVAL; - case VMCALL_RESULT_DATA_UNAVAILABLE: - return -ENODEV; - default: - return -EFAULT; - } -} - -static int controlvm_channel_create(struct visorchipset_device *dev) -{ - struct visorchannel *chan; - u64 addr; - int err; - - err = unisys_vmcall(VMCALL_CONTROLVM_ADDR, - virt_to_phys(&dev->controlvm_params)); - if (err) - return err; - addr = dev->controlvm_params.address; - chan = visorchannel_create(addr, GFP_KERNEL, - &visor_controlvm_channel_guid, true); - if (!chan) - return -ENOMEM; - dev->controlvm_channel = chan; - return 0; -} - -static void setup_crash_devices_work_queue(struct work_struct *work) -{ - struct controlvm_message local_crash_bus_msg; - struct controlvm_message local_crash_dev_msg; - struct controlvm_message msg; - u32 local_crash_msg_offset; - u16 local_crash_msg_count; - - /* send init chipset msg */ - msg.hdr.id = CONTROLVM_CHIPSET_INIT; - msg.cmd.init_chipset.bus_count = 23; - msg.cmd.init_chipset.switch_count = 0; - chipset_init(&msg); - /* get saved message count */ - if (visorchannel_read(chipset_dev->controlvm_channel, - offsetof(struct visor_controlvm_channel, - saved_crash_message_count), - &local_crash_msg_count, sizeof(u16)) < 0) { - dev_err(&chipset_dev->acpi_device->dev, - "failed to read channel\n"); - return; - } - if (local_crash_msg_count != CONTROLVM_CRASHMSG_MAX) { - dev_err(&chipset_dev->acpi_device->dev, "invalid count\n"); - return; - } - /* get saved crash message offset */ - if (visorchannel_read(chipset_dev->controlvm_channel, - offsetof(struct visor_controlvm_channel, - saved_crash_message_offset), - &local_crash_msg_offset, sizeof(u32)) < 0) { - dev_err(&chipset_dev->acpi_device->dev, - "failed to read channel\n"); - return; - } - /* read create device message for storage bus offset */ - if (visorchannel_read(chipset_dev->controlvm_channel, - local_crash_msg_offset, - &local_crash_bus_msg, - sizeof(struct controlvm_message)) < 0) { - dev_err(&chipset_dev->acpi_device->dev, - "failed to read channel\n"); - return; - } - /* read create device message for storage device */ - if (visorchannel_read(chipset_dev->controlvm_channel, - local_crash_msg_offset + - sizeof(struct controlvm_message), - &local_crash_dev_msg, - sizeof(struct controlvm_message)) < 0) { - dev_err(&chipset_dev->acpi_device->dev, - "failed to read channel\n"); - return; - } - /* reuse IOVM create bus message */ - if (!local_crash_bus_msg.cmd.create_bus.channel_addr) { - dev_err(&chipset_dev->acpi_device->dev, - "no valid create_bus message\n"); - return; - } - visorbus_create(&local_crash_bus_msg); - /* reuse create device message for storage device */ - if (!local_crash_dev_msg.cmd.create_device.channel_addr) { - dev_err(&chipset_dev->acpi_device->dev, - "no valid create_device message\n"); - return; - } - visorbus_device_create(&local_crash_dev_msg); -} - -void visorbus_response(struct visor_device *bus_info, int response, - int controlvm_id) -{ - if (!bus_info->pending_msg_hdr) - return; - - controlvm_responder(controlvm_id, bus_info->pending_msg_hdr, response); - kfree(bus_info->pending_msg_hdr); - bus_info->pending_msg_hdr = NULL; -} - -void visorbus_device_changestate_response(struct visor_device *dev_info, - int response, - struct visor_segment_state state) -{ - if (!dev_info->pending_msg_hdr) - return; - - device_changestate_responder(CONTROLVM_DEVICE_CHANGESTATE, dev_info, - response, state); - kfree(dev_info->pending_msg_hdr); - dev_info->pending_msg_hdr = NULL; -} - -static void parser_done(struct parser_context *ctx) -{ - chipset_dev->controlvm_payload_bytes_buffered -= ctx->param_bytes; - kfree(ctx); -} - -static struct parser_context *parser_init_stream(u64 addr, u32 bytes, - bool *retry) -{ - unsigned long allocbytes; - struct parser_context *ctx; - void *mapping; - - *retry = false; - /* alloc an extra byte to ensure payload is \0 terminated */ - allocbytes = (unsigned long)bytes + 1 + (sizeof(struct parser_context) - - sizeof(struct visor_controlvm_parameters_header)); - if ((chipset_dev->controlvm_payload_bytes_buffered + bytes) > - MAX_CONTROLVM_PAYLOAD_BYTES) { - *retry = true; - return NULL; - } - ctx = kzalloc(allocbytes, GFP_KERNEL); - if (!ctx) { - *retry = true; - return NULL; - } - ctx->allocbytes = allocbytes; - ctx->param_bytes = bytes; - mapping = memremap(addr, bytes, MEMREMAP_WB); - if (!mapping) - goto err_finish_ctx; - memcpy(&ctx->data, mapping, bytes); - memunmap(mapping); - ctx->byte_stream = true; - chipset_dev->controlvm_payload_bytes_buffered += ctx->param_bytes; - return ctx; - -err_finish_ctx: - kfree(ctx); - return NULL; -} - -/* - * handle_command() - process a controlvm message - * @inmsg: the message to process - * @channel_addr: address of the controlvm channel - * - * Return: - * 0 - Successfully processed the message - * -EAGAIN - ControlVM message was not processed and should be retried - * reading the next controlvm message; a scenario where this can - * occur is when we need to throttle the allocation of memory in - * which to copy out controlvm payload data. - * < 0 - error: ControlVM message was processed but an error occurred. - */ -static int handle_command(struct controlvm_message inmsg, u64 channel_addr) -{ - struct controlvm_message_packet *cmd = &inmsg.cmd; - u64 parm_addr; - u32 parm_bytes; - struct parser_context *parser_ctx = NULL; - struct controlvm_message ackmsg; - int err = 0; - - /* create parsing context if necessary */ - parm_addr = channel_addr + inmsg.hdr.payload_vm_offset; - parm_bytes = inmsg.hdr.payload_bytes; - /* - * Parameter and channel addresses within test messages actually lie - * within our OS-controlled memory. We need to know that, because it - * makes a difference in how we compute the virtual address. - */ - if (parm_bytes) { - bool retry; - - parser_ctx = parser_init_stream(parm_addr, parm_bytes, &retry); - if (!parser_ctx && retry) - return -EAGAIN; - } - controlvm_init_response(&ackmsg, &inmsg.hdr, CONTROLVM_RESP_SUCCESS); - err = visorchannel_signalinsert(chipset_dev->controlvm_channel, - CONTROLVM_QUEUE_ACK, &ackmsg); - if (err) - return err; - switch (inmsg.hdr.id) { - case CONTROLVM_CHIPSET_INIT: - err = chipset_init(&inmsg); - break; - case CONTROLVM_BUS_CREATE: - err = visorbus_create(&inmsg); - break; - case CONTROLVM_BUS_DESTROY: - err = visorbus_destroy(&inmsg); - break; - case CONTROLVM_BUS_CONFIGURE: - err = visorbus_configure(&inmsg, parser_ctx); - break; - case CONTROLVM_DEVICE_CREATE: - err = visorbus_device_create(&inmsg); - break; - case CONTROLVM_DEVICE_CHANGESTATE: - if (cmd->device_change_state.flags.phys_device) { - err = parahotplug_process_message(&inmsg); - } else { - /* - * save the hdr and cmd structures for later use when - * sending back the response to Command - */ - err = visorbus_device_changestate(&inmsg); - break; - } - break; - case CONTROLVM_DEVICE_DESTROY: - err = visorbus_device_destroy(&inmsg); - break; - case CONTROLVM_DEVICE_CONFIGURE: - /* no op just send a respond that we passed */ - if (inmsg.hdr.flags.response_expected) - controlvm_respond(&inmsg.hdr, CONTROLVM_RESP_SUCCESS, - NULL); - break; - case CONTROLVM_CHIPSET_READY: - err = chipset_ready_uevent(&inmsg.hdr); - break; - case CONTROLVM_CHIPSET_SELFTEST: - err = chipset_selftest_uevent(&inmsg.hdr); - break; - case CONTROLVM_CHIPSET_STOP: - err = chipset_notready_uevent(&inmsg.hdr); - break; - default: - err = -ENOMSG; - if (inmsg.hdr.flags.response_expected) - controlvm_respond(&inmsg.hdr, - -CONTROLVM_RESP_ID_UNKNOWN, NULL); - break; - } - if (parser_ctx) { - parser_done(parser_ctx); - parser_ctx = NULL; - } - return err; -} - -/* - * read_controlvm_event() - retreives the next message from the - * CONTROLVM_QUEUE_EVENT queue in the controlvm - * channel - * @msg: pointer to the retrieved message - * - * Return: 0 if valid message was retrieved or -error - */ -static int read_controlvm_event(struct controlvm_message *msg) -{ - int err = visorchannel_signalremove(chipset_dev->controlvm_channel, - CONTROLVM_QUEUE_EVENT, msg); - - if (err) - return err; - /* got a message */ - if (msg->hdr.flags.test_message == 1) - return -EINVAL; - return 0; -} - -/* - * parahotplug_process_list() - remove any request from the list that's been on - * there too long and respond with an error - */ -static void parahotplug_process_list(void) -{ - struct list_head *pos; - struct list_head *tmp; - - spin_lock(¶hotplug_request_list_lock); - list_for_each_safe(pos, tmp, ¶hotplug_request_list) { - struct parahotplug_request *req = - list_entry(pos, struct parahotplug_request, list); - - if (!time_after_eq(jiffies, req->expiration)) - continue; - list_del(pos); - if (req->msg.hdr.flags.response_expected) - controlvm_respond( - &req->msg.hdr, - CONTROLVM_RESP_DEVICE_UDEV_TIMEOUT, - &req->msg.cmd.device_change_state.state); - parahotplug_request_destroy(req); - } - spin_unlock(¶hotplug_request_list_lock); -} - -static void controlvm_periodic_work(struct work_struct *work) -{ - struct controlvm_message inmsg; - int count = 0; - int err; - - /* Drain the RESPONSE queue make it empty */ - do { - err = visorchannel_signalremove(chipset_dev->controlvm_channel, - CONTROLVM_QUEUE_RESPONSE, - &inmsg); - } while ((!err) && (++count < CONTROLVM_MESSAGE_MAX)); - if (err != -EAGAIN) - goto schedule_out; - if (chipset_dev->controlvm_pending_msg_valid) { - /* - * we throttled processing of a prior msg, so try to process - * it again rather than reading a new one - */ - inmsg = chipset_dev->controlvm_pending_msg; - chipset_dev->controlvm_pending_msg_valid = false; - err = 0; - } else { - err = read_controlvm_event(&inmsg); - } - while (!err) { - chipset_dev->most_recent_message_jiffies = jiffies; - err = handle_command(inmsg, - visorchannel_get_physaddr - (chipset_dev->controlvm_channel)); - if (err == -EAGAIN) { - chipset_dev->controlvm_pending_msg = inmsg; - chipset_dev->controlvm_pending_msg_valid = true; - break; - } - - err = read_controlvm_event(&inmsg); - } - /* parahotplug_worker */ - parahotplug_process_list(); - -/* - * The controlvm messages are sent in a bulk. If we start receiving messages, we - * want the polling to be fast. If we do not receive any message for - * MIN_IDLE_SECONDS, we can slow down the polling. - */ -schedule_out: - if (time_after(jiffies, chipset_dev->most_recent_message_jiffies + - (HZ * MIN_IDLE_SECONDS))) { - /* - * it's been longer than MIN_IDLE_SECONDS since we processed - * our last controlvm message; slow down the polling - */ - if (chipset_dev->poll_jiffies != POLLJIFFIES_CONTROLVM_SLOW) - chipset_dev->poll_jiffies = POLLJIFFIES_CONTROLVM_SLOW; - } else { - if (chipset_dev->poll_jiffies != POLLJIFFIES_CONTROLVM_FAST) - chipset_dev->poll_jiffies = POLLJIFFIES_CONTROLVM_FAST; - } - schedule_delayed_work(&chipset_dev->periodic_controlvm_work, - chipset_dev->poll_jiffies); -} - -static int visorchipset_init(struct acpi_device *acpi_device) -{ - int err = -ENODEV; - struct visorchannel *controlvm_channel; - - chipset_dev = kzalloc(sizeof(*chipset_dev), GFP_KERNEL); - if (!chipset_dev) - goto error; - err = controlvm_channel_create(chipset_dev); - if (err) - goto error_free_chipset_dev; - acpi_device->driver_data = chipset_dev; - chipset_dev->acpi_device = acpi_device; - chipset_dev->poll_jiffies = POLLJIFFIES_CONTROLVM_FAST; - err = sysfs_create_groups(&chipset_dev->acpi_device->dev.kobj, - visorchipset_dev_groups); - if (err < 0) - goto error_destroy_channel; - controlvm_channel = chipset_dev->controlvm_channel; - if (!visor_check_channel(visorchannel_get_header(controlvm_channel), - &chipset_dev->acpi_device->dev, - &visor_controlvm_channel_guid, - "controlvm", - sizeof(struct visor_controlvm_channel), - VISOR_CONTROLVM_CHANNEL_VERSIONID, - VISOR_CHANNEL_SIGNATURE)) - goto error_delete_groups; - /* if booting in a crash kernel */ - if (is_kdump_kernel()) - INIT_DELAYED_WORK(&chipset_dev->periodic_controlvm_work, - setup_crash_devices_work_queue); - else - INIT_DELAYED_WORK(&chipset_dev->periodic_controlvm_work, - controlvm_periodic_work); - chipset_dev->most_recent_message_jiffies = jiffies; - chipset_dev->poll_jiffies = POLLJIFFIES_CONTROLVM_FAST; - schedule_delayed_work(&chipset_dev->periodic_controlvm_work, - chipset_dev->poll_jiffies); - err = visorbus_init(); - if (err < 0) - goto error_cancel_work; - return 0; - -error_cancel_work: - cancel_delayed_work_sync(&chipset_dev->periodic_controlvm_work); - -error_delete_groups: - sysfs_remove_groups(&chipset_dev->acpi_device->dev.kobj, - visorchipset_dev_groups); - -error_destroy_channel: - visorchannel_destroy(chipset_dev->controlvm_channel); - -error_free_chipset_dev: - kfree(chipset_dev); - -error: - dev_err(&acpi_device->dev, "failed with error %d\n", err); - return err; -} - -static int visorchipset_exit(struct acpi_device *acpi_device) -{ - visorbus_exit(); - cancel_delayed_work_sync(&chipset_dev->periodic_controlvm_work); - sysfs_remove_groups(&chipset_dev->acpi_device->dev.kobj, - visorchipset_dev_groups); - visorchannel_destroy(chipset_dev->controlvm_channel); - kfree(chipset_dev); - return 0; -} - -static const struct acpi_device_id unisys_device_ids[] = { - {"PNP0A07", 0}, - {"", 0}, -}; - -static struct acpi_driver unisys_acpi_driver = { - .name = "unisys_acpi", - .class = "unisys_acpi_class", - .owner = THIS_MODULE, - .ids = unisys_device_ids, - .ops = { - .add = visorchipset_init, - .remove = visorchipset_exit, - }, -}; - -MODULE_DEVICE_TABLE(acpi, unisys_device_ids); - -static __init int visorutil_spar_detect(void) -{ - unsigned int eax, ebx, ecx, edx; - - if (boot_cpu_has(X86_FEATURE_HYPERVISOR)) { - /* check the ID */ - cpuid(UNISYS_VISOR_LEAF_ID, &eax, &ebx, &ecx, &edx); - return (ebx == UNISYS_VISOR_ID_EBX) && - (ecx == UNISYS_VISOR_ID_ECX) && - (edx == UNISYS_VISOR_ID_EDX); - } - return 0; -} - -static int __init init_unisys(void) -{ - int result; - - if (!visorutil_spar_detect()) - return -ENODEV; - result = acpi_bus_register_driver(&unisys_acpi_driver); - if (result) - return -ENODEV; - pr_info("Unisys Visorchipset Driver Loaded.\n"); - return 0; -}; - -static void __exit exit_unisys(void) -{ - acpi_bus_unregister_driver(&unisys_acpi_driver); -} - -module_init(init_unisys); -module_exit(exit_unisys); - -MODULE_AUTHOR("Unisys"); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("s-Par visorbus driver for virtual device buses"); diff --git a/drivers/staging/unisys/visorhba/visorhba_main.c b/drivers/staging/unisys/visorhba/visorhba_main.c index da650d64695a..167e98f8688e 100644 --- a/drivers/staging/unisys/visorhba/visorhba_main.c +++ b/drivers/staging/unisys/visorhba/visorhba_main.c @@ -9,12 +9,12 @@ #include #include #include +#include #include #include #include #include -#include "visorbus.h" #include "iochannel.h" /* The Send and Receive Buffers of the IO Queue may both be full */ diff --git a/drivers/staging/unisys/visorinput/visorinput.c b/drivers/staging/unisys/visorinput/visorinput.c index dabc5b44208e..d8048e48658f 100644 --- a/drivers/staging/unisys/visorinput/visorinput.c +++ b/drivers/staging/unisys/visorinput/visorinput.c @@ -16,8 +16,8 @@ #include #include #include +#include -#include "visorbus.h" #include "ultrainputreport.h" /* Keyboard channel {c73416d0-b0b8-44af-b304-9d2ae99f1b3d} */ diff --git a/drivers/staging/unisys/visornic/visornic_main.c b/drivers/staging/unisys/visornic/visornic_main.c index 112a76ec0628..92dceb557886 100644 --- a/drivers/staging/unisys/visornic/visornic_main.c +++ b/drivers/staging/unisys/visornic/visornic_main.c @@ -16,8 +16,8 @@ #include #include #include +#include -#include "visorbus.h" #include "iochannel.h" #define VISORNIC_INFINITE_RSP_WAIT 0 diff --git a/drivers/visorbus/Kconfig b/drivers/visorbus/Kconfig new file mode 100644 index 000000000000..1f5812b936d0 --- /dev/null +++ b/drivers/visorbus/Kconfig @@ -0,0 +1,14 @@ +# +# Unisys visorbus configuration +# + +config UNISYS_VISORBUS + tristate "Unisys visorbus driver" + depends on X86_64 && ACPI + ---help--- + The visorbus driver is a virtualized bus for the Unisys s-Par firmware. + Virtualized devices allow Linux guests on a system to share disks and + network cards that do not have SR-IOV support, and to be accessed using + the partition desktop application. The visorbus driver is required to + discover devices on an s-Par guest, and must be present for any other + s-Par guest driver to function correctly. diff --git a/drivers/visorbus/Makefile b/drivers/visorbus/Makefile new file mode 100644 index 000000000000..e8df59d1301f --- /dev/null +++ b/drivers/visorbus/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for Unisys visorbus +# + +obj-$(CONFIG_UNISYS_VISORBUS) += visorbus.o + +visorbus-y := visorbus_main.o +visorbus-y += visorchannel.o +visorbus-y += visorchipset.o diff --git a/drivers/visorbus/controlvmchannel.h b/drivers/visorbus/controlvmchannel.h new file mode 100644 index 000000000000..8c57562a070a --- /dev/null +++ b/drivers/visorbus/controlvmchannel.h @@ -0,0 +1,650 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2010 - 2015 UNISYS CORPORATION + * All rights reserved. + */ + +#ifndef __CONTROLVMCHANNEL_H__ +#define __CONTROLVMCHANNEL_H__ + +#include +#include + +/* {2B3C2D10-7EF5-4ad8-B966-3448B7386B3D} */ +#define VISOR_CONTROLVM_CHANNEL_GUID \ + GUID_INIT(0x2b3c2d10, 0x7ef5, 0x4ad8, \ + 0xb9, 0x66, 0x34, 0x48, 0xb7, 0x38, 0x6b, 0x3d) + +#define CONTROLVM_MESSAGE_MAX 64 + +/* + * Must increment this whenever you insert or delete fields within this channel + * struct. Also increment whenever you change the meaning of fields within this + * channel struct so as to break pre-existing software. Note that you can + * usually add fields to the END of the channel struct withOUT needing to + * increment this. + */ +#define VISOR_CONTROLVM_CHANNEL_VERSIONID 1 + +/* Defines for various channel queues */ +#define CONTROLVM_QUEUE_REQUEST 0 +#define CONTROLVM_QUEUE_RESPONSE 1 +#define CONTROLVM_QUEUE_EVENT 2 +#define CONTROLVM_QUEUE_ACK 3 + +/* Max num of messages stored during IOVM creation to be reused after crash */ +#define CONTROLVM_CRASHMSG_MAX 2 + +/* + * struct visor_segment_state + * @enabled: May enter other states. + * @active: Assigned to active partition. + * @alive: Configure message sent to service/server. + * @revoked: Similar to partition state ShuttingDown. + * @allocated: Memory (device/port number) has been selected by Command. + * @known: Has been introduced to the service/guest partition. + * @ready: Service/Guest partition has responded to introduction. + * @operating: Resource is configured and operating. + * @reserved: Natural alignment. + * + * Note: Don't use high bit unless we need to switch to ushort which is + * non-compliant. + */ +struct visor_segment_state { + u16 enabled:1; + u16 active:1; + u16 alive:1; + u16 revoked:1; + u16 allocated:1; + u16 known:1; + u16 ready:1; + u16 operating:1; + u16 reserved:8; +} __packed; + +static const struct visor_segment_state segment_state_running = { + 1, 1, 1, 0, 1, 1, 1, 1 +}; + +static const struct visor_segment_state segment_state_paused = { + 1, 1, 1, 0, 1, 1, 1, 0 +}; + +static const struct visor_segment_state segment_state_standby = { + 1, 1, 0, 0, 1, 1, 1, 0 +}; + +/* + * enum controlvm_id + * @CONTROLVM_INVALID: + * @CONTROLVM_BUS_CREATE: CP --> SP, GP. + * @CONTROLVM_BUS_DESTROY: CP --> SP, GP. + * @CONTROLVM_BUS_CONFIGURE: CP --> SP. + * @CONTROLVM_BUS_CHANGESTATE: CP --> SP, GP. + * @CONTROLVM_BUS_CHANGESTATE_EVENT: SP, GP --> CP. + * @CONTROLVM_DEVICE_CREATE: CP --> SP, GP. + * @CONTROLVM_DEVICE_DESTROY: CP --> SP, GP. + * @CONTROLVM_DEVICE_CONFIGURE: CP --> SP. + * @CONTROLVM_DEVICE_CHANGESTATE: CP --> SP, GP. + * @CONTROLVM_DEVICE_CHANGESTATE_EVENT: SP, GP --> CP. + * @CONTROLVM_DEVICE_RECONFIGURE: CP --> Boot. + * @CONTROLVM_CHIPSET_INIT: CP --> SP, GP. + * @CONTROLVM_CHIPSET_STOP: CP --> SP, GP. + * @CONTROLVM_CHIPSET_READY: CP --> SP. + * @CONTROLVM_CHIPSET_SELFTEST: CP --> SP. + * + * Ids for commands that may appear in either queue of a ControlVm channel. + * + * Commands that are initiated by the command partition (CP), by an IO or + * console service partition (SP), or by a guest partition (GP) are: + * - issued on the RequestQueue queue (q #0) in the ControlVm channel + * - responded to on the ResponseQueue queue (q #1) in the ControlVm channel + * + * Events that are initiated by an IO or console service partition (SP) or + * by a guest partition (GP) are: + * - issued on the EventQueue queue (q #2) in the ControlVm channel + * - responded to on the EventAckQueue queue (q #3) in the ControlVm channel + */ +enum controlvm_id { + CONTROLVM_INVALID = 0, + /* + * SWITCH commands required Parameter: SwitchNumber. + * BUS commands required Parameter: BusNumber + */ + CONTROLVM_BUS_CREATE = 0x101, + CONTROLVM_BUS_DESTROY = 0x102, + CONTROLVM_BUS_CONFIGURE = 0x104, + CONTROLVM_BUS_CHANGESTATE = 0x105, + CONTROLVM_BUS_CHANGESTATE_EVENT = 0x106, + /* DEVICE commands required Parameter: BusNumber, DeviceNumber */ + CONTROLVM_DEVICE_CREATE = 0x201, + CONTROLVM_DEVICE_DESTROY = 0x202, + CONTROLVM_DEVICE_CONFIGURE = 0x203, + CONTROLVM_DEVICE_CHANGESTATE = 0x204, + CONTROLVM_DEVICE_CHANGESTATE_EVENT = 0x205, + CONTROLVM_DEVICE_RECONFIGURE = 0x206, + /* CHIPSET commands */ + CONTROLVM_CHIPSET_INIT = 0x301, + CONTROLVM_CHIPSET_STOP = 0x302, + CONTROLVM_CHIPSET_READY = 0x304, + CONTROLVM_CHIPSET_SELFTEST = 0x305, +}; + +/* + * struct irq_info + * @reserved1: Natural alignment purposes + * @recv_irq_handle: Specifies interrupt handle. It is used to retrieve the + * corresponding interrupt pin from Monitor; and the interrupt + * pin is used to connect to the corresponding interrupt. + * Used by IOPart-GP only. + * @recv_irq_vector: Specifies interrupt vector. It, interrupt pin, and shared + * are used to connect to the corresponding interrupt. + * Used by IOPart-GP only. + * @recv_irq_shared: Specifies if the recvInterrupt is shared. It, interrupt + * pin and vector are used to connect to 0 = not shared; + * 1 = shared the corresponding interrupt. + * Used by IOPart-GP only. + * @reserved: Natural alignment purposes + */ +struct irq_info { + u64 reserved1; + u64 recv_irq_handle; + u32 recv_irq_vector; + u8 recv_irq_shared; + u8 reserved[3]; +} __packed; + +/* + * struct efi_visor_indication + * @boot_to_fw_ui: Stop in UEFI UI + * @clear_nvram: Clear NVRAM + * @clear_cmos: Clear CMOS + * @boot_to_tool: Run install tool + * @reserved: Natural alignment + */ +struct efi_visor_indication { + u64 boot_to_fw_ui:1; + u64 clear_nvram:1; + u64 clear_cmos:1; + u64 boot_to_tool:1; + /* Remaining bits are available */ + u64 reserved:60; +} __packed; + +enum visor_chipset_feature { + VISOR_CHIPSET_FEATURE_REPLY = 0x00000001, + VISOR_CHIPSET_FEATURE_PARA_HOTPLUG = 0x00000002, +}; + +/* + * struct controlvm_message_header + * @id: See CONTROLVM_ID. + * @message_size: Includes size of this struct + size of message. + * @segment_index: Index of segment containing Vm message/information. + * @completion_status: Error status code or result of message completion. + * @struct flags: + * @failed: =1 in a response to signify failure. + * @response_expected: =1 in all messages that expect a response. + * @server: =1 in all bus & device-related messages where the + * message receiver is to act as the bus or device + * server. + * @test_message: =1 for testing use only (Control and Command + * ignore this). + * @partial_completion: =1 if there are forthcoming responses/acks + * associated with this message. + * @preserve: =1 this is to let us know to preserve channel + * contents. + * @writer_in_diag: =1 the DiagWriter is active in the Diagnostic + * Partition. + * @reserve: Natural alignment. + * @reserved: Natural alignment. + * @message_handle: Identifies the particular message instance. + * @payload_vm_offset: Offset of payload area from start of this instance. + * @payload_max_bytes: Maximum bytes allocated in payload area of ControlVm + * segment. + * @payload_bytes: Actual number of bytes of payload area to copy between + * IO/Command. If non-zero, there is a payload to copy. + * + * This is the common structure that is at the beginning of every + * ControlVm message (both commands and responses) in any ControlVm + * queue. Commands are easily distinguished from responses by + * looking at the flags.response field. + */ +struct controlvm_message_header { + u32 id; + /* + * For requests, indicates the message type. For responses, indicates + * the type of message we are responding to. + */ + u32 message_size; + u32 segment_index; + u32 completion_status; + struct { + u32 failed:1; + u32 response_expected:1; + u32 server:1; + u32 test_message:1; + u32 partial_completion:1; + u32 preserve:1; + u32 writer_in_diag:1; + u32 reserve:25; + } __packed flags; + u32 reserved; + u64 message_handle; + u64 payload_vm_offset; + u32 payload_max_bytes; + u32 payload_bytes; +} __packed; + +/* + * struct controlvm_packet_device_create - For CONTROLVM_DEVICE_CREATE + * @bus_no: Bus # (0..n-1) from the msg receiver's end. + * @dev_no: Bus-relative (0..n-1) device number. + * @channel_addr: Guest physical address of the channel, which can be + * dereferenced by the receiver of this ControlVm command. + * @channel_bytes: Specifies size of the channel in bytes. + * @data_type_uuid: Specifies format of data in channel. + * @dev_inst_uuid: Instance guid for the device. + * @irq_info intr: Specifies interrupt information. + */ +struct controlvm_packet_device_create { + u32 bus_no; + u32 dev_no; + u64 channel_addr; + u64 channel_bytes; + guid_t data_type_guid; + guid_t dev_inst_guid; + struct irq_info intr; +} __packed; + +/* + * struct controlvm_packet_device_configure - For CONTROLVM_DEVICE_CONFIGURE + * @bus_no: Bus number (0..n-1) from the msg receiver's perspective. + * @dev_no: Bus-relative (0..n-1) device number. + */ +struct controlvm_packet_device_configure { + u32 bus_no; + u32 dev_no; +} __packed; + +/* Total 128 bytes */ +struct controlvm_message_device_create { + struct controlvm_message_header header; + struct controlvm_packet_device_create packet; +} __packed; + +/* Total 56 bytes */ +struct controlvm_message_device_configure { + struct controlvm_message_header header; + struct controlvm_packet_device_configure packet; +} __packed; + +/* + * struct controlvm_message_packet - This is the format for a message in any + * ControlVm queue. + * @struct create_bus: For CONTROLVM_BUS_CREATE. + * @bus_no: Bus # (0..n-1) from the msg receiver's perspective. + * @dev_count: Indicates the max number of devices on this bus. + * @channel_addr: Guest physical address of the channel, which can be + * dereferenced by the receiver of this ControlVM + * command. + * @channel_bytes: Size of the channel. + * @bus_data_type_uuid: Indicates format of data in bus channel. + * @bus_inst_uuid: Instance uuid for the bus. + * + * @struct destroy_bus: For CONTROLVM_BUS_DESTROY. + * @bus_no: Bus # (0..n-1) from the msg receiver's perspective. + * @reserved: Natural alignment purposes. + * + * @struct configure_bus: For CONTROLVM_BUS_CONFIGURE. + * @bus_no: Bus # (0..n-1) from the receiver's perspective. + * @reserved1: For alignment purposes. + * @guest_handle: This is used to convert guest physical address to + * physical address. + * @recv_bus_irq_handle: Specifies interrupt info. It is used by SP to + * register to receive interrupts from the CP. This + * interrupt is used for bus level notifications. + * The corresponding sendBusInterruptHandle is kept + * in CP. + * + * @struct create_device: For CONTROLVM_DEVICE_CREATE. + * + * @struct destroy_device: For CONTROLVM_DEVICE_DESTROY. + * @bus_no: Bus # (0..n-1) from the msg receiver's perspective. + * @dev_no: Bus-relative (0..n-1) device number. + * + * @struct configure_device: For CONTROLVM_DEVICE_CONFIGURE. + * + * @struct reconfigure_device: For CONTROLVM_DEVICE_RECONFIGURE. + * @bus_no: Bus # (0..n-1) from the msg receiver's perspective. + * @dev_no: Bus-relative (0..n-1) device number. + * + * @struct bus_change_state: For CONTROLVM_BUS_CHANGESTATE. + * @bus_no: + * @struct state: + * @reserved: Natural alignment purposes. + * + * @struct device_change_state: For CONTROLVM_DEVICE_CHANGESTATE. + * @bus_no: + * @dev_no: + * @struct state: + * @struct flags: + * @phys_device: =1 if message is for a physical device. + * @reserved: Natural alignment. + * @reserved1: Natural alignment. + * @reserved: Natural alignment purposes. + * + * @struct device_change_state_event: For CONTROLVM_DEVICE_CHANGESTATE_EVENT. + * @bus_no: + * @dev_no: + * @struct state: + * @reserved: Natural alignment purposes. + * + * @struct init_chipset: For CONTROLVM_CHIPSET_INIT. + * @bus_count: Indicates the max number of busses. + * @switch_count: Indicates the max number of switches. + * @enum features: + * @platform_number: + * + * @struct chipset_selftest: For CONTROLVM_CHIPSET_SELFTEST. + * @options: Reserved. + * @test: Bit 0 set to run embedded selftest. + * + * @addr: A physical address of something, that can be dereferenced by the + * receiver of this ControlVm command. + * + * @handle: A handle of something (depends on command id). + */ +struct controlvm_message_packet { + union { + struct { + u32 bus_no; + u32 dev_count; + u64 channel_addr; + u64 channel_bytes; + guid_t bus_data_type_guid; + guid_t bus_inst_guid; + } __packed create_bus; + struct { + u32 bus_no; + u32 reserved; + } __packed destroy_bus; + struct { + u32 bus_no; + u32 reserved1; + u64 guest_handle; + u64 recv_bus_irq_handle; + } __packed configure_bus; + struct controlvm_packet_device_create create_device; + struct { + u32 bus_no; + u32 dev_no; + } __packed destroy_device; + struct controlvm_packet_device_configure configure_device; + struct { + u32 bus_no; + u32 dev_no; + } __packed reconfigure_device; + struct { + u32 bus_no; + struct visor_segment_state state; + u8 reserved[2]; + } __packed bus_change_state; + struct { + u32 bus_no; + u32 dev_no; + struct visor_segment_state state; + struct { + u32 phys_device:1; + u32 reserved:31; + u32 reserved1; + } __packed flags; + u8 reserved[2]; + } __packed device_change_state; + struct { + u32 bus_no; + u32 dev_no; + struct visor_segment_state state; + u8 reserved[6]; + } __packed device_change_state_event; + struct { + u32 bus_count; + u32 switch_count; + enum visor_chipset_feature features; + u32 platform_number; + } __packed init_chipset; + struct { + u32 options; + u32 test; + } __packed chipset_selftest; + u64 addr; + u64 handle; + }; +} __packed; + +/* All messages in any ControlVm queue have this layout. */ +struct controlvm_message { + struct controlvm_message_header hdr; + struct controlvm_message_packet cmd; +} __packed; + +/* + * struct visor_controlvm_channel + * @struct header: + * @gp_controlvm: Guest phys addr of this channel. + * @gp_partition_tables: Guest phys addr of partition tables. + * @gp_diag_guest: Guest phys addr of diagnostic channel. + * @gp_boot_romdisk: Guest phys addr of (read* only) Boot + * ROM disk. + * @gp_boot_ramdisk: Guest phys addr of writable Boot RAM + * disk. + * @gp_acpi_table: Guest phys addr of acpi table. + * @gp_control_channel: Guest phys addr of control channel. + * @gp_diag_romdisk: Guest phys addr of diagnostic ROM disk. + * @gp_nvram: Guest phys addr of NVRAM channel. + * @request_payload_offset: Offset to request payload area. + * @event_payload_offset: Offset to event payload area. + * @request_payload_bytes: Bytes available in request payload area. + * @event_payload_bytes: Bytes available in event payload area. + * @control_channel_bytes: + * @nvram_channel_bytes: Bytes in PartitionNvram segment. + * @message_bytes: sizeof(CONTROLVM_MESSAGE). + * @message_count: CONTROLVM_MESSAGE_MAX. + * @gp_smbios_table: Guest phys addr of SMBIOS tables. + * @gp_physical_smbios_table: Guest phys addr of SMBIOS table. + * @gp_reserved: VISOR_MAX_GUESTS_PER_SERVICE. + * @virtual_guest_firmware_image_base: Guest physical address of EFI firmware + * image base. + * @virtual_guest_firmware_entry_point: Guest physical address of EFI firmware + * entry point. + * @virtual_guest_firmware_image_size: Guest EFI firmware image size. + * @virtual_guest_firmware_boot_base: GPA = 1MB where EFI firmware image is + * copied to. + * @virtual_guest_image_base: + * @virtual_guest_image_size: + * @prototype_control_channel_offset: + * @virtual_guest_partition_handle: + * @restore_action: Restore Action field to restore the + * guest partition. + * @dump_action: For Windows guests it shows if the + * visordisk is in dump mode. + * @nvram_fail_count: + * @saved_crash_message_count: = CONTROLVM_CRASHMSG_MAX. + * @saved_crash_message_offset: Offset to request payload area needed + * for crash dump. + * @installation_error: Type of error encountered during + * installation. + * @installation_text_id: Id of string to display. + * @installation_remaining_steps: Number of remaining installation steps + * (for progress bars). + * @tool_action: VISOR_TOOL_ACTIONS Installation Action + * field. + * @reserved: Alignment. + * @struct efi_visor_ind: + * @sp_reserved: + * @reserved2: Force signals to begin on 128-byte + * cache line. + * @struct request_queue: Guest partition uses this queue to send + * requests to Control. + * @struct response_queue: Control uses this queue to respond to + * service or guest partition request. + * @struct event_queue: Control uses this queue to send events + * to guest partition. + * @struct event_ack_queue: Service or guest partition uses this + * queue to ack Control events. + * @struct request_msg: Request fixed-size message pool - + * does not include payload. + * @struct response_msg: Response fixed-size message pool - + * does not include payload. + * @struct event_msg: Event fixed-size message pool - + * does not include payload. + * @struct event_ack_msg: Ack fixed-size message pool - + * does not include payload. + * @struct saved_crash_msg: Message stored during IOVM creation to + * be reused after crash. + */ +struct visor_controlvm_channel { + struct channel_header header; + u64 gp_controlvm; + u64 gp_partition_tables; + u64 gp_diag_guest; + u64 gp_boot_romdisk; + u64 gp_boot_ramdisk; + u64 gp_acpi_table; + u64 gp_control_channel; + u64 gp_diag_romdisk; + u64 gp_nvram; + u64 request_payload_offset; + u64 event_payload_offset; + u32 request_payload_bytes; + u32 event_payload_bytes; + u32 control_channel_bytes; + u32 nvram_channel_bytes; + u32 message_bytes; + u32 message_count; + u64 gp_smbios_table; + u64 gp_physical_smbios_table; + char gp_reserved[2688]; + u64 virtual_guest_firmware_image_base; + u64 virtual_guest_firmware_entry_point; + u64 virtual_guest_firmware_image_size; + u64 virtual_guest_firmware_boot_base; + u64 virtual_guest_image_base; + u64 virtual_guest_image_size; + u64 prototype_control_channel_offset; + u64 virtual_guest_partition_handle; + u16 restore_action; + u16 dump_action; + u16 nvram_fail_count; + u16 saved_crash_message_count; + u32 saved_crash_message_offset; + u32 installation_error; + u32 installation_text_id; + u16 installation_remaining_steps; + u8 tool_action; + u8 reserved; + struct efi_visor_indication efi_visor_ind; + u32 sp_reserved; + u8 reserved2[28]; + struct signal_queue_header request_queue; + struct signal_queue_header response_queue; + struct signal_queue_header event_queue; + struct signal_queue_header event_ack_queue; + struct controlvm_message request_msg[CONTROLVM_MESSAGE_MAX]; + struct controlvm_message response_msg[CONTROLVM_MESSAGE_MAX]; + struct controlvm_message event_msg[CONTROLVM_MESSAGE_MAX]; + struct controlvm_message event_ack_msg[CONTROLVM_MESSAGE_MAX]; + struct controlvm_message saved_crash_msg[CONTROLVM_CRASHMSG_MAX]; +} __packed; + +/* + * struct visor_controlvm_parameters_header + * + * The following header will be located at the beginning of PayloadVmOffset for + * various ControlVm commands. The receiver of a ControlVm command with a + * PayloadVmOffset will dereference this address and then use connection_offset, + * initiator_offset, and target_offset to get the location of UTF-8 formatted + * strings that can be parsed to obtain command-specific information. The value + * of total_length should equal PayloadBytes. The format of the strings at + * PayloadVmOffset will take different forms depending on the message. + */ +struct visor_controlvm_parameters_header { + u32 total_length; + u32 header_length; + u32 connection_offset; + u32 connection_length; + u32 initiator_offset; + u32 initiator_length; + u32 target_offset; + u32 target_length; + u32 client_offset; + u32 client_length; + u32 name_offset; + u32 name_length; + guid_t id; + u32 revision; + /* Natural alignment */ + u32 reserved; +} __packed; + +/* General Errors------------------------------------------------------[0-99] */ +#define CONTROLVM_RESP_SUCCESS 0 +#define CONTROLVM_RESP_ALREADY_DONE 1 +#define CONTROLVM_RESP_IOREMAP_FAILED 2 +#define CONTROLVM_RESP_KMALLOC_FAILED 3 +#define CONTROLVM_RESP_ID_UNKNOWN 4 +#define CONTROLVM_RESP_ID_INVALID_FOR_CLIENT 5 +/* CONTROLVM_INIT_CHIPSET-------------------------------------------[100-199] */ +#define CONTROLVM_RESP_CLIENT_SWITCHCOUNT_NONZERO 100 +#define CONTROLVM_RESP_EXPECTED_CHIPSET_INIT 101 +/* Maximum Limit----------------------------------------------------[200-299] */ +/* BUS_CREATE */ +#define CONTROLVM_RESP_ERROR_MAX_BUSES 201 +/* DEVICE_CREATE */ +#define CONTROLVM_RESP_ERROR_MAX_DEVICES 202 +/* Payload and Parameter Related------------------------------------[400-499] */ +/* SWITCH_ATTACHEXTPORT, DEVICE_CONFIGURE */ +#define CONTROLVM_RESP_PAYLOAD_INVALID 400 +/* Multiple */ +#define CONTROLVM_RESP_INITIATOR_PARAMETER_INVALID 401 +/* DEVICE_CONFIGURE */ +#define CONTROLVM_RESP_TARGET_PARAMETER_INVALID 402 +/* DEVICE_CONFIGURE */ +#define CONTROLVM_RESP_CLIENT_PARAMETER_INVALID 403 +/* Specified[Packet Structure] Value--------------------------------[500-599] */ +/* SWITCH_ATTACHINTPORT */ +/* BUS_CONFIGURE, DEVICE_CREATE, DEVICE_CONFIG, DEVICE_DESTROY */ +#define CONTROLVM_RESP_BUS_INVALID 500 +/* SWITCH_ATTACHINTPORT*/ +/* DEVICE_CREATE, DEVICE_CONFIGURE, DEVICE_DESTROY */ +#define CONTROLVM_RESP_DEVICE_INVALID 501 +/* DEVICE_CREATE, DEVICE_CONFIGURE */ +#define CONTROLVM_RESP_CHANNEL_INVALID 502 +/* Partition Driver Callback Interface------------------------------[600-699] */ +/* BUS_CREATE, BUS_DESTROY, DEVICE_CREATE, DEVICE_DESTROY */ +#define CONTROLVM_RESP_VIRTPCI_DRIVER_FAILURE 604 +/* Unable to invoke VIRTPCI callback. VIRTPCI Callback returned error. */ +/* BUS_CREATE, BUS_DESTROY, DEVICE_CREATE, DEVICE_DESTROY */ +#define CONTROLVM_RESP_VIRTPCI_DRIVER_CALLBACK_ERROR 605 +/* Generic device callback returned error. */ +/* SWITCH_ATTACHEXTPORT, SWITCH_DETACHEXTPORT, DEVICE_CONFIGURE */ +#define CONTROLVM_RESP_GENERIC_DRIVER_CALLBACK_ERROR 606 +/* Bus Related------------------------------------------------------[700-799] */ +/* BUS_DESTROY */ +#define CONTROLVM_RESP_ERROR_BUS_DEVICE_ATTACHED 700 +/* Channel Related--------------------------------------------------[800-899] */ +/* GET_CHANNELINFO, DEVICE_DESTROY */ +#define CONTROLVM_RESP_CHANNEL_TYPE_UNKNOWN 800 +/* DEVICE_CREATE */ +#define CONTROLVM_RESP_CHANNEL_SIZE_TOO_SMALL 801 +/* Chipset Shutdown Related---------------------------------------[1000-1099] */ +#define CONTROLVM_RESP_CHIPSET_SHUTDOWN_FAILED 1000 +#define CONTROLVM_RESP_CHIPSET_SHUTDOWN_ALREADY_ACTIVE 1001 +/* Chipset Stop Related-------------------------------------------[1100-1199] */ +#define CONTROLVM_RESP_CHIPSET_STOP_FAILED_BUS 1100 +#define CONTROLVM_RESP_CHIPSET_STOP_FAILED_SWITCH 1101 +/* Device Related-------------------------------------------------[1400-1499] */ +#define CONTROLVM_RESP_DEVICE_UDEV_TIMEOUT 1400 + +/* __CONTROLVMCHANNEL_H__ */ +#endif diff --git a/drivers/visorbus/vbuschannel.h b/drivers/visorbus/vbuschannel.h new file mode 100644 index 000000000000..b1dce26166bf --- /dev/null +++ b/drivers/visorbus/vbuschannel.h @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2010 - 2015 UNISYS CORPORATION + * All rights reserved. + */ + +#ifndef __VBUSCHANNEL_H__ +#define __VBUSCHANNEL_H__ + +/* + * The vbus channel is the channel area provided via the BUS_CREATE controlvm + * message for each virtual bus. This channel area is provided to both server + * and client ends of the bus. The channel header area is initialized by + * the server, and the remaining information is filled in by the client. + * We currently use this for the client to provide various information about + * the client devices and client drivers for the server end to see. + */ + +#include +#include + +/* {193b331b-c58f-11da-95a9-00e08161165f} */ +#define VISOR_VBUS_CHANNEL_GUID \ + GUID_INIT(0x193b331b, 0xc58f, 0x11da, \ + 0x95, 0xa9, 0x0, 0xe0, 0x81, 0x61, 0x16, 0x5f) + +/* + * Must increment this whenever you insert or delete fields within this channel + * struct. Also increment whenever you change the meaning of fields within this + * channel struct so as to break pre-existing software. Note that you can + * usually add fields to the END of the channel struct withOUT needing to + * increment this. + */ +#define VISOR_VBUS_CHANNEL_VERSIONID 1 + +/* + * struct visor_vbus_deviceinfo + * @devtype: Short string identifying the device type. + * @drvname: Driver .sys file name. + * @infostrs: Kernel vversion. + * @reserved: Pad size to 256 bytes. + * + * An array of this struct is present in the channel area for each vbus. It is + * filled in by the client side to provide info about the device and driver from + * the client's perspective. + */ +struct visor_vbus_deviceinfo { + u8 devtype[16]; + u8 drvname[16]; + u8 infostrs[96]; + u8 reserved[128]; +} __packed; + +/* + * struct visor_vbus_headerinfo + * @struct_bytes: Size of this struct in bytes. + * @device_info_struct_bytes: Size of VISOR_VBUS_DEVICEINFO. + * @dev_info_count: Num of items in DevInfo member. This is the + * allocated size. + * @chp_info_offset: Byte offset from beginning of this struct to the + * ChpInfo struct. + * @bus_info_offset: Byte offset from beginning of this struct to the + * BusInfo struct. + * @dev_info_offset: Byte offset from beginning of this struct to the + * DevInfo array. + * @reserved: Natural alignment. + */ +struct visor_vbus_headerinfo { + u32 struct_bytes; + u32 device_info_struct_bytes; + u32 dev_info_count; + u32 chp_info_offset; + u32 bus_info_offset; + u32 dev_info_offset; + u8 reserved[104]; +} __packed; + +/* + * struct visor_vbus_channel + * @channel_header: Initialized by server. + * @hdr_info: Initialized by server. + * @chp_info: Describes client chipset device and driver. + * @bus_info: Describes client bus device and driver. + * @dev_info: Describes client device and driver for each device on the + * bus. + */ +struct visor_vbus_channel { + struct channel_header channel_header; + struct visor_vbus_headerinfo hdr_info; + struct visor_vbus_deviceinfo chp_info; + struct visor_vbus_deviceinfo bus_info; + struct visor_vbus_deviceinfo dev_info[0]; +} __packed; + +#endif diff --git a/drivers/visorbus/visorbus_main.c b/drivers/visorbus/visorbus_main.c new file mode 100644 index 000000000000..0b2434cc4ecd --- /dev/null +++ b/drivers/visorbus/visorbus_main.c @@ -0,0 +1,1234 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright � 2010 - 2015 UNISYS CORPORATION + * All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#include "visorbus_private.h" + +static const guid_t visor_vbus_channel_guid = VISOR_VBUS_CHANNEL_GUID; + +/* Display string that is guaranteed to be no longer the 99 characters */ +#define LINESIZE 99 +#define POLLJIFFIES_NORMALCHANNEL 10 + +/* stores whether bus_registration was successful */ +static bool initialized; +static struct dentry *visorbus_debugfs_dir; + +/* + * DEVICE type attributes + * + * The modalias file will contain the guid of the device. + */ +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct visor_device *vdev; + const guid_t *guid; + + vdev = to_visor_device(dev); + guid = visorchannel_get_guid(vdev->visorchannel); + return sprintf(buf, "visorbus:%pUl\n", guid); +} +static DEVICE_ATTR_RO(modalias); + +static struct attribute *visorbus_dev_attrs[] = { + &dev_attr_modalias.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(visorbus_dev); + +/* filled in with info about parent chipset driver when we register with it */ +static struct visor_vbus_deviceinfo chipset_driverinfo; +/* filled in with info about this driver, wrt it servicing client busses */ +static struct visor_vbus_deviceinfo clientbus_driverinfo; + +/* list of visor_device structs, linked via .list_all */ +static LIST_HEAD(list_all_bus_instances); +/* list of visor_device structs, linked via .list_all */ +static LIST_HEAD(list_all_device_instances); + +/* + * Generic function useful for validating any type of channel when it is + * received by the client that will be accessing the channel. + * Note that is only needed for callers in the EFI environment, and + * is used to pass the EFI_DIAG_CAPTURE_PROTOCOL needed to log messages. + */ +int visor_check_channel(struct channel_header *ch, struct device *dev, + const guid_t *expected_guid, char *chname, + u64 expected_min_bytes, u32 expected_version, + u64 expected_signature) +{ + if (!guid_is_null(expected_guid)) { + /* caller wants us to verify type GUID */ + if (!guid_equal(&ch->chtype, expected_guid)) { + dev_err(dev, "Channel mismatch on channel=%s(%pUL) field=type expected=%pUL actual=%pUL\n", + chname, expected_guid, expected_guid, + &ch->chtype); + return 0; + } + } + /* verify channel size */ + if (expected_min_bytes > 0) { + if (ch->size < expected_min_bytes) { + dev_err(dev, "Channel mismatch on channel=%s(%pUL) field=size expected=0x%-8.8Lx actual=0x%-8.8Lx\n", + chname, expected_guid, + (unsigned long long)expected_min_bytes, + ch->size); + return 0; + } + } + /* verify channel version */ + if (expected_version > 0) { + if (ch->version_id != expected_version) { + dev_err(dev, "Channel mismatch on channel=%s(%pUL) field=version expected=0x%-8.8lx actual=0x%-8.8x\n", + chname, expected_guid, + (unsigned long)expected_version, + ch->version_id); + return 0; + } + } + /* verify channel signature */ + if (expected_signature > 0) { + if (ch->signature != expected_signature) { + dev_err(dev, "Channel mismatch on channel=%s(%pUL) field=signature expected=0x%-8.8Lx actual=0x%-8.8Lx\n", + chname, expected_guid, expected_signature, + ch->signature); + return 0; + } + } + return 1; +} + +static int visorbus_uevent(struct device *xdev, struct kobj_uevent_env *env) +{ + struct visor_device *dev; + const guid_t *guid; + + dev = to_visor_device(xdev); + guid = visorchannel_get_guid(dev->visorchannel); + return add_uevent_var(env, "MODALIAS=visorbus:%pUl", guid); +} + +/* + * visorbus_match() - called automatically upon adding a visor_device + * (device_add), or adding a visor_driver + * (visorbus_register_visor_driver) + * @xdev: struct device for the device being matched + * @xdrv: struct device_driver for driver to match device against + * + * Return: 1 iff the provided driver can control the specified device + */ +static int visorbus_match(struct device *xdev, struct device_driver *xdrv) +{ + const guid_t *channel_type; + int i; + struct visor_device *dev; + struct visor_driver *drv; + struct visorchannel *chan; + + dev = to_visor_device(xdev); + channel_type = visorchannel_get_guid(dev->visorchannel); + drv = to_visor_driver(xdrv); + chan = dev->visorchannel; + if (!drv->channel_types) + return 0; + for (i = 0; !guid_is_null(&drv->channel_types[i].guid); i++) + if (guid_equal(&drv->channel_types[i].guid, channel_type) && + visor_check_channel(visorchannel_get_header(chan), + xdev, + &drv->channel_types[i].guid, + (char *)drv->channel_types[i].name, + drv->channel_types[i].min_bytes, + drv->channel_types[i].version, + VISOR_CHANNEL_SIGNATURE)) + return i + 1; + return 0; +} + +/* + * This describes the TYPE of bus. + * (Don't confuse this with an INSTANCE of the bus.) + */ +static struct bus_type visorbus_type = { + .name = "visorbus", + .match = visorbus_match, + .uevent = visorbus_uevent, + .dev_groups = visorbus_dev_groups, +}; + +struct visor_busdev { + u32 bus_no; + u32 dev_no; +}; + +static int match_visorbus_dev_by_id(struct device *dev, void *data) +{ + struct visor_device *vdev = to_visor_device(dev); + struct visor_busdev *id = data; + + if (vdev->chipset_bus_no == id->bus_no && + vdev->chipset_dev_no == id->dev_no) + return 1; + return 0; +} + +struct visor_device *visorbus_get_device_by_id(u32 bus_no, u32 dev_no, + struct visor_device *from) +{ + struct device *dev; + struct device *dev_start = NULL; + struct visor_busdev id = { + .bus_no = bus_no, + .dev_no = dev_no + }; + + if (from) + dev_start = &from->device; + dev = bus_find_device(&visorbus_type, dev_start, (void *)&id, + match_visorbus_dev_by_id); + if (!dev) + return NULL; + return to_visor_device(dev); +} + +/* + * visorbus_release_busdevice() - called when device_unregister() is called for + * the bus device instance, after all other tasks + * involved with destroying the dev are complete + * @xdev: struct device for the bus being released + */ +static void visorbus_release_busdevice(struct device *xdev) +{ + struct visor_device *dev = dev_get_drvdata(xdev); + + debugfs_remove(dev->debugfs_bus_info); + debugfs_remove_recursive(dev->debugfs_dir); + visorchannel_destroy(dev->visorchannel); + kfree(dev); +} + +/* + * visorbus_release_device() - called when device_unregister() is called for + * each child device instance + * @xdev: struct device for the visor device being released + */ +static void visorbus_release_device(struct device *xdev) +{ + struct visor_device *dev = to_visor_device(xdev); + + visorchannel_destroy(dev->visorchannel); + kfree(dev); +} + +/* + * BUS specific channel attributes to appear under + * /sys/bus/visorbus/dev/channel + */ + +static ssize_t physaddr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct visor_device *vdev = to_visor_device(dev); + + return sprintf(buf, "0x%llx\n", + visorchannel_get_physaddr(vdev->visorchannel)); +} +static DEVICE_ATTR_RO(physaddr); + +static ssize_t nbytes_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct visor_device *vdev = to_visor_device(dev); + + return sprintf(buf, "0x%lx\n", + visorchannel_get_nbytes(vdev->visorchannel)); +} +static DEVICE_ATTR_RO(nbytes); + +static ssize_t clientpartition_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct visor_device *vdev = to_visor_device(dev); + + return sprintf(buf, "0x%llx\n", + visorchannel_get_clientpartition(vdev->visorchannel)); +} +static DEVICE_ATTR_RO(clientpartition); + +static ssize_t typeguid_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct visor_device *vdev = to_visor_device(dev); + char typeid[LINESIZE]; + + return sprintf(buf, "%s\n", + visorchannel_id(vdev->visorchannel, typeid)); +} +static DEVICE_ATTR_RO(typeguid); + +static ssize_t zoneguid_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct visor_device *vdev = to_visor_device(dev); + char zoneid[LINESIZE]; + + return sprintf(buf, "%s\n", + visorchannel_zoneid(vdev->visorchannel, zoneid)); +} +static DEVICE_ATTR_RO(zoneguid); + +static ssize_t typename_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int i = 0; + struct bus_type *xbus = dev->bus; + struct device_driver *xdrv = dev->driver; + struct visor_driver *drv = NULL; + + if (!xdrv) + return 0; + i = xbus->match(dev, xdrv); + if (!i) + return 0; + drv = to_visor_driver(xdrv); + return sprintf(buf, "%s\n", drv->channel_types[i - 1].name); +} +static DEVICE_ATTR_RO(typename); + +static struct attribute *channel_attrs[] = { + &dev_attr_physaddr.attr, + &dev_attr_nbytes.attr, + &dev_attr_clientpartition.attr, + &dev_attr_typeguid.attr, + &dev_attr_zoneguid.attr, + &dev_attr_typename.attr, + NULL +}; + +ATTRIBUTE_GROUPS(channel); + +/* + * BUS instance attributes + * + * define & implement display of bus attributes under + * /sys/bus/visorbus/devices/visorbus. + */ +static ssize_t partition_handle_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct visor_device *vdev = to_visor_device(dev); + u64 handle = visorchannel_get_clientpartition(vdev->visorchannel); + + return sprintf(buf, "0x%llx\n", handle); +} +static DEVICE_ATTR_RO(partition_handle); + +static ssize_t partition_guid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct visor_device *vdev = to_visor_device(dev); + + return sprintf(buf, "{%pUb}\n", &vdev->partition_guid); +} +static DEVICE_ATTR_RO(partition_guid); + +static ssize_t partition_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct visor_device *vdev = to_visor_device(dev); + + return sprintf(buf, "%s\n", vdev->name); +} +static DEVICE_ATTR_RO(partition_name); + +static ssize_t channel_addr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct visor_device *vdev = to_visor_device(dev); + u64 addr = visorchannel_get_physaddr(vdev->visorchannel); + + return sprintf(buf, "0x%llx\n", addr); +} +static DEVICE_ATTR_RO(channel_addr); + +static ssize_t channel_bytes_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct visor_device *vdev = to_visor_device(dev); + u64 nbytes = visorchannel_get_nbytes(vdev->visorchannel); + + return sprintf(buf, "0x%llx\n", nbytes); +} +static DEVICE_ATTR_RO(channel_bytes); + +static ssize_t channel_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct visor_device *vdev = to_visor_device(dev); + int len = 0; + + visorchannel_id(vdev->visorchannel, buf); + len = strlen(buf); + buf[len++] = '\n'; + return len; +} +static DEVICE_ATTR_RO(channel_id); + +static struct attribute *visorbus_attrs[] = { + &dev_attr_partition_handle.attr, + &dev_attr_partition_guid.attr, + &dev_attr_partition_name.attr, + &dev_attr_channel_addr.attr, + &dev_attr_channel_bytes.attr, + &dev_attr_channel_id.attr, + NULL +}; + +ATTRIBUTE_GROUPS(visorbus); + +/* + * BUS debugfs entries + * + * define & implement display of debugfs attributes under + * /sys/kernel/debug/visorbus/visorbus. + */ + +/* + * vbuschannel_print_devinfo() - format a struct visor_vbus_deviceinfo + * and write it to a seq_file + * @devinfo: the struct visor_vbus_deviceinfo to format + * @seq: seq_file to write to + * @devix: the device index to be included in the output data, or -1 if no + * device index is to be included + * + * Reads @devInfo, and writes it in human-readable notation to @seq. + */ +static void vbuschannel_print_devinfo(struct visor_vbus_deviceinfo *devinfo, + struct seq_file *seq, int devix) +{ + /* uninitialized vbus device entry */ + if (!isprint(devinfo->devtype[0])) + return; + if (devix >= 0) + seq_printf(seq, "[%d]", devix); + else + /* vbus device entry is for bus or chipset */ + seq_puts(seq, " "); + /* + * Note: because the s-Par back-end is free to scribble in this area, + * we never assume '\0'-termination. + */ + seq_printf(seq, "%-*.*s ", (int)sizeof(devinfo->devtype), + (int)sizeof(devinfo->devtype), devinfo->devtype); + seq_printf(seq, "%-*.*s ", (int)sizeof(devinfo->drvname), + (int)sizeof(devinfo->drvname), devinfo->drvname); + seq_printf(seq, "%.*s\n", (int)sizeof(devinfo->infostrs), + devinfo->infostrs); +} + +static int bus_info_debugfs_show(struct seq_file *seq, void *v) +{ + int i = 0; + unsigned long off; + struct visor_vbus_deviceinfo dev_info; + struct visor_device *vdev = seq->private; + struct visorchannel *channel = vdev->visorchannel; + + if (!channel) + return 0; + + seq_printf(seq, + "Client device/driver info for %s partition (vbus #%u):\n", + ((vdev->name) ? (char *)(vdev->name) : ""), + vdev->chipset_bus_no); + if (visorchannel_read(channel, + offsetof(struct visor_vbus_channel, chp_info), + &dev_info, sizeof(dev_info)) >= 0) + vbuschannel_print_devinfo(&dev_info, seq, -1); + if (visorchannel_read(channel, + offsetof(struct visor_vbus_channel, bus_info), + &dev_info, sizeof(dev_info)) >= 0) + vbuschannel_print_devinfo(&dev_info, seq, -1); + + off = offsetof(struct visor_vbus_channel, dev_info); + while (off + sizeof(dev_info) <= visorchannel_get_nbytes(channel)) { + if (visorchannel_read(channel, off, &dev_info, + sizeof(dev_info)) >= 0) + vbuschannel_print_devinfo(&dev_info, seq, i); + off += sizeof(dev_info); + i++; + } + return 0; +} + +static int bus_info_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, bus_info_debugfs_show, inode->i_private); +} + +static const struct file_operations bus_info_debugfs_fops = { + .owner = THIS_MODULE, + .open = bus_info_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void dev_periodic_work(struct timer_list *t) +{ + struct visor_device *dev = from_timer(dev, t, timer); + struct visor_driver *drv = to_visor_driver(dev->device.driver); + + drv->channel_interrupt(dev); + mod_timer(&dev->timer, jiffies + POLLJIFFIES_NORMALCHANNEL); +} + +static int dev_start_periodic_work(struct visor_device *dev) +{ + if (dev->being_removed || dev->timer_active) + return -EINVAL; + + /* now up by at least 2 */ + get_device(&dev->device); + dev->timer.expires = jiffies + POLLJIFFIES_NORMALCHANNEL; + add_timer(&dev->timer); + dev->timer_active = true; + return 0; +} + +static void dev_stop_periodic_work(struct visor_device *dev) +{ + if (!dev->timer_active) + return; + + del_timer_sync(&dev->timer); + dev->timer_active = false; + put_device(&dev->device); +} + +/* + * visordriver_remove_device() - handle visor device going away + * @xdev: struct device for the visor device being removed + * + * This is called when device_unregister() is called for each child device + * instance, to notify the appropriate visorbus function driver that the device + * is going away, and to decrease the reference count of the device. + * + * Return: 0 iff successful + */ +static int visordriver_remove_device(struct device *xdev) +{ + struct visor_device *dev = to_visor_device(xdev); + struct visor_driver *drv = to_visor_driver(xdev->driver); + + mutex_lock(&dev->visordriver_callback_lock); + dev->being_removed = true; + drv->remove(dev); + mutex_unlock(&dev->visordriver_callback_lock); + dev_stop_periodic_work(dev); + put_device(&dev->device); + return 0; +} + +/* + * visorbus_unregister_visor_driver() - unregisters the provided driver + * @drv: the driver to unregister + * + * A visor function driver calls this function to unregister the driver, + * i.e., within its module_exit function. + */ +void visorbus_unregister_visor_driver(struct visor_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(visorbus_unregister_visor_driver); + +/* + * visorbus_read_channel() - reads from the designated channel into + * the provided buffer + * @dev: the device whose channel is read from + * @offset: the offset into the channel at which reading starts + * @dest: the destination buffer that is written into from the channel + * @nbytes: the number of bytes to read from the channel + * + * If receiving a message, use the visorchannel_signalremove() function instead. + * + * Return: integer indicating success (zero) or failure (non-zero) + */ +int visorbus_read_channel(struct visor_device *dev, unsigned long offset, + void *dest, unsigned long nbytes) +{ + return visorchannel_read(dev->visorchannel, offset, dest, nbytes); +} +EXPORT_SYMBOL_GPL(visorbus_read_channel); + +/* + * visorbus_write_channel() - writes the provided buffer into the designated + * channel + * @dev: the device whose channel is written to + * @offset: the offset into the channel at which writing starts + * @src: the source buffer that is written into the channel + * @nbytes: the number of bytes to write into the channel + * + * If sending a message, use the visorchannel_signalinsert() function instead. + * + * Return: integer indicating success (zero) or failure (non-zero) + */ +int visorbus_write_channel(struct visor_device *dev, unsigned long offset, + void *src, unsigned long nbytes) +{ + return visorchannel_write(dev->visorchannel, offset, src, nbytes); +} +EXPORT_SYMBOL_GPL(visorbus_write_channel); + +/* + * visorbus_enable_channel_interrupts() - enables interrupts on the + * designated device + * @dev: the device on which to enable interrupts + * + * Currently we don't yet have a real interrupt, so for now we just call the + * interrupt function periodically via a timer. + */ +int visorbus_enable_channel_interrupts(struct visor_device *dev) +{ + struct visor_driver *drv = to_visor_driver(dev->device.driver); + + if (!drv->channel_interrupt) { + dev_err(&dev->device, "%s no interrupt function!\n", __func__); + return -ENOENT; + } + + return dev_start_periodic_work(dev); +} +EXPORT_SYMBOL_GPL(visorbus_enable_channel_interrupts); + +/* + * visorbus_disable_channel_interrupts() - disables interrupts on the + * designated device + * @dev: the device on which to disable interrupts + */ +void visorbus_disable_channel_interrupts(struct visor_device *dev) +{ + dev_stop_periodic_work(dev); +} +EXPORT_SYMBOL_GPL(visorbus_disable_channel_interrupts); + +/* + * create_visor_device() - create visor device as a result of receiving the + * controlvm device_create message for a new device + * @dev: a freshly-zeroed struct visor_device, containing only filled-in values + * for chipset_bus_no and chipset_dev_no, that will be initialized + * + * This is how everything starts from the device end. + * This function is called when a channel first appears via a ControlVM + * message. In response, this function allocates a visor_device to correspond + * to the new channel, and attempts to connect it the appropriate * driver. If + * the appropriate driver is found, the visor_driver.probe() function for that + * driver will be called, and will be passed the new * visor_device that we + * just created. + * + * It's ok if the appropriate driver is not yet loaded, because in that case + * the new device struct will just stick around in the bus' list of devices. + * When the appropriate driver calls visorbus_register_visor_driver(), the + * visor_driver.probe() for the new driver will be called with the new device. + * + * Return: 0 if successful, otherwise the negative value returned by + * device_add() indicating the reason for failure + */ +int create_visor_device(struct visor_device *dev) +{ + int err; + u32 chipset_bus_no = dev->chipset_bus_no; + u32 chipset_dev_no = dev->chipset_dev_no; + + mutex_init(&dev->visordriver_callback_lock); + dev->device.bus = &visorbus_type; + dev->device.groups = channel_groups; + device_initialize(&dev->device); + dev->device.release = visorbus_release_device; + /* keep a reference just for us (now 2) */ + get_device(&dev->device); + timer_setup(&dev->timer, dev_periodic_work, 0); + /* + * bus_id must be a unique name with respect to this bus TYPE (NOT bus + * instance). That's why we need to include the bus number within the + * name. + */ + err = dev_set_name(&dev->device, "vbus%u:dev%u", + chipset_bus_no, chipset_dev_no); + if (err) + goto err_put; + /* + * device_add does this: + * bus_add_device(dev) + * ->device_attach(dev) + * ->for each driver drv registered on the bus that dev is on + * if (dev.drv) ** device already has a driver ** + * ** not sure we could ever get here... ** + * else + * if (bus.match(dev,drv)) [visorbus_match] + * dev.drv = drv + * if (!drv.probe(dev)) [visordriver_probe_device] + * dev.drv = NULL + * + * Note that device_add does NOT fail if no driver failed to claim the + * device. The device will be linked onto bus_type.klist_devices + * regardless (use bus_for_each_dev). + */ + err = device_add(&dev->device); + if (err < 0) + goto err_put; + list_add_tail(&dev->list_all, &list_all_device_instances); + dev->state.created = 1; + visorbus_response(dev, err, CONTROLVM_DEVICE_CREATE); + /* success: reference kept via unmatched get_device() */ + return 0; + +err_put: + put_device(&dev->device); + dev_err(&dev->device, "Creating visor device failed. %d\n", err); + return err; +} + +void remove_visor_device(struct visor_device *dev) +{ + list_del(&dev->list_all); + put_device(&dev->device); + if (dev->pending_msg_hdr) + visorbus_response(dev, 0, CONTROLVM_DEVICE_DESTROY); + device_unregister(&dev->device); +} + +static int get_vbus_header_info(struct visorchannel *chan, + struct device *dev, + struct visor_vbus_headerinfo *hdr_info) +{ + int err; + + if (!visor_check_channel(visorchannel_get_header(chan), + dev, + &visor_vbus_channel_guid, + "vbus", + sizeof(struct visor_vbus_channel), + VISOR_VBUS_CHANNEL_VERSIONID, + VISOR_CHANNEL_SIGNATURE)) + return -EINVAL; + + err = visorchannel_read(chan, sizeof(struct channel_header), hdr_info, + sizeof(*hdr_info)); + if (err < 0) + return err; + if (hdr_info->struct_bytes < sizeof(struct visor_vbus_headerinfo)) + return -EINVAL; + if (hdr_info->device_info_struct_bytes < + sizeof(struct visor_vbus_deviceinfo)) + return -EINVAL; + return 0; +} + +/* + * write_vbus_chp_info() - write the contents of to the struct + * visor_vbus_channel.chp_info + * @chan: indentifies the s-Par channel that will be updated + * @hdr_info: used to find appropriate channel offset to write data + * @info: contains the information to write + * + * Writes chipset info into the channel memory to be used for diagnostic + * purposes. + * + * Returns no value since this is debug information and not needed for + * device functionality. + */ +static void write_vbus_chp_info(struct visorchannel *chan, + struct visor_vbus_headerinfo *hdr_info, + struct visor_vbus_deviceinfo *info) +{ + int off; + + if (hdr_info->chp_info_offset == 0) + return; + + off = sizeof(struct channel_header) + hdr_info->chp_info_offset; + visorchannel_write(chan, off, info, sizeof(*info)); +} + +/* + * write_vbus_bus_info() - write the contents of to the struct + * visor_vbus_channel.bus_info + * @chan: indentifies the s-Par channel that will be updated + * @hdr_info: used to find appropriate channel offset to write data + * @info: contains the information to write + * + * Writes bus info into the channel memory to be used for diagnostic + * purposes. + * + * Returns no value since this is debug information and not needed for + * device functionality. + */ +static void write_vbus_bus_info(struct visorchannel *chan, + struct visor_vbus_headerinfo *hdr_info, + struct visor_vbus_deviceinfo *info) +{ + int off; + + if (hdr_info->bus_info_offset == 0) + return; + + off = sizeof(struct channel_header) + hdr_info->bus_info_offset; + visorchannel_write(chan, off, info, sizeof(*info)); +} + +/* + * write_vbus_dev_info() - write the contents of to the struct + * visor_vbus_channel.dev_info[] + * @chan: indentifies the s-Par channel that will be updated + * @hdr_info: used to find appropriate channel offset to write data + * @info: contains the information to write + * @devix: the relative device number (0..n-1) of the device on the bus + * + * Writes device info into the channel memory to be used for diagnostic + * purposes. + * + * Returns no value since this is debug information and not needed for + * device functionality. + */ +static void write_vbus_dev_info(struct visorchannel *chan, + struct visor_vbus_headerinfo *hdr_info, + struct visor_vbus_deviceinfo *info, + unsigned int devix) +{ + int off; + + if (hdr_info->dev_info_offset == 0) + return; + off = (sizeof(struct channel_header) + hdr_info->dev_info_offset) + + (hdr_info->device_info_struct_bytes * devix); + visorchannel_write(chan, off, info, sizeof(*info)); +} + +static void bus_device_info_init( + struct visor_vbus_deviceinfo *bus_device_info_ptr, + const char *dev_type, const char *drv_name) +{ + memset(bus_device_info_ptr, 0, sizeof(struct visor_vbus_deviceinfo)); + snprintf(bus_device_info_ptr->devtype, + sizeof(bus_device_info_ptr->devtype), + "%s", (dev_type) ? dev_type : "unknownType"); + snprintf(bus_device_info_ptr->drvname, + sizeof(bus_device_info_ptr->drvname), + "%s", (drv_name) ? drv_name : "unknownDriver"); + snprintf(bus_device_info_ptr->infostrs, + sizeof(bus_device_info_ptr->infostrs), "kernel ver. %s", + utsname()->release); +} + +/* + * publish_vbus_dev_info() - for a child device just created on a client bus, + * fill in information about the driver that is + * controlling this device into the appropriate slot + * within the vbus channel of the bus instance + * @visordev: struct visor_device for the desired device + */ +static void publish_vbus_dev_info(struct visor_device *visordev) +{ + int i; + struct visor_device *bdev; + struct visor_driver *visordrv; + u32 bus_no = visordev->chipset_bus_no; + u32 dev_no = visordev->chipset_dev_no; + struct visor_vbus_deviceinfo dev_info; + const char *chan_type_name = NULL; + struct visor_vbus_headerinfo *hdr_info; + + if (!visordev->device.driver) + return; + bdev = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); + if (!bdev) + return; + hdr_info = (struct visor_vbus_headerinfo *)bdev->vbus_hdr_info; + if (!hdr_info) + return; + visordrv = to_visor_driver(visordev->device.driver); + + /* + * Within the list of device types (by GUID) that the driver + * says it supports, find out which one of those types matches + * the type of this device, so that we can include the device + * type name + */ + for (i = 0; visordrv->channel_types[i].name; i++) { + if (guid_equal(&visordrv->channel_types[i].guid, + &visordev->channel_type_guid)) { + chan_type_name = visordrv->channel_types[i].name; + break; + } + } + bus_device_info_init(&dev_info, chan_type_name, visordrv->name); + write_vbus_dev_info(bdev->visorchannel, hdr_info, &dev_info, dev_no); + write_vbus_chp_info(bdev->visorchannel, hdr_info, &chipset_driverinfo); + write_vbus_bus_info(bdev->visorchannel, hdr_info, + &clientbus_driverinfo); +} + +/* + * visordriver_probe_device() - handle new visor device coming online + * @xdev: struct device for the visor device being probed + * + * This is called automatically upon adding a visor_device (device_add), or + * adding a visor_driver (visorbus_register_visor_driver), but only after + * visorbus_match() has returned 1 to indicate a successful match between + * driver and device. + * + * If successful, a reference to the device will be held onto via get_device(). + * + * Return: 0 if successful, meaning the function driver's probe() function + * was successful with this device, otherwise a negative errno + * value indicating failure reason + */ +static int visordriver_probe_device(struct device *xdev) +{ + int err; + struct visor_driver *drv = to_visor_driver(xdev->driver); + struct visor_device *dev = to_visor_device(xdev); + + mutex_lock(&dev->visordriver_callback_lock); + dev->being_removed = false; + err = drv->probe(dev); + if (err) { + mutex_unlock(&dev->visordriver_callback_lock); + return err; + } + /* success: reference kept via unmatched get_device() */ + get_device(&dev->device); + publish_vbus_dev_info(dev); + mutex_unlock(&dev->visordriver_callback_lock); + return 0; +} + +/* + * visorbus_register_visor_driver() - registers the provided visor driver for + * handling one or more visor device + * types (channel_types) + * @drv: the driver to register + * + * A visor function driver calls this function to register the driver. The + * caller MUST fill in the following fields within the #drv structure: + * name, version, owner, channel_types, probe, remove + * + * Here's how the whole Linux bus / driver / device model works. + * + * At system start-up, the visorbus kernel module is loaded, which registers + * visorbus_type as a bus type, using bus_register(). + * + * All kernel modules that support particular device types on a + * visorbus bus are loaded. Each of these kernel modules calls + * visorbus_register_visor_driver() in their init functions, passing a + * visor_driver struct. visorbus_register_visor_driver() in turn calls + * register_driver(&visor_driver.driver). This .driver member is + * initialized with generic methods (like probe), whose sole responsibility + * is to act as a broker for the real methods, which are within the + * visor_driver struct. (This is the way the subclass behavior is + * implemented, since visor_driver is essentially a subclass of the + * generic driver.) Whenever a driver_register() happens, core bus code in + * the kernel does (see device_attach() in drivers/base/dd.c): + * + * for each dev associated with the bus (the bus that driver is on) that + * does not yet have a driver + * if bus.match(dev,newdriver) == yes_matched ** .match specified + * ** during bus_register(). + * newdriver.probe(dev) ** for visor drivers, this will call + * ** the generic driver.probe implemented in visorbus.c, + * ** which in turn calls the probe specified within the + * ** struct visor_driver (which was specified by the + * ** actual device driver as part of + * ** visorbus_register_visor_driver()). + * + * The above dance also happens when a new device appears. + * So the question is, how are devices created within the system? + * Basically, just call device_add(dev). See pci_bus_add_devices(). + * pci_scan_device() shows an example of how to build a device struct. It + * returns the newly-created struct to pci_scan_single_device(), who adds it + * to the list of devices at PCIBUS.devices. That list of devices is what + * is traversed by pci_bus_add_devices(). + * + * Return: integer indicating success (zero) or failure (non-zero) + */ +int visorbus_register_visor_driver(struct visor_driver *drv) +{ + /* can't register on a nonexistent bus */ + if (!initialized) + return -ENODEV; + if (!drv->probe) + return -EINVAL; + if (!drv->remove) + return -EINVAL; + if (!drv->pause) + return -EINVAL; + if (!drv->resume) + return -EINVAL; + + drv->driver.name = drv->name; + drv->driver.bus = &visorbus_type; + drv->driver.probe = visordriver_probe_device; + drv->driver.remove = visordriver_remove_device; + drv->driver.owner = drv->owner; + /* + * driver_register does this: + * bus_add_driver(drv) + * ->if (drv.bus) ** (bus_type) ** + * driver_attach(drv) + * for each dev with bus type of drv.bus + * if (!dev.drv) ** no driver assigned yet ** + * if (bus.match(dev,drv)) [visorbus_match] + * dev.drv = drv + * if (!drv.probe(dev)) [visordriver_probe_device] + * dev.drv = NULL + */ + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(visorbus_register_visor_driver); + +/* + * visorbus_create_instance() - create a device instance for the visorbus itself + * @dev: struct visor_device indicating the bus instance + * + * Return: 0 for success, otherwise negative errno value indicating reason for + * failure + */ +int visorbus_create_instance(struct visor_device *dev) +{ + int id = dev->chipset_bus_no; + int err; + struct visor_vbus_headerinfo *hdr_info; + + hdr_info = kzalloc(sizeof(*hdr_info), GFP_KERNEL); + if (!hdr_info) + return -ENOMEM; + dev_set_name(&dev->device, "visorbus%d", id); + dev->device.bus = &visorbus_type; + dev->device.groups = visorbus_groups; + dev->device.release = visorbus_release_busdevice; + dev->debugfs_dir = debugfs_create_dir(dev_name(&dev->device), + visorbus_debugfs_dir); + dev->debugfs_bus_info = debugfs_create_file("client_bus_info", 0440, + dev->debugfs_dir, dev, + &bus_info_debugfs_fops); + dev_set_drvdata(&dev->device, dev); + err = get_vbus_header_info(dev->visorchannel, &dev->device, hdr_info); + if (err < 0) + goto err_debugfs_dir; + err = device_register(&dev->device); + if (err < 0) + goto err_debugfs_dir; + list_add_tail(&dev->list_all, &list_all_bus_instances); + dev->state.created = 1; + dev->vbus_hdr_info = (void *)hdr_info; + write_vbus_chp_info(dev->visorchannel, hdr_info, &chipset_driverinfo); + write_vbus_bus_info(dev->visorchannel, hdr_info, &clientbus_driverinfo); + visorbus_response(dev, err, CONTROLVM_BUS_CREATE); + return 0; + +err_debugfs_dir: + debugfs_remove_recursive(dev->debugfs_dir); + kfree(hdr_info); + dev_err(&dev->device, "%s failed: %d\n", __func__, err); + return err; +} + +/* + * visorbus_remove_instance() - remove a device instance for the visorbus itself + * @dev: struct visor_device indentifying the bus to remove + */ +void visorbus_remove_instance(struct visor_device *dev) +{ + /* + * Note that this will result in the release method for + * dev->dev being called, which will call + * visorbus_release_busdevice(). This has something to do with + * the put_device() done in device_unregister(), but I have never + * successfully been able to trace thru the code to see where/how + * release() gets called. But I know it does. + */ + kfree(dev->vbus_hdr_info); + list_del(&dev->list_all); + if (dev->pending_msg_hdr) + visorbus_response(dev, 0, CONTROLVM_BUS_DESTROY); + device_unregister(&dev->device); +} + +/* + * remove_all_visor_devices() - remove all child visorbus device instances + */ +static void remove_all_visor_devices(void) +{ + struct list_head *listentry, *listtmp; + + list_for_each_safe(listentry, listtmp, &list_all_device_instances) { + struct visor_device *dev; + + dev = list_entry(listentry, struct visor_device, list_all); + remove_visor_device(dev); + } +} + +/* + * pause_state_change_complete() - the callback function to be called by a + * visorbus function driver when a + * pending "pause device" operation has + * completed + * @dev: struct visor_device identifying the paused device + * @status: 0 iff the pause state change completed successfully, otherwise + * a negative errno value indicating the reason for failure + */ +static void pause_state_change_complete(struct visor_device *dev, int status) +{ + if (!dev->pausing) + return; + + dev->pausing = false; + visorbus_device_changestate_response(dev, status, + segment_state_standby); +} + +/* + * resume_state_change_complete() - the callback function to be called by a + * visorbus function driver when a + * pending "resume device" operation has + * completed + * @dev: struct visor_device identifying the resumed device + * @status: 0 iff the resume state change completed successfully, otherwise + * a negative errno value indicating the reason for failure + */ +static void resume_state_change_complete(struct visor_device *dev, int status) +{ + if (!dev->resuming) + return; + + dev->resuming = false; + /* + * Notify the chipset driver that the resume is complete, + * which will presumably want to send some sort of response to + * the initiator. + */ + visorbus_device_changestate_response(dev, status, + segment_state_running); +} + +/* + * visorchipset_initiate_device_pause_resume() - start a pause or resume + * operation for a visor device + * @dev: struct visor_device identifying the device being paused or resumed + * @is_pause: true to indicate pause operation, false to indicate resume + * + * Tell the subordinate function driver for a specific device to pause + * or resume that device. Success/failure result is returned asynchronously + * via a callback function; see pause_state_change_complete() and + * resume_state_change_complete(). + */ +static int visorchipset_initiate_device_pause_resume(struct visor_device *dev, + bool is_pause) +{ + int err; + struct visor_driver *drv; + + /* If no driver associated with the device nothing to pause/resume */ + if (!dev->device.driver) + return 0; + if (dev->pausing || dev->resuming) + return -EBUSY; + + drv = to_visor_driver(dev->device.driver); + if (is_pause) { + dev->pausing = true; + err = drv->pause(dev, pause_state_change_complete); + } else { + /* + * The vbus_dev_info structure in the channel was been cleared, + * make sure it is valid. + */ + publish_vbus_dev_info(dev); + dev->resuming = true; + err = drv->resume(dev, resume_state_change_complete); + } + return err; +} + +/* + * visorchipset_device_pause() - start a pause operation for a visor device + * @dev_info: struct visor_device identifying the device being paused + * + * Tell the subordinate function driver for a specific device to pause + * that device. Success/failure result is returned asynchronously + * via a callback function; see pause_state_change_complete(). + */ +int visorchipset_device_pause(struct visor_device *dev_info) +{ + int err; + + err = visorchipset_initiate_device_pause_resume(dev_info, true); + if (err < 0) { + dev_info->pausing = false; + return err; + } + return 0; +} + +/* + * visorchipset_device_resume() - start a resume operation for a visor device + * @dev_info: struct visor_device identifying the device being resumed + * + * Tell the subordinate function driver for a specific device to resume + * that device. Success/failure result is returned asynchronously + * via a callback function; see resume_state_change_complete(). + */ +int visorchipset_device_resume(struct visor_device *dev_info) +{ + int err; + + err = visorchipset_initiate_device_pause_resume(dev_info, false); + if (err < 0) { + dev_info->resuming = false; + return err; + } + return 0; +} + +int visorbus_init(void) +{ + int err; + + visorbus_debugfs_dir = debugfs_create_dir("visorbus", NULL); + bus_device_info_init(&clientbus_driverinfo, "clientbus", "visorbus"); + err = bus_register(&visorbus_type); + if (err < 0) + return err; + initialized = true; + bus_device_info_init(&chipset_driverinfo, "chipset", "visorchipset"); + return 0; +} + +void visorbus_exit(void) +{ + struct list_head *listentry, *listtmp; + + remove_all_visor_devices(); + list_for_each_safe(listentry, listtmp, &list_all_bus_instances) { + struct visor_device *dev; + + dev = list_entry(listentry, struct visor_device, list_all); + visorbus_remove_instance(dev); + } + bus_unregister(&visorbus_type); + initialized = false; + debugfs_remove_recursive(visorbus_debugfs_dir); +} diff --git a/drivers/visorbus/visorbus_private.h b/drivers/visorbus/visorbus_private.h new file mode 100644 index 000000000000..366380b7f8d9 --- /dev/null +++ b/drivers/visorbus/visorbus_private.h @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2010 - 2015 UNISYS CORPORATION + * All rights reserved. + */ + +#ifndef __VISORBUS_PRIVATE_H__ +#define __VISORBUS_PRIVATE_H__ + +#include +#include +#include + +#include "controlvmchannel.h" +#include "vbuschannel.h" + +struct visor_device *visorbus_get_device_by_id(u32 bus_no, u32 dev_no, + struct visor_device *from); +int visorbus_create_instance(struct visor_device *dev); +void visorbus_remove_instance(struct visor_device *bus_info); +int create_visor_device(struct visor_device *dev_info); +void remove_visor_device(struct visor_device *dev_info); +int visorchipset_device_pause(struct visor_device *dev_info); +int visorchipset_device_resume(struct visor_device *dev_info); +void visorbus_response(struct visor_device *p, int response, int controlvm_id); +void visorbus_device_changestate_response(struct visor_device *p, int response, + struct visor_segment_state state); +int visorbus_init(void); +void visorbus_exit(void); + +/* visorchannel access functions */ +struct visorchannel *visorchannel_create(u64 physaddr, gfp_t gfp, + const guid_t *guid, bool needs_lock); +void visorchannel_destroy(struct visorchannel *channel); +int visorchannel_read(struct visorchannel *channel, ulong offset, + void *dest, ulong nbytes); +int visorchannel_write(struct visorchannel *channel, ulong offset, + void *dest, ulong nbytes); +u64 visorchannel_get_physaddr(struct visorchannel *channel); +ulong visorchannel_get_nbytes(struct visorchannel *channel); +char *visorchannel_id(struct visorchannel *channel, char *s); +char *visorchannel_zoneid(struct visorchannel *channel, char *s); +u64 visorchannel_get_clientpartition(struct visorchannel *channel); +int visorchannel_set_clientpartition(struct visorchannel *channel, + u64 partition_handle); +char *visorchannel_guid_id(const guid_t *guid, char *s); +void *visorchannel_get_header(struct visorchannel *channel); +#endif diff --git a/drivers/visorbus/visorchannel.c b/drivers/visorbus/visorchannel.c new file mode 100644 index 000000000000..bd890e0f456b --- /dev/null +++ b/drivers/visorbus/visorchannel.c @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2010 - 2015 UNISYS CORPORATION + * All rights reserved. + */ + +/* + * This provides s-Par channel communication primitives, which are + * independent of the mechanism used to access the channel data. + */ + +#include +#include +#include +#include + +#include "visorbus_private.h" +#include "controlvmchannel.h" + +#define VISOR_DRV_NAME "visorchannel" + +#define VISOR_CONSOLEVIDEO_CHANNEL_GUID \ + GUID_INIT(0x3cd6e705, 0xd6a2, 0x4aa5, \ + 0xad, 0x5c, 0x7b, 0x8, 0x88, 0x9d, 0xff, 0xe2) + +static const guid_t visor_video_guid = VISOR_CONSOLEVIDEO_CHANNEL_GUID; + +struct visorchannel { + u64 physaddr; + ulong nbytes; + void *mapped; + bool requested; + struct channel_header chan_hdr; + guid_t guid; + /* + * channel creator knows if more than one thread will be inserting or + * removing + */ + bool needs_lock; + /* protect head writes in chan_hdr */ + spinlock_t insert_lock; + /* protect tail writes in chan_hdr */ + spinlock_t remove_lock; + guid_t type; + guid_t inst; +}; + +void visorchannel_destroy(struct visorchannel *channel) +{ + if (!channel) + return; + + if (channel->mapped) { + memunmap(channel->mapped); + if (channel->requested) + release_mem_region(channel->physaddr, channel->nbytes); + } + kfree(channel); +} + +u64 visorchannel_get_physaddr(struct visorchannel *channel) +{ + return channel->physaddr; +} + +ulong visorchannel_get_nbytes(struct visorchannel *channel) +{ + return channel->nbytes; +} + +char *visorchannel_guid_id(const guid_t *guid, char *s) +{ + sprintf(s, "%pUL", guid); + return s; +} + +char *visorchannel_id(struct visorchannel *channel, char *s) +{ + return visorchannel_guid_id(&channel->guid, s); +} + +char *visorchannel_zoneid(struct visorchannel *channel, char *s) +{ + return visorchannel_guid_id(&channel->chan_hdr.zone_guid, s); +} + +u64 visorchannel_get_clientpartition(struct visorchannel *channel) +{ + return channel->chan_hdr.partition_handle; +} + +int visorchannel_set_clientpartition(struct visorchannel *channel, + u64 partition_handle) +{ + channel->chan_hdr.partition_handle = partition_handle; + return 0; +} + +/** + * visorchannel_get_guid() - queries the GUID of the designated channel + * @channel: the channel to query + * + * Return: the GUID of the provided channel + */ +const guid_t *visorchannel_get_guid(struct visorchannel *channel) +{ + return &channel->guid; +} +EXPORT_SYMBOL_GPL(visorchannel_get_guid); + +int visorchannel_read(struct visorchannel *channel, ulong offset, void *dest, + ulong nbytes) +{ + if (offset + nbytes > channel->nbytes) + return -EIO; + + memcpy(dest, channel->mapped + offset, nbytes); + return 0; +} + +int visorchannel_write(struct visorchannel *channel, ulong offset, void *dest, + ulong nbytes) +{ + size_t chdr_size = sizeof(struct channel_header); + size_t copy_size; + + if (offset + nbytes > channel->nbytes) + return -EIO; + + if (offset < chdr_size) { + copy_size = min(chdr_size - offset, nbytes); + memcpy(((char *)(&channel->chan_hdr)) + offset, + dest, copy_size); + } + memcpy(channel->mapped + offset, dest, nbytes); + return 0; +} + +void *visorchannel_get_header(struct visorchannel *channel) +{ + return &channel->chan_hdr; +} + +/* + * Return offset of a specific SIGNAL_QUEUE_HEADER from the beginning of a + * channel header + */ +static int sig_queue_offset(struct channel_header *chan_hdr, int q) +{ + return ((chan_hdr)->ch_space_offset + + ((q) * sizeof(struct signal_queue_header))); +} + +/* + * Return offset of a specific queue entry (data) from the beginning of a + * channel header + */ +static int sig_data_offset(struct channel_header *chan_hdr, int q, + struct signal_queue_header *sig_hdr, int slot) +{ + return (sig_queue_offset(chan_hdr, q) + sig_hdr->sig_base_offset + + (slot * sig_hdr->signal_size)); +} + +/* + * Write the contents of a specific field within a SIGNAL_QUEUE_HEADER back into + * host memory + */ +#define SIG_WRITE_FIELD(channel, queue, sig_hdr, FIELD) \ + visorchannel_write(channel, \ + sig_queue_offset(&channel->chan_hdr, queue) + \ + offsetof(struct signal_queue_header, FIELD), \ + &((sig_hdr)->FIELD), \ + sizeof((sig_hdr)->FIELD)) + +static int sig_read_header(struct visorchannel *channel, u32 queue, + struct signal_queue_header *sig_hdr) +{ + if (channel->chan_hdr.ch_space_offset < sizeof(struct channel_header)) + return -EINVAL; + + /* Read the appropriate SIGNAL_QUEUE_HEADER into local memory. */ + return visorchannel_read(channel, + sig_queue_offset(&channel->chan_hdr, queue), + sig_hdr, sizeof(struct signal_queue_header)); +} + +static int sig_read_data(struct visorchannel *channel, u32 queue, + struct signal_queue_header *sig_hdr, u32 slot, + void *data) +{ + int signal_data_offset = sig_data_offset(&channel->chan_hdr, queue, + sig_hdr, slot); + + return visorchannel_read(channel, signal_data_offset, + data, sig_hdr->signal_size); +} + +static int sig_write_data(struct visorchannel *channel, u32 queue, + struct signal_queue_header *sig_hdr, u32 slot, + void *data) +{ + int signal_data_offset = sig_data_offset(&channel->chan_hdr, queue, + sig_hdr, slot); + + return visorchannel_write(channel, signal_data_offset, + data, sig_hdr->signal_size); +} + +static int signalremove_inner(struct visorchannel *channel, u32 queue, + void *msg) +{ + struct signal_queue_header sig_hdr; + int error; + + error = sig_read_header(channel, queue, &sig_hdr); + if (error) + return error; + /* No signals to remove; have caller try again. */ + if (sig_hdr.head == sig_hdr.tail) + return -EAGAIN; + sig_hdr.tail = (sig_hdr.tail + 1) % sig_hdr.max_slots; + error = sig_read_data(channel, queue, &sig_hdr, sig_hdr.tail, msg); + if (error) + return error; + sig_hdr.num_received++; + /* + * For each data field in SIGNAL_QUEUE_HEADER that was modified, update + * host memory. Required for channel sync. + */ + mb(); + error = SIG_WRITE_FIELD(channel, queue, &sig_hdr, tail); + if (error) + return error; + error = SIG_WRITE_FIELD(channel, queue, &sig_hdr, num_received); + if (error) + return error; + return 0; +} + +/** + * visorchannel_signalremove() - removes a message from the designated + * channel/queue + * @channel: the channel the message will be removed from + * @queue: the queue the message will be removed from + * @msg: the message to remove + * + * Return: integer error code indicating the status of the removal + */ +int visorchannel_signalremove(struct visorchannel *channel, u32 queue, + void *msg) +{ + int rc; + unsigned long flags; + + if (channel->needs_lock) { + spin_lock_irqsave(&channel->remove_lock, flags); + rc = signalremove_inner(channel, queue, msg); + spin_unlock_irqrestore(&channel->remove_lock, flags); + } else { + rc = signalremove_inner(channel, queue, msg); + } + + return rc; +} +EXPORT_SYMBOL_GPL(visorchannel_signalremove); + +static bool queue_empty(struct visorchannel *channel, u32 queue) +{ + struct signal_queue_header sig_hdr; + + if (sig_read_header(channel, queue, &sig_hdr)) + return true; + return (sig_hdr.head == sig_hdr.tail); +} + +/** + * visorchannel_signalempty() - checks if the designated channel/queue contains + * any messages + * @channel: the channel to query + * @queue: the queue in the channel to query + * + * Return: boolean indicating whether any messages in the designated + * channel/queue are present + */ +bool visorchannel_signalempty(struct visorchannel *channel, u32 queue) +{ + bool rc; + unsigned long flags; + + if (!channel->needs_lock) + return queue_empty(channel, queue); + spin_lock_irqsave(&channel->remove_lock, flags); + rc = queue_empty(channel, queue); + spin_unlock_irqrestore(&channel->remove_lock, flags); + return rc; +} +EXPORT_SYMBOL_GPL(visorchannel_signalempty); + +static int signalinsert_inner(struct visorchannel *channel, u32 queue, + void *msg) +{ + struct signal_queue_header sig_hdr; + int err; + + err = sig_read_header(channel, queue, &sig_hdr); + if (err) + return err; + sig_hdr.head = (sig_hdr.head + 1) % sig_hdr.max_slots; + if (sig_hdr.head == sig_hdr.tail) { + sig_hdr.num_overflows++; + err = SIG_WRITE_FIELD(channel, queue, &sig_hdr, num_overflows); + if (err) + return err; + return -EIO; + } + err = sig_write_data(channel, queue, &sig_hdr, sig_hdr.head, msg); + if (err) + return err; + sig_hdr.num_sent++; + /* + * For each data field in SIGNAL_QUEUE_HEADER that was modified, update + * host memory. Required for channel sync. + */ + mb(); + err = SIG_WRITE_FIELD(channel, queue, &sig_hdr, head); + if (err) + return err; + err = SIG_WRITE_FIELD(channel, queue, &sig_hdr, num_sent); + if (err) + return err; + return 0; +} + +/* + * visorchannel_create() - creates the struct visorchannel abstraction for a + * data area in memory, but does NOT modify this data + * area + * @physaddr: physical address of start of channel + * @gfp: gfp_t to use when allocating memory for the data struct + * @guid: GUID that identifies channel type; + * @needs_lock: must specify true if you have multiple threads of execution + * that will be calling visorchannel methods of this + * visorchannel at the same time + * + * Return: pointer to visorchannel that was created if successful, + * otherwise NULL + */ +struct visorchannel *visorchannel_create(u64 physaddr, gfp_t gfp, + const guid_t *guid, bool needs_lock) +{ + struct visorchannel *channel; + int err; + size_t size = sizeof(struct channel_header); + + if (physaddr == 0) + return NULL; + + channel = kzalloc(sizeof(*channel), gfp); + if (!channel) + return NULL; + channel->needs_lock = needs_lock; + spin_lock_init(&channel->insert_lock); + spin_lock_init(&channel->remove_lock); + /* + * Video driver constains the efi framebuffer so it will get a conflict + * resource when requesting its full mem region. Since we are only + * using the efi framebuffer for video we can ignore this. Remember that + * we haven't requested it so we don't try to release later on. + */ + channel->requested = request_mem_region(physaddr, size, VISOR_DRV_NAME); + if (!channel->requested && !guid_equal(guid, &visor_video_guid)) + /* we only care about errors if this is not the video channel */ + goto err_destroy_channel; + channel->mapped = memremap(physaddr, size, MEMREMAP_WB); + if (!channel->mapped) { + release_mem_region(physaddr, size); + goto err_destroy_channel; + } + channel->physaddr = physaddr; + channel->nbytes = size; + err = visorchannel_read(channel, 0, &channel->chan_hdr, size); + if (err) + goto err_destroy_channel; + size = (ulong)channel->chan_hdr.size; + memunmap(channel->mapped); + if (channel->requested) + release_mem_region(channel->physaddr, channel->nbytes); + channel->mapped = NULL; + channel->requested = request_mem_region(channel->physaddr, size, + VISOR_DRV_NAME); + if (!channel->requested && !guid_equal(guid, &visor_video_guid)) + /* we only care about errors if this is not the video channel */ + goto err_destroy_channel; + channel->mapped = memremap(channel->physaddr, size, MEMREMAP_WB); + if (!channel->mapped) { + release_mem_region(channel->physaddr, size); + goto err_destroy_channel; + } + channel->nbytes = size; + guid_copy(&channel->guid, guid); + return channel; + +err_destroy_channel: + visorchannel_destroy(channel); + return NULL; +} + +/** + * visorchannel_signalinsert() - inserts a message into the designated + * channel/queue + * @channel: the channel the message will be added to + * @queue: the queue the message will be added to + * @msg: the message to insert + * + * Return: integer error code indicating the status of the insertion + */ +int visorchannel_signalinsert(struct visorchannel *channel, u32 queue, + void *msg) +{ + int rc; + unsigned long flags; + + if (channel->needs_lock) { + spin_lock_irqsave(&channel->insert_lock, flags); + rc = signalinsert_inner(channel, queue, msg); + spin_unlock_irqrestore(&channel->insert_lock, flags); + } else { + rc = signalinsert_inner(channel, queue, msg); + } + + return rc; +} +EXPORT_SYMBOL_GPL(visorchannel_signalinsert); diff --git a/drivers/visorbus/visorchipset.c b/drivers/visorbus/visorchipset.c new file mode 100644 index 000000000000..ca752b8f495f --- /dev/null +++ b/drivers/visorbus/visorchipset.c @@ -0,0 +1,1686 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2010 - 2015 UNISYS CORPORATION + * All rights reserved. + */ + +#include +#include +#include + +#include "visorbus_private.h" + +/* {72120008-4AAB-11DC-8530-444553544200} */ +#define VISOR_SIOVM_GUID GUID_INIT(0x72120008, 0x4AAB, 0x11DC, 0x85, 0x30, \ + 0x44, 0x45, 0x53, 0x54, 0x42, 0x00) + +static const guid_t visor_vhba_channel_guid = VISOR_VHBA_CHANNEL_GUID; +static const guid_t visor_siovm_guid = VISOR_SIOVM_GUID; +static const guid_t visor_controlvm_channel_guid = VISOR_CONTROLVM_CHANNEL_GUID; + +#define POLLJIFFIES_CONTROLVM_FAST 1 +#define POLLJIFFIES_CONTROLVM_SLOW 100 + +#define MAX_CONTROLVM_PAYLOAD_BYTES (1024 * 128) + +#define UNISYS_VISOR_LEAF_ID 0x40000000 + +/* The s-Par leaf ID returns "UnisysSpar64" encoded across ebx, ecx, edx */ +#define UNISYS_VISOR_ID_EBX 0x73696e55 +#define UNISYS_VISOR_ID_ECX 0x70537379 +#define UNISYS_VISOR_ID_EDX 0x34367261 + +/* + * When the controlvm channel is idle for at least MIN_IDLE_SECONDS, we switch + * to slow polling mode. As soon as we get a controlvm message, we switch back + * to fast polling mode. + */ +#define MIN_IDLE_SECONDS 10 + +struct parser_context { + unsigned long allocbytes; + unsigned long param_bytes; + u8 *curr; + unsigned long bytes_remaining; + bool byte_stream; + struct visor_controlvm_parameters_header data; +}; + +/* VMCALL_CONTROLVM_ADDR: Used by all guests, not just IO. */ +#define VMCALL_CONTROLVM_ADDR 0x0501 + +enum vmcall_result { + VMCALL_RESULT_SUCCESS = 0, + VMCALL_RESULT_INVALID_PARAM = 1, + VMCALL_RESULT_DATA_UNAVAILABLE = 2, + VMCALL_RESULT_FAILURE_UNAVAILABLE = 3, + VMCALL_RESULT_DEVICE_ERROR = 4, + VMCALL_RESULT_DEVICE_NOT_READY = 5 +}; + +/* + * struct vmcall_io_controlvm_addr_params - Structure for IO VMCALLS. Has + * parameters to VMCALL_CONTROLVM_ADDR + * interface. + * @address: The Guest-relative physical address of the ControlVm channel. + * This VMCall fills this in with the appropriate address. + * Contents provided by this VMCALL (OUT). + * @channel_bytes: The size of the ControlVm channel in bytes This VMCall fills + * this in with the appropriate address. Contents provided by + * this VMCALL (OUT). + * @unused: Unused Bytes in the 64-Bit Aligned Struct. + */ +struct vmcall_io_controlvm_addr_params { + u64 address; + u32 channel_bytes; + u8 unused[4]; +} __packed; + +struct visorchipset_device { + struct acpi_device *acpi_device; + unsigned long poll_jiffies; + /* when we got our last controlvm message */ + unsigned long most_recent_message_jiffies; + struct delayed_work periodic_controlvm_work; + struct visorchannel *controlvm_channel; + unsigned long controlvm_payload_bytes_buffered; + /* + * The following variables are used to handle the scenario where we are + * unable to offload the payload from a controlvm message due to memory + * requirements. In this scenario, we simply stash the controlvm + * message, then attempt to process it again the next time + * controlvm_periodic_work() runs. + */ + struct controlvm_message controlvm_pending_msg; + bool controlvm_pending_msg_valid; + struct vmcall_io_controlvm_addr_params controlvm_params; +}; + +static struct visorchipset_device *chipset_dev; + +struct parahotplug_request { + struct list_head list; + int id; + unsigned long expiration; + struct controlvm_message msg; +}; + +/* prototypes for attributes */ +static ssize_t toolaction_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 tool_action = 0; + int err; + + err = visorchannel_read(chipset_dev->controlvm_channel, + offsetof(struct visor_controlvm_channel, + tool_action), + &tool_action, sizeof(u8)); + if (err) + return err; + return sprintf(buf, "%u\n", tool_action); +} + +static ssize_t toolaction_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u8 tool_action; + int err; + + if (kstrtou8(buf, 10, &tool_action)) + return -EINVAL; + err = visorchannel_write(chipset_dev->controlvm_channel, + offsetof(struct visor_controlvm_channel, + tool_action), + &tool_action, sizeof(u8)); + if (err) + return err; + return count; +} +static DEVICE_ATTR_RW(toolaction); + +static ssize_t boottotool_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct efi_visor_indication efi_visor_indication; + int err; + + err = visorchannel_read(chipset_dev->controlvm_channel, + offsetof(struct visor_controlvm_channel, + efi_visor_ind), + &efi_visor_indication, + sizeof(struct efi_visor_indication)); + if (err) + return err; + return sprintf(buf, "%u\n", efi_visor_indication.boot_to_tool); +} + +static ssize_t boottotool_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val, err; + struct efi_visor_indication efi_visor_indication; + + if (kstrtoint(buf, 10, &val)) + return -EINVAL; + efi_visor_indication.boot_to_tool = val; + err = visorchannel_write(chipset_dev->controlvm_channel, + offsetof(struct visor_controlvm_channel, + efi_visor_ind), + &(efi_visor_indication), + sizeof(struct efi_visor_indication)); + if (err) + return err; + return count; +} +static DEVICE_ATTR_RW(boottotool); + +static ssize_t error_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + u32 error = 0; + int err; + + err = visorchannel_read(chipset_dev->controlvm_channel, + offsetof(struct visor_controlvm_channel, + installation_error), + &error, sizeof(u32)); + if (err) + return err; + return sprintf(buf, "%u\n", error); +} + +static ssize_t error_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 error; + int err; + + if (kstrtou32(buf, 10, &error)) + return -EINVAL; + err = visorchannel_write(chipset_dev->controlvm_channel, + offsetof(struct visor_controlvm_channel, + installation_error), + &error, sizeof(u32)); + if (err) + return err; + return count; +} +static DEVICE_ATTR_RW(error); + +static ssize_t textid_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + u32 text_id = 0; + int err; + + err = visorchannel_read(chipset_dev->controlvm_channel, + offsetof(struct visor_controlvm_channel, + installation_text_id), + &text_id, sizeof(u32)); + if (err) + return err; + return sprintf(buf, "%u\n", text_id); +} + +static ssize_t textid_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 text_id; + int err; + + if (kstrtou32(buf, 10, &text_id)) + return -EINVAL; + err = visorchannel_write(chipset_dev->controlvm_channel, + offsetof(struct visor_controlvm_channel, + installation_text_id), + &text_id, sizeof(u32)); + if (err) + return err; + return count; +} +static DEVICE_ATTR_RW(textid); + +static ssize_t remaining_steps_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u16 remaining_steps = 0; + int err; + + err = visorchannel_read(chipset_dev->controlvm_channel, + offsetof(struct visor_controlvm_channel, + installation_remaining_steps), + &remaining_steps, sizeof(u16)); + if (err) + return err; + return sprintf(buf, "%hu\n", remaining_steps); +} + +static ssize_t remaining_steps_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u16 remaining_steps; + int err; + + if (kstrtou16(buf, 10, &remaining_steps)) + return -EINVAL; + err = visorchannel_write(chipset_dev->controlvm_channel, + offsetof(struct visor_controlvm_channel, + installation_remaining_steps), + &remaining_steps, sizeof(u16)); + if (err) + return err; + return count; +} +static DEVICE_ATTR_RW(remaining_steps); + +static void controlvm_init_response(struct controlvm_message *msg, + struct controlvm_message_header *msg_hdr, + int response) +{ + memset(msg, 0, sizeof(struct controlvm_message)); + memcpy(&msg->hdr, msg_hdr, sizeof(struct controlvm_message_header)); + msg->hdr.payload_bytes = 0; + msg->hdr.payload_vm_offset = 0; + msg->hdr.payload_max_bytes = 0; + if (response < 0) { + msg->hdr.flags.failed = 1; + msg->hdr.completion_status = (u32)(-response); + } +} + +static int controlvm_respond_chipset_init( + struct controlvm_message_header *msg_hdr, + int response, + enum visor_chipset_feature features) +{ + struct controlvm_message outmsg; + + controlvm_init_response(&outmsg, msg_hdr, response); + outmsg.cmd.init_chipset.features = features; + return visorchannel_signalinsert(chipset_dev->controlvm_channel, + CONTROLVM_QUEUE_REQUEST, &outmsg); +} + +static int chipset_init(struct controlvm_message *inmsg) +{ + static int chipset_inited; + enum visor_chipset_feature features = 0; + int rc = CONTROLVM_RESP_SUCCESS; + int res = 0; + + if (chipset_inited) { + rc = -CONTROLVM_RESP_ALREADY_DONE; + res = -EIO; + goto out_respond; + } + chipset_inited = 1; + /* + * Set features to indicate we support parahotplug (if Command also + * supports it). Set the "reply" bit so Command knows this is a + * features-aware driver. + */ + features = inmsg->cmd.init_chipset.features & + VISOR_CHIPSET_FEATURE_PARA_HOTPLUG; + features |= VISOR_CHIPSET_FEATURE_REPLY; + +out_respond: + if (inmsg->hdr.flags.response_expected) + res = controlvm_respond_chipset_init(&inmsg->hdr, rc, features); + + return res; +} + +static int controlvm_respond(struct controlvm_message_header *msg_hdr, + int response, struct visor_segment_state *state) +{ + struct controlvm_message outmsg; + + controlvm_init_response(&outmsg, msg_hdr, response); + if (outmsg.hdr.flags.test_message == 1) + return -EINVAL; + if (state) { + outmsg.cmd.device_change_state.state = *state; + outmsg.cmd.device_change_state.flags.phys_device = 1; + } + return visorchannel_signalinsert(chipset_dev->controlvm_channel, + CONTROLVM_QUEUE_REQUEST, &outmsg); +} + +enum crash_obj_type { + CRASH_DEV, + CRASH_BUS, +}; + +static int save_crash_message(struct controlvm_message *msg, + enum crash_obj_type cr_type) +{ + u32 local_crash_msg_offset; + u16 local_crash_msg_count; + int err; + + err = visorchannel_read(chipset_dev->controlvm_channel, + offsetof(struct visor_controlvm_channel, + saved_crash_message_count), + &local_crash_msg_count, sizeof(u16)); + if (err) { + dev_err(&chipset_dev->acpi_device->dev, + "failed to read message count\n"); + return err; + } + if (local_crash_msg_count != CONTROLVM_CRASHMSG_MAX) { + dev_err(&chipset_dev->acpi_device->dev, + "invalid number of messages\n"); + return -EIO; + } + err = visorchannel_read(chipset_dev->controlvm_channel, + offsetof(struct visor_controlvm_channel, + saved_crash_message_offset), + &local_crash_msg_offset, sizeof(u32)); + if (err) { + dev_err(&chipset_dev->acpi_device->dev, + "failed to read offset\n"); + return err; + } + switch (cr_type) { + case CRASH_DEV: + local_crash_msg_offset += sizeof(struct controlvm_message); + err = visorchannel_write(chipset_dev->controlvm_channel, + local_crash_msg_offset, msg, + sizeof(struct controlvm_message)); + if (err) { + dev_err(&chipset_dev->acpi_device->dev, + "failed to write dev msg\n"); + return err; + } + break; + case CRASH_BUS: + err = visorchannel_write(chipset_dev->controlvm_channel, + local_crash_msg_offset, msg, + sizeof(struct controlvm_message)); + if (err) { + dev_err(&chipset_dev->acpi_device->dev, + "failed to write bus msg\n"); + return err; + } + break; + default: + dev_err(&chipset_dev->acpi_device->dev, + "Invalid crash_obj_type\n"); + break; + } + return 0; +} + +static int controlvm_responder(enum controlvm_id cmd_id, + struct controlvm_message_header *pending_msg_hdr, + int response) +{ + if (pending_msg_hdr->id != (u32)cmd_id) + return -EINVAL; + + return controlvm_respond(pending_msg_hdr, response, NULL); +} + +static int device_changestate_responder(enum controlvm_id cmd_id, + struct visor_device *p, int response, + struct visor_segment_state state) +{ + struct controlvm_message outmsg; + + if (p->pending_msg_hdr->id != cmd_id) + return -EINVAL; + + controlvm_init_response(&outmsg, p->pending_msg_hdr, response); + outmsg.cmd.device_change_state.bus_no = p->chipset_bus_no; + outmsg.cmd.device_change_state.dev_no = p->chipset_dev_no; + outmsg.cmd.device_change_state.state = state; + return visorchannel_signalinsert(chipset_dev->controlvm_channel, + CONTROLVM_QUEUE_REQUEST, &outmsg); +} + +static int visorbus_create(struct controlvm_message *inmsg) +{ + struct controlvm_message_packet *cmd = &inmsg->cmd; + struct controlvm_message_header *pmsg_hdr; + u32 bus_no = cmd->create_bus.bus_no; + struct visor_device *bus_info; + struct visorchannel *visorchannel; + int err; + + bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); + if (bus_info && bus_info->state.created == 1) { + dev_err(&chipset_dev->acpi_device->dev, + "failed %s: already exists\n", __func__); + err = -EEXIST; + goto err_respond; + } + bus_info = kzalloc(sizeof(*bus_info), GFP_KERNEL); + if (!bus_info) { + err = -ENOMEM; + goto err_respond; + } + INIT_LIST_HEAD(&bus_info->list_all); + bus_info->chipset_bus_no = bus_no; + bus_info->chipset_dev_no = BUS_ROOT_DEVICE; + if (guid_equal(&cmd->create_bus.bus_inst_guid, &visor_siovm_guid)) { + err = save_crash_message(inmsg, CRASH_BUS); + if (err) + goto err_free_bus_info; + } + if (inmsg->hdr.flags.response_expected == 1) { + pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL); + if (!pmsg_hdr) { + err = -ENOMEM; + goto err_free_bus_info; + } + memcpy(pmsg_hdr, &inmsg->hdr, + sizeof(struct controlvm_message_header)); + bus_info->pending_msg_hdr = pmsg_hdr; + } + visorchannel = visorchannel_create(cmd->create_bus.channel_addr, + GFP_KERNEL, + &cmd->create_bus.bus_data_type_guid, + false); + if (!visorchannel) { + err = -ENOMEM; + goto err_free_pending_msg; + } + bus_info->visorchannel = visorchannel; + /* Response will be handled by visorbus_create_instance on success */ + err = visorbus_create_instance(bus_info); + if (err) + goto err_destroy_channel; + return 0; + +err_destroy_channel: + visorchannel_destroy(visorchannel); + +err_free_pending_msg: + kfree(bus_info->pending_msg_hdr); + +err_free_bus_info: + kfree(bus_info); + +err_respond: + if (inmsg->hdr.flags.response_expected == 1) + controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err); + return err; +} + +static int visorbus_destroy(struct controlvm_message *inmsg) +{ + struct controlvm_message_header *pmsg_hdr; + u32 bus_no = inmsg->cmd.destroy_bus.bus_no; + struct visor_device *bus_info; + int err; + + bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); + if (!bus_info) { + err = -ENODEV; + goto err_respond; + } + if (bus_info->state.created == 0) { + err = -ENOENT; + goto err_respond; + } + if (bus_info->pending_msg_hdr) { + /* only non-NULL if dev is still waiting on a response */ + err = -EEXIST; + goto err_respond; + } + if (inmsg->hdr.flags.response_expected == 1) { + pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL); + if (!pmsg_hdr) { + err = -ENOMEM; + goto err_respond; + } + memcpy(pmsg_hdr, &inmsg->hdr, + sizeof(struct controlvm_message_header)); + bus_info->pending_msg_hdr = pmsg_hdr; + } + /* Response will be handled by visorbus_remove_instance */ + visorbus_remove_instance(bus_info); + return 0; + +err_respond: + if (inmsg->hdr.flags.response_expected == 1) + controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err); + return err; +} + +static const guid_t *parser_id_get(struct parser_context *ctx) +{ + return &ctx->data.id; +} + +static void *parser_string_get(u8 *pscan, int nscan) +{ + int value_length; + void *value; + + if (nscan == 0) + return NULL; + + value_length = strnlen(pscan, nscan); + value = kzalloc(value_length + 1, GFP_KERNEL); + if (!value) + return NULL; + if (value_length > 0) + memcpy(value, pscan, value_length); + return value; +} + +static void *parser_name_get(struct parser_context *ctx) +{ + struct visor_controlvm_parameters_header *phdr; + + phdr = &ctx->data; + if ((unsigned long)phdr->name_offset + + (unsigned long)phdr->name_length > ctx->param_bytes) + return NULL; + ctx->curr = (char *)&phdr + phdr->name_offset; + ctx->bytes_remaining = phdr->name_length; + return parser_string_get(ctx->curr, phdr->name_length); +} + +static int visorbus_configure(struct controlvm_message *inmsg, + struct parser_context *parser_ctx) +{ + struct controlvm_message_packet *cmd = &inmsg->cmd; + u32 bus_no; + struct visor_device *bus_info; + int err = 0; + + bus_no = cmd->configure_bus.bus_no; + bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); + if (!bus_info) { + err = -EINVAL; + goto err_respond; + } + if (bus_info->state.created == 0) { + err = -EINVAL; + goto err_respond; + } + if (bus_info->pending_msg_hdr) { + err = -EIO; + goto err_respond; + } + err = visorchannel_set_clientpartition(bus_info->visorchannel, + cmd->configure_bus.guest_handle); + if (err) + goto err_respond; + if (parser_ctx) { + const guid_t *partition_guid = parser_id_get(parser_ctx); + + guid_copy(&bus_info->partition_guid, partition_guid); + bus_info->name = parser_name_get(parser_ctx); + } + if (inmsg->hdr.flags.response_expected == 1) + controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err); + return 0; + +err_respond: + dev_err(&chipset_dev->acpi_device->dev, + "%s exited with err: %d\n", __func__, err); + if (inmsg->hdr.flags.response_expected == 1) + controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err); + return err; +} + +static int visorbus_device_create(struct controlvm_message *inmsg) +{ + struct controlvm_message_packet *cmd = &inmsg->cmd; + struct controlvm_message_header *pmsg_hdr; + u32 bus_no = cmd->create_device.bus_no; + u32 dev_no = cmd->create_device.dev_no; + struct visor_device *dev_info; + struct visor_device *bus_info; + struct visorchannel *visorchannel; + int err; + + bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); + if (!bus_info) { + dev_err(&chipset_dev->acpi_device->dev, + "failed to get bus by id: %d\n", bus_no); + err = -ENODEV; + goto err_respond; + } + if (bus_info->state.created == 0) { + dev_err(&chipset_dev->acpi_device->dev, + "bus not created, id: %d\n", bus_no); + err = -EINVAL; + goto err_respond; + } + dev_info = visorbus_get_device_by_id(bus_no, dev_no, NULL); + if (dev_info && dev_info->state.created == 1) { + dev_err(&chipset_dev->acpi_device->dev, + "failed to get bus by id: %d/%d\n", bus_no, dev_no); + err = -EEXIST; + goto err_respond; + } + + dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL); + if (!dev_info) { + err = -ENOMEM; + goto err_respond; + } + dev_info->chipset_bus_no = bus_no; + dev_info->chipset_dev_no = dev_no; + guid_copy(&dev_info->inst, &cmd->create_device.dev_inst_guid); + dev_info->device.parent = &bus_info->device; + visorchannel = visorchannel_create(cmd->create_device.channel_addr, + GFP_KERNEL, + &cmd->create_device.data_type_guid, + true); + if (!visorchannel) { + dev_err(&chipset_dev->acpi_device->dev, + "failed to create visorchannel: %d/%d\n", + bus_no, dev_no); + err = -ENOMEM; + goto err_free_dev_info; + } + dev_info->visorchannel = visorchannel; + guid_copy(&dev_info->channel_type_guid, + &cmd->create_device.data_type_guid); + if (guid_equal(&cmd->create_device.data_type_guid, + &visor_vhba_channel_guid)) { + err = save_crash_message(inmsg, CRASH_DEV); + if (err) + goto err_destroy_visorchannel; + } + if (inmsg->hdr.flags.response_expected == 1) { + pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL); + if (!pmsg_hdr) { + err = -ENOMEM; + goto err_destroy_visorchannel; + } + memcpy(pmsg_hdr, &inmsg->hdr, + sizeof(struct controlvm_message_header)); + dev_info->pending_msg_hdr = pmsg_hdr; + } + /* create_visor_device will send response */ + err = create_visor_device(dev_info); + if (err) + goto err_destroy_visorchannel; + + return 0; + +err_destroy_visorchannel: + visorchannel_destroy(visorchannel); + +err_free_dev_info: + kfree(dev_info); + +err_respond: + if (inmsg->hdr.flags.response_expected == 1) + controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err); + return err; +} + +static int visorbus_device_changestate(struct controlvm_message *inmsg) +{ + struct controlvm_message_packet *cmd = &inmsg->cmd; + struct controlvm_message_header *pmsg_hdr; + u32 bus_no = cmd->device_change_state.bus_no; + u32 dev_no = cmd->device_change_state.dev_no; + struct visor_segment_state state = cmd->device_change_state.state; + struct visor_device *dev_info; + int err = 0; + + dev_info = visorbus_get_device_by_id(bus_no, dev_no, NULL); + if (!dev_info) { + err = -ENODEV; + goto err_respond; + } + if (dev_info->state.created == 0) { + err = -EINVAL; + goto err_respond; + } + if (dev_info->pending_msg_hdr) { + /* only non-NULL if dev is still waiting on a response */ + err = -EIO; + goto err_respond; + } + + if (inmsg->hdr.flags.response_expected == 1) { + pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL); + if (!pmsg_hdr) { + err = -ENOMEM; + goto err_respond; + } + memcpy(pmsg_hdr, &inmsg->hdr, + sizeof(struct controlvm_message_header)); + dev_info->pending_msg_hdr = pmsg_hdr; + } + if (state.alive == segment_state_running.alive && + state.operating == segment_state_running.operating) + /* Response will be sent from visorchipset_device_resume */ + err = visorchipset_device_resume(dev_info); + /* ServerNotReady / ServerLost / SegmentStateStandby */ + else if (state.alive == segment_state_standby.alive && + state.operating == segment_state_standby.operating) + /* + * technically this is standby case where server is lost. + * Response will be sent from visorchipset_device_pause. + */ + err = visorchipset_device_pause(dev_info); + if (err) + goto err_respond; + return 0; + +err_respond: + dev_err(&chipset_dev->acpi_device->dev, "failed: %d\n", err); + if (inmsg->hdr.flags.response_expected == 1) + controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err); + return err; +} + +static int visorbus_device_destroy(struct controlvm_message *inmsg) +{ + struct controlvm_message_packet *cmd = &inmsg->cmd; + struct controlvm_message_header *pmsg_hdr; + u32 bus_no = cmd->destroy_device.bus_no; + u32 dev_no = cmd->destroy_device.dev_no; + struct visor_device *dev_info; + int err; + + dev_info = visorbus_get_device_by_id(bus_no, dev_no, NULL); + if (!dev_info) { + err = -ENODEV; + goto err_respond; + } + if (dev_info->state.created == 0) { + err = -EINVAL; + goto err_respond; + } + if (dev_info->pending_msg_hdr) { + /* only non-NULL if dev is still waiting on a response */ + err = -EIO; + goto err_respond; + } + if (inmsg->hdr.flags.response_expected == 1) { + pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL); + if (!pmsg_hdr) { + err = -ENOMEM; + goto err_respond; + } + + memcpy(pmsg_hdr, &inmsg->hdr, + sizeof(struct controlvm_message_header)); + dev_info->pending_msg_hdr = pmsg_hdr; + } + kfree(dev_info->name); + remove_visor_device(dev_info); + return 0; + +err_respond: + if (inmsg->hdr.flags.response_expected == 1) + controlvm_responder(inmsg->hdr.id, &inmsg->hdr, err); + return err; +} + +/* + * The general parahotplug flow works as follows. The visorchipset receives + * a DEVICE_CHANGESTATE message from Command specifying a physical device + * to enable or disable. The CONTROLVM message handler calls + * parahotplug_process_message, which then adds the message to a global list + * and kicks off a udev event which causes a user level script to enable or + * disable the specified device. The udev script then writes to + * /sys/devices/platform/visorchipset/parahotplug, which causes the + * parahotplug store functions to get called, at which point the + * appropriate CONTROLVM message is retrieved from the list and responded to. + */ + +#define PARAHOTPLUG_TIMEOUT_MS 2000 + +/* + * parahotplug_next_id() - generate unique int to match an outstanding + * CONTROLVM message with a udev script /sys + * response + * + * Return: a unique integer value + */ +static int parahotplug_next_id(void) +{ + static atomic_t id = ATOMIC_INIT(0); + + return atomic_inc_return(&id); +} + +/* + * parahotplug_next_expiration() - returns the time (in jiffies) when a + * CONTROLVM message on the list should expire + * -- PARAHOTPLUG_TIMEOUT_MS in the future + * + * Return: expected expiration time (in jiffies) + */ +static unsigned long parahotplug_next_expiration(void) +{ + return jiffies + msecs_to_jiffies(PARAHOTPLUG_TIMEOUT_MS); +} + +/* + * parahotplug_request_create() - create a parahotplug_request, which is + * basically a wrapper for a CONTROLVM_MESSAGE + * that we can stick on a list + * @msg: the message to insert in the request + * + * Return: the request containing the provided message + */ +static struct parahotplug_request *parahotplug_request_create( + struct controlvm_message *msg) +{ + struct parahotplug_request *req; + + req = kmalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return NULL; + req->id = parahotplug_next_id(); + req->expiration = parahotplug_next_expiration(); + req->msg = *msg; + return req; +} + +/* + * parahotplug_request_destroy() - free a parahotplug_request + * @req: the request to deallocate + */ +static void parahotplug_request_destroy(struct parahotplug_request *req) +{ + kfree(req); +} + +static LIST_HEAD(parahotplug_request_list); +/* lock for above */ +static DEFINE_SPINLOCK(parahotplug_request_list_lock); + +/* + * parahotplug_request_complete() - mark request as complete + * @id: the id of the request + * @active: indicates whether the request is assigned to active partition + * + * Called from the /sys handler, which means the user script has + * finished the enable/disable. Find the matching identifier, and + * respond to the CONTROLVM message with success. + * + * Return: 0 on success or -EINVAL on failure + */ +static int parahotplug_request_complete(int id, u16 active) +{ + struct list_head *pos; + struct list_head *tmp; + struct parahotplug_request *req; + + spin_lock(¶hotplug_request_list_lock); + /* Look for a request matching "id". */ + list_for_each_safe(pos, tmp, ¶hotplug_request_list) { + req = list_entry(pos, struct parahotplug_request, list); + if (req->id == id) { + /* + * Found a match. Remove it from the list and + * respond. + */ + list_del(pos); + spin_unlock(¶hotplug_request_list_lock); + req->msg.cmd.device_change_state.state.active = active; + if (req->msg.hdr.flags.response_expected) + controlvm_respond( + &req->msg.hdr, CONTROLVM_RESP_SUCCESS, + &req->msg.cmd.device_change_state.state); + parahotplug_request_destroy(req); + return 0; + } + } + spin_unlock(¶hotplug_request_list_lock); + return -EINVAL; +} + +/* + * devicedisabled_store() - disables the hotplug device + * @dev: sysfs interface variable not utilized in this function + * @attr: sysfs interface variable not utilized in this function + * @buf: buffer containing the device id + * @count: the size of the buffer + * + * The parahotplug/devicedisabled interface gets called by our support script + * when an SR-IOV device has been shut down. The ID is passed to the script + * and then passed back when the device has been removed. + * + * Return: the size of the buffer for success or negative for error + */ +static ssize_t devicedisabled_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int id; + int err; + + if (kstrtouint(buf, 10, &id)) + return -EINVAL; + err = parahotplug_request_complete(id, 0); + if (err < 0) + return err; + return count; +} +static DEVICE_ATTR_WO(devicedisabled); + +/* + * deviceenabled_store() - enables the hotplug device + * @dev: sysfs interface variable not utilized in this function + * @attr: sysfs interface variable not utilized in this function + * @buf: buffer containing the device id + * @count: the size of the buffer + * + * The parahotplug/deviceenabled interface gets called by our support script + * when an SR-IOV device has been recovered. The ID is passed to the script + * and then passed back when the device has been brought back up. + * + * Return: the size of the buffer for success or negative for error + */ +static ssize_t deviceenabled_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int id; + + if (kstrtouint(buf, 10, &id)) + return -EINVAL; + parahotplug_request_complete(id, 1); + return count; +} +static DEVICE_ATTR_WO(deviceenabled); + +static struct attribute *visorchipset_install_attrs[] = { + &dev_attr_toolaction.attr, + &dev_attr_boottotool.attr, + &dev_attr_error.attr, + &dev_attr_textid.attr, + &dev_attr_remaining_steps.attr, + NULL +}; + +static const struct attribute_group visorchipset_install_group = { + .name = "install", + .attrs = visorchipset_install_attrs +}; + +static struct attribute *visorchipset_parahotplug_attrs[] = { + &dev_attr_devicedisabled.attr, + &dev_attr_deviceenabled.attr, + NULL +}; + +static const struct attribute_group visorchipset_parahotplug_group = { + .name = "parahotplug", + .attrs = visorchipset_parahotplug_attrs +}; + +static const struct attribute_group *visorchipset_dev_groups[] = { + &visorchipset_install_group, + &visorchipset_parahotplug_group, + NULL +}; + +/* + * parahotplug_request_kickoff() - initiate parahotplug request + * @req: the request to initiate + * + * Cause uevent to run the user level script to do the disable/enable specified + * in the parahotplug_request. + */ +static int parahotplug_request_kickoff(struct parahotplug_request *req) +{ + struct controlvm_message_packet *cmd = &req->msg.cmd; + char env_cmd[40], env_id[40], env_state[40], env_bus[40], env_dev[40], + env_func[40]; + char *envp[] = { env_cmd, env_id, env_state, env_bus, env_dev, + env_func, NULL + }; + + sprintf(env_cmd, "VISOR_PARAHOTPLUG=1"); + sprintf(env_id, "VISOR_PARAHOTPLUG_ID=%d", req->id); + sprintf(env_state, "VISOR_PARAHOTPLUG_STATE=%d", + cmd->device_change_state.state.active); + sprintf(env_bus, "VISOR_PARAHOTPLUG_BUS=%d", + cmd->device_change_state.bus_no); + sprintf(env_dev, "VISOR_PARAHOTPLUG_DEVICE=%d", + cmd->device_change_state.dev_no >> 3); + sprintf(env_func, "VISOR_PARAHOTPLUG_FUNCTION=%d", + cmd->device_change_state.dev_no & 0x7); + return kobject_uevent_env(&chipset_dev->acpi_device->dev.kobj, + KOBJ_CHANGE, envp); +} + +/* + * parahotplug_process_message() - enables or disables a PCI device by kicking + * off a udev script + * @inmsg: the message indicating whether to enable or disable + */ +static int parahotplug_process_message(struct controlvm_message *inmsg) +{ + struct parahotplug_request *req; + int err; + + req = parahotplug_request_create(inmsg); + if (!req) + return -ENOMEM; + /* + * For enable messages, just respond with success right away, we don't + * need to wait to see if the enable was successful. + */ + if (inmsg->cmd.device_change_state.state.active) { + err = parahotplug_request_kickoff(req); + if (err) + goto err_respond; + controlvm_respond(&inmsg->hdr, CONTROLVM_RESP_SUCCESS, + &inmsg->cmd.device_change_state.state); + parahotplug_request_destroy(req); + return 0; + } + /* + * For disable messages, add the request to the request list before + * kicking off the udev script. It won't get responded to until the + * script has indicated it's done. + */ + spin_lock(¶hotplug_request_list_lock); + list_add_tail(&req->list, ¶hotplug_request_list); + spin_unlock(¶hotplug_request_list_lock); + err = parahotplug_request_kickoff(req); + if (err) + goto err_respond; + return 0; + +err_respond: + controlvm_respond(&inmsg->hdr, err, + &inmsg->cmd.device_change_state.state); + return err; +} + +/* + * chipset_ready_uevent() - sends chipset_ready action + * + * Send ACTION=online for DEVPATH=/sys/devices/platform/visorchipset. + * + * Return: 0 on success, negative on failure + */ +static int chipset_ready_uevent(struct controlvm_message_header *msg_hdr) +{ + int res; + + res = kobject_uevent(&chipset_dev->acpi_device->dev.kobj, KOBJ_ONLINE); + if (msg_hdr->flags.response_expected) + controlvm_respond(msg_hdr, res, NULL); + return res; +} + +/* + * chipset_selftest_uevent() - sends chipset_selftest action + * + * Send ACTION=online for DEVPATH=/sys/devices/platform/visorchipset. + * + * Return: 0 on success, negative on failure + */ +static int chipset_selftest_uevent(struct controlvm_message_header *msg_hdr) +{ + char env_selftest[20]; + char *envp[] = { env_selftest, NULL }; + int res; + + sprintf(env_selftest, "SPARSP_SELFTEST=%d", 1); + res = kobject_uevent_env(&chipset_dev->acpi_device->dev.kobj, + KOBJ_CHANGE, envp); + if (msg_hdr->flags.response_expected) + controlvm_respond(msg_hdr, res, NULL); + return res; +} + +/* + * chipset_notready_uevent() - sends chipset_notready action + * + * Send ACTION=offline for DEVPATH=/sys/devices/platform/visorchipset. + * + * Return: 0 on success, negative on failure + */ +static int chipset_notready_uevent(struct controlvm_message_header *msg_hdr) +{ + int res = kobject_uevent(&chipset_dev->acpi_device->dev.kobj, + KOBJ_OFFLINE); + + if (msg_hdr->flags.response_expected) + controlvm_respond(msg_hdr, res, NULL); + return res; +} + +static int unisys_vmcall(unsigned long tuple, unsigned long param) +{ + int result = 0; + unsigned int cpuid_eax, cpuid_ebx, cpuid_ecx, cpuid_edx; + unsigned long reg_ebx; + unsigned long reg_ecx; + + reg_ebx = param & 0xFFFFFFFF; + reg_ecx = param >> 32; + cpuid(0x00000001, &cpuid_eax, &cpuid_ebx, &cpuid_ecx, &cpuid_edx); + if (!(cpuid_ecx & 0x80000000)) + return -EPERM; + __asm__ __volatile__(".byte 0x00f, 0x001, 0x0c1" : "=a"(result) : + "a"(tuple), "b"(reg_ebx), "c"(reg_ecx)); + if (result) + goto error; + return 0; + +/* Need to convert from VMCALL error codes to Linux */ +error: + switch (result) { + case VMCALL_RESULT_INVALID_PARAM: + return -EINVAL; + case VMCALL_RESULT_DATA_UNAVAILABLE: + return -ENODEV; + default: + return -EFAULT; + } +} + +static int controlvm_channel_create(struct visorchipset_device *dev) +{ + struct visorchannel *chan; + u64 addr; + int err; + + err = unisys_vmcall(VMCALL_CONTROLVM_ADDR, + virt_to_phys(&dev->controlvm_params)); + if (err) + return err; + addr = dev->controlvm_params.address; + chan = visorchannel_create(addr, GFP_KERNEL, + &visor_controlvm_channel_guid, true); + if (!chan) + return -ENOMEM; + dev->controlvm_channel = chan; + return 0; +} + +static void setup_crash_devices_work_queue(struct work_struct *work) +{ + struct controlvm_message local_crash_bus_msg; + struct controlvm_message local_crash_dev_msg; + struct controlvm_message msg; + u32 local_crash_msg_offset; + u16 local_crash_msg_count; + + /* send init chipset msg */ + msg.hdr.id = CONTROLVM_CHIPSET_INIT; + msg.cmd.init_chipset.bus_count = 23; + msg.cmd.init_chipset.switch_count = 0; + chipset_init(&msg); + /* get saved message count */ + if (visorchannel_read(chipset_dev->controlvm_channel, + offsetof(struct visor_controlvm_channel, + saved_crash_message_count), + &local_crash_msg_count, sizeof(u16)) < 0) { + dev_err(&chipset_dev->acpi_device->dev, + "failed to read channel\n"); + return; + } + if (local_crash_msg_count != CONTROLVM_CRASHMSG_MAX) { + dev_err(&chipset_dev->acpi_device->dev, "invalid count\n"); + return; + } + /* get saved crash message offset */ + if (visorchannel_read(chipset_dev->controlvm_channel, + offsetof(struct visor_controlvm_channel, + saved_crash_message_offset), + &local_crash_msg_offset, sizeof(u32)) < 0) { + dev_err(&chipset_dev->acpi_device->dev, + "failed to read channel\n"); + return; + } + /* read create device message for storage bus offset */ + if (visorchannel_read(chipset_dev->controlvm_channel, + local_crash_msg_offset, + &local_crash_bus_msg, + sizeof(struct controlvm_message)) < 0) { + dev_err(&chipset_dev->acpi_device->dev, + "failed to read channel\n"); + return; + } + /* read create device message for storage device */ + if (visorchannel_read(chipset_dev->controlvm_channel, + local_crash_msg_offset + + sizeof(struct controlvm_message), + &local_crash_dev_msg, + sizeof(struct controlvm_message)) < 0) { + dev_err(&chipset_dev->acpi_device->dev, + "failed to read channel\n"); + return; + } + /* reuse IOVM create bus message */ + if (!local_crash_bus_msg.cmd.create_bus.channel_addr) { + dev_err(&chipset_dev->acpi_device->dev, + "no valid create_bus message\n"); + return; + } + visorbus_create(&local_crash_bus_msg); + /* reuse create device message for storage device */ + if (!local_crash_dev_msg.cmd.create_device.channel_addr) { + dev_err(&chipset_dev->acpi_device->dev, + "no valid create_device message\n"); + return; + } + visorbus_device_create(&local_crash_dev_msg); +} + +void visorbus_response(struct visor_device *bus_info, int response, + int controlvm_id) +{ + if (!bus_info->pending_msg_hdr) + return; + + controlvm_responder(controlvm_id, bus_info->pending_msg_hdr, response); + kfree(bus_info->pending_msg_hdr); + bus_info->pending_msg_hdr = NULL; +} + +void visorbus_device_changestate_response(struct visor_device *dev_info, + int response, + struct visor_segment_state state) +{ + if (!dev_info->pending_msg_hdr) + return; + + device_changestate_responder(CONTROLVM_DEVICE_CHANGESTATE, dev_info, + response, state); + kfree(dev_info->pending_msg_hdr); + dev_info->pending_msg_hdr = NULL; +} + +static void parser_done(struct parser_context *ctx) +{ + chipset_dev->controlvm_payload_bytes_buffered -= ctx->param_bytes; + kfree(ctx); +} + +static struct parser_context *parser_init_stream(u64 addr, u32 bytes, + bool *retry) +{ + unsigned long allocbytes; + struct parser_context *ctx; + void *mapping; + + *retry = false; + /* alloc an extra byte to ensure payload is \0 terminated */ + allocbytes = (unsigned long)bytes + 1 + (sizeof(struct parser_context) - + sizeof(struct visor_controlvm_parameters_header)); + if ((chipset_dev->controlvm_payload_bytes_buffered + bytes) > + MAX_CONTROLVM_PAYLOAD_BYTES) { + *retry = true; + return NULL; + } + ctx = kzalloc(allocbytes, GFP_KERNEL); + if (!ctx) { + *retry = true; + return NULL; + } + ctx->allocbytes = allocbytes; + ctx->param_bytes = bytes; + mapping = memremap(addr, bytes, MEMREMAP_WB); + if (!mapping) + goto err_finish_ctx; + memcpy(&ctx->data, mapping, bytes); + memunmap(mapping); + ctx->byte_stream = true; + chipset_dev->controlvm_payload_bytes_buffered += ctx->param_bytes; + return ctx; + +err_finish_ctx: + kfree(ctx); + return NULL; +} + +/* + * handle_command() - process a controlvm message + * @inmsg: the message to process + * @channel_addr: address of the controlvm channel + * + * Return: + * 0 - Successfully processed the message + * -EAGAIN - ControlVM message was not processed and should be retried + * reading the next controlvm message; a scenario where this can + * occur is when we need to throttle the allocation of memory in + * which to copy out controlvm payload data. + * < 0 - error: ControlVM message was processed but an error occurred. + */ +static int handle_command(struct controlvm_message inmsg, u64 channel_addr) +{ + struct controlvm_message_packet *cmd = &inmsg.cmd; + u64 parm_addr; + u32 parm_bytes; + struct parser_context *parser_ctx = NULL; + struct controlvm_message ackmsg; + int err = 0; + + /* create parsing context if necessary */ + parm_addr = channel_addr + inmsg.hdr.payload_vm_offset; + parm_bytes = inmsg.hdr.payload_bytes; + /* + * Parameter and channel addresses within test messages actually lie + * within our OS-controlled memory. We need to know that, because it + * makes a difference in how we compute the virtual address. + */ + if (parm_bytes) { + bool retry; + + parser_ctx = parser_init_stream(parm_addr, parm_bytes, &retry); + if (!parser_ctx && retry) + return -EAGAIN; + } + controlvm_init_response(&ackmsg, &inmsg.hdr, CONTROLVM_RESP_SUCCESS); + err = visorchannel_signalinsert(chipset_dev->controlvm_channel, + CONTROLVM_QUEUE_ACK, &ackmsg); + if (err) + return err; + switch (inmsg.hdr.id) { + case CONTROLVM_CHIPSET_INIT: + err = chipset_init(&inmsg); + break; + case CONTROLVM_BUS_CREATE: + err = visorbus_create(&inmsg); + break; + case CONTROLVM_BUS_DESTROY: + err = visorbus_destroy(&inmsg); + break; + case CONTROLVM_BUS_CONFIGURE: + err = visorbus_configure(&inmsg, parser_ctx); + break; + case CONTROLVM_DEVICE_CREATE: + err = visorbus_device_create(&inmsg); + break; + case CONTROLVM_DEVICE_CHANGESTATE: + if (cmd->device_change_state.flags.phys_device) { + err = parahotplug_process_message(&inmsg); + } else { + /* + * save the hdr and cmd structures for later use when + * sending back the response to Command + */ + err = visorbus_device_changestate(&inmsg); + break; + } + break; + case CONTROLVM_DEVICE_DESTROY: + err = visorbus_device_destroy(&inmsg); + break; + case CONTROLVM_DEVICE_CONFIGURE: + /* no op just send a respond that we passed */ + if (inmsg.hdr.flags.response_expected) + controlvm_respond(&inmsg.hdr, CONTROLVM_RESP_SUCCESS, + NULL); + break; + case CONTROLVM_CHIPSET_READY: + err = chipset_ready_uevent(&inmsg.hdr); + break; + case CONTROLVM_CHIPSET_SELFTEST: + err = chipset_selftest_uevent(&inmsg.hdr); + break; + case CONTROLVM_CHIPSET_STOP: + err = chipset_notready_uevent(&inmsg.hdr); + break; + default: + err = -ENOMSG; + if (inmsg.hdr.flags.response_expected) + controlvm_respond(&inmsg.hdr, + -CONTROLVM_RESP_ID_UNKNOWN, NULL); + break; + } + if (parser_ctx) { + parser_done(parser_ctx); + parser_ctx = NULL; + } + return err; +} + +/* + * read_controlvm_event() - retreives the next message from the + * CONTROLVM_QUEUE_EVENT queue in the controlvm + * channel + * @msg: pointer to the retrieved message + * + * Return: 0 if valid message was retrieved or -error + */ +static int read_controlvm_event(struct controlvm_message *msg) +{ + int err = visorchannel_signalremove(chipset_dev->controlvm_channel, + CONTROLVM_QUEUE_EVENT, msg); + + if (err) + return err; + /* got a message */ + if (msg->hdr.flags.test_message == 1) + return -EINVAL; + return 0; +} + +/* + * parahotplug_process_list() - remove any request from the list that's been on + * there too long and respond with an error + */ +static void parahotplug_process_list(void) +{ + struct list_head *pos; + struct list_head *tmp; + + spin_lock(¶hotplug_request_list_lock); + list_for_each_safe(pos, tmp, ¶hotplug_request_list) { + struct parahotplug_request *req = + list_entry(pos, struct parahotplug_request, list); + + if (!time_after_eq(jiffies, req->expiration)) + continue; + list_del(pos); + if (req->msg.hdr.flags.response_expected) + controlvm_respond( + &req->msg.hdr, + CONTROLVM_RESP_DEVICE_UDEV_TIMEOUT, + &req->msg.cmd.device_change_state.state); + parahotplug_request_destroy(req); + } + spin_unlock(¶hotplug_request_list_lock); +} + +static void controlvm_periodic_work(struct work_struct *work) +{ + struct controlvm_message inmsg; + int count = 0; + int err; + + /* Drain the RESPONSE queue make it empty */ + do { + err = visorchannel_signalremove(chipset_dev->controlvm_channel, + CONTROLVM_QUEUE_RESPONSE, + &inmsg); + } while ((!err) && (++count < CONTROLVM_MESSAGE_MAX)); + if (err != -EAGAIN) + goto schedule_out; + if (chipset_dev->controlvm_pending_msg_valid) { + /* + * we throttled processing of a prior msg, so try to process + * it again rather than reading a new one + */ + inmsg = chipset_dev->controlvm_pending_msg; + chipset_dev->controlvm_pending_msg_valid = false; + err = 0; + } else { + err = read_controlvm_event(&inmsg); + } + while (!err) { + chipset_dev->most_recent_message_jiffies = jiffies; + err = handle_command(inmsg, + visorchannel_get_physaddr + (chipset_dev->controlvm_channel)); + if (err == -EAGAIN) { + chipset_dev->controlvm_pending_msg = inmsg; + chipset_dev->controlvm_pending_msg_valid = true; + break; + } + + err = read_controlvm_event(&inmsg); + } + /* parahotplug_worker */ + parahotplug_process_list(); + +/* + * The controlvm messages are sent in a bulk. If we start receiving messages, we + * want the polling to be fast. If we do not receive any message for + * MIN_IDLE_SECONDS, we can slow down the polling. + */ +schedule_out: + if (time_after(jiffies, chipset_dev->most_recent_message_jiffies + + (HZ * MIN_IDLE_SECONDS))) { + /* + * it's been longer than MIN_IDLE_SECONDS since we processed + * our last controlvm message; slow down the polling + */ + if (chipset_dev->poll_jiffies != POLLJIFFIES_CONTROLVM_SLOW) + chipset_dev->poll_jiffies = POLLJIFFIES_CONTROLVM_SLOW; + } else { + if (chipset_dev->poll_jiffies != POLLJIFFIES_CONTROLVM_FAST) + chipset_dev->poll_jiffies = POLLJIFFIES_CONTROLVM_FAST; + } + schedule_delayed_work(&chipset_dev->periodic_controlvm_work, + chipset_dev->poll_jiffies); +} + +static int visorchipset_init(struct acpi_device *acpi_device) +{ + int err = -ENODEV; + struct visorchannel *controlvm_channel; + + chipset_dev = kzalloc(sizeof(*chipset_dev), GFP_KERNEL); + if (!chipset_dev) + goto error; + err = controlvm_channel_create(chipset_dev); + if (err) + goto error_free_chipset_dev; + acpi_device->driver_data = chipset_dev; + chipset_dev->acpi_device = acpi_device; + chipset_dev->poll_jiffies = POLLJIFFIES_CONTROLVM_FAST; + err = sysfs_create_groups(&chipset_dev->acpi_device->dev.kobj, + visorchipset_dev_groups); + if (err < 0) + goto error_destroy_channel; + controlvm_channel = chipset_dev->controlvm_channel; + if (!visor_check_channel(visorchannel_get_header(controlvm_channel), + &chipset_dev->acpi_device->dev, + &visor_controlvm_channel_guid, + "controlvm", + sizeof(struct visor_controlvm_channel), + VISOR_CONTROLVM_CHANNEL_VERSIONID, + VISOR_CHANNEL_SIGNATURE)) + goto error_delete_groups; + /* if booting in a crash kernel */ + if (is_kdump_kernel()) + INIT_DELAYED_WORK(&chipset_dev->periodic_controlvm_work, + setup_crash_devices_work_queue); + else + INIT_DELAYED_WORK(&chipset_dev->periodic_controlvm_work, + controlvm_periodic_work); + chipset_dev->most_recent_message_jiffies = jiffies; + chipset_dev->poll_jiffies = POLLJIFFIES_CONTROLVM_FAST; + schedule_delayed_work(&chipset_dev->periodic_controlvm_work, + chipset_dev->poll_jiffies); + err = visorbus_init(); + if (err < 0) + goto error_cancel_work; + return 0; + +error_cancel_work: + cancel_delayed_work_sync(&chipset_dev->periodic_controlvm_work); + +error_delete_groups: + sysfs_remove_groups(&chipset_dev->acpi_device->dev.kobj, + visorchipset_dev_groups); + +error_destroy_channel: + visorchannel_destroy(chipset_dev->controlvm_channel); + +error_free_chipset_dev: + kfree(chipset_dev); + +error: + dev_err(&acpi_device->dev, "failed with error %d\n", err); + return err; +} + +static int visorchipset_exit(struct acpi_device *acpi_device) +{ + visorbus_exit(); + cancel_delayed_work_sync(&chipset_dev->periodic_controlvm_work); + sysfs_remove_groups(&chipset_dev->acpi_device->dev.kobj, + visorchipset_dev_groups); + visorchannel_destroy(chipset_dev->controlvm_channel); + kfree(chipset_dev); + return 0; +} + +static const struct acpi_device_id unisys_device_ids[] = { + {"PNP0A07", 0}, + {"", 0}, +}; + +static struct acpi_driver unisys_acpi_driver = { + .name = "unisys_acpi", + .class = "unisys_acpi_class", + .owner = THIS_MODULE, + .ids = unisys_device_ids, + .ops = { + .add = visorchipset_init, + .remove = visorchipset_exit, + }, +}; + +MODULE_DEVICE_TABLE(acpi, unisys_device_ids); + +static __init int visorutil_spar_detect(void) +{ + unsigned int eax, ebx, ecx, edx; + + if (boot_cpu_has(X86_FEATURE_HYPERVISOR)) { + /* check the ID */ + cpuid(UNISYS_VISOR_LEAF_ID, &eax, &ebx, &ecx, &edx); + return (ebx == UNISYS_VISOR_ID_EBX) && + (ecx == UNISYS_VISOR_ID_ECX) && + (edx == UNISYS_VISOR_ID_EDX); + } + return 0; +} + +static int __init init_unisys(void) +{ + int result; + + if (!visorutil_spar_detect()) + return -ENODEV; + result = acpi_bus_register_driver(&unisys_acpi_driver); + if (result) + return -ENODEV; + pr_info("Unisys Visorchipset Driver Loaded.\n"); + return 0; +}; + +static void __exit exit_unisys(void) +{ + acpi_bus_unregister_driver(&unisys_acpi_driver); +} + +module_init(init_unisys); +module_exit(exit_unisys); + +MODULE_AUTHOR("Unisys"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("s-Par visorbus driver for virtual device buses"); -- cgit v1.2.3