VirtualBox

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

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

pdmifs.h: Move the storage related interfaces (PDMIMEDIA, PDMIMOUNT, PDMISCSICONNECTOR, etc.) into a separate header to reduce the overall size of the header a bit

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