VirtualBox

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

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

VMM,Devices: Automatically use a per-device lock instead of the giant IOM lock. With exception of the PIC, APIC, IOAPIC and PCI buses which are all using the PDM crit sect, there should be no calls between devices. So, this change should be relatively safe.

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

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