VirtualBox

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

Last change on this file was 106274, checked in by vboxsync, 2 months ago

DevBusLogic: CritSectIntr also needs to protect uMailboxIncomingPositionCurrent (see bugref:10740).

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