VirtualBox

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

Last change on this file since 54153 was 53343, checked in by vboxsync, 10 years ago

Devices/DevLsiLogic: fixed race

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

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