VirtualBox

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

Last change on this file since 99887 was 99739, checked in by vboxsync, 19 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

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

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