From: Ken Cox Date: Tue, 4 Mar 2014 13:58:07 +0000 (-0600) Subject: staging: visorchipset driver to provide registration and other services X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=12e364b9f08aa335dc7716ce74113e834c993765;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git staging: visorchipset driver to provide registration and other services The visorchipset module receives device creation and destruction events from the Command service partition of s-Par, as well as controlling registration of shared device drivers with the s-Par driver core. The events received are used to populate other s-Par modules with their assigned shared devices. Visorchipset is required for shared device drivers to function properly. Visorchipset also stores information for handling dump disk device creation during kdump. In operation, the visorchipset module processes device creation and destruction messages sent by s-Par's Command service partition through a channel. These messages result in creation (or destruction) of each virtual bus and virtual device. Each bus and device is also associated with a communication channel, which is used to communicate with one or more IO service partitions to perform device IO on behalf of the guest. Signed-off-by: Ken Cox Cc: Ben Romer Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/staging/unisys/Kconfig b/drivers/staging/unisys/Kconfig index 15f16d6b73e3..14e1ea6803f8 100644 --- a/drivers/staging/unisys/Kconfig +++ b/drivers/staging/unisys/Kconfig @@ -11,5 +11,6 @@ if UNISYSSPAR source "drivers/staging/unisys/visorutil/Kconfig" source "drivers/staging/unisys/visorchannel/Kconfig" +source "drivers/staging/unisys/visorchipset/Kconfig" endif # UNISYSSPAR diff --git a/drivers/staging/unisys/Makefile b/drivers/staging/unisys/Makefile index a457d8b49b36..4667f4485d50 100644 --- a/drivers/staging/unisys/Makefile +++ b/drivers/staging/unisys/Makefile @@ -3,4 +3,5 @@ # obj-$(CONFIG_UNISYS_VISORUTIL) += visorutil/ obj-$(CONFIG_UNISYS_VISORCHANNEL) += visorchannel/ +obj-$(CONFIG_UNISYS_VISORCHIPSET) += visorchipset/ diff --git a/drivers/staging/unisys/common-spar/include/channels/channel_guid.h b/drivers/staging/unisys/common-spar/include/channels/channel_guid.h new file mode 100644 index 000000000000..ae0dc2b2ad14 --- /dev/null +++ b/drivers/staging/unisys/common-spar/include/channels/channel_guid.h @@ -0,0 +1,64 @@ +/* Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* + * CHANNEL Guids + */ + +/* Used in IOChannel + * {414815ed-c58c-11da-95a9-00e08161165f} + */ +#define ULTRA_VHBA_CHANNEL_PROTOCOL_GUID \ + { 0x414815ed, 0xc58c, 0x11da, \ + { 0x95, 0xa9, 0x0, 0xe0, 0x81, 0x61, 0x16, 0x5f } } +static const GUID UltraVhbaChannelProtocolGuid = + ULTRA_VHBA_CHANNEL_PROTOCOL_GUID; + +/* Used in IOChannel + * {8cd5994d-c58e-11da-95a9-00e08161165f} + */ +#define ULTRA_VNIC_CHANNEL_PROTOCOL_GUID \ + { 0x8cd5994d, 0xc58e, 0x11da, \ + { 0x95, 0xa9, 0x0, 0xe0, 0x81, 0x61, 0x16, 0x5f } } +static const GUID UltraVnicChannelProtocolGuid = + ULTRA_VNIC_CHANNEL_PROTOCOL_GUID; + +/* Used in IOChannel + * {72120008-4AAB-11DC-8530-444553544200} + */ +#define ULTRA_SIOVM_GUID \ + { 0x72120008, 0x4AAB, 0x11DC, \ + { 0x85, 0x30, 0x44, 0x45, 0x53, 0x54, 0x42, 0x00 } } +static const GUID UltraSIOVMGuid = ULTRA_SIOVM_GUID; + + +/* Used in visornoop/visornoop_main.c + * {5b52c5ac-e5f5-4d42-8dff-429eaecd221f} + */ +#define ULTRA_CONTROLDIRECTOR_CHANNEL_PROTOCOL_GUID \ + { 0x5b52c5ac, 0xe5f5, 0x4d42, \ + { 0x8d, 0xff, 0x42, 0x9e, 0xae, 0xcd, 0x22, 0x1f } } + +static const GUID UltraControlDirectorChannelProtocolGuid = + ULTRA_CONTROLDIRECTOR_CHANNEL_PROTOCOL_GUID; + +/* Used in visorchipset/visorchipset_main.c + * {B4E79625-AEDE-4EAA-9E11-D3EDDCD4504C} + */ +#define ULTRA_DIAG_POOL_CHANNEL_PROTOCOL_GUID \ + {0xb4e79625, 0xaede, 0x4eaa, \ + { 0x9e, 0x11, 0xd3, 0xed, 0xdc, 0xd4, 0x50, 0x4c } } + + diff --git a/drivers/staging/unisys/common-spar/include/channels/controlframework.h b/drivers/staging/unisys/common-spar/include/channels/controlframework.h new file mode 100644 index 000000000000..512643348349 --- /dev/null +++ b/drivers/staging/unisys/common-spar/include/channels/controlframework.h @@ -0,0 +1,77 @@ +/* Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* + * Module Name: + * controlframework.h + * + * Abstract: This file defines common structures in the unmanaged + * Ultravisor (mostly EFI) space. + * + */ + +#ifndef _CONTROL_FRAMEWORK_H_ +#define _CONTROL_FRAMEWORK_H_ + +#include "commontypes.h" +#include "channel.h" + +#define ULTRA_MEMORY_COUNT_Ki 1024 + +/* Scale order 0 is one 32-bit (4-byte) word (in 64 or 128-bit + * architecture potentially 64 or 128-bit word) */ +#define ULTRA_MEMORY_PAGE_WORD 4 + +/* Define Ki scale page to be traditional 4KB page */ +#define ULTRA_MEMORY_PAGE_Ki (ULTRA_MEMORY_PAGE_WORD * ULTRA_MEMORY_COUNT_Ki) +typedef struct _ULTRA_SEGMENT_STATE { + U16 Enabled:1; /* Bit 0: May enter other states */ + U16 Active:1; /* Bit 1: Assigned to active partition */ + U16 Alive:1; /* Bit 2: Configure message sent to + * service/server */ + U16 Revoked:1; /* Bit 3: similar to partition state + * ShuttingDown */ + U16 Allocated:1; /* Bit 4: memory (device/port number) + * has been selected by Command */ + U16 Known:1; /* Bit 5: has been introduced to the + * service/guest partition */ + U16 Ready:1; /* Bit 6: service/Guest partition has + * responded to introduction */ + U16 Operating:1; /* Bit 7: resource is configured and + * operating */ + /* Note: don't use high bit unless we need to switch to ushort + * which is non-compliant */ +} ULTRA_SEGMENT_STATE; +static const ULTRA_SEGMENT_STATE SegmentStateRunning = { + 1, 1, 1, 0, 1, 1, 1, 1 +}; +static const ULTRA_SEGMENT_STATE SegmentStatePaused = { + 1, 1, 1, 0, 1, 1, 1, 0 +}; +static const ULTRA_SEGMENT_STATE SegmentStateStandby = { + 1, 1, 0, 0, 1, 1, 1, 0 +}; +typedef union { + U64 Full; + struct { + U8 Major; /* will be 1 for the first release and + * increment thereafter */ + U8 Minor; + U16 Maintenance; + U32 Revision; /* Subversion revision */ + } Part; +} ULTRA_COMPONENT_VERSION; + +#endif /* _CONTROL_FRAMEWORK_H_ not defined */ diff --git a/drivers/staging/unisys/common-spar/include/channels/controlvmchannel.h b/drivers/staging/unisys/common-spar/include/channels/controlvmchannel.h new file mode 100644 index 000000000000..47f1c4fa1e7e --- /dev/null +++ b/drivers/staging/unisys/common-spar/include/channels/controlvmchannel.h @@ -0,0 +1,619 @@ +/* Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __CONTROLVMCHANNEL_H__ +#define __CONTROLVMCHANNEL_H__ + +#include "commontypes.h" +#include "channel.h" +#include "controlframework.h" +enum { INVALID_GUEST_FIRMWARE, SAMPLE_GUEST_FIRMWARE, + TIANO32_GUEST_FIRMWARE, TIANO64_GUEST_FIRMWARE +}; + +/* {2B3C2D10-7EF5-4ad8-B966-3448B7386B3D} */ +#define ULTRA_CONTROLVM_CHANNEL_PROTOCOL_GUID \ + {0x2b3c2d10, 0x7ef5, 0x4ad8, \ + {0xb9, 0x66, 0x34, 0x48, 0xb7, 0x38, 0x6b, 0x3d} } + +static const GUID UltraControlvmChannelProtocolGuid = + ULTRA_CONTROLVM_CHANNEL_PROTOCOL_GUID; + +#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 ULTRA_CONTROLVM_CHANNEL_OK_CLIENT(pChannel, logCtx) \ + (ULTRA_check_channel_client(pChannel, \ + UltraControlvmChannelProtocolGuid, \ + "controlvm", \ + sizeof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL), \ + ULTRA_CONTROLVM_CHANNEL_PROTOCOL_VERSIONID, \ + ULTRA_CONTROLVM_CHANNEL_PROTOCOL_SIGNATURE, \ + __FILE__, __LINE__, logCtx)) +#define ULTRA_CONTROLVM_CHANNEL_OK_SERVER(actualBytes, logCtx) \ + (ULTRA_check_channel_server(UltraControlvmChannelProtocolGuid, \ + "controlvm", \ + sizeof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL), \ + actualBytes, __FILE__, __LINE__, logCtx)) + +#define MY_DEVICE_INDEX 0 +#define MAX_MACDATA_LEN 8 /* number of bytes for MAC address in config packet */ +#define MAX_SERIAL_NUM 32 + +#define DISK_ZERO_PUN_NUMBER 1 /* Target ID on the SCSI bus for LUN 0 */ +#define DISK_ZERO_LUN_NUMBER 3 /* Logical Unit Number */ + +/* 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 number of messages stored during IOVM creation to be reused + * after crash */ +#define CONTROLVM_CRASHMSG_MAX 2 + +/** 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 + */ +typedef enum { + 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 */ +/* DISK commands required Parameter: BusNumber, DeviceNumber */ + CONTROLVM_DISK_CREATE = 0x221, /* CP --> SP */ + CONTROLVM_DISK_DESTROY = 0x222, /* CP --> SP */ + CONTROLVM_DISK_CONFIGURE = 0x223, /* CP --> SP */ + CONTROLVM_DISK_CHANGESTATE = 0x224, /* CP --> SP */ +/* CHIPSET commands */ + CONTROLVM_CHIPSET_INIT = 0x301, /* CP --> SP, GP */ + CONTROLVM_CHIPSET_STOP = 0x302, /* CP --> SP, GP */ + CONTROLVM_CHIPSET_SHUTDOWN = 0x303, /* CP --> SP */ + CONTROLVM_CHIPSET_READY = 0x304, /* CP --> SP */ + CONTROLVM_CHIPSET_SELFTEST = 0x305, /* CP --> SP */ + +} CONTROLVM_ID; + +struct InterruptInfo { + /**< specifies interrupt info. It is used to send interrupts + * for this channel. The peer at the end of this channel + * who has registered an interrupt (using recv fields + * above) will receive the interrupt. Passed as a parameter + * to Issue_VMCALL_IO_QUEUE_TRANSITION, which generates the + * interrupt. Currently this is used by IOPart-SP to wake + * up GP when Data Channel transitions from empty to + * non-empty.*/ + U64 sendInterruptHandle; + + /**< 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 + * intrrupt. Used by IOPart-GP only. */ + U64 recvInterruptHandle; + + /**< specifies interrupt vector. It, interrupt pin, and shared are + * used to connect to the corresponding interrupt. Used by + * IOPart-GP only. */ + U32 recvInterruptVector; + + /**< 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 recvInterruptShared; + U8 reserved[3]; /* Natural alignment purposes */ +}; + +struct PciId { + U16 Domain; + U8 Bus; + U8 Slot; + U8 Func; + U8 Reserved[3]; /* Natural alignment purposes */ +}; + +struct PciConfigHdr { + U16 VendorId; + U16 SubSysVendor; + U16 DeviceId; + U16 SubSysDevice; + U32 ClassCode; + U32 Reserved; /* Natural alignment purposes */ +}; + +struct ScsiId { + U32 Bus; + U32 Target; + U32 Lun; + U32 Host; /* Command should ignore this for * + * DiskArrival/RemovalEvents */ +}; + +struct WWID { + U32 wwid1; + U32 wwid2; +}; + +struct virtDiskInfo { + U32 switchNo; /* defined by SWITCH_CREATE */ + U32 externalPortNo; /* 0 for SAS RAID provided (external) + * virtual disks, 1 for virtual disk + * images, 2 for gold disk images */ + U16 VirtualDiskIndex; /* Index of disk descriptor in the + * VirtualDisk segment associated with + * externalPortNo */ + U16 Reserved1; + U32 Reserved2; +}; + +typedef enum { + CONTROLVM_ACTION_NONE = 0, + CONTROLVM_ACTION_SET_RESTORE = 0x05E7, + CONTROLVM_ACTION_CLEAR_RESTORE = 0x0C18, + CONTROLVM_ACTION_RESTORING = 0x08E5, + CONTROLVM_ACTION_RESTORE_BUSY = 0x0999, + CONTROLVM_ACTION_CLEAR_NVRAM = 0xB01 +} CONTROLVM_ACTION; + +typedef enum _ULTRA_TOOL_ACTIONS { + /* enumeration that defines intended action */ + ULTRA_TOOL_ACTION_NONE = 0, /* normal boot of boot disk */ + ULTRA_TOOL_ACTION_INSTALL = 1, /* install source disk(s) to boot + * disk */ + ULTRA_TOOL_ACTION_CAPTURE = 2, /* capture boot disk to target disk(s) + * as 'gold image' */ + ULTRA_TOOL_ACTION_REPAIR = 3, /* use source disk(s) to repair + * installation on boot disk */ + ULTRA_TOOL_ACTION_CLEAN = 4, /* 'scrub' virtual disk before + * releasing back to storage pool */ + ULTRA_TOOL_ACTION_UPGRADE = 5, /* upgrade to use content of images + * referenced from newer blueprint */ + ULTRA_TOOL_ACTION_DIAG = 6, /* use tool to invoke diagnostic script + * provided by blueprint */ + ULTRA_TOOL_ACTION_FAILED = 7, /* used when tool fails installation + and cannot continue */ + ULTRA_TOOL_ACTION_COUNT = 8 +} ULTRA_TOOL_ACTIONS; + +typedef struct _ULTRA_EFI_SPAR_INDICATION { + U64 BootToFirmwareUI:1; /* Bit 0: Stop in uefi ui */ + U64 ClearNvram:1; /* Bit 1: Clear NVRAM */ + U64 ClearCmos:1; /* Bit 2: Clear CMOS */ + U64 BootToTool:1; /* Bit 3: Run install tool */ + /* remaining bits are available */ +} ULTRA_EFI_SPAR_INDICATION; + +typedef enum { + ULTRA_CHIPSET_FEATURE_REPLY = 0x00000001, + ULTRA_CHIPSET_FEATURE_PARA_HOTPLUG = 0x00000002, + ULTRA_CHIPSET_FEATURE_PCIVBUS = 0x00000004 +} ULTRA_CHIPSET_FEATURE; + +/** 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. + */ +typedef 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. */ + + U32 MessageSize; /* Includes size of this struct + size + * of message */ + U32 SegmentIndex; /* Index of segment containing Vm + * message/information */ + U32 CompletionStatus; /* Error status code or result of + * message completion */ + struct { + U32 failed:1; /**< =1 in a response to * signify + * failure */ + U32 responseExpected:1; /**< =1 in all messages that expect a + * response (Control ignores this + * bit) */ + U32 server:1; /**< =1 in all bus & device-related + * messages where the message + * receiver is to act as the bus or + * device server */ + U32 testMessage:1; /**< =1 for testing use only + * (Control and Command ignore this + * bit) */ + U32 partialCompletion:1; /**< =1 if there are forthcoming + * responses/acks associated + * with this message */ + U32 preserve:1; /**< =1 this is to let us know to + * preserve channel contents + * (for running guests)*/ + U32 writerInDiag:1; /**< =1 the DiagWriter is active in the + * Diagnostic Partition*/ + + /* remaining bits in this 32-bit word are available */ + } Flags; + U32 Reserved; /* Natural alignment */ + U64 MessageHandle; /* Identifies the particular message instance, + * and is used to match particular */ + /* request instances with the corresponding response instance. */ + U64 PayloadVmOffset; /* Offset of payload area from start of this + * instance of ControlVm segment */ + U32 PayloadMaxBytes; /* Maximum bytes allocated in payload + * area of ControlVm segment */ + U32 PayloadBytes; /* Actual number of bytes of payload + * area to copy between IO/Command; */ + /* if non-zero, there is a payload to copy. */ +} CONTROLVM_MESSAGE_HEADER; + +typedef struct _CONTROLVM_PACKET_DEVICE_CREATE { + U32 busNo; /**< bus # (0..n-1) from the msg receiver's + * perspective */ + + /* Control uses header SegmentIndex field to access bus number... */ + U32 devNo; /**< bus-relative (0..n-1) device number */ + U64 channelAddr; /**< Guest physical address of the channel, which + * can be dereferenced by the receiver + * of this ControlVm command */ + U64 channelBytes; /**< specifies size of the channel in bytes */ + GUID dataTypeGuid;/**< specifies format of data in channel */ + GUID devInstGuid; /**< instance guid for the device */ + struct InterruptInfo intr; /**< specifies interrupt information */ +} CONTROLVM_PACKET_DEVICE_CREATE; /* for CONTROLVM_DEVICE_CREATE */ + +typedef struct _CONTROLVM_PACKET_DEVICE_CONFIGURE { + U32 busNo; /**< bus # (0..n-1) from the msg + * receiver's perspective */ + + /* Control uses header SegmentIndex field to access bus number... */ + U32 devNo; /**< bus-relative (0..n-1) device number */ +} CONTROLVM_PACKET_DEVICE_CONFIGURE; /* for CONTROLVM_DEVICE_CONFIGURE */ + +typedef struct _CONTROLVM_MESSAGE_DEVICE_CREATE { + CONTROLVM_MESSAGE_HEADER Header; + CONTROLVM_PACKET_DEVICE_CREATE Packet; +} CONTROLVM_MESSAGE_DEVICE_CREATE; /* total 128 bytes */ + +typedef struct _CONTROLVM_MESSAGE_DEVICE_CONFIGURE { + CONTROLVM_MESSAGE_HEADER Header; + CONTROLVM_PACKET_DEVICE_CONFIGURE Packet; +} CONTROLVM_MESSAGE_DEVICE_CONFIGURE; /* total 56 bytes */ + +/* This is the format for a message in any ControlVm queue. */ +typedef struct _CONTROLVM_MESSAGE_PACKET { + union { + + /* BEGIN Request messages */ + struct { + U32 busNo; /*< bus # (0..n-1) from the msg + * receiver's perspective */ + + /* Control uses header SegmentIndex field to access bus number... */ + U32 deviceCount; /*< indicates the max number of + * devices on this bus */ + U64 channelAddr; /*< Guest physical address of the + * channel, which can be + * dereferenced by the receiver + * of this ControlVm command */ + U64 channelBytes; /*< size of the channel in bytes */ + GUID busDataTypeGuid;/*< indicates format of data in bus + * channel */ + GUID busInstGuid; /*< instance guid for the bus */ + } createBus; /* for CONTROLVM_BUS_CREATE */ + struct { + U32 busNo; /*< bus # (0..n-1) from the msg + * receiver's perspective */ + + /* Control uses header SegmentIndex field to access bus number... */ + U32 reserved; /* Natural alignment purposes */ + } destroyBus; /* for CONTROLVM_BUS_DESTROY */ + struct { + U32 busNo; /*< bus # (0..n-1) from the + * msg receiver's + * perspective */ + + /* Control uses header SegmentIndex field to access bus number... */ + U32 reserved1; /* for alignment purposes */ + U64 guestHandle; /* This is used to convert + * guest physical address to real + * physical address for DMA, for ex. */ + U64 recvBusInterruptHandle;/*< 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. */ + } configureBus; /* for CONTROLVM_BUS_CONFIGURE */ + + /* for CONTROLVM_DEVICE_CREATE */ + CONTROLVM_PACKET_DEVICE_CREATE createDevice; + struct { + U32 busNo; /*< bus # (0..n-1) from the msg + * receiver's perspective */ + + /* Control uses header SegmentIndex field to access bus number... */ + U32 devNo; /*< bus-relative (0..n-1) device + * number */ + } destroyDevice; /* for CONTROLVM_DEVICE_DESTROY */ + + /* for CONTROLVM_DEVICE_CONFIGURE */ + CONTROLVM_PACKET_DEVICE_CONFIGURE configureDevice; + struct { + U32 busNo; /*< bus # (0..n-1) from the msg + * receiver's perspective */ + + /* Control uses header SegmentIndex field to access bus number... */ + U32 devNo; /*< bus-relative (0..n-1) device + * number */ + } reconfigureDevice; /* for CONTROLVM_DEVICE_RECONFIGURE */ + struct { + U32 busNo; + ULTRA_SEGMENT_STATE state; + U8 reserved[2]; /* Natural alignment purposes */ + } busChangeState; /* for CONTROLVM_BUS_CHANGESTATE */ + struct { + U32 busNo; + U32 devNo; + ULTRA_SEGMENT_STATE state; + struct { + U32 physicalDevice:1; /* =1 if message is for + * a physical device */ + /* remaining bits in this 32-bit word are available */ + } flags; + U8 reserved[2]; /* Natural alignment purposes */ + } deviceChangeState; /* for CONTROLVM_DEVICE_CHANGESTATE */ + struct { + U32 busNo; + U32 devNo; + ULTRA_SEGMENT_STATE state; + U8 reserved[6]; /* Natural alignment purposes */ + } deviceChangeStateEvent; /* for CONTROLVM_DEVICE_CHANGESTATE_EVENT */ + struct { + U32 busCount; /*< indicates the max number of busses */ + U32 switchCount; /*< indicates the max number of + * switches (applicable for service + * partition only) */ + ULTRA_CHIPSET_FEATURE features; + U32 platformNumber; /* Platform Number */ + } initChipset; /* for CONTROLVM_CHIPSET_INIT */ + struct { + U32 Options; /*< reserved */ + U32 Test; /*< bit 0 set to run embedded selftest */ + } chipsetSelftest; /* for CONTROLVM_CHIPSET_SELFTEST */ + + /* END Request messages */ + + /* BEGIN Response messages */ + + /* END Response messages */ + + /* BEGIN Event messages */ + + /* END Event messages */ + + /* BEGIN Ack messages */ + + /* END Ack messages */ + U64 addr; /*< a physical address of something, that + * can be dereferenced by the receiver of + * this ControlVm command (depends on + * command id) */ + U64 handle; /*< a handle of something (depends on + * command id) */ + }; +} CONTROLVM_MESSAGE_PACKET; + +/* All messages in any ControlVm queue have this layout. */ +typedef struct _CONTROLVM_MESSAGE { + CONTROLVM_MESSAGE_HEADER hdr; + CONTROLVM_MESSAGE_PACKET cmd; +} CONTROLVM_MESSAGE; + +typedef struct _DEVICE_MAP { + GUEST_PHYSICAL_ADDRESS DeviceChannelAddress; + U64 DeviceChannelSize; + U32 CA_Index; + U32 Reserved; /* natural alignment */ + U64 Reserved2; /* Align structure on 32-byte boundary */ +} DEVICE_MAP; + +typedef struct _GUEST_DEVICES { + DEVICE_MAP VideoChannel; + DEVICE_MAP KeyboardChannel; + DEVICE_MAP NetworkChannel; + DEVICE_MAP StorageChannel; + DEVICE_MAP ConsoleChannel; + U32 PartitionIndex; + U32 Pad; +} GUEST_DEVICES; + +typedef struct _ULTRA_CONTROLVM_CHANNEL_PROTOCOL { + CHANNEL_HEADER Header; + GUEST_PHYSICAL_ADDRESS gpControlVm; /* guest physical address of + * this channel */ + GUEST_PHYSICAL_ADDRESS gpPartitionTables; /* guest physical address of + * partition tables */ + GUEST_PHYSICAL_ADDRESS gpDiagGuest; /* guest physical address of + * diagnostic channel */ + GUEST_PHYSICAL_ADDRESS gpBootRomDisk; /* guest phys addr of (read + * only) Boot ROM disk */ + GUEST_PHYSICAL_ADDRESS gpBootRamDisk; /* guest phys addr of writable + * Boot RAM disk */ + GUEST_PHYSICAL_ADDRESS gpAcpiTable; /* guest phys addr of acpi + * table */ + GUEST_PHYSICAL_ADDRESS gpControlChannel; /* guest phys addr of control + * channel */ + GUEST_PHYSICAL_ADDRESS gpDiagRomDisk; /* guest phys addr of diagnostic + * ROM disk */ + GUEST_PHYSICAL_ADDRESS gpNvram; /* guest phys addr of NVRAM + * channel */ + U64 RequestPayloadOffset; /* Offset to request payload area */ + U64 EventPayloadOffset; /* Offset to event payload area */ + U32 RequestPayloadBytes; /* Bytes available in request payload + * area */ + U32 EventPayloadBytes; /* Bytes available in event payload area */ + U32 ControlChannelBytes; + U32 NvramChannelBytes; /* Bytes in PartitionNvram segment */ + U32 MessageBytes; /* sizeof(CONTROLVM_MESSAGE) */ + U32 MessageCount; /* CONTROLVM_MESSAGE_MAX */ + GUEST_PHYSICAL_ADDRESS gpSmbiosTable; /* guest phys addr of SMBIOS + * tables */ + GUEST_PHYSICAL_ADDRESS gpPhysicalSmbiosTable; /* guest phys addr of + * SMBIOS table */ + /* ULTRA_MAX_GUESTS_PER_SERVICE */ + GUEST_DEVICES gpObsoleteGuestDevices[16]; + + /* guest physical address of EFI firmware image base */ + GUEST_PHYSICAL_ADDRESS VirtualGuestFirmwareImageBase; + + /* guest physical address of EFI firmware entry point */ + GUEST_PHYSICAL_ADDRESS VirtualGuestFirmwareEntryPoint; + + /* guest EFI firmware image size */ + U64 VirtualGuestFirmwareImageSize; + + /* GPA = 1MB where EFI firmware image is copied to */ + GUEST_PHYSICAL_ADDRESS VirtualGuestFirmwareBootBase; + GUEST_PHYSICAL_ADDRESS VirtualGuestImageBase; + GUEST_PHYSICAL_ADDRESS VirtualGuestImageSize; + U64 PrototypeControlChannelOffset; + GUEST_PHYSICAL_ADDRESS VirtualGuestPartitionHandle; + + U16 RestoreAction; /* Restore Action field to restore the guest + * partition */ + U16 DumpAction; /* For Windows guests it shows if the visordisk + * is running in dump mode */ + U16 NvramFailCount; + U16 SavedCrashMsgCount; /* = CONTROLVM_CRASHMSG_MAX */ + U32 SavedCrashMsgOffset; /* Offset to request payload area needed + * for crash dump */ + U32 InstallationError; /* Type of error encountered during + * installation */ + U32 InstallationTextId; /* Id of string to display */ + U16 InstallationRemainingSteps; /* Number of remaining installation + * steps (for progress bars) */ + U8 ToolAction; /* ULTRA_TOOL_ACTIONS Installation Action + * field */ + U8 Reserved; /* alignment */ + ULTRA_EFI_SPAR_INDICATION EfiSparIndication; + ULTRA_EFI_SPAR_INDICATION EfiSparIndicationSupported; + U32 SPReserved; + U8 Reserved2[28]; /* Force signals to begin on 128-byte cache + * line */ + SIGNAL_QUEUE_HEADER RequestQueue; /* Service or guest partition + * uses this queue to send + * requests to Control */ + SIGNAL_QUEUE_HEADER ResponseQueue; /* Control uses this queue to + * respond to service or guest + * partition requests */ + SIGNAL_QUEUE_HEADER EventQueue; /* Control uses this queue to + * send events to service or + * guest partition */ + SIGNAL_QUEUE_HEADER EventAckQueue; /* Service or guest partition + * uses this queue to ack + * Control events */ + + /* Request fixed-size message pool - does not include payload */ + CONTROLVM_MESSAGE RequestMsg[CONTROLVM_MESSAGE_MAX]; + + /* Response fixed-size message pool - does not include payload */ + CONTROLVM_MESSAGE ResponseMsg[CONTROLVM_MESSAGE_MAX]; + + /* Event fixed-size message pool - does not include payload */ + CONTROLVM_MESSAGE EventMsg[CONTROLVM_MESSAGE_MAX]; + + /* Ack fixed-size message pool - does not include payload */ + CONTROLVM_MESSAGE EventAckMsg[CONTROLVM_MESSAGE_MAX]; + + /* Message stored during IOVM creation to be reused after crash */ + CONTROLVM_MESSAGE SavedCrashMsg[CONTROLVM_CRASHMSG_MAX]; +} ULTRA_CONTROLVM_CHANNEL_PROTOCOL; + +/* Offsets for VM channel attributes... */ +#define VM_CH_REQ_QUEUE_OFFSET \ + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, RequestQueue) +#define VM_CH_RESP_QUEUE_OFFSET \ + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, ResponseQueue) +#define VM_CH_EVENT_QUEUE_OFFSET \ + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, EventQueue) +#define VM_CH_ACK_QUEUE_OFFSET \ + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, EventAckQueue) +#define VM_CH_REQ_MSG_OFFSET \ + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, RequestMsg) +#define VM_CH_RESP_MSG_OFFSET \ + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, ResponseMsg) +#define VM_CH_EVENT_MSG_OFFSET \ + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, EventMsg) +#define VM_CH_ACK_MSG_OFFSET \ + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, EventAckMsg) +#define VM_CH_CRASH_MSG_OFFSET \ + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, SavedCrashMsg) + +/* 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 ConnectionOffset, + * InitiatorOffset, and TargetOffset to get the location of UTF-8 formatted + * strings that can be parsed to obtain command-specific information. The value + * of TotalLength should equal PayloadBytes. The format of the strings at + * PayloadVmOffset will take different forms depending on the message. See the + * following Wiki page for more information: + * https://ustr-linux-1.na.uis.unisys.com/spar/index.php/ControlVm_Parameters_Area + */ +typedef struct _ULTRA_CONTROLVM_PARAMETERS_HEADER { + U32 TotalLength; + U32 HeaderLength; + U32 ConnectionOffset; + U32 ConnectionLength; + U32 InitiatorOffset; + U32 InitiatorLength; + U32 TargetOffset; + U32 TargetLength; + U32 ClientOffset; + U32 ClientLength; + U32 NameOffset; + U32 NameLength; + GUID Id; + U32 Revision; + U32 Reserved; /* Natural alignment */ +} ULTRA_CONTROLVM_PARAMETERS_HEADER; + +#endif /* __CONTROLVMCHANNEL_H__ */ diff --git a/drivers/staging/unisys/common-spar/include/channels/diagchannel.h b/drivers/staging/unisys/common-spar/include/channels/diagchannel.h new file mode 100644 index 000000000000..c93515eb211d --- /dev/null +++ b/drivers/staging/unisys/common-spar/include/channels/diagchannel.h @@ -0,0 +1,427 @@ +/* Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/*++ + * + * Module Name: + * + * diagchannel.h + * + * Abstract: + * + * This file defines the DiagChannel protocol. This protocol is used to aid in + * preserving event data sent by external applications. This protocol provides + * a region for event data to reside in. This data will eventually be sent to + * the Boot Partition where it will be committed to memory and/or disk. This + * file contains platform-independent data that can be built using any + * Supervisor build environment (Windows, Linux, EFI). + * +*/ + +#ifndef _DIAG_CHANNEL_H_ +#define _DIAG_CHANNEL_H_ + +#include "commontypes.h" +#include "channel.h" + +/* {EEA7A573-DB82-447c-8716-EFBEAAAE4858} */ +#define ULTRA_DIAG_CHANNEL_PROTOCOL_GUID \ + {0xeea7a573, 0xdb82, 0x447c, \ + {0x87, 0x16, 0xef, 0xbe, 0xaa, 0xae, 0x48, 0x58} } + +static const GUID UltraDiagChannelProtocolGuid = + ULTRA_DIAG_CHANNEL_PROTOCOL_GUID; + +/* {E850F968-3263-4484-8CA5-2A35D087A5A8} */ +#define ULTRA_DIAG_ROOT_CHANNEL_PROTOCOL_GUID \ + {0xe850f968, 0x3263, 0x4484, \ + {0x8c, 0xa5, 0x2a, 0x35, 0xd0, 0x87, 0xa5, 0xa8} } + +#define ULTRA_DIAG_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 ULTRA_DIAG_CHANNEL_PROTOCOL_VERSIONID 2 + +#define ULTRA_DIAG_CHANNEL_OK_CLIENT(pChannel, logCtx) \ + (ULTRA_check_channel_client(pChannel, \ + UltraDiagChannelProtocolGuid, \ + "diag", \ + sizeof(ULTRA_DIAG_CHANNEL_PROTOCOL), \ + ULTRA_DIAG_CHANNEL_PROTOCOL_VERSIONID, \ + ULTRA_DIAG_CHANNEL_PROTOCOL_SIGNATURE, \ + __FILE__, __LINE__, logCtx)) +#define ULTRA_DIAG_CHANNEL_OK_SERVER(actualBytes, logCtx) \ + (ULTRA_check_channel_server(UltraDiagChannelProtocolGuid, \ + "diag", \ + sizeof(ULTRA_DIAG_CHANNEL_PROTOCOL), \ + actualBytes, __FILE__, __LINE__, logCtx)) +#define MAX_MODULE_NAME_SIZE 128 /* Maximum length of module name... */ +#define MAX_ADDITIONAL_INFO_SIZE 256 /* Maximum length of any additional info + * accompanying event... */ +#define MAX_SUBSYSTEMS 64 /* Maximum number of subsystems allowed in + * DiagChannel... */ +#define LOW_SUBSYSTEMS 32 /* Half of MAX_SUBSYSTEMS to allow 64-bit + * math */ +#define SUBSYSTEM_DEBUG 0 /* Standard subsystem for debug events */ +#define SUBSYSTEM_DEFAULT 1 /* Default subsystem for legacy calls to + * ReportEvent */ + +/* few useful subsystem mask values */ +#define SUBSYSTEM_MASK_DEBUG 0x01 /* Standard subsystem for debug + * events */ +#define SUBSYSTEM_MASK_DEFAULT 0x02 /* Default subsystem for legacy calls to + * ReportEvents */ + +/* Event parameter "Severity" is overloaded with Cause in byte 2 and Severity in + * byte 0, bytes 1 and 3 are reserved */ +#define SEVERITY_MASK 0x0FF /* mask out all but the Severity in byte 0 */ +#define CAUSE_MASK 0x0FF0000 /* mask out all but the cause in byte 2 */ +#define CAUSE_SHIFT_AMT 16 /* shift 2 bytes to place it in byte 2 */ + +/* SubsystemSeverityFilter */ +#define SEVERITY_FILTER_MASK 0x0F /* mask out the Cause half, SeverityFilter is + * in the lower nibble */ +#define CAUSE_FILTER_MASK 0xF0 /* mask out the Severity half, CauseFilter is in + * the upper nibble */ +#define CAUSE_FILTER_SHIFT_AMT 4 /* shift amount to place it in lower or upper + * nibble */ + +/* Copied from EFI's EFI_TIME struct in efidef.h. EFI headers are not allowed +* in some of the Supervisor areas, such as Monitor, so it has been "ported" here +* for use in diagnostic event timestamps... */ +typedef struct _DIAG_EFI_TIME { + U16 Year; /* 1998 - 20XX */ + U8 Month; /* 1 - 12 */ + U8 Day; /* 1 - 31 */ + U8 Hour; /* 0 - 23 */ + U8 Minute; /* 0 - 59 */ + U8 Second; /* 0 - 59 */ + U8 Pad1; + U32 Nanosecond; /* 0 - 999, 999, 999 */ + S16 TimeZone; /* -1440 to 1440 or 2047 */ + U8 Daylight; + U8 Pad2; +} DIAG_EFI_TIME; + +typedef enum { + ULTRA_COMPONENT_GUEST = 0, + ULTRA_COMPONENT_MONITOR = 0x01, + ULTRA_COMPONENT_CCM = 0x02, /* Common Control module */ + /* RESERVED 0x03 - 0x7 */ + + /* Ultravisor Components */ + ULTRA_COMPONENT_BOOT = 0x08, + ULTRA_COMPONENT_IDLE = 0x09, + ULTRA_COMPONENT_CONTROL = 0x0A, + ULTRA_COMPONENT_LOGGER = 0x0B, + ULTRA_COMPONENT_ACPI = 0X0C, + /* RESERVED 0x0D - 0x0F */ + + /* sPAR Components */ + ULTRA_COMPONENT_COMMAND = 0x10, + ULTRA_COMPONENT_IODRIVER = 0x11, + ULTRA_COMPONENT_CONSOLE = 0x12, + ULTRA_COMPONENT_OPERATIONS = 0x13, + ULTRA_COMPONENT_MANAGEMENT = 0x14, + ULTRA_COMPONENT_DIAG = 0x15, + ULTRA_COMPONENT_HWDIAG = 0x16, + ULTRA_COMPONENT_PSERVICES = 0x17, + ULTRA_COMPONENT_PDIAG = 0x18 + /* RESERVED 0x18 - 0x1F */ +} ULTRA_COMPONENT_TYPES; + +/* Structure: DIAG_CHANNEL_EVENT Purpose: Contains attributes that make up an + * event to be written to the DIAG_CHANNEL memory. Attributes: EventId: Id of + * the diagnostic event to write to memory. Severity: Severity of the event + * (Error, Info, etc). ModuleName: Module/file name where event originated. + * LineNumber: Line number in module name where event originated. Timestamp: + * Date/time when event was received by ReportEvent, and written to DiagChannel. + * Reserved: Padding to align structure on a 64-byte cache line boundary. + * AdditionalInfo: Array of characters for additional event info (may be + * empty). */ +typedef struct _DIAG_CHANNEL_EVENT { + U32 EventId; + U32 Severity; + U8 ModuleName[MAX_MODULE_NAME_SIZE]; + U32 LineNumber; + DIAG_EFI_TIME Timestamp; /* Size = 16 bytes */ + U32 PartitionNumber; /* Filled in by Diag Switch as pool blocks are + * filled */ + U16 VirtualProcessorNumber; + U16 LogicalProcessorNumber; + U8 ComponentType; /* ULTRA_COMPONENT_TYPES */ + U8 Subsystem; + U16 Reserved0; /* pad to U64 alignment */ + U32 BlockNumber; /* filled in by DiagSwitch as pool blocks are + * filled */ + U32 BlockNumberHigh; + U32 EventNumber; /* filled in by DiagSwitch as pool blocks are + * filled */ + U32 EventNumberHigh; + + /* The BlockNumber and EventNumber fields are set only by DiagSwitch + * and referenced only by WinDiagDisplay formatting tool as + * additional diagnostic information. Other tools including + * WinDiagDisplay currently ignore these 'Reserved' bytes. */ + U8 Reserved[8]; + U8 AdditionalInfo[MAX_ADDITIONAL_INFO_SIZE]; + + /* NOTE: Changesto DIAG_CHANNEL_EVENT generally need to be reflected in + * existing copies * + * - for AppOS at + * GuestLinux/visordiag_early/supervisor_diagchannel.h * + * - for WinDiagDisplay at + * EFI/Ultra/Tools/WinDiagDisplay/WinDiagDisplay/diagstruct.h */ +} DIAG_CHANNEL_EVENT; + +/* Levels of severity for diagnostic events, in order from lowest severity to +* highest (i.e. fatal errors are the most severe, and should always be logged, +* but info events rarely need to be logged except during debugging). The values +* DIAG_SEVERITY_ENUM_BEGIN and DIAG_SEVERITY_ENUM_END are not valid severity +* values. They exist merely to dilineate the list, so that future additions +* won't require changes to the driver (i.e. when checking for out-of-range +* severities in SetSeverity). The values DIAG_SEVERITY_OVERRIDE and +* DIAG_SEVERITY_SHUTOFF are not valid severity values for logging events but +* they are valid for controlling the amount of event data. This enum is also +* defined in DotNet\sParFramework\ControlFramework\ControlFramework.cs. If a +* change is made to this enum, they should also be reflected in that file. */ +typedef enum { DIAG_SEVERITY_ENUM_BEGIN = 0, + DIAG_SEVERITY_OVERRIDE = DIAG_SEVERITY_ENUM_BEGIN, + DIAG_SEVERITY_VERBOSE = DIAG_SEVERITY_OVERRIDE, /* 0 */ + DIAG_SEVERITY_INFO = DIAG_SEVERITY_VERBOSE + 1, /* 1 */ + DIAG_SEVERITY_WARNING = DIAG_SEVERITY_INFO + 1, /* 2 */ + DIAG_SEVERITY_ERR = DIAG_SEVERITY_WARNING + 1, /* 3 */ + DIAG_SEVERITY_PRINT = DIAG_SEVERITY_ERR + 1, /* 4 */ + DIAG_SEVERITY_SHUTOFF = DIAG_SEVERITY_PRINT + 1, /* 5 */ + DIAG_SEVERITY_ENUM_END = DIAG_SEVERITY_SHUTOFF, /* 5 */ + DIAG_SEVERITY_NONFATAL_ERR = DIAG_SEVERITY_ERR, + DIAG_SEVERITY_FATAL_ERR = DIAG_SEVERITY_PRINT +} DIAG_SEVERITY; + +/* Event Cause enums +* +* Levels of cause for diagnostic events, in order from least to greatest cause +* Internal errors are most urgent since ideally they should never exist +* Invalid requests are preventable by avoiding invalid inputs +* Operations errors depend on environmental factors which may impact which +* requests are possible +* Manifest provides intermediate value to capture firmware and configuration +* version information +* Trace provides suplimental debug information in release firmware +* Unknown Log captures unclasified LogEvent calls. +* Debug is the least urgent since it provides suplimental debug information only +* in debug firmware +* Unknown Debug captures unclassified DebugEvent calls. +* This enum is also defined in +* DotNet\sParFramework\ControlFramework\ControlFramework.cs. +* If a change is made to this enum, they should also be reflected in that +* file. */ + + + +/* A cause value "DIAG_CAUSE_FILE_XFER" together with a severity value of +* "DIAG_SEVERITY_PRINT" (=4), is used for transferring text or binary file to +* the Diag partition. This cause-severity combination will be used by Logger +* DiagSwitch to segregate events into block types. The files are transferred in +* 256 byte chunks maximum, in the AdditionalInfo field of the DIAG_CHANNEL_EVENT +* structure. In the file transfer mode, some event fields will have different +* meaning: EventId specifies the file offset, severity specifies the block type, +* ModuleName specifies the filename, LineNumber specifies the number of valid +* data bytes in an event and AdditionalInfo contains up to 256 bytes of data. */ + +/* The Diag DiagWriter appends event blocks to events.raw as today, and for data + * blocks uses DIAG_CHANNEL_EVENT + * PartitionNumber to extract and append 'AdditionalInfo' to filename (specified + * by ModuleName). */ + +/* The Dell PDiag uses this new mechanism to stash DSET .zip onto the + * 'diagnostic' virtual disk. */ +typedef enum { + DIAG_CAUSE_UNKNOWN = 0, + DIAG_CAUSE_UNKNOWN_DEBUG = DIAG_CAUSE_UNKNOWN + 1, /* 1 */ + DIAG_CAUSE_DEBUG = DIAG_CAUSE_UNKNOWN_DEBUG + 1, /* 2 */ + DIAG_CAUSE_UNKNOWN_LOG = DIAG_CAUSE_DEBUG + 1, /* 3 */ + DIAG_CAUSE_TRACE = DIAG_CAUSE_UNKNOWN_LOG + 1, /* 4 */ + DIAG_CAUSE_MANIFEST = DIAG_CAUSE_TRACE + 1, /* 5 */ + DIAG_CAUSE_OPERATIONS_ERROR = DIAG_CAUSE_MANIFEST + 1, /* 6 */ + DIAG_CAUSE_INVALID_REQUEST = DIAG_CAUSE_OPERATIONS_ERROR + 1, /* 7 */ + DIAG_CAUSE_INTERNAL_ERROR = DIAG_CAUSE_INVALID_REQUEST + 1, /* 8 */ + DIAG_CAUSE_FILE_XFER = DIAG_CAUSE_INTERNAL_ERROR + 1, /* 9 */ + DIAG_CAUSE_ENUM_END = DIAG_CAUSE_FILE_XFER /* 9 */ +} DIAG_CAUSE; + +/* Event Cause category defined into the byte 2 of Severity */ +#define CAUSE_DEBUG (DIAG_CAUSE_DEBUG << CAUSE_SHIFT_AMT) +#define CAUSE_TRACE (DIAG_CAUSE_TRACE << CAUSE_SHIFT_AMT) +#define CAUSE_MANIFEST (DIAG_CAUSE_MANIFEST << CAUSE_SHIFT_AMT) +#define CAUSE_OPERATIONS_ERROR (DIAG_CAUSE_OPERATIONS_ERROR << CAUSE_SHIFT_AMT) +#define CAUSE_INVALID_REQUEST (DIAG_CAUSE_INVALID_REQUEST << CAUSE_SHIFT_AMT) +#define CAUSE_INTERNAL_ERROR (DIAG_CAUSE_INTERNAL_ERROR << CAUSE_SHIFT_AMT) +#define CAUSE_FILE_XFER (DIAG_CAUSE_FILE_XFER << CAUSE_SHIFT_AMT) +#define CAUSE_ENUM_END CAUSE_FILE_XFER + +/* Combine Cause and Severity categories into one */ +#define CAUSE_DEBUG_SEVERITY_VERBOSE \ + (CAUSE_DEBUG | DIAG_SEVERITY_VERBOSE) +#define CAUSE_TRACE_SEVERITY_VERBOSE \ + (CAUSE_TRACE | DIAG_SEVERITY_VERBOSE) +#define CAUSE_MANIFEST_SEVERITY_VERBOSE\ + (CAUSE_MANIFEST | DIAG_SEVERITY_VERBOSE) +#define CAUSE_OPERATIONS_SEVERITY_VERBOSE \ + (CAUSE_OPERATIONS_ERROR | DIAG_SEVERITY_VERBOSE) +#define CAUSE_INVALID_SEVERITY_VERBOSE \ + (CAUSE_INVALID_REQUEST | DIAG_SEVERITY_VERBOSE) +#define CAUSE_INTERNAL_SEVERITY_VERBOSE \ + (CAUSE_INTERNAL_ERROR | DIAG_SEVERITY_VERBOSE) + +#define CAUSE_DEBUG_SEVERITY_INFO \ + (CAUSE_DEBUG | DIAG_SEVERITY_INFO) +#define CAUSE_TRACE_SEVERITY_INFO \ + (CAUSE_TRACE | DIAG_SEVERITY_INFO) +#define CAUSE_MANIFEST_SEVERITY_INFO \ + (CAUSE_MANIFEST | DIAG_SEVERITY_INFO) +#define CAUSE_OPERATIONS_SEVERITY_INFO \ + (CAUSE_OPERATIONS_ERROR | DIAG_SEVERITY_INFO) +#define CAUSE_INVALID_SEVERITY_INFO \ + (CAUSE_INVALID_REQUEST | DIAG_SEVERITY_INFO) +#define CAUSE_INTERNAL_SEVERITY_INFO \ + (CAUSE_INTERNAL_ERROR | DIAG_SEVERITY_INFO) + +#define CAUSE_DEBUG_SEVERITY_WARN \ + (CAUSE_DEBUG | DIAG_SEVERITY_WARNING) +#define CAUSE_TRACE_SEVERITY_WARN \ + (CAUSE_TRACE | DIAG_SEVERITY_WARNING) +#define CAUSE_MANIFEST_SEVERITY_WARN \ + (CAUSE_MANIFEST | DIAG_SEVERITY_WARNING) +#define CAUSE_OPERATIONS_SEVERITY_WARN \ + (CAUSE_OPERATIONS_ERROR | DIAG_SEVERITY_WARNING) +#define CAUSE_INVALID_SEVERITY_WARN \ + (CAUSE_INVALID_REQUEST | DIAG_SEVERITY_WARNING) +#define CAUSE_INTERNAL_SEVERITY_WARN \ + (CAUSE_INTERNAL_ERROR | DIAG_SEVERITY_WARNING) + +#define CAUSE_DEBUG_SEVERITY_ERR \ + (CAUSE_DEBUG | DIAG_SEVERITY_ERR) +#define CAUSE_TRACE_SEVERITY_ERR \ + (CAUSE_TRACE | DIAG_SEVERITY_ERR) +#define CAUSE_MANIFEST_SEVERITY_ERR \ + (CAUSE_MANIFEST | DIAG_SEVERITY_ERR) +#define CAUSE_OPERATIONS_SEVERITY_ERR \ + (CAUSE_OPERATIONS_ERROR | DIAG_SEVERITY_ERR) +#define CAUSE_INVALID_SEVERITY_ERR \ + (CAUSE_INVALID_REQUEST | DIAG_SEVERITY_ERR) +#define CAUSE_INTERNAL_SEVERITY_ERR \ + (CAUSE_INTERNAL_ERROR | DIAG_SEVERITY_ERR) + +#define CAUSE_DEBUG_SEVERITY_PRINT \ + (CAUSE_DEBUG | DIAG_SEVERITY_PRINT) +#define CAUSE_TRACE_SEVERITY_PRINT \ + (CAUSE_TRACE | DIAG_SEVERITY_PRINT) +#define CAUSE_MANIFEST_SEVERITY_PRINT \ + (CAUSE_MANIFEST | DIAG_SEVERITY_PRINT) +#define CAUSE_OPERATIONS_SEVERITY_PRINT \ + (CAUSE_OPERATIONS_ERROR | DIAG_SEVERITY_PRINT) +#define CAUSE_INVALID_SEVERITY_PRINT \ + (CAUSE_INVALID_REQUEST | DIAG_SEVERITY_PRINT) +#define CAUSE_INTERNAL_SEVERITY_PRINT \ + (CAUSE_INTERNAL_ERROR | DIAG_SEVERITY_PRINT) +#define CAUSE_FILE_XFER_SEVERITY_PRINT \ + (CAUSE_FILE_XFER | DIAG_SEVERITY_PRINT) + +/* Structure: DIAG_CHANNEL_PROTOCOL_HEADER + * + * Purpose: Contains attributes that make up the header specific to the + * DIAG_CHANNEL area. + * + * Attributes: + * + * DiagLock: Diag Channel spinlock. + * + *IsChannelInitialized: 1 iff SignalInit was called for this channel; otherwise + * 0, and assume the channel is not ready for use yet. + * + * Reserved: Padding to allign the fields in this structure. + * + *SubsystemSeverityFilter: Level of severity on a subsystem basis that controls + * whether events are logged. Any event's severity for a + * particular subsystem below this level will be discarded. + */ +typedef struct _DIAG_CHANNEL_PROTOCOL_HEADER { + volatile U32 DiagLock; + U8 IsChannelInitialized; + U8 Reserved[3]; + U8 SubsystemSeverityFilter[64]; +} DIAG_CHANNEL_PROTOCOL_HEADER; + +/* The Diagram for the Diagnostic Channel: */ +/* ----------------------- */ +/* | Channel Header | Defined by ULTRA_CHANNEL_PROTOCOL */ +/* ----------------------- */ +/* | Signal Queue Header | Defined by SIGNAL_QUEUE_HEADER */ +/* ----------------------- */ +/* | DiagChannel Header | Defined by DIAG_CHANNEL_PROTOCOL_HEADER */ +/* ----------------------- */ +/* | Channel Event Info | Defined by (DIAG_CHANNEL_EVENT * MAX_EVENTS) */ +/* ----------------------- */ +/* | Reserved | Reserved (pad out to 4MB) */ +/* ----------------------- */ + +/* Offsets/sizes for diagnostic channel attributes... */ +#define DIAG_CH_QUEUE_HEADER_OFFSET (sizeof(ULTRA_CHANNEL_PROTOCOL)) +#define DIAG_CH_QUEUE_HEADER_SIZE (sizeof(SIGNAL_QUEUE_HEADER)) +#define DIAG_CH_PROTOCOL_HEADER_OFFSET \ + (DIAG_CH_QUEUE_HEADER_OFFSET + DIAG_CH_QUEUE_HEADER_SIZE) +#define DIAG_CH_PROTOCOL_HEADER_SIZE (sizeof(DIAG_CHANNEL_PROTOCOL_HEADER)) +#define DIAG_CH_EVENT_OFFSET \ + (DIAG_CH_PROTOCOL_HEADER_OFFSET + DIAG_CH_PROTOCOL_HEADER_SIZE) +#define DIAG_CH_SIZE (4096 * 1024) + +/* For Control and Idle Partitions with larger (8 MB) diagnostic(root) + * channels */ +#define DIAG_CH_LRG_SIZE (2 * DIAG_CH_SIZE) /* 8 MB */ + +/* + * Structure: ULTRA_DIAG_CHANNEL_PROTOCOL + * + * Purpose: Contains attributes that make up the DIAG_CHANNEL memory. + * + * Attributes: + * + * CommonChannelHeader: Header info common to all channels. + * + * QueueHeader: Queue header common to all channels - used to determine where to + * store event. + * + * DiagChannelHeader: Diagnostic channel header info (see + * DIAG_CHANNEL_PROTOCOL_HEADER comments). + * + * Events: Area where diagnostic events (up to MAX_EVENTS) are written. + * + *Reserved: Reserved area to allow for correct channel size padding. +*/ +typedef struct _ULTRA_DIAG_CHANNEL_PROTOCOL { + ULTRA_CHANNEL_PROTOCOL CommonChannelHeader; + SIGNAL_QUEUE_HEADER QueueHeader; + DIAG_CHANNEL_PROTOCOL_HEADER DiagChannelHeader; + DIAG_CHANNEL_EVENT Events[(DIAG_CH_SIZE - DIAG_CH_EVENT_OFFSET) / + sizeof(DIAG_CHANNEL_EVENT)]; +} +ULTRA_DIAG_CHANNEL_PROTOCOL; + +#endif diff --git a/drivers/staging/unisys/common-spar/include/channels/iochannel.h b/drivers/staging/unisys/common-spar/include/channels/iochannel.h new file mode 100644 index 000000000000..94e4b2afd55f --- /dev/null +++ b/drivers/staging/unisys/common-spar/include/channels/iochannel.h @@ -0,0 +1,938 @@ +/* Copyright © 2010 - 2013 UNISYS CORPORATION */ +/* All rights reserved. */ +#ifndef __IOCHANNEL_H__ +#define __IOCHANNEL_H__ + +/* +* Everything needed for IOPart-GuestPart communication is define in +* this file. Note: Everything is OS-independent because this file is +* used by Windows, Linux and possible EFI drivers. */ + + +/* +* Communication flow between the IOPart and GuestPart uses the channel headers +* channel state. The following states are currently being used: +* UNINIT(All Zeroes), CHANNEL_ATTACHING, CHANNEL_ATTACHED, CHANNEL_OPENED +* +* additional states will be used later. No locking is needed to switch between +* states due to the following rules: +* +* 1. IOPart is only the only partition allowed to change from UNIT +* 2. IOPart is only the only partition allowed to change from +* CHANNEL_ATTACHING +* 3. GuestPart is only the only partition allowed to change from +* CHANNEL_ATTACHED +* +* The state changes are the following: IOPart sees the channel is in UNINIT, +* UNINIT -> CHANNEL_ATTACHING (performed only by IOPart) +* CHANNEL_ATTACHING -> CHANNEL_ATTACHED (performed only by IOPart) +* CHANNEL_ATTACHED -> CHANNEL_OPENED (performed only by GuestPart) +*/ + +#include "commontypes.h" +#include "vmcallinterface.h" + +#define _ULTRA_CONTROLVM_CHANNEL_INLINE_ +#include "controlvmchannel.h" +#include "vbuschannel.h" +#undef _ULTRA_CONTROLVM_CHANNEL_INLINE_ +#include "channel.h" + +/* + * CHANNEL Guids + */ + +#include "channel_guid.h" + +#define ULTRA_VHBA_CHANNEL_PROTOCOL_SIGNATURE ULTRA_CHANNEL_PROTOCOL_SIGNATURE +#define ULTRA_VNIC_CHANNEL_PROTOCOL_SIGNATURE ULTRA_CHANNEL_PROTOCOL_SIGNATURE +#define ULTRA_VSWITCH_CHANNEL_PROTOCOL_SIGNATURE \ + ULTRA_CHANNEL_PROTOCOL_SIGNATURE + +/* Must increment these 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_VHBA_CHANNEL_PROTOCOL_VERSIONID 2 +#define ULTRA_VNIC_CHANNEL_PROTOCOL_VERSIONID 2 +#define ULTRA_VSWITCH_CHANNEL_PROTOCOL_VERSIONID 1 + +#define ULTRA_VHBA_CHANNEL_OK_CLIENT(pChannel, logCtx) \ + (ULTRA_check_channel_client(pChannel, UltraVhbaChannelProtocolGuid, \ + "vhba", MIN_IO_CHANNEL_SIZE, \ + ULTRA_VHBA_CHANNEL_PROTOCOL_VERSIONID, \ + ULTRA_VHBA_CHANNEL_PROTOCOL_SIGNATURE, \ + __FILE__, __LINE__, logCtx)) +#define ULTRA_VHBA_CHANNEL_OK_SERVER(actualBytes, logCtx) \ + (ULTRA_check_channel_server(UltraVhbaChannelProtocolGuid, \ + "vhba", MIN_IO_CHANNEL_SIZE, actualBytes, \ + __FILE__, __LINE__, logCtx)) +#define ULTRA_VNIC_CHANNEL_OK_CLIENT(pChannel, logCtx) \ + (ULTRA_check_channel_client(pChannel, UltraVnicChannelProtocolGuid, \ + "vnic", MIN_IO_CHANNEL_SIZE, \ + ULTRA_VNIC_CHANNEL_PROTOCOL_VERSIONID, \ + ULTRA_VNIC_CHANNEL_PROTOCOL_SIGNATURE, \ + __FILE__, __LINE__, logCtx)) +#define ULTRA_VNIC_CHANNEL_OK_SERVER(actualBytes, logCtx) \ + (ULTRA_check_channel_server(UltraVnicChannelProtocolGuid, \ + "vnic", MIN_IO_CHANNEL_SIZE, actualBytes, \ + __FILE__, __LINE__, logCtx)) +#define ULTRA_VSWITCH_CHANNEL_OK_CLIENT(pChannel, logCtx) \ + (ULTRA_check_channel_client(pChannel, UltraVswitchChannelProtocolGuid, \ + "vswitch", MIN_IO_CHANNEL_SIZE, \ + ULTRA_VSWITCH_CHANNEL_PROTOCOL_VERSIONID, \ + ULTRA_VSWITCH_CHANNEL_PROTOCOL_SIGNATURE, \ + __FILE__, __LINE__, logCtx)) +#define ULTRA_VSWITCH_CHANNEL_OK_SERVER(actualBytes, logCtx) \ + (ULTRA_check_channel_server(UltraVswitchChannelProtocolGuid, \ + "vswitch", MIN_IO_CHANNEL_SIZE, \ + actualBytes, \ + __FILE__, __LINE__, logCtx)) +/* +* Everything necessary to handle SCSI & NIC traffic between Guest Partition and +* IO Partition is defined below. */ + + +/* +* Defines and enums. +*/ + +#define MINNUM(a, b) (((a) < (b)) ? (a) : (b)) +#define MAXNUM(a, b) (((a) > (b)) ? (a) : (b)) + +/* these define the two queues per data channel between iopart and + * ioguestparts */ +#define IOCHAN_TO_IOPART 0 /* used by ioguestpart to 'insert' signals to + * iopart */ +#define IOCHAN_FROM_GUESTPART 0 /* used by iopart to 'remove' signals from + * ioguestpart - same queue as previous queue */ + +#define IOCHAN_TO_GUESTPART 1 /* used by iopart to 'insert' signals to + * ioguestpart */ +#define IOCHAN_FROM_IOPART 1 /* used by ioguestpart to 'remove' signals from + * iopart - same queue as previous queue */ + +/* these define the two queues per control channel between controlpart and "its" + * guests, which includes the iopart */ +#define CTRLCHAN_TO_CTRLGUESTPART 0 /* used by ctrlguestpart to 'insert' signals + * to ctrlpart */ +#define CTLRCHAN_FROM_CTRLPART 0 /* used by ctrlpart to 'remove' signals from + * ctrlquestpart - same queue as previous + * queue */ + +#define CTRLCHAN_TO_CTRLPART 1 /* used by ctrlpart to 'insert' signals to + * ctrlguestpart */ +#define CTRLCHAN_FROM_CTRLGUESTPART 1 /* used by ctrguestpart to 'remove' + * signals from ctrlpart - same queue as + * previous queue */ + +/* these define the Event & Ack queues per control channel Events are generated +* by CTRLGUESTPART and sent to CTRLPART; Acks are generated by CTRLPART and sent +* to CTRLGUESTPART. */ +#define CTRLCHAN_EVENT_TO_CTRLPART 2 /* used by ctrlguestpart to 'insert' Events + * to ctrlpart */ +#define CTRLCHAN_EVENT_FROM_CTRLGUESTPART 2 /* used by ctrlpart to 'remove' + * Events from ctrlguestpart */ + +#define CTRLCHAN_ACK_TO_CTRLGUESTPART 3 /* used by ctrlpart to 'insert' Acks to + * ctrlguestpart */ +#define CTRLCHAN_ACK_FROM_CTRLPART 3 /* used by ctrlguestpart to 'remove' Events + * from ctrlpart */ + +/* size of cdb - i.e., scsi cmnd */ +#define MAX_CMND_SIZE 16 +enum dma_data_dir { + DMA_DIR_BIDIR = 0, + DMA_DIR_TO_DEV, + DMA_DIR_FROM_DEV, + DMA_DIR_NONE +}; + +#define MAX_SENSE_SIZE 64 + +#define MAX_PHYS_INFO 64 + +/* Because GuestToGuestCopy is limited to 4KiB segments, and we have limited the +* Emulex Driver to 256 scatter list segments via the lpfc_sg_seg_cnt parameter +* to 256, the maximum I/O size is limited to 256 * 4 KiB = 1 MB */ +#define MAX_IO_SIZE (1024*1024) /* 1 MB */ + +/* NOTE 1: lpfc defines its support for segments in +* #define LPFC_SG_SEG_CNT 64 +* +* NOTE 2: In Linux, frags array in skb is currently allocated to be +* MAX_SKB_FRAGS size, which is 18 which is smaller than MAX_PHYS_INFO for +* now. */ + +#ifndef MAX_SERIAL_NUM +#define MAX_SERIAL_NUM 32 +#endif /* MAX_SERIAL_NUM */ + +#define MAX_SCSI_BUSES 1 +#define MAX_SCSI_TARGETS 8 +#define MAX_SCSI_LUNS 16 +#define MAX_SCSI_FROM_HOST 0xFFFFFFFF /* Indicator to use Physical HBA + * SCSI Host value */ + +/* various types of network packets that can be sent in cmdrsp */ +typedef enum { NET_RCV_POST = 0, /* submit buffer to hold receiving + * incoming packet */ + /* virtnic -> uisnic */ + NET_RCV, /* incoming packet received */ + /* uisnic -> virtpci */ + NET_XMIT, /* for outgoing net packets */ + /* virtnic -> uisnic */ + NET_XMIT_DONE, /* outgoing packet xmitted */ + /* uisnic -> virtpci */ + NET_RCV_ENBDIS, /* enable/disable packet reception */ + /* virtnic -> uisnic */ + NET_RCV_ENBDIS_ACK, /* acknowledge enable/disable packet + * reception */ + /* uisnic -> virtnic */ + NET_RCV_PROMISC, /* enable/disable promiscuous mode */ + /* virtnic -> uisnic */ + NET_CONNECT_STATUS, /* indicate the loss or restoration of a network + * connection */ + /* uisnic -> virtnic */ + NET_MACADDR, /* indicates the client has requested to update + * its MAC addr */ + NET_MACADDR_ACK, /* Mac addres */ + +} NET_TYPES; + +#define ETH_HEADER_SIZE 14 /* size of ethernet header */ + +#define ETH_MIN_DATA_SIZE 46 /* minimum eth data size */ +#define ETH_MIN_PACKET_SIZE (ETH_HEADER_SIZE + ETH_MIN_DATA_SIZE) + +#define ETH_DEF_DATA_SIZE 1500 /* default data size */ +#define ETH_DEF_PACKET_SIZE (ETH_HEADER_SIZE + ETH_DEF_DATA_SIZE) + +#define ETH_MAX_MTU 16384 /* maximum data size */ + +#ifndef MAX_MACADDR_LEN +#define MAX_MACADDR_LEN 6 /* number of bytes in MAC address */ +#endif /* MAX_MACADDR_LEN */ + +#define ETH_IS_LOCALLY_ADMINISTERED(Address) \ + (((U8 *) (Address))[0] & ((U8) 0x02)) +#define NIC_VENDOR_ID 0x0008000B + +/* various types of scsi task mgmt commands */ +typedef enum { TASK_MGMT_ABORT_TASK = + 1, TASK_MGMT_BUS_RESET, TASK_MGMT_LUN_RESET, + TASK_MGMT_TARGET_RESET, +} TASK_MGMT_TYPES; + +/* various types of vdisk mgmt commands */ +typedef enum { VDISK_MGMT_ACQUIRE = 1, VDISK_MGMT_RELEASE, +} VDISK_MGMT_TYPES; + +/* this is used in the vdest field */ +#define VDEST_ALL 0xFFFF + +#define MIN_NUMSIGNALS 64 +#define MAX_NUMSIGNALS 4096 + +/* MAX_NET_RCV_BUF specifies the number of rcv buffers that are created by each +* guest's virtnic and posted to uisnic. Uisnic, for each channel, keeps the rcv +* buffers posted and uses them to receive data on behalf of the guest's virtnic. +* NOTE: the num_rcv_bufs is configurable for each VNIC. So the following is +* simply an upperlimit on what each VNIC can provide. Setting it to half of the +* NUMSIGNALS to prevent queue full deadlocks */ +#define MAX_NET_RCV_BUFS (MIN_NUMSIGNALS / 2) + +/* + * structs with pragma pack */ + + +/* ///////////// BEGIN PRAGMA PACK PUSH 1 ///////////////////////// */ +/* ///////////// ONLY STRUCT TYPE SHOULD BE BELOW */ + +#pragma pack(push, 1) + +struct guest_phys_info { + U64 address; + U64 length; +}; + +#define GPI_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(struct guest_phys_info)) + +struct uisscsi_dest { + U32 channel; /* channel == bus number */ + U32 id; /* id == target number */ + U32 lun; /* lun == logical unit number */ +}; + +struct vhba_wwnn { + U32 wwnn1; + U32 wwnn2; +}; + +/* WARNING: Values stired in this structure must contain maximum counts (not + * maximum values). */ +struct vhba_config_max { /* 20 bytes */ + U32 max_channel; /* maximum channel for devices attached to this + * bus */ + U32 max_id; /* maximum SCSI ID for devices attached to this + * bus */ + U32 max_lun; /* maximum SCSI LUN for devices attached to this + * bus */ + U32 cmd_per_lun; /* maximum number of outstanding commands per + * lun that are allowed at one time */ + U32 max_io_size; /* maximum io size for devices attached to this + * bus */ + /* max io size is often determined by the resource of the hba. e.g */ + /* max scatter gather list length * page size / sector size */ +}; + +struct uiscmdrsp_scsi { + void *scsicmd; /* the handle to the cmd that was received - + * send it back as is in the rsp packet. */ + U8 cmnd[MAX_CMND_SIZE]; /* the cdb for the command */ + U32 bufflen; /* length of data to be transferred out or in */ + U16 guest_phys_entries; /* Number of entries in scatter-gather (sg) + * list */ + struct guest_phys_info gpi_list[MAX_PHYS_INFO]; /* physical address + * information for each + * fragment */ + enum dma_data_dir data_dir; /* direction of the data, if any */ + struct uisscsi_dest vdest; /* identifies the virtual hba, id, + * channel, lun to which cmd was sent */ + + /* the following fields are needed to queue the rsp back to cmd + * originator */ + int linuxstat; /* the original Linux status - for use by linux + * vdisk code */ + U8 scsistat; /* the scsi status */ + U8 addlstat; /* non-scsi status - covers cases like timeout + * needed by windows guests */ +#define ADDL_RESET 1 +#define ADDL_TIMEOUT 2 +#define ADDL_INTERNAL_ERROR 3 +#define ADDL_SEL_TIMEOUT 4 +#define ADDL_CMD_TIMEOUT 5 +#define ADDL_BAD_TARGET 6 +#define ADDL_RETRY 7 + + /* the following fields are need to determine the result of command */ + U8 sensebuf[MAX_SENSE_SIZE]; /* sense info in case cmd failed; */ + /* it holds the sense_data struct; */ + /* see that struct for details. */ + void *vdisk; /* contains pointer to the vdisk so that we can clean up + * when the IO completes. */ + int no_disk_result; /* used to return no disk inquiry result */ + /* when no_disk_result is set to 1, */ + /* scsi.scsistat is SAM_STAT_GOOD */ + /* scsi.addlstat is 0 */ + /* scsi.linuxstat is SAM_STAT_GOOD */ + /* That is, there is NO error. */ +}; + +/* +* Defines to support sending correct inquiry result when no disk is +* configured. */ + +/* From SCSI SPC2 - + * + * If the target is not capable of supporting a device on this logical unit, the + * device server shall set this field to 7Fh (PERIPHERAL QUALIFIER set to 011b + * and PERIPHERAL DEVICE TYPE set to 1Fh). + * + *The device server is capable of supporting the specified peripheral device + *type on this logical unit. However, the physical device is not currently + *connected to this logical unit. + */ + +#define DEV_NOT_PRESENT 0x7f /* old name - compatibility */ +#define DEV_NOT_CAPABLE 0x7f /* peripheral qualifier of 0x3 */ + /* peripheral type of 0x1f */ + /* specifies no device but target present */ + +#define DEV_DISK_CAPABLE_NOT_PRESENT 0x20 /* peripheral qualifier of 0x1 */ + /* peripheral type of 0 - disk */ + /* specifies device capable, but not present */ + +#define DEV_PROC_CAPABLE_NOT_PRESENT 0x23 /* peripheral qualifier of 0x1 */ + /* peripheral type of 3 - processor */ + /* specifies device capable, but not present */ + +#define DEV_HISUPPORT 0x10; /* HiSup = 1; shows support for report luns */ + /* must be returned for lun 0. */ + +/* NOTE: Linux code assumes inquiry contains 36 bytes. Without checking length +* in buf[4] some linux code accesses bytes beyond 5 to retrieve vendor, product +* & revision. Yikes! So let us always send back 36 bytes, the minimum for +* inquiry result. */ +#define NO_DISK_INQUIRY_RESULT_LEN 36 + +#define MIN_INQUIRY_RESULT_LEN 5 /* we need at least 5 bytes minimum for inquiry + * result */ + +/* SCSI device version for no disk inquiry result */ +#define SCSI_SPC2_VER 4 /* indicates SCSI SPC2 (SPC3 is 5) */ + +/* Windows and Linux want different things for a non-existent lun. So, we'll let + * caller pass in the peripheral qualifier and type. + * NOTE:[4] SCSI returns (n-4); so we return length-1-4 or length-5. */ + +#define SET_NO_DISK_INQUIRY_RESULT(buf, len, lun, lun0notpresent, notpresent) \ + do { \ + MEMSET(buf, 0, \ + MINNUM(len, \ + (unsigned int) NO_DISK_INQUIRY_RESULT_LEN)); \ + buf[2] = (U8) SCSI_SPC2_VER; \ + if (lun == 0) { \ + buf[0] = (U8) lun0notpresent; \ + buf[3] = (U8) DEV_HISUPPORT; \ + } else \ + buf[0] = (U8) notpresent; \ + buf[4] = (U8) ( \ + MINNUM(len, \ + (unsigned int) NO_DISK_INQUIRY_RESULT_LEN) - 5); \ + if (len >= NO_DISK_INQUIRY_RESULT_LEN) { \ + buf[8] = 'D'; \ + buf[9] = 'E'; \ + buf[10] = 'L'; \ + buf[11] = 'L'; \ + buf[16] = 'P'; \ + buf[17] = 'S'; \ + buf[18] = 'E'; \ + buf[19] = 'U'; \ + buf[20] = 'D'; \ + buf[21] = 'O'; \ + buf[22] = ' '; \ + buf[23] = 'D'; \ + buf[24] = 'E'; \ + buf[25] = 'V'; \ + buf[26] = 'I'; \ + buf[27] = 'C'; \ + buf[28] = 'E'; \ + buf[30] = ' '; \ + buf[31] = '.'; \ + } \ + } while (0) + + +/* +* Struct & Defines to support sense information. +*/ + + +/* The following struct is returned in sensebuf field in uiscmdrsp_scsi. It is +* initialized in exactly the manner that is recommended in Windows (hence the +* odd values). +* When set, these fields will have the following values: +* ErrorCode = 0x70 indicates current error +* Valid = 1 indicates sense info is valid +* SenseKey contains sense key as defined by SCSI specs. +* AdditionalSenseCode contains sense key as defined by SCSI specs. +* AdditionalSenseCodeQualifier contains qualifier to sense code as defined by +* scsi docs. +* AdditionalSenseLength contains will be sizeof(sense_data)-8=10. +*/ +struct sense_data { + U8 ErrorCode:7; + U8 Valid:1; + U8 SegmentNumber; + U8 SenseKey:4; + U8 Reserved:1; + U8 IncorrectLength:1; + U8 EndOfMedia:1; + U8 FileMark:1; + U8 Information[4]; + U8 AdditionalSenseLength; + U8 CommandSpecificInformation[4]; + U8 AdditionalSenseCode; + U8 AdditionalSenseCodeQualifier; + U8 FieldReplaceableUnitCode; + U8 SenseKeySpecific[3]; +}; + +/* some SCSI ADSENSE codes */ +#ifndef SCSI_ADSENSE_LUN_NOT_READY +#define SCSI_ADSENSE_LUN_NOT_READY 0x04 +#endif /* */ +#ifndef SCSI_ADSENSE_ILLEGAL_COMMAND +#define SCSI_ADSENSE_ILLEGAL_COMMAND 0x20 +#endif /* */ +#ifndef SCSI_ADSENSE_ILLEGAL_BLOCK +#endif /* */ +#ifndef SCSI_ADSENSE_ILLEGAL_BLOCK +#define SCSI_ADSENSE_ILLEGAL_BLOCK 0x21 +#endif /* */ +#ifndef SCSI_ADSENSE_INVALID_CDB +#define SCSI_ADSENSE_INVALID_CDB 0x24 +#endif /* */ +#ifndef SCSI_ADSENSE_INVALID_LUN +#define SCSI_ADSENSE_INVALID_LUN 0x25 +#endif /* */ +#ifndef SCSI_ADWRITE_PROTECT +#define SCSI_ADWRITE_PROTECT 0x27 +#endif /* */ +#ifndef SCSI_ADSENSE_MEDIUM_CHANGED +#define SCSI_ADSENSE_MEDIUM_CHANGED 0x28 +#endif /* */ +#ifndef SCSI_ADSENSE_BUS_RESET +#define SCSI_ADSENSE_BUS_RESET 0x29 +#endif /* */ +#ifndef SCSI_ADSENSE_NO_MEDIA_IN_DEVICE +#define SCSI_ADSENSE_NO_MEDIA_IN_DEVICE 0x3a +#endif /* */ + +struct net_pkt_xmt { + int len; /* full length of data in the packet */ + int num_frags; /* number of fragments in frags containing data */ + struct phys_info frags[MAX_PHYS_INFO]; /* physical page information for + * each fragment */ + char ethhdr[ETH_HEADER_SIZE]; /* the ethernet header */ + struct { + + /* these are needed for csum at uisnic end */ + U8 valid; /* 1 = rest of this struct is valid - else + * ignore */ + U8 hrawoffv; /* 1 = hwrafoff is valid */ + U8 nhrawoffv; /* 1 = nhwrafoff is valid */ + U16 protocol; /* specifies packet protocol */ + U32 csum; /* value used to set skb->csum at IOPart */ + U32 hrawoff; /* value used to set skb->h.raw at IOPart */ + /* hrawoff points to the start of the TRANSPORT LAYER HEADER */ + U32 nhrawoff; /* value used to set skb->nh.raw at IOPart */ + /* nhrawoff points to the start of the NETWORK LAYER HEADER */ + } lincsum; + + /* **** NOTE **** + * The full packet is described in frags but the ethernet header is + * separately kept in ethhdr so that uisnic doesn't have "MAP" the + * guest memory to get to the header. uisnic needs ethhdr to + * determine how to route the packet. + */ +}; + +struct net_pkt_xmtdone { + U32 xmt_done_result; /* result of NET_XMIT */ +#define XMIT_SUCCESS 0 +#define XMIT_FAILED 1 +}; + +/* RCVPOST_BUF_SIZe must be at most page_size(4096) - cache_line_size (64) The +* reason is because dev_skb_alloc which is used to generate RCV_POST skbs in +* virtnic requires that there is "overhead" in the buffer, and pads 16 bytes. I +* prefer to use 1 full cache line size for "overhead" so that transfers are +* better. IOVM requires that a buffer be represented by 1 phys_info structure +* which can only cover page_size. */ +#define RCVPOST_BUF_SIZE 4032 +#define MAX_NET_RCV_CHAIN \ + ((ETH_MAX_MTU+ETH_HEADER_SIZE + RCVPOST_BUF_SIZE-1) / RCVPOST_BUF_SIZE) + +struct net_pkt_rcvpost { + /* rcv buf size must be large enough to include ethernet data len + + * ethernet header len - we are choosing 2K because it is guaranteed + * to be describable */ + struct phys_info frag; /* physical page information for the + * single fragment 2K rcv buf */ + U64 UniqueNum; /* This is used to make sure that + * receive posts are returned to */ + /* the Adapter which sent them origonally. */ +}; + +struct net_pkt_rcv { + + /* the number of receive buffers that can be chained */ + /* is based on max mtu and size of each rcv buf */ + U32 rcv_done_len; /* length of received data */ + U8 numrcvbufs; /* number of receive buffers that contain the */ + /* incoming data; guest end MUST chain these together. */ + void *rcvbuf[MAX_NET_RCV_CHAIN]; /* the list of receive buffers + * that must be chained; */ + /* each entry is a receive buffer provided by NET_RCV_POST. */ + /* NOTE: first rcvbuf in the chain will also be provided in net.buf. */ + U64 UniqueNum; + U32 RcvsDroppedDelta; +}; + +struct net_pkt_enbdis { + void *context; + U16 enable; /* 1 = enable, 0 = disable */ +}; + +struct net_pkt_macaddr { + void *context; + U8 macaddr[MAX_MACADDR_LEN]; /* 6 bytes */ +}; + +/* cmd rsp packet used for VNIC network traffic */ +struct uiscmdrsp_net { + NET_TYPES type; + void *buf; + union { + struct net_pkt_xmt xmt; /* used for NET_XMIT */ + struct net_pkt_xmtdone xmtdone; /* used for NET_XMIT_DONE */ + struct net_pkt_rcvpost rcvpost; /* used for NET_RCV_POST */ + struct net_pkt_rcv rcv; /* used for NET_RCV */ + struct net_pkt_enbdis enbdis; /* used for NET_RCV_ENBDIS, */ + /* NET_RCV_ENBDIS_ACK, */ + /* NET_RCV_PROMSIC, */ + /* and NET_CONNECT_STATUS */ + struct net_pkt_macaddr macaddr; + }; +}; + +struct uiscmdrsp_scsitaskmgmt { + TASK_MGMT_TYPES tasktype; + + /* the type of task */ + struct uisscsi_dest vdest; + + /* the vdisk for which this task mgmt is generated */ + void *scsicmd; + + /* This is some handle that the guest has saved off for its own use. + * Its value is preserved by iopart & returned as is in the task mgmt + * rsp. */ + void *notify; + + /* For linux guests, this is a pointer to wait_queue_head that a + * thread is waiting on to see if the taskmgmt command has completed. + * For windows guests, this is a pointer to a location that a waiting + * thread is testing to see if the taskmgmt command has completed. + * When the rsp is received by guest, the thread receiving the + * response uses this to notify the the thread waiting for taskmgmt + * command completion. Its value is preserved by iopart & returned + * as is in the task mgmt rsp. */ + void *notifyresult; + + /* this is a handle to location in guest where the result of the + * taskmgmt command (result field) is to saved off when the response + * is handled. Its value is preserved by iopart & returned as is in + * the task mgmt rsp. */ + char result; + + /* result of taskmgmt command - set by IOPart - values are: */ +#define TASK_MGMT_FAILED 0 +#define TASK_MGMT_SUCCESS 1 +}; + +/* The following is used by uissd to send disk add/remove notifications to + * Guest */ +/* Note that the vHba pointer is not used by the Client/Guest side. */ +struct uiscmdrsp_disknotify { + U8 add; /* 0-remove, 1-add */ + void *vHba; /* Pointer to vhba_info for channel info to + * route msg */ + U32 channel, id, lun; /* SCSI Path of Disk to added or removed */ +}; + +/* The following is used by virthba/vSCSI to send the Acquire/Release commands +* to the IOVM. */ +struct uiscmdrsp_vdiskmgmt { + VDISK_MGMT_TYPES vdisktype; + + /* the type of task */ + struct uisscsi_dest vdest; + + /* the vdisk for which this task mgmt is generated */ + void *scsicmd; + + /* This is some handle that the guest has saved off for its own use. + * Its value is preserved by iopart & returned as is in the task mgmt + * rsp. */ + void *notify; + + /* For linux guests, this is a pointer to wait_queue_head that a + * thread is waiting on to see if the taskmgmt command has completed. + * For windows guests, this is a pointer to a location that a waiting + * thread is testing to see if the taskmgmt command has completed. + * When the rsp is received by guest, the thread receiving the + * response uses this to notify the the thread waiting for taskmgmt + * command completion. Its value is preserved by iopart & returned + * as is in the task mgmt rsp. */ + void *notifyresult; + + /* this is a handle to location in guest where the result of the + * taskmgmt command (result field) is to saved off when the response + * is handled. Its value is preserved by iopart & returned as is in + * the task mgmt rsp. */ + char result; + + /* result of taskmgmt command - set by IOPart - values are: */ +#define VDISK_MGMT_FAILED 0 +#define VDISK_MGMT_SUCCESS 1 +}; + +/* keeping cmd & rsp info in one structure for now cmd rsp packet for scsi */ +struct uiscmdrsp { + char cmdtype; + + /* describes what type of information is in the struct */ +#define CMD_SCSI_TYPE 1 +#define CMD_NET_TYPE 2 +#define CMD_SCSITASKMGMT_TYPE 3 +#define CMD_NOTIFYGUEST_TYPE 4 +#define CMD_VDISKMGMT_TYPE 5 + union { + struct uiscmdrsp_scsi scsi; + struct uiscmdrsp_net net; + struct uiscmdrsp_scsitaskmgmt scsitaskmgmt; + struct uiscmdrsp_disknotify disknotify; + struct uiscmdrsp_vdiskmgmt vdiskmgmt; + }; + void *private_data; /* used to send the response when the cmd is + * done (scsi & scsittaskmgmt). */ + struct uiscmdrsp *next; /* General Purpose Queue Link */ + struct uiscmdrsp *activeQ_next; /* Used to track active commands */ + struct uiscmdrsp *activeQ_prev; /* Used to track active commands */ +}; + +/* This is just the header of the IO channel. It is assumed that directly after +* this header there is a large region of memory which contains the command and +* response queues as specified in cmdQ and rspQ SIGNAL_QUEUE_HEADERS. */ +typedef struct _ULTRA_IO_CHANNEL_PROTOCOL { + CHANNEL_HEADER ChannelHeader; + SIGNAL_QUEUE_HEADER cmdQ; + SIGNAL_QUEUE_HEADER rspQ; + union { + struct { + struct vhba_wwnn wwnn; /* 8 bytes */ + struct vhba_config_max max; /* 20 bytes */ + } vhba; /* 28 */ + struct { + U8 macaddr[MAX_MACADDR_LEN]; /* 6 bytes */ + U32 num_rcv_bufs; /* 4 */ + U32 mtu; /* 4 */ + GUID zoneGuid; /* 16 */ + } vnic; /* total 30 */ + }; + +#define MAX_CLIENTSTRING_LEN 1024 + U8 clientString[MAX_CLIENTSTRING_LEN]; /* NULL terminated - so holds + * max - 1 bytes */ +} ULTRA_IO_CHANNEL_PROTOCOL; + +#pragma pack(pop) +/* ///////////// END PRAGMA PACK PUSH 1 /////////////////////////// */ + +/* define offsets to members of struct uiscmdrsp */ +#define OFFSET_CMDTYPE OFFSETOF(struct uiscmdrsp, cmdtype) +#define OFFSET_SCSI OFFSETOF(struct uiscmdrsp, scsi) +#define OFFSET_NET OFFSETOF(struct uiscmdrsp, net) +#define OFFSET_SCSITASKMGMT OFFSETOF(struct uiscmdrsp, scsitaskmgmt) +#define OFFSET_NEXT OFFSETOF(struct uiscmdrsp, next) + +/* define offsets to members of struct uiscmdrsp_net */ +#define OFFSET_TYPE OFFSETOF(struct uiscmdrsp_net, type) +#define OFFSET_BUF OFFSETOF(struct uiscmdrsp_net, buf) +#define OFFSET_XMT OFFSETOF(struct uiscmdrsp_net, xmt) +#define OFFSET_XMT_DONE_RESULT OFFSETOF(struct uiscmdrsp_net, xmtdone) +#define OFFSET_RCVPOST OFFSETOF(struct uiscmdrsp_net, rcvpost) +#define OFFSET_RCV_DONE_LEN OFFSETOF(struct uiscmdrsp_net, rcv) +#define OFFSET_ENBDIS OFFSETOF(struct uiscmdrsp_net, enbdis) + +/* define offsets to members of struct net_pkt_rcvpost */ +#define OFFSET_TOTALLEN OFFSETOF(struct net_pkt_rcvpost, totallen) +#define OFFSET_FRAG OFFSETOF(struct net_pkt_rcvpost, frag) + +/* +* INLINE functions for initializing and accessing I/O data channels +*/ + + +#define NUMSIGNALS(x, q) (((ULTRA_IO_CHANNEL_PROTOCOL *)(x))->q.MaxSignalSlots) +#define SIZEOF_PROTOCOL (COVER(sizeof(ULTRA_IO_CHANNEL_PROTOCOL), 64)) +#define SIZEOF_CMDRSP (COVER(sizeof(struct uiscmdrsp), 64)) + +#define IO_CHANNEL_SIZE(x) COVER(SIZEOF_PROTOCOL + \ + (NUMSIGNALS(x, cmdQ) + \ + NUMSIGNALS(x, rspQ)) * SIZEOF_CMDRSP, 4096) +#define MIN_IO_CHANNEL_SIZE COVER(SIZEOF_PROTOCOL + \ + 2 * MIN_NUMSIGNALS * SIZEOF_CMDRSP, 4096) +#ifdef __GNUC__ +/* These defines should only ever be used in service partitons */ +/* because they rely on the size of uiscmdrsp */ +#define QSLOTSFROMBYTES(bytes) (((bytes-SIZEOF_PROTOCOL)/2)/SIZEOF_CMDRSP) +#define QSIZEFROMBYTES(bytes) (QSLOTSFROMBYTES(bytes)*SIZEOF_CMDRSP) +#define SignalQInit(x) \ + do { \ + x->cmdQ.Size = QSIZEFROMBYTES(x->ChannelHeader.Size); \ + x->cmdQ.oSignalBase = SIZEOF_PROTOCOL - \ + OFFSETOF(ULTRA_IO_CHANNEL_PROTOCOL, cmdQ); \ + x->cmdQ.SignalSize = SIZEOF_CMDRSP; \ + x->cmdQ.MaxSignalSlots = \ + QSLOTSFROMBYTES(x->ChannelHeader.Size); \ + x->cmdQ.MaxSignals = x->cmdQ.MaxSignalSlots - 1; \ + x->rspQ.Size = QSIZEFROMBYTES(x->ChannelHeader.Size); \ + x->rspQ.oSignalBase = \ + (SIZEOF_PROTOCOL + x->cmdQ.Size) - \ + OFFSETOF(ULTRA_IO_CHANNEL_PROTOCOL, rspQ); \ + x->rspQ.SignalSize = SIZEOF_CMDRSP; \ + x->rspQ.MaxSignalSlots = \ + QSLOTSFROMBYTES(x->ChannelHeader.Size); \ + x->rspQ.MaxSignals = x->rspQ.MaxSignalSlots - 1; \ + x->ChannelHeader.oChannelSpace = \ + OFFSETOF(ULTRA_IO_CHANNEL_PROTOCOL, cmdQ); \ + } while (0) + +#define INIT_CLIENTSTRING(chan, type, clientStr, clientStrLen) \ + do { \ + if (clientStr) { \ + chan->ChannelHeader.oClientString = \ + OFFSETOF(type, clientString); \ + MEMCPY(chan->clientString, clientStr, \ + MINNUM(clientStrLen, \ + (U32) (MAX_CLIENTSTRING_LEN - 1))); \ + chan->clientString[MINNUM(clientStrLen, \ + (U32) (MAX_CLIENTSTRING_LEN \ + - 1))] \ + = '\0'; \ + } \ + else \ + if (clientStrLen > 0) \ + return 0; \ + } while (0) + + +#define ULTRA_IO_CHANNEL_SERVER_READY(x, chanId, logCtx) \ + ULTRA_CHANNEL_SERVER_TRANSITION(x, chanId, SrvState, CHANNELSRV_READY, \ + logCtx); + +#define ULTRA_IO_CHANNEL_SERVER_NOTREADY(x, chanId, logCtx) \ + ULTRA_CHANNEL_SERVER_TRANSITION(x, chanId, SrvState, \ + CHANNELSRV_UNINITIALIZED, logCtx); + +static inline int ULTRA_VHBA_init_channel(ULTRA_IO_CHANNEL_PROTOCOL *x, + struct vhba_wwnn *wwnn, + struct vhba_config_max *max, + unsigned char *clientStr, + U32 clientStrLen, U64 bytes) { + MEMSET(x, 0, sizeof(ULTRA_IO_CHANNEL_PROTOCOL)); + x->ChannelHeader.VersionId = ULTRA_VHBA_CHANNEL_PROTOCOL_VERSIONID; + x->ChannelHeader.Signature = ULTRA_VHBA_CHANNEL_PROTOCOL_SIGNATURE; + x->ChannelHeader.SrvState = CHANNELSRV_UNINITIALIZED; + x->ChannelHeader.HeaderSize = sizeof(x->ChannelHeader); + x->ChannelHeader.Size = COVER(bytes, 4096); + x->ChannelHeader.Type = UltraVhbaChannelProtocolGuid; + x->ChannelHeader.ZoneGuid = Guid0; + x->vhba.wwnn = *wwnn; + x->vhba.max = *max; + INIT_CLIENTSTRING(x, ULTRA_IO_CHANNEL_PROTOCOL, clientStr, + clientStrLen); + SignalQInit(x); + if ((x->cmdQ.MaxSignalSlots > MAX_NUMSIGNALS) || + (x->rspQ.MaxSignalSlots > MAX_NUMSIGNALS)) { + return 0; + } + if ((x->cmdQ.MaxSignalSlots < MIN_NUMSIGNALS) || + (x->rspQ.MaxSignalSlots < MIN_NUMSIGNALS)) { + return 0; + } + return 1; +} + +static inline void ULTRA_VHBA_set_max(ULTRA_IO_CHANNEL_PROTOCOL *x, + struct vhba_config_max *max) { + x->vhba.max = *max; +} + +static inline int ULTRA_VNIC_init_channel(ULTRA_IO_CHANNEL_PROTOCOL *x, + unsigned char *macaddr, + U32 num_rcv_bufs, U32 mtu, + GUID zoneGuid, + unsigned char *clientStr, + U32 clientStrLen, + U64 bytes) { + MEMSET(x, 0, sizeof(ULTRA_IO_CHANNEL_PROTOCOL)); + x->ChannelHeader.VersionId = ULTRA_VNIC_CHANNEL_PROTOCOL_VERSIONID; + x->ChannelHeader.Signature = ULTRA_VNIC_CHANNEL_PROTOCOL_SIGNATURE; + x->ChannelHeader.SrvState = CHANNELSRV_UNINITIALIZED; + x->ChannelHeader.HeaderSize = sizeof(x->ChannelHeader); + x->ChannelHeader.Size = COVER(bytes, 4096); + x->ChannelHeader.Type = UltraVnicChannelProtocolGuid; + x->ChannelHeader.ZoneGuid = Guid0; + MEMCPY(x->vnic.macaddr, macaddr, MAX_MACADDR_LEN); + x->vnic.num_rcv_bufs = num_rcv_bufs; + x->vnic.mtu = mtu; + x->vnic.zoneGuid = zoneGuid; + INIT_CLIENTSTRING(x, ULTRA_IO_CHANNEL_PROTOCOL, clientStr, + clientStrLen); + SignalQInit(x); + if ((x->cmdQ.MaxSignalSlots > MAX_NUMSIGNALS) || + (x->rspQ.MaxSignalSlots > MAX_NUMSIGNALS)) { + return 0; + } + if ((x->cmdQ.MaxSignalSlots < MIN_NUMSIGNALS) || + (x->rspQ.MaxSignalSlots < MIN_NUMSIGNALS)) { + return 0; + } + return 1; +} + +#endif /* __GNUC__ */ + +/* +* INLINE function for expanding a guest's pfn-off-size into multiple 4K page +* pfn-off-size entires. +*/ + + +/* we deal with 4K page sizes when we it comes to passing page information + * between */ +/* Guest and IOPartition. */ +#define PI_PAGE_SIZE 0x1000 +#define PI_PAGE_MASK 0x0FFF +#define PI_PAGE_SHIFT 12 + +/* returns next non-zero index on success or zero on failure (i.e. out of + * room) + */ +static INLINE U16 +add_physinfo_entries(U32 inp_pfn, /* input - specifies the pfn to be used + * to add entries */ + U16 inp_off, /* input - specifies the off to be used + * to add entries */ + U32 inp_len, /* input - specifies the len to be used + * to add entries */ + U16 index, /* input - index in array at which new + * entries are added */ + U16 max_pi_arr_entries, /* input - specifies the maximum + * entries pi_arr can hold */ + struct phys_info pi_arr[]) /* input & output - array to + * which entries are added */ +{ + U32 len; + U16 i, firstlen; + + firstlen = PI_PAGE_SIZE - inp_off; + if (inp_len <= firstlen) { + + /* the input entry spans only one page - add as is */ + if (index >= max_pi_arr_entries) + return 0; + pi_arr[index].pi_pfn = inp_pfn; + pi_arr[index].pi_off = (U16) inp_off; + pi_arr[index].pi_len = (U16) inp_len; + return index + 1; + } + + /* this entry spans multiple pages */ + for (len = inp_len, i = 0; len; + len -= pi_arr[index + i].pi_len, i++) { + if (index + i >= max_pi_arr_entries) + return 0; + pi_arr[index + i].pi_pfn = inp_pfn + i; + if (i == 0) { + pi_arr[index].pi_off = inp_off; + pi_arr[index].pi_len = firstlen; + } + + else { + pi_arr[index + i].pi_off = 0; + pi_arr[index + i].pi_len = + (U16) MINNUM(len, (U32) PI_PAGE_SIZE); + } + + } + return index + i; +} + +#endif /* __IOCHANNEL_H__ */ diff --git a/drivers/staging/unisys/common-spar/include/channels/vbuschannel.h b/drivers/staging/unisys/common-spar/include/channels/vbuschannel.h new file mode 100644 index 000000000000..ec5a8c0fd504 --- /dev/null +++ b/drivers/staging/unisys/common-spar/include/channels/vbuschannel.h @@ -0,0 +1,127 @@ +/* Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __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 "commontypes.h" +#include "vbusdeviceinfo.h" +#include "channel.h" + +/* {193b331b-c58f-11da-95a9-00e08161165f} */ +#define ULTRA_VBUS_CHANNEL_PROTOCOL_GUID \ + {0x193b331b, 0xc58f, 0x11da, \ + {0x95, 0xa9, 0x0, 0xe0, 0x81, 0x61, 0x16, 0x5f} } +static const GUID UltraVbusChannelProtocolGuid = + ULTRA_VBUS_CHANNEL_PROTOCOL_GUID; + +#define ULTRA_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 ULTRA_VBUS_CHANNEL_PROTOCOL_VERSIONID 1 + +#define ULTRA_VBUS_CHANNEL_OK_CLIENT(pChannel, logCtx) \ + (ULTRA_check_channel_client(pChannel, \ + UltraVbusChannelProtocolGuid, \ + "vbus", \ + sizeof(ULTRA_VBUS_CHANNEL_PROTOCOL), \ + ULTRA_VBUS_CHANNEL_PROTOCOL_VERSIONID, \ + ULTRA_VBUS_CHANNEL_PROTOCOL_SIGNATURE, \ + __FILE__, __LINE__, logCtx)) + +#define ULTRA_VBUS_CHANNEL_OK_SERVER(actualBytes, logCtx) \ + (ULTRA_check_channel_server(UltraVbusChannelProtocolGuid, \ + "vbus", \ + sizeof(ULTRA_VBUS_CHANNEL_PROTOCOL), \ + actualBytes, \ + __FILE__, __LINE__, logCtx)) + + +#pragma pack(push, 1) /* both GCC and VC now allow this pragma */ +typedef struct _ULTRA_VBUS_HEADERINFO { + U32 structBytes; /* size of this struct in bytes */ + U32 deviceInfoStructBytes; /* sizeof(ULTRA_VBUS_DEVICEINFO) */ + U32 devInfoCount; /* num of items in DevInfo member */ + /* (this is the allocated size) */ + U32 chpInfoByteOffset; /* byte offset from beginning of this struct */ + /* to the the ChpInfo struct (below) */ + U32 busInfoByteOffset; /* byte offset from beginning of this struct */ + /* to the the BusInfo struct (below) */ + U32 devInfoByteOffset; /* byte offset from beginning of this struct */ + /* to the the DevInfo array (below) */ + U8 reserved[104]; +} ULTRA_VBUS_HEADERINFO; + +typedef struct _ULTRA_VBUS_CHANNEL_PROTOCOL { + ULTRA_CHANNEL_PROTOCOL ChannelHeader; /* initialized by server */ + ULTRA_VBUS_HEADERINFO HdrInfo; /* initialized by server */ + /* the remainder of this channel is filled in by the client */ + ULTRA_VBUS_DEVICEINFO ChpInfo; /* describes client chipset device and + * driver */ + ULTRA_VBUS_DEVICEINFO BusInfo; /* describes client bus device and + * driver */ + ULTRA_VBUS_DEVICEINFO DevInfo[0]; /* describes client device and + * driver for */ + /* each device on the bus */ +} ULTRA_VBUS_CHANNEL_PROTOCOL; + +#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) + +static INLINE void +ULTRA_VBUS_init_channel(ULTRA_VBUS_CHANNEL_PROTOCOL *x, int bytesAllocated) +{ + /* Please note that the memory at does NOT necessarily have space + * for DevInfo structs allocated at the end, which is why we do NOT use + * to clear. */ + MEMSET(x, 0, sizeof(ULTRA_VBUS_CHANNEL_PROTOCOL)); + if (bytesAllocated < (int) sizeof(ULTRA_VBUS_CHANNEL_PROTOCOL)) + return; + x->ChannelHeader.VersionId = ULTRA_VBUS_CHANNEL_PROTOCOL_VERSIONID; + x->ChannelHeader.Signature = ULTRA_VBUS_CHANNEL_PROTOCOL_SIGNATURE; + x->ChannelHeader.SrvState = CHANNELSRV_READY; + x->ChannelHeader.HeaderSize = sizeof(x->ChannelHeader); + x->ChannelHeader.Size = bytesAllocated; + x->ChannelHeader.Type = UltraVbusChannelProtocolGuid; + x->ChannelHeader.ZoneGuid = Guid0; + x->HdrInfo.structBytes = sizeof(ULTRA_VBUS_HEADERINFO); + x->HdrInfo.chpInfoByteOffset = sizeof(ULTRA_VBUS_HEADERINFO); + x->HdrInfo.busInfoByteOffset = x->HdrInfo.chpInfoByteOffset + + sizeof(ULTRA_VBUS_DEVICEINFO); + x->HdrInfo.devInfoByteOffset = x->HdrInfo.busInfoByteOffset + + sizeof(ULTRA_VBUS_DEVICEINFO); + x->HdrInfo.deviceInfoStructBytes = sizeof(ULTRA_VBUS_DEVICEINFO); + bytesAllocated -= (sizeof(ULTRA_CHANNEL_PROTOCOL) + + x->HdrInfo.devInfoByteOffset); + x->HdrInfo.devInfoCount = + bytesAllocated / x->HdrInfo.deviceInfoStructBytes; +} + +#pragma pack(pop) + +#endif diff --git a/drivers/staging/unisys/common-spar/include/controlvmcompletionstatus.h b/drivers/staging/unisys/common-spar/include/controlvmcompletionstatus.h new file mode 100644 index 000000000000..de30d321d982 --- /dev/null +++ b/drivers/staging/unisys/common-spar/include/controlvmcompletionstatus.h @@ -0,0 +1,92 @@ +/* controlvmcompletionstatus.c + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* 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/common-spar/include/diagnostics/appos_subsystems.h b/drivers/staging/unisys/common-spar/include/diagnostics/appos_subsystems.h new file mode 100644 index 000000000000..4c6294d20606 --- /dev/null +++ b/drivers/staging/unisys/common-spar/include/diagnostics/appos_subsystems.h @@ -0,0 +1,310 @@ +/* Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* Please note that this file is to be used ONLY for defining diagnostic + * subsystem values for the appos (sPAR Linux service partitions) component. + */ +#ifndef __APPOS_SUBSYSTEMS_H__ +#define __APPOS_SUBSYSTEMS_H__ + +#ifdef __KERNEL__ +#include +#include +#else +#include +#include +#endif + +static inline char * +subsys_unknown_to_s(int subsys, char *s, int n) +{ + snprintf(s, n, "SUBSYS-%-2.2d", subsys); + s[n - 1] = '\0'; + return s; +} + +#define SUBSYS_TO_MASK(subsys) (1ULL << (subsys)) + +/* The first SUBSYS_APPOS_MAX subsystems are the same for each AppOS type + * (IOVM, SMS, etc.) The rest have unique values for each AppOS type. + */ +#define SUBSYS_APPOS_MAX 16 + +#define SUBSYS_APPOS_DEFAULT 1 /* or "other" */ +#define SUBSYS_APPOS_CHIPSET 2 /* controlvm and other */ + /* low-level sPAR activity */ +#define SUBSYS_APPOS_BUS 3 /* sPAR bus */ +/* DAK #define SUBSYS_APPOS_DIAG 4 // diagnostics and dump */ +#define SUBSYS_APPOS_CHANNELACCESS 5 /* generic channel access */ +#define SUBSYS_APPOS_NICCLIENT 6 /* virtual NIC client */ +#define SUBSYS_APPOS_HBACLIENT 7 /* virtual HBA client */ +#define SUBSYS_APPOS_CONSOLESERIAL 8 /* sPAR virtual serial console */ +#define SUBSYS_APPOS_UISLIB 9 /* */ +#define SUBSYS_APPOS_VRTCUPDD 10 /* */ +#define SUBSYS_APPOS_WATCHDOG 11 /* watchdog timer and healthcheck */ +#define SUBSYS_APPOS_13 13 /* available */ +#define SUBSYS_APPOS_14 14 /* available */ +#define SUBSYS_APPOS_15 15 /* available */ +#define SUBSYS_APPOS_16 16 /* available */ +static inline char * +subsys_generic_to_s(int subsys, char *s, int n) +{ + switch (subsys) { + case SUBSYS_APPOS_DEFAULT: + strncpy(s, "APPOS_DEFAULT", n); + break; + case SUBSYS_APPOS_CHIPSET: + strncpy(s, "APPOS_CHIPSET", n); + break; + case SUBSYS_APPOS_BUS: + strncpy(s, "APPOS_BUS", n); + break; + case SUBSYS_APPOS_CHANNELACCESS: + strncpy(s, "APPOS_CHANNELACCESS", n); + break; + case SUBSYS_APPOS_NICCLIENT: + strncpy(s, "APPOS_NICCLIENT", n); + break; + case SUBSYS_APPOS_HBACLIENT: + strncpy(s, "APPOS_HBACLIENT", n); + break; + case SUBSYS_APPOS_CONSOLESERIAL: + strncpy(s, "APPOS_CONSOLESERIAL", n); + break; + case SUBSYS_APPOS_UISLIB: + strncpy(s, "APPOS_UISLIB", n); + break; + case SUBSYS_APPOS_VRTCUPDD: + strncpy(s, "APPOS_VRTCUPDD", n); + break; + case SUBSYS_APPOS_WATCHDOG: + strncpy(s, "APPOS_WATCHDOG", n); + break; + case SUBSYS_APPOS_13: + strncpy(s, "APPOS_13", n); + break; + case SUBSYS_APPOS_14: + strncpy(s, "APPOS_14", n); + break; + case SUBSYS_APPOS_15: + strncpy(s, "APPOS_15", n); + break; + case SUBSYS_APPOS_16: + strncpy(s, "APPOS_16", n); + break; + default: + subsys_unknown_to_s(subsys, s, n); + break; + } + s[n - 1] = '\0'; + return s; +} + +/* CONSOLE */ + +#define SUBSYS_CONSOLE_VIDEO (SUBSYS_APPOS_MAX + 1) /* 17 */ +#define SUBSYS_CONSOLE_KBDMOU (SUBSYS_APPOS_MAX + 2) /* 18 */ +#define SUBSYS_CONSOLE_04 (SUBSYS_APPOS_MAX + 4) +#define SUBSYS_CONSOLE_05 (SUBSYS_APPOS_MAX + 5) +#define SUBSYS_CONSOLE_06 (SUBSYS_APPOS_MAX + 6) +#define SUBSYS_CONSOLE_07 (SUBSYS_APPOS_MAX + 7) +#define SUBSYS_CONSOLE_08 (SUBSYS_APPOS_MAX + 8) +#define SUBSYS_CONSOLE_09 (SUBSYS_APPOS_MAX + 9) +#define SUBSYS_CONSOLE_10 (SUBSYS_APPOS_MAX + 10) +#define SUBSYS_CONSOLE_11 (SUBSYS_APPOS_MAX + 11) +#define SUBSYS_CONSOLE_12 (SUBSYS_APPOS_MAX + 12) +#define SUBSYS_CONSOLE_13 (SUBSYS_APPOS_MAX + 13) +#define SUBSYS_CONSOLE_14 (SUBSYS_APPOS_MAX + 14) +#define SUBSYS_CONSOLE_15 (SUBSYS_APPOS_MAX + 15) +#define SUBSYS_CONSOLE_16 (SUBSYS_APPOS_MAX + 16) +#define SUBSYS_CONSOLE_17 (SUBSYS_APPOS_MAX + 17) +#define SUBSYS_CONSOLE_18 (SUBSYS_APPOS_MAX + 18) +#define SUBSYS_CONSOLE_19 (SUBSYS_APPOS_MAX + 19) +#define SUBSYS_CONSOLE_20 (SUBSYS_APPOS_MAX + 20) +#define SUBSYS_CONSOLE_21 (SUBSYS_APPOS_MAX + 21) +#define SUBSYS_CONSOLE_22 (SUBSYS_APPOS_MAX + 22) +#define SUBSYS_CONSOLE_23 (SUBSYS_APPOS_MAX + 23) +#define SUBSYS_CONSOLE_24 (SUBSYS_APPOS_MAX + 24) +#define SUBSYS_CONSOLE_25 (SUBSYS_APPOS_MAX + 25) +#define SUBSYS_CONSOLE_26 (SUBSYS_APPOS_MAX + 26) +#define SUBSYS_CONSOLE_27 (SUBSYS_APPOS_MAX + 27) +#define SUBSYS_CONSOLE_28 (SUBSYS_APPOS_MAX + 28) +#define SUBSYS_CONSOLE_29 (SUBSYS_APPOS_MAX + 29) +#define SUBSYS_CONSOLE_30 (SUBSYS_APPOS_MAX + 30) +#define SUBSYS_CONSOLE_31 (SUBSYS_APPOS_MAX + 31) +#define SUBSYS_CONSOLE_32 (SUBSYS_APPOS_MAX + 32) +#define SUBSYS_CONSOLE_33 (SUBSYS_APPOS_MAX + 33) +#define SUBSYS_CONSOLE_34 (SUBSYS_APPOS_MAX + 34) +#define SUBSYS_CONSOLE_35 (SUBSYS_APPOS_MAX + 35) +#define SUBSYS_CONSOLE_36 (SUBSYS_APPOS_MAX + 36) +#define SUBSYS_CONSOLE_37 (SUBSYS_APPOS_MAX + 37) +#define SUBSYS_CONSOLE_38 (SUBSYS_APPOS_MAX + 38) +#define SUBSYS_CONSOLE_39 (SUBSYS_APPOS_MAX + 39) +#define SUBSYS_CONSOLE_40 (SUBSYS_APPOS_MAX + 40) +#define SUBSYS_CONSOLE_41 (SUBSYS_APPOS_MAX + 41) +#define SUBSYS_CONSOLE_42 (SUBSYS_APPOS_MAX + 42) +#define SUBSYS_CONSOLE_43 (SUBSYS_APPOS_MAX + 43) +#define SUBSYS_CONSOLE_44 (SUBSYS_APPOS_MAX + 44) +#define SUBSYS_CONSOLE_45 (SUBSYS_APPOS_MAX + 45) +#define SUBSYS_CONSOLE_46 (SUBSYS_APPOS_MAX + 46) + +static inline char * +subsys_console_to_s(int subsys, char *s, int n) +{ + switch (subsys) { + case SUBSYS_CONSOLE_VIDEO: + strncpy(s, "CONSOLE_VIDEO", n); + break; + case SUBSYS_CONSOLE_KBDMOU: + strncpy(s, "CONSOLE_KBDMOU", n); + break; + case SUBSYS_CONSOLE_04: + strncpy(s, "CONSOLE_04", n); + break; + case SUBSYS_CONSOLE_05: + strncpy(s, "CONSOLE_05", n); + break; + case SUBSYS_CONSOLE_06: + strncpy(s, "CONSOLE_06", n); + break; + case SUBSYS_CONSOLE_07: + strncpy(s, "CONSOLE_07", n); + break; + case SUBSYS_CONSOLE_08: + strncpy(s, "CONSOLE_08", n); + break; + case SUBSYS_CONSOLE_09: + strncpy(s, "CONSOLE_09", n); + break; + case SUBSYS_CONSOLE_10: + strncpy(s, "CONSOLE_10", n); + break; + case SUBSYS_CONSOLE_11: + strncpy(s, "CONSOLE_11", n); + break; + case SUBSYS_CONSOLE_12: + strncpy(s, "CONSOLE_12", n); + break; + case SUBSYS_CONSOLE_13: + strncpy(s, "CONSOLE_13", n); + break; + case SUBSYS_CONSOLE_14: + strncpy(s, "CONSOLE_14", n); + break; + case SUBSYS_CONSOLE_15: + strncpy(s, "CONSOLE_15", n); + break; + case SUBSYS_CONSOLE_16: + strncpy(s, "CONSOLE_16", n); + break; + case SUBSYS_CONSOLE_17: + strncpy(s, "CONSOLE_17", n); + break; + case SUBSYS_CONSOLE_18: + strncpy(s, "CONSOLE_18", n); + break; + case SUBSYS_CONSOLE_19: + strncpy(s, "CONSOLE_19", n); + break; + case SUBSYS_CONSOLE_20: + strncpy(s, "CONSOLE_20", n); + break; + case SUBSYS_CONSOLE_21: + strncpy(s, "CONSOLE_21", n); + break; + case SUBSYS_CONSOLE_22: + strncpy(s, "CONSOLE_22", n); + break; + case SUBSYS_CONSOLE_23: + strncpy(s, "CONSOLE_23", n); + break; + case SUBSYS_CONSOLE_24: + strncpy(s, "CONSOLE_24", n); + break; + case SUBSYS_CONSOLE_25: + strncpy(s, "CONSOLE_25", n); + break; + case SUBSYS_CONSOLE_26: + strncpy(s, "CONSOLE_26", n); + break; + case SUBSYS_CONSOLE_27: + strncpy(s, "CONSOLE_27", n); + break; + case SUBSYS_CONSOLE_28: + strncpy(s, "CONSOLE_28", n); + break; + case SUBSYS_CONSOLE_29: + strncpy(s, "CONSOLE_29", n); + break; + case SUBSYS_CONSOLE_30: + strncpy(s, "CONSOLE_30", n); + break; + case SUBSYS_CONSOLE_31: + strncpy(s, "CONSOLE_31", n); + break; + case SUBSYS_CONSOLE_32: + strncpy(s, "CONSOLE_32", n); + break; + case SUBSYS_CONSOLE_33: + strncpy(s, "CONSOLE_33", n); + break; + case SUBSYS_CONSOLE_34: + strncpy(s, "CONSOLE_34", n); + break; + case SUBSYS_CONSOLE_35: + strncpy(s, "CONSOLE_35", n); + break; + case SUBSYS_CONSOLE_36: + strncpy(s, "CONSOLE_36", n); + break; + case SUBSYS_CONSOLE_37: + strncpy(s, "CONSOLE_37", n); + break; + case SUBSYS_CONSOLE_38: + strncpy(s, "CONSOLE_38", n); + break; + case SUBSYS_CONSOLE_39: + strncpy(s, "CONSOLE_39", n); + break; + case SUBSYS_CONSOLE_40: + strncpy(s, "CONSOLE_40", n); + break; + case SUBSYS_CONSOLE_41: + strncpy(s, "CONSOLE_41", n); + break; + case SUBSYS_CONSOLE_42: + strncpy(s, "CONSOLE_42", n); + break; + case SUBSYS_CONSOLE_43: + strncpy(s, "CONSOLE_43", n); + break; + case SUBSYS_CONSOLE_44: + strncpy(s, "CONSOLE_44", n); + break; + case SUBSYS_CONSOLE_45: + strncpy(s, "CONSOLE_45", n); + break; + case SUBSYS_CONSOLE_46: + strncpy(s, "CONSOLE_46", n); + break; + default: + subsys_unknown_to_s(subsys, s, n); + break; + } + s[n - 1] = '\0'; + return s; +} + +#endif diff --git a/drivers/staging/unisys/common-spar/include/iovmcall_gnuc.h b/drivers/staging/unisys/common-spar/include/iovmcall_gnuc.h new file mode 100644 index 000000000000..7304e9a0648c --- /dev/null +++ b/drivers/staging/unisys/common-spar/include/iovmcall_gnuc.h @@ -0,0 +1,53 @@ +/* Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* 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) { + __asm__ __volatile__(".byte 0x00f, 0x001, 0x0c1" : "=a"(result) : + "a"(tuple), "b"(reg_ebx), "c"(reg_ecx) + ); + } else { + result = -1; + } + 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) { + __asm__ __volatile__(".byte 0x00f, 0x001, 0x0c1" : "=a"(result) : + "a"(tuple), "b"(reg_ebx), "c"(reg_ecx), + "d"(reg_edx)); + } else { + result = -1; + } + return result; + } diff --git a/drivers/staging/unisys/common-spar/include/vbusdeviceinfo.h b/drivers/staging/unisys/common-spar/include/vbusdeviceinfo.h new file mode 100644 index 000000000000..373677908915 --- /dev/null +++ b/drivers/staging/unisys/common-spar/include/vbusdeviceinfo.h @@ -0,0 +1,209 @@ +/* Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __VBUSDEVICEINFO_H__ +#define __VBUSDEVICEINFO_H__ + +#include "commontypes.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. + */ +typedef struct _ULTRA_VBUS_DEVICEINFO { + U8 devType[16]; /* short string identifying the device type */ + U8 drvName[16]; /* driver .sys file name */ + U8 infoStrings[96]; /* sequence of tab-delimited id strings: */ + /* */ + U8 reserved[128]; /* pad size to 256 bytes */ +} ULTRA_VBUS_DEVICEINFO; + +#pragma pack(pop) + +/* Reads chars from the buffer at for bytes, and writes to + * the buffer at

, which is bytes long, ensuring never to + * overflow the buffer at

, using the following rules: + * - printable characters are simply copied from the buffer at to the + * buffer at

+ * - intervening streaks of non-printable characters in the buffer at + * are replaced with a single space in the buffer at

+ * Note that we pay no attention to '\0'-termination. + * Returns the number of bytes written to

. + * + * Pass

== NULL and == 0 for this special behavior. In this + * case, we simply return the number of bytes that WOULD HAVE been written + * to a buffer at

, 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 == NULL) + chars++; + nonprintable_streak = 0; + } + if (remain > 0) { + *p = *src; + p++; + remain--; + chars++; + } else if (p == NULL) + 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 to an ascii decimal string + * at

, writing at most bytes. Note there is NO '\0' termination + * written to

. + * + * Returns the number of bytes written to

. + * + * 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 */ + 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

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 */ + i = digits; + while (i > 0) { + i--; + *p = s[i]; + p++; + } + return digits; +} + +/* Reads , and converts its contents to a printable string at

, + * writing at most bytes. Note there is NO '\0' termination + * written to

. + * + * Pass >= 0 if you want a device index presented. + * + * Returns the number of bytes written to

. + */ +static inline int +VBUSCHANNEL_devInfoToStringBuffer(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.infoStrings[0]); + nsrc = sizeof(devInfo.infoStrings); + 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/common-spar/include/vmcallinterface.h b/drivers/staging/unisys/common-spar/include/vmcallinterface.h new file mode 100644 index 000000000000..bd8944abd092 --- /dev/null +++ b/drivers/staging/unisys/common-spar/include/vmcallinterface.h @@ -0,0 +1,167 @@ +/* Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __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. +*/ + +#ifdef __GNUC__ +#include "iovmcall_gnuc.h" +#endif /* */ +#include "diagchannel.h" + +#ifdef VMCALL_IO_CONTROLVM_ADDR +#undef VMCALL_IO_CONTROLVM_ADDR +#endif /* */ + +/* define subsystem number for AppOS, used in uislib driver */ +#define MDS_APPOS 0x4000000000000000 /* subsystem = 62 - AppOS */ +typedef enum { /* 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 + */ + + VMCALL_IO_CONTROLVM_ADDR = 0x0501, /* used by all Guests, not just + * IO */ + VMCALL_IO_DIAG_ADDR = 0x0508, + VMCALL_IO_VISORSERIAL_ADDR = 0x0509, + VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET = 0x0708, /* Allow caller to + * query virtual time + * offset */ + VMCALL_CHANNEL_VERSION_MISMATCH = 0x0709, + VMCALL_POST_CODE_LOGEVENT = 0x070B, /* LOGEVENT Post Code (RDX) with + * specified subsystem mask (RCX + * - monitor_subsystems.h) and + * severity (RDX) */ + VMCALL_GENERIC_SURRENDER_QUANTUM_FOREVER = 0x0802, /* Yield the + * remainder & all + * future quantums of + * the caller */ + VMCALL_MEASUREMENT_DO_NOTHING = 0x0901, + VMCALL_UPDATE_PHYSICAL_TIME = 0x0a02 /* Allow + * ULTRA_SERVICE_CAPABILITY_TIME + * capable guest to make + * VMCALL */ +} VMCALL_MONITOR_INTERFACE_METHOD_TUPLE; + +#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(InterfaceMethod, param, result) \ + (result = unisys_vmcall(InterfaceMethod, (param) & 0xFFFFFFFF, \ + (param) >> 32)) +#define ISSUE_IO_EXTENDED_VMCALL(InterfaceMethod, param1, param2, \ + param3, result) \ + (result = unisys_extended_vmcall(InterfaceMethod, 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) \ +do { \ + U32 _tempresult = VMCALL_SUCCESS; \ + ISSUE_IO_EXTENDED_VMCALL(VMCALL_POST_CODE_LOGEVENT, severity, \ + MDS_APPOS, postcode, _tempresult); \ +} while (0) +#endif + +/* Structures for IO VMCALLs */ + +/* ///////////// BEGIN PRAGMA PACK PUSH 1 ///////////////////////// */ +/* ///////////// ONLY STRUCT TYPE SHOULD BE BELOW */ +#pragma pack(push, 1) +struct phys_info { + U64 pi_pfn; + U16 pi_off; + U16 pi_len; +}; + +#pragma pack(pop) +/* ///////////// END PRAGMA PACK PUSH 1 /////////////////////////// */ +typedef struct phys_info IO_DATA_STRUCTURE; + +/* ///////////// BEGIN PRAGMA PACK PUSH 1 ///////////////////////// */ +/* ///////////// ONLY STRUCT TYPE SHOULD BE BELOW */ +#pragma pack(push, 1) +/* Parameters to VMCALL_IO_CONTROLVM_ADDR interface */ +typedef 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 ChannelAddress; /* contents provided by this VMCALL (OUT) */ + /* the size of the ControlVm channel in bytes This VMCall fills this + * in with the appropriate address. */ + U32 ChannelBytes; /* contents provided by this VMCALL (OUT) */ + U8 Unused[4]; /* Unused Bytes in the 64-Bit Aligned Struct */ +} VMCALL_IO_CONTROLVM_ADDR_PARAMS; + +#pragma pack(pop) +/* ///////////// END PRAGMA PACK PUSH 1 /////////////////////////// */ + +/* ///////////// BEGIN PRAGMA PACK PUSH 1 ///////////////////////// */ +/* ///////////// ONLY STRUCT TYPE SHOULD BE BELOW */ +#pragma pack(push, 1) +/* Parameters to VMCALL_IO_DIAG_ADDR interface */ +typedef struct _VMCALL_IO_DIAG_ADDR_PARAMS { + /* The Guest-relative physical address of the diagnostic channel. + * This VMCall fills this in with the appropriate address. */ + U64 ChannelAddress; /* contents provided by this VMCALL (OUT) */ +} VMCALL_IO_DIAG_ADDR_PARAMS; + +#pragma pack(pop) +/* ///////////// END PRAGMA PACK PUSH 1 /////////////////////////// */ + +/* ///////////// BEGIN PRAGMA PACK PUSH 1 ///////////////////////// */ +/* ///////////// ONLY STRUCT TYPE SHOULD BE BELOW */ +#pragma pack(push, 1) +/* Parameters to VMCALL_IO_VISORSERIAL_ADDR interface */ +typedef struct _VMCALL_IO_VISORSERIAL_ADDR_PARAMS { + /* The Guest-relative physical address of the serial console + * channel. This VMCall fills this in with the appropriate + * address. */ + U64 ChannelAddress; /* contents provided by this VMCALL (OUT) */ +} VMCALL_IO_VISORSERIAL_ADDR_PARAMS; + +#pragma pack(pop) +/* ///////////// END PRAGMA PACK PUSH 1 /////////////////////////// */ + +/* Parameters to VMCALL_CHANNEL_MISMATCH interface */ +typedef struct _VMCALL_CHANNEL_VERSION_MISMATCH_PARAMS { + U8 ChannelName[32]; /* Null terminated string giving name of channel + * (IN) */ + U8 ItemName[32]; /* Null terminated string giving name of + * mismatched item (IN) */ + U32 SourceLineNumber; /* line# where invoked. (IN) */ + U8 SourceFileName[36]; /* source code where invoked - Null terminated + * string (IN) */ +} VMCALL_CHANNEL_VERSION_MISMATCH_PARAMS; + +#endif /* __IOMONINTF_H__ */ diff --git a/drivers/staging/unisys/include/guestlinuxdebug.h b/drivers/staging/unisys/include/guestlinuxdebug.h new file mode 100644 index 000000000000..c3de8496e5d6 --- /dev/null +++ b/drivers/staging/unisys/include/guestlinuxdebug.h @@ -0,0 +1,182 @@ +/* Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __GUESTLINUXDEBUG_H__ +#define __GUESTLINUXDEBUG_H__ + +/* +* This file contains supporting interface for "vmcallinterface.h", particuarly +* regarding adding additional structure and functionality to linux +* ISSUE_IO_VMCALL_POSTCODE_SEVERITY */ + + +/******* INFO ON ISSUE_POSTCODE_LINUX() BELOW *******/ +#include "vmcallinterface.h" +typedef enum { /* POSTCODE driver identifier tuples */ + /* visorchipset driver files */ + VISOR_CHIPSET_PC = 0xA0, + VISOR_CHIPSET_PC_controlvm_c = 0xA1, + VISOR_CHIPSET_PC_controlvm_cm2 = 0xA2, + VISOR_CHIPSET_PC_controlvm_direct_c = 0xA3, + VISOR_CHIPSET_PC_file_c = 0xA4, + VISOR_CHIPSET_PC_parser_c = 0xA5, + VISOR_CHIPSET_PC_testing_c = 0xA6, + VISOR_CHIPSET_PC_visorchipset_main_c = 0xA7, + VISOR_CHIPSET_PC_visorswitchbus_c = 0xA8, + /* visorbus driver files */ + VISOR_BUS_PC = 0xB0, + VISOR_BUS_PC_businst_attr_c = 0xB1, + VISOR_BUS_PC_channel_attr_c = 0xB2, + VISOR_BUS_PC_devmajorminor_attr_c = 0xB3, + VISOR_BUS_PC_visorbus_main_c = 0xB4, + /* visorclientbus driver files */ + VISOR_CLIENT_BUS_PC = 0xC0, + VISOR_CLIENT_BUS_PC_visorclientbus_main_c = 0xC1, + /* virt hba driver files */ + VIRT_HBA_PC = 0xC2, + VIRT_HBA_PC_virthba_c = 0xC3, + /* virtpci driver files */ + VIRT_PCI_PC = 0xC4, + VIRT_PCI_PC_virtpci_c = 0xC5, + /* virtnic driver files */ + VIRT_NIC_PC = 0xC6, + VIRT_NIC_P_virtnic_c = 0xC7, + /* uislib driver files */ + UISLIB_PC = 0xD0, + UISLIB_PC_uislib_c = 0xD1, + UISLIB_PC_uisqueue_c = 0xD2, + UISLIB_PC_uisthread_c = 0xD3, + UISLIB_PC_uisutils_c = 0xD4, +} DRIVER_PC; + +typedef enum { /* POSTCODE event identifier tuples */ + ATTACH_PORT_ENTRY_PC = 0x001, + ATTACH_PORT_FAILURE_PC = 0x002, + ATTACH_PORT_SUCCESS_PC = 0x003, + BUS_FAILURE_PC = 0x004, + BUS_CREATE_ENTRY_PC = 0x005, + BUS_CREATE_FAILURE_PC = 0x006, + BUS_CREATE_EXIT_PC = 0x007, + BUS_CONFIGURE_ENTRY_PC = 0x008, + BUS_CONFIGURE_FAILURE_PC = 0x009, + BUS_CONFIGURE_EXIT_PC = 0x00A, + CHIPSET_INIT_ENTRY_PC = 0x00B, + CHIPSET_INIT_SUCCESS_PC = 0x00C, + CHIPSET_INIT_FAILURE_PC = 0x00D, + CHIPSET_INIT_EXIT_PC = 0x00E, + CREATE_WORKQUEUE_PC = 0x00F, + CREATE_WORKQUEUE_FAILED_PC = 0x0A0, + CONTROLVM_INIT_FAILURE_PC = 0x0A1, + DEVICE_CREATE_ENTRY_PC = 0x0A2, + DEVICE_CREATE_FAILURE_PC = 0x0A3, + DEVICE_CREATE_SUCCESS_PC = 0x0A4, + DEVICE_CREATE_EXIT_PC = 0x0A5, + DEVICE_ADD_PC = 0x0A6, + DEVICE_REGISTER_FAILURE_PC = 0x0A7, + DEVICE_CHANGESTATE_ENTRY_PC = 0x0A8, + DEVICE_CHANGESTATE_FAILURE_PC = 0x0A9, + DEVICE_CHANGESTATE_EXIT_PC = 0x0AA, + DRIVER_ENTRY_PC = 0x0AB, + DRIVER_EXIT_PC = 0x0AC, + MALLOC_FAILURE_PC = 0x0AD, + QUEUE_DELAYED_WORK_PC = 0x0AE, + UISLIB_THREAD_FAILURE_PC = 0x0B7, + VBUS_CHANNEL_ENTRY_PC = 0x0B8, + VBUS_CHANNEL_FAILURE_PC = 0x0B9, + VBUS_CHANNEL_EXIT_PC = 0x0BA, + VHBA_CREATE_ENTRY_PC = 0x0BB, + VHBA_CREATE_FAILURE_PC = 0x0BC, + VHBA_CREATE_EXIT_PC = 0x0BD, + VHBA_CREATE_SUCCESS_PC = 0x0BE, + VHBA_COMMAND_HANDLER_PC = 0x0BF, + VHBA_PROBE_ENTRY_PC = 0x0C0, + VHBA_PROBE_FAILURE_PC = 0x0C1, + VHBA_PROBE_EXIT_PC = 0x0C2, + VNIC_CREATE_ENTRY_PC = 0x0C3, + VNIC_CREATE_FAILURE_PC = 0x0C4, + VNIC_CREATE_SUCCESS_PC = 0x0C5, + VNIC_PROBE_ENTRY_PC = 0x0C6, + VNIC_PROBE_FAILURE_PC = 0x0C7, + VNIC_PROBE_EXIT_PC = 0x0C8, + VPCI_CREATE_ENTRY_PC = 0x0C9, + VPCI_CREATE_FAILURE_PC = 0x0CA, + VPCI_CREATE_EXIT_PC = 0x0CB, + VPCI_PROBE_ENTRY_PC = 0x0CC, + VPCI_PROBE_FAILURE_PC = 0x0CD, + VPCI_PROBE_EXIT_PC = 0x0CE, + CRASH_DEV_ENTRY_PC = 0x0CF, + CRASH_DEV_EXIT_PC = 0x0D0, + CRASH_DEV_HADDR_NULL = 0x0D1, + CRASH_DEV_CONTROLVM_NULL = 0x0D2, + CRASH_DEV_RD_BUS_FAIULRE_PC = 0x0D3, + CRASH_DEV_RD_DEV_FAIULRE_PC = 0x0D4, + CRASH_DEV_BUS_NULL_FAILURE_PC = 0x0D5, + CRASH_DEV_DEV_NULL_FAILURE_PC = 0x0D6, + CRASH_DEV_CTRL_RD_FAILURE_PC = 0x0D7, + CRASH_DEV_COUNT_FAILURE_PC = 0x0D8, + SAVE_MSG_BUS_FAILURE_PC = 0x0D9, + SAVE_MSG_DEV_FAILURE_PC = 0x0DA, + CALLHOME_INIT_FAILURE_PC = 0x0DB +} EVENT_PC; + +#ifdef __GNUC__ + +#define POSTCODE_SEVERITY_ERR DIAG_SEVERITY_ERR +#define POSTCODE_SEVERITY_WARNING DIAG_SEVERITY_WARNING +#define POSTCODE_SEVERITY_INFO DIAG_SEVERITY_PRINT /* TODO-> Info currently + * doesnt show, so we + * set info=warning */ +/* example call of POSTCODE_LINUX_2(VISOR_CHIPSET_PC, POSTCODE_SEVERITY_ERR); + * Please also note that the resulting postcode is in hex, so if you are + * searching for the __LINE__ number, convert it first to decimal. The line + * number combined with driver and type of call, will allow you to track down + * exactly what line an error occured on, or where the last driver + * entered/exited from. + */ + +/* BASE FUNCTIONS */ +#define POSTCODE_LINUX_A(DRIVER_PC, EVENT_PC, pc32bit, severity) \ +do { \ + unsigned long long post_code_temp; \ + post_code_temp = (((U64)DRIVER_PC) << 56) | (((U64)EVENT_PC) << 44) | \ + ((((U64)__LINE__) & 0xFFF) << 32) | \ + (((U64)pc32bit) & 0xFFFFFFFF); \ + ISSUE_IO_VMCALL_POSTCODE_SEVERITY(post_code_temp, severity); \ +} while (0) + +#define POSTCODE_LINUX_B(DRIVER_PC, EVENT_PC, pc16bit1, pc16bit2, severity) \ +do { \ + unsigned long long post_code_temp; \ + post_code_temp = (((U64)DRIVER_PC) << 56) | (((U64)EVENT_PC) << 44) | \ + ((((U64)__LINE__) & 0xFFF) << 32) | \ + ((((U64)pc16bit1) & 0xFFFF) << 16) | \ + (((U64)pc16bit2) & 0xFFFF); \ + ISSUE_IO_VMCALL_POSTCODE_SEVERITY(post_code_temp, severity); \ +} while (0) + +/* MOST COMMON */ +#define POSTCODE_LINUX_2(EVENT_PC, severity) \ + POSTCODE_LINUX_A(CURRENT_FILE_PC, EVENT_PC, 0x0000, severity); + +#define POSTCODE_LINUX_3(EVENT_PC, pc32bit, severity) \ + POSTCODE_LINUX_A(CURRENT_FILE_PC, EVENT_PC, pc32bit, severity); + + +#define POSTCODE_LINUX_4(EVENT_PC, pc16bit1, pc16bit2, severity) \ + POSTCODE_LINUX_B(CURRENT_FILE_PC, EVENT_PC, pc16bit1, \ + pc16bit2, severity); + +#endif +#endif diff --git a/drivers/staging/unisys/include/uisqueue.h b/drivers/staging/unisys/include/uisqueue.h new file mode 100644 index 000000000000..a9d95d300915 --- /dev/null +++ b/drivers/staging/unisys/include/uisqueue.h @@ -0,0 +1,472 @@ +/* uisqueue.h + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* + * Unisys IO Virtualization header NOTE: This file contains only Linux + * specific structs. All OS-independent structs are in iochannel.h.xx + */ + +#ifndef __UISQUEUE_H__ +#define __UISQUEUE_H__ + +#include "linux/version.h" +#include "iochannel.h" +#include "uniklog.h" +#include +#include + +#include "controlvmchannel.h" +#include "controlvmcompletionstatus.h" + +struct uisqueue_info { + + pCHANNEL_HEADER chan; + /* channel containing queues in which scsi commands & + * responses are queued + */ + U64 packets_sent; + U64 packets_received; + U64 interrupts_sent; + U64 interrupts_received; + U64 max_not_empty_cnt; + U64 total_wakeup_cnt; + U64 non_empty_wakeup_cnt; + + struct { + SIGNAL_QUEUE_HEADER Reserved1; /* */ + SIGNAL_QUEUE_HEADER Reserved2; /* */ + } safe_uis_queue; + unsigned int (*send_int_if_needed)(struct uisqueue_info *info, + unsigned int whichcqueue, + unsigned char issueInterruptIfEmpty, + U64 interruptHandle, + unsigned char io_termination); +}; + +/* uisqueue_put_cmdrsp_with_lock_client queues a commmand or response + * to the specified queue, at the tail if the queue is full but + * oktowait == 0, then it return 0 indicating failure. otherwise it + * wait for the queue to become non-full. If command is queued, return + * 1 for success. + */ +#define DONT_ISSUE_INTERRUPT 0 +#define ISSUE_INTERRUPT 1 + +#define DONT_WAIT 0 +#define OK_TO_WAIT 1 +#define UISLIB_LOCK_PREFIX \ + ".section .smp_locks,\"a\"\n" \ + _ASM_ALIGN "\n" \ + _ASM_PTR "661f\n" /* address */ \ + ".previous\n" \ + "661:\n\tlock; " + +unsigned long long uisqueue_InterlockedOr(volatile unsigned long long *Target, + unsigned long long Set); +unsigned long long uisqueue_InterlockedAnd(volatile unsigned long long *Target, + unsigned long long Set); + +unsigned int uisqueue_send_int_if_needed(struct uisqueue_info *pqueueinfo, + unsigned int whichqueue, + unsigned char issueInterruptIfEmpty, + U64 interruptHandle, + unsigned char io_termination); + +int uisqueue_put_cmdrsp_with_lock_client(struct uisqueue_info *queueinfo, + struct uiscmdrsp *cmdrsp, + unsigned int queue, + void *insertlock, + unsigned char issueInterruptIfEmpty, + U64 interruptHandle, + char oktowait, + U8 *channelId); + +/* uisqueue_get_cmdrsp gets the cmdrsp entry at the head of the queue + * and copies it to the area pointed by cmdrsp param. + * returns 0 if queue is empty, 1 otherwise + */ +int + +uisqueue_get_cmdrsp(struct uisqueue_info *queueinfo, void *cmdrsp, + unsigned int queue); + +#define MAX_NAME_SIZE_UISQUEUE 64 + +struct extport_info { + U8 valid:1; + /* if 1, indicates this extport slot is occupied + * if 0, indicates that extport slot is unoccupied */ + + U32 num_devs_using; + /* When extport is added, this is set to 0. For exports + * located in NETWORK switches: + * Each time a VNIC, i.e., intport, is added to the switch this + * is used to assign a pref_pnic for the VNIC and when assigned + * to a VNIC this counter is incremented. When a VNIC is + * deleted, the extport corresponding to the VNIC's pref_pnic + * is located and its num_devs_using is decremented. For VNICs, + * num_devs_using is basically used to load-balance transmit + * traffic from VNICs. + */ + + struct switch_info *swtch; + struct PciId pci_id; + char name[MAX_NAME_SIZE_UISQUEUE]; + union { + struct vhba_wwnn wwnn; + unsigned char macaddr[MAX_MACADDR_LEN]; + }; +}; + +struct device_info { + void *chanptr; + U64 channelAddr; + U64 channelBytes; + GUID channelTypeGuid; + GUID devInstGuid; + struct InterruptInfo intr; + struct switch_info *swtch; + char devid[30]; /* "vbus:dev" */ + U16 polling; + struct semaphore interrupt_callback_lock; + U32 busNo; + U32 devNo; + int (*interrupt)(void *); + void *interrupt_context; + void *private_data; + struct list_head list_polling_device_channels; + unsigned long long moved_to_tail_cnt; + unsigned long long first_busy_cnt; + unsigned long long last_on_list_cnt; +}; + +typedef enum { + RECOVERY_LAN = 1, + IB_LAN = 2 +} SWITCH_TYPE; + +struct bus_info { + U32 busNo, deviceCount; + struct device_info **device; + U64 guestHandle, recvBusInterruptHandle; + GUID busInstGuid; + ULTRA_VBUS_CHANNEL_PROTOCOL *pBusChannel; + int busChannelBytes; + struct proc_dir_entry *proc_dir; /* proc/uislib/vbus/ */ + struct proc_dir_entry *proc_info; /* proc/uislib/vbus//info */ + char name[25]; + char partitionName[99]; + struct bus_info *next; + U8 localVnic; /* 1 if local vnic created internally + * by IOVM; 0 otherwise... */ +}; + +#define DEDICATED_SWITCH(pSwitch) ((pSwitch->extPortCount == 1) && \ + (pSwitch->intPortCount == 1)) + +struct sn_list_entry { + struct uisscsi_dest pdest; /* scsi bus, target, lun for + * phys disk */ + U8 sernum[MAX_SERIAL_NUM]; /* serial num of physical + * disk.. The length is always + * MAX_SERIAL_NUM, padded with + * spaces */ + struct sn_list_entry *next; +}; + +struct networkPolicy { + U32 promiscuous:1; + U32 macassign:1; + U32 peerforwarding:1; + U32 nonotify:1; + U32 standby:1; + U32 callhome:2; + char ip_addr[30]; +}; + +/* + * IO messages sent to UisnicControlChanFunc & UissdControlChanFunc by + * code that processes the ControlVm channel messages. + */ + + +typedef enum { + IOPART_ADD_VNIC, + IOPART_DEL_VNIC, + IOPART_DEL_ALL_VNICS, + IOPART_ADD_VHBA, + IOPART_ADD_VDISK, + IOPART_DEL_VHBA, + IOPART_DEL_VDISK, + IOPART_DEL_ALL_VDISKS_FOR_VHBA, + IOPART_DEL_ALL_VHBAS, + IOPART_ATTACH_PHBA, + IOPART_DETACH_PHBA, /* 10 */ + IOPART_ATTACH_PNIC, + IOPART_DETACH_PNIC, + IOPART_DETACH_VHBA, + IOPART_DETACH_VNIC, + IOPART_PAUSE_VDISK, + IOPART_RESUME_VDISK, + IOPART_ADD_DEVICE, /* add generic device */ + IOPART_DEL_DEVICE, /* del generic device */ +} IOPART_MSG_TYPE; + +struct add_virt_iopart { + void *chanptr; /* pointer to data channel */ + U64 guestHandle; /* used to convert guest physical + * address to real physical address + * for DMA, for ex. */ + U64 recvBusInterruptHandle; /* used to register to receive + * bus level interrupts. */ + struct InterruptInfo intr; /* contains recv & send + * interrupt info */ + /* recvInterruptHandle is used to register to receive + * interrupts on the data channel. Used by GuestLinux/Windows + * IO drivers to connect to interrupt. sendInterruptHandle is + * used by IOPart drivers as parameter to + * Issue_VMCALL_IO_QUEUE_TRANSITION to interrupt thread in + * guest linux/windows IO drivers when data channel queue for + * vhba/vnic goes from EMPTY to NON-EMPTY. */ + struct switch_info *swtch; /* pointer to the virtual + * switch to which the vnic is + * connected */ + + U8 useG2GCopy; /* Used to determine if a virtual HBA + * needs to use G2G copy. */ + U8 Filler[7]; + + U32 busNo; + U32 devNo; + char *params; + ulong params_bytes; + +}; + +struct add_vdisk_iopart { + void *chanptr; /* pointer to data channel */ + int implicit; + struct uisscsi_dest vdest; /* scsi bus, target, lun for virt disk */ + struct uisscsi_dest pdest; /* scsi bus, target, lun for phys disk */ + U8 sernum[MAX_SERIAL_NUM]; /* serial num of physical disk */ + U32 serlen; /* length of serial num */ + U32 busNo; + U32 devNo; +}; + +struct del_vdisk_iopart { + void *chanptr; /* pointer to data channel */ + struct uisscsi_dest vdest; /* scsi bus, target, lun for virt disk */ + U32 busNo; + U32 devNo; +}; + +struct del_virt_iopart { + void *chanptr; /* pointer to data channel */ + U32 busNo; + U32 devNo; +}; + +struct det_virt_iopart { /* detach internal port */ + void *chanptr; /* pointer to data channel */ + struct switch_info *swtch; +}; + +struct paures_vdisk_iopart { + void *chanptr; /* pointer to data channel */ + struct uisscsi_dest vdest; /* scsi bus, target, lun for virt disk */ +}; + +struct add_switch_iopart { /* add switch */ + struct switch_info *swtch; + char *params; + ulong params_bytes; +}; + +struct del_switch_iopart { /* destroy switch */ + struct switch_info *swtch; +}; + +struct io_msgs { + + IOPART_MSG_TYPE msgtype; + + /* additional params needed by some messages */ + union { + struct add_virt_iopart add_vhba; + struct add_virt_iopart add_vnic; + struct add_vdisk_iopart add_vdisk; + struct del_virt_iopart del_vhba; + struct del_virt_iopart del_vnic; + struct det_virt_iopart det_vhba; + struct det_virt_iopart det_vnic; + struct del_vdisk_iopart del_vdisk; + struct del_virt_iopart del_all_vdisks_for_vhba; + struct add_virt_iopart add_device; + struct del_virt_iopart del_device; + struct det_virt_iopart det_intport; + struct add_switch_iopart add_switch; + struct del_switch_iopart del_switch; + struct extport_info *extPort; /* for attach or detach + * pnic/generic delete all + * vhbas/allvnics need no + * parameters */ + struct paures_vdisk_iopart paures_vdisk; + }; +}; + +/* +* Guest messages sent to VirtControlChanFunc by code that processes +* the ControlVm channel messages. +*/ + +typedef enum { + GUEST_ADD_VBUS, + GUEST_ADD_VHBA, + GUEST_ADD_VNIC, + GUEST_DEL_VBUS, + GUEST_DEL_VHBA, + GUEST_DEL_VNIC, + GUEST_DEL_ALL_VHBAS, + GUEST_DEL_ALL_VNICS, + GUEST_DEL_ALL_VBUSES, /* deletes all vhbas & vnics on all + * buses and deletes all buses */ + GUEST_PAUSE_VHBA, + GUEST_PAUSE_VNIC, + GUEST_RESUME_VHBA, + GUEST_RESUME_VNIC +} GUESTPART_MSG_TYPE; + +struct add_vbus_guestpart { + void *chanptr; /* pointer to data channel for bus - + * NOT YET USED */ + U32 busNo; /* bus number to be created/deleted */ + U32 deviceCount; /* max num of devices on bus */ + GUID busTypeGuid; /* indicates type of bus */ + GUID busInstGuid; /* instance guid for device */ +}; + +struct del_vbus_guestpart { + U32 busNo; /* bus number to be deleted */ + /* once we start using the bus's channel, add can dump busNo + * into the channel header and then delete will need only one + * parameter, chanptr. */ +}; + +struct add_virt_guestpart { + void *chanptr; /* pointer to data channel */ + U32 busNo; /* bus number for the operation */ + U32 deviceNo; /* number of device on the bus */ + GUID devInstGuid; /* instance guid for device */ + struct InterruptInfo intr; /* recv/send interrupt info */ + /* recvInterruptHandle contains info needed in order to + * register to receive interrupts on the data channel. + * sendInterruptHandle contains handle which is provided to + * monitor VMCALL that will cause an interrupt to be generated + * for the other end. + */ +}; + +struct pause_virt_guestpart { + void *chanptr; /* pointer to data channel */ +}; + +struct resume_virt_guestpart { + void *chanptr; /* pointer to data channel */ +}; + +struct del_virt_guestpart { + void *chanptr; /* pointer to data channel */ +}; + +struct init_chipset_guestpart { + U32 busCount; /* indicates the max number of busses */ + U32 switchCount; /* indicates the max number of switches */ +}; + +struct guest_msgs { + + GUESTPART_MSG_TYPE msgtype; + + /* additional params needed by messages */ + union { + struct add_vbus_guestpart add_vbus; + struct add_virt_guestpart add_vhba; + struct add_virt_guestpart add_vnic; + struct pause_virt_guestpart pause_vhba; + struct pause_virt_guestpart pause_vnic; + struct resume_virt_guestpart resume_vhba; + struct resume_virt_guestpart resume_vnic; + struct del_vbus_guestpart del_vbus; + struct del_virt_guestpart del_vhba; + struct del_virt_guestpart del_vnic; + struct del_vbus_guestpart del_all_vhbas; + struct del_vbus_guestpart del_all_vnics; + /* del_all_vbuses needs no parameters */ + }; + struct init_chipset_guestpart init_chipset; + +}; + +#ifndef __xg +#define __xg(x) ((volatile long *)(x)) +#endif + +/* +* Below code is a copy of Linux kernel's cmpxchg function located at +* this place +* http://tcsxeon:8080/source/xref/00trunk-AppOS-linux/include/asm-x86/cmpxchg_64.h#84 +* Reason for creating our own version of cmpxchg along with +* UISLIB_LOCK_PREFIX is to make the operation atomic even for non SMP +* guests. +*/ + +static inline unsigned long +uislibcmpxchg64(volatile void *ptr, unsigned long old, unsigned long new, + int size) +{ + unsigned long prev; + switch (size) { + case 1: + __asm__ __volatile__(UISLIB_LOCK_PREFIX "cmpxchgb %b1,%2":"=a"(prev) + : "q"(new), "m"(*__xg(ptr)), + "0"(old) + : "memory"); + return prev; + case 2: + __asm__ __volatile__(UISLIB_LOCK_PREFIX "cmpxchgw %w1,%2":"=a"(prev) + : "r"(new), "m"(*__xg(ptr)), + "0"(old) + : "memory"); + return prev; + case 4: + __asm__ __volatile__(UISLIB_LOCK_PREFIX "cmpxchgl %k1,%2":"=a"(prev) + : "r"(new), "m"(*__xg(ptr)), + "0"(old) + : "memory"); + return prev; + case 8: + __asm__ __volatile__(UISLIB_LOCK_PREFIX "cmpxchgq %1,%2":"=a"(prev) + : "r"(new), "m"(*__xg(ptr)), + "0"(old) + : "memory"); + return prev; + } + return old; +} + +#endif /* __UISQUEUE_H__ */ diff --git a/drivers/staging/unisys/include/uisthread.h b/drivers/staging/unisys/include/uisthread.h new file mode 100644 index 000000000000..2b1fba759098 --- /dev/null +++ b/drivers/staging/unisys/include/uisthread.h @@ -0,0 +1,46 @@ +/* uisthread.h + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/*****************************************************************************/ +/* Unisys thread utilities header */ +/*****************************************************************************/ + + +#ifndef __UISTHREAD_H__ +#define __UISTHREAD_H__ + + +#include "linux/completion.h" + +struct uisthread_info { + struct task_struct *task; + int id; + int should_stop; + struct completion has_stopped; +}; + + +/* returns 0 for failure, 1 for success */ +int uisthread_start( + struct uisthread_info *thrinfo, + int (*threadfn)(void *), + void *thrcontext, + char *name); + +void uisthread_stop(struct uisthread_info *thrinfo); + +#endif /* __UISTHREAD_H__ */ diff --git a/drivers/staging/unisys/include/uisutils.h b/drivers/staging/unisys/include/uisutils.h new file mode 100644 index 000000000000..9fee353686c8 --- /dev/null +++ b/drivers/staging/unisys/include/uisutils.h @@ -0,0 +1,359 @@ +/* uisutils.h + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* + * Unisys Virtual HBA utilities header + */ + +#ifndef __UISUTILS__H__ +#define __UISUTILS__H__ +#include +#include +#include +#include + +#include "vmcallinterface.h" +#include "channel.h" +#include "uisthread.h" +#include "uisqueue.h" +#include "diagnostics/appos_subsystems.h" +#include "vbusdeviceinfo.h" +#include + +/* This is the MAGIC number stuffed by virthba in host->this_id. Used to + * identify virtual hbas. + */ +#define UIS_MAGIC_VHBA 707 + +/* global function pointers that act as callback functions into + * uisnicmod, uissdmod, and virtpcimod + */ +extern int (*UisnicControlChanFunc)(struct io_msgs *); +extern int (*UissdControlChanFunc)(struct io_msgs *); +extern int (*VirtControlChanFunc)(struct guest_msgs *); + +/* Return values of above callback functions: */ +#define CCF_ERROR 0 /* completed and failed */ +#define CCF_OK 1 /* completed successfully */ +#define CCF_PENDING 2 /* operation still pending */ +extern atomic_t UisUtils_Registered_Services; + +typedef unsigned int MACARRAY[MAX_MACADDR_LEN]; +typedef struct ReqHandlerInfo_struct { + GUID switchTypeGuid; + int (*controlfunc)(struct io_msgs *); + unsigned long min_channel_bytes; + int (*Server_Channel_Ok)(unsigned long channelBytes); + int (*Server_Channel_Init) + (void *x, unsigned char *clientStr, U32 clientStrLen, U64 bytes); + char switch_type_name[99]; + struct list_head list_link; /* links into ReqHandlerInfo_list */ +} ReqHandlerInfo_t; + +ReqHandlerInfo_t *ReqHandlerAdd(GUID switchTypeGuid, + const char *switch_type_name, + int (*controlfunc)(struct io_msgs *), + unsigned long min_channel_bytes, + int (*Server_Channel_Ok)(unsigned long + channelBytes), + int (*Server_Channel_Init) + (void *x, unsigned char *clientStr, + U32 clientStrLen, U64 bytes)); +ReqHandlerInfo_t *ReqHandlerFind(GUID switchTypeGuid); +int ReqHandlerDel(GUID switchTypeGuid); + +#define uislib_ioremap_cache(addr, size) \ + dbg_ioremap_cache(addr, size, __FILE__, __LINE__) + +static inline void * +dbg_ioremap_cache(U64 addr, unsigned long size, char *file, int line) +{ + void *new; + new = ioremap_cache(addr, size); + return new; +} + +#define uislib_ioremap(addr, size) dbg_ioremap(addr, size, __FILE__, __LINE__) + +static inline void * +dbg_ioremap(U64 addr, unsigned long size, char *file, int line) +{ + void *new; + new = ioremap(addr, size); + return new; +} + +#define uislib_iounmap(addr) dbg_iounmap(addr, __FILE__, __LINE__) + +static inline void +dbg_iounmap(void *addr, char *file, int line) +{ + iounmap(addr); +} + +#define PROC_READ_BUFFER_SIZE 131072 /* size of the buffer to allocate to + * hold all of /proc/XXX/info */ +int util_add_proc_line_ex(int *total, char **buffer, int *buffer_remaining, + char *format, ...); + +int uisctrl_register_req_handler(int type, void *fptr, + ULTRA_VBUS_DEVICEINFO *chipset_DriverInfo); +int uisctrl_register_req_handler_ex(GUID switchTypeGuid, + const char *switch_type_name, + int (*fptr)(struct io_msgs *), + unsigned long min_channel_bytes, + int (*Server_Channel_Ok)(unsigned long + channelBytes), + int (*Server_Channel_Init) + (void *x, unsigned char *clientStr, + U32 clientStrLen, U64 bytes), + ULTRA_VBUS_DEVICEINFO *chipset_DriverInfo); + +int uisctrl_unregister_req_handler_ex(GUID switchTypeGuid); +unsigned char *util_map_virt(struct phys_info *sg); +void util_unmap_virt(struct phys_info *sg); +unsigned char *util_map_virt_atomic(struct phys_info *sg); +void util_unmap_virt_atomic(void *buf); +int uislib_server_inject_add_vnic(U32 switchNo, U32 BusNo, U32 numIntPorts, + U32 numExtPorts, MACARRAY pmac[], + pCHANNEL_HEADER **chan); +void uislib_server_inject_del_vnic(U32 switchNo, U32 busNo, U32 numIntPorts, + U32 numExtPorts); +int uislib_client_inject_add_bus(U32 busNo, GUID instGuid, + U64 channelAddr, ulong nChannelBytes); +int uislib_client_inject_del_bus(U32 busNo); + +int uislib_client_inject_add_vhba(U32 busNo, U32 devNo, + U64 phys_chan_addr, U32 chan_bytes, + int is_test_addr, GUID instGuid, + struct InterruptInfo *intr); +int uislib_client_inject_pause_vhba(U32 busNo, U32 devNo); +int uislib_client_inject_resume_vhba(U32 busNo, U32 devNo); +int uislib_client_inject_del_vhba(U32 busNo, U32 devNo); +int uislib_client_inject_add_vnic(U32 busNo, U32 devNo, + U64 phys_chan_addr, U32 chan_bytes, + int is_test_addr, GUID instGuid, + struct InterruptInfo *intr); +int uislib_client_inject_pause_vnic(U32 busNo, U32 devNo); +int uislib_client_inject_resume_vnic(U32 busNo, U32 devNo); +int uislib_client_inject_del_vnic(U32 busNo, U32 devNo); +#ifdef STORAGE_CHANNEL +U64 uislib_storage_channel(int client_id); +#endif +int uislib_get_owned_pdest(struct uisscsi_dest *pdest); + +int uislib_send_event(CONTROLVM_ID id, CONTROLVM_MESSAGE_PACKET *event); + +/* structure used by vhba & vnic to keep track of queue & thread info */ +struct chaninfo { + struct uisqueue_info *queueinfo; + /* this specifies the queue structures for a channel */ + /* ALLOCATED BY THE OTHER END - WE JUST GET A POINTER TO THE MEMORY */ + spinlock_t insertlock; + /* currently used only in virtnic when sending data to uisnic */ + /* to synchronize the inserts into the signal queue */ + struct uisthread_info threadinfo; + /* this specifies the thread structures used by the thread that */ + /* handles this channel */ +}; + +/* this is the wait code for all the threads - it is used to get +* something from a queue choices: wait_for_completion_interruptible, +* _timeout, interruptible_timeout +*/ +#define UIS_THREAD_WAIT_MSEC(x) { \ + set_current_state(TASK_INTERRUPTIBLE); \ + schedule_timeout(msecs_to_jiffies(x)); \ +} +#define UIS_THREAD_WAIT_USEC(x) { \ + set_current_state(TASK_INTERRUPTIBLE); \ + schedule_timeout(usecs_to_jiffies(x)); \ +} +#define UIS_THREAD_WAIT UIS_THREAD_WAIT_MSEC(5) +#define UIS_THREAD_WAIT_SEC(x) { \ + set_current_state(TASK_INTERRUPTIBLE); \ + schedule_timeout((x)*HZ); \ +} + +#define ALLOC_CMDRSP(cmdrsp) { \ + cmdrsp = kmalloc(SIZEOF_CMDRSP, GFP_ATOMIC); \ + if (cmdrsp != NULL) { \ + memset(cmdrsp, 0, SIZEOF_CMDRSP); \ + } \ +} + +/* This is a hack until we fix IOVM to initialize the channel header + * correctly at DEVICE_CREATE time, INSTEAD OF waiting until + * DEVICE_CONFIGURE time. + */ +#define WAIT_FOR_VALID_GUID(guid) \ + do { \ + while (memcmp(&guid, &Guid0, sizeof(Guid0)) == 0) { \ + LOGERR("Waiting for non-0 GUID (why???)...\n"); \ + UIS_THREAD_WAIT_SEC(5); \ + } \ + LOGERR("OK... GUID is non-0 now\n"); \ + } while (0) + +/* CopyFragsInfoFromSkb returns the number of entries added to frags array + * Returns -1 on failure. + */ +unsigned int util_copy_fragsinfo_from_skb(unsigned char *calling_ctx, + void *skb_in, unsigned int firstfraglen, + unsigned int frags_max, struct phys_info frags[]); + +static inline unsigned int +Issue_VMCALL_IO_CONTROLVM_ADDR(U64 *ControlAddress, U32 *ControlBytes) +{ + 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)) { + *ControlAddress = params.ChannelAddress; + *ControlBytes = params.ChannelBytes; + } + return result; +} + +static inline unsigned int Issue_VMCALL_IO_DIAG_ADDR(U64 *DiagChannelAddress) +{ + VMCALL_IO_DIAG_ADDR_PARAMS params; + int result = VMCALL_SUCCESS; + U64 physaddr; + + physaddr = virt_to_phys(¶ms); + ISSUE_IO_VMCALL(VMCALL_IO_DIAG_ADDR, physaddr, result); + if (VMCALL_SUCCESSFUL(result)) + *DiagChannelAddress = params.ChannelAddress; + return result; +} + +static inline unsigned int +Issue_VMCALL_IO_VISORSERIAL_ADDR(U64 *DiagChannelAddress) +{ + VMCALL_IO_VISORSERIAL_ADDR_PARAMS params; + int result = VMCALL_SUCCESS; + U64 physaddr; + + physaddr = virt_to_phys(¶ms); + ISSUE_IO_VMCALL(VMCALL_IO_VISORSERIAL_ADDR, physaddr, result); + if (VMCALL_SUCCESSFUL(result)) + *DiagChannelAddress = params.ChannelAddress; + return result; +} + +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 S64 Issue_VMCALL_MEASUREMENT_DO_NOTHING(void) +{ + U64 result = VMCALL_SUCCESS; + U64 physaddr = 0; + + ISSUE_IO_VMCALL(VMCALL_MEASUREMENT_DO_NOTHING, physaddr, result); + return result; +} + +struct log_info_t { + volatile unsigned long long last_cycles; + unsigned long long delta_sum[64]; + unsigned long long delta_cnt[64]; + unsigned long long max_delta[64]; + unsigned long long min_delta[64]; +}; + +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 inline unsigned int +Issue_VMCALL_CHANNEL_MISMATCH(const char *ChannelName, + const char *ItemName, + U32 SourceLineNumber, const char *path_n_fn) +{ + VMCALL_CHANNEL_VERSION_MISMATCH_PARAMS params; + int result = VMCALL_SUCCESS; + U64 physaddr; + char *last_slash = NULL; + + strncpy(params.ChannelName, ChannelName, + lengthof(VMCALL_CHANNEL_VERSION_MISMATCH_PARAMS, ChannelName)); + strncpy(params.ItemName, ItemName, + lengthof(VMCALL_CHANNEL_VERSION_MISMATCH_PARAMS, ItemName)); + params.SourceLineNumber = SourceLineNumber; + + last_slash = strrchr(path_n_fn, '/'); + if (last_slash != NULL) { + last_slash++; + strncpy(params.SourceFileName, last_slash, + lengthof(VMCALL_CHANNEL_VERSION_MISMATCH_PARAMS, + SourceFileName)); + } else + strncpy(params.SourceFileName, + "Cannot determine source filename", + lengthof(VMCALL_CHANNEL_VERSION_MISMATCH_PARAMS, + SourceFileName)); + + physaddr = virt_to_phys(¶ms); + ISSUE_IO_VMCALL(VMCALL_CHANNEL_VERSION_MISMATCH, physaddr, result); + return result; +} + +static inline unsigned int Issue_VMCALL_FATAL_BYE_BYE(void) +{ + int result = VMCALL_SUCCESS; + U64 physaddr = 0; + + ISSUE_IO_VMCALL(VMCALL_GENERIC_SURRENDER_QUANTUM_FOREVER, physaddr, + result); + return result; +} + +#define UIS_DAEMONIZE(nam) +void *uislib_malloc(size_t siz, gfp_t gfp, U8 contiguous, char *fn, int ln); +#define UISMALLOC(siz, gfp) uislib_malloc(siz, gfp, 1, __FILE__, __LINE__) +#define UISVMALLOC(siz) uislib_malloc(siz, 0, 0, __FILE__, __LINE__) +void uislib_free(void *p, size_t siz, U8 contiguous, char *fn, int ln); +#define UISFREE(p, siz) uislib_free(p, siz, 1, __FILE__, __LINE__) +#define UISVFREE(p, siz) uislib_free(p, siz, 0, __FILE__, __LINE__) +void *uislib_cache_alloc(struct kmem_cache *cur_pool, char *fn, int ln); +#define UISCACHEALLOC(cur_pool) uislib_cache_alloc(cur_pool, __FILE__, __LINE__) +void uislib_cache_free(struct kmem_cache *cur_pool, void *p, char *fn, int ln); +#define UISCACHEFREE(cur_pool, p) \ + uislib_cache_free(cur_pool, p, __FILE__, __LINE__) + +void uislib_enable_channel_interrupts(U32 busNo, U32 devNo, + int (*interrupt)(void *), + void *interrupt_context); +void uislib_disable_channel_interrupts(U32 busNo, U32 devNo); +void uislib_force_channel_interrupt(U32 busNo, U32 devNo); + +#endif /* __UISUTILS__H__ */ diff --git a/drivers/staging/unisys/include/vbushelper.h b/drivers/staging/unisys/include/vbushelper.h new file mode 100644 index 000000000000..93e35f039ded --- /dev/null +++ b/drivers/staging/unisys/include/vbushelper.h @@ -0,0 +1,47 @@ +/* vbushelper.h + * + * Copyright © 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__ + +#include "vbusdeviceinfo.h" + +/* TARGET_HOSTNAME specified as -DTARGET_HOSTNAME=\"thename\" on the + * command line */ + +#define TARGET_HOSTNAME "linuxguest" + +static inline void +BusDeviceInfo_Init(ULTRA_VBUS_DEVICEINFO *pBusDeviceInfo, + const char *deviceType, const char *driverName, + const char *ver, const char *verTag, + const char *buildDate, const char *buildTime) +{ + memset(pBusDeviceInfo, 0, sizeof(ULTRA_VBUS_DEVICEINFO)); + snprintf(pBusDeviceInfo->devType, sizeof(pBusDeviceInfo->devType), + "%s", (deviceType) ? deviceType : "unknownType"); + snprintf(pBusDeviceInfo->drvName, sizeof(pBusDeviceInfo->drvName), + "%s", (driverName) ? driverName : "unknownDriver"); + snprintf(pBusDeviceInfo->infoStrings, + sizeof(pBusDeviceInfo->infoStrings), "%s\t%s\t%s %s\t%s", + (ver) ? ver : "unknownVer", + (verTag) ? verTag : "unknownVerTag", + (buildDate) ? buildDate : "noBuildDate", + (buildTime) ? buildTime : "nobuildTime", TARGET_HOSTNAME); +} + +#endif diff --git a/drivers/staging/unisys/visorchipset/Kconfig b/drivers/staging/unisys/visorchipset/Kconfig new file mode 100644 index 000000000000..7ca2fbca9d57 --- /dev/null +++ b/drivers/staging/unisys/visorchipset/Kconfig @@ -0,0 +1,10 @@ +# +# Unisys visorchipset configuration +# + +config UNISYS_VISORCHIPSET + tristate "Unisys visorchipset driver" + depends on UNISYSSPAR && UNISYS_VISORUTIL && UNISYS_VISORCHANNEL + ---help--- + If you say Y here, you will enable the Unisys visorchipset driver. + diff --git a/drivers/staging/unisys/visorchipset/Makefile b/drivers/staging/unisys/visorchipset/Makefile new file mode 100644 index 000000000000..f5e8650e1b0e --- /dev/null +++ b/drivers/staging/unisys/visorchipset/Makefile @@ -0,0 +1,18 @@ +# +# Makefile for Unisys visorchipset +# + +obj-$(CONFIG_UNISYS_VISORCHIPSET) += visorchipset.o + +visorchipset-y := visorchipset_main.o controlvm_direct.o file.o filexfer.o \ + parser.o + +ccflags-y += -Idrivers/staging/unisys/include +ccflags-y += -Idrivers/staging/unisys/uislib +ccflags-y += -Idrivers/staging/unisys/visorchannel +ccflags-y += -Idrivers/staging/unisys/common-spar/include +ccflags-y += -Idrivers/staging/unisys/common-spar/include/channels +ccflags-y += -Idrivers/staging/unisys/visorutil +ccflags-y += -Iinclude/generated +ccflags-y += -DCONFIG_SPAR_GUEST -DGUESTDRIVERBUILD -DNOAUTOVERSION + diff --git a/drivers/staging/unisys/visorchipset/controlvm.h b/drivers/staging/unisys/visorchipset/controlvm.h new file mode 100644 index 000000000000..873fa12dfe6f --- /dev/null +++ b/drivers/staging/unisys/visorchipset/controlvm.h @@ -0,0 +1,27 @@ +/* controlvm.h + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __CONTROLVM_H__ +#define __CONTROLVM_H__ + +#include "timskmod.h" + +int controlvm_init(void); +void controlvm_deinit(void); +HOSTADDRESS controlvm_get_channel_address(void); + +#endif diff --git a/drivers/staging/unisys/visorchipset/controlvm_direct.c b/drivers/staging/unisys/visorchipset/controlvm_direct.c new file mode 100644 index 000000000000..7fbc5892bcbc --- /dev/null +++ b/drivers/staging/unisys/visorchipset/controlvm_direct.c @@ -0,0 +1,61 @@ +/* controlvm_direct.c + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* This is a controlvm-related code that is dependent upon firmware running + * on a virtual partition. + */ + +#include "globals.h" +#include "uisutils.h" +#define CURRENT_FILE_PC VISOR_CHIPSET_PC_controlvm_direct_c + + +/* We can fill in this code when we learn how to make vmcalls... */ + + + +int controlvm_init(void) +{ + return 0; +} + + + +void controlvm_deinit(void) +{ +} + + + +HOSTADDRESS controlvm_get_channel_address(void) +{ + static BOOL warned = FALSE; + U64 addr = 0; + + U32 size = 0; + + if (!VMCALL_SUCCESSFUL(Issue_VMCALL_IO_CONTROLVM_ADDR(&addr, &size))) { + if (!warned) { + ERRDRV("%s - vmcall to determine controlvm channel addr failed", + __func__); + warned = TRUE; + } + return 0; + } + INFODRV("controlvm addr=%Lx", addr); + return addr; +} diff --git a/drivers/staging/unisys/visorchipset/file.c b/drivers/staging/unisys/visorchipset/file.c new file mode 100644 index 000000000000..b0d28a2b5231 --- /dev/null +++ b/drivers/staging/unisys/visorchipset/file.c @@ -0,0 +1,223 @@ +/* file.c + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* This contains the implementation that allows a usermode program to + * communicate with the visorchipset driver using a device/file interface. + */ + +#include "globals.h" +#include "visorchannel.h" +#include +#include +#include "uisutils.h" + +#define CURRENT_FILE_PC VISOR_CHIPSET_PC_file_c + +static struct cdev Cdev; +static VISORCHANNEL **PControlVm_channel; +static dev_t MajorDev = -1; /**< indicates major num for device */ +static BOOL Registered = FALSE; + +static int visorchipset_open(struct inode *inode, struct file *file); +static int visorchipset_release(struct inode *inode, struct file *file); +static int visorchipset_mmap(struct file *file, struct vm_area_struct *vma); +#ifdef HAVE_UNLOCKED_IOCTL +long visorchipset_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +#else +int visorchipset_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +#endif + +static const struct file_operations visorchipset_fops = { + .owner = THIS_MODULE, + .open = visorchipset_open, + .read = NULL, + .write = NULL, +#ifdef HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = visorchipset_ioctl, +#else + .ioctl = visorchipset_ioctl, +#endif + .release = visorchipset_release, + .mmap = visorchipset_mmap, +}; + +int +visorchipset_file_init(dev_t majorDev, VISORCHANNEL **pControlVm_channel) +{ + int rc = -1; + + PControlVm_channel = pControlVm_channel; + MajorDev = majorDev; + cdev_init(&Cdev, &visorchipset_fops); + Cdev.owner = THIS_MODULE; + if (MAJOR(MajorDev) == 0) { + /* dynamic major device number registration required */ + if (alloc_chrdev_region(&MajorDev, 0, 1, MYDRVNAME) < 0) { + ERRDRV("Unable to allocate+register char device %s", + MYDRVNAME); + RETINT(-1); + } + Registered = TRUE; + INFODRV("New major number %d registered\n", MAJOR(MajorDev)); + } else { + /* static major device number registration required */ + if (register_chrdev_region(MajorDev, 1, MYDRVNAME) < 0) { + ERRDRV("Unable to register char device %s", MYDRVNAME); + RETINT(-1); + } + Registered = TRUE; + INFODRV("Static major number %d registered\n", MAJOR(MajorDev)); + } + if (cdev_add(&Cdev, MKDEV(MAJOR(MajorDev), 0), 1) < 0) + FAIL("failed to create char device", -1); + INFODRV("Registered char device for %s (major=%d)", + MYDRVNAME, MAJOR(MajorDev)); + RETINT(0); +Away: + return rc; +} + +void +visorchipset_file_cleanup(void) +{ + if (Cdev.ops != NULL) + cdev_del(&Cdev); + Cdev.ops = NULL; + if (Registered) { + if (MAJOR(MajorDev) >= 0) { + unregister_chrdev_region(MajorDev, 1); + MajorDev = MKDEV(0, 0); + } + Registered = FALSE; + } +} + +static int +visorchipset_open(struct inode *inode, struct file *file) +{ + unsigned minor_number = iminor(inode); + int rc = -ENODEV; + + DEBUGDRV("%s", __func__); + if (minor_number != 0) + RETINT(-ENODEV); + file->private_data = NULL; + RETINT(0); +Away: + if (rc < 0) + ERRDRV("%s minor=%d failed", __func__, minor_number); + return rc; +} + +static int +visorchipset_release(struct inode *inode, struct file *file) +{ + int rc = -1; + DEBUGDRV("%s", __func__); + RETINT(0); +Away: + return rc; +} + +static int +visorchipset_mmap(struct file *file, struct vm_area_struct *vma) +{ + ulong physAddr = 0; + ulong offset = vma->vm_pgoff << PAGE_SHIFT; + GUEST_PHYSICAL_ADDRESS addr = 0; + + /* sv_enable_dfp(); */ + DEBUGDRV("%s", __func__); + if (offset & (PAGE_SIZE - 1)) { + ERRDRV("%s virtual address NOT page-aligned!", __func__); + return -ENXIO; /* need aligned offsets */ + } + switch (offset) { + case VISORCHIPSET_MMAP_CONTROLCHANOFFSET: + vma->vm_flags |= VM_IO; + if (*PControlVm_channel == NULL) { + ERRDRV("%s no controlvm channel yet", __func__); + return -ENXIO; + } + visorchannel_read(*PControlVm_channel, + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, + gpControlChannel), &addr, + sizeof(addr)); + if (addr == 0) { + ERRDRV("%s control channel address is 0", __func__); + return -ENXIO; + } + physAddr = (ulong) (addr); + DEBUGDRV("mapping physical address = 0x%lx", physAddr); + if (remap_pfn_range(vma, vma->vm_start, + physAddr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + /*pgprot_noncached */ + (vma->vm_page_prot))) { + ERRDRV("%s remap_pfn_range failed", __func__); + return -EAGAIN; + } + break; + default: + return -ENOSYS; + } + DEBUGDRV("%s success!", __func__); + return 0; +} + +#ifdef HAVE_UNLOCKED_IOCTL +long +visorchipset_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +#else +int +visorchipset_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +#endif +{ + int rc = SUCCESS; + S64 adjustment; + S64 vrtc_offset; + DBGINF("entered visorchipset_ioctl, cmd=%d", cmd); + 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))) + RETINT(-EFAULT); + DBGINF("insde visorchipset_ioctl, cmd=%d, vrtc_offset=%lld", + cmd, vrtc_offset); + break; + case VMCALL_UPDATE_PHYSICAL_TIME: + if (copy_from_user + (&adjustment, (void __user *)arg, sizeof(adjustment))) + RETINT(-EFAULT); + DBGINF("insde visorchipset_ioctl, cmd=%d, adjustment=%lld", cmd, + adjustment); + rc = Issue_VMCALL_UPDATE_PHYSICAL_TIME(adjustment); + break; + default: + LOGERR("visorchipset_ioctl received invalid command"); + RETINT(-EFAULT); + break; + } + RETINT(rc); +Away: + DBGINF("exiting %d!", rc); + return rc; +} diff --git a/drivers/staging/unisys/visorchipset/file.h b/drivers/staging/unisys/visorchipset/file.h new file mode 100644 index 000000000000..597282aa9a06 --- /dev/null +++ b/drivers/staging/unisys/visorchipset/file.h @@ -0,0 +1,26 @@ +/* file.h + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __FILE_H__ +#define __FILE_H__ + +#include "globals.h" + +int visorchipset_file_init(dev_t majorDev, VISORCHANNEL **pControlVm_channel); +void visorchipset_file_cleanup(void); + +#endif diff --git a/drivers/staging/unisys/visorchipset/filexfer.c b/drivers/staging/unisys/visorchipset/filexfer.c new file mode 100644 index 000000000000..431cff844a43 --- /dev/null +++ b/drivers/staging/unisys/visorchipset/filexfer.c @@ -0,0 +1,506 @@ +/* filexfer.c + * + * Copyright © 2013 - 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. + */ + +/* Code here-in is the "glue" that connects controlvm messages with the + * sparfilexfer driver, which is used to transfer file contents as payload + * across the controlvm channel. + */ + +#include "globals.h" +#include "controlvm.h" +#include "visorchipset.h" +#include "filexfer.h" + +#ifdef ENABLE_SPARFILEXFER /* sparfilexfer kernel module enabled in build */ +#include "sparfilexfer.h" + +/* Driver-global memory */ +static LIST_HEAD(Request_list); /* list of struct any_request *, via + * req_list memb */ + +/* lock for above pool for allocation of any_request structs, and pool +* name; note that kmem_cache_create requires that we keep the storage +* for the pool name for the life of the pool + */ +static DEFINE_SPINLOCK(Request_list_lock); + +static struct kmem_cache *Request_memory_pool; +static const char Request_memory_pool_name[] = "filexfer_request_pool"; +size_t Caller_req_context_bytes = 0; /* passed to filexfer_constructor() */ + +/* This structure defines a single controlvm GETFILE conversation, which + * consists of a single controlvm request message and 1 or more controlvm + * response messages. + */ +struct getfile_request { + CONTROLVM_MESSAGE_HEADER controlvm_header; + atomic_t buffers_in_use; + GET_CONTIGUOUS_CONTROLVM_PAYLOAD_FUNC get_contiguous_controlvm_payload; + CONTROLVM_RESPOND_WITH_PAYLOAD_FUNC controlvm_respond_with_payload; +}; + +/* This structure defines a single controlvm PUTFILE conversation, which + * consists of a single controlvm request with a filename, and additional + * controlvm messages with file data. + */ +struct putfile_request { + GET_CONTROLVM_FILEDATA_FUNC get_controlvm_filedata; + CONTROLVM_RESPOND_FUNC controlvm_end_putFile; +}; + +/* This structure defines a single file transfer operation, which can either + * be a GETFILE or PUTFILE. + */ +struct any_request { + struct list_head req_list; + ulong2 file_request_number; + ulong2 data_sequence_number; + TRANSMITFILE_DUMP_FUNC dump_func; + BOOL is_get; + union { + struct getfile_request get; + struct putfile_request put; + }; + /* Size of caller_context_data will be + * bytes. I aligned this because I + * am paranoid about what happens when an arbitrary data + * structure with unknown alignment requirements gets copied + * here. I want caller_context_data to be aligned to the + * coarsest possible alignment boundary that could be required + * for any user data structure. + */ + u8 caller_context_data[1] __aligned(sizeof(ulong2); +}; + +/* + * Links the any_request into the global list of allocated requests + * (). + */ +static void +unit_tracking_create(struct list_head *dev_list_link) +{ + unsigned long flags; + spin_lock_irqsave(&Request_list_lock, flags); + list_add(dev_list_link, &Request_list); + spin_unlock_irqrestore(&Request_list_lock, flags); +} + +/* Unlinks a any_request from the global list (). + */ +static void +unit_tracking_destroy(struct list_head *dev_list_link) +{ + unsigned long flags; + spin_lock_irqsave(&Request_list_lock, flags); + list_del(dev_list_link); + spin_unlock_irqrestore(&Request_list_lock, flags); +} + +/* Allocate memory for and return a new any_request struct, and + * link it to the global list of outstanding requests. + */ +static struct any_request * +alloc_request(char *fn, int ln) +{ + struct any_request *req = (struct any_request *) + (visorchipset_cache_alloc(Request_memory_pool, + FALSE, + fn, ln)); + if (!req) + return NULL; + memset(req, 0, sizeof(struct any_request) + Caller_req_context_bytes); + unit_tracking_create(&req->req_list); + return req; +} + +/* Book-end for alloc_request(). + */ +static void +free_request(struct any_request *req, char *fn, int ln) +{ + unit_tracking_destroy(&req->req_list); + visorchipset_cache_free(Request_memory_pool, req, fn, ln); +} + +/* Constructor for filexfer.o. + */ +int +filexfer_constructor(size_t req_context_bytes) +{ + int rc = -1; + + Caller_req_context_bytes = req_context_bytes; + Request_memory_pool = + kmem_cache_create(Request_memory_pool_name, + sizeof(struct any_request) + + Caller_req_context_bytes, + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!Request_memory_pool) { + LOGERR("failed to alloc Request_memory_pool"); + rc = -ENOMEM; + goto Away; + } + rc = 0; +Away: + if (rc < 0) { + if (Request_memory_pool) { + kmem_cache_destroy(Request_memory_pool); + Request_memory_pool = NULL; + } + } + return rc; +} + +/* Destructor for filexfer.o. + */ +void +filexfer_destructor(void) +{ + if (Request_memory_pool) { + kmem_cache_destroy(Request_memory_pool); + Request_memory_pool = NULL; + } +} + +/* This function will obtain an available chunk from the controlvm payload area, + * store the size in bytes of the chunk in , and return a pointer + * to the chunk. The function is passed to the sparfilexfer driver, which calls + * it whenever payload space is required to copy file data into. + */ +static void * +get_empty_bucket_for_getfile_data(void *context, + ulong min_size, ulong max_size, + ulong *actual_size) +{ + void *bucket; + struct any_request *req = (struct any_request *) context; + + if (!req->is_get) { + LOGERR("%s - unexpected call", __func__); + return NULL; + } + bucket = (*req->get.get_contiguous_controlvm_payload) + (min_size, max_size, actual_size); + if (bucket != NULL) { + atomic_inc(&req->get.buffers_in_use); + DBGINF("%s - sent %lu-byte buffer", __func__, *actual_size); + } + return bucket; +} + +/* This function will send a controlvm response with data in the payload + * (whose space was obtained with get_empty_bucket_for_getfile_data). The + * function is passed to the sparfilexfer driver, which calls it whenever it + * wants to send file data back across the controlvm channel. + */ +static int +send_full_getfile_data_bucket(void *context, void *bucket, + ulong bucket_actual_size, ulong bucket_used_size) +{ + struct any_request *req = (struct any_request *) context; + + if (!req->is_get) { + LOGERR("%s - unexpected call", __func__); + return 0; + } + DBGINF("sending buffer for %lu/%lu", + bucket_used_size, bucket_actual_size); + if (!(*req->get.controlvm_respond_with_payload) + (&req->get.controlvm_header, + req->file_request_number, + req->data_sequence_number++, + 0, bucket, bucket_actual_size, bucket_used_size, TRUE)) + atomic_dec(&req->get.buffers_in_use); + return 0; +} + +/* This function will send a controlvm response indicating the end of a + * GETFILE transfer. The function is passed to the sparfilexfer driver. + */ +static void +send_end_of_getfile_data(void *context, int status) +{ + struct any_request *req = (struct any_request *) context; + if (!req->is_get) { + LOGERR("%s - unexpected call", __func__); + return; + } + LOGINF("status=%d", status); + (*req->get.controlvm_respond_with_payload) + (&req->get.controlvm_header, + req->file_request_number, + req->data_sequence_number++, status, NULL, 0, 0, FALSE); + free_request(req, __FILE__, __LINE__); + module_put(THIS_MODULE); +} + +/* This function supplies data for a PUTFILE transfer. + * The function is passed to the sparfilexfer driver. + */ +static int +get_putfile_data(void *context, void *pbuf, size_t bufsize, + BOOL buf_is_userspace, size_t *bytes_transferred) +{ + struct any_request *req = (struct any_request *) context; + if (req->is_get) { + LOGERR("%s - unexpected call", __func__); + return -1; + } + return (*req->put.get_controlvm_filedata) (&req->caller_context_data[0], + pbuf, bufsize, + buf_is_userspace, + bytes_transferred); +} + +/* This function is called to indicate the end of a PUTFILE transfer. + * The function is passed to the sparfilexfer driver. + */ +static void +end_putfile(void *context, int status) +{ + struct any_request *req = (struct any_request *) context; + if (req->is_get) { + LOGERR("%s - unexpected call", __func__); + return; + } + (*req->put.controlvm_end_putFile) (&req->caller_context_data[0], + status); + free_request(req, __FILE__, __LINE__); + module_put(THIS_MODULE); +} + +/* Refer to filexfer.h for description. */ +BOOL +filexfer_getFile(CONTROLVM_MESSAGE_HEADER *msgHdr, + ulong2 file_request_number, + uint uplink_index, + uint disk_index, + char *file_name, + GET_CONTIGUOUS_CONTROLVM_PAYLOAD_FUNC + get_contiguous_controlvm_payload, + CONTROLVM_RESPOND_WITH_PAYLOAD_FUNC + controlvm_respond_with_payload, + TRANSMITFILE_DUMP_FUNC dump_func) +{ + BOOL use_count_up = FALSE; + BOOL failed = TRUE; + struct any_request *req = alloc_request(__FILE__, __LINE__); + + if (!req) { + LOGERR("allocation of any_request failed"); + goto Away; + } + /* We need to increment this module's use count because we're handing + * off pointers to functions within this module to be used by + * another module. + */ + __module_get(THIS_MODULE); + use_count_up = TRUE; + req->is_get = TRUE; + req->file_request_number = file_request_number; + req->data_sequence_number = 0; + req->dump_func = dump_func; + req->get.controlvm_header = *msgHdr; + atomic_set(&req->get.buffers_in_use, 0); + req->get.get_contiguous_controlvm_payload = + get_contiguous_controlvm_payload; + req->get.controlvm_respond_with_payload = + controlvm_respond_with_payload; + if (sparfilexfer_local2remote(req, /* context, passed to + * callback funcs */ + file_name, + file_request_number, + uplink_index, + disk_index, + get_empty_bucket_for_getfile_data, + send_full_getfile_data_bucket, + send_end_of_getfile_data) < 0) { + LOGERR("sparfilexfer_local2remote failed"); + goto Away; + } + failed = FALSE; +Away: + if (failed) { + if (use_count_up) { + module_put(THIS_MODULE); + use_count_up = FALSE; + } + if (req) { + free_request(req, __FILE__, __LINE__); + req = NULL; + } + return FALSE; + } else { + return TRUE; + /* success; send callbacks will be called for responses */ + } +} + +/* Refer to filexfer.h for description. */ +void * +filexfer_putFile(CONTROLVM_MESSAGE_HEADER *msgHdr, + ulong2 file_request_number, + uint uplink_index, + uint disk_index, + char *file_name, + TRANSMITFILE_INIT_CONTEXT_FUNC init_context, + GET_CONTROLVM_FILEDATA_FUNC get_controlvm_filedata, + CONTROLVM_RESPOND_FUNC controlvm_end_putFile, + TRANSMITFILE_DUMP_FUNC dump_func) +{ + BOOL use_count_up = FALSE; + BOOL failed = TRUE; + struct any_request *req = alloc_request(__FILE__, __LINE__); + void *caller_ctx = NULL; + + if (!req) { + LOGERR("allocation of any_request failed"); + goto Away; + } + caller_ctx = (void *) (&(req->caller_context_data[0])); + /* We need to increment this module's use count because we're handing + * off pointers to functions within this module to be used by + * another module. + */ + __module_get(THIS_MODULE); + use_count_up = TRUE; + req->is_get = FALSE; + req->file_request_number = file_request_number; + req->data_sequence_number = 0; + req->dump_func = dump_func; + req->put.get_controlvm_filedata = get_controlvm_filedata; + req->put.controlvm_end_putFile = controlvm_end_putFile; + (*init_context) (caller_ctx, msgHdr, file_request_number); + if (sparfilexfer_remote2local(req, /* context, passed to + * callback funcs */ + file_name, + file_request_number, + uplink_index, + disk_index, + get_putfile_data, end_putfile) < 0) { + LOGERR("sparfilexfer_remote2local failed"); + goto Away; + } + failed = FALSE; +Away: + if (failed) { + if (use_count_up) { + module_put(THIS_MODULE); + use_count_up = FALSE; + } + if (req) { + free_request(req, __FILE__, __LINE__); + req = NULL; + } + return NULL; + } else { + return caller_ctx; + /* success; callbacks will be called for responses */ + } +} + +static void +dump_get_request(struct seq_file *f, struct getfile_request *getreq) +{ + seq_printf(f, " buffers_in_use=%d\n", + atomic_read(&getreq->buffers_in_use)); +} + +static void +dump_put_request(struct seq_file *f, struct putfile_request *putreq) +{ +} + +static void +dump_request(struct seq_file *f, struct any_request *req) +{ + seq_printf(f, "* %s id=%llu seq=%llu\n", + ((req->is_get) ? "Get" : "Put"), + req->file_request_number, req->data_sequence_number); + if (req->is_get) + dump_get_request(f, &req->get); + else + dump_put_request(f, &req->put); + if (req->dump_func) + (*req->dump_func) (f, &(req->caller_context_data[0]), " "); +} + +void +filexfer_dump(struct seq_file *f) +{ + ulong flags; + struct list_head *entry; + + seq_puts(f, "Outstanding TRANSMIT_FILE requests:\n"); + spin_lock_irqsave(&Request_list_lock, flags); + list_for_each(entry, &Request_list) { + struct any_request *req; + req = list_entry(entry, struct any_request, req_list); + dump_request(f, req); + } + spin_unlock_irqrestore(&Request_list_lock, flags); +} + +#else /* ifdef ENABLE_SPARFILEXFER */ +int +filexfer_constructor(size_t req_context_bytes) +{ + return 0; /* success */ +} + +void +filexfer_destructor(void) +{ +} + +BOOL +filexfer_getFile(CONTROLVM_MESSAGE_HEADER *msgHdr, + u64 file_request_number, + uint uplink_index, + uint disk_index, + char *file_name, + GET_CONTIGUOUS_CONTROLVM_PAYLOAD_FUNC + get_contiguous_controlvm_payload, + CONTROLVM_RESPOND_WITH_PAYLOAD_FUNC + controlvm_respond_with_payload, + TRANSMITFILE_DUMP_FUNC dump_func) +{ + /* since no sparfilexfer module exists to call, we just fail */ + return FALSE; +} + +void * +filexfer_putFile(CONTROLVM_MESSAGE_HEADER *msgHdr, + u64 file_request_number, + uint uplink_index, + uint disk_index, + char *file_name, + TRANSMITFILE_INIT_CONTEXT_FUNC init_context, + GET_CONTROLVM_FILEDATA_FUNC get_controlvm_filedata, + CONTROLVM_RESPOND_FUNC controlvm_end_putFile, + TRANSMITFILE_DUMP_FUNC dump_func) +{ + /* since no sparfilexfer module exists to call, we just fail */ + return NULL; +} + +void +filexfer_dump(struct seq_file *f) +{ +} + +#endif /* ifdef ENABLE_SPARFILEXFER */ diff --git a/drivers/staging/unisys/visorchipset/filexfer.h b/drivers/staging/unisys/visorchipset/filexfer.h new file mode 100644 index 000000000000..a1bfca69cdcf --- /dev/null +++ b/drivers/staging/unisys/visorchipset/filexfer.h @@ -0,0 +1,147 @@ +/* filexfer.h + * + * Copyright © 2013 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* This header file defines the interface that filexfer.c provides to other + * code in the visorchipset driver. + */ + +#ifndef __FILEXFER_H__ +#define __FILEXFER_H__ + +#include "globals.h" +#include "controlvmchannel.h" +#include + +typedef void *(*GET_CONTIGUOUS_CONTROLVM_PAYLOAD_FUNC) (ulong min_size, + ulong max_size, + ulong *actual_size); + +typedef BOOL +(*CONTROLVM_RESPOND_WITH_PAYLOAD_FUNC) (CONTROLVM_MESSAGE_HEADER *msgHdr, + u64 fileRequestNumber, + u64 dataSequenceNumber, + int response, + void *bucket, ulong payloadChunkSize, + ulong payloadUsedBytes, BOOL partial); + +typedef void +(*TRANSMITFILE_INIT_CONTEXT_FUNC)(void *ctx, + const CONTROLVM_MESSAGE_HEADER *hdr, + u64 file_request_number); +typedef void (*TRANSMITFILE_DUMP_FUNC) (struct seq_file *f, void *ctx, + const char *pfx); +typedef int (*GET_CONTROLVM_FILEDATA_FUNC) (void *ctx, + void *buf, size_t bufsize, + BOOL buf_is_userspace, + size_t *bytes_transferred); +typedef void (*CONTROLVM_RESPOND_FUNC) (void *ctx, int response); + +/* Call once to initialize filexfer.o. + * req_context_bytes number of bytes the caller needs to keep track of each file + * transfer conversation. The passed to filexfer_putFile() is + * assumed to be this many bytes in size. Code within filexfer.o will copy this + * into a dynamically-allocated area, and pass back a pointer to that area in + * callback functions. + */ +int filexfer_constructor(size_t req_context_bytes); + +/* Call once to clean up filexfer.o */ +void filexfer_destructor(void); + +/* Call this to dump diagnostic info about all outstanding getFiles/putFiles */ +void filexfer_dump(struct seq_file *f); + +/* Call to transfer a file from the local filesystem (i.e., from the environment + * where this driver is running) across the controlvm channel to a remote + * environment. 1 or more controlvm responses will be sent as a result, each + * of which whose payload contains file data. Only the last controlvm message + * will have Flags.partialCompletion==0. + * + * msgHdr the controlvm message header of the GETFILE request which + * we just received + * file_request_number this is all data from the GETFILE request that + * uplink_index define which file is to be transferred + * disk_index + * file_name + * get_contiguous_controlvm_payload function to call when space is needed + * in the payload area + * controlvm_respond_with_payload function to call to send each controlvm + * response containing file data as the + * payload; returns FALSE only if the + * payload buffer was freed inline + * dump_func function to dump context data in + * human-readable format + * + * Returns TRUE iff the file transfer request has been successfully initiated, + * or FALSE to indicate failure. + */ +BOOL +filexfer_getFile(CONTROLVM_MESSAGE_HEADER *msgHdr, + u64 file_request_number, + uint uplink_index, + uint disk_index, + char *file_name, + GET_CONTIGUOUS_CONTROLVM_PAYLOAD_FUNC + get_contiguous_controlvm_payload, + CONTROLVM_RESPOND_WITH_PAYLOAD_FUNC + controlvm_respond_with_payload, + TRANSMITFILE_DUMP_FUNC dump_func); + +/* Call to create a file in the local filesystem (i.e., in the environment + * where this driver is running) from data received as payload in + * controlvm channel messages from a remote environment. 1 or more controlvm + * messages will be received for this transfer, and only the last will have + * Flags.partialCompletion==0. + * + * msgHdr the controlvm message header of the PUTFILE request which + * we just received + * file_request_number this is all data from the PUTFILE request that + * uplink_index define which file is to be created in the local + * disk_index filesystem + * file_name + * init_context function to call to initialize the + * -sized storage area returned by + * this func; note that it would NOT be sufficient to + * allow the caller to initialize this upon return, as + * the the other user-supplied callbacks might have + * already been called by then + * get_controlvm_filedata function to call to obtain more data for the file + * being written; refer to get_controlvm_filedata() + * in visorchipset_main.c for a complete description + * of parameters + * controlvm_end_putFile function to call to indicate that creation of the + * local file has completed; set to a + * negative value to indicate an error + * dump_func function to dump context data in human-readable + * format + * + * Returns a pointer to a dynamically-allocated storage area of size + * which the caller can use, or NULL for error. The + * caller should NEVER free the returned pointer, but should expect to receive + * it as the argument when callback functions are called. + */ +void *filexfer_putFile(CONTROLVM_MESSAGE_HEADER *msgHdr, + u64 file_request_number, + uint uplink_index, + uint disk_index, + char *file_name, + TRANSMITFILE_INIT_CONTEXT_FUNC init_context, + GET_CONTROLVM_FILEDATA_FUNC get_controlvm_filedata, + CONTROLVM_RESPOND_FUNC controlvm_end_putFile, + TRANSMITFILE_DUMP_FUNC dump_func); + +#endif diff --git a/drivers/staging/unisys/visorchipset/globals.h b/drivers/staging/unisys/visorchipset/globals.h new file mode 100644 index 000000000000..a0e6d4fc03ae --- /dev/null +++ b/drivers/staging/unisys/visorchipset/globals.h @@ -0,0 +1,45 @@ +/* globals.h + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + + +#ifndef __VISORCHIPSET_GLOBALS_H__ +#define __VISORCHIPSET_GLOBALS_H__ + +#include "uniklog.h" +#include "diagnostics/appos_subsystems.h" +#include "timskmod.h" +#include "visorchipset.h" +#include "visorchipset_umode.h" +#include "version.h" + +#define MYDRVNAME "visorchipset" + + +/* module parameters */ + +extern int visorchipset_testvnic; +extern int visorchipset_testvnicclient; +extern int visorchipset_testmsg; +extern int visorchipset_major; +extern int visorchipset_serverregwait; +extern int visorchipset_clientregwait; +extern int visorchipset_testteardown; +extern int visorchipset_disable_controlvm; +extern int visorchipset_crash_kernel; +extern int visorchipset_holdchipsetready; + +#endif diff --git a/drivers/staging/unisys/visorchipset/parser.c b/drivers/staging/unisys/visorchipset/parser.c new file mode 100644 index 000000000000..09b3a8485ca6 --- /dev/null +++ b/drivers/staging/unisys/visorchipset/parser.c @@ -0,0 +1,466 @@ +/* parser.c + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#include "parser.h" +#include "memregion.h" +#include "controlvmchannel.h" +#include +#include + +#define MYDRVNAME "visorchipset_parser" +#define CURRENT_FILE_PC VISOR_CHIPSET_PC_parser_c + +/* We will refuse to allocate more than this many bytes to copy data from + * incoming payloads. This serves as a throttling mechanism. + */ +#define MAX_CONTROLVM_PAYLOAD_BYTES (1024*128) +static ulong Controlvm_Payload_Bytes_Buffered; + +struct PARSER_CONTEXT_Tag { + ulong allocbytes; + ulong param_bytes; + u8 *curr; + ulong bytes_remaining; + BOOL byte_stream; + char data[0]; +}; + +static PARSER_CONTEXT * +parser_init_guts(U64 addr, U32 bytes, BOOL isLocal, + BOOL hasStandardPayloadHeader, BOOL *tryAgain) +{ + int allocbytes = sizeof(PARSER_CONTEXT) + bytes; + PARSER_CONTEXT *rc = NULL; + PARSER_CONTEXT *ctx = NULL; + MEMREGION *rgn = NULL; + ULTRA_CONTROLVM_PARAMETERS_HEADER *phdr = NULL; + + if (tryAgain) + *tryAgain = FALSE; + if (!hasStandardPayloadHeader) + /* alloc and 0 extra byte to ensure payload is + * '\0'-terminated + */ + allocbytes++; + if ((Controlvm_Payload_Bytes_Buffered + bytes) + > MAX_CONTROLVM_PAYLOAD_BYTES) { + ERRDRV("%s (%s:%d) - prevented allocation of %d bytes to prevent exceeding throttling max (%d)", + __func__, __FILE__, __LINE__, allocbytes, + MAX_CONTROLVM_PAYLOAD_BYTES); + if (tryAgain) + *tryAgain = TRUE; + RETPTR(NULL); + } + ctx = kmalloc(allocbytes, GFP_KERNEL|__GFP_NORETRY); + if (ctx == NULL) { + ERRDRV("%s (%s:%d) - failed to allocate %d bytes", + __func__, __FILE__, __LINE__, allocbytes); + if (tryAgain) + *tryAgain = TRUE; + RETPTR(NULL); + } + memset(ctx, 0, allocbytes); + ctx->allocbytes = allocbytes; + ctx->param_bytes = bytes; + ctx->curr = NULL; + ctx->bytes_remaining = 0; + ctx->byte_stream = FALSE; + if (isLocal) { + void *p; + if (addr > virt_to_phys(high_memory - 1)) { + ERRDRV("%s - bad local address (0x%-16.16Lx for %lu)", + __func__, + (unsigned long long) addr, (ulong) bytes); + RETPTR(NULL); + } + p = __va((ulong) (addr)); + memcpy(ctx->data, p, bytes); + } else { + rgn = memregion_create(addr, bytes); + if (!rgn) + RETPTR(NULL); + if (memregion_read(rgn, 0, ctx->data, bytes) < 0) + RETPTR(NULL); + } + if (!hasStandardPayloadHeader) { + ctx->byte_stream = TRUE; + RETPTR(ctx); + } + phdr = (ULTRA_CONTROLVM_PARAMETERS_HEADER *) (ctx->data); + if (phdr->TotalLength != bytes) { + ERRDRV("%s - bad total length %lu (should be %lu)", + __func__, + (ulong) (phdr->TotalLength), (ulong) (bytes)); + RETPTR(NULL); + } + if (phdr->TotalLength < phdr->HeaderLength) { + ERRDRV("%s - total length < header length (%lu < %lu)", + __func__, + (ulong) (phdr->TotalLength), + (ulong) (phdr->HeaderLength)); + RETPTR(NULL); + } + if (phdr->HeaderLength < sizeof(ULTRA_CONTROLVM_PARAMETERS_HEADER)) { + ERRDRV("%s - header is too small (%lu < %lu)", + __func__, + (ulong) (phdr->HeaderLength), + (ulong) (sizeof(ULTRA_CONTROLVM_PARAMETERS_HEADER))); + RETPTR(NULL); + } + + RETPTR(ctx); + +Away: + if (rgn) { + memregion_destroy(rgn); + rgn = NULL; + } + if (rc) + Controlvm_Payload_Bytes_Buffered += ctx->param_bytes; + else { + if (ctx) { + parser_done(ctx); + ctx = NULL; + } + } + return rc; +} + +PARSER_CONTEXT * +parser_init(U64 addr, U32 bytes, BOOL isLocal, BOOL *tryAgain) +{ + return parser_init_guts(addr, bytes, isLocal, TRUE, tryAgain); +} + +/* Call this instead of parser_init() if the payload area consists of just + * a sequence of bytes, rather than a ULTRA_CONTROLVM_PARAMETERS_HEADER + * structures. Afterwards, you can call parser_simpleString_get() or + * parser_byteStream_get() to obtain the data. + */ +PARSER_CONTEXT * +parser_init_byteStream(U64 addr, U32 bytes, BOOL isLocal, BOOL *tryAgain) +{ + return parser_init_guts(addr, bytes, isLocal, FALSE, tryAgain); +} + +/* Obtain '\0'-terminated copy of string in payload area. + */ +char * +parser_simpleString_get(PARSER_CONTEXT *ctx) +{ + if (!ctx->byte_stream) + return NULL; + return ctx->data; /* note this IS '\0'-terminated, because of + * the num of bytes we alloc+clear in + * parser_init_byteStream() */ +} + +/* Obtain a copy of the buffer in the payload area. + */ +void * +parser_byteStream_get(PARSER_CONTEXT *ctx, ulong *nbytes) +{ + if (!ctx->byte_stream) + return NULL; + if (nbytes) + *nbytes = ctx->param_bytes; + return (void *) ctx->data; +} + +GUID +parser_id_get(PARSER_CONTEXT *ctx) +{ + ULTRA_CONTROLVM_PARAMETERS_HEADER *phdr = NULL; + + if (ctx == NULL) { + ERRDRV("%s (%s:%d) - no context", + __func__, __FILE__, __LINE__); + return Guid0; + } + phdr = (ULTRA_CONTROLVM_PARAMETERS_HEADER *) (ctx->data); + return phdr->Id; +} + +void +parser_param_start(PARSER_CONTEXT *ctx, PARSER_WHICH_STRING which_string) +{ + ULTRA_CONTROLVM_PARAMETERS_HEADER *phdr = NULL; + + if (ctx == NULL) { + ERRDRV("%s (%s:%d) - no context", + __func__, __FILE__, __LINE__); + RETVOID; + } + phdr = (ULTRA_CONTROLVM_PARAMETERS_HEADER *) (ctx->data); + switch (which_string) { + case PARSERSTRING_INITIATOR: + ctx->curr = ctx->data + phdr->InitiatorOffset; + ctx->bytes_remaining = phdr->InitiatorLength; + break; + case PARSERSTRING_TARGET: + ctx->curr = ctx->data + phdr->TargetOffset; + ctx->bytes_remaining = phdr->TargetLength; + break; + case PARSERSTRING_CONNECTION: + ctx->curr = ctx->data + phdr->ConnectionOffset; + ctx->bytes_remaining = phdr->ConnectionLength; + break; + case PARSERSTRING_NAME: + ctx->curr = ctx->data + phdr->NameOffset; + ctx->bytes_remaining = phdr->NameLength; + break; + default: + ERRDRV("%s - bad which_string %d", __func__, which_string); + RETVOID; + break; + } + RETVOID; + +Away: + return; +} + +void +parser_done(PARSER_CONTEXT *ctx) +{ + if (!ctx) + return; + Controlvm_Payload_Bytes_Buffered -= ctx->param_bytes; + kfree(ctx); +} + +/** Return length of string not counting trailing spaces. */ +static int +string_length_no_trail(char *s, int len) +{ + int i = len - 1; + while (i >= 0) { + if (!isspace(s[i])) + return i + 1; + i--; + } + return 0; +} + +/** Grab the next name and value out of the parameter buffer. + * The entire parameter buffer looks like this: + * =\0 + * =\0 + * ... + * \0 + * If successful, the next value is returned within the supplied + * buffer (the value is always upper-cased), and the corresponding + * is returned within a kmalloc()ed buffer, whose pointer is + * provided as the return value of this function. + * (The total number of bytes allocated is strlen()+1.) + * + * NULL is returned to indicate failure, which can occur for several reasons: + * - all = pairs have already been processed + * - bad parameter + * - parameter buffer ends prematurely (couldn't find an '=' or '\0' within + * the confines of the parameter buffer) + * - the buffer is not large enough to hold the of the next + * parameter + */ +void * +parser_param_get(PARSER_CONTEXT *ctx, char *nam, int namesize) +{ + u8 *pscan, *pnam = nam; + ulong nscan; + int value_length = -1, orig_value_length = -1; + void *value = NULL; + int i; + int closing_quote = 0; + + if (!ctx) + return NULL; + pscan = ctx->curr; + nscan = ctx->bytes_remaining; + if (nscan == 0) + return NULL; + if (*pscan == '\0') + /* This is the normal return point after you have processed + * all of the = pairs in a syntactically-valid + * parameter buffer. + */ + return NULL; + + /* skip whitespace */ + while (isspace(*pscan)) { + pscan++; + nscan--; + if (nscan == 0) + return NULL; + } + + while (*pscan != ':') { + if (namesize <= 0) { + ERRDRV("%s - name too big", __func__); + return NULL; + } + *pnam = toupper(*pscan); + pnam++; + namesize--; + pscan++; + nscan--; + if (nscan == 0) { + ERRDRV("%s - unexpected end of input parsing name", + __func__); + return NULL; + } + } + if (namesize <= 0) { + ERRDRV("%s - name too big", __func__); + return NULL; + } + *pnam = '\0'; + nam[string_length_no_trail(nam, strlen(nam))] = '\0'; + + /* point to char immediately after ":" in ":" */ + pscan++; + nscan--; + /* skip whitespace */ + while (isspace(*pscan)) { + pscan++; + nscan--; + if (nscan == 0) { + ERRDRV("%s - unexpected end of input looking for value", + __func__); + return NULL; + } + } + if (nscan == 0) { + ERRDRV("%s - unexpected end of input looking for value", + __func__); + return NULL; + } + if (*pscan == '\'' || *pscan == '"') { + closing_quote = *pscan; + pscan++; + nscan--; + if (nscan == 0) { + ERRDRV("%s - unexpected end of input after %c", + __func__, closing_quote); + return NULL; + } + } + + /* look for a separator character, terminator character, or + * end of data + */ + for (i = 0, value_length = -1; i < nscan; i++) { + if (closing_quote) { + if (pscan[i] == '\0') { + ERRDRV("%s - unexpected end of input parsing quoted value", __func__); + return NULL; + } + if (pscan[i] == closing_quote) { + value_length = i; + break; + } + } else + if (pscan[i] == ',' || pscan[i] == ';' + || pscan[i] == '\0') { + value_length = i; + break; + } + } + if (value_length < 0) { + if (closing_quote) { + ERRDRV("%s - unexpected end of input parsing quoted value", __func__); + return NULL; + } + value_length = nscan; + } + orig_value_length = value_length; + if (closing_quote == 0) + value_length = string_length_no_trail(pscan, orig_value_length); + value = kmalloc(value_length + 1, GFP_KERNEL|__GFP_NORETRY); + if (value == NULL) + return NULL; + memcpy(value, pscan, value_length); + ((u8 *) (value))[value_length] = '\0'; + + pscan += orig_value_length; + nscan -= orig_value_length; + + /* skip past separator or closing quote */ + if (nscan > 0) { + if (*pscan != '\0') { + pscan++; + nscan--; + } + } + + if (closing_quote && (nscan > 0)) { + /* we still need to skip around the real separator if present */ + /* first, skip whitespace */ + while (isspace(*pscan)) { + pscan++; + nscan--; + if (nscan == 0) + break; + } + if (nscan > 0) { + if (*pscan == ',' || *pscan == ';') { + pscan++; + nscan--; + } else if (*pscan != '\0') { + ERRDRV("%s - missing separator after quoted string", __func__); + kfree(value); + value = NULL; + return NULL; + } + } + } + ctx->curr = pscan; + ctx->bytes_remaining = nscan; + return value; +} + +void * +parser_string_get(PARSER_CONTEXT *ctx) +{ + u8 *pscan; + ulong 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 == NULL) + return NULL; + if (value_length > 0) + memcpy(value, pscan, value_length); + ((u8 *) (value))[value_length] = '\0'; + return value; +} diff --git a/drivers/staging/unisys/visorchipset/parser.h b/drivers/staging/unisys/visorchipset/parser.h new file mode 100644 index 000000000000..a0cc50a533cd --- /dev/null +++ b/drivers/staging/unisys/visorchipset/parser.h @@ -0,0 +1,45 @@ +/* parser.h + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __PARSER_H__ +#define __PARSER_H__ + +#include "uniklog.h" +#include "timskmod.h" +#include "channel.h" + +typedef enum { + PARSERSTRING_INITIATOR, + PARSERSTRING_TARGET, + PARSERSTRING_CONNECTION, + PARSERSTRING_NAME, +} PARSER_WHICH_STRING; + +typedef struct PARSER_CONTEXT_Tag PARSER_CONTEXT; + +PARSER_CONTEXT *parser_init(U64 addr, U32 bytes, BOOL isLocal, BOOL *tryAgain); +PARSER_CONTEXT *parser_init_byteStream(U64 addr, U32 bytes, BOOL isLocal, + BOOL *tryAgain); +void parser_param_start(PARSER_CONTEXT *ctx, PARSER_WHICH_STRING which_string); +void *parser_param_get(PARSER_CONTEXT *ctx, char *nam, int namesize); +void *parser_string_get(PARSER_CONTEXT *ctx); +GUID parser_id_get(PARSER_CONTEXT *ctx); +char *parser_simpleString_get(PARSER_CONTEXT *ctx); +void *parser_byteStream_get(PARSER_CONTEXT *ctx, ulong *nbytes); +void parser_done(PARSER_CONTEXT *ctx); + +#endif diff --git a/drivers/staging/unisys/visorchipset/testing.h b/drivers/staging/unisys/visorchipset/testing.h new file mode 100644 index 000000000000..a44f5556cb21 --- /dev/null +++ b/drivers/staging/unisys/visorchipset/testing.h @@ -0,0 +1,41 @@ +/* testing.h + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __VISORCHIPSET_TESTING_H__ +#define __VISORCHIPSET_TESTING_H__ + +#define VISORCHIPSET_TEST_PROC +#include "globals.h" +#include "controlvmchannel.h" + +void test_produce_test_message(CONTROLVM_MESSAGE *msg, int isLocalTestAddr); +BOOL test_consume_test_message(CONTROLVM_MESSAGE *msg); +void test_manufacture_vnic_client_add(void *p); +void test_manufacture_vnic_client_add_phys(HOSTADDRESS addr); +void test_manufacture_preamble_messages(void); +void test_manufacture_device_attach(ulong busNo, ulong devNo); +void test_manufacture_device_add(ulong busNo, ulong devNo, GUID dataTypeGuid, + void *pChannel); +void test_manufacture_add_bus(ulong busNo, ulong maxDevices, + GUID id, u8 *name, BOOL isServer); +void test_manufacture_device_destroy(ulong busNo, ulong devNo); +void test_manufacture_bus_destroy(ulong busNo); +void test_manufacture_detach_externalPort(ulong switchNo, ulong externalPortNo); +void test_manufacture_detach_internalPort(ulong switchNo, ulong internalPortNo); +void test_cleanup(void); + +#endif diff --git a/drivers/staging/unisys/visorchipset/visorchipset.h b/drivers/staging/unisys/visorchipset/visorchipset.h new file mode 100644 index 000000000000..8e62a89a7d2a --- /dev/null +++ b/drivers/staging/unisys/visorchipset/visorchipset.h @@ -0,0 +1,307 @@ +/* visorchipset.h + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#ifndef __VISORCHIPSET_H__ +#define __VISORCHIPSET_H__ + +#include "timskmod.h" +#include "channel.h" +#include "controlvmchannel.h" +#include "parser.h" +#include "procobjecttree.h" +#include "vbusdeviceinfo.h" +#include "vbushelper.h" + +/** Describes the state from the perspective of which controlvm messages have + * been received for a bus or device. + */ +typedef struct { + U32 created:1; + U32 attached:1; + U32 configured:1; + U32 running:1; + /* Add new fields above. */ + /* Remaining bits in this 32-bit word are unused. */ +} VISORCHIPSET_STATE; + +typedef enum { + /** address is guest physical, but outside of the physical memory + * region that is controlled by the running OS (this is the normal + * address type for Supervisor channels) + */ + ADDRTYPE_localPhysical, + + /** address is guest physical, and withIN the confines of the + * physical memory controlled by the running OS. + */ + ADDRTYPE_localTest, +} VISORCHIPSET_ADDRESSTYPE; + +typedef enum { + CRASH_dev, + CRASH_bus, +} CRASH_OBJ_TYPE; + +/** Attributes for a particular Supervisor channel. + */ +typedef struct { + VISORCHIPSET_ADDRESSTYPE addrType; + HOSTADDRESS channelAddr; + struct InterruptInfo intr; + U64 nChannelBytes; + GUID channelTypeGuid; + GUID channelInstGuid; + +} VISORCHIPSET_CHANNEL_INFO; + +/** Attributes for a particular Supervisor device. + * Any visorchipset client can query these attributes using + * visorchipset_get_client_device_info() or + * visorchipset_get_server_device_info(). + */ +typedef struct { + struct list_head entry; + U32 busNo; + U32 devNo; + GUID devInstGuid; + VISORCHIPSET_STATE state; + VISORCHIPSET_CHANNEL_INFO chanInfo; + U32 Reserved1; /* CONTROLVM_ID */ + U64 Reserved2; + U32 switchNo; /* when devState.attached==1 */ + U32 internalPortNo; /* when devState.attached==1 */ + CONTROLVM_MESSAGE_HEADER pendingMsgHdr; /* CONTROLVM_MESSAGE */ + /** For private use by the bus driver */ + void *bus_driver_context; + +} VISORCHIPSET_DEVICE_INFO; + +static inline VISORCHIPSET_DEVICE_INFO * +finddevice(struct list_head *list, U32 busNo, U32 devNo) +{ + VISORCHIPSET_DEVICE_INFO *p; + + list_for_each_entry(p, list, entry) { + if (p->busNo == busNo && p->devNo == devNo) + return p; + } + return NULL; +} + +static inline void delbusdevices(struct list_head *list, U32 busNo) +{ + VISORCHIPSET_DEVICE_INFO *p; + + list_for_each_entry(p, list, entry) { + if (p->busNo == busNo) { + list_del(&p->entry); + kfree(p); + } + } +} + +/** Attributes for a particular Supervisor bus. + * (For a service partition acting as the server for buses/devices, there + * is a 1-to-1 relationship between busses and guest partitions.) + * Any visorchipset client can query these attributes using + * visorchipset_get_client_bus_info() or visorchipset_get_bus_info(). + */ +typedef struct { + struct list_head entry; + U32 busNo; + VISORCHIPSET_STATE state; + VISORCHIPSET_CHANNEL_INFO chanInfo; + GUID partitionGuid; + U64 partitionHandle; + U8 *name; /* UTF8 */ + U8 *description; /* UTF8 */ + U64 Reserved1; + U32 Reserved2; + MYPROCOBJECT *procObject; + struct { + U32 server:1; + /* Add new fields above. */ + /* Remaining bits in this 32-bit word are unused. */ + } flags; + CONTROLVM_MESSAGE_HEADER pendingMsgHdr; /* CONTROLVM MsgHdr */ + /** For private use by the bus driver */ + void *bus_driver_context; + U64 devNo; + +} VISORCHIPSET_BUS_INFO; + +static inline VISORCHIPSET_BUS_INFO * +findbus(struct list_head *list, U32 busNo) +{ + VISORCHIPSET_BUS_INFO *p; + + list_for_each_entry(p, list, entry) { + if (p->busNo == busNo) + return p; + } + return NULL; +} + +/** Attributes for a particular Supervisor switch. + */ +typedef struct { + U32 switchNo; + VISORCHIPSET_STATE state; + GUID switchTypeGuid; + U8 *authService1; + U8 *authService2; + U8 *authService3; + U8 *securityContext; + U64 Reserved; + U32 Reserved2; /* CONTROLVM_ID */ + struct device dev; + BOOL dev_exists; + CONTROLVM_MESSAGE_HEADER pendingMsgHdr; + +} VISORCHIPSET_SWITCH_INFO; + +/** Attributes for a particular Supervisor external port, which is connected + * to a specific switch. + */ +typedef struct { + U32 switchNo; + U32 externalPortNo; + VISORCHIPSET_STATE state; + GUID networkZoneGuid; + int pdPort; + U8 *ip; + U8 *ipNetmask; + U8 *ipBroadcast; + U8 *ipNetwork; + U8 *ipGateway; + U8 *ipDNS; + U64 Reserved1; + U32 Reserved2; /* CONTROLVM_ID */ + struct device dev; + BOOL dev_exists; + CONTROLVM_MESSAGE_HEADER pendingMsgHdr; + +} VISORCHIPSET_EXTERNALPORT_INFO; + +/** Attributes for a particular Supervisor internal port, which is how a + * device connects to a particular switch. + */ +typedef struct { + U32 switchNo; + U32 internalPortNo; + VISORCHIPSET_STATE state; + U32 busNo; /* valid only when state.attached == 1 */ + U32 devNo; /* valid only when state.attached == 1 */ + U64 Reserved1; + U32 Reserved2; /* CONTROLVM_ID */ + CONTROLVM_MESSAGE_HEADER pendingMsgHdr; + MYPROCOBJECT *procObject; + +} VISORCHIPSET_INTERNALPORT_INFO; + +/* These functions will be called from within visorchipset when certain + * events happen. (The implementation of these functions is outside of + * visorchipset.) + */ +typedef struct { + void (*bus_create)(ulong busNo); + void (*bus_destroy)(ulong busNo); + void (*device_create)(ulong busNo, ulong devNo); + void (*device_destroy)(ulong busNo, ulong devNo); + void (*device_pause)(ulong busNo, ulong devNo); + void (*device_resume)(ulong busNo, ulong devNo); + int (*get_channel_info)(GUID typeGuid, ulong *minSize, + ulong *maxSize); +} VISORCHIPSET_BUSDEV_NOTIFIERS; + +/* 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 + */ +typedef struct { + void (*bus_create)(ulong busNo, int response); + void (*bus_destroy)(ulong busNo, int response); + void (*device_create)(ulong busNo, ulong devNo, int response); + void (*device_destroy)(ulong busNo, ulong devNo, int response); + void (*device_pause)(ulong busNo, ulong devNo, int response); + void (*device_resume)(ulong busNo, ulong devNo, int response); +} VISORCHIPSET_BUSDEV_RESPONDERS; + +/** Register functions (in the bus driver) to get called by visorchipset + * whenever a bus or device appears for which this service partition is + * to be the server for. visorchipset will fill in , to + * indicate functions the bus driver should call to indicate message + * responses. + */ +void +visorchipset_register_busdev_client(VISORCHIPSET_BUSDEV_NOTIFIERS *notifiers, + VISORCHIPSET_BUSDEV_RESPONDERS *responders, + ULTRA_VBUS_DEVICEINFO *driverInfo); + +/** Register functions (in the bus driver) to get called by visorchipset + * whenever a bus or device appears for which this service partition is + * to be the client for. visorchipset will fill in , to + * indicate functions the bus driver should call to indicate message + * responses. + */ +void +visorchipset_register_busdev_server(VISORCHIPSET_BUSDEV_NOTIFIERS *notifiers, + VISORCHIPSET_BUSDEV_RESPONDERS *responders, + ULTRA_VBUS_DEVICEINFO *driverInfo); + +typedef void (*SPARREPORTEVENT_COMPLETE_FUNC) (CONTROLVM_MESSAGE *msg, + int status); + +void device_pause_response(ulong busNo, ulong devNo, int response); + +BOOL visorchipset_get_bus_info(ulong busNo, VISORCHIPSET_BUS_INFO *busInfo); +BOOL visorchipset_get_device_info(ulong busNo, ulong devNo, + VISORCHIPSET_DEVICE_INFO *devInfo); +BOOL visorchipset_get_switch_info(ulong switchNo, + VISORCHIPSET_SWITCH_INFO *switchInfo); +BOOL visorchipset_get_externalport_info(ulong switchNo, ulong externalPortNo, + VISORCHIPSET_EXTERNALPORT_INFO + *externalPortInfo); +BOOL visorchipset_set_bus_context(ulong busNo, void *context); +BOOL visorchipset_set_device_context(ulong busNo, ulong devNo, void *context); +int visorchipset_chipset_ready(void); +int visorchipset_chipset_selftest(void); +int visorchipset_chipset_notready(void); +void visorchipset_controlvm_respond_reportEvent(CONTROLVM_MESSAGE *msg, + void *payload); +void visorchipset_save_message(CONTROLVM_MESSAGE *msg, CRASH_OBJ_TYPE type); +void *visorchipset_cache_alloc(struct kmem_cache *pool, + BOOL ok_to_block, char *fn, int ln); +void visorchipset_cache_free(struct kmem_cache *pool, void *p, + char *fn, int ln); + +#if defined(TRANSMITFILE_DEBUG) || defined(DEBUG) +#define DBG_GETFILE_PAYLOAD(msg, controlvm_header) \ + LOGINF(msg, \ + (ulong)controlvm_header.PayloadVmOffset, \ + (ulong)controlvm_header.PayloadMaxBytes) +#define DBG_GETFILE(fmt, ...) LOGINF(fmt, ##__VA_ARGS__) +#define DBG_PUTFILE(fmt, ...) LOGINF(fmt, ##__VA_ARGS__) +#else +#define DBG_GETFILE_PAYLOAD(msg, controlvm_header) +#define DBG_GETFILE(fmt, ...) +#define DBG_PUTFILE(fmt, ...) +#endif + +#endif diff --git a/drivers/staging/unisys/visorchipset/visorchipset_main.c b/drivers/staging/unisys/visorchipset/visorchipset_main.c new file mode 100644 index 000000000000..e0ec3a4fa3a7 --- /dev/null +++ b/drivers/staging/unisys/visorchipset/visorchipset_main.c @@ -0,0 +1,2912 @@ +/* visorchipset_main.c + * + * Copyright � 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +#include "globals.h" +#include "controlvm.h" +#include "visorchipset.h" +#include "procobjecttree.h" +#include "visorchannel.h" +#include "periodic_work.h" +#include "testing.h" +#include "file.h" +#include "parser.h" +#include "uniklog.h" +#include "uisutils.h" +#include "guidutils.h" +#include "controlvmcompletionstatus.h" +#include "guestlinuxdebug.h" +#include "filexfer.h" + +#include +#include +#include + +#define CURRENT_FILE_PC VISOR_CHIPSET_PC_visorchipset_main_c +#define TEST_VNIC_PHYSITF "eth0" /* physical network itf for + * vnic loopback test */ +#define TEST_VNIC_SWITCHNO 1 +#define TEST_VNIC_BUSNO 9 + +#define MAX_NAME_SIZE 128 +#define MAX_IP_SIZE 50 +#define MAXOUTSTANDINGCHANNELCOMMAND 256 +#define POLLJIFFIES_CONTROLVMCHANNEL_FAST 1 +#define POLLJIFFIES_CONTROLVMCHANNEL_SLOW 100 + +/* 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 +ulong Poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_FAST; +ulong Most_recent_message_jiffies; /* when we got our last + * controlvm message */ +static inline char * +NONULLSTR(char *s) +{ + if (s) + return s; + else + return ""; +} + +static int serverregistered; +static int clientregistered; + +#define MAX_CHIPSET_EVENTS 2 +static U8 chipset_events[MAX_CHIPSET_EVENTS] = { 0, 0 }; + +static struct delayed_work Periodic_controlvm_work; +static struct workqueue_struct *Periodic_controlvm_workqueue; +DEFINE_SEMAPHORE(NotifierLock); + +typedef struct { + CONTROLVM_MESSAGE message; + unsigned int crc; +} MESSAGE_ENVELOPE; + +static CONTROLVM_MESSAGE_HEADER g_DiagMsgHdr; +static CONTROLVM_MESSAGE_HEADER g_ChipSetMsgHdr; +static CONTROLVM_MESSAGE_HEADER g_DelDumpMsgHdr; +static const GUID UltraDiagPoolChannelProtocolGuid = + ULTRA_DIAG_POOL_CHANNEL_PROTOCOL_GUID; +/* 0xffffff is an invalid Bus/Device number */ +static ulong g_diagpoolBusNo = 0xffffff; +static ulong g_diagpoolDevNo = 0xffffff; +static CONTROLVM_MESSAGE_PACKET g_DeviceChangeStatePacket; + +/* Only VNIC and VHBA channels are sent to visorclientbus (aka + * "visorhackbus") + */ +#define FOR_VISORHACKBUS(channel_type_guid) \ + ((memcmp(&channel_type_guid, &UltraVnicChannelProtocolGuid, \ + sizeof(GUID)) == 0) || \ + (memcmp(&channel_type_guid, &UltraVhbaChannelProtocolGuid, \ + sizeof(GUID)) == 0)) +#define FOR_VISORBUS(channel_type_guid) (!(FOR_VISORHACKBUS(channel_type_guid))) + +#define is_diagpool_channel(channel_type_guid) \ + (memcmp(&channel_type_guid, \ + &UltraDiagPoolChannelProtocolGuid, sizeof(GUID)) == 0) + +typedef enum { + PARTPROP_invalid, + PARTPROP_name, + PARTPROP_description, + PARTPROP_handle, + PARTPROP_busNumber, + /* add new properties above, but don't forget to change + * InitPartitionProperties() and show_partition_property() also... + */ + PARTPROP_last +} PARTITION_property; +static const char *PartitionTypeNames[] = { "partition", NULL }; + +static char *PartitionPropertyNames[PARTPROP_last + 1]; +static void +InitPartitionProperties(void) +{ + char **p = PartitionPropertyNames; + p[PARTPROP_invalid] = ""; + p[PARTPROP_name] = "name"; + p[PARTPROP_description] = "description"; + p[PARTPROP_handle] = "handle"; + p[PARTPROP_busNumber] = "busNumber"; + p[PARTPROP_last] = NULL; +} + +typedef enum { + CTLVMPROP_invalid, + CTLVMPROP_physAddr, + CTLVMPROP_controlChannelAddr, + CTLVMPROP_controlChannelBytes, + CTLVMPROP_sparBootPart, + CTLVMPROP_sparStoragePart, + CTLVMPROP_livedumpLength, + CTLVMPROP_livedumpCrc32, + /* add new properties above, but don't forget to change + * InitControlVmProperties() show_controlvm_property() also... + */ + CTLVMPROP_last +} CONTROLVM_property; + +static const char *ControlVmTypeNames[] = { "controlvm", NULL }; + +static char *ControlVmPropertyNames[CTLVMPROP_last + 1]; +static void +InitControlVmProperties(void) +{ + char **p = ControlVmPropertyNames; + p[CTLVMPROP_invalid] = ""; + p[CTLVMPROP_physAddr] = "physAddr"; + p[CTLVMPROP_controlChannelAddr] = "controlChannelAddr"; + p[CTLVMPROP_controlChannelBytes] = "controlChannelBytes"; + p[CTLVMPROP_sparBootPart] = "spar_boot_part"; + p[CTLVMPROP_sparStoragePart] = "spar_storage_part"; + p[CTLVMPROP_livedumpLength] = "livedumpLength"; + p[CTLVMPROP_livedumpCrc32] = "livedumpCrc32"; + p[CTLVMPROP_last] = NULL; +} + +static MYPROCOBJECT *ControlVmObject; +static MYPROCTYPE *PartitionType; +static MYPROCTYPE *ControlVmType; + +#define VISORCHIPSET_DIAG_PROC_ENTRY_FN "diagdump" +static struct proc_dir_entry *diag_proc_dir; + +#define VISORCHIPSET_CHIPSET_PROC_ENTRY_FN "chipsetready" +static struct proc_dir_entry *chipset_proc_dir; + +#define VISORCHIPSET_PARAHOTPLUG_PROC_ENTRY_FN "parahotplug" +static struct proc_dir_entry *parahotplug_proc_dir; + +static LIST_HEAD(BusInfoList); +static LIST_HEAD(DevInfoList); + +static struct proc_dir_entry *ProcDir; +static VISORCHANNEL *ControlVm_channel; + +static ssize_t visorchipset_proc_read_writeonly(struct file *file, + char __user *buf, + size_t len, loff_t *offset); +static ssize_t proc_read_installer(struct file *file, char __user *buf, + size_t len, loff_t *offset); +static ssize_t proc_write_installer(struct file *file, + const char __user *buffer, + size_t count, loff_t *ppos); +static ssize_t proc_read_toolaction(struct file *file, char __user *buf, + size_t len, loff_t *offset); +static ssize_t proc_write_toolaction(struct file *file, + const char __user *buffer, + size_t count, loff_t *ppos); +static ssize_t proc_read_bootToTool(struct file *file, char __user *buf, + size_t len, loff_t *offset); +static ssize_t proc_write_bootToTool(struct file *file, + const char __user *buffer, + size_t count, loff_t *ppos); +static const struct file_operations proc_installer_fops = { + .read = proc_read_installer, + .write = proc_write_installer, +}; + +static const struct file_operations proc_toolaction_fops = { + .read = proc_read_toolaction, + .write = proc_write_toolaction, +}; + +static const struct file_operations proc_bootToTool_fops = { + .read = proc_read_bootToTool, + .write = proc_write_bootToTool, +}; + +typedef struct { + 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 */ +} CONTROLVM_PAYLOAD_INFO; + +/* Manages the request payload in the controlvm channel */ +static CONTROLVM_PAYLOAD_INFO ControlVm_payload_info; + +static pCHANNEL_HEADER Test_Vnic_channel; + +typedef struct { + CONTROLVM_MESSAGE_HEADER Dumpcapture_header; + CONTROLVM_MESSAGE_HEADER Gettextdump_header; + CONTROLVM_MESSAGE_HEADER Dumpcomplete_header; + BOOL Gettextdump_outstanding; + u32 crc32; + ulong length; + atomic_t buffers_in_use; + ulong destination; +} LIVEDUMP_INFO; +/* Manages the info for a CONTROLVM_DUMP_CAPTURESTATE / + * CONTROLVM_DUMP_GETTEXTDUMP / CONTROLVM_DUMP_COMPLETE conversation. + */ +static LIVEDUMP_INFO LiveDump_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 CONTROLVM_MESSAGE ControlVm_Pending_Msg; +static BOOL ControlVm_Pending_Msg_Valid = FALSE; + +/* Pool of struct putfile_buffer_entry, for keeping track of pending (incoming) + * TRANSMIT_FILE PutFile payloads. + */ +static struct kmem_cache *Putfile_buffer_list_pool; +static const char Putfile_buffer_list_pool_name[] = + "controlvm_putfile_buffer_list_pool"; + +/* 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 */ + PARSER_CONTEXT *parser_ctx; /* points to buffer containing input data */ +}; + +/* 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 */ + PARSER_CONTEXT *parser_ctx; + /* points within data area of parser_ctx to next byte of data */ + u8 *pnext; + /* # bytes left from to the end of this data buffer */ + 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 + * . + */ +struct putfile_request { + u64 sig; /* PUTFILE_REQUEST_SIG */ + + /* header from original TransmitFile request */ + CONTROLVM_MESSAGE_HEADER controlvm_header; + u64 file_request_number; /* from original TransmitFile request */ + + /* link to next struct putfile_request */ + struct list_head next_putfile_request; + + /* most-recent sequence number supplied via a controlvm message */ + u64 data_sequence_number; + + /* 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; +}; + +atomic_t Visorchipset_cache_buffers_in_use = ATOMIC_INIT(0); + +struct parahotplug_request { + struct list_head list; + int id; + unsigned long expiration; + 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 VISORCHIPSET_BUSDEV_NOTIFIERS BusDev_Server_Notifiers; +static VISORCHIPSET_BUSDEV_NOTIFIERS BusDev_Client_Notifiers; + +static void bus_create_response(ulong busNo, int response); +static void bus_destroy_response(ulong busNo, int response); +static void device_create_response(ulong busNo, ulong devNo, int response); +static void device_destroy_response(ulong busNo, ulong devNo, int response); +static void device_resume_response(ulong busNo, ulong devNo, int response); + +static 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 = device_pause_response, + .device_resume = device_resume_response, +}; + +/* info for /dev/visorchipset */ +static dev_t MajorDev = -1; /**< indicates major num for device */ + +/* /sys/devices/platform/visorchipset */ +static struct platform_device Visorchipset_platform_device = { + .name = "visorchipset", + .id = -1, +}; + +/* Function prototypes */ +static void controlvm_respond(CONTROLVM_MESSAGE_HEADER *msgHdr, int response); +static void controlvm_respond_chipset_init(CONTROLVM_MESSAGE_HEADER *msgHdr, + int response, + ULTRA_CHIPSET_FEATURE features); +static void controlvm_respond_physdev_changestate(CONTROLVM_MESSAGE_HEADER * + msgHdr, int response, + ULTRA_SEGMENT_STATE state); + +static void +show_partition_property(struct seq_file *f, void *ctx, int property) +{ + VISORCHIPSET_BUS_INFO *info = (VISORCHIPSET_BUS_INFO *) (ctx); + + switch (property) { + case PARTPROP_name: + seq_printf(f, "%s\n", NONULLSTR(info->name)); + break; + case PARTPROP_description: + seq_printf(f, "%s\n", NONULLSTR(info->description)); + break; + case PARTPROP_handle: + seq_printf(f, "0x%-16.16Lx\n", info->partitionHandle); + break; + case PARTPROP_busNumber: + seq_printf(f, "%d\n", info->busNo); + break; + default: + seq_printf(f, "(%d??)\n", property); + break; + } +} + +static void +show_controlvm_property(struct seq_file *f, void *ctx, int property) +{ + /* Note: ctx is not needed since we only have 1 controlvm channel */ + switch (property) { + case CTLVMPROP_physAddr: + if (ControlVm_channel == NULL) + seq_puts(f, "0x0\n"); + else + seq_printf(f, "0x%-16.16Lx\n", + visorchannel_get_physaddr + (ControlVm_channel)); + break; + case CTLVMPROP_controlChannelAddr: + if (ControlVm_channel == NULL) + seq_puts(f, "0x0\n"); + else { + GUEST_PHYSICAL_ADDRESS addr = 0; + visorchannel_read(ControlVm_channel, + offsetof + (ULTRA_CONTROLVM_CHANNEL_PROTOCOL, + gpControlChannel), &addr, + sizeof(addr)); + seq_printf(f, "0x%-16.16Lx\n", (u64) (addr)); + } + break; + case CTLVMPROP_controlChannelBytes: + if (ControlVm_channel == NULL) + seq_puts(f, "0x0\n"); + else { + U32 bytes = 0; + visorchannel_read(ControlVm_channel, + offsetof + (ULTRA_CONTROLVM_CHANNEL_PROTOCOL, + ControlChannelBytes), &bytes, + sizeof(bytes)); + seq_printf(f, "%lu\n", (ulong) (bytes)); + } + break; + case CTLVMPROP_sparBootPart: + seq_puts(f, "0:0:0:0/1\n"); + break; + case CTLVMPROP_sparStoragePart: + seq_puts(f, "0:0:0:0/2\n"); + break; + case CTLVMPROP_livedumpLength: + seq_printf(f, "%lu\n", LiveDump_info.length); + break; + case CTLVMPROP_livedumpCrc32: + seq_printf(f, "%lu\n", (ulong) LiveDump_info.crc32); + break; + default: + seq_printf(f, "(%d??)\n", property); + break; + } +} + +static void +proc_Init(void) +{ + if (ProcDir == NULL) { + ProcDir = proc_mkdir(MYDRVNAME, NULL); + if (ProcDir == NULL) { + LOGERR("failed to create /proc directory %s", + MYDRVNAME); + POSTCODE_LINUX_2(CHIPSET_INIT_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + } + } +} + +static void +proc_DeInit(void) +{ + if (ProcDir != NULL) + remove_proc_entry(MYDRVNAME, NULL); + ProcDir = NULL; +} + +#if 0 +static void +testUnicode(void) +{ + wchar_t unicodeString[] = { 'a', 'b', 'c', 0 }; + char s[sizeof(unicodeString) * NLS_MAX_CHARSET_SIZE]; + wchar_t unicode2[99]; + + /* NOTE: Either due to a bug, or feature I don't understand, the + * kernel utf8_mbstowcs() and utf_wcstombs() do NOT copy the + * trailed NUL byte!! REALLY!!!!! Arrrrgggghhhhh + */ + + LOGINF("sizeof(wchar_t) = %d", sizeof(wchar_t)); + LOGINF("utf8_wcstombs=%d", + chrs = utf8_wcstombs(s, unicodeString, sizeof(s))); + if (chrs >= 0) + s[chrs] = '\0'; /* GRRRRRRRR */ + LOGINF("s='%s'", s); + LOGINF("utf8_mbstowcs=%d", chrs = utf8_mbstowcs(unicode2, s, 100)); + if (chrs >= 0) + unicode2[chrs] = 0; /* GRRRRRRRR */ + if (memcmp(unicodeString, unicode2, sizeof(unicodeString)) == 0) + LOGINF("strings match... good"); + else + LOGINF("strings did not match!!"); +} +#endif + +static void +busInfo_clear(void *v) +{ + VISORCHIPSET_BUS_INFO *p = (VISORCHIPSET_BUS_INFO *) (v); + + if (p->procObject) { + proc_DestroyObject(p->procObject); + p->procObject = NULL; + } + kfree(p->name); + p->name = NULL; + + kfree(p->description); + p->description = NULL; + + p->state.created = 0; + memset(p, 0, sizeof(VISORCHIPSET_BUS_INFO)); +} + +static void +devInfo_clear(void *v) +{ + VISORCHIPSET_DEVICE_INFO *p = (VISORCHIPSET_DEVICE_INFO *) (v); + p->state.created = 0; + memset(p, 0, sizeof(VISORCHIPSET_DEVICE_INFO)); +} + +static U8 +check_chipset_events(void) +{ + int i; + U8 send_msg = 1; + /* Check events to determine if response should be sent */ + for (i = 0; i < MAX_CHIPSET_EVENTS; i++) + send_msg &= chipset_events[i]; + return send_msg; +} + +static void +clear_chipset_events(void) +{ + int i; + /* Clear chipset_events */ + for (i = 0; i < MAX_CHIPSET_EVENTS; i++) + chipset_events[i] = 0; +} + +void +visorchipset_register_busdev_server(VISORCHIPSET_BUSDEV_NOTIFIERS *notifiers, + VISORCHIPSET_BUSDEV_RESPONDERS *responders, + ULTRA_VBUS_DEVICEINFO *driverInfo) +{ + LOCKSEM_UNINTERRUPTIBLE(&NotifierLock); + if (notifiers == NULL) { + memset(&BusDev_Server_Notifiers, 0, + sizeof(BusDev_Server_Notifiers)); + serverregistered = 0; /* clear flag */ + } else { + BusDev_Server_Notifiers = *notifiers; + serverregistered = 1; /* set flag */ + } + if (responders) + *responders = BusDev_Responders; + if (driverInfo) + BusDeviceInfo_Init(driverInfo, "chipset", "visorchipset", + VERSION, NULL, __DATE__, __TIME__); + + UNLOCKSEM(&NotifierLock); +} +EXPORT_SYMBOL_GPL(visorchipset_register_busdev_server); + +void +visorchipset_register_busdev_client(VISORCHIPSET_BUSDEV_NOTIFIERS *notifiers, + VISORCHIPSET_BUSDEV_RESPONDERS *responders, + ULTRA_VBUS_DEVICEINFO *driverInfo) +{ + LOCKSEM_UNINTERRUPTIBLE(&NotifierLock); + if (notifiers == NULL) { + memset(&BusDev_Client_Notifiers, 0, + sizeof(BusDev_Client_Notifiers)); + clientregistered = 0; /* clear flag */ + } else { + BusDev_Client_Notifiers = *notifiers; + clientregistered = 1; /* set flag */ + } + if (responders) + *responders = BusDev_Responders; + if (driverInfo) + BusDeviceInfo_Init(driverInfo, "chipset(bolts)", "visorchipset", + VERSION, NULL, __DATE__, __TIME__); + UNLOCKSEM(&NotifierLock); +} +EXPORT_SYMBOL_GPL(visorchipset_register_busdev_client); + +static void +cleanup_controlvm_structures(void) +{ + VISORCHIPSET_BUS_INFO *bi; + VISORCHIPSET_DEVICE_INFO *di; + + list_for_each_entry(bi, &BusInfoList, entry) { + busInfo_clear(bi); + list_del(&bi->entry); + kfree(bi); + } + + list_for_each_entry(di, &DevInfoList, entry) { + devInfo_clear(di); + list_del(&di->entry); + kfree(di); + } +} + +static void +chipset_init(CONTROLVM_MESSAGE *inmsg) +{ + static int chipset_inited; + ULTRA_CHIPSET_FEATURE features = 0; + int rc = CONTROLVM_RESP_SUCCESS; + + POSTCODE_LINUX_2(CHIPSET_INIT_ENTRY_PC, POSTCODE_SEVERITY_INFO); + if (chipset_inited) { + LOGERR("CONTROLVM_CHIPSET_INIT Failed: Already Done."); + RETINT(-CONTROLVM_RESP_ERROR_ALREADY_DONE); + } + 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.initChipset. + features & ULTRA_CHIPSET_FEATURE_PARA_HOTPLUG; + + /* Set the "reply" bit so Command knows this is a + * features-aware driver. */ + features |= ULTRA_CHIPSET_FEATURE_REPLY; + +Away: + if (rc < 0) + cleanup_controlvm_structures(); + if (inmsg->hdr.Flags.responseExpected) + controlvm_respond_chipset_init(&inmsg->hdr, rc, features); +} + +static void +controlvm_init_response(CONTROLVM_MESSAGE *msg, + CONTROLVM_MESSAGE_HEADER *msgHdr, int response) +{ + memset(msg, 0, sizeof(CONTROLVM_MESSAGE)); + memcpy(&msg->hdr, msgHdr, sizeof(CONTROLVM_MESSAGE_HEADER)); + msg->hdr.PayloadBytes = 0; + msg->hdr.PayloadVmOffset = 0; + msg->hdr.PayloadMaxBytes = 0; + if (response < 0) { + msg->hdr.Flags.failed = 1; + msg->hdr.CompletionStatus = (U32) (-response); + } +} + +static void +controlvm_respond(CONTROLVM_MESSAGE_HEADER *msgHdr, int response) +{ + CONTROLVM_MESSAGE outmsg; + if (!ControlVm_channel) + return; + controlvm_init_response(&outmsg, msgHdr, response); + /* For DiagPool channel DEVICE_CHANGESTATE, we need to send + * back the deviceChangeState structure in the packet. */ + if (msgHdr->Id == CONTROLVM_DEVICE_CHANGESTATE + && g_DeviceChangeStatePacket.deviceChangeState.busNo == + g_diagpoolBusNo + && g_DeviceChangeStatePacket.deviceChangeState.devNo == + g_diagpoolDevNo) + outmsg.cmd = g_DeviceChangeStatePacket; + if (outmsg.hdr.Flags.testMessage == 1) { + LOGINF("%s controlvm_msg=0x%x response=%d for test message", + __func__, outmsg.hdr.Id, response); + return; + } + if (!visorchannel_signalinsert(ControlVm_channel, + CONTROLVM_QUEUE_REQUEST, &outmsg)) { + LOGERR("signalinsert failed!"); + return; + } +} + +static void +controlvm_respond_chipset_init(CONTROLVM_MESSAGE_HEADER *msgHdr, int response, + ULTRA_CHIPSET_FEATURE features) +{ + CONTROLVM_MESSAGE outmsg; + if (!ControlVm_channel) + return; + controlvm_init_response(&outmsg, msgHdr, response); + outmsg.cmd.initChipset.features = features; + if (!visorchannel_signalinsert(ControlVm_channel, + CONTROLVM_QUEUE_REQUEST, &outmsg)) { + LOGERR("signalinsert failed!"); + return; + } +} + +static void +controlvm_respond_physdev_changestate(CONTROLVM_MESSAGE_HEADER *msgHdr, + int response, ULTRA_SEGMENT_STATE state) +{ + CONTROLVM_MESSAGE outmsg; + if (!ControlVm_channel) + return; + controlvm_init_response(&outmsg, msgHdr, response); + outmsg.cmd.deviceChangeState.state = state; + outmsg.cmd.deviceChangeState.flags.physicalDevice = 1; + if (!visorchannel_signalinsert(ControlVm_channel, + CONTROLVM_QUEUE_REQUEST, &outmsg)) { + LOGERR("signalinsert failed!"); + return; + } +} + +void +visorchipset_save_message(CONTROLVM_MESSAGE *msg, CRASH_OBJ_TYPE type) +{ + U32 localSavedCrashMsgOffset; + U16 localSavedCrashMsgCount; + + /* get saved message count */ + if (visorchannel_read(ControlVm_channel, + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, + SavedCrashMsgCount), + &localSavedCrashMsgCount, sizeof(U16)) < 0) { + LOGERR("failed to get Saved Message Count"); + POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + + if (localSavedCrashMsgCount != CONTROLVM_CRASHMSG_MAX) { + LOGERR("Saved Message Count incorrect %d", + localSavedCrashMsgCount); + POSTCODE_LINUX_3(CRASH_DEV_COUNT_FAILURE_PC, + localSavedCrashMsgCount, + POSTCODE_SEVERITY_ERR); + return; + } + + /* get saved crash message offset */ + if (visorchannel_read(ControlVm_channel, + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, + SavedCrashMsgOffset), + &localSavedCrashMsgOffset, sizeof(U32)) < 0) { + LOGERR("failed to get Saved Message Offset"); + POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + + if (type == CRASH_bus) { + if (visorchannel_write(ControlVm_channel, + localSavedCrashMsgOffset, + msg, sizeof(CONTROLVM_MESSAGE)) < 0) { + LOGERR("SAVE_MSG_BUS_FAILURE: Failed to write CrashCreateBusMsg!"); + POSTCODE_LINUX_2(SAVE_MSG_BUS_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + } else { + if (visorchannel_write(ControlVm_channel, + localSavedCrashMsgOffset + + sizeof(CONTROLVM_MESSAGE), msg, + sizeof(CONTROLVM_MESSAGE)) < 0) { + LOGERR("SAVE_MSG_DEV_FAILURE: Failed to write CrashCreateDevMsg!"); + POSTCODE_LINUX_2(SAVE_MSG_DEV_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + } +} +EXPORT_SYMBOL_GPL(visorchipset_save_message); + +static void +bus_responder(CONTROLVM_ID cmdId, ulong busNo, int response) +{ + VISORCHIPSET_BUS_INFO *p = NULL; + BOOL need_clear = FALSE; + + p = findbus(&BusInfoList, busNo); + if (!p) { + LOGERR("internal error busNo=%lu", busNo); + return; + } + if (response < 0) { + if ((cmdId == CONTROLVM_BUS_CREATE) && + (response != (-CONTROLVM_RESP_ERROR_ALREADY_DONE))) + /* undo the row we just created... */ + delbusdevices(&DevInfoList, busNo); + } else { + if (cmdId == CONTROLVM_BUS_CREATE) + p->state.created = 1; + if (cmdId == CONTROLVM_BUS_DESTROY) + need_clear = TRUE; + } + + if (p->pendingMsgHdr.Id == CONTROLVM_INVALID) { + LOGERR("bus_responder no pending msg"); + return; /* no controlvm response needed */ + } + if (p->pendingMsgHdr.Id != (U32) cmdId) { + LOGERR("expected=%d, found=%d", cmdId, p->pendingMsgHdr.Id); + return; + } + controlvm_respond(&p->pendingMsgHdr, response); + p->pendingMsgHdr.Id = CONTROLVM_INVALID; + if (need_clear) { + busInfo_clear(p); + delbusdevices(&DevInfoList, busNo); + } +} + +static void +device_changestate_responder(CONTROLVM_ID cmdId, + ulong busNo, ulong devNo, int response, + ULTRA_SEGMENT_STATE responseState) +{ + VISORCHIPSET_DEVICE_INFO *p = NULL; + CONTROLVM_MESSAGE outmsg; + + if (!ControlVm_channel) + return; + + p = finddevice(&DevInfoList, busNo, devNo); + if (!p) { + LOGERR("internal error; busNo=%lu, devNo=%lu", busNo, devNo); + return; + } + if (p->pendingMsgHdr.Id == CONTROLVM_INVALID) { + LOGERR("device_responder no pending msg"); + return; /* no controlvm response needed */ + } + if (p->pendingMsgHdr.Id != cmdId) { + LOGERR("expected=%d, found=%d", cmdId, p->pendingMsgHdr.Id); + return; + } + + controlvm_init_response(&outmsg, &p->pendingMsgHdr, response); + + outmsg.cmd.deviceChangeState.busNo = busNo; + outmsg.cmd.deviceChangeState.devNo = devNo; + outmsg.cmd.deviceChangeState.state = responseState; + + if (!visorchannel_signalinsert(ControlVm_channel, + CONTROLVM_QUEUE_REQUEST, &outmsg)) { + LOGERR("signalinsert failed!"); + return; + } + + p->pendingMsgHdr.Id = CONTROLVM_INVALID; +} + +static void +device_responder(CONTROLVM_ID cmdId, ulong busNo, ulong devNo, int response) +{ + VISORCHIPSET_DEVICE_INFO *p = NULL; + BOOL need_clear = FALSE; + + p = finddevice(&DevInfoList, busNo, devNo); + if (!p) { + LOGERR("internal error; busNo=%lu, devNo=%lu", busNo, devNo); + return; + } + if (response >= 0) { + if (cmdId == CONTROLVM_DEVICE_CREATE) + p->state.created = 1; + if (cmdId == CONTROLVM_DEVICE_DESTROY) + need_clear = TRUE; + } + + if (p->pendingMsgHdr.Id == CONTROLVM_INVALID) { + LOGERR("device_responder no pending msg"); + return; /* no controlvm response needed */ + } + if (p->pendingMsgHdr.Id != (U32) cmdId) { + LOGERR("expected=%d, found=%d", cmdId, p->pendingMsgHdr.Id); + return; + } + controlvm_respond(&p->pendingMsgHdr, response); + p->pendingMsgHdr.Id = CONTROLVM_INVALID; + if (need_clear) + devInfo_clear(p); +} + +static void +bus_epilog(U32 busNo, + U32 cmd, CONTROLVM_MESSAGE_HEADER *msgHdr, + int response, BOOL needResponse) +{ + BOOL notified = FALSE; + + VISORCHIPSET_BUS_INFO *pBusInfo = findbus(&BusInfoList, busNo); + + if (!pBusInfo) { + LOGERR("HUH? bad busNo=%d", busNo); + return; + } + if (needResponse) { + memcpy(&pBusInfo->pendingMsgHdr, msgHdr, + sizeof(CONTROLVM_MESSAGE_HEADER)); + } else + pBusInfo->pendingMsgHdr.Id = CONTROLVM_INVALID; + + LOCKSEM_UNINTERRUPTIBLE(&NotifierLock); + if (response == CONTROLVM_RESP_SUCCESS) { + switch (cmd) { + case CONTROLVM_BUS_CREATE: + /* We can't tell from the bus_create + * information which of our 2 bus flavors the + * devices on this bus will ultimately end up. + * FORTUNATELY, it turns out it is harmless to + * send the bus_create to both of them. We can + * narrow things down a little bit, though, + * because we know: - BusDev_Server can handle + * either server or client devices + * - BusDev_Client can handle ONLY client + * devices */ + if (BusDev_Server_Notifiers.bus_create) { + (*BusDev_Server_Notifiers.bus_create) (busNo); + notified = TRUE; + } + if ((!pBusInfo->flags.server) /*client */ && + BusDev_Client_Notifiers.bus_create) { + (*BusDev_Client_Notifiers.bus_create) (busNo); + notified = TRUE; + } + break; + case CONTROLVM_BUS_DESTROY: + if (BusDev_Server_Notifiers.bus_destroy) { + (*BusDev_Server_Notifiers.bus_destroy) (busNo); + notified = TRUE; + } + if ((!pBusInfo->flags.server) /*client */ && + BusDev_Client_Notifiers.bus_destroy) { + (*BusDev_Client_Notifiers.bus_destroy) (busNo); + notified = TRUE; + } + break; + } + } + if (notified) + /* The callback function just called above is responsible + * for calling the appropriate VISORCHIPSET_BUSDEV_RESPONDERS + * function, which will call bus_responder() + */ + ; + else + bus_responder(cmd, busNo, response); + UNLOCKSEM(&NotifierLock); +} + +static void +device_epilog(U32 busNo, U32 devNo, ULTRA_SEGMENT_STATE state, U32 cmd, + CONTROLVM_MESSAGE_HEADER *msgHdr, int response, + BOOL needResponse, BOOL for_visorbus) +{ + VISORCHIPSET_BUSDEV_NOTIFIERS *notifiers = NULL; + BOOL notified = FALSE; + + VISORCHIPSET_DEVICE_INFO *pDevInfo = + finddevice(&DevInfoList, busNo, devNo); + char *envp[] = { + "SPARSP_DIAGPOOL_PAUSED_STATE = 1", + NULL + }; + + if (!pDevInfo) { + LOGERR("HUH? bad busNo=%d, devNo=%d", busNo, devNo); + return; + } + if (for_visorbus) + notifiers = &BusDev_Server_Notifiers; + else + notifiers = &BusDev_Client_Notifiers; + if (needResponse) { + memcpy(&pDevInfo->pendingMsgHdr, msgHdr, + sizeof(CONTROLVM_MESSAGE_HEADER)); + } else + pDevInfo->pendingMsgHdr.Id = CONTROLVM_INVALID; + + LOCKSEM_UNINTERRUPTIBLE(&NotifierLock); + if (response >= 0) { + switch (cmd) { + case CONTROLVM_DEVICE_CREATE: + if (notifiers->device_create) { + (*notifiers->device_create) (busNo, devNo); + notified = TRUE; + } + break; + case CONTROLVM_DEVICE_CHANGESTATE: + /* ServerReady / ServerRunning / SegmentStateRunning */ + if (state.Alive == SegmentStateRunning.Alive && + state.Operating == SegmentStateRunning.Operating) { + if (notifiers->device_resume) { + (*notifiers->device_resume) (busNo, + devNo); + notified = TRUE; + } + } + /* ServerNotReady / ServerLost / SegmentStateStandby */ + else if (state.Alive == SegmentStateStandby.Alive && + state.Operating == + SegmentStateStandby.Operating) { + /* technically this is standby case + * where server is lost + */ + if (notifiers->device_pause) { + (*notifiers->device_pause) (busNo, + devNo); + notified = TRUE; + } + } else if (state.Alive == SegmentStatePaused.Alive && + state.Operating == + SegmentStatePaused.Operating) { + /* this is lite pause where channel is + * still valid just 'pause' of it + */ + if (busNo == g_diagpoolBusNo + && devNo == g_diagpoolDevNo) { + LOGINF("DEVICE_CHANGESTATE(DiagpoolChannel busNo=%d devNo=%d is pausing...)", + busNo, devNo); + /* this will trigger the + * diag_shutdown.sh script in + * the visorchipset hotplug */ + kobject_uevent_env + (&Visorchipset_platform_device.dev. + kobj, KOBJ_ONLINE, envp); + } + } + break; + case CONTROLVM_DEVICE_DESTROY: + if (notifiers->device_destroy) { + (*notifiers->device_destroy) (busNo, devNo); + notified = TRUE; + } + break; + } + } + if (notified) + /* The callback function just called above is responsible + * for calling the appropriate VISORCHIPSET_BUSDEV_RESPONDERS + * function, which will call device_responder() + */ + ; + else + device_responder(cmd, busNo, devNo, response); + UNLOCKSEM(&NotifierLock); +} + +static void +bus_create(CONTROLVM_MESSAGE *inmsg) +{ + CONTROLVM_MESSAGE_PACKET *cmd = &inmsg->cmd; + ulong busNo = cmd->createBus.busNo; + int rc = CONTROLVM_RESP_SUCCESS; + VISORCHIPSET_BUS_INFO *pBusInfo = NULL; + + + pBusInfo = findbus(&BusInfoList, busNo); + if (pBusInfo && (pBusInfo->state.created == 1)) { + LOGERR("CONTROLVM_BUS_CREATE Failed: bus %lu already exists", + busNo); + POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, busNo, + POSTCODE_SEVERITY_ERR); + RETINT(-CONTROLVM_RESP_ERROR_ALREADY_DONE); + } + pBusInfo = kmalloc(sizeof(VISORCHIPSET_BUS_INFO), GFP_KERNEL); + if (pBusInfo == NULL) { + LOGERR("CONTROLVM_BUS_CREATE Failed: bus %lu kmalloc failed", + busNo); + POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, busNo, + POSTCODE_SEVERITY_ERR); + RETINT(-CONTROLVM_RESP_ERROR_KMALLOC_FAILED); + } + + memset(pBusInfo, 0, sizeof(VISORCHIPSET_BUS_INFO)); + INIT_LIST_HEAD(&pBusInfo->entry); + pBusInfo->busNo = busNo; + pBusInfo->devNo = cmd->createBus.deviceCount; + + POSTCODE_LINUX_3(BUS_CREATE_ENTRY_PC, busNo, POSTCODE_SEVERITY_INFO); + + if (inmsg->hdr.Flags.testMessage == 1) + pBusInfo->chanInfo.addrType = ADDRTYPE_localTest; + else + pBusInfo->chanInfo.addrType = ADDRTYPE_localPhysical; + + pBusInfo->flags.server = inmsg->hdr.Flags.server; + pBusInfo->chanInfo.channelAddr = cmd->createBus.channelAddr; + pBusInfo->chanInfo.nChannelBytes = cmd->createBus.channelBytes; + pBusInfo->chanInfo.channelTypeGuid = cmd->createBus.busDataTypeGuid; + pBusInfo->chanInfo.channelInstGuid = cmd->createBus.busInstGuid; + + list_add(&pBusInfo->entry, &BusInfoList); + + POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, busNo, POSTCODE_SEVERITY_INFO); + +Away: + bus_epilog(busNo, CONTROLVM_BUS_CREATE, &inmsg->hdr, + rc, inmsg->hdr.Flags.responseExpected == 1); +} + +static void +bus_destroy(CONTROLVM_MESSAGE *inmsg) +{ + CONTROLVM_MESSAGE_PACKET *cmd = &inmsg->cmd; + ulong busNo = cmd->destroyBus.busNo; + VISORCHIPSET_BUS_INFO *pBusInfo; + int rc = CONTROLVM_RESP_SUCCESS; + + pBusInfo = findbus(&BusInfoList, busNo); + if (!pBusInfo) { + LOGERR("CONTROLVM_BUS_DESTROY Failed: bus %lu invalid", busNo); + RETINT(-CONTROLVM_RESP_ERROR_BUS_INVALID); + } + if (pBusInfo->state.created == 0) { + LOGERR("CONTROLVM_BUS_DESTROY Failed: bus %lu already destroyed", + busNo); + RETINT(-CONTROLVM_RESP_ERROR_ALREADY_DONE); + } + +Away: + bus_epilog(busNo, CONTROLVM_BUS_DESTROY, &inmsg->hdr, + rc, inmsg->hdr.Flags.responseExpected == 1); +} + +static void +bus_configure(CONTROLVM_MESSAGE *inmsg, PARSER_CONTEXT *parser_ctx) +{ + CONTROLVM_MESSAGE_PACKET *cmd = &inmsg->cmd; + ulong busNo = cmd->configureBus.busNo; + VISORCHIPSET_BUS_INFO *pBusInfo = NULL; + int rc = CONTROLVM_RESP_SUCCESS; + char s[99]; + + busNo = cmd->configureBus.busNo; + POSTCODE_LINUX_3(BUS_CONFIGURE_ENTRY_PC, busNo, POSTCODE_SEVERITY_INFO); + + pBusInfo = findbus(&BusInfoList, busNo); + if (!pBusInfo) { + LOGERR("CONTROLVM_BUS_CONFIGURE Failed: bus %lu invalid", + busNo); + POSTCODE_LINUX_3(BUS_CONFIGURE_FAILURE_PC, busNo, + POSTCODE_SEVERITY_ERR); + RETINT(-CONTROLVM_RESP_ERROR_BUS_INVALID); + } + if (pBusInfo->state.created == 0) { + LOGERR("CONTROLVM_BUS_CONFIGURE Failed: Invalid bus %lu - not created yet", + busNo); + POSTCODE_LINUX_3(BUS_CONFIGURE_FAILURE_PC, busNo, + POSTCODE_SEVERITY_ERR); + RETINT(-CONTROLVM_RESP_ERROR_BUS_INVALID); + } + /* TBD - add this check to other commands also... */ + if (pBusInfo->pendingMsgHdr.Id != CONTROLVM_INVALID) { + LOGERR("CONTROLVM_BUS_CONFIGURE Failed: bus %lu MsgId=%u outstanding", + busNo, (uint) pBusInfo->pendingMsgHdr.Id); + POSTCODE_LINUX_3(BUS_CONFIGURE_FAILURE_PC, busNo, + POSTCODE_SEVERITY_ERR); + RETINT(-CONTROLVM_RESP_ERROR_MESSAGE_ID_INVALID_FOR_CLIENT); + } + + pBusInfo->partitionHandle = cmd->configureBus.guestHandle; + pBusInfo->partitionGuid = parser_id_get(parser_ctx); + parser_param_start(parser_ctx, PARSERSTRING_NAME); + pBusInfo->name = parser_string_get(parser_ctx); + + visorchannel_GUID_id(&pBusInfo->partitionGuid, s); + pBusInfo->procObject = + proc_CreateObject(PartitionType, s, (void *) (pBusInfo)); + if (pBusInfo->procObject == NULL) { + LOGERR("CONTROLVM_BUS_CONFIGURE Failed: busNo=%lu failed to create /proc entry", + busNo); + POSTCODE_LINUX_3(BUS_CONFIGURE_FAILURE_PC, busNo, + POSTCODE_SEVERITY_ERR); + RETINT(-CONTROLVM_RESP_ERROR_KMALLOC_FAILED); + } + POSTCODE_LINUX_3(BUS_CONFIGURE_EXIT_PC, busNo, POSTCODE_SEVERITY_INFO); +Away: + bus_epilog(busNo, CONTROLVM_BUS_CONFIGURE, &inmsg->hdr, + rc, inmsg->hdr.Flags.responseExpected == 1); +} + +static void +my_device_create(CONTROLVM_MESSAGE *inmsg) +{ + CONTROLVM_MESSAGE_PACKET *cmd = &inmsg->cmd; + ulong busNo = cmd->createDevice.busNo; + ulong devNo = cmd->createDevice.devNo; + VISORCHIPSET_DEVICE_INFO *pDevInfo = NULL; + VISORCHIPSET_BUS_INFO *pBusInfo = NULL; + int rc = CONTROLVM_RESP_SUCCESS; + + pDevInfo = finddevice(&DevInfoList, busNo, devNo); + if (pDevInfo && (pDevInfo->state.created == 1)) { + LOGERR("CONTROLVM_DEVICE_CREATE Failed: busNo=%lu, devNo=%lu already exists", + busNo, devNo); + POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo, + POSTCODE_SEVERITY_ERR); + RETINT(-CONTROLVM_RESP_ERROR_ALREADY_DONE); + } + pBusInfo = findbus(&BusInfoList, busNo); + if (!pBusInfo) { + LOGERR("CONTROLVM_DEVICE_CREATE Failed: Invalid bus %lu - out of range", + busNo); + POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo, + POSTCODE_SEVERITY_ERR); + RETINT(-CONTROLVM_RESP_ERROR_BUS_INVALID); + } + if (pBusInfo->state.created == 0) { + LOGERR("CONTROLVM_DEVICE_CREATE Failed: Invalid bus %lu - not created yet", + busNo); + POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo, + POSTCODE_SEVERITY_ERR); + RETINT(-CONTROLVM_RESP_ERROR_BUS_INVALID); + } + pDevInfo = kmalloc(sizeof(VISORCHIPSET_DEVICE_INFO), GFP_KERNEL); + if (pDevInfo == NULL) { + LOGERR("CONTROLVM_DEVICE_CREATE Failed: busNo=%lu, devNo=%lu kmaloc failed", + busNo, devNo); + POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo, + POSTCODE_SEVERITY_ERR); + RETINT(-CONTROLVM_RESP_ERROR_KMALLOC_FAILED); + } + memset(pDevInfo, 0, sizeof(VISORCHIPSET_DEVICE_INFO)); + INIT_LIST_HEAD(&pDevInfo->entry); + pDevInfo->busNo = busNo; + pDevInfo->devNo = devNo; + pDevInfo->devInstGuid = cmd->createDevice.devInstGuid; + POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, devNo, busNo, + POSTCODE_SEVERITY_INFO); + + if (inmsg->hdr.Flags.testMessage == 1) + pDevInfo->chanInfo.addrType = ADDRTYPE_localTest; + else + pDevInfo->chanInfo.addrType = ADDRTYPE_localPhysical; + pDevInfo->chanInfo.channelAddr = cmd->createDevice.channelAddr; + pDevInfo->chanInfo.nChannelBytes = cmd->createDevice.channelBytes; + pDevInfo->chanInfo.channelTypeGuid = cmd->createDevice.dataTypeGuid; + pDevInfo->chanInfo.intr = cmd->createDevice.intr; + list_add(&pDevInfo->entry, &DevInfoList); + POSTCODE_LINUX_4(DEVICE_CREATE_EXIT_PC, devNo, busNo, + POSTCODE_SEVERITY_INFO); +Away: + /* get the bus and devNo for DiagPool channel */ + if (is_diagpool_channel(pDevInfo->chanInfo.channelTypeGuid)) { + g_diagpoolBusNo = busNo; + g_diagpoolDevNo = devNo; + LOGINF("CONTROLVM_DEVICE_CREATE for DiagPool channel: busNo=%lu, devNo=%lu", + g_diagpoolBusNo, g_diagpoolDevNo); + } + device_epilog(busNo, devNo, SegmentStateRunning, + CONTROLVM_DEVICE_CREATE, &inmsg->hdr, rc, + inmsg->hdr.Flags.responseExpected == 1, + FOR_VISORBUS(pDevInfo->chanInfo.channelTypeGuid)); +} + +static void +my_device_changestate(CONTROLVM_MESSAGE *inmsg) +{ + CONTROLVM_MESSAGE_PACKET *cmd = &inmsg->cmd; + ulong busNo = cmd->deviceChangeState.busNo; + ulong devNo = cmd->deviceChangeState.devNo; + ULTRA_SEGMENT_STATE state = cmd->deviceChangeState.state; + VISORCHIPSET_DEVICE_INFO *pDevInfo = NULL; + int rc = CONTROLVM_RESP_SUCCESS; + + pDevInfo = finddevice(&DevInfoList, busNo, devNo); + if (!pDevInfo) { + LOGERR("CONTROLVM_DEVICE_CHANGESTATE Failed: busNo=%lu, devNo=%lu invalid (doesn't exist)", + busNo, devNo); + POSTCODE_LINUX_4(DEVICE_CHANGESTATE_FAILURE_PC, devNo, busNo, + POSTCODE_SEVERITY_ERR); + RETINT(-CONTROLVM_RESP_ERROR_DEVICE_INVALID); + } + if (pDevInfo->state.created == 0) { + LOGERR("CONTROLVM_DEVICE_CHANGESTATE Failed: busNo=%lu, devNo=%lu invalid (not created)", + busNo, devNo); + POSTCODE_LINUX_4(DEVICE_CHANGESTATE_FAILURE_PC, devNo, busNo, + POSTCODE_SEVERITY_ERR); + RETINT(-CONTROLVM_RESP_ERROR_DEVICE_INVALID); + } +Away: + if ((rc >= CONTROLVM_RESP_SUCCESS) && pDevInfo) + device_epilog(busNo, devNo, state, CONTROLVM_DEVICE_CHANGESTATE, + &inmsg->hdr, rc, + inmsg->hdr.Flags.responseExpected == 1, + FOR_VISORBUS(pDevInfo->chanInfo.channelTypeGuid)); +} + +static void +my_device_destroy(CONTROLVM_MESSAGE *inmsg) +{ + CONTROLVM_MESSAGE_PACKET *cmd = &inmsg->cmd; + ulong busNo = cmd->destroyDevice.busNo; + ulong devNo = cmd->destroyDevice.devNo; + VISORCHIPSET_DEVICE_INFO *pDevInfo = NULL; + int rc = CONTROLVM_RESP_SUCCESS; + + pDevInfo = finddevice(&DevInfoList, busNo, devNo); + if (!pDevInfo) { + LOGERR("CONTROLVM_DEVICE_DESTROY Failed: busNo=%lu, devNo=%lu invalid", + busNo, devNo); + RETINT(-CONTROLVM_RESP_ERROR_DEVICE_INVALID); + } + if (pDevInfo->state.created == 0) { + LOGERR("CONTROLVM_DEVICE_DESTROY Failed: busNo=%lu, devNo=%lu already destroyed", + busNo, devNo); + RETINT(-CONTROLVM_RESP_ERROR_ALREADY_DONE); + } + +Away: + if ((rc >= CONTROLVM_RESP_SUCCESS) && pDevInfo) + device_epilog(busNo, devNo, SegmentStateRunning, + CONTROLVM_DEVICE_DESTROY, &inmsg->hdr, rc, + inmsg->hdr.Flags.responseExpected == 1, + FOR_VISORBUS(pDevInfo->chanInfo.channelTypeGuid)); +} + +/* 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(HOSTADDRESS phys_addr, U64 offset, U32 bytes, + CONTROLVM_PAYLOAD_INFO *info) +{ + U8 *payload = NULL; + int rc = CONTROLVM_RESP_SUCCESS; + + if (info == NULL) { + LOGERR("HUH ? CONTROLVM_PAYLOAD_INIT Failed : Programmer check at %s:%d", + __FILE__, __LINE__); + RETINT(-CONTROLVM_RESP_ERROR_PAYLOAD_INVALID); + } + memset(info, 0, sizeof(CONTROLVM_PAYLOAD_INFO)); + if ((offset == 0) || (bytes == 0)) { + LOGERR("CONTROLVM_PAYLOAD_INIT Failed: RequestPayloadOffset=%llu RequestPayloadBytes=%llu!", + (u64) offset, (u64) bytes); + RETINT(-CONTROLVM_RESP_ERROR_PAYLOAD_INVALID); + } + payload = ioremap_cache(phys_addr + offset, bytes); + if (payload == NULL) { + LOGERR("CONTROLVM_PAYLOAD_INIT Failed: ioremap_cache %llu for %llu bytes failed", + (u64) offset, (u64) bytes); + RETINT(-CONTROLVM_RESP_ERROR_IOREMAP_FAILED); + } + + info->offset = offset; + info->bytes = bytes; + info->ptr = payload; + LOGINF("offset=%llu, bytes=%lu, ptr=%p", + (u64) (info->offset), (ulong) (info->bytes), info->ptr); + +Away: + if (rc < 0) { + if (payload != NULL) { + iounmap(payload); + payload = NULL; + } + } + return rc; +} + +static void +destroy_controlvm_payload_info(CONTROLVM_PAYLOAD_INFO *info) +{ + if (info->ptr != NULL) { + iounmap(info->ptr); + info->ptr = NULL; + } + memset(info, 0, sizeof(CONTROLVM_PAYLOAD_INFO)); +} + +static void +initialize_controlvm_payload(void) +{ + HOSTADDRESS phys_addr = visorchannel_get_physaddr(ControlVm_channel); + U64 payloadOffset = 0; + U32 payloadBytes = 0; + if (visorchannel_read(ControlVm_channel, + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, + RequestPayloadOffset), + &payloadOffset, sizeof(payloadOffset)) < 0) { + LOGERR("CONTROLVM_PAYLOAD_INIT Failed to read controlvm channel!"); + POSTCODE_LINUX_2(CONTROLVM_INIT_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + if (visorchannel_read(ControlVm_channel, + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, + RequestPayloadBytes), + &payloadBytes, sizeof(payloadBytes)) < 0) { + LOGERR("CONTROLVM_PAYLOAD_INIT Failed to read controlvm channel!"); + POSTCODE_LINUX_2(CONTROLVM_INIT_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + initialize_controlvm_payload_info(phys_addr, + payloadOffset, payloadBytes, + &ControlVm_payload_info); +} + +/* Send ACTION=online for DEVPATH=/sys/devices/platform/visorchipset. + * Returns CONTROLVM_RESP_xxx code. + */ +int +visorchipset_chipset_ready(void) +{ + kobject_uevent(&Visorchipset_platform_device.dev.kobj, KOBJ_ONLINE); + return CONTROLVM_RESP_SUCCESS; +} +EXPORT_SYMBOL_GPL(visorchipset_chipset_ready); + +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; +} +EXPORT_SYMBOL_GPL(visorchipset_chipset_selftest); + +/* Send ACTION=offline for DEVPATH=/sys/devices/platform/visorchipset. + * Returns CONTROLVM_RESP_xxx code. + */ +int +visorchipset_chipset_notready(void) +{ + kobject_uevent(&Visorchipset_platform_device.dev.kobj, KOBJ_OFFLINE); + return CONTROLVM_RESP_SUCCESS; +} +EXPORT_SYMBOL_GPL(visorchipset_chipset_notready); + +static void +chipset_ready(CONTROLVM_MESSAGE_HEADER *msgHdr) +{ + int rc = visorchipset_chipset_ready(); + if (rc != CONTROLVM_RESP_SUCCESS) + rc = -rc; + if (msgHdr->Flags.responseExpected && !visorchipset_holdchipsetready) + controlvm_respond(msgHdr, rc); + if (msgHdr->Flags.responseExpected && visorchipset_holdchipsetready) { + /* Send CHIPSET_READY response when all modules have been loaded + * and disks mounted for the partition + */ + g_ChipSetMsgHdr = *msgHdr; + LOGINF("Holding CHIPSET_READY response"); + } +} + +static void +chipset_selftest(CONTROLVM_MESSAGE_HEADER *msgHdr) +{ + int rc = visorchipset_chipset_selftest(); + if (rc != CONTROLVM_RESP_SUCCESS) + rc = -rc; + if (msgHdr->Flags.responseExpected) + controlvm_respond(msgHdr, rc); +} + +static void +chipset_notready(CONTROLVM_MESSAGE_HEADER *msgHdr) +{ + int rc = visorchipset_chipset_notready(); + if (rc != CONTROLVM_RESP_SUCCESS) + rc = -rc; + if (msgHdr->Flags.responseExpected) + controlvm_respond(msgHdr, 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(CONTROLVM_MESSAGE *msg) +{ + if (visorchannel_signalremove(ControlVm_channel, + CONTROLVM_QUEUE_EVENT, msg)) { + /* got a message */ + if (msg->hdr.Flags.testMessage == 1) { + LOGERR("ignoring bad CONTROLVM_QUEUE_EVENT msg with controlvm_msg_id=0x%x because Flags.testMessage is nonsensical (=1)", msg->hdr.Id); + return FALSE; + } else + 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 + PARAHOTPLUG_TIMEOUT_MS * HZ / 1000; +} + +/* + * 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(CONTROLVM_MESSAGE *msg) +{ + struct parahotplug_request *req = + kmalloc(sizeof(struct parahotplug_request), + GFP_KERNEL|__GFP_NORETRY); + if (req == NULL) + 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) +{ + 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->deviceChangeState.state.Active); + sprintf(env_bus, "SPAR_PARAHOTPLUG_BUS=%d", + cmd->deviceChangeState.busNo); + sprintf(env_dev, "SPAR_PARAHOTPLUG_DEVICE=%d", + cmd->deviceChangeState.devNo >> 3); + sprintf(env_func, "SPAR_PARAHOTPLUG_FUNCTION=%d", + cmd->deviceChangeState.devNo & 0x7); + + LOGINF("parahotplug_request_kickoff: state=%d, bdf=%d/%d/%d, id=%u\n", + cmd->deviceChangeState.state.Active, + cmd->deviceChangeState.busNo, cmd->deviceChangeState.devNo >> 3, + cmd->deviceChangeState.devNo & 7, req->id); + + 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 = NULL; + struct list_head *tmp = NULL; + + spin_lock(&Parahotplug_request_list_lock); + + list_for_each_safe(pos, tmp, &Parahotplug_request_list) { + struct parahotplug_request *req = + list_entry(pos, struct parahotplug_request, list); + if (time_after_eq(jiffies, req->expiration)) { + list_del(pos); + if (req->msg.hdr.Flags.responseExpected) + controlvm_respond_physdev_changestate( + &req->msg.hdr, + CONTROLVM_RESP_ERROR_DEVICE_UDEV_TIMEOUT, + req->msg.cmd.deviceChangeState.state); + parahotplug_request_destroy(req); + } + } + + spin_unlock(&Parahotplug_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 = NULL; + struct list_head *tmp = NULL; + + spin_lock(&Parahotplug_request_list_lock); + + /* Look for a request matching "id". */ + list_for_each_safe(pos, tmp, &Parahotplug_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(&Parahotplug_request_list_lock); + req->msg.cmd.deviceChangeState.state.Active = active; + if (req->msg.hdr.Flags.responseExpected) + controlvm_respond_physdev_changestate( + &req->msg.hdr, CONTROLVM_RESP_SUCCESS, + req->msg.cmd.deviceChangeState.state); + parahotplug_request_destroy(req); + return 0; + } + } + + spin_unlock(&Parahotplug_request_list_lock); + return -1; +} + +/* + * Enables or disables a PCI device by kicking off a udev script + */ +void +parahotplug_process_message(CONTROLVM_MESSAGE *inmsg) +{ + struct parahotplug_request *req; + + req = parahotplug_request_create(inmsg); + + if (req == NULL) { + LOGERR("parahotplug_process_message: couldn't allocate request"); + return; + } + + if (inmsg->cmd.deviceChangeState.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. + deviceChangeState.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(&Parahotplug_request_list_lock); + list_add_tail(&(req->list), &Parahotplug_request_list); + spin_unlock(&Parahotplug_request_list_lock); + + parahotplug_request_kickoff(req); + } +} + +/* + * Gets called when the udev script writes to + * /proc/visorchipset/parahotplug. Expects input in the form of " + * " where is the identifier passed to the script that + * matches a request on the request list, and is 0 or 1 + * indicating whether the device is now enabled or not. + */ +static ssize_t +parahotplug_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + char buf[64]; + uint id; + ushort active; + + if (count > sizeof(buf) - 1) { + LOGERR("parahotplug_proc_write: count (%d) exceeds size of buffer (%d)", + (int) count, (int) sizeof(buf)); + return -EINVAL; + } + if (copy_from_user(buf, buffer, count)) { + LOGERR("parahotplug_proc_write: copy_from_user failed"); + return -EFAULT; + } + buf[count] = '\0'; + + if (sscanf(buf, "%u %hu", &id, &active) != 2) { + id = 0; + active = 0; + } + + if (active != 1 && active != 0) { + LOGERR("parahotplug_proc_write: invalid active field"); + return -EINVAL; + } + + parahotplug_request_complete((int) id, (U16) active); + + return count; +} + +static const struct file_operations parahotplug_proc_fops = { + .owner = THIS_MODULE, + .read = visorchipset_proc_read_writeonly, + .write = parahotplug_proc_write, +}; + +/* 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(CONTROLVM_MESSAGE inmsg, HOSTADDRESS channel_addr) +{ + CONTROLVM_MESSAGE_PACKET *cmd = &inmsg.cmd; + U64 parametersAddr = 0; + U32 parametersBytes = 0; + PARSER_CONTEXT *parser_ctx = NULL; + BOOL isLocalAddr = FALSE; + CONTROLVM_MESSAGE ackmsg; + + /* create parsing context if necessary */ + isLocalAddr = (inmsg.hdr.Flags.testMessage == 1); + if (channel_addr == 0) { + LOGERR("HUH? channel_addr is 0!"); + return TRUE; + } + parametersAddr = channel_addr + inmsg.hdr.PayloadVmOffset; + parametersBytes = inmsg.hdr.PayloadBytes; + + /* 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 (parametersAddr != 0 && parametersBytes != 0) { + BOOL retry = FALSE; + parser_ctx = + parser_init_byteStream(parametersAddr, parametersBytes, + isLocalAddr, &retry); + if (!parser_ctx) { + if (retry) { + LOGWRN("throttling to copy payload"); + return FALSE; + } + LOGWRN("parsing failed"); + LOGWRN("inmsg.hdr.Id=0x%lx", (ulong) inmsg.hdr.Id); + LOGWRN("parametersAddr=0x%llx", (u64) parametersAddr); + LOGWRN("parametersBytes=%lu", (ulong) parametersBytes); + LOGWRN("isLocalAddr=%d", isLocalAddr); + } + } + + if (!isLocalAddr) { + controlvm_init_response(&ackmsg, &inmsg.hdr, + CONTROLVM_RESP_SUCCESS); + if ((ControlVm_channel) + && + (!visorchannel_signalinsert + (ControlVm_channel, CONTROLVM_QUEUE_ACK, &ackmsg))) + LOGWRN("failed to send ACK failed"); + } + switch (inmsg.hdr.Id) { + case CONTROLVM_CHIPSET_INIT: + LOGINF("CHIPSET_INIT(#busses=%lu,#switches=%lu)", + (ulong) inmsg.cmd.initChipset.busCount, + (ulong) inmsg.cmd.initChipset.switchCount); + chipset_init(&inmsg); + break; + case CONTROLVM_BUS_CREATE: + LOGINF("BUS_CREATE(%lu,#devs=%lu)", + (ulong) cmd->createBus.busNo, + (ulong) cmd->createBus.deviceCount); + bus_create(&inmsg); + break; + case CONTROLVM_BUS_DESTROY: + LOGINF("BUS_DESTROY(%lu)", (ulong) cmd->destroyBus.busNo); + bus_destroy(&inmsg); + break; + case CONTROLVM_BUS_CONFIGURE: + LOGINF("BUS_CONFIGURE(%lu)", (ulong) cmd->configureBus.busNo); + bus_configure(&inmsg, parser_ctx); + break; + case CONTROLVM_DEVICE_CREATE: + LOGINF("DEVICE_CREATE(%lu,%lu)", + (ulong) cmd->createDevice.busNo, + (ulong) cmd->createDevice.devNo); + my_device_create(&inmsg); + break; + case CONTROLVM_DEVICE_CHANGESTATE: + if (cmd->deviceChangeState.flags.physicalDevice) { + LOGINF("DEVICE_CHANGESTATE for physical device (%lu,%lu, active=%lu)", + (ulong) cmd->deviceChangeState.busNo, + (ulong) cmd->deviceChangeState.devNo, + (ulong) cmd->deviceChangeState.state.Active); + parahotplug_process_message(&inmsg); + } else { + LOGINF("DEVICE_CHANGESTATE for virtual device (%lu,%lu, state.Alive=0x%lx)", + (ulong) cmd->deviceChangeState.busNo, + (ulong) cmd->deviceChangeState.devNo, + (ulong) cmd->deviceChangeState.state.Alive); + /* save the hdr and cmd structures for later use */ + /* when sending back the response to Command */ + my_device_changestate(&inmsg); + g_DiagMsgHdr = inmsg.hdr; + g_DeviceChangeStatePacket = inmsg.cmd; + break; + } + break; + case CONTROLVM_DEVICE_DESTROY: + LOGINF("DEVICE_DESTROY(%lu,%lu)", + (ulong) cmd->destroyDevice.busNo, + (ulong) cmd->destroyDevice.devNo); + my_device_destroy(&inmsg); + break; + case CONTROLVM_DEVICE_CONFIGURE: + LOGINF("DEVICE_CONFIGURE(%lu,%lu)", + (ulong) cmd->configureDevice.busNo, + (ulong) cmd->configureDevice.devNo); + /* no op for now, just send a respond that we passed */ + if (inmsg.hdr.Flags.responseExpected) + controlvm_respond(&inmsg.hdr, CONTROLVM_RESP_SUCCESS); + break; + case CONTROLVM_CHIPSET_READY: + LOGINF("CHIPSET_READY"); + chipset_ready(&inmsg.hdr); + break; + case CONTROLVM_CHIPSET_SELFTEST: + LOGINF("CHIPSET_SELFTEST"); + chipset_selftest(&inmsg.hdr); + break; + case CONTROLVM_CHIPSET_STOP: + LOGINF("CHIPSET_STOP"); + chipset_notready(&inmsg.hdr); + break; + default: + LOGERR("unrecognized controlvm cmd=%d", (int) inmsg.hdr.Id); + if (inmsg.hdr.Flags.responseExpected) + controlvm_respond(&inmsg.hdr, + -CONTROLVM_RESP_ERROR_MESSAGE_ID_UNKNOWN); + break; + } + + if (parser_ctx != NULL) { + parser_done(parser_ctx); + parser_ctx = NULL; + } + return TRUE; +} + +static void +controlvm_periodic_work(struct work_struct *work) +{ + VISORCHIPSET_CHANNEL_INFO chanInfo; + CONTROLVM_MESSAGE inmsg; + char s[99]; + BOOL gotACommand = FALSE; + BOOL handle_command_failed = FALSE; + static U64 Poll_Count; + + /* make sure visorbus server is registered for controlvm callbacks */ + if (visorchipset_serverregwait && !serverregistered) + RETVOID; + /* make sure visorclientbus server is regsitered for controlvm + * callbacks + */ + if (visorchipset_clientregwait && !clientregistered) + RETVOID; + + memset(&chanInfo, 0, sizeof(VISORCHIPSET_CHANNEL_INFO)); + if (!ControlVm_channel) { + HOSTADDRESS addr = controlvm_get_channel_address(); + if (addr != 0) { + ControlVm_channel = + visorchannel_create_with_lock + (addr, + sizeof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL), + UltraControlvmChannelProtocolGuid); + if (ControlVm_channel == NULL) + LOGERR("failed to create controlvm channel"); + else if (ULTRA_CONTROLVM_CHANNEL_OK_CLIENT + (visorchannel_get_header(ControlVm_channel), + NULL)) { + LOGINF("Channel %s (ControlVm) discovered", + visorchannel_id(ControlVm_channel, s)); + initialize_controlvm_payload(); + } else { + LOGERR("controlvm channel is invalid"); + visorchannel_destroy(ControlVm_channel); + ControlVm_channel = NULL; + } + } + } + + Poll_Count++; + if ((ControlVm_channel != NULL) || (Poll_Count >= 250)) + ; /* keep going */ + else + RETVOID; + + /* Check events to determine if response to CHIPSET_READY + * should be sent + */ + if (visorchipset_holdchipsetready + && (g_ChipSetMsgHdr.Id != CONTROLVM_INVALID)) { + if (check_chipset_events() == 1) { + LOGINF("Sending CHIPSET_READY response"); + controlvm_respond(&g_ChipSetMsgHdr, 0); + clear_chipset_events(); + memset(&g_ChipSetMsgHdr, 0, + sizeof(CONTROLVM_MESSAGE_HEADER)); + } + } + + if (ControlVm_channel) { + while (visorchannel_signalremove(ControlVm_channel, + CONTROLVM_QUEUE_RESPONSE, + &inmsg)) { + if (inmsg.hdr.PayloadMaxBytes != 0) { + LOGERR("Payload of size %lu returned @%lu with unexpected message id %d.", + (ulong) inmsg.hdr.PayloadMaxBytes, + (ulong) inmsg.hdr.PayloadVmOffset, + inmsg.hdr.Id); + } + } + if (!gotACommand) { + 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; + gotACommand = TRUE; + } else + gotACommand = read_controlvm_event(&inmsg); + } + } + + handle_command_failed = FALSE; + while (gotACommand && (!handle_command_failed)) { + Most_recent_message_jiffies = jiffies; + if (ControlVm_channel) { + if (handle_command(inmsg, + visorchannel_get_physaddr + (ControlVm_channel))) + gotACommand = 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; + } + + } else { + handle_command(inmsg, 0); + gotACommand = FALSE; + } + } + + /* parahotplug_worker */ + parahotplug_process_list(); + + RETVOID; + +Away: + + 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) { + LOGINF("switched to slow controlvm polling"); + Poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_SLOW; + } + } else { + if (Poll_jiffies != POLLJIFFIES_CONTROLVMCHANNEL_FAST) { + Poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_FAST; + LOGINF("switched to fast controlvm polling"); + } + } + + if (queue_delayed_work(Periodic_controlvm_workqueue, + &Periodic_controlvm_work, Poll_jiffies) < 0) { + LOGERR("queue_delayed_work failed!"); + POSTCODE_LINUX_2(QUEUE_DELAYED_WORK_PC, POSTCODE_SEVERITY_ERR); + } +} + +static void +setup_crash_devices_work_queue(struct work_struct *work) +{ + + CONTROLVM_MESSAGE localCrashCreateBusMsg; + CONTROLVM_MESSAGE localCrashCreateDevMsg; + CONTROLVM_MESSAGE msg; + HOSTADDRESS host_addr; + U32 localSavedCrashMsgOffset; + U16 localSavedCrashMsgCount; + + /* make sure visorbus server is registered for controlvm callbacks */ + if (visorchipset_serverregwait && !serverregistered) + RETVOID; + + /* make sure visorclientbus server is regsitered for controlvm + * callbacks + */ + if (visorchipset_clientregwait && !clientregistered) + RETVOID; + + POSTCODE_LINUX_2(CRASH_DEV_ENTRY_PC, POSTCODE_SEVERITY_INFO); + + /* send init chipset msg */ + msg.hdr.Id = CONTROLVM_CHIPSET_INIT; + msg.cmd.initChipset.busCount = 23; + msg.cmd.initChipset.switchCount = 0; + + chipset_init(&msg); + + host_addr = controlvm_get_channel_address(); + if (!host_addr) { + LOGERR("Huh? Host address is NULL"); + POSTCODE_LINUX_2(CRASH_DEV_HADDR_NULL, POSTCODE_SEVERITY_ERR); + return; + } + + ControlVm_channel = + visorchannel_create_with_lock + (host_addr, + sizeof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL), + UltraControlvmChannelProtocolGuid); + + if (ControlVm_channel == NULL) { + LOGERR("failed to create controlvm channel"); + POSTCODE_LINUX_2(CRASH_DEV_CONTROLVM_NULL, + POSTCODE_SEVERITY_ERR); + return; + } + + /* get saved message count */ + if (visorchannel_read(ControlVm_channel, + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, + SavedCrashMsgCount), + &localSavedCrashMsgCount, sizeof(U16)) < 0) { + LOGERR("failed to get Saved Message Count"); + POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + + if (localSavedCrashMsgCount != CONTROLVM_CRASHMSG_MAX) { + LOGERR("Saved Message Count incorrect %d", + localSavedCrashMsgCount); + POSTCODE_LINUX_3(CRASH_DEV_COUNT_FAILURE_PC, + localSavedCrashMsgCount, + POSTCODE_SEVERITY_ERR); + return; + } + + /* get saved crash message offset */ + if (visorchannel_read(ControlVm_channel, + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, + SavedCrashMsgOffset), + &localSavedCrashMsgOffset, sizeof(U32)) < 0) { + LOGERR("failed to get Saved Message Offset"); + 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, + localSavedCrashMsgOffset, + &localCrashCreateBusMsg, + sizeof(CONTROLVM_MESSAGE)) < 0) { + LOGERR("CRASH_DEV_RD_BUS_FAIULRE: Failed to read CrashCreateBusMsg!"); + 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, + localSavedCrashMsgOffset + + sizeof(CONTROLVM_MESSAGE), + &localCrashCreateDevMsg, + sizeof(CONTROLVM_MESSAGE)) < 0) { + LOGERR("CRASH_DEV_RD_DEV_FAIULRE: Failed to read CrashCreateDevMsg!"); + POSTCODE_LINUX_2(CRASH_DEV_RD_DEV_FAIULRE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + + /* reuse IOVM create bus message */ + if (localCrashCreateBusMsg.cmd.createBus.channelAddr != 0) + bus_create(&localCrashCreateBusMsg); + else { + LOGERR("CrashCreateBusMsg is null, no dump will be taken"); + POSTCODE_LINUX_2(CRASH_DEV_BUS_NULL_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + + /* reuse create device message for storage device */ + if (localCrashCreateDevMsg.cmd.createDevice.channelAddr != 0) + my_device_create(&localCrashCreateDevMsg); + else { + LOGERR("CrashCreateDevMsg is null, no dump will be taken"); + POSTCODE_LINUX_2(CRASH_DEV_DEV_NULL_FAILURE_PC, + POSTCODE_SEVERITY_ERR); + return; + } + LOGINF("Bus and device ready for dumping"); + POSTCODE_LINUX_2(CRASH_DEV_EXIT_PC, POSTCODE_SEVERITY_INFO); + return; + +Away: + + Poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_SLOW; + + if (queue_delayed_work(Periodic_controlvm_workqueue, + &Periodic_controlvm_work, Poll_jiffies) < 0) { + LOGERR("queue_delayed_work failed!"); + POSTCODE_LINUX_2(QUEUE_DELAYED_WORK_PC, POSTCODE_SEVERITY_ERR); + } +} + +static void +bus_create_response(ulong busNo, int response) +{ + bus_responder(CONTROLVM_BUS_CREATE, busNo, response); +} + +static void +bus_destroy_response(ulong busNo, int response) +{ + bus_responder(CONTROLVM_BUS_DESTROY, busNo, response); +} + +static void +device_create_response(ulong busNo, ulong devNo, int response) +{ + device_responder(CONTROLVM_DEVICE_CREATE, busNo, devNo, response); +} + +static void +device_destroy_response(ulong busNo, ulong devNo, int response) +{ + device_responder(CONTROLVM_DEVICE_DESTROY, busNo, devNo, response); +} + +void +device_pause_response(ulong busNo, ulong devNo, int response) +{ + + device_changestate_responder(CONTROLVM_DEVICE_CHANGESTATE, + busNo, devNo, response, + SegmentStateStandby); +} +EXPORT_SYMBOL_GPL(device_pause_response); + +static void +device_resume_response(ulong busNo, ulong devNo, int response) +{ + device_changestate_responder(CONTROLVM_DEVICE_CHANGESTATE, + busNo, devNo, response, + SegmentStateRunning); +} + +BOOL +visorchipset_get_bus_info(ulong busNo, VISORCHIPSET_BUS_INFO *busInfo) +{ + void *p = findbus(&BusInfoList, busNo); + if (!p) { + LOGERR("(%lu) failed", busNo); + return FALSE; + } + memcpy(busInfo, p, sizeof(VISORCHIPSET_BUS_INFO)); + return TRUE; +} +EXPORT_SYMBOL_GPL(visorchipset_get_bus_info); + +BOOL +visorchipset_set_bus_context(ulong busNo, void *context) +{ + VISORCHIPSET_BUS_INFO *p = findbus(&BusInfoList, busNo); + if (!p) { + LOGERR("(%lu) failed", busNo); + return FALSE; + } + p->bus_driver_context = context; + return TRUE; +} +EXPORT_SYMBOL_GPL(visorchipset_set_bus_context); + +BOOL +visorchipset_get_device_info(ulong busNo, ulong devNo, + VISORCHIPSET_DEVICE_INFO *devInfo) +{ + void *p = finddevice(&DevInfoList, busNo, devNo); + if (!p) { + LOGERR("(%lu,%lu) failed", busNo, devNo); + return FALSE; + } + memcpy(devInfo, p, sizeof(VISORCHIPSET_DEVICE_INFO)); + return TRUE; +} +EXPORT_SYMBOL_GPL(visorchipset_get_device_info); + +BOOL +visorchipset_set_device_context(ulong busNo, ulong devNo, void *context) +{ + VISORCHIPSET_DEVICE_INFO *p = finddevice(&DevInfoList, busNo, devNo); + if (!p) { + LOGERR("(%lu,%lu) failed", busNo, devNo); + return FALSE; + } + p->bus_driver_context = context; + return TRUE; +} +EXPORT_SYMBOL_GPL(visorchipset_set_device_context); + +/* Generic wrapper function for allocating memory from a kmem_cache pool. + */ +void * +visorchipset_cache_alloc(struct kmem_cache *pool, BOOL ok_to_block, + char *fn, int ln) +{ + gfp_t gfp; + void *p; + + if (ok_to_block) + gfp = GFP_KERNEL; + else + gfp = GFP_ATOMIC; + /* __GFP_NORETRY means "ok to fail", meaning + * kmem_cache_alloc() can return NULL, implying the caller CAN + * cope with failure. If you do NOT specify __GFP_NORETRY, + * Linux will go to extreme measures to get memory for you + * (like, invoke oom killer), which will probably cripple the + * system. + */ + gfp |= __GFP_NORETRY; + p = kmem_cache_alloc(pool, gfp); + if (!p) { + LOGERR("kmem_cache_alloc failed early @%s:%d\n", fn, ln); + return NULL; + } + atomic_inc(&Visorchipset_cache_buffers_in_use); + return p; +} + +/* Generic wrapper function for freeing memory from a kmem_cache pool. + */ +void +visorchipset_cache_free(struct kmem_cache *pool, void *p, char *fn, int ln) +{ + if (!p) { + LOGERR("NULL pointer @%s:%d\n", fn, ln); + return; + } + atomic_dec(&Visorchipset_cache_buffers_in_use); + kmem_cache_free(pool, p); +} + +#define gettoken(bufp) strsep(bufp, " -\t\n") + +static ssize_t +chipset_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + char buf[512]; + char *token, *p; + + if (count > sizeof(buf) - 1) { + LOGERR("chipset_proc_write: count (%d) exceeds size of buffer (%d)", + (int) count, (int) sizeof(buffer)); + return -EINVAL; + } + if (copy_from_user(buf, buffer, count)) { + LOGERR("chipset_proc_write: copy_from_user failed"); + return -EFAULT; + } + buf[count] = '\0'; + + p = buf; + token = gettoken(&p); + + if (strcmp(token, "CALLHOMEDISK_MOUNTED") == 0) { + token = gettoken(&p); + /* The Call Home Disk has been mounted */ + if (strcmp(token, "0") == 0) + chipset_events[0] = 1; + } else if (strcmp(token, "MODULES_LOADED") == 0) { + token = gettoken(&p); + /* All modules for the partition have been loaded */ + if (strcmp(token, "0") == 0) + chipset_events[1] = 1; + } else if (token == NULL) { + /* No event specified */ + LOGERR("No event was specified to send CHIPSET_READY response"); + return -1; + } else { + /* Unsupported event specified */ + LOGERR("%s is an invalid event for sending CHIPSET_READY response", token); + return -1; + } + + return count; +} + +static ssize_t +visorchipset_proc_read_writeonly(struct file *file, char __user *buf, + size_t len, loff_t *offset) +{ + return 0; +} + +/** + * Reads the InstallationError, InstallationTextId, + * InstallationRemainingSteps fields of ControlVMChannel. + */ +static ssize_t +proc_read_installer(struct file *file, char __user *buf, + size_t len, loff_t *offset) +{ + int length = 0; + U16 remainingSteps; + U32 error, textId; + char *vbuf; + loff_t pos = *offset; + + if (pos < 0) + return -EINVAL; + + if (pos > 0 || !len) + return 0; + + vbuf = kzalloc(len, GFP_KERNEL); + if (!vbuf) + return -ENOMEM; + + visorchannel_read(ControlVm_channel, + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, + InstallationRemainingSteps), &remainingSteps, + sizeof(U16)); + visorchannel_read(ControlVm_channel, + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, + InstallationError), &error, sizeof(U32)); + visorchannel_read(ControlVm_channel, + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, + InstallationTextId), &textId, sizeof(U32)); + + length = sprintf(vbuf, "%u %u %u\n", remainingSteps, error, textId); + if (copy_to_user(buf, vbuf, length)) { + kfree(vbuf); + return -EFAULT; + } + + kfree(vbuf); + *offset += length; + return length; +} + +/** + * Writes to the InstallationError, InstallationTextId, + * InstallationRemainingSteps fields of + * ControlVMChannel. + * Input: RemainingSteps Error TextId + * Limit 32 characters input + */ +#define UINT16_MAX (65535U) +#define UINT32_MAX (4294967295U) +static ssize_t +proc_write_installer(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + char buf[32]; + U16 remainingSteps; + U32 error, textId; + + /* Check to make sure there is no buffer overflow */ + if (count > (sizeof(buf) - 1)) + return -EINVAL; + + if (copy_from_user(buf, buffer, count)) { + WARN(1, "Error copying from user space\n"); + return -EFAULT; + } + + if (sscanf(buf, "%hu %i %i", &remainingSteps, &error, &textId) != 3) { + remainingSteps = UINT16_MAX; + error = UINT32_MAX; + textId = UINT32_MAX; + } + + if (remainingSteps != UINT16_MAX) { + if (visorchannel_write + (ControlVm_channel, + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, + InstallationRemainingSteps), &remainingSteps, + sizeof(U16)) < 0) + WARN(1, "Installation Status Write Failed - Write function error - RemainingSteps = %d\n", + remainingSteps); + } + + if (error != UINT32_MAX) { + if (visorchannel_write + (ControlVm_channel, + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, + InstallationError), &error, sizeof(U32)) < 0) + WARN(1, "Installation Status Write Failed - Write function error - Error = %d\n", + error); + } + + if (textId != UINT32_MAX) { + if (visorchannel_write + (ControlVm_channel, + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, + InstallationTextId), &textId, sizeof(U32)) < 0) + WARN(1, "Installation Status Write Failed - Write function error - TextId = %d\n", + textId); + } + + /* So this function isn't called multiple times, must return + * size of buffer + */ + return count; +} + +/** + * Reads the ToolAction field of ControlVMChannel. + */ +static ssize_t +proc_read_toolaction(struct file *file, char __user *buf, + size_t len, loff_t *offset) +{ + int length = 0; + U8 toolAction; + char *vbuf; + loff_t pos = *offset; + + if (pos < 0) + return -EINVAL; + + if (pos > 0 || !len) + return 0; + + vbuf = kzalloc(len, GFP_KERNEL); + if (!vbuf) + return -ENOMEM; + + visorchannel_read(ControlVm_channel, + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, + ToolAction), &toolAction, sizeof(U8)); + + length = sprintf(vbuf, "%u\n", toolAction); + if (copy_to_user(buf, vbuf, length)) { + kfree(vbuf); + return -EFAULT; + } + + kfree(vbuf); + *offset += length; + return length; +} + +/** + * Writes to the ToolAction field of ControlVMChannel. + * Input: ToolAction + * Limit 3 characters input + */ +#define UINT8_MAX (255U) +static ssize_t +proc_write_toolaction(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + char buf[3]; + U8 toolAction; + + /* Check to make sure there is no buffer overflow */ + if (count > (sizeof(buf) - 1)) + return -EINVAL; + + if (copy_from_user(buf, buffer, count)) { + WARN(1, "Error copying from user space\n"); + return -EFAULT; + } + + if (sscanf(buf, "%hhd", &toolAction) != 1) + toolAction = UINT8_MAX; + + if (toolAction != UINT8_MAX) { + if (visorchannel_write + (ControlVm_channel, + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, ToolAction), + &toolAction, sizeof(U8)) < 0) + WARN(1, "Installation ToolAction Write Failed - ToolAction = %d\n", + toolAction); + } + + /* So this function isn't called multiple times, must return + * size of buffer + */ + return count; +} + +/** + * Reads the EfiSparIndication.BootToTool field of ControlVMChannel. + */ +static ssize_t +proc_read_bootToTool(struct file *file, char __user *buf, + size_t len, loff_t *offset) +{ + int length = 0; + ULTRA_EFI_SPAR_INDICATION efiSparIndication; + char *vbuf; + loff_t pos = *offset; + + if (pos < 0) + return -EINVAL; + + if (pos > 0 || !len) + return 0; + + vbuf = kzalloc(len, GFP_KERNEL); + if (!vbuf) + return -ENOMEM; + + visorchannel_read(ControlVm_channel, + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, + EfiSparIndication), &efiSparIndication, + sizeof(ULTRA_EFI_SPAR_INDICATION)); + + length = sprintf(vbuf, "%d\n", (int) efiSparIndication.BootToTool); + if (copy_to_user(buf, vbuf, length)) { + kfree(vbuf); + return -EFAULT; + } + + kfree(vbuf); + *offset += length; + return length; +} + +/** + * Writes to the EfiSparIndication.BootToTool field of ControlVMChannel. + * Input: 1 or 0 (1 being on, 0 being off) + */ +static ssize_t +proc_write_bootToTool(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + char buf[3]; + int inputVal; + ULTRA_EFI_SPAR_INDICATION efiSparIndication; + + /* Check to make sure there is no buffer overflow */ + if (count > (sizeof(buf) - 1)) + return -EINVAL; + + if (copy_from_user(buf, buffer, count)) { + WARN(1, "Error copying from user space\n"); + return -EFAULT; + } + + if (sscanf(buf, "%i", &inputVal) != 1) + inputVal = 0; + + efiSparIndication.BootToTool = (inputVal == 1 ? 1 : 0); + + if (visorchannel_write + (ControlVm_channel, + offsetof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL, EfiSparIndication), + &efiSparIndication, sizeof(ULTRA_EFI_SPAR_INDICATION)) < 0) + printk + ("Installation BootToTool Write Failed - BootToTool = %d\n", + (int) efiSparIndication.BootToTool); + + /* So this function isn't called multiple times, must return + * size of buffer + */ + return count; +} + +static const struct file_operations chipset_proc_fops = { + .owner = THIS_MODULE, + .read = visorchipset_proc_read_writeonly, + .write = chipset_proc_write, +}; + +static int __init +visorchipset_init(void) +{ + int rc = 0, x = 0; + struct proc_dir_entry *installer_file; + struct proc_dir_entry *toolaction_file; + struct proc_dir_entry *bootToTool_file; + + LOGINF("chipset driver version %s loaded", VERSION); + /* process module options */ + POSTCODE_LINUX_2(DRIVER_ENTRY_PC, POSTCODE_SEVERITY_INFO); + + LOGINF("option - testvnic=%d", visorchipset_testvnic); + LOGINF("option - testvnicclient=%d", visorchipset_testvnicclient); + LOGINF("option - testmsg=%d", visorchipset_testmsg); + LOGINF("option - testteardown=%d", visorchipset_testteardown); + LOGINF("option - major=%d", visorchipset_major); + LOGINF("option - serverregwait=%d", visorchipset_serverregwait); + LOGINF("option - clientregwait=%d", visorchipset_clientregwait); + LOGINF("option - holdchipsetready=%d", visorchipset_holdchipsetready); + + memset(&BusDev_Server_Notifiers, 0, sizeof(BusDev_Server_Notifiers)); + memset(&BusDev_Client_Notifiers, 0, sizeof(BusDev_Client_Notifiers)); + memset(&ControlVm_payload_info, 0, sizeof(ControlVm_payload_info)); + memset(&LiveDump_info, 0, sizeof(LiveDump_info)); + atomic_set(&LiveDump_info.buffers_in_use, 0); + + if (visorchipset_testvnic) + FAIL_WPOSTCODE_2("testvnic option no longer supported", x, + CHIPSET_INIT_FAILURE_PC, x); + + controlvm_init(); + MajorDev = MKDEV(visorchipset_major, 0); + TRY_WPOSTCODE_1(visorchipset_file_init(MajorDev, &ControlVm_channel), + CHIPSET_INIT_FAILURE_PC); + proc_Init(); + memset(PartitionPropertyNames, 0, sizeof(PartitionPropertyNames)); + memset(ControlVmPropertyNames, 0, sizeof(ControlVmPropertyNames)); + InitPartitionProperties(); + InitControlVmProperties(); + + PartitionType = proc_CreateType(ProcDir, PartitionTypeNames, + (const char **) PartitionPropertyNames, + &show_partition_property); + ControlVmType = + proc_CreateType(ProcDir, ControlVmTypeNames, + (const char **) ControlVmPropertyNames, + &show_controlvm_property); + + ControlVmObject = proc_CreateObject(ControlVmType, NULL, NULL); + + /* Setup Installation fields */ + installer_file = proc_create("installer", 0644, ProcDir, + &proc_installer_fops); + /* Setup the ToolAction field */ + toolaction_file = proc_create("toolaction", 0644, ProcDir, + &proc_toolaction_fops); + /* Setup the BootToTool field */ + bootToTool_file = proc_create("boottotool", 0644, ProcDir, + &proc_bootToTool_fops); + + memset(&g_DiagMsgHdr, 0, sizeof(CONTROLVM_MESSAGE_HEADER)); + + chipset_proc_dir = proc_create(VISORCHIPSET_CHIPSET_PROC_ENTRY_FN, + 0644, ProcDir, &chipset_proc_fops); + memset(&g_ChipSetMsgHdr, 0, sizeof(CONTROLVM_MESSAGE_HEADER)); + + parahotplug_proc_dir = + proc_create(VISORCHIPSET_PARAHOTPLUG_PROC_ENTRY_FN, 0200, + ProcDir, ¶hotplug_proc_fops); + memset(&g_DelDumpMsgHdr, 0, sizeof(CONTROLVM_MESSAGE_HEADER)); + + if (filexfer_constructor(sizeof(struct putfile_request)) < 0) { + FAIL_WPOSTCODE_1("filexfer_constructor failed", -1, + CHIPSET_INIT_FAILURE_PC); + } + Putfile_buffer_list_pool = + kmem_cache_create(Putfile_buffer_list_pool_name, + sizeof(struct putfile_buffer_entry), + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!Putfile_buffer_list_pool) { + FAIL_WPOSTCODE_1("failed to alloc Putfile_buffer_list_pool", -1, + CHIPSET_INIT_FAILURE_PC); + } + if (visorchipset_disable_controlvm) { + LOGINF("visorchipset_init:controlvm disabled"); + } else { + /* if booting in a crash kernel */ + if (visorchipset_crash_kernel) + INIT_DELAYED_WORK(&Periodic_controlvm_work, + setup_crash_devices_work_queue); + else + INIT_DELAYED_WORK(&Periodic_controlvm_work, + controlvm_periodic_work); + Periodic_controlvm_workqueue = + create_singlethread_workqueue("visorchipset_controlvm"); + + if (Periodic_controlvm_workqueue == NULL) + FAIL_WPOSTCODE_1("cannot create controlvm workqueue", + -ENOMEM, CREATE_WORKQUEUE_FAILED_PC); + Most_recent_message_jiffies = jiffies; + Poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_FAST; + TRY_WPOSTCODE_1(queue_delayed_work + (Periodic_controlvm_workqueue, + &Periodic_controlvm_work, Poll_jiffies), + QUEUE_DELAYED_WORK_PC); + } + + Visorchipset_platform_device.dev.devt = MajorDev; + if (platform_device_register(&Visorchipset_platform_device) < 0) + FAIL_WPOSTCODE_1 + ("platform_device_register(visorchipset) failed", -1, + DEVICE_REGISTER_FAILURE_PC); + LOGINF("visorchipset device created"); + POSTCODE_LINUX_2(CHIPSET_INIT_SUCCESS_PC, POSTCODE_SEVERITY_INFO); + RETINT(0); + +Away: + + if (rc) { + LOGERR("visorchipset_init failed"); + POSTCODE_LINUX_3(CHIPSET_INIT_FAILURE_PC, rc, + POSTCODE_SEVERITY_ERR); + } + return rc; +} + +static void +visorchipset_exit(void) +{ + char s[99]; + POSTCODE_LINUX_2(DRIVER_EXIT_PC, POSTCODE_SEVERITY_INFO); + + if (visorchipset_disable_controlvm) { + ; + } else { + cancel_delayed_work(&Periodic_controlvm_work); + flush_workqueue(Periodic_controlvm_workqueue); + destroy_workqueue(Periodic_controlvm_workqueue); + Periodic_controlvm_workqueue = NULL; + destroy_controlvm_payload_info(&ControlVm_payload_info); + } + Test_Vnic_channel = NULL; + if (Putfile_buffer_list_pool) { + kmem_cache_destroy(Putfile_buffer_list_pool); + Putfile_buffer_list_pool = NULL; + } + filexfer_destructor(); + if (ControlVmObject) { + proc_DestroyObject(ControlVmObject); + ControlVmObject = NULL; + } + cleanup_controlvm_structures(); + + if (ControlVmType) { + proc_DestroyType(ControlVmType); + ControlVmType = NULL; + } + if (PartitionType) { + proc_DestroyType(PartitionType); + PartitionType = NULL; + } + if (diag_proc_dir) { + remove_proc_entry(VISORCHIPSET_DIAG_PROC_ENTRY_FN, ProcDir); + diag_proc_dir = NULL; + } + memset(&g_DiagMsgHdr, 0, sizeof(CONTROLVM_MESSAGE_HEADER)); + + if (chipset_proc_dir) { + remove_proc_entry(VISORCHIPSET_CHIPSET_PROC_ENTRY_FN, ProcDir); + chipset_proc_dir = NULL; + } + memset(&g_ChipSetMsgHdr, 0, sizeof(CONTROLVM_MESSAGE_HEADER)); + + if (parahotplug_proc_dir) { + remove_proc_entry(VISORCHIPSET_PARAHOTPLUG_PROC_ENTRY_FN, + ProcDir); + parahotplug_proc_dir = NULL; + } + + memset(&g_DelDumpMsgHdr, 0, sizeof(CONTROLVM_MESSAGE_HEADER)); + + proc_DeInit(); + if (ControlVm_channel != NULL) { + LOGINF("Channel %s (ControlVm) disconnected", + visorchannel_id(ControlVm_channel, s)); + visorchannel_destroy(ControlVm_channel); + ControlVm_channel = NULL; + } + controlvm_deinit(); + visorchipset_file_cleanup(); + POSTCODE_LINUX_2(DRIVER_EXIT_PC, POSTCODE_SEVERITY_INFO); + LOGINF("chipset driver unloaded"); +} + +module_param_named(testvnic, visorchipset_testvnic, int, S_IRUGO); +MODULE_PARM_DESC(visorchipset_testvnic, "1 to test vnic, using dummy VNIC connected via a loopback to a physical ethernet"); +int visorchipset_testvnic = 0; + +module_param_named(testvnicclient, visorchipset_testvnicclient, int, S_IRUGO); +MODULE_PARM_DESC(visorchipset_testvnicclient, "1 to test vnic, using real VNIC channel attached to a separate IOVM guest"); +int visorchipset_testvnicclient = 0; + +module_param_named(testmsg, visorchipset_testmsg, int, S_IRUGO); +MODULE_PARM_DESC(visorchipset_testmsg, + "1 to manufacture the chipset, bus, and switch messages"); +int visorchipset_testmsg = 0; + +module_param_named(major, visorchipset_major, int, S_IRUGO); +MODULE_PARM_DESC(visorchipset_major, "major device number to use for the device node"); +int visorchipset_major = 0; + +module_param_named(serverregwait, visorchipset_serverregwait, int, S_IRUGO); +MODULE_PARM_DESC(visorchipset_serverreqwait, + "1 to have the module wait for the visor bus to register"); +int visorchipset_serverregwait = 0; /* default is off */ +module_param_named(clientregwait, visorchipset_clientregwait, int, S_IRUGO); +MODULE_PARM_DESC(visorchipset_clientregwait, "1 to have the module wait for the visorclientbus to register"); +int visorchipset_clientregwait = 1; /* default is on */ +module_param_named(testteardown, visorchipset_testteardown, int, S_IRUGO); +MODULE_PARM_DESC(visorchipset_testteardown, + "1 to test teardown of the chipset, bus, and switch"); +int visorchipset_testteardown = 0; /* default is off */ +module_param_named(disable_controlvm, visorchipset_disable_controlvm, int, + S_IRUGO); +MODULE_PARM_DESC(visorchipset_disable_controlvm, + "1 to disable polling of controlVm channel"); +int visorchipset_disable_controlvm = 0; /* default is off */ +module_param_named(crash_kernel, visorchipset_crash_kernel, int, S_IRUGO); +MODULE_PARM_DESC(visorchipset_crash_kernel, + "1 means we are running in crash kernel"); +int visorchipset_crash_kernel = 0; /* default is running in non-crash kernel */ +module_param_named(holdchipsetready, visorchipset_holdchipsetready, + int, S_IRUGO); +MODULE_PARM_DESC(visorchipset_holdchipsetready, + "1 to hold response to CHIPSET_READY"); +int visorchipset_holdchipsetready = 0; /* default is to send CHIPSET_READY + * response immediately */ +module_init(visorchipset_init); +module_exit(visorchipset_exit); + +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/visorchipset/visorchipset_umode.h b/drivers/staging/unisys/visorchipset/visorchipset_umode.h new file mode 100644 index 000000000000..259e840376a5 --- /dev/null +++ b/drivers/staging/unisys/visorchipset/visorchipset_umode.h @@ -0,0 +1,37 @@ +/* visorchipset_umode.h + * + * Copyright © 2010 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/** @file ********************************************************************* + * + * This describes structures needed for the interface between the + * visorchipset driver and a user-mode component that opens the device. + * + ****************************************************************************** + */ + +#ifndef __VISORCHIPSET_UMODE_H +#define __VISORCHIPSET_UMODE_H + + + +/** The user-mode program can access the control channel buffer directly + * via this memory map. + */ +#define VISORCHIPSET_MMAP_CONTROLCHANOFFSET (0x00000000) +#define VISORCHIPSET_MMAP_CONTROLCHANSIZE (0x00400000) /* 4MB */ + +#endif /* __VISORCHIPSET_UMODE_H */