// ========== Copyright Header Begin ==========================================
// OpenSPARC T2 Processor File: sas_disk.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_disk.cc" LSI SAS1064E PCIE to 4-Port Serial Attached
* SCSI Controller Simulator
* SCSI disk implementation
* Copyright (C) 2007 Sun Microsystems, Inc.
#define SCSI_DISK_SECTOR_SIZE 512
#define SCSI_DISK_LBLK_SIZE SCSI_DISK_SECTOR_SIZE
// Compute the time when an io request will finish
void SAS::compute_disk_delay(int entry
, int target
, bool wr
) {
int64_t current_time
= mmi_get_time();
if (target
>= 0 && target
< IOC_NUM_PORTS
&& _disk
[target
]) {
disk_delay
= _disk
[target
]->get_write_delay();
disk_delay
= _disk
[target
]->get_read_delay();
if (_earliest_serving_time
[target
] > current_time
)
_earliest_serving_time
[target
] = _earliest_serving_time
[target
] + disk_delay
;
_earliest_serving_time
[target
] = current_time
+ disk_delay
;
_etable
.set_invoke_time(entry
, _earliest_serving_time
[target
]);
// target doesn't exist at all
_etable
.set_invoke_time(entry
, current_time
+ WRITE_DELAY_DEFAULT
);
_etable
.set_invoke_time(entry
, current_time
+ READ_DELAY_DEFAULT
);
// IO request is done, record the reply msg and time in the event table
void SAS::scsi_io_done(int entry
, uint32_t reply
, uint8_t is_cntx
, uint8_t cntx_type
, int target
, bool wr
) {
// update event table entry
_etable
.done(entry
, reply
, is_cntx
, cntx_type
);
compute_disk_delay(entry
, target
, wr
);
// Error msg is needed especially during scsi target probing,
// where it is used to inform the host that a target disk doesn't exist.
void SAS::scsi_io_error(int entry
, msg_scsi_io_request_t
*req
, uint16_t ioc_status
) {
msg_scsi_io_reply_t
*reply
= (msg_scsi_io_reply_t
*)calloc(1, sizeof(msg_scsi_io_reply_t
));
reply
->TargetID
= req
->TargetID
;
reply
->MsgLength
= sizeof(msg_scsi_io_reply
) >> 2;
reply
->Function
= req
->Function
;
reply
->CDBLength
= req
->CDBLength
;
reply
->SenseBufferLength
= req
->SenseBufferLength
;
reply
->MsgFlags
= req
->MsgFlags
;
reply
->MsgContext
= SAM_LE_32(req
->MsgContext
);
reply
->SCSIStatus
= MPI_SCSI_STATUS_SUCCESS
;
reply
->SCSIState
= MPI_SCSI_STATE_NO_SCSI_STATUS
;
reply
->IOCStatus
= SAM_LE_16(ioc_status
& MPI_IOCSTATUS_MASK
);
reply
->TransferCount
= 0;
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 *)reply
, sizeof(msg_scsi_io_reply_t
), true);
scsi_io_done(entry
, msg_addr
>> 1, 0, 0, req
->TargetID
, false);
debug_info("%s: reply frame address= 0x%x, msgcontext=0x%x\n", getName(), msg_addr
, reply
->MsgContext
);
// Process a SCSI request
// step 1: memory_transport(), move the required data from/to disk to/from memory
// step 2: send_mpi_msg(), send the reply to the host
// Some requests may not need the first step.
void SAS::process_scsi_cmd(int entry
, msg_scsi_io_request_t
*req
, uint64_t sge_addr
) {
debug_info("%s: only bus 0 is supported\n", getName());
scsi_io_error(entry
, req
, MPI_IOCSTATUS_SCSI_INVALID_BUS
);
if (!(req
->TargetID
>= 0 && req
->TargetID
< IOC_NUM_PORTS
&& _disk
[req
->TargetID
])) {
debug_info("%s: target %d doesn't exists, check %s\n", getName(), req
->TargetID
, fname
);
scsi_io_error(entry
, req
, MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE
);
if (!(req
->LUN
[0] == 0 && req
->LUN
[1] == 0)) {
debug_err("%s: only LUN 0 is supported\n", getName());
debug_info("%s: process scsi cmd for Bus %d, Target %d...\n", getName(), req
->Bus
, req
->TargetID
);
case SCSI_COMMAND_REPORT_LUNS
: {
scsi_command_report_luns_t
*lunp
;
lunp
= (scsi_command_report_luns_t
*)req
->CDB
;
int32_t alloc_length
= (lunp
->allocation_length
[0] << 24) |
(lunp
->allocation_length
[1] << 16) |
(lunp
->allocation_length
[2] << 8) |
(lunp
->allocation_length
[3] << 0);
debug_err("%s: allocation length in report lun is less than 16 bytes\n", getName());
int datalen
= sizeof(struct scsi_report_luns
);
uchar_t
*datap
= (uchar_t
*)calloc(datalen
, sizeof(uchar_t
));
struct scsi_report_luns
*lundp
= (struct scsi_report_luns
*)datap
;
lundp
->lun_list_len
= SAM_BE_32(8);
memory_transport(mfa_addr(sge_addr
), mfa_addr_mode(), datap
, datalen
, true);
scsi_io_done(entry
, req
->MsgContext
, true, 0, req
->TargetID
, false);
case SCSI_COMMAND_INQUIRY
: {
scsi_command_inquiry_t
*inquiryp
;
inquiryp
= (scsi_command_inquiry_t
*)req
->CDB
;
if ((req
->CDB
[1] & 1) == 0) { /* Non-EVPD Inquiry */
datalen
= sizeof(SCSI_Inquiry_Data
);
datap
= (uchar_t
*)calloc (datalen
, sizeof(uchar_t
));
bcopy(_disk
[req
->TargetID
]->get_SCSI_Inquiry_Data(), datap
, datalen
);
else { /* EVPD Inquiry */
datalen
= sizeof(Supported_VPD
);
datap
= (uchar_t
*)calloc(datalen
, sizeof(uchar_t
));
bcopy(_disk
[req
->TargetID
]->get_Supported_VPD(), datap
, datalen
);
case 0x80: { // Unit Serial Number
datalen
= sizeof(Unit_Serial_Number
);
datap
= (uchar_t
*)calloc (datalen
, sizeof(uchar_t
));
bcopy(_disk
[req
->TargetID
]->get_Unit_Serial_Number(), datap
, datalen
);
case 0x81: { // Implemented Operating Definition
datalen
= sizeof(Implemented_Operating_Definition
);
datap
= (uchar_t
*)calloc(datalen
, sizeof(uchar_t
));
bcopy(_disk
[req
->TargetID
]->get_Implemented_Operating_Definition(), datap
, datalen
);
case 0x83: { // Device Identification
datalen
= sizeof(Device_Identification
);
datap
= (uchar_t
*)calloc(datalen
, sizeof(uchar_t
));
bcopy(_disk
[req
->TargetID
]->get_Device_Identification(), datap
, datalen
);
debug_err("%s: scsi_extended_inquiry page 0x%x NOT IMPL\n", getName(), req
->CDB
[2]);
if (datalen
> inquiryp
->allocation_length
) {
debug_info("%s: inquiry allocated data length is less than datalen\n", getName());
datalen
= inquiryp
->allocation_length
;
memory_transport(mfa_addr(sge_addr
), mfa_addr_mode(), datap
, datalen
, true);
scsi_io_done(entry
, req
->MsgContext
, true, 0, req
->TargetID
, false);
case SCSI_COMMAND_TEST_UNIT_READY
: {
scsi_command_test_unit_ready_t
*turp
;
turp
= (scsi_command_test_unit_ready_t
*)req
->CDB
;
scsi_io_done(entry
, req
->MsgContext
, true, 0, req
->TargetID
, false);
case SCSI_COMMAND_START_STOP_UNIT
: {
scsi_command_start_stop_unit_t
*ssup
;
ssup
= (scsi_command_start_stop_unit_t
*)req
->CDB
;
scsi_io_done(entry
, req
->MsgContext
, true, 0, req
->TargetID
, false);
case SCSI_COMMAND_READ_CAPACITY
: {
scsi_command_read_capacity_t
*rcp
;
rcp
= (scsi_command_read_capacity_t
*)req
->CDB
;
int datalen
= sizeof(scsi_command_read_capacity_data_t
);
uchar_t
*datap
= (uchar_t
*)calloc(datalen
, sizeof(uchar_t
));
scsi_command_read_capacity_data_t
*rcdp
= (scsi_command_read_capacity_data_t
*)datap
;
rcdp
->lblk
= SAM_BE_32(_disk
[req
->TargetID
]->get_capacity());
rcdp
->lblk_size
= SAM_BE_32(SCSI_DISK_LBLK_SIZE
);
memory_transport(mfa_addr(sge_addr
), mfa_addr_mode(), datap
, datalen
, true);
scsi_io_done(entry
, req
->MsgContext
, true, 0, req
->TargetID
, false);
case SCSI_COMMAND_READ
: {
scsi_command_read_t
*rdp
;
rdp
= (scsi_command_read_t
*)req
->CDB
;
lblkno
= rdp
->lblk_msb
<< 16;
lblkno
|= SAM_BE_16(rdp
->lblk
);
blks
= rdp
->transfer_length
;
size
= blks
* SCSI_DISK_LBLK_SIZE
;
tmp_buf
= (uchar_t
*)calloc(size
, sizeof(uchar_t
));
debug_info("%s: read----> blkno: %d, blks: %d\n", getName(), lblkno
, blks
);
_disk
[req
->TargetID
]->disk_read_lblk(lblkno
, tmp_buf
, blks
);
memory_transport(mfa_addr(sge_addr
), mfa_addr_mode(), tmp_buf
, size
, true);
scsi_io_done(entry
, req
->MsgContext
, true, 0, req
->TargetID
, false);
case SCSI_COMMAND_READ_LONG
: {
scsi_command_read_long_t
*rdlp
;
rdlp
= (scsi_command_read_long_t
*)req
->CDB
;
lblkno
= (rdlp
->lblk
[0] << 24) |
blks
= (rdlp
->transfer_length
[0] << 8) |
(rdlp
->transfer_length
[1] << 0);
size
= blks
* SCSI_DISK_LBLK_SIZE
;
tmp_buf
= (uchar_t
*)calloc(size
, sizeof(uchar_t
));
debug_info("%s: read long----> blkno: %d, blks: %d\n", getName(), lblkno
, blks
);
_disk
[req
->TargetID
]->disk_read_lblk(lblkno
, tmp_buf
, blks
);
memory_transport(mfa_addr(sge_addr
), mfa_addr_mode(), tmp_buf
, size
, true);
scsi_io_done(entry
, req
->MsgContext
, true, 0, req
->TargetID
, false);
case SCSI_COMMAND_WRITE
: {
scsi_command_write_t
*wrp
;
wrp
= (scsi_command_write_t
*)req
->CDB
;
lblkno
= (wrp
->lblk_msb
<< 16) | SAM_BE_16(wrp
->lblk
);
blks
= wrp
->transfer_length
;
size
= blks
* SCSI_DISK_LBLK_SIZE
;
tmp_buf
= (uchar_t
*)calloc(size
, sizeof(uchar_t
));
memory_transport(mfa_addr(sge_addr
), mfa_addr_mode(), tmp_buf
, size
, false);
debug_info("%s: write---> blkno: %d, blks: %d\n", getName(), lblkno
, blks
);
_disk
[req
->TargetID
]->disk_write_lblk(lblkno
, tmp_buf
, blks
);
scsi_io_done(entry
, req
->MsgContext
, true, 0, req
->TargetID
, true);
case SCSI_COMMAND_WRITE_LONG
: {
scsi_command_write_long_t
*wrlp
;
wrlp
= (scsi_command_write_long_t
*)req
->CDB
;
lblkno
= (wrlp
->lblk
[0] << 24) |
blks
= (wrlp
->transfer_length
[0] << 8) |
(wrlp
->transfer_length
[1] << 0);
size
= blks
* SCSI_DISK_LBLK_SIZE
;
tmp_buf
= (uchar_t
*)calloc(size
, sizeof(uchar_t
));
memory_transport(mfa_addr(sge_addr
), mfa_addr_mode(), tmp_buf
, size
, false);
debug_info("%s: write long----> blkno: %d, blks: %d\n", getName(), lblkno
, blks
);
_disk
[req
->TargetID
]->disk_write_lblk(lblkno
, tmp_buf
, blks
);
scsi_io_done(entry
, req
->MsgContext
, true, 0, req
->TargetID
, true);
case SCSI_COMMAND_REQUEST_SENSE
: {
scsi_command_request_sense_t
*rqsp
;
rqsp
= (scsi_command_request_sense_t
*)req
->CDB
;
if (rqsp
->allocation_length
< sizeof(scsi_disk_request_sense_data_t
)) {
debug_err("%s: SCSI_COMMAND_REQUEST_SENSE allocation length is too small\n", getName());
int datalen
= sizeof(scsi_disk_request_sense_data_t
);
uchar_t
*datap
= (uchar_t
*)calloc(datalen
, sizeof(uchar_t
));
scsi_disk_request_sense_data_t
*rqsdp
= (scsi_disk_request_sense_data_t
*)datap
;
rqsdp
->error_code
= 0x70;
rqsdp
->segment_number
= 0;
rqsdp
->sense_key
= 0x5; /* Illegal request */
rqsdp
->additional_sense_length
= 10;
rqsdp
->additional_sense_code
= 0x20; /* Invalid command opcode */
rqsdp
->additional_sense_code_qualifier
= 0x00;
memory_transport(mfa_addr(sge_addr
), mfa_addr_mode(), datap
, datalen
, true);
scsi_io_done(entry
, req
->MsgContext
, true, 0, req
->TargetID
, false);
case SCSI_COMMAND_LOG_SENSE
: {
scsi_command_log_sense_t
*lsp
;
lsp
= (scsi_command_log_sense_t
*)req
->CDB
;
scsi_io_done(entry
, req
->MsgContext
, true, 0, req
->TargetID
, false);
case SCSI_COMMAND_MODE_SENSE
: {
scsi_command_mode_sense_t
*msp
;
msp
= (scsi_command_mode_sense_t
*)req
->CDB
;
scsi_io_done(entry
, req
->MsgContext
, true, 0, req
->TargetID
, false);
case SCSI_COMMAND_PERSISTENT_RESERVE_IN
: {
scsi_io_done(entry
, req
->MsgContext
, true, 0, req
->TargetID
, false);
debug_err("%s: scsi command: 0x%x not supported\n", getName(), req
->CDB
[0]);
// Callback function for event handling
void SAS::io_callback(int entry
) {
if (entry
< 0 || entry
>= EVENT_TABLE_SIZE
) {
debug_err("%s: invalid entry id %d for the event table\n", getName(), entry
);
if (_etable
.events
[entry
].done
== 0) {
debug_err("%s: request in entry %d has not been served\n", getName(), entry
);
int64_t invoke_time
= _etable
.get_invoke_time(entry
);
int64_t current_time
= mmi_get_time();
if (invoke_time
!= current_time
) {
debug_err("%s: invoke time %lld is different from current time %lld\n", getName(), invoke_time
, current_time
);
debug_info("%s: io_callback is called, entry = %d, current time = %lld\n", getName(), entry
, current_time
);
send_mpi_msg(_etable
.events
[entry
].reply
, _etable
.events
[entry
].is_cntx
, _etable
.events
[entry
].cntx_type
);
void sas_io_callback(void *arg1
, void *arg2
) {
sasp
->io_callback((int)(long long int)arg2
);