VirtualBox

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

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

Converted system BIOS to Watcom C.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use