VirtualBox

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

Last change on this file since 28964 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

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