| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460 | #ifndef _GPXE_ISCSI_H
#define _GPXE_ISCSI_H
/** @file
 *
 * iSCSI protocol
 *
 */
#include <stdint.h>
#include <gpxe/tcp.h>
#include <gpxe/scsi.h>
/**
 * iSCSI segment lengths
 *
 * iSCSI uses an icky structure with one one-byte field (a dword
 * count) and one three-byte field (a byte count).  This structure,
 * and the accompanying macros, relieve some of the pain.
 */
union iscsi_segment_lengths {
	struct {
		/** The AHS length (measured in dwords) */
		uint8_t ahs_len;
		/** The data length (measured in bytes), in network
		 * byte order
		 */
		uint8_t data_len[3];
	} bytes;
	/** Ths data length (measured in bytes), in network byte
	 * order, with ahs_len as the first byte.
	 */
	uint32_t ahs_and_data_len;
};
/** The length of the additional header segment, in dwords */
#define ISCSI_AHS_LEN( segment_lengths ) \
	( (segment_lengths).bytes.ahs_len )
/** The length of the data segment, in bytes, excluding any padding */
#define ISCSI_DATA_LEN( segment_lengths ) \
	( ntohl ( (segment_lengths).ahs_and_data_len ) & 0xffffff )
/** The padding of the data segment, in bytes */
#define ISCSI_DATA_PAD_LEN( segment_lengths ) \
	( ( 0 - (segment_lengths).bytes.data_len[2] ) & 0x03 )
/** Set additional header and data segment lengths */
#define ISCSI_SET_LENGTHS( segment_lengths, ahs_len, data_len ) do {	\
	(segment_lengths).ahs_and_data_len =				\
		htonl ( data_len | ( ahs_len << 24 ) );			\
	} while ( 0 )
/**
 * iSCSI basic header segment common fields
 *
 */
struct iscsi_bhs_common {
	/** Opcode */
	uint8_t opcode;
	/** Flags */
	uint8_t flags;
	/** Fields specific to the PDU type */
	uint8_t other_a[2];
	/** Segment lengths */
	union iscsi_segment_lengths lengths;
	/** Fields specific to the PDU type */
	uint8_t other_b[8];
	/** Initiator Task Tag */
	uint32_t itt;
	/** Fields specific to the PDU type */
	uint8_t other_c[28];
};
/** Opcode mask */
#define ISCSI_OPCODE_MASK 0x3f
/** Immediate delivery */
#define ISCSI_FLAG_IMMEDIATE 0x40
/** Final PDU of a sequence */
#define ISCSI_FLAG_FINAL 0x80
/**
 * iSCSI login request basic header segment
 *
 */
struct iscsi_bhs_login_request {
	/** Opcode */
	uint8_t opcode;
	/** Flags */
	uint8_t flags;
	/** Maximum supported version number */
	uint8_t version_max;
	/** Minimum supported version number */
	uint8_t version_min;
	/** Segment lengths */
	union iscsi_segment_lengths lengths;
	/** Initiator session ID (IANA format) enterprise number and flags */
	uint32_t isid_iana_en;
	/** Initiator session ID (IANA format) qualifier */
	uint16_t isid_iana_qual;
	/** Target session identifying handle */
	uint16_t tsih;
	/** Initiator Task Tag */
	uint32_t itt;
	/** Connection ID */
	uint16_t cid;
	/** Reserved */
	uint16_t reserved_a;
	/** Command sequence number */
	uint32_t cmdsn;
	/** Expected status sequence number */
	uint32_t expstatsn;
	/** Reserved */
	uint8_t reserved_b[16];
};
/** Login request opcode */
#define ISCSI_OPCODE_LOGIN_REQUEST 0x03
/** Willingness to transition to next stage */
#define ISCSI_LOGIN_FLAG_TRANSITION 0x80
/** Key=value pairs continued in subsequent request */
#define ISCSI_LOGIN_FLAG_CONTINUE 0x40
/* Current stage values and mask */
#define ISCSI_LOGIN_CSG_MASK 0x0c
#define ISCSI_LOGIN_CSG_SECURITY_NEGOTIATION 0x00
#define ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION 0x04
#define ISCSI_LOGIN_CSG_FULL_FEATURE_PHASE 0x0c
/* Next stage values and mask */
#define ISCSI_LOGIN_NSG_MASK 0x03
#define ISCSI_LOGIN_NSG_SECURITY_NEGOTIATION 0x00
#define ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION 0x01
#define ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE 0x03
/** ISID IANA format marker */
#define ISCSI_ISID_IANA 0x40000000
/** Fen Systems Ltd. IANA enterprise number
 *
 * Permission is hereby granted to use Fen Systems Ltd.'s IANA
 * enterprise number with this iSCSI implementation.
 */
