VirtualBox

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

最後變更 在這個檔案從63289是 63218,由 vboxsync 提交於 9 年 前

Devices: warnings (gcc)

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

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