| 1 | // ========== Copyright Header Begin ========================================== |
| 2 | // |
| 3 | // OpenSPARC T2 Processor File: sas_mpi.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_mpi.cc" LSI SAS1064E PCIE to 4-Port Serial Attached |
| 23 | * SCSI Controller Simulator |
| 24 | * Message Passing Interface (MPI) implementation |
| 25 | * |
| 26 | * Copyright (C) 2007 Sun Microsystems, Inc. |
| 27 | * All rights reserved. |
| 28 | * |
| 29 | */ |
| 30 | #include "sas.h" |
| 31 | |
| 32 | |
| 33 | // length is (ceiling(size/8)*8)/4, i.e. 0~7=>0, 8=>2, 9=>4 |
| 34 | uint32_t get_length(uint32_t size) { |
| 35 | uint32_t length; |
| 36 | |
| 37 | assert(size > 0); |
| 38 | |
| 39 | length = size >> 3; |
| 40 | if (size % 8) |
| 41 | length++; |
| 42 | |
| 43 | return(length << 1); |
| 44 | } |
| 45 | |
| 46 | |
| 47 | // be is used to represent each byte in the remainder (div by 8) |
| 48 | uint8_t get_be(uint32_t size) { |
| 49 | uint32_t remain; |
| 50 | uint8_t be = 0; |
| 51 | |
| 52 | assert(size > 0); |
| 53 | |
| 54 | remain = size % 8; |
| 55 | |
| 56 | if (remain == 0) |
| 57 | remain = 8; |
| 58 | |
| 59 | for (int i = 0; i < remain; i++) |
| 60 | be |= (1 << i); |
| 61 | |
| 62 | return(be); |
| 63 | } |
| 64 | |
| 65 | |
| 66 | // Transport data to/from simulated memory (pointed by "va") |
| 67 | // from/to the host memory (pointed by "data"). |
| 68 | void SAS::memory_access(uint64_t va, addrMd_xactnType mode, void *data, uint32_t size, bool wr) { |
| 69 | uint32_t rem_size = size; |
| 70 | uint32_t t_size; |
| 71 | |
| 72 | while (rem_size) { |
| 73 | |
| 74 | // the size of data in each memory transfer is limited by the "length" |
| 75 | // parameter (roughly size/4) in busif_access(), which is 16-bit. |
| 76 | if (rem_size > 0x20000) |
| 77 | t_size = 0x20000; |
| 78 | else |
| 79 | t_size = rem_size; |
| 80 | |
| 81 | pcieCompleter status = busIf->busif_access(PCIE_MEM, wr, va, data, |
| 82 | get_length(t_size), get_be(t_size), getId(), mode,&samId); |
| 83 | if (handleCompletion(status) == -1) { |
| 84 | debug_err("%s: pcie error when transferring data to/from memory\n"); |
| 85 | exit(1); |
| 86 | } |
| 87 | |
| 88 | rem_size -= t_size; |
| 89 | va += t_size; |
| 90 | data = (uchar_t *)data + t_size; |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | |
| 95 | // Transport data to/from simulated memory (pointed by "sge_addr") |
| 96 | // from/to the host memory (pointed by "data"). |
| 97 | // Note: sge_addr is the address of memory location where actually SGE is stored. |
| 98 | // So we have to first read SGE using sge_addr, and this involves memory_access(). |
| 99 | // Then we have to move data to/from the memory location specified by SGE, which |
| 100 | // also involves memory_access(). |
| 101 | void SAS::memory_transport(uint64_t sge_addr, addrMd_xactnType sge_addr_mode, void *data, uint32_t size, bool wr) { |
| 102 | uint32_t flaglength; |
| 103 | |
| 104 | memory_access(sge_addr, sge_addr_mode, (void*)&flaglength, 4, false); |
| 105 | flaglength = SAM_LE_32(flaglength); |
| 106 | |
| 107 | uint8_t flag = flaglength >> MPI_SGE_FLAGS_SHIFT; |
| 108 | uint64_t mem_addr; |
| 109 | addrMd_xactnType mem_addr_mode; |
| 110 | uint64_t next_sge_addr; |
| 111 | addrMd_xactnType next_sge_addr_mode; |
| 112 | |
| 113 | if ((flag & MPI_SGE_FLAGS_ELEMENT_MASK) == MPI_SGE_FLAGS_SIMPLE_ELEMENT) { |
| 114 | uint32_t length = flaglength & MPI_SGE_LENGTH_MASK; |
| 115 | |
| 116 | if (flag & MPI_SGE_FLAGS_64_BIT_ADDRESSING) { |
| 117 | // 64-bit addressing |
| 118 | mem_addr_mode = mem_addr64; |
| 119 | |
| 120 | sge_simple64_t *simple_64 = (sge_simple64_t *)calloc(1, sizeof(sge_simple64_t)); |
| 121 | |
| 122 | memory_access(sge_addr, sge_addr_mode, (void*)simple_64, sizeof(sge_simple64_t), false); |
| 123 | |
| 124 | simple_64->FlagsLength = SAM_LE_32(simple_64->FlagsLength); |
| 125 | simple_64->Address_Low = SAM_LE_32(simple_64->Address_Low); |
| 126 | simple_64->Address_High = SAM_LE_32(simple_64->Address_High); |
| 127 | |
| 128 | mem_addr = (simple_64->Address_High << 32) | simple_64->Address_Low; |
| 129 | } |
| 130 | else { |
| 131 | // 32-bit addressing |
| 132 | mem_addr_mode = mem_addr32; |
| 133 | |
| 134 | sge_simple32_t *simple_32 = (sge_simple32_t *)calloc(1, sizeof(sge_simple32_t)); |
| 135 | |
| 136 | memory_access(sge_addr, sge_addr_mode, (void*)simple_32, sizeof(sge_simple32_t), false); |
| 137 | |
| 138 | simple_32->FlagsLength = SAM_LE_32(simple_32->FlagsLength); |
| 139 | simple_32->Address = SAM_LE_32(simple_32->Address); |
| 140 | |
| 141 | mem_addr = simple_32->Address; |
| 142 | } |
| 143 | |
| 144 | if (length < size) { |
| 145 | // more SGEs |
| 146 | memory_access(mem_addr, mem_addr_mode, data, length, wr); |
| 147 | |
| 148 | next_sge_addr = sge_addr + |
| 149 | (mem_addr_mode == mem_addr32) ? sizeof(sge_simple32) : sizeof(sge_simple64); |
| 150 | |
| 151 | memory_transport(next_sge_addr, sge_addr_mode, (void*)((uchar_t *)data + length), size - length, wr); |
| 152 | |
| 153 | } |
| 154 | else { |
| 155 | // last SGE |
| 156 | memory_access(mem_addr, mem_addr_mode, data, size, wr); |
| 157 | } |
| 158 | } |
| 159 | else if ((flag & MPI_SGE_FLAGS_ELEMENT_MASK) == MPI_SGE_FLAGS_CHAIN_ELEMENT) { |
| 160 | if (flag & MPI_SGE_FLAGS_64_BIT_ADDRESSING) { |
| 161 | // 64-bit addressing |
| 162 | sge_chain64_t *chain_64 = (sge_chain64_t *)calloc(1, sizeof(sge_chain64_t)); |
| 163 | |
| 164 | memory_access(sge_addr, sge_addr_mode, (void*)chain_64, sizeof(sge_chain64_t), false); |
| 165 | |
| 166 | chain_64->Length = SAM_LE_16(chain_64->Length); |
| 167 | chain_64->Address64_Low = SAM_LE_32(chain_64->Address64_Low); |
| 168 | chain_64->Address64_High = SAM_LE_32(chain_64->Address64_High); |
| 169 | |
| 170 | next_sge_addr = (chain_64->Address64_High << 32) | chain_64->Address64_Low; |
| 171 | next_sge_addr_mode = mem_addr64; |
| 172 | } |
| 173 | else { |
| 174 | // 32-bit addressing |
| 175 | sge_chain32_t *chain_32 = (sge_chain32_t *)calloc(1, sizeof(sge_chain32_t)); |
| 176 | |
| 177 | memory_access(sge_addr, sge_addr_mode, (void*)chain_32, sizeof(sge_chain32_t), false); |
| 178 | |
| 179 | chain_32->Length = SAM_LE_16(chain_32->Length); |
| 180 | chain_32->Address = SAM_LE_32(chain_32->Address); |
| 181 | |
| 182 | next_sge_addr = chain_32->Address; |
| 183 | next_sge_addr_mode = mem_addr32; |
| 184 | } |
| 185 | |
| 186 | memory_transport(next_sge_addr, next_sge_addr_mode, data, size, wr); |
| 187 | } |
| 188 | else { |
| 189 | debug_err("%s: only Simple and Chain Elements are supported\n", getName()); |
| 190 | exit(1); |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | |
| 195 | // Transport data to/from simulated memory (pointed by "sge") |
| 196 | // from/to the host memory (pointed by "data"). |
| 197 | // Note: memory_transport() defined above will be called in the |
| 198 | // case of chain element. |
| 199 | void SAS::memory_transport(void *sge, void *data, uint32_t size, bool wr) { |
| 200 | uint32_t flaglength = *(uint32_t *)sge; |
| 201 | |
| 202 | uint8_t flag = flaglength >> MPI_SGE_FLAGS_SHIFT; |
| 203 | |
| 204 | uint64_t mem_addr; |
| 205 | addrMd_xactnType mem_addr_mode; |
| 206 | uint64_t next_sge_addr; |
| 207 | addrMd_xactnType next_sge_addr_mode; |
| 208 | |
| 209 | if ((flag & MPI_SGE_FLAGS_ELEMENT_MASK) == MPI_SGE_FLAGS_SIMPLE_ELEMENT) { |
| 210 | uint32_t length = flaglength & MPI_SGE_LENGTH_MASK; |
| 211 | |
| 212 | if (flag & MPI_SGE_FLAGS_64_BIT_ADDRESSING) { |
| 213 | // 64-bit addressing |
| 214 | mem_addr_mode = mem_addr64; |
| 215 | |
| 216 | sge_simple64 *simple_64 = (sge_simple64 *)sge; |
| 217 | |
| 218 | mem_addr = (simple_64->Address_High << 32) | simple_64->Address_Low; |
| 219 | |
| 220 | } |
| 221 | else { |
| 222 | // 32-bit addressing |
| 223 | mem_addr_mode = mem_addr32; |
| 224 | |
| 225 | sge_simple32 *simple_32 = (sge_simple32 *)sge; |
| 226 | |
| 227 | mem_addr = simple_32->Address; |
| 228 | } |
| 229 | |
| 230 | memory_access(mem_addr, mem_addr_mode, data, size, wr); |
| 231 | } |
| 232 | else if ((flag & MPI_SGE_FLAGS_ELEMENT_MASK) == MPI_SGE_FLAGS_CHAIN_ELEMENT) { |
| 233 | if (flag & MPI_SGE_FLAGS_64_BIT_ADDRESSING) { |
| 234 | // 64-bit addressing |
| 235 | sge_chain64 *chain_64 = (sge_chain64 *)sge; |
| 236 | |
| 237 | next_sge_addr = (chain_64->Address64_High << 32) | chain_64->Address64_Low; |
| 238 | next_sge_addr_mode = mem_addr64; |
| 239 | } |
| 240 | else { |
| 241 | // 32-bit addressing |
| 242 | sge_chain32 *chain_32 = (sge_chain32 *)sge; |
| 243 | |
| 244 | next_sge_addr = chain_32->Address; |
| 245 | next_sge_addr_mode = mem_addr32; |
| 246 | } |
| 247 | |
| 248 | memory_transport(next_sge_addr, next_sge_addr_mode, data, size, wr); |
| 249 | } |
| 250 | else { |
| 251 | debug_err("%s: only Simple and Chain Elements are supported\n", getName()); |
| 252 | exit(1); |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | |
| 257 | // Send handshake reply msg to the host through doorbell |
| 258 | // Note: the first hword is written to doorbell here, |
| 259 | // and all remaining bytes are sent (hword at a time) |
| 260 | // each time the doorbell is read. |
| 261 | void SAS::send_handshake_msg() { |
| 262 | uint16_t value; |
| 263 | |
| 264 | // set the size of message to be sent in hword |
| 265 | _hdshk_msg_reply_size = (((msg_default_reply *)_hdshk_msg_reply_buf)->MsgLength) << 1; |
| 266 | _hdshk_msg_reply_send = 0; |
| 267 | |
| 268 | // put the first hword in the doorbell |
| 269 | value = SAM_LE_16(*(uint16_t *)(_hdshk_msg_reply_buf + (_hdshk_msg_reply_send << 1))); |
| 270 | _mpt_reg->doorbell = (_mpt_reg->doorbell & ~MPI_DOORBELL_DATA_MASK) | value; |
| 271 | _hdshk_msg_reply_send++; |
| 272 | |
| 273 | // set interrupt |
| 274 | set_doorbell_interrupt(); |
| 275 | } |
| 276 | |
| 277 | |
| 278 | // Process a handshake msg received through doorbell |
| 279 | // Request msg is in little-endian, should be converted into big-endian after receiving |
| 280 | // Reply msg is in big-endian, should be converted into little-endian before sending |
| 281 | void SAS::process_handshake_msg() { |
| 282 | msg_request_header_t *msg = (msg_request_header_t *)_hdshk_msg_req_buf; |
| 283 | |
| 284 | switch (msg->Function) { |
| 285 | case MPI_FUNCTION_CONFIG: { |
| 286 | debug_info("%s: MPI_FUNCTION_CONFIG\n", getName()); |
| 287 | |
| 288 | msg_config_t *config_req; |
| 289 | msg_config_reply_t *config_reply; |
| 290 | |
| 291 | config_req = (msg_config_t *)_hdshk_msg_req_buf; |
| 292 | config_reply = (msg_config_reply_t *)_hdshk_msg_reply_buf; |
| 293 | bzero(_hdshk_msg_reply_buf, HDSHK_MSG_SIZE); |
| 294 | |
| 295 | config_req->ExtPageLength = SAM_LE_16(config_req->ExtPageLength); |
| 296 | config_req->MsgContext = SAM_LE_32(config_req->MsgContext); |
| 297 | config_req->PageAddress = SAM_LE_32(config_req->PageAddress); |
| 298 | |
| 299 | // 1. PageBufferSGE is not 32-bit as described in MPT Ch.5 |
| 300 | // 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 |
| 301 | // 3. However, 64-bit is used here according to the driver code in OBP and Solaris |
| 302 | // 4. swap() is based on sge_simple_union_t, it should be diferent if it is sge_chain_union_t |
| 303 | config_req->PageBufferSGE.u1.Simple.FlagsLength = |
| 304 | SAM_LE_32(config_req->PageBufferSGE.u1.Simple.FlagsLength); |
| 305 | config_req->PageBufferSGE.u1.Simple.u1.Address32= |
| 306 | SAM_LE_32(config_req->PageBufferSGE.u1.Simple.u1.Address32); |
| 307 | |
| 308 | switch (config_req->Header.PageType & MPI_CONFIG_PAGETYPE_MASK) { |
| 309 | case MPI_CONFIG_PAGETYPE_IO_UNIT: |
| 310 | debug_err("%s: MPI_CONFIG_PAGETYPE_IO_UNIT not supported\n", getName()); |
| 311 | exit(1); |
| 312 | |
| 313 | case MPI_CONFIG_PAGETYPE_IOC: |
| 314 | access_page_ioc(config_req, config_reply); |
| 315 | break; |
| 316 | |
| 317 | case MPI_CONFIG_PAGETYPE_BIOS: |
| 318 | debug_err("%s: MPI_CONFIG_PAGETYPE_IOC not supported\n", getName()); |
| 319 | exit(1); |
| 320 | |
| 321 | case MPI_CONFIG_PAGETYPE_SCSI_PORT: |
| 322 | debug_err("%s: MPI_CONFIG_PAGETYPE_SCSI_PORT not supported\n", getName()); |
| 323 | exit(1); |
| 324 | |
| 325 | case MPI_CONFIG_PAGETYPE_SCSI_DEVICE: |
| 326 | debug_err("%s: MPI_CONFIG_PAGETYPE_SCSI_DEVICE not supported\n", getName()); |
| 327 | exit(1); |
| 328 | |
| 329 | case MPI_CONFIG_PAGETYPE_FC_PORT: |
| 330 | debug_err("%s: MPI_CONFIG_PAGETYPE_FC_PORT not supported\n", getName()); |
| 331 | exit(1); |
| 332 | |
| 333 | case MPI_CONFIG_PAGETYPE_FC_DEVICE: |
| 334 | debug_err("%s: PI_CONFIG_PAGETYPE_FC_DEVICE not supported\n", getName()); |
| 335 | exit(1); |
| 336 | |
| 337 | case MPI_CONFIG_PAGETYPE_LAN: |
| 338 | debug_err("%s: MPI_CONFIG_PAGETYPE_LAN not supported\n", getName()); |
| 339 | exit(1); |
| 340 | |
| 341 | case MPI_CONFIG_PAGETYPE_RAID_VOLUME: |
| 342 | debug_err("%s: MPI_CONFIG_PAGETYPE_RAID_VOLUME not supported\n", getName()); |
| 343 | exit(1); |
| 344 | |
| 345 | case MPI_CONFIG_PAGETYPE_MANUFACTURING: |
| 346 | access_page_manufacturing(config_req, config_reply); |
| 347 | break; |
| 348 | |
| 349 | case MPI_CONFIG_PAGETYPE_RAID_PHYSDISK: |
| 350 | debug_err("%s: MPI_CONFIG_PAGETYPE_RAID_PHYSDISK not supported\n", getName()); |
| 351 | exit(1); |
| 352 | |
| 353 | case MPI_CONFIG_PAGETYPE_INBAND: |
| 354 | debug_err("%s: MPI_CONFIG_PAGETYPE_INBAND not supported\n", getName()); |
| 355 | exit(1); |
| 356 | |
| 357 | case MPI_CONFIG_PAGETYPE_EXTENDED: |
| 358 | switch (config_req->ExtPageType) { |
| 359 | case MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT: |
| 360 | access_page_sas_io_unit(config_req, config_reply); |
| 361 | break; |
| 362 | |
| 363 | case MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER: |
| 364 | debug_err("%s: MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER not impl\n", getName()); |
| 365 | exit(1); |
| 366 | |
| 367 | case MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE: |
| 368 | access_page_sas_device(config_req, config_reply); |
| 369 | break; |
| 370 | |
| 371 | case MPI_CONFIG_EXTPAGETYPE_SAS_PHY: |
| 372 | access_page_sas_phy(config_req, config_reply); |
| 373 | break; |
| 374 | |
| 375 | default: |
| 376 | debug_err("%s: unknow page type\n", getName()); |
| 377 | exit(1); |
| 378 | } |
| 379 | |
| 380 | break; |
| 381 | |
| 382 | default: |
| 383 | debug_err("%s: unknow page type\n", getName()); |
| 384 | exit(1); |
| 385 | } |
| 386 | |
| 387 | break; |
| 388 | } |
| 389 | |
| 390 | case MPI_FUNCTION_EVENT_NOTIFICATION: { |
| 391 | debug_info("%s: MPI_FUNCTION_EVENT_NOTIFICATION\n", getName()); |
| 392 | |
| 393 | msg_event_notify_t *event_notify_req; |
| 394 | msg_event_notify_reply_t *event_notify_reply; |
| 395 | |
| 396 | event_notify_req = (msg_event_notify_t *)_hdshk_msg_req_buf; |
| 397 | event_notify_reply = (msg_event_notify_reply_t *)_hdshk_msg_reply_buf; |
| 398 | bzero(_hdshk_msg_reply_buf, HDSHK_MSG_SIZE); |
| 399 | |
| 400 | event_notify_reply->EventDataLength = 1; |
| 401 | event_notify_reply->MsgLength = sizeof(msg_event_notify_reply_t) >> 2; |
| 402 | event_notify_reply->Function = event_notify_req->Function; |
| 403 | event_notify_reply->AckRequired = 0; |
| 404 | event_notify_reply->MsgFlags = 0; |
| 405 | event_notify_reply->MsgContext = event_notify_req->MsgContext; |
| 406 | event_notify_reply->IOCStatus = SAM_LE_16(MPI_IOCSTATUS_SUCCESS); |
| 407 | event_notify_reply->IOCLogInfo = 0; |
| 408 | event_notify_reply->Event = SAM_LE_16(MPI_EVENT_EVENT_CHANGE); |
| 409 | event_notify_reply->EventContext = 0; |
| 410 | event_notify_reply->Data[0] = 0; |
| 411 | |
| 412 | send_handshake_msg(); |
| 413 | |
| 414 | break; |
| 415 | } |
| 416 | |
| 417 | case MPI_FUNCTION_FW_DOWNLOAD: |
| 418 | debug_err("%s: MPI_FUNCTION_FW_DOWNLOAD not impl\n", getName()); |
| 419 | exit(1); |
| 420 | |
| 421 | case MPI_FUNCTION_FW_UPLOAD: |
| 422 | debug_err("%s: MPI_FUNCTION_FW_UPLOAD not impl\n", getName()); |
| 423 | exit(1); |
| 424 | |
| 425 | case MPI_FUNCTION_IOC_FACTS: { |
| 426 | debug_info("%s: MPI_FUNCTION_IOC_FACTS\n", getName()); |
| 427 | |
| 428 | msg_ioc_facts_t *ioc_facts_req; |
| 429 | msg_ioc_facts_reply_t *ioc_facts_reply; |
| 430 | |
| 431 | ioc_facts_req = (msg_ioc_facts_t *)_hdshk_msg_req_buf; |
| 432 | ioc_facts_reply = (msg_ioc_facts_reply_t *)_hdshk_msg_reply_buf; |
| 433 | bzero(_hdshk_msg_reply_buf, HDSHK_MSG_SIZE); |
| 434 | |
| 435 | // read from ioc configuration |
| 436 | ioc_facts_reply->WhoInit = _ioc_conf->who_init; |
| 437 | ioc_facts_reply->MaxDevices = _ioc_conf->max_devices; |
| 438 | ioc_facts_reply->MaxBuses = _ioc_conf->max_buses; |
| 439 | ioc_facts_reply->CurrentHostMfaHighAddr = SAM_LE_32(_ioc_conf->host_mfa_high_addr); |
| 440 | ioc_facts_reply->CurrentSenseBufferHighAddr = SAM_LE_32(_ioc_conf->sense_buffer_high_addr); |
| 441 | ioc_facts_reply->CurReplyFrameSize = SAM_LE_16(_ioc_conf->reply_frame_size); |
| 442 | bcopy(&_ioc_conf->host_page_buffer_sge, &ioc_facts_reply->HostPageBufferSGE, sizeof(sge_simple64_t)); |
| 443 | ioc_facts_reply->HostPageBufferSGE.FlagsLength = SAM_LE_32(ioc_facts_reply->HostPageBufferSGE.FlagsLength); |
| 444 | ioc_facts_reply->HostPageBufferSGE.Address_Low = SAM_LE_32(ioc_facts_reply->HostPageBufferSGE.Address_Low); |
| 445 | ioc_facts_reply->HostPageBufferSGE.Address_High = SAM_LE_32(ioc_facts_reply->HostPageBufferSGE.Address_High); |
| 446 | |
| 447 | // others |
| 448 | ioc_facts_reply->MsgVersion = SAM_LE_16(MPI_VERSION); |
| 449 | ioc_facts_reply->HeaderVersion = SAM_LE_16(MPI_HEADER_VERSION); |
| 450 | ioc_facts_reply->MsgLength = sizeof(msg_ioc_facts_reply_t) >> 2; |
| 451 | ioc_facts_reply->Function = ioc_facts_req->Function; |
| 452 | ioc_facts_reply->IOCNumber = function; |
| 453 | ioc_facts_reply->MsgFlags = 0; |
| 454 | ioc_facts_reply->MsgContext = ioc_facts_req->MsgContext; |
| 455 | ioc_facts_reply->IOCExceptions = 0; |
| 456 | ioc_facts_reply->IOCStatus = SAM_LE_16(MPI_IOCSTATUS_SUCCESS); |
| 457 | ioc_facts_reply->IOCLogInfo = 0; |
| 458 | ioc_facts_reply->MaxChainDepth = IOC_CHAIN_DEPTH; |
| 459 | ioc_facts_reply->BlockSize = IOC_BLOCK_SIZE; |
| 460 | ioc_facts_reply->Flags = 4; |
| 461 | ioc_facts_reply->ReplyQueueDepth = SAM_LE_16(IOC_REPLY_QUEUE_DEPTH); |
| 462 | ioc_facts_reply->RequestFrameSize = SAM_LE_16(IOC_REQ_FRAME_SIZE); |
| 463 | ioc_facts_reply->ProductID = SAM_LE_16(IOC_PRODUCTION_ID); |
| 464 | ioc_facts_reply->GlobalCredits = SAM_LE_16(IOC_REQ_QUEUE_DEPTH); |
| 465 | ioc_facts_reply->NumberOfPorts = IOC_NUM_PORTS; |
| 466 | ioc_facts_reply->EventState = 0; |
| 467 | ioc_facts_reply->FWImageSize = 0; |
| 468 | ioc_facts_reply->IOCCapabilities = SAM_LE_32(IOC_CAPABILITIES); |
| 469 | ioc_facts_reply->FWVersion.Word = SAM_LE_32((MPI_VERSION << 16) | MPI_HEADER_VERSION); |
| 470 | ioc_facts_reply->HighPriorityQueueDepth = SAM_LE_16(IOC_PRI_QUEUE_DEPTH); |
| 471 | |
| 472 | send_handshake_msg(); |
| 473 | |
| 474 | break; |
| 475 | } |
| 476 | |
| 477 | case MPI_FUNCTION_IOC_INIT: { |
| 478 | debug_info("%s: MPI_FUNCTION_IOC_INIT\n", getName()); |
| 479 | |
| 480 | msg_ioc_init_t *ioc_init_req; |
| 481 | msg_ioc_init_reply_t *ioc_init_reply; |
| 482 | |
| 483 | ioc_init_req = (msg_ioc_init_t *)_hdshk_msg_req_buf; |
| 484 | ioc_init_reply = (msg_ioc_init_reply_t *)_hdshk_msg_reply_buf; |
| 485 | bzero(_hdshk_msg_reply_buf, HDSHK_MSG_SIZE); |
| 486 | |
| 487 | _ioc_conf->who_init = ioc_init_req->WhoInit; |
| 488 | _ioc_conf->max_devices = ioc_init_req->MaxDevices; |
| 489 | _ioc_conf->max_buses = ioc_init_req->MaxBuses; |
| 490 | _ioc_conf->reply_frame_size = SAM_LE_16(ioc_init_req->ReplyFrameSize); |
| 491 | _ioc_conf->host_mfa_high_addr = SAM_LE_32(ioc_init_req->HostMfaHighAddr); |
| 492 | _ioc_conf->sense_buffer_high_addr = SAM_LE_32(ioc_init_req->SenseBufferHighAddr); |
| 493 | _ioc_conf->host_page_buffer_sge.FlagsLength = SAM_LE_32(ioc_init_req->HostPageBufferSGE.FlagsLength); |
| 494 | _ioc_conf->host_page_buffer_sge.Address_Low = SAM_LE_32(ioc_init_req->HostPageBufferSGE.Address_Low); |
| 495 | _ioc_conf->host_page_buffer_sge.Address_High = SAM_LE_32(ioc_init_req->HostPageBufferSGE.Address_High); |
| 496 | |
| 497 | // change the status to Operational |
| 498 | _mpt_reg->doorbell = (_mpt_reg->doorbell & ~MPI_IOC_STATE_MASK) | MPI_IOC_STATE_OPERATIONAL; |
| 499 | |
| 500 | // read from ioc configuration |
| 501 | ioc_init_reply->WhoInit = _ioc_conf->who_init; |
| 502 | ioc_init_reply->MaxDevices = _ioc_conf->max_devices; |
| 503 | ioc_init_reply->MaxBuses = _ioc_conf->max_buses; |
| 504 | |
| 505 | // others |
| 506 | ioc_init_reply->MsgLength = sizeof(msg_ioc_init_reply_t) >> 2; |
| 507 | ioc_init_reply->Function = ioc_init_req->Function; |
| 508 | ioc_init_reply->Flags = 4; |
| 509 | ioc_init_reply->MsgFlags = 0; |
| 510 | ioc_init_reply->MsgContext = ioc_init_req->MsgContext; |
| 511 | ioc_init_reply->IOCStatus = SAM_LE_16(MPI_IOCSTATUS_SUCCESS); |
| 512 | ioc_init_reply->IOCLogInfo = 0; |
| 513 | |
| 514 | send_handshake_msg(); |
| 515 | |
| 516 | break; |
| 517 | } |
| 518 | |
| 519 | case MPI_FUNCTION_PORT_ENABLE: { |
| 520 | debug_info("%s: MPI_FUNCTION_PORT_ENABLE\n", getName()); |
| 521 | |
| 522 | msg_port_enable_t *port_enable_req; |
| 523 | msg_port_enable_reply_t *port_enable_reply; |
| 524 | |
| 525 | port_enable_req = (msg_port_enable_t *)_hdshk_msg_req_buf; |
| 526 | port_enable_reply = (msg_port_enable_reply_t *)_hdshk_msg_reply_buf; |
| 527 | bzero(_hdshk_msg_reply_buf, HDSHK_MSG_SIZE); |
| 528 | |
| 529 | _port_enabled[port_enable_req->PortNumber] = 1; |
| 530 | |
| 531 | port_enable_reply->MsgLength = sizeof(msg_port_enable_reply_t) >> 2; |
| 532 | port_enable_reply->Function = port_enable_req->Function; |
| 533 | port_enable_reply->PortNumber = port_enable_req->PortNumber; |
| 534 | port_enable_reply->MsgFlags = 0; |
| 535 | port_enable_reply->MsgContext = port_enable_req->MsgContext; |
| 536 | port_enable_reply->IOCStatus = SAM_LE_16(MPI_IOCSTATUS_SUCCESS); |
| 537 | port_enable_reply->IOCLogInfo = 0; |
| 538 | |
| 539 | send_handshake_msg(); |
| 540 | |
| 541 | break; |
| 542 | } |
| 543 | |
| 544 | case MPI_FUNCTION_PORT_FACTS: { |
| 545 | debug_info("%s: MPI_FUNCTION_PORT_FACTS\n", getName()); |
| 546 | |
| 547 | msg_port_facts_t *port_facts_req; |
| 548 | msg_port_facts_reply_t *port_facts_reply; |
| 549 | |
| 550 | port_facts_req = (msg_port_facts_t *)_hdshk_msg_req_buf; |
| 551 | port_facts_reply = (msg_port_facts_reply_t *)_hdshk_msg_reply_buf; |
| 552 | bzero(_hdshk_msg_reply_buf, HDSHK_MSG_SIZE); |
| 553 | |
| 554 | port_facts_reply->MsgLength = sizeof(msg_port_facts_reply_t) >> 2; |
| 555 | port_facts_reply->Function = port_facts_req->Function; |
| 556 | port_facts_reply->PortNumber = port_facts_req->PortNumber; |
| 557 | port_facts_reply->MsgFlags = 0; |
| 558 | port_facts_reply->MsgContext = port_facts_req->MsgContext; |
| 559 | port_facts_reply->IOCStatus = SAM_LE_16(MPI_IOCSTATUS_SUCCESS); |
| 560 | port_facts_reply->IOCLogInfo = 0; |
| 561 | port_facts_reply->PortType = PORT_TYPE; |
| 562 | port_facts_reply->MaxDevices = SAM_LE_16(PORT_MAX_DEVICES); |
| 563 | port_facts_reply->PortSCSIID = SAM_LE_16(PORT_SCSI_ID); |
| 564 | port_facts_reply->ProtocolFlags = SAM_LE_16(PORT_PROTOCOL_FLAGS); |
| 565 | port_facts_reply->MaxPostedCmdBuffers = SAM_LE_16(PORT_MAX_POSTED_CMD_BUFFERS); |
| 566 | port_facts_reply->MaxPersistentIDs = SAM_LE_16(PORT_MAX_PERSISTENT_IDS); |
| 567 | port_facts_reply->MaxLanBuckets = SAM_LE_16(PORT_MAX_LAN_BUCKETS); |
| 568 | |
| 569 | send_handshake_msg(); |
| 570 | |
| 571 | break; |
| 572 | } |
| 573 | |
| 574 | case MPI_FUNCTION_SCSI_TASK_MGMT: { |
| 575 | msg_scsi_task_mgmt_t *scsi_task_mgmt_req; |
| 576 | msg_scsi_task_mgmt_reply *scsi_task_mgmt_reply; |
| 577 | |
| 578 | scsi_task_mgmt_req = (msg_scsi_task_mgmt_t *)_hdshk_msg_req_buf; |
| 579 | scsi_task_mgmt_reply = (msg_scsi_task_mgmt_reply *)_hdshk_msg_reply_buf; |
| 580 | bzero(_hdshk_msg_reply_buf, HDSHK_MSG_SIZE); |
| 581 | |
| 582 | scsi_task_mgmt_reply->TargetID = scsi_task_mgmt_req->TargetID; |
| 583 | scsi_task_mgmt_reply->Bus = scsi_task_mgmt_req->Bus; |
| 584 | scsi_task_mgmt_reply->MsgLength = sizeof(msg_scsi_task_mgmt_reply) >> 2; |
| 585 | scsi_task_mgmt_reply->Function = scsi_task_mgmt_req->Function; |
| 586 | scsi_task_mgmt_reply->TaskType = scsi_task_mgmt_req->TaskType; |
| 587 | scsi_task_mgmt_reply->MsgFlags = 0; |
| 588 | scsi_task_mgmt_reply->MsgContext = scsi_task_mgmt_req->MsgContext; |
| 589 | scsi_task_mgmt_reply->IOCStatus = SAM_LE_16(MPI_IOCSTATUS_SUCCESS); |
| 590 | scsi_task_mgmt_reply->IOCLogInfo = 0; |
| 591 | scsi_task_mgmt_reply->TerminationCount = 0; |
| 592 | |
| 593 | debug_info("%s: MPI_FUNCTION_SCSI_TASK_MGMT, type = %d\n", getName(), scsi_task_mgmt_reply->TaskType); |
| 594 | |
| 595 | if (_reply_free_queue.empty()) { |
| 596 | debug_err("%s: reply free queue is empty\n", getName()); |
| 597 | exit(1); |
| 598 | } |
| 599 | |
| 600 | uint32_t fma = _reply_free_queue.front(); |
| 601 | uint32_t msg_addr = fma & FMA_ADDRESS_MASK; |
| 602 | _reply_free_queue.pop(); |
| 603 | |
| 604 | memory_access(mfa_addr(msg_addr), mfa_addr_mode(), (void *)scsi_task_mgmt_reply, sizeof(msg_scsi_task_mgmt_reply), true); |
| 605 | send_mpi_msg(msg_addr >> 1, false, 0); |
| 606 | |
| 607 | break; |
| 608 | } |
| 609 | |
| 610 | case MPI_FUNCTION_TARGET_ASSIST: |
| 611 | debug_err("%s: MPI_FUNCTION_TARGET_ASSIST not impl\n", getName()); |
| 612 | exit(1); |
| 613 | |
| 614 | case MPI_FUNCTION_TARGET_STATUS_SEND: |
| 615 | debug_err("%s: MPI_FUNCTION_TARGET_STATUS_SEND not impl\n", getName()); |
| 616 | exit(1); |
| 617 | |
| 618 | default: |
| 619 | debug_err("%s: unsupported handshake message\n", getName()); |
| 620 | exit(1); |
| 621 | } |
| 622 | } |
| 623 | |
| 624 | |
| 625 | // Send mpi context/address reply msg to the host |
| 626 | void SAS::send_mpi_msg(uint32_t reply, bool is_cntx, uint8_t cntx_type) { |
| 627 | uint32_t fma = 0; |
| 628 | |
| 629 | if (is_cntx) { |
| 630 | // context reply |
| 631 | fma |= (reply & MPI_CONTEXT_REPLY_CONTEXT_MASK); |
| 632 | fma |= ((cntx_type & 0x3) << MPI_CONTEXT_REPLY_TYPE_SHIFT); |
| 633 | } |
| 634 | else { |
| 635 | // address reply |
| 636 | fma |= (reply & MPI_ADDRESS_REPLY_ADDRESS_MASK); |
| 637 | fma |= MPI_CONTEXT_REPLY_A_BIT; |
| 638 | } |
| 639 | |
| 640 | // both updating queue and sending interrupt should be protected by mutex |
| 641 | pthread_mutex_lock(&_reply_queue_mutex); |
| 642 | |
| 643 | // push fma into reply post queue |
| 644 | _reply_post_queue.push(fma); |
| 645 | |
| 646 | // set interrupt |
| 647 | set_reply_msg_interrupt(); |
| 648 | |
| 649 | pthread_mutex_unlock(&_reply_queue_mutex); |
| 650 | } |
| 651 | |
| 652 | |
| 653 | // Process a mpi message received through request queue |
| 654 | // Request msg is in little-endian, should be converted into big-endian after receiving |
| 655 | // Reply msg is in big-endian, should be converted into little-endian before sending |
| 656 | void SAS::process_mpi_msg(int entry, uint32_t fma) { |
| 657 | uint8_t *msg_buf = (uint8_t *)calloc(256, sizeof(uint8_t)); |
| 658 | uint32_t msg_addr = fma & FMA_ADDRESS_MASK; |
| 659 | uint64_t sge_addr = msg_addr + sizeof(msg_scsi_io_request_t) - sizeof(sge_io_union_t); |
| 660 | |
| 661 | msg_request_header_t *msg_header; |
| 662 | msg_scsi_io_request_t *msg_scsi_io_req; |
| 663 | |
| 664 | // read the header first |
| 665 | memory_access(mfa_addr(msg_addr), mfa_addr_mode(), (void *)msg_buf, sizeof(msg_request_header_t), false); |
| 666 | msg_header = (msg_request_header *)msg_buf; |
| 667 | |
| 668 | // read and process scsi command according to different headers |
| 669 | switch (msg_header->Function) { |
| 670 | case MPI_FUNCTION_SCSI_IO_REQUEST: { |
| 671 | memory_access(mfa_addr(msg_addr), mfa_addr_mode(), (void *)msg_buf, sizeof(msg_scsi_io_request_t), false); |
| 672 | msg_scsi_io_req = (msg_scsi_io_request_t *)msg_buf; |
| 673 | |
| 674 | msg_scsi_io_req->MsgContext = SAM_LE_32(msg_scsi_io_req->MsgContext); |
| 675 | msg_scsi_io_req->Control = SAM_LE_32(msg_scsi_io_req->Control); |
| 676 | msg_scsi_io_req->DataLength = SAM_LE_32(msg_scsi_io_req->DataLength); |
| 677 | msg_scsi_io_req->SenseBufferLowAddr = SAM_LE_32(msg_scsi_io_req->SenseBufferLowAddr); |
| 678 | msg_scsi_io_req->SGL.u1.Simple.FlagsLength = |
| 679 | SAM_LE_32(msg_scsi_io_req->SGL.u1.Simple.FlagsLength); |
| 680 | msg_scsi_io_req->SGL.u1.Simple.u1.Address32= |
| 681 | SAM_LE_32(msg_scsi_io_req->SGL.u1.Simple.u1.Address32); |
| 682 | |
| 683 | process_scsi_cmd(entry, msg_scsi_io_req, sge_addr); |
| 684 | |
| 685 | break; |
| 686 | } |
| 687 | |
| 688 | default: |
| 689 | debug_err("%s: MPI message unsupported\n", getName()); |
| 690 | exit(1); |
| 691 | } |
| 692 | |
| 693 | free(msg_buf); |
| 694 | } |