VirtualBox

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

Last change on this file since 39347 was 39347, checked in by vboxsync, 14 years ago

More refactoring.

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