VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS-new/scsi.c@ 39366

Last change on this file since 39366 was 39366, checked in by vboxsync, 13 years ago

Further streamlined BIOS disk code for ATA/SCSI.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.2 KB
Line 
1/* $Id: scsi.c 39366 2011-11-18 13:57:42Z vboxsync $ */
2/** @file
3 * SCSI host adapter driver to boot from SCSI disks
4 */
5
6/*
7 * Copyright (C) 2004-2011 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 "inlines.h"
22#include "ebda.h"
23#include "ata.h"
24
25
26//#define VBOX_SCSI_DEBUG 1 /* temporary */
27
28#ifdef VBOX_SCSI_DEBUG
29# define VBSCSI_DEBUG(...) BX_INFO(__VA_ARGS__)
30#else
31# define VBSCSI_DEBUG(...)
32#endif
33
34#define VBSCSI_BUSY (1 << 0)
35
36/* The I/O port of the BusLogic SCSI adapter. */
37#define BUSLOGIC_ISA_IO_PORT 0x330
38/* The I/O port of the LsiLogic SCSI adapter. */
39#define LSILOGIC_ISA_IO_PORT 0x340
40/* The I/O port of the LsiLogic SAS adapter. */
41#define LSILOGIC_SAS_ISA_IO_PORT 0x350
42
43#define VBSCSI_REGISTER_STATUS 0
44#define VBSCSI_REGISTER_COMMAND 0
45#define VBSCSI_REGISTER_DATA_IN 1
46#define VBSCSI_REGISTER_IDENTIFY 2
47#define VBSCSI_REGISTER_RESET 3
48
49#define VBSCSI_MAX_DEVICES 16 /* Maximum number of devices a SCSI device can have. */
50
51/* Command opcodes. */
52#define SCSI_INQUIRY 0x12
53#define SCSI_READ_CAPACITY 0x25
54#define SCSI_READ_10 0x28
55#define SCSI_WRITE_10 0x2a
56
57/* Data transfer direction. */
58#define SCSI_TXDIR_FROM_DEVICE 0
59#define SCSI_TXDIR_TO_DEVICE 1
60
61#pragma pack(1)
62
63/* READ_10/WRITE_10 CDB layout. */
64typedef struct {
65 uint16_t command; /* Command. */
66 uint32_t lba; /* LBA, MSB first! */
67 uint8_t pad1; /* Unused. */
68 uint16_t nsect; /* Sector count, MSB first! */
69 uint8_t pad2; /* Unused. */
70} cdb_rw10;
71
72#pragma pack()
73
74ct_assert(sizeof(cdb_rw10) == 10);
75
76int scsi_cmd_data_in(uint16_t io_base, uint8_t device_id, uint8_t __far *aCDB,
77 uint8_t cbCDB, uint8_t __far *buffer, uint16_t cbBuffer)
78{
79 /* Check that the adapter is ready. */
80 uint8_t status;
81 uint16_t i;
82
83 do
84 {
85 status = inb(io_base+VBSCSI_REGISTER_STATUS);
86 } while (status & VBSCSI_BUSY);
87
88 /* Write target ID. */
89 outb(io_base+VBSCSI_REGISTER_COMMAND, device_id);
90 /* Write transfer direction. */
91 outb(io_base+VBSCSI_REGISTER_COMMAND, SCSI_TXDIR_FROM_DEVICE);
92 /* Write the CDB size. */
93 outb(io_base+VBSCSI_REGISTER_COMMAND, cbCDB);
94 /* Write buffer size. */
95 outb(io_base+VBSCSI_REGISTER_COMMAND, cbBuffer);
96 outb(io_base+VBSCSI_REGISTER_COMMAND, (cbBuffer >> 8));
97 /* Write the CDB. */
98 for (i = 0; i < cbCDB; i++)
99 outb(io_base+VBSCSI_REGISTER_COMMAND, aCDB[i]);
100
101 /* Now wait for the command to complete. */
102 do
103 {
104 status = inb(io_base+VBSCSI_REGISTER_STATUS);
105 } while (status & VBSCSI_BUSY);
106
107 /* Get the read data. */
108 rep_insb(buffer, cbBuffer, io_base + VBSCSI_REGISTER_DATA_IN);
109
110 return 0;
111}
112
113int scsi_cmd_data_out(uint16_t io_base, uint8_t device_id, uint8_t __far *aCDB,
114 uint8_t cbCDB, uint8_t __far *buffer, uint16_t cbBuffer)
115{
116 /* Check that the adapter is ready. */
117 uint8_t status;
118 uint16_t i;
119
120 do
121 {
122 status = inb(io_base+VBSCSI_REGISTER_STATUS);
123 } while (status & VBSCSI_BUSY);
124
125 /* Write target ID. */
126 outb(io_base+VBSCSI_REGISTER_COMMAND, device_id);
127 /* Write transfer direction. */
128 outb(io_base+VBSCSI_REGISTER_COMMAND, SCSI_TXDIR_TO_DEVICE);
129 /* Write the CDB size. */
130 outb(io_base+VBSCSI_REGISTER_COMMAND, cbCDB);
131 /* Write buffer size. */
132 outb(io_base+VBSCSI_REGISTER_COMMAND, cbBuffer);
133 outb(io_base+VBSCSI_REGISTER_COMMAND, (cbBuffer >> 8));
134 /* Write the CDB. */
135 for (i = 0; i < cbCDB; i++)
136 outb(io_base+VBSCSI_REGISTER_COMMAND, aCDB[i]);
137
138 /* Write data to I/O port. */
139 rep_outsb(buffer, cbBuffer, io_base+VBSCSI_REGISTER_DATA_IN);
140
141 /* Now wait for the command to complete. */
142 do
143 {
144 status = inb(io_base+VBSCSI_REGISTER_STATUS);
145 } while (status & VBSCSI_BUSY);
146
147 return 0;
148}
149
150/**
151 * Read sectors from an attached SCSI device.
152 *
153 * @returns status code.
154 * @param bios_dsk Pointer to disk request packet (in the
155 * EBDA).
156 */
157int scsi_read_sectors(bio_dsk_t __far *bios_dsk)
158{
159 uint8_t rc;
160 cdb_rw10 cdb;
161 uint16_t count;
162 uint16_t io_base;
163 uint8_t target_id;
164 uint8_t device_id;
165
166 device_id = bios_dsk->drqp.dev_id - BX_MAX_ATA_DEVICES;
167 if (device_id > BX_MAX_SCSI_DEVICES)
168 BX_PANIC("scsi_read_sectors: device_id out of range %d\n", device_id);
169
170 count = bios_dsk->drqp.nsect;
171
172 /* Prepare a CDB. */
173 cdb.command = SCSI_READ_10;
174 cdb.lba = swap_32(bios_dsk->drqp.lba);
175 cdb.pad1 = 0;
176 cdb.nsect = swap_16(count);
177 cdb.pad2 = 0;
178
179
180 io_base = bios_dsk->scsidev[device_id].io_base;
181 target_id = bios_dsk->scsidev[device_id].target_id;
182
183 rc = scsi_cmd_data_in(io_base, target_id, (void __far *)&cdb, 10,
184 bios_dsk->drqp.buffer, (count * 512));
185
186 if (!rc)
187 {
188 bios_dsk->drqp.trsfsectors = count;
189 bios_dsk->drqp.trsfbytes = count * 512;
190 }
191
192 return rc;
193}
194
195/**
196 * Write sectors to an attached SCSI device.
197 *
198 * @returns status code.
199 * @param bios_dsk Pointer to disk request packet (in the
200 * EBDA).
201 */
202int scsi_write_sectors(bio_dsk_t __far *bios_dsk)
203{
204 uint8_t rc;
205 cdb_rw10 cdb;
206 uint16_t count;
207 uint16_t io_base;
208 uint8_t target_id;
209 uint8_t device_id;
210
211 device_id = bios_dsk->drqp.dev_id - BX_MAX_ATA_DEVICES;
212 if (device_id > BX_MAX_SCSI_DEVICES)
213 BX_PANIC("scsi_write_sectors: device_id out of range %d\n", device_id);
214
215 count = bios_dsk->drqp.nsect;
216
217 /* Prepare a CDB. */
218 cdb.command = SCSI_WRITE_10;
219 cdb.lba = swap_32(bios_dsk->drqp.lba);
220 cdb.pad1 = 0;
221 cdb.nsect = swap_16(count);
222 cdb.pad2 = 0;
223
224 io_base = bios_dsk->scsidev[device_id].io_base;
225 target_id = bios_dsk->scsidev[device_id].target_id;
226
227 rc = scsi_cmd_data_out(io_base, target_id, (void __far *)&cdb, 10,
228 bios_dsk->drqp.buffer, (count * 512));
229
230 if (!rc)
231 {
232 bios_dsk->drqp.trsfsectors = count;
233 bios_dsk->drqp.trsfbytes = (count * 512);
234 }
235
236 return rc;
237}
238
239/**
240 * Enumerate attached devices.
241 *
242 * @returns nothing.
243 * @param io_base The I/O base port of the controller.
244 */
245void scsi_enumerate_attached_devices(uint16_t io_base)
246{
247 int i;
248 uint8_t buffer[0x0200];
249 bio_dsk_t __far *bios_dsk;
250
251 bios_dsk = read_word(0x0040, 0x000E) :> &EbdaData->bdisk;
252
253 /* Go through target devices. */
254 for (i = 0; i < VBSCSI_MAX_DEVICES; i++)
255 {
256 uint8_t rc;
257 uint8_t aCDB[10];
258
259 aCDB[0] = SCSI_INQUIRY;
260 aCDB[1] = 0;
261 aCDB[2] = 0;
262 aCDB[3] = 0;
263 aCDB[4] = 5; /* Allocation length. */
264 aCDB[5] = 0;
265
266 rc = scsi_cmd_data_in(io_base, i, aCDB, 6, buffer, 5);
267 if (rc != 0)
268 BX_PANIC("scsi_enumerate_attached_devices: SCSI_INQUIRY failed\n");
269
270 /* Check if there is a disk attached. */
271 if ( ((buffer[0] & 0xe0) == 0)
272 && ((buffer[0] & 0x1f) == 0x00))
273 {
274 VBSCSI_DEBUG("scsi_enumerate_attached_devices: Disk detected at %d\n", i);
275
276 /* We add the disk only if the maximum is not reached yet. */
277 if (bios_dsk->scsi_hdcount < BX_MAX_SCSI_DEVICES)
278 {
279 uint32_t sectors, sector_size, cylinders;
280 uint16_t heads, sectors_per_track;
281 uint8_t hdcount, hdcount_scsi, hd_index;
282
283 /* Issue a read capacity command now. */
284 _fmemset(aCDB, 0, sizeof(aCDB));
285 aCDB[0] = SCSI_READ_CAPACITY;
286
287 rc = scsi_cmd_data_in(io_base, i, aCDB, 10, buffer, 8);
288 if (rc != 0)
289 BX_PANIC("scsi_enumerate_attached_devices: SCSI_READ_CAPACITY failed\n");
290
291 /* Build sector number and size from the buffer. */
292 //@todo: byte swapping for dword sized items should be farmed out...
293 sectors = ((uint32_t)buffer[0] << 24)
294 | ((uint32_t)buffer[1] << 16)
295 | ((uint32_t)buffer[2] << 8)
296 | ((uint32_t)buffer[3]);
297
298 sector_size = ((uint32_t)buffer[4] << 24)
299 | ((uint32_t)buffer[5] << 16)
300 | ((uint32_t)buffer[6] << 8)
301 | ((uint32_t)buffer[7]);
302
303 /* We only support the disk if sector size is 512 bytes. */
304 if (sector_size != 512)
305 {
306 /* Leave a log entry. */
307 BX_INFO("Disk %d has an unsupported sector size of %u\n", i, sector_size);
308 continue;
309 }
310
311 /* We need to calculate the geometry for the disk. From
312 * the BusLogic driver in the Linux kernel.
313 */
314 if (sectors >= 4 * 1024 * 1024)
315 {
316 heads = 255;
317 sectors_per_track = 63;
318 }
319 else if (sectors >= 2 * 1024 * 1024)
320 {
321 heads = 128;
322 sectors_per_track = 32;
323 }
324 else
325 {
326 heads = 64;
327 sectors_per_track = 32;
328 }
329 cylinders = (uint32_t)(sectors / (heads * sectors_per_track));
330 hdcount_scsi = bios_dsk->scsi_hdcount;
331
332 /* Calculate index into the generic disk table. */
333 hd_index = hdcount_scsi + BX_MAX_ATA_DEVICES;
334
335 bios_dsk->scsidev[hdcount_scsi].io_base = io_base;
336 bios_dsk->scsidev[hdcount_scsi].target_id = i;
337 bios_dsk->devices[hd_index].type = ATA_TYPE_SCSI;
338 bios_dsk->devices[hd_index].device = ATA_DEVICE_HD;
339 bios_dsk->devices[hd_index].removable = 0;
340 bios_dsk->devices[hd_index].lock = 0;
341 bios_dsk->devices[hd_index].mode = ATA_MODE_PIO16;
342 bios_dsk->devices[hd_index].blksize = sector_size;
343 bios_dsk->devices[hd_index].translation = ATA_TRANSLATION_LBA;
344
345 /* Write LCHS values. */
346 bios_dsk->devices[hd_index].lchs.heads = heads;
347 bios_dsk->devices[hd_index].lchs.spt = sectors_per_track;
348 if (cylinders > 1024)
349 bios_dsk->devices[hd_index].lchs.cylinders = 1024;
350 else
351 bios_dsk->devices[hd_index].lchs.cylinders = (uint16_t)cylinders;
352
353 /* Write PCHS values. */
354 bios_dsk->devices[hd_index].pchs.heads = heads;
355 bios_dsk->devices[hd_index].pchs.spt = sectors_per_track;
356 if (cylinders > 1024)
357 bios_dsk->devices[hd_index].pchs.cylinders = 1024;
358 else
359 bios_dsk->devices[hd_index].pchs.cylinders = (uint16_t)cylinders;
360
361 bios_dsk->devices[hd_index].sectors = sectors;
362
363 /* Store the id of the disk in the ata hdidmap. */
364 hdcount = bios_dsk->hdcount;
365 bios_dsk->hdidmap[hdcount] = hdcount_scsi + BX_MAX_ATA_DEVICES;
366 hdcount++;
367 bios_dsk->hdcount = hdcount;
368
369 /* Update hdcount in the BDA. */
370 hdcount = read_byte(0x40, 0x75);
371 hdcount++;
372 write_byte(0x40, 0x75, hdcount);
373
374 hdcount_scsi++;
375 bios_dsk->scsi_hdcount = hdcount_scsi;
376 }
377 else
378 {
379 /* We reached the maximum of SCSI disks we can boot from. We can quit detecting. */
380 break;
381 }
382 }
383 else
384 VBSCSI_DEBUG("scsi_enumerate_attached_devices: No disk detected at %d\n", i);
385 }
386}
387
388/**
389 * Init the SCSI driver and detect attached disks.
390 */
391void BIOSCALL scsi_init(void)
392{
393 uint8_t identifier;
394 bio_dsk_t __far *bios_dsk;
395
396 bios_dsk = read_word(0x0040, 0x000E) :> &EbdaData->bdisk;
397
398 bios_dsk->scsi_hdcount = 0;
399
400 identifier = 0;
401
402 /* Detect BusLogic adapter. */
403 outb(BUSLOGIC_ISA_IO_PORT+VBSCSI_REGISTER_IDENTIFY, 0x55);
404 identifier = inb(BUSLOGIC_ISA_IO_PORT+VBSCSI_REGISTER_IDENTIFY);
405
406 if (identifier == 0x55)
407 {
408 /* Detected - Enumerate attached devices. */
409 VBSCSI_DEBUG("scsi_init: BusLogic SCSI adapter detected\n");
410 outb(BUSLOGIC_ISA_IO_PORT+VBSCSI_REGISTER_RESET, 0);
411 scsi_enumerate_attached_devices(BUSLOGIC_ISA_IO_PORT);
412 }
413 else
414 {
415 VBSCSI_DEBUG("scsi_init: BusLogic SCSI adapter not detected\n");
416 }
417
418 /* Detect LsiLogic adapter. */
419 outb(LSILOGIC_ISA_IO_PORT+VBSCSI_REGISTER_IDENTIFY, 0x55);
420 identifier = inb(LSILOGIC_ISA_IO_PORT+VBSCSI_REGISTER_IDENTIFY);
421
422 if (identifier == 0x55)
423 {
424 /* Detected - Enumerate attached devices. */
425 VBSCSI_DEBUG("scsi_init: LSI Logic SCSI adapter detected\n");
426 outb(LSILOGIC_ISA_IO_PORT+VBSCSI_REGISTER_RESET, 0);
427 scsi_enumerate_attached_devices(LSILOGIC_ISA_IO_PORT);
428 }
429 else
430 {
431 VBSCSI_DEBUG("scsi_init: LSI Logic SCSI adapter not detected\n");
432 }
433
434 /* Detect LsiLogic SAS adapter. */
435 outb(LSILOGIC_SAS_ISA_IO_PORT+VBSCSI_REGISTER_IDENTIFY, 0x55);
436 identifier = inb(LSILOGIC_SAS_ISA_IO_PORT+VBSCSI_REGISTER_IDENTIFY);
437
438 if (identifier == 0x55)
439 {
440 /* Detected - Enumerate attached devices. */
441 VBSCSI_DEBUG("scsi_init: LSI Logic SAS adapter detected\n");
442 outb(LSILOGIC_SAS_ISA_IO_PORT+VBSCSI_REGISTER_RESET, 0);
443 scsi_enumerate_attached_devices(LSILOGIC_SAS_ISA_IO_PORT);
444 }
445 else
446 {
447 VBSCSI_DEBUG("scsi_init: LSI Logic SAS adapter not detected\n");
448 }
449}
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