VirtualBox

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

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

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

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