VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS/lsilogic.c

Last change on this file was 106061, checked in by vboxsync, 4 months ago

Copyright year updates by scm.

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

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