aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/visorbus
diff options
context:
space:
mode:
authorDavid Kershner <david.kershner@unisys.com>2017-12-07 12:11:07 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-12-08 16:37:50 +0100
commit93d3ad90c2d470804b16f79e7e872408747d3e77 (patch)
tree8999b224a3185a6c28141b32a3458b36345b57fe /drivers/visorbus
parent84f1e4b08919a3981a5dc0234e6c059e958f73e7 (diff)
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 <david.kershner@unisys.com> Reviewed-by: Tim Sell <timothy.sell@unisys.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/visorbus')
-rw-r--r--drivers/visorbus/Kconfig14
-rw-r--r--drivers/visorbus/Makefile10
-rw-r--r--drivers/visorbus/controlvmchannel.h650
-rw-r--r--drivers/visorbus/vbuschannel.h95
-rw-r--r--drivers/visorbus/visorbus_main.c1234
-rw-r--r--drivers/visorbus/visorbus_private.h48
-rw-r--r--drivers/visorbus/visorchannel.c434
-rw-r--r--drivers/visorbus/visorchipset.c1686
8 files changed, 4171 insertions, 0 deletions
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 <linux/uuid.h>
+#include <linux/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/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 <linux/uuid.h>
+#include <linux/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/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 <linux/ctype.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/visorbus.h>
+#include <linux/uuid.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 <logCtx> 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<x>/dev<y>/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<n>.
+ */
+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<n>.
+ */
+
+/*
+ * 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 <info> 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 <info> 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 <info> to the struct
+ * visor_vbus_channel.dev_info[<devix>]
+ * @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 <linux/uuid.h>
+#include <linux/utsname.h>
+#include <linux/visorbus.h>
+
+#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 <linux/uuid.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/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/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 <linux/acpi.h>
+#include <linux/crash_dump.h>
+#include <linux/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(&parahotplug_request_list_lock);
+ /* Look for a request matching "id". */
+ list_for_each_safe(pos, tmp, &parahotplug_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(&parahotplug_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(&parahotplug_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(&parahotplug_request_list_lock);
+ list_add_tail(&req->list, &parahotplug_request_list);
+ spin_unlock(&parahotplug_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(&parahotplug_request_list_lock);
+ list_for_each_safe(pos, tmp, &parahotplug_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(&parahotplug_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");

Privacy Policy