VirtualBox

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

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

BusLogic: Fix SMP race which results in a guest hang

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 123.2 KB
Line 
1/* $Id: DevBusLogic.cpp 35756 2011-01-28 12:29:38Z 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 ASMAtomicIncU32(&pBusLogic->cMailboxesReady);
1636 if (!ASMAtomicXchgBool(&pBusLogic->fNotificationSend, true))
1637 {
1638 /* Send new notification to the queue. */
1639 PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(pBusLogic->CTX_SUFF(pNotifierQueue));
1640 AssertMsg(pItem, ("Allocating item for queue failed\n"));
1641 PDMQueueInsert(pBusLogic->CTX_SUFF(pNotifierQueue), (PPDMQUEUEITEMCORE)pItem);
1642 }
1643
1644 return rc;
1645 }
1646
1647 /*
1648 * Check if we are already fetch command parameters from the guest.
1649 * If not we initialize executing a new command.
1650 */
1651 if (pBusLogic->uOperationCode == 0xff)
1652 {
1653 pBusLogic->uOperationCode = uVal;
1654 pBusLogic->iParameter = 0;
1655
1656 /* Mark host adapter as busy and clear the invalid status bit. */
1657 pBusLogic->regStatus &= ~(BUSLOGIC_REGISTER_STATUS_HOST_ADAPTER_READY | BUSLOGIC_REGISTER_STATUS_COMMAND_INVALID);
1658
1659 /* Get the number of bytes for parameters from the command code. */
1660 switch (pBusLogic->uOperationCode)
1661 {
1662 case BUSLOGICCOMMAND_INQUIRE_FIRMWARE_VERSION_LETTER:
1663 case BUSLOGICCOMMAND_INQUIRE_BOARD_ID:
1664 case BUSLOGICCOMMAND_INQUIRE_FIRMWARE_VERSION_3RD_LETTER:
1665 case BUSLOGICCOMMAND_INQUIRE_PCI_HOST_ADAPTER_INFORMATION:
1666 case BUSLOGICCOMMAND_INQUIRE_CONFIGURATION:
1667 case BUSLOGICCOMMAND_INQUIRE_TARGET_DEVICES:
1668 pBusLogic->cbCommandParametersLeft = 0;
1669 break;
1670 case BUSLOGICCOMMAND_MODIFY_IO_ADDRESS:
1671 case BUSLOGICCOMMAND_INQUIRE_EXTENDED_SETUP_INFORMATION:
1672 case BUSLOGICCOMMAND_INQUIRE_SETUP_INFORMATION:
1673 case BUSLOGICCOMMAND_INQUIRE_HOST_ADAPTER_MODEL_NUMBER:
1674 case BUSLOGICCOMMAND_ENABLE_STRICT_ROUND_ROBIN_MODE:
1675 case BUSLOGICCOMMAND_SET_CCB_FORMAT:
1676 case BUSLOGICCOMMAND_INQUIRE_SYNCHRONOUS_PERIOD:
1677 case BUSLOGICCOMMAND_DISABLE_HOST_ADAPTER_INTERRUPT:
1678 case BUSLOGICCOMMAND_ECHO_COMMAND_DATA:
1679 case BUSLOGICCOMMAND_SET_PREEMPT_TIME_ON_BUS:
1680 case BUSLOGICCOMMAND_SET_TIME_OFF_BUS:
1681 pBusLogic->cbCommandParametersLeft = 1;
1682 break;
1683 case BUSLOGICCOMMAND_FETCH_HOST_ADAPTER_LOCAL_RAM:
1684 pBusLogic->cbCommandParametersLeft = 2;
1685 break;
1686 case BUSLOGICCOMMAND_INITIALIZE_EXTENDED_MAILBOX:
1687 pBusLogic->cbCommandParametersLeft = sizeof(RequestInitializeExtendedMailbox);
1688 break;
1689 case BUSLOGICCOMMAND_EXT_BIOS_INFO:
1690 case BUSLOGICCOMMAND_UNLOCK_MAILBOX:
1691 /* Invalid commands. */
1692 pBusLogic->cbCommandParametersLeft = 0;
1693 break;
1694 case BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND: /* Should not come here anymore. */
1695 default:
1696 AssertMsgFailed(("Invalid operation code %#x\n", uVal));
1697 }
1698 }
1699 else
1700 {
1701 /*
1702 * The real adapter would set the Command register busy bit in the status register.
1703 * The guest has to wait until it is unset.
1704 * We don't need to do it because the guest does not continue execution while we are in this
1705 * function.
1706 */
1707 pBusLogic->aCommandBuffer[pBusLogic->iParameter] = uVal;
1708 pBusLogic->iParameter++;
1709 pBusLogic->cbCommandParametersLeft--;
1710 }
1711
1712 /* Start execution of command if there are no parameters left. */
1713 if (!pBusLogic->cbCommandParametersLeft)
1714 {
1715 rc = buslogicProcessCommand(pBusLogic);
1716 AssertMsgRC(rc, ("Processing command failed rc=%Rrc\n", rc));
1717 }
1718 break;
1719 }
1720 default:
1721 AssertMsgFailed(("Register not available\n"));
1722 rc = VERR_IOM_IOPORT_UNUSED;
1723 }
1724
1725 return rc;
1726}
1727
1728/**
1729 * Memory mapped I/O Handler for read operations.
1730 *
1731 * @returns VBox status code.
1732 *
1733 * @param pDevIns The device instance.
1734 * @param pvUser User argument.
1735 * @param GCPhysAddr Physical address (in GC) where the read starts.
1736 * @param pv Where to store the result.
1737 * @param cb Number of bytes read.
1738 */
1739PDMBOTHCBDECL(int) buslogicMMIORead(PPDMDEVINS pDevIns, void *pvUser,
1740 RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
1741{
1742 /* the linux driver does not make use of the MMIO area. */
1743 AssertMsgFailed(("MMIO Read\n"));
1744 return VINF_SUCCESS;
1745}
1746
1747/**
1748 * Memory mapped I/O Handler for write operations.
1749 *
1750 * @returns VBox status code.
1751 *
1752 * @param pDevIns The device instance.
1753 * @param pvUser User argument.
1754 * @param GCPhysAddr Physical address (in GC) where the read starts.
1755 * @param pv Where to fetch the result.
1756 * @param cb Number of bytes to write.
1757 */
1758PDMBOTHCBDECL(int) buslogicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser,
1759 RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
1760{
1761 /* the linux driver does not make use of the MMIO area. */
1762 AssertMsgFailed(("MMIO Write\n"));
1763 return VINF_SUCCESS;
1764}
1765
1766/**
1767 * Port I/O Handler for IN operations.
1768 *
1769 * @returns VBox status code.
1770 *
1771 * @param pDevIns The device instance.
1772 * @param pvUser User argument.
1773 * @param uPort Port number used for the IN operation.
1774 * @param pu32 Where to store the result.
1775 * @param cb Number of bytes read.
1776 */
1777PDMBOTHCBDECL(int) buslogicIOPortRead (PPDMDEVINS pDevIns, void *pvUser,
1778 RTIOPORT Port, uint32_t *pu32, unsigned cb)
1779{
1780 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);;
1781 unsigned iRegister = Port - pBusLogic->IOPortBase;
1782
1783 Assert(cb == 1);
1784
1785 return buslogicRegisterRead(pBusLogic, iRegister, pu32);
1786}
1787
1788/**
1789 * Port I/O Handler for OUT operations.
1790 *
1791 * @returns VBox status code.
1792 *
1793 * @param pDevIns The device instance.
1794 * @param pvUser User argument.
1795 * @param uPort Port number used for the IN operation.
1796 * @param u32 The value to output.
1797 * @param cb The value size in bytes.
1798 */
1799PDMBOTHCBDECL(int) buslogicIOPortWrite (PPDMDEVINS pDevIns, void *pvUser,
1800 RTIOPORT Port, uint32_t u32, unsigned cb)
1801{
1802 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
1803 int rc = VINF_SUCCESS;
1804 unsigned iRegister = Port - pBusLogic->IOPortBase;
1805 uint8_t uVal = (uint8_t)u32;
1806
1807 Assert(cb == 1);
1808
1809 rc = buslogicRegisterWrite(pBusLogic, iRegister, (uint8_t)uVal);
1810
1811 Log2(("#%d %s: pvUser=%#p cb=%d u32=%#x Port=%#x rc=%Rrc\n",
1812 pDevIns->iInstance, __FUNCTION__, pvUser, cb, u32, Port, rc));
1813
1814 return rc;
1815}
1816
1817#ifdef IN_RING3
1818/**
1819 * Port I/O Handler for IN operations - legacy port.
1820 *
1821 * @returns VBox status code.
1822 *
1823 * @param pDevIns The device instance.
1824 * @param pvUser User argument.
1825 * @param uPort Port number used for the IN operation.
1826 * @param pu32 Where to store the result.
1827 * @param cb Number of bytes read.
1828 */
1829static int buslogicIsaIOPortRead (PPDMDEVINS pDevIns, void *pvUser,
1830 RTIOPORT Port, uint32_t *pu32, unsigned cb)
1831{
1832 int rc;
1833 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
1834
1835 Assert(cb == 1);
1836
1837 if (!pBusLogic->fISAEnabled)
1838 return VINF_SUCCESS;
1839
1840 rc = vboxscsiReadRegister(&pBusLogic->VBoxSCSI, (Port - BUSLOGIC_ISA_IO_PORT), pu32);
1841
1842 //Log2(("%s: pu32=%p:{%.*Rhxs} iRegister=%d rc=%Rrc\n",
1843 // __FUNCTION__, pu32, 1, pu32, (Port - BUSLOGIC_ISA_IO_PORT), rc));
1844
1845 return rc;
1846}
1847
1848static void buslogicWarningDiskFull(PPDMDEVINS pDevIns)
1849{
1850 int rc;
1851 LogRel(("BusLogic#%d: Host disk full\n", pDevIns->iInstance));
1852 rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_DISKFULL",
1853 N_("Host system reported disk full. VM execution is suspended. You can resume after freeing some space"));
1854 AssertRC(rc);
1855}
1856
1857static void buslogicWarningFileTooBig(PPDMDEVINS pDevIns)
1858{
1859 int rc;
1860 LogRel(("BusLogic#%d: File too big\n", pDevIns->iInstance));
1861 rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_FILETOOBIG",
1862 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"));
1863 AssertRC(rc);
1864}
1865
1866static void buslogicWarningISCSI(PPDMDEVINS pDevIns)
1867{
1868 int rc;
1869 LogRel(("BusLogic#%d: iSCSI target unavailable\n", pDevIns->iInstance));
1870 rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_ISCSIDOWN",
1871 N_("The iSCSI target has stopped responding. VM execution is suspended. You can resume when it is available again"));
1872 AssertRC(rc);
1873}
1874
1875static void buslogicWarningUnknown(PPDMDEVINS pDevIns, int rc)
1876{
1877 int rc2;
1878 LogRel(("BusLogic#%d: Unknown but recoverable error has occurred (rc=%Rrc)\n", pDevIns->iInstance, rc));
1879 rc2 = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_UNKNOWN",
1880 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);
1881 AssertRC(rc2);
1882}
1883
1884static void buslogicRedoSetWarning(PBUSLOGIC pThis, int rc)
1885{
1886 if (rc == VERR_DISK_FULL)
1887 buslogicWarningDiskFull(pThis->CTX_SUFF(pDevIns));
1888 else if (rc == VERR_FILE_TOO_BIG)
1889 buslogicWarningFileTooBig(pThis->CTX_SUFF(pDevIns));
1890 else if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
1891 {
1892 /* iSCSI connection abort (first error) or failure to reestablish
1893 * connection (second error). Pause VM. On resume we'll retry. */
1894 buslogicWarningISCSI(pThis->CTX_SUFF(pDevIns));
1895 }
1896 else
1897 buslogicWarningUnknown(pThis->CTX_SUFF(pDevIns), rc);
1898}
1899
1900
1901static int buslogicPrepareBIOSSCSIRequest(PBUSLOGIC pBusLogic)
1902{
1903 int rc;
1904 PBUSLOGICTASKSTATE pTaskState;
1905 uint32_t uTargetDevice;
1906
1907 rc = RTMemCacheAllocEx(pBusLogic->hTaskCache, (void **)&pTaskState);
1908 AssertMsgRCReturn(rc, ("Getting task from cache failed rc=%Rrc\n", rc), rc);
1909
1910 pTaskState->fBIOS = true;
1911
1912 rc = vboxscsiSetupRequest(&pBusLogic->VBoxSCSI, &pTaskState->PDMScsiRequest, &uTargetDevice);
1913 AssertMsgRCReturn(rc, ("Setting up SCSI request failed rc=%Rrc\n", rc), rc);
1914
1915 pTaskState->PDMScsiRequest.pvUser = pTaskState;
1916
1917 pTaskState->CTX_SUFF(pTargetDevice) = &pBusLogic->aDeviceStates[uTargetDevice];
1918
1919 if (!pTaskState->CTX_SUFF(pTargetDevice)->fPresent)
1920 {
1921 /* Device is not present. */
1922 AssertMsg(pTaskState->PDMScsiRequest.pbCDB[0] == SCSI_INQUIRY,
1923 ("Device is not present but command is not inquiry\n"));
1924
1925 SCSIINQUIRYDATA ScsiInquiryData;
1926
1927 memset(&ScsiInquiryData, 0, sizeof(SCSIINQUIRYDATA));
1928 ScsiInquiryData.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN;
1929 ScsiInquiryData.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED;
1930
1931 memcpy(pBusLogic->VBoxSCSI.pBuf, &ScsiInquiryData, 5);
1932
1933 rc = vboxscsiRequestFinished(&pBusLogic->VBoxSCSI, &pTaskState->PDMScsiRequest);
1934 AssertMsgRCReturn(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc), rc);
1935
1936 RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
1937 }
1938 else
1939 {
1940 LogFlowFunc(("before increment %u\n", pTaskState->CTX_SUFF(pTargetDevice)->cOutstandingRequests));
1941 ASMAtomicIncU32(&pTaskState->CTX_SUFF(pTargetDevice)->cOutstandingRequests);
1942 LogFlowFunc(("after increment %u\n", pTaskState->CTX_SUFF(pTargetDevice)->cOutstandingRequests));
1943
1944 rc = pTaskState->CTX_SUFF(pTargetDevice)->pDrvSCSIConnector->pfnSCSIRequestSend(pTaskState->CTX_SUFF(pTargetDevice)->pDrvSCSIConnector,
1945 &pTaskState->PDMScsiRequest);
1946 AssertMsgRC(rc, ("Sending request to SCSI layer failed rc=%Rrc\n", rc));
1947 }
1948
1949 return rc;
1950}
1951
1952/**
1953 * Port I/O Handler for OUT operations - legacy port.
1954 *
1955 * @returns VBox status code.
1956 *
1957 * @param pDevIns The device instance.
1958 * @param pvUser User argument.
1959 * @param uPort Port number used for the IN operation.
1960 * @param u32 The value to output.
1961 * @param cb The value size in bytes.
1962 */
1963static int buslogicIsaIOPortWrite (PPDMDEVINS pDevIns, void *pvUser,
1964 RTIOPORT Port, uint32_t u32, unsigned cb)
1965{
1966 int rc;
1967 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
1968
1969 Log2(("#%d %s: pvUser=%#p cb=%d u32=%#x Port=%#x\n",
1970 pDevIns->iInstance, __FUNCTION__, pvUser, cb, u32, Port));
1971
1972 Assert(cb == 1);
1973
1974 if (!pBusLogic->fISAEnabled)
1975 return VINF_SUCCESS;
1976
1977 rc = vboxscsiWriteRegister(&pBusLogic->VBoxSCSI, (Port - BUSLOGIC_ISA_IO_PORT), (uint8_t)u32);
1978 if (rc == VERR_MORE_DATA)
1979 {
1980 rc = buslogicPrepareBIOSSCSIRequest(pBusLogic);
1981 AssertRC(rc);
1982 }
1983 else if (RT_FAILURE(rc))
1984 AssertMsgFailed(("Writing BIOS register failed %Rrc\n", rc));
1985
1986 return VINF_SUCCESS;
1987}
1988
1989/**
1990 * Port I/O Handler for primary port range OUT string operations.
1991 * @see FNIOMIOPORTOUTSTRING for details.
1992 */
1993static DECLCALLBACK(int) buslogicIsaIOPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb)
1994{
1995 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
1996 int rc;
1997
1998 Log2(("#%d %s: pvUser=%#p cb=%d Port=%#x\n",
1999 pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port));
2000
2001 rc = vboxscsiWriteString(pDevIns, &pBusLogic->VBoxSCSI, (Port - BUSLOGIC_ISA_IO_PORT),
2002 pGCPtrSrc, pcTransfer, cb);
2003 if (rc == VERR_MORE_DATA)
2004 {
2005 rc = buslogicPrepareBIOSSCSIRequest(pBusLogic);
2006 AssertRC(rc);
2007 }
2008 else if (RT_FAILURE(rc))
2009 AssertMsgFailed(("Writing BIOS register failed %Rrc\n", rc));
2010
2011 return rc;
2012}
2013
2014/**
2015 * Port I/O Handler for primary port range IN string operations.
2016 * @see FNIOMIOPORTINSTRING for details.
2017 */
2018static DECLCALLBACK(int) buslogicIsaIOPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb)
2019{
2020 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2021
2022 LogFlowFunc(("#%d %s: pvUser=%#p cb=%d Port=%#x\n",
2023 pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port));
2024
2025 return vboxscsiReadString(pDevIns, &pBusLogic->VBoxSCSI, (Port - BUSLOGIC_ISA_IO_PORT),
2026 pGCPtrDst, pcTransfer, cb);
2027}
2028
2029static DECLCALLBACK(int) buslogicMMIOMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion,
2030 RTGCPHYS GCPhysAddress, uint32_t cb,
2031 PCIADDRESSSPACE enmType)
2032{
2033 PPDMDEVINS pDevIns = pPciDev->pDevIns;
2034 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2035 int rc = VINF_SUCCESS;
2036
2037 Log2(("%s: registering MMIO area at GCPhysAddr=%RGp cb=%u\n", __FUNCTION__, GCPhysAddress, cb));
2038
2039 Assert(cb >= 32);
2040
2041 if (enmType == PCI_ADDRESS_SPACE_MEM)
2042 {
2043 /* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
2044 rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL,
2045 buslogicMMIOWrite, buslogicMMIORead, NULL, "BusLogic");
2046 if (RT_FAILURE(rc))
2047 return rc;
2048
2049 if (pThis->fR0Enabled)
2050 {
2051 rc = PDMDevHlpMMIORegisterR0(pDevIns, GCPhysAddress, cb, 0,
2052 "buslogicMMIOWrite", "buslogicMMIORead", NULL);
2053 if (RT_FAILURE(rc))
2054 return rc;
2055 }
2056
2057 if (pThis->fGCEnabled)
2058 {
2059 rc = PDMDevHlpMMIORegisterRC(pDevIns, GCPhysAddress, cb, 0,
2060 "buslogicMMIOWrite", "buslogicMMIORead", NULL);
2061 if (RT_FAILURE(rc))
2062 return rc;
2063 }
2064
2065 pThis->MMIOBase = GCPhysAddress;
2066 }
2067 else if (enmType == PCI_ADDRESS_SPACE_IO)
2068 {
2069 rc = PDMDevHlpIOPortRegister(pDevIns, (RTIOPORT)GCPhysAddress, 32,
2070 NULL, buslogicIOPortWrite, buslogicIOPortRead, NULL, NULL, "BusLogic");
2071 if (RT_FAILURE(rc))
2072 return rc;
2073
2074 if (pThis->fR0Enabled)
2075 {
2076 rc = PDMDevHlpIOPortRegisterR0(pDevIns, (RTIOPORT)GCPhysAddress, 32,
2077 0, "buslogicIOPortWrite", "buslogicIOPortRead", NULL, NULL, "BusLogic");
2078 if (RT_FAILURE(rc))
2079 return rc;
2080 }
2081
2082 if (pThis->fGCEnabled)
2083 {
2084 rc = PDMDevHlpIOPortRegisterRC(pDevIns, (RTIOPORT)GCPhysAddress, 32,
2085 0, "buslogicIOPortWrite", "buslogicIOPortRead", NULL, NULL, "BusLogic");
2086 if (RT_FAILURE(rc))
2087 return rc;
2088 }
2089
2090 pThis->IOPortBase = (RTIOPORT)GCPhysAddress;
2091 }
2092 else
2093 AssertMsgFailed(("Invalid enmType=%d\n", enmType));
2094
2095 return rc;
2096}
2097
2098static DECLCALLBACK(int) buslogicDeviceSCSIRequestCompleted(PPDMISCSIPORT pInterface, PPDMSCSIREQUEST pSCSIRequest,
2099 int rcCompletion, bool fRedo, int rcReq)
2100{
2101 int rc;
2102 PBUSLOGICTASKSTATE pTaskState = (PBUSLOGICTASKSTATE)pSCSIRequest->pvUser;
2103 PBUSLOGICDEVICE pBusLogicDevice = pTaskState->CTX_SUFF(pTargetDevice);
2104 PBUSLOGIC pBusLogic = pBusLogicDevice->CTX_SUFF(pBusLogic);
2105
2106 LogFlowFunc(("before decrement %u\n", pBusLogicDevice->cOutstandingRequests));
2107 ASMAtomicDecU32(&pBusLogicDevice->cOutstandingRequests);
2108 LogFlowFunc(("after decrement %u\n", pBusLogicDevice->cOutstandingRequests));
2109
2110 if (fRedo)
2111 {
2112 if (!pTaskState->fBIOS)
2113 {
2114 buslogicDataBufferFree(pTaskState);
2115
2116 if (pTaskState->pbSenseBuffer)
2117 buslogicSenseBufferFree(pTaskState, false /* fCopy */);
2118 }
2119
2120 /* Add to the list. */
2121 do
2122 {
2123 pTaskState->pRedoNext = ASMAtomicReadPtrT(&pBusLogic->pTasksRedoHead, PBUSLOGICTASKSTATE);
2124 } while (!ASMAtomicCmpXchgPtr(&pBusLogic->pTasksRedoHead, pTaskState, pTaskState->pRedoNext));
2125
2126 /* Suspend the VM if not done already. */
2127 if (!ASMAtomicXchgBool(&pBusLogic->fRedo, true))
2128 buslogicRedoSetWarning(pBusLogic, rcReq);
2129 }
2130 else
2131 {
2132 if (pTaskState->fBIOS)
2133 {
2134 rc = vboxscsiRequestFinished(&pBusLogic->VBoxSCSI, pSCSIRequest);
2135 AssertMsgRC(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc));
2136 }
2137 else
2138 {
2139 buslogicDataBufferFree(pTaskState);
2140
2141 if (pTaskState->pbSenseBuffer)
2142 buslogicSenseBufferFree(pTaskState, (rcCompletion != SCSI_STATUS_OK));
2143
2144 buslogicSendIncomingMailbox(pBusLogic, pTaskState,
2145 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_CMD_COMPLETED,
2146 BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_OPERATION_GOOD,
2147 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_WITHOUT_ERROR);
2148 }
2149
2150 /* Add task to the cache. */
2151 RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
2152 }
2153
2154 if (pBusLogicDevice->cOutstandingRequests == 0 && pBusLogic->fSignalIdle)
2155 PDMDevHlpAsyncNotificationCompleted(pBusLogic->pDevInsR3);
2156
2157 return VINF_SUCCESS;
2158}
2159
2160static DECLCALLBACK(int) buslogicQueryDeviceLocation(PPDMISCSIPORT pInterface, const char **ppcszController,
2161 uint32_t *piInstance, uint32_t *piLUN)
2162{
2163 PBUSLOGICDEVICE pBusLogicDevice = PDMISCSIPORT_2_PBUSLOGICDEVICE(pInterface);
2164 PPDMDEVINS pDevIns = pBusLogicDevice->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
2165
2166 AssertPtrReturn(ppcszController, VERR_INVALID_POINTER);
2167 AssertPtrReturn(piInstance, VERR_INVALID_POINTER);
2168 AssertPtrReturn(piLUN, VERR_INVALID_POINTER);
2169
2170 *ppcszController = pDevIns->pReg->szName;
2171 *piInstance = pDevIns->iInstance;
2172 *piLUN = pBusLogicDevice->iLUN;
2173
2174 return VINF_SUCCESS;
2175}
2176
2177static int buslogicDeviceSCSIRequestSetup(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState)
2178{
2179 int rc = VINF_SUCCESS;
2180
2181 /* Fetch CCB. */
2182 RTGCPHYS GCPhysAddrCCB = (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB;
2183 PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrCCB,
2184 &pTaskState->CommandControlBlockGuest, sizeof(CommandControlBlock));
2185
2186 PBUSLOGICDEVICE pTargetDevice = &pBusLogic->aDeviceStates[pTaskState->CommandControlBlockGuest.uTargetId];
2187 pTaskState->CTX_SUFF(pTargetDevice) = pTargetDevice;
2188
2189#ifdef DEBUG
2190 buslogicDumpCCBInfo(&pTaskState->CommandControlBlockGuest);
2191#endif
2192
2193 /* Alloc required buffers. */
2194 rc = buslogicDataBufferAlloc(pTaskState);
2195 AssertMsgRC(rc, ("Alloc failed rc=%Rrc\n", rc));
2196
2197 if (pTaskState->CommandControlBlockGuest.cbSenseData)
2198 {
2199 rc = buslogicSenseBufferAlloc(pTaskState);
2200 AssertMsgRC(rc, ("Mapping sense buffer failed rc=%Rrc\n", rc));
2201 }
2202
2203 /* Check if device is present on bus. If not return error immediately and don't process this further. */
2204 if (!pBusLogic->aDeviceStates[pTaskState->CommandControlBlockGuest.uTargetId].fPresent)
2205 {
2206 buslogicDataBufferFree(pTaskState);
2207
2208 if (pTaskState->pbSenseBuffer)
2209 buslogicSenseBufferFree(pTaskState, true);
2210
2211 buslogicSendIncomingMailbox(pBusLogic, pTaskState,
2212 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_SCSI_SELECTION_TIMEOUT,
2213 BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_OPERATION_GOOD,
2214 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_WITH_ERROR);
2215
2216 RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
2217 }
2218 else
2219 {
2220 /* Setup SCSI request. */
2221 pTaskState->PDMScsiRequest.uLogicalUnit = pTaskState->CommandControlBlockGuest.uLogicalUnit;
2222
2223 if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_UNKNOWN)
2224 pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_UNKNOWN;
2225 else if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_IN)
2226 pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_FROM_DEVICE;
2227 else if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_OUT)
2228 pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_TO_DEVICE;
2229 else if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_NO_DATA)
2230 pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_NONE;
2231 else
2232 AssertMsgFailed(("Invalid data direction type %d\n", pTaskState->CommandControlBlockGuest.uDataDirection));
2233
2234 pTaskState->PDMScsiRequest.cbCDB = pTaskState->CommandControlBlockGuest.cbCDB;
2235 pTaskState->PDMScsiRequest.pbCDB = pTaskState->CommandControlBlockGuest.aCDB;
2236 if (pTaskState->DataSeg.cbSeg)
2237 {
2238 pTaskState->PDMScsiRequest.cbScatterGather = pTaskState->DataSeg.cbSeg;
2239 pTaskState->PDMScsiRequest.cScatterGatherEntries = 1;
2240 pTaskState->PDMScsiRequest.paScatterGatherHead = &pTaskState->DataSeg;
2241 }
2242 else
2243 {
2244 pTaskState->PDMScsiRequest.cbScatterGather = 0;
2245 pTaskState->PDMScsiRequest.cScatterGatherEntries = 0;
2246 pTaskState->PDMScsiRequest.paScatterGatherHead = NULL;
2247 }
2248 pTaskState->PDMScsiRequest.cbSenseBuffer = pTaskState->CommandControlBlockGuest.cbSenseData;
2249 pTaskState->PDMScsiRequest.pbSenseBuffer = pTaskState->pbSenseBuffer;
2250 pTaskState->PDMScsiRequest.pvUser = pTaskState;
2251
2252 ASMAtomicIncU32(&pTargetDevice->cOutstandingRequests);
2253 rc = pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pTargetDevice->pDrvSCSIConnector, &pTaskState->PDMScsiRequest);
2254 AssertMsgRC(rc, ("Sending request to SCSI layer failed rc=%Rrc\n", rc));
2255 }
2256
2257 return rc;
2258}
2259
2260/**
2261 * Read mailbox from the guest and execute command.
2262 *
2263 * @returns VBox status code.
2264 * @param pBusLogic Pointer to the BusLogic instance data.
2265 */
2266static int buslogicProcessMailboxNext(PBUSLOGIC pBusLogic)
2267{
2268 PBUSLOGICTASKSTATE pTaskState = NULL;
2269 RTGCPHYS GCPhysAddrMailboxCurrent;
2270 int rc;
2271
2272 rc = RTMemCacheAllocEx(pBusLogic->hTaskCache, (void **)&pTaskState);
2273 AssertMsgReturn(RT_SUCCESS(rc) && (pTaskState != NULL), ("Failed to get task state from cache\n"), rc);
2274
2275 pTaskState->fBIOS = false;
2276
2277 if (!pBusLogic->fStrictRoundRobinMode)
2278 {
2279 /* Search for a filled mailbox. */
2280 do
2281 {
2282 /* Fetch mailbox from guest memory. */
2283 GCPhysAddrMailboxCurrent = pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->uMailboxOutgoingPositionCurrent * sizeof(Mailbox));
2284
2285 PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxCurrent,
2286 &pTaskState->MailboxGuest, sizeof(Mailbox));
2287
2288 pBusLogic->uMailboxOutgoingPositionCurrent++;
2289
2290 /* Check if we reached the end and start from the beginning if so. */
2291 if (pBusLogic->uMailboxOutgoingPositionCurrent >= pBusLogic->cMailbox)
2292 pBusLogic->uMailboxOutgoingPositionCurrent = 0;
2293 } while (pTaskState->MailboxGuest.u.out.uActionCode == BUSLOGIC_MAILBOX_OUTGOING_ACTION_FREE);
2294 }
2295 else
2296 {
2297 /* Fetch mailbox from guest memory. */
2298 GCPhysAddrMailboxCurrent = pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->uMailboxOutgoingPositionCurrent * sizeof(Mailbox));
2299
2300 PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxCurrent,
2301 &pTaskState->MailboxGuest, sizeof(Mailbox));
2302 }
2303
2304#ifdef DEBUG
2305 buslogicDumpMailboxInfo(&pTaskState->MailboxGuest, true);
2306#endif
2307
2308 if (pTaskState->MailboxGuest.u.out.uActionCode == BUSLOGIC_MAILBOX_OUTGOING_ACTION_START_COMMAND)
2309 rc = buslogicDeviceSCSIRequestSetup(pBusLogic, pTaskState);
2310 else if (pTaskState->MailboxGuest.u.out.uActionCode == BUSLOGIC_MAILBOX_OUTGOING_ACTION_ABORT_COMMAND)
2311 {
2312 AssertMsgFailed(("Not implemented yet\n"));
2313 }
2314 else
2315 AssertMsgFailed(("Invalid outgoing mailbox action code %u\n", pTaskState->MailboxGuest.u.out.uActionCode));
2316
2317 AssertRC(rc);
2318
2319 /* We got the mailbox, mark it as free in the guest. */
2320 pTaskState->MailboxGuest.u.out.uActionCode = BUSLOGIC_MAILBOX_OUTGOING_ACTION_FREE;
2321 PDMDevHlpPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxCurrent, &pTaskState->MailboxGuest, sizeof(Mailbox));
2322
2323 if (pBusLogic->fStrictRoundRobinMode)
2324 {
2325 pBusLogic->uMailboxOutgoingPositionCurrent++;
2326
2327 /* Check if we reached the end and start from the beginning if so. */
2328 if (pBusLogic->uMailboxOutgoingPositionCurrent >= pBusLogic->cMailbox)
2329 pBusLogic->uMailboxOutgoingPositionCurrent = 0;
2330 }
2331
2332 return rc;
2333}
2334
2335/**
2336 * Transmit queue consumer
2337 * Queue a new async task.
2338 *
2339 * @returns Success indicator.
2340 * If false the item will not be removed and the flushing will stop.
2341 * @param pDevIns The device instance.
2342 * @param pItem The item to consume. Upon return this item will be freed.
2343 */
2344static DECLCALLBACK(bool) buslogicNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
2345{
2346 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2347 uint32_t cMailboxesReady = 0;
2348
2349 /* Reset notification send flag now. */
2350 Assert(pBusLogic->fNotificationSend);
2351 ASMAtomicXchgBool(&pBusLogic->fNotificationSend, false);
2352
2353 /*
2354 * It is possible that there is a notification send but that there is no mailbox ready
2355 * in the SMP case. Just do nothing.
2356 */
2357 cMailboxesReady = ASMAtomicXchgU32(&pBusLogic->cMailboxesReady, 0);
2358 if (!cMailboxesReady)
2359 return true;
2360
2361 /* Process mailboxes. */
2362 do
2363 {
2364 int rc;
2365
2366 rc = buslogicProcessMailboxNext(pBusLogic);
2367 AssertMsgRC(rc, ("Processing mailbox failed rc=%Rrc\n", rc));
2368 } while (--cMailboxesReady > 0);
2369
2370 return true;
2371}
2372
2373/**
2374 * Kicks the controller to process pending tasks after the VM was resumed
2375 * or loaded from a saved state.
2376 *
2377 * @returns nothing.
2378 * @param pThis The LsiLogic device instance.
2379 */
2380static void buslogicKick(PBUSLOGIC pThis)
2381{
2382 if (pThis->fRedo)
2383 {
2384 pThis->fRedo = false;
2385 if (pThis->VBoxSCSI.fBusy)
2386 {
2387
2388 /* The BIOS had a request active when we got suspended. Resume it. */
2389 int rc = buslogicPrepareBIOSSCSIRequest(pThis);
2390 AssertRC(rc);
2391 }
2392 else
2393 {
2394 /* Queue all pending tasks again. */
2395 PBUSLOGICTASKSTATE pTaskState = pThis->pTasksRedoHead;
2396
2397 pThis->pTasksRedoHead = NULL;
2398
2399 while (pTaskState)
2400 {
2401 PBUSLOGICTASKSTATE pCur = pTaskState;
2402
2403 int rc = buslogicDeviceSCSIRequestSetup(pThis, pCur);
2404 AssertRC(rc);
2405
2406 pTaskState = pTaskState->pRedoNext;
2407 }
2408 }
2409 }
2410}
2411
2412static DECLCALLBACK(int) buslogicLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
2413{
2414 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2415
2416 /* Save the device config. */
2417 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aDeviceStates); i++)
2418 SSMR3PutBool(pSSM, pThis->aDeviceStates[i].fPresent);
2419
2420 return VINF_SSM_DONT_CALL_AGAIN;
2421}
2422
2423static DECLCALLBACK(int) buslogicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
2424{
2425 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2426
2427 /* Every device first. */
2428 for (unsigned i = 0; i < RT_ELEMENTS(pBusLogic->aDeviceStates); i++)
2429 {
2430 PBUSLOGICDEVICE pDevice = &pBusLogic->aDeviceStates[i];
2431
2432 AssertMsg(!pDevice->cOutstandingRequests,
2433 ("There are still outstanding requests on this device\n"));
2434 SSMR3PutBool(pSSM, pDevice->fPresent);
2435 SSMR3PutU32(pSSM, pDevice->cOutstandingRequests);
2436 }
2437 /* Now the main device state. */
2438 SSMR3PutU8 (pSSM, pBusLogic->regStatus);
2439 SSMR3PutU8 (pSSM, pBusLogic->regInterrupt);
2440 SSMR3PutU8 (pSSM, pBusLogic->regGeometry);
2441 SSMR3PutMem (pSSM, &pBusLogic->LocalRam, sizeof(pBusLogic->LocalRam));
2442 SSMR3PutU8 (pSSM, pBusLogic->uOperationCode);
2443 SSMR3PutMem (pSSM, &pBusLogic->aCommandBuffer, sizeof(pBusLogic->aCommandBuffer));
2444 SSMR3PutU8 (pSSM, pBusLogic->iParameter);
2445 SSMR3PutU8 (pSSM, pBusLogic->cbCommandParametersLeft);
2446 SSMR3PutBool (pSSM, pBusLogic->fUseLocalRam);
2447 SSMR3PutMem (pSSM, pBusLogic->aReplyBuffer, sizeof(pBusLogic->aReplyBuffer));
2448 SSMR3PutU8 (pSSM, pBusLogic->iReply);
2449 SSMR3PutU8 (pSSM, pBusLogic->cbReplyParametersLeft);
2450 SSMR3PutBool (pSSM, pBusLogic->fIRQEnabled);
2451 SSMR3PutBool (pSSM, pBusLogic->fISAEnabled);
2452 SSMR3PutU32 (pSSM, pBusLogic->cMailbox);
2453 SSMR3PutGCPhys(pSSM, pBusLogic->GCPhysAddrMailboxOutgoingBase);
2454 SSMR3PutU32 (pSSM, pBusLogic->uMailboxOutgoingPositionCurrent);
2455 SSMR3PutU32 (pSSM, pBusLogic->cMailboxesReady);
2456 SSMR3PutBool (pSSM, pBusLogic->fNotificationSend);
2457 SSMR3PutGCPhys(pSSM, pBusLogic->GCPhysAddrMailboxIncomingBase);
2458 SSMR3PutU32 (pSSM, pBusLogic->uMailboxIncomingPositionCurrent);
2459 SSMR3PutBool (pSSM, pBusLogic->fStrictRoundRobinMode);
2460 SSMR3PutBool (pSSM, pBusLogic->fExtendedLunCCBFormat);
2461 /* Now the data for the BIOS interface. */
2462 SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.regIdentify);
2463 SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.uTargetDevice);
2464 SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.uTxDir);
2465 SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.cbCDB);
2466 SSMR3PutMem (pSSM, pBusLogic->VBoxSCSI.aCDB, sizeof(pBusLogic->VBoxSCSI.aCDB));
2467 SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.iCDB);
2468 SSMR3PutU32 (pSSM, pBusLogic->VBoxSCSI.cbBuf);
2469 SSMR3PutU32 (pSSM, pBusLogic->VBoxSCSI.iBuf);
2470 SSMR3PutBool (pSSM, pBusLogic->VBoxSCSI.fBusy);
2471 SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.enmState);
2472 if (pBusLogic->VBoxSCSI.cbBuf)
2473 SSMR3PutMem(pSSM, pBusLogic->VBoxSCSI.pBuf, pBusLogic->VBoxSCSI.cbBuf);
2474
2475 /*
2476 * Save the physical addresses of the command control blocks of still pending tasks.
2477 * They are processed again on resume.
2478 *
2479 * The number of pending tasks needs to be determined first.
2480 */
2481 uint32_t cTasks = 0;
2482
2483 PBUSLOGICTASKSTATE pTaskState = pBusLogic->pTasksRedoHead;
2484 if (pBusLogic->fRedo)
2485 {
2486 while (pTaskState)
2487 {
2488 cTasks++;
2489 pTaskState = pTaskState->pRedoNext;
2490 }
2491 }
2492 SSMR3PutU32(pSSM, cTasks);
2493
2494 /* Write the address of every task now. */
2495 pTaskState = pBusLogic->pTasksRedoHead;
2496 while (pTaskState)
2497 {
2498 SSMR3PutU32(pSSM, pTaskState->MailboxGuest.u32PhysAddrCCB);
2499 pTaskState = pTaskState->pRedoNext;
2500 }
2501
2502 return SSMR3PutU32(pSSM, ~0);
2503}
2504
2505static DECLCALLBACK(int) buslogicLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
2506{
2507 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2508
2509 buslogicKick(pThis);
2510 return VINF_SUCCESS;
2511}
2512
2513static DECLCALLBACK(int) buslogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
2514{
2515 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2516 int rc = VINF_SUCCESS;
2517
2518 /* We support saved states only from this and older versions. */
2519 if (uVersion > BUSLOGIC_SAVED_STATE_MINOR_VERSION)
2520 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
2521
2522 /* Every device first. */
2523 for (unsigned i = 0; i < RT_ELEMENTS(pBusLogic->aDeviceStates); i++)
2524 {
2525 PBUSLOGICDEVICE pDevice = &pBusLogic->aDeviceStates[i];
2526
2527 AssertMsg(!pDevice->cOutstandingRequests,
2528 ("There are still outstanding requests on this device\n"));
2529 bool fPresent;
2530 rc = SSMR3GetBool(pSSM, &fPresent);
2531 AssertRCReturn(rc, rc);
2532 if (pDevice->fPresent != fPresent)
2533 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target %u config mismatch: config=%RTbool state=%RTbool"), i, pDevice->fPresent, fPresent);
2534
2535 if (uPass == SSM_PASS_FINAL)
2536 SSMR3GetU32(pSSM, (uint32_t *)&pDevice->cOutstandingRequests);
2537 }
2538
2539 if (uPass != SSM_PASS_FINAL)
2540 return VINF_SUCCESS;
2541
2542 /* Now the main device state. */
2543 SSMR3GetU8 (pSSM, (uint8_t *)&pBusLogic->regStatus);
2544 SSMR3GetU8 (pSSM, (uint8_t *)&pBusLogic->regInterrupt);
2545 SSMR3GetU8 (pSSM, (uint8_t *)&pBusLogic->regGeometry);
2546 SSMR3GetMem (pSSM, &pBusLogic->LocalRam, sizeof(pBusLogic->LocalRam));
2547 SSMR3GetU8 (pSSM, &pBusLogic->uOperationCode);
2548 SSMR3GetMem (pSSM, &pBusLogic->aCommandBuffer, sizeof(pBusLogic->aCommandBuffer));
2549 SSMR3GetU8 (pSSM, &pBusLogic->iParameter);
2550 SSMR3GetU8 (pSSM, &pBusLogic->cbCommandParametersLeft);
2551 SSMR3GetBool (pSSM, &pBusLogic->fUseLocalRam);
2552 SSMR3GetMem (pSSM, pBusLogic->aReplyBuffer, sizeof(pBusLogic->aReplyBuffer));
2553 SSMR3GetU8 (pSSM, &pBusLogic->iReply);
2554 SSMR3GetU8 (pSSM, &pBusLogic->cbReplyParametersLeft);
2555 SSMR3GetBool (pSSM, &pBusLogic->fIRQEnabled);
2556 SSMR3GetBool (pSSM, &pBusLogic->fISAEnabled);
2557 SSMR3GetU32 (pSSM, &pBusLogic->cMailbox);
2558 SSMR3GetGCPhys(pSSM, &pBusLogic->GCPhysAddrMailboxOutgoingBase);
2559 SSMR3GetU32 (pSSM, &pBusLogic->uMailboxOutgoingPositionCurrent);
2560 SSMR3GetU32 (pSSM, (uint32_t *)&pBusLogic->cMailboxesReady);
2561 SSMR3GetBool (pSSM, (bool *)&pBusLogic->fNotificationSend);
2562 SSMR3GetGCPhys(pSSM, &pBusLogic->GCPhysAddrMailboxIncomingBase);
2563 SSMR3GetU32 (pSSM, &pBusLogic->uMailboxIncomingPositionCurrent);
2564 SSMR3GetBool (pSSM, &pBusLogic->fStrictRoundRobinMode);
2565 SSMR3GetBool (pSSM, &pBusLogic->fExtendedLunCCBFormat);
2566 /* Now the data for the BIOS interface. */
2567 SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.regIdentify);
2568 SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.uTargetDevice);
2569 SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.uTxDir);
2570 SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.cbCDB);
2571 SSMR3GetMem (pSSM, pBusLogic->VBoxSCSI.aCDB, sizeof(pBusLogic->VBoxSCSI.aCDB));
2572 SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.iCDB);
2573 SSMR3GetU32 (pSSM, &pBusLogic->VBoxSCSI.cbBuf);
2574 SSMR3GetU32 (pSSM, &pBusLogic->VBoxSCSI.iBuf);
2575 SSMR3GetBool(pSSM, (bool *)&pBusLogic->VBoxSCSI.fBusy);
2576 SSMR3GetU8 (pSSM, (uint8_t *)&pBusLogic->VBoxSCSI.enmState);
2577 if (pBusLogic->VBoxSCSI.cbBuf)
2578 {
2579 pBusLogic->VBoxSCSI.pBuf = (uint8_t *)RTMemAllocZ(pBusLogic->VBoxSCSI.cbBuf);
2580 if (!pBusLogic->VBoxSCSI.pBuf)
2581 {
2582 LogRel(("BusLogic: Out of memory during restore.\n"));
2583 return PDMDEV_SET_ERROR(pDevIns, VERR_NO_MEMORY,
2584 N_("BusLogic: Out of memory during restore\n"));
2585 }
2586 SSMR3GetMem(pSSM, pBusLogic->VBoxSCSI.pBuf, pBusLogic->VBoxSCSI.cbBuf);
2587 }
2588
2589 if (pBusLogic->VBoxSCSI.fBusy)
2590 pBusLogic->fRedo = true;
2591
2592 if (uVersion > BUSLOGIC_SAVED_STATE_MINOR_PRE_ERROR_HANDLING)
2593 {
2594 /* Check if there are pending tasks saved. */
2595 uint32_t cTasks = 0;
2596
2597 SSMR3GetU32(pSSM, &cTasks);
2598
2599 if (cTasks)
2600 pBusLogic->fRedo = true;
2601
2602 for (uint32_t i = 0; i < cTasks; i++)
2603 {
2604 PBUSLOGICTASKSTATE pTaskState = (PBUSLOGICTASKSTATE)RTMemCacheAlloc(pBusLogic->hTaskCache);
2605 if (!pTaskState)
2606 {
2607 rc = VERR_NO_MEMORY;
2608 break;
2609 }
2610
2611 rc = SSMR3GetU32(pSSM, &pTaskState->MailboxGuest.u32PhysAddrCCB);
2612 if (RT_FAILURE(rc))
2613 {
2614 RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
2615 break;
2616 }
2617
2618 /* Link into the list. */
2619 pTaskState->pRedoNext = pBusLogic->pTasksRedoHead;
2620 pBusLogic->pTasksRedoHead = pTaskState;
2621 }
2622 }
2623
2624 if (RT_SUCCESS(rc))
2625 {
2626 uint32_t u32;
2627 rc = SSMR3GetU32(pSSM, &u32);
2628 if (RT_SUCCESS(rc))
2629 AssertMsgReturn(u32 == ~0U, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
2630 }
2631
2632 return rc;
2633}
2634
2635/**
2636 * Gets the pointer to the status LED of a device - called from the SCSi driver.
2637 *
2638 * @returns VBox status code.
2639 * @param pInterface Pointer to the interface structure containing the called function pointer.
2640 * @param iLUN The unit which status LED we desire. Always 0 here as the driver
2641 * doesn't know about other LUN's.
2642 * @param ppLed Where to store the LED pointer.
2643 */
2644static DECLCALLBACK(int) buslogicDeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
2645{
2646 PBUSLOGICDEVICE pDevice = PDMILEDPORTS_2_PBUSLOGICDEVICE(pInterface);
2647 if (iLUN == 0)
2648 {
2649 *ppLed = &pDevice->Led;
2650 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
2651 return VINF_SUCCESS;
2652 }
2653 return VERR_PDM_LUN_NOT_FOUND;
2654}
2655
2656/**
2657 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2658 */
2659static DECLCALLBACK(void *) buslogicDeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2660{
2661 PBUSLOGICDEVICE pDevice = PDMIBASE_2_PBUSLOGICDEVICE(pInterface);
2662 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevice->IBase);
2663 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSIPORT, &pDevice->ISCSIPort);
2664 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pDevice->ILed);
2665 return NULL;
2666}
2667
2668/**
2669 * Gets the pointer to the status LED of a unit.
2670 *
2671 * @returns VBox status code.
2672 * @param pInterface Pointer to the interface structure containing the called function pointer.
2673 * @param iLUN The unit which status LED we desire.
2674 * @param ppLed Where to store the LED pointer.
2675 */
2676static DECLCALLBACK(int) buslogicStatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
2677{
2678 PBUSLOGIC pBusLogic = PDMILEDPORTS_2_PBUSLOGIC(pInterface);
2679 if (iLUN < BUSLOGIC_MAX_DEVICES)
2680 {
2681 *ppLed = &pBusLogic->aDeviceStates[iLUN].Led;
2682 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
2683 return VINF_SUCCESS;
2684 }
2685 return VERR_PDM_LUN_NOT_FOUND;
2686}
2687
2688/**
2689 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2690 */
2691static DECLCALLBACK(void *) buslogicStatusQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2692{
2693 PBUSLOGIC pThis = PDMIBASE_2_PBUSLOGIC(pInterface);
2694 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
2695 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
2696 return NULL;
2697}
2698
2699/* -=-=-=-=- Helper -=-=-=-=- */
2700
2701 /**
2702 * Checks if all asynchronous I/O is finished.
2703 *
2704 * Used by lsilogicReset, lsilogicSuspend and lsilogicPowerOff.
2705 *
2706 * @returns true if quiesced, false if busy.
2707 * @param pDevIns The device instance.
2708 */
2709static bool buslogicR3AllAsyncIOIsFinished(PPDMDEVINS pDevIns)
2710{
2711 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2712
2713 for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aDeviceStates); i++)
2714 {
2715 PBUSLOGICDEVICE pThisDevice = &pThis->aDeviceStates[i];
2716 if (pThisDevice->pDrvBase)
2717 {
2718 if (pThisDevice->cOutstandingRequests != 0)
2719 return false;
2720 }
2721 }
2722
2723 return true;
2724}
2725
2726/**
2727 * Callback employed by lsilogicR3Suspend and lsilogicR3PowerOff..
2728 *
2729 * @returns true if we've quiesced, false if we're still working.
2730 * @param pDevIns The device instance.
2731 */
2732static DECLCALLBACK(bool) buslogicR3IsAsyncSuspendOrPowerOffDone(PPDMDEVINS pDevIns)
2733{
2734 if (!buslogicR3AllAsyncIOIsFinished(pDevIns))
2735 return false;
2736
2737 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2738 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
2739 return true;
2740}
2741
2742/**
2743 * Common worker for ahciR3Suspend and ahciR3PowerOff.
2744 */
2745static void buslogicR3SuspendOrPowerOff(PPDMDEVINS pDevIns, bool fPowerOff)
2746{
2747 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2748
2749 ASMAtomicWriteBool(&pThis->fSignalIdle, true);
2750 if (!buslogicR3AllAsyncIOIsFinished(pDevIns))
2751 PDMDevHlpSetAsyncNotification(pDevIns, buslogicR3IsAsyncSuspendOrPowerOffDone);
2752 else
2753 {
2754 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
2755
2756 AssertMsg(!pThis->fNotificationSend, ("The PDM Queue should be empty at this point\n"));
2757
2758 if (pThis->fRedo)
2759 {
2760 if (fPowerOff)
2761 {
2762 /* Free tasks which would have been queued again on resume. */
2763 PBUSLOGICTASKSTATE pTaskState = pThis->pTasksRedoHead;
2764
2765 pThis->pTasksRedoHead = NULL;
2766
2767 while (pTaskState)
2768 {
2769 PBUSLOGICTASKSTATE pFree;
2770
2771 pFree = pTaskState;
2772 pTaskState = pTaskState->pRedoNext;
2773
2774 RTMemCacheFree(pThis->hTaskCache, pFree);
2775 }
2776 pThis->fRedo = false;
2777 }
2778 else if (pThis->VBoxSCSI.fBusy)
2779 {
2780 /* Destroy the task because the BIOS interface has all necessary information. */
2781 Assert(pThis->pTasksRedoHead->fBIOS);
2782 Assert(!pThis->pTasksRedoHead->pRedoNext);
2783
2784 RTMemCacheFree(pThis->hTaskCache, pThis->pTasksRedoHead);
2785 pThis->pTasksRedoHead = NULL;
2786 }
2787 }
2788 }
2789}
2790
2791/**
2792 * Suspend notification.
2793 *
2794 * @param pDevIns The device instance data.
2795 */
2796static DECLCALLBACK(void) buslogicSuspend(PPDMDEVINS pDevIns)
2797{
2798 Log(("buslogicSuspend\n"));
2799 buslogicR3SuspendOrPowerOff(pDevIns, false /* fPoweroff */);
2800}
2801
2802/**
2803 * Resume notification.
2804 *
2805 * @param pDevIns The device instance data.
2806 */
2807static DECLCALLBACK(void) buslogicResume(PPDMDEVINS pDevIns)
2808{
2809 Log(("buslogicResume\n"));
2810 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2811 buslogicKick(pThis);
2812}
2813
2814
2815/**
2816 * Detach notification.
2817 *
2818 * One harddisk at one port has been unplugged.
2819 * The VM is suspended at this point.
2820 *
2821 * @param pDevIns The device instance.
2822 * @param iLUN The logical unit which is being detached.
2823 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
2824 */
2825static DECLCALLBACK(void) buslogicDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2826{
2827 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2828 PBUSLOGICDEVICE pDevice = &pThis->aDeviceStates[iLUN];
2829
2830 Log(("%s:\n", __FUNCTION__));
2831
2832 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
2833 ("BusLogic: Device does not support hotplugging\n"));
2834
2835 /*
2836 * Zero some important members.
2837 */
2838 pDevice->pDrvBase = NULL;
2839 pDevice->fPresent = false;
2840 pDevice->pDrvSCSIConnector = NULL;
2841}
2842
2843/**
2844 * Attach command.
2845 *
2846 * This is called when we change block driver.
2847 *
2848 * @returns VBox status code.
2849 * @param pDevIns The device instance.
2850 * @param iLUN The logical unit which is being detached.
2851 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
2852 */
2853static DECLCALLBACK(int) buslogicAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2854{
2855 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2856 PBUSLOGICDEVICE pDevice = &pThis->aDeviceStates[iLUN];
2857 int rc;
2858
2859 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
2860 ("BusLogic: Device does not support hotplugging\n"),
2861 VERR_INVALID_PARAMETER);
2862
2863 /* the usual paranoia */
2864 AssertRelease(!pDevice->pDrvBase);
2865 AssertRelease(!pDevice->pDrvSCSIConnector);
2866 Assert(pDevice->iLUN == iLUN);
2867
2868 /*
2869 * Try attach the block device and get the interfaces,
2870 * required as well as optional.
2871 */
2872 rc = PDMDevHlpDriverAttach(pDevIns, pDevice->iLUN, &pDevice->IBase, &pDevice->pDrvBase, NULL);
2873 if (RT_SUCCESS(rc))
2874 {
2875 /* Get SCSI connector interface. */
2876 pDevice->pDrvSCSIConnector = PDMIBASE_QUERY_INTERFACE(pDevice->pDrvBase, PDMISCSICONNECTOR);
2877 AssertMsgReturn(pDevice->pDrvSCSIConnector, ("Missing SCSI interface below\n"), VERR_PDM_MISSING_INTERFACE);
2878 pDevice->fPresent = true;
2879 }
2880 else
2881 AssertMsgFailed(("Failed to attach LUN#%d. rc=%Rrc\n", pDevice->iLUN, rc));
2882
2883 if (RT_FAILURE(rc))
2884 {
2885 pDevice->pDrvBase = NULL;
2886 pDevice->pDrvSCSIConnector = NULL;
2887 }
2888 return rc;
2889}
2890
2891/**
2892 * Callback employed by buslogicR3Reset.
2893 *
2894 * @returns true if we've quiesced, false if we're still working.
2895 * @param pDevIns The device instance.
2896 */
2897static DECLCALLBACK(bool) buslogicR3IsAsyncResetDone(PPDMDEVINS pDevIns)
2898{
2899 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2900
2901 if (!buslogicR3AllAsyncIOIsFinished(pDevIns))
2902 return false;
2903 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
2904
2905 buslogicHwReset(pThis);
2906 return true;
2907}
2908
2909/**
2910 * @copydoc FNPDMDEVRESET
2911 */
2912static DECLCALLBACK(void) buslogicReset(PPDMDEVINS pDevIns)
2913{
2914 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2915
2916 ASMAtomicWriteBool(&pThis->fSignalIdle, true);
2917 if (!buslogicR3AllAsyncIOIsFinished(pDevIns))
2918 PDMDevHlpSetAsyncNotification(pDevIns, buslogicR3IsAsyncResetDone);
2919 else
2920 {
2921 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
2922 buslogicHwReset(pThis);
2923 }
2924}
2925
2926static DECLCALLBACK(void) buslogicRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
2927{
2928 uint32_t i;
2929 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2930
2931 pBusLogic->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
2932 pBusLogic->pNotifierQueueRC = PDMQueueRCPtr(pBusLogic->pNotifierQueueR3);
2933
2934 for (i = 0; i < BUSLOGIC_MAX_DEVICES; i++)
2935 {
2936 PBUSLOGICDEVICE pDevice = &pBusLogic->aDeviceStates[i];
2937
2938 pDevice->pBusLogicRC = PDMINS_2_DATA_RCPTR(pDevIns);
2939 }
2940
2941}
2942
2943/**
2944 * Poweroff notification.
2945 *
2946 * @param pDevIns Pointer to the device instance
2947 */
2948static DECLCALLBACK(void) buslogicPowerOff(PPDMDEVINS pDevIns)
2949{
2950 Log(("buslogicPowerOff\n"));
2951 buslogicR3SuspendOrPowerOff(pDevIns, true /* fPoweroff */);
2952}
2953
2954/**
2955 * Destroy a driver instance.
2956 *
2957 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
2958 * resources can be freed correctly.
2959 *
2960 * @param pDevIns The device instance data.
2961 */
2962static DECLCALLBACK(int) buslogicDestruct(PPDMDEVINS pDevIns)
2963{
2964 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2965 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
2966
2967 PDMR3CritSectDelete(&pThis->CritSectIntr);
2968
2969 /*
2970 * Free all tasks which are still hanging around
2971 * (Power off after the VM was suspended).
2972 */
2973 if (pThis->fRedo)
2974 {
2975 /* Free tasks which would have been queued again on resume. */
2976 PBUSLOGICTASKSTATE pTaskState = pThis->pTasksRedoHead;
2977
2978 pThis->pTasksRedoHead = NULL;
2979
2980 while (pTaskState)
2981 {
2982 PBUSLOGICTASKSTATE pFree;
2983
2984 pFree = pTaskState;
2985 pTaskState = pTaskState->pRedoNext;
2986
2987 RTMemCacheFree(pThis->hTaskCache, pFree);
2988 }
2989 pThis->fRedo = false;
2990 }
2991
2992 int rc = RTMemCacheDestroy(pThis->hTaskCache);
2993 AssertMsgRC(rc, ("Destroying task cache failed rc=%Rrc\n", rc));
2994
2995 return rc;
2996}
2997
2998/**
2999 * @interface_method_impl{PDMDEVREG,pfnConstruct}
3000 */
3001static DECLCALLBACK(int) buslogicConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
3002{
3003 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3004 int rc = VINF_SUCCESS;
3005 bool fBootable = true;
3006 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
3007
3008 /*
3009 * Validate and read configuration.
3010 */
3011 if (!CFGMR3AreValuesValid(pCfg,
3012 "GCEnabled\0"
3013 "R0Enabled\0"
3014 "Bootable\0"))
3015 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
3016 N_("BusLogic configuration error: unknown option specified"));
3017
3018 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true);
3019 if (RT_FAILURE(rc))
3020 return PDMDEV_SET_ERROR(pDevIns, rc,
3021 N_("BusLogic configuration error: failed to read GCEnabled as boolean"));
3022 Log(("%s: fGCEnabled=%d\n", __FUNCTION__, pThis->fGCEnabled));
3023
3024 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
3025 if (RT_FAILURE(rc))
3026 return PDMDEV_SET_ERROR(pDevIns, rc,
3027 N_("BusLogic configuration error: failed to read R0Enabled as boolean"));
3028 Log(("%s: fR0Enabled=%d\n", __FUNCTION__, pThis->fR0Enabled));
3029 rc = CFGMR3QueryBoolDef(pCfg, "Bootable", &fBootable, true);
3030 if (RT_FAILURE(rc))
3031 return PDMDEV_SET_ERROR(pDevIns, rc,
3032 N_("BusLogic configuration error: failed to read Bootable as boolean"));
3033 Log(("%s: fBootable=%RTbool\n", __FUNCTION__, fBootable));
3034
3035 pThis->pDevInsR3 = pDevIns;
3036 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
3037 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
3038 pThis->IBase.pfnQueryInterface = buslogicStatusQueryInterface;
3039 pThis->ILeds.pfnQueryStatusLed = buslogicStatusQueryStatusLed;
3040
3041 PCIDevSetVendorId (&pThis->dev, 0x104b); /* BusLogic */
3042 PCIDevSetDeviceId (&pThis->dev, 0x1040); /* BT-958 */
3043 PCIDevSetCommand (&pThis->dev, 0x0003);
3044 PCIDevSetRevisionId (&pThis->dev, 0x01);
3045 PCIDevSetClassProg (&pThis->dev, 0x00); /* SCSI */
3046 PCIDevSetClassSub (&pThis->dev, 0x00); /* SCSI */
3047 PCIDevSetClassBase (&pThis->dev, 0x01); /* Mass storage */
3048 PCIDevSetBaseAddress (&pThis->dev, 0, true /*IO*/, false /*Pref*/, false /*64-bit*/, 0x00000000);
3049 PCIDevSetBaseAddress (&pThis->dev, 1, false /*IO*/, false /*Pref*/, false /*64-bit*/, 0x00000000);
3050 PCIDevSetSubSystemVendorId(&pThis->dev, 0x104b);
3051 PCIDevSetSubSystemId (&pThis->dev, 0x1040);
3052 PCIDevSetInterruptLine (&pThis->dev, 0x00);
3053 PCIDevSetInterruptPin (&pThis->dev, 0x01);
3054
3055 /*
3056 * Register the PCI device, it's I/O regions.
3057 */
3058 rc = PDMDevHlpPCIRegister (pDevIns, &pThis->dev);
3059 if (RT_FAILURE(rc))
3060 return rc;
3061
3062 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 32, PCI_ADDRESS_SPACE_IO, buslogicMMIOMap);
3063 if (RT_FAILURE(rc))
3064 return rc;
3065
3066 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, 32, PCI_ADDRESS_SPACE_MEM, buslogicMMIOMap);
3067 if (RT_FAILURE(rc))
3068 return rc;
3069
3070 if (fBootable)
3071 {
3072 /* Register I/O port space in ISA region for BIOS access. */
3073 rc = PDMDevHlpIOPortRegister(pDevIns, BUSLOGIC_ISA_IO_PORT, 3, NULL,
3074 buslogicIsaIOPortWrite, buslogicIsaIOPortRead,
3075 buslogicIsaIOPortWriteStr, buslogicIsaIOPortReadStr,
3076 "BusLogic BIOS");
3077 if (RT_FAILURE(rc))
3078 return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register legacy I/O handlers"));
3079 }
3080
3081 /* Initialize task cache. */
3082 rc = RTMemCacheCreate(&pThis->hTaskCache, sizeof(BUSLOGICTASKSTATE), 0, UINT32_MAX,
3083 NULL, NULL, NULL, 0);
3084 if (RT_FAILURE(rc))
3085 return PDMDEV_SET_ERROR(pDevIns, rc,
3086 N_("BusLogic: Failed to initialize task cache\n"));
3087
3088 /* Initialize task queue. */
3089 rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 5, 0,
3090 buslogicNotifyQueueConsumer, true, "BugLogicTask", &pThis->pNotifierQueueR3);
3091 if (RT_FAILURE(rc))
3092 return rc;
3093 pThis->pNotifierQueueR0 = PDMQueueR0Ptr(pThis->pNotifierQueueR3);
3094 pThis->pNotifierQueueRC = PDMQueueRCPtr(pThis->pNotifierQueueR3);
3095
3096 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSectIntr, RT_SRC_POS, "BusLogic-Intr");
3097 if (RT_FAILURE(rc))
3098 return PDMDEV_SET_ERROR(pDevIns, rc,
3099 N_("BusLogic: cannot create critical section"));
3100
3101 /* Initialize per device state. */
3102 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aDeviceStates); i++)
3103 {
3104 char szName[24];
3105 PBUSLOGICDEVICE pDevice = &pThis->aDeviceStates[i];
3106
3107 RTStrPrintf(szName, sizeof(szName), "Device%d", i);
3108
3109 /* Initialize static parts of the device. */
3110 pDevice->iLUN = i;
3111 pDevice->pBusLogicR3 = pThis;
3112 pDevice->pBusLogicR0 = PDMINS_2_DATA_R0PTR(pDevIns);
3113 pDevice->pBusLogicRC = PDMINS_2_DATA_RCPTR(pDevIns);
3114 pDevice->Led.u32Magic = PDMLED_MAGIC;
3115 pDevice->IBase.pfnQueryInterface = buslogicDeviceQueryInterface;
3116 pDevice->ISCSIPort.pfnSCSIRequestCompleted = buslogicDeviceSCSIRequestCompleted;
3117 pDevice->ISCSIPort.pfnQueryDeviceLocation = buslogicQueryDeviceLocation;
3118 pDevice->ILed.pfnQueryStatusLed = buslogicDeviceQueryStatusLed;
3119
3120 /* Attach SCSI driver. */
3121 rc = PDMDevHlpDriverAttach(pDevIns, pDevice->iLUN, &pDevice->IBase, &pDevice->pDrvBase, szName);
3122 if (RT_SUCCESS(rc))
3123 {
3124 /* Get SCSI connector interface. */
3125 pDevice->pDrvSCSIConnector = PDMIBASE_QUERY_INTERFACE(pDevice->pDrvBase, PDMISCSICONNECTOR);
3126 AssertMsgReturn(pDevice->pDrvSCSIConnector, ("Missing SCSI interface below\n"), VERR_PDM_MISSING_INTERFACE);
3127
3128 pDevice->fPresent = true;
3129 }
3130 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
3131 {
3132 pDevice->pDrvBase = NULL;
3133 pDevice->fPresent = false;
3134 rc = VINF_SUCCESS;
3135 Log(("BusLogic: no driver attached to device %s\n", szName));
3136 }
3137 else
3138 {
3139 AssertLogRelMsgFailed(("BusLogic: Failed to attach %s\n", szName));
3140 return rc;
3141 }
3142 }
3143
3144 /*
3145 * Attach status driver (optional).
3146 */
3147 PPDMIBASE pBase;
3148 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pBase, "Status Port");
3149 if (RT_SUCCESS(rc))
3150 pThis->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
3151 else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
3152 {
3153 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
3154 return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot attach to status driver"));
3155 }
3156
3157 rc = PDMDevHlpSSMRegisterEx(pDevIns, BUSLOGIC_SAVED_STATE_MINOR_VERSION, sizeof(*pThis), NULL,
3158 NULL, buslogicLiveExec, NULL,
3159 NULL, buslogicSaveExec, NULL,
3160 NULL, buslogicLoadExec, buslogicLoadDone);
3161 if (RT_FAILURE(rc))
3162 return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register save state handlers"));
3163
3164 rc = buslogicHwReset(pThis);
3165 AssertMsgRC(rc, ("hardware reset of BusLogic host adapter failed rc=%Rrc\n", rc));
3166
3167 return rc;
3168}
3169
3170/**
3171 * The device registration structure.
3172 */
3173const PDMDEVREG g_DeviceBusLogic =
3174{
3175 /* u32Version */
3176 PDM_DEVREG_VERSION,
3177 /* szName */
3178 "buslogic",
3179 /* szRCMod */
3180 "VBoxDDGC.gc",
3181 /* szR0Mod */
3182 "VBoxDDR0.r0",
3183 /* pszDescription */
3184 "BusLogic BT-958 SCSI host adapter.\n",
3185 /* fFlags */
3186 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0 |
3187 PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION,
3188 /* fClass */
3189 PDM_DEVREG_CLASS_STORAGE,
3190 /* cMaxInstances */
3191 ~0,
3192 /* cbInstance */
3193 sizeof(BUSLOGIC),
3194 /* pfnConstruct */
3195 buslogicConstruct,
3196 /* pfnDestruct */
3197 buslogicDestruct,
3198 /* pfnRelocate */
3199 buslogicRelocate,
3200 /* pfnIOCtl */
3201 NULL,
3202 /* pfnPowerOn */
3203 NULL,
3204 /* pfnReset */
3205 buslogicReset,
3206 /* pfnSuspend */
3207 buslogicSuspend,
3208 /* pfnResume */
3209 buslogicResume,
3210 /* pfnAttach */
3211 buslogicAttach,
3212 /* pfnDetach */
3213 buslogicDetach,
3214 /* pfnQueryInterface. */
3215 NULL,
3216 /* pfnInitComplete */
3217 NULL,
3218 /* pfnPowerOff */
3219 buslogicPowerOff,
3220 /* pfnSoftReset */
3221 NULL,
3222 /* u32VersionEnd */
3223 PDM_DEVREG_VERSION
3224};
3225
3226#endif /* IN_RING3 */
3227#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
3228
Note: See TracBrowser for help on using the repository browser.

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