VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DevBusLogic.cpp@ 43517

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

Add new flag to notify devices during a reset first to make sure there is no I/O pending (see #6434)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 131.1 KB
Line 
1/* $Id: DevBusLogic.cpp 43472 2012-09-29 19:54:30Z vboxsync $ */
2/** @file
3 * VBox storage devices: BusLogic SCSI host adapter BT-958.
4 */
5
6/*
7 * Copyright (C) 2006-2012 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/* Implemented looking at the driver source in the linux kernel (drivers/scsi/BusLogic.[ch]).
19 * See also: http://www.drdobbs.com/184410111
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25//#define DEBUG
26#define LOG_GROUP LOG_GROUP_DEV_BUSLOGIC
27#include <VBox/vmm/pdmdev.h>
28#include <VBox/vmm/pdmifs.h>
29#include <VBox/vmm/pdmcritsect.h>
30#include <VBox/scsi.h>
31#include <iprt/asm.h>
32#include <iprt/assert.h>
33#include <iprt/string.h>
34#include <iprt/log.h>
35#ifdef IN_RING3
36# include <iprt/alloc.h>
37# include <iprt/memcache.h>
38# include <iprt/param.h>
39# include <iprt/uuid.h>
40#endif
41
42#include "VBoxSCSI.h"
43#include "VBoxDD.h"
44
45/* Maximum number of attached devices the adapter can handle. */
46#define BUSLOGIC_MAX_DEVICES 16
47
48/* Maximum number of scatter gather elements this device can handle. */
49#define BUSLOGIC_MAX_SCATTER_GATHER_LIST_SIZE 128
50
51/* Size of the command buffer. */
52#define BUSLOGIC_COMMAND_SIZE_MAX 5
53
54/* Size of the reply buffer. */
55#define BUSLOGIC_REPLY_SIZE_MAX 64
56
57/*
58 * Custom fixed I/O ports for BIOS controller access. Note that these should
59 * not be in the ISA range (below 400h) to avoid conflicts with ISA device
60 * probing. Addresses in the 300h-340h range should be especially avoided.
61 */
62
63#define BUSLOGIC_BIOS_IO_PORT 0x330
64
65/** State saved version. */
66#define BUSLOGIC_SAVED_STATE_MINOR_VERSION 2
67
68/** Saved state version before the suspend on error feature was implemented. */
69#define BUSLOGIC_SAVED_STATE_MINOR_PRE_ERROR_HANDLING 1
70
71/** The duration of software-initiated reset. Not documented, set to 500 us. */
72#define BUSLOGIC_RESET_DURATION_NS (500*1000)
73
74/**
75 * State of a device attached to the buslogic host adapter.
76 *
77 * @implements PDMIBASE
78 * @implements PDMISCSIPORT
79 * @implements PDMILEDPORTS
80 */
81typedef struct BUSLOGICDEVICE
82{
83 /** Pointer to the owning buslogic device instance. - R3 pointer */
84 R3PTRTYPE(struct BUSLOGIC *) pBusLogicR3;
85 /** Pointer to the owning buslogic device instance. - R0 pointer */
86 R0PTRTYPE(struct BUSLOGIC *) pBusLogicR0;
87 /** Pointer to the owning buslogic device instance. - RC pointer */
88 RCPTRTYPE(struct BUSLOGIC *) pBusLogicRC;
89
90 /** Flag whether device is present. */
91 bool fPresent;
92 /** LUN of the device. */
93 RTUINT iLUN;
94
95#if HC_ARCH_BITS == 64
96 uint32_t Alignment0;
97#endif
98
99 /** Our base interface. */
100 PDMIBASE IBase;
101 /** SCSI port interface. */
102 PDMISCSIPORT ISCSIPort;
103 /** Led interface. */
104 PDMILEDPORTS ILed;
105 /** Pointer to the attached driver's base interface. */
106 R3PTRTYPE(PPDMIBASE) pDrvBase;
107 /** Pointer to the underlying SCSI connector interface. */
108 R3PTRTYPE(PPDMISCSICONNECTOR) pDrvSCSIConnector;
109 /** The status LED state for this device. */
110 PDMLED Led;
111
112#if HC_ARCH_BITS == 64
113 uint32_t Alignment1;
114#endif
115
116 /** Number of outstanding tasks on the port. */
117 volatile uint32_t cOutstandingRequests;
118
119} BUSLOGICDEVICE, *PBUSLOGICDEVICE;
120
121/*
122 * Commands the BusLogic adapter supports.
123 */
124enum BUSLOGICCOMMAND
125{
126 BUSLOGICCOMMAND_TEST_CMDC_INTERRUPT = 0x00,
127 BUSLOGICCOMMAND_INITIALIZE_MAILBOX = 0x01,
128 BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND = 0x02,
129 BUSLOGICCOMMAND_EXECUTE_BIOS_COMMAND = 0x03,
130 BUSLOGICCOMMAND_INQUIRE_BOARD_ID = 0x04,
131 BUSLOGICCOMMAND_ENABLE_OUTGOING_MAILBOX_AVAILABLE_INTERRUPT = 0x05,
132 BUSLOGICCOMMAND_SET_SCSI_SELECTION_TIMEOUT = 0x06,
133 BUSLOGICCOMMAND_SET_PREEMPT_TIME_ON_BUS = 0x07,
134 BUSLOGICCOMMAND_SET_TIME_OFF_BUS = 0x08,
135 BUSLOGICCOMMAND_SET_BUS_TRANSFER_RATE = 0x09,
136 BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_0_TO_7 = 0x0a,
137 BUSLOGICCOMMAND_INQUIRE_CONFIGURATION = 0x0b,
138 BUSLOGICCOMMAND_ENABLE_TARGET_MODE = 0x0c,
139 BUSLOGICCOMMAND_INQUIRE_SETUP_INFORMATION = 0x0d,
140 BUSLOGICCOMMAND_WRITE_ADAPTER_LOCAL_RAM = 0x1a,
141 BUSLOGICCOMMAND_READ_ADAPTER_LOCAL_RAM = 0x1b,
142 BUSLOGICCOMMAND_WRITE_BUSMASTER_CHIP_FIFO = 0x1c,
143 BUSLOGICCOMMAND_READ_BUSMASTER_CHIP_FIFO = 0x1d,
144 BUSLOGICCOMMAND_ECHO_COMMAND_DATA = 0x1f,
145 BUSLOGICCOMMAND_HOST_ADAPTER_DIAGNOSTIC = 0x20,
146 BUSLOGICCOMMAND_SET_ADAPTER_OPTIONS = 0x21,
147 BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_8_TO_15 = 0x23,
148 BUSLOGICCOMMAND_INQUIRE_TARGET_DEVICES = 0x24,
149 BUSLOGICCOMMAND_DISABLE_HOST_ADAPTER_INTERRUPT = 0x25,
150 BUSLOGICCOMMAND_EXT_BIOS_INFO = 0x28,
151 BUSLOGICCOMMAND_UNLOCK_MAILBOX = 0x29,
152 BUSLOGICCOMMAND_INITIALIZE_EXTENDED_MAILBOX = 0x81,
153 BUSLOGICCOMMAND_EXECUTE_SCSI_COMMAND = 0x83,
154 BUSLOGICCOMMAND_INQUIRE_FIRMWARE_VERSION_3RD_LETTER = 0x84,
155 BUSLOGICCOMMAND_INQUIRE_FIRMWARE_VERSION_LETTER = 0x85,
156 BUSLOGICCOMMAND_INQUIRE_PCI_HOST_ADAPTER_INFORMATION = 0x86,
157 BUSLOGICCOMMAND_INQUIRE_HOST_ADAPTER_MODEL_NUMBER = 0x8b,
158 BUSLOGICCOMMAND_INQUIRE_SYNCHRONOUS_PERIOD = 0x8c,
159 BUSLOGICCOMMAND_INQUIRE_EXTENDED_SETUP_INFORMATION = 0x8d,
160 BUSLOGICCOMMAND_ENABLE_STRICT_ROUND_ROBIN_MODE = 0x8f,
161 BUSLOGICCOMMAND_STORE_HOST_ADAPTER_LOCAL_RAM = 0x90,
162 BUSLOGICCOMMAND_FETCH_HOST_ADAPTER_LOCAL_RAM = 0x91,
163 BUSLOGICCOMMAND_STORE_LOCAL_DATA_IN_EEPROM = 0x92,
164 BUSLOGICCOMMAND_UPLOAD_AUTO_SCSI_CODE = 0x94,
165 BUSLOGICCOMMAND_MODIFY_IO_ADDRESS = 0x95,
166 BUSLOGICCOMMAND_SET_CCB_FORMAT = 0x96,
167 BUSLOGICCOMMAND_WRITE_INQUIRY_BUFFER = 0x9a,
168 BUSLOGICCOMMAND_READ_INQUIRY_BUFFER = 0x9b,
169 BUSLOGICCOMMAND_FLASH_ROM_UPLOAD_DOWNLOAD = 0xa7,
170 BUSLOGICCOMMAND_READ_SCAM_DATA = 0xa8,
171 BUSLOGICCOMMAND_WRITE_SCAM_DATA = 0xa9
172} BUSLOGICCOMMAND;
173
174#pragma pack(1)
175/**
176 * Auto SCSI structure which is located
177 * in host adapter RAM and contains several
178 * configuration parameters.
179 */
180typedef struct AutoSCSIRam
181{
182 uint8_t aInternalSignature[2];
183 uint8_t cbInformation;
184 uint8_t aHostAdaptertype[6];
185 uint8_t uReserved1;
186 bool fFloppyEnabled: 1;
187 bool fFloppySecondary: 1;
188 bool fLevelSensitiveInterrupt: 1;
189 unsigned char uReserved2: 2;
190 unsigned char uSystemRAMAreForBIOS: 3;
191 unsigned char uDMAChannel: 7;
192 bool fDMAAutoConfiguration: 1;
193 unsigned char uIrqChannel: 7;
194 bool fIrqAutoConfiguration: 1;
195 uint8_t uDMATransferRate;
196 uint8_t uSCSIId;
197 bool fLowByteTerminated: 1;
198 bool fParityCheckingEnabled: 1;
199 bool fHighByteTerminated: 1;
200 bool fNoisyCablingEnvironment: 1;
201 bool fFastSynchronousNeogtiation: 1;
202 bool fBusResetEnabled: 1;
203 bool fReserved3: 1;
204 bool fActiveNegotiationEnabled: 1;
205 uint8_t uBusOnDelay;
206 uint8_t uBusOffDelay;
207 bool fHostAdapterBIOSEnabled: 1;
208 bool fBIOSRedirectionOfInt19: 1;
209 bool fExtendedTranslation: 1;
210 bool fMapRemovableAsFixed: 1;
211 bool fReserved4: 1;
212 bool fBIOSSupportsMoreThan2Drives: 1;
213 bool fBIOSInterruptMode: 1;
214 bool fFlopticalSupport: 1;
215 uint16_t u16DeviceEnabledMask;
216 uint16_t u16WidePermittedMask;
217 uint16_t u16FastPermittedMask;
218 uint16_t u16SynchronousPermittedMask;
219 uint16_t u16DisconnectPermittedMask;
220 uint16_t u16SendStartUnitCommandMask;
221 uint16_t u16IgnoreInBIOSScanMask;
222 unsigned char uPCIInterruptPin: 2;
223 unsigned char uHostAdapterIoPortAddress: 2;
224 bool fStrictRoundRobinMode: 1;
225 bool fVesaBusSpeedGreaterThan33MHz: 1;
226 bool fVesaBurstWrite: 1;
227 bool fVesaBurstRead: 1;
228 uint16_t u16UltraPermittedMask;
229 uint32_t uReserved5;
230 uint8_t uReserved6;
231 uint8_t uAutoSCSIMaximumLUN;
232 bool fReserved7: 1;
233 bool fSCAMDominant: 1;
234 bool fSCAMenabled: 1;
235 bool fSCAMLevel2: 1;
236 unsigned char uReserved8: 4;
237 bool fInt13Extension: 1;
238 bool fReserved9: 1;
239 bool fCDROMBoot: 1;
240 unsigned char uReserved10: 5;
241 unsigned char uBootTargetId: 4;
242 unsigned char uBootChannel: 4;
243 bool fForceBusDeviceScanningOrder: 1;
244 unsigned char uReserved11: 7;
245 uint16_t u16NonTaggedToAlternateLunPermittedMask;
246 uint16_t u16RenegotiateSyncAfterCheckConditionMask;
247 uint8_t aReserved12[10];
248 uint8_t aManufacturingDiagnostic[2];
249 uint16_t u16Checksum;
250} AutoSCSIRam, *PAutoSCSIRam;
251AssertCompileSize(AutoSCSIRam, 64);
252#pragma pack()
253
254/**
255 * The local Ram.
256 */
257typedef union HostAdapterLocalRam
258{
259 /* Byte view. */
260 uint8_t u8View[256];
261 /* Structured view. */
262 struct
263 {
264 /** Offset 0 - 63 is for BIOS. */
265 uint8_t u8Bios[64];
266 /** Auto SCSI structure. */
267 AutoSCSIRam autoSCSIData;
268 } structured;
269} HostAdapterLocalRam, *PHostAdapterLocalRam;
270AssertCompileSize(HostAdapterLocalRam, 256);
271
272/** Pointer to a task state structure. */
273typedef struct BUSLOGICTASKSTATE *PBUSLOGICTASKSTATE;
274
275/**
276 * Main BusLogic device state.
277 *
278 * @extends PCIDEVICE
279 * @implements PDMILEDPORTS
280 */
281typedef struct BUSLOGIC
282{
283 /** The PCI device structure. */
284 PCIDEVICE dev;
285 /** Pointer to the device instance - HC ptr */
286 PPDMDEVINSR3 pDevInsR3;
287 /** Pointer to the device instance - R0 ptr */
288 PPDMDEVINSR0 pDevInsR0;
289 /** Pointer to the device instance - RC ptr. */
290 PPDMDEVINSRC pDevInsRC;
291
292 /** Whether R0 is enabled. */
293 bool fR0Enabled;
294 /** Whether RC is enabled. */
295 bool fGCEnabled;
296
297 /** Base address of the I/O ports. */
298 RTIOPORT IOPortBase;
299 /** Base address of the memory mapping. */
300 RTGCPHYS MMIOBase;
301 /** Status register - Readonly. */
302 volatile uint8_t regStatus;
303 /** Interrupt register - Readonly. */
304 volatile uint8_t regInterrupt;
305 /** Geometry register - Readonly. */
306 volatile uint8_t regGeometry;
307 /** Pending (delayed) interrupt. */
308 uint8_t uPendingIntr;
309
310 /** Local RAM for the fetch hostadapter local RAM request.
311 * I don't know how big the buffer really is but the maximum
312 * seems to be 256 bytes because the offset and count field in the command request
313 * are only one byte big.
314 */
315 HostAdapterLocalRam LocalRam;
316
317 /** Command code the guest issued. */
318 uint8_t uOperationCode;
319 /** Buffer for the command parameters the adapter is currently receiving from the guest.
320 * Size of the largest command which is possible.
321 */
322 uint8_t aCommandBuffer[BUSLOGIC_COMMAND_SIZE_MAX]; /* Size of the biggest request. */
323 /** Current position in the command buffer. */
324 uint8_t iParameter;
325 /** Parameters left until the command is complete. */
326 uint8_t cbCommandParametersLeft;
327
328 /** Whether we are using the RAM or reply buffer. */
329 bool fUseLocalRam;
330 /** Buffer to store reply data from the controller to the guest. */
331 uint8_t aReplyBuffer[BUSLOGIC_REPLY_SIZE_MAX]; /* Size of the biggest reply. */
332 /** Position in the buffer we are reading next. */
333 uint8_t iReply;
334 /** Bytes left until the reply buffer is empty. */
335 uint8_t cbReplyParametersLeft;
336
337 /** Flag whether IRQs are enabled. */
338 bool fIRQEnabled;
339 /** Flag whether the ISA I/O port range is disabled
340 * to prevent the BIOs to access the device. */
341 bool fISAEnabled;
342 /** Flag whether 24-bit mailboxes are in use (default is 32-bit). */
343 bool fMbxIs24Bit; //@todo: save?
344 /** ISA I/O port base (encoded in FW-compatible format). */
345 uint8_t uISABaseCode; //@todo: save?
346
347 /** Default ISA I/O port base in FW-compatible format. */
348 uint8_t uDefaultISABaseCode;
349
350 /** ISA I/O port base (disabled if zero). */
351 uint16_t uISABase; //@todo: recalculate when restoring state
352
353 /** Number of mailboxes the guest set up. */
354 uint32_t cMailbox;
355
356#if HC_ARCH_BITS == 64
357 uint32_t Alignment0;
358#endif
359
360 /** Time when HBA reset was last initiated. */ //@todo: does this need to be saved?
361 uint64_t u64ResetTime;
362 /** Physical base address of the outgoing mailboxes. */
363 RTGCPHYS GCPhysAddrMailboxOutgoingBase;
364 /** Current outgoing mailbox position. */
365 uint32_t uMailboxOutgoingPositionCurrent;
366 /** Number of mailboxes ready. */
367 volatile uint32_t cMailboxesReady;
368 /** Whether a notification to R3 was send. */
369 volatile bool fNotificationSend;
370
371#if HC_ARCH_BITS == 64
372 uint32_t Alignment1;
373#endif
374
375 /** Physical base address of the incoming mailboxes. */
376 RTGCPHYS GCPhysAddrMailboxIncomingBase;
377 /** Current incoming mailbox position. */
378 uint32_t uMailboxIncomingPositionCurrent;
379
380 /** Whether strict round robin is enabled. */
381 bool fStrictRoundRobinMode;
382 /** Whether the extended LUN CCB format is enabled for 32 possible logical units. */
383 bool fExtendedLunCCBFormat;
384
385 /** Queue to send tasks to R3. - HC ptr */
386 R3PTRTYPE(PPDMQUEUE) pNotifierQueueR3;
387 /** Queue to send tasks to R3. - HC ptr */
388 R0PTRTYPE(PPDMQUEUE) pNotifierQueueR0;
389 /** Queue to send tasks to R3. - RC ptr */
390 RCPTRTYPE(PPDMQUEUE) pNotifierQueueRC;
391
392 uint32_t Alignment2;
393
394 /** Critical section protecting access to the interrupt status register. */
395 PDMCRITSECT CritSectIntr;
396
397 /** Cache for task states. */
398 R3PTRTYPE(RTMEMCACHE) hTaskCache;
399
400 /** Device state for BIOS access. */
401 VBOXSCSI VBoxSCSI;
402
403 /** BusLogic device states. */
404 BUSLOGICDEVICE aDeviceStates[BUSLOGIC_MAX_DEVICES];
405
406 /** The base interface.
407 * @todo use PDMDEVINS::IBase */
408 PDMIBASE IBase;
409 /** Status Port - Leds interface. */
410 PDMILEDPORTS ILeds;
411 /** Partner of ILeds. */
412 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
413
414#if HC_ARCH_BITS == 64
415 uint32_t Alignment3;
416#endif
417
418 /** Indicates that PDMDevHlpAsyncNotificationCompleted should be called when
419 * a port is entering the idle state. */
420 bool volatile fSignalIdle;
421 /** Flag whether we have tasks which need to be processed again. */
422 bool volatile fRedo;
423 /** List of tasks which can be redone. */
424 R3PTRTYPE(volatile PBUSLOGICTASKSTATE) pTasksRedoHead;
425
426#ifdef LOG_ENABLED
427# if HC_ARCH_BITS == 64
428 uint32_t Alignment4;
429# endif
430
431 volatile uint32_t cInMailboxesReady;
432#endif
433
434} BUSLOGIC, *PBUSLOGIC;
435
436/** Register offsets in the I/O port space. */
437#define BUSLOGIC_REGISTER_CONTROL 0 /* Writeonly */
438/** Fields for the control register. */
439# define BUSLOGIC_REGISTER_CONTROL_SCSI_BUSRESET RT_BIT(4)
440# define BUSLOGIC_REGISTER_CONTROL_INTERRUPT_RESET RT_BIT(5)
441# define BUSLOGIC_REGISTER_CONTROL_SOFT_RESET RT_BIT(6)
442# define BUSLOGIC_REGISTER_CONTROL_HARD_RESET RT_BIT(7)
443
444#define BUSLOGIC_REGISTER_STATUS 0 /* Readonly */
445/** Fields for the status register. */
446# define BUSLOGIC_REGISTER_STATUS_COMMAND_INVALID RT_BIT(0)
447# define BUSLOGIC_REGISTER_STATUS_DATA_IN_REGISTER_READY RT_BIT(2)
448# define BUSLOGIC_REGISTER_STATUS_COMMAND_PARAMETER_REGISTER_BUSY RT_BIT(3)
449# define BUSLOGIC_REGISTER_STATUS_HOST_ADAPTER_READY RT_BIT(4)
450# define BUSLOGIC_REGISTER_STATUS_INITIALIZATION_REQUIRED RT_BIT(5)
451# define BUSLOGIC_REGISTER_STATUS_DIAGNOSTIC_FAILURE RT_BIT(6)
452# define BUSLOGIC_REGISTER_STATUS_DIAGNOSTIC_ACTIVE RT_BIT(7)
453
454#define BUSLOGIC_REGISTER_COMMAND 1 /* Writeonly */
455#define BUSLOGIC_REGISTER_DATAIN 1 /* Readonly */
456#define BUSLOGIC_REGISTER_INTERRUPT 2 /* Readonly */
457/** Fields for the interrupt register. */
458# define BUSLOGIC_REGISTER_INTERRUPT_INCOMING_MAILBOX_LOADED RT_BIT(0)
459# define BUSLOGIC_REGISTER_INTERRUPT_OUTGOING_MAILBOX_AVAILABLE RT_BIT(1)
460# define BUSLOGIC_REGISTER_INTERRUPT_COMMAND_COMPLETE RT_BIT(2)
461# define BUSLOGIC_REGISTER_INTERRUPT_EXTERNAL_BUS_RESET RT_BIT(3)
462# define BUSLOGIC_REGISTER_INTERRUPT_INTERRUPT_VALID RT_BIT(7)
463
464#define BUSLOGIC_REGISTER_GEOMETRY 3 /* Readonly */
465# define BUSLOGIC_REGISTER_GEOMETRY_EXTENTED_TRANSLATION_ENABLED RT_BIT(7)
466
467/* Structure for the INQUIRE_PCI_HOST_ADAPTER_INFORMATION reply. */
468typedef struct ReplyInquirePCIHostAdapterInformation
469{
470 uint8_t IsaIOPort;
471 uint8_t IRQ;
472 unsigned char LowByteTerminated:1;
473 unsigned char HighByteTerminated:1;
474 unsigned char uReserved:2; /* Reserved. */
475 unsigned char JP1:1; /* Whatever that means. */
476 unsigned char JP2:1; /* Whatever that means. */
477 unsigned char JP3:1; /* Whatever that means. */
478 /** Whether the provided info is valid. */
479 unsigned char InformationIsValid: 1;
480 uint8_t uReserved2; /* Reserved. */
481} ReplyInquirePCIHostAdapterInformation, *PReplyInquirePCIHostAdapterInformation;
482AssertCompileSize(ReplyInquirePCIHostAdapterInformation, 4);
483
484/* Structure for the INQUIRE_CONFIGURATION reply. */
485typedef struct ReplyInquireConfiguration
486{
487 unsigned char uReserved1: 5;
488 bool fDmaChannel5: 1;
489 bool fDmaChannel6: 1;
490 bool fDmaChannel7: 1;
491 bool fIrqChannel9: 1;
492 bool fIrqChannel10: 1;
493 bool fIrqChannel11: 1;
494 bool fIrqChannel12: 1;
495 unsigned char uReserved2: 1;
496 bool fIrqChannel14: 1;
497 bool fIrqChannel15: 1;
498 unsigned char uReserved3: 1;
499 unsigned char uHostAdapterId: 4;
500 unsigned char uReserved4: 4;
501} ReplyInquireConfiguration, *PReplyInquireConfiguration;
502AssertCompileSize(ReplyInquireConfiguration, 3);
503
504/* Structure for the INQUIRE_SETUP_INFORMATION reply. */
505typedef struct ReplyInquireSetupInformationSynchronousValue
506{
507 unsigned char uOffset: 4;
508 unsigned char uTransferPeriod: 3;
509 bool fSynchronous: 1;
510}ReplyInquireSetupInformationSynchronousValue, *PReplyInquireSetupInformationSynchronousValue;
511AssertCompileSize(ReplyInquireSetupInformationSynchronousValue, 1);
512
513typedef struct ReplyInquireSetupInformation
514{
515 bool fSynchronousInitiationEnabled: 1;
516 bool fParityCheckingEnabled: 1;
517 unsigned char uReserved1: 6;
518 uint8_t uBusTransferRate;
519 uint8_t uPreemptTimeOnBus;
520 uint8_t uTimeOffBus;
521 uint8_t cMailbox;
522 uint8_t MailboxAddress[3];
523 ReplyInquireSetupInformationSynchronousValue SynchronousValuesId0To7[8];
524 uint8_t uDisconnectPermittedId0To7;
525 uint8_t uSignature;
526 uint8_t uCharacterD;
527 uint8_t uHostBusType;
528 uint8_t uWideTransferPermittedId0To7;
529 uint8_t uWideTransfersActiveId0To7;
530 ReplyInquireSetupInformationSynchronousValue SynchronousValuesId8To15[8];
531 uint8_t uDisconnectPermittedId8To15;
532 uint8_t uReserved2;
533 uint8_t uWideTransferPermittedId8To15;
534 uint8_t uWideTransfersActiveId8To15;
535} ReplyInquireSetupInformation, *PReplyInquireSetupInformation;
536AssertCompileSize(ReplyInquireSetupInformation, 34);
537
538/* Structure for the INQUIRE_EXTENDED_SETUP_INFORMATION. */
539#pragma pack(1)
540typedef struct ReplyInquireExtendedSetupInformation
541{
542 uint8_t uBusType;
543 uint8_t uBiosAddress;
544 uint16_t u16ScatterGatherLimit;
545 uint8_t cMailbox;
546 uint32_t uMailboxAddressBase;
547 unsigned char uReserved1: 2;
548 bool fFastEISA: 1;
549 unsigned char uReserved2: 3;
550 bool fLevelSensitiveInterrupt: 1;
551 unsigned char uReserved3: 1;
552 unsigned char aFirmwareRevision[3];
553 bool fHostWideSCSI: 1;
554 bool fHostDifferentialSCSI: 1;
555 bool fHostSupportsSCAM: 1;
556 bool fHostUltraSCSI: 1;
557 bool fHostSmartTermination: 1;
558 unsigned char uReserved4: 3;
559} ReplyInquireExtendedSetupInformation, *PReplyInquireExtendedSetupInformation;
560AssertCompileSize(ReplyInquireExtendedSetupInformation, 14);
561#pragma pack()
562
563/* Structure for the INITIALIZE EXTENDED MAILBOX request. */
564#pragma pack(1)
565typedef struct RequestInitializeExtendedMailbox
566{
567 /** Number of mailboxes in guest memory. */
568 uint8_t cMailbox;
569 /** Physical address of the first mailbox. */
570 uint32_t uMailboxBaseAddress;
571} RequestInitializeExtendedMailbox, *PRequestInitializeExtendedMailbox;
572AssertCompileSize(RequestInitializeExtendedMailbox, 5);
573#pragma pack()
574
575/*
576 * Structure of a mailbox in guest memory.
577 * The incoming and outgoing mailbox have the same size
578 * but the incoming one has some more fields defined which
579 * are marked as reserved in the outgoing one.
580 * The last field is also different from the type.
581 * For outgoing mailboxes it is the action and
582 * for incoming ones the completion status code for the task.
583 * We use one structure for both types.
584 */
585#pragma pack(1)
586typedef struct Mailbox
587{
588 /** Physical address of the CCB structure in the guest memory. */
589 uint32_t u32PhysAddrCCB;
590 /** Type specific data. */
591 union
592 {
593 /** For outgoing mailboxes. */
594 struct
595 {
596 /** Reserved */
597 uint8_t uReserved[3];
598 /** Action code. */
599 uint8_t uActionCode;
600 } out;
601 /** For incoming mailboxes. */
602 struct
603 {
604 /** The host adapter status after finishing the request. */
605 uint8_t uHostAdapterStatus;
606 /** The status of the device which executed the request after executing it. */
607 uint8_t uTargetDeviceStatus;
608 /** Reserved. */
609 uint8_t uReserved;
610 /** The completion status code of the request. */
611 uint8_t uCompletionCode;
612 } in;
613 } u;
614} Mailbox, *PMailbox;
615AssertCompileSize(Mailbox, 8);
616#pragma pack()
617
618/*
619 * Action codes for outgoing mailboxes.
620 */
621enum BUSLOGIC_MAILBOX_OUTGOING_ACTION
622{
623 BUSLOGIC_MAILBOX_OUTGOING_ACTION_FREE = 0x00,
624 BUSLOGIC_MAILBOX_OUTGOING_ACTION_START_COMMAND = 0x01,
625 BUSLOGIC_MAILBOX_OUTGOING_ACTION_ABORT_COMMAND = 0x02
626};
627
628/*
629 * Completion codes for incoming mailboxes.
630 */
631enum BUSLOGIC_MAILBOX_INCOMING_COMPLETION
632{
633 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_FREE = 0x00,
634 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_WITHOUT_ERROR = 0x01,
635 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_ABORTED = 0x02,
636 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_ABORTED_NOT_FOUND = 0x03,
637 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_WITH_ERROR = 0x04,
638 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_INVALID_CCB = 0x05
639};
640
641/*
642 * Host adapter status for incoming mailboxes.
643 */
644enum BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS
645{
646 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_CMD_COMPLETED = 0x00,
647 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_LINKED_CMD_COMPLETED = 0x0a,
648 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_LINKED_CMD_COMPLETED_WITH_FLAG = 0x0b,
649 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_DATA_UNDERUN = 0x0c,
650 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_SCSI_SELECTION_TIMEOUT = 0x11,
651 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_DATA_OVERRUN = 0x12,
652 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_UNEXPECTED_BUS_FREE = 0x13,
653 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_INVALID_BUS_PHASE_REQUESTED = 0x14,
654 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_INVALID_OUTGOING_MAILBOX_ACTION_CODE = 0x15,
655 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_INVALID_COMMAND_OPERATION_CODE = 0x16,
656 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_LINKED_CCB_HAS_INVALID_LUN = 0x17,
657 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_INVALID_COMMAND_PARAMETER = 0x1a,
658 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_AUTO_REQUEST_SENSE_FAILED = 0x1b,
659 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_TAGGED_QUEUING_MESSAGE_REJECTED = 0x1c,
660 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_UNSUPPORTED_MESSAGE_RECEIVED = 0x1d,
661 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_HOST_ADAPTER_HARDWARE_FAILED = 0x20,
662 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_TARGET_FAILED_RESPONSE_TO_ATN = 0x21,
663 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_HOST_ADAPTER_ASSERTED_RST = 0x22,
664 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_OTHER_DEVICE_ASSERTED_RST = 0x23,
665 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_TARGET_DEVICE_RECONNECTED_IMPROPERLY = 0x24,
666 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_HOST_ADAPTER_ASSERTED_BUS_DEVICE_RESET = 0x25,
667 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_ABORT_QUEUE_GENERATED = 0x26,
668 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_HOST_ADAPTER_SOFTWARE_ERROR = 0x27,
669 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_HOST_ADAPTER_HARDWARE_TIMEOUT_ERROR = 0x30,
670 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_SCSI_PARITY_ERROR_DETECTED = 0x34
671};
672
673/*
674 * Device status codes for incoming mailboxes.
675 */
676enum BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS
677{
678 BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_OPERATION_GOOD = 0x00,
679 BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_CHECK_CONDITION = 0x02,
680 BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_DEVICE_BUSY = 0x08
681};
682
683/*
684 * Opcode types for CCB.
685 */
686enum BUSLOGIC_CCB_OPCODE
687{
688 BUSLOGIC_CCB_OPCODE_INITIATOR_CCB = 0x00,
689 BUSLOGIC_CCB_OPCODE_TARGET_CCB = 0x01,
690 BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_SCATTER_GATHER = 0x02,
691 BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH = 0x03,
692 BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER = 0x04,
693 BUSLOGIC_CCB_OPCODE_BUS_DEVICE_RESET = 0x81
694};
695
696/*
697 * Data transfer direction.
698 */
699enum BUSLOGIC_CCB_DIRECTION
700{
701 BUSLOGIC_CCB_DIRECTION_UNKNOWN = 0x00,
702 BUSLOGIC_CCB_DIRECTION_IN = 0x01,
703 BUSLOGIC_CCB_DIRECTION_OUT = 0x02,
704 BUSLOGIC_CCB_DIRECTION_NO_DATA = 0x03
705};
706
707/*
708 * The command control block for a SCSI request.
709 */
710#pragma pack(1)
711typedef struct CommandControlBlock
712{
713 /** Opcode. */
714 uint8_t uOpcode;
715 /** Reserved */
716 unsigned char uReserved1: 3;
717 /** Data direction for the request. */
718 unsigned char uDataDirection: 2;
719 /** Whether the request is tag queued. */
720 bool fTagQueued: 1;
721 /** Queue tag mode. */
722 unsigned char uQueueTag: 2;
723 /** Length of the SCSI CDB. */
724 uint8_t cbCDB;
725 /** Sense data length. */
726 uint8_t cbSenseData;
727 /** Data length. */
728 uint32_t cbData;
729 /** Data pointer.
730 * This points to the data region or a scatter gather list based on the opcode.
731 */
732 uint32_t u32PhysAddrData;
733 /** Reserved. */
734 uint8_t uReserved2[2];
735 /** Host adapter status. */
736 uint8_t uHostAdapterStatus;
737 /** Device adapter status. */
738 uint8_t uDeviceStatus;
739 /** The device the request is send to. */
740 uint8_t uTargetId;
741 /**The LUN in the device. */
742 unsigned char uLogicalUnit: 5;
743 /** Legacy tag. */
744 bool fLegacyTagEnable: 1;
745 /** Legacy queue tag. */
746 unsigned char uLegacyQueueTag: 2;
747 /** The SCSI CDB. */
748 uint8_t aCDB[12]; /* A CDB can be 12 bytes long. */
749 /** Reserved. */
750 uint8_t uReserved3[6];
751 /** Sense data pointer. */
752 uint32_t u32PhysAddrSenseData;
753} CommandControlBlock, *PCommandControlBlock;
754AssertCompileSize(CommandControlBlock, 40);
755#pragma pack()
756
757#pragma pack(1)
758typedef struct ScatterGatherEntry
759{
760 uint32_t cbSegment;
761 uint32_t u32PhysAddrSegmentBase;
762} ScatterGatherEntry, *PScatterGatherEntry;
763AssertCompileSize(ScatterGatherEntry, 8);
764#pragma pack()
765
766/*
767 * Task state for a CCB request.
768 */
769typedef struct BUSLOGICTASKSTATE
770{
771 /** Next in the redo list. */
772 PBUSLOGICTASKSTATE pRedoNext;
773 /** Device this task is assigned to. */
774 R3PTRTYPE(PBUSLOGICDEVICE) pTargetDeviceR3;
775 /** The command control block from the guest. */
776 CommandControlBlock CommandControlBlockGuest;
777 /** Mailbox read from guest memory. */
778 Mailbox MailboxGuest;
779 /** The SCSI request we pass to the underlying SCSI engine. */
780 PDMSCSIREQUEST PDMScsiRequest;
781 /** Data buffer segment */
782 RTSGSEG DataSeg;
783 /** Pointer to the R3 sense buffer. */
784 uint8_t *pbSenseBuffer;
785 /** Flag whether this is a request from the BIOS. */
786 bool fBIOS;
787} BUSLOGICTASKSTATE;
788
789#ifndef VBOX_DEVICE_STRUCT_TESTCASE
790
791#define PDMIBASE_2_PBUSLOGICDEVICE(pInterface) ( (PBUSLOGICDEVICE)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGICDEVICE, IBase)) )
792#define PDMISCSIPORT_2_PBUSLOGICDEVICE(pInterface) ( (PBUSLOGICDEVICE)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGICDEVICE, ISCSIPort)) )
793#define PDMILEDPORTS_2_PBUSLOGICDEVICE(pInterface) ( (PBUSLOGICDEVICE)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGICDEVICE, ILed)) )
794#define PDMIBASE_2_PBUSLOGIC(pInterface) ( (PBUSLOGIC)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGIC, IBase)) )
795#define PDMILEDPORTS_2_PBUSLOGIC(pInterface) ( (PBUSLOGIC)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGIC, ILeds)) )
796
797/**
798 * Assert IRQ line of the BusLogic adapter.
799 *
800 * @returns nothing.
801 * @param pBusLogic Pointer to the BusLogic device instance.
802 * @param fSuppressIrq Flag to suppress IRQ generation regardless of fIRQEnabled
803 * @param uFlag Type of interrupt being generated.
804 */
805static void buslogicSetInterrupt(PBUSLOGIC pBusLogic, bool fSuppressIrq, uint8_t uIrqType)
806{
807 LogFlowFunc(("pBusLogic=%#p\n", pBusLogic));
808
809 /* The CMDC interrupt has priority over IMBL and MBOR. */
810 if (uIrqType & (BUSLOGIC_REGISTER_INTERRUPT_INCOMING_MAILBOX_LOADED | BUSLOGIC_REGISTER_INTERRUPT_OUTGOING_MAILBOX_AVAILABLE))
811 {
812 if (!(pBusLogic->regInterrupt & BUSLOGIC_REGISTER_INTERRUPT_COMMAND_COMPLETE))
813 pBusLogic->regInterrupt |= uIrqType; /* Report now. */
814 else
815 pBusLogic->uPendingIntr |= uIrqType; /* Report later. */
816 }
817 else if (uIrqType & BUSLOGIC_REGISTER_INTERRUPT_COMMAND_COMPLETE)
818 {
819 Assert(!pBusLogic->regInterrupt);
820 pBusLogic->regInterrupt |= uIrqType;
821 }
822 else
823 AssertMsgFailed(("Invalid interrupt state!\n"));
824
825 pBusLogic->regInterrupt |= BUSLOGIC_REGISTER_INTERRUPT_INTERRUPT_VALID;
826 if (pBusLogic->fIRQEnabled && !fSuppressIrq)
827 PDMDevHlpPCISetIrq(pBusLogic->CTX_SUFF(pDevIns), 0, 1);
828}
829
830/**
831 * Deasserts the interrupt line of the BusLogic adapter.
832 *
833 * @returns nothing
834 * @param pBuslogic Pointer to the BusLogic device instance.
835 */
836static void buslogicClearInterrupt(PBUSLOGIC pBusLogic)
837{
838 LogFlowFunc(("pBusLogic=%#p\n", pBusLogic));
839 pBusLogic->regInterrupt = 0;
840 PDMDevHlpPCISetIrq(pBusLogic->CTX_SUFF(pDevIns), 0, 0);
841 /* If there's another pending interrupt, report it now. */
842 if (pBusLogic->uPendingIntr)
843 {
844 buslogicSetInterrupt(pBusLogic, false, pBusLogic->uPendingIntr);
845 pBusLogic->uPendingIntr = 0;
846 }
847}
848
849#if defined(IN_RING3)
850
851/**
852 * Advances the mailbox pointer to the next slot.
853 */
854DECLINLINE(void) buslogicOutgoingMailboxAdvance(PBUSLOGIC pBusLogic)
855{
856 pBusLogic->uMailboxOutgoingPositionCurrent = (pBusLogic->uMailboxOutgoingPositionCurrent + 1) % pBusLogic->cMailbox;
857}
858
859/**
860 * Returns the physical address of the next outgoing mailbox to process.
861 */
862DECLINLINE(RTGCPHYS) buslogicOutgoingMailboxGetGCPhys(PBUSLOGIC pBusLogic)
863{
864 return pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->uMailboxOutgoingPositionCurrent * sizeof(Mailbox));
865}
866
867/**
868 * Initialize local RAM of host adapter with default values.
869 *
870 * @returns nothing.
871 * @param pBusLogic.
872 */
873static void buslogicInitializeLocalRam(PBUSLOGIC pBusLogic)
874{
875 /*
876 * These values are mostly from what I think is right
877 * looking at the dmesg output from a Linux guest inside
878 * a VMware server VM.
879 *
880 * So they don't have to be right :)
881 */
882 memset(pBusLogic->LocalRam.u8View, 0, sizeof(HostAdapterLocalRam));
883 pBusLogic->LocalRam.structured.autoSCSIData.fLevelSensitiveInterrupt = true;
884 pBusLogic->LocalRam.structured.autoSCSIData.fParityCheckingEnabled = true;
885 pBusLogic->LocalRam.structured.autoSCSIData.fExtendedTranslation = true; /* Same as in geometry register. */
886 pBusLogic->LocalRam.structured.autoSCSIData.u16DeviceEnabledMask = ~0; /* All enabled. Maybe mask out non present devices? */
887 pBusLogic->LocalRam.structured.autoSCSIData.u16WidePermittedMask = ~0;
888 pBusLogic->LocalRam.structured.autoSCSIData.u16FastPermittedMask = ~0;
889 pBusLogic->LocalRam.structured.autoSCSIData.u16SynchronousPermittedMask = ~0;
890 pBusLogic->LocalRam.structured.autoSCSIData.u16DisconnectPermittedMask = ~0;
891 pBusLogic->LocalRam.structured.autoSCSIData.fStrictRoundRobinMode = pBusLogic->fStrictRoundRobinMode;
892 pBusLogic->LocalRam.structured.autoSCSIData.u16UltraPermittedMask = ~0;
893 /* @todo calculate checksum? */
894}
895
896/**
897 * Do a hardware reset of the buslogic adapter.
898 *
899 * @returns VBox status code.
900 * @param pBusLogic Pointer to the BusLogic device instance.
901 */
902static int buslogicHwReset(PBUSLOGIC pBusLogic)
903{
904 LogFlowFunc(("pBusLogic=%#p\n", pBusLogic));
905
906 /* Reset registers to default value. */
907 pBusLogic->regStatus = BUSLOGIC_REGISTER_STATUS_HOST_ADAPTER_READY | BUSLOGIC_REGISTER_STATUS_INITIALIZATION_REQUIRED;
908 pBusLogic->regInterrupt = 0;
909 pBusLogic->uPendingIntr = 0;
910 pBusLogic->regGeometry = BUSLOGIC_REGISTER_GEOMETRY_EXTENTED_TRANSLATION_ENABLED;
911 pBusLogic->uOperationCode = 0xff; /* No command executing. */
912 pBusLogic->iParameter = 0;
913 pBusLogic->cbCommandParametersLeft = 0;
914 pBusLogic->fIRQEnabled = true;
915 pBusLogic->fISAEnabled = true;
916 pBusLogic->uMailboxOutgoingPositionCurrent = 0;
917 pBusLogic->uMailboxIncomingPositionCurrent = 0;
918
919 buslogicInitializeLocalRam(pBusLogic);
920 vboxscsiInitialize(&pBusLogic->VBoxSCSI);
921
922 return VINF_SUCCESS;
923}
924#endif
925
926/**
927 * Resets the command state machine for the next command and notifies the guest.
928 *
929 * @returns nothing.
930 * @param pBusLogic Pointer to the BusLogic device instance
931 * @param fSuppressIrq Flag to suppress IRQ generation regardless of current state
932 */
933static void buslogicCommandComplete(PBUSLOGIC pBusLogic, bool fSuppressIrq)
934{
935 LogFlowFunc(("pBusLogic=%#p\n", pBusLogic));
936
937 pBusLogic->fUseLocalRam = false;
938 pBusLogic->regStatus |= BUSLOGIC_REGISTER_STATUS_HOST_ADAPTER_READY;
939 pBusLogic->iReply = 0;
940
941 /* Modify I/O address does not generate an interrupt. */
942 if (pBusLogic->uOperationCode != BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND)
943 {
944 /* Notify that the command is complete. */
945 pBusLogic->regStatus &= ~BUSLOGIC_REGISTER_STATUS_DATA_IN_REGISTER_READY;
946 buslogicSetInterrupt(pBusLogic, fSuppressIrq, BUSLOGIC_REGISTER_INTERRUPT_COMMAND_COMPLETE);
947 }
948
949 pBusLogic->uOperationCode = 0xff;
950 pBusLogic->iParameter = 0;
951}
952
953#if defined(IN_RING3)
954/**
955 * Initiates a hard reset which was issued from the guest.
956 *
957 * @returns nothing
958 * @param pBusLogic Pointer to the BusLogic device instance.
959 */
960static void buslogicIntiateHardReset(PBUSLOGIC pBusLogic)
961{
962 LogFlowFunc(("pBusLogic=%#p\n", pBusLogic));
963
964 /* Remember when the guest initiated a reset. */
965 pBusLogic->u64ResetTime = PDMDevHlpTMTimeVirtGetNano(pBusLogic->CTX_SUFF(pDevIns));
966
967 buslogicHwReset(pBusLogic);
968
969 /* We set the diagnostic active in the status register. */
970 pBusLogic->regStatus |= BUSLOGIC_REGISTER_STATUS_DIAGNOSTIC_ACTIVE;
971}
972
973/**
974 * Send a mailbox with set status codes to the guest.
975 *
976 * @returns nothing.
977 * @param pBusLogic Pointer to the BusLogic device instance.
978 * @param pTaskState Pointer to the task state with the mailbox to send.
979 * @param uHostAdapterStatus The host adapter status code to set.
980 * @param uDeviceStatus The target device status to set.
981 * @param uMailboxCompletionCode Completion status code to set in the mailbox.
982 */
983static void buslogicSendIncomingMailbox(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState,
984 uint8_t uHostAdapterStatus, uint8_t uDeviceStatus,
985 uint8_t uMailboxCompletionCode)
986{
987 pTaskState->MailboxGuest.u.in.uHostAdapterStatus = uHostAdapterStatus;
988 pTaskState->MailboxGuest.u.in.uTargetDeviceStatus = uDeviceStatus;
989 pTaskState->MailboxGuest.u.in.uCompletionCode = uMailboxCompletionCode;
990
991 int rc = PDMCritSectEnter(&pBusLogic->CritSectIntr, VINF_SUCCESS);
992 AssertRC(rc);
993 RTGCPHYS GCPhysAddrMailboxIncoming = pBusLogic->GCPhysAddrMailboxIncomingBase + (pBusLogic->uMailboxIncomingPositionCurrent * sizeof(Mailbox));
994 RTGCPHYS GCPhysAddrCCB = (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB;
995
996 LogFlowFunc(("Completing CCB %RGp\n", GCPhysAddrCCB));
997
998 /* Update CCB. */
999 pTaskState->CommandControlBlockGuest.uHostAdapterStatus = uHostAdapterStatus;
1000 pTaskState->CommandControlBlockGuest.uDeviceStatus = uDeviceStatus;
1001 /* @todo: this is wrong - writing too much! */
1002 PDMDevHlpPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrCCB, &pTaskState->CommandControlBlockGuest, sizeof(CommandControlBlock));
1003
1004#ifdef RT_STRICT
1005 Mailbox Tmp;
1006 PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxIncoming, &Tmp, sizeof(Mailbox));
1007 Assert(Tmp.u.in.uCompletionCode == BUSLOGIC_MAILBOX_INCOMING_COMPLETION_FREE);
1008#endif
1009
1010 /* Update mailbox. */
1011 PDMDevHlpPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxIncoming, &pTaskState->MailboxGuest, sizeof(Mailbox));
1012
1013 /* Advance to next mailbox position. */
1014 pBusLogic->uMailboxIncomingPositionCurrent++;
1015 if (pBusLogic->uMailboxIncomingPositionCurrent >= pBusLogic->cMailbox)
1016 pBusLogic->uMailboxIncomingPositionCurrent = 0;
1017
1018#ifdef LOG_ENABLED
1019 ASMAtomicIncU32(&pBusLogic->cInMailboxesReady);
1020#endif
1021
1022 buslogicSetInterrupt(pBusLogic, false, BUSLOGIC_REGISTER_INTERRUPT_INCOMING_MAILBOX_LOADED);
1023
1024 PDMCritSectLeave(&pBusLogic->CritSectIntr);
1025}
1026
1027#if defined(DEBUG)
1028/**
1029 * Dumps the content of a mailbox for debugging purposes.
1030 *
1031 * @return nothing
1032 * @param pMailbox The mailbox to dump.
1033 * @param fOutgoing true if dumping the outgoing state.
1034 * false if dumping the incoming state.
1035 */
1036static void buslogicDumpMailboxInfo(PMailbox pMailbox, bool fOutgoing)
1037{
1038 Log(("%s: Dump for %s mailbox:\n", __FUNCTION__, fOutgoing ? "outgoing" : "incoming"));
1039 Log(("%s: u32PhysAddrCCB=%#x\n", __FUNCTION__, pMailbox->u32PhysAddrCCB));
1040 if (fOutgoing)
1041 {
1042 Log(("%s: uActionCode=%u\n", __FUNCTION__, pMailbox->u.out.uActionCode));
1043 }
1044 else
1045 {
1046 Log(("%s: uHostAdapterStatus=%u\n", __FUNCTION__, pMailbox->u.in.uHostAdapterStatus));
1047 Log(("%s: uTargetDeviceStatus=%u\n", __FUNCTION__, pMailbox->u.in.uTargetDeviceStatus));
1048 Log(("%s: uCompletionCode=%u\n", __FUNCTION__, pMailbox->u.in.uCompletionCode));
1049 }
1050}
1051
1052/**
1053 * Dumps the content of a command control block for debugging purposes.
1054 *
1055 * @returns nothing.
1056 * @param pCCB Pointer to the command control block to dump.
1057 */
1058static void buslogicDumpCCBInfo(PCommandControlBlock pCCB)
1059{
1060 Log(("%s: Dump for Command Control Block:\n", __FUNCTION__));
1061 Log(("%s: uOpCode=%#x\n", __FUNCTION__, pCCB->uOpcode));
1062 Log(("%s: uDataDirection=%u\n", __FUNCTION__, pCCB->uDataDirection));
1063 Log(("%s: fTagQueued=%d\n", __FUNCTION__, pCCB->fTagQueued));
1064 Log(("%s: uQueueTag=%u\n", __FUNCTION__, pCCB->uQueueTag));
1065 Log(("%s: cbCDB=%u\n", __FUNCTION__, pCCB->cbCDB));
1066 Log(("%s: cbSenseData=%u\n", __FUNCTION__, pCCB->cbSenseData));
1067 Log(("%s: cbData=%u\n", __FUNCTION__, pCCB->cbData));
1068 Log(("%s: u32PhysAddrData=%#x\n", __FUNCTION__, pCCB->u32PhysAddrData));
1069 Log(("%s: uHostAdapterStatus=%u\n", __FUNCTION__, pCCB->uHostAdapterStatus));
1070 Log(("%s: uDeviceStatus=%u\n", __FUNCTION__, pCCB->uDeviceStatus));
1071 Log(("%s: uTargetId=%u\n", __FUNCTION__, pCCB->uTargetId));
1072 Log(("%s: uLogicalUnit=%u\n", __FUNCTION__, pCCB->uLogicalUnit));
1073 Log(("%s: fLegacyTagEnable=%u\n", __FUNCTION__, pCCB->fLegacyTagEnable));
1074 Log(("%s: uLegacyQueueTag=%u\n", __FUNCTION__, pCCB->uLegacyQueueTag));
1075 Log(("%s: uCDB[0]=%#x\n", __FUNCTION__, pCCB->aCDB[0]));
1076 for (int i = 1; i < pCCB->cbCDB; i++)
1077 Log(("%s: uCDB[%d]=%u\n", __FUNCTION__, i, pCCB->aCDB[i]));
1078 Log(("%s: u32PhysAddrSenseData=%#x\n", __FUNCTION__, pCCB->u32PhysAddrSenseData));
1079}
1080#endif
1081
1082/**
1083 * Allocate data buffer.
1084 *
1085 * @returns VBox status code.
1086 * @param pTaskState Pointer to the task state.
1087 */
1088static int buslogicDataBufferAlloc(PBUSLOGICTASKSTATE pTaskState)
1089{
1090 PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
1091
1092 if ( (pTaskState->CommandControlBlockGuest.uDataDirection != BUSLOGIC_CCB_DIRECTION_NO_DATA)
1093 && (pTaskState->CommandControlBlockGuest.cbData > 0))
1094 {
1095 /*
1096 * @todo: Check following assumption and what residual means.
1097 *
1098 * The BusLogic adapter can handle two different data buffer formats.
1099 * The first one is that the data pointer entry in the CCB points to
1100 * the buffer directly. In second mode the data pointer points to a
1101 * scatter gather list which describes the buffer.
1102 */
1103 if ( (pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_SCATTER_GATHER)
1104 || (pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER))
1105 {
1106 uint32_t cScatterGatherGCRead;
1107 uint32_t iScatterGatherEntry;
1108 ScatterGatherEntry aScatterGatherReadGC[32]; /* Number of scatter gather list entries read from guest memory. */
1109 uint32_t cScatterGatherGCLeft = pTaskState->CommandControlBlockGuest.cbData / sizeof(ScatterGatherEntry);
1110 RTGCPHYS GCPhysAddrScatterGatherCurrent = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrData;
1111 size_t cbDataToTransfer = 0;
1112
1113 /* Count number of bytes to transfer. */
1114 do
1115 {
1116 cScatterGatherGCRead = (cScatterGatherGCLeft < RT_ELEMENTS(aScatterGatherReadGC))
1117 ? cScatterGatherGCLeft
1118 : RT_ELEMENTS(aScatterGatherReadGC);
1119 cScatterGatherGCLeft -= cScatterGatherGCRead;
1120
1121 /* Read the SG entries. */
1122 PDMDevHlpPhysRead(pDevIns, GCPhysAddrScatterGatherCurrent, &aScatterGatherReadGC[0],
1123 cScatterGatherGCRead * sizeof(ScatterGatherEntry));
1124
1125 for (iScatterGatherEntry = 0; iScatterGatherEntry < cScatterGatherGCRead; iScatterGatherEntry++)
1126 {
1127 RTGCPHYS GCPhysAddrDataBase;
1128
1129 Log(("%s: iScatterGatherEntry=%u\n", __FUNCTION__, iScatterGatherEntry));
1130
1131 GCPhysAddrDataBase = (RTGCPHYS)aScatterGatherReadGC[iScatterGatherEntry].u32PhysAddrSegmentBase;
1132 cbDataToTransfer += aScatterGatherReadGC[iScatterGatherEntry].cbSegment;
1133
1134 Log(("%s: GCPhysAddrDataBase=%RGp cbDataToTransfer=%u\n",
1135 __FUNCTION__, GCPhysAddrDataBase,
1136 aScatterGatherReadGC[iScatterGatherEntry].cbSegment));
1137 }
1138
1139 /* Set address to the next entries to read. */
1140 GCPhysAddrScatterGatherCurrent += cScatterGatherGCRead * sizeof(ScatterGatherEntry);
1141 } while (cScatterGatherGCLeft > 0);
1142
1143 Log(("%s: cbDataToTransfer=%d\n", __FUNCTION__, cbDataToTransfer));
1144
1145 /* Allocate buffer */
1146 pTaskState->DataSeg.cbSeg = cbDataToTransfer;
1147 pTaskState->DataSeg.pvSeg = RTMemAlloc(pTaskState->DataSeg.cbSeg);
1148 if (!pTaskState->DataSeg.pvSeg)
1149 return VERR_NO_MEMORY;
1150
1151 /* Copy the data if needed */
1152 if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_OUT)
1153 {
1154 cScatterGatherGCLeft = pTaskState->CommandControlBlockGuest.cbData / sizeof(ScatterGatherEntry);
1155 GCPhysAddrScatterGatherCurrent = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrData;
1156 uint8_t *pbData = (uint8_t *)pTaskState->DataSeg.pvSeg;
1157
1158 do
1159 {
1160 cScatterGatherGCRead = (cScatterGatherGCLeft < RT_ELEMENTS(aScatterGatherReadGC))
1161 ? cScatterGatherGCLeft
1162 : RT_ELEMENTS(aScatterGatherReadGC);
1163 cScatterGatherGCLeft -= cScatterGatherGCRead;
1164
1165 /* Read the SG entries. */
1166 PDMDevHlpPhysRead(pDevIns, GCPhysAddrScatterGatherCurrent, &aScatterGatherReadGC[0],
1167 cScatterGatherGCRead * sizeof(ScatterGatherEntry));
1168
1169 for (iScatterGatherEntry = 0; iScatterGatherEntry < cScatterGatherGCRead; iScatterGatherEntry++)
1170 {
1171 RTGCPHYS GCPhysAddrDataBase;
1172
1173 Log(("%s: iScatterGatherEntry=%u\n", __FUNCTION__, iScatterGatherEntry));
1174
1175 GCPhysAddrDataBase = (RTGCPHYS)aScatterGatherReadGC[iScatterGatherEntry].u32PhysAddrSegmentBase;
1176 cbDataToTransfer = aScatterGatherReadGC[iScatterGatherEntry].cbSegment;
1177
1178 Log(("%s: GCPhysAddrDataBase=%RGp cbDataToTransfer=%u\n", __FUNCTION__, GCPhysAddrDataBase, cbDataToTransfer));
1179
1180 PDMDevHlpPhysRead(pDevIns, GCPhysAddrDataBase, pbData, cbDataToTransfer);
1181 pbData += cbDataToTransfer;
1182 }
1183
1184 /* Set address to the next entries to read. */
1185 GCPhysAddrScatterGatherCurrent += cScatterGatherGCRead * sizeof(ScatterGatherEntry);
1186 } while (cScatterGatherGCLeft > 0);
1187 }
1188
1189 }
1190 else if ( pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB
1191 || pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH)
1192 {
1193 /* The buffer is not scattered. */
1194 RTGCPHYS GCPhysAddrDataBase = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrData;
1195
1196 AssertMsg(GCPhysAddrDataBase != 0, ("Physical address is 0\n"));
1197
1198 pTaskState->DataSeg.cbSeg = pTaskState->CommandControlBlockGuest.cbData;
1199 pTaskState->DataSeg.pvSeg = RTMemAlloc(pTaskState->DataSeg.cbSeg);
1200 if (!pTaskState->DataSeg.pvSeg)
1201 return VERR_NO_MEMORY;
1202
1203 Log(("Non scattered buffer:\n"));
1204 Log(("u32PhysAddrData=%#x\n", pTaskState->CommandControlBlockGuest.u32PhysAddrData));
1205 Log(("cbData=%u\n", pTaskState->CommandControlBlockGuest.cbData));
1206 Log(("GCPhysAddrDataBase=0x%RGp\n", GCPhysAddrDataBase));
1207
1208 /* Copy the data into the buffer. */
1209 PDMDevHlpPhysRead(pDevIns, GCPhysAddrDataBase, pTaskState->DataSeg.pvSeg, pTaskState->DataSeg.cbSeg);
1210 }
1211 }
1212
1213 return VINF_SUCCESS;
1214}
1215
1216/**
1217 * Free allocated resources used for the scatter gather list.
1218 *
1219 * @returns nothing.
1220 * @param pTaskState Pointer to the task state.
1221 */
1222static void buslogicDataBufferFree(PBUSLOGICTASKSTATE pTaskState)
1223{
1224 PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
1225
1226 if ( (pTaskState->CommandControlBlockGuest.cbData > 0)
1227 && ( (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_IN)
1228 || (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_UNKNOWN)))
1229 {
1230 if ( (pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_SCATTER_GATHER)
1231 || (pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER))
1232 {
1233 uint32_t cScatterGatherGCRead;
1234 uint32_t iScatterGatherEntry;
1235 ScatterGatherEntry aScatterGatherReadGC[32]; /* Number of scatter gather list entries read from guest memory. */
1236 uint32_t cScatterGatherGCLeft = pTaskState->CommandControlBlockGuest.cbData / sizeof(ScatterGatherEntry);
1237 RTGCPHYS GCPhysAddrScatterGatherCurrent = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrData;
1238 uint8_t *pbData = (uint8_t *)pTaskState->DataSeg.pvSeg;
1239
1240 do
1241 {
1242 cScatterGatherGCRead = (cScatterGatherGCLeft < RT_ELEMENTS(aScatterGatherReadGC))
1243 ? cScatterGatherGCLeft
1244 : RT_ELEMENTS(aScatterGatherReadGC);
1245 cScatterGatherGCLeft -= cScatterGatherGCRead;
1246
1247 /* Read the SG entries. */
1248 PDMDevHlpPhysRead(pDevIns, GCPhysAddrScatterGatherCurrent, &aScatterGatherReadGC[0],
1249 cScatterGatherGCRead * sizeof(ScatterGatherEntry));
1250
1251 for (iScatterGatherEntry = 0; iScatterGatherEntry < cScatterGatherGCRead; iScatterGatherEntry++)
1252 {
1253 RTGCPHYS GCPhysAddrDataBase;
1254 size_t cbDataToTransfer;
1255
1256 Log(("%s: iScatterGatherEntry=%u\n", __FUNCTION__, iScatterGatherEntry));
1257
1258 GCPhysAddrDataBase = (RTGCPHYS)aScatterGatherReadGC[iScatterGatherEntry].u32PhysAddrSegmentBase;
1259 cbDataToTransfer = aScatterGatherReadGC[iScatterGatherEntry].cbSegment;
1260
1261 Log(("%s: GCPhysAddrDataBase=%RGp cbDataToTransfer=%u\n", __FUNCTION__, GCPhysAddrDataBase, cbDataToTransfer));
1262
1263 PDMDevHlpPhysWrite(pDevIns, GCPhysAddrDataBase, pbData, cbDataToTransfer);
1264 pbData += cbDataToTransfer;
1265 }
1266
1267 /* Set address to the next entries to read. */
1268 GCPhysAddrScatterGatherCurrent += cScatterGatherGCRead * sizeof(ScatterGatherEntry);
1269 } while (cScatterGatherGCLeft > 0);
1270
1271 }
1272 else if ( pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB
1273 || pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH)
1274 {
1275 /* The buffer is not scattered. */
1276 RTGCPHYS GCPhysAddrDataBase = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrData;
1277
1278 AssertMsg(GCPhysAddrDataBase != 0, ("Physical address is 0\n"));
1279
1280 Log(("Non scattered buffer:\n"));
1281 Log(("u32PhysAddrData=%#x\n", pTaskState->CommandControlBlockGuest.u32PhysAddrData));
1282 Log(("cbData=%u\n", pTaskState->CommandControlBlockGuest.cbData));
1283 Log(("GCPhysAddrDataBase=0x%RGp\n", GCPhysAddrDataBase));
1284
1285 /* Copy the data into the guest memory. */
1286 PDMDevHlpPhysWrite(pDevIns, GCPhysAddrDataBase, pTaskState->DataSeg.pvSeg, pTaskState->DataSeg.cbSeg);
1287 }
1288 }
1289
1290 RTMemFree(pTaskState->DataSeg.pvSeg);
1291 pTaskState->DataSeg.pvSeg = NULL;
1292 pTaskState->DataSeg.cbSeg = 0;
1293}
1294
1295/**
1296 * Free the sense buffer.
1297 *
1298 * @returns nothing.
1299 * @param pTaskState Pointer to the task state.
1300 * @param fCopy If sense data should be copied to guest memory.
1301 */
1302static void buslogicSenseBufferFree(PBUSLOGICTASKSTATE pTaskState, bool fCopy)
1303{
1304 PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
1305 RTGCPHYS GCPhysAddrSenseBuffer = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrSenseData;
1306 uint32_t cbSenseBuffer = pTaskState->CommandControlBlockGuest.cbSenseData;
1307
1308 /* Copy into guest memory. */
1309 if (fCopy)
1310 PDMDevHlpPhysWrite(pDevIns, GCPhysAddrSenseBuffer, pTaskState->pbSenseBuffer, cbSenseBuffer);
1311
1312 RTMemFree(pTaskState->pbSenseBuffer);
1313 pTaskState->pbSenseBuffer = NULL;
1314}
1315
1316/**
1317 * Alloc the sense buffer.
1318 *
1319 * @returns VBox status code.
1320 * @param pTaskState Pointer to the task state.
1321 * @note Current assumption is that the sense buffer is not scattered and does not cross a page boundary.
1322 */
1323static int buslogicSenseBufferAlloc(PBUSLOGICTASKSTATE pTaskState)
1324{
1325 PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
1326 uint32_t cbSenseBuffer = pTaskState->CommandControlBlockGuest.cbSenseData;
1327
1328 pTaskState->pbSenseBuffer = (uint8_t *)RTMemAllocZ(cbSenseBuffer);
1329 if (!pTaskState->pbSenseBuffer)
1330 return VERR_NO_MEMORY;
1331
1332 return VINF_SUCCESS;
1333}
1334#endif /* IN_RING3 */
1335
1336/**
1337 * Parses the command buffer and executes it.
1338 *
1339 * @returns VBox status code.
1340 * @param pBusLogic Pointer to the BusLogic device instance.
1341 */
1342static int buslogicProcessCommand(PBUSLOGIC pBusLogic)
1343{
1344 int rc = VINF_SUCCESS;
1345 bool fSuppressIrq = false;
1346
1347 LogFlowFunc(("pBusLogic=%#p\n", pBusLogic));
1348 AssertMsg(pBusLogic->uOperationCode != 0xff, ("There is no command to execute\n"));
1349
1350 switch (pBusLogic->uOperationCode)
1351 {
1352 case BUSLOGICCOMMAND_TEST_CMDC_INTERRUPT:
1353 /* Valid command, no reply. */
1354 pBusLogic->cbReplyParametersLeft = 0;
1355 break;
1356 case BUSLOGICCOMMAND_INQUIRE_PCI_HOST_ADAPTER_INFORMATION:
1357 {
1358 PReplyInquirePCIHostAdapterInformation pReply = (PReplyInquirePCIHostAdapterInformation)pBusLogic->aReplyBuffer;
1359 memset(pReply, 0, sizeof(ReplyInquirePCIHostAdapterInformation));
1360
1361 /* It seems VMware does not provide valid information here too, lets do the same :) */
1362 pReply->InformationIsValid = 0;
1363 pReply->IsaIOPort = 0xff; /* Make it invalid. */
1364 pReply->IRQ = PCIDevGetInterruptLine(&pBusLogic->dev);
1365 pBusLogic->cbReplyParametersLeft = sizeof(ReplyInquirePCIHostAdapterInformation);
1366 break;
1367 }
1368 case BUSLOGICCOMMAND_MODIFY_IO_ADDRESS:
1369 {
1370 pBusLogic->cbReplyParametersLeft = 0;
1371 if (pBusLogic->aCommandBuffer[0] == 0x06)
1372 {
1373 Log(("Disabling ISA I/O ports.\n"));
1374 pBusLogic->fISAEnabled = false;
1375 }
1376 fSuppressIrq = true;
1377 break;
1378 }
1379 case BUSLOGICCOMMAND_INQUIRE_BOARD_ID:
1380 {
1381 /* The special option byte is important: If it is '0' or 'B', Windows NT drivers
1382 * for Adaptec AHA-154x may claim the adapter. The BusLogic drivers will claim
1383 * the adapter only when the byte is *not* '0' or 'B'.
1384 */
1385 pBusLogic->aReplyBuffer[0] = 'A'; /* Firmware option bytes */
1386 pBusLogic->aReplyBuffer[1] = 'A'; /* Special option byte */
1387
1388 /* We report version 5.07B. This reply will provide the first two digits. */
1389 pBusLogic->aReplyBuffer[2] = '5'; /* Major version 5 */
1390 pBusLogic->aReplyBuffer[3] = '0'; /* Minor version 0 */
1391 pBusLogic->cbReplyParametersLeft = 4; /* Reply is 4 bytes long */
1392 break;
1393 }
1394 case BUSLOGICCOMMAND_INQUIRE_FIRMWARE_VERSION_3RD_LETTER:
1395 {
1396 pBusLogic->aReplyBuffer[0] = '7';
1397 pBusLogic->cbReplyParametersLeft = 1;
1398 break;
1399 }
1400 case BUSLOGICCOMMAND_INQUIRE_FIRMWARE_VERSION_LETTER:
1401 {
1402 pBusLogic->aReplyBuffer[0] = 'B';
1403 pBusLogic->cbReplyParametersLeft = 1;
1404 break;
1405 }
1406 case BUSLOGICCOMMAND_SET_ADAPTER_OPTIONS:
1407 /* The parameter list length is determined by the first byte of the command buffer. */
1408 if (pBusLogic->iParameter == 1)
1409 {
1410 /* First pass - set the number of following parameter bytes. */
1411 pBusLogic->cbCommandParametersLeft = pBusLogic->aCommandBuffer[0];
1412 Log(("Set HA options: %u bytes follow\n", pBusLogic->aCommandBuffer[0]));
1413 }
1414 else
1415 {
1416 /* Second pass - process received data. */
1417 Log(("Set HA options: received %u bytes\n", pBusLogic->aCommandBuffer[0]));
1418 /* We ignore the data - it only concerns the SCSI hardware protocol. */
1419 }
1420 pBusLogic->cbReplyParametersLeft = 0;
1421 break;
1422
1423 case BUSLOGICCOMMAND_INQUIRE_HOST_ADAPTER_MODEL_NUMBER:
1424 {
1425 /* The reply length is set by the guest and is found in the first byte of the command buffer. */
1426 pBusLogic->cbReplyParametersLeft = pBusLogic->aCommandBuffer[0];
1427 memset(pBusLogic->aReplyBuffer, ' ', pBusLogic->cbReplyParametersLeft);
1428 const char aModelName[] = "958";
1429 int cCharsToTransfer = (pBusLogic->cbReplyParametersLeft <= (sizeof(aModelName) - 1))
1430 ? pBusLogic->cbReplyParametersLeft
1431 : sizeof(aModelName) - 1;
1432
1433 for (int i = 0; i < cCharsToTransfer; i++)
1434 pBusLogic->aReplyBuffer[i] = aModelName[i];
1435
1436 break;
1437 }
1438 case BUSLOGICCOMMAND_INQUIRE_CONFIGURATION:
1439 {
1440 uint8_t uPciIrq = PCIDevGetInterruptLine(&pBusLogic->dev);
1441
1442 pBusLogic->cbReplyParametersLeft = sizeof(ReplyInquireConfiguration);
1443 PReplyInquireConfiguration pReply = (PReplyInquireConfiguration)pBusLogic->aReplyBuffer;
1444 memset(pReply, 0, sizeof(ReplyInquireConfiguration));
1445
1446 pReply->uHostAdapterId = 7; /* The controller has always 7 as ID. */
1447 //@todo: What should the DMA channel be?
1448 pReply->fDmaChannel6 = 1;
1449 /* The IRQ is not necessarily representable in this structure. */
1450 switch (uPciIrq) {
1451 case 9: pReply->fIrqChannel9 = 1; break;
1452 case 10: pReply->fIrqChannel10 = 1; break;
1453 case 11: pReply->fIrqChannel11 = 1; break;
1454 case 12: pReply->fIrqChannel12 = 1; break;
1455 case 14: pReply->fIrqChannel14 = 1; break;
1456 case 15: pReply->fIrqChannel15 = 1; break;
1457 default:
1458 Log(("Inquire configuration: PCI IRQ %d cannot be represented\n", uPciIrq));
1459 break;
1460 }
1461 break;
1462 }
1463 case BUSLOGICCOMMAND_INQUIRE_EXTENDED_SETUP_INFORMATION:
1464 {
1465 /* The reply length is set by the guest and is found in the first byte of the command buffer. */
1466 pBusLogic->cbReplyParametersLeft = pBusLogic->aCommandBuffer[0];
1467 PReplyInquireExtendedSetupInformation pReply = (PReplyInquireExtendedSetupInformation)pBusLogic->aReplyBuffer;
1468 memset(pReply, 0, sizeof(ReplyInquireExtendedSetupInformation));
1469
1470 //@todo: should this reflect the RAM contents (AutoSCSIRam)?
1471 pReply->uBusType = 'E'; /* EISA style */
1472 pReply->u16ScatterGatherLimit = 8192;
1473 pReply->cMailbox = pBusLogic->cMailbox;
1474 pReply->uMailboxAddressBase = (uint32_t)pBusLogic->GCPhysAddrMailboxOutgoingBase;
1475 pReply->fLevelSensitiveInterrupt = true;
1476 pReply->fHostWideSCSI = true;
1477 pReply->fHostUltraSCSI = true;
1478 memcpy(pReply->aFirmwareRevision, "07B", sizeof(pReply->aFirmwareRevision));
1479
1480 break;
1481 }
1482 case BUSLOGICCOMMAND_INQUIRE_SETUP_INFORMATION:
1483 {
1484 /* The reply length is set by the guest and is found in the first byte of the command buffer. */
1485 pBusLogic->cbReplyParametersLeft = pBusLogic->aCommandBuffer[0];
1486 PReplyInquireSetupInformation pReply = (PReplyInquireSetupInformation)pBusLogic->aReplyBuffer;
1487 memset(pReply, 0, sizeof(ReplyInquireSetupInformation));
1488 break;
1489 }
1490 case BUSLOGICCOMMAND_FETCH_HOST_ADAPTER_LOCAL_RAM:
1491 {
1492 /*
1493 * First element in the command buffer contains start offset to read from
1494 * and second one the number of bytes to read.
1495 */
1496 uint8_t uOffset = pBusLogic->aCommandBuffer[0];
1497 pBusLogic->cbReplyParametersLeft = pBusLogic->aCommandBuffer[1];
1498
1499 pBusLogic->fUseLocalRam = true;
1500 pBusLogic->iReply = uOffset;
1501 break;
1502 }
1503 case BUSLOGICCOMMAND_INITIALIZE_EXTENDED_MAILBOX:
1504 {
1505 PRequestInitializeExtendedMailbox pRequest = (PRequestInitializeExtendedMailbox)pBusLogic->aCommandBuffer;
1506
1507 pBusLogic->cMailbox = pRequest->cMailbox;
1508 pBusLogic->GCPhysAddrMailboxOutgoingBase = (RTGCPHYS)pRequest->uMailboxBaseAddress;
1509 /* The area for incoming mailboxes is right after the last entry of outgoing mailboxes. */
1510 pBusLogic->GCPhysAddrMailboxIncomingBase = (RTGCPHYS)pRequest->uMailboxBaseAddress + (pBusLogic->cMailbox * sizeof(Mailbox));
1511
1512 Log(("GCPhysAddrMailboxOutgoingBase=%RGp\n", pBusLogic->GCPhysAddrMailboxOutgoingBase));
1513 Log(("GCPhysAddrMailboxIncomingBase=%RGp\n", pBusLogic->GCPhysAddrMailboxIncomingBase));
1514 Log(("cMailboxes=%u (32-bit mode)\n", pBusLogic->cMailbox));
1515 LogRel(("Initialized 32-bit mailbox, %d entries at %08x\n", pRequest->cMailbox, pRequest->uMailboxBaseAddress));
1516
1517 pBusLogic->regStatus &= ~BUSLOGIC_REGISTER_STATUS_INITIALIZATION_REQUIRED;
1518 pBusLogic->cbReplyParametersLeft = 0;
1519 break;
1520 }
1521 case BUSLOGICCOMMAND_ENABLE_STRICT_ROUND_ROBIN_MODE:
1522 {
1523 if (pBusLogic->aCommandBuffer[0] == 0)
1524 pBusLogic->fStrictRoundRobinMode = false;
1525 else if (pBusLogic->aCommandBuffer[0] == 1)
1526 pBusLogic->fStrictRoundRobinMode = true;
1527 else
1528 AssertMsgFailed(("Invalid round robin mode %d\n", pBusLogic->aCommandBuffer[0]));
1529
1530 pBusLogic->cbReplyParametersLeft = 0;
1531 break;
1532 }
1533 case BUSLOGICCOMMAND_SET_CCB_FORMAT:
1534 {
1535 if (pBusLogic->aCommandBuffer[0] == 0)
1536 pBusLogic->fExtendedLunCCBFormat = false;
1537 else if (pBusLogic->aCommandBuffer[0] == 1)
1538 pBusLogic->fExtendedLunCCBFormat = true;
1539 else
1540 AssertMsgFailed(("Invalid CCB format %d\n", pBusLogic->aCommandBuffer[0]));
1541
1542 pBusLogic->cbReplyParametersLeft = 0;
1543 break;
1544 }
1545 case BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_0_TO_7:
1546 /* This is supposed to send TEST UNIT READY to each target/LUN.
1547 * We cheat and skip that, since we already know what's attached
1548 */
1549 memset(pBusLogic->aReplyBuffer, 0, 8);
1550 for (int i = 0; i < 8; ++i)
1551 {
1552 if (pBusLogic->aDeviceStates[i].fPresent)
1553 pBusLogic->aReplyBuffer[i] = 1;
1554 }
1555 pBusLogic->aReplyBuffer[7] = 0; /* HA hardcoded at ID 7. */
1556 pBusLogic->cbReplyParametersLeft = 8;
1557 break;
1558 case BUSLOGICCOMMAND_INQUIRE_TARGET_DEVICES:
1559 {
1560 /* Each bit which is set in the 16bit wide variable means a present device. */
1561 uint16_t u16TargetsPresentMask = 0;
1562
1563 for (uint8_t i = 0; i < RT_ELEMENTS(pBusLogic->aDeviceStates); i++)
1564 {
1565 if (pBusLogic->aDeviceStates[i].fPresent)
1566 u16TargetsPresentMask |= (1 << i);
1567 }
1568 pBusLogic->aReplyBuffer[0] = (uint8_t)u16TargetsPresentMask;
1569 pBusLogic->aReplyBuffer[1] = (uint8_t)(u16TargetsPresentMask >> 8);
1570 pBusLogic->cbReplyParametersLeft = 2;
1571 break;
1572 }
1573 case BUSLOGICCOMMAND_INQUIRE_SYNCHRONOUS_PERIOD:
1574 {
1575 pBusLogic->cbReplyParametersLeft = pBusLogic->aCommandBuffer[0];
1576
1577 for (uint8_t i = 0; i < pBusLogic->cbReplyParametersLeft; i++)
1578 pBusLogic->aReplyBuffer[i] = 0; /* @todo Figure if we need something other here. It's not needed for the linux driver */
1579
1580 break;
1581 }
1582 case BUSLOGICCOMMAND_DISABLE_HOST_ADAPTER_INTERRUPT:
1583 {
1584 if (pBusLogic->aCommandBuffer[0] == 0)
1585 pBusLogic->fIRQEnabled = false;
1586 else
1587 pBusLogic->fIRQEnabled = true;
1588 /* No interrupt signaled regardless of enable/disable. */
1589 fSuppressIrq = true;
1590 break;
1591 }
1592 case BUSLOGICCOMMAND_ECHO_COMMAND_DATA:
1593 {
1594 pBusLogic->aReplyBuffer[0] = pBusLogic->aCommandBuffer[0];
1595 pBusLogic->cbReplyParametersLeft = 1;
1596 break;
1597 }
1598 case BUSLOGICCOMMAND_SET_PREEMPT_TIME_ON_BUS:
1599 {
1600 pBusLogic->cbReplyParametersLeft = 0;
1601 pBusLogic->LocalRam.structured.autoSCSIData.uBusOnDelay = pBusLogic->aCommandBuffer[0];
1602 Log(("Bus-on time: %d\n", pBusLogic->aCommandBuffer[0]));
1603 break;
1604 }
1605 case BUSLOGICCOMMAND_SET_TIME_OFF_BUS:
1606 {
1607 pBusLogic->cbReplyParametersLeft = 0;
1608 pBusLogic->LocalRam.structured.autoSCSIData.uBusOffDelay = pBusLogic->aCommandBuffer[0];
1609 Log(("Bus-off time: %d\n", pBusLogic->aCommandBuffer[0]));
1610 break;
1611 }
1612 default:
1613 AssertMsgFailed(("Invalid command %#x\n", pBusLogic->uOperationCode));
1614 case BUSLOGICCOMMAND_EXT_BIOS_INFO:
1615 case BUSLOGICCOMMAND_UNLOCK_MAILBOX:
1616 /* Commands valid for Adaptec 154xC which we don't handle since
1617 * we pretend being 154xB compatible. Just mark the command as invalid.
1618 */
1619 Log(("Command %#x not valid for this adapter\n", pBusLogic->uOperationCode));
1620 pBusLogic->cbReplyParametersLeft = 0;
1621 pBusLogic->regStatus |= BUSLOGIC_REGISTER_STATUS_COMMAND_INVALID;
1622 break;
1623 case BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND: /* Should be handled already. */
1624 AssertMsgFailed(("Invalid mailbox execute state!\n"));
1625 }
1626
1627 Log(("uOperationCode=%#x, cbReplyParametersLeft=%d\n", pBusLogic->uOperationCode, pBusLogic->cbReplyParametersLeft));
1628
1629 /* Set the data in ready bit in the status register in case the command has a reply. */
1630 if (pBusLogic->cbReplyParametersLeft)
1631 pBusLogic->regStatus |= BUSLOGIC_REGISTER_STATUS_DATA_IN_REGISTER_READY;
1632 else if (!pBusLogic->cbCommandParametersLeft)
1633 buslogicCommandComplete(pBusLogic, fSuppressIrq);
1634
1635 return rc;
1636}
1637
1638/**
1639 * Read a register from the BusLogic adapter.
1640 *
1641 * @returns VBox status code.
1642 * @param pBusLogic Pointer to the BusLogic instance data.
1643 * @param iRegister The index of the register to read.
1644 * @param pu32 Where to store the register content.
1645 */
1646static int buslogicRegisterRead(PBUSLOGIC pBusLogic, unsigned iRegister, uint32_t *pu32)
1647{
1648 int rc = VINF_SUCCESS;
1649
1650 switch (iRegister)
1651 {
1652 case BUSLOGIC_REGISTER_STATUS:
1653 {
1654 *pu32 = pBusLogic->regStatus;
1655
1656 /* If the diagnostic active bit is set, we are in a guest-initiated
1657 * hard or soft reset. If the guest reads the status register and
1658 * waits for the host adapter ready bit to be set, we terminate the
1659 * reset right away. However, guests may also expect the reset
1660 * condition to clear automatically after a period of time.
1661 */
1662 if (pBusLogic->regStatus & BUSLOGIC_REGISTER_STATUS_DIAGNOSTIC_ACTIVE)
1663 {
1664 uint64_t u64AccessTime = PDMDevHlpTMTimeVirtGetNano(pBusLogic->CTX_SUFF(pDevIns));
1665
1666 pBusLogic->regStatus &= ~BUSLOGIC_REGISTER_STATUS_DIAGNOSTIC_ACTIVE;
1667 pBusLogic->regStatus |= BUSLOGIC_REGISTER_STATUS_HOST_ADAPTER_READY;
1668 if (u64AccessTime - pBusLogic->u64ResetTime > BUSLOGIC_RESET_DURATION_NS)
1669 {
1670 /* Let the guest see the ready condition right away. */
1671 *pu32 &= ~BUSLOGIC_REGISTER_STATUS_DIAGNOSTIC_ACTIVE;
1672 *pu32 |= BUSLOGIC_REGISTER_STATUS_HOST_ADAPTER_READY;
1673 pBusLogic->u64ResetTime = 0;
1674 }
1675 }
1676 break;
1677 }
1678 case BUSLOGIC_REGISTER_DATAIN:
1679 {
1680 if (pBusLogic->fUseLocalRam)
1681 *pu32 = pBusLogic->LocalRam.u8View[pBusLogic->iReply];
1682 else
1683 *pu32 = pBusLogic->aReplyBuffer[pBusLogic->iReply];
1684
1685 /* Careful about underflow - guest can read data register even if
1686 * no data is available.
1687 */
1688 if (pBusLogic->cbReplyParametersLeft)
1689 {
1690 pBusLogic->iReply++;
1691 pBusLogic->cbReplyParametersLeft--;
1692 if (!pBusLogic->cbReplyParametersLeft)
1693 {
1694 /*
1695 * Reply finished, set command complete bit, unset data-in ready bit and
1696 * interrupt the guest if enabled.
1697 */
1698 buslogicCommandComplete(pBusLogic, false);
1699 }
1700 }
1701 LogFlowFunc(("data=%02x, iReply=%d, cbReplyParametersLeft=%u\n", *pu32,
1702 pBusLogic->iReply, pBusLogic->cbReplyParametersLeft));
1703 break;
1704 }
1705 case BUSLOGIC_REGISTER_INTERRUPT:
1706 {
1707 *pu32 = pBusLogic->regInterrupt;
1708 break;
1709 }
1710 case BUSLOGIC_REGISTER_GEOMETRY:
1711 {
1712 *pu32 = pBusLogic->regGeometry;
1713 break;
1714 }
1715 default:
1716 *pu32 = UINT32_C(0xffffffff);
1717 }
1718
1719 Log2(("%s: pu32=%p:{%.*Rhxs} iRegister=%d rc=%Rrc\n",
1720 __FUNCTION__, pu32, 1, pu32, iRegister, rc));
1721
1722 return rc;
1723}
1724
1725/**
1726 * Write a value to a register.
1727 *
1728 * @returns VBox status code.
1729 * @param pBusLogic Pointer to the BusLogic instance data.
1730 * @param iRegister The index of the register to read.
1731 * @param uVal The value to write.
1732 */
1733static int buslogicRegisterWrite(PBUSLOGIC pBusLogic, unsigned iRegister, uint8_t uVal)
1734{
1735 int rc = VINF_SUCCESS;
1736
1737 switch (iRegister)
1738 {
1739 case BUSLOGIC_REGISTER_CONTROL:
1740 {
1741 rc = PDMCritSectEnter(&pBusLogic->CritSectIntr, VINF_IOM_R3_IOPORT_WRITE);
1742 if (rc != VINF_SUCCESS)
1743 return rc;
1744
1745#ifdef LOG_ENABLED
1746 uint32_t cMailboxesReady = ASMAtomicXchgU32(&pBusLogic->cInMailboxesReady, 0);
1747 Log(("%u incoming mailboxes were ready when this interrupt was cleared\n", cMailboxesReady));
1748#endif
1749
1750 if (uVal & BUSLOGIC_REGISTER_CONTROL_INTERRUPT_RESET)
1751 buslogicClearInterrupt(pBusLogic);
1752
1753 PDMCritSectLeave(&pBusLogic->CritSectIntr);
1754
1755 if ((uVal & BUSLOGIC_REGISTER_CONTROL_HARD_RESET) || (uVal & BUSLOGIC_REGISTER_CONTROL_SOFT_RESET))
1756 {
1757#ifdef IN_RING3
1758 buslogicIntiateHardReset(pBusLogic);
1759#else
1760 rc = VINF_IOM_R3_IOPORT_WRITE;
1761#endif
1762 }
1763
1764 break;
1765 }
1766 case BUSLOGIC_REGISTER_COMMAND:
1767 {
1768 /* Fast path for mailbox execution command. */
1769 if ((uVal == BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND) && (pBusLogic->uOperationCode == 0xff))
1770 {
1771 ASMAtomicIncU32(&pBusLogic->cMailboxesReady);
1772 if (!ASMAtomicXchgBool(&pBusLogic->fNotificationSend, true))
1773 {
1774 /* Send new notification to the queue. */
1775 PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(pBusLogic->CTX_SUFF(pNotifierQueue));
1776 AssertMsg(pItem, ("Allocating item for queue failed\n"));
1777 PDMQueueInsert(pBusLogic->CTX_SUFF(pNotifierQueue), (PPDMQUEUEITEMCORE)pItem);
1778 }
1779
1780 return rc;
1781 }
1782
1783 /*
1784 * Check if we are already fetch command parameters from the guest.
1785 * If not we initialize executing a new command.
1786 */
1787 if (pBusLogic->uOperationCode == 0xff)
1788 {
1789 pBusLogic->uOperationCode = uVal;
1790 pBusLogic->iParameter = 0;
1791
1792 /* Mark host adapter as busy and clear the invalid status bit. */
1793 pBusLogic->regStatus &= ~(BUSLOGIC_REGISTER_STATUS_HOST_ADAPTER_READY | BUSLOGIC_REGISTER_STATUS_COMMAND_INVALID);
1794
1795 /* Get the number of bytes for parameters from the command code. */
1796 switch (pBusLogic->uOperationCode)
1797 {
1798 case BUSLOGICCOMMAND_TEST_CMDC_INTERRUPT:
1799 case BUSLOGICCOMMAND_INQUIRE_FIRMWARE_VERSION_LETTER:
1800 case BUSLOGICCOMMAND_INQUIRE_BOARD_ID:
1801 case BUSLOGICCOMMAND_INQUIRE_FIRMWARE_VERSION_3RD_LETTER:
1802 case BUSLOGICCOMMAND_INQUIRE_PCI_HOST_ADAPTER_INFORMATION:
1803 case BUSLOGICCOMMAND_INQUIRE_CONFIGURATION:
1804 case BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_0_TO_7:
1805 case BUSLOGICCOMMAND_INQUIRE_TARGET_DEVICES:
1806 pBusLogic->cbCommandParametersLeft = 0;
1807 break;
1808 case BUSLOGICCOMMAND_MODIFY_IO_ADDRESS:
1809 case BUSLOGICCOMMAND_INQUIRE_EXTENDED_SETUP_INFORMATION:
1810 case BUSLOGICCOMMAND_INQUIRE_SETUP_INFORMATION:
1811 case BUSLOGICCOMMAND_INQUIRE_HOST_ADAPTER_MODEL_NUMBER:
1812 case BUSLOGICCOMMAND_ENABLE_STRICT_ROUND_ROBIN_MODE:
1813 case BUSLOGICCOMMAND_SET_CCB_FORMAT:
1814 case BUSLOGICCOMMAND_INQUIRE_SYNCHRONOUS_PERIOD:
1815 case BUSLOGICCOMMAND_DISABLE_HOST_ADAPTER_INTERRUPT:
1816 case BUSLOGICCOMMAND_ECHO_COMMAND_DATA:
1817 case BUSLOGICCOMMAND_SET_PREEMPT_TIME_ON_BUS:
1818 case BUSLOGICCOMMAND_SET_TIME_OFF_BUS:
1819 pBusLogic->cbCommandParametersLeft = 1;
1820 break;
1821 case BUSLOGICCOMMAND_FETCH_HOST_ADAPTER_LOCAL_RAM:
1822 pBusLogic->cbCommandParametersLeft = 2;
1823 break;
1824 case BUSLOGICCOMMAND_INITIALIZE_EXTENDED_MAILBOX:
1825 pBusLogic->cbCommandParametersLeft = sizeof(RequestInitializeExtendedMailbox);
1826 break;
1827 case BUSLOGICCOMMAND_SET_ADAPTER_OPTIONS:
1828 /* There must be at least one byte following this command. */
1829 pBusLogic->cbCommandParametersLeft = 1;
1830 break;
1831 case BUSLOGICCOMMAND_EXT_BIOS_INFO:
1832 case BUSLOGICCOMMAND_UNLOCK_MAILBOX:
1833 /* Invalid commands. */
1834 pBusLogic->cbCommandParametersLeft = 0;
1835 break;
1836 case BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND: /* Should not come here anymore. */
1837 default:
1838 AssertMsgFailed(("Invalid operation code %#x\n", uVal));
1839 }
1840 }
1841 else
1842 {
1843 /*
1844 * The real adapter would set the Command register busy bit in the status register.
1845 * The guest has to wait until it is unset.
1846 * We don't need to do it because the guest does not continue execution while we are in this
1847 * function.
1848 */
1849 pBusLogic->aCommandBuffer[pBusLogic->iParameter] = uVal;
1850 pBusLogic->iParameter++;
1851 pBusLogic->cbCommandParametersLeft--;
1852 }
1853
1854 /* Start execution of command if there are no parameters left. */
1855 if (!pBusLogic->cbCommandParametersLeft)
1856 {
1857 rc = buslogicProcessCommand(pBusLogic);
1858 AssertMsgRC(rc, ("Processing command failed rc=%Rrc\n", rc));
1859 }
1860 break;
1861 }
1862
1863 /* On BusLogic adapters, the interrupt and geometry registers are R/W.
1864 * That is different from Adaptec 154x where those are read only.
1865 */
1866 case BUSLOGIC_REGISTER_INTERRUPT:
1867 pBusLogic->regInterrupt = uVal;
1868 break;
1869
1870 case BUSLOGIC_REGISTER_GEOMETRY:
1871 pBusLogic->regGeometry = uVal;
1872 break;
1873
1874 default:
1875 AssertMsgFailed(("Register not available\n"));
1876 rc = VERR_IOM_IOPORT_UNUSED;
1877 }
1878
1879 return rc;
1880}
1881
1882/**
1883 * Memory mapped I/O Handler for read operations.
1884 *
1885 * @returns VBox status code.
1886 *
1887 * @param pDevIns The device instance.
1888 * @param pvUser User argument.
1889 * @param GCPhysAddr Physical address (in GC) where the read starts.
1890 * @param pv Where to store the result.
1891 * @param cb Number of bytes read.
1892 */
1893PDMBOTHCBDECL(int) buslogicMMIORead(PPDMDEVINS pDevIns, void *pvUser,
1894 RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
1895{
1896 /* the linux driver does not make use of the MMIO area. */
1897 AssertMsgFailed(("MMIO Read\n"));
1898 return VINF_SUCCESS;
1899}
1900
1901/**
1902 * Memory mapped I/O Handler for write operations.
1903 *
1904 * @returns VBox status code.
1905 *
1906 * @param pDevIns The device instance.
1907 * @param pvUser User argument.
1908 * @param GCPhysAddr Physical address (in GC) where the read starts.
1909 * @param pv Where to fetch the result.
1910 * @param cb Number of bytes to write.
1911 */
1912PDMBOTHCBDECL(int) buslogicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser,
1913 RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
1914{
1915 /* the linux driver does not make use of the MMIO area. */
1916 AssertMsgFailed(("MMIO Write\n"));
1917 return VINF_SUCCESS;
1918}
1919
1920/**
1921 * Port I/O Handler for IN operations.
1922 *
1923 * @returns VBox status code.
1924 *
1925 * @param pDevIns The device instance.
1926 * @param pvUser User argument.
1927 * @param uPort Port number used for the IN operation.
1928 * @param pu32 Where to store the result.
1929 * @param cb Number of bytes read.
1930 */
1931PDMBOTHCBDECL(int) buslogicIOPortRead (PPDMDEVINS pDevIns, void *pvUser,
1932 RTIOPORT Port, uint32_t *pu32, unsigned cb)
1933{
1934 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);;
1935 unsigned iRegister = Port % 4;
1936
1937 Assert(cb == 1);
1938
1939 return buslogicRegisterRead(pBusLogic, iRegister, pu32);
1940}
1941
1942/**
1943 * Port I/O Handler for OUT operations.
1944 *
1945 * @returns VBox status code.
1946 *
1947 * @param pDevIns The device instance.
1948 * @param pvUser User argument.
1949 * @param uPort Port number used for the IN operation.
1950 * @param u32 The value to output.
1951 * @param cb The value size in bytes.
1952 */
1953PDMBOTHCBDECL(int) buslogicIOPortWrite (PPDMDEVINS pDevIns, void *pvUser,
1954 RTIOPORT Port, uint32_t u32, unsigned cb)
1955{
1956 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
1957 int rc = VINF_SUCCESS;
1958 unsigned iRegister = Port % 4;
1959 uint8_t uVal = (uint8_t)u32;
1960
1961 Assert(cb == 1);
1962
1963 rc = buslogicRegisterWrite(pBusLogic, iRegister, (uint8_t)uVal);
1964
1965 Log2(("#%d %s: pvUser=%#p cb=%d u32=%#x Port=%#x rc=%Rrc\n",
1966 pDevIns->iInstance, __FUNCTION__, pvUser, cb, u32, Port, rc));
1967
1968 return rc;
1969}
1970
1971#ifdef IN_RING3
1972/**
1973 * Port I/O Handler for IN operations - legacy port.
1974 *
1975 * @returns VBox status code.
1976 *
1977 * @param pDevIns The device instance.
1978 * @param pvUser User argument.
1979 * @param uPort Port number used for the IN operation.
1980 * @param pu32 Where to store the result.
1981 * @param cb Number of bytes read.
1982 */
1983static int buslogicBIOSIOPortRead (PPDMDEVINS pDevIns, void *pvUser,
1984 RTIOPORT Port, uint32_t *pu32, unsigned cb)
1985{
1986 int rc;
1987 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
1988
1989 Assert(cb == 1);
1990
1991 if (!pBusLogic->fISAEnabled)
1992 return VINF_SUCCESS;
1993
1994 rc = vboxscsiReadRegister(&pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT), pu32);
1995
1996 //Log2(("%s: pu32=%p:{%.*Rhxs} iRegister=%d rc=%Rrc\n",
1997 // __FUNCTION__, pu32, 1, pu32, (Port - BUSLOGIC_BIOS_IO_PORT), rc));
1998
1999 return rc;
2000}
2001
2002static void buslogicWarningDiskFull(PPDMDEVINS pDevIns)
2003{
2004 int rc;
2005 LogRel(("BusLogic#%d: Host disk full\n", pDevIns->iInstance));
2006 rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_DISKFULL",
2007 N_("Host system reported disk full. VM execution is suspended. You can resume after freeing some space"));
2008 AssertRC(rc);
2009}
2010
2011static void buslogicWarningFileTooBig(PPDMDEVINS pDevIns)
2012{
2013 int rc;
2014 LogRel(("BusLogic#%d: File too big\n", pDevIns->iInstance));
2015 rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_FILETOOBIG",
2016 N_("Host system reported that the file size limit of the host file system has been exceeded. VM execution is suspended. You need to move your virtual hard disk to a filesystem which allows bigger files"));
2017 AssertRC(rc);
2018}
2019
2020static void buslogicWarningISCSI(PPDMDEVINS pDevIns)
2021{
2022 int rc;
2023 LogRel(("BusLogic#%d: iSCSI target unavailable\n", pDevIns->iInstance));
2024 rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_ISCSIDOWN",
2025 N_("The iSCSI target has stopped responding. VM execution is suspended. You can resume when it is available again"));
2026 AssertRC(rc);
2027}
2028
2029static void buslogicWarningUnknown(PPDMDEVINS pDevIns, int rc)
2030{
2031 int rc2;
2032 LogRel(("BusLogic#%d: Unknown but recoverable error has occurred (rc=%Rrc)\n", pDevIns->iInstance, rc));
2033 rc2 = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_UNKNOWN",
2034 N_("An unknown but recoverable I/O error has occurred (rc=%Rrc). VM execution is suspended. You can resume when the error is fixed"), rc);
2035 AssertRC(rc2);
2036}
2037
2038static void buslogicRedoSetWarning(PBUSLOGIC pThis, int rc)
2039{
2040 if (rc == VERR_DISK_FULL)
2041 buslogicWarningDiskFull(pThis->CTX_SUFF(pDevIns));
2042 else if (rc == VERR_FILE_TOO_BIG)
2043 buslogicWarningFileTooBig(pThis->CTX_SUFF(pDevIns));
2044 else if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
2045 {
2046 /* iSCSI connection abort (first error) or failure to reestablish
2047 * connection (second error). Pause VM. On resume we'll retry. */
2048 buslogicWarningISCSI(pThis->CTX_SUFF(pDevIns));
2049 }
2050 else
2051 buslogicWarningUnknown(pThis->CTX_SUFF(pDevIns), rc);
2052}
2053
2054
2055static int buslogicPrepareBIOSSCSIRequest(PBUSLOGIC pBusLogic)
2056{
2057 int rc;
2058 PBUSLOGICTASKSTATE pTaskState;
2059 uint32_t uTargetDevice;
2060
2061 rc = RTMemCacheAllocEx(pBusLogic->hTaskCache, (void **)&pTaskState);
2062 AssertMsgRCReturn(rc, ("Getting task from cache failed rc=%Rrc\n", rc), rc);
2063
2064 pTaskState->fBIOS = true;
2065
2066 rc = vboxscsiSetupRequest(&pBusLogic->VBoxSCSI, &pTaskState->PDMScsiRequest, &uTargetDevice);
2067 AssertMsgRCReturn(rc, ("Setting up SCSI request failed rc=%Rrc\n", rc), rc);
2068
2069 pTaskState->PDMScsiRequest.pvUser = pTaskState;
2070
2071 pTaskState->CTX_SUFF(pTargetDevice) = &pBusLogic->aDeviceStates[uTargetDevice];
2072
2073 if (!pTaskState->CTX_SUFF(pTargetDevice)->fPresent)
2074 {
2075 /* Device is not present. */
2076 AssertMsg(pTaskState->PDMScsiRequest.pbCDB[0] == SCSI_INQUIRY,
2077 ("Device is not present but command is not inquiry\n"));
2078
2079 SCSIINQUIRYDATA ScsiInquiryData;
2080
2081 memset(&ScsiInquiryData, 0, sizeof(SCSIINQUIRYDATA));
2082 ScsiInquiryData.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN;
2083 ScsiInquiryData.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED;
2084
2085 memcpy(pBusLogic->VBoxSCSI.pBuf, &ScsiInquiryData, 5);
2086
2087 rc = vboxscsiRequestFinished(&pBusLogic->VBoxSCSI, &pTaskState->PDMScsiRequest);
2088 AssertMsgRCReturn(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc), rc);
2089
2090 RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
2091 }
2092 else
2093 {
2094 LogFlowFunc(("before increment %u\n", pTaskState->CTX_SUFF(pTargetDevice)->cOutstandingRequests));
2095 ASMAtomicIncU32(&pTaskState->CTX_SUFF(pTargetDevice)->cOutstandingRequests);
2096 LogFlowFunc(("after increment %u\n", pTaskState->CTX_SUFF(pTargetDevice)->cOutstandingRequests));
2097
2098 rc = pTaskState->CTX_SUFF(pTargetDevice)->pDrvSCSIConnector->pfnSCSIRequestSend(pTaskState->CTX_SUFF(pTargetDevice)->pDrvSCSIConnector,
2099 &pTaskState->PDMScsiRequest);
2100 AssertMsgRC(rc, ("Sending request to SCSI layer failed rc=%Rrc\n", rc));
2101 }
2102
2103 return rc;
2104}
2105
2106/**
2107 * Port I/O Handler for OUT operations - legacy port.
2108 *
2109 * @returns VBox status code.
2110 *
2111 * @param pDevIns The device instance.
2112 * @param pvUser User argument.
2113 * @param uPort Port number used for the IN operation.
2114 * @param u32 The value to output.
2115 * @param cb The value size in bytes.
2116 */
2117static int buslogicBIOSIOPortWrite (PPDMDEVINS pDevIns, void *pvUser,
2118 RTIOPORT Port, uint32_t u32, unsigned cb)
2119{
2120 int rc;
2121 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2122
2123 Log2(("#%d %s: pvUser=%#p cb=%d u32=%#x Port=%#x\n",
2124 pDevIns->iInstance, __FUNCTION__, pvUser, cb, u32, Port));
2125
2126 Assert(cb == 1);
2127
2128 if (!pBusLogic->fISAEnabled)
2129 return VINF_SUCCESS;
2130
2131 rc = vboxscsiWriteRegister(&pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT), (uint8_t)u32);
2132 if (rc == VERR_MORE_DATA)
2133 {
2134 rc = buslogicPrepareBIOSSCSIRequest(pBusLogic);
2135 AssertRC(rc);
2136 }
2137 else if (RT_FAILURE(rc))
2138 AssertMsgFailed(("Writing BIOS register failed %Rrc\n", rc));
2139
2140 return VINF_SUCCESS;
2141}
2142
2143/**
2144 * Port I/O Handler for primary port range OUT string operations.
2145 * @see FNIOMIOPORTOUTSTRING for details.
2146 */
2147static DECLCALLBACK(int) buslogicBIOSIOPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb)
2148{
2149 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2150 int rc;
2151
2152 Log2(("#%d %s: pvUser=%#p cb=%d Port=%#x\n",
2153 pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port));
2154
2155 rc = vboxscsiWriteString(pDevIns, &pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT),
2156 pGCPtrSrc, pcTransfer, cb);
2157 if (rc == VERR_MORE_DATA)
2158 {
2159 rc = buslogicPrepareBIOSSCSIRequest(pBusLogic);
2160 AssertRC(rc);
2161 }
2162 else if (RT_FAILURE(rc))
2163 AssertMsgFailed(("Writing BIOS register failed %Rrc\n", rc));
2164
2165 return rc;
2166}
2167
2168/**
2169 * Port I/O Handler for primary port range IN string operations.
2170 * @see FNIOMIOPORTINSTRING for details.
2171 */
2172static DECLCALLBACK(int) buslogicBIOSIOPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb)
2173{
2174 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2175
2176 LogFlowFunc(("#%d %s: pvUser=%#p cb=%d Port=%#x\n",
2177 pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port));
2178
2179 return vboxscsiReadString(pDevIns, &pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT),
2180 pGCPtrDst, pcTransfer, cb);
2181}
2182
2183static DECLCALLBACK(int) buslogicMMIOMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion,
2184 RTGCPHYS GCPhysAddress, uint32_t cb,
2185 PCIADDRESSSPACE enmType)
2186{
2187 PPDMDEVINS pDevIns = pPciDev->pDevIns;
2188 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2189 int rc = VINF_SUCCESS;
2190
2191 Log2(("%s: registering MMIO area at GCPhysAddr=%RGp cb=%u\n", __FUNCTION__, GCPhysAddress, cb));
2192
2193 Assert(cb >= 32);
2194
2195 if (enmType == PCI_ADDRESS_SPACE_MEM)
2196 {
2197 /* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
2198 rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/,
2199 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
2200 buslogicMMIOWrite, buslogicMMIORead, "BusLogic MMIO");
2201 if (RT_FAILURE(rc))
2202 return rc;
2203
2204 if (pThis->fR0Enabled)
2205 {
2206 rc = PDMDevHlpMMIORegisterR0(pDevIns, GCPhysAddress, cb, NIL_RTR0PTR /*pvUser*/,
2207 "buslogicMMIOWrite", "buslogicMMIORead");
2208 if (RT_FAILURE(rc))
2209 return rc;
2210 }
2211
2212 if (pThis->fGCEnabled)
2213 {
2214 rc = PDMDevHlpMMIORegisterRC(pDevIns, GCPhysAddress, cb, NIL_RTRCPTR /*pvUser*/,
2215 "buslogicMMIOWrite", "buslogicMMIORead");
2216 if (RT_FAILURE(rc))
2217 return rc;
2218 }
2219
2220 pThis->MMIOBase = GCPhysAddress;
2221 }
2222 else if (enmType == PCI_ADDRESS_SPACE_IO)
2223 {
2224 rc = PDMDevHlpIOPortRegister(pDevIns, (RTIOPORT)GCPhysAddress, 32,
2225 NULL, buslogicIOPortWrite, buslogicIOPortRead, NULL, NULL, "BusLogic PCI");
2226 if (RT_FAILURE(rc))
2227 return rc;
2228
2229 if (pThis->fR0Enabled)
2230 {
2231 rc = PDMDevHlpIOPortRegisterR0(pDevIns, (RTIOPORT)GCPhysAddress, 32,
2232 0, "buslogicIOPortWrite", "buslogicIOPortRead", NULL, NULL, "BusLogic PCI");
2233 if (RT_FAILURE(rc))
2234 return rc;
2235 }
2236
2237 if (pThis->fGCEnabled)
2238 {
2239 rc = PDMDevHlpIOPortRegisterRC(pDevIns, (RTIOPORT)GCPhysAddress, 32,
2240 0, "buslogicIOPortWrite", "buslogicIOPortRead", NULL, NULL, "BusLogic PCI");
2241 if (RT_FAILURE(rc))
2242 return rc;
2243 }
2244
2245 pThis->IOPortBase = (RTIOPORT)GCPhysAddress;
2246 }
2247 else
2248 AssertMsgFailed(("Invalid enmType=%d\n", enmType));
2249
2250 return rc;
2251}
2252
2253static DECLCALLBACK(int) buslogicDeviceSCSIRequestCompleted(PPDMISCSIPORT pInterface, PPDMSCSIREQUEST pSCSIRequest,
2254 int rcCompletion, bool fRedo, int rcReq)
2255{
2256 int rc;
2257 PBUSLOGICTASKSTATE pTaskState = (PBUSLOGICTASKSTATE)pSCSIRequest->pvUser;
2258 PBUSLOGICDEVICE pBusLogicDevice = pTaskState->CTX_SUFF(pTargetDevice);
2259 PBUSLOGIC pBusLogic = pBusLogicDevice->CTX_SUFF(pBusLogic);
2260
2261 LogFlowFunc(("before decrement %u\n", pBusLogicDevice->cOutstandingRequests));
2262 ASMAtomicDecU32(&pBusLogicDevice->cOutstandingRequests);
2263 LogFlowFunc(("after decrement %u\n", pBusLogicDevice->cOutstandingRequests));
2264
2265 if (fRedo)
2266 {
2267 if (!pTaskState->fBIOS)
2268 {
2269 buslogicDataBufferFree(pTaskState);
2270
2271 if (pTaskState->pbSenseBuffer)
2272 buslogicSenseBufferFree(pTaskState, false /* fCopy */);
2273 }
2274
2275 /* Add to the list. */
2276 do
2277 {
2278 pTaskState->pRedoNext = ASMAtomicReadPtrT(&pBusLogic->pTasksRedoHead, PBUSLOGICTASKSTATE);
2279 } while (!ASMAtomicCmpXchgPtr(&pBusLogic->pTasksRedoHead, pTaskState, pTaskState->pRedoNext));
2280
2281 /* Suspend the VM if not done already. */
2282 if (!ASMAtomicXchgBool(&pBusLogic->fRedo, true))
2283 buslogicRedoSetWarning(pBusLogic, rcReq);
2284 }
2285 else
2286 {
2287 if (pTaskState->fBIOS)
2288 {
2289 rc = vboxscsiRequestFinished(&pBusLogic->VBoxSCSI, pSCSIRequest);
2290 AssertMsgRC(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc));
2291 }
2292 else
2293 {
2294 buslogicDataBufferFree(pTaskState);
2295
2296 if (pTaskState->pbSenseBuffer)
2297 buslogicSenseBufferFree(pTaskState, (rcCompletion != SCSI_STATUS_OK));
2298
2299 if (rcCompletion == SCSI_STATUS_OK)
2300 buslogicSendIncomingMailbox(pBusLogic, pTaskState,
2301 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_CMD_COMPLETED,
2302 BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_OPERATION_GOOD,
2303 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_WITHOUT_ERROR);
2304 else if (rcCompletion == SCSI_STATUS_CHECK_CONDITION)
2305 buslogicSendIncomingMailbox(pBusLogic, pTaskState,
2306 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_CMD_COMPLETED,
2307 BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_CHECK_CONDITION,
2308 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_WITH_ERROR);
2309 else
2310 AssertMsgFailed(("invalid completion status %d\n", rcCompletion));
2311 }
2312 /* Remove task from the cache. */
2313 RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
2314 }
2315
2316 if (pBusLogicDevice->cOutstandingRequests == 0 && pBusLogic->fSignalIdle)
2317 PDMDevHlpAsyncNotificationCompleted(pBusLogic->pDevInsR3);
2318
2319 return VINF_SUCCESS;
2320}
2321
2322static DECLCALLBACK(int) buslogicQueryDeviceLocation(PPDMISCSIPORT pInterface, const char **ppcszController,
2323 uint32_t *piInstance, uint32_t *piLUN)
2324{
2325 PBUSLOGICDEVICE pBusLogicDevice = PDMISCSIPORT_2_PBUSLOGICDEVICE(pInterface);
2326 PPDMDEVINS pDevIns = pBusLogicDevice->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
2327
2328 AssertPtrReturn(ppcszController, VERR_INVALID_POINTER);
2329 AssertPtrReturn(piInstance, VERR_INVALID_POINTER);
2330 AssertPtrReturn(piLUN, VERR_INVALID_POINTER);
2331
2332 *ppcszController = pDevIns->pReg->szName;
2333 *piInstance = pDevIns->iInstance;
2334 *piLUN = pBusLogicDevice->iLUN;
2335
2336 return VINF_SUCCESS;
2337}
2338
2339static int buslogicDeviceSCSIRequestSetup(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState)
2340{
2341 int rc = VINF_SUCCESS;
2342
2343 /* Fetch the CCB from guest memory. */
2344 RTGCPHYS GCPhysAddrCCB = (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB;
2345 PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrCCB,
2346 &pTaskState->CommandControlBlockGuest, sizeof(CommandControlBlock));
2347
2348 PBUSLOGICDEVICE pTargetDevice = &pBusLogic->aDeviceStates[pTaskState->CommandControlBlockGuest.uTargetId];
2349 pTaskState->CTX_SUFF(pTargetDevice) = pTargetDevice;
2350
2351#ifdef DEBUG
2352 buslogicDumpCCBInfo(&pTaskState->CommandControlBlockGuest);
2353#endif
2354
2355 /* Alloc required buffers. */
2356 rc = buslogicDataBufferAlloc(pTaskState);
2357 AssertMsgRC(rc, ("Alloc failed rc=%Rrc\n", rc));
2358
2359 if (pTaskState->CommandControlBlockGuest.cbSenseData)
2360 {
2361 rc = buslogicSenseBufferAlloc(pTaskState);
2362 AssertMsgRC(rc, ("Mapping sense buffer failed rc=%Rrc\n", rc));
2363 }
2364
2365 /* Check if device is present on bus. If not return error immediately and don't process this further. */
2366 if (!pBusLogic->aDeviceStates[pTaskState->CommandControlBlockGuest.uTargetId].fPresent)
2367 {
2368 buslogicDataBufferFree(pTaskState);
2369
2370 if (pTaskState->pbSenseBuffer)
2371 buslogicSenseBufferFree(pTaskState, true);
2372
2373 buslogicSendIncomingMailbox(pBusLogic, pTaskState,
2374 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_SCSI_SELECTION_TIMEOUT,
2375 BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_OPERATION_GOOD,
2376 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_WITH_ERROR);
2377
2378 RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
2379 }
2380 else
2381 {
2382 /* Setup SCSI request. */
2383 pTaskState->PDMScsiRequest.uLogicalUnit = pTaskState->CommandControlBlockGuest.uLogicalUnit;
2384
2385 if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_UNKNOWN)
2386 pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_UNKNOWN;
2387 else if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_IN)
2388 pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_FROM_DEVICE;
2389 else if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_OUT)
2390 pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_TO_DEVICE;
2391 else if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_NO_DATA)
2392 pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_NONE;
2393 else
2394 AssertMsgFailed(("Invalid data direction type %d\n", pTaskState->CommandControlBlockGuest.uDataDirection));
2395
2396 pTaskState->PDMScsiRequest.cbCDB = pTaskState->CommandControlBlockGuest.cbCDB;
2397 pTaskState->PDMScsiRequest.pbCDB = pTaskState->CommandControlBlockGuest.aCDB;
2398 if (pTaskState->DataSeg.cbSeg)
2399 {
2400 pTaskState->PDMScsiRequest.cbScatterGather = pTaskState->DataSeg.cbSeg;
2401 pTaskState->PDMScsiRequest.cScatterGatherEntries = 1;
2402 pTaskState->PDMScsiRequest.paScatterGatherHead = &pTaskState->DataSeg;
2403 }
2404 else
2405 {
2406 pTaskState->PDMScsiRequest.cbScatterGather = 0;
2407 pTaskState->PDMScsiRequest.cScatterGatherEntries = 0;
2408 pTaskState->PDMScsiRequest.paScatterGatherHead = NULL;
2409 }
2410 pTaskState->PDMScsiRequest.cbSenseBuffer = pTaskState->CommandControlBlockGuest.cbSenseData;
2411 pTaskState->PDMScsiRequest.pbSenseBuffer = pTaskState->pbSenseBuffer;
2412 pTaskState->PDMScsiRequest.pvUser = pTaskState;
2413
2414 ASMAtomicIncU32(&pTargetDevice->cOutstandingRequests);
2415 rc = pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pTargetDevice->pDrvSCSIConnector, &pTaskState->PDMScsiRequest);
2416 AssertMsgRC(rc, ("Sending request to SCSI layer failed rc=%Rrc\n", rc));
2417 }
2418
2419 return rc;
2420}
2421
2422/**
2423 * Read mailbox from the guest and execute command.
2424 *
2425 * @returns VBox status code.
2426 * @param pBusLogic Pointer to the BusLogic instance data.
2427 */
2428static int buslogicProcessMailboxNext(PBUSLOGIC pBusLogic)
2429{
2430 PBUSLOGICTASKSTATE pTaskState = NULL;
2431 RTGCPHYS GCPhysAddrMailboxCurrent;
2432 int rc;
2433
2434 rc = RTMemCacheAllocEx(pBusLogic->hTaskCache, (void **)&pTaskState);
2435 AssertMsgReturn(RT_SUCCESS(rc) && (pTaskState != NULL), ("Failed to get task state from cache\n"), rc);
2436
2437 pTaskState->fBIOS = false;
2438
2439 if (!pBusLogic->fStrictRoundRobinMode)
2440 {
2441 /* Search for a filled mailbox - stop if we have scanned all mailboxes. */
2442 uint8_t uMailboxPosCur = pBusLogic->uMailboxOutgoingPositionCurrent;
2443
2444 do
2445 {
2446 /* Fetch mailbox from guest memory. */
2447 GCPhysAddrMailboxCurrent = buslogicOutgoingMailboxGetGCPhys(pBusLogic);
2448
2449 PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxCurrent,
2450 &pTaskState->MailboxGuest, sizeof(Mailbox));
2451
2452 /* Check the next mailbox. */
2453 buslogicOutgoingMailboxAdvance(pBusLogic);
2454 } while ( pTaskState->MailboxGuest.u.out.uActionCode == BUSLOGIC_MAILBOX_OUTGOING_ACTION_FREE
2455 && uMailboxPosCur != pBusLogic->uMailboxOutgoingPositionCurrent);
2456 }
2457 else
2458 {
2459 /* Fetch mailbox from guest memory. */
2460 GCPhysAddrMailboxCurrent = buslogicOutgoingMailboxGetGCPhys(pBusLogic);
2461
2462 PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxCurrent,
2463 &pTaskState->MailboxGuest, sizeof(Mailbox));
2464 }
2465
2466 /*
2467 * Check if the mailbox is actually loaded.
2468 * It might be possible that the guest notified us without
2469 * a loaded mailbox. Do nothing in that case but leave a
2470 * log entry.
2471 */
2472 if (pTaskState->MailboxGuest.u.out.uActionCode == BUSLOGIC_MAILBOX_OUTGOING_ACTION_FREE)
2473 {
2474 Log(("No loaded mailbox left\n"));
2475 RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
2476 return VERR_NO_DATA;
2477 }
2478
2479 LogFlow(("Got loaded mailbox at slot %u, CCB phys %RGp\n", pBusLogic->uMailboxOutgoingPositionCurrent, (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB));
2480#ifdef DEBUG
2481 buslogicDumpMailboxInfo(&pTaskState->MailboxGuest, true);
2482#endif
2483
2484 /* We got the mailbox, mark it as free in the guest. */
2485 uint8_t uActionCode = BUSLOGIC_MAILBOX_OUTGOING_ACTION_FREE;
2486 PDMDevHlpPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxCurrent + RT_OFFSETOF(Mailbox, u.out.uActionCode), &uActionCode, sizeof(uActionCode));
2487
2488 if (pTaskState->MailboxGuest.u.out.uActionCode == BUSLOGIC_MAILBOX_OUTGOING_ACTION_START_COMMAND)
2489 rc = buslogicDeviceSCSIRequestSetup(pBusLogic, pTaskState);
2490 else if (pTaskState->MailboxGuest.u.out.uActionCode == BUSLOGIC_MAILBOX_OUTGOING_ACTION_ABORT_COMMAND)
2491 {
2492 AssertMsgFailed(("Not implemented yet\n"));
2493 }
2494 else
2495 AssertMsgFailed(("Invalid outgoing mailbox action code %u\n", pTaskState->MailboxGuest.u.out.uActionCode));
2496
2497 AssertRC(rc);
2498
2499 /* Advance to the next mailbox. */
2500 if (pBusLogic->fStrictRoundRobinMode)
2501 buslogicOutgoingMailboxAdvance(pBusLogic);
2502
2503 return rc;
2504}
2505
2506/**
2507 * Transmit queue consumer
2508 * Queue a new async task.
2509 *
2510 * @returns Success indicator.
2511 * If false the item will not be removed and the flushing will stop.
2512 * @param pDevIns The device instance.
2513 * @param pItem The item to consume. Upon return this item will be freed.
2514 */
2515static DECLCALLBACK(bool) buslogicNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
2516{
2517 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2518
2519 /* Reset notification send flag now. */
2520 Assert(pBusLogic->fNotificationSend);
2521 ASMAtomicXchgBool(&pBusLogic->fNotificationSend, false);
2522 ASMAtomicXchgU32(&pBusLogic->cMailboxesReady, 0); /* @todo: Actually not required anymore but to stay compatible with older saved states. */
2523
2524 /* Process mailboxes. */
2525 int rc;
2526 do
2527 {
2528 rc = buslogicProcessMailboxNext(pBusLogic);
2529 AssertMsg(RT_SUCCESS(rc) || rc == VERR_NO_DATA, ("Processing mailbox failed rc=%Rrc\n", rc));
2530 } while (RT_SUCCESS(rc));
2531
2532 return true;
2533}
2534
2535/**
2536 * Kicks the controller to process pending tasks after the VM was resumed
2537 * or loaded from a saved state.
2538 *
2539 * @returns nothing.
2540 * @param pThis The BusLogic device instance.
2541 */
2542static void buslogicKick(PBUSLOGIC pThis)
2543{
2544 if (pThis->fRedo)
2545 {
2546 pThis->fRedo = false;
2547 if (pThis->VBoxSCSI.fBusy)
2548 {
2549
2550 /* The BIOS had a request active when we got suspended. Resume it. */
2551 int rc = buslogicPrepareBIOSSCSIRequest(pThis);
2552 AssertRC(rc);
2553 }
2554 else
2555 {
2556 /* Queue all pending tasks again. */
2557 PBUSLOGICTASKSTATE pTaskState = pThis->pTasksRedoHead;
2558
2559 pThis->pTasksRedoHead = NULL;
2560
2561 while (pTaskState)
2562 {
2563 PBUSLOGICTASKSTATE pCur = pTaskState;
2564
2565 int rc = buslogicDeviceSCSIRequestSetup(pThis, pCur);
2566 AssertRC(rc);
2567
2568 pTaskState = pTaskState->pRedoNext;
2569 }
2570 }
2571 }
2572}
2573
2574static DECLCALLBACK(int) buslogicLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
2575{
2576 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2577
2578 /* Save the device config. */
2579 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aDeviceStates); i++)
2580 SSMR3PutBool(pSSM, pThis->aDeviceStates[i].fPresent);
2581
2582 return VINF_SSM_DONT_CALL_AGAIN;
2583}
2584
2585static DECLCALLBACK(int) buslogicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
2586{
2587 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2588
2589 /* Every device first. */
2590 for (unsigned i = 0; i < RT_ELEMENTS(pBusLogic->aDeviceStates); i++)
2591 {
2592 PBUSLOGICDEVICE pDevice = &pBusLogic->aDeviceStates[i];
2593
2594 AssertMsg(!pDevice->cOutstandingRequests,
2595 ("There are still outstanding requests on this device\n"));
2596 SSMR3PutBool(pSSM, pDevice->fPresent);
2597 SSMR3PutU32(pSSM, pDevice->cOutstandingRequests);
2598 }
2599 /* Now the main device state. */
2600 SSMR3PutU8 (pSSM, pBusLogic->regStatus);
2601 SSMR3PutU8 (pSSM, pBusLogic->regInterrupt);
2602 SSMR3PutU8 (pSSM, pBusLogic->regGeometry);
2603 SSMR3PutMem (pSSM, &pBusLogic->LocalRam, sizeof(pBusLogic->LocalRam));
2604 SSMR3PutU8 (pSSM, pBusLogic->uOperationCode);
2605 SSMR3PutMem (pSSM, &pBusLogic->aCommandBuffer, sizeof(pBusLogic->aCommandBuffer));
2606 SSMR3PutU8 (pSSM, pBusLogic->iParameter);
2607 SSMR3PutU8 (pSSM, pBusLogic->cbCommandParametersLeft);
2608 SSMR3PutBool (pSSM, pBusLogic->fUseLocalRam);
2609 SSMR3PutMem (pSSM, pBusLogic->aReplyBuffer, sizeof(pBusLogic->aReplyBuffer));
2610 SSMR3PutU8 (pSSM, pBusLogic->iReply);
2611 SSMR3PutU8 (pSSM, pBusLogic->cbReplyParametersLeft);
2612 SSMR3PutBool (pSSM, pBusLogic->fIRQEnabled);
2613 SSMR3PutBool (pSSM, pBusLogic->fISAEnabled);
2614 SSMR3PutU32 (pSSM, pBusLogic->cMailbox);
2615 SSMR3PutGCPhys(pSSM, pBusLogic->GCPhysAddrMailboxOutgoingBase);
2616 SSMR3PutU32 (pSSM, pBusLogic->uMailboxOutgoingPositionCurrent);
2617 SSMR3PutU32 (pSSM, pBusLogic->cMailboxesReady);
2618 SSMR3PutBool (pSSM, pBusLogic->fNotificationSend);
2619 SSMR3PutGCPhys(pSSM, pBusLogic->GCPhysAddrMailboxIncomingBase);
2620 SSMR3PutU32 (pSSM, pBusLogic->uMailboxIncomingPositionCurrent);
2621 SSMR3PutBool (pSSM, pBusLogic->fStrictRoundRobinMode);
2622 SSMR3PutBool (pSSM, pBusLogic->fExtendedLunCCBFormat);
2623 /* Now the data for the BIOS interface. */
2624 SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.regIdentify);
2625 SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.uTargetDevice);
2626 SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.uTxDir);
2627 SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.cbCDB);
2628 SSMR3PutMem (pSSM, pBusLogic->VBoxSCSI.aCDB, sizeof(pBusLogic->VBoxSCSI.aCDB));
2629 SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.iCDB);
2630 SSMR3PutU32 (pSSM, pBusLogic->VBoxSCSI.cbBuf);
2631 SSMR3PutU32 (pSSM, pBusLogic->VBoxSCSI.iBuf);
2632 SSMR3PutBool (pSSM, pBusLogic->VBoxSCSI.fBusy);
2633 SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.enmState);
2634 if (pBusLogic->VBoxSCSI.cbBuf)
2635 SSMR3PutMem(pSSM, pBusLogic->VBoxSCSI.pBuf, pBusLogic->VBoxSCSI.cbBuf);
2636
2637 /*
2638 * Save the physical addresses of the command control blocks of still pending tasks.
2639 * They are processed again on resume.
2640 *
2641 * The number of pending tasks needs to be determined first.
2642 */
2643 uint32_t cTasks = 0;
2644
2645 PBUSLOGICTASKSTATE pTaskState = pBusLogic->pTasksRedoHead;
2646 if (pBusLogic->fRedo)
2647 {
2648 while (pTaskState)
2649 {
2650 cTasks++;
2651 pTaskState = pTaskState->pRedoNext;
2652 }
2653 }
2654 SSMR3PutU32(pSSM, cTasks);
2655
2656 /* Write the address of every task now. */
2657 pTaskState = pBusLogic->pTasksRedoHead;
2658 while (pTaskState)
2659 {
2660 SSMR3PutU32(pSSM, pTaskState->MailboxGuest.u32PhysAddrCCB);
2661 pTaskState = pTaskState->pRedoNext;
2662 }
2663
2664 return SSMR3PutU32(pSSM, ~0);
2665}
2666
2667static DECLCALLBACK(int) buslogicLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
2668{
2669 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2670
2671 buslogicKick(pThis);
2672 return VINF_SUCCESS;
2673}
2674
2675static DECLCALLBACK(int) buslogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
2676{
2677 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2678 int rc = VINF_SUCCESS;
2679
2680 /* We support saved states only from this and older versions. */
2681 if (uVersion > BUSLOGIC_SAVED_STATE_MINOR_VERSION)
2682 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
2683
2684 /* Every device first. */
2685 for (unsigned i = 0; i < RT_ELEMENTS(pBusLogic->aDeviceStates); i++)
2686 {
2687 PBUSLOGICDEVICE pDevice = &pBusLogic->aDeviceStates[i];
2688
2689 AssertMsg(!pDevice->cOutstandingRequests,
2690 ("There are still outstanding requests on this device\n"));
2691 bool fPresent;
2692 rc = SSMR3GetBool(pSSM, &fPresent);
2693 AssertRCReturn(rc, rc);
2694 if (pDevice->fPresent != fPresent)
2695 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target %u config mismatch: config=%RTbool state=%RTbool"), i, pDevice->fPresent, fPresent);
2696
2697 if (uPass == SSM_PASS_FINAL)
2698 SSMR3GetU32(pSSM, (uint32_t *)&pDevice->cOutstandingRequests);
2699 }
2700
2701 if (uPass != SSM_PASS_FINAL)
2702 return VINF_SUCCESS;
2703
2704 /* Now the main device state. */
2705 SSMR3GetU8 (pSSM, (uint8_t *)&pBusLogic->regStatus);
2706 SSMR3GetU8 (pSSM, (uint8_t *)&pBusLogic->regInterrupt);
2707 SSMR3GetU8 (pSSM, (uint8_t *)&pBusLogic->regGeometry);
2708 SSMR3GetMem (pSSM, &pBusLogic->LocalRam, sizeof(pBusLogic->LocalRam));
2709 SSMR3GetU8 (pSSM, &pBusLogic->uOperationCode);
2710 SSMR3GetMem (pSSM, &pBusLogic->aCommandBuffer, sizeof(pBusLogic->aCommandBuffer));
2711 SSMR3GetU8 (pSSM, &pBusLogic->iParameter);
2712 SSMR3GetU8 (pSSM, &pBusLogic->cbCommandParametersLeft);
2713 SSMR3GetBool (pSSM, &pBusLogic->fUseLocalRam);
2714 SSMR3GetMem (pSSM, pBusLogic->aReplyBuffer, sizeof(pBusLogic->aReplyBuffer));
2715 SSMR3GetU8 (pSSM, &pBusLogic->iReply);
2716 SSMR3GetU8 (pSSM, &pBusLogic->cbReplyParametersLeft);
2717 SSMR3GetBool (pSSM, &pBusLogic->fIRQEnabled);
2718 SSMR3GetBool (pSSM, &pBusLogic->fISAEnabled);
2719 SSMR3GetU32 (pSSM, &pBusLogic->cMailbox);
2720 SSMR3GetGCPhys(pSSM, &pBusLogic->GCPhysAddrMailboxOutgoingBase);
2721 SSMR3GetU32 (pSSM, &pBusLogic->uMailboxOutgoingPositionCurrent);
2722 SSMR3GetU32 (pSSM, (uint32_t *)&pBusLogic->cMailboxesReady);
2723 SSMR3GetBool (pSSM, (bool *)&pBusLogic->fNotificationSend);
2724 SSMR3GetGCPhys(pSSM, &pBusLogic->GCPhysAddrMailboxIncomingBase);
2725 SSMR3GetU32 (pSSM, &pBusLogic->uMailboxIncomingPositionCurrent);
2726 SSMR3GetBool (pSSM, &pBusLogic->fStrictRoundRobinMode);
2727 SSMR3GetBool (pSSM, &pBusLogic->fExtendedLunCCBFormat);
2728 /* Now the data for the BIOS interface. */
2729 SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.regIdentify);
2730 SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.uTargetDevice);
2731 SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.uTxDir);
2732 SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.cbCDB);
2733 SSMR3GetMem (pSSM, pBusLogic->VBoxSCSI.aCDB, sizeof(pBusLogic->VBoxSCSI.aCDB));
2734 SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.iCDB);
2735 SSMR3GetU32 (pSSM, &pBusLogic->VBoxSCSI.cbBuf);
2736 SSMR3GetU32 (pSSM, &pBusLogic->VBoxSCSI.iBuf);
2737 SSMR3GetBool(pSSM, (bool *)&pBusLogic->VBoxSCSI.fBusy);
2738 SSMR3GetU8 (pSSM, (uint8_t *)&pBusLogic->VBoxSCSI.enmState);
2739 if (pBusLogic->VBoxSCSI.cbBuf)
2740 {
2741 pBusLogic->VBoxSCSI.pBuf = (uint8_t *)RTMemAllocZ(pBusLogic->VBoxSCSI.cbBuf);
2742 if (!pBusLogic->VBoxSCSI.pBuf)
2743 {
2744 LogRel(("BusLogic: Out of memory during restore.\n"));
2745 return PDMDEV_SET_ERROR(pDevIns, VERR_NO_MEMORY,
2746 N_("BusLogic: Out of memory during restore\n"));
2747 }
2748 SSMR3GetMem(pSSM, pBusLogic->VBoxSCSI.pBuf, pBusLogic->VBoxSCSI.cbBuf);
2749 }
2750
2751 if (pBusLogic->VBoxSCSI.fBusy)
2752 pBusLogic->fRedo = true;
2753
2754 if (uVersion > BUSLOGIC_SAVED_STATE_MINOR_PRE_ERROR_HANDLING)
2755 {
2756 /* Check if there are pending tasks saved. */
2757 uint32_t cTasks = 0;
2758
2759 SSMR3GetU32(pSSM, &cTasks);
2760
2761 if (cTasks)
2762 pBusLogic->fRedo = true;
2763
2764 for (uint32_t i = 0; i < cTasks; i++)
2765 {
2766 PBUSLOGICTASKSTATE pTaskState = (PBUSLOGICTASKSTATE)RTMemCacheAlloc(pBusLogic->hTaskCache);
2767 if (!pTaskState)
2768 {
2769 rc = VERR_NO_MEMORY;
2770 break;
2771 }
2772
2773 rc = SSMR3GetU32(pSSM, &pTaskState->MailboxGuest.u32PhysAddrCCB);
2774 if (RT_FAILURE(rc))
2775 {
2776 RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
2777 break;
2778 }
2779
2780 /* Link into the list. */
2781 pTaskState->pRedoNext = pBusLogic->pTasksRedoHead;
2782 pBusLogic->pTasksRedoHead = pTaskState;
2783 }
2784 }
2785
2786 if (RT_SUCCESS(rc))
2787 {
2788 uint32_t u32;
2789 rc = SSMR3GetU32(pSSM, &u32);
2790 if (RT_SUCCESS(rc))
2791 AssertMsgReturn(u32 == ~0U, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
2792 }
2793
2794 return rc;
2795}
2796
2797/**
2798 * Gets the pointer to the status LED of a device - called from the SCSi driver.
2799 *
2800 * @returns VBox status code.
2801 * @param pInterface Pointer to the interface structure containing the called function pointer.
2802 * @param iLUN The unit which status LED we desire. Always 0 here as the driver
2803 * doesn't know about other LUN's.
2804 * @param ppLed Where to store the LED pointer.
2805 */
2806static DECLCALLBACK(int) buslogicDeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
2807{
2808 PBUSLOGICDEVICE pDevice = PDMILEDPORTS_2_PBUSLOGICDEVICE(pInterface);
2809 if (iLUN == 0)
2810 {
2811 *ppLed = &pDevice->Led;
2812 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
2813 return VINF_SUCCESS;
2814 }
2815 return VERR_PDM_LUN_NOT_FOUND;
2816}
2817
2818/**
2819 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2820 */
2821static DECLCALLBACK(void *) buslogicDeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2822{
2823 PBUSLOGICDEVICE pDevice = PDMIBASE_2_PBUSLOGICDEVICE(pInterface);
2824 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevice->IBase);
2825 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSIPORT, &pDevice->ISCSIPort);
2826 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pDevice->ILed);
2827 return NULL;
2828}
2829
2830/**
2831 * Gets the pointer to the status LED of a unit.
2832 *
2833 * @returns VBox status code.
2834 * @param pInterface Pointer to the interface structure containing the called function pointer.
2835 * @param iLUN The unit which status LED we desire.
2836 * @param ppLed Where to store the LED pointer.
2837 */
2838static DECLCALLBACK(int) buslogicStatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
2839{
2840 PBUSLOGIC pBusLogic = PDMILEDPORTS_2_PBUSLOGIC(pInterface);
2841 if (iLUN < BUSLOGIC_MAX_DEVICES)
2842 {
2843 *ppLed = &pBusLogic->aDeviceStates[iLUN].Led;
2844 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
2845 return VINF_SUCCESS;
2846 }
2847 return VERR_PDM_LUN_NOT_FOUND;
2848}
2849
2850/**
2851 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2852 */
2853static DECLCALLBACK(void *) buslogicStatusQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2854{
2855 PBUSLOGIC pThis = PDMIBASE_2_PBUSLOGIC(pInterface);
2856 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
2857 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
2858 return NULL;
2859}
2860
2861/* -=-=-=-=- Helper -=-=-=-=- */
2862
2863 /**
2864 * Checks if all asynchronous I/O is finished.
2865 *
2866 * Used by buslogicReset, buslogicSuspend and buslogicPowerOff.
2867 *
2868 * @returns true if quiesced, false if busy.
2869 * @param pDevIns The device instance.
2870 */
2871static bool buslogicR3AllAsyncIOIsFinished(PPDMDEVINS pDevIns)
2872{
2873 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2874
2875 for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aDeviceStates); i++)
2876 {
2877 PBUSLOGICDEVICE pThisDevice = &pThis->aDeviceStates[i];
2878 if (pThisDevice->pDrvBase)
2879 {
2880 if (pThisDevice->cOutstandingRequests != 0)
2881 return false;
2882 }
2883 }
2884
2885 return true;
2886}
2887
2888/**
2889 * Callback employed by buslogicR3Suspend and buslogicR3PowerOff..
2890 *
2891 * @returns true if we've quiesced, false if we're still working.
2892 * @param pDevIns The device instance.
2893 */
2894static DECLCALLBACK(bool) buslogicR3IsAsyncSuspendOrPowerOffDone(PPDMDEVINS pDevIns)
2895{
2896 if (!buslogicR3AllAsyncIOIsFinished(pDevIns))
2897 return false;
2898
2899 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2900 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
2901 return true;
2902}
2903
2904/**
2905 * Common worker for ahciR3Suspend and ahciR3PowerOff.
2906 */
2907static void buslogicR3SuspendOrPowerOff(PPDMDEVINS pDevIns, bool fPowerOff)
2908{
2909 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2910
2911 ASMAtomicWriteBool(&pThis->fSignalIdle, true);
2912 if (!buslogicR3AllAsyncIOIsFinished(pDevIns))
2913 PDMDevHlpSetAsyncNotification(pDevIns, buslogicR3IsAsyncSuspendOrPowerOffDone);
2914 else
2915 {
2916 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
2917
2918 AssertMsg(!pThis->fNotificationSend, ("The PDM Queue should be empty at this point\n"));
2919
2920 if (pThis->fRedo)
2921 {
2922 if (fPowerOff)
2923 {
2924 /* Free tasks which would have been queued again on resume. */
2925 PBUSLOGICTASKSTATE pTaskState = pThis->pTasksRedoHead;
2926
2927 pThis->pTasksRedoHead = NULL;
2928
2929 while (pTaskState)
2930 {
2931 PBUSLOGICTASKSTATE pFree;
2932
2933 pFree = pTaskState;
2934 pTaskState = pTaskState->pRedoNext;
2935
2936 RTMemCacheFree(pThis->hTaskCache, pFree);
2937 }
2938 pThis->fRedo = false;
2939 }
2940 else if (pThis->VBoxSCSI.fBusy)
2941 {
2942 /* Destroy the task because the BIOS interface has all necessary information. */
2943 Assert(pThis->pTasksRedoHead->fBIOS);
2944 Assert(!pThis->pTasksRedoHead->pRedoNext);
2945
2946 RTMemCacheFree(pThis->hTaskCache, pThis->pTasksRedoHead);
2947 pThis->pTasksRedoHead = NULL;
2948 }
2949 }
2950 }
2951}
2952
2953/**
2954 * Suspend notification.
2955 *
2956 * @param pDevIns The device instance data.
2957 */
2958static DECLCALLBACK(void) buslogicSuspend(PPDMDEVINS pDevIns)
2959{
2960 Log(("buslogicSuspend\n"));
2961 buslogicR3SuspendOrPowerOff(pDevIns, false /* fPoweroff */);
2962}
2963
2964/**
2965 * Resume notification.
2966 *
2967 * @param pDevIns The device instance data.
2968 */
2969static DECLCALLBACK(void) buslogicResume(PPDMDEVINS pDevIns)
2970{
2971 Log(("buslogicResume\n"));
2972 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2973 buslogicKick(pThis);
2974}
2975
2976
2977/**
2978 * Detach notification.
2979 *
2980 * One harddisk at one port has been unplugged.
2981 * The VM is suspended at this point.
2982 *
2983 * @param pDevIns The device instance.
2984 * @param iLUN The logical unit which is being detached.
2985 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
2986 */
2987static DECLCALLBACK(void) buslogicDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2988{
2989 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2990 PBUSLOGICDEVICE pDevice = &pThis->aDeviceStates[iLUN];
2991
2992 Log(("%s:\n", __FUNCTION__));
2993
2994 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
2995 ("BusLogic: Device does not support hotplugging\n"));
2996
2997 /*
2998 * Zero some important members.
2999 */
3000 pDevice->pDrvBase = NULL;
3001 pDevice->fPresent = false;
3002 pDevice->pDrvSCSIConnector = NULL;
3003}
3004
3005/**
3006 * Attach command.
3007 *
3008 * This is called when we change block driver.
3009 *
3010 * @returns VBox status code.
3011 * @param pDevIns The device instance.
3012 * @param iLUN The logical unit which is being detached.
3013 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
3014 */
3015static DECLCALLBACK(int) buslogicAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
3016{
3017 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3018 PBUSLOGICDEVICE pDevice = &pThis->aDeviceStates[iLUN];
3019 int rc;
3020
3021 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
3022 ("BusLogic: Device does not support hotplugging\n"),
3023 VERR_INVALID_PARAMETER);
3024
3025 /* the usual paranoia */
3026 AssertRelease(!pDevice->pDrvBase);
3027 AssertRelease(!pDevice->pDrvSCSIConnector);
3028 Assert(pDevice->iLUN == iLUN);
3029
3030 /*
3031 * Try attach the block device and get the interfaces,
3032 * required as well as optional.
3033 */
3034 rc = PDMDevHlpDriverAttach(pDevIns, pDevice->iLUN, &pDevice->IBase, &pDevice->pDrvBase, NULL);
3035 if (RT_SUCCESS(rc))
3036 {
3037 /* Get SCSI connector interface. */
3038 pDevice->pDrvSCSIConnector = PDMIBASE_QUERY_INTERFACE(pDevice->pDrvBase, PDMISCSICONNECTOR);
3039 AssertMsgReturn(pDevice->pDrvSCSIConnector, ("Missing SCSI interface below\n"), VERR_PDM_MISSING_INTERFACE);
3040 pDevice->fPresent = true;
3041 }
3042 else
3043 AssertMsgFailed(("Failed to attach LUN#%d. rc=%Rrc\n", pDevice->iLUN, rc));
3044
3045 if (RT_FAILURE(rc))
3046 {
3047 pDevice->pDrvBase = NULL;
3048 pDevice->pDrvSCSIConnector = NULL;
3049 }
3050 return rc;
3051}
3052
3053/**
3054 * Callback employed by buslogicR3Reset.
3055 *
3056 * @returns true if we've quiesced, false if we're still working.
3057 * @param pDevIns The device instance.
3058 */
3059static DECLCALLBACK(bool) buslogicR3IsAsyncResetDone(PPDMDEVINS pDevIns)
3060{
3061 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3062
3063 if (!buslogicR3AllAsyncIOIsFinished(pDevIns))
3064 return false;
3065 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
3066
3067 buslogicHwReset(pThis);
3068 return true;
3069}
3070
3071/**
3072 * @copydoc FNPDMDEVRESET
3073 */
3074static DECLCALLBACK(void) buslogicReset(PPDMDEVINS pDevIns)
3075{
3076 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3077
3078 ASMAtomicWriteBool(&pThis->fSignalIdle, true);
3079 if (!buslogicR3AllAsyncIOIsFinished(pDevIns))
3080 PDMDevHlpSetAsyncNotification(pDevIns, buslogicR3IsAsyncResetDone);
3081 else
3082 {
3083 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
3084 buslogicHwReset(pThis);
3085 }
3086}
3087
3088static DECLCALLBACK(void) buslogicRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
3089{
3090 uint32_t i;
3091 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3092
3093 pBusLogic->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
3094 pBusLogic->pNotifierQueueRC = PDMQueueRCPtr(pBusLogic->pNotifierQueueR3);
3095
3096 for (i = 0; i < BUSLOGIC_MAX_DEVICES; i++)
3097 {
3098 PBUSLOGICDEVICE pDevice = &pBusLogic->aDeviceStates[i];
3099
3100 pDevice->pBusLogicRC = PDMINS_2_DATA_RCPTR(pDevIns);
3101 }
3102
3103}
3104
3105/**
3106 * Poweroff notification.
3107 *
3108 * @param pDevIns Pointer to the device instance
3109 */
3110static DECLCALLBACK(void) buslogicPowerOff(PPDMDEVINS pDevIns)
3111{
3112 Log(("buslogicPowerOff\n"));
3113 buslogicR3SuspendOrPowerOff(pDevIns, true /* fPoweroff */);
3114}
3115
3116/**
3117 * Destroy a driver instance.
3118 *
3119 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
3120 * resources can be freed correctly.
3121 *
3122 * @param pDevIns The device instance data.
3123 */
3124static DECLCALLBACK(int) buslogicDestruct(PPDMDEVINS pDevIns)
3125{
3126 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3127 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
3128
3129 PDMR3CritSectDelete(&pThis->CritSectIntr);
3130
3131 /*
3132 * Free all tasks which are still hanging around
3133 * (Power off after the VM was suspended).
3134 */
3135 if (pThis->fRedo)
3136 {
3137 /* Free tasks which would have been queued again on resume. */
3138 PBUSLOGICTASKSTATE pTaskState = pThis->pTasksRedoHead;
3139
3140 pThis->pTasksRedoHead = NULL;
3141
3142 while (pTaskState)
3143 {
3144 PBUSLOGICTASKSTATE pFree;
3145
3146 pFree = pTaskState;
3147 pTaskState = pTaskState->pRedoNext;
3148
3149 RTMemCacheFree(pThis->hTaskCache, pFree);
3150 }
3151 pThis->fRedo = false;
3152 }
3153
3154 int rc = RTMemCacheDestroy(pThis->hTaskCache);
3155 AssertMsgRC(rc, ("Destroying task cache failed rc=%Rrc\n", rc));
3156
3157 return rc;
3158}
3159
3160/**
3161 * @interface_method_impl{PDMDEVREG,pfnConstruct}
3162 */
3163static DECLCALLBACK(int) buslogicConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
3164{
3165 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3166 int rc = VINF_SUCCESS;
3167 bool fBootable = true;
3168 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
3169
3170 /*
3171 * Validate and read configuration.
3172 */
3173 if (!CFGMR3AreValuesValid(pCfg,
3174 "GCEnabled\0"
3175 "R0Enabled\0"
3176 "Bootable\0"))
3177 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
3178 N_("BusLogic configuration error: unknown option specified"));
3179
3180 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true);
3181 if (RT_FAILURE(rc))
3182 return PDMDEV_SET_ERROR(pDevIns, rc,
3183 N_("BusLogic configuration error: failed to read GCEnabled as boolean"));
3184 Log(("%s: fGCEnabled=%d\n", __FUNCTION__, pThis->fGCEnabled));
3185
3186 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
3187 if (RT_FAILURE(rc))
3188 return PDMDEV_SET_ERROR(pDevIns, rc,
3189 N_("BusLogic configuration error: failed to read R0Enabled as boolean"));
3190 Log(("%s: fR0Enabled=%d\n", __FUNCTION__, pThis->fR0Enabled));
3191 rc = CFGMR3QueryBoolDef(pCfg, "Bootable", &fBootable, true);
3192 if (RT_FAILURE(rc))
3193 return PDMDEV_SET_ERROR(pDevIns, rc,
3194 N_("BusLogic configuration error: failed to read Bootable as boolean"));
3195 Log(("%s: fBootable=%RTbool\n", __FUNCTION__, fBootable));
3196
3197 pThis->pDevInsR3 = pDevIns;
3198 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
3199 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
3200 pThis->IBase.pfnQueryInterface = buslogicStatusQueryInterface;
3201 pThis->ILeds.pfnQueryStatusLed = buslogicStatusQueryStatusLed;
3202
3203 PCIDevSetVendorId (&pThis->dev, 0x104b); /* BusLogic */
3204 PCIDevSetDeviceId (&pThis->dev, 0x1040); /* BT-958 */
3205 PCIDevSetCommand (&pThis->dev, 0x0003);
3206 PCIDevSetRevisionId (&pThis->dev, 0x01);
3207 PCIDevSetClassProg (&pThis->dev, 0x00); /* SCSI */
3208 PCIDevSetClassSub (&pThis->dev, 0x00); /* SCSI */
3209 PCIDevSetClassBase (&pThis->dev, 0x01); /* Mass storage */
3210 PCIDevSetBaseAddress (&pThis->dev, 0, true /*IO*/, false /*Pref*/, false /*64-bit*/, 0x00000000);
3211 PCIDevSetBaseAddress (&pThis->dev, 1, false /*IO*/, false /*Pref*/, false /*64-bit*/, 0x00000000);
3212 PCIDevSetSubSystemVendorId(&pThis->dev, 0x104b);
3213 PCIDevSetSubSystemId (&pThis->dev, 0x1040);
3214 PCIDevSetInterruptLine (&pThis->dev, 0x00);
3215 PCIDevSetInterruptPin (&pThis->dev, 0x01);
3216
3217 /*
3218 * Register the PCI device, it's I/O regions.
3219 */
3220 rc = PDMDevHlpPCIRegister (pDevIns, &pThis->dev);
3221 if (RT_FAILURE(rc))
3222 return rc;
3223
3224 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 32, PCI_ADDRESS_SPACE_IO, buslogicMMIOMap);
3225 if (RT_FAILURE(rc))
3226 return rc;
3227
3228 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, 32, PCI_ADDRESS_SPACE_MEM, buslogicMMIOMap);
3229 if (RT_FAILURE(rc))
3230 return rc;
3231
3232 if (fBootable)
3233 {
3234 /* Register I/O port space in ISA region for BIOS access. */
3235 rc = PDMDevHlpIOPortRegister(pDevIns, BUSLOGIC_BIOS_IO_PORT, 3, NULL,
3236 buslogicBIOSIOPortWrite, buslogicBIOSIOPortRead,
3237 buslogicBIOSIOPortWriteStr, buslogicBIOSIOPortReadStr,
3238 "BusLogic BIOS");
3239 if (RT_FAILURE(rc))
3240 return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register legacy I/O handlers"));
3241 }
3242
3243 /* Initialize task cache. */
3244 rc = RTMemCacheCreate(&pThis->hTaskCache, sizeof(BUSLOGICTASKSTATE), 0, UINT32_MAX,
3245 NULL, NULL, NULL, 0);
3246 if (RT_FAILURE(rc))
3247 return PDMDEV_SET_ERROR(pDevIns, rc,
3248 N_("BusLogic: Failed to initialize task cache\n"));
3249
3250 /* Initialize task queue. */
3251 rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 5, 0,
3252 buslogicNotifyQueueConsumer, true, "BusLogicTask", &pThis->pNotifierQueueR3);
3253 if (RT_FAILURE(rc))
3254 return rc;
3255 pThis->pNotifierQueueR0 = PDMQueueR0Ptr(pThis->pNotifierQueueR3);
3256 pThis->pNotifierQueueRC = PDMQueueRCPtr(pThis->pNotifierQueueR3);
3257
3258 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSectIntr, RT_SRC_POS, "BusLogic-Intr#%u", pDevIns->iInstance);
3259 if (RT_FAILURE(rc))
3260 return PDMDEV_SET_ERROR(pDevIns, rc,
3261 N_("BusLogic: cannot create critical section"));
3262
3263 /* Initialize per device state. */
3264 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aDeviceStates); i++)
3265 {
3266 char szName[24];
3267 PBUSLOGICDEVICE pDevice = &pThis->aDeviceStates[i];
3268
3269 RTStrPrintf(szName, sizeof(szName), "Device%d", i);
3270
3271 /* Initialize static parts of the device. */
3272 pDevice->iLUN = i;
3273 pDevice->pBusLogicR3 = pThis;
3274 pDevice->pBusLogicR0 = PDMINS_2_DATA_R0PTR(pDevIns);
3275 pDevice->pBusLogicRC = PDMINS_2_DATA_RCPTR(pDevIns);
3276 pDevice->Led.u32Magic = PDMLED_MAGIC;
3277 pDevice->IBase.pfnQueryInterface = buslogicDeviceQueryInterface;
3278 pDevice->ISCSIPort.pfnSCSIRequestCompleted = buslogicDeviceSCSIRequestCompleted;
3279 pDevice->ISCSIPort.pfnQueryDeviceLocation = buslogicQueryDeviceLocation;
3280 pDevice->ILed.pfnQueryStatusLed = buslogicDeviceQueryStatusLed;
3281
3282 /* Attach SCSI driver. */
3283 rc = PDMDevHlpDriverAttach(pDevIns, pDevice->iLUN, &pDevice->IBase, &pDevice->pDrvBase, szName);
3284 if (RT_SUCCESS(rc))
3285 {
3286 /* Get SCSI connector interface. */
3287 pDevice->pDrvSCSIConnector = PDMIBASE_QUERY_INTERFACE(pDevice->pDrvBase, PDMISCSICONNECTOR);
3288 AssertMsgReturn(pDevice->pDrvSCSIConnector, ("Missing SCSI interface below\n"), VERR_PDM_MISSING_INTERFACE);
3289
3290 pDevice->fPresent = true;
3291 }
3292 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
3293 {
3294 pDevice->pDrvBase = NULL;
3295 pDevice->fPresent = false;
3296 rc = VINF_SUCCESS;
3297 Log(("BusLogic: no driver attached to device %s\n", szName));
3298 }
3299 else
3300 {
3301 AssertLogRelMsgFailed(("BusLogic: Failed to attach %s\n", szName));
3302 return rc;
3303 }
3304 }
3305
3306 /*
3307 * Attach status driver (optional).
3308 */
3309 PPDMIBASE pBase;
3310 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pBase, "Status Port");
3311 if (RT_SUCCESS(rc))
3312 pThis->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
3313 else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
3314 {
3315 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
3316 return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot attach to status driver"));
3317 }
3318
3319 rc = PDMDevHlpSSMRegisterEx(pDevIns, BUSLOGIC_SAVED_STATE_MINOR_VERSION, sizeof(*pThis), NULL,
3320 NULL, buslogicLiveExec, NULL,
3321 NULL, buslogicSaveExec, NULL,
3322 NULL, buslogicLoadExec, buslogicLoadDone);
3323 if (RT_FAILURE(rc))
3324 return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register save state handlers"));
3325
3326 rc = buslogicHwReset(pThis);
3327 AssertMsgRC(rc, ("hardware reset of BusLogic host adapter failed rc=%Rrc\n", rc));
3328
3329 return rc;
3330}
3331
3332/**
3333 * The device registration structure.
3334 */
3335const PDMDEVREG g_DeviceBusLogic =
3336{
3337 /* u32Version */
3338 PDM_DEVREG_VERSION,
3339 /* szName */
3340 "buslogic",
3341 /* szRCMod */
3342 "VBoxDDGC.gc",
3343 /* szR0Mod */
3344 "VBoxDDR0.r0",
3345 /* pszDescription */
3346 "BusLogic BT-958 SCSI host adapter.\n",
3347 /* fFlags */
3348 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0 |
3349 PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION |
3350 PDM_DEVREG_FLAGS_FIRST_RESET_NOTIFICATION,
3351 /* fClass */
3352 PDM_DEVREG_CLASS_STORAGE,
3353 /* cMaxInstances */
3354 ~0U,
3355 /* cbInstance */
3356 sizeof(BUSLOGIC),
3357 /* pfnConstruct */
3358 buslogicConstruct,
3359 /* pfnDestruct */
3360 buslogicDestruct,
3361 /* pfnRelocate */
3362 buslogicRelocate,
3363 /* pfnIOCtl */
3364 NULL,
3365 /* pfnPowerOn */
3366 NULL,
3367 /* pfnReset */
3368 buslogicReset,
3369 /* pfnSuspend */
3370 buslogicSuspend,
3371 /* pfnResume */
3372 buslogicResume,
3373 /* pfnAttach */
3374 buslogicAttach,
3375 /* pfnDetach */
3376 buslogicDetach,
3377 /* pfnQueryInterface. */
3378 NULL,
3379 /* pfnInitComplete */
3380 NULL,
3381 /* pfnPowerOff */
3382 buslogicPowerOff,
3383 /* pfnSoftReset */
3384 NULL,
3385 /* u32VersionEnd */
3386 PDM_DEVREG_VERSION
3387};
3388
3389#endif /* IN_RING3 */
3390#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
3391
Note: See TracBrowser for help on using the repository browser.

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