VirtualBox

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

Last change on this file since 35982 was 35768, checked in by vboxsync, 14 years ago

BusLogic: assertion to catch cases where the guest tries to send more mailboxes than it allocated space for during init

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

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