VirtualBox

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

Last change on this file since 100986 was 100986, checked in by vboxsync, 9 months ago

BusLogic: More accurately emulate the geometry register; return sensible model and firmware version for BT-545C emulation.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use