VirtualBox

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

Last change on this file since 64224 was 64224, checked in by vboxsync, 8 years ago

LsiLogic,BusLogic,VBoxSCSI: Convert to PDMIMEDIAEX and friends

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