VirtualBox

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

Last change on this file since 81812 was 81812, checked in by vboxsync, 6 years ago

DevBusLogic.cpp: Converting it to the new PDM device style - BIOS I/O handlers. bugref:9218

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

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