This patch adds the s-Par bus driver to the arch/x86 directory. This location was chosen since the s-Par bus driver is dependent on the x86 architecture. Updates the KBuild and Kconfig files to include being able to build it. Signed-off-by: David Kershner <david.kershner@xxxxxxxxxx> --- arch/x86/Kbuild | 3 + arch/x86/Kconfig | 2 + arch/x86/visorbus/Kconfig | 14 + arch/x86/visorbus/Makefile | 8 + arch/x86/visorbus/controlvmchannel.h | 485 ++++ arch/x86/visorbus/controlvmcompletionstatus.h | 101 + arch/x86/visorbus/iovmcall_gnuc.h | 48 + arch/x86/visorbus/periodic_work.c | 203 ++ arch/x86/visorbus/vbuschannel.h | 95 + arch/x86/visorbus/vbusdeviceinfo.h | 213 ++ arch/x86/visorbus/vbushelper.h | 46 + arch/x86/visorbus/visorbus_main.c | 1344 +++++++++++ arch/x86/visorbus/visorbus_private.h | 68 + arch/x86/visorbus/visorchannel.c | 635 ++++++ arch/x86/visorbus/visorchipset.c | 2355 ++++++++++++++++++++ arch/x86/visorbus/vmcallinterface.h | 91 + drivers/staging/unisys/Kconfig | 1 - drivers/staging/unisys/Makefile | 1 - drivers/staging/unisys/visorbus/Kconfig | 14 - drivers/staging/unisys/visorbus/Makefile | 12 - drivers/staging/unisys/visorbus/controlvmchannel.h | 485 ---- .../unisys/visorbus/controlvmcompletionstatus.h | 101 - drivers/staging/unisys/visorbus/iovmcall_gnuc.h | 48 - drivers/staging/unisys/visorbus/periodic_work.c | 203 -- drivers/staging/unisys/visorbus/vbuschannel.h | 95 - drivers/staging/unisys/visorbus/vbusdeviceinfo.h | 213 -- drivers/staging/unisys/visorbus/vbushelper.h | 46 - drivers/staging/unisys/visorbus/visorbus_main.c | 1344 ----------- drivers/staging/unisys/visorbus/visorbus_private.h | 68 - drivers/staging/unisys/visorbus/visorchannel.c | 635 ------ drivers/staging/unisys/visorbus/visorchipset.c | 2355 -------------------- drivers/staging/unisys/visorbus/vmcallinterface.h | 91 - 32 files changed, 5711 insertions(+), 5712 deletions(-) create mode 100644 arch/x86/visorbus/Kconfig create mode 100644 arch/x86/visorbus/Makefile create mode 100644 arch/x86/visorbus/controlvmchannel.h create mode 100644 arch/x86/visorbus/controlvmcompletionstatus.h create mode 100644 arch/x86/visorbus/iovmcall_gnuc.h create mode 100644 arch/x86/visorbus/periodic_work.c create mode 100644 arch/x86/visorbus/vbuschannel.h create mode 100644 arch/x86/visorbus/vbusdeviceinfo.h create mode 100644 arch/x86/visorbus/vbushelper.h create mode 100644 arch/x86/visorbus/visorbus_main.c create mode 100644 arch/x86/visorbus/visorbus_private.h create mode 100644 arch/x86/visorbus/visorchannel.c create mode 100644 arch/x86/visorbus/visorchipset.c create mode 100644 arch/x86/visorbus/vmcallinterface.h delete mode 100644 drivers/staging/unisys/visorbus/Kconfig delete mode 100644 drivers/staging/unisys/visorbus/Makefile delete mode 100644 drivers/staging/unisys/visorbus/controlvmchannel.h delete mode 100644 drivers/staging/unisys/visorbus/controlvmcompletionstatus.h delete mode 100644 drivers/staging/unisys/visorbus/iovmcall_gnuc.h delete mode 100644 drivers/staging/unisys/visorbus/periodic_work.c delete mode 100644 drivers/staging/unisys/visorbus/vbuschannel.h delete mode 100644 drivers/staging/unisys/visorbus/vbusdeviceinfo.h delete mode 100644 drivers/staging/unisys/visorbus/vbushelper.h delete mode 100644 drivers/staging/unisys/visorbus/visorbus_main.c delete mode 100644 drivers/staging/unisys/visorbus/visorbus_private.h delete mode 100644 drivers/staging/unisys/visorbus/visorchannel.c delete mode 100644 drivers/staging/unisys/visorbus/visorchipset.c delete mode 100644 drivers/staging/unisys/visorbus/vmcallinterface.h diff --git a/arch/x86/Kbuild b/arch/x86/Kbuild index eb3abf8..6d08662 100644 --- a/arch/x86/Kbuild +++ b/arch/x86/Kbuild @@ -10,6 +10,9 @@ obj-$(CONFIG_XEN) += xen/ # lguest paravirtualization support obj-$(CONFIG_LGUEST_GUEST) += lguest/ +# s-Par paravirtualization support +obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ + obj-y += realmode/ obj-y += kernel/ obj-y += mm/ diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 2dc18605..cd46482 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -770,6 +770,8 @@ config PARAVIRT_CLOCK endif #HYPERVISOR_GUEST +source "arch/x86/visorbus/Kconfig" + config NO_BOOTMEM def_bool y diff --git a/arch/x86/visorbus/Kconfig b/arch/x86/visorbus/Kconfig new file mode 100644 index 0000000..5113880 --- /dev/null +++ b/arch/x86/visorbus/Kconfig @@ -0,0 +1,14 @@ +# +# Unisys visorbus configuration +# + +config UNISYS_VISORBUS + tristate "Unisys visorbus driver" + depends on UNISYSSPAR + ---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/arch/x86/visorbus/Makefile b/arch/x86/visorbus/Makefile new file mode 100644 index 0000000..206cd68 --- /dev/null +++ b/arch/x86/visorbus/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for Unisys visorbus +# + +obj-$(CONFIG_UNISYS_VISORBUS) += visorbus.o + +visorbus-objs := visorbus_main.o visorchannel.o visorchipset.o periodic_work.o + diff --git a/arch/x86/visorbus/controlvmchannel.h b/arch/x86/visorbus/controlvmchannel.h new file mode 100644 index 0000000..0a0e221 --- /dev/null +++ b/arch/x86/visorbus/controlvmchannel.h @@ -0,0 +1,485 @@ +/* Copyright (C) 2010 - 2015 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __CONTROLVMCHANNEL_H__ +#define __CONTROLVMCHANNEL_H__ + +#include <linux/uuid.h> +#include <linux/visorbus/channel.h> + +/* {2B3C2D10-7EF5-4ad8-B966-3448B7386B3D} */ +#define SPAR_CONTROLVM_CHANNEL_PROTOCOL_UUID \ + UUID_LE(0x2b3c2d10, 0x7ef5, 0x4ad8, \ + 0xb9, 0x66, 0x34, 0x48, 0xb7, 0x38, 0x6b, 0x3d) + +#define ULTRA_CONTROLVM_CHANNEL_PROTOCOL_SIGNATURE \ + ULTRA_CHANNEL_PROTOCOL_SIGNATURE +#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 ULTRA_CONTROLVM_CHANNEL_PROTOCOL_VERSIONID 1 + +#define SPAR_CONTROLVM_CHANNEL_OK_CLIENT(ch) \ + spar_check_channel_client(ch, \ + SPAR_CONTROLVM_CHANNEL_PROTOCOL_UUID, \ + "controlvm", \ + sizeof(struct spar_controlvm_channel_protocol), \ + ULTRA_CONTROLVM_CHANNEL_PROTOCOL_VERSIONID, \ + ULTRA_CONTROLVM_CHANNEL_PROTOCOL_SIGNATURE) + +#define MAX_SERIAL_NUM 32 + +/* 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 spar_segment_state { + /* Bit 0: May enter other states */ + u16 enabled:1; + /* Bit 1: Assigned to active partition */ + u16 active:1; + /* Bit 2: Configure message sent to service/server */ + u16 alive:1; + /* Bit 3: similar to partition state ShuttingDown */ + u16 revoked:1; + /* Bit 4: memory (device/port number) has been selected by Command */ + u16 allocated:1; + /* Bit 5: has been introduced to the service/guest partition */ + u16 known:1; + /* Bit 6: service/Guest partition has responded to introduction */ + u16 ready:1; + /* Bit 7: resource is configured and operating */ + u16 operating:1; +/* Note: don't use high bit unless we need to switch to ushort + * which is non-compliant + */ +}; + +static const struct spar_segment_state segment_state_running = { + 1, 1, 1, 0, 1, 1, 1, 1 +}; + +static const struct spar_segment_state segment_state_paused = { + 1, 1, 1, 0, 1, 1, 1, 0 +}; + +static const struct spar_segment_state segment_state_standby = { + 1, 1, 0, 0, 1, 1, 1, 0 +}; + +/* 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, /* CP --> SP, GP */ + CONTROLVM_BUS_DESTROY = 0x102, /* CP --> SP, GP */ + CONTROLVM_BUS_CONFIGURE = 0x104, /* CP --> SP */ + CONTROLVM_BUS_CHANGESTATE = 0x105, /* CP --> SP, GP */ + CONTROLVM_BUS_CHANGESTATE_EVENT = 0x106, /* SP, GP --> CP */ +/* DEVICE commands required Parameter: BusNumber, DeviceNumber */ + + CONTROLVM_DEVICE_CREATE = 0x201, /* CP --> SP, GP */ + CONTROLVM_DEVICE_DESTROY = 0x202, /* CP --> SP, GP */ + CONTROLVM_DEVICE_CONFIGURE = 0x203, /* CP --> SP */ + CONTROLVM_DEVICE_CHANGESTATE = 0x204, /* CP --> SP, GP */ + CONTROLVM_DEVICE_CHANGESTATE_EVENT = 0x205, /* SP, GP --> CP */ + CONTROLVM_DEVICE_RECONFIGURE = 0x206, /* CP --> Boot */ +/* CHIPSET commands */ + CONTROLVM_CHIPSET_INIT = 0x301, /* CP --> SP, GP */ + CONTROLVM_CHIPSET_STOP = 0x302, /* CP --> SP, GP */ + CONTROLVM_CHIPSET_READY = 0x304, /* CP --> SP */ + CONTROLVM_CHIPSET_SELFTEST = 0x305, /* CP --> SP */ + +}; + +struct irq_info { + u64 reserved1; + + /* 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. + */ + u64 recv_irq_handle; + + /* specifies interrupt vector. It, interrupt pin, and shared are + * used to connect to the corresponding interrupt. Used by + * IOPart-GP only. + */ + u32 recv_irq_vector; + + /* 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. + */ + u8 recv_irq_shared; + u8 reserved[3]; /* Natural alignment purposes */ +}; + +struct pci_id { + u16 domain; + u8 bus; + u8 slot; + u8 func; + u8 reserved[3]; /* Natural alignment purposes */ +}; + +struct efi_spar_indication { + u64 boot_to_fw_ui:1; /* Bit 0: Stop in uefi ui */ + u64 clear_nvram:1; /* Bit 1: Clear NVRAM */ + u64 clear_cmos:1; /* Bit 2: Clear CMOS */ + u64 boot_to_tool:1; /* Bit 3: Run install tool */ + /* remaining bits are available */ +}; + +enum ultra_chipset_feature { + ULTRA_CHIPSET_FEATURE_REPLY = 0x00000001, + ULTRA_CHIPSET_FEATURE_PARA_HOTPLUG = 0x00000002, +}; + +/* 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; /* See CONTROLVM_ID. */ + /* For requests, indicates the message type. */ + /* For responses, indicates the type of message we are responding to. */ + + /* Includes size of this struct + size of message */ + u32 message_size; + /* Index of segment containing Vm message/information */ + u32 segment_index; + /* Error status code or result of message completion */ + u32 completion_status; + struct { + /* =1 in a response to signify failure */ + u32 failed:1; + /* =1 in all messages that expect a response */ + u32 response_expected:1; + /* =1 in all bus & device-related messages where the message + * receiver is to act as the bus or device server + */ + u32 server:1; + /* =1 for testing use only (Control and Command ignore this */ + u32 test_message:1; + /* =1 if there are forthcoming responses/acks associated + * with this message + */ + u32 partial_completion:1; + /* =1 this is to let us know to preserve channel contents */ + u32 preserve:1; + /* =1 the DiagWriter is active in the Diagnostic Partition */ + u32 writer_in_diag:1; + } flags; + /* Natural alignment */ + u32 reserved; + /* Identifies the particular message instance */ + u64 message_handle; + /* request instances with the corresponding response instance. */ + /* Offset of payload area from start of this instance */ + u64 payload_vm_offset; + /* Maximum bytes allocated in payload area of ControlVm segment */ + u32 payload_max_bytes; + /* Actual number of bytes of payload area to copy between IO/Command */ + u32 payload_bytes; + /* if non-zero, there is a payload to copy. */ +}; + +struct controlvm_packet_device_create { + u32 bus_no; /* bus # (0..n-1) from the msg receiver's end */ + u32 dev_no; /* bus-relative (0..n-1) device number */ + /* Guest physical address of the channel, which can be dereferenced by + * the receiver of this ControlVm command + */ + u64 channel_addr; + u64 channel_bytes; /* specifies size of the channel in bytes */ + uuid_le data_type_uuid; /* specifies format of data in channel */ + uuid_le dev_inst_uuid; /* instance guid for the device */ + struct irq_info intr; /* specifies interrupt information */ +}; /* for CONTROLVM_DEVICE_CREATE */ + +struct controlvm_packet_device_configure { + /* bus # (0..n-1) from the msg receiver's perspective */ + u32 bus_no; + /* Control uses header SegmentIndex field to access bus number... */ + u32 dev_no; /* bus-relative (0..n-1) device number */ +} ; /* for CONTROLVM_DEVICE_CONFIGURE */ + +struct controlvm_message_device_create { + struct controlvm_message_header header; + struct controlvm_packet_device_create packet; +}; /* total 128 bytes */ + +struct controlvm_message_device_configure { + struct controlvm_message_header header; + struct controlvm_packet_device_configure packet; +}; /* total 56 bytes */ + +/* This is the format for a message in any ControlVm queue. */ +struct controlvm_message_packet { + union { + struct { + /* bus # (0..n-1) from the msg receiver's perspective */ + u32 bus_no; + /* indicates the max number of devices on this bus */ + u32 dev_count; + /* Guest physical address of the channel, which can be + * dereferenced by the receiver of this ControlVm command + */ + u64 channel_addr; + u64 channel_bytes; /* size of the channel */ + /* indicates format of data in bus channel*/ + uuid_le bus_data_type_uuid; + uuid_le bus_inst_uuid; /* instance uuid for the bus */ + } create_bus; /* for CONTROLVM_BUS_CREATE */ + struct { + /* bus # (0..n-1) from the msg receiver's perspective */ + u32 bus_no; + u32 reserved; /* Natural alignment purposes */ + } destroy_bus; /* for CONTROLVM_BUS_DESTROY */ + struct { + /* bus # (0..n-1) from the receiver's perspective */ + u32 bus_no; + u32 reserved1; /* for alignment purposes */ + /* This is used to convert guest physical address to physical address */ + u64 guest_handle; + u64 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. + */ + } configure_bus; /* for CONTROLVM_BUS_CONFIGURE */ + /* for CONTROLVM_DEVICE_CREATE */ + struct controlvm_packet_device_create create_device; + struct { + /* bus # (0..n-1) from the msg receiver's perspective */ + u32 bus_no; + u32 dev_no; /* bus-relative (0..n-1) device # */ + } destroy_device; /* for CONTROLVM_DEVICE_DESTROY */ + /* for CONTROLVM_DEVICE_CONFIGURE */ + struct controlvm_packet_device_configure configure_device; + struct { + /* bus # (0..n-1) from the msg receiver's perspective */ + u32 bus_no; + u32 dev_no; /* bus-relative (0..n-1) device # */ + } reconfigure_device; /* for CONTROLVM_DEVICE_RECONFIGURE */ + struct { + u32 bus_no; + struct spar_segment_state state; + u8 reserved[2]; /* Natural alignment purposes */ + } bus_change_state; /* for CONTROLVM_BUS_CHANGESTATE */ + struct { + u32 bus_no; + u32 dev_no; + struct spar_segment_state state; + struct { + /* =1 if message is for a physical device */ + u32 phys_device:1; + } flags; + u8 reserved[2]; /* Natural alignment purposes */ + } device_change_state; /* for CONTROLVM_DEVICE_CHANGESTATE */ + struct { + u32 bus_no; + u32 dev_no; + struct spar_segment_state state; + u8 reserved[6]; /* Natural alignment purposes */ + } device_change_state_event; + /* for CONTROLVM_DEVICE_CHANGESTATE_EVENT */ + struct { + /* indicates the max number of busses */ + u32 bus_count; + /* indicates the max number of switches */ + u32 switch_count; + enum ultra_chipset_feature features; + u32 platform_number; /* Platform Number */ + } init_chipset; /* for CONTROLVM_CHIPSET_INIT */ + struct { + u32 options; /* reserved */ + u32 test; /* bit 0 set to run embedded selftest */ + } chipset_selftest; /* for CONTROLVM_CHIPSET_SELFTEST */ + /* a physical address of something, that can be dereferenced + * by the receiver of this ControlVm command + */ + u64 addr; + /* a handle of something (depends on command id) */ + u64 handle; + }; +}; + +/* All messages in any ControlVm queue have this layout. */ +struct controlvm_message { + struct controlvm_message_header hdr; + struct controlvm_message_packet cmd; +}; + +struct spar_controlvm_channel_protocol { + struct channel_header header; + u64 gp_controlvm; /* guest phys addr of this channel */ + u64 gp_partition_tables;/* guest phys addr of partition tables */ + u64 gp_diag_guest; /* guest phys addr of diagnostic channel */ + u64 gp_boot_romdisk;/* guest phys addr of (read* only) Boot ROM disk */ + u64 gp_boot_ramdisk;/* guest phys addr of writable Boot RAM disk */ + u64 gp_acpi_table; /* guest phys addr of acpi table */ + u64 gp_control_channel;/* guest phys addr of control channel */ + u64 gp_diag_romdisk;/* guest phys addr of diagnostic ROM disk */ + u64 gp_nvram; /* guest phys addr of NVRAM channel */ + u64 request_payload_offset; /* Offset to request payload area */ + u64 event_payload_offset; /* Offset to event payload area */ + /* Bytes available in request payload area */ + u32 request_payload_bytes; + u32 event_payload_bytes;/* Bytes available in event payload area */ + u32 control_channel_bytes; + u32 nvram_channel_bytes; /* Bytes in PartitionNvram segment */ + u32 message_bytes; /* sizeof(CONTROLVM_MESSAGE) */ + u32 message_count; /* CONTROLVM_MESSAGE_MAX */ + u64 gp_smbios_table; /* guest phys addr of SMBIOS tables */ + u64 gp_physical_smbios_table; /* guest phys addr of SMBIOS table */ + /* ULTRA_MAX_GUESTS_PER_SERVICE */ + char gp_reserved[2688]; + + /* guest physical address of EFI firmware image base */ + u64 virtual_guest_firmware_image_base; + + /* guest physical address of EFI firmware entry point */ + u64 virtual_guest_firmware_entry_point; + + /* guest EFI firmware image size */ + u64 virtual_guest_firmware_image_size; + + /* GPA = 1MB where EFI firmware image is copied to */ + 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; + /* Restore Action field to restore the guest partition */ + u16 restore_action; + /* For Windows guests it shows if the visordisk is in dump mode */ + u16 dump_action; + u16 nvram_fail_count; + u16 saved_crash_message_count; /* = CONTROLVM_CRASHMSG_MAX */ + /* Offset to request payload area needed for crash dump */ + u32 saved_crash_message_offset; + /* Type of error encountered during installation */ + u32 installation_error; + u32 installation_text_id; /* Id of string to display */ + /* Number of remaining installation steps (for progress bars) */ + u16 installation_remaining_steps; + /* ULTRA_TOOL_ACTIONS Installation Action field */ + u8 tool_action; + u8 reserved; /* alignment */ + struct efi_spar_indication efi_spar_ind; + struct efi_spar_indication efi_spar_ind_supported; + u32 sp_reserved; + /* Force signals to begin on 128-byte cache line */ + u8 reserved2[28]; + /* guest partition uses this queue to send requests to Control */ + struct signal_queue_header request_queue; + /* Control uses this queue to respond to service or guest + * partition requests + */ + struct signal_queue_header response_queue; + /* Control uses this queue to send events to guest partition */ + struct signal_queue_header event_queue; + /* Service or guest partition uses this queue to ack Control events */ + struct signal_queue_header event_ack_queue; + /* Request fixed-size message pool - does not include payload */ + struct controlvm_message request_msg[CONTROLVM_MESSAGE_MAX]; + + /* Response fixed-size message pool - does not include payload */ + struct controlvm_message response_msg[CONTROLVM_MESSAGE_MAX]; + + /* Event fixed-size message pool - does not include payload */ + struct controlvm_message event_msg[CONTROLVM_MESSAGE_MAX]; + + /* Ack fixed-size message pool - does not include payload */ + struct controlvm_message event_ack_msg[CONTROLVM_MESSAGE_MAX]; + + /* Message stored during IOVM creation to be reused after crash */ + struct controlvm_message saved_crash_msg[CONTROLVM_CRASHMSG_MAX]; +}; + +/* Offsets for VM channel attributes */ +#define VM_CH_REQ_QUEUE_OFFSET \ + offsetof(struct spar_controlvm_channel_protocol, request_queue) +#define VM_CH_RESP_QUEUE_OFFSET \ + offsetof(struct spar_controlvm_channel_protocol, response_queue) +#define VM_CH_EVENT_QUEUE_OFFSET \ + offsetof(struct spar_controlvm_channel_protocol, event_queue) +#define VM_CH_ACK_QUEUE_OFFSET \ + offsetof(struct spar_controlvm_channel_protocol, event_ack_queue) +#define VM_CH_REQ_MSG_OFFSET \ + offsetof(struct spar_controlvm_channel_protocol, request_msg) +#define VM_CH_RESP_MSG_OFFSET \ + offsetof(struct spar_controlvm_channel_protocol, response_msg) +#define VM_CH_EVENT_MSG_OFFSET \ + offsetof(struct spar_controlvm_channel_protocol, event_msg) +#define VM_CH_ACK_MSG_OFFSET \ + offsetof(struct spar_controlvm_channel_protocol, event_ack_msg) +#define VM_CH_CRASH_MSG_OFFSET \ + offsetof(struct spar_controlvm_channel_protocol, saved_crash_msg) + +/* 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 spar_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; + uuid_le id; + u32 revision; + u32 reserved; /* Natural alignment */ +}; + +#endif /* __CONTROLVMCHANNEL_H__ */ diff --git a/arch/x86/visorbus/controlvmcompletionstatus.h b/arch/x86/visorbus/controlvmcompletionstatus.h new file mode 100644 index 0000000..23ad0ea --- /dev/null +++ b/arch/x86/visorbus/controlvmcompletionstatus.h @@ -0,0 +1,101 @@ +/* controlvmcompletionstatus.c + * + * Copyright (C) 2010 - 2015 UNISYS CORPORATION + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* Defines for all valid values returned in the response message header + * completionStatus field. See controlvmchannel.h for description of + * the header: _CONTROLVM_MESSAGE_HEADER. + */ + +#ifndef __CONTROLVMCOMPLETIONSTATUS_H__ +#define __CONTROLVMCOMPLETIONSTATUS_H__ + +/* General Errors------------------------------------------------------[0-99] */ +#define CONTROLVM_RESP_SUCCESS 0 +#define CONTROLVM_RESP_ERROR_ALREADY_DONE 1 +#define CONTROLVM_RESP_ERROR_IOREMAP_FAILED 2 +#define CONTROLVM_RESP_ERROR_KMALLOC_FAILED 3 +#define CONTROLVM_RESP_ERROR_MESSAGE_ID_UNKNOWN 4 +#define CONTROLVM_RESP_ERROR_MESSAGE_ID_INVALID_FOR_CLIENT 5 + +/* CONTROLVM_INIT_CHIPSET-------------------------------------------[100-199] */ +#define CONTROLVM_RESP_ERROR_CLIENT_SWITCHCOUNT_NONZERO 100 +#define CONTROLVM_RESP_ERROR_EXPECTED_CHIPSET_INIT 101 + +/* Maximum Limit----------------------------------------------------[200-299] */ +#define CONTROLVM_RESP_ERROR_MAX_BUSES 201 /* BUS_CREATE */ +#define CONTROLVM_RESP_ERROR_MAX_DEVICES 202 /* DEVICE_CREATE */ +/* Payload and Parameter Related------------------------------------[400-499] */ +#define CONTROLVM_RESP_ERROR_PAYLOAD_INVALID 400 /* SWITCH_ATTACHEXTPORT, + * DEVICE_CONFIGURE + */ +#define CONTROLVM_RESP_ERROR_INITIATOR_PARAMETER_INVALID 401 /* Multiple */ +#define CONTROLVM_RESP_ERROR_TARGET_PARAMETER_INVALID 402 /* DEVICE_CONFIGURE */ +#define CONTROLVM_RESP_ERROR_CLIENT_PARAMETER_INVALID 403 /* DEVICE_CONFIGURE */ +/* Specified[Packet Structure] Value-------------------------------[500-599] */ +#define CONTROLVM_RESP_ERROR_BUS_INVALID 500 /* SWITCH_ATTACHINTPORT, + * BUS_CONFIGURE, + * DEVICE_CREATE, + * DEVICE_CONFIG + * DEVICE_DESTROY + */ +#define CONTROLVM_RESP_ERROR_DEVICE_INVALID 501 /* SWITCH_ATTACHINTPORT */ + /* DEVICE_CREATE, + * DEVICE_CONFIGURE, + * DEVICE_DESTROY + */ +#define CONTROLVM_RESP_ERROR_CHANNEL_INVALID 502 /* DEVICE_CREATE, + * DEVICE_CONFIGURE + */ +/* Partition Driver Callback Interface----------------------[600-699] */ +#define CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE 604 /* BUS_CREATE, + * BUS_DESTROY, + * DEVICE_CREATE, + * DEVICE_DESTROY + */ +/* Unable to invoke VIRTPCI callback */ +#define CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR 605 + /* BUS_CREATE, + * BUS_DESTROY, + * DEVICE_CREATE, + * DEVICE_DESTROY + */ +/* VIRTPCI Callback returned error */ +#define CONTROLVM_RESP_ERROR_GENERIC_DRIVER_CALLBACK_ERROR 606 + /* SWITCH_ATTACHEXTPORT, + * SWITCH_DETACHEXTPORT + * DEVICE_CONFIGURE + */ + +/* generic device callback returned error */ +/* Bus Related------------------------------------------------------[700-799] */ +#define CONTROLVM_RESP_ERROR_BUS_DEVICE_ATTACHED 700 /* BUS_DESTROY */ +/* Channel Related--------------------------------------------------[800-899] */ +#define CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN 800 /* GET_CHANNELINFO, + * DEVICE_DESTROY + */ +#define CONTROLVM_RESP_ERROR_CHANNEL_SIZE_TOO_SMALL 801 /* DEVICE_CREATE */ +/* Chipset Shutdown Related---------------------------------------[1000-1099] */ +#define CONTROLVM_RESP_ERROR_CHIPSET_SHUTDOWN_FAILED 1000 +#define CONTROLVM_RESP_ERROR_CHIPSET_SHUTDOWN_ALREADY_ACTIVE 1001 + +/* Chipset Stop Related-------------------------------------------[1100-1199] */ +#define CONTROLVM_RESP_ERROR_CHIPSET_STOP_FAILED_BUS 1100 +#define CONTROLVM_RESP_ERROR_CHIPSET_STOP_FAILED_SWITCH 1101 + +/* Device Related-------------------------------------------------[1400-1499] */ +#define CONTROLVM_RESP_ERROR_DEVICE_UDEV_TIMEOUT 1400 + +#endif /* __CONTROLVMCOMPLETIONSTATUS_H__ not defined */ diff --git a/arch/x86/visorbus/iovmcall_gnuc.h b/arch/x86/visorbus/iovmcall_gnuc.h new file mode 100644 index 0000000..98ea7f3 --- /dev/null +++ b/arch/x86/visorbus/iovmcall_gnuc.h @@ -0,0 +1,48 @@ +/* Copyright (C) 2010 - 2015 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* Linux GCC Version (32-bit and 64-bit) */ +static inline unsigned long +__unisys_vmcall_gnuc(unsigned long tuple, unsigned long reg_ebx, + unsigned long reg_ecx) +{ + unsigned long result = 0; + unsigned int cpuid_eax, cpuid_ebx, cpuid_ecx, cpuid_edx; + + 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)); + return result; +} + +static inline unsigned long +__unisys_extended_vmcall_gnuc(unsigned long long tuple, + unsigned long long reg_ebx, + unsigned long long reg_ecx, + unsigned long long reg_edx) +{ + unsigned long result = 0; + unsigned int cpuid_eax, cpuid_ebx, cpuid_ecx, cpuid_edx; + + 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), "d"(reg_edx)); + return result; +} diff --git a/arch/x86/visorbus/periodic_work.c b/arch/x86/visorbus/periodic_work.c new file mode 100644 index 0000000..9b3b150 --- /dev/null +++ b/arch/x86/visorbus/periodic_work.c @@ -0,0 +1,203 @@ +/* periodic_work.c + * + * Copyright (C) 2010 - 2015 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* + * Helper functions to schedule periodic work in Linux kernel mode. + */ +#include <linux/sched.h> +#include <linux/visorbus/periodic_work.h> + +#define MYDRVNAME "periodic_work" + +struct periodic_work { + rwlock_t lock; + struct delayed_work work; + void (*workfunc)(void *); + void *workfuncarg; + bool is_scheduled; + bool want_to_stop; + ulong jiffy_interval; + struct workqueue_struct *workqueue; + const char *devnam; +}; + +static void periodic_work_func(struct work_struct *work) +{ + struct periodic_work *pw; + + pw = container_of(work, struct periodic_work, work.work); + (*pw->workfunc)(pw->workfuncarg); +} + +struct periodic_work +*visor_periodic_work_create(ulong jiffy_interval, + struct workqueue_struct *workqueue, + void (*workfunc)(void *), + void *workfuncarg, + const char *devnam) +{ + struct periodic_work *pw; + + pw = kzalloc(sizeof(*pw), GFP_KERNEL | __GFP_NORETRY); + if (!pw) + return NULL; + + rwlock_init(&pw->lock); + pw->jiffy_interval = jiffy_interval; + pw->workqueue = workqueue; + pw->workfunc = workfunc; + pw->workfuncarg = workfuncarg; + pw->devnam = devnam; + return pw; +} +EXPORT_SYMBOL_GPL(visor_periodic_work_create); + +void visor_periodic_work_destroy(struct periodic_work *pw) +{ + kfree(pw); +} +EXPORT_SYMBOL_GPL(visor_periodic_work_destroy); + +/** Call this from your periodic work worker function to schedule the next + * call. + * If this function returns false, there was a failure and the + * periodic work is no longer scheduled + */ +bool visor_periodic_work_nextperiod(struct periodic_work *pw) +{ + bool rc = false; + + write_lock(&pw->lock); + if (pw->want_to_stop) { + pw->is_scheduled = false; + pw->want_to_stop = false; + rc = true; /* yes, true; see visor_periodic_work_stop() */ + goto unlock; + } else if (!queue_delayed_work(pw->workqueue, &pw->work, + pw->jiffy_interval)) { + pw->is_scheduled = false; + rc = false; + goto unlock; + } + rc = true; +unlock: + write_unlock(&pw->lock); + return rc; +} +EXPORT_SYMBOL_GPL(visor_periodic_work_nextperiod); + +/** This function returns true iff new periodic work was actually started. + * If this function returns false, then no work was started + * (either because it was already started, or because of a failure). + */ +bool visor_periodic_work_start(struct periodic_work *pw) +{ + bool rc = false; + + write_lock(&pw->lock); + if (pw->is_scheduled) { + rc = false; + goto unlock; + } + if (pw->want_to_stop) { + rc = false; + goto unlock; + } + INIT_DELAYED_WORK(&pw->work, &periodic_work_func); + if (!queue_delayed_work(pw->workqueue, &pw->work, + pw->jiffy_interval)) { + rc = false; + goto unlock; + } + pw->is_scheduled = true; + rc = true; +unlock: + write_unlock(&pw->lock); + return rc; +} +EXPORT_SYMBOL_GPL(visor_periodic_work_start); + +/** This function returns true iff your call actually stopped the periodic + * work. + * + * -- PAY ATTENTION... this is important -- + * + * NO NO #1 + * + * Do NOT call this function from some function that is running on the + * same workqueue as the work you are trying to stop might be running + * on! If you violate this rule, visor_periodic_work_stop() MIGHT work, + * but it also MIGHT get hung up in an infinite loop saying + * "waiting for delayed work...". This will happen if the delayed work + * you are trying to cancel has been put in the workqueue list, but can't + * run yet because we are running that same workqueue thread right now. + * + * Bottom line: If you need to call visor_periodic_work_stop() from a + * workitem, be sure the workitem is on a DIFFERENT workqueue than the + * workitem that you are trying to cancel. + * + * If I could figure out some way to check for this "no no" condition in + * the code, I would. It would have saved me the trouble of writing this + * long comment. And also, don't think this is some "theoretical" race + * condition. It is REAL, as I have spent the day chasing it. + * + * NO NO #2 + * + * Take close note of the locks that you own when you call this function. + * You must NOT own any locks that are needed by the periodic work + * function that is currently installed. If you DO, a deadlock may result, + * because stopping the periodic work often involves waiting for the last + * iteration of the periodic work function to complete. Again, if you hit + * this deadlock, you will get hung up in an infinite loop saying + * "waiting for delayed work...". + */ +bool visor_periodic_work_stop(struct periodic_work *pw) +{ + bool stopped_something = false; + + write_lock(&pw->lock); + stopped_something = pw->is_scheduled && (!pw->want_to_stop); + while (pw->is_scheduled) { + pw->want_to_stop = true; + if (cancel_delayed_work(&pw->work)) { + /* We get here if the delayed work was pending as + * delayed work, but was NOT run. + */ + WARN_ON(!pw->is_scheduled); + pw->is_scheduled = false; + } else { + /* If we get here, either the delayed work: + * - was run, OR, + * - is running RIGHT NOW on another processor, OR, + * - wasn't even scheduled (there is a miniscule + * timing window where this could be the case) + * flush_workqueue() would make sure it is finished + * executing, but that still isn't very useful, which + * explains the loop... + */ + } + if (pw->is_scheduled) { + write_unlock(&pw->lock); + schedule_timeout_interruptible(msecs_to_jiffies(10)); + write_lock(&pw->lock); + } else { + pw->want_to_stop = false; + } + } + write_unlock(&pw->lock); + return stopped_something; +} +EXPORT_SYMBOL_GPL(visor_periodic_work_stop); diff --git a/arch/x86/visorbus/vbuschannel.h b/arch/x86/visorbus/vbuschannel.h new file mode 100644 index 0000000..3e0388d --- /dev/null +++ b/arch/x86/visorbus/vbuschannel.h @@ -0,0 +1,95 @@ +/* Copyright (C) 2010 - 2015 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __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/channel.h> + +#include "vbusdeviceinfo.h" + +/* {193b331b-c58f-11da-95a9-00e08161165f} */ +#define SPAR_VBUS_CHANNEL_PROTOCOL_UUID \ + UUID_LE(0x193b331b, 0xc58f, 0x11da, \ + 0x95, 0xa9, 0x0, 0xe0, 0x81, 0x61, 0x16, 0x5f) +static const uuid_le spar_vbus_channel_protocol_uuid = + SPAR_VBUS_CHANNEL_PROTOCOL_UUID; + +#define SPAR_VBUS_CHANNEL_PROTOCOL_SIGNATURE ULTRA_CHANNEL_PROTOCOL_SIGNATURE + +/* 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 SPAR_VBUS_CHANNEL_PROTOCOL_VERSIONID 1 + +#define SPAR_VBUS_CHANNEL_OK_CLIENT(ch) \ + spar_check_channel_client(ch, \ + spar_vbus_channel_protocol_uuid, \ + "vbus", \ + sizeof(struct spar_vbus_channel_protocol),\ + SPAR_VBUS_CHANNEL_PROTOCOL_VERSIONID, \ + SPAR_VBUS_CHANNEL_PROTOCOL_SIGNATURE) + +#define SPAR_VBUS_CHANNEL_OK_SERVER(actual_bytes) \ + (spar_check_channel_server(spar_vbus_channel_protocol_uuid, \ + "vbus", \ + sizeof(struct spar_vbus_channel_protocol),\ + actual_bytes)) + +#pragma pack(push, 1) /* both GCC and VC now allow this pragma */ +struct spar_vbus_headerinfo { + u32 struct_bytes; /* size of this struct in bytes */ + u32 device_info_struct_bytes; /* sizeof(ULTRA_VBUS_DEVICEINFO) */ + u32 dev_info_count; /* num of items in DevInfo member */ + /* (this is the allocated size) */ + u32 chp_info_offset; /* byte offset from beginning of this struct */ + /* to the ChpInfo struct (below) */ + u32 bus_info_offset; /* byte offset from beginning of this struct */ + /* to the BusInfo struct (below) */ + u32 dev_info_offset; /* byte offset from beginning of this struct */ + /* to the DevInfo array (below) */ + u8 reserved[104]; +}; + +struct spar_vbus_channel_protocol { + struct channel_header channel_header; /* initialized by server */ + struct spar_vbus_headerinfo hdr_info; /* initialized by server */ + /* the remainder of this channel is filled in by the client */ + struct ultra_vbus_deviceinfo chp_info; + /* describes client chipset device and driver */ + struct ultra_vbus_deviceinfo bus_info; + /* describes client bus device and driver */ + struct ultra_vbus_deviceinfo dev_info[0]; + /* describes client device and driver for each device on the bus */ +}; + +#define VBUS_CH_SIZE_EXACT(MAXDEVICES) \ + (sizeof(ULTRA_VBUS_CHANNEL_PROTOCOL) + ((MAXDEVICES) * \ + sizeof(ULTRA_VBUS_DEVICEINFO))) +#define VBUS_CH_SIZE(MAXDEVICES) COVER(VBUS_CH_SIZE_EXACT(MAXDEVICES), 4096) + +#pragma pack(pop) + +#endif diff --git a/arch/x86/visorbus/vbusdeviceinfo.h b/arch/x86/visorbus/vbusdeviceinfo.h new file mode 100644 index 0000000..abdab4a --- /dev/null +++ b/arch/x86/visorbus/vbusdeviceinfo.h @@ -0,0 +1,213 @@ +/* Copyright (C) 2010 - 2015 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __VBUSDEVICEINFO_H__ +#define __VBUSDEVICEINFO_H__ + +#include <linux/types.h> + +#pragma pack(push, 1) /* both GCC and VC now allow this pragma */ + +/* An array of this struct is present in the channel area for each vbus. + * (See vbuschannel.h.) + * It is filled in by the client side to provide info about the device + * and driver from the client's perspective. + */ +struct ultra_vbus_deviceinfo { + u8 devtype[16]; /* short string identifying the device type */ + u8 drvname[16]; /* driver .sys file name */ + u8 infostrs[96]; /* sequence of tab-delimited id strings: */ + /* <DRIVER_REV> <DRIVER_VERTAG> <DRIVER_COMPILETIME> */ + u8 reserved[128]; /* pad size to 256 bytes */ +}; + +#pragma pack(pop) + +/* Reads chars from the buffer at <src> for <srcmax> bytes, and writes to + * the buffer at <p>, which is <remain> bytes long, ensuring never to + * overflow the buffer at <p>, using the following rules: + * - printable characters are simply copied from the buffer at <src> to the + * buffer at <p> + * - intervening streaks of non-printable characters in the buffer at <src> + * are replaced with a single space in the buffer at <p> + * Note that we pay no attention to '\0'-termination. + * Returns the number of bytes written to <p>. + * + * Pass <p> == NULL and <remain> == 0 for this special behavior. In this + * case, we simply return the number of bytes that WOULD HAVE been written + * to a buffer at <p>, had it been infinitely big. + */ +static inline int +vbuschannel_sanitize_buffer(char *p, int remain, char *src, int srcmax) +{ + int chars = 0; + int nonprintable_streak = 0; + + while (srcmax > 0) { + if ((*src >= ' ') && (*src < 0x7f)) { + if (nonprintable_streak) { + if (remain > 0) { + *p = ' '; + p++; + remain--; + chars++; + } else if (!p) { + chars++; + } + nonprintable_streak = 0; + } + if (remain > 0) { + *p = *src; + p++; + remain--; + chars++; + } else if (!p) { + chars++; + } + } else { + nonprintable_streak = 1; + } + src++; + srcmax--; + } + return chars; +} + +#define VBUSCHANNEL_ADDACHAR(ch, p, remain, chars) \ + do { \ + if (remain <= 0) \ + break; \ + *p = ch; \ + p++; chars++; remain--; \ + } while (0) + +/* Converts the non-negative value at <num> to an ascii decimal string + * at <p>, writing at most <remain> bytes. Note there is NO '\0' termination + * written to <p>. + * + * Returns the number of bytes written to <p>. + * + * Note that we create this function because we need to do this operation in + * an environment-independent way (since we are in a common header file). + */ +static inline int +vbuschannel_itoa(char *p, int remain, int num) +{ + int digits = 0; + char s[32]; + int i; + + if (num == 0) { + /* '0' is a special case */ + if (remain <= 0) + return 0; + *p = '0'; + return 1; + } + /* form a backwards decimal ascii string in <s> */ + while (num > 0) { + if (digits >= (int)sizeof(s)) + return 0; + s[digits++] = (num % 10) + '0'; + num = num / 10; + } + if (remain < digits) { + /* not enough room left at <p> to hold number, so fill with + * '?' + */ + for (i = 0; i < remain; i++, p++) + *p = '?'; + return remain; + } + /* plug in the decimal ascii string representing the number, by */ + /* reversing the string we just built in <s> */ + i = digits; + while (i > 0) { + i--; + *p = s[i]; + p++; + } + return digits; +} + +/* Reads <devInfo>, and converts its contents to a printable string at <p>, + * writing at most <remain> bytes. Note there is NO '\0' termination + * written to <p>. + * + * Pass <devix> >= 0 if you want a device index presented. + * + * Returns the number of bytes written to <p>. + */ +static inline int +vbuschannel_devinfo_to_string(struct ultra_vbus_deviceinfo *devinfo, + char *p, int remain, int devix) +{ + char *psrc; + int nsrc, x, i, pad; + int chars = 0; + + psrc = &devinfo->devtype[0]; + nsrc = sizeof(devinfo->devtype); + if (vbuschannel_sanitize_buffer(NULL, 0, psrc, nsrc) <= 0) + return 0; + + /* emit device index */ + if (devix >= 0) { + VBUSCHANNEL_ADDACHAR('[', p, remain, chars); + x = vbuschannel_itoa(p, remain, devix); + p += x; + remain -= x; + chars += x; + VBUSCHANNEL_ADDACHAR(']', p, remain, chars); + } else { + VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); + VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); + VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); + } + + /* emit device type */ + x = vbuschannel_sanitize_buffer(p, remain, psrc, nsrc); + p += x; + remain -= x; + chars += x; + pad = 15 - x; /* pad device type to be exactly 15 chars */ + for (i = 0; i < pad; i++) + VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); + VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); + + /* emit driver name */ + psrc = &devinfo->drvname[0]; + nsrc = sizeof(devinfo->drvname); + x = vbuschannel_sanitize_buffer(p, remain, psrc, nsrc); + p += x; + remain -= x; + chars += x; + pad = 15 - x; /* pad driver name to be exactly 15 chars */ + for (i = 0; i < pad; i++) + VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); + VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); + + /* emit strings */ + psrc = &devinfo->infostrs[0]; + nsrc = sizeof(devinfo->infostrs); + x = vbuschannel_sanitize_buffer(p, remain, psrc, nsrc); + p += x; + remain -= x; + chars += x; + VBUSCHANNEL_ADDACHAR('\n', p, remain, chars); + + return chars; +} + +#endif diff --git a/arch/x86/visorbus/vbushelper.h b/arch/x86/visorbus/vbushelper.h new file mode 100644 index 0000000..f1b6aac --- /dev/null +++ b/arch/x86/visorbus/vbushelper.h @@ -0,0 +1,46 @@ +/* vbushelper.h + * + * Copyright (C) 2011 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __VBUSHELPER_H__ +#define __VBUSHELPER_H__ + +/* TARGET_HOSTNAME specified as -DTARGET_HOSTNAME=\"thename\" on the + * command line + */ + +#define TARGET_HOSTNAME "linuxguest" + +static inline void bus_device_info_init( + struct ultra_vbus_deviceinfo *bus_device_info_ptr, + const char *dev_type, const char *drv_name, + const char *ver, const char *ver_tag) +{ + memset(bus_device_info_ptr, 0, sizeof(struct ultra_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), "%s\t%s\t%s", + (ver) ? ver : "unknownVer", + (ver_tag) ? ver_tag : "unknownVerTag", + TARGET_HOSTNAME); +} + +#endif diff --git a/arch/x86/visorbus/visorbus_main.c b/arch/x86/visorbus/visorbus_main.c new file mode 100644 index 0000000..efd87c6 --- /dev/null +++ b/arch/x86/visorbus/visorbus_main.c @@ -0,0 +1,1344 @@ +/* visorbus_main.c + * + * Copyright � 2010 - 2015 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#include <linux/uuid.h> + +#include <linux/visorbus/visorbus.h> +#include <linux/visorbus/version.h> +#include <linux/visorbus/periodic_work.h> +#include <linux/visorbus/guestlinuxdebug.h> +#include "visorbus_private.h" +#include "vbuschannel.h" +#include "vmcallinterface.h" + +#define MYDRVNAME "visorbus" + +/* module parameters */ +static int visorbus_debug; +static int visorbus_forcematch; +static int visorbus_forcenomatch; +static int visorbus_debugref; +#define SERIALLOOPBACKCHANADDR (100 * 1024 * 1024) + +/* Display string that is guaranteed to be no longer the 99 characters*/ +#define LINESIZE 99 + +#define CURRENT_FILE_PC VISOR_BUS_PC_visorbus_main_c +#define POLLJIFFIES_TESTWORK 100 +#define POLLJIFFIES_NORMALCHANNEL 10 + +static int busreg_rc = -ENODEV; /* stores the result from bus registration */ + +static int visorbus_uevent(struct device *xdev, struct kobj_uevent_env *env); +static int visorbus_match(struct device *xdev, struct device_driver *xdrv); +static void fix_vbus_dev_info(struct visor_device *visordev); + +/* BUS type attributes + * + * define & implement display of bus attributes under + * /sys/bus/visorbus. + * + */ + +static ssize_t version_show(struct bus_type *bus, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", VERSION); +} + +static BUS_ATTR_RO(version); + +static struct attribute *visorbus_bus_attrs[] = { + &bus_attr_version.attr, + NULL, +}; + +static const struct attribute_group visorbus_bus_group = { + .attrs = visorbus_bus_attrs, +}; + +static const struct attribute_group *visorbus_bus_groups[] = { + &visorbus_bus_group, + NULL, +}; + +/* + * 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; + uuid_le guid; + + vdev = to_visor_device(dev); + guid = visorchannel_get_uuid(vdev->visorchannel); + return snprintf(buf, PAGE_SIZE, "visorbus:%pUl\n", &guid); +} +static DEVICE_ATTR_RO(modalias); + +static struct attribute *visorbus_dev_attrs[] = { + &dev_attr_modalias.attr, + NULL, +}; + +/* sysfs example for bridge-only sysfs files using device_type's */ +static const struct attribute_group visorbus_dev_group = { + .attrs = visorbus_dev_attrs, +}; + +static const struct attribute_group *visorbus_dev_groups[] = { + &visorbus_dev_group, + NULL, +}; + +/** This describes the TYPE of bus. + * (Don't confuse this with an INSTANCE of the bus.) + */ +struct bus_type visorbus_type = { + .name = "visorbus", + .match = visorbus_match, + .uevent = visorbus_uevent, + .dev_groups = visorbus_dev_groups, + .bus_groups = visorbus_bus_groups, +}; + +static struct delayed_work periodic_work; + +/* YES, we need 2 workqueues. + * The reason is, workitems on the test queue may need to cancel + * workitems on the other queue. You will be in for trouble if you try to + * do this with workitems queued on the same workqueue. + */ +static struct workqueue_struct *periodic_test_workqueue; +static struct workqueue_struct *periodic_dev_workqueue; +static long long bus_count; /** number of bus instances */ + /** ever-increasing */ + +static void chipset_bus_create(struct visor_device *bus_info); +static void chipset_bus_destroy(struct visor_device *bus_info); +static void chipset_device_create(struct visor_device *dev_info); +static void chipset_device_destroy(struct visor_device *dev_info); +static void chipset_device_pause(struct visor_device *dev_info); +static void chipset_device_resume(struct visor_device *dev_info); + +/** These functions are implemented herein, and are called by the chipset + * driver to notify us about specific events. + */ +static struct visorchipset_busdev_notifiers chipset_notifiers = { + .bus_create = chipset_bus_create, + .bus_destroy = chipset_bus_destroy, + .device_create = chipset_device_create, + .device_destroy = chipset_device_destroy, + .device_pause = chipset_device_pause, + .device_resume = chipset_device_resume, +}; + +/** These functions are implemented in the chipset driver, and we call them + * herein when we want to acknowledge a specific event. + */ +static struct visorchipset_busdev_responders chipset_responders; + +/* filled in with info about parent chipset driver when we register with it */ +static struct ultra_vbus_deviceinfo chipset_driverinfo; +/* filled in with info about this driver, wrt it servicing client busses */ +static struct ultra_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); + +static int +visorbus_uevent(struct device *xdev, struct kobj_uevent_env *env) +{ + struct visor_device *dev; + uuid_le guid; + + dev = to_visor_device(xdev); + guid = visorchannel_get_uuid(dev->visorchannel); + + if (add_uevent_var(env, "MODALIAS=visorbus:%pUl", &guid)) + return -ENOMEM; + return 0; +} + +/* This is called automatically upon adding a visor_device (device_add), or + * adding a visor_driver (visorbus_register_visor_driver), and returns 1 iff the + * provided driver can control the specified device. + */ +static int +visorbus_match(struct device *xdev, struct device_driver *xdrv) +{ + uuid_le channel_type; + int i; + struct visor_device *dev; + struct visor_driver *drv; + + dev = to_visor_device(xdev); + drv = to_visor_driver(xdrv); + channel_type = visorchannel_get_uuid(dev->visorchannel); + + if (visorbus_forcematch) + return 1; + if (visorbus_forcenomatch) + return 0; + if (!drv->channel_types) + return 0; + + for (i = 0; + (uuid_le_cmp(drv->channel_types[i].guid, NULL_UUID_LE) != 0) || + (drv->channel_types[i].name); + i++) + if (uuid_le_cmp(drv->channel_types[i].guid, + channel_type) == 0) + return i + 1; + + return 0; +} + +/** This is called when device_unregister() is called for the bus device + * instance, after all other tasks involved with destroying the device + * are complete. + */ +static void +visorbus_release_busdevice(struct device *xdev) +{ + struct visor_device *dev = dev_get_drvdata(xdev); + + kfree(dev); +} + +/** This is called when device_unregister() is called for each child + * device instance. + */ +static void +visorbus_release_device(struct device *xdev) +{ + struct visor_device *dev = to_visor_device(xdev); + + if (dev->periodic_work) { + visor_periodic_work_destroy(dev->periodic_work); + dev->periodic_work = NULL; + } + if (dev->visorchannel) { + visorchannel_destroy(dev->visorchannel); + dev->visorchannel = NULL; + } + kfree(dev); +} + +/* begin implementation of 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); + + if (!vdev->visorchannel) + return 0; + return snprintf(buf, PAGE_SIZE, "0x%llx\n", + visorchannel_get_physaddr(vdev->visorchannel)); +} + +static ssize_t nbytes_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct visor_device *vdev = to_visor_device(dev); + + if (!vdev->visorchannel) + return 0; + return snprintf(buf, PAGE_SIZE, "0x%lx\n", + visorchannel_get_nbytes(vdev->visorchannel)); +} + +static ssize_t clientpartition_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct visor_device *vdev = to_visor_device(dev); + + if (!vdev->visorchannel) + return 0; + return snprintf(buf, PAGE_SIZE, "0x%llx\n", + visorchannel_get_clientpartition(vdev->visorchannel)); +} + +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]; + + if (!vdev->visorchannel) + return 0; + return snprintf(buf, PAGE_SIZE, "%s\n", + visorchannel_id(vdev->visorchannel, typeid)); +} + +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]; + + if (!vdev->visorchannel) + return 0; + return snprintf(buf, PAGE_SIZE, "%s\n", + visorchannel_zoneid(vdev->visorchannel, zoneid)); +} + +static ssize_t typename_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct visor_device *vdev = to_visor_device(dev); + int i = 0; + struct bus_type *xbus = dev->bus; + struct device_driver *xdrv = dev->driver; + struct visor_driver *drv = NULL; + + if (!vdev->visorchannel || !xbus || !xdrv) + return 0; + i = xbus->match(dev, xdrv); + if (!i) + return 0; + drv = to_visor_driver(xdrv); + return snprintf(buf, PAGE_SIZE, "%s\n", drv->channel_types[i - 1].name); +} + +static DEVICE_ATTR_RO(physaddr); +static DEVICE_ATTR_RO(nbytes); +static DEVICE_ATTR_RO(clientpartition); +static DEVICE_ATTR_RO(typeguid); +static DEVICE_ATTR_RO(zoneguid); +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 +}; + +static struct attribute_group channel_attr_grp = { + .name = "channel", + .attrs = channel_attrs, +}; + +static const struct attribute_group *visorbus_channel_groups[] = { + &channel_attr_grp, + NULL +}; + +/* end implementation of specific channel attributes */ + +/* BUS instance attributes + * + * define & implement display of bus attributes under + * /sys/bus/visorbus/busses/visorbus<n>. + * + * This is a bit hoaky because the kernel does not yet have the infrastructure + * to separate bus INSTANCE attributes from bus TYPE attributes... + * so we roll our own. See businst.c / businst.h. + * + */ + +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 snprintf(buf, PAGE_SIZE, "0x%llx\n", 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 snprintf(buf, PAGE_SIZE, "{%pUb}\n", &vdev->partition_uuid); +} + +static ssize_t partition_name_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct visor_device *vdev = to_visor_device(dev); + + return snprintf(buf, PAGE_SIZE, "%s\n", vdev->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 snprintf(buf, PAGE_SIZE, "0x%llx\n", 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 snprintf(buf, PAGE_SIZE, "0x%llx\n", nbytes); +} + +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; + + if (vdev->visorchannel) { + visorchannel_id(vdev->visorchannel, buf); + len = strlen(buf); + buf[len++] = '\n'; + } + return len; +} + +static ssize_t client_bus_info_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct visor_device *vdev = to_visor_device(dev); + struct visorchannel *channel = vdev->visorchannel; + + int i, shift, remain = PAGE_SIZE; + unsigned long off; + char *pos = buf; + u8 *partition_name; + struct ultra_vbus_deviceinfo dev_info; + + partition_name = ""; + if (channel) { + if (vdev->name) + partition_name = vdev->name; + shift = snprintf(pos, remain, + "Client device / client driver info for %s eartition (vbus #%d):\n", + partition_name, vdev->chipset_dev_no); + pos += shift; + remain -= shift; + shift = visorchannel_read(channel, + offsetof(struct + spar_vbus_channel_protocol, + chp_info), + &dev_info, sizeof(dev_info)); + if (shift >= 0) { + shift = vbuschannel_devinfo_to_string(&dev_info, pos, + remain, -1); + pos += shift; + remain -= shift; + } + shift = visorchannel_read(channel, + offsetof(struct + spar_vbus_channel_protocol, + bus_info), + &dev_info, sizeof(dev_info)); + if (shift >= 0) { + shift = vbuschannel_devinfo_to_string(&dev_info, pos, + remain, -1); + pos += shift; + remain -= shift; + } + off = offsetof(struct spar_vbus_channel_protocol, dev_info); + i = 0; + while (off + sizeof(dev_info) <= + visorchannel_get_nbytes(channel)) { + shift = visorchannel_read(channel, + off, &dev_info, + sizeof(dev_info)); + if (shift >= 0) { + shift = vbuschannel_devinfo_to_string + (&dev_info, pos, remain, i); + pos += shift; + remain -= shift; + } + off += sizeof(dev_info); + i++; + } + } + return PAGE_SIZE - remain; +} + +static DEVICE_ATTR_RO(partition_handle); +static DEVICE_ATTR_RO(partition_guid); +static DEVICE_ATTR_RO(partition_name); +static DEVICE_ATTR_RO(channel_addr); +static DEVICE_ATTR_RO(channel_bytes); +static DEVICE_ATTR_RO(channel_id); +static DEVICE_ATTR_RO(client_bus_info); + +static struct attribute *dev_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, + &dev_attr_client_bus_info.attr, + NULL +}; + +static struct attribute_group dev_attr_grp = { + .attrs = dev_attrs, +}; + +static const struct attribute_group *visorbus_groups[] = { + &dev_attr_grp, + NULL +}; + +/* DRIVER attributes + * + * define & implement display of driver attributes under + * /sys/bus/visorbus/drivers/<drivername>. + * + */ + +static ssize_t +DRIVER_ATTR_version(struct device_driver *xdrv, char *buf) +{ + struct visor_driver *drv = to_visor_driver(xdrv); + + return snprintf(buf, PAGE_SIZE, "%s\n", drv->version); +} + +static int +register_driver_attributes(struct visor_driver *drv) +{ + struct driver_attribute version = + __ATTR(version, S_IRUGO, DRIVER_ATTR_version, NULL); + drv->version_attr = version; + return driver_create_file(&drv->driver, &drv->version_attr); +} + +static void +unregister_driver_attributes(struct visor_driver *drv) +{ + driver_remove_file(&drv->driver, &drv->version_attr); +} + +static void +dev_periodic_work(void *xdev) +{ + struct visor_device *dev = xdev; + struct visor_driver *drv = to_visor_driver(dev->device.driver); + + down(&dev->visordriver_callback_lock); + if (drv->channel_interrupt) + drv->channel_interrupt(dev); + up(&dev->visordriver_callback_lock); + if (!visor_periodic_work_nextperiod(dev->periodic_work)) + put_device(&dev->device); +} + +static void +dev_start_periodic_work(struct visor_device *dev) +{ + if (dev->being_removed) + return; + /* now up by at least 2 */ + get_device(&dev->device); + if (!visor_periodic_work_start(dev->periodic_work)) + put_device(&dev->device); +} + +static void +dev_stop_periodic_work(struct visor_device *dev) +{ + if (visor_periodic_work_stop(dev->periodic_work)) + put_device(&dev->device); +} + +/** 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. + */ +static int +visordriver_probe_device(struct device *xdev) +{ + int res; + struct visor_driver *drv; + struct visor_device *dev; + + drv = to_visor_driver(xdev->driver); + dev = to_visor_device(xdev); + + if (!drv->probe) + return -ENODEV; + + down(&dev->visordriver_callback_lock); + dev->being_removed = false; + + res = drv->probe(dev); + if (res >= 0) { + /* success: reference kept via unmatched get_device() */ + get_device(&dev->device); + fix_vbus_dev_info(dev); + } + + up(&dev->visordriver_callback_lock); + return res; +} + +/** This is called when device_unregister() is called for each child device + * instance, to notify the appropriate visorbus_driver that the device is + * going away, and to decrease the reference count of the device. + */ +static int +visordriver_remove_device(struct device *xdev) +{ + struct visor_device *dev; + struct visor_driver *drv; + + dev = to_visor_device(xdev); + drv = to_visor_driver(xdev->driver); + down(&dev->visordriver_callback_lock); + dev->being_removed = true; + if (drv->remove) + drv->remove(dev); + up(&dev->visordriver_callback_lock); + dev_stop_periodic_work(dev); + + put_device(&dev->device); + return 0; +} + +/** A particular type of visor 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(). + * + */ +int visorbus_register_visor_driver(struct visor_driver *drv) +{ + int rc = 0; + + if (busreg_rc < 0) + return -ENODEV; /*can't register on a nonexistent bus*/ + + 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 + */ + + rc = driver_register(&drv->driver); + if (rc < 0) + return rc; + rc = register_driver_attributes(drv); + if (rc < 0) + driver_unregister(&drv->driver); + return rc; +} +EXPORT_SYMBOL_GPL(visorbus_register_visor_driver); + +/** A particular type of visor driver calls this function to unregister + * the driver, i.e., within its module_exit function. + */ +void +visorbus_unregister_visor_driver(struct visor_driver *drv) +{ + unregister_driver_attributes(drv); + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(visorbus_unregister_visor_driver); + +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); + +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); + +int +visorbus_clear_channel(struct visor_device *dev, unsigned long offset, u8 ch, + unsigned long nbytes) +{ + return visorchannel_clear(dev->visorchannel, offset, ch, nbytes); +} +EXPORT_SYMBOL_GPL(visorbus_clear_channel); + +/** We don't really have a real interrupt, so for now we just call the + * interrupt function periodically... + */ +void +visorbus_enable_channel_interrupts(struct visor_device *dev) +{ + dev_start_periodic_work(dev); +} +EXPORT_SYMBOL_GPL(visorbus_enable_channel_interrupts); + +void +visorbus_disable_channel_interrupts(struct visor_device *dev) +{ + dev_stop_periodic_work(dev); +} +EXPORT_SYMBOL_GPL(visorbus_disable_channel_interrupts); + +/** 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. + */ +static 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; + + POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, chipset_dev_no, chipset_bus_no, + POSTCODE_SEVERITY_INFO); + + sema_init(&dev->visordriver_callback_lock, 1); /* unlocked */ + dev->device.bus = &visorbus_type; + dev->device.groups = visorbus_channel_groups; + device_initialize(&dev->device); + dev->device.release = visorbus_release_device; + /* keep a reference just for us (now 2) */ + get_device(&dev->device); + dev->periodic_work = + visor_periodic_work_create(POLLJIFFIES_NORMALCHANNEL, + periodic_dev_workqueue, + dev_periodic_work, + dev, dev_name(&dev->device)); + if (!dev->periodic_work) { + POSTCODE_LINUX_3(DEVICE_CREATE_FAILURE_PC, chipset_dev_no, + DIAG_SEVERITY_ERR); + err = -EINVAL; + goto err_put; + } + + /* 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. + */ + dev_set_name(&dev->device, "vbus%u:dev%u", + chipset_bus_no, chipset_dev_no); + + /* 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) { + POSTCODE_LINUX_3(DEVICE_ADD_PC, chipset_bus_no, + DIAG_SEVERITY_ERR); + goto err_put; + } + + list_add_tail(&dev->list_all, &list_all_device_instances); + return 0; /* success: reference kept via unmatched get_device() */ + +err_put: + put_device(&dev->device); + return err; +} + +static void +remove_visor_device(struct visor_device *dev) +{ + list_del(&dev->list_all); + put_device(&dev->device); + device_unregister(&dev->device); +} + +static int +get_vbus_header_info(struct visorchannel *chan, + struct spar_vbus_headerinfo *hdr_info) +{ + if (!SPAR_VBUS_CHANNEL_OK_CLIENT(visorchannel_get_header(chan))) + return -EINVAL; + + if (visorchannel_read(chan, sizeof(struct channel_header), hdr_info, + sizeof(*hdr_info)) < 0) { + return -EIO; + } + if (hdr_info->struct_bytes < sizeof(struct spar_vbus_headerinfo)) + return -EINVAL; + + if (hdr_info->device_info_struct_bytes < + sizeof(struct ultra_vbus_deviceinfo)) { + return -EINVAL; + } + return 0; +} + +/* Write the contents of <info> to the struct + * spar_vbus_channel_protocol.chp_info. + */ + +static int +write_vbus_chp_info(struct visorchannel *chan, + struct spar_vbus_headerinfo *hdr_info, + struct ultra_vbus_deviceinfo *info) +{ + int off = sizeof(struct channel_header) + hdr_info->chp_info_offset; + + if (hdr_info->chp_info_offset == 0) + return -EFAULT; + + if (visorchannel_write(chan, off, info, sizeof(*info)) < 0) + return -EFAULT; + return 0; +} + +/* Write the contents of <info> to the struct + * spar_vbus_channel_protocol.bus_info. + */ + +static int +write_vbus_bus_info(struct visorchannel *chan, + struct spar_vbus_headerinfo *hdr_info, + struct ultra_vbus_deviceinfo *info) +{ + int off = sizeof(struct channel_header) + hdr_info->bus_info_offset; + + if (hdr_info->bus_info_offset == 0) + return -EFAULT; + + if (visorchannel_write(chan, off, info, sizeof(*info)) < 0) + return -EFAULT; + return 0; +} + +/* Write the contents of <info> to the + * struct spar_vbus_channel_protocol.dev_info[<devix>]. + */ +static int +write_vbus_dev_info(struct visorchannel *chan, + struct spar_vbus_headerinfo *hdr_info, + struct ultra_vbus_deviceinfo *info, int devix) +{ + int off = + (sizeof(struct channel_header) + hdr_info->dev_info_offset) + + (hdr_info->device_info_struct_bytes * devix); + + if (hdr_info->dev_info_offset == 0) + return -EFAULT; + + if (visorchannel_write(chan, off, info, sizeof(*info)) < 0) + return -EFAULT; + return 0; +} + +/* For a child device just created on a client bus, fill in + * information about the driver that is controlling this device into + * the the appropriate slot within the vbus channel of the bus + * instance. + */ +static void +fix_vbus_dev_info(struct visor_device *visordev) +{ + int i; + struct visor_device *bdev; + struct visor_driver *visordrv; + int bus_no = visordev->chipset_bus_no; + int dev_no = visordev->chipset_dev_no; + struct ultra_vbus_deviceinfo dev_info; + const char *chan_type_name = NULL; + struct spar_vbus_headerinfo *hdr_info; + + if (!visordev->device.driver) + return; + + hdr_info = (struct spar_vbus_headerinfo *)visordev->vbus_hdr_info; + if (!hdr_info) + return; + + bdev = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); + if (!bdev) + 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 (memcmp(&visordrv->channel_types[i].guid, + &visordev->channel_type_guid, + sizeof(visordrv->channel_types[i].guid)) == 0) { + chan_type_name = visordrv->channel_types[i].name; + break; + } + } + + bus_device_info_init(&dev_info, chan_type_name, + visordrv->name, visordrv->version, + visordrv->vertag); + write_vbus_dev_info(bdev->visorchannel, hdr_info, &dev_info, dev_no); + + /* Re-write bus+chipset info, because it is possible that this + * was previously written by our evil counterpart, virtpci. + */ + write_vbus_chp_info(bdev->visorchannel, hdr_info, &chipset_driverinfo); + write_vbus_bus_info(bdev->visorchannel, hdr_info, + &clientbus_driverinfo); +} + +/** Create a device instance for the visor bus itself. + */ +static int +create_bus_instance(struct visor_device *dev) +{ + int id = dev->chipset_bus_no; + struct spar_vbus_headerinfo *hdr_info; + + POSTCODE_LINUX_2(BUS_CREATE_ENTRY_PC, POSTCODE_SEVERITY_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; + + if (device_register(&dev->device) < 0) { + POSTCODE_LINUX_3(DEVICE_CREATE_FAILURE_PC, id, + POSTCODE_SEVERITY_ERR); + kfree(hdr_info); + return -ENODEV; + } + + if (get_vbus_header_info(dev->visorchannel, hdr_info) >= 0) { + 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); + } else { + kfree(hdr_info); + } + bus_count++; + list_add_tail(&dev->list_all, &list_all_bus_instances); + dev_set_drvdata(&dev->device, dev); + return 0; +} + +/** Remove a device instance for the visor bus itself. + */ +static void +remove_bus_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. + */ + bus_count--; + if (dev->visorchannel) { + visorchannel_destroy(dev->visorchannel); + dev->visorchannel = NULL; + } + kfree(dev->vbus_hdr_info); + list_del(&dev->list_all); + device_unregister(&dev->device); +} + +/** Create and register the one-and-only one instance of + * the visor bus type (visorbus_type). + */ +static int +create_bus_type(void) +{ + busreg_rc = bus_register(&visorbus_type); + return busreg_rc; +} + +/** Remove the one-and-only one instance of the visor bus type (visorbus_type). + */ +static void +remove_bus_type(void) +{ + bus_unregister(&visorbus_type); +} + +/** Remove all child visor bus 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 = list_entry(listentry, + struct visor_device, + list_all); + remove_visor_device(dev); + } +} + +static void +chipset_bus_create(struct visor_device *dev) +{ + int rc; + u32 bus_no = dev->chipset_bus_no; + + POSTCODE_LINUX_3(BUS_CREATE_ENTRY_PC, bus_no, POSTCODE_SEVERITY_INFO); + rc = create_bus_instance(dev); + POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, bus_no, POSTCODE_SEVERITY_INFO); + + if (rc < 0) + POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no, + POSTCODE_SEVERITY_ERR); + else + POSTCODE_LINUX_3(CHIPSET_INIT_SUCCESS_PC, bus_no, + POSTCODE_SEVERITY_INFO); + + if (chipset_responders.bus_create) + (*chipset_responders.bus_create) (dev, rc); +} + +static void +chipset_bus_destroy(struct visor_device *dev) +{ + remove_bus_instance(dev); + if (chipset_responders.bus_destroy) + (*chipset_responders.bus_destroy)(dev, 0); +} + +static void +chipset_device_create(struct visor_device *dev_info) +{ + int rc; + u32 bus_no = dev_info->chipset_bus_no; + u32 dev_no = dev_info->chipset_dev_no; + + POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, dev_no, bus_no, + POSTCODE_SEVERITY_INFO); + + rc = create_visor_device(dev_info); + if (chipset_responders.device_create) + chipset_responders.device_create(dev_info, rc); + + if (rc < 0) + POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no, + POSTCODE_SEVERITY_ERR); + else + POSTCODE_LINUX_4(DEVICE_CREATE_SUCCESS_PC, dev_no, bus_no, + POSTCODE_SEVERITY_INFO); +} + +static void +chipset_device_destroy(struct visor_device *dev_info) +{ + remove_visor_device(dev_info); + + if (chipset_responders.device_destroy) + (*chipset_responders.device_destroy) (dev_info, 0); +} + +/* This is the callback function specified for a function driver, to + * be called when a pending "pause device" operation has been + * completed. + */ +static void +pause_state_change_complete(struct visor_device *dev, int status) +{ + if (!dev->pausing) + return; + + dev->pausing = false; + if (!chipset_responders.device_pause) /* this can never happen! */ + return; + + /* Notify the chipset driver that the pause is complete, which + * will presumably want to send some sort of response to the + * initiator. + */ + (*chipset_responders.device_pause) (dev, status); +} + +/* This is the callback function specified for a function driver, to + * be called when a pending "resume device" operation has been + * completed. + */ +static void +resume_state_change_complete(struct visor_device *dev, int status) +{ + if (!dev->resuming) + return; + + dev->resuming = false; + if (!chipset_responders.device_resume) /* this can never happen! */ + return; + + /* Notify the chipset driver that the resume is complete, + * which will presumably want to send some sort of response to + * the initiator. + */ + (*chipset_responders.device_resume) (dev, status); +} + +/* Tell the subordinate function driver for a specific device to pause + * or resume that device. Result is returned asynchronously via a + * callback function. + */ +static void +initiate_chipset_device_pause_resume(struct visor_device *dev, bool is_pause) +{ + int rc; + struct visor_driver *drv = NULL; + void (*notify_func)(struct visor_device *dev, int response) = NULL; + + if (is_pause) + notify_func = chipset_responders.device_pause; + else + notify_func = chipset_responders.device_resume; + if (!notify_func) + return; + + drv = to_visor_driver(dev->device.driver); + if (!drv) { + (*notify_func)(dev, -ENODEV); + return; + } + + if (dev->pausing || dev->resuming) { + (*notify_func)(dev, -EBUSY); + return; + } + + /* Note that even though both drv->pause() and drv->resume + * specify a callback function, it is NOT necessary for us to + * increment our local module usage count. Reason is, there + * is already a linkage dependency between child function + * drivers and visorbus, so it is already IMPOSSIBLE to unload + * visorbus while child function drivers are still running. + */ + if (is_pause) { + if (!drv->pause) { + (*notify_func)(dev, -EINVAL); + return; + } + + dev->pausing = true; + rc = drv->pause(dev, pause_state_change_complete); + } else { + /* This should be done at BUS resume time, but an + * existing problem prevents us from ever getting a bus + * resume... This hack would fail to work should we + * ever have a bus that contains NO devices, since we + * would never even get here in that case. + */ + fix_vbus_dev_info(dev); + if (!drv->resume) { + (*notify_func)(dev, -EINVAL); + return; + } + + dev->resuming = true; + rc = drv->resume(dev, resume_state_change_complete); + } + if (rc < 0) { + if (is_pause) + dev->pausing = false; + else + dev->resuming = false; + (*notify_func)(dev, -EINVAL); + } +} + +static void +chipset_device_pause(struct visor_device *dev_info) +{ + initiate_chipset_device_pause_resume(dev_info, true); +} + +static void +chipset_device_resume(struct visor_device *dev_info) +{ + initiate_chipset_device_pause_resume(dev_info, false); +} + +struct channel_size_info { + uuid_le guid; + unsigned long min_size; + unsigned long max_size; +}; + +int +visorbus_init(void) +{ + int err; + + POSTCODE_LINUX_3(DRIVER_ENTRY_PC, 0, POSTCODE_SEVERITY_INFO); + bus_device_info_init(&clientbus_driverinfo, + "clientbus", "visorbus", + VERSION, NULL); + + err = create_bus_type(); + if (err < 0) { + POSTCODE_LINUX_2(BUS_CREATE_ENTRY_PC, DIAG_SEVERITY_ERR); + goto error; + } + + periodic_dev_workqueue = create_singlethread_workqueue("visorbus_dev"); + if (!periodic_dev_workqueue) { + POSTCODE_LINUX_2(CREATE_WORKQUEUE_PC, DIAG_SEVERITY_ERR); + err = -ENOMEM; + goto error; + } + + /* This enables us to receive notifications when devices appear for + * which this service partition is to be a server for. + */ + visorchipset_register_busdev(&chipset_notifiers, + &chipset_responders, + &chipset_driverinfo); + + return 0; + +error: + POSTCODE_LINUX_3(CHIPSET_INIT_FAILURE_PC, err, POSTCODE_SEVERITY_ERR); + return err; +} + +void +visorbus_exit(void) +{ + struct list_head *listentry, *listtmp; + + visorchipset_register_busdev(NULL, NULL, NULL); + remove_all_visor_devices(); + + flush_workqueue(periodic_dev_workqueue); /* better not be any work! */ + destroy_workqueue(periodic_dev_workqueue); + periodic_dev_workqueue = NULL; + + if (periodic_test_workqueue) { + cancel_delayed_work(&periodic_work); + flush_workqueue(periodic_test_workqueue); + destroy_workqueue(periodic_test_workqueue); + periodic_test_workqueue = NULL; + } + + list_for_each_safe(listentry, listtmp, &list_all_bus_instances) { + struct visor_device *dev = list_entry(listentry, + struct visor_device, + list_all); + remove_bus_instance(dev); + } + remove_bus_type(); +} + +module_param_named(debug, visorbus_debug, int, S_IRUGO); +MODULE_PARM_DESC(visorbus_debug, "1 to debug"); + +module_param_named(forcematch, visorbus_forcematch, int, S_IRUGO); +MODULE_PARM_DESC(visorbus_forcematch, + "1 to force a successful dev <--> drv match"); + +module_param_named(forcenomatch, visorbus_forcenomatch, int, S_IRUGO); +MODULE_PARM_DESC(visorbus_forcenomatch, + "1 to force an UNsuccessful dev <--> drv match"); + +module_param_named(debugref, visorbus_debugref, int, S_IRUGO); +MODULE_PARM_DESC(visorbus_debugref, "1 to debug reference counting"); diff --git a/arch/x86/visorbus/visorbus_private.h b/arch/x86/visorbus/visorbus_private.h new file mode 100644 index 0000000..39edd20 --- /dev/null +++ b/arch/x86/visorbus/visorbus_private.h @@ -0,0 +1,68 @@ +/* visorchipset.h + * + * Copyright (C) 2010 - 2015 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __VISORCHIPSET_H__ +#define __VISORCHIPSET_H__ + +#include <linux/uuid.h> + +#include "controlvmchannel.h" +#include "vbusdeviceinfo.h" +#include "vbushelper.h" + +/* These functions will be called from within visorchipset when certain + * events happen. (The implementation of these functions is outside of + * visorchipset.) + */ +struct visorchipset_busdev_notifiers { + void (*bus_create)(struct visor_device *bus_info); + void (*bus_destroy)(struct visor_device *bus_info); + void (*device_create)(struct visor_device *bus_info); + void (*device_destroy)(struct visor_device *bus_info); + void (*device_pause)(struct visor_device *bus_info); + void (*device_resume)(struct visor_device *bus_info); +}; + +/* These functions live inside visorchipset, and will be called to indicate + * responses to specific events (by code outside of visorchipset). + * For now, the value for each response is simply either: + * 0 = it worked + * -1 = it failed + */ +struct visorchipset_busdev_responders { + void (*bus_create)(struct visor_device *p, int response); + void (*bus_destroy)(struct visor_device *p, int response); + void (*device_create)(struct visor_device *p, int response); + void (*device_destroy)(struct visor_device *p, int response); + void (*device_pause)(struct visor_device *p, int response); + void (*device_resume)(struct visor_device *p, int response); +}; + +/** Register functions (in the bus driver) to get called by visorchipset + * whenever a bus or device appears for which this guest is to be the + * client for. visorchipset will fill in <responders>, to indicate + * functions the bus driver should call to indicate message responses. + */ +void +visorchipset_register_busdev( + struct visorchipset_busdev_notifiers *notifiers, + struct visorchipset_busdev_responders *responders, + struct ultra_vbus_deviceinfo *driver_info); + +/* visorbus init and exit functions */ +int visorbus_init(void); +void visorbus_exit(void); +#endif diff --git a/arch/x86/visorbus/visorchannel.c b/arch/x86/visorbus/visorchannel.c new file mode 100644 index 0000000..4e8b0ef --- /dev/null +++ b/arch/x86/visorbus/visorchannel.c @@ -0,0 +1,635 @@ +/* visorchannel_funcs.c + * + * Copyright (C) 2010 - 2015 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* + * This provides Supervisor 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/visorbus/version.h> +#include <linux/visorbus/visorbus.h> +#include "controlvmchannel.h" + +#define MYDRVNAME "visorchannel" + +#define SPAR_CONSOLEVIDEO_CHANNEL_PROTOCOL_GUID \ + UUID_LE(0x3cd6e705, 0xd6a2, 0x4aa5, \ + 0xad, 0x5c, 0x7b, 0x8, 0x88, 0x9d, 0xff, 0xe2) +static const uuid_le spar_video_guid = SPAR_CONSOLEVIDEO_CHANNEL_PROTOCOL_GUID; + +struct visorchannel { + u64 physaddr; + ulong nbytes; + void *mapped; + bool requested; + struct channel_header chan_hdr; + uuid_le guid; + bool needs_lock; /* channel creator knows if more than one */ + /* thread will be inserting or removing */ + spinlock_t insert_lock; /* protect head writes in chan_hdr */ + spinlock_t remove_lock; /* protect tail writes in chan_hdr */ + + struct { + struct signal_queue_header req_queue; + struct signal_queue_header rsp_queue; + struct signal_queue_header event_queue; + struct signal_queue_header ack_queue; + } safe_uis_queue; + uuid_le type; + uuid_le inst; +}; + +/* Creates the struct visorchannel abstraction for a data area in memory, + * but does NOT modify this data area. + */ +static struct visorchannel * +visorchannel_create_guts(u64 physaddr, unsigned long channel_bytes, + gfp_t gfp, unsigned long off, + uuid_le 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, MYDRVNAME); + if (!channel->requested) { + if (uuid_le_cmp(guid, spar_video_guid)) { + /* Not the video channel we care about this */ + 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, + sizeof(struct channel_header)); + if (err) + goto err_destroy_channel; + + /* we had better be a CLIENT of this channel */ + if (channel_bytes == 0) + channel_bytes = (ulong)channel->chan_hdr.size; + if (uuid_le_cmp(guid, NULL_UUID_LE) == 0) + guid = channel->chan_hdr.chtype; + + memunmap(channel->mapped); + if (channel->requested) + release_mem_region(channel->physaddr, channel->nbytes); + channel->mapped = NULL; + channel->requested = request_mem_region(channel->physaddr, + channel_bytes, MYDRVNAME); + if (!channel->requested) { + if (uuid_le_cmp(guid, spar_video_guid)) { + /* Different we care about this */ + goto err_destroy_channel; + } + } + + channel->mapped = memremap(channel->physaddr, channel_bytes, + MEMREMAP_WB); + if (!channel->mapped) { + release_mem_region(channel->physaddr, channel_bytes); + goto err_destroy_channel; + } + + channel->nbytes = channel_bytes; + channel->guid = guid; + return channel; + +err_destroy_channel: + visorchannel_destroy(channel); + return NULL; +} + +struct visorchannel * +visorchannel_create(u64 physaddr, unsigned long channel_bytes, + gfp_t gfp, uuid_le guid) +{ + return visorchannel_create_guts(physaddr, channel_bytes, gfp, 0, guid, + false); +} +EXPORT_SYMBOL_GPL(visorchannel_create); + +struct visorchannel * +visorchannel_create_with_lock(u64 physaddr, unsigned long channel_bytes, + gfp_t gfp, uuid_le guid) +{ + return visorchannel_create_guts(physaddr, channel_bytes, gfp, 0, guid, + true); +} +EXPORT_SYMBOL_GPL(visorchannel_create_with_lock); + +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); +} +EXPORT_SYMBOL_GPL(visorchannel_destroy); + +u64 +visorchannel_get_physaddr(struct visorchannel *channel) +{ + return channel->physaddr; +} +EXPORT_SYMBOL_GPL(visorchannel_get_physaddr); + +ulong +visorchannel_get_nbytes(struct visorchannel *channel) +{ + return channel->nbytes; +} +EXPORT_SYMBOL_GPL(visorchannel_get_nbytes); + +char * +visorchannel_uuid_id(uuid_le *guid, char *s) +{ + sprintf(s, "%pUL", guid); + return s; +} +EXPORT_SYMBOL_GPL(visorchannel_uuid_id); + +char * +visorchannel_id(struct visorchannel *channel, char *s) +{ + return visorchannel_uuid_id(&channel->guid, s); +} +EXPORT_SYMBOL_GPL(visorchannel_id); + +char * +visorchannel_zoneid(struct visorchannel *channel, char *s) +{ + return visorchannel_uuid_id(&channel->chan_hdr.zone_uuid, s); +} +EXPORT_SYMBOL_GPL(visorchannel_zoneid); + +u64 +visorchannel_get_clientpartition(struct visorchannel *channel) +{ + return channel->chan_hdr.partition_handle; +} +EXPORT_SYMBOL_GPL(visorchannel_get_clientpartition); + +int +visorchannel_set_clientpartition(struct visorchannel *channel, + u64 partition_handle) +{ + channel->chan_hdr.partition_handle = partition_handle; + return 0; +} +EXPORT_SYMBOL_GPL(visorchannel_set_clientpartition); + +uuid_le +visorchannel_get_uuid(struct visorchannel *channel) +{ + return channel->guid; +} +EXPORT_SYMBOL_GPL(visorchannel_get_uuid); + +int +visorchannel_read(struct visorchannel *channel, ulong offset, + void *local, ulong nbytes) +{ + if (offset + nbytes > channel->nbytes) + return -EIO; + + memcpy(local, channel->mapped + offset, nbytes); + + return 0; +} +EXPORT_SYMBOL_GPL(visorchannel_read); + +int +visorchannel_write(struct visorchannel *channel, ulong offset, + void *local, 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, + local, copy_size); + } + + memcpy(channel->mapped + offset, local, nbytes); + + return 0; +} +EXPORT_SYMBOL_GPL(visorchannel_write); + +int +visorchannel_clear(struct visorchannel *channel, ulong offset, u8 ch, + ulong nbytes) +{ + int err; + int bufsize = PAGE_SIZE; + int written = 0; + u8 *buf; + + buf = (u8 *)__get_free_page(GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memset(buf, ch, bufsize); + + while (nbytes > 0) { + int thisbytes = bufsize; + + if (nbytes < thisbytes) + thisbytes = nbytes; + err = visorchannel_write(channel, offset + written, + buf, thisbytes); + if (err) + goto out_free_page; + + written += thisbytes; + nbytes -= thisbytes; + } + err = 0; + +out_free_page: + free_page((unsigned long)buf); + return err; +} +EXPORT_SYMBOL_GPL(visorchannel_clear); + +void __iomem * +visorchannel_get_header(struct visorchannel *channel) +{ + return (void __iomem *)&channel->chan_hdr; +} +EXPORT_SYMBOL_GPL(visorchannel_get_header); + +/** Return offset of a specific SIGNAL_QUEUE_HEADER from the beginning of a + * channel header + */ +#define SIG_QUEUE_OFFSET(chan_hdr, q) \ + ((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 + */ +#define SIG_DATA_OFFSET(chan_hdr, q, sig_hdr, slot) \ + (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)) >= 0) + +static bool +sig_read_header(struct visorchannel *channel, u32 queue, + struct signal_queue_header *sig_hdr) +{ + int err; + + if (channel->chan_hdr.ch_space_offset < sizeof(struct channel_header)) + return false; + + /* Read the appropriate SIGNAL_QUEUE_HEADER into local memory. */ + err = visorchannel_read(channel, + SIG_QUEUE_OFFSET(&channel->chan_hdr, queue), + sig_hdr, sizeof(struct signal_queue_header)); + if (err) + return false; + + return true; +} + +static inline bool +sig_read_data(struct visorchannel *channel, u32 queue, + struct signal_queue_header *sig_hdr, u32 slot, void *data) +{ + int err; + int signal_data_offset = SIG_DATA_OFFSET(&channel->chan_hdr, queue, + sig_hdr, slot); + + err = visorchannel_read(channel, signal_data_offset, + data, sig_hdr->signal_size); + if (err) + return false; + + return true; +} + +static inline bool +sig_write_data(struct visorchannel *channel, u32 queue, + struct signal_queue_header *sig_hdr, u32 slot, void *data) +{ + int err; + int signal_data_offset = SIG_DATA_OFFSET(&channel->chan_hdr, queue, + sig_hdr, slot); + + err = visorchannel_write(channel, signal_data_offset, + data, sig_hdr->signal_size); + if (err) + return false; + + return true; +} + +static bool +signalremove_inner(struct visorchannel *channel, u32 queue, void *msg) +{ + struct signal_queue_header sig_hdr; + + if (!sig_read_header(channel, queue, &sig_hdr)) + return false; + if (sig_hdr.head == sig_hdr.tail) + return false; /* no signals to remove */ + + sig_hdr.tail = (sig_hdr.tail + 1) % sig_hdr.max_slots; + if (!sig_read_data(channel, queue, &sig_hdr, sig_hdr.tail, msg)) + return false; + sig_hdr.num_received++; + + /* For each data field in SIGNAL_QUEUE_HEADER that was modified, + * update host memory. + */ + mb(); /* required for channel synch */ + if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, tail)) + return false; + if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, num_received)) + return false; + return true; +} + +bool +visorchannel_signalremove(struct visorchannel *channel, u32 queue, void *msg) +{ + bool 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); + +bool +visorchannel_signalempty(struct visorchannel *channel, u32 queue) +{ + unsigned long flags = 0; + struct signal_queue_header sig_hdr; + bool rc = false; + + if (channel->needs_lock) + spin_lock_irqsave(&channel->remove_lock, flags); + + if (!sig_read_header(channel, queue, &sig_hdr)) + rc = true; + if (sig_hdr.head == sig_hdr.tail) + rc = true; + if (channel->needs_lock) + spin_unlock_irqrestore(&channel->remove_lock, flags); + + return rc; +} +EXPORT_SYMBOL_GPL(visorchannel_signalempty); + +static bool +signalinsert_inner(struct visorchannel *channel, u32 queue, void *msg) +{ + struct signal_queue_header sig_hdr; + + if (!sig_read_header(channel, queue, &sig_hdr)) + return false; + + sig_hdr.head = (sig_hdr.head + 1) % sig_hdr.max_slots; + if (sig_hdr.head == sig_hdr.tail) { + sig_hdr.num_overflows++; + visorchannel_write(channel, + SIG_QUEUE_OFFSET(&channel->chan_hdr, queue) + + offsetof(struct signal_queue_header, + num_overflows), + &sig_hdr.num_overflows, + sizeof(sig_hdr.num_overflows)); + return false; + } + + if (!sig_write_data(channel, queue, &sig_hdr, sig_hdr.head, msg)) + return false; + + sig_hdr.num_sent++; + + /* For each data field in SIGNAL_QUEUE_HEADER that was modified, + * update host memory. + */ + mb(); /* required for channel synch */ + if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, head)) + return false; + if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, num_sent)) + return false; + + return true; +} + +bool +visorchannel_signalinsert(struct visorchannel *channel, u32 queue, void *msg) +{ + bool 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); + +int +visorchannel_signalqueue_slots_avail(struct visorchannel *channel, u32 queue) +{ + struct signal_queue_header sig_hdr; + u32 slots_avail, slots_used; + u32 head, tail; + + if (!sig_read_header(channel, queue, &sig_hdr)) + return 0; + head = sig_hdr.head; + tail = sig_hdr.tail; + if (head < tail) + head = head + sig_hdr.max_slots; + slots_used = head - tail; + slots_avail = sig_hdr.max_signals - slots_used; + return (int)slots_avail; +} +EXPORT_SYMBOL_GPL(visorchannel_signalqueue_slots_avail); + +int +visorchannel_signalqueue_max_slots(struct visorchannel *channel, u32 queue) +{ + struct signal_queue_header sig_hdr; + + if (!sig_read_header(channel, queue, &sig_hdr)) + return 0; + return (int)sig_hdr.max_signals; +} +EXPORT_SYMBOL_GPL(visorchannel_signalqueue_max_slots); + +static void +sigqueue_debug(struct signal_queue_header *q, int which, struct seq_file *seq) +{ + seq_printf(seq, "Signal Queue #%d\n", which); + seq_printf(seq, " VersionId = %lu\n", (ulong)q->version); + seq_printf(seq, " Type = %lu\n", (ulong)q->chtype); + seq_printf(seq, " oSignalBase = %llu\n", + (long long)q->sig_base_offset); + seq_printf(seq, " SignalSize = %lu\n", (ulong)q->signal_size); + seq_printf(seq, " MaxSignalSlots = %lu\n", + (ulong)q->max_slots); + seq_printf(seq, " MaxSignals = %lu\n", (ulong)q->max_signals); + seq_printf(seq, " FeatureFlags = %-16.16Lx\n", + (long long)q->features); + seq_printf(seq, " NumSignalsSent = %llu\n", + (long long)q->num_sent); + seq_printf(seq, " NumSignalsReceived = %llu\n", + (long long)q->num_received); + seq_printf(seq, " NumOverflows = %llu\n", + (long long)q->num_overflows); + seq_printf(seq, " Head = %lu\n", (ulong)q->head); + seq_printf(seq, " Tail = %lu\n", (ulong)q->tail); +} + +void +visorchannel_debug(struct visorchannel *channel, int num_queues, + struct seq_file *seq, u32 off) +{ + u64 addr = 0; + ulong nbytes = 0, nbytes_region = 0; + struct channel_header hdr; + struct channel_header *phdr = &hdr; + int i = 0; + int errcode = 0; + + if (!channel) + return; + + addr = visorchannel_get_physaddr(channel); + nbytes_region = visorchannel_get_nbytes(channel); + errcode = visorchannel_read(channel, off, + phdr, sizeof(struct channel_header)); + if (errcode < 0) { + seq_printf(seq, + "Read of channel header failed with errcode=%d)\n", + errcode); + if (off == 0) { + phdr = &channel->chan_hdr; + seq_puts(seq, "(following data may be stale)\n"); + } else { + return; + } + } + nbytes = (ulong)(phdr->size); + seq_printf(seq, "--- Begin channel @0x%-16.16Lx for 0x%lx bytes (region=0x%lx bytes) ---\n", + addr + off, nbytes, nbytes_region); + seq_printf(seq, "Type = %pUL\n", &phdr->chtype); + seq_printf(seq, "ZoneGuid = %pUL\n", &phdr->zone_uuid); + seq_printf(seq, "Signature = 0x%-16.16Lx\n", + (long long)phdr->signature); + seq_printf(seq, "LegacyState = %lu\n", (ulong)phdr->legacy_state); + seq_printf(seq, "SrvState = %lu\n", (ulong)phdr->srv_state); + seq_printf(seq, "CliStateBoot = %lu\n", (ulong)phdr->cli_state_boot); + seq_printf(seq, "CliStateOS = %lu\n", (ulong)phdr->cli_state_os); + seq_printf(seq, "HeaderSize = %lu\n", (ulong)phdr->header_size); + seq_printf(seq, "Size = %llu\n", (long long)phdr->size); + seq_printf(seq, "Features = 0x%-16.16llx\n", + (long long)phdr->features); + seq_printf(seq, "PartitionHandle = 0x%-16.16llx\n", + (long long)phdr->partition_handle); + seq_printf(seq, "Handle = 0x%-16.16llx\n", + (long long)phdr->handle); + seq_printf(seq, "VersionId = %lu\n", (ulong)phdr->version_id); + seq_printf(seq, "oChannelSpace = %llu\n", + (long long)phdr->ch_space_offset); + if ((phdr->ch_space_offset == 0) || (errcode < 0)) + ; + else + for (i = 0; i < num_queues; i++) { + struct signal_queue_header q; + + errcode = visorchannel_read(channel, + off + + phdr->ch_space_offset + + (i * sizeof(q)), + &q, sizeof(q)); + if (errcode < 0) { + seq_printf(seq, + "failed to read signal queue #%d from channel @0x%-16.16Lx errcode=%d\n", + i, addr, errcode); + continue; + } + sigqueue_debug(&q, i, seq); + } + seq_printf(seq, "--- End channel @0x%-16.16Lx for 0x%lx bytes ---\n", + addr + off, nbytes); +} +EXPORT_SYMBOL_GPL(visorchannel_debug); diff --git a/arch/x86/visorbus/visorchipset.c b/arch/x86/visorbus/visorchipset.c new file mode 100644 index 0000000..630137a --- /dev/null +++ b/arch/x86/visorbus/visorchipset.c @@ -0,0 +1,2355 @@ +/* visorchipset_main.c + * + * Copyright (C) 2010 - 2015 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#include <linux/acpi.h> +#include <linux/cdev.h> +#include <linux/ctype.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/nls.h> +#include <linux/netdevice.h> +#include <linux/platform_device.h> +#include <linux/uuid.h> +#include <linux/crash_dump.h> +#include <linux/visorbus/channel_guid.h> +#include <linux/visorbus/guestlinuxdebug.h> +#include <linux/visorbus/periodic_work.h> +#include <linux/visorbus/version.h> +#include <linux/visorbus/visorbus.h> + +#include "controlvmchannel.h" +#include "controlvmcompletionstatus.h" +#include "visorbus_private.h" +#include "vmcallinterface.h" + +#define CURRENT_FILE_PC VISOR_CHIPSET_PC_visorchipset_main_c + +#define MAX_NAME_SIZE 128 +#define MAX_IP_SIZE 50 +#define MAXOUTSTANDINGCHANNELCOMMAND 256 +#define POLLJIFFIES_CONTROLVMCHANNEL_FAST 1 +#define POLLJIFFIES_CONTROLVMCHANNEL_SLOW 100 + +#define MAX_CONTROLVM_PAYLOAD_BYTES (1024 * 128) + +#define VISORCHIPSET_MMAP_CONTROLCHANOFFSET 0x00000000 + +#define UNISYS_SPAR_LEAF_ID 0x40000000 + +/* The s-Par leaf ID returns "UnisysSpar64" encoded across ebx, ecx, edx */ +#define UNISYS_SPAR_ID_EBX 0x73696e55 +#define UNISYS_SPAR_ID_ECX 0x70537379 +#define UNISYS_SPAR_ID_EDX 0x34367261 + +/* + * Module parameters + */ +static int visorchipset_major; +static int visorchipset_visorbusregwait = 1; /* default is on */ +static unsigned long controlvm_payload_bytes_buffered; +static u32 dump_vhba_bus; + +static int +visorchipset_open(struct inode *inode, struct file *file) +{ + unsigned int minor_number = iminor(inode); + + if (minor_number) + return -ENODEV; + file->private_data = NULL; + return 0; +} + +static int +visorchipset_release(struct inode *inode, struct file *file) +{ + return 0; +} + +/* 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 +static unsigned long poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_FAST; +/* when we got our last controlvm message */ +static unsigned long most_recent_message_jiffies; +static int visorbusregistered; + +struct parser_context { + unsigned long allocbytes; + unsigned long param_bytes; + u8 *curr; + unsigned long bytes_remaining; + bool byte_stream; + char data[0]; +}; + +static struct delayed_work periodic_controlvm_work; +static DEFINE_SEMAPHORE(notifier_lock); + +static struct cdev file_cdev; +static struct visorchannel **file_controlvm_channel; +static struct controlvm_message_packet g_devicechangestate_packet; + +static LIST_HEAD(bus_info_list); +static LIST_HEAD(dev_info_list); + +static struct visorchannel *controlvm_channel; + +/* Manages the request payload in the controlvm channel */ +struct visor_controlvm_payload_info { + u8 *ptr; /* pointer to base address of payload pool */ + u64 offset; /* offset from beginning of controlvm + * channel to beginning of payload * pool + */ + u32 bytes; /* number of bytes in payload pool */ +}; + +static struct visor_controlvm_payload_info controlvm_payload_info; + +/* The following globals 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. + */ +static struct controlvm_message controlvm_pending_msg; +static bool controlvm_pending_msg_valid; + +/* This identifies a data buffer that has been received via a controlvm messages + * in a remote --> local CONTROLVM_TRANSMIT_FILE conversation. + */ +struct putfile_buffer_entry { + struct list_head next; /* putfile_buffer_entry list */ + struct parser_context *parser_ctx; /* points to input data buffer */ +}; + +/* List of struct putfile_request *, via next_putfile_request member. + * Each entry in this list identifies an outstanding TRANSMIT_FILE + * conversation. + */ +static LIST_HEAD(putfile_request_list); + +/* This describes a buffer and its current state of transfer (e.g., how many + * bytes have already been supplied as putfile data, and how many bytes are + * remaining) for a putfile_request. + */ +struct putfile_active_buffer { + /* a payload from a controlvm message, containing a file data buffer */ + struct parser_context *parser_ctx; + /* points within data area of parser_ctx to next byte of data */ + size_t bytes_remaining; +}; + +#define PUTFILE_REQUEST_SIG 0x0906101302281211 +/* This identifies a single remote --> local CONTROLVM_TRANSMIT_FILE + * conversation. Structs of this type are dynamically linked into + * <Putfile_request_list>. + */ +struct putfile_request { + u64 sig; /* PUTFILE_REQUEST_SIG */ + + /* header from original TransmitFile request */ + struct controlvm_message_header controlvm_header; + + /* link to next struct putfile_request */ + struct list_head next_putfile_request; + + /* head of putfile_buffer_entry list, which describes the data to be + * supplied as putfile data; + * - this list is added to when controlvm messages come in that supply + * file data + * - this list is removed from via the hotplug program that is actually + * consuming these buffers to write as file data + */ + struct list_head input_buffer_list; + spinlock_t req_list_lock; /* lock for input_buffer_list */ + + /* waiters for input_buffer_list to go non-empty */ + wait_queue_head_t input_buffer_wq; + + /* data not yet read within current putfile_buffer_entry */ + struct putfile_active_buffer active_buf; + + /* <0 = failed, 0 = in-progress, >0 = successful; */ + /* note that this must be set with req_list_lock, and if you set <0, */ + /* it is your responsibility to also free up all of the other objects */ + /* in this struct (like input_buffer_list, active_buf.parser_ctx) */ + /* before releasing the lock */ + int completion_status; +}; + +struct parahotplug_request { + struct list_head list; + int id; + unsigned long expiration; + struct controlvm_message msg; +}; + +static LIST_HEAD(parahotplug_request_list); +static DEFINE_SPINLOCK(parahotplug_request_list_lock); /* lock for above */ +static void parahotplug_process_list(void); + +/* Manages the info for a CONTROLVM_DUMP_CAPTURESTATE / + * CONTROLVM_REPORTEVENT. + */ +static struct visorchipset_busdev_notifiers busdev_notifiers; + +static void bus_create_response(struct visor_device *p, int response); +static void bus_destroy_response(struct visor_device *p, int response); +static void device_create_response(struct visor_device *p, int response); +static void device_destroy_response(struct visor_device *p, int response); +static void device_resume_response(struct visor_device *p, int response); + +static void visorchipset_device_pause_response(struct visor_device *p, + int response); + +static struct visorchipset_busdev_responders busdev_responders = { + .bus_create = bus_create_response, + .bus_destroy = bus_destroy_response, + .device_create = device_create_response, + .device_destroy = device_destroy_response, + .device_pause = visorchipset_device_pause_response, + .device_resume = device_resume_response, +}; + +/* info for /dev/visorchipset */ +static dev_t major_dev = -1; /**< indicates major num for device */ + +/* prototypes for attributes */ +static ssize_t toolaction_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t toolaction_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static DEVICE_ATTR_RW(toolaction); + +static ssize_t boottotool_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t boottotool_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count); +static DEVICE_ATTR_RW(boottotool); + +static ssize_t error_show(struct device *dev, struct device_attribute *attr, + char *buf); +static ssize_t error_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); +static DEVICE_ATTR_RW(error); + +static ssize_t textid_show(struct device *dev, struct device_attribute *attr, + char *buf); +static ssize_t textid_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); +static DEVICE_ATTR_RW(textid); + +static ssize_t remaining_steps_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t remaining_steps_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static DEVICE_ATTR_RW(remaining_steps); + +static ssize_t devicedisabled_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static DEVICE_ATTR_WO(devicedisabled); + +static ssize_t deviceenabled_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t 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 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 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 +}; + +static void visorchipset_dev_release(struct device *dev) +{ +} + +/* /sys/devices/platform/visorchipset */ +static struct platform_device visorchipset_platform_device = { + .name = "visorchipset", + .id = -1, + .dev.groups = visorchipset_dev_groups, + .dev.release = visorchipset_dev_release, +}; + +/* Function prototypes */ +static void controlvm_respond(struct controlvm_message_header *msg_hdr, + int response); +static void controlvm_respond_chipset_init( + struct controlvm_message_header *msg_hdr, int response, + enum ultra_chipset_feature features); +static void controlvm_respond_physdev_changestate( + struct controlvm_message_header *msg_hdr, int response, + struct spar_segment_state state); + +static void parser_done(struct parser_context *ctx); + +static struct parser_context * +parser_init_byte_stream(u64 addr, u32 bytes, bool local, bool *retry) +{ + int allocbytes = sizeof(struct parser_context) + bytes; + struct parser_context *ctx; + + if (retry) + *retry = false; + + /* + * alloc an 0 extra byte to ensure payload is + * '\0'-terminated + */ + allocbytes++; + if ((controlvm_payload_bytes_buffered + bytes) + > MAX_CONTROLVM_PAYLOAD_BYTES) { + if (retry) + *retry = true; + return NULL; + } + ctx = kzalloc(allocbytes, GFP_KERNEL | __GFP_NORETRY); + if (!ctx) { + if (retry) + *retry = true; + return NULL; + } + + ctx->allocbytes = allocbytes; + ctx->param_bytes = bytes; + ctx->curr = NULL; + ctx->bytes_remaining = 0; + ctx->byte_stream = false; + if (local) { + void *p; + + if (addr > virt_to_phys(high_memory - 1)) + goto err_finish_ctx; + p = __va((unsigned long)(addr)); + memcpy(ctx->data, p, bytes); + } else { + void *mapping = memremap(addr, bytes, MEMREMAP_WB); + + if (!mapping) + goto err_finish_ctx; + memcpy(ctx->data, mapping, bytes); + memunmap(mapping); + } + + ctx->byte_stream = true; + controlvm_payload_bytes_buffered += ctx->param_bytes; + + return ctx; + +err_finish_ctx: + parser_done(ctx); + return NULL; +} + +static uuid_le +parser_id_get(struct parser_context *ctx) +{ + struct spar_controlvm_parameters_header *phdr = NULL; + + if (!ctx) + return NULL_UUID_LE; + phdr = (struct spar_controlvm_parameters_header *)(ctx->data); + return phdr->id; +} + +/** Describes the state from the perspective of which controlvm messages have + * been received for a bus or device. + */ + +enum PARSER_WHICH_STRING { + PARSERSTRING_INITIATOR, + PARSERSTRING_TARGET, + PARSERSTRING_CONNECTION, + PARSERSTRING_NAME, /* TODO: only PARSERSTRING_NAME is used ? */ +}; + +static void +parser_param_start(struct parser_context *ctx, + enum PARSER_WHICH_STRING which_string) +{ + struct spar_controlvm_parameters_header *phdr = NULL; + + if (!ctx) + return; + + phdr = (struct spar_controlvm_parameters_header *)(ctx->data); + switch (which_string) { + case PARSERSTRING_INITIATOR: + ctx->curr = ctx->data + phdr->initiator_offset; + ctx->bytes_remaining = phdr->initiator_length; + break; + case PARSERSTRING_TARGET: + ctx->curr = ctx->data + phdr->target_offset; + ctx->bytes_remaining = phdr->target_length; + break; + case PARSERSTRING_CONNECTION: + ctx->curr = ctx->data + phdr->connection_offset; + ctx->bytes_remaining = phdr->connection_length; + break; + case PARSERSTRING_NAME: + ctx->curr = ctx->data + phdr->name_offset; + ctx->bytes_remaining = phdr->name_length; + break; + default: + break; + } +} + +static void parser_done(struct parser_context *ctx) +{ + if (!ctx) + return; + controlvm_payload_bytes_buffered -= ctx->param_bytes; + kfree(ctx); +} + +static void * +parser_string_get(struct parser_context *ctx) +{ + u8 *pscan; + unsigned long nscan; + int value_length = -1; + void *value = NULL; + int i; + + if (!ctx) + return NULL; + pscan = ctx->curr; + nscan = ctx->bytes_remaining; + if (nscan == 0) + return NULL; + if (!pscan) + return NULL; + for (i = 0, value_length = -1; i < nscan; i++) + if (pscan[i] == '\0') { + value_length = i; + break; + } + if (value_length < 0) /* '\0' was not included in the length */ + value_length = nscan; + value = kmalloc(value_length + 1, GFP_KERNEL | __GFP_NORETRY); + if (!value) + return NULL; + if (value_length > 0) + memcpy(value, pscan, value_length); + ((u8 *)(value))[value_length] = '\0'; + return value; +} + +static ssize_t toolaction_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 tool_action = 0; + + visorchannel_read(controlvm_channel, + offsetof(struct spar_controlvm_channel_protocol, + tool_action), &tool_action, sizeof(u8)); + return scnprintf(buf, PAGE_SIZE, "%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 ret; + + if (kstrtou8(buf, 10, &tool_action)) + return -EINVAL; + + ret = visorchannel_write + (controlvm_channel, + offsetof(struct spar_controlvm_channel_protocol, + tool_action), + &tool_action, sizeof(u8)); + + if (ret) + return ret; + return count; +} + +static ssize_t boottotool_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct efi_spar_indication efi_spar_indication; + + visorchannel_read(controlvm_channel, + offsetof(struct spar_controlvm_channel_protocol, + efi_spar_ind), &efi_spar_indication, + sizeof(struct efi_spar_indication)); + return scnprintf(buf, PAGE_SIZE, "%u\n", + efi_spar_indication.boot_to_tool); +} + +static ssize_t boottotool_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val, ret; + struct efi_spar_indication efi_spar_indication; + + if (kstrtoint(buf, 10, &val)) + return -EINVAL; + + efi_spar_indication.boot_to_tool = val; + ret = visorchannel_write + (controlvm_channel, + offsetof(struct spar_controlvm_channel_protocol, + efi_spar_ind), &(efi_spar_indication), + sizeof(struct efi_spar_indication)); + + if (ret) + return ret; + return count; +} + +static ssize_t error_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + u32 error = 0; + + visorchannel_read(controlvm_channel, + offsetof(struct spar_controlvm_channel_protocol, + installation_error), + &error, sizeof(u32)); + return scnprintf(buf, PAGE_SIZE, "%i\n", error); +} + +static ssize_t error_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 error; + int ret; + + if (kstrtou32(buf, 10, &error)) + return -EINVAL; + + ret = visorchannel_write + (controlvm_channel, + offsetof(struct spar_controlvm_channel_protocol, + installation_error), + &error, sizeof(u32)); + if (ret) + return ret; + return count; +} + +static ssize_t textid_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + u32 text_id = 0; + + visorchannel_read + (controlvm_channel, + offsetof(struct spar_controlvm_channel_protocol, + installation_text_id), + &text_id, sizeof(u32)); + return scnprintf(buf, PAGE_SIZE, "%i\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 ret; + + if (kstrtou32(buf, 10, &text_id)) + return -EINVAL; + + ret = visorchannel_write + (controlvm_channel, + offsetof(struct spar_controlvm_channel_protocol, + installation_text_id), + &text_id, sizeof(u32)); + if (ret) + return ret; + return count; +} + +static ssize_t remaining_steps_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u16 remaining_steps = 0; + + visorchannel_read(controlvm_channel, + offsetof(struct spar_controlvm_channel_protocol, + installation_remaining_steps), + &remaining_steps, sizeof(u16)); + return scnprintf(buf, PAGE_SIZE, "%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 ret; + + if (kstrtou16(buf, 10, &remaining_steps)) + return -EINVAL; + + ret = visorchannel_write + (controlvm_channel, + offsetof(struct spar_controlvm_channel_protocol, + installation_remaining_steps), + &remaining_steps, sizeof(u16)); + if (ret) + return ret; + return count; +} + +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; + u32 bus_no = id->bus_no; + u32 dev_no = id->dev_no; + + if ((vdev->chipset_bus_no == bus_no) && + (vdev->chipset_dev_no == 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_device *vdev = 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) + vdev = to_visor_device(dev); + return vdev; +} +EXPORT_SYMBOL(visorbus_get_device_by_id); + +void +visorchipset_register_busdev( + struct visorchipset_busdev_notifiers *notifiers, + struct visorchipset_busdev_responders *responders, + struct ultra_vbus_deviceinfo *driver_info) +{ + down(¬ifier_lock); + if (!notifiers) { + memset(&busdev_notifiers, 0, + sizeof(busdev_notifiers)); + visorbusregistered = 0; /* clear flag */ + } else { + busdev_notifiers = *notifiers; + visorbusregistered = 1; /* set flag */ + } + if (responders) + *responders = busdev_responders; + if (driver_info) + bus_device_info_init(driver_info, "chipset", "visorchipset", + VERSION, NULL); + + up(¬ifier_lock); +} +EXPORT_SYMBOL_GPL(visorchipset_register_busdev); + +static void +chipset_init(struct controlvm_message *inmsg) +{ + static int chipset_inited; + enum ultra_chipset_feature features = 0; + int rc = CONTROLVM_RESP_SUCCESS; + + POSTCODE_LINUX_2(CHIPSET_INIT_ENTRY_PC, POSTCODE_SEVERITY_INFO); + if (chipset_inited) { + rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE; + goto out_respond; + } + chipset_inited = 1; + POSTCODE_LINUX_2(CHIPSET_INIT_EXIT_PC, POSTCODE_SEVERITY_INFO); + + /* Set features to indicate we support parahotplug (if Command + * also supports it). + */ + features = + inmsg->cmd.init_chipset. + features & ULTRA_CHIPSET_FEATURE_PARA_HOTPLUG; + + /* Set the "reply" bit so Command knows this is a + * features-aware driver. + */ + features |= ULTRA_CHIPSET_FEATURE_REPLY; + +out_respond: + if (inmsg->hdr.flags.response_expected) + controlvm_respond_chipset_init(&inmsg->hdr, rc, features); +} + +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 void +controlvm_respond(struct controlvm_message_header *msg_hdr, int response) +{ + struct controlvm_message outmsg; + + controlvm_init_response(&outmsg, msg_hdr, response); + if (outmsg.hdr.flags.test_message == 1) + return; + + if (!visorchannel_signalinsert(controlvm_channel, + CONTROLVM_QUEUE_REQUEST, &outmsg)) { + return; + } +} + +static void +controlvm_respond_chipset_init(struct controlvm_message_header *msg_hdr, + int response, + enum ultra_chipset_feature features) +{ + struct controlvm_message outmsg; + + controlvm_init_response(&outmsg, msg_hdr, response); + outmsg.cmd.init_chipset.features = features; + if (!visorchannel_signalinsert(controlvm_channel, + CONTROLVM_QUEUE_REQUEST, &outmsg)) { + return; + } +} + +static void controlvm_respond_physdev_changestate( + struct controlvm_message_header *msg_hdr, int response, + struct spar_segment_state state) +{ + struct controlvm_message outmsg; + + controlvm_init_response(&outmsg, msg_hdr, response); + outmsg.cmd.device_change_state.state = state; + outmsg.cmd.device_change_state.flags.phys_device = 1; + if (!visorchannel_signalinsert(controlvm_channel, + CONTROLVM_QUEUE_REQUEST, &outmsg)) { + return; + } +} + +enum crash_obj_type { + CRASH_DEV, + CRASH_BUS, +}; + +static void +save_crash_message(struct controlvm_message *msg, enum crash_obj_type typ) +{ + u32 local_crash_msg_offset; + u16 local_crash_msg_count; + + if (visorchannel_read(controlvm_channel, + offsetof(struct spar_controlvm_channel_protocol, + saved_crash_message_count), + &local_crash_msg_count, sizeof(u16)) < 0) { + POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + + if (local_crash_msg_count != CONTROLVM_CRASHMSG_MAX) { + POSTCODE_LINUX_3(CRASH_DEV_COUNT_FAILURE_PC, + local_crash_msg_count, + POSTCODE_SEVERITY_ERR); + return; + } + + if (visorchannel_read(controlvm_channel, + offsetof(struct spar_controlvm_channel_protocol, + saved_crash_message_offset), + &local_crash_msg_offset, sizeof(u32)) < 0) { + POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + + if (typ == CRASH_BUS) { + if (visorchannel_write(controlvm_channel, + local_crash_msg_offset, + msg, + sizeof(struct controlvm_message)) < 0) { + POSTCODE_LINUX_2(SAVE_MSG_BUS_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + } else { + local_crash_msg_offset += sizeof(struct controlvm_message); + if (visorchannel_write(controlvm_channel, + local_crash_msg_offset, + msg, + sizeof(struct controlvm_message)) < 0) { + POSTCODE_LINUX_2(SAVE_MSG_DEV_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + } +} + +static void +bus_responder(enum controlvm_id cmd_id, + struct controlvm_message_header *pending_msg_hdr, + int response) +{ + if (!pending_msg_hdr) + return; /* no controlvm response needed */ + + if (pending_msg_hdr->id != (u32)cmd_id) + return; + + controlvm_respond(pending_msg_hdr, response); +} + +static void +device_changestate_responder(enum controlvm_id cmd_id, + struct visor_device *p, int response, + struct spar_segment_state response_state) +{ + struct controlvm_message outmsg; + u32 bus_no = p->chipset_bus_no; + u32 dev_no = p->chipset_dev_no; + + if (!p->pending_msg_hdr) + return; /* no controlvm response needed */ + if (p->pending_msg_hdr->id != cmd_id) + return; + + controlvm_init_response(&outmsg, p->pending_msg_hdr, response); + + outmsg.cmd.device_change_state.bus_no = bus_no; + outmsg.cmd.device_change_state.dev_no = dev_no; + outmsg.cmd.device_change_state.state = response_state; + + if (!visorchannel_signalinsert(controlvm_channel, + CONTROLVM_QUEUE_REQUEST, &outmsg)) + return; +} + +static void +device_responder(enum controlvm_id cmd_id, + struct controlvm_message_header *pending_msg_hdr, + int response) +{ + if (!pending_msg_hdr) + return; /* no controlvm response needed */ + + if (pending_msg_hdr->id != (u32)cmd_id) + return; + + controlvm_respond(pending_msg_hdr, response); +} + +static void +bus_epilog(struct visor_device *bus_info, + u32 cmd, struct controlvm_message_header *msg_hdr, + int response, bool need_response) +{ + struct controlvm_message_header *pmsg_hdr = NULL; + + down(¬ifier_lock); + + if (!bus_info) { + /* relying on a valid passed in response code */ + /* be lazy and re-use msg_hdr for this failure, is this ok?? */ + pmsg_hdr = msg_hdr; + goto out_respond_and_unlock; + } + + if (bus_info->pending_msg_hdr) { + /* only non-NULL if dev is still waiting on a response */ + response = -CONTROLVM_RESP_ERROR_MESSAGE_ID_INVALID_FOR_CLIENT; + pmsg_hdr = bus_info->pending_msg_hdr; + goto out_respond_and_unlock; + } + + if (need_response) { + pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL); + if (!pmsg_hdr) { + POSTCODE_LINUX_4(MALLOC_FAILURE_PC, cmd, + bus_info->chipset_bus_no, + POSTCODE_SEVERITY_ERR); + goto out_unlock; + } + + memcpy(pmsg_hdr, msg_hdr, + sizeof(struct controlvm_message_header)); + bus_info->pending_msg_hdr = pmsg_hdr; + } + + if (response == CONTROLVM_RESP_SUCCESS) { + switch (cmd) { + case CONTROLVM_BUS_CREATE: + if (busdev_notifiers.bus_create) { + (*busdev_notifiers.bus_create) (bus_info); + goto out_unlock; + } + break; + case CONTROLVM_BUS_DESTROY: + if (busdev_notifiers.bus_destroy) { + (*busdev_notifiers.bus_destroy) (bus_info); + goto out_unlock; + } + break; + } + } + +out_respond_and_unlock: + bus_responder(cmd, pmsg_hdr, response); + +out_unlock: + up(¬ifier_lock); +} + +static void +device_epilog(struct visor_device *dev_info, + struct spar_segment_state state, u32 cmd, + struct controlvm_message_header *msg_hdr, int response, + bool need_response, bool for_visorbus) +{ + struct visorchipset_busdev_notifiers *notifiers; + struct controlvm_message_header *pmsg_hdr = NULL; + + notifiers = &busdev_notifiers; + + down(¬ifier_lock); + if (!dev_info) { + /* relying on a valid passed in response code */ + /* be lazy and re-use msg_hdr for this failure, is this ok?? */ + pmsg_hdr = msg_hdr; + goto out_respond_and_unlock; + } + + if (dev_info->pending_msg_hdr) { + /* only non-NULL if dev is still waiting on a response */ + response = -CONTROLVM_RESP_ERROR_MESSAGE_ID_INVALID_FOR_CLIENT; + pmsg_hdr = dev_info->pending_msg_hdr; + goto out_respond_and_unlock; + } + + if (need_response) { + pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL); + if (!pmsg_hdr) { + response = -CONTROLVM_RESP_ERROR_KMALLOC_FAILED; + goto out_respond_and_unlock; + } + + memcpy(pmsg_hdr, msg_hdr, + sizeof(struct controlvm_message_header)); + dev_info->pending_msg_hdr = pmsg_hdr; + } + + if (response >= 0) { + switch (cmd) { + case CONTROLVM_DEVICE_CREATE: + if (notifiers->device_create) { + (*notifiers->device_create) (dev_info); + goto out_unlock; + } + break; + case CONTROLVM_DEVICE_CHANGESTATE: + /* ServerReady / ServerRunning / SegmentStateRunning */ + if (state.alive == segment_state_running.alive && + state.operating == + segment_state_running.operating) { + if (notifiers->device_resume) { + (*notifiers->device_resume) (dev_info); + goto out_unlock; + } + } + /* 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 + */ + if (notifiers->device_pause) { + (*notifiers->device_pause) (dev_info); + goto out_unlock; + } + } + break; + case CONTROLVM_DEVICE_DESTROY: + if (notifiers->device_destroy) { + (*notifiers->device_destroy) (dev_info); + goto out_unlock; + } + break; + } + } + +out_respond_and_unlock: + device_responder(cmd, pmsg_hdr, response); + +out_unlock: + up(¬ifier_lock); +} + +static void +bus_create(struct controlvm_message *inmsg) +{ + struct controlvm_message_packet *cmd = &inmsg->cmd; + u32 bus_no = cmd->create_bus.bus_no; + int rc = CONTROLVM_RESP_SUCCESS; + struct visor_device *bus_info; + struct visorchannel *visorchannel; + + bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); + if (bus_info && (bus_info->state.created == 1)) { + POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no, + POSTCODE_SEVERITY_ERR); + rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE; + goto out_bus_epilog; + } + bus_info = kzalloc(sizeof(*bus_info), GFP_KERNEL); + if (!bus_info) { + POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no, + POSTCODE_SEVERITY_ERR); + rc = -CONTROLVM_RESP_ERROR_KMALLOC_FAILED; + goto out_bus_epilog; + } + + INIT_LIST_HEAD(&bus_info->list_all); + bus_info->chipset_bus_no = bus_no; + bus_info->chipset_dev_no = BUS_ROOT_DEVICE; + + POSTCODE_LINUX_3(BUS_CREATE_ENTRY_PC, bus_no, POSTCODE_SEVERITY_INFO); + + visorchannel = visorchannel_create(cmd->create_bus.channel_addr, + cmd->create_bus.channel_bytes, + GFP_KERNEL, + cmd->create_bus.bus_data_type_uuid); + + if (!visorchannel) { + POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no, + POSTCODE_SEVERITY_ERR); + rc = -CONTROLVM_RESP_ERROR_KMALLOC_FAILED; + kfree(bus_info); + bus_info = NULL; + goto out_bus_epilog; + } + bus_info->visorchannel = visorchannel; + if (uuid_le_cmp(cmd->create_bus.bus_inst_uuid, spar_siovm_uuid) == 0) { + dump_vhba_bus = bus_no; + save_crash_message(inmsg, CRASH_BUS); + } + + POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, bus_no, POSTCODE_SEVERITY_INFO); + +out_bus_epilog: + bus_epilog(bus_info, CONTROLVM_BUS_CREATE, &inmsg->hdr, + rc, inmsg->hdr.flags.response_expected == 1); +} + +static void +bus_destroy(struct controlvm_message *inmsg) +{ + struct controlvm_message_packet *cmd = &inmsg->cmd; + u32 bus_no = cmd->destroy_bus.bus_no; + struct visor_device *bus_info; + int rc = CONTROLVM_RESP_SUCCESS; + + bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); + if (!bus_info) + rc = -CONTROLVM_RESP_ERROR_BUS_INVALID; + else if (bus_info->state.created == 0) + rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE; + + bus_epilog(bus_info, CONTROLVM_BUS_DESTROY, &inmsg->hdr, + rc, inmsg->hdr.flags.response_expected == 1); + + /* bus_info is freed as part of the busdevice_release function */ +} + +static void +bus_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 rc = CONTROLVM_RESP_SUCCESS; + + bus_no = cmd->configure_bus.bus_no; + POSTCODE_LINUX_3(BUS_CONFIGURE_ENTRY_PC, bus_no, + POSTCODE_SEVERITY_INFO); + + bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); + if (!bus_info) { + POSTCODE_LINUX_3(BUS_CONFIGURE_FAILURE_PC, bus_no, + POSTCODE_SEVERITY_ERR); + rc = -CONTROLVM_RESP_ERROR_BUS_INVALID; + } else if (bus_info->state.created == 0) { + POSTCODE_LINUX_3(BUS_CONFIGURE_FAILURE_PC, bus_no, + POSTCODE_SEVERITY_ERR); + rc = -CONTROLVM_RESP_ERROR_BUS_INVALID; + } else if (bus_info->pending_msg_hdr) { + POSTCODE_LINUX_3(BUS_CONFIGURE_FAILURE_PC, bus_no, + POSTCODE_SEVERITY_ERR); + rc = -CONTROLVM_RESP_ERROR_MESSAGE_ID_INVALID_FOR_CLIENT; + } else { + visorchannel_set_clientpartition + (bus_info->visorchannel, + cmd->configure_bus.guest_handle); + bus_info->partition_uuid = parser_id_get(parser_ctx); + parser_param_start(parser_ctx, PARSERSTRING_NAME); + bus_info->name = parser_string_get(parser_ctx); + + POSTCODE_LINUX_3(BUS_CONFIGURE_EXIT_PC, bus_no, + POSTCODE_SEVERITY_INFO); + } + bus_epilog(bus_info, CONTROLVM_BUS_CONFIGURE, &inmsg->hdr, + rc, inmsg->hdr.flags.response_expected == 1); +} + +static void +my_device_create(struct controlvm_message *inmsg) +{ + struct controlvm_message_packet *cmd = &inmsg->cmd; + u32 bus_no = cmd->create_device.bus_no; + u32 dev_no = cmd->create_device.dev_no; + struct visor_device *dev_info = NULL; + struct visor_device *bus_info; + struct visorchannel *visorchannel; + int rc = CONTROLVM_RESP_SUCCESS; + + bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); + if (!bus_info) { + POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no, + POSTCODE_SEVERITY_ERR); + rc = -CONTROLVM_RESP_ERROR_BUS_INVALID; + goto out_respond; + } + + if (bus_info->state.created == 0) { + POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no, + POSTCODE_SEVERITY_ERR); + rc = -CONTROLVM_RESP_ERROR_BUS_INVALID; + goto out_respond; + } + + dev_info = visorbus_get_device_by_id(bus_no, dev_no, NULL); + if (dev_info && (dev_info->state.created == 1)) { + POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no, + POSTCODE_SEVERITY_ERR); + rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE; + goto out_respond; + } + + dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL); + if (!dev_info) { + POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no, + POSTCODE_SEVERITY_ERR); + rc = -CONTROLVM_RESP_ERROR_KMALLOC_FAILED; + goto out_respond; + } + + dev_info->chipset_bus_no = bus_no; + dev_info->chipset_dev_no = dev_no; + dev_info->inst = cmd->create_device.dev_inst_uuid; + + /* not sure where the best place to set the 'parent' */ + dev_info->device.parent = &bus_info->device; + + POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, dev_no, bus_no, + POSTCODE_SEVERITY_INFO); + + visorchannel = + visorchannel_create_with_lock(cmd->create_device.channel_addr, + cmd->create_device.channel_bytes, + GFP_KERNEL, + cmd->create_device.data_type_uuid); + + if (!visorchannel) { + POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no, + POSTCODE_SEVERITY_ERR); + rc = -CONTROLVM_RESP_ERROR_KMALLOC_FAILED; + kfree(dev_info); + dev_info = NULL; + goto out_respond; + } + dev_info->visorchannel = visorchannel; + dev_info->channel_type_guid = cmd->create_device.data_type_uuid; + if (uuid_le_cmp(cmd->create_device.data_type_uuid, + spar_vhba_channel_protocol_uuid) == 0) + save_crash_message(inmsg, CRASH_DEV); + + POSTCODE_LINUX_4(DEVICE_CREATE_EXIT_PC, dev_no, bus_no, + POSTCODE_SEVERITY_INFO); +out_respond: + device_epilog(dev_info, segment_state_running, + CONTROLVM_DEVICE_CREATE, &inmsg->hdr, rc, + inmsg->hdr.flags.response_expected == 1, 1); +} + +static void +my_device_changestate(struct controlvm_message *inmsg) +{ + struct controlvm_message_packet *cmd = &inmsg->cmd; + u32 bus_no = cmd->device_change_state.bus_no; + u32 dev_no = cmd->device_change_state.dev_no; + struct spar_segment_state state = cmd->device_change_state.state; + struct visor_device *dev_info; + int rc = CONTROLVM_RESP_SUCCESS; + + dev_info = visorbus_get_device_by_id(bus_no, dev_no, NULL); + if (!dev_info) { + POSTCODE_LINUX_4(DEVICE_CHANGESTATE_FAILURE_PC, dev_no, bus_no, + POSTCODE_SEVERITY_ERR); + rc = -CONTROLVM_RESP_ERROR_DEVICE_INVALID; + } else if (dev_info->state.created == 0) { + POSTCODE_LINUX_4(DEVICE_CHANGESTATE_FAILURE_PC, dev_no, bus_no, + POSTCODE_SEVERITY_ERR); + rc = -CONTROLVM_RESP_ERROR_DEVICE_INVALID; + } + if ((rc >= CONTROLVM_RESP_SUCCESS) && dev_info) + device_epilog(dev_info, state, + CONTROLVM_DEVICE_CHANGESTATE, &inmsg->hdr, rc, + inmsg->hdr.flags.response_expected == 1, 1); +} + +static void +my_device_destroy(struct controlvm_message *inmsg) +{ + struct controlvm_message_packet *cmd = &inmsg->cmd; + u32 bus_no = cmd->destroy_device.bus_no; + u32 dev_no = cmd->destroy_device.dev_no; + struct visor_device *dev_info; + int rc = CONTROLVM_RESP_SUCCESS; + + dev_info = visorbus_get_device_by_id(bus_no, dev_no, NULL); + if (!dev_info) + rc = -CONTROLVM_RESP_ERROR_DEVICE_INVALID; + else if (dev_info->state.created == 0) + rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE; + + if ((rc >= CONTROLVM_RESP_SUCCESS) && dev_info) + device_epilog(dev_info, segment_state_running, + CONTROLVM_DEVICE_DESTROY, &inmsg->hdr, rc, + inmsg->hdr.flags.response_expected == 1, 1); +} + +/* When provided with the physical address of the controlvm channel + * (phys_addr), the offset to the payload area we need to manage + * (offset), and the size of this payload area (bytes), fills in the + * controlvm_payload_info struct. Returns true for success or false + * for failure. + */ +static int +initialize_controlvm_payload_info(u64 phys_addr, u64 offset, u32 bytes, + struct visor_controlvm_payload_info *info) +{ + u8 *payload = NULL; + + if (!info) + return -CONTROLVM_RESP_ERROR_PAYLOAD_INVALID; + + memset(info, 0, sizeof(struct visor_controlvm_payload_info)); + if ((offset == 0) || (bytes == 0)) + return -CONTROLVM_RESP_ERROR_PAYLOAD_INVALID; + + payload = memremap(phys_addr + offset, bytes, MEMREMAP_WB); + if (!payload) + return -CONTROLVM_RESP_ERROR_IOREMAP_FAILED; + + info->offset = offset; + info->bytes = bytes; + info->ptr = payload; + + return CONTROLVM_RESP_SUCCESS; +} + +static void +destroy_controlvm_payload_info(struct visor_controlvm_payload_info *info) +{ + if (info->ptr) { + memunmap(info->ptr); + info->ptr = NULL; + } + memset(info, 0, sizeof(struct visor_controlvm_payload_info)); +} + +static void +initialize_controlvm_payload(void) +{ + u64 phys_addr = visorchannel_get_physaddr(controlvm_channel); + u64 payload_offset = 0; + u32 payload_bytes = 0; + + if (visorchannel_read(controlvm_channel, + offsetof(struct spar_controlvm_channel_protocol, + request_payload_offset), + &payload_offset, sizeof(payload_offset)) < 0) { + POSTCODE_LINUX_2(CONTROLVM_INIT_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + if (visorchannel_read(controlvm_channel, + offsetof(struct spar_controlvm_channel_protocol, + request_payload_bytes), + &payload_bytes, sizeof(payload_bytes)) < 0) { + POSTCODE_LINUX_2(CONTROLVM_INIT_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + initialize_controlvm_payload_info(phys_addr, + payload_offset, payload_bytes, + &controlvm_payload_info); +} + +/* Send ACTION=online for DEVPATH=/sys/devices/platform/visorchipset. + * Returns CONTROLVM_RESP_xxx code. + */ +static int +visorchipset_chipset_ready(void) +{ + kobject_uevent(&visorchipset_platform_device.dev.kobj, KOBJ_ONLINE); + return CONTROLVM_RESP_SUCCESS; +} + +static int +visorchipset_chipset_selftest(void) +{ + char env_selftest[20]; + char *envp[] = { env_selftest, NULL }; + + sprintf(env_selftest, "SPARSP_SELFTEST=%d", 1); + kobject_uevent_env(&visorchipset_platform_device.dev.kobj, KOBJ_CHANGE, + envp); + return CONTROLVM_RESP_SUCCESS; +} + +/* Send ACTION=offline for DEVPATH=/sys/devices/platform/visorchipset. + * Returns CONTROLVM_RESP_xxx code. + */ +static int +visorchipset_chipset_notready(void) +{ + kobject_uevent(&visorchipset_platform_device.dev.kobj, KOBJ_OFFLINE); + return CONTROLVM_RESP_SUCCESS; +} + +static void +chipset_ready(struct controlvm_message_header *msg_hdr) +{ + int rc = visorchipset_chipset_ready(); + + if (rc != CONTROLVM_RESP_SUCCESS) + rc = -rc; + if (msg_hdr->flags.response_expected) + controlvm_respond(msg_hdr, rc); +} + +static void +chipset_selftest(struct controlvm_message_header *msg_hdr) +{ + int rc = visorchipset_chipset_selftest(); + + if (rc != CONTROLVM_RESP_SUCCESS) + rc = -rc; + if (msg_hdr->flags.response_expected) + controlvm_respond(msg_hdr, rc); +} + +static void +chipset_notready(struct controlvm_message_header *msg_hdr) +{ + int rc = visorchipset_chipset_notready(); + + if (rc != CONTROLVM_RESP_SUCCESS) + rc = -rc; + if (msg_hdr->flags.response_expected) + controlvm_respond(msg_hdr, rc); +} + +/* This is your "one-stop" shop for grabbing the next message from the + * CONTROLVM_QUEUE_EVENT queue in the controlvm channel. + */ +static bool +read_controlvm_event(struct controlvm_message *msg) +{ + if (visorchannel_signalremove(controlvm_channel, + CONTROLVM_QUEUE_EVENT, msg)) { + /* got a message */ + if (msg->hdr.flags.test_message == 1) + return false; + return true; + } + return false; +} + +/* + * The general parahotplug flow works as follows. The visorchipset + * driver 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 + * /proc/visorchipset/parahotplug, which causes parahotplug_proc_write + * to get called, at which point the appropriate CONTROLVM message is + * retrieved from the list and responded to. + */ + +#define PARAHOTPLUG_TIMEOUT_MS 2000 + +/* + * Generate unique int to match an outstanding CONTROLVM message with a + * udev script /proc response + */ +static int +parahotplug_next_id(void) +{ + static atomic_t id = ATOMIC_INIT(0); + + return atomic_inc_return(&id); +} + +/* + * Returns the time (in jiffies) when a CONTROLVM message on the list + * should expire -- PARAHOTPLUG_TIMEOUT_MS in the future + */ +static unsigned long +parahotplug_next_expiration(void) +{ + return jiffies + msecs_to_jiffies(PARAHOTPLUG_TIMEOUT_MS); +} + +/* + * Create a parahotplug_request, which is basically a wrapper for a + * CONTROLVM_MESSAGE that we can stick on a list + */ +static struct parahotplug_request * +parahotplug_request_create(struct controlvm_message *msg) +{ + struct parahotplug_request *req; + + req = kmalloc(sizeof(*req), GFP_KERNEL | __GFP_NORETRY); + if (!req) + return NULL; + + req->id = parahotplug_next_id(); + req->expiration = parahotplug_next_expiration(); + req->msg = *msg; + + return req; +} + +/* + * Free a parahotplug_request. + */ +static void +parahotplug_request_destroy(struct parahotplug_request *req) +{ + kfree(req); +} + +/* + * Cause uevent to run the user level script to do the disable/enable + * specified in (the CONTROLVM message in) the specified + * parahotplug_request + */ +static void +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, "SPAR_PARAHOTPLUG=1"); + sprintf(env_id, "SPAR_PARAHOTPLUG_ID=%d", req->id); + sprintf(env_state, "SPAR_PARAHOTPLUG_STATE=%d", + cmd->device_change_state.state.active); + sprintf(env_bus, "SPAR_PARAHOTPLUG_BUS=%d", + cmd->device_change_state.bus_no); + sprintf(env_dev, "SPAR_PARAHOTPLUG_DEVICE=%d", + cmd->device_change_state.dev_no >> 3); + sprintf(env_func, "SPAR_PARAHOTPLUG_FUNCTION=%d", + cmd->device_change_state.dev_no & 0x7); + + kobject_uevent_env(&visorchipset_platform_device.dev.kobj, KOBJ_CHANGE, + envp); +} + +/* + * Remove any request from the list that's been on there too long and + * respond with an error. + */ +static void +parahotplug_process_list(void) +{ + struct list_head *pos; + struct list_head *tmp; + + spin_lock(¶hotplug_request_list_lock); + + list_for_each_safe(pos, tmp, ¶hotplug_request_list) { + struct parahotplug_request *req = + list_entry(pos, struct parahotplug_request, list); + + if (!time_after_eq(jiffies, req->expiration)) + continue; + + list_del(pos); + if (req->msg.hdr.flags.response_expected) + controlvm_respond_physdev_changestate( + &req->msg.hdr, + CONTROLVM_RESP_ERROR_DEVICE_UDEV_TIMEOUT, + req->msg.cmd.device_change_state.state); + parahotplug_request_destroy(req); + } + + spin_unlock(¶hotplug_request_list_lock); +} + +/* + * Called from the /proc handler, which means the user script has + * finished the enable/disable. Find the matching identifier, and + * respond to the CONTROLVM message with success. + */ +static int +parahotplug_request_complete(int id, u16 active) +{ + struct list_head *pos; + struct list_head *tmp; + + spin_lock(¶hotplug_request_list_lock); + + /* Look for a request matching "id". */ + list_for_each_safe(pos, tmp, ¶hotplug_request_list) { + struct parahotplug_request *req = + list_entry(pos, struct parahotplug_request, list); + if (req->id == id) { + /* Found a match. Remove it from the list and + * respond. + */ + list_del(pos); + spin_unlock(¶hotplug_request_list_lock); + req->msg.cmd.device_change_state.state.active = active; + if (req->msg.hdr.flags.response_expected) + controlvm_respond_physdev_changestate( + &req->msg.hdr, CONTROLVM_RESP_SUCCESS, + req->msg.cmd.device_change_state.state); + parahotplug_request_destroy(req); + return 0; + } + } + + spin_unlock(¶hotplug_request_list_lock); + return -EINVAL; +} + +/* + * Enables or disables a PCI device by kicking off a udev script + */ +static void +parahotplug_process_message(struct controlvm_message *inmsg) +{ + struct parahotplug_request *req; + + req = parahotplug_request_create(inmsg); + + if (!req) + return; + + if (inmsg->cmd.device_change_state.state.active) { + /* For enable messages, just respond with success + * right away. This is a bit of a hack, but there are + * issues with the early enable messages we get (with + * either the udev script not detecting that the device + * is up, or not getting called at all). Fortunately + * the messages that get lost don't matter anyway, as + * devices are automatically enabled at + * initialization. + */ + parahotplug_request_kickoff(req); + controlvm_respond_physdev_changestate + (&inmsg->hdr, + CONTROLVM_RESP_SUCCESS, + inmsg->cmd.device_change_state.state); + parahotplug_request_destroy(req); + } else { + /* For disable messages, add the request to the + * request list before kicking off the udev script. It + * won't get responded to until the script has + * indicated it's done. + */ + spin_lock(¶hotplug_request_list_lock); + list_add_tail(&req->list, ¶hotplug_request_list); + spin_unlock(¶hotplug_request_list_lock); + + parahotplug_request_kickoff(req); + } +} + +/* Process a controlvm message. + * Return result: + * false - this function will return false only in the case where the + * controlvm message was NOT processed, but processing must be + * retried before 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 + * true - processing of the controlvm message completed, + * either successfully or with an error. + */ +static bool +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; + bool local_addr; + struct controlvm_message ackmsg; + + /* create parsing context if necessary */ + local_addr = (inmsg.hdr.flags.test_message == 1); + if (channel_addr == 0) + return true; + 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_addr && parm_bytes) { + bool retry = false; + + parser_ctx = + parser_init_byte_stream(parm_addr, parm_bytes, + local_addr, &retry); + if (!parser_ctx && retry) + return false; + } + + if (!local_addr) { + controlvm_init_response(&ackmsg, &inmsg.hdr, + CONTROLVM_RESP_SUCCESS); + if (controlvm_channel) + visorchannel_signalinsert(controlvm_channel, + CONTROLVM_QUEUE_ACK, + &ackmsg); + } + switch (inmsg.hdr.id) { + case CONTROLVM_CHIPSET_INIT: + chipset_init(&inmsg); + break; + case CONTROLVM_BUS_CREATE: + bus_create(&inmsg); + break; + case CONTROLVM_BUS_DESTROY: + bus_destroy(&inmsg); + break; + case CONTROLVM_BUS_CONFIGURE: + bus_configure(&inmsg, parser_ctx); + break; + case CONTROLVM_DEVICE_CREATE: + my_device_create(&inmsg); + break; + case CONTROLVM_DEVICE_CHANGESTATE: + if (cmd->device_change_state.flags.phys_device) { + parahotplug_process_message(&inmsg); + } else { + /* save the hdr and cmd structures for later use */ + /* when sending back the response to Command */ + my_device_changestate(&inmsg); + g_devicechangestate_packet = inmsg.cmd; + break; + } + break; + case CONTROLVM_DEVICE_DESTROY: + my_device_destroy(&inmsg); + break; + case CONTROLVM_DEVICE_CONFIGURE: + /* no op for now, just send a respond that we passed */ + if (inmsg.hdr.flags.response_expected) + controlvm_respond(&inmsg.hdr, CONTROLVM_RESP_SUCCESS); + break; + case CONTROLVM_CHIPSET_READY: + chipset_ready(&inmsg.hdr); + break; + case CONTROLVM_CHIPSET_SELFTEST: + chipset_selftest(&inmsg.hdr); + break; + case CONTROLVM_CHIPSET_STOP: + chipset_notready(&inmsg.hdr); + break; + default: + if (inmsg.hdr.flags.response_expected) + controlvm_respond + (&inmsg.hdr, + -CONTROLVM_RESP_ERROR_MESSAGE_ID_UNKNOWN); + break; + } + + if (parser_ctx) { + parser_done(parser_ctx); + parser_ctx = NULL; + } + return true; +} + +static inline unsigned int +issue_vmcall_io_controlvm_addr(u64 *control_addr, u32 *control_bytes) +{ + struct vmcall_io_controlvm_addr_params params; + int result = VMCALL_SUCCESS; + u64 physaddr; + + physaddr = virt_to_phys(¶ms); + ISSUE_IO_VMCALL(VMCALL_IO_CONTROLVM_ADDR, physaddr, result); + if (VMCALL_SUCCESSFUL(result)) { + *control_addr = params.address; + *control_bytes = params.channel_bytes; + } + return result; +} + +static u64 controlvm_get_channel_address(void) +{ + u64 addr = 0; + u32 size = 0; + + if (!VMCALL_SUCCESSFUL(issue_vmcall_io_controlvm_addr(&addr, &size))) + return 0; + + return addr; +} + +static void +controlvm_periodic_work(struct work_struct *work) +{ + struct controlvm_message inmsg; + bool got_command = false; + bool handle_command_failed = false; + + /* make sure visorbus server is registered for controlvm callbacks */ + if (visorchipset_visorbusregwait && !visorbusregistered) + goto cleanup; + + while (visorchannel_signalremove(controlvm_channel, + CONTROLVM_QUEUE_RESPONSE, + &inmsg)) + ; + if (!got_command) { + if (controlvm_pending_msg_valid) { + /* we throttled processing of a prior + * msg, so try to process it again + * rather than reading a new one + */ + inmsg = controlvm_pending_msg; + controlvm_pending_msg_valid = false; + got_command = true; + } else { + got_command = read_controlvm_event(&inmsg); + } + } + + handle_command_failed = false; + while (got_command && (!handle_command_failed)) { + most_recent_message_jiffies = jiffies; + if (handle_command(inmsg, + visorchannel_get_physaddr + (controlvm_channel))) + got_command = read_controlvm_event(&inmsg); + else { + /* this is a scenario where throttling + * is required, but probably NOT an + * error...; we stash the current + * controlvm msg so we will attempt to + * reprocess it on our next loop + */ + handle_command_failed = true; + controlvm_pending_msg = inmsg; + controlvm_pending_msg_valid = true; + } + } + + /* parahotplug_worker */ + parahotplug_process_list(); + +cleanup: + + if (time_after(jiffies, + 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 (poll_jiffies != POLLJIFFIES_CONTROLVMCHANNEL_SLOW) + poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_SLOW; + } else { + if (poll_jiffies != POLLJIFFIES_CONTROLVMCHANNEL_FAST) + poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_FAST; + } + + schedule_delayed_work(&periodic_controlvm_work, poll_jiffies); +} + +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; + + /* make sure visorbus is registered for controlvm callbacks */ + if (visorchipset_visorbusregwait && !visorbusregistered) { + poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_SLOW; + schedule_delayed_work(&periodic_controlvm_work, poll_jiffies); + return; + } + + POSTCODE_LINUX_2(CRASH_DEV_ENTRY_PC, POSTCODE_SEVERITY_INFO); + + /* 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(controlvm_channel, + offsetof(struct spar_controlvm_channel_protocol, + saved_crash_message_count), + &local_crash_msg_count, sizeof(u16)) < 0) { + POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + + if (local_crash_msg_count != CONTROLVM_CRASHMSG_MAX) { + POSTCODE_LINUX_3(CRASH_DEV_COUNT_FAILURE_PC, + local_crash_msg_count, + POSTCODE_SEVERITY_ERR); + return; + } + + /* get saved crash message offset */ + if (visorchannel_read(controlvm_channel, + offsetof(struct spar_controlvm_channel_protocol, + saved_crash_message_offset), + &local_crash_msg_offset, sizeof(u32)) < 0) { + POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + + /* read create device message for storage bus offset */ + if (visorchannel_read(controlvm_channel, + local_crash_msg_offset, + &local_crash_bus_msg, + sizeof(struct controlvm_message)) < 0) { + POSTCODE_LINUX_2(CRASH_DEV_RD_BUS_FAIULRE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + + /* read create device message for storage device */ + if (visorchannel_read(controlvm_channel, + local_crash_msg_offset + + sizeof(struct controlvm_message), + &local_crash_dev_msg, + sizeof(struct controlvm_message)) < 0) { + POSTCODE_LINUX_2(CRASH_DEV_RD_DEV_FAIULRE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + + /* reuse IOVM create bus message */ + if (local_crash_bus_msg.cmd.create_bus.channel_addr) { + bus_create(&local_crash_bus_msg); + } else { + POSTCODE_LINUX_2(CRASH_DEV_BUS_NULL_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + + /* reuse create device message for storage device */ + if (local_crash_dev_msg.cmd.create_device.channel_addr) { + my_device_create(&local_crash_dev_msg); + } else { + POSTCODE_LINUX_2(CRASH_DEV_DEV_NULL_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + POSTCODE_LINUX_2(CRASH_DEV_EXIT_PC, POSTCODE_SEVERITY_INFO); +} + +static void +bus_create_response(struct visor_device *bus_info, int response) +{ + if (response >= 0) + bus_info->state.created = 1; + + bus_responder(CONTROLVM_BUS_CREATE, bus_info->pending_msg_hdr, + response); + + kfree(bus_info->pending_msg_hdr); + bus_info->pending_msg_hdr = NULL; +} + +static void +bus_destroy_response(struct visor_device *bus_info, int response) +{ + bus_responder(CONTROLVM_BUS_DESTROY, bus_info->pending_msg_hdr, + response); + + kfree(bus_info->pending_msg_hdr); + bus_info->pending_msg_hdr = NULL; +} + +static void +device_create_response(struct visor_device *dev_info, int response) +{ + if (response >= 0) + dev_info->state.created = 1; + + device_responder(CONTROLVM_DEVICE_CREATE, dev_info->pending_msg_hdr, + response); + + kfree(dev_info->pending_msg_hdr); + dev_info->pending_msg_hdr = NULL; +} + +static void +device_destroy_response(struct visor_device *dev_info, int response) +{ + device_responder(CONTROLVM_DEVICE_DESTROY, dev_info->pending_msg_hdr, + response); + + kfree(dev_info->pending_msg_hdr); + dev_info->pending_msg_hdr = NULL; +} + +static void +visorchipset_device_pause_response(struct visor_device *dev_info, + int response) +{ + device_changestate_responder(CONTROLVM_DEVICE_CHANGESTATE, + dev_info, response, + segment_state_standby); + + kfree(dev_info->pending_msg_hdr); + dev_info->pending_msg_hdr = NULL; +} + +static void +device_resume_response(struct visor_device *dev_info, int response) +{ + device_changestate_responder(CONTROLVM_DEVICE_CHANGESTATE, + dev_info, response, + segment_state_running); + + kfree(dev_info->pending_msg_hdr); + dev_info->pending_msg_hdr = NULL; +} + +/* 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. + */ +static ssize_t devicedisabled_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, 0); + return count; +} + +/* 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. + */ +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 int +visorchipset_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long physaddr = 0; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + u64 addr = 0; + + /* sv_enable_dfp(); */ + if (offset & (PAGE_SIZE - 1)) + return -ENXIO; /* need aligned offsets */ + + switch (offset) { + case VISORCHIPSET_MMAP_CONTROLCHANOFFSET: + vma->vm_flags |= VM_IO; + if (!*file_controlvm_channel) + return -ENXIO; + + visorchannel_read + (*file_controlvm_channel, + offsetof(struct spar_controlvm_channel_protocol, + gp_control_channel), + &addr, sizeof(addr)); + if (!addr) + return -ENXIO; + + physaddr = (unsigned long)addr; + if (remap_pfn_range(vma, vma->vm_start, + physaddr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + /*pgprot_noncached */ + (vma->vm_page_prot))) { + return -EAGAIN; + } + break; + default: + return -ENXIO; + } + return 0; +} + +static inline s64 issue_vmcall_query_guest_virtual_time_offset(void) +{ + u64 result = VMCALL_SUCCESS; + u64 physaddr = 0; + + ISSUE_IO_VMCALL(VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET, physaddr, + result); + return result; +} + +static inline int issue_vmcall_update_physical_time(u64 adjustment) +{ + int result = VMCALL_SUCCESS; + + ISSUE_IO_VMCALL(VMCALL_UPDATE_PHYSICAL_TIME, adjustment, result); + return result; +} + +static long visorchipset_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + u64 adjustment; + s64 vrtc_offset; + + switch (cmd) { + case VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET: + /* get the physical rtc offset */ + vrtc_offset = issue_vmcall_query_guest_virtual_time_offset(); + if (copy_to_user((void __user *)arg, &vrtc_offset, + sizeof(vrtc_offset))) { + return -EFAULT; + } + return 0; + case VMCALL_UPDATE_PHYSICAL_TIME: + if (copy_from_user(&adjustment, (void __user *)arg, + sizeof(adjustment))) { + return -EFAULT; + } + return issue_vmcall_update_physical_time(adjustment); + default: + return -EFAULT; + } +} + +static const struct file_operations visorchipset_fops = { + .owner = THIS_MODULE, + .open = visorchipset_open, + .read = NULL, + .write = NULL, + .unlocked_ioctl = visorchipset_ioctl, + .release = visorchipset_release, + .mmap = visorchipset_mmap, +}; + +static int +visorchipset_file_init(dev_t major_dev, struct visorchannel **controlvm_channel) +{ + int rc = 0; + + file_controlvm_channel = controlvm_channel; + cdev_init(&file_cdev, &visorchipset_fops); + file_cdev.owner = THIS_MODULE; + if (MAJOR(major_dev) == 0) { + rc = alloc_chrdev_region(&major_dev, 0, 1, "visorchipset"); + /* dynamic major device number registration required */ + if (rc < 0) + return rc; + } else { + /* static major device number registration required */ + rc = register_chrdev_region(major_dev, 1, "visorchipset"); + if (rc < 0) + return rc; + } + rc = cdev_add(&file_cdev, MKDEV(MAJOR(major_dev), 0), 1); + if (rc < 0) { + unregister_chrdev_region(major_dev, 1); + return rc; + } + return 0; +} + +static void +visorchipset_file_cleanup(dev_t major_dev) +{ + if (file_cdev.ops) + cdev_del(&file_cdev); + file_cdev.ops = NULL; + unregister_chrdev_region(major_dev, 1); +} + +static int +visorchipset_init(struct acpi_device *acpi_device) +{ + int err = -ENODEV; + u64 addr; + uuid_le uuid = SPAR_CONTROLVM_CHANNEL_PROTOCOL_UUID; + + addr = controlvm_get_channel_address(); + if (!addr) + goto error; + + memset(&busdev_notifiers, 0, sizeof(busdev_notifiers)); + memset(&controlvm_payload_info, 0, sizeof(controlvm_payload_info)); + + controlvm_channel = visorchannel_create_with_lock(addr, 0, + GFP_KERNEL, uuid); + if (!controlvm_channel) + goto error; + + if (SPAR_CONTROLVM_CHANNEL_OK_CLIENT( + visorchannel_get_header(controlvm_channel))) { + initialize_controlvm_payload(); + } else { + goto error_destroy_channel; + } + + major_dev = MKDEV(visorchipset_major, 0); + err = visorchipset_file_init(major_dev, &controlvm_channel); + if (err < 0) + goto error_destroy_payload; + + /* if booting in a crash kernel */ + if (is_kdump_kernel()) + INIT_DELAYED_WORK(&periodic_controlvm_work, + setup_crash_devices_work_queue); + else + INIT_DELAYED_WORK(&periodic_controlvm_work, + controlvm_periodic_work); + + most_recent_message_jiffies = jiffies; + poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_FAST; + schedule_delayed_work(&periodic_controlvm_work, poll_jiffies); + + visorchipset_platform_device.dev.devt = major_dev; + if (platform_device_register(&visorchipset_platform_device) < 0) { + POSTCODE_LINUX_2(DEVICE_REGISTER_FAILURE_PC, DIAG_SEVERITY_ERR); + err = -ENODEV; + goto error_cancel_work; + } + POSTCODE_LINUX_2(CHIPSET_INIT_SUCCESS_PC, POSTCODE_SEVERITY_INFO); + + err = visorbus_init(); + if (err < 0) + goto error_unregister; + + return 0; + +error_unregister: + platform_device_unregister(&visorchipset_platform_device); + +error_cancel_work: + cancel_delayed_work_sync(&periodic_controlvm_work); + visorchipset_file_cleanup(major_dev); + +error_destroy_payload: + destroy_controlvm_payload_info(&controlvm_payload_info); + +error_destroy_channel: + visorchannel_destroy(controlvm_channel); + +error: + POSTCODE_LINUX_3(CHIPSET_INIT_FAILURE_PC, err, POSTCODE_SEVERITY_ERR); + return err; +} + +static int +visorchipset_exit(struct acpi_device *acpi_device) +{ + POSTCODE_LINUX_2(DRIVER_EXIT_PC, POSTCODE_SEVERITY_INFO); + + visorbus_exit(); + + cancel_delayed_work_sync(&periodic_controlvm_work); + destroy_controlvm_payload_info(&controlvm_payload_info); + + visorchannel_destroy(controlvm_channel); + + visorchipset_file_cleanup(visorchipset_platform_device.dev.devt); + platform_device_unregister(&visorchipset_platform_device); + POSTCODE_LINUX_2(DRIVER_EXIT_PC, POSTCODE_SEVERITY_INFO); + + 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 uint32_t visorutil_spar_detect(void) +{ + unsigned int eax, ebx, ecx, edx; + + if (cpu_has_hypervisor) { + /* check the ID */ + cpuid(UNISYS_SPAR_LEAF_ID, &eax, &ebx, &ecx, &edx); + return (ebx == UNISYS_SPAR_ID_EBX) && + (ecx == UNISYS_SPAR_ID_ECX) && + (edx == UNISYS_SPAR_ID_EDX); + } else { + return 0; + } +} + +static int 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_unisys(void) +{ + acpi_bus_unregister_driver(&unisys_acpi_driver); +} + +module_param_named(major, visorchipset_major, int, S_IRUGO); +MODULE_PARM_DESC(visorchipset_major, + "major device number to use for the device node"); +module_param_named(visorbusregwait, visorchipset_visorbusregwait, int, S_IRUGO); +MODULE_PARM_DESC(visorchipset_visorbusregwait, + "1 to have the module wait for the visor bus to register"); + +module_init(init_unisys); +module_exit(exit_unisys); + +MODULE_AUTHOR("Unisys"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Supervisor chipset driver for service partition: ver " + VERSION); +MODULE_VERSION(VERSION); diff --git a/arch/x86/visorbus/vmcallinterface.h b/arch/x86/visorbus/vmcallinterface.h new file mode 100644 index 0000000..aac7000 --- /dev/null +++ b/arch/x86/visorbus/vmcallinterface.h @@ -0,0 +1,91 @@ +/* Copyright (C) 2010 - 2015 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __IOMONINTF_H__ +#define __IOMONINTF_H__ + +/* +* This file contains all structures needed to support the VMCALLs for IO +* Virtualization. The VMCALLs are provided by Monitor and used by IO code +* running on IO Partitions. +*/ + +#include <linux/visorbus/diagchannel.h> + +#include "iovmcall_gnuc.h" + +#ifdef VMCALL_IO_CONTROLVM_ADDR +#undef VMCALL_IO_CONTROLVM_ADDR +#endif /* */ + +/* define subsystem number for AppOS, used in uislib driver */ +#define MDS_APPOS 0x4000000000000000L /* subsystem = 62 - AppOS */ +enum vmcall_monitor_interface_method_tuple { /* VMCALL identification tuples */ + /* Note: when a new VMCALL is added: + * - the 1st 2 hex digits correspond to one of the + * VMCALL_MONITOR_INTERFACE types and + * - the next 2 hex digits are the nth relative instance of within a + * type + * E.G. for VMCALL_VIRTPART_RECYCLE_PART, + * - the 0x02 identifies it as a VMCALL_VIRTPART type and + * - the 0x01 identifies it as the 1st instance of a VMCALL_VIRTPART + * type of VMCALL + */ + /* used by all Guests, not just IO */ + VMCALL_IO_CONTROLVM_ADDR = 0x0501, + /* Allow caller to query virtual time offset */ + VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET = 0x0708, + /* LOGEVENT Post Code (RDX) with specified subsystem mask */ + /* (RCX - monitor_subsystems.h) and severity (RDX) */ + VMCALL_POST_CODE_LOGEVENT = 0x070B, + /* Allow ULTRA_SERVICE_CAPABILITY_TIME capable guest to make VMCALL */ + VMCALL_UPDATE_PHYSICAL_TIME = 0x0a02 +}; + +#define VMCALL_SUCCESS 0 +#define VMCALL_SUCCESSFUL(result) (result == 0) + +#ifdef __GNUC__ +#define unisys_vmcall(tuple, reg_ebx, reg_ecx) \ + __unisys_vmcall_gnuc(tuple, reg_ebx, reg_ecx) +#define unisys_extended_vmcall(tuple, reg_ebx, reg_ecx, reg_edx) \ + __unisys_extended_vmcall_gnuc(tuple, reg_ebx, reg_ecx, reg_edx) +#define ISSUE_IO_VMCALL(method, param, result) \ + (result = unisys_vmcall(method, (param) & 0xFFFFFFFF, \ + (param) >> 32)) +#define ISSUE_IO_EXTENDED_VMCALL(method, param1, param2, param3) \ + unisys_extended_vmcall(method, param1, param2, param3) + + /* The following uses VMCALL_POST_CODE_LOGEVENT interface but is currently + * not used much + */ +#define ISSUE_IO_VMCALL_POSTCODE_SEVERITY(postcode, severity) \ + ISSUE_IO_EXTENDED_VMCALL(VMCALL_POST_CODE_LOGEVENT, severity, \ + MDS_APPOS, postcode) +#endif + +/* Structures for IO VMCALLs */ + +/* Parameters to VMCALL_IO_CONTROLVM_ADDR interface */ +struct vmcall_io_controlvm_addr_params { + /* The Guest-relative physical address of the ControlVm channel. */ + /* This VMCall fills this in with the appropriate address. */ + u64 address; /* contents provided by this VMCALL (OUT) */ + /* the size of the ControlVm channel in bytes This VMCall fills this */ + /* in with the appropriate address. */ + u32 channel_bytes; /* contents provided by this VMCALL (OUT) */ + u8 unused[4]; /* Unused Bytes in the 64-Bit Aligned Struct */ +} __packed; + +#endif /* __IOMONINTF_H__ */ diff --git a/drivers/staging/unisys/Kconfig b/drivers/staging/unisys/Kconfig index 4f1f5e6..688b5e3 100644 --- a/drivers/staging/unisys/Kconfig +++ b/drivers/staging/unisys/Kconfig @@ -11,7 +11,6 @@ menuconfig UNISYSSPAR if UNISYSSPAR -source "drivers/staging/unisys/visorbus/Kconfig" source "drivers/staging/unisys/visornic/Kconfig" source "drivers/staging/unisys/visorinput/Kconfig" source "drivers/staging/unisys/visorhba/Kconfig" diff --git a/drivers/staging/unisys/Makefile b/drivers/staging/unisys/Makefile index 20eb098..e45f44b 100644 --- a/drivers/staging/unisys/Makefile +++ b/drivers/staging/unisys/Makefile @@ -1,7 +1,6 @@ # # Makefile for Unisys SPAR drivers # -obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ obj-$(CONFIG_UNISYS_VISORNIC) += visornic/ obj-$(CONFIG_UNISYS_VISORINPUT) += visorinput/ obj-$(CONFIG_UNISYS_VISORHBA) += visorhba/ diff --git a/drivers/staging/unisys/visorbus/Kconfig b/drivers/staging/unisys/visorbus/Kconfig deleted file mode 100644 index 5113880..0000000 --- a/drivers/staging/unisys/visorbus/Kconfig +++ /dev/null @@ -1,14 +0,0 @@ -# -# Unisys visorbus configuration -# - -config UNISYS_VISORBUS - tristate "Unisys visorbus driver" - depends on UNISYSSPAR - ---help--- - The visorbus driver is a virtualized bus for the Unisys s-Par firmware. - Virtualized devices allow Linux guests on a system to share disks and - network cards that do not have SR-IOV support, and to be accessed using - the partition desktop application. The visorbus driver is required to - discover devices on an s-Par guest, and must be present for any other - s-Par guest driver to function correctly. diff --git a/drivers/staging/unisys/visorbus/Makefile b/drivers/staging/unisys/visorbus/Makefile deleted file mode 100644 index fc790e7..0000000 --- a/drivers/staging/unisys/visorbus/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# -# Makefile for Unisys visorbus -# - -obj-$(CONFIG_UNISYS_VISORBUS) += visorbus.o - -visorbus-y := visorbus_main.o -visorbus-y += visorchannel.o -visorbus-y += visorchipset.o -visorbus-y += periodic_work.o - -ccflags-y += -Idrivers/staging/unisys/include diff --git a/drivers/staging/unisys/visorbus/controlvmchannel.h b/drivers/staging/unisys/visorbus/controlvmchannel.h deleted file mode 100644 index 0a0e221..0000000 --- a/drivers/staging/unisys/visorbus/controlvmchannel.h +++ /dev/null @@ -1,485 +0,0 @@ -/* Copyright (C) 2010 - 2015 UNISYS CORPORATION - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - */ - -#ifndef __CONTROLVMCHANNEL_H__ -#define __CONTROLVMCHANNEL_H__ - -#include <linux/uuid.h> -#include <linux/visorbus/channel.h> - -/* {2B3C2D10-7EF5-4ad8-B966-3448B7386B3D} */ -#define SPAR_CONTROLVM_CHANNEL_PROTOCOL_UUID \ - UUID_LE(0x2b3c2d10, 0x7ef5, 0x4ad8, \ - 0xb9, 0x66, 0x34, 0x48, 0xb7, 0x38, 0x6b, 0x3d) - -#define ULTRA_CONTROLVM_CHANNEL_PROTOCOL_SIGNATURE \ - ULTRA_CHANNEL_PROTOCOL_SIGNATURE -#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 ULTRA_CONTROLVM_CHANNEL_PROTOCOL_VERSIONID 1 - -#define SPAR_CONTROLVM_CHANNEL_OK_CLIENT(ch) \ - spar_check_channel_client(ch, \ - SPAR_CONTROLVM_CHANNEL_PROTOCOL_UUID, \ - "controlvm", \ - sizeof(struct spar_controlvm_channel_protocol), \ - ULTRA_CONTROLVM_CHANNEL_PROTOCOL_VERSIONID, \ - ULTRA_CONTROLVM_CHANNEL_PROTOCOL_SIGNATURE) - -#define MAX_SERIAL_NUM 32 - -/* 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 spar_segment_state { - /* Bit 0: May enter other states */ - u16 enabled:1; - /* Bit 1: Assigned to active partition */ - u16 active:1; - /* Bit 2: Configure message sent to service/server */ - u16 alive:1; - /* Bit 3: similar to partition state ShuttingDown */ - u16 revoked:1; - /* Bit 4: memory (device/port number) has been selected by Command */ - u16 allocated:1; - /* Bit 5: has been introduced to the service/guest partition */ - u16 known:1; - /* Bit 6: service/Guest partition has responded to introduction */ - u16 ready:1; - /* Bit 7: resource is configured and operating */ - u16 operating:1; -/* Note: don't use high bit unless we need to switch to ushort - * which is non-compliant - */ -}; - -static const struct spar_segment_state segment_state_running = { - 1, 1, 1, 0, 1, 1, 1, 1 -}; - -static const struct spar_segment_state segment_state_paused = { - 1, 1, 1, 0, 1, 1, 1, 0 -}; - -static const struct spar_segment_state segment_state_standby = { - 1, 1, 0, 0, 1, 1, 1, 0 -}; - -/* 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, /* CP --> SP, GP */ - CONTROLVM_BUS_DESTROY = 0x102, /* CP --> SP, GP */ - CONTROLVM_BUS_CONFIGURE = 0x104, /* CP --> SP */ - CONTROLVM_BUS_CHANGESTATE = 0x105, /* CP --> SP, GP */ - CONTROLVM_BUS_CHANGESTATE_EVENT = 0x106, /* SP, GP --> CP */ -/* DEVICE commands required Parameter: BusNumber, DeviceNumber */ - - CONTROLVM_DEVICE_CREATE = 0x201, /* CP --> SP, GP */ - CONTROLVM_DEVICE_DESTROY = 0x202, /* CP --> SP, GP */ - CONTROLVM_DEVICE_CONFIGURE = 0x203, /* CP --> SP */ - CONTROLVM_DEVICE_CHANGESTATE = 0x204, /* CP --> SP, GP */ - CONTROLVM_DEVICE_CHANGESTATE_EVENT = 0x205, /* SP, GP --> CP */ - CONTROLVM_DEVICE_RECONFIGURE = 0x206, /* CP --> Boot */ -/* CHIPSET commands */ - CONTROLVM_CHIPSET_INIT = 0x301, /* CP --> SP, GP */ - CONTROLVM_CHIPSET_STOP = 0x302, /* CP --> SP, GP */ - CONTROLVM_CHIPSET_READY = 0x304, /* CP --> SP */ - CONTROLVM_CHIPSET_SELFTEST = 0x305, /* CP --> SP */ - -}; - -struct irq_info { - u64 reserved1; - - /* 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. - */ - u64 recv_irq_handle; - - /* specifies interrupt vector. It, interrupt pin, and shared are - * used to connect to the corresponding interrupt. Used by - * IOPart-GP only. - */ - u32 recv_irq_vector; - - /* 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. - */ - u8 recv_irq_shared; - u8 reserved[3]; /* Natural alignment purposes */ -}; - -struct pci_id { - u16 domain; - u8 bus; - u8 slot; - u8 func; - u8 reserved[3]; /* Natural alignment purposes */ -}; - -struct efi_spar_indication { - u64 boot_to_fw_ui:1; /* Bit 0: Stop in uefi ui */ - u64 clear_nvram:1; /* Bit 1: Clear NVRAM */ - u64 clear_cmos:1; /* Bit 2: Clear CMOS */ - u64 boot_to_tool:1; /* Bit 3: Run install tool */ - /* remaining bits are available */ -}; - -enum ultra_chipset_feature { - ULTRA_CHIPSET_FEATURE_REPLY = 0x00000001, - ULTRA_CHIPSET_FEATURE_PARA_HOTPLUG = 0x00000002, -}; - -/* 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; /* See CONTROLVM_ID. */ - /* For requests, indicates the message type. */ - /* For responses, indicates the type of message we are responding to. */ - - /* Includes size of this struct + size of message */ - u32 message_size; - /* Index of segment containing Vm message/information */ - u32 segment_index; - /* Error status code or result of message completion */ - u32 completion_status; - struct { - /* =1 in a response to signify failure */ - u32 failed:1; - /* =1 in all messages that expect a response */ - u32 response_expected:1; - /* =1 in all bus & device-related messages where the message - * receiver is to act as the bus or device server - */ - u32 server:1; - /* =1 for testing use only (Control and Command ignore this */ - u32 test_message:1; - /* =1 if there are forthcoming responses/acks associated - * with this message - */ - u32 partial_completion:1; - /* =1 this is to let us know to preserve channel contents */ - u32 preserve:1; - /* =1 the DiagWriter is active in the Diagnostic Partition */ - u32 writer_in_diag:1; - } flags; - /* Natural alignment */ - u32 reserved; - /* Identifies the particular message instance */ - u64 message_handle; - /* request instances with the corresponding response instance. */ - /* Offset of payload area from start of this instance */ - u64 payload_vm_offset; - /* Maximum bytes allocated in payload area of ControlVm segment */ - u32 payload_max_bytes; - /* Actual number of bytes of payload area to copy between IO/Command */ - u32 payload_bytes; - /* if non-zero, there is a payload to copy. */ -}; - -struct controlvm_packet_device_create { - u32 bus_no; /* bus # (0..n-1) from the msg receiver's end */ - u32 dev_no; /* bus-relative (0..n-1) device number */ - /* Guest physical address of the channel, which can be dereferenced by - * the receiver of this ControlVm command - */ - u64 channel_addr; - u64 channel_bytes; /* specifies size of the channel in bytes */ - uuid_le data_type_uuid; /* specifies format of data in channel */ - uuid_le dev_inst_uuid; /* instance guid for the device */ - struct irq_info intr; /* specifies interrupt information */ -}; /* for CONTROLVM_DEVICE_CREATE */ - -struct controlvm_packet_device_configure { - /* bus # (0..n-1) from the msg receiver's perspective */ - u32 bus_no; - /* Control uses header SegmentIndex field to access bus number... */ - u32 dev_no; /* bus-relative (0..n-1) device number */ -} ; /* for CONTROLVM_DEVICE_CONFIGURE */ - -struct controlvm_message_device_create { - struct controlvm_message_header header; - struct controlvm_packet_device_create packet; -}; /* total 128 bytes */ - -struct controlvm_message_device_configure { - struct controlvm_message_header header; - struct controlvm_packet_device_configure packet; -}; /* total 56 bytes */ - -/* This is the format for a message in any ControlVm queue. */ -struct controlvm_message_packet { - union { - struct { - /* bus # (0..n-1) from the msg receiver's perspective */ - u32 bus_no; - /* indicates the max number of devices on this bus */ - u32 dev_count; - /* Guest physical address of the channel, which can be - * dereferenced by the receiver of this ControlVm command - */ - u64 channel_addr; - u64 channel_bytes; /* size of the channel */ - /* indicates format of data in bus channel*/ - uuid_le bus_data_type_uuid; - uuid_le bus_inst_uuid; /* instance uuid for the bus */ - } create_bus; /* for CONTROLVM_BUS_CREATE */ - struct { - /* bus # (0..n-1) from the msg receiver's perspective */ - u32 bus_no; - u32 reserved; /* Natural alignment purposes */ - } destroy_bus; /* for CONTROLVM_BUS_DESTROY */ - struct { - /* bus # (0..n-1) from the receiver's perspective */ - u32 bus_no; - u32 reserved1; /* for alignment purposes */ - /* This is used to convert guest physical address to physical address */ - u64 guest_handle; - u64 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. - */ - } configure_bus; /* for CONTROLVM_BUS_CONFIGURE */ - /* for CONTROLVM_DEVICE_CREATE */ - struct controlvm_packet_device_create create_device; - struct { - /* bus # (0..n-1) from the msg receiver's perspective */ - u32 bus_no; - u32 dev_no; /* bus-relative (0..n-1) device # */ - } destroy_device; /* for CONTROLVM_DEVICE_DESTROY */ - /* for CONTROLVM_DEVICE_CONFIGURE */ - struct controlvm_packet_device_configure configure_device; - struct { - /* bus # (0..n-1) from the msg receiver's perspective */ - u32 bus_no; - u32 dev_no; /* bus-relative (0..n-1) device # */ - } reconfigure_device; /* for CONTROLVM_DEVICE_RECONFIGURE */ - struct { - u32 bus_no; - struct spar_segment_state state; - u8 reserved[2]; /* Natural alignment purposes */ - } bus_change_state; /* for CONTROLVM_BUS_CHANGESTATE */ - struct { - u32 bus_no; - u32 dev_no; - struct spar_segment_state state; - struct { - /* =1 if message is for a physical device */ - u32 phys_device:1; - } flags; - u8 reserved[2]; /* Natural alignment purposes */ - } device_change_state; /* for CONTROLVM_DEVICE_CHANGESTATE */ - struct { - u32 bus_no; - u32 dev_no; - struct spar_segment_state state; - u8 reserved[6]; /* Natural alignment purposes */ - } device_change_state_event; - /* for CONTROLVM_DEVICE_CHANGESTATE_EVENT */ - struct { - /* indicates the max number of busses */ - u32 bus_count; - /* indicates the max number of switches */ - u32 switch_count; - enum ultra_chipset_feature features; - u32 platform_number; /* Platform Number */ - } init_chipset; /* for CONTROLVM_CHIPSET_INIT */ - struct { - u32 options; /* reserved */ - u32 test; /* bit 0 set to run embedded selftest */ - } chipset_selftest; /* for CONTROLVM_CHIPSET_SELFTEST */ - /* a physical address of something, that can be dereferenced - * by the receiver of this ControlVm command - */ - u64 addr; - /* a handle of something (depends on command id) */ - u64 handle; - }; -}; - -/* All messages in any ControlVm queue have this layout. */ -struct controlvm_message { - struct controlvm_message_header hdr; - struct controlvm_message_packet cmd; -}; - -struct spar_controlvm_channel_protocol { - struct channel_header header; - u64 gp_controlvm; /* guest phys addr of this channel */ - u64 gp_partition_tables;/* guest phys addr of partition tables */ - u64 gp_diag_guest; /* guest phys addr of diagnostic channel */ - u64 gp_boot_romdisk;/* guest phys addr of (read* only) Boot ROM disk */ - u64 gp_boot_ramdisk;/* guest phys addr of writable Boot RAM disk */ - u64 gp_acpi_table; /* guest phys addr of acpi table */ - u64 gp_control_channel;/* guest phys addr of control channel */ - u64 gp_diag_romdisk;/* guest phys addr of diagnostic ROM disk */ - u64 gp_nvram; /* guest phys addr of NVRAM channel */ - u64 request_payload_offset; /* Offset to request payload area */ - u64 event_payload_offset; /* Offset to event payload area */ - /* Bytes available in request payload area */ - u32 request_payload_bytes; - u32 event_payload_bytes;/* Bytes available in event payload area */ - u32 control_channel_bytes; - u32 nvram_channel_bytes; /* Bytes in PartitionNvram segment */ - u32 message_bytes; /* sizeof(CONTROLVM_MESSAGE) */ - u32 message_count; /* CONTROLVM_MESSAGE_MAX */ - u64 gp_smbios_table; /* guest phys addr of SMBIOS tables */ - u64 gp_physical_smbios_table; /* guest phys addr of SMBIOS table */ - /* ULTRA_MAX_GUESTS_PER_SERVICE */ - char gp_reserved[2688]; - - /* guest physical address of EFI firmware image base */ - u64 virtual_guest_firmware_image_base; - - /* guest physical address of EFI firmware entry point */ - u64 virtual_guest_firmware_entry_point; - - /* guest EFI firmware image size */ - u64 virtual_guest_firmware_image_size; - - /* GPA = 1MB where EFI firmware image is copied to */ - 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; - /* Restore Action field to restore the guest partition */ - u16 restore_action; - /* For Windows guests it shows if the visordisk is in dump mode */ - u16 dump_action; - u16 nvram_fail_count; - u16 saved_crash_message_count; /* = CONTROLVM_CRASHMSG_MAX */ - /* Offset to request payload area needed for crash dump */ - u32 saved_crash_message_offset; - /* Type of error encountered during installation */ - u32 installation_error; - u32 installation_text_id; /* Id of string to display */ - /* Number of remaining installation steps (for progress bars) */ - u16 installation_remaining_steps; - /* ULTRA_TOOL_ACTIONS Installation Action field */ - u8 tool_action; - u8 reserved; /* alignment */ - struct efi_spar_indication efi_spar_ind; - struct efi_spar_indication efi_spar_ind_supported; - u32 sp_reserved; - /* Force signals to begin on 128-byte cache line */ - u8 reserved2[28]; - /* guest partition uses this queue to send requests to Control */ - struct signal_queue_header request_queue; - /* Control uses this queue to respond to service or guest - * partition requests - */ - struct signal_queue_header response_queue; - /* Control uses this queue to send events to guest partition */ - struct signal_queue_header event_queue; - /* Service or guest partition uses this queue to ack Control events */ - struct signal_queue_header event_ack_queue; - /* Request fixed-size message pool - does not include payload */ - struct controlvm_message request_msg[CONTROLVM_MESSAGE_MAX]; - - /* Response fixed-size message pool - does not include payload */ - struct controlvm_message response_msg[CONTROLVM_MESSAGE_MAX]; - - /* Event fixed-size message pool - does not include payload */ - struct controlvm_message event_msg[CONTROLVM_MESSAGE_MAX]; - - /* Ack fixed-size message pool - does not include payload */ - struct controlvm_message event_ack_msg[CONTROLVM_MESSAGE_MAX]; - - /* Message stored during IOVM creation to be reused after crash */ - struct controlvm_message saved_crash_msg[CONTROLVM_CRASHMSG_MAX]; -}; - -/* Offsets for VM channel attributes */ -#define VM_CH_REQ_QUEUE_OFFSET \ - offsetof(struct spar_controlvm_channel_protocol, request_queue) -#define VM_CH_RESP_QUEUE_OFFSET \ - offsetof(struct spar_controlvm_channel_protocol, response_queue) -#define VM_CH_EVENT_QUEUE_OFFSET \ - offsetof(struct spar_controlvm_channel_protocol, event_queue) -#define VM_CH_ACK_QUEUE_OFFSET \ - offsetof(struct spar_controlvm_channel_protocol, event_ack_queue) -#define VM_CH_REQ_MSG_OFFSET \ - offsetof(struct spar_controlvm_channel_protocol, request_msg) -#define VM_CH_RESP_MSG_OFFSET \ - offsetof(struct spar_controlvm_channel_protocol, response_msg) -#define VM_CH_EVENT_MSG_OFFSET \ - offsetof(struct spar_controlvm_channel_protocol, event_msg) -#define VM_CH_ACK_MSG_OFFSET \ - offsetof(struct spar_controlvm_channel_protocol, event_ack_msg) -#define VM_CH_CRASH_MSG_OFFSET \ - offsetof(struct spar_controlvm_channel_protocol, saved_crash_msg) - -/* 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 spar_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; - uuid_le id; - u32 revision; - u32 reserved; /* Natural alignment */ -}; - -#endif /* __CONTROLVMCHANNEL_H__ */ diff --git a/drivers/staging/unisys/visorbus/controlvmcompletionstatus.h b/drivers/staging/unisys/visorbus/controlvmcompletionstatus.h deleted file mode 100644 index 23ad0ea..0000000 --- a/drivers/staging/unisys/visorbus/controlvmcompletionstatus.h +++ /dev/null @@ -1,101 +0,0 @@ -/* controlvmcompletionstatus.c - * - * Copyright (C) 2010 - 2015 UNISYS CORPORATION - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - */ - -/* Defines for all valid values returned in the response message header - * completionStatus field. See controlvmchannel.h for description of - * the header: _CONTROLVM_MESSAGE_HEADER. - */ - -#ifndef __CONTROLVMCOMPLETIONSTATUS_H__ -#define __CONTROLVMCOMPLETIONSTATUS_H__ - -/* General Errors------------------------------------------------------[0-99] */ -#define CONTROLVM_RESP_SUCCESS 0 -#define CONTROLVM_RESP_ERROR_ALREADY_DONE 1 -#define CONTROLVM_RESP_ERROR_IOREMAP_FAILED 2 -#define CONTROLVM_RESP_ERROR_KMALLOC_FAILED 3 -#define CONTROLVM_RESP_ERROR_MESSAGE_ID_UNKNOWN 4 -#define CONTROLVM_RESP_ERROR_MESSAGE_ID_INVALID_FOR_CLIENT 5 - -/* CONTROLVM_INIT_CHIPSET-------------------------------------------[100-199] */ -#define CONTROLVM_RESP_ERROR_CLIENT_SWITCHCOUNT_NONZERO 100 -#define CONTROLVM_RESP_ERROR_EXPECTED_CHIPSET_INIT 101 - -/* Maximum Limit----------------------------------------------------[200-299] */ -#define CONTROLVM_RESP_ERROR_MAX_BUSES 201 /* BUS_CREATE */ -#define CONTROLVM_RESP_ERROR_MAX_DEVICES 202 /* DEVICE_CREATE */ -/* Payload and Parameter Related------------------------------------[400-499] */ -#define CONTROLVM_RESP_ERROR_PAYLOAD_INVALID 400 /* SWITCH_ATTACHEXTPORT, - * DEVICE_CONFIGURE - */ -#define CONTROLVM_RESP_ERROR_INITIATOR_PARAMETER_INVALID 401 /* Multiple */ -#define CONTROLVM_RESP_ERROR_TARGET_PARAMETER_INVALID 402 /* DEVICE_CONFIGURE */ -#define CONTROLVM_RESP_ERROR_CLIENT_PARAMETER_INVALID 403 /* DEVICE_CONFIGURE */ -/* Specified[Packet Structure] Value-------------------------------[500-599] */ -#define CONTROLVM_RESP_ERROR_BUS_INVALID 500 /* SWITCH_ATTACHINTPORT, - * BUS_CONFIGURE, - * DEVICE_CREATE, - * DEVICE_CONFIG - * DEVICE_DESTROY - */ -#define CONTROLVM_RESP_ERROR_DEVICE_INVALID 501 /* SWITCH_ATTACHINTPORT */ - /* DEVICE_CREATE, - * DEVICE_CONFIGURE, - * DEVICE_DESTROY - */ -#define CONTROLVM_RESP_ERROR_CHANNEL_INVALID 502 /* DEVICE_CREATE, - * DEVICE_CONFIGURE - */ -/* Partition Driver Callback Interface----------------------[600-699] */ -#define CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE 604 /* BUS_CREATE, - * BUS_DESTROY, - * DEVICE_CREATE, - * DEVICE_DESTROY - */ -/* Unable to invoke VIRTPCI callback */ -#define CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR 605 - /* BUS_CREATE, - * BUS_DESTROY, - * DEVICE_CREATE, - * DEVICE_DESTROY - */ -/* VIRTPCI Callback returned error */ -#define CONTROLVM_RESP_ERROR_GENERIC_DRIVER_CALLBACK_ERROR 606 - /* SWITCH_ATTACHEXTPORT, - * SWITCH_DETACHEXTPORT - * DEVICE_CONFIGURE - */ - -/* generic device callback returned error */ -/* Bus Related------------------------------------------------------[700-799] */ -#define CONTROLVM_RESP_ERROR_BUS_DEVICE_ATTACHED 700 /* BUS_DESTROY */ -/* Channel Related--------------------------------------------------[800-899] */ -#define CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN 800 /* GET_CHANNELINFO, - * DEVICE_DESTROY - */ -#define CONTROLVM_RESP_ERROR_CHANNEL_SIZE_TOO_SMALL 801 /* DEVICE_CREATE */ -/* Chipset Shutdown Related---------------------------------------[1000-1099] */ -#define CONTROLVM_RESP_ERROR_CHIPSET_SHUTDOWN_FAILED 1000 -#define CONTROLVM_RESP_ERROR_CHIPSET_SHUTDOWN_ALREADY_ACTIVE 1001 - -/* Chipset Stop Related-------------------------------------------[1100-1199] */ -#define CONTROLVM_RESP_ERROR_CHIPSET_STOP_FAILED_BUS 1100 -#define CONTROLVM_RESP_ERROR_CHIPSET_STOP_FAILED_SWITCH 1101 - -/* Device Related-------------------------------------------------[1400-1499] */ -#define CONTROLVM_RESP_ERROR_DEVICE_UDEV_TIMEOUT 1400 - -#endif /* __CONTROLVMCOMPLETIONSTATUS_H__ not defined */ diff --git a/drivers/staging/unisys/visorbus/iovmcall_gnuc.h b/drivers/staging/unisys/visorbus/iovmcall_gnuc.h deleted file mode 100644 index 98ea7f3..0000000 --- a/drivers/staging/unisys/visorbus/iovmcall_gnuc.h +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright (C) 2010 - 2015 UNISYS CORPORATION - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - */ - -/* Linux GCC Version (32-bit and 64-bit) */ -static inline unsigned long -__unisys_vmcall_gnuc(unsigned long tuple, unsigned long reg_ebx, - unsigned long reg_ecx) -{ - unsigned long result = 0; - unsigned int cpuid_eax, cpuid_ebx, cpuid_ecx, cpuid_edx; - - 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)); - return result; -} - -static inline unsigned long -__unisys_extended_vmcall_gnuc(unsigned long long tuple, - unsigned long long reg_ebx, - unsigned long long reg_ecx, - unsigned long long reg_edx) -{ - unsigned long result = 0; - unsigned int cpuid_eax, cpuid_ebx, cpuid_ecx, cpuid_edx; - - 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), "d"(reg_edx)); - return result; -} diff --git a/drivers/staging/unisys/visorbus/periodic_work.c b/drivers/staging/unisys/visorbus/periodic_work.c deleted file mode 100644 index 9b3b150..0000000 --- a/drivers/staging/unisys/visorbus/periodic_work.c +++ /dev/null @@ -1,203 +0,0 @@ -/* periodic_work.c - * - * Copyright (C) 2010 - 2015 UNISYS CORPORATION - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - */ - -/* - * Helper functions to schedule periodic work in Linux kernel mode. - */ -#include <linux/sched.h> -#include <linux/visorbus/periodic_work.h> - -#define MYDRVNAME "periodic_work" - -struct periodic_work { - rwlock_t lock; - struct delayed_work work; - void (*workfunc)(void *); - void *workfuncarg; - bool is_scheduled; - bool want_to_stop; - ulong jiffy_interval; - struct workqueue_struct *workqueue; - const char *devnam; -}; - -static void periodic_work_func(struct work_struct *work) -{ - struct periodic_work *pw; - - pw = container_of(work, struct periodic_work, work.work); - (*pw->workfunc)(pw->workfuncarg); -} - -struct periodic_work -*visor_periodic_work_create(ulong jiffy_interval, - struct workqueue_struct *workqueue, - void (*workfunc)(void *), - void *workfuncarg, - const char *devnam) -{ - struct periodic_work *pw; - - pw = kzalloc(sizeof(*pw), GFP_KERNEL | __GFP_NORETRY); - if (!pw) - return NULL; - - rwlock_init(&pw->lock); - pw->jiffy_interval = jiffy_interval; - pw->workqueue = workqueue; - pw->workfunc = workfunc; - pw->workfuncarg = workfuncarg; - pw->devnam = devnam; - return pw; -} -EXPORT_SYMBOL_GPL(visor_periodic_work_create); - -void visor_periodic_work_destroy(struct periodic_work *pw) -{ - kfree(pw); -} -EXPORT_SYMBOL_GPL(visor_periodic_work_destroy); - -/** Call this from your periodic work worker function to schedule the next - * call. - * If this function returns false, there was a failure and the - * periodic work is no longer scheduled - */ -bool visor_periodic_work_nextperiod(struct periodic_work *pw) -{ - bool rc = false; - - write_lock(&pw->lock); - if (pw->want_to_stop) { - pw->is_scheduled = false; - pw->want_to_stop = false; - rc = true; /* yes, true; see visor_periodic_work_stop() */ - goto unlock; - } else if (!queue_delayed_work(pw->workqueue, &pw->work, - pw->jiffy_interval)) { - pw->is_scheduled = false; - rc = false; - goto unlock; - } - rc = true; -unlock: - write_unlock(&pw->lock); - return rc; -} -EXPORT_SYMBOL_GPL(visor_periodic_work_nextperiod); - -/** This function returns true iff new periodic work was actually started. - * If this function returns false, then no work was started - * (either because it was already started, or because of a failure). - */ -bool visor_periodic_work_start(struct periodic_work *pw) -{ - bool rc = false; - - write_lock(&pw->lock); - if (pw->is_scheduled) { - rc = false; - goto unlock; - } - if (pw->want_to_stop) { - rc = false; - goto unlock; - } - INIT_DELAYED_WORK(&pw->work, &periodic_work_func); - if (!queue_delayed_work(pw->workqueue, &pw->work, - pw->jiffy_interval)) { - rc = false; - goto unlock; - } - pw->is_scheduled = true; - rc = true; -unlock: - write_unlock(&pw->lock); - return rc; -} -EXPORT_SYMBOL_GPL(visor_periodic_work_start); - -/** This function returns true iff your call actually stopped the periodic - * work. - * - * -- PAY ATTENTION... this is important -- - * - * NO NO #1 - * - * Do NOT call this function from some function that is running on the - * same workqueue as the work you are trying to stop might be running - * on! If you violate this rule, visor_periodic_work_stop() MIGHT work, - * but it also MIGHT get hung up in an infinite loop saying - * "waiting for delayed work...". This will happen if the delayed work - * you are trying to cancel has been put in the workqueue list, but can't - * run yet because we are running that same workqueue thread right now. - * - * Bottom line: If you need to call visor_periodic_work_stop() from a - * workitem, be sure the workitem is on a DIFFERENT workqueue than the - * workitem that you are trying to cancel. - * - * If I could figure out some way to check for this "no no" condition in - * the code, I would. It would have saved me the trouble of writing this - * long comment. And also, don't think this is some "theoretical" race - * condition. It is REAL, as I have spent the day chasing it. - * - * NO NO #2 - * - * Take close note of the locks that you own when you call this function. - * You must NOT own any locks that are needed by the periodic work - * function that is currently installed. If you DO, a deadlock may result, - * because stopping the periodic work often involves waiting for the last - * iteration of the periodic work function to complete. Again, if you hit - * this deadlock, you will get hung up in an infinite loop saying - * "waiting for delayed work...". - */ -bool visor_periodic_work_stop(struct periodic_work *pw) -{ - bool stopped_something = false; - - write_lock(&pw->lock); - stopped_something = pw->is_scheduled && (!pw->want_to_stop); - while (pw->is_scheduled) { - pw->want_to_stop = true; - if (cancel_delayed_work(&pw->work)) { - /* We get here if the delayed work was pending as - * delayed work, but was NOT run. - */ - WARN_ON(!pw->is_scheduled); - pw->is_scheduled = false; - } else { - /* If we get here, either the delayed work: - * - was run, OR, - * - is running RIGHT NOW on another processor, OR, - * - wasn't even scheduled (there is a miniscule - * timing window where this could be the case) - * flush_workqueue() would make sure it is finished - * executing, but that still isn't very useful, which - * explains the loop... - */ - } - if (pw->is_scheduled) { - write_unlock(&pw->lock); - schedule_timeout_interruptible(msecs_to_jiffies(10)); - write_lock(&pw->lock); - } else { - pw->want_to_stop = false; - } - } - write_unlock(&pw->lock); - return stopped_something; -} -EXPORT_SYMBOL_GPL(visor_periodic_work_stop); diff --git a/drivers/staging/unisys/visorbus/vbuschannel.h b/drivers/staging/unisys/visorbus/vbuschannel.h deleted file mode 100644 index 3e0388d..0000000 --- a/drivers/staging/unisys/visorbus/vbuschannel.h +++ /dev/null @@ -1,95 +0,0 @@ -/* Copyright (C) 2010 - 2015 UNISYS CORPORATION - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - */ - -#ifndef __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/channel.h> - -#include "vbusdeviceinfo.h" - -/* {193b331b-c58f-11da-95a9-00e08161165f} */ -#define SPAR_VBUS_CHANNEL_PROTOCOL_UUID \ - UUID_LE(0x193b331b, 0xc58f, 0x11da, \ - 0x95, 0xa9, 0x0, 0xe0, 0x81, 0x61, 0x16, 0x5f) -static const uuid_le spar_vbus_channel_protocol_uuid = - SPAR_VBUS_CHANNEL_PROTOCOL_UUID; - -#define SPAR_VBUS_CHANNEL_PROTOCOL_SIGNATURE ULTRA_CHANNEL_PROTOCOL_SIGNATURE - -/* 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 SPAR_VBUS_CHANNEL_PROTOCOL_VERSIONID 1 - -#define SPAR_VBUS_CHANNEL_OK_CLIENT(ch) \ - spar_check_channel_client(ch, \ - spar_vbus_channel_protocol_uuid, \ - "vbus", \ - sizeof(struct spar_vbus_channel_protocol),\ - SPAR_VBUS_CHANNEL_PROTOCOL_VERSIONID, \ - SPAR_VBUS_CHANNEL_PROTOCOL_SIGNATURE) - -#define SPAR_VBUS_CHANNEL_OK_SERVER(actual_bytes) \ - (spar_check_channel_server(spar_vbus_channel_protocol_uuid, \ - "vbus", \ - sizeof(struct spar_vbus_channel_protocol),\ - actual_bytes)) - -#pragma pack(push, 1) /* both GCC and VC now allow this pragma */ -struct spar_vbus_headerinfo { - u32 struct_bytes; /* size of this struct in bytes */ - u32 device_info_struct_bytes; /* sizeof(ULTRA_VBUS_DEVICEINFO) */ - u32 dev_info_count; /* num of items in DevInfo member */ - /* (this is the allocated size) */ - u32 chp_info_offset; /* byte offset from beginning of this struct */ - /* to the ChpInfo struct (below) */ - u32 bus_info_offset; /* byte offset from beginning of this struct */ - /* to the BusInfo struct (below) */ - u32 dev_info_offset; /* byte offset from beginning of this struct */ - /* to the DevInfo array (below) */ - u8 reserved[104]; -}; - -struct spar_vbus_channel_protocol { - struct channel_header channel_header; /* initialized by server */ - struct spar_vbus_headerinfo hdr_info; /* initialized by server */ - /* the remainder of this channel is filled in by the client */ - struct ultra_vbus_deviceinfo chp_info; - /* describes client chipset device and driver */ - struct ultra_vbus_deviceinfo bus_info; - /* describes client bus device and driver */ - struct ultra_vbus_deviceinfo dev_info[0]; - /* describes client device and driver for each device on the bus */ -}; - -#define VBUS_CH_SIZE_EXACT(MAXDEVICES) \ - (sizeof(ULTRA_VBUS_CHANNEL_PROTOCOL) + ((MAXDEVICES) * \ - sizeof(ULTRA_VBUS_DEVICEINFO))) -#define VBUS_CH_SIZE(MAXDEVICES) COVER(VBUS_CH_SIZE_EXACT(MAXDEVICES), 4096) - -#pragma pack(pop) - -#endif diff --git a/drivers/staging/unisys/visorbus/vbusdeviceinfo.h b/drivers/staging/unisys/visorbus/vbusdeviceinfo.h deleted file mode 100644 index abdab4a..0000000 --- a/drivers/staging/unisys/visorbus/vbusdeviceinfo.h +++ /dev/null @@ -1,213 +0,0 @@ -/* Copyright (C) 2010 - 2015 UNISYS CORPORATION - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - */ - -#ifndef __VBUSDEVICEINFO_H__ -#define __VBUSDEVICEINFO_H__ - -#include <linux/types.h> - -#pragma pack(push, 1) /* both GCC and VC now allow this pragma */ - -/* An array of this struct is present in the channel area for each vbus. - * (See vbuschannel.h.) - * It is filled in by the client side to provide info about the device - * and driver from the client's perspective. - */ -struct ultra_vbus_deviceinfo { - u8 devtype[16]; /* short string identifying the device type */ - u8 drvname[16]; /* driver .sys file name */ - u8 infostrs[96]; /* sequence of tab-delimited id strings: */ - /* <DRIVER_REV> <DRIVER_VERTAG> <DRIVER_COMPILETIME> */ - u8 reserved[128]; /* pad size to 256 bytes */ -}; - -#pragma pack(pop) - -/* Reads chars from the buffer at <src> for <srcmax> bytes, and writes to - * the buffer at <p>, which is <remain> bytes long, ensuring never to - * overflow the buffer at <p>, using the following rules: - * - printable characters are simply copied from the buffer at <src> to the - * buffer at <p> - * - intervening streaks of non-printable characters in the buffer at <src> - * are replaced with a single space in the buffer at <p> - * Note that we pay no attention to '\0'-termination. - * Returns the number of bytes written to <p>. - * - * Pass <p> == NULL and <remain> == 0 for this special behavior. In this - * case, we simply return the number of bytes that WOULD HAVE been written - * to a buffer at <p>, had it been infinitely big. - */ -static inline int -vbuschannel_sanitize_buffer(char *p, int remain, char *src, int srcmax) -{ - int chars = 0; - int nonprintable_streak = 0; - - while (srcmax > 0) { - if ((*src >= ' ') && (*src < 0x7f)) { - if (nonprintable_streak) { - if (remain > 0) { - *p = ' '; - p++; - remain--; - chars++; - } else if (!p) { - chars++; - } - nonprintable_streak = 0; - } - if (remain > 0) { - *p = *src; - p++; - remain--; - chars++; - } else if (!p) { - chars++; - } - } else { - nonprintable_streak = 1; - } - src++; - srcmax--; - } - return chars; -} - -#define VBUSCHANNEL_ADDACHAR(ch, p, remain, chars) \ - do { \ - if (remain <= 0) \ - break; \ - *p = ch; \ - p++; chars++; remain--; \ - } while (0) - -/* Converts the non-negative value at <num> to an ascii decimal string - * at <p>, writing at most <remain> bytes. Note there is NO '\0' termination - * written to <p>. - * - * Returns the number of bytes written to <p>. - * - * Note that we create this function because we need to do this operation in - * an environment-independent way (since we are in a common header file). - */ -static inline int -vbuschannel_itoa(char *p, int remain, int num) -{ - int digits = 0; - char s[32]; - int i; - - if (num == 0) { - /* '0' is a special case */ - if (remain <= 0) - return 0; - *p = '0'; - return 1; - } - /* form a backwards decimal ascii string in <s> */ - while (num > 0) { - if (digits >= (int)sizeof(s)) - return 0; - s[digits++] = (num % 10) + '0'; - num = num / 10; - } - if (remain < digits) { - /* not enough room left at <p> to hold number, so fill with - * '?' - */ - for (i = 0; i < remain; i++, p++) - *p = '?'; - return remain; - } - /* plug in the decimal ascii string representing the number, by */ - /* reversing the string we just built in <s> */ - i = digits; - while (i > 0) { - i--; - *p = s[i]; - p++; - } - return digits; -} - -/* Reads <devInfo>, and converts its contents to a printable string at <p>, - * writing at most <remain> bytes. Note there is NO '\0' termination - * written to <p>. - * - * Pass <devix> >= 0 if you want a device index presented. - * - * Returns the number of bytes written to <p>. - */ -static inline int -vbuschannel_devinfo_to_string(struct ultra_vbus_deviceinfo *devinfo, - char *p, int remain, int devix) -{ - char *psrc; - int nsrc, x, i, pad; - int chars = 0; - - psrc = &devinfo->devtype[0]; - nsrc = sizeof(devinfo->devtype); - if (vbuschannel_sanitize_buffer(NULL, 0, psrc, nsrc) <= 0) - return 0; - - /* emit device index */ - if (devix >= 0) { - VBUSCHANNEL_ADDACHAR('[', p, remain, chars); - x = vbuschannel_itoa(p, remain, devix); - p += x; - remain -= x; - chars += x; - VBUSCHANNEL_ADDACHAR(']', p, remain, chars); - } else { - VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); - VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); - VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); - } - - /* emit device type */ - x = vbuschannel_sanitize_buffer(p, remain, psrc, nsrc); - p += x; - remain -= x; - chars += x; - pad = 15 - x; /* pad device type to be exactly 15 chars */ - for (i = 0; i < pad; i++) - VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); - VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); - - /* emit driver name */ - psrc = &devinfo->drvname[0]; - nsrc = sizeof(devinfo->drvname); - x = vbuschannel_sanitize_buffer(p, remain, psrc, nsrc); - p += x; - remain -= x; - chars += x; - pad = 15 - x; /* pad driver name to be exactly 15 chars */ - for (i = 0; i < pad; i++) - VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); - VBUSCHANNEL_ADDACHAR(' ', p, remain, chars); - - /* emit strings */ - psrc = &devinfo->infostrs[0]; - nsrc = sizeof(devinfo->infostrs); - x = vbuschannel_sanitize_buffer(p, remain, psrc, nsrc); - p += x; - remain -= x; - chars += x; - VBUSCHANNEL_ADDACHAR('\n', p, remain, chars); - - return chars; -} - -#endif diff --git a/drivers/staging/unisys/visorbus/vbushelper.h b/drivers/staging/unisys/visorbus/vbushelper.h deleted file mode 100644 index f1b6aac..0000000 --- a/drivers/staging/unisys/visorbus/vbushelper.h +++ /dev/null @@ -1,46 +0,0 @@ -/* vbushelper.h - * - * Copyright (C) 2011 - 2013 UNISYS CORPORATION - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - */ - -#ifndef __VBUSHELPER_H__ -#define __VBUSHELPER_H__ - -/* TARGET_HOSTNAME specified as -DTARGET_HOSTNAME=\"thename\" on the - * command line - */ - -#define TARGET_HOSTNAME "linuxguest" - -static inline void bus_device_info_init( - struct ultra_vbus_deviceinfo *bus_device_info_ptr, - const char *dev_type, const char *drv_name, - const char *ver, const char *ver_tag) -{ - memset(bus_device_info_ptr, 0, sizeof(struct ultra_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), "%s\t%s\t%s", - (ver) ? ver : "unknownVer", - (ver_tag) ? ver_tag : "unknownVerTag", - TARGET_HOSTNAME); -} - -#endif diff --git a/drivers/staging/unisys/visorbus/visorbus_main.c b/drivers/staging/unisys/visorbus/visorbus_main.c deleted file mode 100644 index efd87c6..0000000 --- a/drivers/staging/unisys/visorbus/visorbus_main.c +++ /dev/null @@ -1,1344 +0,0 @@ -/* visorbus_main.c - * - * Copyright � 2010 - 2015 UNISYS CORPORATION - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - */ - -#include <linux/uuid.h> - -#include <linux/visorbus/visorbus.h> -#include <linux/visorbus/version.h> -#include <linux/visorbus/periodic_work.h> -#include <linux/visorbus/guestlinuxdebug.h> -#include "visorbus_private.h" -#include "vbuschannel.h" -#include "vmcallinterface.h" - -#define MYDRVNAME "visorbus" - -/* module parameters */ -static int visorbus_debug; -static int visorbus_forcematch; -static int visorbus_forcenomatch; -static int visorbus_debugref; -#define SERIALLOOPBACKCHANADDR (100 * 1024 * 1024) - -/* Display string that is guaranteed to be no longer the 99 characters*/ -#define LINESIZE 99 - -#define CURRENT_FILE_PC VISOR_BUS_PC_visorbus_main_c -#define POLLJIFFIES_TESTWORK 100 -#define POLLJIFFIES_NORMALCHANNEL 10 - -static int busreg_rc = -ENODEV; /* stores the result from bus registration */ - -static int visorbus_uevent(struct device *xdev, struct kobj_uevent_env *env); -static int visorbus_match(struct device *xdev, struct device_driver *xdrv); -static void fix_vbus_dev_info(struct visor_device *visordev); - -/* BUS type attributes - * - * define & implement display of bus attributes under - * /sys/bus/visorbus. - * - */ - -static ssize_t version_show(struct bus_type *bus, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%s\n", VERSION); -} - -static BUS_ATTR_RO(version); - -static struct attribute *visorbus_bus_attrs[] = { - &bus_attr_version.attr, - NULL, -}; - -static const struct attribute_group visorbus_bus_group = { - .attrs = visorbus_bus_attrs, -}; - -static const struct attribute_group *visorbus_bus_groups[] = { - &visorbus_bus_group, - NULL, -}; - -/* - * 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; - uuid_le guid; - - vdev = to_visor_device(dev); - guid = visorchannel_get_uuid(vdev->visorchannel); - return snprintf(buf, PAGE_SIZE, "visorbus:%pUl\n", &guid); -} -static DEVICE_ATTR_RO(modalias); - -static struct attribute *visorbus_dev_attrs[] = { - &dev_attr_modalias.attr, - NULL, -}; - -/* sysfs example for bridge-only sysfs files using device_type's */ -static const struct attribute_group visorbus_dev_group = { - .attrs = visorbus_dev_attrs, -}; - -static const struct attribute_group *visorbus_dev_groups[] = { - &visorbus_dev_group, - NULL, -}; - -/** This describes the TYPE of bus. - * (Don't confuse this with an INSTANCE of the bus.) - */ -struct bus_type visorbus_type = { - .name = "visorbus", - .match = visorbus_match, - .uevent = visorbus_uevent, - .dev_groups = visorbus_dev_groups, - .bus_groups = visorbus_bus_groups, -}; - -static struct delayed_work periodic_work; - -/* YES, we need 2 workqueues. - * The reason is, workitems on the test queue may need to cancel - * workitems on the other queue. You will be in for trouble if you try to - * do this with workitems queued on the same workqueue. - */ -static struct workqueue_struct *periodic_test_workqueue; -static struct workqueue_struct *periodic_dev_workqueue; -static long long bus_count; /** number of bus instances */ - /** ever-increasing */ - -static void chipset_bus_create(struct visor_device *bus_info); -static void chipset_bus_destroy(struct visor_device *bus_info); -static void chipset_device_create(struct visor_device *dev_info); -static void chipset_device_destroy(struct visor_device *dev_info); -static void chipset_device_pause(struct visor_device *dev_info); -static void chipset_device_resume(struct visor_device *dev_info); - -/** These functions are implemented herein, and are called by the chipset - * driver to notify us about specific events. - */ -static struct visorchipset_busdev_notifiers chipset_notifiers = { - .bus_create = chipset_bus_create, - .bus_destroy = chipset_bus_destroy, - .device_create = chipset_device_create, - .device_destroy = chipset_device_destroy, - .device_pause = chipset_device_pause, - .device_resume = chipset_device_resume, -}; - -/** These functions are implemented in the chipset driver, and we call them - * herein when we want to acknowledge a specific event. - */ -static struct visorchipset_busdev_responders chipset_responders; - -/* filled in with info about parent chipset driver when we register with it */ -static struct ultra_vbus_deviceinfo chipset_driverinfo; -/* filled in with info about this driver, wrt it servicing client busses */ -static struct ultra_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); - -static int -visorbus_uevent(struct device *xdev, struct kobj_uevent_env *env) -{ - struct visor_device *dev; - uuid_le guid; - - dev = to_visor_device(xdev); - guid = visorchannel_get_uuid(dev->visorchannel); - - if (add_uevent_var(env, "MODALIAS=visorbus:%pUl", &guid)) - return -ENOMEM; - return 0; -} - -/* This is called automatically upon adding a visor_device (device_add), or - * adding a visor_driver (visorbus_register_visor_driver), and returns 1 iff the - * provided driver can control the specified device. - */ -static int -visorbus_match(struct device *xdev, struct device_driver *xdrv) -{ - uuid_le channel_type; - int i; - struct visor_device *dev; - struct visor_driver *drv; - - dev = to_visor_device(xdev); - drv = to_visor_driver(xdrv); - channel_type = visorchannel_get_uuid(dev->visorchannel); - - if (visorbus_forcematch) - return 1; - if (visorbus_forcenomatch) - return 0; - if (!drv->channel_types) - return 0; - - for (i = 0; - (uuid_le_cmp(drv->channel_types[i].guid, NULL_UUID_LE) != 0) || - (drv->channel_types[i].name); - i++) - if (uuid_le_cmp(drv->channel_types[i].guid, - channel_type) == 0) - return i + 1; - - return 0; -} - -/** This is called when device_unregister() is called for the bus device - * instance, after all other tasks involved with destroying the device - * are complete. - */ -static void -visorbus_release_busdevice(struct device *xdev) -{ - struct visor_device *dev = dev_get_drvdata(xdev); - - kfree(dev); -} - -/** This is called when device_unregister() is called for each child - * device instance. - */ -static void -visorbus_release_device(struct device *xdev) -{ - struct visor_device *dev = to_visor_device(xdev); - - if (dev->periodic_work) { - visor_periodic_work_destroy(dev->periodic_work); - dev->periodic_work = NULL; - } - if (dev->visorchannel) { - visorchannel_destroy(dev->visorchannel); - dev->visorchannel = NULL; - } - kfree(dev); -} - -/* begin implementation of 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); - - if (!vdev->visorchannel) - return 0; - return snprintf(buf, PAGE_SIZE, "0x%llx\n", - visorchannel_get_physaddr(vdev->visorchannel)); -} - -static ssize_t nbytes_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct visor_device *vdev = to_visor_device(dev); - - if (!vdev->visorchannel) - return 0; - return snprintf(buf, PAGE_SIZE, "0x%lx\n", - visorchannel_get_nbytes(vdev->visorchannel)); -} - -static ssize_t clientpartition_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct visor_device *vdev = to_visor_device(dev); - - if (!vdev->visorchannel) - return 0; - return snprintf(buf, PAGE_SIZE, "0x%llx\n", - visorchannel_get_clientpartition(vdev->visorchannel)); -} - -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]; - - if (!vdev->visorchannel) - return 0; - return snprintf(buf, PAGE_SIZE, "%s\n", - visorchannel_id(vdev->visorchannel, typeid)); -} - -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]; - - if (!vdev->visorchannel) - return 0; - return snprintf(buf, PAGE_SIZE, "%s\n", - visorchannel_zoneid(vdev->visorchannel, zoneid)); -} - -static ssize_t typename_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct visor_device *vdev = to_visor_device(dev); - int i = 0; - struct bus_type *xbus = dev->bus; - struct device_driver *xdrv = dev->driver; - struct visor_driver *drv = NULL; - - if (!vdev->visorchannel || !xbus || !xdrv) - return 0; - i = xbus->match(dev, xdrv); - if (!i) - return 0; - drv = to_visor_driver(xdrv); - return snprintf(buf, PAGE_SIZE, "%s\n", drv->channel_types[i - 1].name); -} - -static DEVICE_ATTR_RO(physaddr); -static DEVICE_ATTR_RO(nbytes); -static DEVICE_ATTR_RO(clientpartition); -static DEVICE_ATTR_RO(typeguid); -static DEVICE_ATTR_RO(zoneguid); -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 -}; - -static struct attribute_group channel_attr_grp = { - .name = "channel", - .attrs = channel_attrs, -}; - -static const struct attribute_group *visorbus_channel_groups[] = { - &channel_attr_grp, - NULL -}; - -/* end implementation of specific channel attributes */ - -/* BUS instance attributes - * - * define & implement display of bus attributes under - * /sys/bus/visorbus/busses/visorbus<n>. - * - * This is a bit hoaky because the kernel does not yet have the infrastructure - * to separate bus INSTANCE attributes from bus TYPE attributes... - * so we roll our own. See businst.c / businst.h. - * - */ - -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 snprintf(buf, PAGE_SIZE, "0x%llx\n", 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 snprintf(buf, PAGE_SIZE, "{%pUb}\n", &vdev->partition_uuid); -} - -static ssize_t partition_name_show(struct device *dev, - struct device_attribute *attr, - char *buf) { - struct visor_device *vdev = to_visor_device(dev); - - return snprintf(buf, PAGE_SIZE, "%s\n", vdev->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 snprintf(buf, PAGE_SIZE, "0x%llx\n", 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 snprintf(buf, PAGE_SIZE, "0x%llx\n", nbytes); -} - -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; - - if (vdev->visorchannel) { - visorchannel_id(vdev->visorchannel, buf); - len = strlen(buf); - buf[len++] = '\n'; - } - return len; -} - -static ssize_t client_bus_info_show(struct device *dev, - struct device_attribute *attr, - char *buf) { - struct visor_device *vdev = to_visor_device(dev); - struct visorchannel *channel = vdev->visorchannel; - - int i, shift, remain = PAGE_SIZE; - unsigned long off; - char *pos = buf; - u8 *partition_name; - struct ultra_vbus_deviceinfo dev_info; - - partition_name = ""; - if (channel) { - if (vdev->name) - partition_name = vdev->name; - shift = snprintf(pos, remain, - "Client device / client driver info for %s eartition (vbus #%d):\n", - partition_name, vdev->chipset_dev_no); - pos += shift; - remain -= shift; - shift = visorchannel_read(channel, - offsetof(struct - spar_vbus_channel_protocol, - chp_info), - &dev_info, sizeof(dev_info)); - if (shift >= 0) { - shift = vbuschannel_devinfo_to_string(&dev_info, pos, - remain, -1); - pos += shift; - remain -= shift; - } - shift = visorchannel_read(channel, - offsetof(struct - spar_vbus_channel_protocol, - bus_info), - &dev_info, sizeof(dev_info)); - if (shift >= 0) { - shift = vbuschannel_devinfo_to_string(&dev_info, pos, - remain, -1); - pos += shift; - remain -= shift; - } - off = offsetof(struct spar_vbus_channel_protocol, dev_info); - i = 0; - while (off + sizeof(dev_info) <= - visorchannel_get_nbytes(channel)) { - shift = visorchannel_read(channel, - off, &dev_info, - sizeof(dev_info)); - if (shift >= 0) { - shift = vbuschannel_devinfo_to_string - (&dev_info, pos, remain, i); - pos += shift; - remain -= shift; - } - off += sizeof(dev_info); - i++; - } - } - return PAGE_SIZE - remain; -} - -static DEVICE_ATTR_RO(partition_handle); -static DEVICE_ATTR_RO(partition_guid); -static DEVICE_ATTR_RO(partition_name); -static DEVICE_ATTR_RO(channel_addr); -static DEVICE_ATTR_RO(channel_bytes); -static DEVICE_ATTR_RO(channel_id); -static DEVICE_ATTR_RO(client_bus_info); - -static struct attribute *dev_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, - &dev_attr_client_bus_info.attr, - NULL -}; - -static struct attribute_group dev_attr_grp = { - .attrs = dev_attrs, -}; - -static const struct attribute_group *visorbus_groups[] = { - &dev_attr_grp, - NULL -}; - -/* DRIVER attributes - * - * define & implement display of driver attributes under - * /sys/bus/visorbus/drivers/<drivername>. - * - */ - -static ssize_t -DRIVER_ATTR_version(struct device_driver *xdrv, char *buf) -{ - struct visor_driver *drv = to_visor_driver(xdrv); - - return snprintf(buf, PAGE_SIZE, "%s\n", drv->version); -} - -static int -register_driver_attributes(struct visor_driver *drv) -{ - struct driver_attribute version = - __ATTR(version, S_IRUGO, DRIVER_ATTR_version, NULL); - drv->version_attr = version; - return driver_create_file(&drv->driver, &drv->version_attr); -} - -static void -unregister_driver_attributes(struct visor_driver *drv) -{ - driver_remove_file(&drv->driver, &drv->version_attr); -} - -static void -dev_periodic_work(void *xdev) -{ - struct visor_device *dev = xdev; - struct visor_driver *drv = to_visor_driver(dev->device.driver); - - down(&dev->visordriver_callback_lock); - if (drv->channel_interrupt) - drv->channel_interrupt(dev); - up(&dev->visordriver_callback_lock); - if (!visor_periodic_work_nextperiod(dev->periodic_work)) - put_device(&dev->device); -} - -static void -dev_start_periodic_work(struct visor_device *dev) -{ - if (dev->being_removed) - return; - /* now up by at least 2 */ - get_device(&dev->device); - if (!visor_periodic_work_start(dev->periodic_work)) - put_device(&dev->device); -} - -static void -dev_stop_periodic_work(struct visor_device *dev) -{ - if (visor_periodic_work_stop(dev->periodic_work)) - put_device(&dev->device); -} - -/** 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. - */ -static int -visordriver_probe_device(struct device *xdev) -{ - int res; - struct visor_driver *drv; - struct visor_device *dev; - - drv = to_visor_driver(xdev->driver); - dev = to_visor_device(xdev); - - if (!drv->probe) - return -ENODEV; - - down(&dev->visordriver_callback_lock); - dev->being_removed = false; - - res = drv->probe(dev); - if (res >= 0) { - /* success: reference kept via unmatched get_device() */ - get_device(&dev->device); - fix_vbus_dev_info(dev); - } - - up(&dev->visordriver_callback_lock); - return res; -} - -/** This is called when device_unregister() is called for each child device - * instance, to notify the appropriate visorbus_driver that the device is - * going away, and to decrease the reference count of the device. - */ -static int -visordriver_remove_device(struct device *xdev) -{ - struct visor_device *dev; - struct visor_driver *drv; - - dev = to_visor_device(xdev); - drv = to_visor_driver(xdev->driver); - down(&dev->visordriver_callback_lock); - dev->being_removed = true; - if (drv->remove) - drv->remove(dev); - up(&dev->visordriver_callback_lock); - dev_stop_periodic_work(dev); - - put_device(&dev->device); - return 0; -} - -/** A particular type of visor 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(). - * - */ -int visorbus_register_visor_driver(struct visor_driver *drv) -{ - int rc = 0; - - if (busreg_rc < 0) - return -ENODEV; /*can't register on a nonexistent bus*/ - - 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 - */ - - rc = driver_register(&drv->driver); - if (rc < 0) - return rc; - rc = register_driver_attributes(drv); - if (rc < 0) - driver_unregister(&drv->driver); - return rc; -} -EXPORT_SYMBOL_GPL(visorbus_register_visor_driver); - -/** A particular type of visor driver calls this function to unregister - * the driver, i.e., within its module_exit function. - */ -void -visorbus_unregister_visor_driver(struct visor_driver *drv) -{ - unregister_driver_attributes(drv); - driver_unregister(&drv->driver); -} -EXPORT_SYMBOL_GPL(visorbus_unregister_visor_driver); - -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); - -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); - -int -visorbus_clear_channel(struct visor_device *dev, unsigned long offset, u8 ch, - unsigned long nbytes) -{ - return visorchannel_clear(dev->visorchannel, offset, ch, nbytes); -} -EXPORT_SYMBOL_GPL(visorbus_clear_channel); - -/** We don't really have a real interrupt, so for now we just call the - * interrupt function periodically... - */ -void -visorbus_enable_channel_interrupts(struct visor_device *dev) -{ - dev_start_periodic_work(dev); -} -EXPORT_SYMBOL_GPL(visorbus_enable_channel_interrupts); - -void -visorbus_disable_channel_interrupts(struct visor_device *dev) -{ - dev_stop_periodic_work(dev); -} -EXPORT_SYMBOL_GPL(visorbus_disable_channel_interrupts); - -/** 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. - */ -static 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; - - POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, chipset_dev_no, chipset_bus_no, - POSTCODE_SEVERITY_INFO); - - sema_init(&dev->visordriver_callback_lock, 1); /* unlocked */ - dev->device.bus = &visorbus_type; - dev->device.groups = visorbus_channel_groups; - device_initialize(&dev->device); - dev->device.release = visorbus_release_device; - /* keep a reference just for us (now 2) */ - get_device(&dev->device); - dev->periodic_work = - visor_periodic_work_create(POLLJIFFIES_NORMALCHANNEL, - periodic_dev_workqueue, - dev_periodic_work, - dev, dev_name(&dev->device)); - if (!dev->periodic_work) { - POSTCODE_LINUX_3(DEVICE_CREATE_FAILURE_PC, chipset_dev_no, - DIAG_SEVERITY_ERR); - err = -EINVAL; - goto err_put; - } - - /* 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. - */ - dev_set_name(&dev->device, "vbus%u:dev%u", - chipset_bus_no, chipset_dev_no); - - /* 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) { - POSTCODE_LINUX_3(DEVICE_ADD_PC, chipset_bus_no, - DIAG_SEVERITY_ERR); - goto err_put; - } - - list_add_tail(&dev->list_all, &list_all_device_instances); - return 0; /* success: reference kept via unmatched get_device() */ - -err_put: - put_device(&dev->device); - return err; -} - -static void -remove_visor_device(struct visor_device *dev) -{ - list_del(&dev->list_all); - put_device(&dev->device); - device_unregister(&dev->device); -} - -static int -get_vbus_header_info(struct visorchannel *chan, - struct spar_vbus_headerinfo *hdr_info) -{ - if (!SPAR_VBUS_CHANNEL_OK_CLIENT(visorchannel_get_header(chan))) - return -EINVAL; - - if (visorchannel_read(chan, sizeof(struct channel_header), hdr_info, - sizeof(*hdr_info)) < 0) { - return -EIO; - } - if (hdr_info->struct_bytes < sizeof(struct spar_vbus_headerinfo)) - return -EINVAL; - - if (hdr_info->device_info_struct_bytes < - sizeof(struct ultra_vbus_deviceinfo)) { - return -EINVAL; - } - return 0; -} - -/* Write the contents of <info> to the struct - * spar_vbus_channel_protocol.chp_info. - */ - -static int -write_vbus_chp_info(struct visorchannel *chan, - struct spar_vbus_headerinfo *hdr_info, - struct ultra_vbus_deviceinfo *info) -{ - int off = sizeof(struct channel_header) + hdr_info->chp_info_offset; - - if (hdr_info->chp_info_offset == 0) - return -EFAULT; - - if (visorchannel_write(chan, off, info, sizeof(*info)) < 0) - return -EFAULT; - return 0; -} - -/* Write the contents of <info> to the struct - * spar_vbus_channel_protocol.bus_info. - */ - -static int -write_vbus_bus_info(struct visorchannel *chan, - struct spar_vbus_headerinfo *hdr_info, - struct ultra_vbus_deviceinfo *info) -{ - int off = sizeof(struct channel_header) + hdr_info->bus_info_offset; - - if (hdr_info->bus_info_offset == 0) - return -EFAULT; - - if (visorchannel_write(chan, off, info, sizeof(*info)) < 0) - return -EFAULT; - return 0; -} - -/* Write the contents of <info> to the - * struct spar_vbus_channel_protocol.dev_info[<devix>]. - */ -static int -write_vbus_dev_info(struct visorchannel *chan, - struct spar_vbus_headerinfo *hdr_info, - struct ultra_vbus_deviceinfo *info, int devix) -{ - int off = - (sizeof(struct channel_header) + hdr_info->dev_info_offset) + - (hdr_info->device_info_struct_bytes * devix); - - if (hdr_info->dev_info_offset == 0) - return -EFAULT; - - if (visorchannel_write(chan, off, info, sizeof(*info)) < 0) - return -EFAULT; - return 0; -} - -/* For a child device just created on a client bus, fill in - * information about the driver that is controlling this device into - * the the appropriate slot within the vbus channel of the bus - * instance. - */ -static void -fix_vbus_dev_info(struct visor_device *visordev) -{ - int i; - struct visor_device *bdev; - struct visor_driver *visordrv; - int bus_no = visordev->chipset_bus_no; - int dev_no = visordev->chipset_dev_no; - struct ultra_vbus_deviceinfo dev_info; - const char *chan_type_name = NULL; - struct spar_vbus_headerinfo *hdr_info; - - if (!visordev->device.driver) - return; - - hdr_info = (struct spar_vbus_headerinfo *)visordev->vbus_hdr_info; - if (!hdr_info) - return; - - bdev = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); - if (!bdev) - 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 (memcmp(&visordrv->channel_types[i].guid, - &visordev->channel_type_guid, - sizeof(visordrv->channel_types[i].guid)) == 0) { - chan_type_name = visordrv->channel_types[i].name; - break; - } - } - - bus_device_info_init(&dev_info, chan_type_name, - visordrv->name, visordrv->version, - visordrv->vertag); - write_vbus_dev_info(bdev->visorchannel, hdr_info, &dev_info, dev_no); - - /* Re-write bus+chipset info, because it is possible that this - * was previously written by our evil counterpart, virtpci. - */ - write_vbus_chp_info(bdev->visorchannel, hdr_info, &chipset_driverinfo); - write_vbus_bus_info(bdev->visorchannel, hdr_info, - &clientbus_driverinfo); -} - -/** Create a device instance for the visor bus itself. - */ -static int -create_bus_instance(struct visor_device *dev) -{ - int id = dev->chipset_bus_no; - struct spar_vbus_headerinfo *hdr_info; - - POSTCODE_LINUX_2(BUS_CREATE_ENTRY_PC, POSTCODE_SEVERITY_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; - - if (device_register(&dev->device) < 0) { - POSTCODE_LINUX_3(DEVICE_CREATE_FAILURE_PC, id, - POSTCODE_SEVERITY_ERR); - kfree(hdr_info); - return -ENODEV; - } - - if (get_vbus_header_info(dev->visorchannel, hdr_info) >= 0) { - 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); - } else { - kfree(hdr_info); - } - bus_count++; - list_add_tail(&dev->list_all, &list_all_bus_instances); - dev_set_drvdata(&dev->device, dev); - return 0; -} - -/** Remove a device instance for the visor bus itself. - */ -static void -remove_bus_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. - */ - bus_count--; - if (dev->visorchannel) { - visorchannel_destroy(dev->visorchannel); - dev->visorchannel = NULL; - } - kfree(dev->vbus_hdr_info); - list_del(&dev->list_all); - device_unregister(&dev->device); -} - -/** Create and register the one-and-only one instance of - * the visor bus type (visorbus_type). - */ -static int -create_bus_type(void) -{ - busreg_rc = bus_register(&visorbus_type); - return busreg_rc; -} - -/** Remove the one-and-only one instance of the visor bus type (visorbus_type). - */ -static void -remove_bus_type(void) -{ - bus_unregister(&visorbus_type); -} - -/** Remove all child visor bus 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 = list_entry(listentry, - struct visor_device, - list_all); - remove_visor_device(dev); - } -} - -static void -chipset_bus_create(struct visor_device *dev) -{ - int rc; - u32 bus_no = dev->chipset_bus_no; - - POSTCODE_LINUX_3(BUS_CREATE_ENTRY_PC, bus_no, POSTCODE_SEVERITY_INFO); - rc = create_bus_instance(dev); - POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, bus_no, POSTCODE_SEVERITY_INFO); - - if (rc < 0) - POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no, - POSTCODE_SEVERITY_ERR); - else - POSTCODE_LINUX_3(CHIPSET_INIT_SUCCESS_PC, bus_no, - POSTCODE_SEVERITY_INFO); - - if (chipset_responders.bus_create) - (*chipset_responders.bus_create) (dev, rc); -} - -static void -chipset_bus_destroy(struct visor_device *dev) -{ - remove_bus_instance(dev); - if (chipset_responders.bus_destroy) - (*chipset_responders.bus_destroy)(dev, 0); -} - -static void -chipset_device_create(struct visor_device *dev_info) -{ - int rc; - u32 bus_no = dev_info->chipset_bus_no; - u32 dev_no = dev_info->chipset_dev_no; - - POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, dev_no, bus_no, - POSTCODE_SEVERITY_INFO); - - rc = create_visor_device(dev_info); - if (chipset_responders.device_create) - chipset_responders.device_create(dev_info, rc); - - if (rc < 0) - POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no, - POSTCODE_SEVERITY_ERR); - else - POSTCODE_LINUX_4(DEVICE_CREATE_SUCCESS_PC, dev_no, bus_no, - POSTCODE_SEVERITY_INFO); -} - -static void -chipset_device_destroy(struct visor_device *dev_info) -{ - remove_visor_device(dev_info); - - if (chipset_responders.device_destroy) - (*chipset_responders.device_destroy) (dev_info, 0); -} - -/* This is the callback function specified for a function driver, to - * be called when a pending "pause device" operation has been - * completed. - */ -static void -pause_state_change_complete(struct visor_device *dev, int status) -{ - if (!dev->pausing) - return; - - dev->pausing = false; - if (!chipset_responders.device_pause) /* this can never happen! */ - return; - - /* Notify the chipset driver that the pause is complete, which - * will presumably want to send some sort of response to the - * initiator. - */ - (*chipset_responders.device_pause) (dev, status); -} - -/* This is the callback function specified for a function driver, to - * be called when a pending "resume device" operation has been - * completed. - */ -static void -resume_state_change_complete(struct visor_device *dev, int status) -{ - if (!dev->resuming) - return; - - dev->resuming = false; - if (!chipset_responders.device_resume) /* this can never happen! */ - return; - - /* Notify the chipset driver that the resume is complete, - * which will presumably want to send some sort of response to - * the initiator. - */ - (*chipset_responders.device_resume) (dev, status); -} - -/* Tell the subordinate function driver for a specific device to pause - * or resume that device. Result is returned asynchronously via a - * callback function. - */ -static void -initiate_chipset_device_pause_resume(struct visor_device *dev, bool is_pause) -{ - int rc; - struct visor_driver *drv = NULL; - void (*notify_func)(struct visor_device *dev, int response) = NULL; - - if (is_pause) - notify_func = chipset_responders.device_pause; - else - notify_func = chipset_responders.device_resume; - if (!notify_func) - return; - - drv = to_visor_driver(dev->device.driver); - if (!drv) { - (*notify_func)(dev, -ENODEV); - return; - } - - if (dev->pausing || dev->resuming) { - (*notify_func)(dev, -EBUSY); - return; - } - - /* Note that even though both drv->pause() and drv->resume - * specify a callback function, it is NOT necessary for us to - * increment our local module usage count. Reason is, there - * is already a linkage dependency between child function - * drivers and visorbus, so it is already IMPOSSIBLE to unload - * visorbus while child function drivers are still running. - */ - if (is_pause) { - if (!drv->pause) { - (*notify_func)(dev, -EINVAL); - return; - } - - dev->pausing = true; - rc = drv->pause(dev, pause_state_change_complete); - } else { - /* This should be done at BUS resume time, but an - * existing problem prevents us from ever getting a bus - * resume... This hack would fail to work should we - * ever have a bus that contains NO devices, since we - * would never even get here in that case. - */ - fix_vbus_dev_info(dev); - if (!drv->resume) { - (*notify_func)(dev, -EINVAL); - return; - } - - dev->resuming = true; - rc = drv->resume(dev, resume_state_change_complete); - } - if (rc < 0) { - if (is_pause) - dev->pausing = false; - else - dev->resuming = false; - (*notify_func)(dev, -EINVAL); - } -} - -static void -chipset_device_pause(struct visor_device *dev_info) -{ - initiate_chipset_device_pause_resume(dev_info, true); -} - -static void -chipset_device_resume(struct visor_device *dev_info) -{ - initiate_chipset_device_pause_resume(dev_info, false); -} - -struct channel_size_info { - uuid_le guid; - unsigned long min_size; - unsigned long max_size; -}; - -int -visorbus_init(void) -{ - int err; - - POSTCODE_LINUX_3(DRIVER_ENTRY_PC, 0, POSTCODE_SEVERITY_INFO); - bus_device_info_init(&clientbus_driverinfo, - "clientbus", "visorbus", - VERSION, NULL); - - err = create_bus_type(); - if (err < 0) { - POSTCODE_LINUX_2(BUS_CREATE_ENTRY_PC, DIAG_SEVERITY_ERR); - goto error; - } - - periodic_dev_workqueue = create_singlethread_workqueue("visorbus_dev"); - if (!periodic_dev_workqueue) { - POSTCODE_LINUX_2(CREATE_WORKQUEUE_PC, DIAG_SEVERITY_ERR); - err = -ENOMEM; - goto error; - } - - /* This enables us to receive notifications when devices appear for - * which this service partition is to be a server for. - */ - visorchipset_register_busdev(&chipset_notifiers, - &chipset_responders, - &chipset_driverinfo); - - return 0; - -error: - POSTCODE_LINUX_3(CHIPSET_INIT_FAILURE_PC, err, POSTCODE_SEVERITY_ERR); - return err; -} - -void -visorbus_exit(void) -{ - struct list_head *listentry, *listtmp; - - visorchipset_register_busdev(NULL, NULL, NULL); - remove_all_visor_devices(); - - flush_workqueue(periodic_dev_workqueue); /* better not be any work! */ - destroy_workqueue(periodic_dev_workqueue); - periodic_dev_workqueue = NULL; - - if (periodic_test_workqueue) { - cancel_delayed_work(&periodic_work); - flush_workqueue(periodic_test_workqueue); - destroy_workqueue(periodic_test_workqueue); - periodic_test_workqueue = NULL; - } - - list_for_each_safe(listentry, listtmp, &list_all_bus_instances) { - struct visor_device *dev = list_entry(listentry, - struct visor_device, - list_all); - remove_bus_instance(dev); - } - remove_bus_type(); -} - -module_param_named(debug, visorbus_debug, int, S_IRUGO); -MODULE_PARM_DESC(visorbus_debug, "1 to debug"); - -module_param_named(forcematch, visorbus_forcematch, int, S_IRUGO); -MODULE_PARM_DESC(visorbus_forcematch, - "1 to force a successful dev <--> drv match"); - -module_param_named(forcenomatch, visorbus_forcenomatch, int, S_IRUGO); -MODULE_PARM_DESC(visorbus_forcenomatch, - "1 to force an UNsuccessful dev <--> drv match"); - -module_param_named(debugref, visorbus_debugref, int, S_IRUGO); -MODULE_PARM_DESC(visorbus_debugref, "1 to debug reference counting"); diff --git a/drivers/staging/unisys/visorbus/visorbus_private.h b/drivers/staging/unisys/visorbus/visorbus_private.h deleted file mode 100644 index 39edd20..0000000 --- a/drivers/staging/unisys/visorbus/visorbus_private.h +++ /dev/null @@ -1,68 +0,0 @@ -/* visorchipset.h - * - * Copyright (C) 2010 - 2015 UNISYS CORPORATION - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - */ - -#ifndef __VISORCHIPSET_H__ -#define __VISORCHIPSET_H__ - -#include <linux/uuid.h> - -#include "controlvmchannel.h" -#include "vbusdeviceinfo.h" -#include "vbushelper.h" - -/* These functions will be called from within visorchipset when certain - * events happen. (The implementation of these functions is outside of - * visorchipset.) - */ -struct visorchipset_busdev_notifiers { - void (*bus_create)(struct visor_device *bus_info); - void (*bus_destroy)(struct visor_device *bus_info); - void (*device_create)(struct visor_device *bus_info); - void (*device_destroy)(struct visor_device *bus_info); - void (*device_pause)(struct visor_device *bus_info); - void (*device_resume)(struct visor_device *bus_info); -}; - -/* These functions live inside visorchipset, and will be called to indicate - * responses to specific events (by code outside of visorchipset). - * For now, the value for each response is simply either: - * 0 = it worked - * -1 = it failed - */ -struct visorchipset_busdev_responders { - void (*bus_create)(struct visor_device *p, int response); - void (*bus_destroy)(struct visor_device *p, int response); - void (*device_create)(struct visor_device *p, int response); - void (*device_destroy)(struct visor_device *p, int response); - void (*device_pause)(struct visor_device *p, int response); - void (*device_resume)(struct visor_device *p, int response); -}; - -/** Register functions (in the bus driver) to get called by visorchipset - * whenever a bus or device appears for which this guest is to be the - * client for. visorchipset will fill in <responders>, to indicate - * functions the bus driver should call to indicate message responses. - */ -void -visorchipset_register_busdev( - struct visorchipset_busdev_notifiers *notifiers, - struct visorchipset_busdev_responders *responders, - struct ultra_vbus_deviceinfo *driver_info); - -/* visorbus init and exit functions */ -int visorbus_init(void); -void visorbus_exit(void); -#endif diff --git a/drivers/staging/unisys/visorbus/visorchannel.c b/drivers/staging/unisys/visorbus/visorchannel.c deleted file mode 100644 index 4e8b0ef..0000000 --- a/drivers/staging/unisys/visorbus/visorchannel.c +++ /dev/null @@ -1,635 +0,0 @@ -/* visorchannel_funcs.c - * - * Copyright (C) 2010 - 2015 UNISYS CORPORATION - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - */ - -/* - * This provides Supervisor 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/visorbus/version.h> -#include <linux/visorbus/visorbus.h> -#include "controlvmchannel.h" - -#define MYDRVNAME "visorchannel" - -#define SPAR_CONSOLEVIDEO_CHANNEL_PROTOCOL_GUID \ - UUID_LE(0x3cd6e705, 0xd6a2, 0x4aa5, \ - 0xad, 0x5c, 0x7b, 0x8, 0x88, 0x9d, 0xff, 0xe2) -static const uuid_le spar_video_guid = SPAR_CONSOLEVIDEO_CHANNEL_PROTOCOL_GUID; - -struct visorchannel { - u64 physaddr; - ulong nbytes; - void *mapped; - bool requested; - struct channel_header chan_hdr; - uuid_le guid; - bool needs_lock; /* channel creator knows if more than one */ - /* thread will be inserting or removing */ - spinlock_t insert_lock; /* protect head writes in chan_hdr */ - spinlock_t remove_lock; /* protect tail writes in chan_hdr */ - - struct { - struct signal_queue_header req_queue; - struct signal_queue_header rsp_queue; - struct signal_queue_header event_queue; - struct signal_queue_header ack_queue; - } safe_uis_queue; - uuid_le type; - uuid_le inst; -}; - -/* Creates the struct visorchannel abstraction for a data area in memory, - * but does NOT modify this data area. - */ -static struct visorchannel * -visorchannel_create_guts(u64 physaddr, unsigned long channel_bytes, - gfp_t gfp, unsigned long off, - uuid_le 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, MYDRVNAME); - if (!channel->requested) { - if (uuid_le_cmp(guid, spar_video_guid)) { - /* Not the video channel we care about this */ - 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, - sizeof(struct channel_header)); - if (err) - goto err_destroy_channel; - - /* we had better be a CLIENT of this channel */ - if (channel_bytes == 0) - channel_bytes = (ulong)channel->chan_hdr.size; - if (uuid_le_cmp(guid, NULL_UUID_LE) == 0) - guid = channel->chan_hdr.chtype; - - memunmap(channel->mapped); - if (channel->requested) - release_mem_region(channel->physaddr, channel->nbytes); - channel->mapped = NULL; - channel->requested = request_mem_region(channel->physaddr, - channel_bytes, MYDRVNAME); - if (!channel->requested) { - if (uuid_le_cmp(guid, spar_video_guid)) { - /* Different we care about this */ - goto err_destroy_channel; - } - } - - channel->mapped = memremap(channel->physaddr, channel_bytes, - MEMREMAP_WB); - if (!channel->mapped) { - release_mem_region(channel->physaddr, channel_bytes); - goto err_destroy_channel; - } - - channel->nbytes = channel_bytes; - channel->guid = guid; - return channel; - -err_destroy_channel: - visorchannel_destroy(channel); - return NULL; -} - -struct visorchannel * -visorchannel_create(u64 physaddr, unsigned long channel_bytes, - gfp_t gfp, uuid_le guid) -{ - return visorchannel_create_guts(physaddr, channel_bytes, gfp, 0, guid, - false); -} -EXPORT_SYMBOL_GPL(visorchannel_create); - -struct visorchannel * -visorchannel_create_with_lock(u64 physaddr, unsigned long channel_bytes, - gfp_t gfp, uuid_le guid) -{ - return visorchannel_create_guts(physaddr, channel_bytes, gfp, 0, guid, - true); -} -EXPORT_SYMBOL_GPL(visorchannel_create_with_lock); - -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); -} -EXPORT_SYMBOL_GPL(visorchannel_destroy); - -u64 -visorchannel_get_physaddr(struct visorchannel *channel) -{ - return channel->physaddr; -} -EXPORT_SYMBOL_GPL(visorchannel_get_physaddr); - -ulong -visorchannel_get_nbytes(struct visorchannel *channel) -{ - return channel->nbytes; -} -EXPORT_SYMBOL_GPL(visorchannel_get_nbytes); - -char * -visorchannel_uuid_id(uuid_le *guid, char *s) -{ - sprintf(s, "%pUL", guid); - return s; -} -EXPORT_SYMBOL_GPL(visorchannel_uuid_id); - -char * -visorchannel_id(struct visorchannel *channel, char *s) -{ - return visorchannel_uuid_id(&channel->guid, s); -} -EXPORT_SYMBOL_GPL(visorchannel_id); - -char * -visorchannel_zoneid(struct visorchannel *channel, char *s) -{ - return visorchannel_uuid_id(&channel->chan_hdr.zone_uuid, s); -} -EXPORT_SYMBOL_GPL(visorchannel_zoneid); - -u64 -visorchannel_get_clientpartition(struct visorchannel *channel) -{ - return channel->chan_hdr.partition_handle; -} -EXPORT_SYMBOL_GPL(visorchannel_get_clientpartition); - -int -visorchannel_set_clientpartition(struct visorchannel *channel, - u64 partition_handle) -{ - channel->chan_hdr.partition_handle = partition_handle; - return 0; -} -EXPORT_SYMBOL_GPL(visorchannel_set_clientpartition); - -uuid_le -visorchannel_get_uuid(struct visorchannel *channel) -{ - return channel->guid; -} -EXPORT_SYMBOL_GPL(visorchannel_get_uuid); - -int -visorchannel_read(struct visorchannel *channel, ulong offset, - void *local, ulong nbytes) -{ - if (offset + nbytes > channel->nbytes) - return -EIO; - - memcpy(local, channel->mapped + offset, nbytes); - - return 0; -} -EXPORT_SYMBOL_GPL(visorchannel_read); - -int -visorchannel_write(struct visorchannel *channel, ulong offset, - void *local, 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, - local, copy_size); - } - - memcpy(channel->mapped + offset, local, nbytes); - - return 0; -} -EXPORT_SYMBOL_GPL(visorchannel_write); - -int -visorchannel_clear(struct visorchannel *channel, ulong offset, u8 ch, - ulong nbytes) -{ - int err; - int bufsize = PAGE_SIZE; - int written = 0; - u8 *buf; - - buf = (u8 *)__get_free_page(GFP_KERNEL); - if (!buf) - return -ENOMEM; - - memset(buf, ch, bufsize); - - while (nbytes > 0) { - int thisbytes = bufsize; - - if (nbytes < thisbytes) - thisbytes = nbytes; - err = visorchannel_write(channel, offset + written, - buf, thisbytes); - if (err) - goto out_free_page; - - written += thisbytes; - nbytes -= thisbytes; - } - err = 0; - -out_free_page: - free_page((unsigned long)buf); - return err; -} -EXPORT_SYMBOL_GPL(visorchannel_clear); - -void __iomem * -visorchannel_get_header(struct visorchannel *channel) -{ - return (void __iomem *)&channel->chan_hdr; -} -EXPORT_SYMBOL_GPL(visorchannel_get_header); - -/** Return offset of a specific SIGNAL_QUEUE_HEADER from the beginning of a - * channel header - */ -#define SIG_QUEUE_OFFSET(chan_hdr, q) \ - ((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 - */ -#define SIG_DATA_OFFSET(chan_hdr, q, sig_hdr, slot) \ - (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)) >= 0) - -static bool -sig_read_header(struct visorchannel *channel, u32 queue, - struct signal_queue_header *sig_hdr) -{ - int err; - - if (channel->chan_hdr.ch_space_offset < sizeof(struct channel_header)) - return false; - - /* Read the appropriate SIGNAL_QUEUE_HEADER into local memory. */ - err = visorchannel_read(channel, - SIG_QUEUE_OFFSET(&channel->chan_hdr, queue), - sig_hdr, sizeof(struct signal_queue_header)); - if (err) - return false; - - return true; -} - -static inline bool -sig_read_data(struct visorchannel *channel, u32 queue, - struct signal_queue_header *sig_hdr, u32 slot, void *data) -{ - int err; - int signal_data_offset = SIG_DATA_OFFSET(&channel->chan_hdr, queue, - sig_hdr, slot); - - err = visorchannel_read(channel, signal_data_offset, - data, sig_hdr->signal_size); - if (err) - return false; - - return true; -} - -static inline bool -sig_write_data(struct visorchannel *channel, u32 queue, - struct signal_queue_header *sig_hdr, u32 slot, void *data) -{ - int err; - int signal_data_offset = SIG_DATA_OFFSET(&channel->chan_hdr, queue, - sig_hdr, slot); - - err = visorchannel_write(channel, signal_data_offset, - data, sig_hdr->signal_size); - if (err) - return false; - - return true; -} - -static bool -signalremove_inner(struct visorchannel *channel, u32 queue, void *msg) -{ - struct signal_queue_header sig_hdr; - - if (!sig_read_header(channel, queue, &sig_hdr)) - return false; - if (sig_hdr.head == sig_hdr.tail) - return false; /* no signals to remove */ - - sig_hdr.tail = (sig_hdr.tail + 1) % sig_hdr.max_slots; - if (!sig_read_data(channel, queue, &sig_hdr, sig_hdr.tail, msg)) - return false; - sig_hdr.num_received++; - - /* For each data field in SIGNAL_QUEUE_HEADER that was modified, - * update host memory. - */ - mb(); /* required for channel synch */ - if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, tail)) - return false; - if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, num_received)) - return false; - return true; -} - -bool -visorchannel_signalremove(struct visorchannel *channel, u32 queue, void *msg) -{ - bool 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); - -bool -visorchannel_signalempty(struct visorchannel *channel, u32 queue) -{ - unsigned long flags = 0; - struct signal_queue_header sig_hdr; - bool rc = false; - - if (channel->needs_lock) - spin_lock_irqsave(&channel->remove_lock, flags); - - if (!sig_read_header(channel, queue, &sig_hdr)) - rc = true; - if (sig_hdr.head == sig_hdr.tail) - rc = true; - if (channel->needs_lock) - spin_unlock_irqrestore(&channel->remove_lock, flags); - - return rc; -} -EXPORT_SYMBOL_GPL(visorchannel_signalempty); - -static bool -signalinsert_inner(struct visorchannel *channel, u32 queue, void *msg) -{ - struct signal_queue_header sig_hdr; - - if (!sig_read_header(channel, queue, &sig_hdr)) - return false; - - sig_hdr.head = (sig_hdr.head + 1) % sig_hdr.max_slots; - if (sig_hdr.head == sig_hdr.tail) { - sig_hdr.num_overflows++; - visorchannel_write(channel, - SIG_QUEUE_OFFSET(&channel->chan_hdr, queue) + - offsetof(struct signal_queue_header, - num_overflows), - &sig_hdr.num_overflows, - sizeof(sig_hdr.num_overflows)); - return false; - } - - if (!sig_write_data(channel, queue, &sig_hdr, sig_hdr.head, msg)) - return false; - - sig_hdr.num_sent++; - - /* For each data field in SIGNAL_QUEUE_HEADER that was modified, - * update host memory. - */ - mb(); /* required for channel synch */ - if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, head)) - return false; - if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, num_sent)) - return false; - - return true; -} - -bool -visorchannel_signalinsert(struct visorchannel *channel, u32 queue, void *msg) -{ - bool 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); - -int -visorchannel_signalqueue_slots_avail(struct visorchannel *channel, u32 queue) -{ - struct signal_queue_header sig_hdr; - u32 slots_avail, slots_used; - u32 head, tail; - - if (!sig_read_header(channel, queue, &sig_hdr)) - return 0; - head = sig_hdr.head; - tail = sig_hdr.tail; - if (head < tail) - head = head + sig_hdr.max_slots; - slots_used = head - tail; - slots_avail = sig_hdr.max_signals - slots_used; - return (int)slots_avail; -} -EXPORT_SYMBOL_GPL(visorchannel_signalqueue_slots_avail); - -int -visorchannel_signalqueue_max_slots(struct visorchannel *channel, u32 queue) -{ - struct signal_queue_header sig_hdr; - - if (!sig_read_header(channel, queue, &sig_hdr)) - return 0; - return (int)sig_hdr.max_signals; -} -EXPORT_SYMBOL_GPL(visorchannel_signalqueue_max_slots); - -static void -sigqueue_debug(struct signal_queue_header *q, int which, struct seq_file *seq) -{ - seq_printf(seq, "Signal Queue #%d\n", which); - seq_printf(seq, " VersionId = %lu\n", (ulong)q->version); - seq_printf(seq, " Type = %lu\n", (ulong)q->chtype); - seq_printf(seq, " oSignalBase = %llu\n", - (long long)q->sig_base_offset); - seq_printf(seq, " SignalSize = %lu\n", (ulong)q->signal_size); - seq_printf(seq, " MaxSignalSlots = %lu\n", - (ulong)q->max_slots); - seq_printf(seq, " MaxSignals = %lu\n", (ulong)q->max_signals); - seq_printf(seq, " FeatureFlags = %-16.16Lx\n", - (long long)q->features); - seq_printf(seq, " NumSignalsSent = %llu\n", - (long long)q->num_sent); - seq_printf(seq, " NumSignalsReceived = %llu\n", - (long long)q->num_received); - seq_printf(seq, " NumOverflows = %llu\n", - (long long)q->num_overflows); - seq_printf(seq, " Head = %lu\n", (ulong)q->head); - seq_printf(seq, " Tail = %lu\n", (ulong)q->tail); -} - -void -visorchannel_debug(struct visorchannel *channel, int num_queues, - struct seq_file *seq, u32 off) -{ - u64 addr = 0; - ulong nbytes = 0, nbytes_region = 0; - struct channel_header hdr; - struct channel_header *phdr = &hdr; - int i = 0; - int errcode = 0; - - if (!channel) - return; - - addr = visorchannel_get_physaddr(channel); - nbytes_region = visorchannel_get_nbytes(channel); - errcode = visorchannel_read(channel, off, - phdr, sizeof(struct channel_header)); - if (errcode < 0) { - seq_printf(seq, - "Read of channel header failed with errcode=%d)\n", - errcode); - if (off == 0) { - phdr = &channel->chan_hdr; - seq_puts(seq, "(following data may be stale)\n"); - } else { - return; - } - } - nbytes = (ulong)(phdr->size); - seq_printf(seq, "--- Begin channel @0x%-16.16Lx for 0x%lx bytes (region=0x%lx bytes) ---\n", - addr + off, nbytes, nbytes_region); - seq_printf(seq, "Type = %pUL\n", &phdr->chtype); - seq_printf(seq, "ZoneGuid = %pUL\n", &phdr->zone_uuid); - seq_printf(seq, "Signature = 0x%-16.16Lx\n", - (long long)phdr->signature); - seq_printf(seq, "LegacyState = %lu\n", (ulong)phdr->legacy_state); - seq_printf(seq, "SrvState = %lu\n", (ulong)phdr->srv_state); - seq_printf(seq, "CliStateBoot = %lu\n", (ulong)phdr->cli_state_boot); - seq_printf(seq, "CliStateOS = %lu\n", (ulong)phdr->cli_state_os); - seq_printf(seq, "HeaderSize = %lu\n", (ulong)phdr->header_size); - seq_printf(seq, "Size = %llu\n", (long long)phdr->size); - seq_printf(seq, "Features = 0x%-16.16llx\n", - (long long)phdr->features); - seq_printf(seq, "PartitionHandle = 0x%-16.16llx\n", - (long long)phdr->partition_handle); - seq_printf(seq, "Handle = 0x%-16.16llx\n", - (long long)phdr->handle); - seq_printf(seq, "VersionId = %lu\n", (ulong)phdr->version_id); - seq_printf(seq, "oChannelSpace = %llu\n", - (long long)phdr->ch_space_offset); - if ((phdr->ch_space_offset == 0) || (errcode < 0)) - ; - else - for (i = 0; i < num_queues; i++) { - struct signal_queue_header q; - - errcode = visorchannel_read(channel, - off + - phdr->ch_space_offset + - (i * sizeof(q)), - &q, sizeof(q)); - if (errcode < 0) { - seq_printf(seq, - "failed to read signal queue #%d from channel @0x%-16.16Lx errcode=%d\n", - i, addr, errcode); - continue; - } - sigqueue_debug(&q, i, seq); - } - seq_printf(seq, "--- End channel @0x%-16.16Lx for 0x%lx bytes ---\n", - addr + off, nbytes); -} -EXPORT_SYMBOL_GPL(visorchannel_debug); diff --git a/drivers/staging/unisys/visorbus/visorchipset.c b/drivers/staging/unisys/visorbus/visorchipset.c deleted file mode 100644 index 630137a..0000000 --- a/drivers/staging/unisys/visorbus/visorchipset.c +++ /dev/null @@ -1,2355 +0,0 @@ -/* visorchipset_main.c - * - * Copyright (C) 2010 - 2015 UNISYS CORPORATION - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - */ - -#include <linux/acpi.h> -#include <linux/cdev.h> -#include <linux/ctype.h> -#include <linux/fs.h> -#include <linux/mm.h> -#include <linux/nls.h> -#include <linux/netdevice.h> -#include <linux/platform_device.h> -#include <linux/uuid.h> -#include <linux/crash_dump.h> -#include <linux/visorbus/channel_guid.h> -#include <linux/visorbus/guestlinuxdebug.h> -#include <linux/visorbus/periodic_work.h> -#include <linux/visorbus/version.h> -#include <linux/visorbus/visorbus.h> - -#include "controlvmchannel.h" -#include "controlvmcompletionstatus.h" -#include "visorbus_private.h" -#include "vmcallinterface.h" - -#define CURRENT_FILE_PC VISOR_CHIPSET_PC_visorchipset_main_c - -#define MAX_NAME_SIZE 128 -#define MAX_IP_SIZE 50 -#define MAXOUTSTANDINGCHANNELCOMMAND 256 -#define POLLJIFFIES_CONTROLVMCHANNEL_FAST 1 -#define POLLJIFFIES_CONTROLVMCHANNEL_SLOW 100 - -#define MAX_CONTROLVM_PAYLOAD_BYTES (1024 * 128) - -#define VISORCHIPSET_MMAP_CONTROLCHANOFFSET 0x00000000 - -#define UNISYS_SPAR_LEAF_ID 0x40000000 - -/* The s-Par leaf ID returns "UnisysSpar64" encoded across ebx, ecx, edx */ -#define UNISYS_SPAR_ID_EBX 0x73696e55 -#define UNISYS_SPAR_ID_ECX 0x70537379 -#define UNISYS_SPAR_ID_EDX 0x34367261 - -/* - * Module parameters - */ -static int visorchipset_major; -static int visorchipset_visorbusregwait = 1; /* default is on */ -static unsigned long controlvm_payload_bytes_buffered; -static u32 dump_vhba_bus; - -static int -visorchipset_open(struct inode *inode, struct file *file) -{ - unsigned int minor_number = iminor(inode); - - if (minor_number) - return -ENODEV; - file->private_data = NULL; - return 0; -} - -static int -visorchipset_release(struct inode *inode, struct file *file) -{ - return 0; -} - -/* 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 -static unsigned long poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_FAST; -/* when we got our last controlvm message */ -static unsigned long most_recent_message_jiffies; -static int visorbusregistered; - -struct parser_context { - unsigned long allocbytes; - unsigned long param_bytes; - u8 *curr; - unsigned long bytes_remaining; - bool byte_stream; - char data[0]; -}; - -static struct delayed_work periodic_controlvm_work; -static DEFINE_SEMAPHORE(notifier_lock); - -static struct cdev file_cdev; -static struct visorchannel **file_controlvm_channel; -static struct controlvm_message_packet g_devicechangestate_packet; - -static LIST_HEAD(bus_info_list); -static LIST_HEAD(dev_info_list); - -static struct visorchannel *controlvm_channel; - -/* Manages the request payload in the controlvm channel */ -struct visor_controlvm_payload_info { - u8 *ptr; /* pointer to base address of payload pool */ - u64 offset; /* offset from beginning of controlvm - * channel to beginning of payload * pool - */ - u32 bytes; /* number of bytes in payload pool */ -}; - -static struct visor_controlvm_payload_info controlvm_payload_info; - -/* The following globals 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. - */ -static struct controlvm_message controlvm_pending_msg; -static bool controlvm_pending_msg_valid; - -/* This identifies a data buffer that has been received via a controlvm messages - * in a remote --> local CONTROLVM_TRANSMIT_FILE conversation. - */ -struct putfile_buffer_entry { - struct list_head next; /* putfile_buffer_entry list */ - struct parser_context *parser_ctx; /* points to input data buffer */ -}; - -/* List of struct putfile_request *, via next_putfile_request member. - * Each entry in this list identifies an outstanding TRANSMIT_FILE - * conversation. - */ -static LIST_HEAD(putfile_request_list); - -/* This describes a buffer and its current state of transfer (e.g., how many - * bytes have already been supplied as putfile data, and how many bytes are - * remaining) for a putfile_request. - */ -struct putfile_active_buffer { - /* a payload from a controlvm message, containing a file data buffer */ - struct parser_context *parser_ctx; - /* points within data area of parser_ctx to next byte of data */ - size_t bytes_remaining; -}; - -#define PUTFILE_REQUEST_SIG 0x0906101302281211 -/* This identifies a single remote --> local CONTROLVM_TRANSMIT_FILE - * conversation. Structs of this type are dynamically linked into - * <Putfile_request_list>. - */ -struct putfile_request { - u64 sig; /* PUTFILE_REQUEST_SIG */ - - /* header from original TransmitFile request */ - struct controlvm_message_header controlvm_header; - - /* link to next struct putfile_request */ - struct list_head next_putfile_request; - - /* head of putfile_buffer_entry list, which describes the data to be - * supplied as putfile data; - * - this list is added to when controlvm messages come in that supply - * file data - * - this list is removed from via the hotplug program that is actually - * consuming these buffers to write as file data - */ - struct list_head input_buffer_list; - spinlock_t req_list_lock; /* lock for input_buffer_list */ - - /* waiters for input_buffer_list to go non-empty */ - wait_queue_head_t input_buffer_wq; - - /* data not yet read within current putfile_buffer_entry */ - struct putfile_active_buffer active_buf; - - /* <0 = failed, 0 = in-progress, >0 = successful; */ - /* note that this must be set with req_list_lock, and if you set <0, */ - /* it is your responsibility to also free up all of the other objects */ - /* in this struct (like input_buffer_list, active_buf.parser_ctx) */ - /* before releasing the lock */ - int completion_status; -}; - -struct parahotplug_request { - struct list_head list; - int id; - unsigned long expiration; - struct controlvm_message msg; -}; - -static LIST_HEAD(parahotplug_request_list); -static DEFINE_SPINLOCK(parahotplug_request_list_lock); /* lock for above */ -static void parahotplug_process_list(void); - -/* Manages the info for a CONTROLVM_DUMP_CAPTURESTATE / - * CONTROLVM_REPORTEVENT. - */ -static struct visorchipset_busdev_notifiers busdev_notifiers; - -static void bus_create_response(struct visor_device *p, int response); -static void bus_destroy_response(struct visor_device *p, int response); -static void device_create_response(struct visor_device *p, int response); -static void device_destroy_response(struct visor_device *p, int response); -static void device_resume_response(struct visor_device *p, int response); - -static void visorchipset_device_pause_response(struct visor_device *p, - int response); - -static struct visorchipset_busdev_responders busdev_responders = { - .bus_create = bus_create_response, - .bus_destroy = bus_destroy_response, - .device_create = device_create_response, - .device_destroy = device_destroy_response, - .device_pause = visorchipset_device_pause_response, - .device_resume = device_resume_response, -}; - -/* info for /dev/visorchipset */ -static dev_t major_dev = -1; /**< indicates major num for device */ - -/* prototypes for attributes */ -static ssize_t toolaction_show(struct device *dev, - struct device_attribute *attr, char *buf); -static ssize_t toolaction_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count); -static DEVICE_ATTR_RW(toolaction); - -static ssize_t boottotool_show(struct device *dev, - struct device_attribute *attr, char *buf); -static ssize_t boottotool_store(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t count); -static DEVICE_ATTR_RW(boottotool); - -static ssize_t error_show(struct device *dev, struct device_attribute *attr, - char *buf); -static ssize_t error_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count); -static DEVICE_ATTR_RW(error); - -static ssize_t textid_show(struct device *dev, struct device_attribute *attr, - char *buf); -static ssize_t textid_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count); -static DEVICE_ATTR_RW(textid); - -static ssize_t remaining_steps_show(struct device *dev, - struct device_attribute *attr, char *buf); -static ssize_t remaining_steps_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count); -static DEVICE_ATTR_RW(remaining_steps); - -static ssize_t devicedisabled_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count); -static DEVICE_ATTR_WO(devicedisabled); - -static ssize_t deviceenabled_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t 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 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 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 -}; - -static void visorchipset_dev_release(struct device *dev) -{ -} - -/* /sys/devices/platform/visorchipset */ -static struct platform_device visorchipset_platform_device = { - .name = "visorchipset", - .id = -1, - .dev.groups = visorchipset_dev_groups, - .dev.release = visorchipset_dev_release, -}; - -/* Function prototypes */ -static void controlvm_respond(struct controlvm_message_header *msg_hdr, - int response); -static void controlvm_respond_chipset_init( - struct controlvm_message_header *msg_hdr, int response, - enum ultra_chipset_feature features); -static void controlvm_respond_physdev_changestate( - struct controlvm_message_header *msg_hdr, int response, - struct spar_segment_state state); - -static void parser_done(struct parser_context *ctx); - -static struct parser_context * -parser_init_byte_stream(u64 addr, u32 bytes, bool local, bool *retry) -{ - int allocbytes = sizeof(struct parser_context) + bytes; - struct parser_context *ctx; - - if (retry) - *retry = false; - - /* - * alloc an 0 extra byte to ensure payload is - * '\0'-terminated - */ - allocbytes++; - if ((controlvm_payload_bytes_buffered + bytes) - > MAX_CONTROLVM_PAYLOAD_BYTES) { - if (retry) - *retry = true; - return NULL; - } - ctx = kzalloc(allocbytes, GFP_KERNEL | __GFP_NORETRY); - if (!ctx) { - if (retry) - *retry = true; - return NULL; - } - - ctx->allocbytes = allocbytes; - ctx->param_bytes = bytes; - ctx->curr = NULL; - ctx->bytes_remaining = 0; - ctx->byte_stream = false; - if (local) { - void *p; - - if (addr > virt_to_phys(high_memory - 1)) - goto err_finish_ctx; - p = __va((unsigned long)(addr)); - memcpy(ctx->data, p, bytes); - } else { - void *mapping = memremap(addr, bytes, MEMREMAP_WB); - - if (!mapping) - goto err_finish_ctx; - memcpy(ctx->data, mapping, bytes); - memunmap(mapping); - } - - ctx->byte_stream = true; - controlvm_payload_bytes_buffered += ctx->param_bytes; - - return ctx; - -err_finish_ctx: - parser_done(ctx); - return NULL; -} - -static uuid_le -parser_id_get(struct parser_context *ctx) -{ - struct spar_controlvm_parameters_header *phdr = NULL; - - if (!ctx) - return NULL_UUID_LE; - phdr = (struct spar_controlvm_parameters_header *)(ctx->data); - return phdr->id; -} - -/** Describes the state from the perspective of which controlvm messages have - * been received for a bus or device. - */ - -enum PARSER_WHICH_STRING { - PARSERSTRING_INITIATOR, - PARSERSTRING_TARGET, - PARSERSTRING_CONNECTION, - PARSERSTRING_NAME, /* TODO: only PARSERSTRING_NAME is used ? */ -}; - -static void -parser_param_start(struct parser_context *ctx, - enum PARSER_WHICH_STRING which_string) -{ - struct spar_controlvm_parameters_header *phdr = NULL; - - if (!ctx) - return; - - phdr = (struct spar_controlvm_parameters_header *)(ctx->data); - switch (which_string) { - case PARSERSTRING_INITIATOR: - ctx->curr = ctx->data + phdr->initiator_offset; - ctx->bytes_remaining = phdr->initiator_length; - break; - case PARSERSTRING_TARGET: - ctx->curr = ctx->data + phdr->target_offset; - ctx->bytes_remaining = phdr->target_length; - break; - case PARSERSTRING_CONNECTION: - ctx->curr = ctx->data + phdr->connection_offset; - ctx->bytes_remaining = phdr->connection_length; - break; - case PARSERSTRING_NAME: - ctx->curr = ctx->data + phdr->name_offset; - ctx->bytes_remaining = phdr->name_length; - break; - default: - break; - } -} - -static void parser_done(struct parser_context *ctx) -{ - if (!ctx) - return; - controlvm_payload_bytes_buffered -= ctx->param_bytes; - kfree(ctx); -} - -static void * -parser_string_get(struct parser_context *ctx) -{ - u8 *pscan; - unsigned long nscan; - int value_length = -1; - void *value = NULL; - int i; - - if (!ctx) - return NULL; - pscan = ctx->curr; - nscan = ctx->bytes_remaining; - if (nscan == 0) - return NULL; - if (!pscan) - return NULL; - for (i = 0, value_length = -1; i < nscan; i++) - if (pscan[i] == '\0') { - value_length = i; - break; - } - if (value_length < 0) /* '\0' was not included in the length */ - value_length = nscan; - value = kmalloc(value_length + 1, GFP_KERNEL | __GFP_NORETRY); - if (!value) - return NULL; - if (value_length > 0) - memcpy(value, pscan, value_length); - ((u8 *)(value))[value_length] = '\0'; - return value; -} - -static ssize_t toolaction_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 tool_action = 0; - - visorchannel_read(controlvm_channel, - offsetof(struct spar_controlvm_channel_protocol, - tool_action), &tool_action, sizeof(u8)); - return scnprintf(buf, PAGE_SIZE, "%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 ret; - - if (kstrtou8(buf, 10, &tool_action)) - return -EINVAL; - - ret = visorchannel_write - (controlvm_channel, - offsetof(struct spar_controlvm_channel_protocol, - tool_action), - &tool_action, sizeof(u8)); - - if (ret) - return ret; - return count; -} - -static ssize_t boottotool_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct efi_spar_indication efi_spar_indication; - - visorchannel_read(controlvm_channel, - offsetof(struct spar_controlvm_channel_protocol, - efi_spar_ind), &efi_spar_indication, - sizeof(struct efi_spar_indication)); - return scnprintf(buf, PAGE_SIZE, "%u\n", - efi_spar_indication.boot_to_tool); -} - -static ssize_t boottotool_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int val, ret; - struct efi_spar_indication efi_spar_indication; - - if (kstrtoint(buf, 10, &val)) - return -EINVAL; - - efi_spar_indication.boot_to_tool = val; - ret = visorchannel_write - (controlvm_channel, - offsetof(struct spar_controlvm_channel_protocol, - efi_spar_ind), &(efi_spar_indication), - sizeof(struct efi_spar_indication)); - - if (ret) - return ret; - return count; -} - -static ssize_t error_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - u32 error = 0; - - visorchannel_read(controlvm_channel, - offsetof(struct spar_controlvm_channel_protocol, - installation_error), - &error, sizeof(u32)); - return scnprintf(buf, PAGE_SIZE, "%i\n", error); -} - -static ssize_t error_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - u32 error; - int ret; - - if (kstrtou32(buf, 10, &error)) - return -EINVAL; - - ret = visorchannel_write - (controlvm_channel, - offsetof(struct spar_controlvm_channel_protocol, - installation_error), - &error, sizeof(u32)); - if (ret) - return ret; - return count; -} - -static ssize_t textid_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - u32 text_id = 0; - - visorchannel_read - (controlvm_channel, - offsetof(struct spar_controlvm_channel_protocol, - installation_text_id), - &text_id, sizeof(u32)); - return scnprintf(buf, PAGE_SIZE, "%i\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 ret; - - if (kstrtou32(buf, 10, &text_id)) - return -EINVAL; - - ret = visorchannel_write - (controlvm_channel, - offsetof(struct spar_controlvm_channel_protocol, - installation_text_id), - &text_id, sizeof(u32)); - if (ret) - return ret; - return count; -} - -static ssize_t remaining_steps_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u16 remaining_steps = 0; - - visorchannel_read(controlvm_channel, - offsetof(struct spar_controlvm_channel_protocol, - installation_remaining_steps), - &remaining_steps, sizeof(u16)); - return scnprintf(buf, PAGE_SIZE, "%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 ret; - - if (kstrtou16(buf, 10, &remaining_steps)) - return -EINVAL; - - ret = visorchannel_write - (controlvm_channel, - offsetof(struct spar_controlvm_channel_protocol, - installation_remaining_steps), - &remaining_steps, sizeof(u16)); - if (ret) - return ret; - return count; -} - -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; - u32 bus_no = id->bus_no; - u32 dev_no = id->dev_no; - - if ((vdev->chipset_bus_no == bus_no) && - (vdev->chipset_dev_no == 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_device *vdev = 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) - vdev = to_visor_device(dev); - return vdev; -} -EXPORT_SYMBOL(visorbus_get_device_by_id); - -void -visorchipset_register_busdev( - struct visorchipset_busdev_notifiers *notifiers, - struct visorchipset_busdev_responders *responders, - struct ultra_vbus_deviceinfo *driver_info) -{ - down(¬ifier_lock); - if (!notifiers) { - memset(&busdev_notifiers, 0, - sizeof(busdev_notifiers)); - visorbusregistered = 0; /* clear flag */ - } else { - busdev_notifiers = *notifiers; - visorbusregistered = 1; /* set flag */ - } - if (responders) - *responders = busdev_responders; - if (driver_info) - bus_device_info_init(driver_info, "chipset", "visorchipset", - VERSION, NULL); - - up(¬ifier_lock); -} -EXPORT_SYMBOL_GPL(visorchipset_register_busdev); - -static void -chipset_init(struct controlvm_message *inmsg) -{ - static int chipset_inited; - enum ultra_chipset_feature features = 0; - int rc = CONTROLVM_RESP_SUCCESS; - - POSTCODE_LINUX_2(CHIPSET_INIT_ENTRY_PC, POSTCODE_SEVERITY_INFO); - if (chipset_inited) { - rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE; - goto out_respond; - } - chipset_inited = 1; - POSTCODE_LINUX_2(CHIPSET_INIT_EXIT_PC, POSTCODE_SEVERITY_INFO); - - /* Set features to indicate we support parahotplug (if Command - * also supports it). - */ - features = - inmsg->cmd.init_chipset. - features & ULTRA_CHIPSET_FEATURE_PARA_HOTPLUG; - - /* Set the "reply" bit so Command knows this is a - * features-aware driver. - */ - features |= ULTRA_CHIPSET_FEATURE_REPLY; - -out_respond: - if (inmsg->hdr.flags.response_expected) - controlvm_respond_chipset_init(&inmsg->hdr, rc, features); -} - -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 void -controlvm_respond(struct controlvm_message_header *msg_hdr, int response) -{ - struct controlvm_message outmsg; - - controlvm_init_response(&outmsg, msg_hdr, response); - if (outmsg.hdr.flags.test_message == 1) - return; - - if (!visorchannel_signalinsert(controlvm_channel, - CONTROLVM_QUEUE_REQUEST, &outmsg)) { - return; - } -} - -static void -controlvm_respond_chipset_init(struct controlvm_message_header *msg_hdr, - int response, - enum ultra_chipset_feature features) -{ - struct controlvm_message outmsg; - - controlvm_init_response(&outmsg, msg_hdr, response); - outmsg.cmd.init_chipset.features = features; - if (!visorchannel_signalinsert(controlvm_channel, - CONTROLVM_QUEUE_REQUEST, &outmsg)) { - return; - } -} - -static void controlvm_respond_physdev_changestate( - struct controlvm_message_header *msg_hdr, int response, - struct spar_segment_state state) -{ - struct controlvm_message outmsg; - - controlvm_init_response(&outmsg, msg_hdr, response); - outmsg.cmd.device_change_state.state = state; - outmsg.cmd.device_change_state.flags.phys_device = 1; - if (!visorchannel_signalinsert(controlvm_channel, - CONTROLVM_QUEUE_REQUEST, &outmsg)) { - return; - } -} - -enum crash_obj_type { - CRASH_DEV, - CRASH_BUS, -}; - -static void -save_crash_message(struct controlvm_message *msg, enum crash_obj_type typ) -{ - u32 local_crash_msg_offset; - u16 local_crash_msg_count; - - if (visorchannel_read(controlvm_channel, - offsetof(struct spar_controlvm_channel_protocol, - saved_crash_message_count), - &local_crash_msg_count, sizeof(u16)) < 0) { - POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC, - POSTCODE_SEVERITY_ERR); - return; - } - - if (local_crash_msg_count != CONTROLVM_CRASHMSG_MAX) { - POSTCODE_LINUX_3(CRASH_DEV_COUNT_FAILURE_PC, - local_crash_msg_count, - POSTCODE_SEVERITY_ERR); - return; - } - - if (visorchannel_read(controlvm_channel, - offsetof(struct spar_controlvm_channel_protocol, - saved_crash_message_offset), - &local_crash_msg_offset, sizeof(u32)) < 0) { - POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC, - POSTCODE_SEVERITY_ERR); - return; - } - - if (typ == CRASH_BUS) { - if (visorchannel_write(controlvm_channel, - local_crash_msg_offset, - msg, - sizeof(struct controlvm_message)) < 0) { - POSTCODE_LINUX_2(SAVE_MSG_BUS_FAILURE_PC, - POSTCODE_SEVERITY_ERR); - return; - } - } else { - local_crash_msg_offset += sizeof(struct controlvm_message); - if (visorchannel_write(controlvm_channel, - local_crash_msg_offset, - msg, - sizeof(struct controlvm_message)) < 0) { - POSTCODE_LINUX_2(SAVE_MSG_DEV_FAILURE_PC, - POSTCODE_SEVERITY_ERR); - return; - } - } -} - -static void -bus_responder(enum controlvm_id cmd_id, - struct controlvm_message_header *pending_msg_hdr, - int response) -{ - if (!pending_msg_hdr) - return; /* no controlvm response needed */ - - if (pending_msg_hdr->id != (u32)cmd_id) - return; - - controlvm_respond(pending_msg_hdr, response); -} - -static void -device_changestate_responder(enum controlvm_id cmd_id, - struct visor_device *p, int response, - struct spar_segment_state response_state) -{ - struct controlvm_message outmsg; - u32 bus_no = p->chipset_bus_no; - u32 dev_no = p->chipset_dev_no; - - if (!p->pending_msg_hdr) - return; /* no controlvm response needed */ - if (p->pending_msg_hdr->id != cmd_id) - return; - - controlvm_init_response(&outmsg, p->pending_msg_hdr, response); - - outmsg.cmd.device_change_state.bus_no = bus_no; - outmsg.cmd.device_change_state.dev_no = dev_no; - outmsg.cmd.device_change_state.state = response_state; - - if (!visorchannel_signalinsert(controlvm_channel, - CONTROLVM_QUEUE_REQUEST, &outmsg)) - return; -} - -static void -device_responder(enum controlvm_id cmd_id, - struct controlvm_message_header *pending_msg_hdr, - int response) -{ - if (!pending_msg_hdr) - return; /* no controlvm response needed */ - - if (pending_msg_hdr->id != (u32)cmd_id) - return; - - controlvm_respond(pending_msg_hdr, response); -} - -static void -bus_epilog(struct visor_device *bus_info, - u32 cmd, struct controlvm_message_header *msg_hdr, - int response, bool need_response) -{ - struct controlvm_message_header *pmsg_hdr = NULL; - - down(¬ifier_lock); - - if (!bus_info) { - /* relying on a valid passed in response code */ - /* be lazy and re-use msg_hdr for this failure, is this ok?? */ - pmsg_hdr = msg_hdr; - goto out_respond_and_unlock; - } - - if (bus_info->pending_msg_hdr) { - /* only non-NULL if dev is still waiting on a response */ - response = -CONTROLVM_RESP_ERROR_MESSAGE_ID_INVALID_FOR_CLIENT; - pmsg_hdr = bus_info->pending_msg_hdr; - goto out_respond_and_unlock; - } - - if (need_response) { - pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL); - if (!pmsg_hdr) { - POSTCODE_LINUX_4(MALLOC_FAILURE_PC, cmd, - bus_info->chipset_bus_no, - POSTCODE_SEVERITY_ERR); - goto out_unlock; - } - - memcpy(pmsg_hdr, msg_hdr, - sizeof(struct controlvm_message_header)); - bus_info->pending_msg_hdr = pmsg_hdr; - } - - if (response == CONTROLVM_RESP_SUCCESS) { - switch (cmd) { - case CONTROLVM_BUS_CREATE: - if (busdev_notifiers.bus_create) { - (*busdev_notifiers.bus_create) (bus_info); - goto out_unlock; - } - break; - case CONTROLVM_BUS_DESTROY: - if (busdev_notifiers.bus_destroy) { - (*busdev_notifiers.bus_destroy) (bus_info); - goto out_unlock; - } - break; - } - } - -out_respond_and_unlock: - bus_responder(cmd, pmsg_hdr, response); - -out_unlock: - up(¬ifier_lock); -} - -static void -device_epilog(struct visor_device *dev_info, - struct spar_segment_state state, u32 cmd, - struct controlvm_message_header *msg_hdr, int response, - bool need_response, bool for_visorbus) -{ - struct visorchipset_busdev_notifiers *notifiers; - struct controlvm_message_header *pmsg_hdr = NULL; - - notifiers = &busdev_notifiers; - - down(¬ifier_lock); - if (!dev_info) { - /* relying on a valid passed in response code */ - /* be lazy and re-use msg_hdr for this failure, is this ok?? */ - pmsg_hdr = msg_hdr; - goto out_respond_and_unlock; - } - - if (dev_info->pending_msg_hdr) { - /* only non-NULL if dev is still waiting on a response */ - response = -CONTROLVM_RESP_ERROR_MESSAGE_ID_INVALID_FOR_CLIENT; - pmsg_hdr = dev_info->pending_msg_hdr; - goto out_respond_and_unlock; - } - - if (need_response) { - pmsg_hdr = kzalloc(sizeof(*pmsg_hdr), GFP_KERNEL); - if (!pmsg_hdr) { - response = -CONTROLVM_RESP_ERROR_KMALLOC_FAILED; - goto out_respond_and_unlock; - } - - memcpy(pmsg_hdr, msg_hdr, - sizeof(struct controlvm_message_header)); - dev_info->pending_msg_hdr = pmsg_hdr; - } - - if (response >= 0) { - switch (cmd) { - case CONTROLVM_DEVICE_CREATE: - if (notifiers->device_create) { - (*notifiers->device_create) (dev_info); - goto out_unlock; - } - break; - case CONTROLVM_DEVICE_CHANGESTATE: - /* ServerReady / ServerRunning / SegmentStateRunning */ - if (state.alive == segment_state_running.alive && - state.operating == - segment_state_running.operating) { - if (notifiers->device_resume) { - (*notifiers->device_resume) (dev_info); - goto out_unlock; - } - } - /* 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 - */ - if (notifiers->device_pause) { - (*notifiers->device_pause) (dev_info); - goto out_unlock; - } - } - break; - case CONTROLVM_DEVICE_DESTROY: - if (notifiers->device_destroy) { - (*notifiers->device_destroy) (dev_info); - goto out_unlock; - } - break; - } - } - -out_respond_and_unlock: - device_responder(cmd, pmsg_hdr, response); - -out_unlock: - up(¬ifier_lock); -} - -static void -bus_create(struct controlvm_message *inmsg) -{ - struct controlvm_message_packet *cmd = &inmsg->cmd; - u32 bus_no = cmd->create_bus.bus_no; - int rc = CONTROLVM_RESP_SUCCESS; - struct visor_device *bus_info; - struct visorchannel *visorchannel; - - bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); - if (bus_info && (bus_info->state.created == 1)) { - POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no, - POSTCODE_SEVERITY_ERR); - rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE; - goto out_bus_epilog; - } - bus_info = kzalloc(sizeof(*bus_info), GFP_KERNEL); - if (!bus_info) { - POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no, - POSTCODE_SEVERITY_ERR); - rc = -CONTROLVM_RESP_ERROR_KMALLOC_FAILED; - goto out_bus_epilog; - } - - INIT_LIST_HEAD(&bus_info->list_all); - bus_info->chipset_bus_no = bus_no; - bus_info->chipset_dev_no = BUS_ROOT_DEVICE; - - POSTCODE_LINUX_3(BUS_CREATE_ENTRY_PC, bus_no, POSTCODE_SEVERITY_INFO); - - visorchannel = visorchannel_create(cmd->create_bus.channel_addr, - cmd->create_bus.channel_bytes, - GFP_KERNEL, - cmd->create_bus.bus_data_type_uuid); - - if (!visorchannel) { - POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no, - POSTCODE_SEVERITY_ERR); - rc = -CONTROLVM_RESP_ERROR_KMALLOC_FAILED; - kfree(bus_info); - bus_info = NULL; - goto out_bus_epilog; - } - bus_info->visorchannel = visorchannel; - if (uuid_le_cmp(cmd->create_bus.bus_inst_uuid, spar_siovm_uuid) == 0) { - dump_vhba_bus = bus_no; - save_crash_message(inmsg, CRASH_BUS); - } - - POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, bus_no, POSTCODE_SEVERITY_INFO); - -out_bus_epilog: - bus_epilog(bus_info, CONTROLVM_BUS_CREATE, &inmsg->hdr, - rc, inmsg->hdr.flags.response_expected == 1); -} - -static void -bus_destroy(struct controlvm_message *inmsg) -{ - struct controlvm_message_packet *cmd = &inmsg->cmd; - u32 bus_no = cmd->destroy_bus.bus_no; - struct visor_device *bus_info; - int rc = CONTROLVM_RESP_SUCCESS; - - bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); - if (!bus_info) - rc = -CONTROLVM_RESP_ERROR_BUS_INVALID; - else if (bus_info->state.created == 0) - rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE; - - bus_epilog(bus_info, CONTROLVM_BUS_DESTROY, &inmsg->hdr, - rc, inmsg->hdr.flags.response_expected == 1); - - /* bus_info is freed as part of the busdevice_release function */ -} - -static void -bus_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 rc = CONTROLVM_RESP_SUCCESS; - - bus_no = cmd->configure_bus.bus_no; - POSTCODE_LINUX_3(BUS_CONFIGURE_ENTRY_PC, bus_no, - POSTCODE_SEVERITY_INFO); - - bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); - if (!bus_info) { - POSTCODE_LINUX_3(BUS_CONFIGURE_FAILURE_PC, bus_no, - POSTCODE_SEVERITY_ERR); - rc = -CONTROLVM_RESP_ERROR_BUS_INVALID; - } else if (bus_info->state.created == 0) { - POSTCODE_LINUX_3(BUS_CONFIGURE_FAILURE_PC, bus_no, - POSTCODE_SEVERITY_ERR); - rc = -CONTROLVM_RESP_ERROR_BUS_INVALID; - } else if (bus_info->pending_msg_hdr) { - POSTCODE_LINUX_3(BUS_CONFIGURE_FAILURE_PC, bus_no, - POSTCODE_SEVERITY_ERR); - rc = -CONTROLVM_RESP_ERROR_MESSAGE_ID_INVALID_FOR_CLIENT; - } else { - visorchannel_set_clientpartition - (bus_info->visorchannel, - cmd->configure_bus.guest_handle); - bus_info->partition_uuid = parser_id_get(parser_ctx); - parser_param_start(parser_ctx, PARSERSTRING_NAME); - bus_info->name = parser_string_get(parser_ctx); - - POSTCODE_LINUX_3(BUS_CONFIGURE_EXIT_PC, bus_no, - POSTCODE_SEVERITY_INFO); - } - bus_epilog(bus_info, CONTROLVM_BUS_CONFIGURE, &inmsg->hdr, - rc, inmsg->hdr.flags.response_expected == 1); -} - -static void -my_device_create(struct controlvm_message *inmsg) -{ - struct controlvm_message_packet *cmd = &inmsg->cmd; - u32 bus_no = cmd->create_device.bus_no; - u32 dev_no = cmd->create_device.dev_no; - struct visor_device *dev_info = NULL; - struct visor_device *bus_info; - struct visorchannel *visorchannel; - int rc = CONTROLVM_RESP_SUCCESS; - - bus_info = visorbus_get_device_by_id(bus_no, BUS_ROOT_DEVICE, NULL); - if (!bus_info) { - POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no, - POSTCODE_SEVERITY_ERR); - rc = -CONTROLVM_RESP_ERROR_BUS_INVALID; - goto out_respond; - } - - if (bus_info->state.created == 0) { - POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no, - POSTCODE_SEVERITY_ERR); - rc = -CONTROLVM_RESP_ERROR_BUS_INVALID; - goto out_respond; - } - - dev_info = visorbus_get_device_by_id(bus_no, dev_no, NULL); - if (dev_info && (dev_info->state.created == 1)) { - POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no, - POSTCODE_SEVERITY_ERR); - rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE; - goto out_respond; - } - - dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL); - if (!dev_info) { - POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no, - POSTCODE_SEVERITY_ERR); - rc = -CONTROLVM_RESP_ERROR_KMALLOC_FAILED; - goto out_respond; - } - - dev_info->chipset_bus_no = bus_no; - dev_info->chipset_dev_no = dev_no; - dev_info->inst = cmd->create_device.dev_inst_uuid; - - /* not sure where the best place to set the 'parent' */ - dev_info->device.parent = &bus_info->device; - - POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, dev_no, bus_no, - POSTCODE_SEVERITY_INFO); - - visorchannel = - visorchannel_create_with_lock(cmd->create_device.channel_addr, - cmd->create_device.channel_bytes, - GFP_KERNEL, - cmd->create_device.data_type_uuid); - - if (!visorchannel) { - POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no, - POSTCODE_SEVERITY_ERR); - rc = -CONTROLVM_RESP_ERROR_KMALLOC_FAILED; - kfree(dev_info); - dev_info = NULL; - goto out_respond; - } - dev_info->visorchannel = visorchannel; - dev_info->channel_type_guid = cmd->create_device.data_type_uuid; - if (uuid_le_cmp(cmd->create_device.data_type_uuid, - spar_vhba_channel_protocol_uuid) == 0) - save_crash_message(inmsg, CRASH_DEV); - - POSTCODE_LINUX_4(DEVICE_CREATE_EXIT_PC, dev_no, bus_no, - POSTCODE_SEVERITY_INFO); -out_respond: - device_epilog(dev_info, segment_state_running, - CONTROLVM_DEVICE_CREATE, &inmsg->hdr, rc, - inmsg->hdr.flags.response_expected == 1, 1); -} - -static void -my_device_changestate(struct controlvm_message *inmsg) -{ - struct controlvm_message_packet *cmd = &inmsg->cmd; - u32 bus_no = cmd->device_change_state.bus_no; - u32 dev_no = cmd->device_change_state.dev_no; - struct spar_segment_state state = cmd->device_change_state.state; - struct visor_device *dev_info; - int rc = CONTROLVM_RESP_SUCCESS; - - dev_info = visorbus_get_device_by_id(bus_no, dev_no, NULL); - if (!dev_info) { - POSTCODE_LINUX_4(DEVICE_CHANGESTATE_FAILURE_PC, dev_no, bus_no, - POSTCODE_SEVERITY_ERR); - rc = -CONTROLVM_RESP_ERROR_DEVICE_INVALID; - } else if (dev_info->state.created == 0) { - POSTCODE_LINUX_4(DEVICE_CHANGESTATE_FAILURE_PC, dev_no, bus_no, - POSTCODE_SEVERITY_ERR); - rc = -CONTROLVM_RESP_ERROR_DEVICE_INVALID; - } - if ((rc >= CONTROLVM_RESP_SUCCESS) && dev_info) - device_epilog(dev_info, state, - CONTROLVM_DEVICE_CHANGESTATE, &inmsg->hdr, rc, - inmsg->hdr.flags.response_expected == 1, 1); -} - -static void -my_device_destroy(struct controlvm_message *inmsg) -{ - struct controlvm_message_packet *cmd = &inmsg->cmd; - u32 bus_no = cmd->destroy_device.bus_no; - u32 dev_no = cmd->destroy_device.dev_no; - struct visor_device *dev_info; - int rc = CONTROLVM_RESP_SUCCESS; - - dev_info = visorbus_get_device_by_id(bus_no, dev_no, NULL); - if (!dev_info) - rc = -CONTROLVM_RESP_ERROR_DEVICE_INVALID; - else if (dev_info->state.created == 0) - rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE; - - if ((rc >= CONTROLVM_RESP_SUCCESS) && dev_info) - device_epilog(dev_info, segment_state_running, - CONTROLVM_DEVICE_DESTROY, &inmsg->hdr, rc, - inmsg->hdr.flags.response_expected == 1, 1); -} - -/* When provided with the physical address of the controlvm channel - * (phys_addr), the offset to the payload area we need to manage - * (offset), and the size of this payload area (bytes), fills in the - * controlvm_payload_info struct. Returns true for success or false - * for failure. - */ -static int -initialize_controlvm_payload_info(u64 phys_addr, u64 offset, u32 bytes, - struct visor_controlvm_payload_info *info) -{ - u8 *payload = NULL; - - if (!info) - return -CONTROLVM_RESP_ERROR_PAYLOAD_INVALID; - - memset(info, 0, sizeof(struct visor_controlvm_payload_info)); - if ((offset == 0) || (bytes == 0)) - return -CONTROLVM_RESP_ERROR_PAYLOAD_INVALID; - - payload = memremap(phys_addr + offset, bytes, MEMREMAP_WB); - if (!payload) - return -CONTROLVM_RESP_ERROR_IOREMAP_FAILED; - - info->offset = offset; - info->bytes = bytes; - info->ptr = payload; - - return CONTROLVM_RESP_SUCCESS; -} - -static void -destroy_controlvm_payload_info(struct visor_controlvm_payload_info *info) -{ - if (info->ptr) { - memunmap(info->ptr); - info->ptr = NULL; - } - memset(info, 0, sizeof(struct visor_controlvm_payload_info)); -} - -static void -initialize_controlvm_payload(void) -{ - u64 phys_addr = visorchannel_get_physaddr(controlvm_channel); - u64 payload_offset = 0; - u32 payload_bytes = 0; - - if (visorchannel_read(controlvm_channel, - offsetof(struct spar_controlvm_channel_protocol, - request_payload_offset), - &payload_offset, sizeof(payload_offset)) < 0) { - POSTCODE_LINUX_2(CONTROLVM_INIT_FAILURE_PC, - POSTCODE_SEVERITY_ERR); - return; - } - if (visorchannel_read(controlvm_channel, - offsetof(struct spar_controlvm_channel_protocol, - request_payload_bytes), - &payload_bytes, sizeof(payload_bytes)) < 0) { - POSTCODE_LINUX_2(CONTROLVM_INIT_FAILURE_PC, - POSTCODE_SEVERITY_ERR); - return; - } - initialize_controlvm_payload_info(phys_addr, - payload_offset, payload_bytes, - &controlvm_payload_info); -} - -/* Send ACTION=online for DEVPATH=/sys/devices/platform/visorchipset. - * Returns CONTROLVM_RESP_xxx code. - */ -static int -visorchipset_chipset_ready(void) -{ - kobject_uevent(&visorchipset_platform_device.dev.kobj, KOBJ_ONLINE); - return CONTROLVM_RESP_SUCCESS; -} - -static int -visorchipset_chipset_selftest(void) -{ - char env_selftest[20]; - char *envp[] = { env_selftest, NULL }; - - sprintf(env_selftest, "SPARSP_SELFTEST=%d", 1); - kobject_uevent_env(&visorchipset_platform_device.dev.kobj, KOBJ_CHANGE, - envp); - return CONTROLVM_RESP_SUCCESS; -} - -/* Send ACTION=offline for DEVPATH=/sys/devices/platform/visorchipset. - * Returns CONTROLVM_RESP_xxx code. - */ -static int -visorchipset_chipset_notready(void) -{ - kobject_uevent(&visorchipset_platform_device.dev.kobj, KOBJ_OFFLINE); - return CONTROLVM_RESP_SUCCESS; -} - -static void -chipset_ready(struct controlvm_message_header *msg_hdr) -{ - int rc = visorchipset_chipset_ready(); - - if (rc != CONTROLVM_RESP_SUCCESS) - rc = -rc; - if (msg_hdr->flags.response_expected) - controlvm_respond(msg_hdr, rc); -} - -static void -chipset_selftest(struct controlvm_message_header *msg_hdr) -{ - int rc = visorchipset_chipset_selftest(); - - if (rc != CONTROLVM_RESP_SUCCESS) - rc = -rc; - if (msg_hdr->flags.response_expected) - controlvm_respond(msg_hdr, rc); -} - -static void -chipset_notready(struct controlvm_message_header *msg_hdr) -{ - int rc = visorchipset_chipset_notready(); - - if (rc != CONTROLVM_RESP_SUCCESS) - rc = -rc; - if (msg_hdr->flags.response_expected) - controlvm_respond(msg_hdr, rc); -} - -/* This is your "one-stop" shop for grabbing the next message from the - * CONTROLVM_QUEUE_EVENT queue in the controlvm channel. - */ -static bool -read_controlvm_event(struct controlvm_message *msg) -{ - if (visorchannel_signalremove(controlvm_channel, - CONTROLVM_QUEUE_EVENT, msg)) { - /* got a message */ - if (msg->hdr.flags.test_message == 1) - return false; - return true; - } - return false; -} - -/* - * The general parahotplug flow works as follows. The visorchipset - * driver 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 - * /proc/visorchipset/parahotplug, which causes parahotplug_proc_write - * to get called, at which point the appropriate CONTROLVM message is - * retrieved from the list and responded to. - */ - -#define PARAHOTPLUG_TIMEOUT_MS 2000 - -/* - * Generate unique int to match an outstanding CONTROLVM message with a - * udev script /proc response - */ -static int -parahotplug_next_id(void) -{ - static atomic_t id = ATOMIC_INIT(0); - - return atomic_inc_return(&id); -} - -/* - * Returns the time (in jiffies) when a CONTROLVM message on the list - * should expire -- PARAHOTPLUG_TIMEOUT_MS in the future - */ -static unsigned long -parahotplug_next_expiration(void) -{ - return jiffies + msecs_to_jiffies(PARAHOTPLUG_TIMEOUT_MS); -} - -/* - * Create a parahotplug_request, which is basically a wrapper for a - * CONTROLVM_MESSAGE that we can stick on a list - */ -static struct parahotplug_request * -parahotplug_request_create(struct controlvm_message *msg) -{ - struct parahotplug_request *req; - - req = kmalloc(sizeof(*req), GFP_KERNEL | __GFP_NORETRY); - if (!req) - return NULL; - - req->id = parahotplug_next_id(); - req->expiration = parahotplug_next_expiration(); - req->msg = *msg; - - return req; -} - -/* - * Free a parahotplug_request. - */ -static void -parahotplug_request_destroy(struct parahotplug_request *req) -{ - kfree(req); -} - -/* - * Cause uevent to run the user level script to do the disable/enable - * specified in (the CONTROLVM message in) the specified - * parahotplug_request - */ -static void -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, "SPAR_PARAHOTPLUG=1"); - sprintf(env_id, "SPAR_PARAHOTPLUG_ID=%d", req->id); - sprintf(env_state, "SPAR_PARAHOTPLUG_STATE=%d", - cmd->device_change_state.state.active); - sprintf(env_bus, "SPAR_PARAHOTPLUG_BUS=%d", - cmd->device_change_state.bus_no); - sprintf(env_dev, "SPAR_PARAHOTPLUG_DEVICE=%d", - cmd->device_change_state.dev_no >> 3); - sprintf(env_func, "SPAR_PARAHOTPLUG_FUNCTION=%d", - cmd->device_change_state.dev_no & 0x7); - - kobject_uevent_env(&visorchipset_platform_device.dev.kobj, KOBJ_CHANGE, - envp); -} - -/* - * Remove any request from the list that's been on there too long and - * respond with an error. - */ -static void -parahotplug_process_list(void) -{ - struct list_head *pos; - struct list_head *tmp; - - spin_lock(¶hotplug_request_list_lock); - - list_for_each_safe(pos, tmp, ¶hotplug_request_list) { - struct parahotplug_request *req = - list_entry(pos, struct parahotplug_request, list); - - if (!time_after_eq(jiffies, req->expiration)) - continue; - - list_del(pos); - if (req->msg.hdr.flags.response_expected) - controlvm_respond_physdev_changestate( - &req->msg.hdr, - CONTROLVM_RESP_ERROR_DEVICE_UDEV_TIMEOUT, - req->msg.cmd.device_change_state.state); - parahotplug_request_destroy(req); - } - - spin_unlock(¶hotplug_request_list_lock); -} - -/* - * Called from the /proc handler, which means the user script has - * finished the enable/disable. Find the matching identifier, and - * respond to the CONTROLVM message with success. - */ -static int -parahotplug_request_complete(int id, u16 active) -{ - struct list_head *pos; - struct list_head *tmp; - - spin_lock(¶hotplug_request_list_lock); - - /* Look for a request matching "id". */ - list_for_each_safe(pos, tmp, ¶hotplug_request_list) { - struct parahotplug_request *req = - list_entry(pos, struct parahotplug_request, list); - if (req->id == id) { - /* Found a match. Remove it from the list and - * respond. - */ - list_del(pos); - spin_unlock(¶hotplug_request_list_lock); - req->msg.cmd.device_change_state.state.active = active; - if (req->msg.hdr.flags.response_expected) - controlvm_respond_physdev_changestate( - &req->msg.hdr, CONTROLVM_RESP_SUCCESS, - req->msg.cmd.device_change_state.state); - parahotplug_request_destroy(req); - return 0; - } - } - - spin_unlock(¶hotplug_request_list_lock); - return -EINVAL; -} - -/* - * Enables or disables a PCI device by kicking off a udev script - */ -static void -parahotplug_process_message(struct controlvm_message *inmsg) -{ - struct parahotplug_request *req; - - req = parahotplug_request_create(inmsg); - - if (!req) - return; - - if (inmsg->cmd.device_change_state.state.active) { - /* For enable messages, just respond with success - * right away. This is a bit of a hack, but there are - * issues with the early enable messages we get (with - * either the udev script not detecting that the device - * is up, or not getting called at all). Fortunately - * the messages that get lost don't matter anyway, as - * devices are automatically enabled at - * initialization. - */ - parahotplug_request_kickoff(req); - controlvm_respond_physdev_changestate - (&inmsg->hdr, - CONTROLVM_RESP_SUCCESS, - inmsg->cmd.device_change_state.state); - parahotplug_request_destroy(req); - } else { - /* For disable messages, add the request to the - * request list before kicking off the udev script. It - * won't get responded to until the script has - * indicated it's done. - */ - spin_lock(¶hotplug_request_list_lock); - list_add_tail(&req->list, ¶hotplug_request_list); - spin_unlock(¶hotplug_request_list_lock); - - parahotplug_request_kickoff(req); - } -} - -/* Process a controlvm message. - * Return result: - * false - this function will return false only in the case where the - * controlvm message was NOT processed, but processing must be - * retried before 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 - * true - processing of the controlvm message completed, - * either successfully or with an error. - */ -static bool -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; - bool local_addr; - struct controlvm_message ackmsg; - - /* create parsing context if necessary */ - local_addr = (inmsg.hdr.flags.test_message == 1); - if (channel_addr == 0) - return true; - 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_addr && parm_bytes) { - bool retry = false; - - parser_ctx = - parser_init_byte_stream(parm_addr, parm_bytes, - local_addr, &retry); - if (!parser_ctx && retry) - return false; - } - - if (!local_addr) { - controlvm_init_response(&ackmsg, &inmsg.hdr, - CONTROLVM_RESP_SUCCESS); - if (controlvm_channel) - visorchannel_signalinsert(controlvm_channel, - CONTROLVM_QUEUE_ACK, - &ackmsg); - } - switch (inmsg.hdr.id) { - case CONTROLVM_CHIPSET_INIT: - chipset_init(&inmsg); - break; - case CONTROLVM_BUS_CREATE: - bus_create(&inmsg); - break; - case CONTROLVM_BUS_DESTROY: - bus_destroy(&inmsg); - break; - case CONTROLVM_BUS_CONFIGURE: - bus_configure(&inmsg, parser_ctx); - break; - case CONTROLVM_DEVICE_CREATE: - my_device_create(&inmsg); - break; - case CONTROLVM_DEVICE_CHANGESTATE: - if (cmd->device_change_state.flags.phys_device) { - parahotplug_process_message(&inmsg); - } else { - /* save the hdr and cmd structures for later use */ - /* when sending back the response to Command */ - my_device_changestate(&inmsg); - g_devicechangestate_packet = inmsg.cmd; - break; - } - break; - case CONTROLVM_DEVICE_DESTROY: - my_device_destroy(&inmsg); - break; - case CONTROLVM_DEVICE_CONFIGURE: - /* no op for now, just send a respond that we passed */ - if (inmsg.hdr.flags.response_expected) - controlvm_respond(&inmsg.hdr, CONTROLVM_RESP_SUCCESS); - break; - case CONTROLVM_CHIPSET_READY: - chipset_ready(&inmsg.hdr); - break; - case CONTROLVM_CHIPSET_SELFTEST: - chipset_selftest(&inmsg.hdr); - break; - case CONTROLVM_CHIPSET_STOP: - chipset_notready(&inmsg.hdr); - break; - default: - if (inmsg.hdr.flags.response_expected) - controlvm_respond - (&inmsg.hdr, - -CONTROLVM_RESP_ERROR_MESSAGE_ID_UNKNOWN); - break; - } - - if (parser_ctx) { - parser_done(parser_ctx); - parser_ctx = NULL; - } - return true; -} - -static inline unsigned int -issue_vmcall_io_controlvm_addr(u64 *control_addr, u32 *control_bytes) -{ - struct vmcall_io_controlvm_addr_params params; - int result = VMCALL_SUCCESS; - u64 physaddr; - - physaddr = virt_to_phys(¶ms); - ISSUE_IO_VMCALL(VMCALL_IO_CONTROLVM_ADDR, physaddr, result); - if (VMCALL_SUCCESSFUL(result)) { - *control_addr = params.address; - *control_bytes = params.channel_bytes; - } - return result; -} - -static u64 controlvm_get_channel_address(void) -{ - u64 addr = 0; - u32 size = 0; - - if (!VMCALL_SUCCESSFUL(issue_vmcall_io_controlvm_addr(&addr, &size))) - return 0; - - return addr; -} - -static void -controlvm_periodic_work(struct work_struct *work) -{ - struct controlvm_message inmsg; - bool got_command = false; - bool handle_command_failed = false; - - /* make sure visorbus server is registered for controlvm callbacks */ - if (visorchipset_visorbusregwait && !visorbusregistered) - goto cleanup; - - while (visorchannel_signalremove(controlvm_channel, - CONTROLVM_QUEUE_RESPONSE, - &inmsg)) - ; - if (!got_command) { - if (controlvm_pending_msg_valid) { - /* we throttled processing of a prior - * msg, so try to process it again - * rather than reading a new one - */ - inmsg = controlvm_pending_msg; - controlvm_pending_msg_valid = false; - got_command = true; - } else { - got_command = read_controlvm_event(&inmsg); - } - } - - handle_command_failed = false; - while (got_command && (!handle_command_failed)) { - most_recent_message_jiffies = jiffies; - if (handle_command(inmsg, - visorchannel_get_physaddr - (controlvm_channel))) - got_command = read_controlvm_event(&inmsg); - else { - /* this is a scenario where throttling - * is required, but probably NOT an - * error...; we stash the current - * controlvm msg so we will attempt to - * reprocess it on our next loop - */ - handle_command_failed = true; - controlvm_pending_msg = inmsg; - controlvm_pending_msg_valid = true; - } - } - - /* parahotplug_worker */ - parahotplug_process_list(); - -cleanup: - - if (time_after(jiffies, - 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 (poll_jiffies != POLLJIFFIES_CONTROLVMCHANNEL_SLOW) - poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_SLOW; - } else { - if (poll_jiffies != POLLJIFFIES_CONTROLVMCHANNEL_FAST) - poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_FAST; - } - - schedule_delayed_work(&periodic_controlvm_work, poll_jiffies); -} - -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; - - /* make sure visorbus is registered for controlvm callbacks */ - if (visorchipset_visorbusregwait && !visorbusregistered) { - poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_SLOW; - schedule_delayed_work(&periodic_controlvm_work, poll_jiffies); - return; - } - - POSTCODE_LINUX_2(CRASH_DEV_ENTRY_PC, POSTCODE_SEVERITY_INFO); - - /* 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(controlvm_channel, - offsetof(struct spar_controlvm_channel_protocol, - saved_crash_message_count), - &local_crash_msg_count, sizeof(u16)) < 0) { - POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC, - POSTCODE_SEVERITY_ERR); - return; - } - - if (local_crash_msg_count != CONTROLVM_CRASHMSG_MAX) { - POSTCODE_LINUX_3(CRASH_DEV_COUNT_FAILURE_PC, - local_crash_msg_count, - POSTCODE_SEVERITY_ERR); - return; - } - - /* get saved crash message offset */ - if (visorchannel_read(controlvm_channel, - offsetof(struct spar_controlvm_channel_protocol, - saved_crash_message_offset), - &local_crash_msg_offset, sizeof(u32)) < 0) { - POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC, - POSTCODE_SEVERITY_ERR); - return; - } - - /* read create device message for storage bus offset */ - if (visorchannel_read(controlvm_channel, - local_crash_msg_offset, - &local_crash_bus_msg, - sizeof(struct controlvm_message)) < 0) { - POSTCODE_LINUX_2(CRASH_DEV_RD_BUS_FAIULRE_PC, - POSTCODE_SEVERITY_ERR); - return; - } - - /* read create device message for storage device */ - if (visorchannel_read(controlvm_channel, - local_crash_msg_offset + - sizeof(struct controlvm_message), - &local_crash_dev_msg, - sizeof(struct controlvm_message)) < 0) { - POSTCODE_LINUX_2(CRASH_DEV_RD_DEV_FAIULRE_PC, - POSTCODE_SEVERITY_ERR); - return; - } - - /* reuse IOVM create bus message */ - if (local_crash_bus_msg.cmd.create_bus.channel_addr) { - bus_create(&local_crash_bus_msg); - } else { - POSTCODE_LINUX_2(CRASH_DEV_BUS_NULL_FAILURE_PC, - POSTCODE_SEVERITY_ERR); - return; - } - - /* reuse create device message for storage device */ - if (local_crash_dev_msg.cmd.create_device.channel_addr) { - my_device_create(&local_crash_dev_msg); - } else { - POSTCODE_LINUX_2(CRASH_DEV_DEV_NULL_FAILURE_PC, - POSTCODE_SEVERITY_ERR); - return; - } - POSTCODE_LINUX_2(CRASH_DEV_EXIT_PC, POSTCODE_SEVERITY_INFO); -} - -static void -bus_create_response(struct visor_device *bus_info, int response) -{ - if (response >= 0) - bus_info->state.created = 1; - - bus_responder(CONTROLVM_BUS_CREATE, bus_info->pending_msg_hdr, - response); - - kfree(bus_info->pending_msg_hdr); - bus_info->pending_msg_hdr = NULL; -} - -static void -bus_destroy_response(struct visor_device *bus_info, int response) -{ - bus_responder(CONTROLVM_BUS_DESTROY, bus_info->pending_msg_hdr, - response); - - kfree(bus_info->pending_msg_hdr); - bus_info->pending_msg_hdr = NULL; -} - -static void -device_create_response(struct visor_device *dev_info, int response) -{ - if (response >= 0) - dev_info->state.created = 1; - - device_responder(CONTROLVM_DEVICE_CREATE, dev_info->pending_msg_hdr, - response); - - kfree(dev_info->pending_msg_hdr); - dev_info->pending_msg_hdr = NULL; -} - -static void -device_destroy_response(struct visor_device *dev_info, int response) -{ - device_responder(CONTROLVM_DEVICE_DESTROY, dev_info->pending_msg_hdr, - response); - - kfree(dev_info->pending_msg_hdr); - dev_info->pending_msg_hdr = NULL; -} - -static void -visorchipset_device_pause_response(struct visor_device *dev_info, - int response) -{ - device_changestate_responder(CONTROLVM_DEVICE_CHANGESTATE, - dev_info, response, - segment_state_standby); - - kfree(dev_info->pending_msg_hdr); - dev_info->pending_msg_hdr = NULL; -} - -static void -device_resume_response(struct visor_device *dev_info, int response) -{ - device_changestate_responder(CONTROLVM_DEVICE_CHANGESTATE, - dev_info, response, - segment_state_running); - - kfree(dev_info->pending_msg_hdr); - dev_info->pending_msg_hdr = NULL; -} - -/* 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. - */ -static ssize_t devicedisabled_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, 0); - return count; -} - -/* 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. - */ -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 int -visorchipset_mmap(struct file *file, struct vm_area_struct *vma) -{ - unsigned long physaddr = 0; - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - u64 addr = 0; - - /* sv_enable_dfp(); */ - if (offset & (PAGE_SIZE - 1)) - return -ENXIO; /* need aligned offsets */ - - switch (offset) { - case VISORCHIPSET_MMAP_CONTROLCHANOFFSET: - vma->vm_flags |= VM_IO; - if (!*file_controlvm_channel) - return -ENXIO; - - visorchannel_read - (*file_controlvm_channel, - offsetof(struct spar_controlvm_channel_protocol, - gp_control_channel), - &addr, sizeof(addr)); - if (!addr) - return -ENXIO; - - physaddr = (unsigned long)addr; - if (remap_pfn_range(vma, vma->vm_start, - physaddr >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, - /*pgprot_noncached */ - (vma->vm_page_prot))) { - return -EAGAIN; - } - break; - default: - return -ENXIO; - } - return 0; -} - -static inline s64 issue_vmcall_query_guest_virtual_time_offset(void) -{ - u64 result = VMCALL_SUCCESS; - u64 physaddr = 0; - - ISSUE_IO_VMCALL(VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET, physaddr, - result); - return result; -} - -static inline int issue_vmcall_update_physical_time(u64 adjustment) -{ - int result = VMCALL_SUCCESS; - - ISSUE_IO_VMCALL(VMCALL_UPDATE_PHYSICAL_TIME, adjustment, result); - return result; -} - -static long visorchipset_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - u64 adjustment; - s64 vrtc_offset; - - switch (cmd) { - case VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET: - /* get the physical rtc offset */ - vrtc_offset = issue_vmcall_query_guest_virtual_time_offset(); - if (copy_to_user((void __user *)arg, &vrtc_offset, - sizeof(vrtc_offset))) { - return -EFAULT; - } - return 0; - case VMCALL_UPDATE_PHYSICAL_TIME: - if (copy_from_user(&adjustment, (void __user *)arg, - sizeof(adjustment))) { - return -EFAULT; - } - return issue_vmcall_update_physical_time(adjustment); - default: - return -EFAULT; - } -} - -static const struct file_operations visorchipset_fops = { - .owner = THIS_MODULE, - .open = visorchipset_open, - .read = NULL, - .write = NULL, - .unlocked_ioctl = visorchipset_ioctl, - .release = visorchipset_release, - .mmap = visorchipset_mmap, -}; - -static int -visorchipset_file_init(dev_t major_dev, struct visorchannel **controlvm_channel) -{ - int rc = 0; - - file_controlvm_channel = controlvm_channel; - cdev_init(&file_cdev, &visorchipset_fops); - file_cdev.owner = THIS_MODULE; - if (MAJOR(major_dev) == 0) { - rc = alloc_chrdev_region(&major_dev, 0, 1, "visorchipset"); - /* dynamic major device number registration required */ - if (rc < 0) - return rc; - } else { - /* static major device number registration required */ - rc = register_chrdev_region(major_dev, 1, "visorchipset"); - if (rc < 0) - return rc; - } - rc = cdev_add(&file_cdev, MKDEV(MAJOR(major_dev), 0), 1); - if (rc < 0) { - unregister_chrdev_region(major_dev, 1); - return rc; - } - return 0; -} - -static void -visorchipset_file_cleanup(dev_t major_dev) -{ - if (file_cdev.ops) - cdev_del(&file_cdev); - file_cdev.ops = NULL; - unregister_chrdev_region(major_dev, 1); -} - -static int -visorchipset_init(struct acpi_device *acpi_device) -{ - int err = -ENODEV; - u64 addr; - uuid_le uuid = SPAR_CONTROLVM_CHANNEL_PROTOCOL_UUID; - - addr = controlvm_get_channel_address(); - if (!addr) - goto error; - - memset(&busdev_notifiers, 0, sizeof(busdev_notifiers)); - memset(&controlvm_payload_info, 0, sizeof(controlvm_payload_info)); - - controlvm_channel = visorchannel_create_with_lock(addr, 0, - GFP_KERNEL, uuid); - if (!controlvm_channel) - goto error; - - if (SPAR_CONTROLVM_CHANNEL_OK_CLIENT( - visorchannel_get_header(controlvm_channel))) { - initialize_controlvm_payload(); - } else { - goto error_destroy_channel; - } - - major_dev = MKDEV(visorchipset_major, 0); - err = visorchipset_file_init(major_dev, &controlvm_channel); - if (err < 0) - goto error_destroy_payload; - - /* if booting in a crash kernel */ - if (is_kdump_kernel()) - INIT_DELAYED_WORK(&periodic_controlvm_work, - setup_crash_devices_work_queue); - else - INIT_DELAYED_WORK(&periodic_controlvm_work, - controlvm_periodic_work); - - most_recent_message_jiffies = jiffies; - poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_FAST; - schedule_delayed_work(&periodic_controlvm_work, poll_jiffies); - - visorchipset_platform_device.dev.devt = major_dev; - if (platform_device_register(&visorchipset_platform_device) < 0) { - POSTCODE_LINUX_2(DEVICE_REGISTER_FAILURE_PC, DIAG_SEVERITY_ERR); - err = -ENODEV; - goto error_cancel_work; - } - POSTCODE_LINUX_2(CHIPSET_INIT_SUCCESS_PC, POSTCODE_SEVERITY_INFO); - - err = visorbus_init(); - if (err < 0) - goto error_unregister; - - return 0; - -error_unregister: - platform_device_unregister(&visorchipset_platform_device); - -error_cancel_work: - cancel_delayed_work_sync(&periodic_controlvm_work); - visorchipset_file_cleanup(major_dev); - -error_destroy_payload: - destroy_controlvm_payload_info(&controlvm_payload_info); - -error_destroy_channel: - visorchannel_destroy(controlvm_channel); - -error: - POSTCODE_LINUX_3(CHIPSET_INIT_FAILURE_PC, err, POSTCODE_SEVERITY_ERR); - return err; -} - -static int -visorchipset_exit(struct acpi_device *acpi_device) -{ - POSTCODE_LINUX_2(DRIVER_EXIT_PC, POSTCODE_SEVERITY_INFO); - - visorbus_exit(); - - cancel_delayed_work_sync(&periodic_controlvm_work); - destroy_controlvm_payload_info(&controlvm_payload_info); - - visorchannel_destroy(controlvm_channel); - - visorchipset_file_cleanup(visorchipset_platform_device.dev.devt); - platform_device_unregister(&visorchipset_platform_device); - POSTCODE_LINUX_2(DRIVER_EXIT_PC, POSTCODE_SEVERITY_INFO); - - 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 uint32_t visorutil_spar_detect(void) -{ - unsigned int eax, ebx, ecx, edx; - - if (cpu_has_hypervisor) { - /* check the ID */ - cpuid(UNISYS_SPAR_LEAF_ID, &eax, &ebx, &ecx, &edx); - return (ebx == UNISYS_SPAR_ID_EBX) && - (ecx == UNISYS_SPAR_ID_ECX) && - (edx == UNISYS_SPAR_ID_EDX); - } else { - return 0; - } -} - -static int 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_unisys(void) -{ - acpi_bus_unregister_driver(&unisys_acpi_driver); -} - -module_param_named(major, visorchipset_major, int, S_IRUGO); -MODULE_PARM_DESC(visorchipset_major, - "major device number to use for the device node"); -module_param_named(visorbusregwait, visorchipset_visorbusregwait, int, S_IRUGO); -MODULE_PARM_DESC(visorchipset_visorbusregwait, - "1 to have the module wait for the visor bus to register"); - -module_init(init_unisys); -module_exit(exit_unisys); - -MODULE_AUTHOR("Unisys"); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Supervisor chipset driver for service partition: ver " - VERSION); -MODULE_VERSION(VERSION); diff --git a/drivers/staging/unisys/visorbus/vmcallinterface.h b/drivers/staging/unisys/visorbus/vmcallinterface.h deleted file mode 100644 index aac7000..0000000 --- a/drivers/staging/unisys/visorbus/vmcallinterface.h +++ /dev/null @@ -1,91 +0,0 @@ -/* Copyright (C) 2010 - 2015 UNISYS CORPORATION - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - */ - -#ifndef __IOMONINTF_H__ -#define __IOMONINTF_H__ - -/* -* This file contains all structures needed to support the VMCALLs for IO -* Virtualization. The VMCALLs are provided by Monitor and used by IO code -* running on IO Partitions. -*/ - -#include <linux/visorbus/diagchannel.h> - -#include "iovmcall_gnuc.h" - -#ifdef VMCALL_IO_CONTROLVM_ADDR -#undef VMCALL_IO_CONTROLVM_ADDR -#endif /* */ - -/* define subsystem number for AppOS, used in uislib driver */ -#define MDS_APPOS 0x4000000000000000L /* subsystem = 62 - AppOS */ -enum vmcall_monitor_interface_method_tuple { /* VMCALL identification tuples */ - /* Note: when a new VMCALL is added: - * - the 1st 2 hex digits correspond to one of the - * VMCALL_MONITOR_INTERFACE types and - * - the next 2 hex digits are the nth relative instance of within a - * type - * E.G. for VMCALL_VIRTPART_RECYCLE_PART, - * - the 0x02 identifies it as a VMCALL_VIRTPART type and - * - the 0x01 identifies it as the 1st instance of a VMCALL_VIRTPART - * type of VMCALL - */ - /* used by all Guests, not just IO */ - VMCALL_IO_CONTROLVM_ADDR = 0x0501, - /* Allow caller to query virtual time offset */ - VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET = 0x0708, - /* LOGEVENT Post Code (RDX) with specified subsystem mask */ - /* (RCX - monitor_subsystems.h) and severity (RDX) */ - VMCALL_POST_CODE_LOGEVENT = 0x070B, - /* Allow ULTRA_SERVICE_CAPABILITY_TIME capable guest to make VMCALL */ - VMCALL_UPDATE_PHYSICAL_TIME = 0x0a02 -}; - -#define VMCALL_SUCCESS 0 -#define VMCALL_SUCCESSFUL(result) (result == 0) - -#ifdef __GNUC__ -#define unisys_vmcall(tuple, reg_ebx, reg_ecx) \ - __unisys_vmcall_gnuc(tuple, reg_ebx, reg_ecx) -#define unisys_extended_vmcall(tuple, reg_ebx, reg_ecx, reg_edx) \ - __unisys_extended_vmcall_gnuc(tuple, reg_ebx, reg_ecx, reg_edx) -#define ISSUE_IO_VMCALL(method, param, result) \ - (result = unisys_vmcall(method, (param) & 0xFFFFFFFF, \ - (param) >> 32)) -#define ISSUE_IO_EXTENDED_VMCALL(method, param1, param2, param3) \ - unisys_extended_vmcall(method, param1, param2, param3) - - /* The following uses VMCALL_POST_CODE_LOGEVENT interface but is currently - * not used much - */ -#define ISSUE_IO_VMCALL_POSTCODE_SEVERITY(postcode, severity) \ - ISSUE_IO_EXTENDED_VMCALL(VMCALL_POST_CODE_LOGEVENT, severity, \ - MDS_APPOS, postcode) -#endif - -/* Structures for IO VMCALLs */ - -/* Parameters to VMCALL_IO_CONTROLVM_ADDR interface */ -struct vmcall_io_controlvm_addr_params { - /* The Guest-relative physical address of the ControlVm channel. */ - /* This VMCall fills this in with the appropriate address. */ - u64 address; /* contents provided by this VMCALL (OUT) */ - /* the size of the ControlVm channel in bytes This VMCall fills this */ - /* in with the appropriate address. */ - u32 channel_bytes; /* contents provided by this VMCALL (OUT) */ - u8 unused[4]; /* Unused Bytes in the 64-Bit Aligned Struct */ -} __packed; - -#endif /* __IOMONINTF_H__ */ -- 1.9.1 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel