VirtualBox

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

Last change on this file since 89330 was 89272, checked in by vboxsync, 4 years ago

PC/BIOS/buslogic: Don't disable interrupts or guests will be confused, bugref:4841

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 8.7 KB
Line 
1/* $Id: buslogic.c 89272 2021-05-25 12:38:19Z vboxsync $ */
2/** @file
3 * BusLogic SCSI host adapter driver to boot from disks.
4 */
5
6/*
7 * Copyright (C) 2021 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <stdint.h>
19#include <string.h>
20#include "biosint.h"
21#include "ebda.h"
22#include "inlines.h"
23#include "pciutil.h"
24#include "vds.h"
25#include "scsi.h"
26
27//#define DEBUG_BUSLOGIC 1
28#if DEBUG_BUSLOGIC
29# define DBG_BUSLOGIC(...) BX_INFO(__VA_ARGS__)
30#else
31# define DBG_BUSLOGIC(...)
32#endif
33
34#define BUSLOGICCOMMAND_DISABLE_HOST_ADAPTER_INTERRUPT 0x25
35#define BUSLOGICCOMMAND_EXECUTE_SCSI_COMMAND 0x83
36
37
38#define RT_BIT(bit) (1 << (bit))
39
40/** Register offsets in the I/O port space. */
41#define BUSLOGIC_REGISTER_CONTROL 0 /**< Writeonly */
42/** Fields for the control register. */
43# define BL_CTRL_RSBUS RT_BIT(4) /* Reset SCSI Bus. */
44# define BL_CTRL_RINT RT_BIT(5) /* Reset Interrupt. */
45# define BL_CTRL_RSOFT RT_BIT(6) /* Soft Reset. */
46# define BL_CTRL_RHARD RT_BIT(7) /* Hard Reset. */
47
48#define BUSLOGIC_REGISTER_STATUS 0 /**< Readonly */
49/** Fields for the status register. */
50# define BL_STAT_CMDINV RT_BIT(0) /* Command Invalid. */
51# define BL_STAT_DIRRDY RT_BIT(2) /* Data In Register Ready. */
52# define BL_STAT_CPRBSY RT_BIT(3) /* Command/Parameter Out Register Busy. */
53# define BL_STAT_HARDY RT_BIT(4) /* Host Adapter Ready. */
54# define BL_STAT_INREQ RT_BIT(5) /* Initialization Required. */
55# define BL_STAT_DFAIL RT_BIT(6) /* Diagnostic Failure. */
56# define BL_STAT_DACT RT_BIT(7) /* Diagnistic Active. */
57
58#define BUSLOGIC_REGISTER_COMMAND 1 /**< Writeonly */
59#define BUSLOGIC_REGISTER_DATAIN 1 /**< Readonly */
60#define BUSLOGIC_REGISTER_INTERRUPT 2 /**< Readonly */
61/** Fields for the interrupt register. */
62# define BL_INTR_IMBL RT_BIT(0) /* Incoming Mailbox Loaded. */
63# define BL_INTR_OMBR RT_BIT(1) /* Outgoing Mailbox Available. */
64# define BL_INTR_CMDC RT_BIT(2) /* Command Complete. */
65# define BL_INTR_RSTS RT_BIT(3) /* SCSI Bus Reset State. */
66# define BL_INTR_INTV RT_BIT(7) /* Interrupt Valid. */
67
68/**
69 * The structure for the "Execute SCSI Command" command.
70 */
71typedef struct ESCMD
72{
73 /** Data length. */
74 uint32_t cbData;
75 /** Data pointer. */
76 uint32_t u32PhysAddrData;
77 /** The device the request is sent to. */
78 uint8_t uTargetId;
79 /** The LUN in the device. */
80 uint8_t uLogicalUnit;
81 /** Reserved */
82 unsigned char uReserved1 : 3;
83 /** Data direction for the request. */
84 unsigned char uDataDirection : 2;
85 /** Reserved */
86 unsigned char uReserved2 : 3;
87 /** Length of the SCSI CDB. */
88 uint8_t cbCDB;
89 /** The SCSI CDB. (A CDB can be 12 bytes long.) */
90 uint8_t abCDB[16];
91} ESCMD, *PESCMD;
92
93/**
94 * BusLogic-SCSI controller data.
95 */
96typedef struct
97{
98 /** The execute SCSI command. */
99 ESCMD EsCmd;
100 /** I/O base of device. */
101 uint16_t u16IoBase;
102 /** The sink buf. */
103 void __far *pvSinkBuf;
104 /** Size of the sink buffer in bytes. */
105 uint16_t cbSinkBuf;
106} buslogic_t;
107
108/* The BusLogic specific data must fit into 1KB (statically allocated). */
109ct_assert(sizeof(buslogic_t) <= 1024);
110
111/**
112 * Converts a segment:offset pair into a 32bit physical address.
113 */
114static uint32_t buslogic_addr_to_phys(void __far *ptr)
115{
116 return ((uint32_t)FP_SEG(ptr) << 4) + FP_OFF(ptr);
117}
118
119static int buslogic_cmd(buslogic_t __far *buslogic, uint8_t uCmd, uint8_t __far *pbReq, uint16_t cbReq,
120 uint8_t __far *pbReply, uint16_t cbReply)
121{
122 uint16_t i;
123
124 outb(buslogic->u16IoBase + BUSLOGIC_REGISTER_COMMAND, uCmd);
125 for (i = 0; i < cbReq; i++)
126 outb(buslogic->u16IoBase + BUSLOGIC_REGISTER_COMMAND, *pbReq++);
127
128 /* Wait for the HBA to finish processing the command. */
129 if (cbReply)
130 {
131 while (!(inb(buslogic->u16IoBase + BUSLOGIC_REGISTER_STATUS) & BL_STAT_DIRRDY));
132 for (i = 0; i < cbReply; i++)
133 *pbReply++ = inb(buslogic->u16IoBase + BUSLOGIC_REGISTER_DATAIN);
134 }
135
136 while (!(inb(buslogic->u16IoBase + BUSLOGIC_REGISTER_STATUS) & BL_STAT_HARDY));
137
138 /* Clear interrupt status. */
139 outb(buslogic->u16IoBase + BUSLOGIC_REGISTER_CONTROL, BL_CTRL_RINT);
140
141 return 0;
142}
143
144int buslogic_scsi_cmd_data_out(void __far *pvHba, uint8_t idTgt, uint8_t __far *aCDB,
145 uint8_t cbCDB, uint8_t __far *buffer, uint32_t length)
146{
147 buslogic_t __far *buslogic = (buslogic_t __far *)pvHba;
148 uint8_t abReply[4];
149 int i;
150 int rc;
151
152 _fmemset(&buslogic->EsCmd, 0, sizeof(buslogic->EsCmd));
153 _fmemset(abReply, 0, sizeof(abReply));
154
155 buslogic->EsCmd.cbData = length;
156 buslogic->EsCmd.u32PhysAddrData = buslogic_addr_to_phys(buffer);
157 buslogic->EsCmd.uTargetId = idTgt;
158 buslogic->EsCmd.uLogicalUnit = 0;
159 buslogic->EsCmd.uDataDirection = 0;
160 buslogic->EsCmd.cbCDB = cbCDB;
161
162 for (i = 0; i < cbCDB; i++)
163 buslogic->EsCmd.abCDB[i] = aCDB[i];
164
165 rc = buslogic_cmd(buslogic, BUSLOGICCOMMAND_EXECUTE_SCSI_COMMAND, (uint8_t __far *)&buslogic->EsCmd,
166 sizeof(buslogic->EsCmd) - sizeof(buslogic->EsCmd.abCDB) + cbCDB, &abReply[0], sizeof(abReply));
167 if (!rc)
168 rc = abReply[2];
169
170 return rc;
171}
172
173int buslogic_scsi_cmd_data_in(void __far *pvHba, uint8_t idTgt, uint8_t __far *aCDB,
174 uint8_t cbCDB, uint8_t __far *buffer, uint32_t length, uint16_t skip_b,
175 uint16_t skip_a)
176{
177 buslogic_t __far *buslogic = (buslogic_t __far *)pvHba;
178 uint8_t abReply[4];
179 int i;
180 int rc;
181
182 DBG_BUSLOGIC("buslogic_scsi_cmd_data_in:\n");
183
184 if ( ( skip_b
185 || skip_a)
186 && skip_b + length + skip_a > buslogic->cbSinkBuf) /* Sink buffer is only 16KB at the moment. */
187 return 1;
188
189 _fmemset(&buslogic->EsCmd, 0, sizeof(buslogic->EsCmd));
190 _fmemset(abReply, 0, sizeof(abReply));
191
192 if (!skip_b && !skip_a)
193 {
194 buslogic->EsCmd.cbData = length;
195 buslogic->EsCmd.u32PhysAddrData = buslogic_addr_to_phys(buffer);
196 }
197 else
198 {
199 buslogic->EsCmd.cbData = length + skip_b + skip_a;
200 buslogic->EsCmd.u32PhysAddrData = buslogic_addr_to_phys(buslogic->pvSinkBuf); /* Requires the sink buffer because there is no S/G variant. */
201 }
202 buslogic->EsCmd.uTargetId = idTgt;
203 buslogic->EsCmd.uLogicalUnit = 0;
204 buslogic->EsCmd.uDataDirection = 0;
205 buslogic->EsCmd.cbCDB = cbCDB;
206
207 for (i = 0; i < cbCDB; i++)
208 buslogic->EsCmd.abCDB[i] = aCDB[i];
209
210 rc = buslogic_cmd(buslogic, BUSLOGICCOMMAND_EXECUTE_SCSI_COMMAND, (uint8_t __far *)&buslogic->EsCmd,
211 sizeof(buslogic->EsCmd) - sizeof(buslogic->EsCmd.abCDB) + cbCDB, &abReply[0], sizeof(abReply));
212 if (!rc)
213 {
214 /* Copy the data over from the sink buffer. */
215 if (abReply[2] == 0)
216 {
217 if (skip_b || skip_a)
218 _fmemcpy(buffer, (const uint8_t __far *)buslogic->pvSinkBuf + skip_b, length);
219 }
220 else
221 rc = abReply[2];
222 }
223
224 return rc;
225}
226
227/**
228 * Initializes the BusLogic SCSI HBA and detects attached devices.
229 */
230static int buslogic_scsi_hba_init(buslogic_t __far *buslogic)
231{
232 /* Hard reset. */
233 outb(buslogic->u16IoBase + BUSLOGIC_REGISTER_CONTROL, BL_CTRL_RHARD);
234 while (!(inb(buslogic->u16IoBase + BUSLOGIC_REGISTER_STATUS) & BL_STAT_HARDY));
235
236 return 0;
237}
238
239/**
240 * Init the BusLogic SCSI driver and detect attached disks.
241 */
242int buslogic_scsi_init(void __far *pvHba, void __far *pvSinkBuf, uint16_t cbSinkBuf, uint8_t u8Bus, uint8_t u8DevFn)
243{
244 buslogic_t __far *buslogic = (buslogic_t __far *)pvHba;
245 uint32_t u32Bar;
246
247 DBG_BUSLOGIC("BusLogic SCSI HBA at Bus %u DevFn 0x%x (raw 0x%x)\n", u8Bus, u8DevFn);
248
249 u32Bar = pci_read_config_dword(u8Bus, u8DevFn, 0x10);
250
251 DBG_BUSLOGIC("BAR at 0x10 : 0x%x\n", u32Bar);
252
253 if ((u32Bar & 0x01) != 0)
254 {
255 uint16_t u16IoBase = (u32Bar & 0xfff0);
256
257 /* Enable PCI memory, I/O, bus mastering access in command register. */
258 pci_write_config_word(u8Bus, u8DevFn, 4, 0x7);
259
260 DBG_BUSLOGIC("I/O base: 0x%x\n", u16IoBase);
261 buslogic->u16IoBase = u16IoBase;
262 buslogic->pvSinkBuf = pvSinkBuf;
263 buslogic->cbSinkBuf = cbSinkBuf;
264 return buslogic_scsi_hba_init(buslogic);
265 }
266 else
267 DBG_BUSLOGIC("BAR is MMIO\n");
268
269 return 1;
270}
Note: See TracBrowser for help on using the repository browser.

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