VirtualBox

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

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

Devices: unused parameter warnings.

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

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