#define IANA_EN_FEN_SYSTEMS 10019
/**
 * iSCSI login response basic header segment
 *
 */
struct iscsi_bhs_login_response {
	/** Opcode */
	uint8_t opcode;
	/** Flags */
	uint8_t flags;
	/** Maximum supported version number */
	uint8_t version_max;
	/** Minimum supported version number */
	uint8_t version_min;
	/** Segment lengths */
	union iscsi_segment_lengths lengths;
	/** Initiator session ID (IANA format) enterprise number and flags */
	uint32_t isid_iana_en;
	/** Initiator session ID (IANA format) qualifier */
	uint16_t isid_iana_qual;
	/** Target session identifying handle */
	uint16_t tsih;
	/** Initiator Task Tag */
	uint32_t itt;
	/** Reserved */
	uint32_t reserved_a;
	/** Status sequence number */
	uint32_t statsn;
	/** Expected command sequence number */
	uint32_t expcmdsn;
	/** Maximum command sequence number */
	uint32_t maxcmdsn;
	/** Status class */
	uint8_t status_class;
	/** Status detail */
	uint8_t status_detail;
	/** Reserved */
	uint8_t reserved_b[10];
};
/** Login response opcode */
#define ISCSI_OPCODE_LOGIN_RESPONSE 0x23
/**
 * iSCSI SCSI command basic header segment
 *
 */
struct iscsi_bhs_scsi_command {
	/** Opcode */
	uint8_t opcode;
	/** Flags */
	uint8_t flags;
	/** Reserved */
	uint16_t reserved_a;
	/** Segment lengths */
	union iscsi_segment_lengths lengths;
	/** SCSI Logical Unit Number */
	uint8_t lun[8];
	/** Initiator Task Tag */
	uint32_t itt;
	/** Expected data transfer length */
	uint32_t exp_len;
	/** Command sequence number */
	uint32_t cmdsn;
	/** Expected status sequence number */
	uint32_t expstatsn;
	/** SCSI Command Descriptor Block (CDB) */
	union scsi_cdb cdb;
};
/** SCSI command opcode */
#define ISCSI_OPCODE_SCSI_COMMAND 0x01
/** Command will read data */
#define ISCSI_COMMAND_FLAG_READ 0x40
/** Command will write data */
#define ISCSI_COMMAND_FLAG_WRITE 0x20
/* Task attributes */
#define ISCSI_COMMAND_ATTR_UNTAGGED 0x00
#define ISCSI_COMMAND_ATTR_SIMPLE 0x01
#define ISCSI_COMMAND_ATTR_ORDERED 0x02
#define ISCSI_COMMAND_ATTR_HEAD_OF_QUEUE 0x03
#define ISCSI_COMMAND_ATTR_ACA 0x04
/**
 * iSCSI SCSI response basic header segment
 *
 */
struct iscsi_bhs_scsi_response {
	/** Opcode */
	uint8_t opcode;
	/** Flags */
	uint8_t flags;
	/** Response code */
	uint8_t response;
	/** SCSI status code */
	uint8_t status;
	/** Segment lengths */
	union iscsi_segment_lengths lengths;
	/** Reserved */
	uint8_t reserved_a[8];
	/** Initiator Task Tag */
	uint32_t itt;
	/** SNACK tag */
	uint32_t snack;
	/** Status sequence number */
	uint32_t statsn;
	/** Expected command sequence number */
	uint32_t expcmdsn;
	/** Maximum command sequence number */
	uint32_t maxcmdsn;
	/** Expected data sequence number */
	uint32_t expdatasn;
	/** Reserved */
	uint8_t reserved_b[8];
};
/** SCSI response opcode */
#define ISCSI_OPCODE_SCSI_RESPONSE 0x21
/** SCSI command completed at target */
#define ISCSI_RESPONSE_COMMAND_COMPLETE 0x00
/** SCSI target failure */
#define ISCSI_RESPONSE_TARGET_FAILURE 0x01
/**
 * iSCSI data in basic header segment
 *
 */
struct iscsi_bhs_data_in {
	/** Opcode */
	uint8_t opcode;
	/** Flags */
	uint8_t flags;
	/** Reserved */
	uint8_t reserved_a;
	/** SCSI status code */
	uint8_t status;
	/** Segment lengths */
	union iscsi_segment_lengths lengths;
	/** Logical Unit Number */
	uint8_t lun[8];
	/** Initiator Task Tag */
	uint32_t itt;
	/** Target Transfer Tag */
	uint32_t ttt;
	/** Status sequence number */
	uint32_t statsn;
	/** Expected command sequence number */
	uint32_t expcmdsn;
	/** Maximum command sequence number */
	uint32_t maxcmdsn;
	/** Data sequence number */
	uint32_t datasn;
	/** Buffer offset */
	uint32_t offset;
	/** Residual count */
	uint32_t residual_count;
};
/** Data in opcode */
#define ISCSI_OPCODE_DATA_IN 0x25
/** Data requires acknowledgement */
#define ISCSI_DATA_FLAG_ACKNOWLEDGE 0x40
/** Data overflow occurred */
#define ISCSI_DATA_FLAG_OVERFLOW 0x04
/** Data underflow occurred */
#define ISCSI_DATA_FLAG_UNDERFLOW 0x02
/** SCSI status code and verflow/underflow flags are valid */
#define ISCSI_DATA_FLAG_STATUS 0x01
/**
 * An iSCSI basic header segment
 */
