VirtualBox

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

Last change on this file since 39135 was 39135, checked in by vboxsync, 13 years ago

Changed PDMDevHlpMMIORegister to take flags and drop pfnFill. Added PDMDevHlpMMIORegisterEx for the one user of pfnFill.

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