// ========== Copyright Header Begin ==========================================
// OpenSPARC T2 Processor File: sas_mpi.cc
// Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
// The above named program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License version 2 as published by the Free Software Foundation.
// The above named 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. See the GNU
// General Public License for more details.
// You should have received a copy of the GNU General Public
// License along with this work; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
// ========== Copyright Header End ============================================
* "sas_mpi.cc" LSI SAS1064E PCIE to 4-Port Serial Attached
* SCSI Controller Simulator
* Message Passing Interface (MPI) implementation
* Copyright (C) 2007 Sun Microsystems, Inc.
// length is (ceiling(size/8)*8)/4, i.e. 0~7=>0, 8=>2, 9=>4
uint32_t get_length(uint32_t size
) {
// be is used to represent each byte in the remainder (div by 8)
uint8_t get_be(uint32_t size
) {
for (int i
= 0; i
< remain
; i
++)
// Transport data to/from simulated memory (pointed by "va")
// from/to the host memory (pointed by "data").
void SAS::memory_access(uint64_t va
, addrMd_xactnType mode
, void *data
, uint32_t size
, bool wr
) {
uint32_t rem_size
= size
;
// the size of data in each memory transfer is limited by the "length"
// parameter (roughly size/4) in busif_access(), which is 16-bit.
pcieCompleter status
= busIf
->busif_access(PCIE_MEM
, wr
, va
, data
,
get_length(t_size
), get_be(t_size
), getId(), mode
,&samId
);
if (handleCompletion(status
) == -1) {
debug_err("%s: pcie error when transferring data to/from memory\n");
data
= (uchar_t
*)data
+ t_size
;
// Transport data to/from simulated memory (pointed by "sge_addr")
// from/to the host memory (pointed by "data").
// Note: sge_addr is the address of memory location where actually SGE is stored.
// So we have to first read SGE using sge_addr, and this involves memory_access().
// Then we have to move data to/from the memory location specified by SGE, which
// also involves memory_access().
void SAS::memory_transport(uint64_t sge_addr
, addrMd_xactnType sge_addr_mode
, void *data
, uint32_t size
, bool wr
) {
memory_access(sge_addr
, sge_addr_mode
, (void*)&flaglength
, 4, false);
flaglength
= SAM_LE_32(flaglength
);
uint8_t flag
= flaglength
>> MPI_SGE_FLAGS_SHIFT
;
addrMd_xactnType mem_addr_mode
;
addrMd_xactnType next_sge_addr_mode
;
if ((flag
& MPI_SGE_FLAGS_ELEMENT_MASK
) == MPI_SGE_FLAGS_SIMPLE_ELEMENT
) {
uint32_t length
= flaglength
& MPI_SGE_LENGTH_MASK
;
if (flag
& MPI_SGE_FLAGS_64_BIT_ADDRESSING
) {
mem_addr_mode
= mem_addr64
;
sge_simple64_t
*simple_64
= (sge_simple64_t
*)calloc(1, sizeof(sge_simple64_t
));
memory_access(sge_addr
, sge_addr_mode
, (void*)simple_64
, sizeof(sge_simple64_t
), false);
simple_64
->FlagsLength
= SAM_LE_32(simple_64
->FlagsLength
);
simple_64
->Address_Low
= SAM_LE_32(simple_64
->Address_Low
);
simple_64
->Address_High
= SAM_LE_32(simple_64
->Address_High
);
mem_addr
= (simple_64
->Address_High
<< 32) | simple_64
->Address_Low
;
mem_addr_mode
= mem_addr32
;
sge_simple32_t
*simple_32
= (sge_simple32_t
*)calloc(1, sizeof(sge_simple32_t
));
memory_access(sge_addr
, sge_addr_mode
, (void*)simple_32
, sizeof(sge_simple32_t
), false);
simple_32
->FlagsLength
= SAM_LE_32(simple_32
->FlagsLength
);
simple_32
->Address
= SAM_LE_32(simple_32
->Address
);
mem_addr
= simple_32
->Address
;
memory_access(mem_addr
, mem_addr_mode
, data
, length
, wr
);
next_sge_addr
= sge_addr
+
(mem_addr_mode
== mem_addr32
) ? sizeof(sge_simple32
) : sizeof(sge_simple64
);
memory_transport(next_sge_addr
, sge_addr_mode
, (void*)((uchar_t
*)data
+ length
), size
- length
, wr
);
memory_access(mem_addr
, mem_addr_mode
, data
, size
, wr
);
else if ((flag
& MPI_SGE_FLAGS_ELEMENT_MASK
) == MPI_SGE_FLAGS_CHAIN_ELEMENT
) {
if (flag
& MPI_SGE_FLAGS_64_BIT_ADDRESSING
) {
sge_chain64_t
*chain_64
= (sge_chain64_t
*)calloc(1, sizeof(sge_chain64_t
));
memory_access(sge_addr
, sge_addr_mode
, (void*)chain_64
, sizeof(sge_chain64_t
), false);
chain_64
->Length
= SAM_LE_16(chain_64
->Length
);
chain_64
->Address64_Low
= SAM_LE_32(chain_64
->Address64_Low
);
chain_64
->Address64_High
= SAM_LE_32(chain_64
->Address64_High
);
next_sge_addr
= (chain_64
->Address64_High
<< 32) | chain_64
->Address64_Low
;
next_sge_addr_mode
= mem_addr64
;
sge_chain32_t
*chain_32
= (sge_chain32_t
*)calloc(1, sizeof(sge_chain32_t
));
memory_access(sge_addr
, sge_addr_mode
, (void*)chain_32
, sizeof(sge_chain32_t
), false);
chain_32
->Length
= SAM_LE_16(chain_32
->Length
);
chain_32
->Address
= SAM_LE_32(chain_32
->Address
);
next_sge_addr
= chain_32
->Address
;
next_sge_addr_mode
= mem_addr32
;
memory_transport(next_sge_addr
, next_sge_addr_mode
, data
, size
, wr
);
debug_err("%s: only Simple and Chain Elements are supported\n", getName());
// Transport data to/from simulated memory (pointed by "sge")
// from/to the host memory (pointed by "data").
// Note: memory_transport() defined above will be called in the
// case of chain element.
void SAS::memory_transport(void *sge
, void *data
, uint32_t size
, bool wr
) {
uint32_t flaglength
= *(uint32_t *)sge
;
uint8_t flag
= flaglength
>> MPI_SGE_FLAGS_SHIFT
;
addrMd_xactnType mem_addr_mode
;
addrMd_xactnType next_sge_addr_mode
;
if ((flag
& MPI_SGE_FLAGS_ELEMENT_MASK
) == MPI_SGE_FLAGS_SIMPLE_ELEMENT
) {
uint32_t length
= flaglength
& MPI_SGE_LENGTH_MASK
;
if (flag
& MPI_SGE_FLAGS_64_BIT_ADDRESSING
) {
mem_addr_mode
= mem_addr64
;
sge_simple64
*simple_64
= (sge_simple64
*)sge
;
mem_addr
= (simple_64
->Address_High
<< 32) | simple_64
->Address_Low
;
mem_addr_mode
= mem_addr32
;
sge_simple32
*simple_32
= (sge_simple32
*)sge
;
mem_addr
= simple_32
->Address
;
memory_access(mem_addr
, mem_addr_mode
, data
, size
, wr
);
else if ((flag
& MPI_SGE_FLAGS_ELEMENT_MASK
) == MPI_SGE_FLAGS_CHAIN_ELEMENT
) {
if (flag
& MPI_SGE_FLAGS_64_BIT_ADDRESSING
) {
sge_chain64
*chain_64
= (sge_chain64
*)sge
;
next_sge_addr
= (chain_64
->Address64_High
<< 32) | chain_64
->Address64_Low
;
next_sge_addr_mode
= mem_addr64
;
sge_chain32
*chain_32
= (sge_chain32
*)sge
;
next_sge_addr
= chain_32
->Address
;
next_sge_addr_mode
= mem_addr32
;
memory_transport(next_sge_addr
, next_sge_addr_mode
, data
, size
, wr
);
debug_err("%s: only Simple and Chain Elements are supported\n", getName());
// Send handshake reply msg to the host through doorbell
// Note: the first hword is written to doorbell here,
// and all remaining bytes are sent (hword at a time)
// each time the doorbell is read.
void SAS::send_handshake_msg() {
// set the size of message to be sent in hword
_hdshk_msg_reply_size
= (((msg_default_reply
*)_hdshk_msg_reply_buf
)->MsgLength
) << 1;
_hdshk_msg_reply_send
= 0;
// put the first hword in the doorbell
value
= SAM_LE_16(*(uint16_t *)(_hdshk_msg_reply_buf
+ (_hdshk_msg_reply_send
<< 1)));
_mpt_reg
->doorbell
= (_mpt_reg
->doorbell
& ~MPI_DOORBELL_DATA_MASK
) | value
;
set_doorbell_interrupt();
// Process a handshake msg received through doorbell
// Request msg is in little-endian, should be converted into big-endian after receiving
// Reply msg is in big-endian, should be converted into little-endian before sending
void SAS::process_handshake_msg() {
msg_request_header_t
*msg
= (msg_request_header_t
*)_hdshk_msg_req_buf
;
case MPI_FUNCTION_CONFIG
: {
debug_info("%s: MPI_FUNCTION_CONFIG\n", getName());
msg_config_t
*config_req
;
msg_config_reply_t
*config_reply
;
config_req
= (msg_config_t
*)_hdshk_msg_req_buf
;
config_reply
= (msg_config_reply_t
*)_hdshk_msg_reply_buf
;
bzero(_hdshk_msg_reply_buf
, HDSHK_MSG_SIZE
);
config_req
->ExtPageLength
= SAM_LE_16(config_req
->ExtPageLength
);
config_req
->MsgContext
= SAM_LE_32(config_req
->MsgContext
);
config_req
->PageAddress
= SAM_LE_32(config_req
->PageAddress
);
// 1. PageBufferSGE is not 32-bit as described in MPT Ch.5
// 2. I am not sure it should be 64-bit or 128-bit, I prefer 128-bit as used by ioc_init and ioc_facts
// 3. However, 64-bit is used here according to the driver code in OBP and Solaris
// 4. swap() is based on sge_simple_union_t, it should be diferent if it is sge_chain_union_t
config_req
->PageBufferSGE
.u1
.Simple
.FlagsLength
=
SAM_LE_32(config_req
->PageBufferSGE
.u1
.Simple
.FlagsLength
);
config_req
->PageBufferSGE
.u1
.Simple
.u1
.Address32
=
SAM_LE_32(config_req
->PageBufferSGE
.u1
.Simple
.u1
.Address32
);
switch (config_req
->Header
.PageType
& MPI_CONFIG_PAGETYPE_MASK
) {
case MPI_CONFIG_PAGETYPE_IO_UNIT
:
debug_err("%s: MPI_CONFIG_PAGETYPE_IO_UNIT not supported\n", getName());
case MPI_CONFIG_PAGETYPE_IOC
:
access_page_ioc(config_req
, config_reply
);
case MPI_CONFIG_PAGETYPE_BIOS
:
debug_err("%s: MPI_CONFIG_PAGETYPE_IOC not supported\n", getName());
case MPI_CONFIG_PAGETYPE_SCSI_PORT
:
debug_err("%s: MPI_CONFIG_PAGETYPE_SCSI_PORT not supported\n", getName());
case MPI_CONFIG_PAGETYPE_SCSI_DEVICE
:
debug_err("%s: MPI_CONFIG_PAGETYPE_SCSI_DEVICE not supported\n", getName());
case MPI_CONFIG_PAGETYPE_FC_PORT
:
debug_err("%s: MPI_CONFIG_PAGETYPE_FC_PORT not supported\n", getName());
case MPI_CONFIG_PAGETYPE_FC_DEVICE
:
debug_err("%s: PI_CONFIG_PAGETYPE_FC_DEVICE not supported\n", getName());
case MPI_CONFIG_PAGETYPE_LAN
:
debug_err("%s: MPI_CONFIG_PAGETYPE_LAN not supported\n", getName());
case MPI_CONFIG_PAGETYPE_RAID_VOLUME
:
debug_err("%s: MPI_CONFIG_PAGETYPE_RAID_VOLUME not supported\n", getName());
case MPI_CONFIG_PAGETYPE_MANUFACTURING
:
access_page_manufacturing(config_req
, config_reply
);
case MPI_CONFIG_PAGETYPE_RAID_PHYSDISK
:
debug_err("%s: MPI_CONFIG_PAGETYPE_RAID_PHYSDISK not supported\n", getName());
case MPI_CONFIG_PAGETYPE_INBAND
:
debug_err("%s: MPI_CONFIG_PAGETYPE_INBAND not supported\n", getName());
case MPI_CONFIG_PAGETYPE_EXTENDED
:
switch (config_req
->ExtPageType
) {
case MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT
:
access_page_sas_io_unit(config_req
, config_reply
);
case MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER
:
debug_err("%s: MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER not impl\n", getName());
case MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE
:
access_page_sas_device(config_req
, config_reply
);
case MPI_CONFIG_EXTPAGETYPE_SAS_PHY
:
access_page_sas_phy(config_req
, config_reply
);
debug_err("%s: unknow page type\n", getName());
debug_err("%s: unknow page type\n", getName());
case MPI_FUNCTION_EVENT_NOTIFICATION
: {
debug_info("%s: MPI_FUNCTION_EVENT_NOTIFICATION\n", getName());
msg_event_notify_t
*event_notify_req
;
msg_event_notify_reply_t
*event_notify_reply
;
event_notify_req
= (msg_event_notify_t
*)_hdshk_msg_req_buf
;
event_notify_reply
= (msg_event_notify_reply_t
*)_hdshk_msg_reply_buf
;
bzero(_hdshk_msg_reply_buf
, HDSHK_MSG_SIZE
);
event_notify_reply
->EventDataLength
= 1;
event_notify_reply
->MsgLength
= sizeof(msg_event_notify_reply_t
) >> 2;
event_notify_reply
->Function
= event_notify_req
->Function
;
event_notify_reply
->AckRequired
= 0;
event_notify_reply
->MsgFlags
= 0;
event_notify_reply
->MsgContext
= event_notify_req
->MsgContext
;
event_notify_reply
->IOCStatus
= SAM_LE_16(MPI_IOCSTATUS_SUCCESS
);
event_notify_reply
->IOCLogInfo
= 0;
event_notify_reply
->Event
= SAM_LE_16(MPI_EVENT_EVENT_CHANGE
);
event_notify_reply
->EventContext
= 0;
event_notify_reply
->Data
[0] = 0;
case MPI_FUNCTION_FW_DOWNLOAD
:
debug_err("%s: MPI_FUNCTION_FW_DOWNLOAD not impl\n", getName());
case MPI_FUNCTION_FW_UPLOAD
:
debug_err("%s: MPI_FUNCTION_FW_UPLOAD not impl\n", getName());
case MPI_FUNCTION_IOC_FACTS
: {
debug_info("%s: MPI_FUNCTION_IOC_FACTS\n", getName());
msg_ioc_facts_t
*ioc_facts_req
;
msg_ioc_facts_reply_t
*ioc_facts_reply
;
ioc_facts_req
= (msg_ioc_facts_t
*)_hdshk_msg_req_buf
;
ioc_facts_reply
= (msg_ioc_facts_reply_t
*)_hdshk_msg_reply_buf
;
bzero(_hdshk_msg_reply_buf
, HDSHK_MSG_SIZE
);
// read from ioc configuration
ioc_facts_reply
->WhoInit
= _ioc_conf
->who_init
;
ioc_facts_reply
->MaxDevices
= _ioc_conf
->max_devices
;
ioc_facts_reply
->MaxBuses
= _ioc_conf
->max_buses
;
ioc_facts_reply
->CurrentHostMfaHighAddr
= SAM_LE_32(_ioc_conf
->host_mfa_high_addr
);
ioc_facts_reply
->CurrentSenseBufferHighAddr
= SAM_LE_32(_ioc_conf
->sense_buffer_high_addr
);
ioc_facts_reply
->CurReplyFrameSize
= SAM_LE_16(_ioc_conf
->reply_frame_size
);
bcopy(&_ioc_conf
->host_page_buffer_sge
, &ioc_facts_reply
->HostPageBufferSGE
, sizeof(sge_simple64_t
));
ioc_facts_reply
->HostPageBufferSGE
.FlagsLength
= SAM_LE_32(ioc_facts_reply
->HostPageBufferSGE
.FlagsLength
);
ioc_facts_reply
->HostPageBufferSGE
.Address_Low
= SAM_LE_32(ioc_facts_reply
->HostPageBufferSGE
.Address_Low
);
ioc_facts_reply
->HostPageBufferSGE
.Address_High
= SAM_LE_32(ioc_facts_reply
->HostPageBufferSGE
.Address_High
);
ioc_facts_reply
->MsgVersion
= SAM_LE_16(MPI_VERSION
);
ioc_facts_reply
->HeaderVersion
= SAM_LE_16(MPI_HEADER_VERSION
);
ioc_facts_reply
->MsgLength
= sizeof(msg_ioc_facts_reply_t
) >> 2;
ioc_facts_reply
->Function
= ioc_facts_req
->Function
;
ioc_facts_reply
->IOCNumber
= function
;
ioc_facts_reply
->MsgFlags
= 0;
ioc_facts_reply
->MsgContext
= ioc_facts_req
->MsgContext
;
ioc_facts_reply
->IOCExceptions
= 0;
ioc_facts_reply
->IOCStatus
= SAM_LE_16(MPI_IOCSTATUS_SUCCESS
);
ioc_facts_reply
->IOCLogInfo
= 0;
ioc_facts_reply
->MaxChainDepth
= IOC_CHAIN_DEPTH
;
ioc_facts_reply
->BlockSize
= IOC_BLOCK_SIZE
;
ioc_facts_reply
->Flags
= 4;
ioc_facts_reply
->ReplyQueueDepth
= SAM_LE_16(IOC_REPLY_QUEUE_DEPTH
);
ioc_facts_reply
->RequestFrameSize
= SAM_LE_16(IOC_REQ_FRAME_SIZE
);
ioc_facts_reply
->ProductID
= SAM_LE_16(IOC_PRODUCTION_ID
);
ioc_facts_reply
->GlobalCredits
= SAM_LE_16(IOC_REQ_QUEUE_DEPTH
);
ioc_facts_reply
->NumberOfPorts
= IOC_NUM_PORTS
;
ioc_facts_reply
->EventState
= 0;
ioc_facts_reply
->FWImageSize
= 0;
ioc_facts_reply
->IOCCapabilities
= SAM_LE_32(IOC_CAPABILITIES
);
ioc_facts_reply
->FWVersion
.Word
= SAM_LE_32((MPI_VERSION
<< 16) | MPI_HEADER_VERSION
);
ioc_facts_reply
->HighPriorityQueueDepth
= SAM_LE_16(IOC_PRI_QUEUE_DEPTH
);
case MPI_FUNCTION_IOC_INIT
: {
debug_info("%s: MPI_FUNCTION_IOC_INIT\n", getName());
msg_ioc_init_t
*ioc_init_req
;
msg_ioc_init_reply_t
*ioc_init_reply
;
ioc_init_req
= (msg_ioc_init_t
*)_hdshk_msg_req_buf
;
ioc_init_reply
= (msg_ioc_init_reply_t
*)_hdshk_msg_reply_buf
;
bzero(_hdshk_msg_reply_buf
, HDSHK_MSG_SIZE
);
_ioc_conf
->who_init
= ioc_init_req
->WhoInit
;
_ioc_conf
->max_devices
= ioc_init_req
->MaxDevices
;
_ioc_conf
->max_buses
= ioc_init_req
->MaxBuses
;
_ioc_conf
->reply_frame_size
= SAM_LE_16(ioc_init_req
->ReplyFrameSize
);
_ioc_conf
->host_mfa_high_addr
= SAM_LE_32(ioc_init_req
->HostMfaHighAddr
);
_ioc_conf
->sense_buffer_high_addr
= SAM_LE_32(ioc_init_req
->SenseBufferHighAddr
);
_ioc_conf
->host_page_buffer_sge
.FlagsLength
= SAM_LE_32(ioc_init_req
->HostPageBufferSGE
.FlagsLength
);
_ioc_conf
->host_page_buffer_sge
.Address_Low
= SAM_LE_32(ioc_init_req
->HostPageBufferSGE
.Address_Low
);
_ioc_conf
->host_page_buffer_sge
.Address_High
= SAM_LE_32(ioc_init_req
->HostPageBufferSGE
.Address_High
);
// change the status to Operational
_mpt_reg
->doorbell
= (_mpt_reg
->doorbell
& ~MPI_IOC_STATE_MASK
) | MPI_IOC_STATE_OPERATIONAL
;
// read from ioc configuration
ioc_init_reply
->WhoInit
= _ioc_conf
->who_init
;
ioc_init_reply
->MaxDevices
= _ioc_conf
->max_devices
;
ioc_init_reply
->MaxBuses
= _ioc_conf
->max_buses
;
ioc_init_reply
->MsgLength
= sizeof(msg_ioc_init_reply_t
) >> 2;
ioc_init_reply
->Function
= ioc_init_req
->Function
;
ioc_init_reply
->Flags
= 4;
ioc_init_reply
->MsgFlags
= 0;
ioc_init_reply
->MsgContext
= ioc_init_req
->MsgContext
;
ioc_init_reply
->IOCStatus
= SAM_LE_16(MPI_IOCSTATUS_SUCCESS
);
ioc_init_reply
->IOCLogInfo
= 0;
case MPI_FUNCTION_PORT_ENABLE
: {
debug_info("%s: MPI_FUNCTION_PORT_ENABLE\n", getName());
msg_port_enable_t
*port_enable_req
;
msg_port_enable_reply_t
*port_enable_reply
;
port_enable_req
= (msg_port_enable_t
*)_hdshk_msg_req_buf
;
port_enable_reply
= (msg_port_enable_reply_t
*)_hdshk_msg_reply_buf
;
bzero(_hdshk_msg_reply_buf
, HDSHK_MSG_SIZE
);
_port_enabled
[port_enable_req
->PortNumber
] = 1;
port_enable_reply
->MsgLength
= sizeof(msg_port_enable_reply_t
) >> 2;
port_enable_reply
->Function
= port_enable_req
->Function
;
port_enable_reply
->PortNumber
= port_enable_req
->PortNumber
;
port_enable_reply
->MsgFlags
= 0;
port_enable_reply
->MsgContext
= port_enable_req
->MsgContext
;
port_enable_reply
->IOCStatus
= SAM_LE_16(MPI_IOCSTATUS_SUCCESS
);
port_enable_reply
->IOCLogInfo
= 0;
case MPI_FUNCTION_PORT_FACTS
: {
debug_info("%s: MPI_FUNCTION_PORT_FACTS\n", getName());
msg_port_facts_t
*port_facts_req
;
msg_port_facts_reply_t
*port_facts_reply
;
port_facts_req
= (msg_port_facts_t
*)_hdshk_msg_req_buf
;
port_facts_reply
= (msg_port_facts_reply_t
*)_hdshk_msg_reply_buf
;
bzero(_hdshk_msg_reply_buf
, HDSHK_MSG_SIZE
);
port_facts_reply
->MsgLength
= sizeof(msg_port_facts_reply_t
) >> 2;
port_facts_reply
->Function
= port_facts_req
->Function
;
port_facts_reply
->PortNumber
= port_facts_req
->PortNumber
;
port_facts_reply
->MsgFlags
= 0;
port_facts_reply
->MsgContext
= port_facts_req
->MsgContext
;
port_facts_reply
->IOCStatus
= SAM_LE_16(MPI_IOCSTATUS_SUCCESS
);
port_facts_reply
->IOCLogInfo
= 0;
port_facts_reply
->PortType
= PORT_TYPE
;
port_facts_reply
->MaxDevices
= SAM_LE_16(PORT_MAX_DEVICES
);
port_facts_reply
->PortSCSIID
= SAM_LE_16(PORT_SCSI_ID
);
port_facts_reply
->ProtocolFlags
= SAM_LE_16(PORT_PROTOCOL_FLAGS
);
port_facts_reply
->MaxPostedCmdBuffers
= SAM_LE_16(PORT_MAX_POSTED_CMD_BUFFERS
);
port_facts_reply
->MaxPersistentIDs
= SAM_LE_16(PORT_MAX_PERSISTENT_IDS
);
port_facts_reply
->MaxLanBuckets
= SAM_LE_16(PORT_MAX_LAN_BUCKETS
);
case MPI_FUNCTION_SCSI_TASK_MGMT
: {
msg_scsi_task_mgmt_t
*scsi_task_mgmt_req
;
msg_scsi_task_mgmt_reply
*scsi_task_mgmt_reply
;
scsi_task_mgmt_req
= (msg_scsi_task_mgmt_t
*)_hdshk_msg_req_buf
;
scsi_task_mgmt_reply
= (msg_scsi_task_mgmt_reply
*)_hdshk_msg_reply_buf
;
bzero(_hdshk_msg_reply_buf
, HDSHK_MSG_SIZE
);
scsi_task_mgmt_reply
->TargetID
= scsi_task_mgmt_req
->TargetID
;
scsi_task_mgmt_reply
->Bus
= scsi_task_mgmt_req
->Bus
;
scsi_task_mgmt_reply
->MsgLength
= sizeof(msg_scsi_task_mgmt_reply
) >> 2;
scsi_task_mgmt_reply
->Function
= scsi_task_mgmt_req
->Function
;
scsi_task_mgmt_reply
->TaskType
= scsi_task_mgmt_req
->TaskType
;
scsi_task_mgmt_reply
->MsgFlags
= 0;
scsi_task_mgmt_reply
->MsgContext
= scsi_task_mgmt_req
->MsgContext
;
scsi_task_mgmt_reply
->IOCStatus
= SAM_LE_16(MPI_IOCSTATUS_SUCCESS
);
scsi_task_mgmt_reply
->IOCLogInfo
= 0;
scsi_task_mgmt_reply
->TerminationCount
= 0;
debug_info("%s: MPI_FUNCTION_SCSI_TASK_MGMT, type = %d\n", getName(), scsi_task_mgmt_reply
->TaskType
);
if (_reply_free_queue
.empty()) {
debug_err("%s: reply free queue is empty\n", getName());
uint32_t fma
= _reply_free_queue
.front();
uint32_t msg_addr
= fma
& FMA_ADDRESS_MASK
;
memory_access(mfa_addr(msg_addr
), mfa_addr_mode(), (void *)scsi_task_mgmt_reply
, sizeof(msg_scsi_task_mgmt_reply
), true);
send_mpi_msg(msg_addr
>> 1, false, 0);
case MPI_FUNCTION_TARGET_ASSIST
:
debug_err("%s: MPI_FUNCTION_TARGET_ASSIST not impl\n", getName());
case MPI_FUNCTION_TARGET_STATUS_SEND
:
debug_err("%s: MPI_FUNCTION_TARGET_STATUS_SEND not impl\n", getName());
debug_err("%s: unsupported handshake message\n", getName());
// Send mpi context/address reply msg to the host
void SAS::send_mpi_msg(uint32_t reply
, bool is_cntx
, uint8_t cntx_type
) {
fma
|= (reply
& MPI_CONTEXT_REPLY_CONTEXT_MASK
);
fma
|= ((cntx_type
& 0x3) << MPI_CONTEXT_REPLY_TYPE_SHIFT
);
fma
|= (reply
& MPI_ADDRESS_REPLY_ADDRESS_MASK
);
fma
|= MPI_CONTEXT_REPLY_A_BIT
;
// both updating queue and sending interrupt should be protected by mutex
pthread_mutex_lock(&_reply_queue_mutex
);
// push fma into reply post queue
_reply_post_queue
.push(fma
);
set_reply_msg_interrupt();
pthread_mutex_unlock(&_reply_queue_mutex
);
// Process a mpi message received through request queue
// Request msg is in little-endian, should be converted into big-endian after receiving
// Reply msg is in big-endian, should be converted into little-endian before sending
void SAS::process_mpi_msg(int entry
, uint32_t fma
) {
uint8_t *msg_buf
= (uint8_t *)calloc(256, sizeof(uint8_t));
uint32_t msg_addr
= fma
& FMA_ADDRESS_MASK
;
uint64_t sge_addr
= msg_addr
+ sizeof(msg_scsi_io_request_t
) - sizeof(sge_io_union_t
);
msg_request_header_t
*msg_header
;
msg_scsi_io_request_t
*msg_scsi_io_req
;
memory_access(mfa_addr(msg_addr
), mfa_addr_mode(), (void *)msg_buf
, sizeof(msg_request_header_t
), false);
msg_header
= (msg_request_header
*)msg_buf
;
// read and process scsi command according to different headers
switch (msg_header
->Function
) {
case MPI_FUNCTION_SCSI_IO_REQUEST
: {
memory_access(mfa_addr(msg_addr
), mfa_addr_mode(), (void *)msg_buf
, sizeof(msg_scsi_io_request_t
), false);
msg_scsi_io_req
= (msg_scsi_io_request_t
*)msg_buf
;
msg_scsi_io_req
->MsgContext
= SAM_LE_32(msg_scsi_io_req
->MsgContext
);
msg_scsi_io_req
->Control
= SAM_LE_32(msg_scsi_io_req
->Control
);
msg_scsi_io_req
->DataLength
= SAM_LE_32(msg_scsi_io_req
->DataLength
);
msg_scsi_io_req
->SenseBufferLowAddr
= SAM_LE_32(msg_scsi_io_req
->SenseBufferLowAddr
);
msg_scsi_io_req
->SGL
.u1
.Simple
.FlagsLength
=
SAM_LE_32(msg_scsi_io_req
->SGL
.u1
.Simple
.FlagsLength
);
msg_scsi_io_req
->SGL
.u1
.Simple
.u1
.Address32
=
SAM_LE_32(msg_scsi_io_req
->SGL
.u1
.Simple
.u1
.Address32
);
process_scsi_cmd(entry
, msg_scsi_io_req
, sge_addr
);
debug_err("%s: MPI message unsupported\n", getName());