VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS/scsi.c@ 33095

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

BIOS: SCSI writes via string I/O (currently disabled).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.1 KB
Line 
1/* $Id: scsi.c 33095 2010-10-13 11:38:59Z vboxsync $ */
2/** @file
3 * SCSI host adapter driver to boot from SCSI disks
4 */
5
6/*
7 * Copyright (C) 2004-2009 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/* The I/O port of the BusLogic SCSI adapter. */
19#define BUSLOGIC_ISA_IO_PORT 0x330
20/* The I/O port of the LsiLogic SCSI adapter. */
21#define LSILOGIC_ISA_IO_PORT 0x340
22/* The I/O port of the LsiLogic SAS adapter. */
23#define LSILOGIC_SAS_ISA_IO_PORT 0x350
24
25#define VBOXSCSI_REGISTER_STATUS 0
26#define VBOXSCSI_REGISTER_COMMAND 0
27#define VBOXSCSI_REGISTER_DATA_IN 1
28#define VBOXSCSI_REGISTER_IDENTIFY 2
29#define VBOXSCSI_REGISTER_RESET 3
30
31#define VBOXSCSI_MAX_DEVICES 16 /* Maximum number of devices a SCSI device can have. */
32
33/* Command opcodes. */
34#define SCSI_INQUIRY 0x12
35#define SCSI_READ_CAPACITY 0x25
36#define SCSI_READ_10 0x28
37#define SCSI_WRITE_10 0x2a
38
39/* Data transfer direction. */
40#define SCSI_TXDIR_FROM_DEVICE 0
41#define SCSI_TXDIR_TO_DEVICE 1
42
43#define VBOXSCSI_BUSY (1 << 0)
44
45//#define VBOX_SCSI_DEBUG 1 /* temporary */
46
47#ifdef VBOX_SCSI_DEBUG
48# define VBOXSCSI_DEBUG(a...) BX_INFO(a)
49#else
50# define VBOXSCSI_DEBUG(a...)
51#endif
52
53int scsi_cmd_data_in(io_base, device_id, cdb_segment, aCDB, cbCDB, segment, offset, cbBuffer)
54 Bit16u io_base, aCDB, offset, cbBuffer, segment, cdb_segment;
55 Bit8u device_id, cbCDB;
56{
57 /* Check that the adapter is ready. */
58 Bit8u status;
59 Bit16u i;
60
61 do
62 {
63 status = inb(io_base+VBOXSCSI_REGISTER_STATUS);
64 } while (status & VBOXSCSI_BUSY);
65
66 /* Write target ID. */
67 outb(io_base+VBOXSCSI_REGISTER_COMMAND, device_id);
68 /* Write transfer direction. */
69 outb(io_base+VBOXSCSI_REGISTER_COMMAND, SCSI_TXDIR_FROM_DEVICE);
70 /* Write the CDB size. */
71 outb(io_base+VBOXSCSI_REGISTER_COMMAND, cbCDB);
72 /* Write buffer size. */
73 outb(io_base+VBOXSCSI_REGISTER_COMMAND, cbBuffer);
74 outb(io_base+VBOXSCSI_REGISTER_COMMAND, (cbBuffer >> 8));
75 /* Write the CDB. */
76 for (i = 0; i < cbCDB; i++)
77 outb(io_base+VBOXSCSI_REGISTER_COMMAND, read_byte(cdb_segment, aCDB + i));
78
79 /* Now wait for the command to complete. */
80 do
81 {
82 status = inb(io_base+VBOXSCSI_REGISTER_STATUS);
83 } while (status & VBOXSCSI_BUSY);
84
85#if 0
86 /* Get the read data. */
87 for (i = 0; i < cbBuffer; i++)
88 {
89 Bit8u data;
90
91 data = inb(io_base+VBOXSCSI_REGISTER_DATA_IN);
92 write_byte(segment, offset+i, data);
93
94 VBOXSCSI_DEBUG("buffer[%d]=%x\n", i, data);
95 }
96#else
97 io_base = io_base + VBOXSCSI_REGISTER_DATA_IN;
98
99ASM_START
100 push bp
101 mov bp, sp
102 mov di, _scsi_cmd_data_in.offset + 2[bp]
103 mov ax, _scsi_cmd_data_in.segment + 2[bp]
104 mov cx, _scsi_cmd_data_in.cbBuffer + 2[bp]
105
106 mov es, ax ;; segment in es
107 mov dx, _scsi_cmd_data_in.io_base + 2[bp] ;; SCSI data read port
108
109 rep
110 insb ;; CX dwords transfered from port(DX) to ES:[DI]
111
112 pop bp
113ASM_END
114#endif
115
116 return 0;
117}
118
119int scsi_cmd_data_out(io_base, device_id, cdb_segment, aCDB, cbCDB, segment, offset, cbBuffer)
120 Bit16u io_base, aCDB, offset, cbBuffer, segment, cdb_segment;
121 Bit8u device_id, cbCDB;
122{
123 /* Check that the adapter is ready. */
124 Bit8u status;
125 Bit16u i;
126
127 do
128 {
129 status = inb(io_base+VBOXSCSI_REGISTER_STATUS);
130 } while (status & VBOXSCSI_BUSY);
131
132 /* Write target ID. */
133 outb(io_base+VBOXSCSI_REGISTER_COMMAND, device_id);
134 /* Write transfer direction. */
135 outb(io_base+VBOXSCSI_REGISTER_COMMAND, SCSI_TXDIR_TO_DEVICE);
136 /* Write the CDB size. */
137 outb(io_base+VBOXSCSI_REGISTER_COMMAND, cbCDB);
138 /* Write buffer size. */
139 outb(io_base+VBOXSCSI_REGISTER_COMMAND, cbBuffer);
140 outb(io_base+VBOXSCSI_REGISTER_COMMAND, (cbBuffer >> 8));
141 /* Write the CDB. */
142 for (i = 0; i < cbCDB; i++)
143 outb(io_base+VBOXSCSI_REGISTER_COMMAND, read_byte(cdb_segment, aCDB + i));
144
145#if 1
146 /* Write data to I/O port. */
147 for (i = 0; i < cbBuffer; i++)
148 {
149 Bit8u data;
150
151 data = read_byte(segment, offset+i);
152 outb(io_base+VBOXSCSI_REGISTER_DATA_IN, data);
153
154 VBOXSCSI_DEBUG("buffer[%d]=%x\n", i, data);
155 }
156#else
157ASM_START
158 push bp
159 mov bp, sp
160 mov si, _scsi_cmd_data_out.offset + 2[bp]
161 mov ax, _scsi_cmd_data_out.segment + 2[bp]
162 mov cx, _scsi_cmd_data_out.cbBuffer + 2[bp]
163
164 mov dx, _scsi_cmd_data_out.io_base + 2[bp] ;; SCSI data write port
165 add dx, #VBOXSCSI_REGISTER_DATA_IN
166 push ds
167 mov ds, ax ;; segment in ds
168
169 rep
170 outsb ;; CX bytes transfered from DS:[SI] to port(DX)
171
172 pop ds
173 pop bp
174ASM_END
175#endif
176
177 /* Now wait for the command to complete. */
178 do
179 {
180 status = inb(io_base+VBOXSCSI_REGISTER_STATUS);
181 } while (status & VBOXSCSI_BUSY);
182
183 return 0;
184}
185
186
187/**
188 * Read sectors from an attached scsi device.
189 *
190 * @returns status code.
191 * @param device_id Id of the SCSI device to read from.
192 * @param count The number of sectors to read.
193 * @param lba The start sector to read from.
194 * @param segment The segment the buffer is in.
195 * @param offset The offset of the buffer.
196 */
197int scsi_read_sectors(device_id, count, lba, segment, offset)
198 Bit8u device_id;
199 Bit16u count, segment, offset;
200 Bit32u lba;
201{
202 Bit8u rc;
203 Bit8u aCDB[10];
204 Bit16u io_base, ebda_seg;
205 Bit8u target_id;
206
207 if (device_id > BX_MAX_SCSI_DEVICES)
208 BX_PANIC("scsi_read_sectors: device_id out of range %d\n", device_id);
209
210 ebda_seg = read_word(0x0040, 0x000E);
211 // Reset count of transferred data
212 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
213 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
214
215 /* Prepare CDB */
216 write_byte(get_SS(), aCDB + 0, SCSI_READ_10);
217 write_byte(get_SS(), aCDB + 1, 0);
218 write_byte(get_SS(), aCDB + 2, (uint8_t)(lba >> 24));
219 write_byte(get_SS(), aCDB + 3, (uint8_t)(lba >> 16));
220 write_byte(get_SS(), aCDB + 4, (uint8_t)(lba >> 8));
221 write_byte(get_SS(), aCDB + 5, (uint8_t)(lba));
222 write_byte(get_SS(), aCDB + 6, 0);
223 write_byte(get_SS(), aCDB + 7, (uint8_t)(count >> 8));
224 write_byte(get_SS(), aCDB + 8, (uint8_t)(count));
225 write_byte(get_SS(), aCDB + 9, 0);
226
227 io_base = read_word(ebda_seg, &EbdaData->scsi.devices[device_id].io_base);
228 target_id = read_byte(ebda_seg, &EbdaData->scsi.devices[device_id].target_id);
229
230 rc = scsi_cmd_data_in(io_base, target_id, get_SS(), aCDB, 10, segment, offset, (count * 512));
231
232 if (!rc)
233 {
234 write_word(ebda_seg, &EbdaData->ata.trsfsectors, count);
235 write_dword(ebda_seg, &EbdaData->ata.trsfbytes, (count * 512));
236 }
237
238 return rc;
239}
240
241/**
242 * Write sectors to an attached scsi device.
243 *
244 * @returns status code.
245 * @param device_id Id of the SCSI device to write to.
246 * @param count The number of sectors to write.
247 * @param lba The start sector to write to.
248 * @param segment The segment the buffer is in.
249 * @param offset The offset of the buffer.
250 */
251int scsi_write_sectors(device_id, count, lba, segment, offset)
252 Bit8u device_id;
253 Bit16u count, segment, offset;
254 Bit32u lba;
255{
256 Bit8u rc;
257 Bit8u aCDB[10];
258 Bit16u io_base, ebda_seg;
259 Bit8u target_id;
260
261 if (device_id > BX_MAX_SCSI_DEVICES)
262 BX_PANIC("scsi_write_sectors: device_id out of range %d\n", device_id);
263
264 ebda_seg = read_word(0x0040, 0x000E);
265 // Reset count of transferred data
266 write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
267 write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
268
269 /* Prepare CDB */
270 write_byte(get_SS(), aCDB + 0, SCSI_WRITE_10);
271 write_byte(get_SS(), aCDB + 1, 0);
272 write_byte(get_SS(), aCDB + 2, (uint8_t)(lba >> 24));
273 write_byte(get_SS(), aCDB + 3, (uint8_t)(lba >> 16));
274 write_byte(get_SS(), aCDB + 4, (uint8_t)(lba >> 8));
275 write_byte(get_SS(), aCDB + 5, (uint8_t)(lba));
276 write_byte(get_SS(), aCDB + 6, 0);
277 write_byte(get_SS(), aCDB + 7, (uint8_t)(count >> 8));
278 write_byte(get_SS(), aCDB + 8, (uint8_t)(count));
279 write_byte(get_SS(), aCDB + 9, 0);
280
281 io_base = read_word(ebda_seg, &EbdaData->scsi.devices[device_id].io_base);
282 target_id = read_byte(ebda_seg, &EbdaData->scsi.devices[device_id].target_id);
283
284 rc = scsi_cmd_data_out(io_base, target_id, get_SS(), aCDB, 10, segment, offset, (count * 512));
285
286 if (!rc)
287 {
288 write_word(ebda_seg, &EbdaData->ata.trsfsectors, count);
289 write_dword(ebda_seg, &EbdaData->ata.trsfbytes, (count * 512));
290 }
291
292 return rc;
293}
294
295/**
296 * Enumerate attached devices.
297 *
298 * @returns nothing.
299 * @param io_base The I/O base port of the controller.
300 */
301void scsi_enumerate_attached_devices(io_base)
302 Bit16u io_base;
303{
304 Bit16u ebda_seg;
305 Bit8u i;
306 Bit8u buffer[0x0200];
307
308 ebda_seg = read_word(0x0040, 0x000E);
309
310 /* Go through target devices. */
311 for (i = 0; i < VBOXSCSI_MAX_DEVICES; i++)
312 {
313 Bit8u rc;
314 Bit8u z;
315 Bit8u aCDB[10];
316
317 write_byte(get_SS(), aCDB + 0, SCSI_INQUIRY);
318 write_byte(get_SS(), aCDB + 1, 0);
319 write_byte(get_SS(), aCDB + 2, 0);
320 write_byte(get_SS(), aCDB + 3, 0);
321 write_byte(get_SS(), aCDB + 4, 5); /* Allocation length. */
322 write_byte(get_SS(), aCDB + 5, 0);
323
324 rc = scsi_cmd_data_in(io_base, i, get_SS(), aCDB, 6, get_SS(), buffer, 5);
325 if (rc != 0)
326 BX_PANIC("scsi_enumerate_attached_devices: SCSI_INQUIRY failed\n");
327
328 /* Check if there is a disk attached. */
329 if ( ((buffer[0] & 0xe0) == 0)
330 && ((buffer[0] & 0x1f) == 0x00))
331 {
332 VBOXSCSI_DEBUG("scsi_enumerate_attached_devices: Disk detected at %d\n", i);
333
334 /* We add the disk only if the maximum is not reached yet. */
335 if (read_byte(ebda_seg, &EbdaData->scsi.hdcount) < BX_MAX_SCSI_DEVICES)
336 {
337 Bit32u sectors, sector_size, cylinders;
338 Bit16u heads, sectors_per_track;
339 Bit8u hdcount, hdcount_scsi;
340
341 /* Issue a read capacity command now. */
342 write_byte(get_SS(), aCDB + 0, SCSI_READ_CAPACITY);
343 memsetb(get_SS(), aCDB + 1, 0, 9);
344
345 rc = scsi_cmd_data_in(io_base, i, get_SS(), aCDB, 10, get_SS(), buffer, 8);
346 if (rc != 0)
347 BX_PANIC("scsi_enumerate_attached_devices: SCSI_READ_CAPACITY failed\n");
348
349 /* Build sector number and size from the buffer. */
350 sectors = ((uint32_t)read_byte(get_SS(), buffer + 0) << 24)
351 | ((uint32_t)read_byte(get_SS(), buffer + 1) << 16)
352 | ((uint32_t)read_byte(get_SS(), buffer + 2) << 8)
353 | ((uint32_t)read_byte(get_SS(), buffer + 3));
354
355 sector_size = ((uint32_t)read_byte(get_SS(), buffer + 4) << 24)
356 | ((uint32_t)read_byte(get_SS(), buffer + 5) << 16)
357 | ((uint32_t)read_byte(get_SS(), buffer + 6) << 8)
358 | ((uint32_t)read_byte(get_SS(), buffer + 7));
359
360 /* We only support the disk if sector size is 512 bytes. */
361 if (sector_size != 512)
362 {
363 /* Leave a log entry. */
364 BX_INFO("Disk %d has an unsupported sector size of %u\n", i, sector_size);
365 continue;
366 }
367
368 /* We need to calculate the geometry for the disk. */
369 if (io_base == BUSLOGIC_ISA_IO_PORT)
370 {
371 /* This is from the BusLogic driver in the Linux kernel. */
372 if (sectors >= 4 * 1024 * 1024)
373 {
374 heads = 255;
375 sectors_per_track = 63;
376 }
377 else if (sectors >= 2 * 1024 * 1024)
378 {
379 heads = 128;
380 sectors_per_track = 32;
381 }
382 else
383 {
384 heads = 64;
385 sectors_per_track = 32;
386 }
387 cylinders = (uint32_t)(sectors / (heads * sectors_per_track));
388 }
389 else if (io_base == LSILOGIC_ISA_IO_PORT || io_base == LSILOGIC_SAS_ISA_IO_PORT)
390 {
391 /* This is from the BusLogic driver in the Linux kernel. */
392 if (sectors >= 4 * 1024 * 1024)
393 {
394 heads = 255;
395 sectors_per_track = 63;
396 }
397 else if (sectors >= 2 * 1024 * 1024)
398 {
399 heads = 128;
400 sectors_per_track = 32;
401 }
402 else
403 {
404 heads = 64;
405 sectors_per_track = 32;
406 }
407 cylinders = (uint32_t)(sectors / (heads * sectors_per_track));
408 }
409 hdcount_scsi = read_byte(ebda_seg, &EbdaData->scsi.hdcount);
410
411 write_word(ebda_seg, &EbdaData->scsi.devices[hdcount_scsi].io_base, io_base);
412 write_byte(ebda_seg, &EbdaData->scsi.devices[hdcount_scsi].target_id, i);
413 write_byte(ebda_seg, &EbdaData->scsi.devices[hdcount_scsi].device_info.type, ATA_TYPE_SCSI);
414 write_byte(ebda_seg, &EbdaData->scsi.devices[hdcount_scsi].device_info.device, ATA_DEVICE_HD);
415 write_byte(ebda_seg, &EbdaData->scsi.devices[hdcount_scsi].device_info.removable, 0);
416 write_byte(ebda_seg, &EbdaData->scsi.devices[hdcount_scsi].device_info.lock, 0);
417 write_byte(ebda_seg, &EbdaData->scsi.devices[hdcount_scsi].device_info.mode, ATA_MODE_PIO16);
418 write_word(ebda_seg, &EbdaData->scsi.devices[hdcount_scsi].device_info.blksize, sector_size);
419 write_byte(ebda_seg, &EbdaData->scsi.devices[hdcount_scsi].device_info.translation, ATA_TRANSLATION_LBA);
420
421 /* Write lchs values. */
422 write_word(ebda_seg, &EbdaData->scsi.devices[hdcount_scsi].device_info.lchs.heads, heads);
423 write_word(ebda_seg, &EbdaData->scsi.devices[hdcount_scsi].device_info.lchs.spt, sectors_per_track);
424 if (cylinders > 1024)
425 write_word(ebda_seg, &EbdaData->scsi.devices[hdcount_scsi].device_info.lchs.cylinders, 1024);
426 else
427 write_word(ebda_seg, &EbdaData->scsi.devices[hdcount_scsi].device_info.lchs.cylinders, (Bit16u)cylinders);
428
429 /* Write pchs values. */
430 write_word(ebda_seg, &EbdaData->scsi.devices[hdcount_scsi].device_info.pchs.heads, heads);
431 write_word(ebda_seg, &EbdaData->scsi.devices[hdcount_scsi].device_info.pchs.spt, sectors_per_track);
432 if (cylinders > 1024)
433 write_word(ebda_seg, &EbdaData->scsi.devices[hdcount_scsi].device_info.pchs.cylinders, 1024);
434 else
435 write_word(ebda_seg, &EbdaData->scsi.devices[hdcount_scsi].device_info.pchs.cylinders, (Bit16u)cylinders);
436
437 write_dword(ebda_seg, &EbdaData->scsi.devices[hdcount_scsi].device_info.sectors, sectors);
438
439 /* Store the id of the disk in the ata hdidmap. */
440 hdcount = read_byte(ebda_seg, &EbdaData->ata.hdcount);
441 write_byte(ebda_seg, &EbdaData->ata.hdidmap[hdcount], hdcount_scsi + BX_MAX_ATA_DEVICES);
442 hdcount++;
443 write_byte(ebda_seg, &EbdaData->ata.hdcount, hdcount);
444
445 hdcount_scsi++;
446 write_byte(ebda_seg, &EbdaData->scsi.hdcount, hdcount_scsi);
447 }
448 else
449 {
450 /* We reached the maximum of SCSI disks we can boot from. We can quit detecting. */
451 break;
452 }
453 }
454 else
455 VBOXSCSI_DEBUG("scsi_enumerate_attached_devices: No disk detected at %d\n", i);
456 }
457}
458
459/**
460 * Init the SCSI driver and detect attached disks.
461 */
462void scsi_init( )
463{
464 Bit8u identifier;
465 Bit16u ebda_seg;
466
467
468 ebda_seg = read_word(0x0040, 0x000E);
469 write_byte(ebda_seg, &EbdaData->scsi.hdcount, 0);
470
471 identifier = 0;
472
473 /* Detect BusLogic adapter. */
474 outb(BUSLOGIC_ISA_IO_PORT+VBOXSCSI_REGISTER_IDENTIFY, 0x55);
475 identifier = inb(BUSLOGIC_ISA_IO_PORT+VBOXSCSI_REGISTER_IDENTIFY);
476
477 if (identifier == 0x55)
478 {
479 /* Detected - Enumerate attached devices. */
480 VBOXSCSI_DEBUG("scsi_init: BusLogic SCSI adapter detected\n");
481 outb(BUSLOGIC_ISA_IO_PORT+VBOXSCSI_REGISTER_RESET, 0);
482 scsi_enumerate_attached_devices(BUSLOGIC_ISA_IO_PORT);
483 }
484 else
485 {
486 VBOXSCSI_DEBUG("scsi_init: BusLogic SCSI adapter not detected\n");
487 }
488
489 /* Detect LsiLogic adapter. */
490 outb(LSILOGIC_ISA_IO_PORT+VBOXSCSI_REGISTER_IDENTIFY, 0x55);
491 identifier = inb(LSILOGIC_ISA_IO_PORT+VBOXSCSI_REGISTER_IDENTIFY);
492
493 if (identifier == 0x55)
494 {
495 /* Detected - Enumerate attached devices. */
496 VBOXSCSI_DEBUG("scsi_init: LsiLogic SCSI adapter detected\n");
497 outb(LSILOGIC_ISA_IO_PORT+VBOXSCSI_REGISTER_RESET, 0);
498 scsi_enumerate_attached_devices(LSILOGIC_ISA_IO_PORT);
499 }
500 else
501 {
502 VBOXSCSI_DEBUG("scsi_init: LsiLogic SCSI adapter not detected\n");
503 }
504
505 /* Detect LsiLogic SAS adapter. */
506 outb(LSILOGIC_SAS_ISA_IO_PORT+VBOXSCSI_REGISTER_IDENTIFY, 0x55);
507 identifier = inb(LSILOGIC_SAS_ISA_IO_PORT+VBOXSCSI_REGISTER_IDENTIFY);
508
509 if (identifier == 0x55)
510 {
511 /* Detected - Enumerate attached devices. */
512 VBOXSCSI_DEBUG("scsi_init: LsiLogic SAS adapter detected\n");
513 outb(LSILOGIC_SAS_ISA_IO_PORT+VBOXSCSI_REGISTER_RESET, 0);
514 scsi_enumerate_attached_devices(LSILOGIC_SAS_ISA_IO_PORT);
515 }
516 else
517 {
518 VBOXSCSI_DEBUG("scsi_init: LsiLogic SAS adapter not detected\n");
519 }
520}
521
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