VirtualBox

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

Last change on this file since 47631 was 46265, checked in by vboxsync, 12 years ago

Devices/LsiLogic: Move most of the work from EMT to a dedicated worker thread and use SUP event semaphore to notify it about new work

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

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