| /* Copyright (C) 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 <linux/uuid.h> |
| |
| #include <linux/dma-direction.h> |
| #include "channel.h" |
| #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 SPAR_VHBA_CHANNEL_OK_CLIENT(ch) \ |
| (spar_check_channel_client(ch, spar_vhba_channel_protocol_uuid, \ |
| "vhba", MIN_IO_CHANNEL_SIZE, \ |
| ULTRA_VHBA_CHANNEL_PROTOCOL_VERSIONID, \ |
| ULTRA_VHBA_CHANNEL_PROTOCOL_SIGNATURE)) |
| |
| #define SPAR_VNIC_CHANNEL_OK_CLIENT(ch) \ |
| (spar_check_channel_client(ch, spar_vnic_channel_protocol_uuid, \ |
| "vnic", MIN_IO_CHANNEL_SIZE, \ |
| ULTRA_VNIC_CHANNEL_PROTOCOL_VERSIONID, \ |
| ULTRA_VNIC_CHANNEL_PROTOCOL_SIGNATURE)) |
| |
| /* |
| * 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_IOPART 1 /* used by ioguestpart to 'remove' signals from |
| * iopart - same queue as previous queue */ |
| |
| /* size of cdb - i.e., scsi cmnd */ |
| #define MAX_CMND_SIZE 16 |
| |
| #define MAX_SENSE_SIZE 64 |
| |
| #define MAX_PHYS_INFO 64 |
| |
| /* various types of network packets that can be sent in cmdrsp */ |
| enum net_types { |
| 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 address */ |
| |
| }; |
| |
| #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_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 */ |
| |
| /* various types of scsi task mgmt commands */ |
| enum task_mgmt_types { |
| TASK_MGMT_ABORT_TASK = 1, |
| TASK_MGMT_BUS_RESET, |
| TASK_MGMT_LUN_RESET, |
| TASK_MGMT_TARGET_RESET, |
| }; |
| |
| /* various types of vdisk mgmt commands */ |
| enum vdisk_mgmt_types { |
| VDISK_MGMT_ACQUIRE = 1, |
| VDISK_MGMT_RELEASE, |
| }; |
| |
| struct phys_info { |
| u64 pi_pfn; |
| u16 pi_off; |
| u16 pi_len; |
| } __packed; |
| |
| #define MIN_NUMSIGNALS 64 |
| |
| /* structs with pragma pack */ |
| |
| struct guest_phys_info { |
| u64 address; |
| u64 length; |
| } __packed; |
| |
| #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 */ |
| } __packed; |
| |
| struct vhba_wwnn { |
| u32 wwnn1; |
| u32 wwnn2; |
| } __packed; |
| |
| /* 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 */ |
| } __packed; |
| |
| struct uiscmdrsp_scsi { |
| u64 handle; /* 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_direction 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_SEL_TIMEOUT 4 |
| |
| /* 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. |
| */ |
| } __packed; |
| |
| /* 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_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_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 segment_number; |
| u8 sense_key:4; |
| u8 reserved:1; |
| u8 incorrect_length:1; |
| u8 end_of_media:1; |
| u8 file_mark:1; |
| u8 information[4]; |
| u8 additional_sense_length; |
| u8 command_specific_information[4]; |
| u8 additional_sense_code; |
| u8 additional_sense_code_qualifier; |
| u8 fru_code; |
| u8 sense_key_specific[3]; |
| } __packed; |
| |
| 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. |
| */ |
| } __packed; |
| |
| struct net_pkt_xmtdone { |
| u32 xmt_done_result; /* result of NET_XMIT */ |
| } __packed; |
| |
| /* 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 unique_num; /* This is used to make sure that |
| * receive posts are returned to */ |
| /* the Adapter which we sent them originally. */ |
| } __packed; |
| |
| 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 unique_num; |
| u32 rcvs_dropped_delta; |
| } __packed; |
| |
| struct net_pkt_enbdis { |
| void *context; |
| u16 enable; /* 1 = enable, 0 = disable */ |
| } __packed; |
| |
| struct net_pkt_macaddr { |
| void *context; |
| u8 macaddr[MAX_MACADDR_LEN]; /* 6 bytes */ |
| } __packed; |
| |
| /* cmd rsp packet used for VNIC network traffic */ |
| struct uiscmdrsp_net { |
| enum 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; |
| }; |
| } __packed; |
| |
| struct uiscmdrsp_scsitaskmgmt { |
| enum task_mgmt_types tasktype; |
| |
| /* the type of task */ |
| struct uisscsi_dest vdest; |
| |
| /* the vdisk for which this task mgmt is generated */ |
| u64 handle; |
| |
| /* This is a 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. |
| */ |
| u64 notify_handle; |
| |
| /* 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. |
| * When the rsp is received by guest, the thread receiving the |
| * response uses this to notify the thread waiting for taskmgmt |
| * command completion. Its value is preserved by iopart & returned |
| * as is in the task mgmt rsp. |
| */ |
| u64 notifyresult_handle; |
| |
| /* 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 |
| } __packed; |
| |
| /* 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 *v_hba; /* Pointer to vhba_info for channel info to |
| * route msg */ |
| u32 channel, id, lun; /* SCSI Path of Disk to added or removed */ |
| } __packed; |
| |
| /* The following is used by virthba/vSCSI to send the Acquire/Release commands |
| * to the IOVM. */ |
| struct uiscmdrsp_vdiskmgmt { |
| enum vdisk_mgmt_types vdisktype; |
| |
| /* the type of task */ |
| struct uisscsi_dest vdest; |
| |
| /* the vdisk for which this task mgmt is generated */ |
| u64 handle; |
| |
| /* This is a 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. |
| */ |
| u64 notify_handle; |
| |
| /* For linux guests, this is a pointer to wait_queue_head that a |
| * thread is waiting on to see if the tskmgmt command has completed. |
| * When the rsp is received by guest, the thread receiving the |
| * response uses this to notify the thread waiting for taskmgmt |
| * command completion. Its value is preserved by iopart & returned |
| * as is in the task mgmt rsp. |
| */ |
| u64 notifyresult_handle; |
| |
| /* 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 |
| } __packed; |
| |
| /* 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 */ |
| } __packed; |
| |
| struct iochannel_vhba { |
| struct vhba_wwnn wwnn; /* 8 bytes */ |
| struct vhba_config_max max; /* 20 bytes */ |
| } __packed; /* total = 28 bytes */ |
| struct iochannel_vnic { |
| u8 macaddr[6]; /* 6 bytes */ |
| u32 num_rcv_bufs; /* 4 bytes */ |
| u32 mtu; /* 4 bytes */ |
| uuid_le zone_uuid; /* 16 bytes */ |
| } __packed; |
| /* 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 cmd_q and rsp_q SIGNAL_QUEUE_HEADERS. |
| */ |
| struct spar_io_channel_protocol { |
| struct channel_header channel_header; |
| struct signal_queue_header cmd_q; |
| struct signal_queue_header rsp_q; |
| union { |
| struct iochannel_vhba vhba; |
| struct iochannel_vnic vnic; |
| } __packed; |
| |
| #define MAX_CLIENTSTRING_LEN 1024 |
| u8 client_string[MAX_CLIENTSTRING_LEN];/* NULL terminated - so holds |
| * max - 1 bytes */ |
| } __packed; |
| |
| |
| /* |
| * INLINE functions for initializing and accessing I/O data channels |
| */ |
| |
| #define SIZEOF_PROTOCOL (COVER(sizeof(struct spar_io_channel_protocol), 64)) |
| #define SIZEOF_CMDRSP (COVER(sizeof(struct uiscmdrsp), 64)) |
| |
| #define MIN_IO_CHANNEL_SIZE COVER(SIZEOF_PROTOCOL + \ |
| 2 * MIN_NUMSIGNALS * SIZEOF_CMDRSP, 4096) |
| |
| /* |
| * 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 |
| |
| /* 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__ */ |