Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / devices / sas / sas_mpi.cc
// ========== 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.
* All rights reserved.
*
*/
#include "sas.h"
// length is (ceiling(size/8)*8)/4, i.e. 0~7=>0, 8=>2, 9=>4
uint32_t get_length(uint32_t size) {
uint32_t length;
assert(size > 0);
length = size >> 3;
if (size % 8)
length++;
return(length << 1);
}
// be is used to represent each byte in the remainder (div by 8)
uint8_t get_be(uint32_t size) {
uint32_t remain;
uint8_t be = 0;
assert(size > 0);
remain = size % 8;
if (remain == 0)
remain = 8;
for (int i = 0; i < remain; i++)
be |= (1 << i);
return(be);
}
// 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;
uint32_t t_size;
while (rem_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.
if (rem_size > 0x20000)
t_size = 0x20000;
else
t_size = rem_size;
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");
exit(1);
}
rem_size -= t_size;
va += t_size;
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) {
uint32_t flaglength;
memory_access(sge_addr, sge_addr_mode, (void*)&flaglength, 4, false);
flaglength = SAM_LE_32(flaglength);
uint8_t flag = flaglength >> MPI_SGE_FLAGS_SHIFT;
uint64_t mem_addr;
addrMd_xactnType mem_addr_mode;
uint64_t next_sge_addr;
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) {
// 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;
}
else {
// 32-bit addressing
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;
}
if (length < size) {
// more SGEs
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);
}
else {
// last SGE
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) {
// 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;
}
else {
// 32-bit addressing
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);
}
else {
debug_err("%s: only Simple and Chain Elements are supported\n", getName());
exit(1);
}
}
// 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;
uint64_t mem_addr;
addrMd_xactnType mem_addr_mode;
uint64_t next_sge_addr;
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) {
// 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;
}
else {
// 32-bit addressing
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) {
// 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;
}
else {
// 32-bit addressing
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);
}
else {
debug_err("%s: only Simple and Chain Elements are supported\n", getName());
exit(1);
}
}
// 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() {
uint16_t value;
// 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;
_hdshk_msg_reply_send++;
// set interrupt
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;
switch (msg->Function) {
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());
exit(1);
case MPI_CONFIG_PAGETYPE_IOC:
access_page_ioc(config_req, config_reply);
break;
case MPI_CONFIG_PAGETYPE_BIOS:
debug_err("%s: MPI_CONFIG_PAGETYPE_IOC not supported\n", getName());
exit(1);
case MPI_CONFIG_PAGETYPE_SCSI_PORT:
debug_err("%s: MPI_CONFIG_PAGETYPE_SCSI_PORT not supported\n", getName());
exit(1);
case MPI_CONFIG_PAGETYPE_SCSI_DEVICE:
debug_err("%s: MPI_CONFIG_PAGETYPE_SCSI_DEVICE not supported\n", getName());
exit(1);
case MPI_CONFIG_PAGETYPE_FC_PORT:
debug_err("%s: MPI_CONFIG_PAGETYPE_FC_PORT not supported\n", getName());
exit(1);
case MPI_CONFIG_PAGETYPE_FC_DEVICE:
debug_err("%s: PI_CONFIG_PAGETYPE_FC_DEVICE not supported\n", getName());
exit(1);
case MPI_CONFIG_PAGETYPE_LAN:
debug_err("%s: MPI_CONFIG_PAGETYPE_LAN not supported\n", getName());
exit(1);
case MPI_CONFIG_PAGETYPE_RAID_VOLUME:
debug_err("%s: MPI_CONFIG_PAGETYPE_RAID_VOLUME not supported\n", getName());
exit(1);
case MPI_CONFIG_PAGETYPE_MANUFACTURING:
access_page_manufacturing(config_req, config_reply);
break;
case MPI_CONFIG_PAGETYPE_RAID_PHYSDISK:
debug_err("%s: MPI_CONFIG_PAGETYPE_RAID_PHYSDISK not supported\n", getName());
exit(1);
case MPI_CONFIG_PAGETYPE_INBAND:
debug_err("%s: MPI_CONFIG_PAGETYPE_INBAND not supported\n", getName());
exit(1);
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);
break;
case MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER:
debug_err("%s: MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER not impl\n", getName());
exit(1);
case MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE:
access_page_sas_device(config_req, config_reply);
break;
case MPI_CONFIG_EXTPAGETYPE_SAS_PHY:
access_page_sas_phy(config_req, config_reply);
break;
default:
debug_err("%s: unknow page type\n", getName());
exit(1);
}
break;
default:
debug_err("%s: unknow page type\n", getName());
exit(1);
}
break;
}
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;
send_handshake_msg();
break;
}
case MPI_FUNCTION_FW_DOWNLOAD:
debug_err("%s: MPI_FUNCTION_FW_DOWNLOAD not impl\n", getName());
exit(1);
case MPI_FUNCTION_FW_UPLOAD:
debug_err("%s: MPI_FUNCTION_FW_UPLOAD not impl\n", getName());
exit(1);
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);
// others
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);
send_handshake_msg();
break;
}
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;
// others
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;
send_handshake_msg();
break;
}
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;
send_handshake_msg();
break;
}
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);
send_handshake_msg();
break;
}
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());
exit(1);
}
uint32_t fma = _reply_free_queue.front();
uint32_t msg_addr = fma & FMA_ADDRESS_MASK;
_reply_free_queue.pop();
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);
break;
}
case MPI_FUNCTION_TARGET_ASSIST:
debug_err("%s: MPI_FUNCTION_TARGET_ASSIST not impl\n", getName());
exit(1);
case MPI_FUNCTION_TARGET_STATUS_SEND:
debug_err("%s: MPI_FUNCTION_TARGET_STATUS_SEND not impl\n", getName());
exit(1);
default:
debug_err("%s: unsupported handshake message\n", getName());
exit(1);
}
}
// Send mpi context/address reply msg to the host
void SAS::send_mpi_msg(uint32_t reply, bool is_cntx, uint8_t cntx_type) {
uint32_t fma = 0;
if (is_cntx) {
// context reply
fma |= (reply & MPI_CONTEXT_REPLY_CONTEXT_MASK);
fma |= ((cntx_type & 0x3) << MPI_CONTEXT_REPLY_TYPE_SHIFT);
}
else {
// address reply
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 interrupt
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;
// read the header first
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);
break;
}
default:
debug_err("%s: MPI message unsupported\n", getName());
exit(1);
}
free(msg_buf);
}