VirtualBox

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

Last change on this file was 104233, checked in by vboxsync, 5 weeks ago

Devices/DevBusLogic.cpp: Increase the maxmium size of the CDB which can be issues using the Execute SCSI command interface because the BIOS uses 16 byte CDBs (fixes booting from BusLogic since r162599), bugref:10613

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

© 2023 Oracle
ContactPrivacy policyTerms of Use