VirtualBox

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

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

Reshuffled things some more.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.3 KB
Line 
1/* $Id: scsi.c 39355 2011-11-17 17:25:25Z 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//#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/**
152 * Read sectors from an attached scsi device.
153 *
154 * @returns status code.
155 * @param device_id Id of the SCSI device to read from.
156 * @param count The number of sectors to read.
157 * @param lba The start sector to read from.
158 * @param buffer A far pointer to the buffer.
159 */
160int scsi_read_sectors(uint8_t device_id, uint16_t count, uint32_t lba, void __far *buffer)
161{
162 uint8_t rc;
163 cdb_rw10 cdb;
164 uint16_t io_base;
165 uint8_t target_id;
166 bio_dsk_t __far *bios_dsk;
167
168 if (device_id > BX_MAX_SCSI_DEVICES)
169 BX_PANIC("scsi_read_sectors: device_id out of range %d\n", device_id);
170
171 /* Prepare a CDB. */
172 cdb.command = SCSI_READ_10;
173 cdb.lba = swap_32(lba);
174 cdb.pad1 = 0;
175 cdb.nsect = swap_16(count);
176 cdb.pad2 = 0;
177
178 bios_dsk = read_word(0x0040, 0x000E) :> &EbdaData->bdisk;
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, buffer, (count * 512));
184
185 if (!rc)
186 {
187 bios_dsk->drqp.trsfsectors = count;
188 bios_dsk->drqp.trsfbytes = count * 512;
189 }
190
191 return rc;
192}
193
194/**
195 * Write sectors to an attached scsi device.
196 *
197 * @returns status code.
198 * @param device_id Id of the SCSI device to write to.
199 * @param count The number of sectors to write.
200 * @param lba The start sector to write to.
201 * @param buffer A far pointer to the buffer.
202 */
203int scsi_write_sectors(uint8_t device_id, uint16_t count, uint32_t lba, void __far *buffer)
204{
205 uint8_t rc;
206 cdb_rw10 cdb;
207 uint16_t io_base;
208 uint8_t target_id;
209 bio_dsk_t __far *bios_dsk;
210
211 if (device_id > BX_MAX_SCSI_DEVICES)
212 BX_PANIC("scsi_write_sectors: device_id out of range %d\n", device_id);
213
214 /* Prepare a CDB. */
215 cdb.command = SCSI_WRITE_10;
216 cdb.lba = swap_32(lba);
217 cdb.pad1 = 0;
218 cdb.nsect = swap_16(count);
219 cdb.pad2 = 0;
220
221 bios_dsk = read_word(0x0040, 0x000E) :> &EbdaData->bdisk;
222
223 io_base = bios_dsk->scsidev[device_id].io_base;
224 target_id = bios_dsk->scsidev[device_id].target_id;
225
226 rc = scsi_cmd_data_out(io_base, target_id, (void __far *)&cdb, 10, buffer, (count * 512));
227
228 if (!rc)
229 {
230 bios_dsk->drqp.trsfsectors = count;
231 bios_dsk->drqp.trsfbytes = (count * 512);
232 }
233
234 return rc;
235}
236
237/**
238 * Enumerate attached devices.
239 *
240 * @returns nothing.
241 * @param io_base The I/O base port of the controller.
242 */
243void scsi_enumerate_attached_devices(uint16_t io_base)
244{
245 int i;
246 uint8_t buffer[0x0200];
247 bio_dsk_t __far *bios_dsk;
248
249 bios_dsk = read_word(0x0040, 0x000E) :> &EbdaData->bdisk;
250
251 /* Go through target devices. */
252 for (i = 0; i < VBSCSI_MAX_DEVICES; i++)
253 {
254 uint8_t rc;
255 uint8_t aCDB[10];
256
257 aCDB[0] = SCSI_INQUIRY;
258 aCDB[1] = 0;
259 aCDB[2] = 0;
260 aCDB[3] = 0;
261 aCDB[4] = 5; /* Allocation length. */
262 aCDB[5] = 0;
263
264 rc = scsi_cmd_data_in(io_base, i, aCDB, 6, buffer, 5);
265 if (rc != 0)
266 BX_PANIC("scsi_enumerate_attached_devices: SCSI_INQUIRY failed\n");
267
268 /* Check if there is a disk attached. */
269 if ( ((buffer[0] & 0xe0) == 0)
270 && ((buffer[0] & 0x1f) == 0x00))
271 {
272 VBSCSI_DEBUG("scsi_enumerate_attached_devices: Disk detected at %d\n", i);
273
274 /* We add the disk only if the maximum is not reached yet. */
275 if (bios_dsk->scsi_hdcount < BX_MAX_SCSI_DEVICES)
276 {
277 uint32_t sectors, sector_size, cylinders;
278 uint16_t heads, sectors_per_track;
279 uint8_t hdcount, hdcount_scsi, hd_index;
280
281 /* Issue a read capacity command now. */
282 _fmemset(aCDB, 0, sizeof(aCDB));
283 aCDB[0] = SCSI_READ_CAPACITY;
284
285 rc = scsi_cmd_data_in(io_base, i, aCDB, 10, buffer, 8);
286 if (rc != 0)
287 BX_PANIC("scsi_enumerate_attached_devices: SCSI_READ_CAPACITY failed\n");
288
289 /* Build sector number and size from the buffer. */
290 //@todo: byte swapping for dword sized items should be farmed out...
291 sectors = ((uint32_t)buffer[0] << 24)
292 | ((uint32_t)buffer[1] << 16)
293 | ((uint32_t)buffer[2] << 8)
294 | ((uint32_t)buffer[3]);
295
296 sector_size = ((uint32_t)buffer[4] << 24)
297 | ((uint32_t)buffer[5] << 16)
298 | ((uint32_t)buffer[6] << 8)
299 | ((uint32_t)buffer[7]);
300
301 /* We only support the disk if sector size is 512 bytes. */
302 if (sector_size != 512)
303 {
304 /* Leave a log entry. */
305 BX_INFO("Disk %d has an unsupported sector size of %u\n", i, sector_size);
306 continue;
307 }
308
309 /* We need to calculate the geometry for the disk. From
310 * the BusLogic driver in the Linux kernel.
311 */
312 if (sectors >= 4 * 1024 * 1024)
313 {
314 heads = 255;
315 sectors_per_track = 63;
316 }
317 else if (sectors >= 2 * 1024 * 1024)
318 {
319 heads = 128;
320 sectors_per_track = 32;
321 }
322 else
323 {
324 heads = 64;
325 sectors_per_track = 32;
326 }
327 cylinders = (uint32_t)(sectors / (heads * sectors_per_track));
328 hdcount_scsi = bios_dsk->scsi_hdcount;
329
330 /* Calculate index into the generic disk table. */
331 hd_index = hdcount_scsi + BX_MAX_ATA_DEVICES;
332
333 bios_dsk->scsidev[hdcount_scsi].io_base = io_base;
334 bios_dsk->scsidev[hdcount_scsi].target_id = i;
335 bios_dsk->devices[hd_index].type = ATA_TYPE_SCSI;
336 bios_dsk->devices[hd_index].device = ATA_DEVICE_HD;
337 bios_dsk->devices[hd_index].removable = 0;
338 bios_dsk->devices[hd_index].lock = 0;
339 bios_dsk->devices[hd_index].mode = ATA_MODE_PIO16;
340 bios_dsk->devices[hd_index].blksize = sector_size;
341 bios_dsk->devices[hd_index].translation = ATA_TRANSLATION_LBA;
342
343 /* Write LCHS values. */
344 bios_dsk->devices[hd_index].lchs.heads = heads;
345 bios_dsk->devices[hd_index].lchs.spt = sectors_per_track;
346 if (cylinders > 1024)
347 bios_dsk->devices[hd_index].lchs.cylinders = 1024;
348 else
349 bios_dsk->devices[hd_index].lchs.cylinders = (uint16_t)cylinders;
350
351 /* Write PCHS values. */
352 bios_dsk->devices[hd_index].pchs.heads = heads;
353 bios_dsk->devices[hd_index].pchs.spt = sectors_per_track;
354 if (cylinders > 1024)
355 bios_dsk->devices[hd_index].pchs.cylinders = 1024;
356 else
357 bios_dsk->devices[hd_index].pchs.cylinders = (uint16_t)cylinders;
358
359 bios_dsk->devices[hd_index].sectors = sectors;
360
361 /* Store the id of the disk in the ata hdidmap. */
362 hdcount = bios_dsk->hdcount;
363 bios_dsk->hdidmap[hdcount] = hdcount_scsi + BX_MAX_ATA_DEVICES;
364 hdcount++;
365 bios_dsk->hdcount = hdcount;
366
367 /* Update hdcount in the BDA. */
368 hdcount = read_byte(0x40, 0x75);
369 hdcount++;
370 write_byte(0x40, 0x75, hdcount);
371
372 hdcount_scsi++;
373 bios_dsk->scsi_hdcount = hdcount_scsi;
374 }
375 else
376 {
377 /* We reached the maximum of SCSI disks we can boot from. We can quit detecting. */
378 break;
379 }
380 }
381 else
382 VBSCSI_DEBUG("scsi_enumerate_attached_devices: No disk detected at %d\n", i);
383 }
384}
385
386/**
387 * Init the SCSI driver and detect attached disks.
388 */
389void BIOSCALL scsi_init(void)
390{
391 uint8_t identifier;
392 bio_dsk_t __far *bios_dsk;
393
394 bios_dsk = read_word(0x0040, 0x000E) :> &EbdaData->bdisk;
395
396 bios_dsk->scsi_hdcount = 0;
397
398 identifier = 0;
399
400 /* Detect BusLogic adapter. */
401 outb(BUSLOGIC_ISA_IO_PORT+VBSCSI_REGISTER_IDENTIFY, 0x55);
402 identifier = inb(BUSLOGIC_ISA_IO_PORT+VBSCSI_REGISTER_IDENTIFY);
403
404 if (identifier == 0x55)
405 {
406 /* Detected - Enumerate attached devices. */
407 VBSCSI_DEBUG("scsi_init: BusLogic SCSI adapter detected\n");
408 outb(BUSLOGIC_ISA_IO_PORT+VBSCSI_REGISTER_RESET, 0);
409 scsi_enumerate_attached_devices(BUSLOGIC_ISA_IO_PORT);
410 }
411 else
412 {
413 VBSCSI_DEBUG("scsi_init: BusLogic SCSI adapter not detected\n");
414 }
415
416 /* Detect LsiLogic adapter. */
417 outb(LSILOGIC_ISA_IO_PORT+VBSCSI_REGISTER_IDENTIFY, 0x55);
418 identifier = inb(LSILOGIC_ISA_IO_PORT+VBSCSI_REGISTER_IDENTIFY);
419
420 if (identifier == 0x55)
421 {
422 /* Detected - Enumerate attached devices. */
423 VBSCSI_DEBUG("scsi_init: LSI Logic SCSI adapter detected\n");
424 outb(LSILOGIC_ISA_IO_PORT+VBSCSI_REGISTER_RESET, 0);
425 scsi_enumerate_attached_devices(LSILOGIC_ISA_IO_PORT);
426 }
427 else
428 {
429 VBSCSI_DEBUG("scsi_init: LSI Logic SCSI adapter not detected\n");
430 }
431
432 /* Detect LsiLogic SAS adapter. */
433 outb(LSILOGIC_SAS_ISA_IO_PORT+VBSCSI_REGISTER_IDENTIFY, 0x55);
434 identifier = inb(LSILOGIC_SAS_ISA_IO_PORT+VBSCSI_REGISTER_IDENTIFY);
435
436 if (identifier == 0x55)
437 {
438 /* Detected - Enumerate attached devices. */
439 VBSCSI_DEBUG("scsi_init: LSI Logic SAS adapter detected\n");
440 outb(LSILOGIC_SAS_ISA_IO_PORT+VBSCSI_REGISTER_RESET, 0);
441 scsi_enumerate_attached_devices(LSILOGIC_SAS_ISA_IO_PORT);
442 }
443 else
444 {
445 VBSCSI_DEBUG("scsi_init: LSI Logic SAS adapter not detected\n");
446 }
447}
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