union iscsi_bhs {
	struct iscsi_bhs_common common;
	struct iscsi_bhs_login_request login_request;
	struct iscsi_bhs_login_response login_response;
	struct iscsi_bhs_scsi_command scsi_command;
	struct iscsi_bhs_scsi_response scsi_response;
	struct iscsi_bhs_data_in data_in;
	unsigned char bytes[ sizeof ( struct iscsi_bhs_common ) ];
};
/** State */
enum iscsi_state {
	/** In the process of logging in */
	ISCSI_STATE_FAILED = -1,
	ISCSI_STATE_NOT_CONNECTED = 0,
	ISCSI_STATE_IDLE,
	ISCSI_STATE_LOGGING_IN,
	ISCSI_STATE_READING_DATA,
};
/** State of an iSCSI TX engine */
enum iscsi_tx_state {
	/** Nothing to send */
	ISCSI_TX_IDLE = 0,
	/** Sending the basic header segment */
	ISCSI_TX_BHS,
	/** Sending the additional header segment */
	ISCSI_TX_AHS,
	/** Sending the data segment */
	ISCSI_TX_DATA,
	/** Sending the data segment padding */
	ISCSI_TX_DATA_PADDING,
};
/** State of an iSCSI RX engine */
enum iscsi_rx_state {
	/** Receiving the basic header segment */
	ISCSI_RX_BHS = 0,
	/** Receiving the additional header segment */
	ISCSI_RX_AHS,
	/** Receiving the data segment */
	ISCSI_RX_DATA,
	/** Receiving the data segment padding */
	ISCSI_RX_DATA_PADDING,
};
/** An iSCSI session */
struct iscsi_session {
	/** TCP connection for this session */
	struct tcp_connection tcp;
	/** Initiator IQN */
	const char *initiator;
	/** Target IQN */
	const char *target;
	/** Block size in bytes */
	size_t block_size;
	/** Starting block number of the current data transfer */
	unsigned long block_start;
	/** Block count of the current data transfer */
	unsigned long block_count;
	/** Block read callback function
	 *
	 * Note that this may be called several times, since it is
	 * called per-packet rather than per-block.
	 */
	void ( * block_read_callback ) ( void *private, const void *data,
					 unsigned long offset, size_t len );
	/** Block read callback private data
	 *
	 * This is passed to block_read_callback()
	 */
	void *block_read_private;
	/** State of the session */
	enum iscsi_state state;
	/** Target session identifying handle
	 *
	 * This is assigned by the target when we first log in, and
	 * must be reused on subsequent login attempts.
	 */
	uint16_t tsih;
	/** Initiator task tag
	 *
	 * This is the tag of the current command.  It is incremented
	 * whenever a final response PDU is received.
	 */
	uint32_t itt;
	/** Command sequence number
	 *
	 * This is the sequence number of the current command, used to
	 * fill out the CmdSN field in iSCSI request PDUs.  It is
	 * updated with the value of the ExpCmdSN field whenever we
	 * receive an iSCSI response PDU containing such a field.
	 */
	uint32_t cmdsn;
	/** Status sequence number
	 *
	 * This is the most recent status sequence number present in
	 * the StatSN field of an iSCSI response PDU containing such a
	 * field.  Whenever we send an iSCSI request PDU, we fill out
	 * the ExpStatSN field with this value plus one.
	 */
	uint32_t statsn;
	
	/** Basic header segment for current TX PDU */
	union iscsi_bhs tx_bhs;
	/** State of the TX engine */
	enum iscsi_tx_state tx_state;
	/** Byte offset within the current TX state */
	size_t tx_offset;
	/** Basic header segment for current RX PDU */
	union iscsi_bhs rx_bhs;
	/** State of the RX engine */
	enum iscsi_rx_state rx_state;
	/** Byte offset within the current RX state */
	size_t rx_offset;
};
static inline int iscsi_busy ( struct iscsi_session *iscsi ) {
	return ( iscsi->state > ISCSI_STATE_IDLE );
}
static inline int iscsi_error ( struct iscsi_session *iscsi ) {
	return ( iscsi->state == ISCSI_STATE_FAILED );
}
#endif /* _GPXE_ISCSI_H */
 |