VirtualBox

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

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

BusLogic,LsiLogic: Implement medium ejected callbacks properly so Main gets notified when the guest ejected a medium

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

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