VirtualBox

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

Last change on this file since 90778 was 90445, checked in by vboxsync, 3 years ago

Dev*: Check PDMDevHlpCritSectEnter return status better. bugref:6695

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

© 2023 Oracle
ContactPrivacy policyTerms of Use