VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DevLsiLogicSCSI.cpp@ 45959

Last change on this file since 45959 was 45959, checked in by vboxsync, 12 years ago

Storage/LsiLogic: Start cleaning up the implementation, rework I/O buffer handling and rename pTaskState to pLsiReq

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 219.5 KB
Line 
1/* $Id: DevLsiLogicSCSI.cpp 45959 2013-05-08 20:27:08Z vboxsync $ */
2/** @file
3 * DevLsiLogicSCSI - LsiLogic LSI53c1030 SCSI controller.
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_DEV_LSILOGICSCSI
22#include <VBox/vmm/pdmdev.h>
23#include <VBox/vmm/pdmqueue.h>
24#include <VBox/vmm/pdmcritsect.h>
25#include <VBox/scsi.h>
26#include <iprt/assert.h>
27#include <iprt/asm.h>
28#include <iprt/string.h>
29#ifdef IN_RING3
30# include <iprt/memcache.h>
31# include <iprt/mem.h>
32# include <iprt/param.h>
33# include <iprt/uuid.h>
34# include <iprt/time.h>
35#endif
36
37#include "DevLsiLogicSCSI.h"
38#include "VBoxSCSI.h"
39
40#include "VBoxDD.h"
41
42
43/*******************************************************************************
44* Defined Constants And Macros *
45*******************************************************************************/
46/** The current saved state version. */
47#define LSILOGIC_SAVED_STATE_VERSION 4
48/** The saved state version used by VirtualBox before the doorbell status flag
49 * was changed from bool to a 32bit enum. */
50#define LSILOGIC_SAVED_STATE_VERSION_BOOL_DOORBELL 3
51/** The saved state version used by VirtualBox before SAS support was added. */
52#define LSILOGIC_SAVED_STATE_VERSION_PRE_SAS 2
53/** The saved state version used by VirtualBox 3.0 and earlier. It does not
54 * include the device config part. */
55#define LSILOGIC_SAVED_STATE_VERSION_VBOX_30 1
56
57/** Maximum number of entries in the release log. */
58#define MAX_REL_LOG_ERRORS 1024
59
60#define LSILOGIC_RTGCPHYS_FROM_U32(Hi, Lo) ( (RTGCPHYS)RT_MAKE_U64(Lo, Hi) )
61
62/** Upper number a buffer is freed if it was too big before. */
63#define LSILOGIC_MAX_ALLOC_TOO_MUCH 20
64
65/*******************************************************************************
66* Structures and Typedefs *
67*******************************************************************************/
68
69/**
70 * I/O buffer copy worker.
71 *
72 * @returns nothing.
73 * @param pDevIns Device instance data.
74 * @param GCPhysIoBuf Guest physical address of the I/O buffer.
75 * @param pvBuf R3 buffer pointer.
76 * @param cbCopy How much to copy.
77 */
78typedef DECLCALLBACK(void) FNLSILOGICIOBUFCOPY(PPDMDEVINS pDevIns, RTGCPHYS GCPhysIoBuf,
79 void *pvBuf, size_t cbCopy);
80/** Pointer to a I/O buffer copy worker. */
81typedef FNLSILOGICIOBUFCOPY *PFNLSILOGICIOBUFCOPY;
82
83/**
84 * Reply data.
85 */
86typedef struct LSILOGICSCSIREPLY
87{
88 /** Lower 32 bits of the reply address in memory. */
89 uint32_t u32HostMFALowAddress;
90 /** Full address of the reply in guest memory. */
91 RTGCPHYS GCPhysReplyAddress;
92 /** Size of the reply. */
93 uint32_t cbReply;
94 /** Different views to the reply depending on the request type. */
95 MptReplyUnion Reply;
96} LSILOGICSCSIREPLY;
97/** Pointer to reply data. */
98typedef LSILOGICSCSIREPLY *PLSILOGICSCSIREPLY;
99
100/**
101 * State of a device attached to the buslogic host adapter.
102 *
103 * @implements PDMIBASE
104 * @implements PDMISCSIPORT
105 * @implements PDMILEDPORTS
106 */
107typedef struct LSILOGICDEVICE
108{
109 /** Pointer to the owning lsilogic device instance. - R3 pointer */
110 R3PTRTYPE(struct LSILOGICSCSI *) pLsiLogicR3;
111
112 /** LUN of the device. */
113 uint32_t iLUN;
114 /** Number of outstanding tasks on the port. */
115 volatile uint32_t cOutstandingRequests;
116
117#if HC_ARCH_BITS == 64
118 uint32_t Alignment0;
119#endif
120
121 /** Our base interface. */
122 PDMIBASE IBase;
123 /** SCSI port interface. */
124 PDMISCSIPORT ISCSIPort;
125 /** Led interface. */
126 PDMILEDPORTS ILed;
127 /** Pointer to the attached driver's base interface. */
128 R3PTRTYPE(PPDMIBASE) pDrvBase;
129 /** Pointer to the underlying SCSI connector interface. */
130 R3PTRTYPE(PPDMISCSICONNECTOR) pDrvSCSIConnector;
131 /** The status LED state for this device. */
132 PDMLED Led;
133
134} LSILOGICDEVICE;
135/** Pointer to a device state. */
136typedef LSILOGICDEVICE *PLSILOGICDEVICE;
137
138/** Pointer to a task state. */
139typedef struct LSILOGICREQ *PLSILOGICREQ;
140
141/**
142 * Device instance data for the emulated SCSI controller.
143 */
144typedef struct LSILOGICSCSI
145{
146 /** PCI device structure. */
147 PCIDEVICE PciDev;
148 /** Pointer to the device instance. - R3 ptr. */
149 PPDMDEVINSR3 pDevInsR3;
150 /** Pointer to the device instance. - R0 ptr. */
151 PPDMDEVINSR0 pDevInsR0;
152 /** Pointer to the device instance. - RC ptr. */
153 PPDMDEVINSRC pDevInsRC;
154
155 /** Flag whether the GC part of the device is enabled. */
156 bool fGCEnabled;
157 /** Flag whether the R0 part of the device is enabled. */
158 bool fR0Enabled;
159
160 /** The state the controller is currently in. */
161 LSILOGICSTATE enmState;
162 /** Who needs to init the driver to get into operational state. */
163 LSILOGICWHOINIT enmWhoInit;
164 /** Flag whether we are in doorbell function. */
165 LSILOGICDOORBELLSTATE enmDoorbellState;
166 /** Flag whether diagnostic access is enabled. */
167 bool fDiagnosticEnabled;
168 /** Flag whether a notification was send to R3. */
169 bool fNotificationSend;
170 /** Flag whether the guest enabled event notification from the IOC. */
171 bool fEventNotificationEnabled;
172
173 /** Queue to send tasks to R3. - R3 ptr */
174 R3PTRTYPE(PPDMQUEUE) pNotificationQueueR3;
175 /** Queue to send tasks to R3. - R0 ptr */
176 R0PTRTYPE(PPDMQUEUE) pNotificationQueueR0;
177 /** Queue to send tasks to R3. - RC ptr */
178 RCPTRTYPE(PPDMQUEUE) pNotificationQueueRC;
179
180 /** Number of device states allocated. */
181 uint32_t cDeviceStates;
182
183 /** States for attached devices. */
184 R3PTRTYPE(PLSILOGICDEVICE) paDeviceStates;
185#if HC_ARCH_BITS == 32
186 RTR3PTR R3PtrPadding0;
187#endif
188
189 /** Interrupt mask. */
190 volatile uint32_t uInterruptMask;
191 /** Interrupt status register. */
192 volatile uint32_t uInterruptStatus;
193
194 /** Buffer for messages which are passed through the doorbell using the
195 * handshake method. */
196 uint32_t aMessage[sizeof(MptConfigurationRequest)]; /** @todo r=bird: Looks like 4 tims the required size? Please explain in comment if this correct... */
197 /** Actual position in the buffer. */
198 uint32_t iMessage;
199 /** Size of the message which is given in the doorbell message in dwords. */
200 uint32_t cMessage;
201
202 /** Reply buffer.
203 * @note 60 bytes */
204 MptReplyUnion ReplyBuffer;
205 /** Next entry to read. */
206 uint32_t uNextReplyEntryRead;
207 /** Size of the reply in the buffer in 16bit words. */
208 uint32_t cReplySize;
209
210 /** The fault code of the I/O controller if we are in the fault state. */
211 uint16_t u16IOCFaultCode;
212
213 /** I/O port address the device is mapped to. */
214 RTIOPORT IOPortBase;
215 /** MMIO address the device is mapped to. */
216 RTGCPHYS GCPhysMMIOBase;
217
218 /** Upper 32 bits of the message frame address to locate requests in guest memory. */
219 uint32_t u32HostMFAHighAddr;
220 /** Upper 32 bits of the sense buffer address. */
221 uint32_t u32SenseBufferHighAddr;
222 /** Maximum number of devices the driver reported he can handle. */
223 uint8_t cMaxDevices;
224 /** Maximum number of buses the driver reported he can handle. */
225 uint8_t cMaxBuses;
226 /** Current size of reply message frames in the guest. */
227 uint16_t cbReplyFrame;
228
229 /** Next key to write in the sequence to get access
230 * to diagnostic memory. */
231 uint32_t iDiagnosticAccess;
232
233 /** Number entries allocated for the reply queue. */
234 uint32_t cReplyQueueEntries;
235 /** Number entries allocated for the outstanding request queue. */
236 uint32_t cRequestQueueEntries;
237
238
239 /** Critical section protecting the reply post queue. */
240 PDMCRITSECT ReplyPostQueueCritSect;
241 /** Critical section protecting the reply free queue. */
242 PDMCRITSECT ReplyFreeQueueCritSect;
243
244 /** Pointer to the start of the reply free queue - R3. */
245 R3PTRTYPE(volatile uint32_t *) pReplyFreeQueueBaseR3;
246 /** Pointer to the start of the reply post queue - R3. */
247 R3PTRTYPE(volatile uint32_t *) pReplyPostQueueBaseR3;
248 /** Pointer to the start of the request queue - R3. */
249 R3PTRTYPE(volatile uint32_t *) pRequestQueueBaseR3;
250
251 /** Pointer to the start of the reply queue - R0. */
252 R0PTRTYPE(volatile uint32_t *) pReplyFreeQueueBaseR0;
253 /** Pointer to the start of the reply queue - R0. */
254 R0PTRTYPE(volatile uint32_t *) pReplyPostQueueBaseR0;
255 /** Pointer to the start of the request queue - R0. */
256 R0PTRTYPE(volatile uint32_t *) pRequestQueueBaseR0;
257
258 /** Pointer to the start of the reply queue - RC. */
259 RCPTRTYPE(volatile uint32_t *) pReplyFreeQueueBaseRC;
260 /** Pointer to the start of the reply queue - RC. */
261 RCPTRTYPE(volatile uint32_t *) pReplyPostQueueBaseRC;
262 /** Pointer to the start of the request queue - RC. */
263 RCPTRTYPE(volatile uint32_t *) pRequestQueueBaseRC;
264 /** End these RC pointers on a 64-bit boundrary. */
265 RTRCPTR RCPtrPadding1;
266
267 /** Next free entry in the reply queue the guest can write a address to. */
268 volatile uint32_t uReplyFreeQueueNextEntryFreeWrite;
269 /** Next valid entry the controller can read a valid address for reply frames from. */
270 volatile uint32_t uReplyFreeQueueNextAddressRead;
271
272 /** Next free entry in the reply queue the guest can write a address to. */
273 volatile uint32_t uReplyPostQueueNextEntryFreeWrite;
274 /** Next valid entry the controller can read a valid address for reply frames from. */
275 volatile uint32_t uReplyPostQueueNextAddressRead;
276
277 /** Next free entry the guest can write a address to a request frame to. */
278 volatile uint32_t uRequestQueueNextEntryFreeWrite;
279 /** Next valid entry the controller can read a valid address for request frames from. */
280 volatile uint32_t uRequestQueueNextAddressRead;
281
282 /** Emulated controller type */
283 LSILOGICCTRLTYPE enmCtrlType;
284 /** Handle counter */
285 uint16_t u16NextHandle;
286
287 /** Number of ports this controller has. */
288 uint8_t cPorts;
289
290 /** BIOS emulation. */
291 VBOXSCSI VBoxSCSI;
292
293 /** Cache for allocated tasks. */
294 R3PTRTYPE(RTMEMCACHE) hTaskCache;
295 /** Status LUN: The base interface. */
296 PDMIBASE IBase;
297 /** Status LUN: Leds interface. */
298 PDMILEDPORTS ILeds;
299 /** Status LUN: Partner of ILeds. */
300 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
301 /** Pointer to the configuration page area. */
302 R3PTRTYPE(PMptConfigurationPagesSupported) pConfigurationPages;
303
304 /** Indicates that PDMDevHlpAsyncNotificationCompleted should be called when
305 * a port is entering the idle state. */
306 bool volatile fSignalIdle;
307 /** Flag whether we have tasks which need to be processed again- */
308 bool volatile fRedo;
309 /** Alignment padding. */
310 bool afPAdding2[HC_ARCH_BITS == 32 ? 2 : 6];
311 /** List of tasks which can be redone. */
312 R3PTRTYPE(volatile PLSILOGICREQ) pTasksRedoHead;
313
314} LSILOGISCSI;
315/** Pointer to the device instance data of the LsiLogic emulation. */
316typedef LSILOGICSCSI *PLSILOGICSCSI;
317
318/**
319 * Task state object which holds all necessary data while
320 * processing the request from the guest.
321 */
322typedef struct LSILOGICREQ
323{
324 /** Next in the redo list. */
325 PLSILOGICREQ pRedoNext;
326 /** Target device. */
327 PLSILOGICDEVICE pTargetDevice;
328 /** The message request from the guest. */
329 MptRequestUnion GuestRequest;
330 /** Reply message if the request produces one. */
331 MptReplyUnion IOCReply;
332 /** SCSI request structure for the SCSI driver. */
333 PDMSCSIREQUEST PDMScsiRequest;
334 /** Address of the message request frame in guests memory.
335 * Used to read the S/G entries in the second step. */
336 RTGCPHYS GCPhysMessageFrameAddr;
337 /** Physical start address of the S/G list. */
338 RTGCPHYS GCPhysSgStart;
339 /** Chain offset */
340 uint32_t cChainOffset;
341 /** Segment describing the I/O buffer. */
342 RTSGSEG SegIoBuf;
343 /** Additional memory allocation for this task. */
344 void *pvAlloc;
345 /** Siize of the allocation. */
346 size_t cbAlloc;
347 /** Number of times we had too much memory allocated for the request. */
348 unsigned cAllocTooMuch;
349 /** Pointer to the sense buffer. */
350 uint8_t abSenseBuffer[18];
351 /** Flag whether the request was issued from the BIOS. */
352 bool fBIOS;
353} LSILOGICREQ;
354
355
356#ifndef VBOX_DEVICE_STRUCT_TESTCASE
357
358/*******************************************************************************
359* Internal Functions *
360*******************************************************************************/
361RT_C_DECLS_BEGIN
362#ifdef IN_RING3
363static void lsilogicR3InitializeConfigurationPages(PLSILOGICSCSI pThis);
364static void lsilogicR3ConfigurationPagesFree(PLSILOGICSCSI pThis);
365static int lsilogicR3ProcessConfigurationRequest(PLSILOGICSCSI pThis, PMptConfigurationRequest pConfigurationReq,
366 PMptConfigurationReply pReply);
367#endif
368RT_C_DECLS_END
369
370
371/*******************************************************************************
372* Global Variables *
373*******************************************************************************/
374/** Key sequence the guest has to write to enable access
375 * to diagnostic memory. */
376static const uint8_t g_lsilogicDiagnosticAccess[] = {0x04, 0x0b, 0x02, 0x07, 0x0d};
377
378/**
379 * Updates the status of the interrupt pin of the device.
380 *
381 * @returns nothing.
382 * @param pThis Pointer to the LsiLogic device state.
383 */
384static void lsilogicUpdateInterrupt(PLSILOGICSCSI pThis)
385{
386 uint32_t uIntSts;
387
388 LogFlowFunc(("Updating interrupts\n"));
389
390 /* Mask out doorbell status so that it does not affect interrupt updating. */
391 uIntSts = (ASMAtomicReadU32(&pThis->uInterruptStatus) & ~LSILOGIC_REG_HOST_INTR_STATUS_DOORBELL_STS);
392 /* Check maskable interrupts. */
393 uIntSts &= ~(ASMAtomicReadU32(&pThis->uInterruptMask) & ~LSILOGIC_REG_HOST_INTR_MASK_IRQ_ROUTING);
394
395 if (uIntSts)
396 {
397 LogFlowFunc(("Setting interrupt\n"));
398 PDMDevHlpPCISetIrq(pThis->CTX_SUFF(pDevIns), 0, 1);
399 }
400 else
401 {
402 LogFlowFunc(("Clearing interrupt\n"));
403 PDMDevHlpPCISetIrq(pThis->CTX_SUFF(pDevIns), 0, 0);
404 }
405}
406
407/**
408 * Sets a given interrupt status bit in the status register and
409 * updates the interrupt status.
410 *
411 * @returns nothing.
412 * @param pThis Pointer to the LsiLogic device state.
413 * @param uStatus The status bit to set.
414 */
415DECLINLINE(void) lsilogicSetInterrupt(PLSILOGICSCSI pThis, uint32_t uStatus)
416{
417 ASMAtomicOrU32(&pThis->uInterruptStatus, uStatus);
418 lsilogicUpdateInterrupt(pThis);
419}
420
421/**
422 * Clears a given interrupt status bit in the status register and
423 * updates the interrupt status.
424 *
425 * @returns nothing.
426 * @param pThis Pointer to the LsiLogic device state.
427 * @param uStatus The status bit to set.
428 */
429DECLINLINE(void) lsilogicClearInterrupt(PLSILOGICSCSI pThis, uint32_t uStatus)
430{
431 ASMAtomicAndU32(&pThis->uInterruptStatus, ~uStatus);
432 lsilogicUpdateInterrupt(pThis);
433}
434
435/**
436 * Sets the I/O controller into fault state and sets the fault code.
437 *
438 * @returns nothing
439 * @param pThis Pointer to the LsiLogic device state.
440 * @param uIOCFaultCode Fault code to set.
441 */
442DECLINLINE(void) lsilogicSetIOCFaultCode(PLSILOGICSCSI pThis, uint16_t uIOCFaultCode)
443{
444 if (pThis->enmState != LSILOGICSTATE_FAULT)
445 {
446 Log(("%s: Setting I/O controller into FAULT state: uIOCFaultCode=%u\n", __FUNCTION__, uIOCFaultCode));
447 pThis->enmState = LSILOGICSTATE_FAULT;
448 pThis->u16IOCFaultCode = uIOCFaultCode;
449 }
450 else
451 Log(("%s: We are already in FAULT state\n"));
452}
453
454#ifdef IN_RING3
455
456/**
457 * Performs a hard reset on the controller.
458 *
459 * @returns VBox status code.
460 * @param pThis Pointer to the LsiLogic device state.
461 */
462static int lsilogicR3HardReset(PLSILOGICSCSI pThis)
463{
464 pThis->enmState = LSILOGICSTATE_RESET;
465 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
466
467 /* The interrupts are masked out. */
468 pThis->uInterruptMask |= LSILOGIC_REG_HOST_INTR_MASK_DOORBELL
469 | LSILOGIC_REG_HOST_INTR_MASK_REPLY;
470 /* Reset interrupt states. */
471 pThis->uInterruptStatus = 0;
472 lsilogicUpdateInterrupt(pThis);
473
474 /* Reset the queues. */
475 pThis->uReplyFreeQueueNextEntryFreeWrite = 0;
476 pThis->uReplyFreeQueueNextAddressRead = 0;
477 pThis->uReplyPostQueueNextEntryFreeWrite = 0;
478 pThis->uReplyPostQueueNextAddressRead = 0;
479 pThis->uRequestQueueNextEntryFreeWrite = 0;
480 pThis->uRequestQueueNextAddressRead = 0;
481
482 /* Disable diagnostic access. */
483 pThis->iDiagnosticAccess = 0;
484
485 /* Set default values. */
486 pThis->cMaxDevices = pThis->cDeviceStates;
487 pThis->cMaxBuses = 1;
488 pThis->cbReplyFrame = 128; /* @todo Figure out where it is needed. */
489 pThis->u16NextHandle = 1;
490 /** @todo: Put stuff to reset here. */
491
492 lsilogicR3ConfigurationPagesFree(pThis);
493 lsilogicR3InitializeConfigurationPages(pThis);
494
495 /* Mark that we finished performing the reset. */
496 pThis->enmState = LSILOGICSTATE_READY;
497 return VINF_SUCCESS;
498}
499
500/**
501 * Frees the configuration pages if allocated.
502 *
503 * @returns nothing.
504 * @param pThis The LsiLogic controller instance
505 */
506static void lsilogicR3ConfigurationPagesFree(PLSILOGICSCSI pThis)
507{
508
509 if (pThis->pConfigurationPages)
510 {
511 /* Destroy device list if we emulate a SAS controller. */
512 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
513 {
514 PMptConfigurationPagesSas pSasPages = &pThis->pConfigurationPages->u.SasPages;
515 PMptSASDevice pSASDeviceCurr = pSasPages->pSASDeviceHead;
516
517 while (pSASDeviceCurr)
518 {
519 PMptSASDevice pFree = pSASDeviceCurr;
520
521 pSASDeviceCurr = pSASDeviceCurr->pNext;
522 RTMemFree(pFree);
523 }
524 if (pSasPages->paPHYs)
525 RTMemFree(pSasPages->paPHYs);
526 if (pSasPages->pManufacturingPage7)
527 RTMemFree(pSasPages->pManufacturingPage7);
528 if (pSasPages->pSASIOUnitPage0)
529 RTMemFree(pSasPages->pSASIOUnitPage0);
530 if (pSasPages->pSASIOUnitPage1)
531 RTMemFree(pSasPages->pSASIOUnitPage1);
532 }
533
534 RTMemFree(pThis->pConfigurationPages);
535 }
536}
537
538/**
539 * Finishes a context reply.
540 *
541 * @returns nothing
542 * @param pThis Pointer to the LsiLogic device state.
543 * @param u32MessageContext The message context ID to post.
544 */
545static void lsilogicR3FinishContextReply(PLSILOGICSCSI pThis, uint32_t u32MessageContext)
546{
547 int rc;
548
549 LogFlowFunc(("pThis=%#p u32MessageContext=%#x\n", pThis, u32MessageContext));
550
551 AssertMsg(pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_NOT_IN_USE, ("We are in a doorbell function\n"));
552
553 /* Write message context ID into reply post queue. */
554 rc = PDMCritSectEnter(&pThis->ReplyPostQueueCritSect, VINF_SUCCESS);
555 AssertRC(rc);
556
557# if 0
558 /* Check for a entry in the queue. */
559 if (RT_UNLIKELY(pThis->uReplyPostQueueNextAddressRead != pThis->uReplyPostQueueNextEntryFreeWrite))
560 {
561 /* Set error code. */
562 lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES);
563 PDMCritSectLeave(&pThis->ReplyPostQueueCritSect);
564 return;
565 }
566# endif
567
568 /* We have a context reply. */
569 ASMAtomicWriteU32(&pThis->CTX_SUFF(pReplyPostQueueBase)[pThis->uReplyPostQueueNextEntryFreeWrite], u32MessageContext);
570 ASMAtomicIncU32(&pThis->uReplyPostQueueNextEntryFreeWrite);
571 pThis->uReplyPostQueueNextEntryFreeWrite %= pThis->cReplyQueueEntries;
572
573 /* Set interrupt. */
574 lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR);
575
576 PDMCritSectLeave(&pThis->ReplyPostQueueCritSect);
577}
578
579#endif /* IN_RING3 */
580
581/**
582 * Takes necessary steps to finish a reply frame.
583 *
584 * @returns nothing
585 * @param pThis Pointer to the LsiLogic device state.
586 * @param pReply Pointer to the reply message.
587 * @param fForceReplyFifo Flag whether the use of the reply post fifo is forced.
588 */
589static void lsilogicFinishAddressReply(PLSILOGICSCSI pThis, PMptReplyUnion pReply, bool fForceReplyFifo)
590{
591 /*
592 * If we are in a doorbell function we set the reply size now and
593 * set the system doorbell status interrupt to notify the guest that
594 * we are ready to send the reply.
595 */
596 if (pThis->enmDoorbellState != LSILOGICDOORBELLSTATE_NOT_IN_USE && !fForceReplyFifo)
597 {
598 /* Set size of the reply in 16bit words. The size in the reply is in 32bit dwords. */
599 pThis->cReplySize = pReply->Header.u8MessageLength * 2;
600 Log(("%s: cReplySize=%u\n", __FUNCTION__, pThis->cReplySize));
601 pThis->uNextReplyEntryRead = 0;
602 lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
603 }
604 else
605 {
606 /*
607 * The reply queues are only used if the request was fetched from the request queue.
608 * Requests from the request queue are always transferred to R3. So it is not possible
609 * that this case happens in R0 or GC.
610 */
611#ifdef IN_RING3
612 int rc;
613 /* Grab a free reply message from the queue. */
614 rc = PDMCritSectEnter(&pThis->ReplyFreeQueueCritSect, VINF_SUCCESS);
615 AssertRC(rc);
616
617#if 0
618 /* Check for a free reply frame. */
619 if (RT_UNLIKELY(pThis->uReplyFreeQueueNextAddressRead != pThis->uReplyFreeQueueNextEntryFreeWrite))
620 {
621 /* Set error code. */
622 lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES);
623 PDMCritSectLeave(&pThis->ReplyFreeQueueCritSect);
624 return;
625 }
626#endif
627
628 uint32_t u32ReplyFrameAddressLow = pThis->CTX_SUFF(pReplyFreeQueueBase)[pThis->uReplyFreeQueueNextAddressRead];
629
630 pThis->uReplyFreeQueueNextAddressRead++;
631 pThis->uReplyFreeQueueNextAddressRead %= pThis->cReplyQueueEntries;
632
633 PDMCritSectLeave(&pThis->ReplyFreeQueueCritSect);
634
635 /* Build 64bit physical address. */
636 RTGCPHYS GCPhysReplyMessage = LSILOGIC_RTGCPHYS_FROM_U32(pThis->u32HostMFAHighAddr, u32ReplyFrameAddressLow);
637 size_t cbReplyCopied = (pThis->cbReplyFrame < sizeof(MptReplyUnion)) ? pThis->cbReplyFrame : sizeof(MptReplyUnion);
638
639 /* Write reply to guest memory. */
640 PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns), GCPhysReplyMessage, pReply, cbReplyCopied);
641
642 /* Write low 32bits of reply frame into post reply queue. */
643 rc = PDMCritSectEnter(&pThis->ReplyPostQueueCritSect, VINF_SUCCESS);
644 AssertRC(rc);
645
646#if 0
647 /* Check for a entry in the queue. */
648 if (RT_UNLIKELY(pThis->uReplyPostQueueNextAddressRead != pThis->uReplyPostQueueNextEntryFreeWrite))
649 {
650 /* Set error code. */
651 lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES);
652 PDMCritSectLeave(&pThis->ReplyPostQueueCritSect);
653 return;
654 }
655#endif
656
657 /* We have a address reply. Set the 31th bit to indicate that. */
658 ASMAtomicWriteU32(&pThis->CTX_SUFF(pReplyPostQueueBase)[pThis->uReplyPostQueueNextEntryFreeWrite],
659 RT_BIT(31) | (u32ReplyFrameAddressLow >> 1));
660 ASMAtomicIncU32(&pThis->uReplyPostQueueNextEntryFreeWrite);
661 pThis->uReplyPostQueueNextEntryFreeWrite %= pThis->cReplyQueueEntries;
662
663 if (fForceReplyFifo)
664 {
665 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
666 lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
667 }
668
669 /* Set interrupt. */
670 lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR);
671
672 PDMCritSectLeave(&pThis->ReplyPostQueueCritSect);
673#else
674 AssertMsgFailed(("This is not allowed to happen.\n"));
675#endif
676 }
677}
678
679/**
680 * Returns the number of frames in the reply free queue.
681 *
682 * @returns Number of frames in the reply free queue.
683 * @param pThis Pointer to the LsiLogic device state.
684 */
685DECLINLINE(uint32_t) lsilogicReplyFreeQueueGetFrameCount(PLSILOGICSCSI pThis)
686{
687 uint32_t cReplyFrames = 0;
688
689 if (pThis->uReplyFreeQueueNextAddressRead <= pThis->uReplyFreeQueueNextEntryFreeWrite)
690 cReplyFrames = pThis->uReplyFreeQueueNextEntryFreeWrite - pThis->uReplyFreeQueueNextAddressRead;
691 else
692 cReplyFrames = pThis->cReplyQueueEntries - pThis->uReplyFreeQueueNextAddressRead + pThis->uReplyFreeQueueNextEntryFreeWrite;
693
694 return cReplyFrames;
695}
696
697#ifdef IN_RING3
698/**
699 * Processes a given Request from the guest
700 *
701 * @returns VBox status code.
702 * @param pThis Pointer to the LsiLogic device state.
703 * @param pMessageHdr Pointer to the message header of the request.
704 * @param pReply Pointer to the reply.
705 */
706static int lsilogicR3ProcessMessageRequest(PLSILOGICSCSI pThis, PMptMessageHdr pMessageHdr, PMptReplyUnion pReply)
707{
708 int rc = VINF_SUCCESS;
709 bool fForceReplyPostFifo = false;
710
711# ifdef LOG_ENABLED
712 if (pMessageHdr->u8Function < RT_ELEMENTS(g_apszMPTFunctionNames))
713 Log(("Message request function: %s\n", g_apszMPTFunctionNames[pMessageHdr->u8Function]));
714 else
715 Log(("Message request function: <unknown>\n"));
716# endif
717
718 memset(pReply, 0, sizeof(MptReplyUnion));
719
720 switch (pMessageHdr->u8Function)
721 {
722 case MPT_MESSAGE_HDR_FUNCTION_SCSI_TASK_MGMT:
723 {
724 PMptSCSITaskManagementRequest pTaskMgmtReq = (PMptSCSITaskManagementRequest)pMessageHdr;
725
726 LogFlow(("u8TaskType=%u\n", pTaskMgmtReq->u8TaskType));
727 LogFlow(("u32TaskMessageContext=%#x\n", pTaskMgmtReq->u32TaskMessageContext));
728
729 pReply->SCSITaskManagement.u8MessageLength = 6; /* 6 32bit dwords. */
730 pReply->SCSITaskManagement.u8TaskType = pTaskMgmtReq->u8TaskType;
731 pReply->SCSITaskManagement.u32TerminationCount = 0;
732 fForceReplyPostFifo = true;
733 break;
734 }
735 case MPT_MESSAGE_HDR_FUNCTION_IOC_INIT:
736 {
737 /*
738 * This request sets the I/O controller to the
739 * operational state.
740 */
741 PMptIOCInitRequest pIOCInitReq = (PMptIOCInitRequest)pMessageHdr;
742
743 /* Update configuration values. */
744 pThis->enmWhoInit = (LSILOGICWHOINIT)pIOCInitReq->u8WhoInit;
745 pThis->cbReplyFrame = pIOCInitReq->u16ReplyFrameSize;
746 pThis->cMaxBuses = pIOCInitReq->u8MaxBuses;
747 pThis->cMaxDevices = pIOCInitReq->u8MaxDevices;
748 pThis->u32HostMFAHighAddr = pIOCInitReq->u32HostMfaHighAddr;
749 pThis->u32SenseBufferHighAddr = pIOCInitReq->u32SenseBufferHighAddr;
750
751 if (pThis->enmState == LSILOGICSTATE_READY)
752 {
753 pThis->enmState = LSILOGICSTATE_OPERATIONAL;
754 }
755
756 /* Return reply. */
757 pReply->IOCInit.u8MessageLength = 5;
758 pReply->IOCInit.u8WhoInit = pThis->enmWhoInit;
759 pReply->IOCInit.u8MaxDevices = pThis->cMaxDevices;
760 pReply->IOCInit.u8MaxBuses = pThis->cMaxBuses;
761 break;
762 }
763 case MPT_MESSAGE_HDR_FUNCTION_IOC_FACTS:
764 {
765 pReply->IOCFacts.u8MessageLength = 15; /* 15 32bit dwords. */
766
767 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
768 {
769 pReply->IOCFacts.u16MessageVersion = 0x0102; /* Version from the specification. */
770 pReply->IOCFacts.u8NumberOfPorts = pThis->cPorts;
771 }
772 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
773 {
774 pReply->IOCFacts.u16MessageVersion = 0x0105; /* Version from the specification. */
775 pReply->IOCFacts.u8NumberOfPorts = pThis->cPorts;
776 }
777 else
778 AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
779
780 pReply->IOCFacts.u8IOCNumber = 0; /* PCI function number. */
781 pReply->IOCFacts.u16IOCExceptions = 0;
782 pReply->IOCFacts.u8MaxChainDepth = LSILOGICSCSI_MAXIMUM_CHAIN_DEPTH;
783 pReply->IOCFacts.u8WhoInit = pThis->enmWhoInit;
784 pReply->IOCFacts.u8BlockSize = 12; /* Block size in 32bit dwords. This is the largest request we can get (SCSI I/O). */
785 pReply->IOCFacts.u8Flags = 0; /* Bit 0 is set if the guest must upload the FW prior to using the controller. Obviously not needed here. */
786 pReply->IOCFacts.u16ReplyQueueDepth = pThis->cReplyQueueEntries - 1; /* One entry is always free. */
787 pReply->IOCFacts.u16RequestFrameSize = 128; /* @todo Figure out where it is needed. */
788 pReply->IOCFacts.u16ProductID = 0xcafe; /* Our own product ID :) */
789 pReply->IOCFacts.u32CurrentHostMFAHighAddr = pThis->u32HostMFAHighAddr;
790 pReply->IOCFacts.u16GlobalCredits = pThis->cRequestQueueEntries - 1; /* One entry is always free. */
791
792 pReply->IOCFacts.u8EventState = 0; /* Event notifications not enabled. */
793 pReply->IOCFacts.u32CurrentSenseBufferHighAddr = pThis->u32SenseBufferHighAddr;
794 pReply->IOCFacts.u16CurReplyFrameSize = pThis->cbReplyFrame;
795 pReply->IOCFacts.u8MaxDevices = pThis->cMaxDevices;
796 pReply->IOCFacts.u8MaxBuses = pThis->cMaxBuses;
797 pReply->IOCFacts.u32FwImageSize = 0; /* No image needed. */
798 pReply->IOCFacts.u32FWVersion = 0;
799 break;
800 }
801 case MPT_MESSAGE_HDR_FUNCTION_PORT_FACTS:
802 {
803 PMptPortFactsRequest pPortFactsReq = (PMptPortFactsRequest)pMessageHdr;
804
805 pReply->PortFacts.u8MessageLength = 10;
806 pReply->PortFacts.u8PortNumber = pPortFactsReq->u8PortNumber;
807
808 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
809 {
810 /* This controller only supports one bus with bus number 0. */
811 if (pPortFactsReq->u8PortNumber >= pThis->cPorts)
812 {
813 pReply->PortFacts.u8PortType = 0; /* Not existant. */
814 }
815 else
816 {
817 pReply->PortFacts.u8PortType = 0x01; /* SCSI Port. */
818 pReply->PortFacts.u16MaxDevices = LSILOGICSCSI_PCI_SPI_DEVICES_PER_BUS_MAX;
819 pReply->PortFacts.u16ProtocolFlags = RT_BIT(3) | RT_BIT(0); /* SCSI initiator and LUN supported. */
820 pReply->PortFacts.u16PortSCSIID = 7; /* Default */
821 pReply->PortFacts.u16MaxPersistentIDs = 0;
822 pReply->PortFacts.u16MaxPostedCmdBuffers = 0; /* Only applies for target mode which we dont support. */
823 pReply->PortFacts.u16MaxLANBuckets = 0; /* Only for the LAN controller. */
824 }
825 }
826 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
827 {
828 if (pPortFactsReq->u8PortNumber >= pThis->cPorts)
829 {
830 pReply->PortFacts.u8PortType = 0; /* Not existant. */
831 }
832 else
833 {
834 pReply->PortFacts.u8PortType = 0x30; /* SAS Port. */
835 pReply->PortFacts.u16MaxDevices = pThis->cPorts;
836 pReply->PortFacts.u16ProtocolFlags = RT_BIT(3) | RT_BIT(0); /* SCSI initiator and LUN supported. */
837 pReply->PortFacts.u16PortSCSIID = pThis->cPorts;
838 pReply->PortFacts.u16MaxPersistentIDs = 0;
839 pReply->PortFacts.u16MaxPostedCmdBuffers = 0; /* Only applies for target mode which we dont support. */
840 pReply->PortFacts.u16MaxLANBuckets = 0; /* Only for the LAN controller. */
841 }
842 }
843 else
844 AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
845 break;
846 }
847 case MPT_MESSAGE_HDR_FUNCTION_PORT_ENABLE:
848 {
849 /*
850 * The port enable request notifies the IOC to make the port available and perform
851 * appropriate discovery on the associated link.
852 */
853 PMptPortEnableRequest pPortEnableReq = (PMptPortEnableRequest)pMessageHdr;
854
855 pReply->PortEnable.u8MessageLength = 5;
856 pReply->PortEnable.u8PortNumber = pPortEnableReq->u8PortNumber;
857 break;
858 }
859 case MPT_MESSAGE_HDR_FUNCTION_EVENT_NOTIFICATION:
860 {
861 PMptEventNotificationRequest pEventNotificationReq = (PMptEventNotificationRequest)pMessageHdr;
862
863 if (pEventNotificationReq->u8Switch)
864 pThis->fEventNotificationEnabled = true;
865 else
866 pThis->fEventNotificationEnabled = false;
867
868 pReply->EventNotification.u16EventDataLength = 1; /* 1 32bit D-Word. */
869 pReply->EventNotification.u8MessageLength = 8;
870 pReply->EventNotification.u8MessageFlags = (1 << 7);
871 pReply->EventNotification.u8AckRequired = 0;
872 pReply->EventNotification.u32Event = MPT_EVENT_EVENT_CHANGE;
873 pReply->EventNotification.u32EventContext = 0;
874 pReply->EventNotification.u32EventData = pThis->fEventNotificationEnabled ? 1 : 0;
875
876 break;
877 }
878 case MPT_MESSAGE_HDR_FUNCTION_EVENT_ACK:
879 {
880 AssertMsgFailed(("todo"));
881 break;
882 }
883 case MPT_MESSAGE_HDR_FUNCTION_CONFIG:
884 {
885 PMptConfigurationRequest pConfigurationReq = (PMptConfigurationRequest)pMessageHdr;
886
887 rc = lsilogicR3ProcessConfigurationRequest(pThis, pConfigurationReq, &pReply->Configuration);
888 AssertRC(rc);
889 break;
890 }
891 case MPT_MESSAGE_HDR_FUNCTION_FW_UPLOAD:
892 {
893 PMptFWUploadRequest pFWUploadReq = (PMptFWUploadRequest)pMessageHdr;
894
895 pReply->FWUpload.u8ImageType = pFWUploadReq->u8ImageType;
896 pReply->FWUpload.u8MessageLength = 6;
897 pReply->FWUpload.u32ActualImageSize = 0;
898 break;
899 }
900 case MPT_MESSAGE_HDR_FUNCTION_FW_DOWNLOAD:
901 {
902 //PMptFWDownloadRequest pFWDownloadReq = (PMptFWDownloadRequest)pMessageHdr;
903
904 pReply->FWDownload.u8MessageLength = 5;
905 break;
906 }
907 case MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST: /* Should be handled already. */
908 default:
909 AssertMsgFailed(("Invalid request function %#x\n", pMessageHdr->u8Function));
910 }
911
912 /* Copy common bits from request message frame to reply. */
913 pReply->Header.u8Function = pMessageHdr->u8Function;
914 pReply->Header.u32MessageContext = pMessageHdr->u32MessageContext;
915
916 lsilogicFinishAddressReply(pThis, pReply, fForceReplyPostFifo);
917 return rc;
918}
919#endif /* IN_RING3 */
920
921/**
922 * Writes a value to a register at a given offset.
923 *
924 * @returns VBox status code.
925 * @param pThis Pointer to the LsiLogic device state.
926 * @param offReg Offset of the register to write.
927 * @param u32 The value being written.
928 */
929static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t offReg, uint32_t u32)
930{
931 LogFlowFunc(("pThis=%#p offReg=%#x u32=%#x\n", pThis, offReg, u32));
932 switch (offReg)
933 {
934 case LSILOGIC_REG_REPLY_QUEUE:
935 {
936 /* Add the entry to the reply free queue. */
937 ASMAtomicWriteU32(&pThis->CTX_SUFF(pReplyFreeQueueBase)[pThis->uReplyFreeQueueNextEntryFreeWrite], u32);
938 pThis->uReplyFreeQueueNextEntryFreeWrite++;
939 pThis->uReplyFreeQueueNextEntryFreeWrite %= pThis->cReplyQueueEntries;
940 break;
941 }
942 case LSILOGIC_REG_REQUEST_QUEUE:
943 {
944 uint32_t uNextWrite = ASMAtomicReadU32(&pThis->uRequestQueueNextEntryFreeWrite);
945
946 ASMAtomicWriteU32(&pThis->CTX_SUFF(pRequestQueueBase)[uNextWrite], u32);
947
948 /*
949 * Don't update the value in place. It can happen that we get preempted
950 * after the increment but before the modulo.
951 * Another EMT will read the wrong value when processing the queues
952 * and hang in an endless loop creating thousands of requests.
953 */
954 uNextWrite++;
955 uNextWrite %= pThis->cRequestQueueEntries;
956 ASMAtomicWriteU32(&pThis->uRequestQueueNextEntryFreeWrite, uNextWrite);
957
958 /* Send notification to R3 if there is not one send already. */
959 if (!ASMAtomicXchgBool(&pThis->fNotificationSend, true))
960 {
961 PPDMQUEUEITEMCORE pNotificationItem = PDMQueueAlloc(pThis->CTX_SUFF(pNotificationQueue));
962 AssertPtr(pNotificationItem);
963
964 PDMQueueInsert(pThis->CTX_SUFF(pNotificationQueue), pNotificationItem);
965 }
966 break;
967 }
968 case LSILOGIC_REG_DOORBELL:
969 {
970 /*
971 * When the guest writes to this register a real device would set the
972 * doorbell status bit in the interrupt status register to indicate that the IOP
973 * has still to process the message.
974 * The guest needs to wait with posting new messages here until the bit is cleared.
975 * Because the guest is not continuing execution while we are here we can skip this.
976 */
977 if (pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_NOT_IN_USE)
978 {
979 uint32_t uFunction = LSILOGIC_REG_DOORBELL_GET_FUNCTION(u32);
980
981 switch (uFunction)
982 {
983 case LSILOGIC_DOORBELL_FUNCTION_IO_UNIT_RESET:
984 case LSILOGIC_DOORBELL_FUNCTION_IOC_MSG_UNIT_RESET:
985 {
986 /*
987 * The I/O unit reset does much more on real hardware like
988 * reloading the firmware, nothing we need to do here,
989 * so this is like the IOC message unit reset.
990 */
991 pThis->enmState = LSILOGICSTATE_RESET;
992
993 /* Reset interrupt status. */
994 pThis->uInterruptStatus = 0;
995 lsilogicUpdateInterrupt(pThis);
996
997 /* Reset the queues. */
998 pThis->uReplyFreeQueueNextEntryFreeWrite = 0;
999 pThis->uReplyFreeQueueNextAddressRead = 0;
1000 pThis->uReplyPostQueueNextEntryFreeWrite = 0;
1001 pThis->uReplyPostQueueNextAddressRead = 0;
1002 pThis->uRequestQueueNextEntryFreeWrite = 0;
1003 pThis->uRequestQueueNextAddressRead = 0;
1004
1005 /* Only the IOC message unit reset transisionts to the ready state. */
1006 if (uFunction == LSILOGIC_DOORBELL_FUNCTION_IOC_MSG_UNIT_RESET)
1007 pThis->enmState = LSILOGICSTATE_READY;
1008 break;
1009 }
1010 case LSILOGIC_DOORBELL_FUNCTION_HANDSHAKE:
1011 {
1012 pThis->cMessage = LSILOGIC_REG_DOORBELL_GET_SIZE(u32);
1013 pThis->iMessage = 0;
1014 AssertMsg(pThis->cMessage <= RT_ELEMENTS(pThis->aMessage),
1015 ("Message doesn't fit into the buffer, cMessage=%u", pThis->cMessage));
1016 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_FN_HANDSHAKE;
1017 /* Update the interrupt status to notify the guest that a doorbell function was started. */
1018 lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1019 break;
1020 }
1021 case LSILOGIC_DOORBELL_FUNCTION_REPLY_FRAME_REMOVAL:
1022 {
1023 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_LOW;
1024 /* Update the interrupt status to notify the guest that a doorbell function was started. */
1025 lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1026 break;
1027 }
1028 default:
1029 AssertMsgFailed(("Unknown function %u to perform\n", uFunction));
1030 }
1031 }
1032 else if (pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_FN_HANDSHAKE)
1033 {
1034 /*
1035 * We are already performing a doorbell function.
1036 * Get the remaining parameters.
1037 */
1038 AssertMsg(pThis->iMessage < RT_ELEMENTS(pThis->aMessage), ("Message is too big to fit into the buffer\n"));
1039 /*
1040 * If the last byte of the message is written, force a switch to R3 because some requests might force
1041 * a reply through the FIFO which cannot be handled in GC or R0.
1042 */
1043#ifndef IN_RING3
1044 if (pThis->iMessage == pThis->cMessage - 1)
1045 return VINF_IOM_R3_MMIO_WRITE;
1046#endif
1047 pThis->aMessage[pThis->iMessage++] = u32;
1048#ifdef IN_RING3
1049 if (pThis->iMessage == pThis->cMessage)
1050 {
1051 int rc = lsilogicR3ProcessMessageRequest(pThis, (PMptMessageHdr)pThis->aMessage, &pThis->ReplyBuffer);
1052 AssertRC(rc);
1053 }
1054#endif
1055 }
1056 break;
1057 }
1058 case LSILOGIC_REG_HOST_INTR_STATUS:
1059 {
1060 /*
1061 * Clear the bits the guest wants except the system doorbell interrupt and the IO controller
1062 * status bit.
1063 * The former bit is always cleared no matter what the guest writes to the register and
1064 * the latter one is read only.
1065 */
1066 ASMAtomicAndU32(&pThis->uInterruptStatus, ~LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1067
1068 /*
1069 * Check if there is still a doorbell function in progress. Set the
1070 * system doorbell interrupt bit again if it is.
1071 * We do not use lsilogicSetInterrupt here because the interrupt status
1072 * is updated afterwards anyway.
1073 */
1074 if ( (pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_FN_HANDSHAKE)
1075 && (pThis->cMessage == pThis->iMessage))
1076 {
1077 if (pThis->uNextReplyEntryRead == pThis->cReplySize)
1078 {
1079 /* Reply finished. Reset doorbell in progress status. */
1080 Log(("%s: Doorbell function finished\n", __FUNCTION__));
1081 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
1082 }
1083 ASMAtomicOrU32(&pThis->uInterruptStatus, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1084 }
1085 else if ( pThis->enmDoorbellState != LSILOGICDOORBELLSTATE_NOT_IN_USE
1086 && pThis->enmDoorbellState != LSILOGICDOORBELLSTATE_FN_HANDSHAKE)
1087 {
1088 /* Reply frame removal, check whether the reply free queue is empty. */
1089 if ( pThis->uReplyFreeQueueNextAddressRead == pThis->uReplyFreeQueueNextEntryFreeWrite
1090 && pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW)
1091 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
1092 ASMAtomicOrU32(&pThis->uInterruptStatus, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1093 }
1094
1095 lsilogicUpdateInterrupt(pThis);
1096 break;
1097 }
1098 case LSILOGIC_REG_HOST_INTR_MASK:
1099 {
1100 ASMAtomicWriteU32(&pThis->uInterruptMask, u32 & LSILOGIC_REG_HOST_INTR_MASK_W_MASK);
1101 lsilogicUpdateInterrupt(pThis);
1102 break;
1103 }
1104 case LSILOGIC_REG_WRITE_SEQUENCE:
1105 {
1106 if (pThis->fDiagnosticEnabled)
1107 {
1108 /* Any value will cause a reset and disabling access. */
1109 pThis->fDiagnosticEnabled = false;
1110 pThis->iDiagnosticAccess = 0;
1111 }
1112 else if ((u32 & 0xf) == g_lsilogicDiagnosticAccess[pThis->iDiagnosticAccess])
1113 {
1114 pThis->iDiagnosticAccess++;
1115 if (pThis->iDiagnosticAccess == RT_ELEMENTS(g_lsilogicDiagnosticAccess))
1116 {
1117 /*
1118 * Key sequence successfully written. Enable access to diagnostic
1119 * memory and register.
1120 */
1121 pThis->fDiagnosticEnabled = true;
1122 }
1123 }
1124 else
1125 {
1126 /* Wrong value written - reset to beginning. */
1127 pThis->iDiagnosticAccess = 0;
1128 }
1129 break;
1130 }
1131 case LSILOGIC_REG_HOST_DIAGNOSTIC:
1132 {
1133#ifndef IN_RING3
1134 return VINF_IOM_R3_IOPORT_WRITE;
1135#else
1136 if (u32 & LSILOGIC_REG_HOST_DIAGNOSTIC_RESET_ADAPTER)
1137 {
1138 lsilogicR3HardReset(pThis);
1139 }
1140 break;
1141#endif
1142 }
1143 default: /* Ignore. */
1144 {
1145 break;
1146 }
1147 }
1148 return VINF_SUCCESS;
1149}
1150
1151/**
1152 * Reads the content of a register at a given offset.
1153 *
1154 * @returns VBox status code.
1155 * @param pThis Pointer to the LsiLogic device state.
1156 * @param offReg Offset of the register to read.
1157 * @param pu32 Where to store the content of the register.
1158 */
1159static int lsilogicRegisterRead(PLSILOGICSCSI pThis, uint32_t offReg, uint32_t *pu32)
1160{
1161 int rc = VINF_SUCCESS;
1162 uint32_t u32 = 0;
1163 Assert(!(offReg & 3));
1164
1165 /* Align to a 4 byte offset. */
1166 switch (offReg)
1167 {
1168 case LSILOGIC_REG_REPLY_QUEUE:
1169 {
1170 rc = PDMCritSectEnter(&pThis->ReplyPostQueueCritSect, VINF_IOM_R3_MMIO_READ);
1171 if (rc != VINF_SUCCESS)
1172 break;
1173
1174 uint32_t idxReplyPostQueueWrite = ASMAtomicUoReadU32(&pThis->uReplyPostQueueNextEntryFreeWrite);
1175 uint32_t idxReplyPostQueueRead = ASMAtomicUoReadU32(&pThis->uReplyPostQueueNextAddressRead);
1176
1177 if (idxReplyPostQueueWrite != idxReplyPostQueueRead)
1178 {
1179 u32 = pThis->CTX_SUFF(pReplyPostQueueBase)[idxReplyPostQueueRead];
1180 idxReplyPostQueueRead++;
1181 idxReplyPostQueueRead %= pThis->cReplyQueueEntries;
1182 ASMAtomicWriteU32(&pThis->uReplyPostQueueNextAddressRead, idxReplyPostQueueRead);
1183 }
1184 else
1185 {
1186 /* The reply post queue is empty. Reset interrupt. */
1187 u32 = UINT32_C(0xffffffff);
1188 lsilogicClearInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR);
1189 }
1190 PDMCritSectLeave(&pThis->ReplyPostQueueCritSect);
1191
1192 Log(("%s: Returning address %#x\n", __FUNCTION__, u32));
1193 break;
1194 }
1195 case LSILOGIC_REG_DOORBELL:
1196 {
1197 u32 = LSILOGIC_REG_DOORBELL_SET_STATE(pThis->enmState);
1198 u32 |= LSILOGIC_REG_DOORBELL_SET_USED(pThis->enmDoorbellState);
1199 u32 |= LSILOGIC_REG_DOORBELL_SET_WHOINIT(pThis->enmWhoInit);
1200 /*
1201 * If there is a doorbell function in progress we pass the return value
1202 * instead of the status code. We transfer 16bit of the reply
1203 * during one read.
1204 */
1205 switch (pThis->enmDoorbellState)
1206 {
1207 case LSILOGICDOORBELLSTATE_NOT_IN_USE:
1208 /* We return the status code of the I/O controller. */
1209 u32 |= pThis->u16IOCFaultCode;
1210 break;
1211 case LSILOGICDOORBELLSTATE_FN_HANDSHAKE:
1212 /* Return next 16bit value. */
1213 u32 |= pThis->ReplyBuffer.au16Reply[pThis->uNextReplyEntryRead++];
1214 lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1215 break;
1216 case LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_LOW:
1217 {
1218 uint32_t cReplyFrames = lsilogicReplyFreeQueueGetFrameCount(pThis);
1219
1220 u32 |= cReplyFrames & UINT32_C(0xffff);
1221 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_HIGH;
1222 lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1223 break;
1224 }
1225 case LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_HIGH:
1226 {
1227 uint32_t cReplyFrames = lsilogicReplyFreeQueueGetFrameCount(pThis);
1228
1229 u32 |= cReplyFrames >> 16;
1230 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW;
1231 lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1232 break;
1233 }
1234 case LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW:
1235 if (pThis->uReplyFreeQueueNextEntryFreeWrite != pThis->uReplyFreeQueueNextAddressRead)
1236 {
1237 u32 |= pThis->CTX_SUFF(pReplyFreeQueueBase)[pThis->uReplyFreeQueueNextAddressRead] & UINT32_C(0xffff);
1238 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_HIGH;
1239 lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1240 }
1241 break;
1242 case LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_HIGH:
1243 u32 |= pThis->CTX_SUFF(pReplyFreeQueueBase)[pThis->uReplyFreeQueueNextAddressRead] >> 16;
1244 pThis->uReplyFreeQueueNextAddressRead++;
1245 pThis->uReplyFreeQueueNextAddressRead %= pThis->cReplyQueueEntries;
1246 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW;
1247 lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
1248 break;
1249 default:
1250 AssertMsgFailed(("Invalid doorbell state %d\n", pThis->enmDoorbellState));
1251 }
1252
1253 break;
1254 }
1255 case LSILOGIC_REG_HOST_INTR_STATUS:
1256 {
1257 u32 = ASMAtomicReadU32(&pThis->uInterruptStatus);
1258 break;
1259 }
1260 case LSILOGIC_REG_HOST_INTR_MASK:
1261 {
1262 u32 = ASMAtomicReadU32(&pThis->uInterruptMask);
1263 break;
1264 }
1265 case LSILOGIC_REG_HOST_DIAGNOSTIC:
1266 {
1267 if (pThis->fDiagnosticEnabled)
1268 u32 = LSILOGIC_REG_HOST_DIAGNOSTIC_DRWE;
1269 else
1270 u32 = 0;
1271 break;
1272 }
1273 case LSILOGIC_REG_TEST_BASE_ADDRESS: /* The spec doesn't say anything about these registers, so we just ignore them */
1274 case LSILOGIC_REG_DIAG_RW_DATA:
1275 case LSILOGIC_REG_DIAG_RW_ADDRESS:
1276 default: /* Ignore. */
1277 {
1278 /** @todo LSILOGIC_REG_DIAG_* should return all F's when accessed by MMIO. We
1279 * return 0. Likely to apply to undefined offsets as well. */
1280 break;
1281 }
1282 }
1283
1284 *pu32 = u32;
1285 LogFlowFunc(("pThis=%#p offReg=%#x u32=%#x\n", pThis, offReg, u32));
1286 return rc;
1287}
1288
1289/**
1290 * @callback_method_impl{FNIOMIOPORTOUT}
1291 */
1292PDMBOTHCBDECL(int) lsilogicIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
1293{
1294 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
1295 uint32_t offReg = Port - pThis->IOPortBase;
1296 int rc;
1297
1298 if (!(offReg & 3))
1299 {
1300 rc = lsilogicRegisterWrite(pThis, offReg, u32);
1301 if (rc == VINF_IOM_R3_MMIO_WRITE)
1302 rc = VINF_IOM_R3_IOPORT_WRITE;
1303 }
1304 else
1305 {
1306 Log(("lsilogicIOPortWrite: Ignoring misaligned write - offReg=%#x u32=%#x cb=%#x\n", offReg, u32, cb));
1307 rc = VINF_SUCCESS;
1308 }
1309
1310 return rc;
1311}
1312
1313/**
1314 * @callback_method_impl{FNIOMIOPORTIN}
1315 */
1316PDMBOTHCBDECL(int) lsilogicIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
1317{
1318 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
1319 uint32_t offReg = Port - pThis->IOPortBase;
1320
1321 int rc = lsilogicRegisterRead(pThis, offReg & ~(uint32_t)3, pu32);
1322 if (rc == VINF_IOM_R3_MMIO_READ)
1323 rc = VINF_IOM_R3_IOPORT_READ;
1324
1325 return rc;
1326}
1327
1328/**
1329 * @callback_method_impl{FNIOMMMIOWRITE}
1330 */
1331PDMBOTHCBDECL(int) lsilogicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
1332{
1333 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
1334 uint32_t offReg = GCPhysAddr - pThis->GCPhysMMIOBase;
1335 uint32_t u32;
1336 int rc;
1337
1338 /* See comments in lsilogicR3Map regarding size and alignment. */
1339 if (cb == 4)
1340 u32 = *(uint32_t const *)pv;
1341 else
1342 {
1343 if (cb > 4)
1344 u32 = *(uint32_t const *)pv;
1345 else if (cb >= 2)
1346 u32 = *(uint16_t const *)pv;
1347 else
1348 u32 = *(uint8_t const *)pv;
1349 Log(("lsilogicMMIOWrite: Non-DWORD write access - offReg=%#x u32=%#x cb=%#x\n", offReg, u32, cb));
1350 }
1351
1352 if (!(offReg & 3))
1353 {
1354 rc = lsilogicRegisterWrite(pThis, offReg, u32);
1355 if (rc == VINF_IOM_R3_MMIO_WRITE)
1356 rc = VINF_IOM_R3_IOPORT_WRITE;
1357 }
1358 else
1359 {
1360 Log(("lsilogicIOPortWrite: Ignoring misaligned write - offReg=%#x u32=%#x cb=%#x\n", offReg, u32, cb));
1361 rc = VINF_SUCCESS;
1362 }
1363 return rc;
1364}
1365
1366/**
1367 * @callback_method_impl{FNIOMMMIOREAD}
1368 */
1369PDMBOTHCBDECL(int) lsilogicMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
1370{
1371 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
1372 uint32_t offReg = GCPhysAddr - pThis->GCPhysMMIOBase;
1373 Assert(!(offReg & 3)); Assert(cb == 4);
1374
1375 return lsilogicRegisterRead(pThis, offReg, (uint32_t *)pv);
1376}
1377
1378PDMBOTHCBDECL(int) lsilogicDiagnosticWrite(PPDMDEVINS pDevIns, void *pvUser,
1379 RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
1380{
1381 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
1382
1383 LogFlowFunc(("pThis=%#p GCPhysAddr=%RGp pv=%#p{%.*Rhxs} cb=%u\n", pThis, GCPhysAddr, pv, cb, pv, cb));
1384
1385 return VINF_SUCCESS;
1386}
1387
1388PDMBOTHCBDECL(int) lsilogicDiagnosticRead(PPDMDEVINS pDevIns, void *pvUser,
1389 RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
1390{
1391 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
1392
1393 LogFlowFunc(("pThis=%#p GCPhysAddr=%RGp pv=%#p{%.*Rhxs} cb=%u\n", pThis, GCPhysAddr, pv, cb, pv, cb));
1394
1395 return VINF_SUCCESS;
1396}
1397
1398#ifdef IN_RING3
1399
1400# ifdef LOG_ENABLED
1401/**
1402 * Dump an SG entry.
1403 *
1404 * @returns nothing.
1405 * @param pSGEntry Pointer to the SG entry to dump
1406 */
1407static void lsilogicDumpSGEntry(PMptSGEntryUnion pSGEntry)
1408{
1409 if (LogIsEnabled())
1410 {
1411 switch (pSGEntry->Simple32.u2ElementType)
1412 {
1413 case MPTSGENTRYTYPE_SIMPLE:
1414 {
1415 Log(("%s: Dumping info for SIMPLE SG entry:\n", __FUNCTION__));
1416 Log(("%s: u24Length=%u\n", __FUNCTION__, pSGEntry->Simple32.u24Length));
1417 Log(("%s: fEndOfList=%d\n", __FUNCTION__, pSGEntry->Simple32.fEndOfList));
1418 Log(("%s: f64BitAddress=%d\n", __FUNCTION__, pSGEntry->Simple32.f64BitAddress));
1419 Log(("%s: fBufferContainsData=%d\n", __FUNCTION__, pSGEntry->Simple32.fBufferContainsData));
1420 Log(("%s: fLocalAddress=%d\n", __FUNCTION__, pSGEntry->Simple32.fLocalAddress));
1421 Log(("%s: fEndOfBuffer=%d\n", __FUNCTION__, pSGEntry->Simple32.fEndOfBuffer));
1422 Log(("%s: fLastElement=%d\n", __FUNCTION__, pSGEntry->Simple32.fLastElement));
1423 Log(("%s: u32DataBufferAddressLow=%u\n", __FUNCTION__, pSGEntry->Simple32.u32DataBufferAddressLow));
1424 if (pSGEntry->Simple32.f64BitAddress)
1425 {
1426 Log(("%s: u32DataBufferAddressHigh=%u\n", __FUNCTION__, pSGEntry->Simple64.u32DataBufferAddressHigh));
1427 Log(("%s: GCDataBufferAddress=%RGp\n", __FUNCTION__,
1428 ((uint64_t)pSGEntry->Simple64.u32DataBufferAddressHigh << 32)
1429 | pSGEntry->Simple64.u32DataBufferAddressLow));
1430 }
1431 else
1432 Log(("%s: GCDataBufferAddress=%RGp\n", __FUNCTION__, pSGEntry->Simple32.u32DataBufferAddressLow));
1433
1434 break;
1435 }
1436 case MPTSGENTRYTYPE_CHAIN:
1437 {
1438 Log(("%s: Dumping info for CHAIN SG entry:\n", __FUNCTION__));
1439 Log(("%s: u16Length=%u\n", __FUNCTION__, pSGEntry->Chain.u16Length));
1440 Log(("%s: u8NExtChainOffset=%d\n", __FUNCTION__, pSGEntry->Chain.u8NextChainOffset));
1441 Log(("%s: f64BitAddress=%d\n", __FUNCTION__, pSGEntry->Chain.f64BitAddress));
1442 Log(("%s: fLocalAddress=%d\n", __FUNCTION__, pSGEntry->Chain.fLocalAddress));
1443 Log(("%s: u32SegmentAddressLow=%u\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressLow));
1444 Log(("%s: u32SegmentAddressHigh=%u\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressHigh));
1445 if (pSGEntry->Chain.f64BitAddress)
1446 Log(("%s: GCSegmentAddress=%RGp\n", __FUNCTION__,
1447 ((uint64_t)pSGEntry->Chain.u32SegmentAddressHigh << 32) | pSGEntry->Chain.u32SegmentAddressLow));
1448 else
1449 Log(("%s: GCSegmentAddress=%RGp\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressLow));
1450 break;
1451 }
1452 }
1453 }
1454}
1455# endif /* LOG_ENABLED */
1456
1457static void lsilogicSgBufWalker(PPDMDEVINS pDevIns, PLSILOGICREQ pLsiReq, size_t cbCopy,
1458 PFNLSILOGICIOBUFCOPY pfnIoBufCopy)
1459{
1460 bool fEndOfList = false;
1461 RTGCPHYS GCPhysSgEntryNext = pLsiReq->GCPhysSgStart;
1462 RTGCPHYS GCPhysSegmentStart = pLsiReq->GCPhysSgStart;
1463 uint32_t cChainOffsetNext = pLsiReq->cChainOffset;
1464 uint8_t *pbBuf = (uint8_t *)pLsiReq->SegIoBuf.pvSeg;
1465
1466 /* Go through the list until we reach the end. */
1467 while ( !fEndOfList
1468 && cbCopy)
1469 {
1470 bool fEndOfSegment = false;
1471
1472 while ( !fEndOfSegment
1473 && cbCopy)
1474 {
1475 MptSGEntryUnion SGEntry;
1476
1477 Log(("%s: Reading SG entry from %RGp\n", __FUNCTION__, GCPhysSgEntryNext));
1478
1479 /* Read the entry. */
1480 PDMDevHlpPhysRead(pDevIns, GCPhysSgEntryNext, &SGEntry, sizeof(MptSGEntryUnion));
1481
1482# ifdef LOG_ENABLED
1483 lsilogicDumpSGEntry(&SGEntry);
1484# endif
1485
1486 AssertMsg(SGEntry.Simple32.u2ElementType == MPTSGENTRYTYPE_SIMPLE, ("Invalid SG entry type\n"));
1487
1488 /* Check if this is a zero element and abort. */
1489 if ( !SGEntry.Simple32.u24Length
1490 && SGEntry.Simple32.fEndOfList
1491 && SGEntry.Simple32.fEndOfBuffer)
1492 return;
1493
1494 uint32_t cbCopyThis = SGEntry.Simple32.u24Length;
1495 RTGCPHYS GCPhysAddrDataBuffer = SGEntry.Simple32.u32DataBufferAddressLow;
1496
1497 if (SGEntry.Simple32.f64BitAddress)
1498 {
1499 GCPhysAddrDataBuffer |= ((uint64_t)SGEntry.Simple64.u32DataBufferAddressHigh) << 32;
1500 GCPhysSgEntryNext += sizeof(MptSGEntrySimple64);
1501 }
1502 else
1503 GCPhysSgEntryNext += sizeof(MptSGEntrySimple32);
1504
1505
1506 pfnIoBufCopy(pDevIns, GCPhysAddrDataBuffer, pbBuf, cbCopyThis);
1507 pbBuf += cbCopyThis;
1508 cbCopyThis -= cbCopyThis;
1509
1510 /* Check if we reached the end of the list. */
1511 if (SGEntry.Simple32.fEndOfList)
1512 {
1513 /* We finished. */
1514 fEndOfSegment = true;
1515 fEndOfList = true;
1516 }
1517 else if (SGEntry.Simple32.fLastElement)
1518 fEndOfSegment = true;
1519 } /* while (!fEndOfSegment) */
1520
1521 /* Get next chain element. */
1522 if (cChainOffsetNext)
1523 {
1524 MptSGEntryChain SGEntryChain;
1525
1526 PDMDevHlpPhysRead(pDevIns, GCPhysSegmentStart + cChainOffsetNext, &SGEntryChain, sizeof(MptSGEntryChain));
1527
1528 AssertMsg(SGEntryChain.u2ElementType == MPTSGENTRYTYPE_CHAIN, ("Invalid SG entry type\n"));
1529
1530 /* Set the next address now. */
1531 GCPhysSgEntryNext = SGEntryChain.u32SegmentAddressLow;
1532 if (SGEntryChain.f64BitAddress)
1533 GCPhysSgEntryNext |= ((uint64_t)SGEntryChain.u32SegmentAddressHigh) << 32;
1534
1535 GCPhysSegmentStart = GCPhysSgEntryNext;
1536 cChainOffsetNext = SGEntryChain.u8NextChainOffset * sizeof(uint32_t);
1537 }
1538 } /* while (!fEndOfList) */
1539}
1540
1541static DECLCALLBACK(void) lsilogicCopyFromGuest(PPDMDEVINS pDevIns, RTGCPHYS GCPhysIoBuf,
1542 void *pvBuf, size_t cbCopy)
1543{
1544 PDMDevHlpPhysRead(pDevIns, GCPhysIoBuf, pvBuf, cbCopy);
1545}
1546
1547static DECLCALLBACK(void) lsilogicCopyToGuest(PPDMDEVINS pDevIns, RTGCPHYS GCPhysIoBuf,
1548 void *pvBuf, size_t cbCopy)
1549{
1550 PDMDevHlpPCIPhysWrite(pDevIns, GCPhysIoBuf, pvBuf, cbCopy);
1551}
1552
1553/**
1554 * Copy from a guest S/G buffer to the I/O buffer.
1555 *
1556 * @returns nothing.
1557 * @param pDevIns Device instance data.
1558 * @param pLsiReq Request data.
1559 * @param cbCopy How much to copy over.
1560 */
1561DECLINLINE(void) lsilogicCopyFromSgBuf(PPDMDEVINS pDevIns, PLSILOGICREQ pLsiReq, size_t cbCopy)
1562{
1563 lsilogicSgBufWalker(pDevIns, pLsiReq, cbCopy, lsilogicCopyFromGuest);
1564}
1565
1566/**
1567 * Copy from an I/O buffer to the guest S/G buffer.
1568 *
1569 * @returns nothing.
1570 * @param pDevIns Device instance data.
1571 * @param pLsiReq Request data.
1572 * @param cbCopy How much to copy over.
1573 */
1574DECLINLINE(void) lsilogicCopyToSgBuf(PPDMDEVINS pDevIns, PLSILOGICREQ pLsiReq, size_t cbCopy)
1575{
1576 lsilogicSgBufWalker(pDevIns, pLsiReq, cbCopy, lsilogicCopyToGuest);
1577}
1578
1579/**
1580 * Allocates memory for the given request using already allocated memory if possible.
1581 *
1582 * @returns Pointer to the memory or NULL on failure
1583 * @param pLsiReq The request to allocate memory for.
1584 * @param cb The amount of memory to allocate.
1585 */
1586static void *lsilogicReqMemAlloc(PLSILOGICREQ pLsiReq, size_t cb)
1587{
1588 if (pLsiReq->cbAlloc > cb)
1589 pLsiReq->cAllocTooMuch++;
1590 else if (pLsiReq->cbAlloc < cb)
1591 {
1592 if (pLsiReq->cbAlloc)
1593 RTMemPageFree(pLsiReq->pvAlloc, pLsiReq->cbAlloc);
1594
1595 pLsiReq->cbAlloc = RT_ALIGN_Z(cb, _4K);
1596 pLsiReq->pvAlloc = RTMemPageAlloc(pLsiReq->cbAlloc);
1597 pLsiReq->cAllocTooMuch = 0;
1598 if (RT_UNLIKELY(!pLsiReq->pvAlloc))
1599 pLsiReq->cbAlloc = 0;
1600 }
1601
1602 return pLsiReq->pvAlloc;
1603}
1604
1605/**
1606 * Frees memory allocated for the given request.
1607 *
1608 * @returns nothing.
1609 * @param pLsiReq The request.
1610 */
1611static void lsilogicReqMemFree(PLSILOGICREQ pLsiReq)
1612{
1613 if (pLsiReq->cAllocTooMuch >= LSILOGIC_MAX_ALLOC_TOO_MUCH)
1614 {
1615 RTMemPageFree(pLsiReq->pvAlloc, pLsiReq->cbAlloc);
1616 pLsiReq->cbAlloc = 0;
1617 pLsiReq->cAllocTooMuch = 0;
1618 }
1619}
1620
1621/**
1622 * Allocate I/O memory and copies the guest buffer for writes.
1623 *
1624 * @returns VBox status code.
1625 * @param pDevIns The device instance.
1626 * @param pLsiReq The request state.
1627 * @param cbTransfer Amount of bytes to allocate.
1628 */
1629static int lsilogicIoBufAllocate(PPDMDEVINS pDevIns, PLSILOGICREQ pLsiReq,
1630 size_t cbTransfer)
1631{
1632 uint8_t uTxDir = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_GET(pLsiReq->GuestRequest.SCSIIO.u32Control);
1633
1634 AssertMsg( uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE
1635 || uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ,
1636 ("Allocating I/O memory for a non I/O request is not allowed\n"));
1637
1638 pLsiReq->SegIoBuf.pvSeg = lsilogicReqMemAlloc(pLsiReq, cbTransfer);
1639 if (!pLsiReq->SegIoBuf.pvSeg)
1640 return VERR_NO_MEMORY;
1641
1642 pLsiReq->SegIoBuf.cbSeg = cbTransfer;
1643 if (uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE)
1644 lsilogicCopyFromSgBuf(pDevIns, pLsiReq, cbTransfer);
1645
1646 return VINF_SUCCESS;
1647}
1648
1649/**
1650 * Frees the I/O memory of the given request and updates the guest buffer if necessary.
1651 *
1652 * @returns nothing.
1653 * @param pDevIns The device instance.
1654 * @param pLsiReq The request state.
1655 * @param fCopyToGuest Flag whether to update the guest buffer if necessary.
1656 * Nothing is copied if false even if the request was a read.
1657 */
1658static void lsilogicIoBufFree(PPDMDEVINS pDevIns, PLSILOGICREQ pLsiReq,
1659 bool fCopyToGuest)
1660{
1661 uint8_t uTxDir = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_GET(pLsiReq->GuestRequest.SCSIIO.u32Control);
1662
1663 AssertMsg( uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE
1664 || uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ,
1665 ("Allocating I/O memory for a non I/O request is not allowed\n"));
1666
1667 if ( uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ
1668 && fCopyToGuest)
1669 lsilogicCopyToSgBuf(pDevIns, pLsiReq, pLsiReq->SegIoBuf.cbSeg);
1670
1671 lsilogicReqMemFree(pLsiReq);
1672 pLsiReq->SegIoBuf.pvSeg = NULL;
1673 pLsiReq->SegIoBuf.cbSeg = 0;
1674}
1675
1676# ifdef LOG_ENABLED
1677static void lsilogicR3DumpSCSIIORequest(PMptSCSIIORequest pSCSIIORequest)
1678{
1679 if (LogIsEnabled())
1680 {
1681 Log(("%s: u8TargetID=%d\n", __FUNCTION__, pSCSIIORequest->u8TargetID));
1682 Log(("%s: u8Bus=%d\n", __FUNCTION__, pSCSIIORequest->u8Bus));
1683 Log(("%s: u8ChainOffset=%d\n", __FUNCTION__, pSCSIIORequest->u8ChainOffset));
1684 Log(("%s: u8Function=%d\n", __FUNCTION__, pSCSIIORequest->u8Function));
1685 Log(("%s: u8CDBLength=%d\n", __FUNCTION__, pSCSIIORequest->u8CDBLength));
1686 Log(("%s: u8SenseBufferLength=%d\n", __FUNCTION__, pSCSIIORequest->u8SenseBufferLength));
1687 Log(("%s: u8MessageFlags=%d\n", __FUNCTION__, pSCSIIORequest->u8MessageFlags));
1688 Log(("%s: u32MessageContext=%#x\n", __FUNCTION__, pSCSIIORequest->u32MessageContext));
1689 for (unsigned i = 0; i < RT_ELEMENTS(pSCSIIORequest->au8LUN); i++)
1690 Log(("%s: u8LUN[%d]=%d\n", __FUNCTION__, i, pSCSIIORequest->au8LUN[i]));
1691 Log(("%s: u32Control=%#x\n", __FUNCTION__, pSCSIIORequest->u32Control));
1692 for (unsigned i = 0; i < RT_ELEMENTS(pSCSIIORequest->au8CDB); i++)
1693 Log(("%s: u8CDB[%d]=%d\n", __FUNCTION__, i, pSCSIIORequest->au8CDB[i]));
1694 Log(("%s: u32DataLength=%#x\n", __FUNCTION__, pSCSIIORequest->u32DataLength));
1695 Log(("%s: u32SenseBufferLowAddress=%#x\n", __FUNCTION__, pSCSIIORequest->u32SenseBufferLowAddress));
1696 }
1697}
1698# endif
1699
1700static void lsilogicR3WarningDiskFull(PPDMDEVINS pDevIns)
1701{
1702 int rc;
1703 LogRel(("LsiLogic#%d: Host disk full\n", pDevIns->iInstance));
1704 rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevLsiLogic_DISKFULL",
1705 N_("Host system reported disk full. VM execution is suspended. You can resume after freeing some space"));
1706 AssertRC(rc);
1707}
1708
1709static void lsilogicR3WarningFileTooBig(PPDMDEVINS pDevIns)
1710{
1711 int rc;
1712 LogRel(("LsiLogic#%d: File too big\n", pDevIns->iInstance));
1713 rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevLsiLogic_FILETOOBIG",
1714 N_("Host system reported that the file size limit of the host file system has been exceeded. VM execution is suspended. You need to move your virtual hard disk to a filesystem which allows bigger files"));
1715 AssertRC(rc);
1716}
1717
1718static void lsilogicR3WarningISCSI(PPDMDEVINS pDevIns)
1719{
1720 int rc;
1721 LogRel(("LsiLogic#%d: iSCSI target unavailable\n", pDevIns->iInstance));
1722 rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevLsiLogic_ISCSIDOWN",
1723 N_("The iSCSI target has stopped responding. VM execution is suspended. You can resume when it is available again"));
1724 AssertRC(rc);
1725}
1726
1727static void lsilogicR3WarningUnknown(PPDMDEVINS pDevIns, int rc)
1728{
1729 int rc2;
1730 LogRel(("LsiLogic#%d: Unknown but recoverable error has occurred (rc=%Rrc)\n", pDevIns->iInstance, rc));
1731 rc2 = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevLsiLogic_UNKNOWN",
1732 N_("An unknown but recoverable I/O error has occurred (rc=%Rrc). VM execution is suspended. You can resume when the error is fixed"), rc);
1733 AssertRC(rc2);
1734}
1735
1736static void lsilogicR3RedoSetWarning(PLSILOGICSCSI pThis, int rc)
1737{
1738 if (rc == VERR_DISK_FULL)
1739 lsilogicR3WarningDiskFull(pThis->CTX_SUFF(pDevIns));
1740 else if (rc == VERR_FILE_TOO_BIG)
1741 lsilogicR3WarningFileTooBig(pThis->CTX_SUFF(pDevIns));
1742 else if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
1743 {
1744 /* iSCSI connection abort (first error) or failure to reestablish
1745 * connection (second error). Pause VM. On resume we'll retry. */
1746 lsilogicR3WarningISCSI(pThis->CTX_SUFF(pDevIns));
1747 }
1748 else
1749 lsilogicR3WarningUnknown(pThis->CTX_SUFF(pDevIns), rc);
1750}
1751
1752/**
1753 * Processes a SCSI I/O request by setting up the request
1754 * and sending it to the underlying SCSI driver.
1755 * Steps needed to complete request are done in the
1756 * callback called by the driver below upon completion of
1757 * the request.
1758 *
1759 * @returns VBox status code.
1760 * @param pThis Pointer to the LsiLogic device state.
1761 * @param pLsiReq Pointer to the task state data.
1762 */
1763static int lsilogicR3ProcessSCSIIORequest(PLSILOGICSCSI pThis, PLSILOGICREQ pLsiReq)
1764{
1765 int rc = VINF_SUCCESS;
1766
1767# ifdef LOG_ENABLED
1768 lsilogicR3DumpSCSIIORequest(&pLsiReq->GuestRequest.SCSIIO);
1769# endif
1770
1771 pLsiReq->fBIOS = false;
1772 pLsiReq->GCPhysSgStart = pLsiReq->GCPhysMessageFrameAddr + sizeof(MptSCSIIORequest);
1773 pLsiReq->cChainOffset = pLsiReq->GuestRequest.SCSIIO.u8ChainOffset;
1774 if (pLsiReq->cChainOffset)
1775 pLsiReq->cChainOffset = pLsiReq->cChainOffset * sizeof(uint32_t) - sizeof(MptSCSIIORequest);
1776
1777 if (RT_LIKELY( (pLsiReq->GuestRequest.SCSIIO.u8TargetID < pThis->cDeviceStates)
1778 && (pLsiReq->GuestRequest.SCSIIO.u8Bus == 0)))
1779 {
1780 PLSILOGICDEVICE pTargetDevice;
1781 pTargetDevice = &pThis->paDeviceStates[pLsiReq->GuestRequest.SCSIIO.u8TargetID];
1782
1783 if (pTargetDevice->pDrvBase)
1784 {
1785
1786 if (pLsiReq->GuestRequest.SCSIIO.u32DataLength)
1787 {
1788
1789 rc = lsilogicIoBufAllocate(pThis->CTX_SUFF(pDevIns), pLsiReq,
1790 pLsiReq->GuestRequest.SCSIIO.u32DataLength);
1791 AssertRC(rc); /** @todo: Insufficient resources error. */
1792 }
1793
1794 /* Setup the SCSI request. */
1795 pLsiReq->pTargetDevice = pTargetDevice;
1796 pLsiReq->PDMScsiRequest.uLogicalUnit = pLsiReq->GuestRequest.SCSIIO.au8LUN[1];
1797
1798 uint8_t uDataDirection = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_GET(pLsiReq->GuestRequest.SCSIIO.u32Control);
1799
1800 if (uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE)
1801 pLsiReq->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_NONE;
1802 else if (uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE)
1803 pLsiReq->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_TO_DEVICE;
1804 else if (uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ)
1805 pLsiReq->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_FROM_DEVICE;
1806
1807 pLsiReq->PDMScsiRequest.cbCDB = pLsiReq->GuestRequest.SCSIIO.u8CDBLength;
1808 pLsiReq->PDMScsiRequest.pbCDB = pLsiReq->GuestRequest.SCSIIO.au8CDB;
1809 pLsiReq->PDMScsiRequest.cbScatterGather = pLsiReq->GuestRequest.SCSIIO.u32DataLength;
1810 if (pLsiReq->PDMScsiRequest.cbScatterGather)
1811 {
1812 pLsiReq->PDMScsiRequest.cScatterGatherEntries = 1;
1813 pLsiReq->PDMScsiRequest.paScatterGatherHead = &pLsiReq->SegIoBuf;
1814 }
1815 else
1816 {
1817 pLsiReq->PDMScsiRequest.cScatterGatherEntries = 0;
1818 pLsiReq->PDMScsiRequest.paScatterGatherHead = NULL;
1819 }
1820 pLsiReq->PDMScsiRequest.cbSenseBuffer = sizeof(pLsiReq->abSenseBuffer);
1821 memset(pLsiReq->abSenseBuffer, 0, pLsiReq->PDMScsiRequest.cbSenseBuffer);
1822 pLsiReq->PDMScsiRequest.pbSenseBuffer = pLsiReq->abSenseBuffer;
1823 pLsiReq->PDMScsiRequest.pvUser = pLsiReq;
1824
1825 ASMAtomicIncU32(&pTargetDevice->cOutstandingRequests);
1826 rc = pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pTargetDevice->pDrvSCSIConnector, &pLsiReq->PDMScsiRequest);
1827 AssertMsgRC(rc, ("Sending request to SCSI layer failed rc=%Rrc\n", rc));
1828 return VINF_SUCCESS;
1829 }
1830 else
1831 {
1832 /* Device is not present report SCSI selection timeout. */
1833 pLsiReq->IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_DEVICE_NOT_THERE;
1834 }
1835 }
1836 else
1837 {
1838 /* Report out of bounds target ID or bus. */
1839 if (pLsiReq->GuestRequest.SCSIIO.u8Bus != 0)
1840 pLsiReq->IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_INVALID_BUS;
1841 else
1842 pLsiReq->IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_INVALID_TARGETID;
1843 }
1844
1845 static int g_cLogged = 0;
1846
1847 if (g_cLogged++ < MAX_REL_LOG_ERRORS)
1848 {
1849 LogRel(("LsiLogic#%d: %d/%d (Bus/Target) doesn't exist\n", pThis->CTX_SUFF(pDevIns)->iInstance,
1850 pLsiReq->GuestRequest.SCSIIO.u8TargetID, pLsiReq->GuestRequest.SCSIIO.u8Bus));
1851 /* Log the CDB too */
1852 LogRel(("LsiLogic#%d: Guest issued CDB {%#x",
1853 pThis->CTX_SUFF(pDevIns)->iInstance, pLsiReq->GuestRequest.SCSIIO.au8CDB[0]));
1854 for (unsigned i = 1; i < pLsiReq->GuestRequest.SCSIIO.u8CDBLength; i++)
1855 LogRel((", %#x", pLsiReq->GuestRequest.SCSIIO.au8CDB[i]));
1856 LogRel(("}\n"));
1857 }
1858
1859 /* The rest is equal to both errors. */
1860 pLsiReq->IOCReply.SCSIIOError.u8TargetID = pLsiReq->GuestRequest.SCSIIO.u8TargetID;
1861 pLsiReq->IOCReply.SCSIIOError.u8Bus = pLsiReq->GuestRequest.SCSIIO.u8Bus;
1862 pLsiReq->IOCReply.SCSIIOError.u8MessageLength = sizeof(MptSCSIIOErrorReply) / 4;
1863 pLsiReq->IOCReply.SCSIIOError.u8Function = pLsiReq->GuestRequest.SCSIIO.u8Function;
1864 pLsiReq->IOCReply.SCSIIOError.u8CDBLength = pLsiReq->GuestRequest.SCSIIO.u8CDBLength;
1865 pLsiReq->IOCReply.SCSIIOError.u8SenseBufferLength = pLsiReq->GuestRequest.SCSIIO.u8SenseBufferLength;
1866 pLsiReq->IOCReply.SCSIIOError.u32MessageContext = pLsiReq->GuestRequest.SCSIIO.u32MessageContext;
1867 pLsiReq->IOCReply.SCSIIOError.u8SCSIStatus = SCSI_STATUS_OK;
1868 pLsiReq->IOCReply.SCSIIOError.u8SCSIState = MPT_SCSI_IO_ERROR_SCSI_STATE_TERMINATED;
1869 pLsiReq->IOCReply.SCSIIOError.u32IOCLogInfo = 0;
1870 pLsiReq->IOCReply.SCSIIOError.u32TransferCount = 0;
1871 pLsiReq->IOCReply.SCSIIOError.u32SenseCount = 0;
1872 pLsiReq->IOCReply.SCSIIOError.u32ResponseInfo = 0;
1873
1874 lsilogicFinishAddressReply(pThis, &pLsiReq->IOCReply, false);
1875 RTMemCacheFree(pThis->hTaskCache, pLsiReq);
1876
1877 return rc;
1878}
1879
1880
1881/**
1882 * @interface_method_impl{PDMISCSIPORT,pfnSCSIRequestCompleted}
1883 */
1884static DECLCALLBACK(int) lsilogicR3DeviceSCSIRequestCompleted(PPDMISCSIPORT pInterface, PPDMSCSIREQUEST pSCSIRequest,
1885 int rcCompletion, bool fRedo, int rcReq)
1886{
1887 PLSILOGICREQ pLsiReq = (PLSILOGICREQ)pSCSIRequest->pvUser;
1888 PLSILOGICDEVICE pLsiLogicDevice = pLsiReq->pTargetDevice;
1889 PLSILOGICSCSI pThis = pLsiLogicDevice->CTX_SUFF(pLsiLogic);
1890
1891 /* If the task failed but it is possible to redo it again after a suspend
1892 * add it to the list. */
1893 if (fRedo)
1894 {
1895 if (!pLsiReq->fBIOS && pLsiReq->PDMScsiRequest.cbScatterGather)
1896 lsilogicIoBufFree(pThis->CTX_SUFF(pDevIns), pLsiReq, false /* fCopyToGuest */);
1897
1898 /* Add to the list. */
1899 do
1900 {
1901 pLsiReq->pRedoNext = ASMAtomicReadPtrT(&pThis->pTasksRedoHead, PLSILOGICREQ);
1902 } while (!ASMAtomicCmpXchgPtr(&pThis->pTasksRedoHead, pLsiReq, pLsiReq->pRedoNext));
1903
1904 /* Suspend the VM if not done already. */
1905 if (!ASMAtomicXchgBool(&pThis->fRedo, true))
1906 lsilogicR3RedoSetWarning(pThis, rcReq);
1907 }
1908 else
1909 {
1910 if (RT_UNLIKELY(pLsiReq->fBIOS))
1911 {
1912 int rc = vboxscsiRequestFinished(&pThis->VBoxSCSI, pSCSIRequest, rcCompletion);
1913 AssertMsgRC(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc));
1914 }
1915 else
1916 {
1917 RTGCPHYS GCPhysAddrSenseBuffer;
1918
1919 GCPhysAddrSenseBuffer = pLsiReq->GuestRequest.SCSIIO.u32SenseBufferLowAddress;
1920 GCPhysAddrSenseBuffer |= ((uint64_t)pThis->u32SenseBufferHighAddr << 32);
1921
1922 /* Copy the sense buffer over. */
1923 PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns), GCPhysAddrSenseBuffer, pLsiReq->abSenseBuffer,
1924 RT_UNLIKELY( pLsiReq->GuestRequest.SCSIIO.u8SenseBufferLength
1925 < pLsiReq->PDMScsiRequest.cbSenseBuffer)
1926 ? pLsiReq->GuestRequest.SCSIIO.u8SenseBufferLength
1927 : pLsiReq->PDMScsiRequest.cbSenseBuffer);
1928
1929 if (pLsiReq->PDMScsiRequest.cbScatterGather)
1930 lsilogicIoBufFree(pThis->CTX_SUFF(pDevIns), pLsiReq, true /* fCopyToGuest */);
1931
1932
1933 if (RT_LIKELY(rcCompletion == SCSI_STATUS_OK))
1934 lsilogicR3FinishContextReply(pThis, pLsiReq->GuestRequest.SCSIIO.u32MessageContext);
1935 else
1936 {
1937 /* The SCSI target encountered an error during processing post a reply. */
1938 memset(&pLsiReq->IOCReply, 0, sizeof(MptReplyUnion));
1939 pLsiReq->IOCReply.SCSIIOError.u8TargetID = pLsiReq->GuestRequest.SCSIIO.u8TargetID;
1940 pLsiReq->IOCReply.SCSIIOError.u8Bus = pLsiReq->GuestRequest.SCSIIO.u8Bus;
1941 pLsiReq->IOCReply.SCSIIOError.u8MessageLength = 8;
1942 pLsiReq->IOCReply.SCSIIOError.u8Function = pLsiReq->GuestRequest.SCSIIO.u8Function;
1943 pLsiReq->IOCReply.SCSIIOError.u8CDBLength = pLsiReq->GuestRequest.SCSIIO.u8CDBLength;
1944 pLsiReq->IOCReply.SCSIIOError.u8SenseBufferLength = pLsiReq->GuestRequest.SCSIIO.u8SenseBufferLength;
1945 pLsiReq->IOCReply.SCSIIOError.u8MessageFlags = pLsiReq->GuestRequest.SCSIIO.u8MessageFlags;
1946 pLsiReq->IOCReply.SCSIIOError.u32MessageContext = pLsiReq->GuestRequest.SCSIIO.u32MessageContext;
1947 pLsiReq->IOCReply.SCSIIOError.u8SCSIStatus = rcCompletion;
1948 pLsiReq->IOCReply.SCSIIOError.u8SCSIState = MPT_SCSI_IO_ERROR_SCSI_STATE_AUTOSENSE_VALID;
1949 pLsiReq->IOCReply.SCSIIOError.u16IOCStatus = 0;
1950 pLsiReq->IOCReply.SCSIIOError.u32IOCLogInfo = 0;
1951 pLsiReq->IOCReply.SCSIIOError.u32TransferCount = 0;
1952 pLsiReq->IOCReply.SCSIIOError.u32SenseCount = sizeof(pLsiReq->abSenseBuffer);
1953 pLsiReq->IOCReply.SCSIIOError.u32ResponseInfo = 0;
1954
1955 lsilogicFinishAddressReply(pThis, &pLsiReq->IOCReply, true);
1956 }
1957 }
1958
1959 RTMemCacheFree(pThis->hTaskCache, pLsiReq);
1960 }
1961
1962 ASMAtomicDecU32(&pLsiLogicDevice->cOutstandingRequests);
1963
1964 if (pLsiLogicDevice->cOutstandingRequests == 0 && pThis->fSignalIdle)
1965 PDMDevHlpAsyncNotificationCompleted(pThis->pDevInsR3);
1966
1967 return VINF_SUCCESS;
1968}
1969
1970/**
1971 * @interface_method_impl{PDMISCSIPORT,pfnQueryDeviceLocation}
1972 */
1973static DECLCALLBACK(int) lsilogicR3QueryDeviceLocation(PPDMISCSIPORT pInterface, const char **ppcszController,
1974 uint32_t *piInstance, uint32_t *piLUN)
1975{
1976 PLSILOGICDEVICE pLsiLogicDevice = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, ISCSIPort);
1977 PPDMDEVINS pDevIns = pLsiLogicDevice->CTX_SUFF(pLsiLogic)->CTX_SUFF(pDevIns);
1978
1979 AssertPtrReturn(ppcszController, VERR_INVALID_POINTER);
1980 AssertPtrReturn(piInstance, VERR_INVALID_POINTER);
1981 AssertPtrReturn(piLUN, VERR_INVALID_POINTER);
1982
1983 *ppcszController = pDevIns->pReg->szName;
1984 *piInstance = pDevIns->iInstance;
1985 *piLUN = pLsiLogicDevice->iLUN;
1986
1987 return VINF_SUCCESS;
1988}
1989
1990/**
1991 * Return the configuration page header and data
1992 * which matches the given page type and number.
1993 *
1994 * @returns VINF_SUCCESS if successful
1995 * VERR_NOT_FOUND if the requested page could be found.
1996 * @param u8PageNumber Number of the page to get.
1997 * @param ppPageHeader Where to store the pointer to the page header.
1998 * @param ppbPageData Where to store the pointer to the page data.
1999 */
2000static int lsilogicR3ConfigurationIOUnitPageGetFromNumber(PLSILOGICSCSI pThis,
2001 PMptConfigurationPagesSupported pPages,
2002 uint8_t u8PageNumber,
2003 PMptConfigurationPageHeader *ppPageHeader,
2004 uint8_t **ppbPageData, size_t *pcbPage)
2005{
2006 int rc = VINF_SUCCESS;
2007
2008 AssertPtr(ppPageHeader); Assert(ppbPageData);
2009
2010 switch (u8PageNumber)
2011 {
2012 case 0:
2013 *ppPageHeader = &pPages->IOUnitPage0.u.fields.Header;
2014 *ppbPageData = pPages->IOUnitPage0.u.abPageData;
2015 *pcbPage = sizeof(pPages->IOUnitPage0);
2016 break;
2017 case 1:
2018 *ppPageHeader = &pPages->IOUnitPage1.u.fields.Header;
2019 *ppbPageData = pPages->IOUnitPage1.u.abPageData;
2020 *pcbPage = sizeof(pPages->IOUnitPage1);
2021 break;
2022 case 2:
2023 *ppPageHeader = &pPages->IOUnitPage2.u.fields.Header;
2024 *ppbPageData = pPages->IOUnitPage2.u.abPageData;
2025 *pcbPage = sizeof(pPages->IOUnitPage2);
2026 break;
2027 case 3:
2028 *ppPageHeader = &pPages->IOUnitPage3.u.fields.Header;
2029 *ppbPageData = pPages->IOUnitPage3.u.abPageData;
2030 *pcbPage = sizeof(pPages->IOUnitPage3);
2031 break;
2032 case 4:
2033 *ppPageHeader = &pPages->IOUnitPage4.u.fields.Header;
2034 *ppbPageData = pPages->IOUnitPage4.u.abPageData;
2035 *pcbPage = sizeof(pPages->IOUnitPage4);
2036 break;
2037 default:
2038 rc = VERR_NOT_FOUND;
2039 }
2040
2041 return rc;
2042}
2043
2044/**
2045 * Return the configuration page header and data
2046 * which matches the given page type and number.
2047 *
2048 * @returns VINF_SUCCESS if successful
2049 * VERR_NOT_FOUND if the requested page could be found.
2050 * @param u8PageNumber Number of the page to get.
2051 * @param ppPageHeader Where to store the pointer to the page header.
2052 * @param ppbPageData Where to store the pointer to the page data.
2053 */
2054static int lsilogicR3ConfigurationIOCPageGetFromNumber(PLSILOGICSCSI pThis,
2055 PMptConfigurationPagesSupported pPages,
2056 uint8_t u8PageNumber,
2057 PMptConfigurationPageHeader *ppPageHeader,
2058 uint8_t **ppbPageData, size_t *pcbPage)
2059{
2060 int rc = VINF_SUCCESS;
2061
2062 AssertPtr(ppPageHeader); Assert(ppbPageData);
2063
2064 switch (u8PageNumber)
2065 {
2066 case 0:
2067 *ppPageHeader = &pPages->IOCPage0.u.fields.Header;
2068 *ppbPageData = pPages->IOCPage0.u.abPageData;
2069 *pcbPage = sizeof(pPages->IOCPage0);
2070 break;
2071 case 1:
2072 *ppPageHeader = &pPages->IOCPage1.u.fields.Header;
2073 *ppbPageData = pPages->IOCPage1.u.abPageData;
2074 *pcbPage = sizeof(pPages->IOCPage1);
2075 break;
2076 case 2:
2077 *ppPageHeader = &pPages->IOCPage2.u.fields.Header;
2078 *ppbPageData = pPages->IOCPage2.u.abPageData;
2079 *pcbPage = sizeof(pPages->IOCPage2);
2080 break;
2081 case 3:
2082 *ppPageHeader = &pPages->IOCPage3.u.fields.Header;
2083 *ppbPageData = pPages->IOCPage3.u.abPageData;
2084 *pcbPage = sizeof(pPages->IOCPage3);
2085 break;
2086 case 4:
2087 *ppPageHeader = &pPages->IOCPage4.u.fields.Header;
2088 *ppbPageData = pPages->IOCPage4.u.abPageData;
2089 *pcbPage = sizeof(pPages->IOCPage4);
2090 break;
2091 case 6:
2092 *ppPageHeader = &pPages->IOCPage6.u.fields.Header;
2093 *ppbPageData = pPages->IOCPage6.u.abPageData;
2094 *pcbPage = sizeof(pPages->IOCPage6);
2095 break;
2096 default:
2097 rc = VERR_NOT_FOUND;
2098 }
2099
2100 return rc;
2101}
2102
2103/**
2104 * Return the configuration page header and data
2105 * which matches the given page type and number.
2106 *
2107 * @returns VINF_SUCCESS if successful
2108 * VERR_NOT_FOUND if the requested page could be found.
2109 * @param u8PageNumber Number of the page to get.
2110 * @param ppPageHeader Where to store the pointer to the page header.
2111 * @param ppbPageData Where to store the pointer to the page data.
2112 */
2113static int lsilogicR3ConfigurationManufacturingPageGetFromNumber(PLSILOGICSCSI pThis,
2114 PMptConfigurationPagesSupported pPages,
2115 uint8_t u8PageNumber,
2116 PMptConfigurationPageHeader *ppPageHeader,
2117 uint8_t **ppbPageData, size_t *pcbPage)
2118{
2119 int rc = VINF_SUCCESS;
2120
2121 AssertPtr(ppPageHeader); Assert(ppbPageData);
2122
2123 switch (u8PageNumber)
2124 {
2125 case 0:
2126 *ppPageHeader = &pPages->ManufacturingPage0.u.fields.Header;
2127 *ppbPageData = pPages->ManufacturingPage0.u.abPageData;
2128 *pcbPage = sizeof(pPages->ManufacturingPage0);
2129 break;
2130 case 1:
2131 *ppPageHeader = &pPages->ManufacturingPage1.u.fields.Header;
2132 *ppbPageData = pPages->ManufacturingPage1.u.abPageData;
2133 *pcbPage = sizeof(pPages->ManufacturingPage1);
2134 break;
2135 case 2:
2136 *ppPageHeader = &pPages->ManufacturingPage2.u.fields.Header;
2137 *ppbPageData = pPages->ManufacturingPage2.u.abPageData;
2138 *pcbPage = sizeof(pPages->ManufacturingPage2);
2139 break;
2140 case 3:
2141 *ppPageHeader = &pPages->ManufacturingPage3.u.fields.Header;
2142 *ppbPageData = pPages->ManufacturingPage3.u.abPageData;
2143 *pcbPage = sizeof(pPages->ManufacturingPage3);
2144 break;
2145 case 4:
2146 *ppPageHeader = &pPages->ManufacturingPage4.u.fields.Header;
2147 *ppbPageData = pPages->ManufacturingPage4.u.abPageData;
2148 *pcbPage = sizeof(pPages->ManufacturingPage4);
2149 break;
2150 case 5:
2151 *ppPageHeader = &pPages->ManufacturingPage5.u.fields.Header;
2152 *ppbPageData = pPages->ManufacturingPage5.u.abPageData;
2153 *pcbPage = sizeof(pPages->ManufacturingPage5);
2154 break;
2155 case 6:
2156 *ppPageHeader = &pPages->ManufacturingPage6.u.fields.Header;
2157 *ppbPageData = pPages->ManufacturingPage6.u.abPageData;
2158 *pcbPage = sizeof(pPages->ManufacturingPage6);
2159 break;
2160 case 7:
2161 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
2162 {
2163 *ppPageHeader = &pPages->u.SasPages.pManufacturingPage7->u.fields.Header;
2164 *ppbPageData = pPages->u.SasPages.pManufacturingPage7->u.abPageData;
2165 *pcbPage = pPages->u.SasPages.cbManufacturingPage7;
2166 }
2167 else
2168 rc = VERR_NOT_FOUND;
2169 break;
2170 case 8:
2171 *ppPageHeader = &pPages->ManufacturingPage8.u.fields.Header;
2172 *ppbPageData = pPages->ManufacturingPage8.u.abPageData;
2173 *pcbPage = sizeof(pPages->ManufacturingPage8);
2174 break;
2175 case 9:
2176 *ppPageHeader = &pPages->ManufacturingPage9.u.fields.Header;
2177 *ppbPageData = pPages->ManufacturingPage9.u.abPageData;
2178 *pcbPage = sizeof(pPages->ManufacturingPage9);
2179 break;
2180 case 10:
2181 *ppPageHeader = &pPages->ManufacturingPage10.u.fields.Header;
2182 *ppbPageData = pPages->ManufacturingPage10.u.abPageData;
2183 *pcbPage = sizeof(pPages->ManufacturingPage10);
2184 break;
2185 default:
2186 rc = VERR_NOT_FOUND;
2187 }
2188
2189 return rc;
2190}
2191
2192/**
2193 * Return the configuration page header and data
2194 * which matches the given page type and number.
2195 *
2196 * @returns VINF_SUCCESS if successful
2197 * VERR_NOT_FOUND if the requested page could be found.
2198 * @param u8PageNumber Number of the page to get.
2199 * @param ppPageHeader Where to store the pointer to the page header.
2200 * @param ppbPageData Where to store the pointer to the page data.
2201 */
2202static int lsilogicR3ConfigurationBiosPageGetFromNumber(PLSILOGICSCSI pThis,
2203 PMptConfigurationPagesSupported pPages,
2204 uint8_t u8PageNumber,
2205 PMptConfigurationPageHeader *ppPageHeader,
2206 uint8_t **ppbPageData, size_t *pcbPage)
2207{
2208 int rc = VINF_SUCCESS;
2209
2210 AssertPtr(ppPageHeader); Assert(ppbPageData);
2211
2212 switch (u8PageNumber)
2213 {
2214 case 1:
2215 *ppPageHeader = &pPages->BIOSPage1.u.fields.Header;
2216 *ppbPageData = pPages->BIOSPage1.u.abPageData;
2217 *pcbPage = sizeof(pPages->BIOSPage1);
2218 break;
2219 case 2:
2220 *ppPageHeader = &pPages->BIOSPage2.u.fields.Header;
2221 *ppbPageData = pPages->BIOSPage2.u.abPageData;
2222 *pcbPage = sizeof(pPages->BIOSPage2);
2223 break;
2224 case 4:
2225 *ppPageHeader = &pPages->BIOSPage4.u.fields.Header;
2226 *ppbPageData = pPages->BIOSPage4.u.abPageData;
2227 *pcbPage = sizeof(pPages->BIOSPage4);
2228 break;
2229 default:
2230 rc = VERR_NOT_FOUND;
2231 }
2232
2233 return rc;
2234}
2235
2236/**
2237 * Return the configuration page header and data
2238 * which matches the given page type and number.
2239 *
2240 * @returns VINF_SUCCESS if successful
2241 * VERR_NOT_FOUND if the requested page could be found.
2242 * @param u8PageNumber Number of the page to get.
2243 * @param ppPageHeader Where to store the pointer to the page header.
2244 * @param ppbPageData Where to store the pointer to the page data.
2245 */
2246static int lsilogicR3ConfigurationSCSISPIPortPageGetFromNumber(PLSILOGICSCSI pThis,
2247 PMptConfigurationPagesSupported pPages,
2248 uint8_t u8Port,
2249 uint8_t u8PageNumber,
2250 PMptConfigurationPageHeader *ppPageHeader,
2251 uint8_t **ppbPageData, size_t *pcbPage)
2252{
2253 int rc = VINF_SUCCESS;
2254 AssertPtr(ppPageHeader); Assert(ppbPageData);
2255
2256
2257 if (u8Port >= RT_ELEMENTS(pPages->u.SpiPages.aPortPages))
2258 return VERR_NOT_FOUND;
2259
2260 switch (u8PageNumber)
2261 {
2262 case 0:
2263 *ppPageHeader = &pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage0.u.fields.Header;
2264 *ppbPageData = pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage0.u.abPageData;
2265 *pcbPage = sizeof(pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage0);
2266 break;
2267 case 1:
2268 *ppPageHeader = &pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage1.u.fields.Header;
2269 *ppbPageData = pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage1.u.abPageData;
2270 *pcbPage = sizeof(pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage1);
2271 break;
2272 case 2:
2273 *ppPageHeader = &pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage2.u.fields.Header;
2274 *ppbPageData = pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage2.u.abPageData;
2275 *pcbPage = sizeof(pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage2);
2276 break;
2277 default:
2278 rc = VERR_NOT_FOUND;
2279 }
2280
2281 return rc;
2282}
2283
2284/**
2285 * Return the configuration page header and data
2286 * which matches the given page type and number.
2287 *
2288 * @returns VINF_SUCCESS if successful
2289 * VERR_NOT_FOUND if the requested page could be found.
2290 * @param u8PageNumber Number of the page to get.
2291 * @param ppPageHeader Where to store the pointer to the page header.
2292 * @param ppbPageData Where to store the pointer to the page data.
2293 */
2294static int lsilogicR3ConfigurationSCSISPIDevicePageGetFromNumber(PLSILOGICSCSI pThis,
2295 PMptConfigurationPagesSupported pPages,
2296 uint8_t u8Bus,
2297 uint8_t u8TargetID, uint8_t u8PageNumber,
2298 PMptConfigurationPageHeader *ppPageHeader,
2299 uint8_t **ppbPageData, size_t *pcbPage)
2300{
2301 int rc = VINF_SUCCESS;
2302 AssertPtr(ppPageHeader); Assert(ppbPageData);
2303
2304 if (u8Bus >= RT_ELEMENTS(pPages->u.SpiPages.aBuses))
2305 return VERR_NOT_FOUND;
2306
2307 if (u8TargetID >= RT_ELEMENTS(pPages->u.SpiPages.aBuses[u8Bus].aDevicePages))
2308 return VERR_NOT_FOUND;
2309
2310 switch (u8PageNumber)
2311 {
2312 case 0:
2313 *ppPageHeader = &pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage0.u.fields.Header;
2314 *ppbPageData = pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage0.u.abPageData;
2315 *pcbPage = sizeof(pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage0);
2316 break;
2317 case 1:
2318 *ppPageHeader = &pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage1.u.fields.Header;
2319 *ppbPageData = pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage1.u.abPageData;
2320 *pcbPage = sizeof(pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage1);
2321 break;
2322 case 2:
2323 *ppPageHeader = &pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage2.u.fields.Header;
2324 *ppbPageData = pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage2.u.abPageData;
2325 *pcbPage = sizeof(pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage2);
2326 break;
2327 case 3:
2328 *ppPageHeader = &pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage3.u.fields.Header;
2329 *ppbPageData = pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage3.u.abPageData;
2330 *pcbPage = sizeof(pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage3);
2331 break;
2332 default:
2333 rc = VERR_NOT_FOUND;
2334 }
2335
2336 return rc;
2337}
2338
2339static int lsilogicR3ConfigurationSASIOUnitPageGetFromNumber(PLSILOGICSCSI pThis,
2340 PMptConfigurationPagesSupported pPages,
2341 uint8_t u8PageNumber,
2342 PMptExtendedConfigurationPageHeader *ppPageHeader,
2343 uint8_t **ppbPageData, size_t *pcbPage)
2344{
2345 int rc = VINF_SUCCESS;
2346
2347 switch (u8PageNumber)
2348 {
2349 case 0:
2350 *ppPageHeader = &pPages->u.SasPages.pSASIOUnitPage0->u.fields.ExtHeader;
2351 *ppbPageData = pPages->u.SasPages.pSASIOUnitPage0->u.abPageData;
2352 *pcbPage = pPages->u.SasPages.cbSASIOUnitPage0;
2353 break;
2354 case 1:
2355 *ppPageHeader = &pPages->u.SasPages.pSASIOUnitPage1->u.fields.ExtHeader;
2356 *ppbPageData = pPages->u.SasPages.pSASIOUnitPage1->u.abPageData;
2357 *pcbPage = pPages->u.SasPages.cbSASIOUnitPage1;
2358 break;
2359 case 2:
2360 *ppPageHeader = &pPages->u.SasPages.SASIOUnitPage2.u.fields.ExtHeader;
2361 *ppbPageData = pPages->u.SasPages.SASIOUnitPage2.u.abPageData;
2362 *pcbPage = sizeof(pPages->u.SasPages.SASIOUnitPage2);
2363 break;
2364 case 3:
2365 *ppPageHeader = &pPages->u.SasPages.SASIOUnitPage3.u.fields.ExtHeader;
2366 *ppbPageData = pPages->u.SasPages.SASIOUnitPage3.u.abPageData;
2367 *pcbPage = sizeof(pPages->u.SasPages.SASIOUnitPage3);
2368 break;
2369 default:
2370 rc = VERR_NOT_FOUND;
2371 }
2372
2373 return rc;
2374}
2375
2376static int lsilogicR3ConfigurationSASPHYPageGetFromNumber(PLSILOGICSCSI pThis,
2377 PMptConfigurationPagesSupported pPages,
2378 uint8_t u8PageNumber,
2379 MptConfigurationPageAddress PageAddress,
2380 PMptExtendedConfigurationPageHeader *ppPageHeader,
2381 uint8_t **ppbPageData, size_t *pcbPage)
2382{
2383 int rc = VINF_SUCCESS;
2384 uint8_t uAddressForm = MPT_CONFIGURATION_PAGE_ADDRESS_GET_SAS_FORM(PageAddress);
2385 PMptConfigurationPagesSas pPagesSas = &pPages->u.SasPages;
2386 PMptPHY pPHYPages = NULL;
2387
2388 Log(("Address form %d\n", uAddressForm));
2389
2390 if (uAddressForm == 0) /* PHY number */
2391 {
2392 uint8_t u8PhyNumber = PageAddress.SASPHY.Form0.u8PhyNumber;
2393
2394 Log(("PHY number %d\n", u8PhyNumber));
2395
2396 if (u8PhyNumber >= pPagesSas->cPHYs)
2397 return VERR_NOT_FOUND;
2398
2399 pPHYPages = &pPagesSas->paPHYs[u8PhyNumber];
2400 }
2401 else if (uAddressForm == 1) /* Index form */
2402 {
2403 uint16_t u16Index = PageAddress.SASPHY.Form1.u16Index;
2404
2405 Log(("PHY index %d\n", u16Index));
2406
2407 if (u16Index >= pPagesSas->cPHYs)
2408 return VERR_NOT_FOUND;
2409
2410 pPHYPages = &pPagesSas->paPHYs[u16Index];
2411 }
2412 else
2413 rc = VERR_NOT_FOUND; /* Correct? */
2414
2415 if (pPHYPages)
2416 {
2417 switch (u8PageNumber)
2418 {
2419 case 0:
2420 *ppPageHeader = &pPHYPages->SASPHYPage0.u.fields.ExtHeader;
2421 *ppbPageData = pPHYPages->SASPHYPage0.u.abPageData;
2422 *pcbPage = sizeof(pPHYPages->SASPHYPage0);
2423 break;
2424 case 1:
2425 *ppPageHeader = &pPHYPages->SASPHYPage1.u.fields.ExtHeader;
2426 *ppbPageData = pPHYPages->SASPHYPage1.u.abPageData;
2427 *pcbPage = sizeof(pPHYPages->SASPHYPage1);
2428 break;
2429 default:
2430 rc = VERR_NOT_FOUND;
2431 }
2432 }
2433 else
2434 rc = VERR_NOT_FOUND;
2435
2436 return rc;
2437}
2438
2439static int lsilogicR3ConfigurationSASDevicePageGetFromNumber(PLSILOGICSCSI pThis,
2440 PMptConfigurationPagesSupported pPages,
2441 uint8_t u8PageNumber,
2442 MptConfigurationPageAddress PageAddress,
2443 PMptExtendedConfigurationPageHeader *ppPageHeader,
2444 uint8_t **ppbPageData, size_t *pcbPage)
2445{
2446 int rc = VINF_SUCCESS;
2447 uint8_t uAddressForm = MPT_CONFIGURATION_PAGE_ADDRESS_GET_SAS_FORM(PageAddress);
2448 PMptConfigurationPagesSas pPagesSas = &pPages->u.SasPages;
2449 PMptSASDevice pSASDevice = NULL;
2450
2451 Log(("Address form %d\n", uAddressForm));
2452
2453 if (uAddressForm == 0)
2454 {
2455 uint16_t u16Handle = PageAddress.SASDevice.Form0And2.u16Handle;
2456
2457 Log(("Get next handle %#x\n", u16Handle));
2458
2459 pSASDevice = pPagesSas->pSASDeviceHead;
2460
2461 /* Get the first device? */
2462 if (u16Handle != 0xffff)
2463 {
2464 /* No, search for the right one. */
2465
2466 while ( pSASDevice
2467 && pSASDevice->SASDevicePage0.u.fields.u16DevHandle != u16Handle)
2468 pSASDevice = pSASDevice->pNext;
2469
2470 if (pSASDevice)
2471 pSASDevice = pSASDevice->pNext;
2472 }
2473 }
2474 else if (uAddressForm == 1)
2475 {
2476 uint8_t u8TargetID = PageAddress.SASDevice.Form1.u8TargetID;
2477 uint8_t u8Bus = PageAddress.SASDevice.Form1.u8Bus;
2478
2479 Log(("u8TargetID=%d u8Bus=%d\n", u8TargetID, u8Bus));
2480
2481 pSASDevice = pPagesSas->pSASDeviceHead;
2482
2483 while ( pSASDevice
2484 && ( pSASDevice->SASDevicePage0.u.fields.u8TargetID != u8TargetID
2485 || pSASDevice->SASDevicePage0.u.fields.u8Bus != u8Bus))
2486 pSASDevice = pSASDevice->pNext;
2487 }
2488 else if (uAddressForm == 2)
2489 {
2490 uint16_t u16Handle = PageAddress.SASDevice.Form0And2.u16Handle;
2491
2492 Log(("Handle %#x\n", u16Handle));
2493
2494 pSASDevice = pPagesSas->pSASDeviceHead;
2495
2496 while ( pSASDevice
2497 && pSASDevice->SASDevicePage0.u.fields.u16DevHandle != u16Handle)
2498 pSASDevice = pSASDevice->pNext;
2499 }
2500
2501 if (pSASDevice)
2502 {
2503 switch (u8PageNumber)
2504 {
2505 case 0:
2506 *ppPageHeader = &pSASDevice->SASDevicePage0.u.fields.ExtHeader;
2507 *ppbPageData = pSASDevice->SASDevicePage0.u.abPageData;
2508 *pcbPage = sizeof(pSASDevice->SASDevicePage0);
2509 break;
2510 case 1:
2511 *ppPageHeader = &pSASDevice->SASDevicePage1.u.fields.ExtHeader;
2512 *ppbPageData = pSASDevice->SASDevicePage1.u.abPageData;
2513 *pcbPage = sizeof(pSASDevice->SASDevicePage1);
2514 break;
2515 case 2:
2516 *ppPageHeader = &pSASDevice->SASDevicePage2.u.fields.ExtHeader;
2517 *ppbPageData = pSASDevice->SASDevicePage2.u.abPageData;
2518 *pcbPage = sizeof(pSASDevice->SASDevicePage2);
2519 break;
2520 default:
2521 rc = VERR_NOT_FOUND;
2522 }
2523 }
2524 else
2525 rc = VERR_NOT_FOUND;
2526
2527 return rc;
2528}
2529
2530/**
2531 * Returns the extended configuration page header and data.
2532 * @returns VINF_SUCCESS if successful
2533 * VERR_NOT_FOUND if the requested page could be found.
2534 * @param pThis Pointer to the LsiLogic device state.
2535 * @param pConfigurationReq The configuration request.
2536 * @param u8PageNumber Number of the page to get.
2537 * @param ppPageHeader Where to store the pointer to the page header.
2538 * @param ppbPageData Where to store the pointer to the page data.
2539 */
2540static int lsilogicR3ConfigurationPageGetExtended(PLSILOGICSCSI pThis, PMptConfigurationRequest pConfigurationReq,
2541 PMptExtendedConfigurationPageHeader *ppPageHeader,
2542 uint8_t **ppbPageData, size_t *pcbPage)
2543{
2544 int rc = VINF_SUCCESS;
2545
2546 Log(("Extended page requested:\n"));
2547 Log(("u8ExtPageType=%#x\n", pConfigurationReq->u8ExtPageType));
2548 Log(("u8ExtPageLength=%d\n", pConfigurationReq->u16ExtPageLength));
2549
2550 switch (pConfigurationReq->u8ExtPageType)
2551 {
2552 case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASIOUNIT:
2553 {
2554 rc = lsilogicR3ConfigurationSASIOUnitPageGetFromNumber(pThis,
2555 pThis->pConfigurationPages,
2556 pConfigurationReq->u8PageNumber,
2557 ppPageHeader, ppbPageData, pcbPage);
2558 break;
2559 }
2560 case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASPHYS:
2561 {
2562 rc = lsilogicR3ConfigurationSASPHYPageGetFromNumber(pThis,
2563 pThis->pConfigurationPages,
2564 pConfigurationReq->u8PageNumber,
2565 pConfigurationReq->PageAddress,
2566 ppPageHeader, ppbPageData, pcbPage);
2567 break;
2568 }
2569 case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASDEVICE:
2570 {
2571 rc = lsilogicR3ConfigurationSASDevicePageGetFromNumber(pThis,
2572 pThis->pConfigurationPages,
2573 pConfigurationReq->u8PageNumber,
2574 pConfigurationReq->PageAddress,
2575 ppPageHeader, ppbPageData, pcbPage);
2576 break;
2577 }
2578 case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASEXPANDER: /* No expanders supported */
2579 case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_ENCLOSURE: /* No enclosures supported */
2580 default:
2581 rc = VERR_NOT_FOUND;
2582 }
2583
2584 return rc;
2585}
2586
2587/**
2588 * Processes a Configuration request.
2589 *
2590 * @returns VBox status code.
2591 * @param pThis Pointer to the LsiLogic device state.
2592 * @param pConfigurationReq Pointer to the request structure.
2593 * @param pReply Pointer to the reply message frame
2594 */
2595static int lsilogicR3ProcessConfigurationRequest(PLSILOGICSCSI pThis, PMptConfigurationRequest pConfigurationReq,
2596 PMptConfigurationReply pReply)
2597{
2598 int rc = VINF_SUCCESS;
2599 uint8_t *pbPageData = NULL;
2600 PMptConfigurationPageHeader pPageHeader = NULL;
2601 PMptExtendedConfigurationPageHeader pExtPageHeader = NULL;
2602 uint8_t u8PageType;
2603 uint8_t u8PageAttribute;
2604 size_t cbPage = 0;
2605
2606 LogFlowFunc(("pThis=%#p\n", pThis));
2607
2608 u8PageType = MPT_CONFIGURATION_PAGE_TYPE_GET(pConfigurationReq->u8PageType);
2609 u8PageAttribute = MPT_CONFIGURATION_PAGE_ATTRIBUTE_GET(pConfigurationReq->u8PageType);
2610
2611 Log(("GuestRequest:\n"));
2612 Log(("u8Action=%#x\n", pConfigurationReq->u8Action));
2613 Log(("u8PageType=%#x\n", u8PageType));
2614 Log(("u8PageNumber=%d\n", pConfigurationReq->u8PageNumber));
2615 Log(("u8PageLength=%d\n", pConfigurationReq->u8PageLength));
2616 Log(("u8PageVersion=%d\n", pConfigurationReq->u8PageVersion));
2617
2618 /* Copy common bits from the request into the reply. */
2619 pReply->u8MessageLength = 6; /* 6 32bit D-Words. */
2620 pReply->u8Action = pConfigurationReq->u8Action;
2621 pReply->u8Function = pConfigurationReq->u8Function;
2622 pReply->u32MessageContext = pConfigurationReq->u32MessageContext;
2623
2624 switch (u8PageType)
2625 {
2626 case MPT_CONFIGURATION_PAGE_TYPE_IO_UNIT:
2627 {
2628 /* Get the page data. */
2629 rc = lsilogicR3ConfigurationIOUnitPageGetFromNumber(pThis,
2630 pThis->pConfigurationPages,
2631 pConfigurationReq->u8PageNumber,
2632 &pPageHeader, &pbPageData, &cbPage);
2633 break;
2634 }
2635 case MPT_CONFIGURATION_PAGE_TYPE_IOC:
2636 {
2637 /* Get the page data. */
2638 rc = lsilogicR3ConfigurationIOCPageGetFromNumber(pThis,
2639 pThis->pConfigurationPages,
2640 pConfigurationReq->u8PageNumber,
2641 &pPageHeader, &pbPageData, &cbPage);
2642 break;
2643 }
2644 case MPT_CONFIGURATION_PAGE_TYPE_MANUFACTURING:
2645 {
2646 /* Get the page data. */
2647 rc = lsilogicR3ConfigurationManufacturingPageGetFromNumber(pThis,
2648 pThis->pConfigurationPages,
2649 pConfigurationReq->u8PageNumber,
2650 &pPageHeader, &pbPageData, &cbPage);
2651 break;
2652 }
2653 case MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_PORT:
2654 {
2655 /* Get the page data. */
2656 rc = lsilogicR3ConfigurationSCSISPIPortPageGetFromNumber(pThis,
2657 pThis->pConfigurationPages,
2658 pConfigurationReq->PageAddress.MPIPortNumber.u8PortNumber,
2659 pConfigurationReq->u8PageNumber,
2660 &pPageHeader, &pbPageData, &cbPage);
2661 break;
2662 }
2663 case MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_DEVICE:
2664 {
2665 /* Get the page data. */
2666 rc = lsilogicR3ConfigurationSCSISPIDevicePageGetFromNumber(pThis,
2667 pThis->pConfigurationPages,
2668 pConfigurationReq->PageAddress.BusAndTargetId.u8Bus,
2669 pConfigurationReq->PageAddress.BusAndTargetId.u8TargetID,
2670 pConfigurationReq->u8PageNumber,
2671 &pPageHeader, &pbPageData, &cbPage);
2672 break;
2673 }
2674 case MPT_CONFIGURATION_PAGE_TYPE_BIOS:
2675 {
2676 rc = lsilogicR3ConfigurationBiosPageGetFromNumber(pThis,
2677 pThis->pConfigurationPages,
2678 pConfigurationReq->u8PageNumber,
2679 &pPageHeader, &pbPageData, &cbPage);
2680 break;
2681 }
2682 case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED:
2683 {
2684 rc = lsilogicR3ConfigurationPageGetExtended(pThis,
2685 pConfigurationReq,
2686 &pExtPageHeader, &pbPageData, &cbPage);
2687 break;
2688 }
2689 default:
2690 rc = VERR_NOT_FOUND;
2691 }
2692
2693 if (rc == VERR_NOT_FOUND)
2694 {
2695 Log(("Page not found\n"));
2696 pReply->u8PageType = pConfigurationReq->u8PageType;
2697 pReply->u8PageNumber = pConfigurationReq->u8PageNumber;
2698 pReply->u8PageLength = pConfigurationReq->u8PageLength;
2699 pReply->u8PageVersion = pConfigurationReq->u8PageVersion;
2700 pReply->u16IOCStatus = MPT_IOCSTATUS_CONFIG_INVALID_PAGE;
2701 return VINF_SUCCESS;
2702 }
2703
2704 if (u8PageType == MPT_CONFIGURATION_PAGE_TYPE_EXTENDED)
2705 {
2706 pReply->u8PageType = pExtPageHeader->u8PageType;
2707 pReply->u8PageNumber = pExtPageHeader->u8PageNumber;
2708 pReply->u8PageVersion = pExtPageHeader->u8PageVersion;
2709 pReply->u8ExtPageType = pExtPageHeader->u8ExtPageType;
2710 pReply->u16ExtPageLength = pExtPageHeader->u16ExtPageLength;
2711
2712 for (int i = 0; i < pExtPageHeader->u16ExtPageLength; i++)
2713 LogFlowFunc(("PageData[%d]=%#x\n", i, ((uint32_t *)pbPageData)[i]));
2714 }
2715 else
2716 {
2717 pReply->u8PageType = pPageHeader->u8PageType;
2718 pReply->u8PageNumber = pPageHeader->u8PageNumber;
2719 pReply->u8PageLength = pPageHeader->u8PageLength;
2720 pReply->u8PageVersion = pPageHeader->u8PageVersion;
2721
2722 for (int i = 0; i < pReply->u8PageLength; i++)
2723 LogFlowFunc(("PageData[%d]=%#x\n", i, ((uint32_t *)pbPageData)[i]));
2724 }
2725
2726 /*
2727 * Don't use the scatter gather handling code as the configuration request always have only one
2728 * simple element.
2729 */
2730 switch (pConfigurationReq->u8Action)
2731 {
2732 case MPT_CONFIGURATION_REQUEST_ACTION_DEFAULT: /* Nothing to do. We are always using the defaults. */
2733 case MPT_CONFIGURATION_REQUEST_ACTION_HEADER:
2734 {
2735 /* Already copied above nothing to do. */
2736 break;
2737 }
2738 case MPT_CONFIGURATION_REQUEST_ACTION_READ_NVRAM:
2739 case MPT_CONFIGURATION_REQUEST_ACTION_READ_CURRENT:
2740 case MPT_CONFIGURATION_REQUEST_ACTION_READ_DEFAULT:
2741 {
2742 uint32_t cbBuffer = pConfigurationReq->SimpleSGElement.u24Length;
2743 if (cbBuffer != 0)
2744 {
2745 RTGCPHYS GCPhysAddrPageBuffer = pConfigurationReq->SimpleSGElement.u32DataBufferAddressLow;
2746 if (pConfigurationReq->SimpleSGElement.f64BitAddress)
2747 GCPhysAddrPageBuffer |= (uint64_t)pConfigurationReq->SimpleSGElement.u32DataBufferAddressHigh << 32;
2748
2749 PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns), GCPhysAddrPageBuffer, pbPageData, RT_MIN(cbBuffer, cbPage));
2750 }
2751 break;
2752 }
2753 case MPT_CONFIGURATION_REQUEST_ACTION_WRITE_CURRENT:
2754 case MPT_CONFIGURATION_REQUEST_ACTION_WRITE_NVRAM:
2755 {
2756 uint32_t cbBuffer = pConfigurationReq->SimpleSGElement.u24Length;
2757 if (cbBuffer != 0)
2758 {
2759 RTGCPHYS GCPhysAddrPageBuffer = pConfigurationReq->SimpleSGElement.u32DataBufferAddressLow;
2760 if (pConfigurationReq->SimpleSGElement.f64BitAddress)
2761 GCPhysAddrPageBuffer |= (uint64_t)pConfigurationReq->SimpleSGElement.u32DataBufferAddressHigh << 32;
2762
2763 LogFlow(("cbBuffer=%u cbPage=%u\n", cbBuffer, cbPage));
2764
2765 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCPhysAddrPageBuffer, pbPageData,
2766 RT_MIN(cbBuffer, cbPage));
2767 }
2768 break;
2769 }
2770 default:
2771 AssertMsgFailed(("todo\n"));
2772 }
2773
2774 return VINF_SUCCESS;
2775}
2776
2777/**
2778 * Initializes the configuration pages for the SPI SCSI controller.
2779 *
2780 * @returns nothing
2781 * @param pThis Pointer to the LsiLogic device state.
2782 */
2783static void lsilogicR3InitializeConfigurationPagesSpi(PLSILOGICSCSI pThis)
2784{
2785 PMptConfigurationPagesSpi pPages = &pThis->pConfigurationPages->u.SpiPages;
2786
2787 AssertMsg(pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI, ("Controller is not the SPI SCSI one\n"));
2788
2789 LogFlowFunc(("pThis=%#p\n", pThis));
2790
2791 /* Clear everything first. */
2792 memset(pPages, 0, sizeof(MptConfigurationPagesSpi));
2793
2794 for (unsigned i = 0; i < RT_ELEMENTS(pPages->aPortPages); i++)
2795 {
2796 /* SCSI-SPI port page 0. */
2797 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
2798 | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_PORT;
2799 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.Header.u8PageNumber = 0;
2800 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIPort0) / 4;
2801 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.fInformationUnitTransfersCapable = true;
2802 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.fDTCapable = true;
2803 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.fQASCapable = true;
2804 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.u8MinimumSynchronousTransferPeriod = 0;
2805 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.u8MaximumSynchronousOffset = 0xff;
2806 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.fWide = true;
2807 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.fAIPCapable = true;
2808 pPages->aPortPages[i].SCSISPIPortPage0.u.fields.u2SignalingType = 0x3; /* Single Ended. */
2809
2810 /* SCSI-SPI port page 1. */
2811 pPages->aPortPages[i].SCSISPIPortPage1.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE
2812 | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_PORT;
2813 pPages->aPortPages[i].SCSISPIPortPage1.u.fields.Header.u8PageNumber = 1;
2814 pPages->aPortPages[i].SCSISPIPortPage1.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIPort1) / 4;
2815 pPages->aPortPages[i].SCSISPIPortPage1.u.fields.u8SCSIID = 7;
2816 pPages->aPortPages[i].SCSISPIPortPage1.u.fields.u16PortResponseIDsBitmask = (1 << 7);
2817 pPages->aPortPages[i].SCSISPIPortPage1.u.fields.u32OnBusTimerValue = 0;
2818
2819 /* SCSI-SPI port page 2. */
2820 pPages->aPortPages[i].SCSISPIPortPage2.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE
2821 | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_PORT;
2822 pPages->aPortPages[i].SCSISPIPortPage2.u.fields.Header.u8PageNumber = 2;
2823 pPages->aPortPages[i].SCSISPIPortPage2.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIPort2) / 4;
2824 pPages->aPortPages[i].SCSISPIPortPage2.u.fields.u4HostSCSIID = 7;
2825 pPages->aPortPages[i].SCSISPIPortPage2.u.fields.u2InitializeHBA = 0x3;
2826 pPages->aPortPages[i].SCSISPIPortPage2.u.fields.fTerminationDisabled = true;
2827 for (unsigned iDevice = 0; iDevice < RT_ELEMENTS(pPages->aPortPages[i].SCSISPIPortPage2.u.fields.aDeviceSettings); iDevice++)
2828 {
2829 pPages->aPortPages[i].SCSISPIPortPage2.u.fields.aDeviceSettings[iDevice].fBootChoice = true;
2830 }
2831 /* Everything else 0 for now. */
2832 }
2833
2834 for (unsigned uBusCurr = 0; uBusCurr < RT_ELEMENTS(pPages->aBuses); uBusCurr++)
2835 {
2836 for (unsigned uDeviceCurr = 0; uDeviceCurr < RT_ELEMENTS(pPages->aBuses[uBusCurr].aDevicePages); uDeviceCurr++)
2837 {
2838 /* SCSI-SPI device page 0. */
2839 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage0.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
2840 | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_DEVICE;
2841 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage0.u.fields.Header.u8PageNumber = 0;
2842 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage0.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIDevice0) / 4;
2843 /* Everything else 0 for now. */
2844
2845 /* SCSI-SPI device page 1. */
2846 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage1.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE
2847 | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_DEVICE;
2848 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage1.u.fields.Header.u8PageNumber = 1;
2849 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage1.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIDevice1) / 4;
2850 /* Everything else 0 for now. */
2851
2852 /* SCSI-SPI device page 2. */
2853 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage2.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE
2854 | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_DEVICE;
2855 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage2.u.fields.Header.u8PageNumber = 2;
2856 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage2.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIDevice2) / 4;
2857 /* Everything else 0 for now. */
2858
2859 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage3.u.fields.Header.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
2860 | MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_DEVICE;
2861 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage3.u.fields.Header.u8PageNumber = 3;
2862 pPages->aBuses[uBusCurr].aDevicePages[uDeviceCurr].SCSISPIDevicePage3.u.fields.Header.u8PageLength = sizeof(MptConfigurationPageSCSISPIDevice3) / 4;
2863 /* Everything else 0 for now. */
2864 }
2865 }
2866}
2867
2868/**
2869 * Generates a handle.
2870 *
2871 * @returns the handle.
2872 * @param pThis Pointer to the LsiLogic device state.
2873 */
2874DECLINLINE(uint16_t) lsilogicGetHandle(PLSILOGICSCSI pThis)
2875{
2876 uint16_t u16Handle = pThis->u16NextHandle++;
2877 return u16Handle;
2878}
2879
2880/**
2881 * Generates a SAS address (WWID)
2882 *
2883 * @returns nothing.
2884 * @param pSASAddress Pointer to an unitialised SAS address.
2885 * @param iId iId which will go into the address.
2886 *
2887 * @todo Generate better SAS addresses. (Request a block from SUN probably)
2888 */
2889void lsilogicSASAddressGenerate(PSASADDRESS pSASAddress, unsigned iId)
2890{
2891 pSASAddress->u8Address[0] = (0x5 << 5);
2892 pSASAddress->u8Address[1] = 0x01;
2893 pSASAddress->u8Address[2] = 0x02;
2894 pSASAddress->u8Address[3] = 0x03;
2895 pSASAddress->u8Address[4] = 0x04;
2896 pSASAddress->u8Address[5] = 0x05;
2897 pSASAddress->u8Address[6] = 0x06;
2898 pSASAddress->u8Address[7] = iId;
2899}
2900
2901/**
2902 * Initializes the configuration pages for the SAS SCSI controller.
2903 *
2904 * @returns nothing
2905 * @param pThis Pointer to the LsiLogic device state.
2906 */
2907static void lsilogicR3InitializeConfigurationPagesSas(PLSILOGICSCSI pThis)
2908{
2909 PMptConfigurationPagesSas pPages = &pThis->pConfigurationPages->u.SasPages;
2910
2911 AssertMsg(pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS, ("Controller is not the SAS SCSI one\n"));
2912
2913 LogFlowFunc(("pThis=%#p\n", pThis));
2914
2915 /* Manufacturing Page 7 - Connector settings. */
2916 pPages->cbManufacturingPage7 = LSILOGICSCSI_MANUFACTURING7_GET_SIZE(pThis->cPorts);
2917 PMptConfigurationPageManufacturing7 pManufacturingPage7 = (PMptConfigurationPageManufacturing7)RTMemAllocZ(pPages->cbManufacturingPage7);
2918 AssertPtr(pManufacturingPage7);
2919 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(pManufacturingPage7,
2920 0, 7,
2921 MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
2922 /* Set size manually. */
2923 if (pPages->cbManufacturingPage7 / 4 > 255)
2924 pManufacturingPage7->u.fields.Header.u8PageLength = 255;
2925 else
2926 pManufacturingPage7->u.fields.Header.u8PageLength = pPages->cbManufacturingPage7 / 4;
2927 pManufacturingPage7->u.fields.u8NumPhys = pThis->cPorts;
2928 pPages->pManufacturingPage7 = pManufacturingPage7;
2929
2930 /* SAS I/O unit page 0 - Port specific information. */
2931 pPages->cbSASIOUnitPage0 = LSILOGICSCSI_SASIOUNIT0_GET_SIZE(pThis->cPorts);
2932 PMptConfigurationPageSASIOUnit0 pSASPage0 = (PMptConfigurationPageSASIOUnit0)RTMemAllocZ(pPages->cbSASIOUnitPage0);
2933 AssertPtr(pSASPage0);
2934
2935 MPT_CONFIG_EXTENDED_PAGE_HEADER_INIT(pSASPage0, pPages->cbSASIOUnitPage0,
2936 0, MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY,
2937 MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASIOUNIT);
2938 pSASPage0->u.fields.u8NumPhys = pThis->cPorts;
2939 pPages->pSASIOUnitPage0 = pSASPage0;
2940
2941 /* SAS I/O unit page 1 - Port specific settings. */
2942 pPages->cbSASIOUnitPage1 = LSILOGICSCSI_SASIOUNIT1_GET_SIZE(pThis->cPorts);
2943 PMptConfigurationPageSASIOUnit1 pSASPage1 = (PMptConfigurationPageSASIOUnit1)RTMemAllocZ(pPages->cbSASIOUnitPage1);
2944 AssertPtr(pSASPage1);
2945
2946 MPT_CONFIG_EXTENDED_PAGE_HEADER_INIT(pSASPage1, pPages->cbSASIOUnitPage1,
2947 1, MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE,
2948 MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASIOUNIT);
2949 pSASPage1->u.fields.u8NumPhys = pSASPage0->u.fields.u8NumPhys;
2950 pSASPage1->u.fields.u16ControlFlags = 0;
2951 pSASPage1->u.fields.u16AdditionalControlFlags = 0;
2952 pPages->pSASIOUnitPage1 = pSASPage1;
2953
2954 /* SAS I/O unit page 2 - Port specific information. */
2955 pPages->SASIOUnitPage2.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
2956 | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
2957 pPages->SASIOUnitPage2.u.fields.ExtHeader.u8PageNumber = 2;
2958 pPages->SASIOUnitPage2.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASIOUNIT;
2959 pPages->SASIOUnitPage2.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASIOUnit2) / 4;
2960
2961 /* SAS I/O unit page 3 - Port specific information. */
2962 pPages->SASIOUnitPage3.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
2963 | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
2964 pPages->SASIOUnitPage3.u.fields.ExtHeader.u8PageNumber = 3;
2965 pPages->SASIOUnitPage3.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASIOUNIT;
2966 pPages->SASIOUnitPage3.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASIOUnit3) / 4;
2967
2968 pPages->cPHYs = pThis->cPorts;
2969 pPages->paPHYs = (PMptPHY)RTMemAllocZ(pPages->cPHYs * sizeof(MptPHY));
2970 AssertPtr(pPages->paPHYs);
2971
2972 /* Initialize the PHY configuration */
2973 for (unsigned i = 0; i < pThis->cPorts; i++)
2974 {
2975 PMptPHY pPHYPages = &pPages->paPHYs[i];
2976 uint16_t u16ControllerHandle = lsilogicGetHandle(pThis);
2977
2978 pManufacturingPage7->u.fields.aPHY[i].u8Location = LSILOGICSCSI_MANUFACTURING7_LOCATION_AUTO;
2979
2980 pSASPage0->u.fields.aPHY[i].u8Port = i;
2981 pSASPage0->u.fields.aPHY[i].u8PortFlags = 0;
2982 pSASPage0->u.fields.aPHY[i].u8PhyFlags = 0;
2983 pSASPage0->u.fields.aPHY[i].u8NegotiatedLinkRate = LSILOGICSCSI_SASIOUNIT0_NEGOTIATED_RATE_FAILED;
2984 pSASPage0->u.fields.aPHY[i].u32ControllerPhyDeviceInfo = LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_SET(LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_NO);
2985 pSASPage0->u.fields.aPHY[i].u16ControllerDevHandle = u16ControllerHandle;
2986 pSASPage0->u.fields.aPHY[i].u16AttachedDevHandle = 0; /* No device attached. */
2987 pSASPage0->u.fields.aPHY[i].u32DiscoveryStatus = 0; /* No errors */
2988
2989 pSASPage1->u.fields.aPHY[i].u8Port = i;
2990 pSASPage1->u.fields.aPHY[i].u8PortFlags = 0;
2991 pSASPage1->u.fields.aPHY[i].u8PhyFlags = 0;
2992 pSASPage1->u.fields.aPHY[i].u8MaxMinLinkRate = LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MIN_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_15GB)
2993 | LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MAX_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_30GB);
2994 pSASPage1->u.fields.aPHY[i].u32ControllerPhyDeviceInfo = LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_SET(LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_NO);
2995
2996 /* SAS PHY page 0. */
2997 pPHYPages->SASPHYPage0.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
2998 | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
2999 pPHYPages->SASPHYPage0.u.fields.ExtHeader.u8PageNumber = 0;
3000 pPHYPages->SASPHYPage0.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASPHYS;
3001 pPHYPages->SASPHYPage0.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASPHY0) / 4;
3002 pPHYPages->SASPHYPage0.u.fields.u8AttachedPhyIdentifier = i;
3003 pPHYPages->SASPHYPage0.u.fields.u32AttachedDeviceInfo = LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_SET(LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_NO);
3004 pPHYPages->SASPHYPage0.u.fields.u8ProgrammedLinkRate = LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MIN_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_15GB)
3005 | LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MAX_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_30GB);
3006 pPHYPages->SASPHYPage0.u.fields.u8HwLinkRate = LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MIN_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_15GB)
3007 | LSILOGICSCSI_SASIOUNIT1_LINK_RATE_MAX_SET(LSILOGICSCSI_SASIOUNIT1_LINK_RATE_30GB);
3008
3009 /* SAS PHY page 1. */
3010 pPHYPages->SASPHYPage1.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
3011 | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
3012 pPHYPages->SASPHYPage1.u.fields.ExtHeader.u8PageNumber = 1;
3013 pPHYPages->SASPHYPage1.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASPHYS;
3014 pPHYPages->SASPHYPage1.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASPHY1) / 4;
3015
3016 /* Settings for present devices. */
3017 if (pThis->paDeviceStates[i].pDrvBase)
3018 {
3019 uint16_t u16DeviceHandle = lsilogicGetHandle(pThis);
3020 SASADDRESS SASAddress;
3021 PMptSASDevice pSASDevice = (PMptSASDevice)RTMemAllocZ(sizeof(MptSASDevice));
3022 AssertPtr(pSASDevice);
3023
3024 memset(&SASAddress, 0, sizeof(SASADDRESS));
3025 lsilogicSASAddressGenerate(&SASAddress, i);
3026
3027 pSASPage0->u.fields.aPHY[i].u8NegotiatedLinkRate = LSILOGICSCSI_SASIOUNIT0_NEGOTIATED_RATE_SET(LSILOGICSCSI_SASIOUNIT0_NEGOTIATED_RATE_30GB);
3028 pSASPage0->u.fields.aPHY[i].u32ControllerPhyDeviceInfo = LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_SET(LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_END)
3029 | LSILOGICSCSI_SASIOUNIT0_DEVICE_SSP_TARGET;
3030 pSASPage0->u.fields.aPHY[i].u16AttachedDevHandle = u16DeviceHandle;
3031 pSASPage1->u.fields.aPHY[i].u32ControllerPhyDeviceInfo = LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_SET(LSILOGICSCSI_SASIOUNIT0_DEVICE_TYPE_END)
3032 | LSILOGICSCSI_SASIOUNIT0_DEVICE_SSP_TARGET;
3033 pSASPage0->u.fields.aPHY[i].u16ControllerDevHandle = u16DeviceHandle;
3034
3035 pPHYPages->SASPHYPage0.u.fields.u32AttachedDeviceInfo = LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_SET(LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_END);
3036 pPHYPages->SASPHYPage0.u.fields.SASAddress = SASAddress;
3037 pPHYPages->SASPHYPage0.u.fields.u16OwnerDevHandle = u16DeviceHandle;
3038 pPHYPages->SASPHYPage0.u.fields.u16AttachedDevHandle = u16DeviceHandle;
3039
3040 /* SAS device page 0. */
3041 pSASDevice->SASDevicePage0.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
3042 | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
3043 pSASDevice->SASDevicePage0.u.fields.ExtHeader.u8PageNumber = 0;
3044 pSASDevice->SASDevicePage0.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASDEVICE;
3045 pSASDevice->SASDevicePage0.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASDevice0) / 4;
3046 pSASDevice->SASDevicePage0.u.fields.SASAddress = SASAddress;
3047 pSASDevice->SASDevicePage0.u.fields.u16ParentDevHandle = u16ControllerHandle;
3048 pSASDevice->SASDevicePage0.u.fields.u8PhyNum = i;
3049 pSASDevice->SASDevicePage0.u.fields.u8AccessStatus = LSILOGICSCSI_SASDEVICE0_STATUS_NO_ERRORS;
3050 pSASDevice->SASDevicePage0.u.fields.u16DevHandle = u16DeviceHandle;
3051 pSASDevice->SASDevicePage0.u.fields.u8TargetID = i;
3052 pSASDevice->SASDevicePage0.u.fields.u8Bus = 0;
3053 pSASDevice->SASDevicePage0.u.fields.u32DeviceInfo = LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_SET(LSILOGICSCSI_SASPHY0_DEV_INFO_DEVICE_TYPE_END)
3054 | LSILOGICSCSI_SASIOUNIT0_DEVICE_SSP_TARGET;
3055 pSASDevice->SASDevicePage0.u.fields.u16Flags = LSILOGICSCSI_SASDEVICE0_FLAGS_DEVICE_PRESENT
3056 | LSILOGICSCSI_SASDEVICE0_FLAGS_DEVICE_MAPPED_TO_BUS_AND_TARGET_ID
3057 | LSILOGICSCSI_SASDEVICE0_FLAGS_DEVICE_MAPPING_PERSISTENT;
3058 pSASDevice->SASDevicePage0.u.fields.u8PhysicalPort = i;
3059
3060 /* SAS device page 1. */
3061 pSASDevice->SASDevicePage1.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
3062 | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
3063 pSASDevice->SASDevicePage1.u.fields.ExtHeader.u8PageNumber = 1;
3064 pSASDevice->SASDevicePage1.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASDEVICE;
3065 pSASDevice->SASDevicePage1.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASDevice1) / 4;
3066 pSASDevice->SASDevicePage1.u.fields.SASAddress = SASAddress;
3067 pSASDevice->SASDevicePage1.u.fields.u16DevHandle = u16DeviceHandle;
3068 pSASDevice->SASDevicePage1.u.fields.u8TargetID = i;
3069 pSASDevice->SASDevicePage1.u.fields.u8Bus = 0;
3070
3071 /* SAS device page 2. */
3072 pSASDevice->SASDevicePage2.u.fields.ExtHeader.u8PageType = MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY
3073 | MPT_CONFIGURATION_PAGE_TYPE_EXTENDED;
3074 pSASDevice->SASDevicePage2.u.fields.ExtHeader.u8PageNumber = 2;
3075 pSASDevice->SASDevicePage2.u.fields.ExtHeader.u8ExtPageType = MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASDEVICE;
3076 pSASDevice->SASDevicePage2.u.fields.ExtHeader.u16ExtPageLength = sizeof(MptConfigurationPageSASDevice2) / 4;
3077 pSASDevice->SASDevicePage2.u.fields.SASAddress = SASAddress;
3078
3079 /* Link into device list. */
3080 if (!pPages->cDevices)
3081 {
3082 pPages->pSASDeviceHead = pSASDevice;
3083 pPages->pSASDeviceTail = pSASDevice;
3084 pPages->cDevices = 1;
3085 }
3086 else
3087 {
3088 pSASDevice->pPrev = pPages->pSASDeviceTail;
3089 pPages->pSASDeviceTail->pNext = pSASDevice;
3090 pPages->pSASDeviceTail = pSASDevice;
3091 pPages->cDevices++;
3092 }
3093 }
3094 }
3095}
3096
3097/**
3098 * Initializes the configuration pages.
3099 *
3100 * @returns nothing
3101 * @param pThis Pointer to the LsiLogic device state.
3102 */
3103static void lsilogicR3InitializeConfigurationPages(PLSILOGICSCSI pThis)
3104{
3105 /* Initialize the common pages. */
3106 PMptConfigurationPagesSupported pPages = (PMptConfigurationPagesSupported)RTMemAllocZ(sizeof(MptConfigurationPagesSupported));
3107
3108 pThis->pConfigurationPages = pPages;
3109
3110 LogFlowFunc(("pThis=%#p\n", pThis));
3111
3112 /* Clear everything first. */
3113 memset(pPages, 0, sizeof(MptConfigurationPagesSupported));
3114
3115 /* Manufacturing Page 0. */
3116 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage0,
3117 MptConfigurationPageManufacturing0, 0,
3118 MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
3119 strncpy((char *)pPages->ManufacturingPage0.u.fields.abChipName, "VBox MPT Fusion", 16);
3120 strncpy((char *)pPages->ManufacturingPage0.u.fields.abChipRevision, "1.0", 8);
3121 strncpy((char *)pPages->ManufacturingPage0.u.fields.abBoardName, "VBox MPT Fusion", 16);
3122 strncpy((char *)pPages->ManufacturingPage0.u.fields.abBoardAssembly, "SUN", 8);
3123 strncpy((char *)pPages->ManufacturingPage0.u.fields.abBoardTracerNumber, "CAFECAFECAFECAFE", 16);
3124
3125 /* Manufacturing Page 1 - I don't know what this contains so we leave it 0 for now. */
3126 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage1,
3127 MptConfigurationPageManufacturing1, 1,
3128 MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
3129
3130 /* Manufacturing Page 2. */
3131 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage2,
3132 MptConfigurationPageManufacturing2, 2,
3133 MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
3134
3135 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
3136 {
3137 pPages->ManufacturingPage2.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SPI_DEVICE_ID;
3138 pPages->ManufacturingPage2.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SPI_REVISION_ID;
3139 }
3140 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
3141 {
3142 pPages->ManufacturingPage2.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SAS_DEVICE_ID;
3143 pPages->ManufacturingPage2.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SAS_REVISION_ID;
3144 }
3145
3146 /* Manufacturing Page 3. */
3147 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage3,
3148 MptConfigurationPageManufacturing3, 3,
3149 MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
3150
3151 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
3152 {
3153 pPages->ManufacturingPage3.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SPI_DEVICE_ID;
3154 pPages->ManufacturingPage3.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SPI_REVISION_ID;
3155 }
3156 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
3157 {
3158 pPages->ManufacturingPage3.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SAS_DEVICE_ID;
3159 pPages->ManufacturingPage3.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SAS_REVISION_ID;
3160 }
3161
3162 /* Manufacturing Page 4 - I don't know what this contains so we leave it 0 for now. */
3163 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage4,
3164 MptConfigurationPageManufacturing4, 4,
3165 MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
3166
3167 /* Manufacturing Page 5 - WWID settings. */
3168 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage5,
3169 MptConfigurationPageManufacturing5, 5,
3170 MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
3171
3172 /* Manufacturing Page 6 - Product specific settings. */
3173 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage6,
3174 MptConfigurationPageManufacturing6, 6,
3175 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3176
3177 /* Manufacturing Page 8 - Product specific settings. */
3178 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage8,
3179 MptConfigurationPageManufacturing8, 8,
3180 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3181
3182 /* Manufacturing Page 9 - Product specific settings. */
3183 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage9,
3184 MptConfigurationPageManufacturing9, 9,
3185 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3186
3187 /* Manufacturing Page 10 - Product specific settings. */
3188 MPT_CONFIG_PAGE_HEADER_INIT_MANUFACTURING(&pPages->ManufacturingPage10,
3189 MptConfigurationPageManufacturing10, 10,
3190 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3191
3192 /* I/O Unit page 0. */
3193 MPT_CONFIG_PAGE_HEADER_INIT_IO_UNIT(&pPages->IOUnitPage0,
3194 MptConfigurationPageIOUnit0, 0,
3195 MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
3196 pPages->IOUnitPage0.u.fields.u64UniqueIdentifier = 0xcafe;
3197
3198 /* I/O Unit page 1. */
3199 MPT_CONFIG_PAGE_HEADER_INIT_IO_UNIT(&pPages->IOUnitPage1,
3200 MptConfigurationPageIOUnit1, 1,
3201 MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
3202 pPages->IOUnitPage1.u.fields.fSingleFunction = true;
3203 pPages->IOUnitPage1.u.fields.fAllPathsMapped = false;
3204 pPages->IOUnitPage1.u.fields.fIntegratedRAIDDisabled = true;
3205 pPages->IOUnitPage1.u.fields.f32BitAccessForced = false;
3206
3207 /* I/O Unit page 2. */
3208 MPT_CONFIG_PAGE_HEADER_INIT_IO_UNIT(&pPages->IOUnitPage2,
3209 MptConfigurationPageIOUnit2, 2,
3210 MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT);
3211 pPages->IOUnitPage2.u.fields.fPauseOnError = false;
3212 pPages->IOUnitPage2.u.fields.fVerboseModeEnabled = false;
3213 pPages->IOUnitPage2.u.fields.fDisableColorVideo = false;
3214 pPages->IOUnitPage2.u.fields.fNotHookInt40h = false;
3215 pPages->IOUnitPage2.u.fields.u32BIOSVersion = 0xcafecafe;
3216 pPages->IOUnitPage2.u.fields.aAdapterOrder[0].fAdapterEnabled = true;
3217 pPages->IOUnitPage2.u.fields.aAdapterOrder[0].fAdapterEmbedded = true;
3218 pPages->IOUnitPage2.u.fields.aAdapterOrder[0].u8PCIBusNumber = 0;
3219 pPages->IOUnitPage2.u.fields.aAdapterOrder[0].u8PCIDevFn = pThis->PciDev.devfn;
3220
3221 /* I/O Unit page 3. */
3222 MPT_CONFIG_PAGE_HEADER_INIT_IO_UNIT(&pPages->IOUnitPage3,
3223 MptConfigurationPageIOUnit3, 3,
3224 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3225 pPages->IOUnitPage3.u.fields.u8GPIOCount = 0;
3226
3227 /* I/O Unit page 4. */
3228 MPT_CONFIG_PAGE_HEADER_INIT_IO_UNIT(&pPages->IOUnitPage4,
3229 MptConfigurationPageIOUnit4, 4,
3230 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3231
3232 /* IOC page 0. */
3233 MPT_CONFIG_PAGE_HEADER_INIT_IOC(&pPages->IOCPage0,
3234 MptConfigurationPageIOC0, 0,
3235 MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
3236 pPages->IOCPage0.u.fields.u32TotalNVStore = 0;
3237 pPages->IOCPage0.u.fields.u32FreeNVStore = 0;
3238
3239 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
3240 {
3241 pPages->IOCPage0.u.fields.u16VendorId = LSILOGICSCSI_PCI_VENDOR_ID;
3242 pPages->IOCPage0.u.fields.u16DeviceId = LSILOGICSCSI_PCI_SPI_DEVICE_ID;
3243 pPages->IOCPage0.u.fields.u8RevisionId = LSILOGICSCSI_PCI_SPI_REVISION_ID;
3244 pPages->IOCPage0.u.fields.u32ClassCode = LSILOGICSCSI_PCI_SPI_CLASS_CODE;
3245 pPages->IOCPage0.u.fields.u16SubsystemVendorId = LSILOGICSCSI_PCI_SPI_SUBSYSTEM_VENDOR_ID;
3246 pPages->IOCPage0.u.fields.u16SubsystemId = LSILOGICSCSI_PCI_SPI_SUBSYSTEM_ID;
3247 }
3248 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
3249 {
3250 pPages->IOCPage0.u.fields.u16VendorId = LSILOGICSCSI_PCI_VENDOR_ID;
3251 pPages->IOCPage0.u.fields.u16DeviceId = LSILOGICSCSI_PCI_SAS_DEVICE_ID;
3252 pPages->IOCPage0.u.fields.u8RevisionId = LSILOGICSCSI_PCI_SAS_REVISION_ID;
3253 pPages->IOCPage0.u.fields.u32ClassCode = LSILOGICSCSI_PCI_SAS_CLASS_CODE;
3254 pPages->IOCPage0.u.fields.u16SubsystemVendorId = LSILOGICSCSI_PCI_SAS_SUBSYSTEM_VENDOR_ID;
3255 pPages->IOCPage0.u.fields.u16SubsystemId = LSILOGICSCSI_PCI_SAS_SUBSYSTEM_ID;
3256 }
3257
3258 /* IOC page 1. */
3259 MPT_CONFIG_PAGE_HEADER_INIT_IOC(&pPages->IOCPage1,
3260 MptConfigurationPageIOC1, 1,
3261 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3262 pPages->IOCPage1.u.fields.fReplyCoalescingEnabled = false;
3263 pPages->IOCPage1.u.fields.u32CoalescingTimeout = 0;
3264 pPages->IOCPage1.u.fields.u8CoalescingDepth = 0;
3265
3266 /* IOC page 2. */
3267 MPT_CONFIG_PAGE_HEADER_INIT_IOC(&pPages->IOCPage2,
3268 MptConfigurationPageIOC2, 2,
3269 MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
3270 /* Everything else here is 0. */
3271
3272 /* IOC page 3. */
3273 MPT_CONFIG_PAGE_HEADER_INIT_IOC(&pPages->IOCPage3,
3274 MptConfigurationPageIOC3, 3,
3275 MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
3276 /* Everything else here is 0. */
3277
3278 /* IOC page 4. */
3279 MPT_CONFIG_PAGE_HEADER_INIT_IOC(&pPages->IOCPage4,
3280 MptConfigurationPageIOC4, 4,
3281 MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
3282 /* Everything else here is 0. */
3283
3284 /* IOC page 6. */
3285 MPT_CONFIG_PAGE_HEADER_INIT_IOC(&pPages->IOCPage6,
3286 MptConfigurationPageIOC6, 6,
3287 MPT_CONFIGURATION_PAGE_ATTRIBUTE_READONLY);
3288 /* Everything else here is 0. */
3289
3290 /* BIOS page 1. */
3291 MPT_CONFIG_PAGE_HEADER_INIT_BIOS(&pPages->BIOSPage1,
3292 MptConfigurationPageBIOS1, 1,
3293 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3294
3295 /* BIOS page 2. */
3296 MPT_CONFIG_PAGE_HEADER_INIT_BIOS(&pPages->BIOSPage2,
3297 MptConfigurationPageBIOS2, 2,
3298 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3299
3300 /* BIOS page 4. */
3301 MPT_CONFIG_PAGE_HEADER_INIT_BIOS(&pPages->BIOSPage4,
3302 MptConfigurationPageBIOS4, 4,
3303 MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
3304
3305 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
3306 lsilogicR3InitializeConfigurationPagesSpi(pThis);
3307 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
3308 lsilogicR3InitializeConfigurationPagesSas(pThis);
3309 else
3310 AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
3311}
3312
3313/**
3314 * @callback_method_impl{FNPDMQUEUEDEV, Transmit queue consumer.}
3315 */
3316static DECLCALLBACK(bool) lsilogicR3NotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
3317{
3318 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
3319 int rc = VINF_SUCCESS;
3320
3321 LogFlowFunc(("pDevIns=%#p pItem=%#p\n", pDevIns, pItem));
3322
3323 /* Reset notification event. */
3324 ASMAtomicXchgBool(&pThis->fNotificationSend, false);
3325
3326 /* Only process request which arrived before we received the notification. */
3327 uint32_t uRequestQueueNextEntryWrite = ASMAtomicReadU32(&pThis->uRequestQueueNextEntryFreeWrite);
3328
3329 /* Go through the messages now and process them. */
3330 while ( RT_LIKELY(pThis->enmState == LSILOGICSTATE_OPERATIONAL)
3331 && (pThis->uRequestQueueNextAddressRead != uRequestQueueNextEntryWrite))
3332 {
3333 uint32_t u32RequestMessageFrameDesc = pThis->CTX_SUFF(pRequestQueueBase)[pThis->uRequestQueueNextAddressRead];
3334 RTGCPHYS GCPhysMessageFrameAddr = LSILOGIC_RTGCPHYS_FROM_U32(pThis->u32HostMFAHighAddr,
3335 (u32RequestMessageFrameDesc & ~0x07));
3336
3337 PLSILOGICREQ pLsiReq;
3338
3339 /* Get new task state. */
3340 rc = RTMemCacheAllocEx(pThis->hTaskCache, (void **)&pLsiReq);
3341 AssertRC(rc);
3342
3343 pLsiReq->GCPhysMessageFrameAddr = GCPhysMessageFrameAddr;
3344
3345 /* Read the message header from the guest first. */
3346 PDMDevHlpPhysRead(pDevIns, GCPhysMessageFrameAddr, &pLsiReq->GuestRequest, sizeof(MptMessageHdr));
3347
3348 /* Determine the size of the request. */
3349 uint32_t cbRequest = 0;
3350
3351 switch (pLsiReq->GuestRequest.Header.u8Function)
3352 {
3353 case MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST:
3354 cbRequest = sizeof(MptSCSIIORequest);
3355 break;
3356 case MPT_MESSAGE_HDR_FUNCTION_SCSI_TASK_MGMT:
3357 cbRequest = sizeof(MptSCSITaskManagementRequest);
3358 break;
3359 case MPT_MESSAGE_HDR_FUNCTION_IOC_INIT:
3360 cbRequest = sizeof(MptIOCInitRequest);
3361 break;
3362 case MPT_MESSAGE_HDR_FUNCTION_IOC_FACTS:
3363 cbRequest = sizeof(MptIOCFactsRequest);
3364 break;
3365 case MPT_MESSAGE_HDR_FUNCTION_CONFIG:
3366 cbRequest = sizeof(MptConfigurationRequest);
3367 break;
3368 case MPT_MESSAGE_HDR_FUNCTION_PORT_FACTS:
3369 cbRequest = sizeof(MptPortFactsRequest);
3370 break;
3371 case MPT_MESSAGE_HDR_FUNCTION_PORT_ENABLE:
3372 cbRequest = sizeof(MptPortEnableRequest);
3373 break;
3374 case MPT_MESSAGE_HDR_FUNCTION_EVENT_NOTIFICATION:
3375 cbRequest = sizeof(MptEventNotificationRequest);
3376 break;
3377 case MPT_MESSAGE_HDR_FUNCTION_EVENT_ACK:
3378 AssertMsgFailed(("todo\n"));
3379 //cbRequest = sizeof(MptEventAckRequest);
3380 break;
3381 case MPT_MESSAGE_HDR_FUNCTION_FW_DOWNLOAD:
3382 cbRequest = sizeof(MptFWDownloadRequest);
3383 break;
3384 case MPT_MESSAGE_HDR_FUNCTION_FW_UPLOAD:
3385 cbRequest = sizeof(MptFWUploadRequest);
3386 break;
3387 default:
3388 AssertMsgFailed(("Unknown function issued %u\n", pLsiReq->GuestRequest.Header.u8Function));
3389 lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INVALID_FUNCTION);
3390 }
3391
3392 if (cbRequest != 0)
3393 {
3394 /* Read the complete message frame from guest memory now. */
3395 PDMDevHlpPhysRead(pDevIns, GCPhysMessageFrameAddr, &pLsiReq->GuestRequest, cbRequest);
3396
3397 /* Handle SCSI I/O requests now. */
3398 if (pLsiReq->GuestRequest.Header.u8Function == MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST)
3399 {
3400 rc = lsilogicR3ProcessSCSIIORequest(pThis, pLsiReq);
3401 AssertRC(rc);
3402 }
3403 else
3404 {
3405 MptReplyUnion Reply;
3406 rc = lsilogicR3ProcessMessageRequest(pThis, &pLsiReq->GuestRequest.Header, &Reply);
3407 AssertRC(rc);
3408 RTMemCacheFree(pThis->hTaskCache, pLsiReq);
3409 }
3410
3411 pThis->uRequestQueueNextAddressRead++;
3412 pThis->uRequestQueueNextAddressRead %= pThis->cRequestQueueEntries;
3413 }
3414 }
3415
3416 return true;
3417}
3418
3419/**
3420 * Sets the emulated controller type from a given string.
3421 *
3422 * @returns VBox status code.
3423 *
3424 * @param pThis Pointer to the LsiLogic device state.
3425 * @param pcszCtrlType The string to use.
3426 */
3427static int lsilogicR3GetCtrlTypeFromString(PLSILOGICSCSI pThis, const char *pcszCtrlType)
3428{
3429 int rc = VERR_INVALID_PARAMETER;
3430
3431 if (!RTStrCmp(pcszCtrlType, LSILOGICSCSI_PCI_SPI_CTRLNAME))
3432 {
3433 pThis->enmCtrlType = LSILOGICCTRLTYPE_SCSI_SPI;
3434 rc = VINF_SUCCESS;
3435 }
3436 else if (!RTStrCmp(pcszCtrlType, LSILOGICSCSI_PCI_SAS_CTRLNAME))
3437 {
3438 pThis->enmCtrlType = LSILOGICCTRLTYPE_SCSI_SAS;
3439 rc = VINF_SUCCESS;
3440 }
3441
3442 return rc;
3443}
3444
3445/**
3446 * @callback_method_impl{FNIOMIOPORTIN, Legacy ISA port.}
3447 */
3448static DECLCALLBACK(int) lsilogicR3IsaIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3449{
3450 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
3451
3452 Assert(cb == 1);
3453
3454 uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
3455 ? Port - LSILOGIC_BIOS_IO_PORT
3456 : Port - LSILOGIC_SAS_BIOS_IO_PORT;
3457 int rc = vboxscsiReadRegister(&pThis->VBoxSCSI, iRegister, pu32);
3458
3459 Log2(("%s: pu32=%p:{%.*Rhxs} iRegister=%d rc=%Rrc\n",
3460 __FUNCTION__, pu32, 1, pu32, iRegister, rc));
3461
3462 return rc;
3463}
3464
3465/**
3466 * Prepares a request from the BIOS.
3467 *
3468 * @returns VBox status code.
3469 * @param pThis Pointer to the LsiLogic device state.
3470 */
3471static int lsilogicR3PrepareBiosScsiRequest(PLSILOGICSCSI pThis)
3472{
3473 int rc;
3474 PLSILOGICREQ pLsiReq;
3475 uint32_t uTargetDevice;
3476
3477 rc = RTMemCacheAllocEx(pThis->hTaskCache, (void **)&pLsiReq);
3478 AssertMsgRCReturn(rc, ("Getting task from cache failed rc=%Rrc\n", rc), rc);
3479
3480 pLsiReq->fBIOS = true;
3481
3482 rc = vboxscsiSetupRequest(&pThis->VBoxSCSI, &pLsiReq->PDMScsiRequest, &uTargetDevice);
3483 AssertMsgRCReturn(rc, ("Setting up SCSI request failed rc=%Rrc\n", rc), rc);
3484
3485 pLsiReq->PDMScsiRequest.pvUser = pLsiReq;
3486
3487 if (uTargetDevice < pThis->cDeviceStates)
3488 {
3489 pLsiReq->pTargetDevice = &pThis->paDeviceStates[uTargetDevice];
3490
3491 if (pLsiReq->pTargetDevice->pDrvBase)
3492 {
3493 ASMAtomicIncU32(&pLsiReq->pTargetDevice->cOutstandingRequests);
3494
3495 rc = pLsiReq->pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pLsiReq->pTargetDevice->pDrvSCSIConnector,
3496 &pLsiReq->PDMScsiRequest);
3497 AssertMsgRCReturn(rc, ("Sending request to SCSI layer failed rc=%Rrc\n", rc), rc);
3498 return VINF_SUCCESS;
3499 }
3500 }
3501
3502 /* Device is not present. */
3503 AssertMsg(pLsiReq->PDMScsiRequest.pbCDB[0] == SCSI_INQUIRY,
3504 ("Device is not present but command is not inquiry\n"));
3505
3506 SCSIINQUIRYDATA ScsiInquiryData;
3507
3508 memset(&ScsiInquiryData, 0, sizeof(SCSIINQUIRYDATA));
3509 ScsiInquiryData.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN;
3510 ScsiInquiryData.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED;
3511
3512 memcpy(pThis->VBoxSCSI.pbBuf, &ScsiInquiryData, 5);
3513
3514 rc = vboxscsiRequestFinished(&pThis->VBoxSCSI, &pLsiReq->PDMScsiRequest, SCSI_STATUS_OK);
3515 AssertMsgRCReturn(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc), rc);
3516
3517 RTMemCacheFree(pThis->hTaskCache, pLsiReq);
3518 return rc;
3519}
3520
3521/**
3522 * @callback_method_impl{FNIOMIOPORTOUT, Legacy ISA port.}
3523 */
3524static DECLCALLBACK(int) lsilogicR3IsaIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3525{
3526 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
3527 Log2(("#%d %s: pvUser=%#p cb=%d u32=%#x Port=%#x\n", pDevIns->iInstance, __FUNCTION__, pvUser, cb, u32, Port));
3528
3529 Assert(cb == 1);
3530
3531 uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
3532 ? Port - LSILOGIC_BIOS_IO_PORT
3533 : Port - LSILOGIC_SAS_BIOS_IO_PORT;
3534 int rc = vboxscsiWriteRegister(&pThis->VBoxSCSI, iRegister, (uint8_t)u32);
3535 if (rc == VERR_MORE_DATA)
3536 {
3537 rc = lsilogicR3PrepareBiosScsiRequest(pThis);
3538 AssertRC(rc);
3539 }
3540 else if (RT_FAILURE(rc))
3541 AssertMsgFailed(("Writing BIOS register failed %Rrc\n", rc));
3542
3543 return VINF_SUCCESS;
3544}
3545
3546/**
3547 * @callback_method_impl{FNIOMIOPORTOUTSTRING,
3548 * Port I/O Handler for primary port range OUT string operations.}
3549 */
3550static DECLCALLBACK(int) lsilogicR3IsaIOPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port,
3551 PRTGCPTR pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb)
3552{
3553 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
3554 Log2(("#%d %s: pvUser=%#p cb=%d Port=%#x\n", pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port));
3555
3556 uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
3557 ? Port - LSILOGIC_BIOS_IO_PORT
3558 : Port - LSILOGIC_SAS_BIOS_IO_PORT;
3559 int rc = vboxscsiWriteString(pDevIns, &pThis->VBoxSCSI, iRegister, pGCPtrSrc, pcTransfer, cb);
3560 if (rc == VERR_MORE_DATA)
3561 {
3562 rc = lsilogicR3PrepareBiosScsiRequest(pThis);
3563 AssertRC(rc);
3564 }
3565 else if (RT_FAILURE(rc))
3566 AssertMsgFailed(("Writing BIOS register failed %Rrc\n", rc));
3567
3568 return rc;
3569}
3570
3571/**
3572 * @callback_method_impl{FNIOMIOPORTINSTRING,
3573 * Port I/O Handler for primary port range IN string operations.}
3574 */
3575static DECLCALLBACK(int) lsilogicR3IsaIOPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst,
3576 PRTGCUINTREG pcTransfer, unsigned cb)
3577{
3578 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
3579
3580 LogFlowFunc(("#%d %s: pvUser=%#p cb=%d Port=%#x\n",
3581 pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port));
3582
3583 uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
3584 ? Port - LSILOGIC_BIOS_IO_PORT
3585 : Port - LSILOGIC_SAS_BIOS_IO_PORT;
3586 return vboxscsiReadString(pDevIns, &pThis->VBoxSCSI, iRegister, pGCPtrDst, pcTransfer, cb);
3587}
3588
3589/**
3590 * @callback_method_impl{FNPCIIOREGIONMAP}
3591 */
3592static DECLCALLBACK(int) lsilogicR3Map(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion,
3593 RTGCPHYS GCPhysAddress, uint32_t cb,
3594 PCIADDRESSSPACE enmType)
3595{
3596 PPDMDEVINS pDevIns = pPciDev->pDevIns;
3597 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
3598 int rc = VINF_SUCCESS;
3599 const char *pcszCtrl = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
3600 ? "LsiLogic"
3601 : "LsiLogicSas";
3602 const char *pcszDiag = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
3603 ? "LsiLogicDiag"
3604 : "LsiLogicSasDiag";
3605
3606 Log2(("%s: registering area at GCPhysAddr=%RGp cb=%u\n", __FUNCTION__, GCPhysAddress, cb));
3607
3608 AssertMsg( (enmType == PCI_ADDRESS_SPACE_MEM && cb >= LSILOGIC_PCI_SPACE_MEM_SIZE)
3609 || (enmType == PCI_ADDRESS_SPACE_IO && cb >= LSILOGIC_PCI_SPACE_IO_SIZE),
3610 ("PCI region type and size do not match\n"));
3611
3612 if (enmType == PCI_ADDRESS_SPACE_MEM && iRegion == 1)
3613 {
3614 /*
3615 * Non-4-byte read access to LSILOGIC_REG_REPLY_QUEUE may cause real strange behavior
3616 * because the data is part of a physical guest address. But some drivers use 1-byte
3617 * access to scan for SCSI controllers. So, we simplify our code by telling IOM to
3618 * read DWORDs.
3619 *
3620 * Regarding writes, we couldn't find anything specific in the specs about what should
3621 * happen. So far we've ignored unaligned writes and assumed the missing bytes of
3622 * byte and word access to be zero. We suspect that IOMMMIO_FLAGS_WRITE_ONLY_DWORD
3623 * or IOMMMIO_FLAGS_WRITE_DWORD_ZEROED would be the most appropriate here, but since we
3624 * don't have real hw to test one, the old behavior is kept exactly like it used to be.
3625 */
3626 /** @todo Check out unaligned writes and non-dword writes on real LsiLogic
3627 * hardware. */
3628 rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/,
3629 IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_PASSTHRU,
3630 lsilogicMMIOWrite, lsilogicMMIORead, pcszCtrl);
3631 if (RT_FAILURE(rc))
3632 return rc;
3633
3634 if (pThis->fR0Enabled)
3635 {
3636 rc = PDMDevHlpMMIORegisterR0(pDevIns, GCPhysAddress, cb, NIL_RTR0PTR /*pvUser*/,
3637 "lsilogicMMIOWrite", "lsilogicMMIORead");
3638 if (RT_FAILURE(rc))
3639 return rc;
3640 }
3641
3642 if (pThis->fGCEnabled)
3643 {
3644 rc = PDMDevHlpMMIORegisterRC(pDevIns, GCPhysAddress, cb, NIL_RTRCPTR /*pvUser*/,
3645 "lsilogicMMIOWrite", "lsilogicMMIORead");
3646 if (RT_FAILURE(rc))
3647 return rc;
3648 }
3649
3650 pThis->GCPhysMMIOBase = GCPhysAddress;
3651 }
3652 else if (enmType == PCI_ADDRESS_SPACE_MEM && iRegion == 2)
3653 {
3654 /* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
3655 rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/,
3656 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
3657 lsilogicDiagnosticWrite, lsilogicDiagnosticRead, pcszDiag);
3658 if (RT_FAILURE(rc))
3659 return rc;
3660
3661 if (pThis->fR0Enabled)
3662 {
3663 rc = PDMDevHlpMMIORegisterR0(pDevIns, GCPhysAddress, cb, NIL_RTR0PTR /*pvUser*/,
3664 "lsilogicDiagnosticWrite", "lsilogicDiagnosticRead");
3665 if (RT_FAILURE(rc))
3666 return rc;
3667 }
3668
3669 if (pThis->fGCEnabled)
3670 {
3671 rc = PDMDevHlpMMIORegisterRC(pDevIns, GCPhysAddress, cb, NIL_RTRCPTR /*pvUser*/,
3672 "lsilogicDiagnosticWrite", "lsilogicDiagnosticRead");
3673 if (RT_FAILURE(rc))
3674 return rc;
3675 }
3676 }
3677 else if (enmType == PCI_ADDRESS_SPACE_IO)
3678 {
3679 rc = PDMDevHlpIOPortRegister(pDevIns, (RTIOPORT)GCPhysAddress, LSILOGIC_PCI_SPACE_IO_SIZE,
3680 NULL, lsilogicIOPortWrite, lsilogicIOPortRead, NULL, NULL, pcszCtrl);
3681 if (RT_FAILURE(rc))
3682 return rc;
3683
3684 if (pThis->fR0Enabled)
3685 {
3686 rc = PDMDevHlpIOPortRegisterR0(pDevIns, (RTIOPORT)GCPhysAddress, LSILOGIC_PCI_SPACE_IO_SIZE,
3687 0, "lsilogicIOPortWrite", "lsilogicIOPortRead", NULL, NULL, pcszCtrl);
3688 if (RT_FAILURE(rc))
3689 return rc;
3690 }
3691
3692 if (pThis->fGCEnabled)
3693 {
3694 rc = PDMDevHlpIOPortRegisterRC(pDevIns, (RTIOPORT)GCPhysAddress, LSILOGIC_PCI_SPACE_IO_SIZE,
3695 0, "lsilogicIOPortWrite", "lsilogicIOPortRead", NULL, NULL, pcszCtrl);
3696 if (RT_FAILURE(rc))
3697 return rc;
3698 }
3699
3700 pThis->IOPortBase = (RTIOPORT)GCPhysAddress;
3701 }
3702 else
3703 AssertMsgFailed(("Invalid enmType=%d iRegion=%d\n", enmType, iRegion));
3704
3705 return rc;
3706}
3707
3708/**
3709 * @callback_method_impl{PFNDBGFHANDLERDEV}
3710 */
3711static DECLCALLBACK(void) lsilogicR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
3712{
3713 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
3714 bool fVerbose = false;
3715
3716 /*
3717 * Parse args.
3718 */
3719 if (pszArgs)
3720 fVerbose = strstr(pszArgs, "verbose") != NULL;
3721
3722 /*
3723 * Show info.
3724 */
3725 pHlp->pfnPrintf(pHlp,
3726 "%s#%d: port=%RTiop mmio=%RGp max-devices=%u GC=%RTbool R0=%RTbool\n",
3727 pDevIns->pReg->szName,
3728 pDevIns->iInstance,
3729 pThis->IOPortBase, pThis->GCPhysMMIOBase,
3730 pThis->cDeviceStates,
3731 pThis->fGCEnabled ? true : false,
3732 pThis->fR0Enabled ? true : false);
3733
3734 /*
3735 * Show general state.
3736 */
3737 pHlp->pfnPrintf(pHlp, "enmState=%u\n", pThis->enmState);
3738 pHlp->pfnPrintf(pHlp, "enmWhoInit=%u\n", pThis->enmWhoInit);
3739 pHlp->pfnPrintf(pHlp, "enmDoorbellState=%d\n", pThis->enmDoorbellState);
3740 pHlp->pfnPrintf(pHlp, "fDiagnosticEnabled=%RTbool\n", pThis->fDiagnosticEnabled);
3741 pHlp->pfnPrintf(pHlp, "fNotificationSend=%RTbool\n", pThis->fNotificationSend);
3742 pHlp->pfnPrintf(pHlp, "fEventNotificationEnabled=%RTbool\n", pThis->fEventNotificationEnabled);
3743 pHlp->pfnPrintf(pHlp, "uInterruptMask=%#x\n", pThis->uInterruptMask);
3744 pHlp->pfnPrintf(pHlp, "uInterruptStatus=%#x\n", pThis->uInterruptStatus);
3745 pHlp->pfnPrintf(pHlp, "u16IOCFaultCode=%#06x\n", pThis->u16IOCFaultCode);
3746 pHlp->pfnPrintf(pHlp, "u32HostMFAHighAddr=%#x\n", pThis->u32HostMFAHighAddr);
3747 pHlp->pfnPrintf(pHlp, "u32SenseBufferHighAddr=%#x\n", pThis->u32SenseBufferHighAddr);
3748 pHlp->pfnPrintf(pHlp, "cMaxDevices=%u\n", pThis->cMaxDevices);
3749 pHlp->pfnPrintf(pHlp, "cMaxBuses=%u\n", pThis->cMaxBuses);
3750 pHlp->pfnPrintf(pHlp, "cbReplyFrame=%u\n", pThis->cbReplyFrame);
3751 pHlp->pfnPrintf(pHlp, "cReplyQueueEntries=%u\n", pThis->cReplyQueueEntries);
3752 pHlp->pfnPrintf(pHlp, "cRequestQueueEntries=%u\n", pThis->cRequestQueueEntries);
3753 pHlp->pfnPrintf(pHlp, "cPorts=%u\n", pThis->cPorts);
3754
3755 /*
3756 * Show queue status.
3757 */
3758 pHlp->pfnPrintf(pHlp, "uReplyFreeQueueNextEntryFreeWrite=%u\n", pThis->uReplyFreeQueueNextEntryFreeWrite);
3759 pHlp->pfnPrintf(pHlp, "uReplyFreeQueueNextAddressRead=%u\n", pThis->uReplyFreeQueueNextAddressRead);
3760 pHlp->pfnPrintf(pHlp, "uReplyPostQueueNextEntryFreeWrite=%u\n", pThis->uReplyPostQueueNextEntryFreeWrite);
3761 pHlp->pfnPrintf(pHlp, "uReplyPostQueueNextAddressRead=%u\n", pThis->uReplyPostQueueNextAddressRead);
3762 pHlp->pfnPrintf(pHlp, "uRequestQueueNextEntryFreeWrite=%u\n", pThis->uRequestQueueNextEntryFreeWrite);
3763 pHlp->pfnPrintf(pHlp, "uRequestQueueNextAddressRead=%u\n", pThis->uRequestQueueNextAddressRead);
3764
3765 /*
3766 * Show queue content if verbose
3767 */
3768 if (fVerbose)
3769 {
3770 for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
3771 pHlp->pfnPrintf(pHlp, "RFQ[%u]=%#x\n", i, pThis->pReplyFreeQueueBaseR3[i]);
3772
3773 pHlp->pfnPrintf(pHlp, "\n");
3774
3775 for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
3776 pHlp->pfnPrintf(pHlp, "RPQ[%u]=%#x\n", i, pThis->pReplyPostQueueBaseR3[i]);
3777
3778 pHlp->pfnPrintf(pHlp, "\n");
3779
3780 for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++)
3781 pHlp->pfnPrintf(pHlp, "ReqQ[%u]=%#x\n", i, pThis->pRequestQueueBaseR3[i]);
3782 }
3783
3784 /*
3785 * Print the device status.
3786 */
3787 for (unsigned i = 0; i < pThis->cDeviceStates; i++)
3788 {
3789 PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[i];
3790
3791 pHlp->pfnPrintf(pHlp, "\n");
3792
3793 pHlp->pfnPrintf(pHlp, "Device[%u]: device-attached=%RTbool cOutstandingRequests=%u\n",
3794 i, pDevice->pDrvBase != NULL, pDevice->cOutstandingRequests);
3795 }
3796}
3797
3798/**
3799 * Allocate the queues.
3800 *
3801 * @returns VBox status code.
3802 *
3803 * @param pThis Pointer to the LsiLogic device state.
3804 */
3805static int lsilogicR3QueuesAlloc(PLSILOGICSCSI pThis)
3806{
3807 PVM pVM = PDMDevHlpGetVM(pThis->pDevInsR3);
3808 uint32_t cbQueues;
3809
3810 Assert(!pThis->pReplyFreeQueueBaseR3);
3811
3812 cbQueues = 2*pThis->cReplyQueueEntries * sizeof(uint32_t);
3813 cbQueues += pThis->cRequestQueueEntries * sizeof(uint32_t);
3814 int rc = MMHyperAlloc(pVM, cbQueues, 1, MM_TAG_PDM_DEVICE_USER,
3815 (void **)&pThis->pReplyFreeQueueBaseR3);
3816 if (RT_FAILURE(rc))
3817 return VERR_NO_MEMORY;
3818 pThis->pReplyFreeQueueBaseR0 = MMHyperR3ToR0(pVM, (void *)pThis->pReplyFreeQueueBaseR3);
3819 pThis->pReplyFreeQueueBaseRC = MMHyperR3ToRC(pVM, (void *)pThis->pReplyFreeQueueBaseR3);
3820
3821 pThis->pReplyPostQueueBaseR3 = pThis->pReplyFreeQueueBaseR3 + pThis->cReplyQueueEntries;
3822 pThis->pReplyPostQueueBaseR0 = MMHyperR3ToR0(pVM, (void *)pThis->pReplyPostQueueBaseR3);
3823 pThis->pReplyPostQueueBaseRC = MMHyperR3ToRC(pVM, (void *)pThis->pReplyPostQueueBaseR3);
3824
3825 pThis->pRequestQueueBaseR3 = pThis->pReplyPostQueueBaseR3 + pThis->cReplyQueueEntries;
3826 pThis->pRequestQueueBaseR0 = MMHyperR3ToR0(pVM, (void *)pThis->pRequestQueueBaseR3);
3827 pThis->pRequestQueueBaseRC = MMHyperR3ToRC(pVM, (void *)pThis->pRequestQueueBaseR3);
3828
3829 return VINF_SUCCESS;
3830}
3831
3832/**
3833 * Free the hyper memory used or the queues.
3834 *
3835 * @returns nothing.
3836 *
3837 * @param pThis Pointer to the LsiLogic device state.
3838 */
3839static void lsilogicR3QueuesFree(PLSILOGICSCSI pThis)
3840{
3841 PVM pVM = PDMDevHlpGetVM(pThis->pDevInsR3);
3842 int rc = VINF_SUCCESS;
3843
3844 AssertPtr(pThis->pReplyFreeQueueBaseR3);
3845
3846 rc = MMHyperFree(pVM, (void *)pThis->pReplyFreeQueueBaseR3);
3847 AssertRC(rc);
3848
3849 pThis->pReplyFreeQueueBaseR3 = NULL;
3850 pThis->pReplyPostQueueBaseR3 = NULL;
3851 pThis->pRequestQueueBaseR3 = NULL;
3852}
3853
3854/**
3855 * Kicks the controller to process pending tasks after the VM was resumed
3856 * or loaded from a saved state.
3857 *
3858 * @returns nothing.
3859 * @param pThis Pointer to the LsiLogic device state.
3860 */
3861static void lsilogicR3Kick(PLSILOGICSCSI pThis)
3862{
3863 if (pThis->fNotificationSend)
3864 {
3865 /* Send a notifier to the PDM queue that there are pending requests. */
3866 PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(pThis->CTX_SUFF(pNotificationQueue));
3867 AssertMsg(pItem, ("Allocating item for queue failed\n"));
3868 PDMQueueInsert(pThis->CTX_SUFF(pNotificationQueue), (PPDMQUEUEITEMCORE)pItem);
3869 }
3870 else if (pThis->VBoxSCSI.fBusy)
3871 {
3872 /* The BIOS had a request active when we got suspended. Resume it. */
3873 int rc = lsilogicR3PrepareBiosScsiRequest(pThis);
3874 AssertRC(rc);
3875 }
3876
3877}
3878
3879
3880/*
3881 * Saved state.
3882 */
3883
3884/**
3885 * @callback_method_impl{FNSSMDEVLIVEEXEC}
3886 */
3887static DECLCALLBACK(int) lsilogicR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
3888{
3889 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
3890
3891 SSMR3PutU32(pSSM, pThis->enmCtrlType);
3892 SSMR3PutU32(pSSM, pThis->cDeviceStates);
3893 SSMR3PutU32(pSSM, pThis->cPorts);
3894
3895 /* Save the device config. */
3896 for (unsigned i = 0; i < pThis->cDeviceStates; i++)
3897 SSMR3PutBool(pSSM, pThis->paDeviceStates[i].pDrvBase != NULL);
3898
3899 return VINF_SSM_DONT_CALL_AGAIN;
3900}
3901
3902/**
3903 * @callback_method_impl{FNSSMDEVSAVEEXEC}
3904 */
3905static DECLCALLBACK(int) lsilogicR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
3906{
3907 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
3908
3909 /* Every device first. */
3910 lsilogicR3LiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
3911 for (unsigned i = 0; i < pThis->cDeviceStates; i++)
3912 {
3913 PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[i];
3914
3915 AssertMsg(!pDevice->cOutstandingRequests,
3916 ("There are still outstanding requests on this device\n"));
3917 SSMR3PutU32(pSSM, pDevice->cOutstandingRequests);
3918 }
3919 /* Now the main device state. */
3920 SSMR3PutU32 (pSSM, pThis->enmState);
3921 SSMR3PutU32 (pSSM, pThis->enmWhoInit);
3922 SSMR3PutU32 (pSSM, pThis->enmDoorbellState);
3923 SSMR3PutBool (pSSM, pThis->fDiagnosticEnabled);
3924 SSMR3PutBool (pSSM, pThis->fNotificationSend);
3925 SSMR3PutBool (pSSM, pThis->fEventNotificationEnabled);
3926 SSMR3PutU32 (pSSM, pThis->uInterruptMask);
3927 SSMR3PutU32 (pSSM, pThis->uInterruptStatus);
3928 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aMessage); i++)
3929 SSMR3PutU32 (pSSM, pThis->aMessage[i]);
3930 SSMR3PutU32 (pSSM, pThis->iMessage);
3931 SSMR3PutU32 (pSSM, pThis->cMessage);
3932 SSMR3PutMem (pSSM, &pThis->ReplyBuffer, sizeof(pThis->ReplyBuffer));
3933 SSMR3PutU32 (pSSM, pThis->uNextReplyEntryRead);
3934 SSMR3PutU32 (pSSM, pThis->cReplySize);
3935 SSMR3PutU16 (pSSM, pThis->u16IOCFaultCode);
3936 SSMR3PutU32 (pSSM, pThis->u32HostMFAHighAddr);
3937 SSMR3PutU32 (pSSM, pThis->u32SenseBufferHighAddr);
3938 SSMR3PutU8 (pSSM, pThis->cMaxDevices);
3939 SSMR3PutU8 (pSSM, pThis->cMaxBuses);
3940 SSMR3PutU16 (pSSM, pThis->cbReplyFrame);
3941 SSMR3PutU32 (pSSM, pThis->iDiagnosticAccess);
3942 SSMR3PutU32 (pSSM, pThis->cReplyQueueEntries);
3943 SSMR3PutU32 (pSSM, pThis->cRequestQueueEntries);
3944 SSMR3PutU32 (pSSM, pThis->uReplyFreeQueueNextEntryFreeWrite);
3945 SSMR3PutU32 (pSSM, pThis->uReplyFreeQueueNextAddressRead);
3946 SSMR3PutU32 (pSSM, pThis->uReplyPostQueueNextEntryFreeWrite);
3947 SSMR3PutU32 (pSSM, pThis->uReplyPostQueueNextAddressRead);
3948 SSMR3PutU32 (pSSM, pThis->uRequestQueueNextEntryFreeWrite);
3949 SSMR3PutU32 (pSSM, pThis->uRequestQueueNextAddressRead);
3950
3951 for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
3952 SSMR3PutU32(pSSM, pThis->pReplyFreeQueueBaseR3[i]);
3953 for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
3954 SSMR3PutU32(pSSM, pThis->pReplyPostQueueBaseR3[i]);
3955 for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++)
3956 SSMR3PutU32(pSSM, pThis->pRequestQueueBaseR3[i]);
3957
3958 SSMR3PutU16 (pSSM, pThis->u16NextHandle);
3959
3960 PMptConfigurationPagesSupported pPages = pThis->pConfigurationPages;
3961
3962 SSMR3PutMem (pSSM, &pPages->ManufacturingPage0, sizeof(MptConfigurationPageManufacturing0));
3963 SSMR3PutMem (pSSM, &pPages->ManufacturingPage1, sizeof(MptConfigurationPageManufacturing1));
3964 SSMR3PutMem (pSSM, &pPages->ManufacturingPage2, sizeof(MptConfigurationPageManufacturing2));
3965 SSMR3PutMem (pSSM, &pPages->ManufacturingPage3, sizeof(MptConfigurationPageManufacturing3));
3966 SSMR3PutMem (pSSM, &pPages->ManufacturingPage4, sizeof(MptConfigurationPageManufacturing4));
3967 SSMR3PutMem (pSSM, &pPages->ManufacturingPage5, sizeof(MptConfigurationPageManufacturing5));
3968 SSMR3PutMem (pSSM, &pPages->ManufacturingPage6, sizeof(MptConfigurationPageManufacturing6));
3969 SSMR3PutMem (pSSM, &pPages->ManufacturingPage8, sizeof(MptConfigurationPageManufacturing8));
3970 SSMR3PutMem (pSSM, &pPages->ManufacturingPage9, sizeof(MptConfigurationPageManufacturing9));
3971 SSMR3PutMem (pSSM, &pPages->ManufacturingPage10, sizeof(MptConfigurationPageManufacturing10));
3972 SSMR3PutMem (pSSM, &pPages->IOUnitPage0, sizeof(MptConfigurationPageIOUnit0));
3973 SSMR3PutMem (pSSM, &pPages->IOUnitPage1, sizeof(MptConfigurationPageIOUnit1));
3974 SSMR3PutMem (pSSM, &pPages->IOUnitPage2, sizeof(MptConfigurationPageIOUnit2));
3975 SSMR3PutMem (pSSM, &pPages->IOUnitPage3, sizeof(MptConfigurationPageIOUnit3));
3976 SSMR3PutMem (pSSM, &pPages->IOUnitPage4, sizeof(MptConfigurationPageIOUnit4));
3977 SSMR3PutMem (pSSM, &pPages->IOCPage0, sizeof(MptConfigurationPageIOC0));
3978 SSMR3PutMem (pSSM, &pPages->IOCPage1, sizeof(MptConfigurationPageIOC1));
3979 SSMR3PutMem (pSSM, &pPages->IOCPage2, sizeof(MptConfigurationPageIOC2));
3980 SSMR3PutMem (pSSM, &pPages->IOCPage3, sizeof(MptConfigurationPageIOC3));
3981 SSMR3PutMem (pSSM, &pPages->IOCPage4, sizeof(MptConfigurationPageIOC4));
3982 SSMR3PutMem (pSSM, &pPages->IOCPage6, sizeof(MptConfigurationPageIOC6));
3983 SSMR3PutMem (pSSM, &pPages->BIOSPage1, sizeof(MptConfigurationPageBIOS1));
3984 SSMR3PutMem (pSSM, &pPages->BIOSPage2, sizeof(MptConfigurationPageBIOS2));
3985 SSMR3PutMem (pSSM, &pPages->BIOSPage4, sizeof(MptConfigurationPageBIOS4));
3986
3987 /* Device dependent pages */
3988 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
3989 {
3990 PMptConfigurationPagesSpi pSpiPages = &pPages->u.SpiPages;
3991
3992 SSMR3PutMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage0, sizeof(MptConfigurationPageSCSISPIPort0));
3993 SSMR3PutMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage1, sizeof(MptConfigurationPageSCSISPIPort1));
3994 SSMR3PutMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage2, sizeof(MptConfigurationPageSCSISPIPort2));
3995
3996 for (unsigned i = 0; i < RT_ELEMENTS(pSpiPages->aBuses[0].aDevicePages); i++)
3997 {
3998 SSMR3PutMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage0, sizeof(MptConfigurationPageSCSISPIDevice0));
3999 SSMR3PutMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage1, sizeof(MptConfigurationPageSCSISPIDevice1));
4000 SSMR3PutMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage2, sizeof(MptConfigurationPageSCSISPIDevice2));
4001 SSMR3PutMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage3, sizeof(MptConfigurationPageSCSISPIDevice3));
4002 }
4003 }
4004 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
4005 {
4006 PMptConfigurationPagesSas pSasPages = &pPages->u.SasPages;
4007
4008 SSMR3PutU32(pSSM, pSasPages->cbManufacturingPage7);
4009 SSMR3PutU32(pSSM, pSasPages->cbSASIOUnitPage0);
4010 SSMR3PutU32(pSSM, pSasPages->cbSASIOUnitPage1);
4011
4012 SSMR3PutMem(pSSM, pSasPages->pManufacturingPage7, pSasPages->cbManufacturingPage7);
4013 SSMR3PutMem(pSSM, pSasPages->pSASIOUnitPage0, pSasPages->cbSASIOUnitPage0);
4014 SSMR3PutMem(pSSM, pSasPages->pSASIOUnitPage1, pSasPages->cbSASIOUnitPage1);
4015
4016 SSMR3PutMem(pSSM, &pSasPages->SASIOUnitPage2, sizeof(MptConfigurationPageSASIOUnit2));
4017 SSMR3PutMem(pSSM, &pSasPages->SASIOUnitPage3, sizeof(MptConfigurationPageSASIOUnit3));
4018
4019 SSMR3PutU32(pSSM, pSasPages->cPHYs);
4020 for (unsigned i = 0; i < pSasPages->cPHYs; i++)
4021 {
4022 SSMR3PutMem(pSSM, &pSasPages->paPHYs[i].SASPHYPage0, sizeof(MptConfigurationPageSASPHY0));
4023 SSMR3PutMem(pSSM, &pSasPages->paPHYs[i].SASPHYPage1, sizeof(MptConfigurationPageSASPHY1));
4024 }
4025
4026 /* The number of devices first. */
4027 SSMR3PutU32(pSSM, pSasPages->cDevices);
4028
4029 PMptSASDevice pCurr = pSasPages->pSASDeviceHead;
4030
4031 while (pCurr)
4032 {
4033 SSMR3PutMem(pSSM, &pCurr->SASDevicePage0, sizeof(MptConfigurationPageSASDevice0));
4034 SSMR3PutMem(pSSM, &pCurr->SASDevicePage1, sizeof(MptConfigurationPageSASDevice1));
4035 SSMR3PutMem(pSSM, &pCurr->SASDevicePage2, sizeof(MptConfigurationPageSASDevice2));
4036
4037 pCurr = pCurr->pNext;
4038 }
4039 }
4040 else
4041 AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
4042
4043 /* Now the data for the BIOS interface. */
4044 SSMR3PutU8 (pSSM, pThis->VBoxSCSI.regIdentify);
4045 SSMR3PutU8 (pSSM, pThis->VBoxSCSI.uTargetDevice);
4046 SSMR3PutU8 (pSSM, pThis->VBoxSCSI.uTxDir);
4047 SSMR3PutU8 (pSSM, pThis->VBoxSCSI.cbCDB);
4048 SSMR3PutMem (pSSM, pThis->VBoxSCSI.abCDB, sizeof(pThis->VBoxSCSI.abCDB));
4049 SSMR3PutU8 (pSSM, pThis->VBoxSCSI.iCDB);
4050 SSMR3PutU32 (pSSM, pThis->VBoxSCSI.cbBuf);
4051 SSMR3PutU32 (pSSM, pThis->VBoxSCSI.iBuf);
4052 SSMR3PutBool (pSSM, pThis->VBoxSCSI.fBusy);
4053 SSMR3PutU8 (pSSM, pThis->VBoxSCSI.enmState);
4054 if (pThis->VBoxSCSI.cbBuf)
4055 SSMR3PutMem(pSSM, pThis->VBoxSCSI.pbBuf, pThis->VBoxSCSI.cbBuf);
4056
4057 return SSMR3PutU32(pSSM, ~0);
4058}
4059
4060/**
4061 * @callback_method_impl{FNSSMDEVLOADDONE}
4062 */
4063static DECLCALLBACK(int) lsilogicR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4064{
4065 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4066
4067 lsilogicR3Kick(pThis);
4068 return VINF_SUCCESS;
4069}
4070
4071/**
4072 * @callback_method_impl{FNSSMDEVLOADEXEC}
4073 */
4074static DECLCALLBACK(int) lsilogicR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
4075{
4076 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4077 int rc;
4078
4079 if ( uVersion != LSILOGIC_SAVED_STATE_VERSION
4080 && uVersion != LSILOGIC_SAVED_STATE_VERSION_PRE_SAS
4081 && uVersion != LSILOGIC_SAVED_STATE_VERSION_VBOX_30)
4082 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4083
4084 /* device config */
4085 if (uVersion > LSILOGIC_SAVED_STATE_VERSION_PRE_SAS)
4086 {
4087 LSILOGICCTRLTYPE enmCtrlType;
4088 uint32_t cDeviceStates, cPorts;
4089
4090 rc = SSMR3GetU32(pSSM, (uint32_t *)&enmCtrlType);
4091 AssertRCReturn(rc, rc);
4092 rc = SSMR3GetU32(pSSM, &cDeviceStates);
4093 AssertRCReturn(rc, rc);
4094 rc = SSMR3GetU32(pSSM, &cPorts);
4095 AssertRCReturn(rc, rc);
4096
4097 if (enmCtrlType != pThis->enmCtrlType)
4098 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Controller type): config=%d state=%d"),
4099 pThis->enmCtrlType, enmCtrlType);
4100 if (cDeviceStates != pThis->cDeviceStates)
4101 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Device states): config=%u state=%u"),
4102 pThis->cDeviceStates, cDeviceStates);
4103 if (cPorts != pThis->cPorts)
4104 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Ports): config=%u state=%u"),
4105 pThis->cPorts, cPorts);
4106 }
4107 if (uVersion > LSILOGIC_SAVED_STATE_VERSION_VBOX_30)
4108 {
4109 for (unsigned i = 0; i < pThis->cDeviceStates; i++)
4110 {
4111 bool fPresent;
4112 rc = SSMR3GetBool(pSSM, &fPresent);
4113 AssertRCReturn(rc, rc);
4114 if (fPresent != (pThis->paDeviceStates[i].pDrvBase != NULL))
4115 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target %u config mismatch: config=%RTbool state=%RTbool"),
4116 i, pThis->paDeviceStates[i].pDrvBase != NULL, fPresent);
4117 }
4118 }
4119 if (uPass != SSM_PASS_FINAL)
4120 return VINF_SUCCESS;
4121
4122 /* Every device first. */
4123 for (unsigned i = 0; i < pThis->cDeviceStates; i++)
4124 {
4125 PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[i];
4126
4127 AssertMsg(!pDevice->cOutstandingRequests,
4128 ("There are still outstanding requests on this device\n"));
4129 SSMR3GetU32(pSSM, (uint32_t *)&pDevice->cOutstandingRequests);
4130 }
4131 /* Now the main device state. */
4132 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->enmState);
4133 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->enmWhoInit);
4134 if (uVersion <= LSILOGIC_SAVED_STATE_VERSION_BOOL_DOORBELL)
4135 {
4136 bool fDoorbellInProgress = false;
4137
4138 /*
4139 * The doorbell status flag distinguishes only between
4140 * doorbell not in use or a Function handshake is currently in progress.
4141 */
4142 SSMR3GetBool (pSSM, &fDoorbellInProgress);
4143 if (fDoorbellInProgress)
4144 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_FN_HANDSHAKE;
4145 else
4146 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
4147 }
4148 else
4149 SSMR3GetU32(pSSM, (uint32_t *)&pThis->enmDoorbellState);
4150 SSMR3GetBool (pSSM, &pThis->fDiagnosticEnabled);
4151 SSMR3GetBool (pSSM, &pThis->fNotificationSend);
4152 SSMR3GetBool (pSSM, &pThis->fEventNotificationEnabled);
4153 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uInterruptMask);
4154 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uInterruptStatus);
4155 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aMessage); i++)
4156 SSMR3GetU32 (pSSM, &pThis->aMessage[i]);
4157 SSMR3GetU32 (pSSM, &pThis->iMessage);
4158 SSMR3GetU32 (pSSM, &pThis->cMessage);
4159 SSMR3GetMem (pSSM, &pThis->ReplyBuffer, sizeof(pThis->ReplyBuffer));
4160 SSMR3GetU32 (pSSM, &pThis->uNextReplyEntryRead);
4161 SSMR3GetU32 (pSSM, &pThis->cReplySize);
4162 SSMR3GetU16 (pSSM, &pThis->u16IOCFaultCode);
4163 SSMR3GetU32 (pSSM, &pThis->u32HostMFAHighAddr);
4164 SSMR3GetU32 (pSSM, &pThis->u32SenseBufferHighAddr);
4165 SSMR3GetU8 (pSSM, &pThis->cMaxDevices);
4166 SSMR3GetU8 (pSSM, &pThis->cMaxBuses);
4167 SSMR3GetU16 (pSSM, &pThis->cbReplyFrame);
4168 SSMR3GetU32 (pSSM, &pThis->iDiagnosticAccess);
4169
4170 uint32_t cReplyQueueEntries, cRequestQueueEntries;
4171 SSMR3GetU32 (pSSM, &cReplyQueueEntries);
4172 SSMR3GetU32 (pSSM, &cRequestQueueEntries);
4173
4174 if ( cReplyQueueEntries != pThis->cReplyQueueEntries
4175 || cRequestQueueEntries != pThis->cRequestQueueEntries)
4176 {
4177 LogFlow(("Reallocating queues cReplyQueueEntries=%u cRequestQueuEntries=%u\n",
4178 cReplyQueueEntries, cRequestQueueEntries));
4179 lsilogicR3QueuesFree(pThis);
4180 pThis->cReplyQueueEntries = cReplyQueueEntries;
4181 pThis->cRequestQueueEntries = cRequestQueueEntries;
4182 rc = lsilogicR3QueuesAlloc(pThis);
4183 if (RT_FAILURE(rc))
4184 return rc;
4185 }
4186
4187 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyFreeQueueNextEntryFreeWrite);
4188 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyFreeQueueNextAddressRead);
4189 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyPostQueueNextEntryFreeWrite);
4190 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyPostQueueNextAddressRead);
4191 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uRequestQueueNextEntryFreeWrite);
4192 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uRequestQueueNextAddressRead);
4193
4194 PMptConfigurationPagesSupported pPages = pThis->pConfigurationPages;
4195
4196 if (uVersion <= LSILOGIC_SAVED_STATE_VERSION_PRE_SAS)
4197 {
4198 PMptConfigurationPagesSpi pSpiPages = &pPages->u.SpiPages;
4199 MptConfigurationPagesSupported_SSM_V2 ConfigPagesV2;
4200
4201 if (pThis->enmCtrlType != LSILOGICCTRLTYPE_SCSI_SPI)
4202 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch: Expected SPI SCSI controller"));
4203
4204 SSMR3GetMem(pSSM, &ConfigPagesV2,
4205 sizeof(MptConfigurationPagesSupported_SSM_V2));
4206
4207 pPages->ManufacturingPage0 = ConfigPagesV2.ManufacturingPage0;
4208 pPages->ManufacturingPage1 = ConfigPagesV2.ManufacturingPage1;
4209 pPages->ManufacturingPage2 = ConfigPagesV2.ManufacturingPage2;
4210 pPages->ManufacturingPage3 = ConfigPagesV2.ManufacturingPage3;
4211 pPages->ManufacturingPage4 = ConfigPagesV2.ManufacturingPage4;
4212 pPages->IOUnitPage0 = ConfigPagesV2.IOUnitPage0;
4213 pPages->IOUnitPage1 = ConfigPagesV2.IOUnitPage1;
4214 pPages->IOUnitPage2 = ConfigPagesV2.IOUnitPage2;
4215 pPages->IOUnitPage3 = ConfigPagesV2.IOUnitPage3;
4216 pPages->IOCPage0 = ConfigPagesV2.IOCPage0;
4217 pPages->IOCPage1 = ConfigPagesV2.IOCPage1;
4218 pPages->IOCPage2 = ConfigPagesV2.IOCPage2;
4219 pPages->IOCPage3 = ConfigPagesV2.IOCPage3;
4220 pPages->IOCPage4 = ConfigPagesV2.IOCPage4;
4221 pPages->IOCPage6 = ConfigPagesV2.IOCPage6;
4222
4223 pSpiPages->aPortPages[0].SCSISPIPortPage0 = ConfigPagesV2.aPortPages[0].SCSISPIPortPage0;
4224 pSpiPages->aPortPages[0].SCSISPIPortPage1 = ConfigPagesV2.aPortPages[0].SCSISPIPortPage1;
4225 pSpiPages->aPortPages[0].SCSISPIPortPage2 = ConfigPagesV2.aPortPages[0].SCSISPIPortPage2;
4226
4227 for (unsigned i = 0; i < RT_ELEMENTS(pPages->u.SpiPages.aBuses[0].aDevicePages); i++)
4228 {
4229 pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage0 = ConfigPagesV2.aBuses[0].aDevicePages[i].SCSISPIDevicePage0;
4230 pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage1 = ConfigPagesV2.aBuses[0].aDevicePages[i].SCSISPIDevicePage1;
4231 pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage2 = ConfigPagesV2.aBuses[0].aDevicePages[i].SCSISPIDevicePage2;
4232 pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage3 = ConfigPagesV2.aBuses[0].aDevicePages[i].SCSISPIDevicePage3;
4233 }
4234 }
4235 else
4236 {
4237 /* Queue content */
4238 for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
4239 SSMR3GetU32(pSSM, (uint32_t *)&pThis->pReplyFreeQueueBaseR3[i]);
4240 for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
4241 SSMR3GetU32(pSSM, (uint32_t *)&pThis->pReplyPostQueueBaseR3[i]);
4242 for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++)
4243 SSMR3GetU32(pSSM, (uint32_t *)&pThis->pRequestQueueBaseR3[i]);
4244
4245 SSMR3GetU16(pSSM, &pThis->u16NextHandle);
4246
4247 /* Configuration pages */
4248 SSMR3GetMem(pSSM, &pPages->ManufacturingPage0, sizeof(MptConfigurationPageManufacturing0));
4249 SSMR3GetMem(pSSM, &pPages->ManufacturingPage1, sizeof(MptConfigurationPageManufacturing1));
4250 SSMR3GetMem(pSSM, &pPages->ManufacturingPage2, sizeof(MptConfigurationPageManufacturing2));
4251 SSMR3GetMem(pSSM, &pPages->ManufacturingPage3, sizeof(MptConfigurationPageManufacturing3));
4252 SSMR3GetMem(pSSM, &pPages->ManufacturingPage4, sizeof(MptConfigurationPageManufacturing4));
4253 SSMR3GetMem(pSSM, &pPages->ManufacturingPage5, sizeof(MptConfigurationPageManufacturing5));
4254 SSMR3GetMem(pSSM, &pPages->ManufacturingPage6, sizeof(MptConfigurationPageManufacturing6));
4255 SSMR3GetMem(pSSM, &pPages->ManufacturingPage8, sizeof(MptConfigurationPageManufacturing8));
4256 SSMR3GetMem(pSSM, &pPages->ManufacturingPage9, sizeof(MptConfigurationPageManufacturing9));
4257 SSMR3GetMem(pSSM, &pPages->ManufacturingPage10, sizeof(MptConfigurationPageManufacturing10));
4258 SSMR3GetMem(pSSM, &pPages->IOUnitPage0, sizeof(MptConfigurationPageIOUnit0));
4259 SSMR3GetMem(pSSM, &pPages->IOUnitPage1, sizeof(MptConfigurationPageIOUnit1));
4260 SSMR3GetMem(pSSM, &pPages->IOUnitPage2, sizeof(MptConfigurationPageIOUnit2));
4261 SSMR3GetMem(pSSM, &pPages->IOUnitPage3, sizeof(MptConfigurationPageIOUnit3));
4262 SSMR3GetMem(pSSM, &pPages->IOUnitPage4, sizeof(MptConfigurationPageIOUnit4));
4263 SSMR3GetMem(pSSM, &pPages->IOCPage0, sizeof(MptConfigurationPageIOC0));
4264 SSMR3GetMem(pSSM, &pPages->IOCPage1, sizeof(MptConfigurationPageIOC1));
4265 SSMR3GetMem(pSSM, &pPages->IOCPage2, sizeof(MptConfigurationPageIOC2));
4266 SSMR3GetMem(pSSM, &pPages->IOCPage3, sizeof(MptConfigurationPageIOC3));
4267 SSMR3GetMem(pSSM, &pPages->IOCPage4, sizeof(MptConfigurationPageIOC4));
4268 SSMR3GetMem(pSSM, &pPages->IOCPage6, sizeof(MptConfigurationPageIOC6));
4269 SSMR3GetMem(pSSM, &pPages->BIOSPage1, sizeof(MptConfigurationPageBIOS1));
4270 SSMR3GetMem(pSSM, &pPages->BIOSPage2, sizeof(MptConfigurationPageBIOS2));
4271 SSMR3GetMem(pSSM, &pPages->BIOSPage4, sizeof(MptConfigurationPageBIOS4));
4272
4273 /* Device dependent pages */
4274 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
4275 {
4276 PMptConfigurationPagesSpi pSpiPages = &pPages->u.SpiPages;
4277
4278 SSMR3GetMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage0, sizeof(MptConfigurationPageSCSISPIPort0));
4279 SSMR3GetMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage1, sizeof(MptConfigurationPageSCSISPIPort1));
4280 SSMR3GetMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage2, sizeof(MptConfigurationPageSCSISPIPort2));
4281
4282 for (unsigned i = 0; i < RT_ELEMENTS(pSpiPages->aBuses[0].aDevicePages); i++)
4283 {
4284 SSMR3GetMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage0, sizeof(MptConfigurationPageSCSISPIDevice0));
4285 SSMR3GetMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage1, sizeof(MptConfigurationPageSCSISPIDevice1));
4286 SSMR3GetMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage2, sizeof(MptConfigurationPageSCSISPIDevice2));
4287 SSMR3GetMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage3, sizeof(MptConfigurationPageSCSISPIDevice3));
4288 }
4289 }
4290 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
4291 {
4292 uint32_t cbPage0, cbPage1, cPHYs, cbManufacturingPage7;
4293 PMptConfigurationPagesSas pSasPages = &pPages->u.SasPages;
4294
4295 SSMR3GetU32(pSSM, &cbManufacturingPage7);
4296 SSMR3GetU32(pSSM, &cbPage0);
4297 SSMR3GetU32(pSSM, &cbPage1);
4298
4299 if ( (cbPage0 != pSasPages->cbSASIOUnitPage0)
4300 || (cbPage1 != pSasPages->cbSASIOUnitPage1)
4301 || (cbManufacturingPage7 != pSasPages->cbManufacturingPage7))
4302 return VERR_SSM_LOAD_CONFIG_MISMATCH;
4303
4304 AssertPtr(pSasPages->pManufacturingPage7);
4305 AssertPtr(pSasPages->pSASIOUnitPage0);
4306 AssertPtr(pSasPages->pSASIOUnitPage1);
4307
4308 SSMR3GetMem(pSSM, pSasPages->pManufacturingPage7, pSasPages->cbManufacturingPage7);
4309 SSMR3GetMem(pSSM, pSasPages->pSASIOUnitPage0, pSasPages->cbSASIOUnitPage0);
4310 SSMR3GetMem(pSSM, pSasPages->pSASIOUnitPage1, pSasPages->cbSASIOUnitPage1);
4311
4312 SSMR3GetMem(pSSM, &pSasPages->SASIOUnitPage2, sizeof(MptConfigurationPageSASIOUnit2));
4313 SSMR3GetMem(pSSM, &pSasPages->SASIOUnitPage3, sizeof(MptConfigurationPageSASIOUnit3));
4314
4315 SSMR3GetU32(pSSM, &cPHYs);
4316 if (cPHYs != pSasPages->cPHYs)
4317 return VERR_SSM_LOAD_CONFIG_MISMATCH;
4318
4319 AssertPtr(pSasPages->paPHYs);
4320 for (unsigned i = 0; i < pSasPages->cPHYs; i++)
4321 {
4322 SSMR3GetMem(pSSM, &pSasPages->paPHYs[i].SASPHYPage0, sizeof(MptConfigurationPageSASPHY0));
4323 SSMR3GetMem(pSSM, &pSasPages->paPHYs[i].SASPHYPage1, sizeof(MptConfigurationPageSASPHY1));
4324 }
4325
4326 /* The number of devices first. */
4327 SSMR3GetU32(pSSM, &pSasPages->cDevices);
4328
4329 PMptSASDevice pCurr = pSasPages->pSASDeviceHead;
4330
4331 for (unsigned i = 0; i < pSasPages->cDevices; i++)
4332 {
4333 SSMR3GetMem(pSSM, &pCurr->SASDevicePage0, sizeof(MptConfigurationPageSASDevice0));
4334 SSMR3GetMem(pSSM, &pCurr->SASDevicePage1, sizeof(MptConfigurationPageSASDevice1));
4335 SSMR3GetMem(pSSM, &pCurr->SASDevicePage2, sizeof(MptConfigurationPageSASDevice2));
4336
4337 pCurr = pCurr->pNext;
4338 }
4339
4340 Assert(!pCurr);
4341 }
4342 else
4343 AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
4344 }
4345
4346 /* Now the data for the BIOS interface. */
4347 SSMR3GetU8 (pSSM, &pThis->VBoxSCSI.regIdentify);
4348 SSMR3GetU8 (pSSM, &pThis->VBoxSCSI.uTargetDevice);
4349 SSMR3GetU8 (pSSM, &pThis->VBoxSCSI.uTxDir);
4350 SSMR3GetU8 (pSSM, &pThis->VBoxSCSI.cbCDB);
4351 SSMR3GetMem (pSSM, pThis->VBoxSCSI.abCDB, sizeof(pThis->VBoxSCSI.abCDB));
4352 SSMR3GetU8 (pSSM, &pThis->VBoxSCSI.iCDB);
4353 SSMR3GetU32 (pSSM, &pThis->VBoxSCSI.cbBuf);
4354 SSMR3GetU32 (pSSM, &pThis->VBoxSCSI.iBuf);
4355 SSMR3GetBool(pSSM, (bool *)&pThis->VBoxSCSI.fBusy);
4356 SSMR3GetU8 (pSSM, (uint8_t *)&pThis->VBoxSCSI.enmState);
4357 if (pThis->VBoxSCSI.cbBuf)
4358 {
4359 pThis->VBoxSCSI.pbBuf = (uint8_t *)RTMemAllocZ(pThis->VBoxSCSI.cbBuf);
4360 if (!pThis->VBoxSCSI.pbBuf)
4361 {
4362 LogRel(("LsiLogic: Out of memory during restore.\n"));
4363 return PDMDEV_SET_ERROR(pDevIns, VERR_NO_MEMORY,
4364 N_("LsiLogic: Out of memory during restore\n"));
4365 }
4366 SSMR3GetMem(pSSM, pThis->VBoxSCSI.pbBuf, pThis->VBoxSCSI.cbBuf);
4367 }
4368
4369 uint32_t u32;
4370 rc = SSMR3GetU32(pSSM, &u32);
4371 if (RT_FAILURE(rc))
4372 return rc;
4373 AssertMsgReturn(u32 == ~0U, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
4374
4375 return VINF_SUCCESS;
4376}
4377
4378
4379/*
4380 * The device level IBASE and LED interfaces.
4381 */
4382
4383/**
4384 * @interface_method_impl{PDMILEDPORTS,pfnQueryInterface, For a SCSI device.}
4385 *
4386 * @remarks Called by the scsi driver, proxying the main calls.
4387 */
4388static DECLCALLBACK(int) lsilogicR3DeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4389{
4390 PLSILOGICDEVICE pDevice = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, ILed);
4391 if (iLUN == 0)
4392 {
4393 *ppLed = &pDevice->Led;
4394 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
4395 return VINF_SUCCESS;
4396 }
4397 return VERR_PDM_LUN_NOT_FOUND;
4398}
4399
4400
4401/**
4402 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4403 */
4404static DECLCALLBACK(void *) lsilogicR3DeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4405{
4406 PLSILOGICDEVICE pDevice = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, IBase);
4407
4408 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevice->IBase);
4409 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSIPORT, &pDevice->ISCSIPort);
4410 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pDevice->ILed);
4411 return NULL;
4412}
4413
4414
4415/*
4416 * The controller level IBASE and LED interfaces.
4417 */
4418
4419/**
4420 * Gets the pointer to the status LED of a unit.
4421 *
4422 * @returns VBox status code.
4423 * @param pInterface Pointer to the interface structure containing the called function pointer.
4424 * @param iLUN The unit which status LED we desire.
4425 * @param ppLed Where to store the LED pointer.
4426 */
4427static DECLCALLBACK(int) lsilogicR3StatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4428{
4429 PLSILOGICSCSI pThis = RT_FROM_MEMBER(pInterface, LSILOGICSCSI, ILeds);
4430 if (iLUN < pThis->cDeviceStates)
4431 {
4432 *ppLed = &pThis->paDeviceStates[iLUN].Led;
4433 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
4434 return VINF_SUCCESS;
4435 }
4436 return VERR_PDM_LUN_NOT_FOUND;
4437}
4438
4439/**
4440 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4441 */
4442static DECLCALLBACK(void *) lsilogicR3StatusQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4443{
4444 PLSILOGICSCSI pThis = RT_FROM_MEMBER(pInterface, LSILOGICSCSI, IBase);
4445 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
4446 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
4447 return NULL;
4448}
4449
4450
4451/*
4452 * The PDM device interface and some helpers.
4453 */
4454
4455/**
4456 * Checks if all asynchronous I/O is finished.
4457 *
4458 * Used by lsilogicR3Reset, lsilogicR3Suspend and lsilogicR3PowerOff.
4459 *
4460 * @returns true if quiesced, false if busy.
4461 * @param pDevIns The device instance.
4462 */
4463static bool lsilogicR3AllAsyncIOIsFinished(PPDMDEVINS pDevIns)
4464{
4465 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4466
4467 for (uint32_t i = 0; i < pThis->cDeviceStates; i++)
4468 {
4469 PLSILOGICDEVICE pThisDevice = &pThis->paDeviceStates[i];
4470 if (pThisDevice->pDrvBase)
4471 {
4472 if (pThisDevice->cOutstandingRequests != 0)
4473 return false;
4474 }
4475 }
4476
4477 return true;
4478}
4479
4480/**
4481 * @callback_method_impl{FNPDMDEVASYNCNOTIFY,
4482 * Callback employed by lsilogicR3Suspend and lsilogicR3PowerOff.}
4483 */
4484static DECLCALLBACK(bool) lsilogicR3IsAsyncSuspendOrPowerOffDone(PPDMDEVINS pDevIns)
4485{
4486 if (!lsilogicR3AllAsyncIOIsFinished(pDevIns))
4487 return false;
4488
4489 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4490 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
4491 return true;
4492}
4493
4494/**
4495 * Common worker for ahciR3Suspend and ahciR3PowerOff.
4496 */
4497static void lsilogicR3SuspendOrPowerOff(PPDMDEVINS pDevIns)
4498{
4499 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4500
4501 ASMAtomicWriteBool(&pThis->fSignalIdle, true);
4502 if (!lsilogicR3AllAsyncIOIsFinished(pDevIns))
4503 PDMDevHlpSetAsyncNotification(pDevIns, lsilogicR3IsAsyncSuspendOrPowerOffDone);
4504 else
4505 {
4506 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
4507
4508 AssertMsg(!pThis->fNotificationSend, ("The PDM Queue should be empty at this point\n"));
4509
4510 if (pThis->fRedo)
4511 {
4512 /*
4513 * We have tasks which we need to redo. Put the message frame addresses
4514 * into the request queue (we save the requests).
4515 * Guest execution is suspended at this point so there is no race between us and
4516 * lsilogicRegisterWrite.
4517 */
4518 PLSILOGICREQ pLsiReq = pThis->pTasksRedoHead;
4519
4520 pThis->pTasksRedoHead = NULL;
4521
4522 while (pLsiReq)
4523 {
4524 PLSILOGICREQ pFree;
4525
4526 if (!pLsiReq->fBIOS)
4527 {
4528 /* Write only the lower 32bit part of the address. */
4529 ASMAtomicWriteU32(&pThis->CTX_SUFF(pRequestQueueBase)[pThis->uRequestQueueNextEntryFreeWrite],
4530 pLsiReq->GCPhysMessageFrameAddr & UINT32_C(0xffffffff));
4531
4532 pThis->uRequestQueueNextEntryFreeWrite++;
4533 pThis->uRequestQueueNextEntryFreeWrite %= pThis->cRequestQueueEntries;
4534
4535 pThis->fNotificationSend = true;
4536 }
4537 else
4538 {
4539 AssertMsg(!pLsiReq->pRedoNext, ("Only one BIOS task can be active!\n"));
4540 vboxscsiSetRequestRedo(&pThis->VBoxSCSI, &pLsiReq->PDMScsiRequest);
4541 }
4542
4543 pFree = pLsiReq;
4544 pLsiReq = pLsiReq->pRedoNext;
4545
4546 RTMemCacheFree(pThis->hTaskCache, pFree);
4547 }
4548 pThis->fRedo = false;
4549 }
4550 }
4551}
4552
4553/**
4554 * @interface_method_impl{PDMDEVREG,pfnSuspend}
4555 */
4556static DECLCALLBACK(void) lsilogicR3Suspend(PPDMDEVINS pDevIns)
4557{
4558 Log(("lsilogicR3Suspend\n"));
4559 lsilogicR3SuspendOrPowerOff(pDevIns);
4560}
4561
4562/**
4563 * @interface_method_impl{PDMDEVREG,pfnResume}
4564 */
4565static DECLCALLBACK(void) lsilogicR3Resume(PPDMDEVINS pDevIns)
4566{
4567 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4568
4569 Log(("lsilogicR3Resume\n"));
4570
4571 lsilogicR3Kick(pThis);
4572}
4573
4574/**
4575 * @interface_method_impl{PDMDEVREG,pfnDetach}
4576 *
4577 * One harddisk at one port has been unplugged.
4578 * The VM is suspended at this point.
4579 */
4580static DECLCALLBACK(void) lsilogicR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
4581{
4582 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4583 PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[iLUN];
4584
4585 if (iLUN >= pThis->cDeviceStates)
4586 return;
4587
4588 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
4589 ("LsiLogic: Device does not support hotplugging\n"));
4590
4591 Log(("%s:\n", __FUNCTION__));
4592
4593 /*
4594 * Zero some important members.
4595 */
4596 pDevice->pDrvBase = NULL;
4597 pDevice->pDrvSCSIConnector = NULL;
4598}
4599
4600/**
4601 * @interface_method_impl{PDMDEVREG,pfnAttach}
4602 */
4603static DECLCALLBACK(int) lsilogicR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
4604{
4605 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4606 PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[iLUN];
4607 int rc;
4608
4609 if (iLUN >= pThis->cDeviceStates)
4610 return VERR_PDM_LUN_NOT_FOUND;
4611
4612 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
4613 ("LsiLogic: Device does not support hotplugging\n"),
4614 VERR_INVALID_PARAMETER);
4615
4616 /* the usual paranoia */
4617 AssertRelease(!pDevice->pDrvBase);
4618 AssertRelease(!pDevice->pDrvSCSIConnector);
4619 Assert(pDevice->iLUN == iLUN);
4620
4621 /*
4622 * Try attach the block device and get the interfaces,
4623 * required as well as optional.
4624 */
4625 rc = PDMDevHlpDriverAttach(pDevIns, pDevice->iLUN, &pDevice->IBase, &pDevice->pDrvBase, NULL);
4626 if (RT_SUCCESS(rc))
4627 {
4628 /* Get SCSI connector interface. */
4629 pDevice->pDrvSCSIConnector = PDMIBASE_QUERY_INTERFACE(pDevice->pDrvBase, PDMISCSICONNECTOR);
4630 AssertMsgReturn(pDevice->pDrvSCSIConnector, ("Missing SCSI interface below\n"), VERR_PDM_MISSING_INTERFACE);
4631 }
4632 else
4633 AssertMsgFailed(("Failed to attach LUN#%d. rc=%Rrc\n", pDevice->iLUN, rc));
4634
4635 if (RT_FAILURE(rc))
4636 {
4637 pDevice->pDrvBase = NULL;
4638 pDevice->pDrvSCSIConnector = NULL;
4639 }
4640 return rc;
4641}
4642
4643/**
4644 * Common reset worker.
4645 *
4646 * @param pDevIns The device instance data.
4647 */
4648static void lsilogicR3ResetCommon(PPDMDEVINS pDevIns)
4649{
4650 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4651 int rc;
4652
4653 rc = lsilogicR3HardReset(pThis);
4654 AssertRC(rc);
4655
4656 vboxscsiInitialize(&pThis->VBoxSCSI);
4657}
4658
4659/**
4660 * @callback_method_impl{FNPDMDEVASYNCNOTIFY,
4661 * Callback employed by lsilogicR3Reset.}
4662 */
4663static DECLCALLBACK(bool) lsilogicR3IsAsyncResetDone(PPDMDEVINS pDevIns)
4664{
4665 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4666
4667 if (!lsilogicR3AllAsyncIOIsFinished(pDevIns))
4668 return false;
4669 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
4670
4671 lsilogicR3ResetCommon(pDevIns);
4672 return true;
4673}
4674
4675/**
4676 * @interface_method_impl{PDMDEVREG,pfnReset}
4677 */
4678static DECLCALLBACK(void) lsilogicR3Reset(PPDMDEVINS pDevIns)
4679{
4680 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4681
4682 ASMAtomicWriteBool(&pThis->fSignalIdle, true);
4683 if (!lsilogicR3AllAsyncIOIsFinished(pDevIns))
4684 PDMDevHlpSetAsyncNotification(pDevIns, lsilogicR3IsAsyncResetDone);
4685 else
4686 {
4687 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
4688 lsilogicR3ResetCommon(pDevIns);
4689 }
4690}
4691
4692/**
4693 * @interface_method_impl{PDMDEVREG,pfnRelocate}
4694 */
4695static DECLCALLBACK(void) lsilogicR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
4696{
4697 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4698
4699 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
4700 pThis->pNotificationQueueRC = PDMQueueRCPtr(pThis->pNotificationQueueR3);
4701
4702 /* Relocate queues. */
4703 pThis->pReplyFreeQueueBaseRC += offDelta;
4704 pThis->pReplyPostQueueBaseRC += offDelta;
4705 pThis->pRequestQueueBaseRC += offDelta;
4706}
4707
4708/**
4709 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
4710 */
4711static DECLCALLBACK(void) lsilogicR3PowerOff(PPDMDEVINS pDevIns)
4712{
4713 Log(("lsilogicR3PowerOff\n"));
4714 lsilogicR3SuspendOrPowerOff(pDevIns);
4715}
4716
4717/**
4718 * @interface_method_impl{PDMDEVREG,pfnDestruct}
4719 */
4720static DECLCALLBACK(int) lsilogicR3Destruct(PPDMDEVINS pDevIns)
4721{
4722 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4723 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
4724
4725 PDMR3CritSectDelete(&pThis->ReplyFreeQueueCritSect);
4726 PDMR3CritSectDelete(&pThis->ReplyPostQueueCritSect);
4727
4728 RTMemFree(pThis->paDeviceStates);
4729 pThis->paDeviceStates = NULL;
4730
4731 /* Destroy task cache. */
4732 if (pThis->hTaskCache != NIL_RTMEMCACHE)
4733 {
4734 int rc = RTMemCacheDestroy(pThis->hTaskCache); AssertRC(rc);
4735 pThis->hTaskCache = NIL_RTMEMCACHE;
4736 }
4737
4738 lsilogicR3ConfigurationPagesFree(pThis);
4739
4740 return VINF_SUCCESS;
4741}
4742
4743/**
4744 * @interface_method_impl{PDMDEVREG,pfnConstruct}
4745 */
4746static DECLCALLBACK(int) lsilogicR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
4747{
4748 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4749 int rc = VINF_SUCCESS;
4750 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
4751
4752 /*
4753 * Initialize enought of the state to make the destructure not trip up.
4754 */
4755 pThis->hTaskCache = NIL_RTMEMCACHE;
4756
4757 /*
4758 * Validate and read configuration.
4759 */
4760 rc = CFGMR3AreValuesValid(pCfg, "GCEnabled\0"
4761 "R0Enabled\0"
4762 "ReplyQueueDepth\0"
4763 "RequestQueueDepth\0"
4764 "ControllerType\0"
4765 "NumPorts\0"
4766 "Bootable\0");
4767 if (RT_FAILURE(rc))
4768 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
4769 N_("LsiLogic configuration error: unknown option specified"));
4770 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true);
4771 if (RT_FAILURE(rc))
4772 return PDMDEV_SET_ERROR(pDevIns, rc,
4773 N_("LsiLogic configuration error: failed to read GCEnabled as boolean"));
4774 Log(("%s: fGCEnabled=%d\n", __FUNCTION__, pThis->fGCEnabled));
4775
4776 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
4777 if (RT_FAILURE(rc))
4778 return PDMDEV_SET_ERROR(pDevIns, rc,
4779 N_("LsiLogic configuration error: failed to read R0Enabled as boolean"));
4780 Log(("%s: fR0Enabled=%d\n", __FUNCTION__, pThis->fR0Enabled));
4781
4782 rc = CFGMR3QueryU32Def(pCfg, "ReplyQueueDepth",
4783 &pThis->cReplyQueueEntries,
4784 LSILOGICSCSI_REPLY_QUEUE_DEPTH_DEFAULT);
4785 if (RT_FAILURE(rc))
4786 return PDMDEV_SET_ERROR(pDevIns, rc,
4787 N_("LsiLogic configuration error: failed to read ReplyQueue as integer"));
4788 Log(("%s: ReplyQueueDepth=%u\n", __FUNCTION__, pThis->cReplyQueueEntries));
4789
4790 rc = CFGMR3QueryU32Def(pCfg, "RequestQueueDepth",
4791 &pThis->cRequestQueueEntries,
4792 LSILOGICSCSI_REQUEST_QUEUE_DEPTH_DEFAULT);
4793 if (RT_FAILURE(rc))
4794 return PDMDEV_SET_ERROR(pDevIns, rc,
4795 N_("LsiLogic configuration error: failed to read RequestQueue as integer"));
4796 Log(("%s: RequestQueueDepth=%u\n", __FUNCTION__, pThis->cRequestQueueEntries));
4797
4798 char *pszCtrlType;
4799 rc = CFGMR3QueryStringAllocDef(pCfg, "ControllerType",
4800 &pszCtrlType, LSILOGICSCSI_PCI_SPI_CTRLNAME);
4801 if (RT_FAILURE(rc))
4802 return PDMDEV_SET_ERROR(pDevIns, rc,
4803 N_("LsiLogic configuration error: failed to read ControllerType as string"));
4804 Log(("%s: ControllerType=%s\n", __FUNCTION__, pszCtrlType));
4805
4806 rc = lsilogicR3GetCtrlTypeFromString(pThis, pszCtrlType);
4807 MMR3HeapFree(pszCtrlType);
4808
4809 char szDevTag[20];
4810 RTStrPrintf(szDevTag, sizeof(szDevTag), "LSILOGIC%s-%u",
4811 pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI ? "SPI" : "SAS",
4812 iInstance);
4813
4814
4815 if (RT_FAILURE(rc))
4816 return PDMDEV_SET_ERROR(pDevIns, rc,
4817 N_("LsiLogic configuration error: failed to determine controller type from string"));
4818
4819 rc = CFGMR3QueryU8(pCfg, "NumPorts",
4820 &pThis->cPorts);
4821 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
4822 {
4823 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
4824 pThis->cPorts = LSILOGICSCSI_PCI_SPI_PORTS_MAX;
4825 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
4826 pThis->cPorts = LSILOGICSCSI_PCI_SAS_PORTS_DEFAULT;
4827 else
4828 AssertMsgFailed(("Invalid controller type: %d\n", pThis->enmCtrlType));
4829 }
4830 else if (RT_FAILURE(rc))
4831 return PDMDEV_SET_ERROR(pDevIns, rc,
4832 N_("LsiLogic configuration error: failed to read NumPorts as integer"));
4833
4834 bool fBootable;
4835 rc = CFGMR3QueryBoolDef(pCfg, "Bootable", &fBootable, true);
4836 if (RT_FAILURE(rc))
4837 return PDMDEV_SET_ERROR(pDevIns, rc,
4838 N_("LsiLogic configuration error: failed to read Bootable as boolean"));
4839 Log(("%s: Bootable=%RTbool\n", __FUNCTION__, fBootable));
4840
4841 /* Init static parts. */
4842 PCIDevSetVendorId(&pThis->PciDev, LSILOGICSCSI_PCI_VENDOR_ID); /* LsiLogic */
4843
4844 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
4845 {
4846 PCIDevSetDeviceId (&pThis->PciDev, LSILOGICSCSI_PCI_SPI_DEVICE_ID); /* LSI53C1030 */
4847 PCIDevSetSubSystemVendorId(&pThis->PciDev, LSILOGICSCSI_PCI_SPI_SUBSYSTEM_VENDOR_ID);
4848 PCIDevSetSubSystemId (&pThis->PciDev, LSILOGICSCSI_PCI_SPI_SUBSYSTEM_ID);
4849 }
4850 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
4851 {
4852 PCIDevSetDeviceId (&pThis->PciDev, LSILOGICSCSI_PCI_SAS_DEVICE_ID); /* SAS1068 */
4853 PCIDevSetSubSystemVendorId(&pThis->PciDev, LSILOGICSCSI_PCI_SAS_SUBSYSTEM_VENDOR_ID);
4854 PCIDevSetSubSystemId (&pThis->PciDev, LSILOGICSCSI_PCI_SAS_SUBSYSTEM_ID);
4855 }
4856 else
4857 AssertMsgFailed(("Invalid controller type: %d\n", pThis->enmCtrlType));
4858
4859 PCIDevSetClassProg (&pThis->PciDev, 0x00); /* SCSI */
4860 PCIDevSetClassSub (&pThis->PciDev, 0x00); /* SCSI */
4861 PCIDevSetClassBase (&pThis->PciDev, 0x01); /* Mass storage */
4862 PCIDevSetInterruptPin(&pThis->PciDev, 0x01); /* Interrupt pin A */
4863
4864# ifdef VBOX_WITH_MSI_DEVICES
4865 PCIDevSetStatus(&pThis->PciDev, VBOX_PCI_STATUS_CAP_LIST);
4866 PCIDevSetCapabilityList(&pThis->PciDev, 0x80);
4867# endif
4868
4869 pThis->pDevInsR3 = pDevIns;
4870 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
4871 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
4872 pThis->IBase.pfnQueryInterface = lsilogicR3StatusQueryInterface;
4873 pThis->ILeds.pfnQueryStatusLed = lsilogicR3StatusQueryStatusLed;
4874
4875 /*
4876 * Create critical sections protecting the reply post and free queues.
4877 * Note! We do our own syncronization, so NOP the default crit sect for the device.
4878 */
4879 rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
4880 AssertRCReturn(rc, rc);
4881
4882 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->ReplyFreeQueueCritSect, RT_SRC_POS, "%sRFQ", szDevTag);
4883 if (RT_FAILURE(rc))
4884 return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: cannot create critical section for reply free queue"));
4885
4886 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->ReplyPostQueueCritSect, RT_SRC_POS, "%sRPQ", szDevTag);
4887 if (RT_FAILURE(rc))
4888 return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: cannot create critical section for reply post queue"));
4889
4890 /*
4891 * Register the PCI device, it's I/O regions.
4892 */
4893 rc = PDMDevHlpPCIRegister(pDevIns, &pThis->PciDev);
4894 if (RT_FAILURE(rc))
4895 return rc;
4896
4897# ifdef VBOX_WITH_MSI_DEVICES
4898 PDMMSIREG MsiReg;
4899 RT_ZERO(MsiReg);
4900 /* use this code for MSI-X support */
4901# if 0
4902 MsiReg.cMsixVectors = 1;
4903 MsiReg.iMsixCapOffset = 0x80;
4904 MsiReg.iMsixNextOffset = 0x00;
4905 MsiReg.iMsixBar = 3;
4906# else
4907 MsiReg.cMsiVectors = 1;
4908 MsiReg.iMsiCapOffset = 0x80;
4909 MsiReg.iMsiNextOffset = 0x00;
4910# endif
4911 rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
4912 if (RT_FAILURE (rc))
4913 {
4914 LogRel(("Chipset cannot do MSI: %Rrc\n", rc));
4915 /* That's OK, we can work without MSI */
4916 PCIDevSetCapabilityList(&pThis->PciDev, 0x0);
4917 }
4918# endif
4919
4920 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, LSILOGIC_PCI_SPACE_IO_SIZE, PCI_ADDRESS_SPACE_IO, lsilogicR3Map);
4921 if (RT_FAILURE(rc))
4922 return rc;
4923
4924 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, LSILOGIC_PCI_SPACE_MEM_SIZE, PCI_ADDRESS_SPACE_MEM, lsilogicR3Map);
4925 if (RT_FAILURE(rc))
4926 return rc;
4927
4928 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 2, LSILOGIC_PCI_SPACE_MEM_SIZE, PCI_ADDRESS_SPACE_MEM, lsilogicR3Map);
4929 if (RT_FAILURE(rc))
4930 return rc;
4931
4932 /* Initialize task queue. (Need two items to handle SMP guest concurrency.) */
4933 char szTaggedText[64];
4934 RTStrPrintf(szTaggedText, sizeof(szTaggedText), "%s-Task", szDevTag);
4935 rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 2, 0,
4936 lsilogicR3NotifyQueueConsumer, true,
4937 szTaggedText,
4938 &pThis->pNotificationQueueR3);
4939 if (RT_FAILURE(rc))
4940 return rc;
4941 pThis->pNotificationQueueR0 = PDMQueueR0Ptr(pThis->pNotificationQueueR3);
4942 pThis->pNotificationQueueRC = PDMQueueRCPtr(pThis->pNotificationQueueR3);
4943
4944 /*
4945 * We need one entry free in the queue.
4946 */
4947 pThis->cReplyQueueEntries++;
4948 pThis->cRequestQueueEntries++;
4949
4950 /*
4951 * Allocate memory for the queues.
4952 */
4953 rc = lsilogicR3QueuesAlloc(pThis);
4954 if (RT_FAILURE(rc))
4955 return rc;
4956
4957 /*
4958 * Allocate task cache.
4959 */
4960 rc = RTMemCacheCreate(&pThis->hTaskCache, sizeof(LSILOGICREQ), 0, UINT32_MAX,
4961 NULL, NULL, NULL, 0);
4962 if (RT_FAILURE(rc))
4963 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Cannot create task cache"));
4964
4965 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
4966 pThis->cDeviceStates = pThis->cPorts * LSILOGICSCSI_PCI_SPI_DEVICES_PER_BUS_MAX;
4967 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
4968 pThis->cDeviceStates = pThis->cPorts * LSILOGICSCSI_PCI_SAS_DEVICES_PER_PORT_MAX;
4969 else
4970 AssertMsgFailed(("Invalid controller type: %d\n", pThis->enmCtrlType));
4971
4972 /*
4973 * Allocate device states.
4974 */
4975 pThis->paDeviceStates = (PLSILOGICDEVICE)RTMemAllocZ(sizeof(LSILOGICDEVICE) * pThis->cDeviceStates);
4976 if (!pThis->paDeviceStates)
4977 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to allocate memory for device states"));
4978
4979 for (unsigned i = 0; i < pThis->cDeviceStates; i++)
4980 {
4981 char szName[24];
4982 PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[i];
4983
4984 /* Initialize static parts of the device. */
4985 pDevice->iLUN = i;
4986 pDevice->pLsiLogicR3 = pThis;
4987 pDevice->Led.u32Magic = PDMLED_MAGIC;
4988 pDevice->IBase.pfnQueryInterface = lsilogicR3DeviceQueryInterface;
4989 pDevice->ISCSIPort.pfnSCSIRequestCompleted = lsilogicR3DeviceSCSIRequestCompleted;
4990 pDevice->ISCSIPort.pfnQueryDeviceLocation = lsilogicR3QueryDeviceLocation;
4991 pDevice->ILed.pfnQueryStatusLed = lsilogicR3DeviceQueryStatusLed;
4992
4993 RTStrPrintf(szName, sizeof(szName), "Device%u", i);
4994
4995 /* Attach SCSI driver. */
4996 rc = PDMDevHlpDriverAttach(pDevIns, pDevice->iLUN, &pDevice->IBase, &pDevice->pDrvBase, szName);
4997 if (RT_SUCCESS(rc))
4998 {
4999 /* Get SCSI connector interface. */
5000 pDevice->pDrvSCSIConnector = PDMIBASE_QUERY_INTERFACE(pDevice->pDrvBase, PDMISCSICONNECTOR);
5001 AssertMsgReturn(pDevice->pDrvSCSIConnector, ("Missing SCSI interface below\n"), VERR_PDM_MISSING_INTERFACE);
5002 }
5003 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
5004 {
5005 pDevice->pDrvBase = NULL;
5006 rc = VINF_SUCCESS;
5007 Log(("LsiLogic: no driver attached to device %s\n", szName));
5008 }
5009 else
5010 {
5011 AssertLogRelMsgFailed(("LsiLogic: Failed to attach %s\n", szName));
5012 return rc;
5013 }
5014 }
5015
5016 /*
5017 * Attach status driver (optional).
5018 */
5019 PPDMIBASE pBase;
5020 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pBase, "Status Port");
5021 if (RT_SUCCESS(rc))
5022 pThis->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
5023 else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
5024 {
5025 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
5026 return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic cannot attach to status driver"));
5027 }
5028
5029 /* Initialize the SCSI emulation for the BIOS. */
5030 rc = vboxscsiInitialize(&pThis->VBoxSCSI);
5031 AssertRC(rc);
5032
5033 /*
5034 * Register I/O port space in ISA region for BIOS access
5035 * if the controller is marked as bootable.
5036 */
5037 if (fBootable)
5038 {
5039 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
5040 rc = PDMDevHlpIOPortRegister(pDevIns, LSILOGIC_BIOS_IO_PORT, 4, NULL,
5041 lsilogicR3IsaIOPortWrite, lsilogicR3IsaIOPortRead,
5042 lsilogicR3IsaIOPortWriteStr, lsilogicR3IsaIOPortReadStr,
5043 "LsiLogic BIOS");
5044 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
5045 rc = PDMDevHlpIOPortRegister(pDevIns, LSILOGIC_SAS_BIOS_IO_PORT, 4, NULL,
5046 lsilogicR3IsaIOPortWrite, lsilogicR3IsaIOPortRead,
5047 lsilogicR3IsaIOPortWriteStr, lsilogicR3IsaIOPortReadStr,
5048 "LsiLogic SAS BIOS");
5049 else
5050 AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
5051
5052 if (RT_FAILURE(rc))
5053 return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic cannot register legacy I/O handlers"));
5054 }
5055
5056 /* Register save state handlers. */
5057 rc = PDMDevHlpSSMRegisterEx(pDevIns, LSILOGIC_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
5058 NULL, lsilogicR3LiveExec, NULL,
5059 NULL, lsilogicR3SaveExec, NULL,
5060 NULL, lsilogicR3LoadExec, lsilogicR3LoadDone);
5061 if (RT_FAILURE(rc))
5062 return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic cannot register save state handlers"));
5063
5064 pThis->enmWhoInit = LSILOGICWHOINIT_SYSTEM_BIOS;
5065
5066 /*
5067 * Register the info item.
5068 */
5069 char szTmp[128];
5070 RTStrPrintf(szTmp, sizeof(szTmp), "%s%u", pDevIns->pReg->szName, pDevIns->iInstance);
5071 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp,
5072 pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
5073 ? "LsiLogic SPI info."
5074 : "LsiLogic SAS info.", lsilogicR3Info);
5075
5076 /* Perform hard reset. */
5077 rc = lsilogicR3HardReset(pThis);
5078 AssertRC(rc);
5079
5080 return rc;
5081}
5082
5083/**
5084 * The device registration structure - SPI SCSI controller.
5085 */
5086const PDMDEVREG g_DeviceLsiLogicSCSI =
5087{
5088 /* u32Version */
5089 PDM_DEVREG_VERSION,
5090 /* szName */
5091 "lsilogicscsi",
5092 /* szRCMod */
5093 "VBoxDDGC.gc",
5094 /* szR0Mod */
5095 "VBoxDDR0.r0",
5096 /* pszDescription */
5097 "LSI Logic 53c1030 SCSI controller.\n",
5098 /* fFlags */
5099 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0 |
5100 PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION,
5101 /* fClass */
5102 PDM_DEVREG_CLASS_STORAGE,
5103 /* cMaxInstances */
5104 ~0U,
5105 /* cbInstance */
5106 sizeof(LSILOGICSCSI),
5107 /* pfnConstruct */
5108 lsilogicR3Construct,
5109 /* pfnDestruct */
5110 lsilogicR3Destruct,
5111 /* pfnRelocate */
5112 lsilogicR3Relocate,
5113 /* pfnMemSetup */
5114 NULL,
5115 /* pfnPowerOn */
5116 NULL,
5117 /* pfnReset */
5118 lsilogicR3Reset,
5119 /* pfnSuspend */
5120 lsilogicR3Suspend,
5121 /* pfnResume */
5122 lsilogicR3Resume,
5123 /* pfnAttach */
5124 lsilogicR3Attach,
5125 /* pfnDetach */
5126 lsilogicR3Detach,
5127 /* pfnQueryInterface. */
5128 NULL,
5129 /* pfnInitComplete */
5130 NULL,
5131 /* pfnPowerOff */
5132 lsilogicR3PowerOff,
5133 /* pfnSoftReset */
5134 NULL,
5135 /* u32VersionEnd */
5136 PDM_DEVREG_VERSION
5137};
5138
5139/**
5140 * The device registration structure - SAS controller.
5141 */
5142const PDMDEVREG g_DeviceLsiLogicSAS =
5143{
5144 /* u32Version */
5145 PDM_DEVREG_VERSION,
5146 /* szName */
5147 "lsilogicsas",
5148 /* szRCMod */
5149 "VBoxDDGC.gc",
5150 /* szR0Mod */
5151 "VBoxDDR0.r0",
5152 /* pszDescription */
5153 "LSI Logic SAS1068 controller.\n",
5154 /* fFlags */
5155 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0 |
5156 PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION |
5157 PDM_DEVREG_FLAGS_FIRST_RESET_NOTIFICATION,
5158 /* fClass */
5159 PDM_DEVREG_CLASS_STORAGE,
5160 /* cMaxInstances */
5161 ~0U,
5162 /* cbInstance */
5163 sizeof(LSILOGICSCSI),
5164 /* pfnConstruct */
5165 lsilogicR3Construct,
5166 /* pfnDestruct */
5167 lsilogicR3Destruct,
5168 /* pfnRelocate */
5169 lsilogicR3Relocate,
5170 /* pfnMemSetup */
5171 NULL,
5172 /* pfnPowerOn */
5173 NULL,
5174 /* pfnReset */
5175 lsilogicR3Reset,
5176 /* pfnSuspend */
5177 lsilogicR3Suspend,
5178 /* pfnResume */
5179 lsilogicR3Resume,
5180 /* pfnAttach */
5181 lsilogicR3Attach,
5182 /* pfnDetach */
5183 lsilogicR3Detach,
5184 /* pfnQueryInterface. */
5185 NULL,
5186 /* pfnInitComplete */
5187 NULL,
5188 /* pfnPowerOff */
5189 lsilogicR3PowerOff,
5190 /* pfnSoftReset */
5191 NULL,
5192 /* u32VersionEnd */
5193 PDM_DEVREG_VERSION
5194};
5195
5196#endif /* IN_RING3 */
5197#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette