Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / devices / sas / sas_disk.cc
CommitLineData
920dae64
AT
1// ========== Copyright Header Begin ==========================================
2//
3// OpenSPARC T2 Processor File: sas_disk.cc
4// Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
5// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
6//
7// The above named program is free software; you can redistribute it and/or
8// modify it under the terms of the GNU General Public
9// License version 2 as published by the Free Software Foundation.
10//
11// The above named program is distributed in the hope that it will be
12// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14// General Public License for more details.
15//
16// You should have received a copy of the GNU General Public
17// License along with this work; if not, write to the Free Software
18// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19//
20// ========== Copyright Header End ============================================
21/*
22 * "sas_disk.cc" LSI SAS1064E PCIE to 4-Port Serial Attached
23 * SCSI Controller Simulator
24 * SCSI disk implementation
25 *
26 * Copyright (C) 2007 Sun Microsystems, Inc.
27 * All rights reserved.
28 *
29 */
30#include <sys/types.h>
31#include <stdio.h>
32#include <unistd.h>
33#include <errno.h>
34#include <sys/stat.h>
35#include <sys/dirent.h>
36#include <fcntl.h>
37
38#include "sas.h"
39#include "sas_disk.h"
40
41
42// scsi disk block size
43#define SCSI_DISK_SECTOR_SIZE 512
44#define SCSI_DISK_LBLK_SIZE SCSI_DISK_SECTOR_SIZE
45
46
47// Compute the time when an io request will finish
48void SAS::compute_disk_delay(int entry, int target, bool wr) {
49 int64_t current_time = mmi_get_time();
50
51 if (target >= 0 && target < IOC_NUM_PORTS && _disk[target]) {
52 // target exists
53 uint32_t disk_delay;
54 if (wr)
55 disk_delay = _disk[target]->get_write_delay();
56 else
57 disk_delay = _disk[target]->get_read_delay();
58
59 if (_earliest_serving_time[target] > current_time)
60 _earliest_serving_time[target] = _earliest_serving_time[target] + disk_delay;
61 else
62 _earliest_serving_time[target] = current_time + disk_delay;
63
64 _etable.set_invoke_time(entry, _earliest_serving_time[target]);
65 }
66 else {
67 // target doesn't exist at all
68 if (wr)
69 _etable.set_invoke_time(entry, current_time + WRITE_DELAY_DEFAULT);
70 else
71 _etable.set_invoke_time(entry, current_time + READ_DELAY_DEFAULT);
72 }
73}
74
75
76// IO request is done, record the reply msg and time in the event table
77void SAS::scsi_io_done(int entry, uint32_t reply, uint8_t is_cntx, uint8_t cntx_type, int target, bool wr) {
78 // update event table entry
79 _etable.done(entry, reply, is_cntx, cntx_type);
80 compute_disk_delay(entry, target, wr);
81}
82
83
84// Error msg is needed especially during scsi target probing,
85// where it is used to inform the host that a target disk doesn't exist.
86void SAS::scsi_io_error(int entry, msg_scsi_io_request_t *req, uint16_t ioc_status) {
87 msg_scsi_io_reply_t *reply = (msg_scsi_io_reply_t *)calloc(1, sizeof(msg_scsi_io_reply_t));
88
89 reply->TargetID = req->TargetID;
90 reply->Bus = req->Bus;
91 reply->MsgLength = sizeof(msg_scsi_io_reply) >> 2;
92 reply->Function = req->Function;
93 reply->CDBLength = req->CDBLength;
94 reply->SenseBufferLength = req->SenseBufferLength;
95 reply->MsgFlags = req->MsgFlags;
96 reply->MsgContext = SAM_LE_32(req->MsgContext);
97 reply->SCSIStatus = MPI_SCSI_STATUS_SUCCESS;
98 reply->SCSIState = MPI_SCSI_STATE_NO_SCSI_STATUS;
99 reply->IOCStatus = SAM_LE_16(ioc_status & MPI_IOCSTATUS_MASK);
100 reply->IOCLogInfo = 0;
101 reply->TransferCount = 0;
102 reply->SenseCount = 0;
103 reply->ResponseInfo = 0;
104 reply->TaskTag = 0;
105
106 if (_reply_free_queue.empty()) {
107 debug_err("%s: reply free queue is empty\n", getName());
108 exit(1);
109 }
110
111 uint32_t fma = _reply_free_queue.front();
112 uint32_t msg_addr = fma & FMA_ADDRESS_MASK;
113 _reply_free_queue.pop();
114
115 memory_access(mfa_addr(msg_addr), mfa_addr_mode(), (void *)reply, sizeof(msg_scsi_io_reply_t), true);
116 scsi_io_done(entry, msg_addr >> 1, 0, 0, req->TargetID, false);
117 debug_info("%s: reply frame address= 0x%x, msgcontext=0x%x\n", getName(), msg_addr, reply->MsgContext);
118
119 free(reply);
120}
121
122
123// Process a SCSI request
124// step 1: memory_transport(), move the required data from/to disk to/from memory
125// step 2: send_mpi_msg(), send the reply to the host
126// Some requests may not need the first step.
127void SAS::process_scsi_cmd(int entry, msg_scsi_io_request_t *req, uint64_t sge_addr) {
128 if (!(req->Bus == 0)) {
129 debug_info("%s: only bus 0 is supported\n", getName());
130 scsi_io_error(entry, req, MPI_IOCSTATUS_SCSI_INVALID_BUS);
131 return;
132 }
133
134 if (!(req->TargetID >= 0 && req->TargetID < IOC_NUM_PORTS && _disk[req->TargetID])) {
135 debug_info("%s: target %d doesn't exists, check %s\n", getName(), req->TargetID, fname);
136 scsi_io_error(entry, req, MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE);
137 return;
138 }
139
140 if (!(req->LUN[0] == 0 && req->LUN[1] == 0)) {
141 debug_err("%s: only LUN 0 is supported\n", getName());
142 exit(1);
143 }
144
145 debug_info("%s: process scsi cmd for Bus %d, Target %d...\n", getName(), req->Bus, req->TargetID);
146
147 switch (req->CDB[0]) {
148 case SCSI_COMMAND_REPORT_LUNS: {
149 scsi_command_report_luns_t *lunp;
150
151 lunp = (scsi_command_report_luns_t *)req->CDB;
152
153 int32_t alloc_length = (lunp->allocation_length[0] << 24) |
154 (lunp->allocation_length[1] << 16) |
155 (lunp->allocation_length[2] << 8) |
156 (lunp->allocation_length[3] << 0);
157
158 if (alloc_length < 16) {
159 debug_err("%s: allocation length in report lun is less than 16 bytes\n", getName());
160 exit(1);
161 }
162
163 int datalen = sizeof(struct scsi_report_luns);
164 uchar_t *datap = (uchar_t *)calloc(datalen, sizeof(uchar_t));
165
166 struct scsi_report_luns *lundp = (struct scsi_report_luns *)datap;
167 lundp->lun_list_len = SAM_BE_32(8);
168
169 memory_transport(mfa_addr(sge_addr), mfa_addr_mode(), datap, datalen, true);
170
171 scsi_io_done(entry, req->MsgContext, true, 0, req->TargetID, false);
172
173 free(datap);
174
175 break;
176 }
177
178 case SCSI_COMMAND_INQUIRY: {
179 scsi_command_inquiry_t *inquiryp;
180
181 inquiryp = (scsi_command_inquiry_t *)req->CDB;
182
183 int datalen = 0;
184 uchar_t *datap;
185
186 if ((req->CDB[1] & 1) == 0) { /* Non-EVPD Inquiry */
187
188 datalen = sizeof(SCSI_Inquiry_Data);
189 datap = (uchar_t *)calloc (datalen, sizeof(uchar_t));
190
191 bcopy(_disk[req->TargetID]->get_SCSI_Inquiry_Data(), datap, datalen);
192 }
193 else { /* EVPD Inquiry */
194 switch (req->CDB[2]) {
195 case 0x00: {
196 datalen = sizeof(Supported_VPD);
197 datap = (uchar_t*)calloc(datalen, sizeof(uchar_t));
198
199 bcopy(_disk[req->TargetID]->get_Supported_VPD(), datap, datalen);
200
201 break;
202 }
203
204 case 0x80: { // Unit Serial Number
205 datalen = sizeof(Unit_Serial_Number);
206 datap = (uchar_t*)calloc (datalen, sizeof(uchar_t));
207
208 bcopy(_disk[req->TargetID]->get_Unit_Serial_Number(), datap, datalen);
209
210 break;
211 }
212
213 case 0x81: { // Implemented Operating Definition
214 datalen = sizeof(Implemented_Operating_Definition);
215 datap = (uchar_t*)calloc(datalen, sizeof(uchar_t));
216
217 bcopy(_disk[req->TargetID]->get_Implemented_Operating_Definition(), datap, datalen);
218
219 break;
220 }
221
222
223 case 0x83: { // Device Identification
224 datalen = sizeof(Device_Identification);
225 datap = (uchar_t*)calloc(datalen, sizeof(uchar_t));
226
227 bcopy(_disk[req->TargetID]->get_Device_Identification(), datap, datalen);
228
229 break;
230 }
231
232 default:
233 debug_err("%s: scsi_extended_inquiry page 0x%x NOT IMPL\n", getName(), req->CDB[2]);
234 exit(1);
235 }
236 }
237
238 assert(datalen > 0);
239
240 if (datalen > inquiryp->allocation_length) {
241 debug_info("%s: inquiry allocated data length is less than datalen\n", getName());
242 datalen = inquiryp->allocation_length;
243 }
244
245 memory_transport(mfa_addr(sge_addr), mfa_addr_mode(), datap, datalen, true);
246
247 scsi_io_done(entry, req->MsgContext, true, 0, req->TargetID, false);
248
249 free(datap);
250
251 break;
252 }
253
254 case SCSI_COMMAND_TEST_UNIT_READY: {
255 scsi_command_test_unit_ready_t *turp;
256
257 turp = (scsi_command_test_unit_ready_t *)req->CDB;
258
259 scsi_io_done(entry, req->MsgContext, true, 0, req->TargetID, false);
260
261 break;
262 }
263
264 case SCSI_COMMAND_START_STOP_UNIT: {
265 scsi_command_start_stop_unit_t *ssup;
266 ssup = (scsi_command_start_stop_unit_t *)req->CDB;
267
268 scsi_io_done(entry, req->MsgContext, true, 0, req->TargetID, false);
269
270 break;
271 }
272
273 case SCSI_COMMAND_READ_CAPACITY: {
274 scsi_command_read_capacity_t *rcp;
275 rcp = (scsi_command_read_capacity_t *)req->CDB;
276
277 int datalen = sizeof(scsi_command_read_capacity_data_t);
278 uchar_t *datap = (uchar_t *)calloc(datalen, sizeof(uchar_t));
279
280 scsi_command_read_capacity_data_t *rcdp = (scsi_command_read_capacity_data_t *)datap;
281 rcdp->lblk = SAM_BE_32(_disk[req->TargetID]->get_capacity());
282 rcdp->lblk_size = SAM_BE_32(SCSI_DISK_LBLK_SIZE);
283
284 memory_transport(mfa_addr(sge_addr), mfa_addr_mode(), datap, datalen, true);
285
286 scsi_io_done(entry, req->MsgContext, true, 0, req->TargetID, false);
287
288 free(datap);
289
290 break;
291 }
292
293 case SCSI_COMMAND_READ: {
294 scsi_command_read_t *rdp;
295 daddr_t lblkno;
296 size_t blks, size;
297 uchar_t *tmp_buf;
298
299 rdp = (scsi_command_read_t *)req->CDB;
300 lblkno = rdp->lblk_msb << 16;
301 lblkno |= SAM_BE_16(rdp->lblk);
302 blks = rdp->transfer_length;
303
304 size = blks * SCSI_DISK_LBLK_SIZE;
305 tmp_buf = (uchar_t *)calloc(size, sizeof(uchar_t));
306
307 debug_info("%s: read----> blkno: %d, blks: %d\n", getName(), lblkno, blks);
308 _disk[req->TargetID]->disk_read_lblk(lblkno, tmp_buf, blks);
309
310 memory_transport(mfa_addr(sge_addr), mfa_addr_mode(), tmp_buf, size, true);
311
312 scsi_io_done(entry, req->MsgContext, true, 0, req->TargetID, false);
313
314 free(tmp_buf);
315
316 break;
317 }
318
319 case SCSI_COMMAND_READ_LONG: {
320 scsi_command_read_long_t *rdlp;
321 daddr_t lblkno;
322 size_t blks, size;
323 uchar_t *tmp_buf;
324
325 rdlp = (scsi_command_read_long_t *)req->CDB;
326 lblkno = (rdlp->lblk[0] << 24) |
327 (rdlp->lblk[1] << 16) |
328 (rdlp->lblk[2] << 8) |
329 (rdlp->lblk[3] << 0);
330 blks = (rdlp->transfer_length[0] << 8) |
331 (rdlp->transfer_length[1] << 0);
332
333 size = blks * SCSI_DISK_LBLK_SIZE;
334 tmp_buf = (uchar_t *)calloc(size, sizeof(uchar_t));
335
336 debug_info("%s: read long----> blkno: %d, blks: %d\n", getName(), lblkno, blks);
337 _disk[req->TargetID]->disk_read_lblk(lblkno, tmp_buf, blks);
338
339 memory_transport(mfa_addr(sge_addr), mfa_addr_mode(), tmp_buf, size, true);
340
341 scsi_io_done(entry, req->MsgContext, true, 0, req->TargetID, false);
342
343 free(tmp_buf);
344
345 break;
346 }
347
348 case SCSI_COMMAND_WRITE: {
349 scsi_command_write_t *wrp;
350 daddr_t lblkno;
351 size_t blks, size;
352 uchar_t *tmp_buf;
353
354 wrp = (scsi_command_write_t *)req->CDB;
355 lblkno = (wrp->lblk_msb << 16) | SAM_BE_16(wrp->lblk);
356 blks = wrp->transfer_length;
357
358 size = blks * SCSI_DISK_LBLK_SIZE;
359 tmp_buf = (uchar_t *)calloc(size, sizeof(uchar_t));
360
361 memory_transport(mfa_addr(sge_addr), mfa_addr_mode(), tmp_buf, size, false);
362
363 debug_info("%s: write---> blkno: %d, blks: %d\n", getName(), lblkno, blks);
364 _disk[req->TargetID]->disk_write_lblk(lblkno, tmp_buf, blks);
365
366 scsi_io_done(entry, req->MsgContext, true, 0, req->TargetID, true);
367
368 free(tmp_buf);
369
370 break;
371 }
372
373 case SCSI_COMMAND_WRITE_LONG: {
374 scsi_command_write_long_t *wrlp;
375 daddr_t lblkno;
376 size_t blks, size;
377 uchar_t *tmp_buf;
378
379 wrlp = (scsi_command_write_long_t *)req->CDB;
380 lblkno = (wrlp->lblk[0] << 24) |
381 (wrlp->lblk[1] << 16) |
382 (wrlp->lblk[2] << 8) |
383 (wrlp->lblk[3] << 0);
384 blks = (wrlp->transfer_length[0] << 8) |
385 (wrlp->transfer_length[1] << 0);
386
387 size = blks * SCSI_DISK_LBLK_SIZE;
388 tmp_buf = (uchar_t *)calloc(size, sizeof(uchar_t));
389
390 memory_transport(mfa_addr(sge_addr), mfa_addr_mode(), tmp_buf, size, false);
391
392 debug_info("%s: write long----> blkno: %d, blks: %d\n", getName(), lblkno, blks);
393 _disk[req->TargetID]->disk_write_lblk(lblkno, tmp_buf, blks);
394
395 scsi_io_done(entry, req->MsgContext, true, 0, req->TargetID, true);
396
397 free(tmp_buf);
398
399 break;
400 }
401
402 case SCSI_COMMAND_REQUEST_SENSE: {
403 scsi_command_request_sense_t *rqsp;
404
405 rqsp = (scsi_command_request_sense_t *)req->CDB;
406
407 if (rqsp->allocation_length < sizeof(scsi_disk_request_sense_data_t)) {
408 debug_err("%s: SCSI_COMMAND_REQUEST_SENSE allocation length is too small\n", getName());
409 exit(1);
410 }
411
412 int datalen = sizeof(scsi_disk_request_sense_data_t);
413 uchar_t *datap = (uchar_t *)calloc(datalen, sizeof(uchar_t));
414
415 scsi_disk_request_sense_data_t *rqsdp = (scsi_disk_request_sense_data_t *)datap;
416 rqsdp->valid = 1;
417 rqsdp->error_code = 0x70;
418 rqsdp->segment_number = 0;
419 rqsdp->sense_key = 0x5; /* Illegal request */
420 rqsdp->additional_sense_length = 10;
421 rqsdp->additional_sense_code = 0x20; /* Invalid command opcode */
422 rqsdp->additional_sense_code_qualifier = 0x00;
423
424 memory_transport(mfa_addr(sge_addr), mfa_addr_mode(), datap, datalen, true);
425
426 scsi_io_done(entry, req->MsgContext, true, 0, req->TargetID, false);
427
428 free(datap);
429
430 break;
431 }
432
433 case SCSI_COMMAND_LOG_SENSE: {
434 scsi_command_log_sense_t *lsp;
435
436 lsp = (scsi_command_log_sense_t *)req->CDB;
437
438 scsi_io_done(entry, req->MsgContext, true, 0, req->TargetID, false);
439
440 break;
441 }
442
443 case SCSI_COMMAND_MODE_SENSE: {
444 scsi_command_mode_sense_t *msp;
445
446 msp = (scsi_command_mode_sense_t *)req->CDB;
447
448 scsi_io_done(entry, req->MsgContext, true, 0, req->TargetID, false);
449
450 break;
451 }
452
453 case SCSI_COMMAND_PERSISTENT_RESERVE_IN: {
454 scsi_io_done(entry, req->MsgContext, true, 0, req->TargetID, false);
455
456 break;
457 }
458
459 default:
460 debug_err("%s: scsi command: 0x%x not supported\n", getName(), req->CDB[0]);
461 exit(1);
462 }
463}
464
465
466// Callback function for event handling
467void SAS::io_callback(int entry) {
468 if (entry < 0 || entry >= EVENT_TABLE_SIZE) {
469 debug_err("%s: invalid entry id %d for the event table\n", getName(), entry);
470 exit(1);
471 }
472
473 if (_etable.events[entry].done == 0) {
474 debug_err("%s: request in entry %d has not been served\n", getName(), entry);
475 exit(1);
476 }
477
478 int64_t invoke_time = _etable.get_invoke_time(entry);
479 int64_t current_time = mmi_get_time();
480
481 if (invoke_time != current_time) {
482 debug_err("%s: invoke time %lld is different from current time %lld\n", getName(), invoke_time, current_time);
483 exit(1);
484 }
485
486 debug_info("%s: io_callback is called, entry = %d, current time = %lld\n", getName(), entry, current_time);
487 send_mpi_msg(_etable.events[entry].reply, _etable.events[entry].is_cntx, _etable.events[entry].cntx_type);
488
489 // free the entry
490 _etable.free(entry);
491}
492
493
494void sas_io_callback(void *arg1, void *arg2) {
495 SAS *sasp = (SAS *)arg1;
496
497 sasp->io_callback((int)(long long int)arg2);
498}