VirtualBox

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

Last change on this file since 89193 was 89193, checked in by vboxsync, 4 years ago

Devices/Storage/DevLsiLogicSCSI,Devices/testcase: Get rid of the VBoxSCSI interface now that we have proper drivers in the BIOS, bugref:4841

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

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