VirtualBox

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

Last change on this file was 100984, checked in by vboxsync, 8 months ago

BIOS: Support also ISA BusLogic/Adaptec HBAs, not just PCI (see bugref:6549).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.2 KB
Line 
1/* $Id: buslogic.c 100984 2023-08-28 11:09:40Z vboxsync $ */
2/** @file
3 * BusLogic SCSI host adapter driver to boot from disks.
4 */
5
6/*
7 * Copyright (C) 2021-2023 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 DEBUG_BUSLOGIC 1
38#if DEBUG_BUSLOGIC
39# define DBG_BUSLOGIC(...) BX_INFO(__VA_ARGS__)
40#else
41# define DBG_BUSLOGIC(...)
42#endif
43
44#define BUSLOGICCOMMAND_DISABLE_HOST_ADAPTER_INTERRUPT 0x25
45#define BUSLOGICCOMMAND_EXECUTE_SCSI_COMMAND 0x83
46
47
48#define RT_BIT(bit) (1 << (bit))
49
50/** Register offsets in the I/O port space. */
51#define BUSLOGIC_REGISTER_CONTROL 0 /**< Writeonly */
52/** Fields for the control register. */
53# define BL_CTRL_RSBUS RT_BIT(4) /* Reset SCSI Bus. */
54# define BL_CTRL_RINT RT_BIT(5) /* Reset Interrupt. */
55# define BL_CTRL_RSOFT RT_BIT(6) /* Soft Reset. */
56# define BL_CTRL_RHARD RT_BIT(7) /* Hard Reset. */
57
58#define BUSLOGIC_REGISTER_STATUS 0 /**< Readonly */
59/** Fields for the status register. */
60# define BL_STAT_CMDINV RT_BIT(0) /* Command Invalid. */
61# define BL_STAT_DIRRDY RT_BIT(2) /* Data In Register Ready. */
62# define BL_STAT_CPRBSY RT_BIT(3) /* Command/Parameter Out Register Busy. */
63# define BL_STAT_HARDY RT_BIT(4) /* Host Adapter Ready. */
64# define BL_STAT_INREQ RT_BIT(5) /* Initialization Required. */
65# define BL_STAT_DFAIL RT_BIT(6) /* Diagnostic Failure. */
66# define BL_STAT_DACT RT_BIT(7) /* Diagnistic Active. */
67
68#define BUSLOGIC_REGISTER_COMMAND 1 /**< Writeonly */
69#define BUSLOGIC_REGISTER_DATAIN 1 /**< Readonly */
70#define BUSLOGIC_REGISTER_INTERRUPT 2 /**< Readonly */
71#define BUSLOGIC_REGISTER_GEOMETRY 3 /**< Readonly */
72
73/** Fields for the interrupt register. */
74# define BL_INTR_IMBL RT_BIT(0) /* Incoming Mailbox Loaded. */
75# define BL_INTR_OMBR RT_BIT(1) /* Outgoing Mailbox Available. */
76# define BL_INTR_CMDC RT_BIT(2) /* Command Complete. */
77# define BL_INTR_RSTS RT_BIT(3) /* SCSI Bus Reset State. */
78# define BL_INTR_INTV RT_BIT(7) /* Interrupt Valid. */
79
80/**
81 * The structure for the "Execute SCSI Command" command.
82 */
83typedef struct ESCMD
84{
85 /** Data length. */
86 uint32_t cbData;
87 /** Data pointer. */
88 uint32_t u32PhysAddrData;
89 /** The device the request is sent to. */
90 uint8_t uTargetId;
91 /** The LUN in the device. */
92 uint8_t uLogicalUnit;
93 /** Reserved */
94 unsigned char uReserved1 : 3;
95 /** Data direction for the request. */
96 unsigned char uDataDirection : 2;
97 /** Reserved */
98 unsigned char uReserved2 : 3;
99 /** Length of the SCSI CDB. */
100 uint8_t cbCDB;
101 /** The SCSI CDB. (A CDB can be 12 bytes long.) */
102 uint8_t abCDB[16];
103} ESCMD, *PESCMD;
104
105/**
106 * BusLogic-SCSI controller data.
107 */
108typedef struct
109{
110 /** The execute SCSI command. */
111 ESCMD EsCmd;
112 /** I/O base of device. */
113 uint16_t u16IoBase;
114} buslogic_t;
115
116/* The BusLogic specific data must fit into 1KB (statically allocated). */
117ct_assert(sizeof(buslogic_t) <= 1024);
118
119/**
120 * Converts a segment:offset pair into a 32bit physical address.
121 */
122static uint32_t buslogic_addr_to_phys(void __far *ptr)
123{
124 return ((uint32_t)FP_SEG(ptr) << 4) + FP_OFF(ptr);
125}
126
127static int buslogic_cmd(buslogic_t __far *buslogic, uint8_t uCmd, uint8_t __far *pbReq, uint16_t cbReq,
128 uint8_t __far *pbReply, uint16_t cbReply)
129{
130 uint16_t i;
131
132 outb(buslogic->u16IoBase + BUSLOGIC_REGISTER_COMMAND, uCmd);
133 for (i = 0; i < cbReq; i++)
134 outb(buslogic->u16IoBase + BUSLOGIC_REGISTER_COMMAND, *pbReq++);
135
136 /* Wait for the HBA to finish processing the command. */
137 if (cbReply)
138 {
139 while (!(inb(buslogic->u16IoBase + BUSLOGIC_REGISTER_STATUS) & BL_STAT_DIRRDY));
140 for (i = 0; i < cbReply; i++)
141 *pbReply++ = inb(buslogic->u16IoBase + BUSLOGIC_REGISTER_DATAIN);
142 }
143
144 while (!(inb(buslogic->u16IoBase + BUSLOGIC_REGISTER_STATUS) & BL_STAT_HARDY));
145
146 /* Clear interrupt status. */
147 outb(buslogic->u16IoBase + BUSLOGIC_REGISTER_CONTROL, BL_CTRL_RINT);
148
149 return 0;
150}
151
152int buslogic_scsi_cmd_data_out(void __far *pvHba, uint8_t idTgt, uint8_t __far *aCDB,
153 uint8_t cbCDB, uint8_t __far *buffer, uint32_t length)
154{
155 buslogic_t __far *buslogic = (buslogic_t __far *)pvHba;
156 uint8_t abReply[4];
157 int i;
158 int rc;
159
160 _fmemset(&buslogic->EsCmd, 0, sizeof(buslogic->EsCmd));
161 _fmemset(abReply, 0, sizeof(abReply));
162
163 buslogic->EsCmd.cbData = length;
164 buslogic->EsCmd.u32PhysAddrData = buslogic_addr_to_phys(buffer);
165 buslogic->EsCmd.uTargetId = idTgt;
166 buslogic->EsCmd.uLogicalUnit = 0;
167 buslogic->EsCmd.uDataDirection = 0;
168 buslogic->EsCmd.cbCDB = cbCDB;
169
170 for (i = 0; i < cbCDB; i++)
171 buslogic->EsCmd.abCDB[i] = aCDB[i];
172
173 rc = buslogic_cmd(buslogic, BUSLOGICCOMMAND_EXECUTE_SCSI_COMMAND, (uint8_t __far *)&buslogic->EsCmd,
174 sizeof(buslogic->EsCmd) - sizeof(buslogic->EsCmd.abCDB) + cbCDB, &abReply[0], sizeof(abReply));
175 if (!rc)
176 rc = abReply[2];
177
178 return rc;
179}
180
181int buslogic_scsi_cmd_data_in(void __far *pvHba, uint8_t idTgt, uint8_t __far *aCDB,
182 uint8_t cbCDB, uint8_t __far *buffer, uint32_t length)
183{
184 buslogic_t __far *buslogic = (buslogic_t __far *)pvHba;
185 uint8_t abReply[4];
186 int i;
187 int rc;
188
189 DBG_BUSLOGIC("buslogic_scsi_cmd_data_in:\n");
190
191 _fmemset(&buslogic->EsCmd, 0, sizeof(buslogic->EsCmd));
192 _fmemset(abReply, 0, sizeof(abReply));
193
194 buslogic->EsCmd.cbData = length;
195 buslogic->EsCmd.u32PhysAddrData = buslogic_addr_to_phys(buffer);
196 buslogic->EsCmd.uTargetId = idTgt;
197 buslogic->EsCmd.uLogicalUnit = 0;
198 buslogic->EsCmd.uDataDirection = 0;
199 buslogic->EsCmd.cbCDB = cbCDB;
200
201 for (i = 0; i < cbCDB; i++)
202 buslogic->EsCmd.abCDB[i] = aCDB[i];
203
204 rc = buslogic_cmd(buslogic, BUSLOGICCOMMAND_EXECUTE_SCSI_COMMAND, (uint8_t __far *)&buslogic->EsCmd,
205 sizeof(buslogic->EsCmd) - sizeof(buslogic->EsCmd.abCDB) + cbCDB, &abReply[0], sizeof(abReply));
206 if (!rc)
207 rc = abReply[2];
208
209 return rc;
210}
211
212/**
213 * Initializes the BusLogic SCSI HBA and detects attached devices.
214 */
215static int buslogic_scsi_hba_init(buslogic_t __far *buslogic)
216{
217 /* Hard reset. */
218 outb(buslogic->u16IoBase + BUSLOGIC_REGISTER_CONTROL, BL_CTRL_RHARD);
219 while (!(inb(buslogic->u16IoBase + BUSLOGIC_REGISTER_STATUS) & BL_STAT_HARDY));
220
221 return 0;
222}
223
224/**
225 * Init the BusLogic PCI SCSI driver and detect attached disks.
226 */
227int buslogic_scsi_init(void __far *pvHba, uint8_t u8Bus, uint8_t u8DevFn)
228{
229 buslogic_t __far *buslogic = (buslogic_t __far *)pvHba;
230 uint32_t u32Bar;
231
232 DBG_BUSLOGIC("BusLogic SCSI HBA at Bus %u DevFn 0x%x (raw 0x%x)\n", u8Bus, u8DevFn);
233
234 u32Bar = pci_read_config_dword(u8Bus, u8DevFn, 0x10);
235
236 DBG_BUSLOGIC("BAR at 0x10 : 0x%x\n", u32Bar);
237
238 if ((u32Bar & 0x01) != 0)
239 {
240 uint16_t u16IoBase = (u32Bar & 0xfff0);
241
242 /* Enable PCI memory, I/O, bus mastering access in command register. */
243 pci_write_config_word(u8Bus, u8DevFn, 4, 0x7);
244
245 DBG_BUSLOGIC("I/O base: 0x%x\n", u16IoBase);
246 buslogic->u16IoBase = u16IoBase;
247 return buslogic_scsi_hba_init(buslogic);
248 }
249 else
250 DBG_BUSLOGIC("BAR is MMIO\n");
251
252 return 1;
253}
254
255/**
256 * Init the AHA-154x compatible ISA SCSI driver and find attached disks.
257 * The HBA was already detected.
258 */
259int btaha_scsi_init(void __far *pvHba, uint8_t u8Bus, uint8_t u8DevFn)
260{
261 buslogic_t __far *buslogic = (buslogic_t __far *)pvHba;
262
263 buslogic->u16IoBase = (u8Bus << 8) | u8DevFn;
264 DBG_BUSLOGIC("AHA 154x compatible SCSI HBA at I/O port 0x%x)\n", buslogic->u16IoBase);
265
266 return buslogic_scsi_hba_init(buslogic);
267}
268
269/**
270 * Detect AHA-154x compatible ISA SCSI HBA presence.
271 */
272uint16_t btaha_scsi_detect(void)
273{
274 uint16_t bases[] = { 0x330, 0x334, 0 };
275 uint16_t iobase;
276 uint8_t val;
277 int i;
278
279 for (i = 0, iobase = bases[0]; iobase; iobase = bases[++i])
280 {
281 /* Read the status register. The possible valid values after power-up
282 * or reset are 0x10 or 0x30.
283 */
284 val = inb(iobase + BUSLOGIC_REGISTER_STATUS);
285 if ((val != 0x30) && (val != 0x10))
286 continue;
287
288 /* Exclude PCI adapters in ISA compatible mode. The check reads the
289 * undocumented "geometry" register and only continues if bit 6 is
290 * set.
291 * The logic is kind of genius. On AHA-154xB and earlier, there's
292 * nothing and the read returns 0xFF. On AHA-154xC, the register
293 * returns the letters 'ADAP' in a round-robin fashion. On BusLogic
294 * ISA adapters, the firmware sets the register to 0x55 during
295 * power-up/reset (possibly also setting bit 7 if > 1GB drive
296 * support is enabled). In all cases, bit 6 will be set.
297 * But on BusLogic PCI HBAs, the geometry register is 0x80 (in our
298 * emulation) or possibly 0 and bit 6 is clear.
299 * Thus if bit 6 is not set, the device is rejected because it was
300 * likely already found as a PCI device, and must not be detected
301 * again at the alternative ISA-compatible I/O base.
302 */
303 val = inb(iobase + BUSLOGIC_REGISTER_GEOMETRY);
304 if ((val & 0x40) == 0)
305 continue;
306
307 /* If we got this far, the I/O base is valid and we're done. */
308 break;
309 }
310
311 return iobase ? iobase: 0xffff;
312}
313
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use