Commit | Line | Data |
---|---|---|
920dae64 AT |
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 | } |