VirtualBox

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

Last change on this file since 54153 was 53341, checked in by vboxsync, 10 years ago

Devices/DevLsiLogic: Fixed a Guru meditation under certain conditions in raw mode. The worker thread runs independent from EMT and can force the raw context to create wakeup events even if the worker thread is not sleeping.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 161.6 KB
Line 
1/* $Id: DevBusLogic.cpp 53341 2014-11-17 15:56:12Z vboxsync $ */
2/** @file
3 * VBox storage devices - BusLogic SCSI host adapter BT-958.
4 *
5 * Based on the Multi-Master Ultra SCSI Systems Technical Reference Manual.
6 */
7
8/*
9 * Copyright (C) 2006-2013 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20
21/*******************************************************************************
22* Header Files *
23*******************************************************************************/
24#define LOG_GROUP LOG_GROUP_DEV_BUSLOGIC
25#include <VBox/vmm/pdmdev.h>
26#include <VBox/vmm/pdmifs.h>
27#include <VBox/vmm/pdmcritsect.h>
28#include <VBox/scsi.h>
29#include <iprt/asm.h>
30#include <iprt/assert.h>
31#include <iprt/string.h>
32#include <iprt/log.h>
33#ifdef IN_RING3
34# include <iprt/alloc.h>
35# include <iprt/memcache.h>
36# include <iprt/param.h>
37# include <iprt/uuid.h>
38#endif
39
40#include "VBoxSCSI.h"
41#include "VBoxDD.h"
42
43
44/*******************************************************************************
45* Defined Constants And Macros *
46*******************************************************************************/
47/** Maximum number of attached devices the adapter can handle. */
48#define BUSLOGIC_MAX_DEVICES 16
49
50/** Maximum number of scatter gather elements this device can handle. */
51#define BUSLOGIC_MAX_SCATTER_GATHER_LIST_SIZE 128
52
53/** Size of the command buffer. */
54#define BUSLOGIC_COMMAND_SIZE_MAX 53
55
56/** Size of the reply buffer. */
57#define BUSLOGIC_REPLY_SIZE_MAX 64
58
59/** Custom fixed I/O ports for BIOS controller access.
60 * Note that these should not be in the ISA range (below 400h) to avoid
61 * conflicts with ISA device probing. Addresses in the 300h-340h range should be
62 * especially avoided.
63 */
64#define BUSLOGIC_BIOS_IO_PORT 0x430
65
66/** State saved version. */
67#define BUSLOGIC_SAVED_STATE_MINOR_VERSION 4
68
69/** Saved state version before the suspend on error feature was implemented. */
70#define BUSLOGIC_SAVED_STATE_MINOR_PRE_ERROR_HANDLING 1
71/** Saved state version before 24-bit mailbox support was implemented. */
72#define BUSLOGIC_SAVED_STATE_MINOR_PRE_24BIT_MBOX 2
73/** Saved state version before command buffer size was raised. */
74#define BUSLOGIC_SAVED_STATE_MINOR_PRE_CMDBUF_RESIZE 3
75
76/** Command buffer size in old saved states. */
77#define BUSLOGIC_COMMAND_SIZE_OLD 5
78
79/** The duration of software-initiated reset (in nano seconds).
80 * Not documented, set to 50 ms. */
81#define BUSLOGIC_RESET_DURATION_NS UINT64_C(50000000)
82
83
84/*******************************************************************************
85* Structures and Typedefs *
86*******************************************************************************/
87/**
88 * State of a device attached to the buslogic host adapter.
89 *
90 * @implements PDMIBASE
91 * @implements PDMISCSIPORT
92 * @implements PDMILEDPORTS
93 */
94typedef struct BUSLOGICDEVICE
95{
96 /** Pointer to the owning buslogic device instance. - R3 pointer */
97 R3PTRTYPE(struct BUSLOGIC *) pBusLogicR3;
98 /** Pointer to the owning buslogic device instance. - R0 pointer */
99 R0PTRTYPE(struct BUSLOGIC *) pBusLogicR0;
100 /** Pointer to the owning buslogic device instance. - RC pointer */
101 RCPTRTYPE(struct BUSLOGIC *) pBusLogicRC;
102
103 /** Flag whether device is present. */
104 bool fPresent;
105 /** LUN of the device. */
106 RTUINT iLUN;
107
108#if HC_ARCH_BITS == 64
109 uint32_t Alignment0;
110#endif
111
112 /** Our base interface. */
113 PDMIBASE IBase;
114 /** SCSI port interface. */
115 PDMISCSIPORT ISCSIPort;
116 /** Led interface. */
117 PDMILEDPORTS ILed;
118 /** Pointer to the attached driver's base interface. */
119 R3PTRTYPE(PPDMIBASE) pDrvBase;
120 /** Pointer to the underlying SCSI connector interface. */
121 R3PTRTYPE(PPDMISCSICONNECTOR) pDrvSCSIConnector;
122 /** The status LED state for this device. */
123 PDMLED Led;
124
125#if HC_ARCH_BITS == 64
126 uint32_t Alignment1;
127#endif
128
129 /** Number of outstanding tasks on the port. */
130 volatile uint32_t cOutstandingRequests;
131
132} BUSLOGICDEVICE, *PBUSLOGICDEVICE;
133
134/**
135 * Commands the BusLogic adapter supports.
136 */
137enum BUSLOGICCOMMAND
138{
139 BUSLOGICCOMMAND_TEST_CMDC_INTERRUPT = 0x00,
140 BUSLOGICCOMMAND_INITIALIZE_MAILBOX = 0x01,
141 BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND = 0x02,
142 BUSLOGICCOMMAND_EXECUTE_BIOS_COMMAND = 0x03,
143 BUSLOGICCOMMAND_INQUIRE_BOARD_ID = 0x04,
144 BUSLOGICCOMMAND_ENABLE_OUTGOING_MAILBOX_AVAILABLE_INTERRUPT = 0x05,
145 BUSLOGICCOMMAND_SET_SCSI_SELECTION_TIMEOUT = 0x06,
146 BUSLOGICCOMMAND_SET_PREEMPT_TIME_ON_BUS = 0x07,
147 BUSLOGICCOMMAND_SET_TIME_OFF_BUS = 0x08,
148 BUSLOGICCOMMAND_SET_BUS_TRANSFER_RATE = 0x09,
149 BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_0_TO_7 = 0x0a,
150 BUSLOGICCOMMAND_INQUIRE_CONFIGURATION = 0x0b,
151 BUSLOGICCOMMAND_ENABLE_TARGET_MODE = 0x0c,
152 BUSLOGICCOMMAND_INQUIRE_SETUP_INFORMATION = 0x0d,
153 BUSLOGICCOMMAND_WRITE_ADAPTER_LOCAL_RAM = 0x1a,
154 BUSLOGICCOMMAND_READ_ADAPTER_LOCAL_RAM = 0x1b,
155 BUSLOGICCOMMAND_WRITE_BUSMASTER_CHIP_FIFO = 0x1c,
156 BUSLOGICCOMMAND_READ_BUSMASTER_CHIP_FIFO = 0x1d,
157 BUSLOGICCOMMAND_ECHO_COMMAND_DATA = 0x1f,
158 BUSLOGICCOMMAND_HOST_ADAPTER_DIAGNOSTIC = 0x20,
159 BUSLOGICCOMMAND_SET_ADAPTER_OPTIONS = 0x21,
160 BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_8_TO_15 = 0x23,
161 BUSLOGICCOMMAND_INQUIRE_TARGET_DEVICES = 0x24,
162 BUSLOGICCOMMAND_DISABLE_HOST_ADAPTER_INTERRUPT = 0x25,
163 BUSLOGICCOMMAND_EXT_BIOS_INFO = 0x28,
164 BUSLOGICCOMMAND_UNLOCK_MAILBOX = 0x29,
165 BUSLOGICCOMMAND_INITIALIZE_EXTENDED_MAILBOX = 0x81,
166 BUSLOGICCOMMAND_EXECUTE_SCSI_COMMAND = 0x83,
167 BUSLOGICCOMMAND_INQUIRE_FIRMWARE_VERSION_3RD_LETTER = 0x84,
168 BUSLOGICCOMMAND_INQUIRE_FIRMWARE_VERSION_LETTER = 0x85,
169 BUSLOGICCOMMAND_INQUIRE_PCI_HOST_ADAPTER_INFORMATION = 0x86,
170 BUSLOGICCOMMAND_INQUIRE_HOST_ADAPTER_MODEL_NUMBER = 0x8b,
171 BUSLOGICCOMMAND_INQUIRE_SYNCHRONOUS_PERIOD = 0x8c,
172 BUSLOGICCOMMAND_INQUIRE_EXTENDED_SETUP_INFORMATION = 0x8d,
173 BUSLOGICCOMMAND_ENABLE_STRICT_ROUND_ROBIN_MODE = 0x8f,
174 BUSLOGICCOMMAND_STORE_HOST_ADAPTER_LOCAL_RAM = 0x90,
175 BUSLOGICCOMMAND_FETCH_HOST_ADAPTER_LOCAL_RAM = 0x91,
176 BUSLOGICCOMMAND_STORE_LOCAL_DATA_IN_EEPROM = 0x92,
177 BUSLOGICCOMMAND_UPLOAD_AUTO_SCSI_CODE = 0x94,
178 BUSLOGICCOMMAND_MODIFY_IO_ADDRESS = 0x95,
179 BUSLOGICCOMMAND_SET_CCB_FORMAT = 0x96,
180 BUSLOGICCOMMAND_WRITE_INQUIRY_BUFFER = 0x9a,
181 BUSLOGICCOMMAND_READ_INQUIRY_BUFFER = 0x9b,
182 BUSLOGICCOMMAND_FLASH_ROM_UPLOAD_DOWNLOAD = 0xa7,
183 BUSLOGICCOMMAND_READ_SCAM_DATA = 0xa8,
184 BUSLOGICCOMMAND_WRITE_SCAM_DATA = 0xa9
185} BUSLOGICCOMMAND;
186
187#pragma pack(1)
188/**
189 * Auto SCSI structure which is located
190 * in host adapter RAM and contains several
191 * configuration parameters.
192 */
193typedef struct AutoSCSIRam
194{
195 uint8_t aInternalSignature[2];
196 uint8_t cbInformation;
197 uint8_t aHostAdaptertype[6];
198 uint8_t uReserved1;
199 bool fFloppyEnabled : 1;
200 bool fFloppySecondary : 1;
201 bool fLevelSensitiveInterrupt : 1;
202 unsigned char uReserved2 : 2;
203 unsigned char uSystemRAMAreForBIOS : 3;
204 unsigned char uDMAChannel : 7;
205 bool fDMAAutoConfiguration : 1;
206 unsigned char uIrqChannel : 7;
207 bool fIrqAutoConfiguration : 1;
208 uint8_t uDMATransferRate;
209 uint8_t uSCSIId;
210 bool fLowByteTerminated : 1;
211 bool fParityCheckingEnabled : 1;
212 bool fHighByteTerminated : 1;
213 bool fNoisyCablingEnvironment : 1;
214 bool fFastSynchronousNeogtiation : 1;
215 bool fBusResetEnabled : 1;
216 bool fReserved3 : 1;
217 bool fActiveNegotiationEnabled : 1;
218 uint8_t uBusOnDelay;
219 uint8_t uBusOffDelay;
220 bool fHostAdapterBIOSEnabled : 1;
221 bool fBIOSRedirectionOfInt19 : 1;
222 bool fExtendedTranslation : 1;
223 bool fMapRemovableAsFixed : 1;
224 bool fReserved4 : 1;
225 bool fBIOSSupportsMoreThan2Drives : 1;
226 bool fBIOSInterruptMode : 1;
227 bool fFlopticalSupport : 1;
228 uint16_t u16DeviceEnabledMask;
229 uint16_t u16WidePermittedMask;
230 uint16_t u16FastPermittedMask;
231 uint16_t u16SynchronousPermittedMask;
232 uint16_t u16DisconnectPermittedMask;
233 uint16_t u16SendStartUnitCommandMask;
234 uint16_t u16IgnoreInBIOSScanMask;
235 unsigned char uPCIInterruptPin : 2;
236 unsigned char uHostAdapterIoPortAddress : 2;
237 bool fStrictRoundRobinMode : 1;
238 bool fVesaBusSpeedGreaterThan33MHz : 1;
239 bool fVesaBurstWrite : 1;
240 bool fVesaBurstRead : 1;
241 uint16_t u16UltraPermittedMask;
242 uint32_t uReserved5;
243 uint8_t uReserved6;
244 uint8_t uAutoSCSIMaximumLUN;
245 bool fReserved7 : 1;
246 bool fSCAMDominant : 1;
247 bool fSCAMenabled : 1;
248 bool fSCAMLevel2 : 1;
249 unsigned char uReserved8 : 4;
250 bool fInt13Extension : 1;
251 bool fReserved9 : 1;
252 bool fCDROMBoot : 1;
253 unsigned char uReserved10 : 5;
254 unsigned char uBootTargetId : 4;
255 unsigned char uBootChannel : 4;
256 bool fForceBusDeviceScanningOrder : 1;
257 unsigned char uReserved11 : 7;
258 uint16_t u16NonTaggedToAlternateLunPermittedMask;
259 uint16_t u16RenegotiateSyncAfterCheckConditionMask;
260 uint8_t aReserved12[10];
261 uint8_t aManufacturingDiagnostic[2];
262 uint16_t u16Checksum;
263} AutoSCSIRam, *PAutoSCSIRam;
264AssertCompileSize(AutoSCSIRam, 64);
265#pragma pack()
266
267/**
268 * The local Ram.
269 */
270typedef union HostAdapterLocalRam
271{
272 /** Byte view. */
273 uint8_t u8View[256];
274 /** Structured view. */
275 struct
276 {
277 /** Offset 0 - 63 is for BIOS. */
278 uint8_t u8Bios[64];
279 /** Auto SCSI structure. */
280 AutoSCSIRam autoSCSIData;
281 } structured;
282} HostAdapterLocalRam, *PHostAdapterLocalRam;
283AssertCompileSize(HostAdapterLocalRam, 256);
284
285
286/** Ugly 24-bit big-endian addressing. */
287typedef struct
288{
289 uint8_t hi;
290 uint8_t mid;
291 uint8_t lo;
292} Addr24, Len24;
293AssertCompileSize(Addr24, 3);
294
295#define ADDR_TO_U32(x) (((x).hi << 16) | ((x).mid << 8) | (x).lo)
296#define LEN_TO_U32 ADDR_TO_U32
297#define U32_TO_ADDR(a, x) do {(a).hi = (x) >> 16; (a).mid = (x) >> 8; (a).lo = (x);} while(0)
298#define U32_TO_LEN U32_TO_ADDR
299
300/** @name Compatible ISA base I/O port addresses. Disabled if zero.
301 * @{ */
302#define NUM_ISA_BASES 8
303#define MAX_ISA_BASE (NUM_ISA_BASES - 1)
304#define ISA_BASE_DISABLED 6
305
306static uint16_t const g_aISABases[NUM_ISA_BASES] =
307{
308 0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0, 0
309};
310/** @} */
311
312/** Pointer to a task state structure. */
313typedef struct BUSLOGICTASKSTATE *PBUSLOGICTASKSTATE;
314
315/**
316 * Main BusLogic device state.
317 *
318 * @extends PCIDEVICE
319 * @implements PDMILEDPORTS
320 */
321typedef struct BUSLOGIC
322{
323 /** The PCI device structure. */
324 PCIDEVICE dev;
325 /** Pointer to the device instance - HC ptr */
326 PPDMDEVINSR3 pDevInsR3;
327 /** Pointer to the device instance - R0 ptr */
328 PPDMDEVINSR0 pDevInsR0;
329 /** Pointer to the device instance - RC ptr. */
330 PPDMDEVINSRC pDevInsRC;
331
332 /** Whether R0 is enabled. */
333 bool fR0Enabled;
334 /** Whether RC is enabled. */
335 bool fGCEnabled;
336
337 /** Base address of the I/O ports. */
338 RTIOPORT IOPortBase;
339 /** Base address of the memory mapping. */
340 RTGCPHYS MMIOBase;
341 /** Status register - Readonly. */
342 volatile uint8_t regStatus;
343 /** Interrupt register - Readonly. */
344 volatile uint8_t regInterrupt;
345 /** Geometry register - Readonly. */
346 volatile uint8_t regGeometry;
347 /** Pending (delayed) interrupt. */
348 uint8_t uPendingIntr;
349
350 /** Local RAM for the fetch hostadapter local RAM request.
351 * I don't know how big the buffer really is but the maximum
352 * seems to be 256 bytes because the offset and count field in the command request
353 * are only one byte big.
354 */
355 HostAdapterLocalRam LocalRam;
356
357 /** Command code the guest issued. */
358 uint8_t uOperationCode;
359 /** Buffer for the command parameters the adapter is currently receiving from the guest.
360 * Size of the largest command which is possible.
361 */
362 uint8_t aCommandBuffer[BUSLOGIC_COMMAND_SIZE_MAX]; /* Size of the biggest request. */
363 /** Current position in the command buffer. */
364 uint8_t iParameter;
365 /** Parameters left until the command is complete. */
366 uint8_t cbCommandParametersLeft;
367
368 /** Whether we are using the RAM or reply buffer. */
369 bool fUseLocalRam;
370 /** Buffer to store reply data from the controller to the guest. */
371 uint8_t aReplyBuffer[BUSLOGIC_REPLY_SIZE_MAX]; /* Size of the biggest reply. */
372 /** Position in the buffer we are reading next. */
373 uint8_t iReply;
374 /** Bytes left until the reply buffer is empty. */
375 uint8_t cbReplyParametersLeft;
376
377 /** Flag whether IRQs are enabled. */
378 bool fIRQEnabled;
379 /** Flag whether the ISA I/O port range is disabled
380 * to prevent the BIOS to access the device. */
381 bool fISAEnabled; /**< @todo unused, to be removed */
382 /** Flag whether 24-bit mailboxes are in use (default is 32-bit). */
383 bool fMbxIs24Bit;
384 /** ISA I/O port base (encoded in FW-compatible format). */
385 uint8_t uISABaseCode;
386
387 /** ISA I/O port base (disabled if zero). */
388 RTIOPORT IOISABase;
389 /** Default ISA I/O port base in FW-compatible format. */
390 uint8_t uDefaultISABaseCode;
391
392 /** Number of mailboxes the guest set up. */
393 uint32_t cMailbox;
394
395#if HC_ARCH_BITS == 64
396 uint32_t Alignment0;
397#endif
398
399 /** Time when HBA reset was last initiated. */ /**< @todo does this need to be saved? */
400 uint64_t u64ResetTime;
401 /** Physical base address of the outgoing mailboxes. */
402 RTGCPHYS GCPhysAddrMailboxOutgoingBase;
403 /** Current outgoing mailbox position. */
404 uint32_t uMailboxOutgoingPositionCurrent;
405 /** Number of mailboxes ready. */
406 volatile uint32_t cMailboxesReady;
407 /** Whether a notification to R3 was send. */
408 volatile bool fNotificationSend;
409
410#if HC_ARCH_BITS == 64
411 uint32_t Alignment1;
412#endif
413
414 /** Physical base address of the incoming mailboxes. */
415 RTGCPHYS GCPhysAddrMailboxIncomingBase;
416 /** Current incoming mailbox position. */
417 uint32_t uMailboxIncomingPositionCurrent;
418
419 /** Whether strict round robin is enabled. */
420 bool fStrictRoundRobinMode;
421 /** Whether the extended LUN CCB format is enabled for 32 possible logical units. */
422 bool fExtendedLunCCBFormat;
423
424 /** Queue to send tasks to R3. - HC ptr */
425 R3PTRTYPE(PPDMQUEUE) pNotifierQueueR3;
426 /** Queue to send tasks to R3. - HC ptr */
427 R0PTRTYPE(PPDMQUEUE) pNotifierQueueR0;
428 /** Queue to send tasks to R3. - RC ptr */
429 RCPTRTYPE(PPDMQUEUE) pNotifierQueueRC;
430
431 uint32_t Alignment2;
432
433 /** Critical section protecting access to the interrupt status register. */
434 PDMCRITSECT CritSectIntr;
435
436 /** Cache for task states. */
437 R3PTRTYPE(RTMEMCACHE) hTaskCache;
438
439 /** Device state for BIOS access. */
440 VBOXSCSI VBoxSCSI;
441
442 /** BusLogic device states. */
443 BUSLOGICDEVICE aDeviceStates[BUSLOGIC_MAX_DEVICES];
444
445 /** The base interface.
446 * @todo use PDMDEVINS::IBase */
447 PDMIBASE IBase;
448 /** Status Port - Leds interface. */
449 PDMILEDPORTS ILeds;
450 /** Partner of ILeds. */
451 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
452
453#if HC_ARCH_BITS == 64
454 uint32_t Alignment3;
455#endif
456
457 /** Indicates that PDMDevHlpAsyncNotificationCompleted should be called when
458 * a port is entering the idle state. */
459 bool volatile fSignalIdle;
460 /** Flag whether we have tasks which need to be processed again. */
461 bool volatile fRedo;
462 /** List of tasks which can be redone. */
463 R3PTRTYPE(volatile PBUSLOGICTASKSTATE) pTasksRedoHead;
464
465#ifdef LOG_ENABLED
466# if HC_ARCH_BITS == 64
467 uint32_t Alignment4;
468# endif
469
470 volatile uint32_t cInMailboxesReady;
471#endif
472
473} BUSLOGIC, *PBUSLOGIC;
474
475/** Register offsets in the I/O port space. */
476#define BUSLOGIC_REGISTER_CONTROL 0 /**< Writeonly */
477/** Fields for the control register. */
478# define BL_CTRL_RSBUS RT_BIT(4) /* Reset SCSI Bus. */
479# define BL_CTRL_RINT RT_BIT(5) /* Reset Interrupt. */
480# define BL_CTRL_RSOFT RT_BIT(6) /* Soft Reset. */
481# define BL_CTRL_RHARD RT_BIT(7) /* Hard Reset. */
482
483#define BUSLOGIC_REGISTER_STATUS 0 /**< Readonly */
484/** Fields for the status register. */
485# define BL_STAT_CMDINV RT_BIT(0) /* Command Invalid. */
486# define BL_STAT_DIRRDY RT_BIT(2) /* Data In Register Ready. */
487# define BL_STAT_CPRBSY RT_BIT(3) /* Command/Parameter Out Register Busy. */
488# define BL_STAT_HARDY RT_BIT(4) /* Host Adapter Ready. */
489# define BL_STAT_INREQ RT_BIT(5) /* Initialization Required. */
490# define BL_STAT_DFAIL RT_BIT(6) /* Diagnostic Failure. */
491# define BL_STAT_DACT RT_BIT(7) /* Diagnistic Active. */
492
493#define BUSLOGIC_REGISTER_COMMAND 1 /**< Writeonly */
494#define BUSLOGIC_REGISTER_DATAIN 1 /**< Readonly */
495#define BUSLOGIC_REGISTER_INTERRUPT 2 /**< Readonly */
496/** Fields for the interrupt register. */
497# define BL_INTR_IMBL RT_BIT(0) /* Incoming Mailbox Loaded. */
498# define BL_INTR_OMBR RT_BIT(1) /* Outgoing Mailbox Available. */
499# define BL_INTR_CMDC RT_BIT(2) /* Command Complete. */
500# define BL_INTR_RSTS RT_BIT(3) /* SCSO Bus Reset State. */
501# define BL_INTR_INTV RT_BIT(7) /* Interrupt Valid. */
502
503#define BUSLOGIC_REGISTER_GEOMETRY 3 /* Readonly */
504# define BL_GEOM_XLATEN RT_BIT(7) /* Extended geometry translation enabled. */
505
506/** Structure for the INQUIRE_PCI_HOST_ADAPTER_INFORMATION reply. */
507typedef struct ReplyInquirePCIHostAdapterInformation
508{
509 uint8_t IsaIOPort;
510 uint8_t IRQ;
511 unsigned char LowByteTerminated : 1;
512 unsigned char HighByteTerminated : 1;
513 unsigned char uReserved : 2; /* Reserved. */
514 unsigned char JP1 : 1; /* Whatever that means. */
515 unsigned char JP2 : 1; /* Whatever that means. */
516 unsigned char JP3 : 1; /* Whatever that means. */
517 /** Whether the provided info is valid. */
518 unsigned char InformationIsValid: 1;
519 uint8_t uReserved2; /* Reserved. */
520} ReplyInquirePCIHostAdapterInformation, *PReplyInquirePCIHostAdapterInformation;
521AssertCompileSize(ReplyInquirePCIHostAdapterInformation, 4);
522
523/** Structure for the INQUIRE_CONFIGURATION reply. */
524typedef struct ReplyInquireConfiguration
525{
526 unsigned char uReserved1 : 5;
527 bool fDmaChannel5 : 1;
528 bool fDmaChannel6 : 1;
529 bool fDmaChannel7 : 1;
530 bool fIrqChannel9 : 1;
531 bool fIrqChannel10 : 1;
532 bool fIrqChannel11 : 1;
533 bool fIrqChannel12 : 1;
534 unsigned char uReserved2 : 1;
535 bool fIrqChannel14 : 1;
536 bool fIrqChannel15 : 1;
537 unsigned char uReserved3 : 1;
538 unsigned char uHostAdapterId : 4;
539 unsigned char uReserved4 : 4;
540} ReplyInquireConfiguration, *PReplyInquireConfiguration;
541AssertCompileSize(ReplyInquireConfiguration, 3);
542
543/** Structure for the INQUIRE_SETUP_INFORMATION reply. */
544typedef struct ReplyInquireSetupInformationSynchronousValue
545{
546 unsigned char uOffset : 4;
547 unsigned char uTransferPeriod : 3;
548 bool fSynchronous : 1;
549}ReplyInquireSetupInformationSynchronousValue, *PReplyInquireSetupInformationSynchronousValue;
550AssertCompileSize(ReplyInquireSetupInformationSynchronousValue, 1);
551
552typedef struct ReplyInquireSetupInformation
553{
554 bool fSynchronousInitiationEnabled : 1;
555 bool fParityCheckingEnabled : 1;
556 unsigned char uReserved1 : 6;
557 uint8_t uBusTransferRate;
558 uint8_t uPreemptTimeOnBus;
559 uint8_t uTimeOffBus;
560 uint8_t cMailbox;
561 Addr24 MailboxAddress;
562 ReplyInquireSetupInformationSynchronousValue SynchronousValuesId0To7[8];
563 uint8_t uDisconnectPermittedId0To7;
564 uint8_t uSignature;
565 uint8_t uCharacterD;
566 uint8_t uHostBusType;
567 uint8_t uWideTransferPermittedId0To7;
568 uint8_t uWideTransfersActiveId0To7;
569 ReplyInquireSetupInformationSynchronousValue SynchronousValuesId8To15[8];
570 uint8_t uDisconnectPermittedId8To15;
571 uint8_t uReserved2;
572 uint8_t uWideTransferPermittedId8To15;
573 uint8_t uWideTransfersActiveId8To15;
574} ReplyInquireSetupInformation, *PReplyInquireSetupInformation;
575AssertCompileSize(ReplyInquireSetupInformation, 34);
576
577/** Structure for the INQUIRE_EXTENDED_SETUP_INFORMATION. */
578#pragma pack(1)
579typedef struct ReplyInquireExtendedSetupInformation
580{
581 uint8_t uBusType;
582 uint8_t uBiosAddress;
583 uint16_t u16ScatterGatherLimit;
584 uint8_t cMailbox;
585 uint32_t uMailboxAddressBase;
586 unsigned char uReserved1 : 2;
587 bool fFastEISA : 1;
588 unsigned char uReserved2 : 3;
589 bool fLevelSensitiveInterrupt : 1;
590 unsigned char uReserved3 : 1;
591 unsigned char aFirmwareRevision[3];
592 bool fHostWideSCSI : 1;
593 bool fHostDifferentialSCSI : 1;
594 bool fHostSupportsSCAM : 1;
595 bool fHostUltraSCSI : 1;
596 bool fHostSmartTermination : 1;
597 unsigned char uReserved4 : 3;
598} ReplyInquireExtendedSetupInformation, *PReplyInquireExtendedSetupInformation;
599AssertCompileSize(ReplyInquireExtendedSetupInformation, 14);
600#pragma pack()
601
602/** Structure for the INITIALIZE EXTENDED MAILBOX request. */
603#pragma pack(1)
604typedef struct RequestInitializeExtendedMailbox
605{
606 /** Number of mailboxes in guest memory. */
607 uint8_t cMailbox;
608 /** Physical address of the first mailbox. */
609 uint32_t uMailboxBaseAddress;
610} RequestInitializeExtendedMailbox, *PRequestInitializeExtendedMailbox;
611AssertCompileSize(RequestInitializeExtendedMailbox, 5);
612#pragma pack()
613
614/** Structure for the INITIALIZE MAILBOX request. */
615typedef struct
616{
617 /** Number of mailboxes to set up. */
618 uint8_t cMailbox;
619 /** Physical address of the first mailbox. */
620 Addr24 aMailboxBaseAddr;
621} RequestInitMbx, *PRequestInitMbx;
622AssertCompileSize(RequestInitMbx, 4);
623
624/**
625 * Structure of a mailbox in guest memory.
626 * The incoming and outgoing mailbox have the same size
627 * but the incoming one has some more fields defined which
628 * are marked as reserved in the outgoing one.
629 * The last field is also different from the type.
630 * For outgoing mailboxes it is the action and
631 * for incoming ones the completion status code for the task.
632 * We use one structure for both types.
633 */
634typedef struct Mailbox32
635{
636 /** Physical address of the CCB structure in the guest memory. */
637 uint32_t u32PhysAddrCCB;
638 /** Type specific data. */
639 union
640 {
641 /** For outgoing mailboxes. */
642 struct
643 {
644 /** Reserved */
645 uint8_t uReserved[3];
646 /** Action code. */
647 uint8_t uActionCode;
648 } out;
649 /** For incoming mailboxes. */
650 struct
651 {
652 /** The host adapter status after finishing the request. */
653 uint8_t uHostAdapterStatus;
654 /** The status of the device which executed the request after executing it. */
655 uint8_t uTargetDeviceStatus;
656 /** Reserved. */
657 uint8_t uReserved;
658 /** The completion status code of the request. */
659 uint8_t uCompletionCode;
660 } in;
661 } u;
662} Mailbox32, *PMailbox32;
663AssertCompileSize(Mailbox32, 8);
664
665/** Old style 24-bit mailbox entry. */
666typedef struct Mailbox24
667{
668 /** Mailbox command (incoming) or state (outgoing). */
669 uint8_t uCmdState;
670 /** Physical address of the CCB structure in the guest memory. */
671 Addr24 aPhysAddrCCB;
672} Mailbox24, *PMailbox24;
673AssertCompileSize(Mailbox24, 4);
674
675/**
676 * Action codes for outgoing mailboxes.
677 */
678enum BUSLOGIC_MAILBOX_OUTGOING_ACTION
679{
680 BUSLOGIC_MAILBOX_OUTGOING_ACTION_FREE = 0x00,
681 BUSLOGIC_MAILBOX_OUTGOING_ACTION_START_COMMAND = 0x01,
682 BUSLOGIC_MAILBOX_OUTGOING_ACTION_ABORT_COMMAND = 0x02
683};
684
685/**
686 * Completion codes for incoming mailboxes.
687 */
688enum BUSLOGIC_MAILBOX_INCOMING_COMPLETION
689{
690 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_FREE = 0x00,
691 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_WITHOUT_ERROR = 0x01,
692 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_ABORTED = 0x02,
693 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_ABORTED_NOT_FOUND = 0x03,
694 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_WITH_ERROR = 0x04,
695 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_INVALID_CCB = 0x05
696};
697
698/**
699 * Host adapter status for incoming mailboxes.
700 */
701enum BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS
702{
703 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_CMD_COMPLETED = 0x00,
704 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_LINKED_CMD_COMPLETED = 0x0a,
705 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_LINKED_CMD_COMPLETED_WITH_FLAG = 0x0b,
706 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_DATA_UNDERUN = 0x0c,
707 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_SCSI_SELECTION_TIMEOUT = 0x11,
708 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_DATA_OVERRUN = 0x12,
709 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_UNEXPECTED_BUS_FREE = 0x13,
710 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_INVALID_BUS_PHASE_REQUESTED = 0x14,
711 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_INVALID_OUTGOING_MAILBOX_ACTION_CODE = 0x15,
712 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_INVALID_COMMAND_OPERATION_CODE = 0x16,
713 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_LINKED_CCB_HAS_INVALID_LUN = 0x17,
714 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_INVALID_COMMAND_PARAMETER = 0x1a,
715 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_AUTO_REQUEST_SENSE_FAILED = 0x1b,
716 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_TAGGED_QUEUING_MESSAGE_REJECTED = 0x1c,
717 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_UNSUPPORTED_MESSAGE_RECEIVED = 0x1d,
718 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_HOST_ADAPTER_HARDWARE_FAILED = 0x20,
719 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_TARGET_FAILED_RESPONSE_TO_ATN = 0x21,
720 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_HOST_ADAPTER_ASSERTED_RST = 0x22,
721 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_OTHER_DEVICE_ASSERTED_RST = 0x23,
722 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_TARGET_DEVICE_RECONNECTED_IMPROPERLY = 0x24,
723 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_HOST_ADAPTER_ASSERTED_BUS_DEVICE_RESET = 0x25,
724 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_ABORT_QUEUE_GENERATED = 0x26,
725 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_HOST_ADAPTER_SOFTWARE_ERROR = 0x27,
726 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_HOST_ADAPTER_HARDWARE_TIMEOUT_ERROR = 0x30,
727 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_SCSI_PARITY_ERROR_DETECTED = 0x34
728};
729
730/**
731 * Device status codes for incoming mailboxes.
732 */
733enum BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS
734{
735 BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_OPERATION_GOOD = 0x00,
736 BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_CHECK_CONDITION = 0x02,
737 BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_DEVICE_BUSY = 0x08
738};
739
740/**
741 * Opcode types for CCB.
742 */
743enum BUSLOGIC_CCB_OPCODE
744{
745 BUSLOGIC_CCB_OPCODE_INITIATOR_CCB = 0x00,
746 BUSLOGIC_CCB_OPCODE_TARGET_CCB = 0x01,
747 BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_SCATTER_GATHER = 0x02,
748 BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH = 0x03,
749 BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER = 0x04,
750 BUSLOGIC_CCB_OPCODE_BUS_DEVICE_RESET = 0x81
751};
752
753/**
754 * Data transfer direction.
755 */
756enum BUSLOGIC_CCB_DIRECTION
757{
758 BUSLOGIC_CCB_DIRECTION_UNKNOWN = 0x00,
759 BUSLOGIC_CCB_DIRECTION_IN = 0x01,
760 BUSLOGIC_CCB_DIRECTION_OUT = 0x02,
761 BUSLOGIC_CCB_DIRECTION_NO_DATA = 0x03
762};
763
764/**
765 * The command control block for a SCSI request.
766 */
767typedef struct CCB32
768{
769 /** Opcode. */
770 uint8_t uOpcode;
771 /** Reserved */
772 unsigned char uReserved1 : 3;
773 /** Data direction for the request. */
774 unsigned char uDataDirection : 2;
775 /** Whether the request is tag queued. */
776 bool fTagQueued : 1;
777 /** Queue tag mode. */
778 unsigned char uQueueTag : 2;
779 /** Length of the SCSI CDB. */
780 uint8_t cbCDB;
781 /** Sense data length. */
782 uint8_t cbSenseData;
783 /** Data length. */
784 uint32_t cbData;
785 /** Data pointer.
786 * This points to the data region or a scatter gather list based on the opcode.
787 */
788 uint32_t u32PhysAddrData;
789 /** Reserved. */
790 uint8_t uReserved2[2];
791 /** Host adapter status. */
792 uint8_t uHostAdapterStatus;
793 /** Device adapter status. */
794 uint8_t uDeviceStatus;
795 /** The device the request is sent to. */
796 uint8_t uTargetId;
797 /**The LUN in the device. */
798 unsigned char uLogicalUnit : 5;
799 /** Legacy tag. */
800 bool fLegacyTagEnable : 1;
801 /** Legacy queue tag. */
802 unsigned char uLegacyQueueTag : 2;
803 /** The SCSI CDB. (A CDB can be 12 bytes long.) */
804 uint8_t abCDB[12];
805 /** Reserved. */
806 uint8_t uReserved3[6];
807 /** Sense data pointer. */
808 uint32_t u32PhysAddrSenseData;
809} CCB32, *PCCB32;
810AssertCompileSize(CCB32, 40);
811
812
813/**
814 * The 24-bit command control block.
815 */
816typedef struct CCB24
817{
818 /** Opcode. */
819 uint8_t uOpcode;
820 /** The LUN in the device. */
821 unsigned char uLogicalUnit : 3;
822 /** Data direction for the request. */
823 unsigned char uDataDirection : 2;
824 /** The target device ID. */
825 unsigned char uTargetId : 3;
826 /** Length of the SCSI CDB. */
827 uint8_t cbCDB;
828 /** Sense data length. */
829 uint8_t cbSenseData;
830 /** Data length. */
831 Len24 acbData;
832 /** Data pointer.
833 * This points to the data region or a scatter gather list based on the opc
834 */
835 Addr24 aPhysAddrData;
836 /** Pointer to next CCB for linked commands. */
837 Addr24 aPhysAddrLink;
838 /** Command linking identifier. */
839 uint8_t uLinkId;
840 /** Host adapter status. */
841 uint8_t uHostAdapterStatus;
842 /** Device adapter status. */
843 uint8_t uDeviceStatus;
844 /** Two unused bytes. */
845 uint8_t aReserved[2];
846 /** The SCSI CDB. (A CDB can be 12 bytes long.) */
847 uint8_t abCDB[12];
848} CCB24, *PCCB24;
849AssertCompileSize(CCB24, 30);
850
851/**
852 * The common 24-bit/32-bit command control block. The 32-bit CCB is laid out
853 * such that many fields are in the same location as in the older 24-bit CCB.
854 */
855typedef struct CCBC
856{
857 /** Opcode. */
858 uint8_t uOpcode;
859 /** The LUN in the device. */
860 unsigned char uPad1 : 3;
861 /** Data direction for the request. */
862 unsigned char uDataDirection : 2;
863 /** The target device ID. */
864 unsigned char uPad2 : 3;
865 /** Length of the SCSI CDB. */
866 uint8_t cbCDB;
867 /** Sense data length. */
868 uint8_t cbSenseData;
869 uint8_t aPad1[10];
870 /** Host adapter status. */
871 uint8_t uHostAdapterStatus;
872 /** Device adapter status. */
873 uint8_t uDeviceStatus;
874 uint8_t aPad2[2];
875 /** The SCSI CDB (up to 12 bytes). */
876 uint8_t abCDB[12];
877} CCBC, *PCCBC;
878AssertCompileSize(CCB24, 30);
879
880/* Make sure that the 24-bit/32-bit/common CCB offsets match. */
881AssertCompileMemberOffset(CCBC, cbCDB, 2);
882AssertCompileMemberOffset(CCB24, cbCDB, 2);
883AssertCompileMemberOffset(CCB32, cbCDB, 2);
884AssertCompileMemberOffset(CCBC, uHostAdapterStatus, 14);
885AssertCompileMemberOffset(CCB24, uHostAdapterStatus, 14);
886AssertCompileMemberOffset(CCB32, uHostAdapterStatus, 14);
887AssertCompileMemberOffset(CCBC, abCDB, 18);
888AssertCompileMemberOffset(CCB24, abCDB, 18);
889AssertCompileMemberOffset(CCB32, abCDB, 18);
890
891/** A union of all CCB types (24-bit/32-bit/common). */
892typedef union CCBU
893{
894 CCB32 n; /**< New 32-bit CCB. */
895 CCB24 o; /**< Old 24-bit CCB. */
896 CCBC c; /**< Common CCB subset. */
897} CCBU, *PCCBU;
898
899/** 32-bit scatter-gather list entry. */
900typedef struct SGE32
901{
902 uint32_t cbSegment;
903 uint32_t u32PhysAddrSegmentBase;
904} SGE32, *PSGE32;
905AssertCompileSize(SGE32, 8);
906
907/** 24-bit scatter-gather list entry. */
908typedef struct SGE24
909{
910 Len24 acbSegment;
911 Addr24 aPhysAddrSegmentBase;
912} SGE24, *PSGE24;
913AssertCompileSize(SGE24, 6);
914
915/**
916 * The structure for the "Execute SCSI Command" command.
917 */
918typedef struct ESCMD
919{
920 /** Data length. */
921 uint32_t cbData;
922 /** Data pointer. */
923 uint32_t u32PhysAddrData;
924 /** The device the request is sent to. */
925 uint8_t uTargetId;
926 /** The LUN in the device. */
927 uint8_t uLogicalUnit;
928 /** Reserved */
929 unsigned char uReserved1 : 3;
930 /** Data direction for the request. */
931 unsigned char uDataDirection : 2;
932 /** Reserved */
933 unsigned char uReserved2 : 3;
934 /** Length of the SCSI CDB. */
935 uint8_t cbCDB;
936 /** The SCSI CDB. (A CDB can be 12 bytes long.) */
937 uint8_t abCDB[12];
938} ESCMD, *PESCMD;
939AssertCompileSize(ESCMD, 24);
940
941/**
942 * Task state for a CCB request.
943 */
944typedef struct BUSLOGICTASKSTATE
945{
946 /** Next in the redo list. */
947 PBUSLOGICTASKSTATE pRedoNext;
948 /** Device this task is assigned to. */
949 R3PTRTYPE(PBUSLOGICDEVICE) pTargetDeviceR3;
950 /** The command control block from the guest. */
951 CCBU CommandControlBlockGuest;
952 /** Mailbox read from guest memory. */
953 Mailbox32 MailboxGuest;
954 /** The SCSI request we pass to the underlying SCSI engine. */
955 PDMSCSIREQUEST PDMScsiRequest;
956 /** Data buffer segment */
957 RTSGSEG DataSeg;
958 /** Pointer to the R3 sense buffer. */
959 uint8_t *pbSenseBuffer;
960 /** Flag whether this is a request from the BIOS. */
961 bool fBIOS;
962 /** 24-bit request flag (default is 32-bit). */
963 bool fIs24Bit;
964 /** S/G entry size (depends on the above flag). */
965 uint8_t cbSGEntry;
966} BUSLOGICTASKSTATE;
967
968#ifndef VBOX_DEVICE_STRUCT_TESTCASE
969
970#define PDMIBASE_2_PBUSLOGICDEVICE(pInterface) ( (PBUSLOGICDEVICE)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGICDEVICE, IBase)) )
971#define PDMISCSIPORT_2_PBUSLOGICDEVICE(pInterface) ( (PBUSLOGICDEVICE)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGICDEVICE, ISCSIPort)) )
972#define PDMILEDPORTS_2_PBUSLOGICDEVICE(pInterface) ( (PBUSLOGICDEVICE)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGICDEVICE, ILed)) )
973#define PDMIBASE_2_PBUSLOGIC(pInterface) ( (PBUSLOGIC)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGIC, IBase)) )
974#define PDMILEDPORTS_2_PBUSLOGIC(pInterface) ( (PBUSLOGIC)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGIC, ILeds)) )
975
976/*******************************************************************************
977* Internal Functions *
978*******************************************************************************/
979static int buslogicR3RegisterISARange(PBUSLOGIC pBusLogic, uint8_t uBaseCode);
980
981
982/**
983 * Assert IRQ line of the BusLogic adapter.
984 *
985 * @returns nothing.
986 * @param pBusLogic Pointer to the BusLogic device instance.
987 * @param fSuppressIrq Flag to suppress IRQ generation regardless of fIRQEnabled
988 * @param uFlag Type of interrupt being generated.
989 */
990static void buslogicSetInterrupt(PBUSLOGIC pBusLogic, bool fSuppressIrq, uint8_t uIrqType)
991{
992 LogFlowFunc(("pBusLogic=%#p\n", pBusLogic));
993
994 /* The CMDC interrupt has priority over IMBL and OMBR. */
995 if (uIrqType & (BL_INTR_IMBL | BL_INTR_OMBR))
996 {
997 if (!(pBusLogic->regInterrupt & BL_INTR_CMDC))
998 pBusLogic->regInterrupt |= uIrqType; /* Report now. */
999 else
1000 pBusLogic->uPendingIntr |= uIrqType; /* Report later. */
1001 }
1002 else if (uIrqType & BL_INTR_CMDC)
1003 {
1004 AssertMsg(pBusLogic->regInterrupt == 0 || pBusLogic->regInterrupt == (BL_INTR_INTV | BL_INTR_CMDC),
1005 ("regInterrupt=%02X\n", pBusLogic->regInterrupt));
1006 pBusLogic->regInterrupt |= uIrqType;
1007 }
1008 else
1009 AssertMsgFailed(("Invalid interrupt state!\n"));
1010
1011 pBusLogic->regInterrupt |= BL_INTR_INTV;
1012 if (pBusLogic->fIRQEnabled && !fSuppressIrq)
1013 PDMDevHlpPCISetIrq(pBusLogic->CTX_SUFF(pDevIns), 0, 1);
1014}
1015
1016/**
1017 * Deasserts the interrupt line of the BusLogic adapter.
1018 *
1019 * @returns nothing
1020 * @param pBuslogic Pointer to the BusLogic device instance.
1021 */
1022static void buslogicClearInterrupt(PBUSLOGIC pBusLogic)
1023{
1024 LogFlowFunc(("pBusLogic=%#p, clearing %#02x (pending %#02x)\n",
1025 pBusLogic, pBusLogic->regInterrupt, pBusLogic->uPendingIntr));
1026 pBusLogic->regInterrupt = 0;
1027 PDMDevHlpPCISetIrq(pBusLogic->CTX_SUFF(pDevIns), 0, 0);
1028 /* If there's another pending interrupt, report it now. */
1029 if (pBusLogic->uPendingIntr)
1030 {
1031 buslogicSetInterrupt(pBusLogic, false, pBusLogic->uPendingIntr);
1032 pBusLogic->uPendingIntr = 0;
1033 }
1034}
1035
1036#if defined(IN_RING3)
1037
1038/**
1039 * Advances the mailbox pointer to the next slot.
1040 */
1041DECLINLINE(void) buslogicR3OutgoingMailboxAdvance(PBUSLOGIC pBusLogic)
1042{
1043 pBusLogic->uMailboxOutgoingPositionCurrent = (pBusLogic->uMailboxOutgoingPositionCurrent + 1) % pBusLogic->cMailbox;
1044}
1045
1046/**
1047 * Initialize local RAM of host adapter with default values.
1048 *
1049 * @returns nothing.
1050 * @param pBusLogic.
1051 */
1052static void buslogicR3InitializeLocalRam(PBUSLOGIC pBusLogic)
1053{
1054 /*
1055 * These values are mostly from what I think is right
1056 * looking at the dmesg output from a Linux guest inside
1057 * a VMware server VM.
1058 *
1059 * So they don't have to be right :)
1060 */
1061 memset(pBusLogic->LocalRam.u8View, 0, sizeof(HostAdapterLocalRam));
1062 pBusLogic->LocalRam.structured.autoSCSIData.fLevelSensitiveInterrupt = true;
1063 pBusLogic->LocalRam.structured.autoSCSIData.fParityCheckingEnabled = true;
1064 pBusLogic->LocalRam.structured.autoSCSIData.fExtendedTranslation = true; /* Same as in geometry register. */
1065 pBusLogic->LocalRam.structured.autoSCSIData.u16DeviceEnabledMask = ~0; /* All enabled. Maybe mask out non present devices? */
1066 pBusLogic->LocalRam.structured.autoSCSIData.u16WidePermittedMask = ~0;
1067 pBusLogic->LocalRam.structured.autoSCSIData.u16FastPermittedMask = ~0;
1068 pBusLogic->LocalRam.structured.autoSCSIData.u16SynchronousPermittedMask = ~0;
1069 pBusLogic->LocalRam.structured.autoSCSIData.u16DisconnectPermittedMask = ~0;
1070 pBusLogic->LocalRam.structured.autoSCSIData.fStrictRoundRobinMode = pBusLogic->fStrictRoundRobinMode;
1071 pBusLogic->LocalRam.structured.autoSCSIData.u16UltraPermittedMask = ~0;
1072 /** @todo calculate checksum? */
1073}
1074
1075/**
1076 * Do a hardware reset of the buslogic adapter.
1077 *
1078 * @returns VBox status code.
1079 * @param pBusLogic Pointer to the BusLogic device instance.
1080 * @param fResetIO Flag determining whether ISA I/O should be reset.
1081 */
1082static int buslogicR3HwReset(PBUSLOGIC pBusLogic, bool fResetIO)
1083{
1084 LogFlowFunc(("pBusLogic=%#p\n", pBusLogic));
1085
1086 /* Reset registers to default values. */
1087 pBusLogic->regStatus = BL_STAT_HARDY | BL_STAT_INREQ;
1088 pBusLogic->regGeometry = BL_GEOM_XLATEN;
1089 pBusLogic->uOperationCode = 0xff; /* No command executing. */
1090 pBusLogic->iParameter = 0;
1091 pBusLogic->cbCommandParametersLeft = 0;
1092 pBusLogic->fIRQEnabled = true;
1093 pBusLogic->fStrictRoundRobinMode = false;
1094 pBusLogic->fExtendedLunCCBFormat = false;
1095 pBusLogic->uMailboxOutgoingPositionCurrent = 0;
1096 pBusLogic->uMailboxIncomingPositionCurrent = 0;
1097
1098 /* Clear any active/pending interrupts. */
1099 pBusLogic->uPendingIntr = 0;
1100 buslogicClearInterrupt(pBusLogic);
1101
1102 /* Guest-initiated HBA reset does not affect ISA port I/O. */
1103 if (fResetIO)
1104 {
1105 buslogicR3RegisterISARange(pBusLogic, pBusLogic->uDefaultISABaseCode);
1106 }
1107 buslogicR3InitializeLocalRam(pBusLogic);
1108 vboxscsiInitialize(&pBusLogic->VBoxSCSI);
1109
1110 return VINF_SUCCESS;
1111}
1112
1113#endif /* IN_RING3 */
1114
1115/**
1116 * Resets the command state machine for the next command and notifies the guest.
1117 *
1118 * @returns nothing.
1119 * @param pBusLogic Pointer to the BusLogic device instance
1120 * @param fSuppressIrq Flag to suppress IRQ generation regardless of current state
1121 */
1122static void buslogicCommandComplete(PBUSLOGIC pBusLogic, bool fSuppressIrq)
1123{
1124 LogFlowFunc(("pBusLogic=%#p\n", pBusLogic));
1125
1126 pBusLogic->fUseLocalRam = false;
1127 pBusLogic->regStatus |= BL_STAT_HARDY;
1128 pBusLogic->iReply = 0;
1129
1130 /* Modify I/O address does not generate an interrupt. */
1131 if (pBusLogic->uOperationCode != BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND)
1132 {
1133 /* Notify that the command is complete. */
1134 pBusLogic->regStatus &= ~BL_STAT_DIRRDY;
1135 buslogicSetInterrupt(pBusLogic, fSuppressIrq, BL_INTR_CMDC);
1136 }
1137
1138 pBusLogic->uOperationCode = 0xff;
1139 pBusLogic->iParameter = 0;
1140}
1141
1142#if defined(IN_RING3)
1143
1144/**
1145 * Initiates a hard reset which was issued from the guest.
1146 *
1147 * @returns nothing
1148 * @param pBusLogic Pointer to the BusLogic device instance.
1149 * @param fHardReset Flag initiating a hard (vs. soft) reset.
1150 */
1151static void buslogicR3InitiateReset(PBUSLOGIC pBusLogic, bool fHardReset)
1152{
1153 LogFlowFunc(("pBusLogic=%#p fHardReset=%d\n", pBusLogic, fHardReset));
1154
1155 buslogicR3HwReset(pBusLogic, false);
1156
1157 if (fHardReset)
1158 {
1159 /* Set the diagnostic active bit in the status register and clear the ready state. */
1160 pBusLogic->regStatus |= BL_STAT_DACT;
1161 pBusLogic->regStatus &= ~BL_STAT_HARDY;
1162
1163 /* Remember when the guest initiated a reset (after we're done resetting). */
1164 pBusLogic->u64ResetTime = PDMDevHlpTMTimeVirtGetNano(pBusLogic->CTX_SUFF(pDevIns));
1165 }
1166}
1167
1168/**
1169 * Send a mailbox with set status codes to the guest.
1170 *
1171 * @returns nothing.
1172 * @param pBusLogic Pointer to the BusLogic device instance.
1173 * @param pTaskState Pointer to the task state with the mailbox to send.
1174 * @param uHostAdapterStatus The host adapter status code to set.
1175 * @param uDeviceStatus The target device status to set.
1176 * @param uMailboxCompletionCode Completion status code to set in the mailbox.
1177 */
1178static void buslogicR3SendIncomingMailbox(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState,
1179 uint8_t uHostAdapterStatus, uint8_t uDeviceStatus,
1180 uint8_t uMailboxCompletionCode)
1181{
1182 pTaskState->MailboxGuest.u.in.uHostAdapterStatus = uHostAdapterStatus;
1183 pTaskState->MailboxGuest.u.in.uTargetDeviceStatus = uDeviceStatus;
1184 pTaskState->MailboxGuest.u.in.uCompletionCode = uMailboxCompletionCode;
1185
1186 int rc = PDMCritSectEnter(&pBusLogic->CritSectIntr, VINF_SUCCESS);
1187 AssertRC(rc);
1188
1189 RTGCPHYS GCPhysAddrMailboxIncoming = pBusLogic->GCPhysAddrMailboxIncomingBase
1190 + ( pBusLogic->uMailboxIncomingPositionCurrent
1191 * (pTaskState->fIs24Bit ? sizeof(Mailbox24) : sizeof(Mailbox32)) );
1192
1193 if (uMailboxCompletionCode != BUSLOGIC_MAILBOX_INCOMING_COMPLETION_ABORTED_NOT_FOUND)
1194 {
1195 RTGCPHYS GCPhysAddrCCB = pTaskState->MailboxGuest.u32PhysAddrCCB;
1196 LogFlowFunc(("Completing CCB %RGp hstat=%u, dstat=%u, outgoing mailbox at %RGp\n", GCPhysAddrCCB,
1197 uHostAdapterStatus, uDeviceStatus, GCPhysAddrMailboxIncoming));
1198
1199 /* Update CCB. */
1200 pTaskState->CommandControlBlockGuest.c.uHostAdapterStatus = uHostAdapterStatus;
1201 pTaskState->CommandControlBlockGuest.c.uDeviceStatus = uDeviceStatus;
1202 /* Rewrite CCB up to the CDB; perhaps more than necessary. */
1203 PDMDevHlpPCIPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrCCB,
1204 &pTaskState->CommandControlBlockGuest, RT_OFFSETOF(CCBC, abCDB));
1205 }
1206
1207# ifdef RT_STRICT
1208 uint8_t uCode;
1209 unsigned uCodeOffs = pTaskState->fIs24Bit ? RT_OFFSETOF(Mailbox24, uCmdState) : RT_OFFSETOF(Mailbox32, u.out.uActionCode);
1210 PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxIncoming + uCodeOffs, &uCode, sizeof(uCode));
1211 Assert(uCode == BUSLOGIC_MAILBOX_INCOMING_COMPLETION_FREE);
1212# endif
1213
1214 /* Update mailbox. */
1215 if (pTaskState->fIs24Bit)
1216 {
1217 Mailbox24 Mbx24;
1218
1219 Mbx24.uCmdState = pTaskState->MailboxGuest.u.in.uCompletionCode;
1220 U32_TO_ADDR(Mbx24.aPhysAddrCCB, pTaskState->MailboxGuest.u32PhysAddrCCB);
1221 Log(("24-bit mailbox: completion code=%u, CCB at %RGp\n", Mbx24.uCmdState, (RTGCPHYS)ADDR_TO_U32(Mbx24.aPhysAddrCCB)));
1222 PDMDevHlpPCIPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxIncoming, &Mbx24, sizeof(Mailbox24));
1223 }
1224 else
1225 {
1226 Log(("32-bit mailbox: completion code=%u, CCB at %RGp\n", pTaskState->MailboxGuest.u.in.uCompletionCode, (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB));
1227 PDMDevHlpPCIPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxIncoming,
1228 &pTaskState->MailboxGuest, sizeof(Mailbox32));
1229 }
1230
1231 /* Advance to next mailbox position. */
1232 pBusLogic->uMailboxIncomingPositionCurrent++;
1233 if (pBusLogic->uMailboxIncomingPositionCurrent >= pBusLogic->cMailbox)
1234 pBusLogic->uMailboxIncomingPositionCurrent = 0;
1235
1236# ifdef LOG_ENABLED
1237 ASMAtomicIncU32(&pBusLogic->cInMailboxesReady);
1238# endif
1239
1240 buslogicSetInterrupt(pBusLogic, false, BL_INTR_IMBL);
1241
1242 PDMCritSectLeave(&pBusLogic->CritSectIntr);
1243}
1244
1245# ifdef LOG_ENABLED
1246
1247/**
1248 * Dumps the content of a mailbox for debugging purposes.
1249 *
1250 * @return nothing
1251 * @param pMailbox The mailbox to dump.
1252 * @param fOutgoing true if dumping the outgoing state.
1253 * false if dumping the incoming state.
1254 */
1255static void buslogicR3DumpMailboxInfo(PMailbox32 pMailbox, bool fOutgoing)
1256{
1257 Log(("%s: Dump for %s mailbox:\n", __FUNCTION__, fOutgoing ? "outgoing" : "incoming"));
1258 Log(("%s: u32PhysAddrCCB=%#x\n", __FUNCTION__, pMailbox->u32PhysAddrCCB));
1259 if (fOutgoing)
1260 {
1261 Log(("%s: uActionCode=%u\n", __FUNCTION__, pMailbox->u.out.uActionCode));
1262 }
1263 else
1264 {
1265 Log(("%s: uHostAdapterStatus=%u\n", __FUNCTION__, pMailbox->u.in.uHostAdapterStatus));
1266 Log(("%s: uTargetDeviceStatus=%u\n", __FUNCTION__, pMailbox->u.in.uTargetDeviceStatus));
1267 Log(("%s: uCompletionCode=%u\n", __FUNCTION__, pMailbox->u.in.uCompletionCode));
1268 }
1269}
1270
1271/**
1272 * Dumps the content of a command control block for debugging purposes.
1273 *
1274 * @returns nothing.
1275 * @param pCCB Pointer to the command control block to dump.
1276 * @param fIs24BitCCB Flag to determine CCB format.
1277 */
1278static void buslogicR3DumpCCBInfo(PCCBU pCCB, bool fIs24BitCCB)
1279{
1280 Log(("%s: Dump for %s Command Control Block:\n", __FUNCTION__, fIs24BitCCB ? "24-bit" : "32-bit"));
1281 Log(("%s: uOpCode=%#x\n", __FUNCTION__, pCCB->c.uOpcode));
1282 Log(("%s: uDataDirection=%u\n", __FUNCTION__, pCCB->c.uDataDirection));
1283 Log(("%s: cbCDB=%u\n", __FUNCTION__, pCCB->c.cbCDB));
1284 Log(("%s: cbSenseData=%u\n", __FUNCTION__, pCCB->c.cbSenseData));
1285 Log(("%s: uHostAdapterStatus=%u\n", __FUNCTION__, pCCB->c.uHostAdapterStatus));
1286 Log(("%s: uDeviceStatus=%u\n", __FUNCTION__, pCCB->c.uDeviceStatus));
1287 if (fIs24BitCCB)
1288 {
1289 Log(("%s: cbData=%u\n", __FUNCTION__, LEN_TO_U32(pCCB->o.acbData)));
1290 Log(("%s: PhysAddrData=%#x\n", __FUNCTION__, ADDR_TO_U32(pCCB->o.aPhysAddrData)));
1291 Log(("%s: uTargetId=%u\n", __FUNCTION__, pCCB->o.uTargetId));
1292 Log(("%s: uLogicalUnit=%u\n", __FUNCTION__, pCCB->o.uLogicalUnit));
1293 }
1294 else
1295 {
1296 Log(("%s: cbData=%u\n", __FUNCTION__, pCCB->n.cbData));
1297 Log(("%s: PhysAddrData=%#x\n", __FUNCTION__, pCCB->n.u32PhysAddrData));
1298 Log(("%s: uTargetId=%u\n", __FUNCTION__, pCCB->n.uTargetId));
1299 Log(("%s: uLogicalUnit=%u\n", __FUNCTION__, pCCB->n.uLogicalUnit));
1300 Log(("%s: fTagQueued=%d\n", __FUNCTION__, pCCB->n.fTagQueued));
1301 Log(("%s: uQueueTag=%u\n", __FUNCTION__, pCCB->n.uQueueTag));
1302 Log(("%s: fLegacyTagEnable=%u\n", __FUNCTION__, pCCB->n.fLegacyTagEnable));
1303 Log(("%s: uLegacyQueueTag=%u\n", __FUNCTION__, pCCB->n.uLegacyQueueTag));
1304 Log(("%s: PhysAddrSenseData=%#x\n", __FUNCTION__, pCCB->n.u32PhysAddrSenseData));
1305 }
1306 Log(("%s: uCDB[0]=%#x\n", __FUNCTION__, pCCB->c.abCDB[0]));
1307 for (int i = 1; i < pCCB->c.cbCDB; i++)
1308 Log(("%s: uCDB[%d]=%u\n", __FUNCTION__, i, pCCB->c.abCDB[i]));
1309}
1310
1311# endif /* LOG_ENABLED */
1312
1313/**
1314 * Allocate data buffer.
1315 *
1316 * @param pTaskState Pointer to the task state.
1317 * @param GCSGList Guest physical address of S/G list.
1318 * @param cEntries Number of list entries to read.
1319 * @param pSGEList Pointer to 32-bit S/G list storage.
1320 */
1321static void buslogicR3ReadSGEntries(PBUSLOGICTASKSTATE pTaskState, RTGCPHYS GCSGList, uint32_t cEntries, SGE32 *pSGEList)
1322{
1323 PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
1324 SGE24 aSGE24[32];
1325 Assert(cEntries <= RT_ELEMENTS(aSGE24));
1326
1327 /* Read the S/G entries. Convert 24-bit entries to 32-bit format. */
1328 if (pTaskState->fIs24Bit)
1329 {
1330 Log2(("Converting %u 24-bit S/G entries to 32-bit\n", cEntries));
1331 PDMDevHlpPhysRead(pDevIns, GCSGList, &aSGE24, cEntries * sizeof(SGE24));
1332 for (uint32_t i = 0; i < cEntries; ++i)
1333 {
1334 pSGEList[i].cbSegment = LEN_TO_U32(aSGE24[i].acbSegment);
1335 pSGEList[i].u32PhysAddrSegmentBase = ADDR_TO_U32(aSGE24[i].aPhysAddrSegmentBase);
1336 }
1337 }
1338 else
1339 PDMDevHlpPhysRead(pDevIns, GCSGList, pSGEList, cEntries * sizeof(SGE32));
1340}
1341
1342/**
1343 * Allocate data buffer.
1344 *
1345 * @returns VBox status code.
1346 * @param pTaskState Pointer to the task state.
1347 */
1348static int buslogicR3DataBufferAlloc(PBUSLOGICTASKSTATE pTaskState)
1349{
1350 PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
1351 uint32_t cbDataCCB;
1352 uint32_t u32PhysAddrCCB;
1353
1354 /* Extract the data length and physical address from the CCB. */
1355 if (pTaskState->fIs24Bit)
1356 {
1357 u32PhysAddrCCB = ADDR_TO_U32(pTaskState->CommandControlBlockGuest.o.aPhysAddrData);
1358 cbDataCCB = LEN_TO_U32(pTaskState->CommandControlBlockGuest.o.acbData);
1359 }
1360 else
1361 {
1362 u32PhysAddrCCB = pTaskState->CommandControlBlockGuest.n.u32PhysAddrData;
1363 cbDataCCB = pTaskState->CommandControlBlockGuest.n.cbData;
1364 }
1365
1366 if ( (pTaskState->CommandControlBlockGuest.c.uDataDirection != BUSLOGIC_CCB_DIRECTION_NO_DATA)
1367 && cbDataCCB)
1368 {
1369 /** @todo Check following assumption and what residual means. */
1370 /*
1371 * The BusLogic adapter can handle two different data buffer formats.
1372 * The first one is that the data pointer entry in the CCB points to
1373 * the buffer directly. In second mode the data pointer points to a
1374 * scatter gather list which describes the buffer.
1375 */
1376 if ( (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_SCATTER_GATHER)
1377 || (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER))
1378 {
1379 uint32_t cScatterGatherGCRead;
1380 uint32_t iScatterGatherEntry;
1381 SGE32 aScatterGatherReadGC[32]; /* A buffer for scatter gather list entries read from guest memory. */
1382 uint32_t cScatterGatherGCLeft = cbDataCCB / pTaskState->cbSGEntry;
1383 RTGCPHYS GCPhysAddrScatterGatherCurrent = u32PhysAddrCCB;
1384 size_t cbDataToTransfer = 0;
1385
1386 /* Count number of bytes to transfer. */
1387 do
1388 {
1389 cScatterGatherGCRead = (cScatterGatherGCLeft < RT_ELEMENTS(aScatterGatherReadGC))
1390 ? cScatterGatherGCLeft
1391 : RT_ELEMENTS(aScatterGatherReadGC);
1392 cScatterGatherGCLeft -= cScatterGatherGCRead;
1393
1394 buslogicR3ReadSGEntries(pTaskState, GCPhysAddrScatterGatherCurrent, cScatterGatherGCRead, aScatterGatherReadGC);
1395
1396 for (iScatterGatherEntry = 0; iScatterGatherEntry < cScatterGatherGCRead; iScatterGatherEntry++)
1397 {
1398 RTGCPHYS GCPhysAddrDataBase;
1399
1400 Log(("%s: iScatterGatherEntry=%u\n", __FUNCTION__, iScatterGatherEntry));
1401
1402 GCPhysAddrDataBase = (RTGCPHYS)aScatterGatherReadGC[iScatterGatherEntry].u32PhysAddrSegmentBase;
1403 cbDataToTransfer += aScatterGatherReadGC[iScatterGatherEntry].cbSegment;
1404
1405 Log(("%s: GCPhysAddrDataBase=%RGp cbDataToTransfer=%u\n",
1406 __FUNCTION__, GCPhysAddrDataBase,
1407 aScatterGatherReadGC[iScatterGatherEntry].cbSegment));
1408 }
1409
1410 /* Set address to the next entries to read. */
1411 GCPhysAddrScatterGatherCurrent += cScatterGatherGCRead * pTaskState->cbSGEntry;
1412 } while (cScatterGatherGCLeft > 0);
1413
1414 Log(("%s: cbDataToTransfer=%d\n", __FUNCTION__, cbDataToTransfer));
1415
1416 /* Allocate buffer */
1417 pTaskState->DataSeg.cbSeg = cbDataToTransfer;
1418 pTaskState->DataSeg.pvSeg = RTMemAlloc(pTaskState->DataSeg.cbSeg);
1419 if (!pTaskState->DataSeg.pvSeg)
1420 return VERR_NO_MEMORY;
1421
1422 /* Copy the data if needed */
1423 if ( (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_OUT)
1424 || (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_UNKNOWN))
1425 {
1426 cScatterGatherGCLeft = cbDataCCB / pTaskState->cbSGEntry;
1427 GCPhysAddrScatterGatherCurrent = u32PhysAddrCCB;
1428 uint8_t *pbData = (uint8_t *)pTaskState->DataSeg.pvSeg;
1429
1430 do
1431 {
1432 cScatterGatherGCRead = (cScatterGatherGCLeft < RT_ELEMENTS(aScatterGatherReadGC))
1433 ? cScatterGatherGCLeft
1434 : RT_ELEMENTS(aScatterGatherReadGC);
1435 cScatterGatherGCLeft -= cScatterGatherGCRead;
1436
1437 buslogicR3ReadSGEntries(pTaskState, GCPhysAddrScatterGatherCurrent, cScatterGatherGCRead, aScatterGatherReadGC);
1438
1439 for (iScatterGatherEntry = 0; iScatterGatherEntry < cScatterGatherGCRead; iScatterGatherEntry++)
1440 {
1441 RTGCPHYS GCPhysAddrDataBase;
1442
1443 Log(("%s: iScatterGatherEntry=%u\n", __FUNCTION__, iScatterGatherEntry));
1444
1445 GCPhysAddrDataBase = (RTGCPHYS)aScatterGatherReadGC[iScatterGatherEntry].u32PhysAddrSegmentBase;
1446 cbDataToTransfer = aScatterGatherReadGC[iScatterGatherEntry].cbSegment;
1447
1448 Log(("%s: GCPhysAddrDataBase=%RGp cbDataToTransfer=%u\n", __FUNCTION__, GCPhysAddrDataBase, cbDataToTransfer));
1449
1450 PDMDevHlpPhysRead(pDevIns, GCPhysAddrDataBase, pbData, cbDataToTransfer);
1451 pbData += cbDataToTransfer;
1452 }
1453
1454 /* Set address to the next entries to read. */
1455 GCPhysAddrScatterGatherCurrent += cScatterGatherGCRead * pTaskState->cbSGEntry;
1456 } while (cScatterGatherGCLeft > 0);
1457 }
1458
1459 }
1460 else if ( pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB
1461 || pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH)
1462 {
1463 /* The buffer is not scattered. */
1464 RTGCPHYS GCPhysAddrDataBase = u32PhysAddrCCB;
1465
1466 AssertMsg(GCPhysAddrDataBase != 0, ("Physical address is 0\n"));
1467
1468 pTaskState->DataSeg.cbSeg = cbDataCCB;
1469 pTaskState->DataSeg.pvSeg = RTMemAlloc(pTaskState->DataSeg.cbSeg);
1470 if (!pTaskState->DataSeg.pvSeg)
1471 return VERR_NO_MEMORY;
1472
1473 Log(("Non scattered buffer:\n"));
1474 Log(("u32PhysAddrData=%#x\n", u32PhysAddrCCB));
1475 Log(("cbData=%u\n", cbDataCCB));
1476 Log(("GCPhysAddrDataBase=0x%RGp\n", GCPhysAddrDataBase));
1477
1478 /* Copy the data into the buffer. */
1479 PDMDevHlpPhysRead(pDevIns, GCPhysAddrDataBase, pTaskState->DataSeg.pvSeg, pTaskState->DataSeg.cbSeg);
1480 }
1481 }
1482
1483 return VINF_SUCCESS;
1484}
1485
1486/**
1487 * Free allocated resources used for the scatter gather list.
1488 *
1489 * @returns nothing.
1490 * @param pTaskState Pointer to the task state.
1491 */
1492static void buslogicR3DataBufferFree(PBUSLOGICTASKSTATE pTaskState)
1493{
1494 PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
1495 uint32_t cbDataCCB;
1496 uint32_t u32PhysAddrCCB;
1497
1498 /* Extract the data length and physical address from the CCB. */
1499 if (pTaskState->fIs24Bit)
1500 {
1501 u32PhysAddrCCB = ADDR_TO_U32(pTaskState->CommandControlBlockGuest.o.aPhysAddrData);
1502 cbDataCCB = LEN_TO_U32(pTaskState->CommandControlBlockGuest.o.acbData);
1503 }
1504 else
1505 {
1506 u32PhysAddrCCB = pTaskState->CommandControlBlockGuest.n.u32PhysAddrData;
1507 cbDataCCB = pTaskState->CommandControlBlockGuest.n.cbData;
1508 }
1509
1510#if 1
1511 /* Hack for NT 10/91: A CCB describes a 2K buffer, but TEST UNIT READY is executed. This command
1512 * returns no data, hence the buffer must be left alone!
1513 */
1514 if (pTaskState->CommandControlBlockGuest.c.abCDB[0] == 0)
1515 cbDataCCB = 0;
1516#endif
1517
1518 LogFlowFunc(("pTaskState=%#p cbDataCCB=%u direction=%u cbSeg=%u\n", pTaskState, cbDataCCB,
1519 pTaskState->CommandControlBlockGuest.c.uDataDirection, pTaskState->DataSeg.cbSeg));
1520
1521 if ( (cbDataCCB > 0)
1522 && ( (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_IN)
1523 || (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_UNKNOWN)))
1524 {
1525 if ( (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_SCATTER_GATHER)
1526 || (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER))
1527 {
1528 uint32_t cScatterGatherGCRead;
1529 uint32_t iScatterGatherEntry;
1530 SGE32 aScatterGatherReadGC[32]; /* Number of scatter gather list entries read from guest memory. */
1531 uint32_t cScatterGatherGCLeft = cbDataCCB / pTaskState->cbSGEntry;
1532 RTGCPHYS GCPhysAddrScatterGatherCurrent = u32PhysAddrCCB;
1533 uint8_t *pbData = (uint8_t *)pTaskState->DataSeg.pvSeg;
1534
1535 do
1536 {
1537 cScatterGatherGCRead = (cScatterGatherGCLeft < RT_ELEMENTS(aScatterGatherReadGC))
1538 ? cScatterGatherGCLeft
1539 : RT_ELEMENTS(aScatterGatherReadGC);
1540 cScatterGatherGCLeft -= cScatterGatherGCRead;
1541
1542 buslogicR3ReadSGEntries(pTaskState, GCPhysAddrScatterGatherCurrent, cScatterGatherGCRead, aScatterGatherReadGC);
1543
1544 for (iScatterGatherEntry = 0; iScatterGatherEntry < cScatterGatherGCRead; iScatterGatherEntry++)
1545 {
1546 RTGCPHYS GCPhysAddrDataBase;
1547 size_t cbDataToTransfer;
1548
1549 Log(("%s: iScatterGatherEntry=%u\n", __FUNCTION__, iScatterGatherEntry));
1550
1551 GCPhysAddrDataBase = (RTGCPHYS)aScatterGatherReadGC[iScatterGatherEntry].u32PhysAddrSegmentBase;
1552 cbDataToTransfer = aScatterGatherReadGC[iScatterGatherEntry].cbSegment;
1553
1554 Log(("%s: GCPhysAddrDataBase=%RGp cbDataToTransfer=%u\n", __FUNCTION__, GCPhysAddrDataBase, cbDataToTransfer));
1555
1556 PDMDevHlpPCIPhysWrite(pDevIns, GCPhysAddrDataBase, pbData, cbDataToTransfer);
1557 pbData += cbDataToTransfer;
1558 }
1559
1560 /* Set address to the next entries to read. */
1561 GCPhysAddrScatterGatherCurrent += cScatterGatherGCRead * pTaskState->cbSGEntry;
1562 } while (cScatterGatherGCLeft > 0);
1563
1564 }
1565 else if ( pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB
1566 || pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH)
1567 {
1568 /* The buffer is not scattered. */
1569 RTGCPHYS GCPhysAddrDataBase = u32PhysAddrCCB;
1570
1571 AssertMsg(GCPhysAddrDataBase != 0, ("Physical address is 0\n"));
1572
1573 Log(("Non-scattered buffer:\n"));
1574 Log(("u32PhysAddrData=%#x\n", u32PhysAddrCCB));
1575 Log(("cbData=%u\n", cbDataCCB));
1576 Log(("GCPhysAddrDataBase=0x%RGp\n", GCPhysAddrDataBase));
1577
1578 /* Copy the data into the guest memory. */
1579 PDMDevHlpPCIPhysWrite(pDevIns, GCPhysAddrDataBase, pTaskState->DataSeg.pvSeg, pTaskState->DataSeg.cbSeg);
1580 }
1581
1582 }
1583 /* Update residual data length. */
1584 if ( (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH)
1585 || (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER))
1586 {
1587 uint32_t cbResidual;
1588
1589 /** @todo we need to get the actual transfer length from the VSCSI layer?! */
1590 cbResidual = 0; //LEN_TO_U32(pTaskState->CCBGuest.acbData) - ???;
1591 if (pTaskState->fIs24Bit)
1592 U32_TO_LEN(pTaskState->CommandControlBlockGuest.o.acbData, cbResidual);
1593 else
1594 pTaskState->CommandControlBlockGuest.n.cbData = cbResidual;
1595 }
1596
1597 RTMemFree(pTaskState->DataSeg.pvSeg);
1598 pTaskState->DataSeg.pvSeg = NULL;
1599 pTaskState->DataSeg.cbSeg = 0;
1600}
1601
1602/** Convert sense buffer length taking into account shortcut values. */
1603static uint32_t buslogicR3ConvertSenseBufferLength(uint32_t cbSense)
1604{
1605 /* Convert special sense buffer length values. */
1606 if (cbSense == 0)
1607 cbSense = 14; /* 0 means standard 14-byte buffer. */
1608 else if (cbSense == 1)
1609 cbSense = 0; /* 1 means no sense data. */
1610 else if (cbSense < 8)
1611 AssertMsgFailed(("Reserved cbSense value of %d used!\n", cbSense));
1612
1613 return cbSense;
1614}
1615
1616/**
1617 * Free the sense buffer.
1618 *
1619 * @returns nothing.
1620 * @param pTaskState Pointer to the task state.
1621 * @param fCopy If sense data should be copied to guest memory.
1622 */
1623static void buslogicR3SenseBufferFree(PBUSLOGICTASKSTATE pTaskState, bool fCopy)
1624{
1625 uint32_t cbSenseBuffer;
1626
1627 cbSenseBuffer = buslogicR3ConvertSenseBufferLength(pTaskState->CommandControlBlockGuest.c.cbSenseData);
1628
1629 /* Copy the sense buffer into guest memory if requested. */
1630 if (fCopy && cbSenseBuffer)
1631 {
1632 PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
1633 RTGCPHYS GCPhysAddrSenseBuffer;
1634
1635 /* With 32-bit CCBs, the (optional) sense buffer physical address is provided separately.
1636 * On the other hand, with 24-bit CCBs, the sense buffer is simply located at the end of
1637 * the CCB, right after the variable-length CDB.
1638 */
1639 if (pTaskState->fIs24Bit)
1640 {
1641 GCPhysAddrSenseBuffer = pTaskState->MailboxGuest.u32PhysAddrCCB;
1642 GCPhysAddrSenseBuffer += pTaskState->CommandControlBlockGuest.c.cbCDB + RT_OFFSETOF(CCB24, abCDB);
1643 }
1644 else
1645 GCPhysAddrSenseBuffer = pTaskState->CommandControlBlockGuest.n.u32PhysAddrSenseData;
1646
1647 Log3(("%s: sense buffer: %.*Rhxs\n", __FUNCTION__, cbSenseBuffer, pTaskState->pbSenseBuffer));
1648 PDMDevHlpPCIPhysWrite(pDevIns, GCPhysAddrSenseBuffer, pTaskState->pbSenseBuffer, cbSenseBuffer);
1649 }
1650
1651 RTMemFree(pTaskState->pbSenseBuffer);
1652 pTaskState->pbSenseBuffer = NULL;
1653}
1654
1655/**
1656 * Alloc the sense buffer.
1657 *
1658 * @returns VBox status code.
1659 * @param pTaskState Pointer to the task state.
1660 * @note Current assumption is that the sense buffer is not scattered and does not cross a page boundary.
1661 */
1662static int buslogicR3SenseBufferAlloc(PBUSLOGICTASKSTATE pTaskState)
1663{
1664 PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
1665 uint32_t cbSenseBuffer;
1666
1667 pTaskState->pbSenseBuffer = NULL;
1668
1669 cbSenseBuffer = buslogicR3ConvertSenseBufferLength(pTaskState->CommandControlBlockGuest.c.cbSenseData);
1670 if (cbSenseBuffer)
1671 {
1672 pTaskState->pbSenseBuffer = (uint8_t *)RTMemAllocZ(cbSenseBuffer);
1673 if (!pTaskState->pbSenseBuffer)
1674 return VERR_NO_MEMORY;
1675 }
1676
1677 return VINF_SUCCESS;
1678}
1679
1680#endif /* IN_RING3 */
1681
1682/**
1683 * Parses the command buffer and executes it.
1684 *
1685 * @returns VBox status code.
1686 * @param pBusLogic Pointer to the BusLogic device instance.
1687 */
1688static int buslogicProcessCommand(PBUSLOGIC pBusLogic)
1689{
1690 int rc = VINF_SUCCESS;
1691 bool fSuppressIrq = false;
1692
1693 LogFlowFunc(("pBusLogic=%#p\n", pBusLogic));
1694 AssertMsg(pBusLogic->uOperationCode != 0xff, ("There is no command to execute\n"));
1695
1696 switch (pBusLogic->uOperationCode)
1697 {
1698 case BUSLOGICCOMMAND_TEST_CMDC_INTERRUPT:
1699 /* Valid command, no reply. */
1700 pBusLogic->cbReplyParametersLeft = 0;
1701 break;
1702 case BUSLOGICCOMMAND_INQUIRE_PCI_HOST_ADAPTER_INFORMATION:
1703 {
1704 PReplyInquirePCIHostAdapterInformation pReply = (PReplyInquirePCIHostAdapterInformation)pBusLogic->aReplyBuffer;
1705 memset(pReply, 0, sizeof(ReplyInquirePCIHostAdapterInformation));
1706
1707 /* It seems VMware does not provide valid information here too, lets do the same :) */
1708 pReply->InformationIsValid = 0;
1709 pReply->IsaIOPort = pBusLogic->uISABaseCode;
1710 pReply->IRQ = PCIDevGetInterruptLine(&pBusLogic->dev);
1711 pBusLogic->cbReplyParametersLeft = sizeof(ReplyInquirePCIHostAdapterInformation);
1712 break;
1713 }
1714 case BUSLOGICCOMMAND_MODIFY_IO_ADDRESS:
1715 {
1716 /* Modify the ISA-compatible I/O port base. Note that this technically
1717 * violates the PCI spec, as this address is not reported through PCI.
1718 * However, it is required for compatibility with old drivers.
1719 */
1720#ifdef IN_RING3
1721 Log(("ISA I/O for PCI (code %x)\n", pBusLogic->aCommandBuffer[0]));
1722 buslogicR3RegisterISARange(pBusLogic, pBusLogic->aCommandBuffer[0]);
1723 pBusLogic->cbReplyParametersLeft = 0;
1724 fSuppressIrq = true;
1725 break;
1726#else
1727 AssertMsgFailed(("Must never get here!\n"));
1728#endif
1729 }
1730 case BUSLOGICCOMMAND_INQUIRE_BOARD_ID:
1731 {
1732 /* The special option byte is important: If it is '0' or 'B', Windows NT drivers
1733 * for Adaptec AHA-154x may claim the adapter. The BusLogic drivers will claim
1734 * the adapter only when the byte is *not* '0' or 'B'.
1735 */
1736 pBusLogic->aReplyBuffer[0] = 'A'; /* Firmware option bytes */
1737 pBusLogic->aReplyBuffer[1] = 'A'; /* Special option byte */
1738
1739 /* We report version 5.07B. This reply will provide the first two digits. */
1740 pBusLogic->aReplyBuffer[2] = '5'; /* Major version 5 */
1741 pBusLogic->aReplyBuffer[3] = '0'; /* Minor version 0 */
1742 pBusLogic->cbReplyParametersLeft = 4; /* Reply is 4 bytes long */
1743 break;
1744 }
1745 case BUSLOGICCOMMAND_INQUIRE_FIRMWARE_VERSION_3RD_LETTER:
1746 {
1747 pBusLogic->aReplyBuffer[0] = '7';
1748 pBusLogic->cbReplyParametersLeft = 1;
1749 break;
1750 }
1751 case BUSLOGICCOMMAND_INQUIRE_FIRMWARE_VERSION_LETTER:
1752 {
1753 pBusLogic->aReplyBuffer[0] = 'B';
1754 pBusLogic->cbReplyParametersLeft = 1;
1755 break;
1756 }
1757 case BUSLOGICCOMMAND_SET_ADAPTER_OPTIONS:
1758 /* The parameter list length is determined by the first byte of the command buffer. */
1759 if (pBusLogic->iParameter == 1)
1760 {
1761 /* First pass - set the number of following parameter bytes. */
1762 pBusLogic->cbCommandParametersLeft = pBusLogic->aCommandBuffer[0];
1763 Log(("Set HA options: %u bytes follow\n", pBusLogic->cbCommandParametersLeft));
1764 }
1765 else
1766 {
1767 /* Second pass - process received data. */
1768 Log(("Set HA options: received %u bytes\n", pBusLogic->aCommandBuffer[0]));
1769 /* We ignore the data - it only concerns the SCSI hardware protocol. */
1770 }
1771 pBusLogic->cbReplyParametersLeft = 0;
1772 break;
1773
1774 case BUSLOGICCOMMAND_EXECUTE_SCSI_COMMAND:
1775 /* The parameter list length is at least 12 bytes; the 12th byte determines
1776 * the number of additional CDB bytes that will follow.
1777 */
1778 if (pBusLogic->iParameter == 12)
1779 {
1780 /* First pass - set the number of following CDB bytes. */
1781 pBusLogic->cbCommandParametersLeft = pBusLogic->aCommandBuffer[11];
1782 Log(("Execute SCSI cmd: %u more bytes follow\n", pBusLogic->cbCommandParametersLeft));
1783 }
1784 else
1785 {
1786 PESCMD pCmd;
1787
1788 /* Second pass - process received data. */
1789 Log(("Execute SCSI cmd: received %u bytes\n", pBusLogic->aCommandBuffer[0]));
1790
1791 pCmd = (PESCMD)pBusLogic->aCommandBuffer;
1792 Log(("Addr %08X, cbData %08X, cbCDB=%u\n", pCmd->u32PhysAddrData, pCmd->cbData, pCmd->cbCDB));
1793 }
1794 // This is currently a dummy - just fails every command.
1795 pBusLogic->cbReplyParametersLeft = 4;
1796 pBusLogic->aReplyBuffer[0] = pBusLogic->aReplyBuffer[1] = 0;
1797 pBusLogic->aReplyBuffer[2] = 0x11; /* HBA status (timeout). */
1798 pBusLogic->aReplyBuffer[3] = 0; /* Device status. */
1799 break;
1800
1801 case BUSLOGICCOMMAND_INQUIRE_HOST_ADAPTER_MODEL_NUMBER:
1802 {
1803 /* The reply length is set by the guest and is found in the first byte of the command buffer. */
1804 pBusLogic->cbReplyParametersLeft = pBusLogic->aCommandBuffer[0];
1805 memset(pBusLogic->aReplyBuffer, ' ', pBusLogic->cbReplyParametersLeft);
1806 const char aModelName[] = "958";
1807 int cCharsToTransfer = (pBusLogic->cbReplyParametersLeft <= (sizeof(aModelName) - 1))
1808 ? pBusLogic->cbReplyParametersLeft
1809 : sizeof(aModelName) - 1;
1810
1811 for (int i = 0; i < cCharsToTransfer; i++)
1812 pBusLogic->aReplyBuffer[i] = aModelName[i];
1813
1814 break;
1815 }
1816 case BUSLOGICCOMMAND_INQUIRE_CONFIGURATION:
1817 {
1818 uint8_t uPciIrq = PCIDevGetInterruptLine(&pBusLogic->dev);
1819
1820 pBusLogic->cbReplyParametersLeft = sizeof(ReplyInquireConfiguration);
1821 PReplyInquireConfiguration pReply = (PReplyInquireConfiguration)pBusLogic->aReplyBuffer;
1822 memset(pReply, 0, sizeof(ReplyInquireConfiguration));
1823
1824 pReply->uHostAdapterId = 7; /* The controller has always 7 as ID. */
1825 pReply->fDmaChannel6 = 1; /* DMA channel 6 is a good default. */
1826 /* The PCI IRQ is not necessarily representable in this structure.
1827 * If that is the case, the guest likely won't function correctly,
1828 * therefore we log a warning.
1829 */
1830 switch (uPciIrq)
1831 {
1832 case 9: pReply->fIrqChannel9 = 1; break;
1833 case 10: pReply->fIrqChannel10 = 1; break;
1834 case 11: pReply->fIrqChannel11 = 1; break;
1835 case 12: pReply->fIrqChannel12 = 1; break;
1836 case 14: pReply->fIrqChannel14 = 1; break;
1837 case 15: pReply->fIrqChannel15 = 1; break;
1838 default:
1839 LogRel(("Warning: PCI IRQ %d cannot be represented as ISA!\n", uPciIrq));
1840 break;
1841 }
1842 break;
1843 }
1844 case BUSLOGICCOMMAND_INQUIRE_EXTENDED_SETUP_INFORMATION:
1845 {
1846 /* Some Adaptec AHA-154x drivers (e.g. OS/2) execute this command and expect
1847 * it to fail. If it succeeds, the drivers refuse to load. However, some newer
1848 * Adaptec 154x models supposedly support it too??
1849 */
1850
1851 /* The reply length is set by the guest and is found in the first byte of the command buffer. */
1852 pBusLogic->cbReplyParametersLeft = pBusLogic->aCommandBuffer[0];
1853 PReplyInquireExtendedSetupInformation pReply = (PReplyInquireExtendedSetupInformation)pBusLogic->aReplyBuffer;
1854 memset(pReply, 0, sizeof(ReplyInquireExtendedSetupInformation));
1855
1856 /** @todo should this reflect the RAM contents (AutoSCSIRam)? */
1857 pReply->uBusType = 'E'; /* EISA style */
1858 pReply->u16ScatterGatherLimit = 8192;
1859 pReply->cMailbox = pBusLogic->cMailbox;
1860 pReply->uMailboxAddressBase = (uint32_t)pBusLogic->GCPhysAddrMailboxOutgoingBase;
1861 pReply->fLevelSensitiveInterrupt = true;
1862 pReply->fHostWideSCSI = true;
1863 pReply->fHostUltraSCSI = true;
1864 memcpy(pReply->aFirmwareRevision, "07B", sizeof(pReply->aFirmwareRevision));
1865
1866 break;
1867 }
1868 case BUSLOGICCOMMAND_INQUIRE_SETUP_INFORMATION:
1869 {
1870 /* The reply length is set by the guest and is found in the first byte of the command buffer. */
1871 pBusLogic->cbReplyParametersLeft = pBusLogic->aCommandBuffer[0];
1872 PReplyInquireSetupInformation pReply = (PReplyInquireSetupInformation)pBusLogic->aReplyBuffer;
1873 memset(pReply, 0, sizeof(ReplyInquireSetupInformation));
1874 pReply->fSynchronousInitiationEnabled = true;
1875 pReply->fParityCheckingEnabled = true;
1876 pReply->cMailbox = pBusLogic->cMailbox;
1877 U32_TO_ADDR(pReply->MailboxAddress, pBusLogic->GCPhysAddrMailboxOutgoingBase);
1878 pReply->uSignature = 'B';
1879 /* The 'D' signature prevents Adaptec's OS/2 drivers from getting too
1880 * friendly with BusLogic hardware and upsetting the HBA state.
1881 */
1882 pReply->uCharacterD = 'D'; /* BusLogic model. */
1883 pReply->uHostBusType = 'F'; /* PCI bus. */
1884 break;
1885 }
1886 case BUSLOGICCOMMAND_FETCH_HOST_ADAPTER_LOCAL_RAM:
1887 {
1888 /*
1889 * First element in the command buffer contains start offset to read from
1890 * and second one the number of bytes to read.
1891 */
1892 uint8_t uOffset = pBusLogic->aCommandBuffer[0];
1893 pBusLogic->cbReplyParametersLeft = pBusLogic->aCommandBuffer[1];
1894
1895 pBusLogic->fUseLocalRam = true;
1896 pBusLogic->iReply = uOffset;
1897 break;
1898 }
1899 case BUSLOGICCOMMAND_INITIALIZE_MAILBOX:
1900 {
1901 PRequestInitMbx pRequest = (PRequestInitMbx)pBusLogic->aCommandBuffer;
1902
1903 pBusLogic->fMbxIs24Bit = true;
1904 pBusLogic->cMailbox = pRequest->cMailbox;
1905 pBusLogic->GCPhysAddrMailboxOutgoingBase = (RTGCPHYS)ADDR_TO_U32(pRequest->aMailboxBaseAddr);
1906 /* The area for incoming mailboxes is right after the last entry of outgoing mailboxes. */
1907 pBusLogic->GCPhysAddrMailboxIncomingBase = pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->cMailbox * sizeof(Mailbox24));
1908
1909 Log(("GCPhysAddrMailboxOutgoingBase=%RGp\n", pBusLogic->GCPhysAddrMailboxOutgoingBase));
1910 Log(("GCPhysAddrMailboxIncomingBase=%RGp\n", pBusLogic->GCPhysAddrMailboxIncomingBase));
1911 Log(("cMailboxes=%u (24-bit mode)\n", pBusLogic->cMailbox));
1912 LogRel(("Initialized 24-bit mailbox, %d entries at %08x\n", pRequest->cMailbox, ADDR_TO_U32(pRequest->aMailboxBaseAddr)));
1913
1914 pBusLogic->regStatus &= ~BL_STAT_INREQ;
1915 pBusLogic->cbReplyParametersLeft = 0;
1916 break;
1917 }
1918 case BUSLOGICCOMMAND_INITIALIZE_EXTENDED_MAILBOX:
1919 {
1920 PRequestInitializeExtendedMailbox pRequest = (PRequestInitializeExtendedMailbox)pBusLogic->aCommandBuffer;
1921
1922 pBusLogic->fMbxIs24Bit = false;
1923 pBusLogic->cMailbox = pRequest->cMailbox;
1924 pBusLogic->GCPhysAddrMailboxOutgoingBase = (RTGCPHYS)pRequest->uMailboxBaseAddress;
1925 /* The area for incoming mailboxes is right after the last entry of outgoing mailboxes. */
1926 pBusLogic->GCPhysAddrMailboxIncomingBase = (RTGCPHYS)pRequest->uMailboxBaseAddress + (pBusLogic->cMailbox * sizeof(Mailbox32));
1927
1928 Log(("GCPhysAddrMailboxOutgoingBase=%RGp\n", pBusLogic->GCPhysAddrMailboxOutgoingBase));
1929 Log(("GCPhysAddrMailboxIncomingBase=%RGp\n", pBusLogic->GCPhysAddrMailboxIncomingBase));
1930 Log(("cMailboxes=%u (32-bit mode)\n", pBusLogic->cMailbox));
1931 LogRel(("Initialized 32-bit mailbox, %d entries at %08x\n", pRequest->cMailbox, pRequest->uMailboxBaseAddress));
1932
1933 pBusLogic->regStatus &= ~BL_STAT_INREQ;
1934 pBusLogic->cbReplyParametersLeft = 0;
1935 break;
1936 }
1937 case BUSLOGICCOMMAND_ENABLE_STRICT_ROUND_ROBIN_MODE:
1938 {
1939 if (pBusLogic->aCommandBuffer[0] == 0)
1940 pBusLogic->fStrictRoundRobinMode = false;
1941 else if (pBusLogic->aCommandBuffer[0] == 1)
1942 pBusLogic->fStrictRoundRobinMode = true;
1943 else
1944 AssertMsgFailed(("Invalid round robin mode %d\n", pBusLogic->aCommandBuffer[0]));
1945
1946 pBusLogic->cbReplyParametersLeft = 0;
1947 break;
1948 }
1949 case BUSLOGICCOMMAND_SET_CCB_FORMAT:
1950 {
1951 if (pBusLogic->aCommandBuffer[0] == 0)
1952 pBusLogic->fExtendedLunCCBFormat = false;
1953 else if (pBusLogic->aCommandBuffer[0] == 1)
1954 pBusLogic->fExtendedLunCCBFormat = true;
1955 else
1956 AssertMsgFailed(("Invalid CCB format %d\n", pBusLogic->aCommandBuffer[0]));
1957
1958 pBusLogic->cbReplyParametersLeft = 0;
1959 break;
1960 }
1961 case BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_0_TO_7:
1962 /* This is supposed to send TEST UNIT READY to each target/LUN.
1963 * We cheat and skip that, since we already know what's attached
1964 */
1965 memset(pBusLogic->aReplyBuffer, 0, 8);
1966 for (int i = 0; i < 8; ++i)
1967 {
1968 if (pBusLogic->aDeviceStates[i].fPresent)
1969 pBusLogic->aReplyBuffer[i] = 1;
1970 }
1971 pBusLogic->aReplyBuffer[7] = 0; /* HA hardcoded at ID 7. */
1972 pBusLogic->cbReplyParametersLeft = 8;
1973 break;
1974 case BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_8_TO_15:
1975 /* See note about cheating above. */
1976 memset(pBusLogic->aReplyBuffer, 0, 8);
1977 for (int i = 0; i < 8; ++i)
1978 {
1979 if (pBusLogic->aDeviceStates[i + 8].fPresent)
1980 pBusLogic->aReplyBuffer[i] = 1;
1981 }
1982 pBusLogic->cbReplyParametersLeft = 8;
1983 break;
1984 case BUSLOGICCOMMAND_INQUIRE_TARGET_DEVICES:
1985 {
1986 /* Each bit which is set in the 16bit wide variable means a present device. */
1987 uint16_t u16TargetsPresentMask = 0;
1988
1989 for (uint8_t i = 0; i < RT_ELEMENTS(pBusLogic->aDeviceStates); i++)
1990 {
1991 if (pBusLogic->aDeviceStates[i].fPresent)
1992 u16TargetsPresentMask |= (1 << i);
1993 }
1994 pBusLogic->aReplyBuffer[0] = (uint8_t)u16TargetsPresentMask;
1995 pBusLogic->aReplyBuffer[1] = (uint8_t)(u16TargetsPresentMask >> 8);
1996 pBusLogic->cbReplyParametersLeft = 2;
1997 break;
1998 }
1999 case BUSLOGICCOMMAND_INQUIRE_SYNCHRONOUS_PERIOD:
2000 {
2001 pBusLogic->cbReplyParametersLeft = pBusLogic->aCommandBuffer[0];
2002
2003 for (uint8_t i = 0; i < pBusLogic->cbReplyParametersLeft; i++)
2004 pBusLogic->aReplyBuffer[i] = 0; /** @todo Figure if we need something other here. It's not needed for the linux driver */
2005
2006 break;
2007 }
2008 case BUSLOGICCOMMAND_DISABLE_HOST_ADAPTER_INTERRUPT:
2009 {
2010 if (pBusLogic->aCommandBuffer[0] == 0)
2011 pBusLogic->fIRQEnabled = false;
2012 else
2013 pBusLogic->fIRQEnabled = true;
2014 /* No interrupt signaled regardless of enable/disable. */
2015 fSuppressIrq = true;
2016 break;
2017 }
2018 case BUSLOGICCOMMAND_ECHO_COMMAND_DATA:
2019 {
2020 pBusLogic->aReplyBuffer[0] = pBusLogic->aCommandBuffer[0];
2021 pBusLogic->cbReplyParametersLeft = 1;
2022 break;
2023 }
2024 case BUSLOGICCOMMAND_SET_PREEMPT_TIME_ON_BUS:
2025 {
2026 pBusLogic->cbReplyParametersLeft = 0;
2027 pBusLogic->LocalRam.structured.autoSCSIData.uBusOnDelay = pBusLogic->aCommandBuffer[0];
2028 Log(("Bus-on time: %d\n", pBusLogic->aCommandBuffer[0]));
2029 break;
2030 }
2031 case BUSLOGICCOMMAND_SET_TIME_OFF_BUS:
2032 {
2033 pBusLogic->cbReplyParametersLeft = 0;
2034 pBusLogic->LocalRam.structured.autoSCSIData.uBusOffDelay = pBusLogic->aCommandBuffer[0];
2035 Log(("Bus-off time: %d\n", pBusLogic->aCommandBuffer[0]));
2036 break;
2037 }
2038 case BUSLOGICCOMMAND_SET_BUS_TRANSFER_RATE:
2039 {
2040 pBusLogic->cbReplyParametersLeft = 0;
2041 pBusLogic->LocalRam.structured.autoSCSIData.uDMATransferRate = pBusLogic->aCommandBuffer[0];
2042 Log(("Bus transfer rate: %02X\n", pBusLogic->aCommandBuffer[0]));
2043 break;
2044 }
2045 case BUSLOGICCOMMAND_WRITE_BUSMASTER_CHIP_FIFO:
2046 {
2047 RTGCPHYS GCPhysFifoBuf;
2048 Addr24 addr;
2049
2050 pBusLogic->cbReplyParametersLeft = 0;
2051 addr.hi = pBusLogic->aCommandBuffer[0];
2052 addr.mid = pBusLogic->aCommandBuffer[1];
2053 addr.lo = pBusLogic->aCommandBuffer[2];
2054 GCPhysFifoBuf = (RTGCPHYS)ADDR_TO_U32(addr);
2055 Log(("Write busmaster FIFO at: %04X\n", ADDR_TO_U32(addr)));
2056 PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysFifoBuf,
2057 &pBusLogic->LocalRam.u8View[64], 64);
2058 break;
2059 }
2060 case BUSLOGICCOMMAND_READ_BUSMASTER_CHIP_FIFO:
2061 {
2062 RTGCPHYS GCPhysFifoBuf;
2063 Addr24 addr;
2064
2065 pBusLogic->cbReplyParametersLeft = 0;
2066 addr.hi = pBusLogic->aCommandBuffer[0];
2067 addr.mid = pBusLogic->aCommandBuffer[1];
2068 addr.lo = pBusLogic->aCommandBuffer[2];
2069 GCPhysFifoBuf = (RTGCPHYS)ADDR_TO_U32(addr);
2070 Log(("Read busmaster FIFO at: %04X\n", ADDR_TO_U32(addr)));
2071 PDMDevHlpPCIPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysFifoBuf,
2072 &pBusLogic->LocalRam.u8View[64], 64);
2073 break;
2074 }
2075 default:
2076 AssertMsgFailed(("Invalid command %#x\n", pBusLogic->uOperationCode));
2077 case BUSLOGICCOMMAND_EXT_BIOS_INFO:
2078 case BUSLOGICCOMMAND_UNLOCK_MAILBOX:
2079 /* Commands valid for Adaptec 154xC which we don't handle since
2080 * we pretend being 154xB compatible. Just mark the command as invalid.
2081 */
2082 Log(("Command %#x not valid for this adapter\n", pBusLogic->uOperationCode));
2083 pBusLogic->cbReplyParametersLeft = 0;
2084 pBusLogic->regStatus |= BL_STAT_CMDINV;
2085 break;
2086 case BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND: /* Should be handled already. */
2087 AssertMsgFailed(("Invalid mailbox execute state!\n"));
2088 }
2089
2090 Log(("uOperationCode=%#x, cbReplyParametersLeft=%d\n", pBusLogic->uOperationCode, pBusLogic->cbReplyParametersLeft));
2091
2092 /* Set the data in ready bit in the status register in case the command has a reply. */
2093 if (pBusLogic->cbReplyParametersLeft)
2094 pBusLogic->regStatus |= BL_STAT_DIRRDY;
2095 else if (!pBusLogic->cbCommandParametersLeft)
2096 buslogicCommandComplete(pBusLogic, fSuppressIrq);
2097
2098 return rc;
2099}
2100
2101/**
2102 * Read a register from the BusLogic adapter.
2103 *
2104 * @returns VBox status code.
2105 * @param pBusLogic Pointer to the BusLogic instance data.
2106 * @param iRegister The index of the register to read.
2107 * @param pu32 Where to store the register content.
2108 */
2109static int buslogicRegisterRead(PBUSLOGIC pBusLogic, unsigned iRegister, uint32_t *pu32)
2110{
2111 int rc = VINF_SUCCESS;
2112
2113 switch (iRegister)
2114 {
2115 case BUSLOGIC_REGISTER_STATUS:
2116 {
2117 *pu32 = pBusLogic->regStatus;
2118
2119 /* If the diagnostic active bit is set, we are in a guest-initiated
2120 * hard reset. If the guest reads the status register and waits for
2121 * the host adapter ready bit to be set, we terminate the reset right
2122 * away. However, guests may also expect the reset condition to clear
2123 * automatically after a period of time, in which case we can't show
2124 * the DIAG bit at all.
2125 */
2126 if (pBusLogic->regStatus & BL_STAT_DACT)
2127 {
2128 uint64_t u64AccessTime = PDMDevHlpTMTimeVirtGetNano(pBusLogic->CTX_SUFF(pDevIns));
2129
2130 pBusLogic->regStatus &= ~BL_STAT_DACT;
2131 pBusLogic->regStatus |= BL_STAT_HARDY;
2132
2133 if (u64AccessTime - pBusLogic->u64ResetTime > BUSLOGIC_RESET_DURATION_NS)
2134 {
2135 /* If reset already expired, let the guest see that right away. */
2136 *pu32 = pBusLogic->regStatus;
2137 pBusLogic->u64ResetTime = 0;
2138 }
2139 }
2140 break;
2141 }
2142 case BUSLOGIC_REGISTER_DATAIN:
2143 {
2144 if (pBusLogic->fUseLocalRam)
2145 *pu32 = pBusLogic->LocalRam.u8View[pBusLogic->iReply];
2146 else
2147 *pu32 = pBusLogic->aReplyBuffer[pBusLogic->iReply];
2148
2149 /* Careful about underflow - guest can read data register even if
2150 * no data is available.
2151 */
2152 if (pBusLogic->cbReplyParametersLeft)
2153 {
2154 pBusLogic->iReply++;
2155 pBusLogic->cbReplyParametersLeft--;
2156 if (!pBusLogic->cbReplyParametersLeft)
2157 {
2158 /*
2159 * Reply finished, set command complete bit, unset data-in ready bit and
2160 * interrupt the guest if enabled.
2161 */
2162 buslogicCommandComplete(pBusLogic, false);
2163 }
2164 }
2165 LogFlowFunc(("data=%02x, iReply=%d, cbReplyParametersLeft=%u\n", *pu32,
2166 pBusLogic->iReply, pBusLogic->cbReplyParametersLeft));
2167 break;
2168 }
2169 case BUSLOGIC_REGISTER_INTERRUPT:
2170 {
2171 *pu32 = pBusLogic->regInterrupt;
2172 break;
2173 }
2174 case BUSLOGIC_REGISTER_GEOMETRY:
2175 {
2176 *pu32 = pBusLogic->regGeometry;
2177 break;
2178 }
2179 default:
2180 *pu32 = UINT32_C(0xffffffff);
2181 }
2182
2183 Log2(("%s: pu32=%p:{%.*Rhxs} iRegister=%d rc=%Rrc\n",
2184 __FUNCTION__, pu32, 1, pu32, iRegister, rc));
2185
2186 return rc;
2187}
2188
2189/**
2190 * Write a value to a register.
2191 *
2192 * @returns VBox status code.
2193 * @param pBusLogic Pointer to the BusLogic instance data.
2194 * @param iRegister The index of the register to read.
2195 * @param uVal The value to write.
2196 */
2197static int buslogicRegisterWrite(PBUSLOGIC pBusLogic, unsigned iRegister, uint8_t uVal)
2198{
2199 int rc = VINF_SUCCESS;
2200
2201 switch (iRegister)
2202 {
2203 case BUSLOGIC_REGISTER_CONTROL:
2204 {
2205 if ((uVal & BL_CTRL_RHARD) || (uVal & BL_CTRL_RSOFT))
2206 {
2207#ifdef IN_RING3
2208 bool fHardReset = !!(uVal & BL_CTRL_RHARD);
2209
2210 LogRel(("BusLogic: %s reset\n", fHardReset ? "hard" : "soft"));
2211 buslogicR3InitiateReset(pBusLogic, fHardReset);
2212#else
2213 rc = VINF_IOM_R3_IOPORT_WRITE;
2214#endif
2215 break;
2216 }
2217
2218 rc = PDMCritSectEnter(&pBusLogic->CritSectIntr, VINF_IOM_R3_IOPORT_WRITE);
2219 if (rc != VINF_SUCCESS)
2220 return rc;
2221
2222#ifdef LOG_ENABLED
2223 uint32_t cMailboxesReady = ASMAtomicXchgU32(&pBusLogic->cInMailboxesReady, 0);
2224 Log(("%u incoming mailboxes were ready when this interrupt was cleared\n", cMailboxesReady));
2225#endif
2226
2227 if (uVal & BL_CTRL_RINT)
2228 buslogicClearInterrupt(pBusLogic);
2229
2230 PDMCritSectLeave(&pBusLogic->CritSectIntr);
2231
2232 break;
2233 }
2234 case BUSLOGIC_REGISTER_COMMAND:
2235 {
2236 /* Fast path for mailbox execution command. */
2237 if ((uVal == BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND) && (pBusLogic->uOperationCode == 0xff))
2238 {
2239 /* If there are no mailboxes configured, don't even try to do anything. */
2240 if (pBusLogic->cMailbox)
2241 {
2242 ASMAtomicIncU32(&pBusLogic->cMailboxesReady);
2243 if (!ASMAtomicXchgBool(&pBusLogic->fNotificationSend, true))
2244 {
2245 /* Send new notification to the queue. */
2246 PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(pBusLogic->CTX_SUFF(pNotifierQueue));
2247 AssertMsg(pItem, ("Allocating item for queue failed\n"));
2248 PDMQueueInsert(pBusLogic->CTX_SUFF(pNotifierQueue), (PPDMQUEUEITEMCORE)pItem);
2249 }
2250 }
2251
2252 return rc;
2253 }
2254
2255 /*
2256 * Check if we are already fetch command parameters from the guest.
2257 * If not we initialize executing a new command.
2258 */
2259 if (pBusLogic->uOperationCode == 0xff)
2260 {
2261 pBusLogic->uOperationCode = uVal;
2262 pBusLogic->iParameter = 0;
2263
2264 /* Mark host adapter as busy and clear the invalid status bit. */
2265 pBusLogic->regStatus &= ~(BL_STAT_HARDY | BL_STAT_CMDINV);
2266
2267 /* Get the number of bytes for parameters from the command code. */
2268 switch (pBusLogic->uOperationCode)
2269 {
2270 case BUSLOGICCOMMAND_TEST_CMDC_INTERRUPT:
2271 case BUSLOGICCOMMAND_INQUIRE_FIRMWARE_VERSION_LETTER:
2272 case BUSLOGICCOMMAND_INQUIRE_BOARD_ID:
2273 case BUSLOGICCOMMAND_INQUIRE_FIRMWARE_VERSION_3RD_LETTER:
2274 case BUSLOGICCOMMAND_INQUIRE_PCI_HOST_ADAPTER_INFORMATION:
2275 case BUSLOGICCOMMAND_INQUIRE_CONFIGURATION:
2276 case BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_0_TO_7:
2277 case BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_8_TO_15:
2278 case BUSLOGICCOMMAND_INQUIRE_TARGET_DEVICES:
2279 pBusLogic->cbCommandParametersLeft = 0;
2280 break;
2281 case BUSLOGICCOMMAND_MODIFY_IO_ADDRESS:
2282 case BUSLOGICCOMMAND_INQUIRE_EXTENDED_SETUP_INFORMATION:
2283 case BUSLOGICCOMMAND_INQUIRE_SETUP_INFORMATION:
2284 case BUSLOGICCOMMAND_INQUIRE_HOST_ADAPTER_MODEL_NUMBER:
2285 case BUSLOGICCOMMAND_ENABLE_STRICT_ROUND_ROBIN_MODE:
2286 case BUSLOGICCOMMAND_SET_CCB_FORMAT:
2287 case BUSLOGICCOMMAND_INQUIRE_SYNCHRONOUS_PERIOD:
2288 case BUSLOGICCOMMAND_DISABLE_HOST_ADAPTER_INTERRUPT:
2289 case BUSLOGICCOMMAND_ECHO_COMMAND_DATA:
2290 case BUSLOGICCOMMAND_SET_PREEMPT_TIME_ON_BUS:
2291 case BUSLOGICCOMMAND_SET_TIME_OFF_BUS:
2292 case BUSLOGICCOMMAND_SET_BUS_TRANSFER_RATE:
2293 pBusLogic->cbCommandParametersLeft = 1;
2294 break;
2295 case BUSLOGICCOMMAND_FETCH_HOST_ADAPTER_LOCAL_RAM:
2296 pBusLogic->cbCommandParametersLeft = 2;
2297 break;
2298 case BUSLOGICCOMMAND_READ_BUSMASTER_CHIP_FIFO:
2299 case BUSLOGICCOMMAND_WRITE_BUSMASTER_CHIP_FIFO:
2300 pBusLogic->cbCommandParametersLeft = 3;
2301 break;
2302 case BUSLOGICCOMMAND_INITIALIZE_MAILBOX:
2303 pBusLogic->cbCommandParametersLeft = sizeof(RequestInitMbx);
2304 break;
2305 case BUSLOGICCOMMAND_INITIALIZE_EXTENDED_MAILBOX:
2306 pBusLogic->cbCommandParametersLeft = sizeof(RequestInitializeExtendedMailbox);
2307 break;
2308 case BUSLOGICCOMMAND_SET_ADAPTER_OPTIONS:
2309 /* There must be at least one byte following this command. */
2310 pBusLogic->cbCommandParametersLeft = 1;
2311 break;
2312 case BUSLOGICCOMMAND_EXECUTE_SCSI_COMMAND:
2313 /* 12 bytes + variable-length CDB. */
2314 pBusLogic->cbCommandParametersLeft = 12;
2315 break;
2316 case BUSLOGICCOMMAND_EXT_BIOS_INFO:
2317 case BUSLOGICCOMMAND_UNLOCK_MAILBOX:
2318 /* Invalid commands. */
2319 pBusLogic->cbCommandParametersLeft = 0;
2320 break;
2321 case BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND: /* Should not come here anymore. */
2322 default:
2323 AssertMsgFailed(("Invalid operation code %#x\n", uVal));
2324 }
2325 }
2326 else
2327 {
2328#ifndef IN_RING3
2329 /* This command must be executed in R3 as it rehooks the ISA I/O port. */
2330 if (pBusLogic->uOperationCode == BUSLOGICCOMMAND_MODIFY_IO_ADDRESS)
2331 {
2332 rc = VINF_IOM_R3_IOPORT_WRITE;
2333 break;
2334 }
2335#endif
2336 /*
2337 * The real adapter would set the Command register busy bit in the status register.
2338 * The guest has to wait until it is unset.
2339 * We don't need to do it because the guest does not continue execution while we are in this
2340 * function.
2341 */
2342 pBusLogic->aCommandBuffer[pBusLogic->iParameter] = uVal;
2343 pBusLogic->iParameter++;
2344 pBusLogic->cbCommandParametersLeft--;
2345 }
2346
2347 /* Start execution of command if there are no parameters left. */
2348 if (!pBusLogic->cbCommandParametersLeft)
2349 {
2350 rc = buslogicProcessCommand(pBusLogic);
2351 AssertMsgRC(rc, ("Processing command failed rc=%Rrc\n", rc));
2352 }
2353 break;
2354 }
2355
2356 /* On BusLogic adapters, the interrupt and geometry registers are R/W.
2357 * That is different from Adaptec 154x where those are read only.
2358 */
2359 case BUSLOGIC_REGISTER_INTERRUPT:
2360 pBusLogic->regInterrupt = uVal;
2361 break;
2362
2363 case BUSLOGIC_REGISTER_GEOMETRY:
2364 pBusLogic->regGeometry = uVal;
2365 break;
2366
2367 default:
2368 AssertMsgFailed(("Register not available\n"));
2369 rc = VERR_IOM_IOPORT_UNUSED;
2370 }
2371
2372 return rc;
2373}
2374
2375/**
2376 * Memory mapped I/O Handler for read operations.
2377 *
2378 * @returns VBox status code.
2379 *
2380 * @param pDevIns The device instance.
2381 * @param pvUser User argument.
2382 * @param GCPhysAddr Physical address (in GC) where the read starts.
2383 * @param pv Where to store the result.
2384 * @param cb Number of bytes read.
2385 */
2386PDMBOTHCBDECL(int) buslogicMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
2387{
2388 /* the linux driver does not make use of the MMIO area. */
2389 AssertMsgFailed(("MMIO Read\n"));
2390 return VINF_SUCCESS;
2391}
2392
2393/**
2394 * Memory mapped I/O Handler for write operations.
2395 *
2396 * @returns VBox status code.
2397 *
2398 * @param pDevIns The device instance.
2399 * @param pvUser User argument.
2400 * @param GCPhysAddr Physical address (in GC) where the read starts.
2401 * @param pv Where to fetch the result.
2402 * @param cb Number of bytes to write.
2403 */
2404PDMBOTHCBDECL(int) buslogicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
2405{
2406 /* the linux driver does not make use of the MMIO area. */
2407 AssertMsgFailed(("MMIO Write\n"));
2408 return VINF_SUCCESS;
2409}
2410
2411/**
2412 * Port I/O Handler for IN operations.
2413 *
2414 * @returns VBox status code.
2415 *
2416 * @param pDevIns The device instance.
2417 * @param pvUser User argument.
2418 * @param uPort Port number used for the IN operation.
2419 * @param pu32 Where to store the result.
2420 * @param cb Number of bytes read.
2421 */
2422PDMBOTHCBDECL(int) buslogicIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2423{
2424 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2425 unsigned iRegister = Port % 4;
2426
2427 Assert(cb == 1);
2428
2429 return buslogicRegisterRead(pBusLogic, iRegister, pu32);
2430}
2431
2432/**
2433 * Port I/O Handler for OUT operations.
2434 *
2435 * @returns VBox status code.
2436 *
2437 * @param pDevIns The device instance.
2438 * @param pvUser User argument.
2439 * @param uPort Port number used for the IN operation.
2440 * @param u32 The value to output.
2441 * @param cb The value size in bytes.
2442 */
2443PDMBOTHCBDECL(int) buslogicIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2444{
2445 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2446 int rc = VINF_SUCCESS;
2447 unsigned iRegister = Port % 4;
2448 uint8_t uVal = (uint8_t)u32;
2449
2450 Assert(cb == 1);
2451
2452 rc = buslogicRegisterWrite(pBusLogic, iRegister, (uint8_t)uVal);
2453
2454 Log2(("#%d %s: pvUser=%#p cb=%d u32=%#x Port=%#x rc=%Rrc\n",
2455 pDevIns->iInstance, __FUNCTION__, pvUser, cb, u32, Port, rc));
2456
2457 return rc;
2458}
2459
2460#ifdef IN_RING3
2461
2462static int buslogicR3PrepareBIOSSCSIRequest(PBUSLOGIC pBusLogic)
2463{
2464 int rc;
2465 PBUSLOGICTASKSTATE pTaskState;
2466 uint32_t uTargetDevice;
2467
2468 rc = RTMemCacheAllocEx(pBusLogic->hTaskCache, (void **)&pTaskState);
2469 AssertMsgRCReturn(rc, ("Getting task from cache failed rc=%Rrc\n", rc), rc);
2470
2471 pTaskState->fBIOS = true;
2472
2473 rc = vboxscsiSetupRequest(&pBusLogic->VBoxSCSI, &pTaskState->PDMScsiRequest, &uTargetDevice);
2474 AssertMsgRCReturn(rc, ("Setting up SCSI request failed rc=%Rrc\n", rc), rc);
2475
2476 pTaskState->PDMScsiRequest.pvUser = pTaskState;
2477
2478 pTaskState->CTX_SUFF(pTargetDevice) = &pBusLogic->aDeviceStates[uTargetDevice];
2479
2480 if (!pTaskState->CTX_SUFF(pTargetDevice)->fPresent)
2481 {
2482 /* Device is not present. */
2483 AssertMsg(pTaskState->PDMScsiRequest.pbCDB[0] == SCSI_INQUIRY,
2484 ("Device is not present but command is not inquiry\n"));
2485
2486 SCSIINQUIRYDATA ScsiInquiryData;
2487
2488 memset(&ScsiInquiryData, 0, sizeof(SCSIINQUIRYDATA));
2489 ScsiInquiryData.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN;
2490 ScsiInquiryData.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED;
2491
2492 memcpy(pBusLogic->VBoxSCSI.pbBuf, &ScsiInquiryData, 5);
2493
2494 rc = vboxscsiRequestFinished(&pBusLogic->VBoxSCSI, &pTaskState->PDMScsiRequest, SCSI_STATUS_OK);
2495 AssertMsgRCReturn(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc), rc);
2496
2497 RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
2498 }
2499 else
2500 {
2501 LogFlowFunc(("before increment %u\n", pTaskState->CTX_SUFF(pTargetDevice)->cOutstandingRequests));
2502 ASMAtomicIncU32(&pTaskState->CTX_SUFF(pTargetDevice)->cOutstandingRequests);
2503 LogFlowFunc(("after increment %u\n", pTaskState->CTX_SUFF(pTargetDevice)->cOutstandingRequests));
2504
2505 rc = pTaskState->CTX_SUFF(pTargetDevice)->pDrvSCSIConnector->pfnSCSIRequestSend(pTaskState->CTX_SUFF(pTargetDevice)->pDrvSCSIConnector,
2506 &pTaskState->PDMScsiRequest);
2507 AssertMsgRC(rc, ("Sending request to SCSI layer failed rc=%Rrc\n", rc));
2508 }
2509
2510 return rc;
2511}
2512
2513
2514/**
2515 * Port I/O Handler for IN operations - BIOS port.
2516 *
2517 * @returns VBox status code.
2518 *
2519 * @param pDevIns The device instance.
2520 * @param pvUser User argument.
2521 * @param uPort Port number used for the IN operation.
2522 * @param pu32 Where to store the result.
2523 * @param cb Number of bytes read.
2524 */
2525static DECLCALLBACK(int) buslogicR3BiosIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2526{
2527 int rc;
2528 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2529
2530 Assert(cb == 1);
2531
2532 rc = vboxscsiReadRegister(&pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT), pu32);
2533
2534 //Log2(("%s: pu32=%p:{%.*Rhxs} iRegister=%d rc=%Rrc\n",
2535 // __FUNCTION__, pu32, 1, pu32, (Port - BUSLOGIC_BIOS_IO_PORT), rc));
2536
2537 return rc;
2538}
2539
2540/**
2541 * Port I/O Handler for OUT operations - BIOS port.
2542 *
2543 * @returns VBox status code.
2544 *
2545 * @param pDevIns The device instance.
2546 * @param pvUser User argument.
2547 * @param uPort Port number used for the IN operation.
2548 * @param u32 The value to output.
2549 * @param cb The value size in bytes.
2550 */
2551static DECLCALLBACK(int) buslogicR3BiosIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2552{
2553 int rc;
2554 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2555
2556 Log2(("#%d %s: pvUser=%#p cb=%d u32=%#x Port=%#x\n",
2557 pDevIns->iInstance, __FUNCTION__, pvUser, cb, u32, Port));
2558
2559 Assert(cb == 1);
2560
2561 rc = vboxscsiWriteRegister(&pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT), (uint8_t)u32);
2562 if (rc == VERR_MORE_DATA)
2563 {
2564 rc = buslogicR3PrepareBIOSSCSIRequest(pBusLogic);
2565 AssertRC(rc);
2566 }
2567 else if (RT_FAILURE(rc))
2568 AssertMsgFailed(("Writing BIOS register failed %Rrc\n", rc));
2569
2570 return VINF_SUCCESS;
2571}
2572
2573/**
2574 * Port I/O Handler for primary port range OUT string operations.
2575 * @see FNIOMIOPORTOUTSTRING for details.
2576 */
2577static DECLCALLBACK(int) buslogicR3BiosIoPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrSrc,
2578 PRTGCUINTREG pcTransfer, unsigned cb)
2579{
2580 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2581 int rc;
2582
2583 Log2(("#%d %s: pvUser=%#p cb=%d Port=%#x\n",
2584 pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port));
2585
2586 rc = vboxscsiWriteString(pDevIns, &pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT),
2587 pGCPtrSrc, pcTransfer, cb);
2588 if (rc == VERR_MORE_DATA)
2589 {
2590 rc = buslogicR3PrepareBIOSSCSIRequest(pBusLogic);
2591 AssertRC(rc);
2592 }
2593 else if (RT_FAILURE(rc))
2594 AssertMsgFailed(("Writing BIOS register failed %Rrc\n", rc));
2595
2596 return rc;
2597}
2598
2599/**
2600 * Port I/O Handler for primary port range IN string operations.
2601 * @see FNIOMIOPORTINSTRING for details.
2602 */
2603static DECLCALLBACK(int) buslogicR3BiosIoPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst,
2604 PRTGCUINTREG pcTransfer, unsigned cb)
2605{
2606 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2607
2608 LogFlowFunc(("#%d %s: pvUser=%#p cb=%d Port=%#x\n",
2609 pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port));
2610
2611 return vboxscsiReadString(pDevIns, &pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT),
2612 pGCPtrDst, pcTransfer, cb);
2613}
2614
2615/**
2616 * Update the ISA I/O range.
2617 *
2618 * @returns nothing.
2619 * @param pBusLogic Pointer to the BusLogic device instance.
2620 * @param uBaseCode Encoded ISA I/O base; only low 3 bits are used.
2621 */
2622static int buslogicR3RegisterISARange(PBUSLOGIC pBusLogic, uint8_t uBaseCode)
2623{
2624 uint8_t uCode = uBaseCode & MAX_ISA_BASE;
2625 uint16_t uNewBase = g_aISABases[uCode];
2626 int rc = VINF_SUCCESS;
2627
2628 LogFlowFunc(("ISA I/O code %02X, new base %X\n", uBaseCode, uNewBase));
2629
2630 /* Check if the same port range is already registered. */
2631 if (uNewBase != pBusLogic->IOISABase)
2632 {
2633 /* Unregister the old range, if any. */
2634 if (pBusLogic->IOISABase)
2635 rc = PDMDevHlpIOPortDeregister(pBusLogic->CTX_SUFF(pDevIns), pBusLogic->IOISABase, 4);
2636
2637 if (RT_SUCCESS(rc))
2638 {
2639 pBusLogic->IOISABase = 0; /* First mark as unregistered. */
2640 pBusLogic->uISABaseCode = ISA_BASE_DISABLED;
2641
2642 if (uNewBase)
2643 {
2644 /* Register the new range if requested. */
2645 rc = PDMDevHlpIOPortRegister(pBusLogic->CTX_SUFF(pDevIns), uNewBase, 4, NULL,
2646 buslogicIOPortWrite, buslogicIOPortRead,
2647 NULL, NULL,
2648 "BusLogic ISA");
2649 if (RT_SUCCESS(rc))
2650 {
2651 pBusLogic->IOISABase = uNewBase;
2652 pBusLogic->uISABaseCode = uCode;
2653 }
2654 }
2655 }
2656 if (RT_SUCCESS(rc))
2657 {
2658 if (uNewBase)
2659 {
2660 Log(("ISA I/O base: %x\n", uNewBase));
2661 LogRel(("BusLogic: ISA I/O base: %x\n", uNewBase));
2662 }
2663 else
2664 {
2665 Log(("Disabling ISA I/O ports.\n"));
2666 LogRel(("BusLogic: ISA I/O disabled\n"));
2667 }
2668 }
2669
2670 }
2671 return rc;
2672}
2673
2674static void buslogicR3WarningDiskFull(PPDMDEVINS pDevIns)
2675{
2676 int rc;
2677 LogRel(("BusLogic#%d: Host disk full\n", pDevIns->iInstance));
2678 rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_DISKFULL",
2679 N_("Host system reported disk full. VM execution is suspended. You can resume after freeing some space"));
2680 AssertRC(rc);
2681}
2682
2683static void buslogicR3WarningFileTooBig(PPDMDEVINS pDevIns)
2684{
2685 int rc;
2686 LogRel(("BusLogic#%d: File too big\n", pDevIns->iInstance));
2687 rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_FILETOOBIG",
2688 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"));
2689 AssertRC(rc);
2690}
2691
2692static void buslogicR3WarningISCSI(PPDMDEVINS pDevIns)
2693{
2694 int rc;
2695 LogRel(("BusLogic#%d: iSCSI target unavailable\n", pDevIns->iInstance));
2696 rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_ISCSIDOWN",
2697 N_("The iSCSI target has stopped responding. VM execution is suspended. You can resume when it is available again"));
2698 AssertRC(rc);
2699}
2700
2701static void buslogicR3WarningUnknown(PPDMDEVINS pDevIns, int rc)
2702{
2703 int rc2;
2704 LogRel(("BusLogic#%d: Unknown but recoverable error has occurred (rc=%Rrc)\n", pDevIns->iInstance, rc));
2705 rc2 = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_UNKNOWN",
2706 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);
2707 AssertRC(rc2);
2708}
2709
2710static void buslogicR3RedoSetWarning(PBUSLOGIC pThis, int rc)
2711{
2712 if (rc == VERR_DISK_FULL)
2713 buslogicR3WarningDiskFull(pThis->CTX_SUFF(pDevIns));
2714 else if (rc == VERR_FILE_TOO_BIG)
2715 buslogicR3WarningFileTooBig(pThis->CTX_SUFF(pDevIns));
2716 else if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
2717 {
2718 /* iSCSI connection abort (first error) or failure to reestablish
2719 * connection (second error). Pause VM. On resume we'll retry. */
2720 buslogicR3WarningISCSI(pThis->CTX_SUFF(pDevIns));
2721 }
2722 else
2723 buslogicR3WarningUnknown(pThis->CTX_SUFF(pDevIns), rc);
2724}
2725
2726
2727static DECLCALLBACK(int) buslogicR3MmioMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion,
2728 RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
2729{
2730 PPDMDEVINS pDevIns = pPciDev->pDevIns;
2731 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
2732 int rc = VINF_SUCCESS;
2733
2734 Log2(("%s: registering MMIO area at GCPhysAddr=%RGp cb=%u\n", __FUNCTION__, GCPhysAddress, cb));
2735
2736 Assert(cb >= 32);
2737
2738 if (enmType == PCI_ADDRESS_SPACE_MEM)
2739 {
2740 /* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
2741 rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/,
2742 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
2743 buslogicMMIOWrite, buslogicMMIORead, "BusLogic MMIO");
2744 if (RT_FAILURE(rc))
2745 return rc;
2746
2747 if (pThis->fR0Enabled)
2748 {
2749 rc = PDMDevHlpMMIORegisterR0(pDevIns, GCPhysAddress, cb, NIL_RTR0PTR /*pvUser*/,
2750 "buslogicMMIOWrite", "buslogicMMIORead");
2751 if (RT_FAILURE(rc))
2752 return rc;
2753 }
2754
2755 if (pThis->fGCEnabled)
2756 {
2757 rc = PDMDevHlpMMIORegisterRC(pDevIns, GCPhysAddress, cb, NIL_RTRCPTR /*pvUser*/,
2758 "buslogicMMIOWrite", "buslogicMMIORead");
2759 if (RT_FAILURE(rc))
2760 return rc;
2761 }
2762
2763 pThis->MMIOBase = GCPhysAddress;
2764 }
2765 else if (enmType == PCI_ADDRESS_SPACE_IO)
2766 {
2767 rc = PDMDevHlpIOPortRegister(pDevIns, (RTIOPORT)GCPhysAddress, 32,
2768 NULL, buslogicIOPortWrite, buslogicIOPortRead, NULL, NULL, "BusLogic PCI");
2769 if (RT_FAILURE(rc))
2770 return rc;
2771
2772 if (pThis->fR0Enabled)
2773 {
2774 rc = PDMDevHlpIOPortRegisterR0(pDevIns, (RTIOPORT)GCPhysAddress, 32,
2775 0, "buslogicIOPortWrite", "buslogicIOPortRead", NULL, NULL, "BusLogic PCI");
2776 if (RT_FAILURE(rc))
2777 return rc;
2778 }
2779
2780 if (pThis->fGCEnabled)
2781 {
2782 rc = PDMDevHlpIOPortRegisterRC(pDevIns, (RTIOPORT)GCPhysAddress, 32,
2783 0, "buslogicIOPortWrite", "buslogicIOPortRead", NULL, NULL, "BusLogic PCI");
2784 if (RT_FAILURE(rc))
2785 return rc;
2786 }
2787
2788 pThis->IOPortBase = (RTIOPORT)GCPhysAddress;
2789 }
2790 else
2791 AssertMsgFailed(("Invalid enmType=%d\n", enmType));
2792
2793 return rc;
2794}
2795
2796static DECLCALLBACK(int) buslogicR3DeviceSCSIRequestCompleted(PPDMISCSIPORT pInterface, PPDMSCSIREQUEST pSCSIRequest,
2797 int rcCompletion, bool fRedo, int rcReq)
2798{
2799 int rc;
2800 PBUSLOGICTASKSTATE pTaskState = (PBUSLOGICTASKSTATE)pSCSIRequest->pvUser;
2801 PBUSLOGICDEVICE pBusLogicDevice = pTaskState->CTX_SUFF(pTargetDevice);
2802 PBUSLOGIC pBusLogic = pBusLogicDevice->CTX_SUFF(pBusLogic);
2803
2804 LogFlowFunc(("before decrement %u\n", pBusLogicDevice->cOutstandingRequests));
2805 ASMAtomicDecU32(&pBusLogicDevice->cOutstandingRequests);
2806 LogFlowFunc(("after decrement %u\n", pBusLogicDevice->cOutstandingRequests));
2807
2808 if (fRedo)
2809 {
2810 if (!pTaskState->fBIOS)
2811 {
2812 buslogicR3DataBufferFree(pTaskState);
2813
2814 if (pTaskState->pbSenseBuffer)
2815 buslogicR3SenseBufferFree(pTaskState, false /* fCopy */);
2816 }
2817
2818 /* Add to the list. */
2819 do
2820 {
2821 pTaskState->pRedoNext = ASMAtomicReadPtrT(&pBusLogic->pTasksRedoHead, PBUSLOGICTASKSTATE);
2822 } while (!ASMAtomicCmpXchgPtr(&pBusLogic->pTasksRedoHead, pTaskState, pTaskState->pRedoNext));
2823
2824 /* Suspend the VM if not done already. */
2825 if (!ASMAtomicXchgBool(&pBusLogic->fRedo, true))
2826 buslogicR3RedoSetWarning(pBusLogic, rcReq);
2827 }
2828 else
2829 {
2830 if (pTaskState->fBIOS)
2831 {
2832 rc = vboxscsiRequestFinished(&pBusLogic->VBoxSCSI, pSCSIRequest, rcCompletion);
2833 AssertMsgRC(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc));
2834 }
2835 else
2836 {
2837 buslogicR3DataBufferFree(pTaskState);
2838
2839 if (pTaskState->pbSenseBuffer)
2840 buslogicR3SenseBufferFree(pTaskState, (rcCompletion != SCSI_STATUS_OK));
2841
2842 if (rcCompletion == SCSI_STATUS_OK)
2843 buslogicR3SendIncomingMailbox(pBusLogic, pTaskState,
2844 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_CMD_COMPLETED,
2845 BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_OPERATION_GOOD,
2846 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_WITHOUT_ERROR);
2847 else if (rcCompletion == SCSI_STATUS_CHECK_CONDITION)
2848 buslogicR3SendIncomingMailbox(pBusLogic, pTaskState,
2849 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_CMD_COMPLETED,
2850 BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_CHECK_CONDITION,
2851 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_WITH_ERROR);
2852 else
2853 AssertMsgFailed(("invalid completion status %d\n", rcCompletion));
2854 }
2855#ifdef LOG_ENABLED
2856 buslogicR3DumpCCBInfo(&pTaskState->CommandControlBlockGuest, pTaskState->fIs24Bit);
2857#endif
2858
2859 /* Remove task from the cache. */
2860 RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
2861 }
2862
2863 if (pBusLogicDevice->cOutstandingRequests == 0 && pBusLogic->fSignalIdle)
2864 PDMDevHlpAsyncNotificationCompleted(pBusLogic->pDevInsR3);
2865
2866 return VINF_SUCCESS;
2867}
2868
2869static DECLCALLBACK(int) buslogicR3QueryDeviceLocation(PPDMISCSIPORT pInterface, const char **ppcszController,
2870 uint32_t *piInstance, uint32_t *piLUN)
2871{
2872 PBUSLOGICDEVICE pBusLogicDevice = PDMISCSIPORT_2_PBUSLOGICDEVICE(pInterface);
2873 PPDMDEVINS pDevIns = pBusLogicDevice->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
2874
2875 AssertPtrReturn(ppcszController, VERR_INVALID_POINTER);
2876 AssertPtrReturn(piInstance, VERR_INVALID_POINTER);
2877 AssertPtrReturn(piLUN, VERR_INVALID_POINTER);
2878
2879 *ppcszController = pDevIns->pReg->szName;
2880 *piInstance = pDevIns->iInstance;
2881 *piLUN = pBusLogicDevice->iLUN;
2882
2883 return VINF_SUCCESS;
2884}
2885
2886static int buslogicR3DeviceSCSIRequestSetup(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState)
2887{
2888 int rc = VINF_SUCCESS;
2889 uint8_t uTargetIdCCB;
2890 PBUSLOGICDEVICE pTargetDevice;
2891
2892 /* Fetch the CCB from guest memory. */
2893 /** @todo How much do we really have to read? */
2894 RTGCPHYS GCPhysAddrCCB = (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB;
2895 PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrCCB,
2896 &pTaskState->CommandControlBlockGuest, sizeof(CCB32));
2897
2898 uTargetIdCCB = pTaskState->fIs24Bit ? pTaskState->CommandControlBlockGuest.o.uTargetId : pTaskState->CommandControlBlockGuest.n.uTargetId;
2899 pTargetDevice = &pBusLogic->aDeviceStates[uTargetIdCCB];
2900 pTaskState->CTX_SUFF(pTargetDevice) = pTargetDevice;
2901
2902#ifdef LOG_ENABLED
2903 buslogicR3DumpCCBInfo(&pTaskState->CommandControlBlockGuest, pTaskState->fIs24Bit);
2904#endif
2905
2906 /* Alloc required buffers. */
2907 rc = buslogicR3DataBufferAlloc(pTaskState);
2908 AssertMsgRC(rc, ("Alloc failed rc=%Rrc\n", rc));
2909
2910 rc = buslogicR3SenseBufferAlloc(pTaskState);
2911 AssertMsgRC(rc, ("Mapping sense buffer failed rc=%Rrc\n", rc));
2912
2913 /* Check if device is present on bus. If not return error immediately and don't process this further. */
2914 if (!pBusLogic->aDeviceStates[uTargetIdCCB].fPresent)
2915 {
2916 buslogicR3DataBufferFree(pTaskState);
2917
2918 if (pTaskState->pbSenseBuffer)
2919 buslogicR3SenseBufferFree(pTaskState, true);
2920
2921 buslogicR3SendIncomingMailbox(pBusLogic, pTaskState,
2922 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_SCSI_SELECTION_TIMEOUT,
2923 BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_OPERATION_GOOD,
2924 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_WITH_ERROR);
2925
2926 RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
2927 }
2928 else
2929 {
2930 /* Setup SCSI request. */
2931 pTaskState->PDMScsiRequest.uLogicalUnit = pTaskState->fIs24Bit ? pTaskState->CommandControlBlockGuest.o.uLogicalUnit
2932 : pTaskState->CommandControlBlockGuest.n.uLogicalUnit;
2933
2934 if (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_UNKNOWN)
2935 pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_UNKNOWN;
2936 else if (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_IN)
2937 pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_FROM_DEVICE;
2938 else if (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_OUT)
2939 pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_TO_DEVICE;
2940 else if (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_NO_DATA)
2941 pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_NONE;
2942 else
2943 AssertMsgFailed(("Invalid data direction type %d\n", pTaskState->CommandControlBlockGuest.c.uDataDirection));
2944
2945 pTaskState->PDMScsiRequest.cbCDB = pTaskState->CommandControlBlockGuest.c.cbCDB;
2946 pTaskState->PDMScsiRequest.pbCDB = pTaskState->CommandControlBlockGuest.c.abCDB;
2947 if (pTaskState->DataSeg.cbSeg)
2948 {
2949 pTaskState->PDMScsiRequest.cbScatterGather = pTaskState->DataSeg.cbSeg;
2950 pTaskState->PDMScsiRequest.cScatterGatherEntries = 1;
2951 pTaskState->PDMScsiRequest.paScatterGatherHead = &pTaskState->DataSeg;
2952 }
2953 else
2954 {
2955 pTaskState->PDMScsiRequest.cbScatterGather = 0;
2956 pTaskState->PDMScsiRequest.cScatterGatherEntries = 0;
2957 pTaskState->PDMScsiRequest.paScatterGatherHead = NULL;
2958 }
2959 pTaskState->PDMScsiRequest.cbSenseBuffer = buslogicR3ConvertSenseBufferLength(pTaskState->CommandControlBlockGuest.c.cbSenseData);
2960 pTaskState->PDMScsiRequest.pbSenseBuffer = pTaskState->pbSenseBuffer;
2961 pTaskState->PDMScsiRequest.pvUser = pTaskState;
2962
2963 ASMAtomicIncU32(&pTargetDevice->cOutstandingRequests);
2964 rc = pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pTargetDevice->pDrvSCSIConnector, &pTaskState->PDMScsiRequest);
2965 AssertMsgRC(rc, ("Sending request to SCSI layer failed rc=%Rrc\n", rc));
2966 }
2967
2968 return rc;
2969}
2970
2971static int buslogicR3DeviceSCSIRequestAbort(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState)
2972{
2973 int rc = VINF_SUCCESS;
2974 uint8_t uTargetIdCCB;
2975 PBUSLOGICDEVICE pTargetDevice;
2976 RTGCPHYS GCPhysAddrCCB = (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB;
2977
2978 PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrCCB,
2979 &pTaskState->CommandControlBlockGuest, sizeof(CCB32));
2980
2981 uTargetIdCCB = pTaskState->fIs24Bit ? pTaskState->CommandControlBlockGuest.o.uTargetId : pTaskState->CommandControlBlockGuest.n.uTargetId;
2982 pTargetDevice = &pBusLogic->aDeviceStates[uTargetIdCCB];
2983 pTaskState->CTX_SUFF(pTargetDevice) = pTargetDevice;
2984
2985 buslogicR3SendIncomingMailbox(pBusLogic, pTaskState,
2986 BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_ABORT_QUEUE_GENERATED,
2987 BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_OPERATION_GOOD,
2988 BUSLOGIC_MAILBOX_INCOMING_COMPLETION_ABORTED_NOT_FOUND);
2989
2990 RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
2991
2992 return rc;
2993}
2994
2995/**
2996 * Read a mailbox from guest memory. Convert 24-bit mailboxes to
2997 * 32-bit format.
2998 *
2999 * @returns Mailbox guest physical address.
3000 * @param pBusLogic Pointer to the BusLogic instance data.
3001 * @param pTaskStat Pointer to the task state being set up.
3002 */
3003static RTGCPHYS buslogicR3ReadOutgoingMailbox(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState)
3004{
3005 RTGCPHYS GCMailbox;
3006
3007 if (pBusLogic->fMbxIs24Bit)
3008 {
3009 Mailbox24 Mbx24;
3010
3011 GCMailbox = pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->uMailboxOutgoingPositionCurrent * sizeof(Mailbox24));
3012 PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCMailbox, &Mbx24, sizeof(Mailbox24));
3013 pTaskState->MailboxGuest.u32PhysAddrCCB = ADDR_TO_U32(Mbx24.aPhysAddrCCB);
3014 pTaskState->MailboxGuest.u.out.uActionCode = Mbx24.uCmdState;
3015 }
3016 else
3017 {
3018 GCMailbox = pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->uMailboxOutgoingPositionCurrent * sizeof(Mailbox32));
3019 PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCMailbox, &pTaskState->MailboxGuest, sizeof(Mailbox32));
3020 }
3021
3022 return GCMailbox;
3023}
3024
3025/**
3026 * Read mailbox from the guest and execute command.
3027 *
3028 * @returns VBox status code.
3029 * @param pBusLogic Pointer to the BusLogic instance data.
3030 */
3031static int buslogicR3ProcessMailboxNext(PBUSLOGIC pBusLogic)
3032{
3033 PBUSLOGICTASKSTATE pTaskState = NULL;
3034 RTGCPHYS GCPhysAddrMailboxCurrent;
3035 int rc;
3036
3037 rc = RTMemCacheAllocEx(pBusLogic->hTaskCache, (void **)&pTaskState);
3038 AssertMsgReturn(RT_SUCCESS(rc) && (pTaskState != NULL), ("Failed to get task state from cache\n"), rc);
3039
3040 pTaskState->fBIOS = false;
3041 pTaskState->fIs24Bit = pBusLogic->fMbxIs24Bit;
3042 pTaskState->cbSGEntry = pBusLogic->fMbxIs24Bit ? sizeof(SGE24) : sizeof(SGE32);
3043
3044 if (!pBusLogic->fStrictRoundRobinMode)
3045 {
3046 /* Search for a filled mailbox - stop if we have scanned all mailboxes. */
3047 uint8_t uMailboxPosCur = pBusLogic->uMailboxOutgoingPositionCurrent;
3048
3049 do
3050 {
3051 /* Fetch mailbox from guest memory. */
3052 GCPhysAddrMailboxCurrent = buslogicR3ReadOutgoingMailbox(pBusLogic,pTaskState);
3053
3054 /* Check the next mailbox. */
3055 buslogicR3OutgoingMailboxAdvance(pBusLogic);
3056 } while ( pTaskState->MailboxGuest.u.out.uActionCode == BUSLOGIC_MAILBOX_OUTGOING_ACTION_FREE
3057 && uMailboxPosCur != pBusLogic->uMailboxOutgoingPositionCurrent);
3058 }
3059 else
3060 {
3061 /* Fetch mailbox from guest memory. */
3062 GCPhysAddrMailboxCurrent = buslogicR3ReadOutgoingMailbox(pBusLogic,pTaskState);
3063 }
3064
3065 /*
3066 * Check if the mailbox is actually loaded.
3067 * It might be possible that the guest notified us without
3068 * a loaded mailbox. Do nothing in that case but leave a
3069 * log entry.
3070 */
3071 if (pTaskState->MailboxGuest.u.out.uActionCode == BUSLOGIC_MAILBOX_OUTGOING_ACTION_FREE)
3072 {
3073 Log(("No loaded mailbox left\n"));
3074 RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
3075 return VERR_NO_DATA;
3076 }
3077
3078 LogFlow(("Got loaded mailbox at slot %u, CCB phys %RGp\n", pBusLogic->uMailboxOutgoingPositionCurrent, (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB));
3079#ifdef LOG_ENABLED
3080 buslogicR3DumpMailboxInfo(&pTaskState->MailboxGuest, true);
3081#endif
3082
3083 /* We got the mailbox, mark it as free in the guest. */
3084 uint8_t uActionCode = BUSLOGIC_MAILBOX_OUTGOING_ACTION_FREE;
3085 unsigned uCodeOffs = pTaskState->fIs24Bit ? RT_OFFSETOF(Mailbox24, uCmdState) : RT_OFFSETOF(Mailbox32, u.out.uActionCode);
3086 PDMDevHlpPCIPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxCurrent + uCodeOffs, &uActionCode, sizeof(uActionCode));
3087
3088 if (pTaskState->MailboxGuest.u.out.uActionCode == BUSLOGIC_MAILBOX_OUTGOING_ACTION_START_COMMAND)
3089 rc = buslogicR3DeviceSCSIRequestSetup(pBusLogic, pTaskState);
3090 else if (pTaskState->MailboxGuest.u.out.uActionCode == BUSLOGIC_MAILBOX_OUTGOING_ACTION_ABORT_COMMAND)
3091 {
3092 LogFlow(("Aborting mailbox\n"));
3093 rc = buslogicR3DeviceSCSIRequestAbort(pBusLogic, pTaskState);
3094 }
3095 else
3096 AssertMsgFailed(("Invalid outgoing mailbox action code %u\n", pTaskState->MailboxGuest.u.out.uActionCode));
3097
3098 AssertRC(rc);
3099
3100 /* Advance to the next mailbox. */
3101 if (pBusLogic->fStrictRoundRobinMode)
3102 buslogicR3OutgoingMailboxAdvance(pBusLogic);
3103
3104 return rc;
3105}
3106
3107/**
3108 * Transmit queue consumer
3109 * Queue a new async task.
3110 *
3111 * @returns Success indicator.
3112 * If false the item will not be removed and the flushing will stop.
3113 * @param pDevIns The device instance.
3114 * @param pItem The item to consume. Upon return this item will be freed.
3115 */
3116static DECLCALLBACK(bool) buslogicR3NotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
3117{
3118 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3119
3120 /* Reset notification send flag now. */
3121 Assert(pBusLogic->fNotificationSend);
3122 ASMAtomicXchgBool(&pBusLogic->fNotificationSend, false);
3123 ASMAtomicXchgU32(&pBusLogic->cMailboxesReady, 0); /** @todo Actually not required anymore but to stay compatible with older saved states. */
3124
3125 /* Process mailboxes. */
3126 int rc;
3127 do
3128 {
3129 rc = buslogicR3ProcessMailboxNext(pBusLogic);
3130 AssertMsg(RT_SUCCESS(rc) || rc == VERR_NO_DATA, ("Processing mailbox failed rc=%Rrc\n", rc));
3131 } while (RT_SUCCESS(rc));
3132
3133 return true;
3134}
3135
3136/**
3137 * Kicks the controller to process pending tasks after the VM was resumed
3138 * or loaded from a saved state.
3139 *
3140 * @returns nothing.
3141 * @param pThis The BusLogic device instance.
3142 */
3143static void buslogicR3Kick(PBUSLOGIC pThis)
3144{
3145 if (pThis->fRedo)
3146 {
3147 pThis->fRedo = false;
3148 if (pThis->VBoxSCSI.fBusy)
3149 {
3150
3151 /* The BIOS had a request active when we got suspended. Resume it. */
3152 int rc = buslogicR3PrepareBIOSSCSIRequest(pThis);
3153 AssertRC(rc);
3154 }
3155 else
3156 {
3157 /* Queue all pending tasks again. */
3158 PBUSLOGICTASKSTATE pTaskState = pThis->pTasksRedoHead;
3159
3160 pThis->pTasksRedoHead = NULL;
3161
3162 while (pTaskState)
3163 {
3164 PBUSLOGICTASKSTATE pCur = pTaskState;
3165
3166 int rc = buslogicR3DeviceSCSIRequestSetup(pThis, pCur);
3167 AssertRC(rc);
3168
3169 pTaskState = pTaskState->pRedoNext;
3170 }
3171 }
3172 }
3173}
3174
3175/** @callback_method_impl{FNSSMDEVLIVEEXEC} */
3176static DECLCALLBACK(int) buslogicR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
3177{
3178 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3179
3180 /* Save the device config. */
3181 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aDeviceStates); i++)
3182 SSMR3PutBool(pSSM, pThis->aDeviceStates[i].fPresent);
3183
3184 return VINF_SSM_DONT_CALL_AGAIN;
3185}
3186
3187/** @callback_method_impl{FNSSMDEVSAVEEXEC} */
3188static DECLCALLBACK(int) buslogicR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
3189{
3190 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3191
3192 /* Every device first. */
3193 for (unsigned i = 0; i < RT_ELEMENTS(pBusLogic->aDeviceStates); i++)
3194 {
3195 PBUSLOGICDEVICE pDevice = &pBusLogic->aDeviceStates[i];
3196
3197 AssertMsg(!pDevice->cOutstandingRequests,
3198 ("There are still outstanding requests on this device\n"));
3199 SSMR3PutBool(pSSM, pDevice->fPresent);
3200 SSMR3PutU32(pSSM, pDevice->cOutstandingRequests);
3201 }
3202 /* Now the main device state. */
3203 SSMR3PutU8 (pSSM, pBusLogic->regStatus);
3204 SSMR3PutU8 (pSSM, pBusLogic->regInterrupt);
3205 SSMR3PutU8 (pSSM, pBusLogic->regGeometry);
3206 SSMR3PutMem (pSSM, &pBusLogic->LocalRam, sizeof(pBusLogic->LocalRam));
3207 SSMR3PutU8 (pSSM, pBusLogic->uOperationCode);
3208 SSMR3PutMem (pSSM, &pBusLogic->aCommandBuffer, sizeof(pBusLogic->aCommandBuffer));
3209 SSMR3PutU8 (pSSM, pBusLogic->iParameter);
3210 SSMR3PutU8 (pSSM, pBusLogic->cbCommandParametersLeft);
3211 SSMR3PutBool (pSSM, pBusLogic->fUseLocalRam);
3212 SSMR3PutMem (pSSM, pBusLogic->aReplyBuffer, sizeof(pBusLogic->aReplyBuffer));
3213 SSMR3PutU8 (pSSM, pBusLogic->iReply);
3214 SSMR3PutU8 (pSSM, pBusLogic->cbReplyParametersLeft);
3215 SSMR3PutBool (pSSM, pBusLogic->fIRQEnabled);
3216 SSMR3PutU8 (pSSM, pBusLogic->uISABaseCode);
3217 SSMR3PutU32 (pSSM, pBusLogic->cMailbox);
3218 SSMR3PutBool (pSSM, pBusLogic->fMbxIs24Bit);
3219 SSMR3PutGCPhys(pSSM, pBusLogic->GCPhysAddrMailboxOutgoingBase);
3220 SSMR3PutU32 (pSSM, pBusLogic->uMailboxOutgoingPositionCurrent);
3221 SSMR3PutU32 (pSSM, pBusLogic->cMailboxesReady);
3222 SSMR3PutBool (pSSM, pBusLogic->fNotificationSend);
3223 SSMR3PutGCPhys(pSSM, pBusLogic->GCPhysAddrMailboxIncomingBase);
3224 SSMR3PutU32 (pSSM, pBusLogic->uMailboxIncomingPositionCurrent);
3225 SSMR3PutBool (pSSM, pBusLogic->fStrictRoundRobinMode);
3226 SSMR3PutBool (pSSM, pBusLogic->fExtendedLunCCBFormat);
3227 /* Now the data for the BIOS interface. */
3228 SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.regIdentify);
3229 SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.uTargetDevice);
3230 SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.uTxDir);
3231 SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.cbCDB);
3232 SSMR3PutMem (pSSM, pBusLogic->VBoxSCSI.abCDB, sizeof(pBusLogic->VBoxSCSI.abCDB));
3233 SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.iCDB);
3234 SSMR3PutU32 (pSSM, pBusLogic->VBoxSCSI.cbBuf);
3235 SSMR3PutU32 (pSSM, pBusLogic->VBoxSCSI.iBuf);
3236 SSMR3PutBool (pSSM, pBusLogic->VBoxSCSI.fBusy);
3237 SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.enmState);
3238 if (pBusLogic->VBoxSCSI.cbBuf)
3239 SSMR3PutMem(pSSM, pBusLogic->VBoxSCSI.pbBuf, pBusLogic->VBoxSCSI.cbBuf);
3240
3241 /*
3242 * Save the physical addresses of the command control blocks of still pending tasks.
3243 * They are processed again on resume.
3244 *
3245 * The number of pending tasks needs to be determined first.
3246 */
3247 uint32_t cTasks = 0;
3248
3249 PBUSLOGICTASKSTATE pTaskState = pBusLogic->pTasksRedoHead;
3250 if (pBusLogic->fRedo)
3251 {
3252 while (pTaskState)
3253 {
3254 cTasks++;
3255 pTaskState = pTaskState->pRedoNext;
3256 }
3257 }
3258 SSMR3PutU32(pSSM, cTasks);
3259
3260 /* Write the address of every task now. */
3261 pTaskState = pBusLogic->pTasksRedoHead;
3262 while (pTaskState)
3263 {
3264 SSMR3PutU32(pSSM, pTaskState->MailboxGuest.u32PhysAddrCCB);
3265 pTaskState = pTaskState->pRedoNext;
3266 }
3267
3268 return SSMR3PutU32(pSSM, ~0);
3269}
3270
3271/** @callback_method_impl{FNSSMDEVLOADDONE} */
3272static DECLCALLBACK(int) buslogicR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
3273{
3274 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3275
3276 buslogicR3RegisterISARange(pThis, pThis->uISABaseCode);
3277 buslogicR3Kick(pThis);
3278 return VINF_SUCCESS;
3279}
3280
3281/** @callback_method_impl{FNSSMDEVLOADEXEC} */
3282static DECLCALLBACK(int) buslogicR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
3283{
3284 PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3285 int rc = VINF_SUCCESS;
3286
3287 /* We support saved states only from this and older versions. */
3288 if (uVersion > BUSLOGIC_SAVED_STATE_MINOR_VERSION)
3289 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
3290
3291 /* Every device first. */
3292 for (unsigned i = 0; i < RT_ELEMENTS(pBusLogic->aDeviceStates); i++)
3293 {
3294 PBUSLOGICDEVICE pDevice = &pBusLogic->aDeviceStates[i];
3295
3296 AssertMsg(!pDevice->cOutstandingRequests,
3297 ("There are still outstanding requests on this device\n"));
3298 bool fPresent;
3299 rc = SSMR3GetBool(pSSM, &fPresent);
3300 AssertRCReturn(rc, rc);
3301 if (pDevice->fPresent != fPresent)
3302 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target %u config mismatch: config=%RTbool state=%RTbool"), i, pDevice->fPresent, fPresent);
3303
3304 if (uPass == SSM_PASS_FINAL)
3305 SSMR3GetU32(pSSM, (uint32_t *)&pDevice->cOutstandingRequests);
3306 }
3307
3308 if (uPass != SSM_PASS_FINAL)
3309 return VINF_SUCCESS;
3310
3311 /* Now the main device state. */
3312 SSMR3GetU8 (pSSM, (uint8_t *)&pBusLogic->regStatus);
3313 SSMR3GetU8 (pSSM, (uint8_t *)&pBusLogic->regInterrupt);
3314 SSMR3GetU8 (pSSM, (uint8_t *)&pBusLogic->regGeometry);
3315 SSMR3GetMem (pSSM, &pBusLogic->LocalRam, sizeof(pBusLogic->LocalRam));
3316 SSMR3GetU8 (pSSM, &pBusLogic->uOperationCode);
3317 if (uVersion > BUSLOGIC_SAVED_STATE_MINOR_PRE_CMDBUF_RESIZE)
3318 SSMR3GetMem (pSSM, &pBusLogic->aCommandBuffer, sizeof(pBusLogic->aCommandBuffer));
3319 else
3320 SSMR3GetMem (pSSM, &pBusLogic->aCommandBuffer, BUSLOGIC_COMMAND_SIZE_OLD);
3321 SSMR3GetU8 (pSSM, &pBusLogic->iParameter);
3322 SSMR3GetU8 (pSSM, &pBusLogic->cbCommandParametersLeft);
3323 SSMR3GetBool (pSSM, &pBusLogic->fUseLocalRam);
3324 SSMR3GetMem (pSSM, pBusLogic->aReplyBuffer, sizeof(pBusLogic->aReplyBuffer));
3325 SSMR3GetU8 (pSSM, &pBusLogic->iReply);
3326 SSMR3GetU8 (pSSM, &pBusLogic->cbReplyParametersLeft);
3327 SSMR3GetBool (pSSM, &pBusLogic->fIRQEnabled);
3328 SSMR3GetU8 (pSSM, &pBusLogic->uISABaseCode);
3329 SSMR3GetU32 (pSSM, &pBusLogic->cMailbox);
3330 if (uVersion > BUSLOGIC_SAVED_STATE_MINOR_PRE_24BIT_MBOX)
3331 SSMR3GetBool (pSSM, &pBusLogic->fMbxIs24Bit);
3332 SSMR3GetGCPhys(pSSM, &pBusLogic->GCPhysAddrMailboxOutgoingBase);
3333 SSMR3GetU32 (pSSM, &pBusLogic->uMailboxOutgoingPositionCurrent);
3334 SSMR3GetU32 (pSSM, (uint32_t *)&pBusLogic->cMailboxesReady);
3335 SSMR3GetBool (pSSM, (bool *)&pBusLogic->fNotificationSend);
3336 SSMR3GetGCPhys(pSSM, &pBusLogic->GCPhysAddrMailboxIncomingBase);
3337 SSMR3GetU32 (pSSM, &pBusLogic->uMailboxIncomingPositionCurrent);
3338 SSMR3GetBool (pSSM, &pBusLogic->fStrictRoundRobinMode);
3339 SSMR3GetBool (pSSM, &pBusLogic->fExtendedLunCCBFormat);
3340 /* Now the data for the BIOS interface. */
3341 SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.regIdentify);
3342 SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.uTargetDevice);
3343 SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.uTxDir);
3344 SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.cbCDB);
3345 SSMR3GetMem (pSSM, pBusLogic->VBoxSCSI.abCDB, sizeof(pBusLogic->VBoxSCSI.abCDB));
3346 SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.iCDB);
3347 SSMR3GetU32 (pSSM, &pBusLogic->VBoxSCSI.cbBuf);
3348 SSMR3GetU32 (pSSM, &pBusLogic->VBoxSCSI.iBuf);
3349 SSMR3GetBool(pSSM, (bool *)&pBusLogic->VBoxSCSI.fBusy);
3350 SSMR3GetU8 (pSSM, (uint8_t *)&pBusLogic->VBoxSCSI.enmState);
3351 if (pBusLogic->VBoxSCSI.cbBuf)
3352 {
3353 pBusLogic->VBoxSCSI.pbBuf = (uint8_t *)RTMemAllocZ(pBusLogic->VBoxSCSI.cbBuf);
3354 if (!pBusLogic->VBoxSCSI.pbBuf)
3355 {
3356 LogRel(("BusLogic: Out of memory during restore.\n"));
3357 return PDMDEV_SET_ERROR(pDevIns, VERR_NO_MEMORY,
3358 N_("BusLogic: Out of memory during restore\n"));
3359 }
3360 SSMR3GetMem(pSSM, pBusLogic->VBoxSCSI.pbBuf, pBusLogic->VBoxSCSI.cbBuf);
3361 }
3362
3363 if (pBusLogic->VBoxSCSI.fBusy)
3364 pBusLogic->fRedo = true;
3365
3366 if (uVersion > BUSLOGIC_SAVED_STATE_MINOR_PRE_ERROR_HANDLING)
3367 {
3368 /* Check if there are pending tasks saved. */
3369 uint32_t cTasks = 0;
3370
3371 SSMR3GetU32(pSSM, &cTasks);
3372
3373 if (cTasks)
3374 pBusLogic->fRedo = true;
3375
3376 for (uint32_t i = 0; i < cTasks; i++)
3377 {
3378 PBUSLOGICTASKSTATE pTaskState = (PBUSLOGICTASKSTATE)RTMemCacheAlloc(pBusLogic->hTaskCache);
3379 if (!pTaskState)
3380 {
3381 rc = VERR_NO_MEMORY;
3382 break;
3383 }
3384
3385 rc = SSMR3GetU32(pSSM, &pTaskState->MailboxGuest.u32PhysAddrCCB);
3386 if (RT_FAILURE(rc))
3387 {
3388 RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
3389 break;
3390 }
3391
3392 /* Link into the list. */
3393 pTaskState->pRedoNext = pBusLogic->pTasksRedoHead;
3394 pBusLogic->pTasksRedoHead = pTaskState;
3395 }
3396 }
3397
3398 if (RT_SUCCESS(rc))
3399 {
3400 uint32_t u32;
3401 rc = SSMR3GetU32(pSSM, &u32);
3402 if (RT_SUCCESS(rc))
3403 AssertMsgReturn(u32 == ~0U, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
3404 }
3405
3406 return rc;
3407}
3408
3409/**
3410 * Gets the pointer to the status LED of a device - called from the SCSI driver.
3411 *
3412 * @returns VBox status code.
3413 * @param pInterface Pointer to the interface structure containing the called function pointer.
3414 * @param iLUN The unit which status LED we desire. Always 0 here as the driver
3415 * doesn't know about other LUN's.
3416 * @param ppLed Where to store the LED pointer.
3417 */
3418static DECLCALLBACK(int) buslogicR3DeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
3419{
3420 PBUSLOGICDEVICE pDevice = PDMILEDPORTS_2_PBUSLOGICDEVICE(pInterface);
3421 if (iLUN == 0)
3422 {
3423 *ppLed = &pDevice->Led;
3424 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
3425 return VINF_SUCCESS;
3426 }
3427 return VERR_PDM_LUN_NOT_FOUND;
3428}
3429
3430/**
3431 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
3432 */
3433static DECLCALLBACK(void *) buslogicR3DeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
3434{
3435 PBUSLOGICDEVICE pDevice = PDMIBASE_2_PBUSLOGICDEVICE(pInterface);
3436 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevice->IBase);
3437 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSIPORT, &pDevice->ISCSIPort);
3438 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pDevice->ILed);
3439 return NULL;
3440}
3441
3442/**
3443 * Gets the pointer to the status LED of a unit.
3444 *
3445 * @returns VBox status code.
3446 * @param pInterface Pointer to the interface structure containing the called function pointer.
3447 * @param iLUN The unit which status LED we desire.
3448 * @param ppLed Where to store the LED pointer.
3449 */
3450static DECLCALLBACK(int) buslogicR3StatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
3451{
3452 PBUSLOGIC pBusLogic = PDMILEDPORTS_2_PBUSLOGIC(pInterface);
3453 if (iLUN < BUSLOGIC_MAX_DEVICES)
3454 {
3455 *ppLed = &pBusLogic->aDeviceStates[iLUN].Led;
3456 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
3457 return VINF_SUCCESS;
3458 }
3459 return VERR_PDM_LUN_NOT_FOUND;
3460}
3461
3462/**
3463 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
3464 */
3465static DECLCALLBACK(void *) buslogicR3StatusQueryInterface(PPDMIBASE pInterface, const char *pszIID)
3466{
3467 PBUSLOGIC pThis = PDMIBASE_2_PBUSLOGIC(pInterface);
3468 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
3469 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
3470 return NULL;
3471}
3472
3473/**
3474 * BusLogic debugger info callback.
3475 *
3476 * @param pDevIns The device instance.
3477 * @param pHlp The output helpers.
3478 * @param pszArgs The arguments.
3479 */
3480static DECLCALLBACK(void) buslogicR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
3481{
3482 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3483 unsigned i;
3484 bool fVerbose = false;
3485
3486 /* Parse arguments. */
3487 if (pszArgs)
3488 fVerbose = strstr(pszArgs, "verbose") != NULL;
3489
3490 /* Show basic information. */
3491 pHlp->pfnPrintf(pHlp,
3492 "%s#%d: PCI I/O=%RTiop ISA I/O=%RTiop MMIO=%RGp IRQ=%u GC=%RTbool R0=%RTbool\n",
3493 pDevIns->pReg->szName,
3494 pDevIns->iInstance,
3495 pThis->IOPortBase, pThis->IOISABase, pThis->MMIOBase,
3496 PCIDevGetInterruptLine(&pThis->dev),
3497 !!pThis->fGCEnabled, !!pThis->fR0Enabled);
3498
3499 /* Print mailbox state. */
3500 if (pThis->regStatus & BL_STAT_INREQ)
3501 pHlp->pfnPrintf(pHlp, "Mailbox not initialized\n");
3502 else
3503 pHlp->pfnPrintf(pHlp, "%u-bit mailbox with %u entries at %RGp (%d LUN CCBs)\n",
3504 pThis->fMbxIs24Bit ? 24 : 32, pThis->cMailbox,
3505 pThis->GCPhysAddrMailboxOutgoingBase,
3506 pThis->fMbxIs24Bit ? 8 : pThis->fExtendedLunCCBFormat ? 64 : 8);
3507
3508 /* Print register contents. */
3509 pHlp->pfnPrintf(pHlp, "Registers: STAT=%02x INTR=%02x GEOM=%02x\n",
3510 pThis->regStatus, pThis->regInterrupt, pThis->regGeometry);
3511
3512 /* Print miscellaneous state. */
3513 pHlp->pfnPrintf(pHlp, "HAC interrupts: %s\n",
3514 pThis->fIRQEnabled ? "on" : "off");
3515
3516 /* Print the current command, if any. */
3517 if (pThis->uOperationCode != 0xff )
3518 pHlp->pfnPrintf(pHlp, "Current command: %02X\n", pThis->uOperationCode);
3519
3520 if (fVerbose && (pThis->regStatus & BL_STAT_INREQ) == 0)
3521 {
3522 RTGCPHYS GCMailbox;
3523
3524 /* Dump the mailbox contents. */
3525 if (pThis->fMbxIs24Bit)
3526 {
3527 Mailbox24 Mbx24;
3528
3529 /* Outgoing mailbox, 24-bit format. */
3530 GCMailbox = pThis->GCPhysAddrMailboxOutgoingBase;
3531 pHlp->pfnPrintf(pHlp, " Outgoing mailbox entries (24-bit) at %06X:\n", GCMailbox);
3532 for (i = 0; i < pThis->cMailbox; ++i)
3533 {
3534 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCMailbox, &Mbx24, sizeof(Mailbox24));
3535 pHlp->pfnPrintf(pHlp, " slot %03d: CCB at %06X action code %02X", i, ADDR_TO_U32(Mbx24.aPhysAddrCCB), Mbx24.uCmdState);
3536 pHlp->pfnPrintf(pHlp, "%s\n", pThis->uMailboxOutgoingPositionCurrent == i ? " *" : "");
3537 GCMailbox += sizeof(Mailbox24);
3538 }
3539
3540 /* Incoming mailbox, 24-bit format. */
3541 GCMailbox = pThis->GCPhysAddrMailboxOutgoingBase + (pThis->cMailbox * sizeof(Mailbox24));
3542 pHlp->pfnPrintf(pHlp, " Incoming mailbox entries (24-bit) at %06X:\n", GCMailbox);
3543 for (i = 0; i < pThis->cMailbox; ++i)
3544 {
3545 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCMailbox, &Mbx24, sizeof(Mailbox24));
3546 pHlp->pfnPrintf(pHlp, " slot %03d: CCB at %06X completion code %02X", i, ADDR_TO_U32(Mbx24.aPhysAddrCCB), Mbx24.uCmdState);
3547 pHlp->pfnPrintf(pHlp, "%s\n", pThis->uMailboxIncomingPositionCurrent == i ? " *" : "");
3548 GCMailbox += sizeof(Mailbox24);
3549 }
3550
3551 }
3552 else
3553 {
3554 Mailbox32 Mbx32;
3555
3556 /* Outgoing mailbox, 32-bit format. */
3557 GCMailbox = pThis->GCPhysAddrMailboxOutgoingBase;
3558 pHlp->pfnPrintf(pHlp, " Outgoing mailbox entries (32-bit) at %08X:\n", (uint32_t)GCMailbox);
3559 for (i = 0; i < pThis->cMailbox; ++i)
3560 {
3561 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCMailbox, &Mbx32, sizeof(Mailbox32));
3562 pHlp->pfnPrintf(pHlp, " slot %03d: CCB at %08X action code %02X", i, Mbx32.u32PhysAddrCCB, Mbx32.u.out.uActionCode);
3563 pHlp->pfnPrintf(pHlp, "%s\n", pThis->uMailboxOutgoingPositionCurrent == i ? " *" : "");
3564 GCMailbox += sizeof(Mailbox32);
3565 }
3566
3567 /* Incoming mailbox, 32-bit format. */
3568 GCMailbox = pThis->GCPhysAddrMailboxOutgoingBase + (pThis->cMailbox * sizeof(Mailbox32));
3569 pHlp->pfnPrintf(pHlp, " Outgoing mailbox entries (32-bit) at %08X:\n", (uint32_t)GCMailbox);
3570 for (i = 0; i < pThis->cMailbox; ++i)
3571 {
3572 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCMailbox, &Mbx32, sizeof(Mailbox32));
3573 pHlp->pfnPrintf(pHlp, " slot %03d: CCB at %08X completion code %02X BTSTAT %02X SDSTAT %02X", i,
3574 Mbx32.u32PhysAddrCCB, Mbx32.u.in.uCompletionCode, Mbx32.u.in.uHostAdapterStatus, Mbx32.u.in.uTargetDeviceStatus);
3575 pHlp->pfnPrintf(pHlp, "%s\n", pThis->uMailboxOutgoingPositionCurrent == i ? " *" : "");
3576 GCMailbox += sizeof(Mailbox32);
3577 }
3578
3579 }
3580 }
3581}
3582
3583/* -=-=-=-=- Helper -=-=-=-=- */
3584
3585 /**
3586 * Checks if all asynchronous I/O is finished.
3587 *
3588 * Used by buslogicR3Reset, buslogicR3Suspend and buslogicR3PowerOff.
3589 *
3590 * @returns true if quiesced, false if busy.
3591 * @param pDevIns The device instance.
3592 */
3593static bool buslogicR3AllAsyncIOIsFinished(PPDMDEVINS pDevIns)
3594{
3595 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3596
3597 for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aDeviceStates); i++)
3598 {
3599 PBUSLOGICDEVICE pThisDevice = &pThis->aDeviceStates[i];
3600 if (pThisDevice->pDrvBase)
3601 {
3602 if (pThisDevice->cOutstandingRequests != 0)
3603 return false;
3604 }
3605 }
3606
3607 return true;
3608}
3609
3610/**
3611 * Callback employed by buslogicR3Suspend and buslogicR3PowerOff..
3612 *
3613 * @returns true if we've quiesced, false if we're still working.
3614 * @param pDevIns The device instance.
3615 */
3616static DECLCALLBACK(bool) buslogicR3IsAsyncSuspendOrPowerOffDone(PPDMDEVINS pDevIns)
3617{
3618 if (!buslogicR3AllAsyncIOIsFinished(pDevIns))
3619 return false;
3620
3621 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3622 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
3623 return true;
3624}
3625
3626/**
3627 * Common worker for ahciR3Suspend and ahciR3PowerOff.
3628 */
3629static void buslogicR3SuspendOrPowerOff(PPDMDEVINS pDevIns, bool fPowerOff)
3630{
3631 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3632
3633 ASMAtomicWriteBool(&pThis->fSignalIdle, true);
3634 if (!buslogicR3AllAsyncIOIsFinished(pDevIns))
3635 PDMDevHlpSetAsyncNotification(pDevIns, buslogicR3IsAsyncSuspendOrPowerOffDone);
3636 else
3637 {
3638 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
3639
3640 AssertMsg(!pThis->fNotificationSend, ("The PDM Queue should be empty at this point\n"));
3641
3642 if (pThis->fRedo)
3643 {
3644 if (fPowerOff)
3645 {
3646 /* Free tasks which would have been queued again on resume. */
3647 PBUSLOGICTASKSTATE pTaskState = pThis->pTasksRedoHead;
3648
3649 pThis->pTasksRedoHead = NULL;
3650
3651 while (pTaskState)
3652 {
3653 PBUSLOGICTASKSTATE pFree;
3654
3655 pFree = pTaskState;
3656 pTaskState = pTaskState->pRedoNext;
3657
3658 RTMemCacheFree(pThis->hTaskCache, pFree);
3659 }
3660 pThis->fRedo = false;
3661 }
3662 else if (pThis->VBoxSCSI.fBusy)
3663 {
3664 /* Destroy the task because the BIOS interface has all necessary information. */
3665 Assert(pThis->pTasksRedoHead->fBIOS);
3666 Assert(!pThis->pTasksRedoHead->pRedoNext);
3667
3668 RTMemCacheFree(pThis->hTaskCache, pThis->pTasksRedoHead);
3669 pThis->pTasksRedoHead = NULL;
3670 }
3671 }
3672 }
3673}
3674
3675/**
3676 * Suspend notification.
3677 *
3678 * @param pDevIns The device instance data.
3679 */
3680static DECLCALLBACK(void) buslogicR3Suspend(PPDMDEVINS pDevIns)
3681{
3682 Log(("buslogicR3Suspend\n"));
3683 buslogicR3SuspendOrPowerOff(pDevIns, false /* fPoweroff */);
3684}
3685
3686/**
3687 * Resume notification.
3688 *
3689 * @param pDevIns The device instance data.
3690 */
3691static DECLCALLBACK(void) buslogicR3Resume(PPDMDEVINS pDevIns)
3692{
3693 Log(("buslogicR3Resume\n"));
3694 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3695 buslogicR3Kick(pThis);
3696}
3697
3698
3699/**
3700 * Detach notification.
3701 *
3702 * One harddisk at one port has been unplugged.
3703 * The VM is suspended at this point.
3704 *
3705 * @param pDevIns The device instance.
3706 * @param iLUN The logical unit which is being detached.
3707 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
3708 */
3709static DECLCALLBACK(void) buslogicR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
3710{
3711 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3712 PBUSLOGICDEVICE pDevice = &pThis->aDeviceStates[iLUN];
3713
3714 Log(("%s:\n", __FUNCTION__));
3715
3716 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
3717 ("BusLogic: Device does not support hotplugging\n"));
3718
3719 /*
3720 * Zero some important members.
3721 */
3722 pDevice->pDrvBase = NULL;
3723 pDevice->fPresent = false;
3724 pDevice->pDrvSCSIConnector = NULL;
3725}
3726
3727/**
3728 * Attach command.
3729 *
3730 * This is called when we change block driver.
3731 *
3732 * @returns VBox status code.
3733 * @param pDevIns The device instance.
3734 * @param iLUN The logical unit which is being detached.
3735 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
3736 */
3737static DECLCALLBACK(int) buslogicR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
3738{
3739 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3740 PBUSLOGICDEVICE pDevice = &pThis->aDeviceStates[iLUN];
3741 int rc;
3742
3743 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
3744 ("BusLogic: Device does not support hotplugging\n"),
3745 VERR_INVALID_PARAMETER);
3746
3747 /* the usual paranoia */
3748 AssertRelease(!pDevice->pDrvBase);
3749 AssertRelease(!pDevice->pDrvSCSIConnector);
3750 Assert(pDevice->iLUN == iLUN);
3751
3752 /*
3753 * Try attach the block device and get the interfaces,
3754 * required as well as optional.
3755 */
3756 rc = PDMDevHlpDriverAttach(pDevIns, pDevice->iLUN, &pDevice->IBase, &pDevice->pDrvBase, NULL);
3757 if (RT_SUCCESS(rc))
3758 {
3759 /* Get SCSI connector interface. */
3760 pDevice->pDrvSCSIConnector = PDMIBASE_QUERY_INTERFACE(pDevice->pDrvBase, PDMISCSICONNECTOR);
3761 AssertMsgReturn(pDevice->pDrvSCSIConnector, ("Missing SCSI interface below\n"), VERR_PDM_MISSING_INTERFACE);
3762 pDevice->fPresent = true;
3763 }
3764 else
3765 AssertMsgFailed(("Failed to attach LUN#%d. rc=%Rrc\n", pDevice->iLUN, rc));
3766
3767 if (RT_FAILURE(rc))
3768 {
3769 pDevice->pDrvBase = NULL;
3770 pDevice->pDrvSCSIConnector = NULL;
3771 }
3772 return rc;
3773}
3774
3775/**
3776 * Callback employed by buslogicR3Reset.
3777 *
3778 * @returns true if we've quiesced, false if we're still working.
3779 * @param pDevIns The device instance.
3780 */
3781static DECLCALLBACK(bool) buslogicR3IsAsyncResetDone(PPDMDEVINS pDevIns)
3782{
3783 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3784
3785 if (!buslogicR3AllAsyncIOIsFinished(pDevIns))
3786 return false;
3787 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
3788
3789 buslogicR3HwReset(pThis, true);
3790 return true;
3791}
3792
3793/**
3794 * @copydoc FNPDMDEVRESET
3795 */
3796static DECLCALLBACK(void) buslogicR3Reset(PPDMDEVINS pDevIns)
3797{
3798 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3799
3800 ASMAtomicWriteBool(&pThis->fSignalIdle, true);
3801 if (!buslogicR3AllAsyncIOIsFinished(pDevIns))
3802 PDMDevHlpSetAsyncNotification(pDevIns, buslogicR3IsAsyncResetDone);
3803 else
3804 {
3805 ASMAtomicWriteBool(&pThis->fSignalIdle, false);
3806 buslogicR3HwReset(pThis, true);
3807 }
3808}
3809
3810static DECLCALLBACK(void) buslogicR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
3811{
3812 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3813
3814 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
3815 pThis->pNotifierQueueRC = PDMQueueRCPtr(pThis->pNotifierQueueR3);
3816
3817 for (uint32_t i = 0; i < BUSLOGIC_MAX_DEVICES; i++)
3818 {
3819 PBUSLOGICDEVICE pDevice = &pThis->aDeviceStates[i];
3820
3821 pDevice->pBusLogicRC = PDMINS_2_DATA_RCPTR(pDevIns);
3822 }
3823
3824}
3825
3826/**
3827 * Poweroff notification.
3828 *
3829 * @param pDevIns Pointer to the device instance
3830 */
3831static DECLCALLBACK(void) buslogicR3PowerOff(PPDMDEVINS pDevIns)
3832{
3833 Log(("buslogicR3PowerOff\n"));
3834 buslogicR3SuspendOrPowerOff(pDevIns, true /* fPoweroff */);
3835}
3836
3837/**
3838 * Destroy a driver instance.
3839 *
3840 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
3841 * resources can be freed correctly.
3842 *
3843 * @param pDevIns The device instance data.
3844 */
3845static DECLCALLBACK(int) buslogicR3Destruct(PPDMDEVINS pDevIns)
3846{
3847 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3848 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
3849
3850 PDMR3CritSectDelete(&pThis->CritSectIntr);
3851
3852 /*
3853 * Free all tasks which are still hanging around
3854 * (Power off after the VM was suspended).
3855 */
3856 if (pThis->fRedo)
3857 {
3858 /* Free tasks which would have been queued again on resume. */
3859 PBUSLOGICTASKSTATE pTaskState = pThis->pTasksRedoHead;
3860
3861 pThis->pTasksRedoHead = NULL;
3862
3863 while (pTaskState)
3864 {
3865 PBUSLOGICTASKSTATE pFree;
3866
3867 pFree = pTaskState;
3868 pTaskState = pTaskState->pRedoNext;
3869
3870 RTMemCacheFree(pThis->hTaskCache, pFree);
3871 }
3872 pThis->fRedo = false;
3873 }
3874
3875 int rc = RTMemCacheDestroy(pThis->hTaskCache);
3876 AssertMsgRC(rc, ("Destroying task cache failed rc=%Rrc\n", rc));
3877
3878 return rc;
3879}
3880
3881/**
3882 * @interface_method_impl{PDMDEVREG,pfnConstruct}
3883 */
3884static DECLCALLBACK(int) buslogicR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
3885{
3886 PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
3887 int rc = VINF_SUCCESS;
3888 bool fBootable = true;
3889 char achISACompat[16];
3890 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
3891
3892 /*
3893 * Init instance data (do early because of constructor).
3894 */
3895 pThis->hTaskCache = NIL_RTMEMCACHE;
3896 pThis->pDevInsR3 = pDevIns;
3897 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
3898 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
3899 pThis->IBase.pfnQueryInterface = buslogicR3StatusQueryInterface;
3900 pThis->ILeds.pfnQueryStatusLed = buslogicR3StatusQueryStatusLed;
3901
3902 PCIDevSetVendorId (&pThis->dev, 0x104b); /* BusLogic */
3903 PCIDevSetDeviceId (&pThis->dev, 0x1040); /* BT-958 */
3904 PCIDevSetCommand (&pThis->dev, 0x0003);
3905 PCIDevSetRevisionId (&pThis->dev, 0x01);
3906 PCIDevSetClassProg (&pThis->dev, 0x00); /* SCSI */
3907 PCIDevSetClassSub (&pThis->dev, 0x00); /* SCSI */
3908 PCIDevSetClassBase (&pThis->dev, 0x01); /* Mass storage */
3909 PCIDevSetBaseAddress (&pThis->dev, 0, true /*IO*/, false /*Pref*/, false /*64-bit*/, 0x00000000);
3910 PCIDevSetBaseAddress (&pThis->dev, 1, false /*IO*/, false /*Pref*/, false /*64-bit*/, 0x00000000);
3911 PCIDevSetSubSystemVendorId(&pThis->dev, 0x104b);
3912 PCIDevSetSubSystemId (&pThis->dev, 0x1040);
3913 PCIDevSetInterruptLine (&pThis->dev, 0x00);
3914 PCIDevSetInterruptPin (&pThis->dev, 0x01);
3915
3916 /*
3917 * Validate and read configuration.
3918 */
3919 if (!CFGMR3AreValuesValid(pCfg,
3920 "GCEnabled\0"
3921 "R0Enabled\0"
3922 "Bootable\0"
3923 "ISACompat\0"))
3924 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
3925 N_("BusLogic configuration error: unknown option specified"));
3926
3927 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true);
3928 if (RT_FAILURE(rc))
3929 return PDMDEV_SET_ERROR(pDevIns, rc,
3930 N_("BusLogic configuration error: failed to read GCEnabled as boolean"));
3931 Log(("%s: fGCEnabled=%d\n", __FUNCTION__, pThis->fGCEnabled));
3932
3933 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
3934 if (RT_FAILURE(rc))
3935 return PDMDEV_SET_ERROR(pDevIns, rc,
3936 N_("BusLogic configuration error: failed to read R0Enabled as boolean"));
3937 Log(("%s: fR0Enabled=%d\n", __FUNCTION__, pThis->fR0Enabled));
3938 rc = CFGMR3QueryBoolDef(pCfg, "Bootable", &fBootable, true);
3939 if (RT_FAILURE(rc))
3940 return PDMDEV_SET_ERROR(pDevIns, rc,
3941 N_("BusLogic configuration error: failed to read Bootable as boolean"));
3942 Log(("%s: fBootable=%RTbool\n", __FUNCTION__, fBootable));
3943
3944 /* Only the first instance defaults to having the ISA compatibility ports enabled. */
3945 if (iInstance == 0)
3946 rc = CFGMR3QueryStringDef(pCfg, "ISACompat", achISACompat, sizeof(achISACompat), "Alternate");
3947 else
3948 rc = CFGMR3QueryStringDef(pCfg, "ISACompat", achISACompat, sizeof(achISACompat), "Disabled");
3949 if (RT_FAILURE(rc))
3950 return PDMDEV_SET_ERROR(pDevIns, rc,
3951 N_("BusLogic configuration error: failed to read ISACompat as string"));
3952 Log(("%s: ISACompat=%s\n", __FUNCTION__, achISACompat));
3953
3954 /* Grok the ISACompat setting. */
3955 if (!strcmp(achISACompat, "Disabled"))
3956 pThis->uDefaultISABaseCode = ISA_BASE_DISABLED;
3957 else if (!strcmp(achISACompat, "Primary"))
3958 pThis->uDefaultISABaseCode = 0; /* I/O base at 330h. */
3959 else if (!strcmp(achISACompat, "Alternate"))
3960 pThis->uDefaultISABaseCode = 1; /* I/O base at 334h. */
3961 else
3962 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
3963 N_("BusLogic configuration error: invalid ISACompat setting"));
3964
3965 /*
3966 * Register the PCI device and its I/O regions.
3967 */
3968 rc = PDMDevHlpPCIRegister(pDevIns, &pThis->dev);
3969 if (RT_FAILURE(rc))
3970 return rc;
3971
3972 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 32, PCI_ADDRESS_SPACE_IO, buslogicR3MmioMap);
3973 if (RT_FAILURE(rc))
3974 return rc;
3975
3976 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, 32, PCI_ADDRESS_SPACE_MEM, buslogicR3MmioMap);
3977 if (RT_FAILURE(rc))
3978 return rc;
3979
3980 if (fBootable)
3981 {
3982 /* Register I/O port space for BIOS access. */
3983 rc = PDMDevHlpIOPortRegister(pDevIns, BUSLOGIC_BIOS_IO_PORT, 4, NULL,
3984 buslogicR3BiosIoPortWrite, buslogicR3BiosIoPortRead,
3985 buslogicR3BiosIoPortWriteStr, buslogicR3BiosIoPortReadStr,
3986 "BusLogic BIOS");
3987 if (RT_FAILURE(rc))
3988 return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register BIOS I/O handlers"));
3989 }
3990
3991 /* Set up the compatibility I/O range. */
3992 rc = buslogicR3RegisterISARange(pThis, pThis->uDefaultISABaseCode);
3993 if (RT_FAILURE(rc))
3994 return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register ISA I/O handlers"));
3995
3996 /* Initialize task cache. */
3997 rc = RTMemCacheCreate(&pThis->hTaskCache, sizeof(BUSLOGICTASKSTATE), 0, UINT32_MAX,
3998 NULL, NULL, NULL, 0);
3999 if (RT_FAILURE(rc))
4000 return PDMDEV_SET_ERROR(pDevIns, rc,
4001 N_("BusLogic: Failed to initialize task cache\n"));
4002
4003 /* Initialize task queue. */
4004 rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 5, 0,
4005 buslogicR3NotifyQueueConsumer, true, "BusLogicTask", &pThis->pNotifierQueueR3);
4006 if (RT_FAILURE(rc))
4007 return rc;
4008 pThis->pNotifierQueueR0 = PDMQueueR0Ptr(pThis->pNotifierQueueR3);
4009 pThis->pNotifierQueueRC = PDMQueueRCPtr(pThis->pNotifierQueueR3);
4010
4011 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSectIntr, RT_SRC_POS, "BusLogic-Intr#%u", pDevIns->iInstance);
4012 if (RT_FAILURE(rc))
4013 return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic: cannot create critical section"));
4014
4015 /* Initialize per device state. */
4016 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aDeviceStates); i++)
4017 {
4018 char szName[24];
4019 PBUSLOGICDEVICE pDevice = &pThis->aDeviceStates[i];
4020
4021 RTStrPrintf(szName, sizeof(szName), "Device%u", i);
4022
4023 /* Initialize static parts of the device. */
4024 pDevice->iLUN = i;
4025 pDevice->pBusLogicR3 = pThis;
4026 pDevice->pBusLogicR0 = PDMINS_2_DATA_R0PTR(pDevIns);
4027 pDevice->pBusLogicRC = PDMINS_2_DATA_RCPTR(pDevIns);
4028 pDevice->Led.u32Magic = PDMLED_MAGIC;
4029 pDevice->IBase.pfnQueryInterface = buslogicR3DeviceQueryInterface;
4030 pDevice->ISCSIPort.pfnSCSIRequestCompleted = buslogicR3DeviceSCSIRequestCompleted;
4031 pDevice->ISCSIPort.pfnQueryDeviceLocation = buslogicR3QueryDeviceLocation;
4032 pDevice->ILed.pfnQueryStatusLed = buslogicR3DeviceQueryStatusLed;
4033
4034 /* Attach SCSI driver. */
4035 rc = PDMDevHlpDriverAttach(pDevIns, pDevice->iLUN, &pDevice->IBase, &pDevice->pDrvBase, szName);
4036 if (RT_SUCCESS(rc))
4037 {
4038 /* Get SCSI connector interface. */
4039 pDevice->pDrvSCSIConnector = PDMIBASE_QUERY_INTERFACE(pDevice->pDrvBase, PDMISCSICONNECTOR);
4040 AssertMsgReturn(pDevice->pDrvSCSIConnector, ("Missing SCSI interface below\n"), VERR_PDM_MISSING_INTERFACE);
4041
4042 pDevice->fPresent = true;
4043 }
4044 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
4045 {
4046 pDevice->pDrvBase = NULL;
4047 pDevice->fPresent = false;
4048 rc = VINF_SUCCESS;
4049 Log(("BusLogic: no driver attached to device %s\n", szName));
4050 }
4051 else
4052 {
4053 AssertLogRelMsgFailed(("BusLogic: Failed to attach %s\n", szName));
4054 return rc;
4055 }
4056 }
4057
4058 /*
4059 * Attach status driver (optional).
4060 */
4061 PPDMIBASE pBase;
4062 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pBase, "Status Port");
4063 if (RT_SUCCESS(rc))
4064 pThis->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
4065 else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
4066 {
4067 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
4068 return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot attach to status driver"));
4069 }
4070
4071 rc = PDMDevHlpSSMRegisterEx(pDevIns, BUSLOGIC_SAVED_STATE_MINOR_VERSION, sizeof(*pThis), NULL,
4072 NULL, buslogicR3LiveExec, NULL,
4073 NULL, buslogicR3SaveExec, NULL,
4074 NULL, buslogicR3LoadExec, buslogicR3LoadDone);
4075 if (RT_FAILURE(rc))
4076 return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register save state handlers"));
4077
4078 /*
4079 * Register the debugger info callback.
4080 */
4081 char szTmp[128];
4082 RTStrPrintf(szTmp, sizeof(szTmp), "%s%d", pDevIns->pReg->szName, pDevIns->iInstance);
4083 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "BusLogic HBA info", buslogicR3Info);
4084
4085 rc = buslogicR3HwReset(pThis, true);
4086 AssertMsgRC(rc, ("hardware reset of BusLogic host adapter failed rc=%Rrc\n", rc));
4087
4088 return rc;
4089}
4090
4091/**
4092 * The device registration structure.
4093 */
4094const PDMDEVREG g_DeviceBusLogic =
4095{
4096 /* u32Version */
4097 PDM_DEVREG_VERSION,
4098 /* szName */
4099 "buslogic",
4100 /* szRCMod */
4101 "VBoxDDGC.gc",
4102 /* szR0Mod */
4103 "VBoxDDR0.r0",
4104 /* pszDescription */
4105 "BusLogic BT-958 SCSI host adapter.\n",
4106 /* fFlags */
4107 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0 |
4108 PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION |
4109 PDM_DEVREG_FLAGS_FIRST_RESET_NOTIFICATION,
4110 /* fClass */
4111 PDM_DEVREG_CLASS_STORAGE,
4112 /* cMaxInstances */
4113 ~0U,
4114 /* cbInstance */
4115 sizeof(BUSLOGIC),
4116 /* pfnConstruct */
4117 buslogicR3Construct,
4118 /* pfnDestruct */
4119 buslogicR3Destruct,
4120 /* pfnRelocate */
4121 buslogicR3Relocate,
4122 /* pfnMemSetup */
4123 NULL,
4124 /* pfnPowerOn */
4125 NULL,
4126 /* pfnReset */
4127 buslogicR3Reset,
4128 /* pfnSuspend */
4129 buslogicR3Suspend,
4130 /* pfnResume */
4131 buslogicR3Resume,
4132 /* pfnAttach */
4133 buslogicR3Attach,
4134 /* pfnDetach */
4135 buslogicR3Detach,
4136 /* pfnQueryInterface. */
4137 NULL,
4138 /* pfnInitComplete */
4139 NULL,
4140 /* pfnPowerOff */
4141 buslogicR3PowerOff,
4142 /* pfnSoftReset */
4143 NULL,
4144 /* u32VersionEnd */
4145 PDM_DEVREG_VERSION
4146};
4147
4148#endif /* IN_RING3 */
4149#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Note: See TracBrowser for help on using the repository browser.

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