[89168] | 1 | /* $Id: lsilogic.c 101004 2023-09-03 11:07:46Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
| 3 | * LsiLogic SCSI host adapter driver to boot from disks.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2021-2023 Oracle and/or its affiliates.
|
---|
[89168] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
| 11 | *
|
---|
| 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
| 25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[89168] | 26 | */
|
---|
| 27 |
|
---|
| 28 | #include <stdint.h>
|
---|
| 29 | #include <string.h>
|
---|
| 30 | #include "biosint.h"
|
---|
| 31 | #include "ebda.h"
|
---|
| 32 | #include "inlines.h"
|
---|
| 33 | #include "pciutil.h"
|
---|
| 34 | #include "vds.h"
|
---|
| 35 | #include "scsi.h"
|
---|
| 36 |
|
---|
[101004] | 37 | #define USE_VDS
|
---|
| 38 |
|
---|
[89168] | 39 | //#define DEBUG_LSILOGIC 1
|
---|
| 40 | #if DEBUG_LSILOGIC
|
---|
| 41 | # define DBG_LSILOGIC(...) BX_INFO(__VA_ARGS__)
|
---|
| 42 | #else
|
---|
| 43 | # define DBG_LSILOGIC(...)
|
---|
| 44 | #endif
|
---|
| 45 |
|
---|
| 46 | #define RT_BIT(bit) (1L << (bit))
|
---|
| 47 |
|
---|
[101004] | 48 |
|
---|
| 49 | #ifdef USE_VDS
|
---|
| 50 | #define NUM_SG_BUFFERS 34
|
---|
| 51 | #endif
|
---|
| 52 |
|
---|
| 53 |
|
---|
[89168] | 54 | /**
|
---|
| 55 | * A simple SG element for a 32bit address.
|
---|
| 56 | */
|
---|
| 57 | typedef struct MptSGEntrySimple32
|
---|
| 58 | {
|
---|
| 59 | /** Length of the buffer this entry describes. */
|
---|
| 60 | uint32_t u24Length: 24;
|
---|
| 61 | /** Flag whether this element is the end of the list. */
|
---|
| 62 | uint32_t fEndOfList: 1;
|
---|
| 63 | /** Flag whether the address is 32bit or 64bits wide. */
|
---|
| 64 | uint32_t f64BitAddress: 1;
|
---|
| 65 | /** Flag whether this buffer contains data to be transferred or is the destination. */
|
---|
| 66 | uint32_t fBufferContainsData: 1;
|
---|
| 67 | /** Flag whether this is a local address or a system address. */
|
---|
| 68 | uint32_t fLocalAddress: 1;
|
---|
| 69 | /** Element type. */
|
---|
| 70 | uint32_t u2ElementType: 2;
|
---|
| 71 | /** Flag whether this is the last element of the buffer. */
|
---|
| 72 | uint32_t fEndOfBuffer: 1;
|
---|
| 73 | /** Flag whether this is the last element of the current segment. */
|
---|
| 74 | uint32_t fLastElement: 1;
|
---|
| 75 | /** Lower 32bits of the address of the data buffer. */
|
---|
| 76 | uint32_t u32DataBufferAddressLow: 32;
|
---|
| 77 | } MptSGEntrySimple32, *PMptSGEntrySimple32;
|
---|
| 78 |
|
---|
| 79 | /** Defined function codes found in the message header. */
|
---|
| 80 | #define MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST (0x00)
|
---|
| 81 | #define MPT_MESSAGE_HDR_FUNCTION_IOC_INIT (0x02)
|
---|
| 82 |
|
---|
| 83 | /**
|
---|
| 84 | * SCSI IO Request
|
---|
| 85 | */
|
---|
| 86 | typedef struct MptSCSIIORequest
|
---|
| 87 | {
|
---|
| 88 | /** Target ID */
|
---|
| 89 | uint8_t u8TargetID;
|
---|
| 90 | /** Bus number */
|
---|
| 91 | uint8_t u8Bus;
|
---|
| 92 | /** Chain offset */
|
---|
| 93 | uint8_t u8ChainOffset;
|
---|
| 94 | /** Function number. */
|
---|
| 95 | uint8_t u8Function;
|
---|
| 96 | /** CDB length. */
|
---|
| 97 | uint8_t u8CDBLength;
|
---|
| 98 | /** Sense buffer length. */
|
---|
| 99 | uint8_t u8SenseBufferLength;
|
---|
| 100 | /** Reserved */
|
---|
| 101 | uint8_t u8Reserved;
|
---|
| 102 | /** Message flags. */
|
---|
| 103 | uint8_t u8MessageFlags;
|
---|
| 104 | /** Message context ID. */
|
---|
| 105 | uint32_t u32MessageContext;
|
---|
| 106 | /** LUN */
|
---|
| 107 | uint8_t au8LUN[8];
|
---|
| 108 | /** Control values. */
|
---|
| 109 | uint32_t u32Control;
|
---|
| 110 | /** The CDB. */
|
---|
| 111 | uint8_t au8CDB[16];
|
---|
| 112 | /** Data length. */
|
---|
| 113 | uint32_t u32DataLength;
|
---|
| 114 | /** Sense buffer low 32bit address. */
|
---|
| 115 | uint32_t u32SenseBufferLowAddress;
|
---|
| 116 | } MptSCSIIORequest, *PMptSCSIIORequest;
|
---|
| 117 |
|
---|
| 118 | #define MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE (0x0L)
|
---|
| 119 | #define MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE (0x1L)
|
---|
| 120 | #define MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ (0x2L)
|
---|
| 121 |
|
---|
| 122 | /**
|
---|
| 123 | * SCSI IO error reply.
|
---|
| 124 | */
|
---|
| 125 | typedef struct MptSCSIIOErrorReply
|
---|
| 126 | {
|
---|
| 127 | /** Target ID */
|
---|
| 128 | uint8_t u8TargetID;
|
---|
| 129 | /** Bus number */
|
---|
| 130 | uint8_t u8Bus;
|
---|
| 131 | /** Message length. */
|
---|
| 132 | uint8_t u8MessageLength;
|
---|
| 133 | /** Function number. */
|
---|
| 134 | uint8_t u8Function;
|
---|
| 135 | /** CDB length */
|
---|
| 136 | uint8_t u8CDBLength;
|
---|
| 137 | /** Sense buffer length */
|
---|
| 138 | uint8_t u8SenseBufferLength;
|
---|
| 139 | /** Reserved */
|
---|
| 140 | uint8_t u8Reserved;
|
---|
| 141 | /** Message flags */
|
---|
| 142 | uint8_t u8MessageFlags;
|
---|
| 143 | /** Message context ID */
|
---|
| 144 | uint32_t u32MessageContext;
|
---|
| 145 | /** SCSI status. */
|
---|
| 146 | uint8_t u8SCSIStatus;
|
---|
| 147 | /** SCSI state */
|
---|
| 148 | uint8_t u8SCSIState;
|
---|
| 149 | /** IO controller status */
|
---|
| 150 | uint16_t u16IOCStatus;
|
---|
| 151 | /** IO controller log information */
|
---|
| 152 | uint32_t u32IOCLogInfo;
|
---|
| 153 | /** Transfer count */
|
---|
| 154 | uint32_t u32TransferCount;
|
---|
| 155 | /** Sense count */
|
---|
| 156 | uint32_t u32SenseCount;
|
---|
| 157 | /** Response information */
|
---|
| 158 | uint32_t u32ResponseInfo;
|
---|
| 159 | } MptSCSIIOErrorReply, *PMptSCSIIOErrorReply;
|
---|
| 160 |
|
---|
| 161 | /**
|
---|
| 162 | * IO controller init request.
|
---|
| 163 | */
|
---|
| 164 | typedef struct MptIOCInitRequest
|
---|
| 165 | {
|
---|
| 166 | /** Which system send this init request. */
|
---|
| 167 | uint8_t u8WhoInit;
|
---|
| 168 | /** Reserved */
|
---|
| 169 | uint8_t u8Reserved;
|
---|
| 170 | /** Chain offset in the SG list. */
|
---|
| 171 | uint8_t u8ChainOffset;
|
---|
| 172 | /** Function to execute. */
|
---|
| 173 | uint8_t u8Function;
|
---|
| 174 | /** Flags */
|
---|
| 175 | uint8_t u8Flags;
|
---|
| 176 | /** Maximum number of devices the driver can handle. */
|
---|
| 177 | uint8_t u8MaxDevices;
|
---|
| 178 | /** Maximum number of buses the driver can handle. */
|
---|
| 179 | uint8_t u8MaxBuses;
|
---|
| 180 | /** Message flags. */
|
---|
| 181 | uint8_t u8MessageFlags;
|
---|
| 182 | /** Message context ID. */
|
---|
| 183 | uint32_t u32MessageContext;
|
---|
| 184 | /** Reply frame size. */
|
---|
| 185 | uint16_t u16ReplyFrameSize;
|
---|
| 186 | /** Reserved */
|
---|
| 187 | uint16_t u16Reserved;
|
---|
| 188 | /** Upper 32bit part of the 64bit address the message frames are in.
|
---|
| 189 | * That means all frames must be in the same 4GB segment. */
|
---|
| 190 | uint32_t u32HostMfaHighAddr;
|
---|
| 191 | /** Upper 32bit of the sense buffer. */
|
---|
| 192 | uint32_t u32SenseBufferHighAddr;
|
---|
| 193 | } MptIOCInitRequest, *PMptIOCInitRequest;
|
---|
| 194 |
|
---|
| 195 | #define LSILOGICWHOINIT_SYSTEM_BIOS 0x01
|
---|
| 196 |
|
---|
| 197 |
|
---|
| 198 | /**
|
---|
| 199 | * IO controller init reply.
|
---|
| 200 | */
|
---|
| 201 | typedef struct MptIOCInitReply
|
---|
| 202 | {
|
---|
| 203 | /** Which subsystem send this init request. */
|
---|
| 204 | uint8_t u8WhoInit;
|
---|
| 205 | /** Reserved */
|
---|
| 206 | uint8_t u8Reserved;
|
---|
| 207 | /** Message length */
|
---|
| 208 | uint8_t u8MessageLength;
|
---|
| 209 | /** Function. */
|
---|
| 210 | uint8_t u8Function;
|
---|
| 211 | /** Flags */
|
---|
| 212 | uint8_t u8Flags;
|
---|
| 213 | /** Maximum number of devices the driver can handle. */
|
---|
| 214 | uint8_t u8MaxDevices;
|
---|
| 215 | /** Maximum number of busses the driver can handle. */
|
---|
| 216 | uint8_t u8MaxBuses;
|
---|
| 217 | /** Message flags. */
|
---|
| 218 | uint8_t u8MessageFlags;
|
---|
| 219 | /** Message context ID */
|
---|
| 220 | uint32_t u32MessageContext;
|
---|
| 221 | /** Reserved */
|
---|
| 222 | uint16_t u16Reserved;
|
---|
| 223 | /** IO controller status. */
|
---|
| 224 | uint16_t u16IOCStatus;
|
---|
| 225 | /** IO controller log information. */
|
---|
| 226 | uint32_t u32IOCLogInfo;
|
---|
| 227 | } MptIOCInitReply, *PMptIOCInitReply;
|
---|
| 228 |
|
---|
| 229 | /**
|
---|
| 230 | * Doorbell register - Used to get the status of the controller and
|
---|
| 231 | * initialise it.
|
---|
| 232 | */
|
---|
| 233 | #define LSILOGIC_REG_DOORBELL 0x00
|
---|
| 234 | # define LSILOGIC_REG_DOORBELL_SET_STATE(enmState) (((enmState) & 0x0f) << 28)
|
---|
| 235 | # define LSILOGIC_REG_DOORBELL_SET_USED(enmDoorbell) (((enmDoorbell != LSILOGICDOORBELLSTATE_NOT_IN_USE) ? 1 : 0) << 27)
|
---|
| 236 | # define LSILOGIC_REG_DOORBELL_SET_WHOINIT(enmWhoInit) (((enmWhoInit) & 0x07) << 24)
|
---|
| 237 | # define LSILOGIC_REG_DOORBELL_SET_FAULT_CODE(u16Code) (u16Code)
|
---|
| 238 | # define LSILOGIC_REG_DOORBELL_GET_FUNCTION(x) (((x) & 0xff000000) >> 24)
|
---|
| 239 | # define LSILOGIC_REG_DOORBELL_GET_SIZE(x) (((x) & 0x00ff0000) >> 16)
|
---|
| 240 |
|
---|
| 241 | /**
|
---|
| 242 | * Functions which can be passed through the system doorbell.
|
---|
| 243 | */
|
---|
| 244 | #define LSILOGIC_DOORBELL_FUNCTION_IOC_MSG_UNIT_RESET 0x40L
|
---|
| 245 | #define LSILOGIC_DOORBELL_FUNCTION_IO_UNIT_RESET 0x41L
|
---|
| 246 | #define LSILOGIC_DOORBELL_FUNCTION_HANDSHAKE 0x42L
|
---|
| 247 | #define LSILOGIC_DOORBELL_FUNCTION_REPLY_FRAME_REMOVAL 0x43L
|
---|
| 248 |
|
---|
| 249 | /**
|
---|
| 250 | * Write sequence register for the diagnostic register.
|
---|
| 251 | */
|
---|
| 252 | #define LSILOGIC_REG_WRITE_SEQUENCE 0x04
|
---|
| 253 |
|
---|
| 254 | /**
|
---|
| 255 | * Diagnostic register - used to reset the controller.
|
---|
| 256 | */
|
---|
| 257 | #define LSILOGIC_REG_HOST_DIAGNOSTIC 0x08
|
---|
| 258 | # define LSILOGIC_REG_HOST_DIAGNOSTIC_DIAG_MEM_ENABLE (RT_BIT(0))
|
---|
| 259 | # define LSILOGIC_REG_HOST_DIAGNOSTIC_DISABLE_ARM (RT_BIT(1))
|
---|
| 260 | # define LSILOGIC_REG_HOST_DIAGNOSTIC_RESET_ADAPTER (RT_BIT(2))
|
---|
| 261 | # define LSILOGIC_REG_HOST_DIAGNOSTIC_DIAG_RW_ENABLE (RT_BIT(4))
|
---|
| 262 | # define LSILOGIC_REG_HOST_DIAGNOSTIC_RESET_HISTORY (RT_BIT(5))
|
---|
| 263 | # define LSILOGIC_REG_HOST_DIAGNOSTIC_FLASH_BAD_SIG (RT_BIT(6))
|
---|
| 264 | # define LSILOGIC_REG_HOST_DIAGNOSTIC_DRWE (RT_BIT(7))
|
---|
| 265 | # define LSILOGIC_REG_HOST_DIAGNOSTIC_PREVENT_IOC_BOOT (RT_BIT(9))
|
---|
| 266 | # define LSILOGIC_REG_HOST_DIAGNOSTIC_CLEAR_FLASH_BAD_SIG (RT_BIT(10))
|
---|
| 267 |
|
---|
| 268 | #define LSILOGIC_REG_TEST_BASE_ADDRESS 0x0c
|
---|
| 269 | #define LSILOGIC_REG_DIAG_RW_DATA 0x10
|
---|
| 270 | #define LSILOGIC_REG_DIAG_RW_ADDRESS 0x14
|
---|
| 271 |
|
---|
| 272 | /**
|
---|
| 273 | * Interrupt status register.
|
---|
| 274 | */
|
---|
| 275 | #define LSILOGIC_REG_HOST_INTR_STATUS 0x30
|
---|
| 276 | # define LSILOGIC_REG_HOST_INTR_STATUS_W_MASK (RT_BIT(3))
|
---|
| 277 | # define LSILOGIC_REG_HOST_INTR_STATUS_DOORBELL_STS (RT_BIT(31))
|
---|
| 278 | # define LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR (RT_BIT(3))
|
---|
| 279 | # define LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL (RT_BIT(0))
|
---|
| 280 |
|
---|
| 281 | /**
|
---|
| 282 | * Interrupt mask register.
|
---|
| 283 | */
|
---|
| 284 | #define LSILOGIC_REG_HOST_INTR_MASK 0x34
|
---|
| 285 | # define LSILOGIC_REG_HOST_INTR_MASK_W_MASK (RT_BIT(0) | RT_BIT(3) | RT_BIT(8) | RT_BIT(9))
|
---|
| 286 | # define LSILOGIC_REG_HOST_INTR_MASK_IRQ_ROUTING (RT_BIT(8) | RT_BIT(9))
|
---|
| 287 | # define LSILOGIC_REG_HOST_INTR_MASK_DOORBELL RT_BIT(0)
|
---|
| 288 | # define LSILOGIC_REG_HOST_INTR_MASK_REPLY RT_BIT(3)
|
---|
| 289 |
|
---|
| 290 | /**
|
---|
| 291 | * Queue registers.
|
---|
| 292 | */
|
---|
| 293 | #define LSILOGIC_REG_REQUEST_QUEUE 0x40
|
---|
| 294 | #define LSILOGIC_REG_REPLY_QUEUE 0x44
|
---|
| 295 |
|
---|
| 296 | /**
|
---|
| 297 | * LsiLogic-SCSI controller data.
|
---|
| 298 | */
|
---|
| 299 | typedef struct
|
---|
| 300 | {
|
---|
| 301 | /** The SCSI I/O request structure. */
|
---|
[101004] | 302 | MptSCSIIORequest ScsiIoReq;
|
---|
[89168] | 303 | /** S/G elements being used, must come after the I/O request structure. */
|
---|
[101004] | 304 | MptSGEntrySimple32 Sge[NUM_SG_BUFFERS];
|
---|
[89168] | 305 | /** The reply frame used for address replies. */
|
---|
[101004] | 306 | uint8_t abReply[128];
|
---|
[89168] | 307 | /** I/O base of device. */
|
---|
[101004] | 308 | uint16_t u16IoBase;
|
---|
| 309 | #ifdef USE_VDS
|
---|
| 310 | /** VDS EDDS structure for the I/O request structure. */
|
---|
| 311 | vds_edds edds_req;
|
---|
| 312 | /** VDS EDDS DMA buffer descriptor structure. */
|
---|
| 313 | vds_edds edds;
|
---|
| 314 | vds_sg edds_more_sg[NUM_SG_BUFFERS - 1];
|
---|
| 315 | #endif
|
---|
[89168] | 316 | } lsilogic_t;
|
---|
| 317 |
|
---|
| 318 | /* The BusLogic specific data must fit into 1KB (statically allocated). */
|
---|
| 319 | ct_assert(sizeof(lsilogic_t) <= 1024);
|
---|
| 320 |
|
---|
| 321 | #define VBOX_LSILOGIC_NO_DEVICE 0xffff
|
---|
| 322 |
|
---|
| 323 | /* Warning: Destroys high bits of EAX. */
|
---|
| 324 | uint32_t inpd(uint16_t port);
|
---|
| 325 | #pragma aux inpd = \
|
---|
| 326 | ".386" \
|
---|
| 327 | "in eax, dx" \
|
---|
| 328 | "mov dx, ax" \
|
---|
| 329 | "shr eax, 16" \
|
---|
| 330 | "xchg ax, dx" \
|
---|
| 331 | parm [dx] value [dx ax] modify nomemory;
|
---|
| 332 |
|
---|
| 333 | /* Warning: Destroys high bits of EAX. */
|
---|
| 334 | void outpd(uint16_t port, uint32_t val);
|
---|
| 335 | #pragma aux outpd = \
|
---|
| 336 | ".386" \
|
---|
| 337 | "xchg ax, cx" \
|
---|
| 338 | "shl eax, 16" \
|
---|
| 339 | "mov ax, cx" \
|
---|
| 340 | "out dx, eax" \
|
---|
| 341 | parm [dx] [cx ax] modify nomemory;
|
---|
| 342 |
|
---|
| 343 | /**
|
---|
| 344 | * Converts a segment:offset pair into a 32bit physical address.
|
---|
| 345 | */
|
---|
| 346 | static uint32_t lsilogic_addr_to_phys(void __far *ptr)
|
---|
| 347 | {
|
---|
| 348 | return ((uint32_t)FP_SEG(ptr) << 4) + FP_OFF(ptr);
|
---|
| 349 | }
|
---|
| 350 |
|
---|
| 351 | static int lsilogic_cmd(lsilogic_t __far *lsilogic, const void __far *pvReq, uint16_t cbReq,
|
---|
| 352 | void __far *pvReply, uint16_t cbReply)
|
---|
| 353 | {
|
---|
| 354 | uint16_t i;
|
---|
| 355 | const uint32_t __far *pu32Req = (const uint32_t __far *)pvReq;
|
---|
| 356 | uint16_t __far *pu16Reply = (uint16_t *)pvReply;
|
---|
| 357 | uint32_t cMsg = cbReq / sizeof(uint32_t);
|
---|
| 358 | uint16_t cReply = cbReply / sizeof(uint16_t);
|
---|
| 359 | uint32_t u32Fn = (LSILOGIC_DOORBELL_FUNCTION_HANDSHAKE << 24) | (cMsg << 16);
|
---|
| 360 |
|
---|
| 361 | if ( cbReq % sizeof(uint32_t)
|
---|
| 362 | || cbReply % sizeof(uint16_t))
|
---|
| 363 | return 1;
|
---|
| 364 |
|
---|
| 365 | outpd(lsilogic->u16IoBase + LSILOGIC_REG_DOORBELL, u32Fn);
|
---|
| 366 | for (i = 0; i < cMsg; i++)
|
---|
| 367 | outpd(lsilogic->u16IoBase + LSILOGIC_REG_DOORBELL, pu32Req[i]);
|
---|
| 368 |
|
---|
| 369 | for (i = 0; i < cReply; i++)
|
---|
| 370 | {
|
---|
| 371 | /* Wait for the system doorbell interrupt status to be set. */
|
---|
| 372 | while (!(inpd(lsilogic->u16IoBase + LSILOGIC_REG_HOST_INTR_STATUS) & LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL));
|
---|
| 373 |
|
---|
| 374 | pu16Reply[i] = (uint16_t)inpd(lsilogic->u16IoBase + LSILOGIC_REG_DOORBELL); /* The lower 16bits contain the reply data. */
|
---|
| 375 | outpd(lsilogic->u16IoBase + LSILOGIC_REG_HOST_INTR_STATUS, 1);
|
---|
| 376 | }
|
---|
| 377 |
|
---|
| 378 | return 0;
|
---|
| 379 | }
|
---|
| 380 |
|
---|
| 381 | static int lsilogic_scsi_cmd_exec(lsilogic_t __far *lsilogic)
|
---|
| 382 | {
|
---|
[101004] | 383 | uint32_t u32Reply = 0;
|
---|
| 384 | uint32_t u32ReplyDummy = 0;
|
---|
[89168] | 385 |
|
---|
[101004] | 386 | #ifdef USE_VDS
|
---|
| 387 | /* Lock request memory. */
|
---|
| 388 | lsilogic->edds_req.num_avail = 1;
|
---|
| 389 | vds_build_sg_list(&lsilogic->edds_req, &lsilogic->ScsiIoReq, sizeof(lsilogic->ScsiIoReq));
|
---|
| 390 |
|
---|
[89168] | 391 | /* Send it off. */
|
---|
[101004] | 392 | outpd(lsilogic->u16IoBase + LSILOGIC_REG_REQUEST_QUEUE, lsilogic->edds_req.u.sg[0].phys_addr);
|
---|
| 393 | #else
|
---|
| 394 | /* Send it off. */
|
---|
[89168] | 395 | outpd(lsilogic->u16IoBase + LSILOGIC_REG_REQUEST_QUEUE, lsilogic_addr_to_phys(&lsilogic->ScsiIoReq));
|
---|
[101004] | 396 | #endif
|
---|
[89168] | 397 |
|
---|
| 398 | /* Wait for it to finish. */
|
---|
| 399 | while (!(inpd(lsilogic->u16IoBase + LSILOGIC_REG_HOST_INTR_STATUS) & LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR));
|
---|
| 400 |
|
---|
| 401 | outpd(lsilogic->u16IoBase + LSILOGIC_REG_HOST_INTR_STATUS, 1);
|
---|
| 402 |
|
---|
[101004] | 403 | #ifdef USE_VDS
|
---|
| 404 | /* Unlock the request again. */
|
---|
| 405 | vds_free_sg_list(&lsilogic->edds_req);
|
---|
| 406 | #endif
|
---|
[89168] | 407 | /* Read the reply queue. */
|
---|
| 408 | u32Reply = inpd(lsilogic->u16IoBase + LSILOGIC_REG_REPLY_QUEUE);
|
---|
| 409 | u32ReplyDummy = inpd(lsilogic->u16IoBase + LSILOGIC_REG_REPLY_QUEUE);
|
---|
| 410 | if (u32ReplyDummy != 0xffffffff)
|
---|
| 411 | return 5;
|
---|
| 412 | if (u32Reply & RT_BIT(31))
|
---|
| 413 | {
|
---|
| 414 | /*
|
---|
| 415 | * This is an address reply indicating a failed transaction, so just return an error without
|
---|
| 416 | * bothering to check the exact failure reason for now.
|
---|
| 417 | *
|
---|
| 418 | * Just provide the reply frame to the reply queue again.
|
---|
| 419 | */
|
---|
| 420 | outpd(lsilogic->u16IoBase + LSILOGIC_REG_REPLY_QUEUE, lsilogic_addr_to_phys(&lsilogic->abReply));
|
---|
| 421 | return 4;
|
---|
| 422 | }
|
---|
| 423 |
|
---|
| 424 | if (u32Reply != 0xcafe) /* Getting a different context ID should never ever happen. */
|
---|
| 425 | return 3;
|
---|
| 426 |
|
---|
| 427 | return 0;
|
---|
| 428 | }
|
---|
| 429 |
|
---|
| 430 | int lsilogic_scsi_cmd_data_out(void __far *pvHba, uint8_t idTgt, uint8_t __far *aCDB,
|
---|
[101004] | 431 | uint8_t cbCDB, unsigned char __far *buffer, uint32_t length)
|
---|
[89168] | 432 | {
|
---|
[101004] | 433 | lsilogic_t __far *lsilogic = (lsilogic_t __far *)pvHba;
|
---|
| 434 | int i;
|
---|
| 435 | int rc;
|
---|
[89168] | 436 |
|
---|
| 437 | _fmemset(&lsilogic->ScsiIoReq, 0, sizeof(lsilogic->ScsiIoReq));
|
---|
| 438 |
|
---|
[101004] | 439 | #ifdef USE_VDS
|
---|
| 440 | lsilogic->edds.num_avail = NUM_SG_BUFFERS;
|
---|
| 441 | vds_build_sg_list(&lsilogic->edds, buffer, length);
|
---|
| 442 | #endif
|
---|
| 443 |
|
---|
[89168] | 444 | lsilogic->ScsiIoReq.u8TargetID = idTgt;
|
---|
| 445 | lsilogic->ScsiIoReq.u8Bus = 0;
|
---|
| 446 | lsilogic->ScsiIoReq.u8ChainOffset = 0;
|
---|
| 447 | lsilogic->ScsiIoReq.u8Function = MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST;
|
---|
| 448 | lsilogic->ScsiIoReq.u8CDBLength = cbCDB;
|
---|
| 449 | lsilogic->ScsiIoReq.u8SenseBufferLength = 0;
|
---|
| 450 | lsilogic->ScsiIoReq.u32MessageContext = 0xcafe;
|
---|
| 451 | lsilogic->ScsiIoReq.u32Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE << 24;
|
---|
| 452 | lsilogic->ScsiIoReq.u32DataLength = length;
|
---|
| 453 | for (i = 0; i < cbCDB; i++)
|
---|
| 454 | lsilogic->ScsiIoReq.au8CDB[i] = aCDB[i];
|
---|
| 455 |
|
---|
[101004] | 456 | #ifdef USE_VDS
|
---|
| 457 | for (i = 0; i < lsilogic->edds.num_used; ++i)
|
---|
| 458 | {
|
---|
| 459 | lsilogic->Sge[i].u24Length = lsilogic->edds.u.sg[i].size;
|
---|
| 460 | lsilogic->Sge[i].fEndOfList = 0;
|
---|
| 461 | lsilogic->Sge[i].f64BitAddress = 0;
|
---|
| 462 | lsilogic->Sge[i].fBufferContainsData = 0;
|
---|
| 463 | lsilogic->Sge[i].fLocalAddress = 0;
|
---|
| 464 | lsilogic->Sge[i].u2ElementType = 0x01; /* Simple type */
|
---|
| 465 | lsilogic->Sge[i].fEndOfBuffer = 0;
|
---|
| 466 | lsilogic->Sge[i].fLastElement = 0;
|
---|
| 467 | lsilogic->Sge[i].u32DataBufferAddressLow = lsilogic->edds.u.sg[i].phys_addr;
|
---|
| 468 | }
|
---|
| 469 | --i;
|
---|
| 470 | lsilogic->Sge[i].fEndOfList = lsilogic->Sge[i].fEndOfBuffer = lsilogic->Sge[i].fLastElement = 1;
|
---|
| 471 | #else
|
---|
| 472 | lsilogic->Sge[0].u24Length = length;
|
---|
| 473 | lsilogic->Sge[0].fEndOfList = 1;
|
---|
| 474 | lsilogic->Sge[0].f64BitAddress = 0;
|
---|
| 475 | lsilogic->Sge[0].fBufferContainsData = 0;
|
---|
| 476 | lsilogic->Sge[0].fLocalAddress = 0;
|
---|
| 477 | lsilogic->Sge[0].u2ElementType = 0x01; /* Simple type */
|
---|
| 478 | lsilogic->Sge[0].fEndOfBuffer = 1;
|
---|
| 479 | lsilogic->Sge[0].fLastElement = 1;
|
---|
| 480 | lsilogic->Sge[0].u32DataBufferAddressLow = phys_buf;
|
---|
| 481 | #endif
|
---|
[89168] | 482 |
|
---|
[101004] | 483 | rc = lsilogic_scsi_cmd_exec(lsilogic);
|
---|
| 484 |
|
---|
| 485 | #ifdef USE_VDS
|
---|
| 486 | /* Unlock the buffer again. */
|
---|
| 487 | vds_free_sg_list(&lsilogic->edds);
|
---|
| 488 | #endif
|
---|
| 489 |
|
---|
| 490 | return rc;
|
---|
[89168] | 491 | }
|
---|
| 492 |
|
---|
| 493 | int lsilogic_scsi_cmd_data_in(void __far *pvHba, uint8_t idTgt, uint8_t __far *aCDB,
|
---|
[101004] | 494 | uint8_t cbCDB, unsigned char __far *buffer, uint32_t length)
|
---|
[89168] | 495 | {
|
---|
[101004] | 496 | lsilogic_t __far *lsilogic = (lsilogic_t __far *)pvHba;
|
---|
| 497 | int i;
|
---|
| 498 | int rc;
|
---|
[89168] | 499 |
|
---|
| 500 | _fmemset(&lsilogic->ScsiIoReq, 0, sizeof(lsilogic->ScsiIoReq));
|
---|
| 501 |
|
---|
[101004] | 502 | #ifdef USE_VDS
|
---|
| 503 | lsilogic->edds.num_avail = NUM_SG_BUFFERS;
|
---|
| 504 | vds_build_sg_list(&lsilogic->edds, buffer, length);
|
---|
| 505 | #endif
|
---|
| 506 |
|
---|
[89168] | 507 | lsilogic->ScsiIoReq.u8TargetID = idTgt;
|
---|
| 508 | lsilogic->ScsiIoReq.u8Bus = 0;
|
---|
| 509 | lsilogic->ScsiIoReq.u8ChainOffset = 0;
|
---|
| 510 | lsilogic->ScsiIoReq.u8Function = MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST;
|
---|
| 511 | lsilogic->ScsiIoReq.u8CDBLength = cbCDB;
|
---|
| 512 | lsilogic->ScsiIoReq.u8SenseBufferLength = 0;
|
---|
| 513 | lsilogic->ScsiIoReq.u32MessageContext = 0xcafe;
|
---|
| 514 | lsilogic->ScsiIoReq.u32Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ << 24;
|
---|
[89364] | 515 | lsilogic->ScsiIoReq.u32DataLength = length;
|
---|
[89168] | 516 | for (i = 0; i < cbCDB; i++)
|
---|
| 517 | lsilogic->ScsiIoReq.au8CDB[i] = aCDB[i];
|
---|
| 518 |
|
---|
[101004] | 519 | #ifdef USE_VDS
|
---|
| 520 | for (i = 0; i < lsilogic->edds.num_used; ++i)
|
---|
| 521 | {
|
---|
| 522 | lsilogic->Sge[i].u24Length = lsilogic->edds.u.sg[i].size;
|
---|
| 523 | lsilogic->Sge[i].fEndOfList = 0;
|
---|
| 524 | lsilogic->Sge[i].f64BitAddress = 0;
|
---|
| 525 | lsilogic->Sge[i].fBufferContainsData = 0;
|
---|
| 526 | lsilogic->Sge[i].fLocalAddress = 0;
|
---|
| 527 | lsilogic->Sge[i].u2ElementType = 0x01; /* Simple type */
|
---|
| 528 | lsilogic->Sge[i].fEndOfBuffer = 0;
|
---|
| 529 | lsilogic->Sge[i].fLastElement = 0;
|
---|
| 530 | lsilogic->Sge[i].u32DataBufferAddressLow = lsilogic->edds.u.sg[i].phys_addr;
|
---|
| 531 | }
|
---|
| 532 | --i;
|
---|
| 533 | lsilogic->Sge[i].fEndOfList = lsilogic->Sge[i].fEndOfBuffer = lsilogic->Sge[i].fLastElement = 1;
|
---|
[89168] | 534 |
|
---|
[101004] | 535 | #else
|
---|
| 536 | lsilogic->Sge[0].u24Length = length;
|
---|
| 537 | lsilogic->Sge[0].fEndOfList = 1;
|
---|
| 538 | lsilogic->Sge[0].f64BitAddress = 0;
|
---|
| 539 | lsilogic->Sge[0].fBufferContainsData = 0;
|
---|
| 540 | lsilogic->Sge[0].fLocalAddress = 0;
|
---|
| 541 | lsilogic->Sge[0].u2ElementType = 0x01; /* Simple type */
|
---|
| 542 | lsilogic->Sge[0].fEndOfBuffer = 1;
|
---|
| 543 | lsilogic->Sge[0].fLastElement = 1;
|
---|
| 544 | lsilogic->Sge[0].u32DataBufferAddressLow = lsilogic_addr_to_phys(buffer);
|
---|
| 545 | #endif
|
---|
| 546 |
|
---|
| 547 | rc = lsilogic_scsi_cmd_exec(lsilogic);
|
---|
| 548 |
|
---|
| 549 | #ifdef USE_VDS
|
---|
| 550 | /* Unlock the buffer again. */
|
---|
| 551 | vds_free_sg_list(&lsilogic->edds);
|
---|
| 552 | #endif
|
---|
| 553 |
|
---|
| 554 | return rc;
|
---|
[89168] | 555 | }
|
---|
| 556 |
|
---|
| 557 | /**
|
---|
| 558 | * Initializes the LsiLogic SCSI HBA and detects attached devices.
|
---|
| 559 | */
|
---|
| 560 | static int lsilogic_scsi_hba_init(lsilogic_t __far *lsilogic)
|
---|
| 561 | {
|
---|
| 562 | int rc;
|
---|
| 563 | MptIOCInitRequest IocInitReq;
|
---|
| 564 | MptIOCInitReply IocInitReply;
|
---|
| 565 |
|
---|
| 566 | /*
|
---|
| 567 | * The following initialization sequence is stripped down to the point to work with
|
---|
| 568 | * our emulated LsiLogic controller, it will most certainly fail on real hardware.
|
---|
| 569 | */
|
---|
| 570 |
|
---|
| 571 | /* Hard reset, write the sequence to enable the diagnostic access. */
|
---|
| 572 | outpd(lsilogic->u16IoBase + LSILOGIC_REG_WRITE_SEQUENCE, 0x04);
|
---|
| 573 | outpd(lsilogic->u16IoBase + LSILOGIC_REG_WRITE_SEQUENCE, 0x02);
|
---|
| 574 | outpd(lsilogic->u16IoBase + LSILOGIC_REG_WRITE_SEQUENCE, 0x07);
|
---|
| 575 | outpd(lsilogic->u16IoBase + LSILOGIC_REG_WRITE_SEQUENCE, 0x0d);
|
---|
| 576 | outpd(lsilogic->u16IoBase + LSILOGIC_REG_HOST_DIAGNOSTIC, LSILOGIC_REG_HOST_DIAGNOSTIC_RESET_ADAPTER);
|
---|
| 577 |
|
---|
| 578 | IocInitReq.u8WhoInit = LSILOGICWHOINIT_SYSTEM_BIOS;
|
---|
| 579 | IocInitReq.u8Function = MPT_MESSAGE_HDR_FUNCTION_IOC_INIT;
|
---|
| 580 | IocInitReq.u32HostMfaHighAddr = 0;
|
---|
| 581 | IocInitReq.u32SenseBufferHighAddr = 0;
|
---|
| 582 | IocInitReq.u8MaxBuses = 1;
|
---|
| 583 | IocInitReq.u8MaxDevices = 4;
|
---|
| 584 | IocInitReq.u16ReplyFrameSize = sizeof(lsilogic->abReply);
|
---|
| 585 | rc = lsilogic_cmd(lsilogic, &IocInitReq, sizeof(IocInitReq), &IocInitReply, sizeof(IocInitReply));
|
---|
| 586 | if (!rc)
|
---|
| 587 | {
|
---|
| 588 | /* Provide a single reply frame for SCSI I/O errors. */
|
---|
| 589 | outpd(lsilogic->u16IoBase + LSILOGIC_REG_REPLY_QUEUE, lsilogic_addr_to_phys(&lsilogic->abReply));
|
---|
| 590 | return 0;
|
---|
| 591 | }
|
---|
| 592 |
|
---|
| 593 | return 1;
|
---|
| 594 | }
|
---|
| 595 |
|
---|
| 596 | /**
|
---|
| 597 | * Init the LsiLogic SCSI driver and detect attached disks.
|
---|
| 598 | */
|
---|
[89364] | 599 | int lsilogic_scsi_init(void __far *pvHba, uint8_t u8Bus, uint8_t u8DevFn)
|
---|
[89168] | 600 | {
|
---|
| 601 | lsilogic_t __far *lsilogic = (lsilogic_t __far *)pvHba;
|
---|
| 602 | uint32_t u32Bar;
|
---|
| 603 |
|
---|
| 604 | DBG_LSILOGIC("LsiLogic SCSI HBA at Bus %u DevFn 0x%x (raw 0x%x)\n", u8Bus, u8DevFn);
|
---|
| 605 |
|
---|
| 606 | u32Bar = pci_read_config_dword(u8Bus, u8DevFn, 0x10);
|
---|
| 607 |
|
---|
| 608 | DBG_LSILOGIC("BAR at 0x10 : 0x%x\n", u32Bar);
|
---|
| 609 |
|
---|
| 610 | if ((u32Bar & 0x01) != 0)
|
---|
| 611 | {
|
---|
| 612 | uint16_t u16IoBase = (u32Bar & 0xfff0);
|
---|
| 613 |
|
---|
| 614 | /* Enable PCI memory, I/O, bus mastering access in command register. */
|
---|
| 615 | pci_write_config_word(u8Bus, u8DevFn, 4, 0x7);
|
---|
| 616 |
|
---|
| 617 | DBG_LSILOGIC("I/O base: 0x%x\n", u16IoBase);
|
---|
| 618 | lsilogic->u16IoBase = u16IoBase;
|
---|
| 619 | return lsilogic_scsi_hba_init(lsilogic);
|
---|
| 620 | }
|
---|
| 621 | else
|
---|
| 622 | DBG_LSILOGIC("BAR is MMIO\n");
|
---|
| 623 |
|
---|
| 624 | return 1;
|
---|
| 625 | }
|
---|
| 626 |
|
---|