VirtualBox

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

Last change on this file since 56992 was 56992, checked in by vboxsync, 9 years ago

Devices: Log & Assertion formatting fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 235.9 KB
Line 
1/* $Id: DevLsiLogicSCSI.cpp 56992 2015-07-18 23:01:44Z vboxsync $ */
2/** @file
3 * DevLsiLogicSCSI - LsiLogic LSI53c1030 SCSI controller.
4 */
5
6/*
7 * Copyright (C) 2006-2015 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 LogFunc(("Setting I/O controller into FAULT state: uIOCFaultCode=%u\n", uIOCFaultCode));
501 pThis->enmState = LSILOGICSTATE_FAULT;
502 pThis->u16IOCFaultCode = uIOCFaultCode;
503 }
504 else
505 LogFunc(("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 uint8_t const *pbSrc, uint32_t *pcTransfers, 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, pbSrc, pcTransfers, 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,
3879 uint8_t *pbDst, uint32_t *pcTransfers, unsigned cb)
3880{
3881 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
3882 LogFlowFunc(("#%d %s: pvUser=%#p cb=%d Port=%#x\n", pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port));
3883
3884 uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
3885 ? Port - LSILOGIC_BIOS_IO_PORT
3886 : Port - LSILOGIC_SAS_BIOS_IO_PORT;
3887 return vboxscsiReadString(pDevIns, &pThis->VBoxSCSI, iRegister, pbDst, pcTransfers, cb);
3888}
3889
3890/**
3891 * @callback_method_impl{FNPCIIOREGIONMAP}
3892 */
3893static DECLCALLBACK(int) lsilogicR3Map(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion,
3894 RTGCPHYS GCPhysAddress, uint32_t cb,
3895 PCIADDRESSSPACE enmType)
3896{
3897 PPDMDEVINS pDevIns = pPciDev->pDevIns;
3898 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
3899 int rc = VINF_SUCCESS;
3900 const char *pcszCtrl = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
3901 ? "LsiLogic"
3902 : "LsiLogicSas";
3903 const char *pcszDiag = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
3904 ? "LsiLogicDiag"
3905 : "LsiLogicSasDiag";
3906
3907 Log2(("%s: registering area at GCPhysAddr=%RGp cb=%u\n", __FUNCTION__, GCPhysAddress, cb));
3908
3909 AssertMsg( (enmType == PCI_ADDRESS_SPACE_MEM && cb >= LSILOGIC_PCI_SPACE_MEM_SIZE)
3910 || (enmType == PCI_ADDRESS_SPACE_IO && cb >= LSILOGIC_PCI_SPACE_IO_SIZE),
3911 ("PCI region type and size do not match\n"));
3912
3913 if (enmType == PCI_ADDRESS_SPACE_MEM && iRegion == 1)
3914 {
3915 /*
3916 * Non-4-byte read access to LSILOGIC_REG_REPLY_QUEUE may cause real strange behavior
3917 * because the data is part of a physical guest address. But some drivers use 1-byte
3918 * access to scan for SCSI controllers. So, we simplify our code by telling IOM to
3919 * read DWORDs.
3920 *
3921 * Regarding writes, we couldn't find anything specific in the specs about what should
3922 * happen. So far we've ignored unaligned writes and assumed the missing bytes of
3923 * byte and word access to be zero. We suspect that IOMMMIO_FLAGS_WRITE_ONLY_DWORD
3924 * or IOMMMIO_FLAGS_WRITE_DWORD_ZEROED would be the most appropriate here, but since we
3925 * don't have real hw to test one, the old behavior is kept exactly like it used to be.
3926 */
3927 /** @todo Check out unaligned writes and non-dword writes on real LsiLogic
3928 * hardware. */
3929 rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/,
3930 IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_PASSTHRU,
3931 lsilogicMMIOWrite, lsilogicMMIORead, pcszCtrl);
3932 if (RT_FAILURE(rc))
3933 return rc;
3934
3935 if (pThis->fR0Enabled)
3936 {
3937 rc = PDMDevHlpMMIORegisterR0(pDevIns, GCPhysAddress, cb, NIL_RTR0PTR /*pvUser*/,
3938 "lsilogicMMIOWrite", "lsilogicMMIORead");
3939 if (RT_FAILURE(rc))
3940 return rc;
3941 }
3942
3943 if (pThis->fGCEnabled)
3944 {
3945 rc = PDMDevHlpMMIORegisterRC(pDevIns, GCPhysAddress, cb, NIL_RTRCPTR /*pvUser*/,
3946 "lsilogicMMIOWrite", "lsilogicMMIORead");
3947 if (RT_FAILURE(rc))
3948 return rc;
3949 }
3950
3951 pThis->GCPhysMMIOBase = GCPhysAddress;
3952 }
3953 else if (enmType == PCI_ADDRESS_SPACE_MEM && iRegion == 2)
3954 {
3955 /* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
3956 rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/,
3957 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
3958 lsilogicDiagnosticWrite, lsilogicDiagnosticRead, pcszDiag);
3959 if (RT_FAILURE(rc))
3960 return rc;
3961
3962 if (pThis->fR0Enabled)
3963 {
3964 rc = PDMDevHlpMMIORegisterR0(pDevIns, GCPhysAddress, cb, NIL_RTR0PTR /*pvUser*/,
3965 "lsilogicDiagnosticWrite", "lsilogicDiagnosticRead");
3966 if (RT_FAILURE(rc))
3967 return rc;
3968 }
3969
3970 if (pThis->fGCEnabled)
3971 {
3972 rc = PDMDevHlpMMIORegisterRC(pDevIns, GCPhysAddress, cb, NIL_RTRCPTR /*pvUser*/,
3973 "lsilogicDiagnosticWrite", "lsilogicDiagnosticRead");
3974 if (RT_FAILURE(rc))
3975 return rc;
3976 }
3977 }
3978 else if (enmType == PCI_ADDRESS_SPACE_IO)
3979 {
3980 rc = PDMDevHlpIOPortRegister(pDevIns, (RTIOPORT)GCPhysAddress, LSILOGIC_PCI_SPACE_IO_SIZE,
3981 NULL, lsilogicIOPortWrite, lsilogicIOPortRead, NULL, NULL, pcszCtrl);
3982 if (RT_FAILURE(rc))
3983 return rc;
3984
3985 if (pThis->fR0Enabled)
3986 {
3987 rc = PDMDevHlpIOPortRegisterR0(pDevIns, (RTIOPORT)GCPhysAddress, LSILOGIC_PCI_SPACE_IO_SIZE,
3988 0, "lsilogicIOPortWrite", "lsilogicIOPortRead", NULL, NULL, pcszCtrl);
3989 if (RT_FAILURE(rc))
3990 return rc;
3991 }
3992
3993 if (pThis->fGCEnabled)
3994 {
3995 rc = PDMDevHlpIOPortRegisterRC(pDevIns, (RTIOPORT)GCPhysAddress, LSILOGIC_PCI_SPACE_IO_SIZE,
3996 0, "lsilogicIOPortWrite", "lsilogicIOPortRead", NULL, NULL, pcszCtrl);
3997 if (RT_FAILURE(rc))
3998 return rc;
3999 }
4000
4001 pThis->IOPortBase = (RTIOPORT)GCPhysAddress;
4002 }
4003 else
4004 AssertMsgFailed(("Invalid enmType=%d iRegion=%d\n", enmType, iRegion));
4005
4006 return rc;
4007}
4008
4009/**
4010 * @callback_method_impl{PFNDBGFHANDLERDEV}
4011 */
4012static DECLCALLBACK(void) lsilogicR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4013{
4014 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4015 bool fVerbose = false;
4016
4017 /*
4018 * Parse args.
4019 */
4020 if (pszArgs)
4021 fVerbose = strstr(pszArgs, "verbose") != NULL;
4022
4023 /*
4024 * Show info.
4025 */
4026 pHlp->pfnPrintf(pHlp,
4027 "%s#%d: port=%RTiop mmio=%RGp max-devices=%u GC=%RTbool R0=%RTbool\n",
4028 pDevIns->pReg->szName,
4029 pDevIns->iInstance,
4030 pThis->IOPortBase, pThis->GCPhysMMIOBase,
4031 pThis->cDeviceStates,
4032 pThis->fGCEnabled ? true : false,
4033 pThis->fR0Enabled ? true : false);
4034
4035 /*
4036 * Show general state.
4037 */
4038 pHlp->pfnPrintf(pHlp, "enmState=%u\n", pThis->enmState);
4039 pHlp->pfnPrintf(pHlp, "enmWhoInit=%u\n", pThis->enmWhoInit);
4040 pHlp->pfnPrintf(pHlp, "enmDoorbellState=%d\n", pThis->enmDoorbellState);
4041 pHlp->pfnPrintf(pHlp, "fDiagnosticEnabled=%RTbool\n", pThis->fDiagnosticEnabled);
4042 pHlp->pfnPrintf(pHlp, "fNotificationSent=%RTbool\n", pThis->fNotificationSent);
4043 pHlp->pfnPrintf(pHlp, "fEventNotificationEnabled=%RTbool\n", pThis->fEventNotificationEnabled);
4044 pHlp->pfnPrintf(pHlp, "uInterruptMask=%#x\n", pThis->uInterruptMask);
4045 pHlp->pfnPrintf(pHlp, "uInterruptStatus=%#x\n", pThis->uInterruptStatus);
4046 pHlp->pfnPrintf(pHlp, "u16IOCFaultCode=%#06x\n", pThis->u16IOCFaultCode);
4047 pHlp->pfnPrintf(pHlp, "u32HostMFAHighAddr=%#x\n", pThis->u32HostMFAHighAddr);
4048 pHlp->pfnPrintf(pHlp, "u32SenseBufferHighAddr=%#x\n", pThis->u32SenseBufferHighAddr);
4049 pHlp->pfnPrintf(pHlp, "cMaxDevices=%u\n", pThis->cMaxDevices);
4050 pHlp->pfnPrintf(pHlp, "cMaxBuses=%u\n", pThis->cMaxBuses);
4051 pHlp->pfnPrintf(pHlp, "cbReplyFrame=%u\n", pThis->cbReplyFrame);
4052 pHlp->pfnPrintf(pHlp, "cReplyQueueEntries=%u\n", pThis->cReplyQueueEntries);
4053 pHlp->pfnPrintf(pHlp, "cRequestQueueEntries=%u\n", pThis->cRequestQueueEntries);
4054 pHlp->pfnPrintf(pHlp, "cPorts=%u\n", pThis->cPorts);
4055
4056 /*
4057 * Show queue status.
4058 */
4059 pHlp->pfnPrintf(pHlp, "uReplyFreeQueueNextEntryFreeWrite=%u\n", pThis->uReplyFreeQueueNextEntryFreeWrite);
4060 pHlp->pfnPrintf(pHlp, "uReplyFreeQueueNextAddressRead=%u\n", pThis->uReplyFreeQueueNextAddressRead);
4061 pHlp->pfnPrintf(pHlp, "uReplyPostQueueNextEntryFreeWrite=%u\n", pThis->uReplyPostQueueNextEntryFreeWrite);
4062 pHlp->pfnPrintf(pHlp, "uReplyPostQueueNextAddressRead=%u\n", pThis->uReplyPostQueueNextAddressRead);
4063 pHlp->pfnPrintf(pHlp, "uRequestQueueNextEntryFreeWrite=%u\n", pThis->uRequestQueueNextEntryFreeWrite);
4064 pHlp->pfnPrintf(pHlp, "uRequestQueueNextAddressRead=%u\n", pThis->uRequestQueueNextAddressRead);
4065
4066 /*
4067 * Show queue content if verbose
4068 */
4069 if (fVerbose)
4070 {
4071 for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
4072 pHlp->pfnPrintf(pHlp, "RFQ[%u]=%#x\n", i, pThis->pReplyFreeQueueBaseR3[i]);
4073
4074 pHlp->pfnPrintf(pHlp, "\n");
4075
4076 for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
4077 pHlp->pfnPrintf(pHlp, "RPQ[%u]=%#x\n", i, pThis->pReplyPostQueueBaseR3[i]);
4078
4079 pHlp->pfnPrintf(pHlp, "\n");
4080
4081 for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++)
4082 pHlp->pfnPrintf(pHlp, "ReqQ[%u]=%#x\n", i, pThis->pRequestQueueBaseR3[i]);
4083 }
4084
4085 /*
4086 * Print the device status.
4087 */
4088 for (unsigned i = 0; i < pThis->cDeviceStates; i++)
4089 {
4090 PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[i];
4091
4092 pHlp->pfnPrintf(pHlp, "\n");
4093
4094 pHlp->pfnPrintf(pHlp, "Device[%u]: device-attached=%RTbool cOutstandingRequests=%u\n",
4095 i, pDevice->pDrvBase != NULL, pDevice->cOutstandingRequests);
4096 }
4097}
4098
4099/**
4100 * Allocate the queues.
4101 *
4102 * @returns VBox status code.
4103 *
4104 * @param pThis Pointer to the LsiLogic device state.
4105 */
4106static int lsilogicR3QueuesAlloc(PLSILOGICSCSI pThis)
4107{
4108 PVM pVM = PDMDevHlpGetVM(pThis->pDevInsR3);
4109 uint32_t cbQueues;
4110
4111 Assert(!pThis->pReplyFreeQueueBaseR3);
4112
4113 cbQueues = 2*pThis->cReplyQueueEntries * sizeof(uint32_t);
4114 cbQueues += pThis->cRequestQueueEntries * sizeof(uint32_t);
4115 int rc = MMHyperAlloc(pVM, cbQueues, 1, MM_TAG_PDM_DEVICE_USER,
4116 (void **)&pThis->pReplyFreeQueueBaseR3);
4117 if (RT_FAILURE(rc))
4118 return VERR_NO_MEMORY;
4119 pThis->pReplyFreeQueueBaseR0 = MMHyperR3ToR0(pVM, (void *)pThis->pReplyFreeQueueBaseR3);
4120 pThis->pReplyFreeQueueBaseRC = MMHyperR3ToRC(pVM, (void *)pThis->pReplyFreeQueueBaseR3);
4121
4122 pThis->pReplyPostQueueBaseR3 = pThis->pReplyFreeQueueBaseR3 + pThis->cReplyQueueEntries;
4123 pThis->pReplyPostQueueBaseR0 = MMHyperR3ToR0(pVM, (void *)pThis->pReplyPostQueueBaseR3);
4124 pThis->pReplyPostQueueBaseRC = MMHyperR3ToRC(pVM, (void *)pThis->pReplyPostQueueBaseR3);
4125
4126 pThis->pRequestQueueBaseR3 = pThis->pReplyPostQueueBaseR3 + pThis->cReplyQueueEntries;
4127 pThis->pRequestQueueBaseR0 = MMHyperR3ToR0(pVM, (void *)pThis->pRequestQueueBaseR3);
4128 pThis->pRequestQueueBaseRC = MMHyperR3ToRC(pVM, (void *)pThis->pRequestQueueBaseR3);
4129
4130 return VINF_SUCCESS;
4131}
4132
4133/**
4134 * Free the hyper memory used or the queues.
4135 *
4136 * @returns nothing.
4137 *
4138 * @param pThis Pointer to the LsiLogic device state.
4139 */
4140static void lsilogicR3QueuesFree(PLSILOGICSCSI pThis)
4141{
4142 PVM pVM = PDMDevHlpGetVM(pThis->pDevInsR3);
4143 int rc = VINF_SUCCESS;
4144
4145 AssertPtr(pThis->pReplyFreeQueueBaseR3);
4146
4147 rc = MMHyperFree(pVM, (void *)pThis->pReplyFreeQueueBaseR3);
4148 AssertRC(rc);
4149
4150 pThis->pReplyFreeQueueBaseR3 = NULL;
4151 pThis->pReplyPostQueueBaseR3 = NULL;
4152 pThis->pRequestQueueBaseR3 = NULL;
4153}
4154
4155
4156/* The worker thread. */
4157static DECLCALLBACK(int) lsilogicR3Worker(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
4158{
4159 PLSILOGICSCSI pThis = (PLSILOGICSCSI)pThread->pvUser;
4160 int rc = VINF_SUCCESS;
4161
4162 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
4163 return VINF_SUCCESS;
4164
4165 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
4166 {
4167 ASMAtomicWriteBool(&pThis->fWrkThreadSleeping, true);
4168 bool fNotificationSent = ASMAtomicXchgBool(&pThis->fNotificationSent, false);
4169 if (!fNotificationSent)
4170 {
4171 Assert(ASMAtomicReadBool(&pThis->fWrkThreadSleeping));
4172 rc = SUPSemEventWaitNoResume(pThis->pSupDrvSession, pThis->hEvtProcess, RT_INDEFINITE_WAIT);
4173 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
4174 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
4175 break;
4176 LogFlowFunc(("Woken up with rc=%Rrc\n", rc));
4177 ASMAtomicWriteBool(&pThis->fNotificationSent, false);
4178 }
4179
4180 ASMAtomicWriteBool(&pThis->fWrkThreadSleeping, false);
4181
4182 /* Only process request which arrived before we received the notification. */
4183 uint32_t uRequestQueueNextEntryWrite = ASMAtomicReadU32(&pThis->uRequestQueueNextEntryFreeWrite);
4184
4185 /* Go through the messages now and process them. */
4186 while ( RT_LIKELY(pThis->enmState == LSILOGICSTATE_OPERATIONAL)
4187 && (pThis->uRequestQueueNextAddressRead != uRequestQueueNextEntryWrite))
4188 {
4189 uint32_t u32RequestMessageFrameDesc = pThis->CTX_SUFF(pRequestQueueBase)[pThis->uRequestQueueNextAddressRead];
4190 RTGCPHYS GCPhysMessageFrameAddr = LSILOGIC_RTGCPHYS_FROM_U32(pThis->u32HostMFAHighAddr,
4191 (u32RequestMessageFrameDesc & ~0x07));
4192
4193 PLSILOGICREQ pLsiReq;
4194
4195 /* Get new task state. */
4196 rc = RTMemCacheAllocEx(pThis->hTaskCache, (void **)&pLsiReq);
4197 AssertRC(rc);
4198
4199 pLsiReq->GCPhysMessageFrameAddr = GCPhysMessageFrameAddr;
4200
4201 /* Read the message header from the guest first. */
4202 PDMDevHlpPhysRead(pDevIns, GCPhysMessageFrameAddr, &pLsiReq->GuestRequest, sizeof(MptMessageHdr));
4203
4204 /* Determine the size of the request. */
4205 uint32_t cbRequest = 0;
4206
4207 switch (pLsiReq->GuestRequest.Header.u8Function)
4208 {
4209 case MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST:
4210 cbRequest = sizeof(MptSCSIIORequest);
4211 break;
4212 case MPT_MESSAGE_HDR_FUNCTION_SCSI_TASK_MGMT:
4213 cbRequest = sizeof(MptSCSITaskManagementRequest);
4214 break;
4215 case MPT_MESSAGE_HDR_FUNCTION_IOC_INIT:
4216 cbRequest = sizeof(MptIOCInitRequest);
4217 break;
4218 case MPT_MESSAGE_HDR_FUNCTION_IOC_FACTS:
4219 cbRequest = sizeof(MptIOCFactsRequest);
4220 break;
4221 case MPT_MESSAGE_HDR_FUNCTION_CONFIG:
4222 cbRequest = sizeof(MptConfigurationRequest);
4223 break;
4224 case MPT_MESSAGE_HDR_FUNCTION_PORT_FACTS:
4225 cbRequest = sizeof(MptPortFactsRequest);
4226 break;
4227 case MPT_MESSAGE_HDR_FUNCTION_PORT_ENABLE:
4228 cbRequest = sizeof(MptPortEnableRequest);
4229 break;
4230 case MPT_MESSAGE_HDR_FUNCTION_EVENT_NOTIFICATION:
4231 cbRequest = sizeof(MptEventNotificationRequest);
4232 break;
4233 case MPT_MESSAGE_HDR_FUNCTION_EVENT_ACK:
4234 AssertMsgFailed(("todo\n"));
4235 //cbRequest = sizeof(MptEventAckRequest);
4236 break;
4237 case MPT_MESSAGE_HDR_FUNCTION_FW_DOWNLOAD:
4238 cbRequest = sizeof(MptFWDownloadRequest);
4239 break;
4240 case MPT_MESSAGE_HDR_FUNCTION_FW_UPLOAD:
4241 cbRequest = sizeof(MptFWUploadRequest);
4242 break;
4243 default:
4244 AssertMsgFailed(("Unknown function issued %u\n", pLsiReq->GuestRequest.Header.u8Function));
4245 lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INVALID_FUNCTION);
4246 }
4247
4248 if (cbRequest != 0)
4249 {
4250 /* Read the complete message frame from guest memory now. */
4251 PDMDevHlpPhysRead(pDevIns, GCPhysMessageFrameAddr, &pLsiReq->GuestRequest, cbRequest);
4252
4253 /* Handle SCSI I/O requests now. */
4254 if (pLsiReq->GuestRequest.Header.u8Function == MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST)
4255 {
4256 rc = lsilogicR3ProcessSCSIIORequest(pThis, pLsiReq);
4257 AssertRC(rc);
4258 }
4259 else
4260 {
4261 MptReplyUnion Reply;
4262 rc = lsilogicR3ProcessMessageRequest(pThis, &pLsiReq->GuestRequest.Header, &Reply);
4263 AssertRC(rc);
4264 RTMemCacheFree(pThis->hTaskCache, pLsiReq);
4265 }
4266
4267 pThis->uRequestQueueNextAddressRead++;
4268 pThis->uRequestQueueNextAddressRead %= pThis->cRequestQueueEntries;
4269 }
4270 } /* While request frames available. */
4271 } /* While running */
4272
4273 return VINF_SUCCESS;
4274}
4275
4276
4277/**
4278 * Unblock the worker thread so it can respond to a state change.
4279 *
4280 * @returns VBox status code.
4281 * @param pDevIns The pcnet device instance.
4282 * @param pThread The send thread.
4283 */
4284static DECLCALLBACK(int) lsilogicR3WorkerWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
4285{
4286 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4287 return SUPSemEventSignal(pThis->pSupDrvSession, pThis->hEvtProcess);
4288}
4289
4290
4291/**
4292 * Kicks the controller to process pending tasks after the VM was resumed
4293 * or loaded from a saved state.
4294 *
4295 * @returns nothing.
4296 * @param pThis Pointer to the LsiLogic device state.
4297 */
4298static void lsilogicR3Kick(PLSILOGICSCSI pThis)
4299{
4300 if (pThis->fNotificationSent)
4301 {
4302 /* Send a notifier to the PDM queue that there are pending requests. */
4303 PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(pThis->CTX_SUFF(pNotificationQueue));
4304 AssertMsg(pItem, ("Allocating item for queue failed\n"));
4305 PDMQueueInsert(pThis->CTX_SUFF(pNotificationQueue), (PPDMQUEUEITEMCORE)pItem);
4306 }
4307 else if (pThis->VBoxSCSI.fBusy)
4308 {
4309 /* The BIOS had a request active when we got suspended. Resume it. */
4310 int rc = lsilogicR3PrepareBiosScsiRequest(pThis);
4311 AssertRC(rc);
4312 }
4313
4314}
4315
4316
4317/*
4318 * Saved state.
4319 */
4320
4321/**
4322 * @callback_method_impl{FNSSMDEVLIVEEXEC}
4323 */
4324static DECLCALLBACK(int) lsilogicR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
4325{
4326 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4327
4328 SSMR3PutU32(pSSM, pThis->enmCtrlType);
4329 SSMR3PutU32(pSSM, pThis->cDeviceStates);
4330 SSMR3PutU32(pSSM, pThis->cPorts);
4331
4332 /* Save the device config. */
4333 for (unsigned i = 0; i < pThis->cDeviceStates; i++)
4334 SSMR3PutBool(pSSM, pThis->paDeviceStates[i].pDrvBase != NULL);
4335
4336 return VINF_SSM_DONT_CALL_AGAIN;
4337}
4338
4339/**
4340 * @callback_method_impl{FNSSMDEVSAVEEXEC}
4341 */
4342static DECLCALLBACK(int) lsilogicR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4343{
4344 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4345
4346 /* Every device first. */
4347 lsilogicR3LiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
4348 for (unsigned i = 0; i < pThis->cDeviceStates; i++)
4349 {
4350 PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[i];
4351
4352 AssertMsg(!pDevice->cOutstandingRequests,
4353 ("There are still outstanding requests on this device\n"));
4354 SSMR3PutU32(pSSM, pDevice->cOutstandingRequests);
4355 }
4356 /* Now the main device state. */
4357 SSMR3PutU32 (pSSM, pThis->enmState);
4358 SSMR3PutU32 (pSSM, pThis->enmWhoInit);
4359 SSMR3PutU32 (pSSM, pThis->enmDoorbellState);
4360 SSMR3PutBool (pSSM, pThis->fDiagnosticEnabled);
4361 SSMR3PutBool (pSSM, pThis->fNotificationSent);
4362 SSMR3PutBool (pSSM, pThis->fEventNotificationEnabled);
4363 SSMR3PutU32 (pSSM, pThis->uInterruptMask);
4364 SSMR3PutU32 (pSSM, pThis->uInterruptStatus);
4365 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aMessage); i++)
4366 SSMR3PutU32 (pSSM, pThis->aMessage[i]);
4367 SSMR3PutU32 (pSSM, pThis->iMessage);
4368 SSMR3PutU32 (pSSM, pThis->cMessage);
4369 SSMR3PutMem (pSSM, &pThis->ReplyBuffer, sizeof(pThis->ReplyBuffer));
4370 SSMR3PutU32 (pSSM, pThis->uNextReplyEntryRead);
4371 SSMR3PutU32 (pSSM, pThis->cReplySize);
4372 SSMR3PutU16 (pSSM, pThis->u16IOCFaultCode);
4373 SSMR3PutU32 (pSSM, pThis->u32HostMFAHighAddr);
4374 SSMR3PutU32 (pSSM, pThis->u32SenseBufferHighAddr);
4375 SSMR3PutU8 (pSSM, pThis->cMaxDevices);
4376 SSMR3PutU8 (pSSM, pThis->cMaxBuses);
4377 SSMR3PutU16 (pSSM, pThis->cbReplyFrame);
4378 SSMR3PutU32 (pSSM, pThis->iDiagnosticAccess);
4379 SSMR3PutU32 (pSSM, pThis->cReplyQueueEntries);
4380 SSMR3PutU32 (pSSM, pThis->cRequestQueueEntries);
4381 SSMR3PutU32 (pSSM, pThis->uReplyFreeQueueNextEntryFreeWrite);
4382 SSMR3PutU32 (pSSM, pThis->uReplyFreeQueueNextAddressRead);
4383 SSMR3PutU32 (pSSM, pThis->uReplyPostQueueNextEntryFreeWrite);
4384 SSMR3PutU32 (pSSM, pThis->uReplyPostQueueNextAddressRead);
4385 SSMR3PutU32 (pSSM, pThis->uRequestQueueNextEntryFreeWrite);
4386 SSMR3PutU32 (pSSM, pThis->uRequestQueueNextAddressRead);
4387
4388 for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
4389 SSMR3PutU32(pSSM, pThis->pReplyFreeQueueBaseR3[i]);
4390 for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
4391 SSMR3PutU32(pSSM, pThis->pReplyPostQueueBaseR3[i]);
4392 for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++)
4393 SSMR3PutU32(pSSM, pThis->pRequestQueueBaseR3[i]);
4394
4395 SSMR3PutU16 (pSSM, pThis->u16NextHandle);
4396
4397 /* Save diagnostic memory register and data regions. */
4398 SSMR3PutU32 (pSSM, pThis->u32DiagMemAddr);
4399 SSMR3PutU32 (pSSM, lsilogicR3MemRegionsCount(pThis));
4400
4401 PLSILOGICMEMREGN pIt = NULL;
4402 RTListForEach(&pThis->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList)
4403 {
4404 SSMR3PutU32(pSSM, pIt->u32AddrStart);
4405 SSMR3PutU32(pSSM, pIt->u32AddrEnd);
4406 SSMR3PutMem(pSSM, &pIt->au32Data[0], (pIt->u32AddrEnd - pIt->u32AddrStart + 1) * sizeof(uint32_t));
4407 }
4408
4409 PMptConfigurationPagesSupported pPages = pThis->pConfigurationPages;
4410
4411 SSMR3PutMem (pSSM, &pPages->ManufacturingPage0, sizeof(MptConfigurationPageManufacturing0));
4412 SSMR3PutMem (pSSM, &pPages->ManufacturingPage1, sizeof(MptConfigurationPageManufacturing1));
4413 SSMR3PutMem (pSSM, &pPages->ManufacturingPage2, sizeof(MptConfigurationPageManufacturing2));
4414 SSMR3PutMem (pSSM, &pPages->ManufacturingPage3, sizeof(MptConfigurationPageManufacturing3));
4415 SSMR3PutMem (pSSM, &pPages->ManufacturingPage4, sizeof(MptConfigurationPageManufacturing4));
4416 SSMR3PutMem (pSSM, &pPages->ManufacturingPage5, sizeof(MptConfigurationPageManufacturing5));
4417 SSMR3PutMem (pSSM, &pPages->ManufacturingPage6, sizeof(MptConfigurationPageManufacturing6));
4418 SSMR3PutMem (pSSM, &pPages->ManufacturingPage8, sizeof(MptConfigurationPageManufacturing8));
4419 SSMR3PutMem (pSSM, &pPages->ManufacturingPage9, sizeof(MptConfigurationPageManufacturing9));
4420 SSMR3PutMem (pSSM, &pPages->ManufacturingPage10, sizeof(MptConfigurationPageManufacturing10));
4421 SSMR3PutMem (pSSM, &pPages->IOUnitPage0, sizeof(MptConfigurationPageIOUnit0));
4422 SSMR3PutMem (pSSM, &pPages->IOUnitPage1, sizeof(MptConfigurationPageIOUnit1));
4423 SSMR3PutMem (pSSM, &pPages->IOUnitPage2, sizeof(MptConfigurationPageIOUnit2));
4424 SSMR3PutMem (pSSM, &pPages->IOUnitPage3, sizeof(MptConfigurationPageIOUnit3));
4425 SSMR3PutMem (pSSM, &pPages->IOUnitPage4, sizeof(MptConfigurationPageIOUnit4));
4426 SSMR3PutMem (pSSM, &pPages->IOCPage0, sizeof(MptConfigurationPageIOC0));
4427 SSMR3PutMem (pSSM, &pPages->IOCPage1, sizeof(MptConfigurationPageIOC1));
4428 SSMR3PutMem (pSSM, &pPages->IOCPage2, sizeof(MptConfigurationPageIOC2));
4429 SSMR3PutMem (pSSM, &pPages->IOCPage3, sizeof(MptConfigurationPageIOC3));
4430 SSMR3PutMem (pSSM, &pPages->IOCPage4, sizeof(MptConfigurationPageIOC4));
4431 SSMR3PutMem (pSSM, &pPages->IOCPage6, sizeof(MptConfigurationPageIOC6));
4432 SSMR3PutMem (pSSM, &pPages->BIOSPage1, sizeof(MptConfigurationPageBIOS1));
4433 SSMR3PutMem (pSSM, &pPages->BIOSPage2, sizeof(MptConfigurationPageBIOS2));
4434 SSMR3PutMem (pSSM, &pPages->BIOSPage4, sizeof(MptConfigurationPageBIOS4));
4435
4436 /* Device dependent pages */
4437 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
4438 {
4439 PMptConfigurationPagesSpi pSpiPages = &pPages->u.SpiPages;
4440
4441 SSMR3PutMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage0, sizeof(MptConfigurationPageSCSISPIPort0));
4442 SSMR3PutMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage1, sizeof(MptConfigurationPageSCSISPIPort1));
4443 SSMR3PutMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage2, sizeof(MptConfigurationPageSCSISPIPort2));
4444
4445 for (unsigned i = 0; i < RT_ELEMENTS(pSpiPages->aBuses[0].aDevicePages); i++)
4446 {
4447 SSMR3PutMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage0, sizeof(MptConfigurationPageSCSISPIDevice0));
4448 SSMR3PutMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage1, sizeof(MptConfigurationPageSCSISPIDevice1));
4449 SSMR3PutMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage2, sizeof(MptConfigurationPageSCSISPIDevice2));
4450 SSMR3PutMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage3, sizeof(MptConfigurationPageSCSISPIDevice3));
4451 }
4452 }
4453 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
4454 {
4455 PMptConfigurationPagesSas pSasPages = &pPages->u.SasPages;
4456
4457 SSMR3PutU32(pSSM, pSasPages->cbManufacturingPage7);
4458 SSMR3PutU32(pSSM, pSasPages->cbSASIOUnitPage0);
4459 SSMR3PutU32(pSSM, pSasPages->cbSASIOUnitPage1);
4460
4461 SSMR3PutMem(pSSM, pSasPages->pManufacturingPage7, pSasPages->cbManufacturingPage7);
4462 SSMR3PutMem(pSSM, pSasPages->pSASIOUnitPage0, pSasPages->cbSASIOUnitPage0);
4463 SSMR3PutMem(pSSM, pSasPages->pSASIOUnitPage1, pSasPages->cbSASIOUnitPage1);
4464
4465 SSMR3PutMem(pSSM, &pSasPages->SASIOUnitPage2, sizeof(MptConfigurationPageSASIOUnit2));
4466 SSMR3PutMem(pSSM, &pSasPages->SASIOUnitPage3, sizeof(MptConfigurationPageSASIOUnit3));
4467
4468 SSMR3PutU32(pSSM, pSasPages->cPHYs);
4469 for (unsigned i = 0; i < pSasPages->cPHYs; i++)
4470 {
4471 SSMR3PutMem(pSSM, &pSasPages->paPHYs[i].SASPHYPage0, sizeof(MptConfigurationPageSASPHY0));
4472 SSMR3PutMem(pSSM, &pSasPages->paPHYs[i].SASPHYPage1, sizeof(MptConfigurationPageSASPHY1));
4473 }
4474
4475 /* The number of devices first. */
4476 SSMR3PutU32(pSSM, pSasPages->cDevices);
4477
4478 PMptSASDevice pCurr = pSasPages->pSASDeviceHead;
4479
4480 while (pCurr)
4481 {
4482 SSMR3PutMem(pSSM, &pCurr->SASDevicePage0, sizeof(MptConfigurationPageSASDevice0));
4483 SSMR3PutMem(pSSM, &pCurr->SASDevicePage1, sizeof(MptConfigurationPageSASDevice1));
4484 SSMR3PutMem(pSSM, &pCurr->SASDevicePage2, sizeof(MptConfigurationPageSASDevice2));
4485
4486 pCurr = pCurr->pNext;
4487 }
4488 }
4489 else
4490 AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
4491
4492 vboxscsiR3SaveExec(&pThis->VBoxSCSI, pSSM);
4493 return SSMR3PutU32(pSSM, ~0);
4494}
4495
4496/**
4497 * @callback_method_impl{FNSSMDEVLOADDONE}
4498 */
4499static DECLCALLBACK(int) lsilogicR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4500{
4501 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4502
4503 lsilogicR3Kick(pThis);
4504 return VINF_SUCCESS;
4505}
4506
4507/**
4508 * @callback_method_impl{FNSSMDEVLOADEXEC}
4509 */
4510static DECLCALLBACK(int) lsilogicR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
4511{
4512 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4513 int rc;
4514
4515 if ( uVersion != LSILOGIC_SAVED_STATE_VERSION
4516 && uVersion != LSILOGIC_SAVED_STATE_VERSION_PRE_DIAG_MEM
4517 && uVersion != LSILOGIC_SAVED_STATE_VERSION_BOOL_DOORBELL
4518 && uVersion != LSILOGIC_SAVED_STATE_VERSION_PRE_SAS
4519 && uVersion != LSILOGIC_SAVED_STATE_VERSION_VBOX_30)
4520 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4521
4522 /* device config */
4523 if (uVersion > LSILOGIC_SAVED_STATE_VERSION_PRE_SAS)
4524 {
4525 LSILOGICCTRLTYPE enmCtrlType;
4526 uint32_t cDeviceStates, cPorts;
4527
4528 rc = SSMR3GetU32(pSSM, (uint32_t *)&enmCtrlType);
4529 AssertRCReturn(rc, rc);
4530 rc = SSMR3GetU32(pSSM, &cDeviceStates);
4531 AssertRCReturn(rc, rc);
4532 rc = SSMR3GetU32(pSSM, &cPorts);
4533 AssertRCReturn(rc, rc);
4534
4535 if (enmCtrlType != pThis->enmCtrlType)
4536 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Controller type): config=%d state=%d"),
4537 pThis->enmCtrlType, enmCtrlType);
4538 if (cDeviceStates != pThis->cDeviceStates)
4539 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Device states): config=%u state=%u"),
4540 pThis->cDeviceStates, cDeviceStates);
4541 if (cPorts != pThis->cPorts)
4542 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Ports): config=%u state=%u"),
4543 pThis->cPorts, cPorts);
4544 }
4545 if (uVersion > LSILOGIC_SAVED_STATE_VERSION_VBOX_30)
4546 {
4547 for (unsigned i = 0; i < pThis->cDeviceStates; i++)
4548 {
4549 bool fPresent;
4550 rc = SSMR3GetBool(pSSM, &fPresent);
4551 AssertRCReturn(rc, rc);
4552 if (fPresent != (pThis->paDeviceStates[i].pDrvBase != NULL))
4553 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target %u config mismatch: config=%RTbool state=%RTbool"),
4554 i, pThis->paDeviceStates[i].pDrvBase != NULL, fPresent);
4555 }
4556 }
4557 if (uPass != SSM_PASS_FINAL)
4558 return VINF_SUCCESS;
4559
4560 /* Every device first. */
4561 for (unsigned i = 0; i < pThis->cDeviceStates; i++)
4562 {
4563 PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[i];
4564
4565 AssertMsg(!pDevice->cOutstandingRequests,
4566 ("There are still outstanding requests on this device\n"));
4567 SSMR3GetU32(pSSM, (uint32_t *)&pDevice->cOutstandingRequests);
4568 }
4569 /* Now the main device state. */
4570 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->enmState);
4571 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->enmWhoInit);
4572 if (uVersion <= LSILOGIC_SAVED_STATE_VERSION_BOOL_DOORBELL)
4573 {
4574 bool fDoorbellInProgress = false;
4575
4576 /*
4577 * The doorbell status flag distinguishes only between
4578 * doorbell not in use or a Function handshake is currently in progress.
4579 */
4580 SSMR3GetBool (pSSM, &fDoorbellInProgress);
4581 if (fDoorbellInProgress)
4582 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_FN_HANDSHAKE;
4583 else
4584 pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
4585 }
4586 else
4587 SSMR3GetU32(pSSM, (uint32_t *)&pThis->enmDoorbellState);
4588 SSMR3GetBool (pSSM, &pThis->fDiagnosticEnabled);
4589 SSMR3GetBool (pSSM, &pThis->fNotificationSent);
4590 SSMR3GetBool (pSSM, &pThis->fEventNotificationEnabled);
4591 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uInterruptMask);
4592 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uInterruptStatus);
4593 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aMessage); i++)
4594 SSMR3GetU32 (pSSM, &pThis->aMessage[i]);
4595 SSMR3GetU32 (pSSM, &pThis->iMessage);
4596 SSMR3GetU32 (pSSM, &pThis->cMessage);
4597 SSMR3GetMem (pSSM, &pThis->ReplyBuffer, sizeof(pThis->ReplyBuffer));
4598 SSMR3GetU32 (pSSM, &pThis->uNextReplyEntryRead);
4599 SSMR3GetU32 (pSSM, &pThis->cReplySize);
4600 SSMR3GetU16 (pSSM, &pThis->u16IOCFaultCode);
4601 SSMR3GetU32 (pSSM, &pThis->u32HostMFAHighAddr);
4602 SSMR3GetU32 (pSSM, &pThis->u32SenseBufferHighAddr);
4603 SSMR3GetU8 (pSSM, &pThis->cMaxDevices);
4604 SSMR3GetU8 (pSSM, &pThis->cMaxBuses);
4605 SSMR3GetU16 (pSSM, &pThis->cbReplyFrame);
4606 SSMR3GetU32 (pSSM, &pThis->iDiagnosticAccess);
4607
4608 uint32_t cReplyQueueEntries, cRequestQueueEntries;
4609 SSMR3GetU32 (pSSM, &cReplyQueueEntries);
4610 SSMR3GetU32 (pSSM, &cRequestQueueEntries);
4611
4612 if ( cReplyQueueEntries != pThis->cReplyQueueEntries
4613 || cRequestQueueEntries != pThis->cRequestQueueEntries)
4614 {
4615 LogFlow(("Reallocating queues cReplyQueueEntries=%u cRequestQueuEntries=%u\n",
4616 cReplyQueueEntries, cRequestQueueEntries));
4617 lsilogicR3QueuesFree(pThis);
4618 pThis->cReplyQueueEntries = cReplyQueueEntries;
4619 pThis->cRequestQueueEntries = cRequestQueueEntries;
4620 rc = lsilogicR3QueuesAlloc(pThis);
4621 if (RT_FAILURE(rc))
4622 return rc;
4623 }
4624
4625 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyFreeQueueNextEntryFreeWrite);
4626 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyFreeQueueNextAddressRead);
4627 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyPostQueueNextEntryFreeWrite);
4628 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyPostQueueNextAddressRead);
4629 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uRequestQueueNextEntryFreeWrite);
4630 SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uRequestQueueNextAddressRead);
4631
4632 PMptConfigurationPagesSupported pPages = pThis->pConfigurationPages;
4633
4634 if (uVersion <= LSILOGIC_SAVED_STATE_VERSION_PRE_SAS)
4635 {
4636 PMptConfigurationPagesSpi pSpiPages = &pPages->u.SpiPages;
4637 MptConfigurationPagesSupported_SSM_V2 ConfigPagesV2;
4638
4639 if (pThis->enmCtrlType != LSILOGICCTRLTYPE_SCSI_SPI)
4640 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch: Expected SPI SCSI controller"));
4641
4642 SSMR3GetMem(pSSM, &ConfigPagesV2,
4643 sizeof(MptConfigurationPagesSupported_SSM_V2));
4644
4645 pPages->ManufacturingPage0 = ConfigPagesV2.ManufacturingPage0;
4646 pPages->ManufacturingPage1 = ConfigPagesV2.ManufacturingPage1;
4647 pPages->ManufacturingPage2 = ConfigPagesV2.ManufacturingPage2;
4648 pPages->ManufacturingPage3 = ConfigPagesV2.ManufacturingPage3;
4649 pPages->ManufacturingPage4 = ConfigPagesV2.ManufacturingPage4;
4650 pPages->IOUnitPage0 = ConfigPagesV2.IOUnitPage0;
4651 pPages->IOUnitPage1 = ConfigPagesV2.IOUnitPage1;
4652 pPages->IOUnitPage2 = ConfigPagesV2.IOUnitPage2;
4653 pPages->IOUnitPage3 = ConfigPagesV2.IOUnitPage3;
4654 pPages->IOCPage0 = ConfigPagesV2.IOCPage0;
4655 pPages->IOCPage1 = ConfigPagesV2.IOCPage1;
4656 pPages->IOCPage2 = ConfigPagesV2.IOCPage2;
4657 pPages->IOCPage3 = ConfigPagesV2.IOCPage3;
4658 pPages->IOCPage4 = ConfigPagesV2.IOCPage4;
4659 pPages->IOCPage6 = ConfigPagesV2.IOCPage6;
4660
4661 pSpiPages->aPortPages[0].SCSISPIPortPage0 = ConfigPagesV2.aPortPages[0].SCSISPIPortPage0;
4662 pSpiPages->aPortPages[0].SCSISPIPortPage1 = ConfigPagesV2.aPortPages[0].SCSISPIPortPage1;
4663 pSpiPages->aPortPages[0].SCSISPIPortPage2 = ConfigPagesV2.aPortPages[0].SCSISPIPortPage2;
4664
4665 for (unsigned i = 0; i < RT_ELEMENTS(pPages->u.SpiPages.aBuses[0].aDevicePages); i++)
4666 {
4667 pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage0 = ConfigPagesV2.aBuses[0].aDevicePages[i].SCSISPIDevicePage0;
4668 pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage1 = ConfigPagesV2.aBuses[0].aDevicePages[i].SCSISPIDevicePage1;
4669 pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage2 = ConfigPagesV2.aBuses[0].aDevicePages[i].SCSISPIDevicePage2;
4670 pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage3 = ConfigPagesV2.aBuses[0].aDevicePages[i].SCSISPIDevicePage3;
4671 }
4672 }
4673 else
4674 {
4675 /* Queue content */
4676 for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
4677 SSMR3GetU32(pSSM, (uint32_t *)&pThis->pReplyFreeQueueBaseR3[i]);
4678 for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
4679 SSMR3GetU32(pSSM, (uint32_t *)&pThis->pReplyPostQueueBaseR3[i]);
4680 for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++)
4681 SSMR3GetU32(pSSM, (uint32_t *)&pThis->pRequestQueueBaseR3[i]);
4682
4683 SSMR3GetU16(pSSM, &pThis->u16NextHandle);
4684
4685 if (uVersion > LSILOGIC_SAVED_STATE_VERSION_PRE_DIAG_MEM)
4686 {
4687 uint32_t cMemRegions = 0;
4688
4689 /* Save diagnostic memory register and data regions. */
4690 SSMR3GetU32 (pSSM, &pThis->u32DiagMemAddr);
4691 SSMR3GetU32 (pSSM, &cMemRegions);
4692
4693 while (cMemRegions)
4694 {
4695 uint32_t u32AddrStart = 0;
4696 uint32_t u32AddrEnd = 0;
4697 uint32_t cRegion = 0;
4698 PLSILOGICMEMREGN pRegion = NULL;
4699
4700 SSMR3GetU32(pSSM, &u32AddrStart);
4701 SSMR3GetU32(pSSM, &u32AddrEnd);
4702
4703 cRegion = u32AddrEnd - u32AddrStart + 1;
4704 pRegion = (PLSILOGICMEMREGN)RTMemAllocZ(RT_OFFSETOF(LSILOGICMEMREGN, au32Data[cRegion]));
4705 if (pRegion)
4706 {
4707 pRegion->u32AddrStart = u32AddrStart;
4708 pRegion->u32AddrEnd = u32AddrEnd;
4709 SSMR3GetMem(pSSM, &pRegion->au32Data[0], cRegion * sizeof(uint32_t));
4710 lsilogicR3MemRegionInsert(pThis, pRegion);
4711 pThis->cbMemRegns += cRegion * sizeof(uint32_t);
4712 }
4713 else
4714 {
4715 /* Leave a log message but continue. */
4716 LogRel(("LsiLogic: Out of memory while restoring the state, might not work as expected\n"));
4717 SSMR3Skip(pSSM, cRegion * sizeof(uint32_t));
4718 }
4719 cMemRegions--;
4720 }
4721 }
4722
4723 /* Configuration pages */
4724 SSMR3GetMem(pSSM, &pPages->ManufacturingPage0, sizeof(MptConfigurationPageManufacturing0));
4725 SSMR3GetMem(pSSM, &pPages->ManufacturingPage1, sizeof(MptConfigurationPageManufacturing1));
4726 SSMR3GetMem(pSSM, &pPages->ManufacturingPage2, sizeof(MptConfigurationPageManufacturing2));
4727 SSMR3GetMem(pSSM, &pPages->ManufacturingPage3, sizeof(MptConfigurationPageManufacturing3));
4728 SSMR3GetMem(pSSM, &pPages->ManufacturingPage4, sizeof(MptConfigurationPageManufacturing4));
4729 SSMR3GetMem(pSSM, &pPages->ManufacturingPage5, sizeof(MptConfigurationPageManufacturing5));
4730 SSMR3GetMem(pSSM, &pPages->ManufacturingPage6, sizeof(MptConfigurationPageManufacturing6));
4731 SSMR3GetMem(pSSM, &pPages->ManufacturingPage8, sizeof(MptConfigurationPageManufacturing8));
4732 SSMR3GetMem(pSSM, &pPages->ManufacturingPage9, sizeof(MptConfigurationPageManufacturing9));
4733 SSMR3GetMem(pSSM, &pPages->ManufacturingPage10, sizeof(MptConfigurationPageManufacturing10));
4734 SSMR3GetMem(pSSM, &pPages->IOUnitPage0, sizeof(MptConfigurationPageIOUnit0));
4735 SSMR3GetMem(pSSM, &pPages->IOUnitPage1, sizeof(MptConfigurationPageIOUnit1));
4736 SSMR3GetMem(pSSM, &pPages->IOUnitPage2, sizeof(MptConfigurationPageIOUnit2));
4737 SSMR3GetMem(pSSM, &pPages->IOUnitPage3, sizeof(MptConfigurationPageIOUnit3));
4738 SSMR3GetMem(pSSM, &pPages->IOUnitPage4, sizeof(MptConfigurationPageIOUnit4));
4739 SSMR3GetMem(pSSM, &pPages->IOCPage0, sizeof(MptConfigurationPageIOC0));
4740 SSMR3GetMem(pSSM, &pPages->IOCPage1, sizeof(MptConfigurationPageIOC1));
4741 SSMR3GetMem(pSSM, &pPages->IOCPage2, sizeof(MptConfigurationPageIOC2));
4742 SSMR3GetMem(pSSM, &pPages->IOCPage3, sizeof(MptConfigurationPageIOC3));
4743 SSMR3GetMem(pSSM, &pPages->IOCPage4, sizeof(MptConfigurationPageIOC4));
4744 SSMR3GetMem(pSSM, &pPages->IOCPage6, sizeof(MptConfigurationPageIOC6));
4745 SSMR3GetMem(pSSM, &pPages->BIOSPage1, sizeof(MptConfigurationPageBIOS1));
4746 SSMR3GetMem(pSSM, &pPages->BIOSPage2, sizeof(MptConfigurationPageBIOS2));
4747 SSMR3GetMem(pSSM, &pPages->BIOSPage4, sizeof(MptConfigurationPageBIOS4));
4748
4749 /* Device dependent pages */
4750 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
4751 {
4752 PMptConfigurationPagesSpi pSpiPages = &pPages->u.SpiPages;
4753
4754 SSMR3GetMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage0, sizeof(MptConfigurationPageSCSISPIPort0));
4755 SSMR3GetMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage1, sizeof(MptConfigurationPageSCSISPIPort1));
4756 SSMR3GetMem(pSSM, &pSpiPages->aPortPages[0].SCSISPIPortPage2, sizeof(MptConfigurationPageSCSISPIPort2));
4757
4758 for (unsigned i = 0; i < RT_ELEMENTS(pSpiPages->aBuses[0].aDevicePages); i++)
4759 {
4760 SSMR3GetMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage0, sizeof(MptConfigurationPageSCSISPIDevice0));
4761 SSMR3GetMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage1, sizeof(MptConfigurationPageSCSISPIDevice1));
4762 SSMR3GetMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage2, sizeof(MptConfigurationPageSCSISPIDevice2));
4763 SSMR3GetMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage3, sizeof(MptConfigurationPageSCSISPIDevice3));
4764 }
4765 }
4766 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
4767 {
4768 uint32_t cbPage0, cbPage1, cPHYs, cbManufacturingPage7;
4769 PMptConfigurationPagesSas pSasPages = &pPages->u.SasPages;
4770
4771 SSMR3GetU32(pSSM, &cbManufacturingPage7);
4772 SSMR3GetU32(pSSM, &cbPage0);
4773 SSMR3GetU32(pSSM, &cbPage1);
4774
4775 if ( (cbPage0 != pSasPages->cbSASIOUnitPage0)
4776 || (cbPage1 != pSasPages->cbSASIOUnitPage1)
4777 || (cbManufacturingPage7 != pSasPages->cbManufacturingPage7))
4778 return VERR_SSM_LOAD_CONFIG_MISMATCH;
4779
4780 AssertPtr(pSasPages->pManufacturingPage7);
4781 AssertPtr(pSasPages->pSASIOUnitPage0);
4782 AssertPtr(pSasPages->pSASIOUnitPage1);
4783
4784 SSMR3GetMem(pSSM, pSasPages->pManufacturingPage7, pSasPages->cbManufacturingPage7);
4785 SSMR3GetMem(pSSM, pSasPages->pSASIOUnitPage0, pSasPages->cbSASIOUnitPage0);
4786 SSMR3GetMem(pSSM, pSasPages->pSASIOUnitPage1, pSasPages->cbSASIOUnitPage1);
4787
4788 SSMR3GetMem(pSSM, &pSasPages->SASIOUnitPage2, sizeof(MptConfigurationPageSASIOUnit2));
4789 SSMR3GetMem(pSSM, &pSasPages->SASIOUnitPage3, sizeof(MptConfigurationPageSASIOUnit3));
4790
4791 SSMR3GetU32(pSSM, &cPHYs);
4792 if (cPHYs != pSasPages->cPHYs)
4793 return VERR_SSM_LOAD_CONFIG_MISMATCH;
4794
4795 AssertPtr(pSasPages->paPHYs);
4796 for (unsigned i = 0; i < pSasPages->cPHYs; i++)
4797 {
4798 SSMR3GetMem(pSSM, &pSasPages->paPHYs[i].SASPHYPage0, sizeof(MptConfigurationPageSASPHY0));
4799 SSMR3GetMem(pSSM, &pSasPages->paPHYs[i].SASPHYPage1, sizeof(MptConfigurationPageSASPHY1));
4800 }
4801
4802 /* The number of devices first. */
4803 SSMR3GetU32(pSSM, &pSasPages->cDevices);
4804
4805 PMptSASDevice pCurr = pSasPages->pSASDeviceHead;
4806
4807 for (unsigned i = 0; i < pSasPages->cDevices; i++)
4808 {
4809 SSMR3GetMem(pSSM, &pCurr->SASDevicePage0, sizeof(MptConfigurationPageSASDevice0));
4810 SSMR3GetMem(pSSM, &pCurr->SASDevicePage1, sizeof(MptConfigurationPageSASDevice1));
4811 SSMR3GetMem(pSSM, &pCurr->SASDevicePage2, sizeof(MptConfigurationPageSASDevice2));
4812
4813 pCurr = pCurr->pNext;
4814 }
4815
4816 Assert(!pCurr);
4817 }
4818 else
4819 AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
4820 }
4821
4822 rc = vboxscsiR3LoadExec(&pThis->VBoxSCSI, pSSM);
4823 if (RT_FAILURE(rc))
4824 {
4825 LogRel(("LsiLogic: Failed to restore BIOS state: %Rrc.\n", rc));
4826 return PDMDEV_SET_ERROR(pDevIns, rc,
4827 N_("LsiLogic: Failed to restore BIOS state\n"));
4828 }
4829
4830 uint32_t u32;
4831 rc = SSMR3GetU32(pSSM, &u32);
4832 if (RT_FAILURE(rc))
4833 return rc;
4834 AssertMsgReturn(u32 == ~0U, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
4835
4836 return VINF_SUCCESS;
4837}
4838
4839
4840/*
4841 * The device level IBASE and LED interfaces.
4842 */
4843
4844/**
4845 * @interface_method_impl{PDMILEDPORTS,pfnQueryInterface, For a SCSI device.}
4846 *
4847 * @remarks Called by the scsi driver, proxying the main calls.
4848 */
4849static DECLCALLBACK(int) lsilogicR3DeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4850{
4851 PLSILOGICDEVICE pDevice = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, ILed);
4852 if (iLUN == 0)
4853 {
4854 *ppLed = &pDevice->Led;
4855 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
4856 return VINF_SUCCESS;
4857 }
4858 return VERR_PDM_LUN_NOT_FOUND;
4859}
4860
4861
4862/**
4863 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4864 */
4865static DECLCALLBACK(void *) lsilogicR3DeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4866{
4867 PLSILOGICDEVICE pDevice = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, IBase);
4868
4869 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevice->IBase);
4870 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSIPORT, &pDevice->ISCSIPort);
4871 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pDevice->ILed);
4872 return NULL;
4873}
4874
4875
4876/*
4877 * The controller level IBASE and LED interfaces.
4878 */
4879
4880/**
4881 * Gets the pointer to the status LED of a unit.
4882 *
4883 * @returns VBox status code.
4884 * @param pInterface Pointer to the interface structure containing the called function pointer.
4885 * @param iLUN The unit which status LED we desire.
4886 * @param ppLed Where to store the LED pointer.
4887 */
4888static DECLCALLBACK(int) lsilogicR3StatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4889{
4890 PLSILOGICSCSI pThis = RT_FROM_MEMBER(pInterface, LSILOGICSCSI, ILeds);
4891 if (iLUN < pThis->cDeviceStates)
4892 {
4893 *ppLed = &pThis->paDeviceStates[iLUN].Led;
4894 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
4895 return VINF_SUCCESS;
4896 }
4897 return VERR_PDM_LUN_NOT_FOUND;
4898}
4899
4900/**
4901 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4902 */
4903static DECLCALLBACK(void *) lsilogicR3StatusQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4904{
4905 PLSILOGICSCSI pThis = RT_FROM_MEMBER(pInterface, LSILOGICSCSI, IBase);
4906 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
4907 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
4908 return NULL;
4909}
4910
4911
4912/*
4913 * The PDM device interface and some helpers.
4914 */
4915
4916/**
4917 * Checks if all asynchronous I/O is finished.
4918 *
4919 * Used by lsilogicR3Reset, lsilogicR3Suspend and lsilogicR3PowerOff.
4920 *
4921 * @returns true if quiesced, false if busy.
4922 * @param pDevIns The device instance.
4923 */
4924static bool lsilogicR3AllAsyncIOIsFinished(PPDMDEVINS pDevIns)
4925{
4926 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4927
4928 for (uint32_t i = 0; i < pThis->cDeviceStates; i++)
4929 {
4930 PLSILOGICDEVICE pThisDevice = &pThis->paDeviceStates[i];
4931 if (pThisDevice->pDrvBase)
4932 {
4933 if (pThisDevice->cOutstandingRequests != 0)
4934 return false;
4935 }
4936 }
4937
4938 return true;
4939}
4940
4941/**
4942 * @callback_method_impl{FNPDMDEVASYNCNOTIFY,
4943 * Callback employed by lsilogicR3Suspend and lsilogicR3PowerOff.}
4944 */
4945static DECLCALLBACK(bool) lsilogicR3IsAsyncSuspendOrPowerOffDone(PPDMDEVINS pDevIns)
4946{
4947 if (!lsilogicR3AllAsyncIOIsFinished(pDevIns))
4948 return false;
4949
4950 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4951 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
4952 return true;
4953}
4954
4955/**
4956 * Common worker for ahciR3Suspend and ahciR3PowerOff.
4957 */
4958static void lsilogicR3SuspendOrPowerOff(PPDMDEVINS pDevIns)
4959{
4960 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
4961
4962 ASMAtomicWriteBool(&pThis->fSignalIdle, true);
4963 if (!lsilogicR3AllAsyncIOIsFinished(pDevIns))
4964 PDMDevHlpSetAsyncNotification(pDevIns, lsilogicR3IsAsyncSuspendOrPowerOffDone);
4965 else
4966 {
4967 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
4968
4969 AssertMsg(!pThis->fNotificationSent, ("The PDM Queue should be empty at this point\n"));
4970
4971 if (pThis->fRedo)
4972 {
4973 /*
4974 * We have tasks which we need to redo. Put the message frame addresses
4975 * into the request queue (we save the requests).
4976 * Guest execution is suspended at this point so there is no race between us and
4977 * lsilogicRegisterWrite.
4978 */
4979 PLSILOGICREQ pLsiReq = pThis->pTasksRedoHead;
4980
4981 pThis->pTasksRedoHead = NULL;
4982
4983 while (pLsiReq)
4984 {
4985 PLSILOGICREQ pFree;
4986
4987 if (!pLsiReq->fBIOS)
4988 {
4989 /* Write only the lower 32bit part of the address. */
4990 ASMAtomicWriteU32(&pThis->CTX_SUFF(pRequestQueueBase)[pThis->uRequestQueueNextEntryFreeWrite],
4991 pLsiReq->GCPhysMessageFrameAddr & UINT32_C(0xffffffff));
4992
4993 pThis->uRequestQueueNextEntryFreeWrite++;
4994 pThis->uRequestQueueNextEntryFreeWrite %= pThis->cRequestQueueEntries;
4995
4996 pThis->fNotificationSent = true;
4997 }
4998 else
4999 {
5000 AssertMsg(!pLsiReq->pRedoNext, ("Only one BIOS task can be active!\n"));
5001 vboxscsiSetRequestRedo(&pThis->VBoxSCSI, &pLsiReq->PDMScsiRequest);
5002 }
5003
5004 pFree = pLsiReq;
5005 pLsiReq = pLsiReq->pRedoNext;
5006
5007 RTMemCacheFree(pThis->hTaskCache, pFree);
5008 }
5009 pThis->fRedo = false;
5010 }
5011 }
5012}
5013
5014/**
5015 * @interface_method_impl{PDMDEVREG,pfnSuspend}
5016 */
5017static DECLCALLBACK(void) lsilogicR3Suspend(PPDMDEVINS pDevIns)
5018{
5019 Log(("lsilogicR3Suspend\n"));
5020 lsilogicR3SuspendOrPowerOff(pDevIns);
5021}
5022
5023/**
5024 * @interface_method_impl{PDMDEVREG,pfnResume}
5025 */
5026static DECLCALLBACK(void) lsilogicR3Resume(PPDMDEVINS pDevIns)
5027{
5028 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
5029
5030 Log(("lsilogicR3Resume\n"));
5031
5032 lsilogicR3Kick(pThis);
5033}
5034
5035/**
5036 * @interface_method_impl{PDMDEVREG,pfnDetach}
5037 *
5038 * One harddisk at one port has been unplugged.
5039 * The VM is suspended at this point.
5040 */
5041static DECLCALLBACK(void) lsilogicR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
5042{
5043 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
5044 PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[iLUN];
5045
5046 if (iLUN >= pThis->cDeviceStates)
5047 return;
5048
5049 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
5050 ("LsiLogic: Device does not support hotplugging\n"));
5051
5052 Log(("%s:\n", __FUNCTION__));
5053
5054 /*
5055 * Zero some important members.
5056 */
5057 pDevice->pDrvBase = NULL;
5058 pDevice->pDrvSCSIConnector = NULL;
5059}
5060
5061/**
5062 * @interface_method_impl{PDMDEVREG,pfnAttach}
5063 */
5064static DECLCALLBACK(int) lsilogicR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
5065{
5066 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
5067 PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[iLUN];
5068 int rc;
5069
5070 if (iLUN >= pThis->cDeviceStates)
5071 return VERR_PDM_LUN_NOT_FOUND;
5072
5073 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
5074 ("LsiLogic: Device does not support hotplugging\n"),
5075 VERR_INVALID_PARAMETER);
5076
5077 /* the usual paranoia */
5078 AssertRelease(!pDevice->pDrvBase);
5079 AssertRelease(!pDevice->pDrvSCSIConnector);
5080 Assert(pDevice->iLUN == iLUN);
5081
5082 /*
5083 * Try attach the block device and get the interfaces,
5084 * required as well as optional.
5085 */
5086 rc = PDMDevHlpDriverAttach(pDevIns, pDevice->iLUN, &pDevice->IBase, &pDevice->pDrvBase, NULL);
5087 if (RT_SUCCESS(rc))
5088 {
5089 /* Get SCSI connector interface. */
5090 pDevice->pDrvSCSIConnector = PDMIBASE_QUERY_INTERFACE(pDevice->pDrvBase, PDMISCSICONNECTOR);
5091 AssertMsgReturn(pDevice->pDrvSCSIConnector, ("Missing SCSI interface below\n"), VERR_PDM_MISSING_INTERFACE);
5092 }
5093 else
5094 AssertMsgFailed(("Failed to attach LUN#%d. rc=%Rrc\n", pDevice->iLUN, rc));
5095
5096 if (RT_FAILURE(rc))
5097 {
5098 pDevice->pDrvBase = NULL;
5099 pDevice->pDrvSCSIConnector = NULL;
5100 }
5101 return rc;
5102}
5103
5104/**
5105 * Common reset worker.
5106 *
5107 * @param pDevIns The device instance data.
5108 */
5109static void lsilogicR3ResetCommon(PPDMDEVINS pDevIns)
5110{
5111 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
5112 int rc;
5113
5114 rc = lsilogicR3HardReset(pThis);
5115 AssertRC(rc);
5116
5117 vboxscsiInitialize(&pThis->VBoxSCSI);
5118}
5119
5120/**
5121 * @callback_method_impl{FNPDMDEVASYNCNOTIFY,
5122 * Callback employed by lsilogicR3Reset.}
5123 */
5124static DECLCALLBACK(bool) lsilogicR3IsAsyncResetDone(PPDMDEVINS pDevIns)
5125{
5126 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
5127
5128 if (!lsilogicR3AllAsyncIOIsFinished(pDevIns))
5129 return false;
5130 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
5131
5132 lsilogicR3ResetCommon(pDevIns);
5133 return true;
5134}
5135
5136/**
5137 * @interface_method_impl{PDMDEVREG,pfnReset}
5138 */
5139static DECLCALLBACK(void) lsilogicR3Reset(PPDMDEVINS pDevIns)
5140{
5141 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
5142
5143 ASMAtomicWriteBool(&pThis->fSignalIdle, true);
5144 if (!lsilogicR3AllAsyncIOIsFinished(pDevIns))
5145 PDMDevHlpSetAsyncNotification(pDevIns, lsilogicR3IsAsyncResetDone);
5146 else
5147 {
5148 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
5149 lsilogicR3ResetCommon(pDevIns);
5150 }
5151}
5152
5153/**
5154 * @interface_method_impl{PDMDEVREG,pfnRelocate}
5155 */
5156static DECLCALLBACK(void) lsilogicR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
5157{
5158 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
5159
5160 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5161 pThis->pNotificationQueueRC = PDMQueueRCPtr(pThis->pNotificationQueueR3);
5162
5163 /* Relocate queues. */
5164 pThis->pReplyFreeQueueBaseRC += offDelta;
5165 pThis->pReplyPostQueueBaseRC += offDelta;
5166 pThis->pRequestQueueBaseRC += offDelta;
5167}
5168
5169/**
5170 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
5171 */
5172static DECLCALLBACK(void) lsilogicR3PowerOff(PPDMDEVINS pDevIns)
5173{
5174 Log(("lsilogicR3PowerOff\n"));
5175 lsilogicR3SuspendOrPowerOff(pDevIns);
5176}
5177
5178/**
5179 * @interface_method_impl{PDMDEVREG,pfnDestruct}
5180 */
5181static DECLCALLBACK(int) lsilogicR3Destruct(PPDMDEVINS pDevIns)
5182{
5183 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
5184 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
5185
5186 PDMR3CritSectDelete(&pThis->ReplyFreeQueueCritSect);
5187 PDMR3CritSectDelete(&pThis->ReplyPostQueueCritSect);
5188
5189 RTMemFree(pThis->paDeviceStates);
5190 pThis->paDeviceStates = NULL;
5191
5192 /* Destroy task cache. */
5193 if (pThis->hTaskCache != NIL_RTMEMCACHE)
5194 {
5195 int rc = RTMemCacheDestroy(pThis->hTaskCache); AssertRC(rc);
5196 pThis->hTaskCache = NIL_RTMEMCACHE;
5197 }
5198
5199 if (pThis->hEvtProcess != NIL_SUPSEMEVENT)
5200 {
5201 SUPSemEventClose(pThis->pSupDrvSession, pThis->hEvtProcess);
5202 pThis->hEvtProcess = NIL_SUPSEMEVENT;
5203 }
5204
5205 lsilogicR3ConfigurationPagesFree(pThis);
5206 lsilogicR3MemRegionsFree(pThis);
5207
5208 return VINF_SUCCESS;
5209}
5210
5211/**
5212 * @interface_method_impl{PDMDEVREG,pfnConstruct}
5213 */
5214static DECLCALLBACK(int) lsilogicR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
5215{
5216 PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
5217 int rc = VINF_SUCCESS;
5218 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5219
5220 /*
5221 * Initialize enought of the state to make the destructure not trip up.
5222 */
5223 pThis->hTaskCache = NIL_RTMEMCACHE;
5224 pThis->hEvtProcess = NIL_SUPSEMEVENT;
5225 RTListInit(&pThis->ListMemRegns);
5226
5227 /*
5228 * Validate and read configuration.
5229 */
5230 rc = CFGMR3AreValuesValid(pCfg, "GCEnabled\0"
5231 "R0Enabled\0"
5232 "ReplyQueueDepth\0"
5233 "RequestQueueDepth\0"
5234 "ControllerType\0"
5235 "NumPorts\0"
5236 "Bootable\0");
5237 if (RT_FAILURE(rc))
5238 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
5239 N_("LsiLogic configuration error: unknown option specified"));
5240 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true);
5241 if (RT_FAILURE(rc))
5242 return PDMDEV_SET_ERROR(pDevIns, rc,
5243 N_("LsiLogic configuration error: failed to read GCEnabled as boolean"));
5244 Log(("%s: fGCEnabled=%d\n", __FUNCTION__, pThis->fGCEnabled));
5245
5246 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
5247 if (RT_FAILURE(rc))
5248 return PDMDEV_SET_ERROR(pDevIns, rc,
5249 N_("LsiLogic configuration error: failed to read R0Enabled as boolean"));
5250 Log(("%s: fR0Enabled=%d\n", __FUNCTION__, pThis->fR0Enabled));
5251
5252 rc = CFGMR3QueryU32Def(pCfg, "ReplyQueueDepth",
5253 &pThis->cReplyQueueEntries,
5254 LSILOGICSCSI_REPLY_QUEUE_DEPTH_DEFAULT);
5255 if (RT_FAILURE(rc))
5256 return PDMDEV_SET_ERROR(pDevIns, rc,
5257 N_("LsiLogic configuration error: failed to read ReplyQueue as integer"));
5258 Log(("%s: ReplyQueueDepth=%u\n", __FUNCTION__, pThis->cReplyQueueEntries));
5259
5260 rc = CFGMR3QueryU32Def(pCfg, "RequestQueueDepth",
5261 &pThis->cRequestQueueEntries,
5262 LSILOGICSCSI_REQUEST_QUEUE_DEPTH_DEFAULT);
5263 if (RT_FAILURE(rc))
5264 return PDMDEV_SET_ERROR(pDevIns, rc,
5265 N_("LsiLogic configuration error: failed to read RequestQueue as integer"));
5266 Log(("%s: RequestQueueDepth=%u\n", __FUNCTION__, pThis->cRequestQueueEntries));
5267
5268 char *pszCtrlType;
5269 rc = CFGMR3QueryStringAllocDef(pCfg, "ControllerType",
5270 &pszCtrlType, LSILOGICSCSI_PCI_SPI_CTRLNAME);
5271 if (RT_FAILURE(rc))
5272 return PDMDEV_SET_ERROR(pDevIns, rc,
5273 N_("LsiLogic configuration error: failed to read ControllerType as string"));
5274 Log(("%s: ControllerType=%s\n", __FUNCTION__, pszCtrlType));
5275
5276 rc = lsilogicR3GetCtrlTypeFromString(pThis, pszCtrlType);
5277 MMR3HeapFree(pszCtrlType);
5278
5279 char szDevTag[20];
5280 RTStrPrintf(szDevTag, sizeof(szDevTag), "LSILOGIC%s-%u",
5281 pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI ? "SPI" : "SAS",
5282 iInstance);
5283
5284
5285 if (RT_FAILURE(rc))
5286 return PDMDEV_SET_ERROR(pDevIns, rc,
5287 N_("LsiLogic configuration error: failed to determine controller type from string"));
5288
5289 rc = CFGMR3QueryU8(pCfg, "NumPorts",
5290 &pThis->cPorts);
5291 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
5292 {
5293 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
5294 pThis->cPorts = LSILOGICSCSI_PCI_SPI_PORTS_MAX;
5295 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
5296 pThis->cPorts = LSILOGICSCSI_PCI_SAS_PORTS_DEFAULT;
5297 else
5298 AssertMsgFailed(("Invalid controller type: %d\n", pThis->enmCtrlType));
5299 }
5300 else if (RT_FAILURE(rc))
5301 return PDMDEV_SET_ERROR(pDevIns, rc,
5302 N_("LsiLogic configuration error: failed to read NumPorts as integer"));
5303
5304 bool fBootable;
5305 rc = CFGMR3QueryBoolDef(pCfg, "Bootable", &fBootable, true);
5306 if (RT_FAILURE(rc))
5307 return PDMDEV_SET_ERROR(pDevIns, rc,
5308 N_("LsiLogic configuration error: failed to read Bootable as boolean"));
5309 Log(("%s: Bootable=%RTbool\n", __FUNCTION__, fBootable));
5310
5311 /* Init static parts. */
5312 PCIDevSetVendorId(&pThis->PciDev, LSILOGICSCSI_PCI_VENDOR_ID); /* LsiLogic */
5313
5314 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
5315 {
5316 PCIDevSetDeviceId (&pThis->PciDev, LSILOGICSCSI_PCI_SPI_DEVICE_ID); /* LSI53C1030 */
5317 PCIDevSetSubSystemVendorId(&pThis->PciDev, LSILOGICSCSI_PCI_SPI_SUBSYSTEM_VENDOR_ID);
5318 PCIDevSetSubSystemId (&pThis->PciDev, LSILOGICSCSI_PCI_SPI_SUBSYSTEM_ID);
5319 }
5320 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
5321 {
5322 PCIDevSetDeviceId (&pThis->PciDev, LSILOGICSCSI_PCI_SAS_DEVICE_ID); /* SAS1068 */
5323 PCIDevSetSubSystemVendorId(&pThis->PciDev, LSILOGICSCSI_PCI_SAS_SUBSYSTEM_VENDOR_ID);
5324 PCIDevSetSubSystemId (&pThis->PciDev, LSILOGICSCSI_PCI_SAS_SUBSYSTEM_ID);
5325 }
5326 else
5327 AssertMsgFailed(("Invalid controller type: %d\n", pThis->enmCtrlType));
5328
5329 PCIDevSetClassProg (&pThis->PciDev, 0x00); /* SCSI */
5330 PCIDevSetClassSub (&pThis->PciDev, 0x00); /* SCSI */
5331 PCIDevSetClassBase (&pThis->PciDev, 0x01); /* Mass storage */
5332 PCIDevSetInterruptPin(&pThis->PciDev, 0x01); /* Interrupt pin A */
5333
5334# ifdef VBOX_WITH_MSI_DEVICES
5335 PCIDevSetStatus(&pThis->PciDev, VBOX_PCI_STATUS_CAP_LIST);
5336 PCIDevSetCapabilityList(&pThis->PciDev, 0x80);
5337# endif
5338
5339 pThis->pDevInsR3 = pDevIns;
5340 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
5341 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5342 pThis->pSupDrvSession = PDMDevHlpGetSupDrvSession(pDevIns);
5343 pThis->IBase.pfnQueryInterface = lsilogicR3StatusQueryInterface;
5344 pThis->ILeds.pfnQueryStatusLed = lsilogicR3StatusQueryStatusLed;
5345
5346 /*
5347 * Create critical sections protecting the reply post and free queues.
5348 * Note! We do our own syncronization, so NOP the default crit sect for the device.
5349 */
5350 rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
5351 AssertRCReturn(rc, rc);
5352
5353 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->ReplyFreeQueueCritSect, RT_SRC_POS, "%sRFQ", szDevTag);
5354 if (RT_FAILURE(rc))
5355 return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: cannot create critical section for reply free queue"));
5356
5357 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->ReplyPostQueueCritSect, RT_SRC_POS, "%sRPQ", szDevTag);
5358 if (RT_FAILURE(rc))
5359 return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: cannot create critical section for reply post queue"));
5360
5361 /*
5362 * Register the PCI device, it's I/O regions.
5363 */
5364 rc = PDMDevHlpPCIRegister(pDevIns, &pThis->PciDev);
5365 if (RT_FAILURE(rc))
5366 return rc;
5367
5368# ifdef VBOX_WITH_MSI_DEVICES
5369 PDMMSIREG MsiReg;
5370 RT_ZERO(MsiReg);
5371 /* use this code for MSI-X support */
5372# if 0
5373 MsiReg.cMsixVectors = 1;
5374 MsiReg.iMsixCapOffset = 0x80;
5375 MsiReg.iMsixNextOffset = 0x00;
5376 MsiReg.iMsixBar = 3;
5377# else
5378 MsiReg.cMsiVectors = 1;
5379 MsiReg.iMsiCapOffset = 0x80;
5380 MsiReg.iMsiNextOffset = 0x00;
5381# endif
5382 rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
5383 if (RT_FAILURE (rc))
5384 {
5385 /* That's OK, we can work without MSI */
5386 PCIDevSetCapabilityList(&pThis->PciDev, 0x0);
5387 }
5388# endif
5389
5390 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, LSILOGIC_PCI_SPACE_IO_SIZE, PCI_ADDRESS_SPACE_IO, lsilogicR3Map);
5391 if (RT_FAILURE(rc))
5392 return rc;
5393
5394 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, LSILOGIC_PCI_SPACE_MEM_SIZE, PCI_ADDRESS_SPACE_MEM, lsilogicR3Map);
5395 if (RT_FAILURE(rc))
5396 return rc;
5397
5398 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 2, LSILOGIC_PCI_SPACE_MEM_SIZE, PCI_ADDRESS_SPACE_MEM, lsilogicR3Map);
5399 if (RT_FAILURE(rc))
5400 return rc;
5401
5402 /* Initialize task queue. (Need two items to handle SMP guest concurrency.) */
5403 char szTaggedText[64];
5404 RTStrPrintf(szTaggedText, sizeof(szTaggedText), "%s-Task", szDevTag);
5405 rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 2, 0,
5406 lsilogicR3NotifyQueueConsumer, true,
5407 szTaggedText,
5408 &pThis->pNotificationQueueR3);
5409 if (RT_FAILURE(rc))
5410 return rc;
5411 pThis->pNotificationQueueR0 = PDMQueueR0Ptr(pThis->pNotificationQueueR3);
5412 pThis->pNotificationQueueRC = PDMQueueRCPtr(pThis->pNotificationQueueR3);
5413
5414 /*
5415 * We need one entry free in the queue.
5416 */
5417 pThis->cReplyQueueEntries++;
5418 pThis->cRequestQueueEntries++;
5419
5420 /*
5421 * Allocate memory for the queues.
5422 */
5423 rc = lsilogicR3QueuesAlloc(pThis);
5424 if (RT_FAILURE(rc))
5425 return rc;
5426
5427 /*
5428 * Allocate task cache.
5429 */
5430 rc = RTMemCacheCreate(&pThis->hTaskCache, sizeof(LSILOGICREQ), 0, UINT32_MAX,
5431 NULL, NULL, NULL, 0);
5432 if (RT_FAILURE(rc))
5433 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Cannot create task cache"));
5434
5435 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
5436 pThis->cDeviceStates = pThis->cPorts * LSILOGICSCSI_PCI_SPI_DEVICES_PER_BUS_MAX;
5437 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
5438 pThis->cDeviceStates = pThis->cPorts * LSILOGICSCSI_PCI_SAS_DEVICES_PER_PORT_MAX;
5439 else
5440 AssertMsgFailed(("Invalid controller type: %d\n", pThis->enmCtrlType));
5441
5442 /*
5443 * Create event semaphore and worker thread.
5444 */
5445 rc = PDMDevHlpThreadCreate(pDevIns, &pThis->pThreadWrk, pThis, lsilogicR3Worker,
5446 lsilogicR3WorkerWakeUp, 0, RTTHREADTYPE_IO, szDevTag);
5447 if (RT_FAILURE(rc))
5448 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
5449 N_("LsiLogic: Failed to create worker thread %s"), szDevTag);
5450
5451 rc = SUPSemEventCreate(pThis->pSupDrvSession, &pThis->hEvtProcess);
5452 if (RT_FAILURE(rc))
5453 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
5454 N_("LsiLogic: Failed to create SUP event semaphore"));
5455
5456 /*
5457 * Allocate device states.
5458 */
5459 pThis->paDeviceStates = (PLSILOGICDEVICE)RTMemAllocZ(sizeof(LSILOGICDEVICE) * pThis->cDeviceStates);
5460 if (!pThis->paDeviceStates)
5461 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to allocate memory for device states"));
5462
5463 for (unsigned i = 0; i < pThis->cDeviceStates; i++)
5464 {
5465 char szName[24];
5466 PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[i];
5467
5468 /* Initialize static parts of the device. */
5469 pDevice->iLUN = i;
5470 pDevice->pLsiLogicR3 = pThis;
5471 pDevice->Led.u32Magic = PDMLED_MAGIC;
5472 pDevice->IBase.pfnQueryInterface = lsilogicR3DeviceQueryInterface;
5473 pDevice->ISCSIPort.pfnSCSIRequestCompleted = lsilogicR3DeviceSCSIRequestCompleted;
5474 pDevice->ISCSIPort.pfnQueryDeviceLocation = lsilogicR3QueryDeviceLocation;
5475 pDevice->ILed.pfnQueryStatusLed = lsilogicR3DeviceQueryStatusLed;
5476
5477 RTStrPrintf(szName, sizeof(szName), "Device%u", i);
5478
5479 /* Attach SCSI driver. */
5480 rc = PDMDevHlpDriverAttach(pDevIns, pDevice->iLUN, &pDevice->IBase, &pDevice->pDrvBase, szName);
5481 if (RT_SUCCESS(rc))
5482 {
5483 /* Get SCSI connector interface. */
5484 pDevice->pDrvSCSIConnector = PDMIBASE_QUERY_INTERFACE(pDevice->pDrvBase, PDMISCSICONNECTOR);
5485 AssertMsgReturn(pDevice->pDrvSCSIConnector, ("Missing SCSI interface below\n"), VERR_PDM_MISSING_INTERFACE);
5486 }
5487 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
5488 {
5489 pDevice->pDrvBase = NULL;
5490 rc = VINF_SUCCESS;
5491 Log(("LsiLogic: no driver attached to device %s\n", szName));
5492 }
5493 else
5494 {
5495 AssertLogRelMsgFailed(("LsiLogic: Failed to attach %s\n", szName));
5496 return rc;
5497 }
5498 }
5499
5500 /*
5501 * Attach status driver (optional).
5502 */
5503 PPDMIBASE pBase;
5504 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pBase, "Status Port");
5505 if (RT_SUCCESS(rc))
5506 pThis->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
5507 else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
5508 {
5509 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
5510 return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic cannot attach to status driver"));
5511 }
5512
5513 /* Initialize the SCSI emulation for the BIOS. */
5514 rc = vboxscsiInitialize(&pThis->VBoxSCSI);
5515 AssertRC(rc);
5516
5517 /*
5518 * Register I/O port space in ISA region for BIOS access
5519 * if the controller is marked as bootable.
5520 */
5521 if (fBootable)
5522 {
5523 if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
5524 rc = PDMDevHlpIOPortRegister(pDevIns, LSILOGIC_BIOS_IO_PORT, 4, NULL,
5525 lsilogicR3IsaIOPortWrite, lsilogicR3IsaIOPortRead,
5526 lsilogicR3IsaIOPortWriteStr, lsilogicR3IsaIOPortReadStr,
5527 "LsiLogic BIOS");
5528 else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
5529 rc = PDMDevHlpIOPortRegister(pDevIns, LSILOGIC_SAS_BIOS_IO_PORT, 4, NULL,
5530 lsilogicR3IsaIOPortWrite, lsilogicR3IsaIOPortRead,
5531 lsilogicR3IsaIOPortWriteStr, lsilogicR3IsaIOPortReadStr,
5532 "LsiLogic SAS BIOS");
5533 else
5534 AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
5535
5536 if (RT_FAILURE(rc))
5537 return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic cannot register legacy I/O handlers"));
5538 }
5539
5540 /* Register save state handlers. */
5541 rc = PDMDevHlpSSMRegisterEx(pDevIns, LSILOGIC_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
5542 NULL, lsilogicR3LiveExec, NULL,
5543 NULL, lsilogicR3SaveExec, NULL,
5544 NULL, lsilogicR3LoadExec, lsilogicR3LoadDone);
5545 if (RT_FAILURE(rc))
5546 return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic cannot register save state handlers"));
5547
5548 pThis->enmWhoInit = LSILOGICWHOINIT_SYSTEM_BIOS;
5549
5550 /*
5551 * Register the info item.
5552 */
5553 char szTmp[128];
5554 RTStrPrintf(szTmp, sizeof(szTmp), "%s%u", pDevIns->pReg->szName, pDevIns->iInstance);
5555 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp,
5556 pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
5557 ? "LsiLogic SPI info."
5558 : "LsiLogic SAS info.", lsilogicR3Info);
5559
5560 /* Perform hard reset. */
5561 rc = lsilogicR3HardReset(pThis);
5562 AssertRC(rc);
5563
5564 return rc;
5565}
5566
5567/**
5568 * The device registration structure - SPI SCSI controller.
5569 */
5570const PDMDEVREG g_DeviceLsiLogicSCSI =
5571{
5572 /* u32Version */
5573 PDM_DEVREG_VERSION,
5574 /* szName */
5575 "lsilogicscsi",
5576 /* szRCMod */
5577 "VBoxDDRC.rc",
5578 /* szR0Mod */
5579 "VBoxDDR0.r0",
5580 /* pszDescription */
5581 "LSI Logic 53c1030 SCSI controller.\n",
5582 /* fFlags */
5583 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0 |
5584 PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION,
5585 /* fClass */
5586 PDM_DEVREG_CLASS_STORAGE,
5587 /* cMaxInstances */
5588 ~0U,
5589 /* cbInstance */
5590 sizeof(LSILOGICSCSI),
5591 /* pfnConstruct */
5592 lsilogicR3Construct,
5593 /* pfnDestruct */
5594 lsilogicR3Destruct,
5595 /* pfnRelocate */
5596 lsilogicR3Relocate,
5597 /* pfnMemSetup */
5598 NULL,
5599 /* pfnPowerOn */
5600 NULL,
5601 /* pfnReset */
5602 lsilogicR3Reset,
5603 /* pfnSuspend */
5604 lsilogicR3Suspend,
5605 /* pfnResume */
5606 lsilogicR3Resume,
5607 /* pfnAttach */
5608 lsilogicR3Attach,
5609 /* pfnDetach */
5610 lsilogicR3Detach,
5611 /* pfnQueryInterface. */
5612 NULL,
5613 /* pfnInitComplete */
5614 NULL,
5615 /* pfnPowerOff */
5616 lsilogicR3PowerOff,
5617 /* pfnSoftReset */
5618 NULL,
5619 /* u32VersionEnd */
5620 PDM_DEVREG_VERSION
5621};
5622
5623/**
5624 * The device registration structure - SAS controller.
5625 */
5626const PDMDEVREG g_DeviceLsiLogicSAS =
5627{
5628 /* u32Version */
5629 PDM_DEVREG_VERSION,
5630 /* szName */
5631 "lsilogicsas",
5632 /* szRCMod */
5633 "VBoxDDRC.rc",
5634 /* szR0Mod */
5635 "VBoxDDR0.r0",
5636 /* pszDescription */
5637 "LSI Logic SAS1068 controller.\n",
5638 /* fFlags */
5639 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0 |
5640 PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION |
5641 PDM_DEVREG_FLAGS_FIRST_RESET_NOTIFICATION,
5642 /* fClass */
5643 PDM_DEVREG_CLASS_STORAGE,
5644 /* cMaxInstances */
5645 ~0U,
5646 /* cbInstance */
5647 sizeof(LSILOGICSCSI),
5648 /* pfnConstruct */
5649 lsilogicR3Construct,
5650 /* pfnDestruct */
5651 lsilogicR3Destruct,
5652 /* pfnRelocate */
5653 lsilogicR3Relocate,
5654 /* pfnMemSetup */
5655 NULL,
5656 /* pfnPowerOn */
5657 NULL,
5658 /* pfnReset */
5659 lsilogicR3Reset,
5660 /* pfnSuspend */
5661 lsilogicR3Suspend,
5662 /* pfnResume */
5663 lsilogicR3Resume,
5664 /* pfnAttach */
5665 lsilogicR3Attach,
5666 /* pfnDetach */
5667 lsilogicR3Detach,
5668 /* pfnQueryInterface. */
5669 NULL,
5670 /* pfnInitComplete */
5671 NULL,
5672 /* pfnPowerOff */
5673 lsilogicR3PowerOff,
5674 /* pfnSoftReset */
5675 NULL,
5676 /* u32VersionEnd */
5677 PDM_DEVREG_VERSION
5678};
5679
5680#endif /* IN_RING3 */
5681#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