VirtualBox

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

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

Devices/LsiLogic: Limit the amount of memory the guest can allocate for the diagnostic memory to 1MB, prevents malicous guests from DOSing the host my llocating loads of memory

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