VirtualBox

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

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

Devices/BusLogic: Respond with a invalid command parameter error if the CDB length for a SCSI I/O request is invalid, bugref:10613

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

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