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