blob: 81b191be6915c64a5ad4427b7b3001e2f5b1fecc [file] [log] [blame]
Kevin O'Connor76977b22010-02-17 01:01:32 -05001// Support for several common scsi like command data block requests
2//
3// Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net>
4// Copyright (C) 2002 MandrakeSoft S.A.
5//
6// This file may be distributed under the terms of the GNU LGPLv3 license.
7
8#include "biosvar.h" // GET_GLOBAL
Kevin O'Connorb3064592012-08-14 21:20:10 -04009#include "util.h" // dprintf
10#include "byteorder.h" // be32_to_cpu
Kevin O'Connor76977b22010-02-17 01:01:32 -050011#include "disk.h" // struct disk_op_s
12#include "blockcmd.h" // struct cdb_request_sense
13#include "ata.h" // atapi_cmd_data
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +010014#include "ahci.h" // atapi_cmd_data
Kevin O'Connor7149fc82010-02-17 23:24:42 -050015#include "usb-msc.h" // usb_cmd_data
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +020016#include "usb-uas.h" // usb_cmd_data
Paolo Bonzinic5c488f2012-02-27 17:22:23 +010017#include "virtio-scsi.h" // virtio_scsi_cmd_data
Gerd Hoffmann9d6bac12012-07-20 10:59:25 +020018#include "lsi-scsi.h" // lsi_scsi_cmd_data
Paolo Bonzini7a39e722012-08-06 13:15:06 +020019#include "esp-scsi.h" // esp_scsi_cmd_data
Hannes Reinecke2df70bf2012-11-13 15:03:31 +010020#include "megasas.h" // megasas_cmd_data
Kevin O'Connor0fedfab2012-03-06 07:18:07 -050021#include "boot.h" // boot_add_hd
Kevin O'Connor76977b22010-02-17 01:01:32 -050022
Kevin O'Connor7149fc82010-02-17 23:24:42 -050023// Route command to low-level handler.
Kevin O'Connor76977b22010-02-17 01:01:32 -050024static int
25cdb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
26{
27 u8 type = GET_GLOBAL(op->drive_g->type);
28 switch (type) {
Kevin O'Connorbd6afe52012-07-21 12:01:12 -040029 case DTYPE_ATA_ATAPI:
Kevin O'Connor76977b22010-02-17 01:01:32 -050030 return atapi_cmd_data(op, cdbcmd, blocksize);
Kevin O'Connor7149fc82010-02-17 23:24:42 -050031 case DTYPE_USB:
32 return usb_cmd_data(op, cdbcmd, blocksize);
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +020033 case DTYPE_UAS:
34 return uas_cmd_data(op, cdbcmd, blocksize);
Kevin O'Connorbd6afe52012-07-21 12:01:12 -040035 case DTYPE_AHCI_ATAPI:
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +010036 return ahci_cmd_data(op, cdbcmd, blocksize);
Paolo Bonzinic5c488f2012-02-27 17:22:23 +010037 case DTYPE_VIRTIO_SCSI:
38 return virtio_scsi_cmd_data(op, cdbcmd, blocksize);
Gerd Hoffmann9d6bac12012-07-20 10:59:25 +020039 case DTYPE_LSI_SCSI:
40 return lsi_scsi_cmd_data(op, cdbcmd, blocksize);
Paolo Bonzini7a39e722012-08-06 13:15:06 +020041 case DTYPE_ESP_SCSI:
42 return esp_scsi_cmd_data(op, cdbcmd, blocksize);
Hannes Reinecke2df70bf2012-11-13 15:03:31 +010043 case DTYPE_MEGASAS:
44 return megasas_cmd_data(op, cdbcmd, blocksize);
Kevin O'Connor76977b22010-02-17 01:01:32 -050045 default:
46 op->count = 0;
47 return DISK_RET_EPARAM;
48 }
49}
50
Kevin O'Connor1fd9a892012-03-14 21:27:45 -040051// Determine if the command is a request to pull data from the device
52int
53cdb_is_read(u8 *cdbcmd, u16 blocksize)
54{
55 return blocksize && cdbcmd[0] != CDB_CMD_WRITE_10;
56}
57
Kevin O'Connor7149fc82010-02-17 23:24:42 -050058int
Paolo Bonzini8c976e32011-11-16 13:02:51 +010059scsi_is_ready(struct disk_op_s *op)
60{
61 dprintf(6, "scsi_is_ready (drive=%p)\n", op->drive_g);
62
63 /* Retry TEST UNIT READY for 5 seconds unless MEDIUM NOT PRESENT is
64 * reported by the device. If the device reports "IN PROGRESS",
65 * 30 seconds is added. */
66 int in_progress = 0;
67 u64 end = calc_future_tsc(5000);
68 for (;;) {
69 if (check_tsc(end)) {
70 dprintf(1, "test unit ready failed\n");
71 return -1;
72 }
73
74 int ret = cdb_test_unit_ready(op);
75 if (!ret)
76 // Success
77 break;
78
79 struct cdbres_request_sense sense;
80 ret = cdb_get_sense(op, &sense);
81 if (ret)
82 // Error - retry.
83 continue;
84
85 // Sense succeeded.
86 if (sense.asc == 0x3a) { /* MEDIUM NOT PRESENT */
87 dprintf(1, "Device reports MEDIUM NOT PRESENT\n");
88 return -1;
89 }
90
91 if (sense.asc == 0x04 && sense.ascq == 0x01 && !in_progress) {
92 /* IN PROGRESS OF BECOMING READY */
93 printf("Waiting for device to detect medium... ");
94 /* Allow 30 seconds more */
95 end = calc_future_tsc(30000);
96 in_progress = 1;
97 }
98 }
99 return 0;
100}
101
Kevin O'Connor279dcb12012-02-18 11:02:27 -0500102// Validate drive, find block size / sector count, and register drive.
Paolo Bonzinided04a32011-11-17 10:23:02 +0100103int
Kevin O'Connor279dcb12012-02-18 11:02:27 -0500104scsi_init_drive(struct drive_s *drive, const char *s, int prio)
Paolo Bonzinided04a32011-11-17 10:23:02 +0100105{
Paolo Bonzinided04a32011-11-17 10:23:02 +0100106 struct disk_op_s dop;
107 memset(&dop, 0, sizeof(dop));
108 dop.drive_g = drive;
109 struct cdbres_inquiry data;
110 int ret = cdb_get_inquiry(&dop, &data);
111 if (ret)
112 return ret;
113 char vendor[sizeof(data.vendor)+1], product[sizeof(data.product)+1];
114 char rev[sizeof(data.rev)+1];
115 strtcpy(vendor, data.vendor, sizeof(vendor));
116 nullTrailingSpace(vendor);
117 strtcpy(product, data.product, sizeof(product));
118 nullTrailingSpace(product);
119 strtcpy(rev, data.rev, sizeof(rev));
120 nullTrailingSpace(rev);
Kevin O'Connor279dcb12012-02-18 11:02:27 -0500121 int pdt = data.pdt & 0x1f;
Paolo Bonzinided04a32011-11-17 10:23:02 +0100122 int removable = !!(data.removable & 0x80);
123 dprintf(1, "%s vendor='%s' product='%s' rev='%s' type=%d removable=%d\n"
Kevin O'Connor279dcb12012-02-18 11:02:27 -0500124 , s, vendor, product, rev, pdt, removable);
Paolo Bonzinided04a32011-11-17 10:23:02 +0100125 drive->removable = removable;
126
Kevin O'Connor279dcb12012-02-18 11:02:27 -0500127 if (pdt == SCSI_TYPE_CDROM) {
Paolo Bonzinided04a32011-11-17 10:23:02 +0100128 drive->blksize = CDROM_SECTOR_SIZE;
129 drive->sectors = (u64)-1;
130
Kevin O'Connor279dcb12012-02-18 11:02:27 -0500131 char *desc = znprintf(MAXDESCSIZE, "DVD/CD [%s Drive %s %s %s]"
Paolo Bonzinided04a32011-11-17 10:23:02 +0100132 , s, vendor, product, rev);
Kevin O'Connor279dcb12012-02-18 11:02:27 -0500133 boot_add_cd(drive, desc, prio);
Paolo Bonzinided04a32011-11-17 10:23:02 +0100134 return 0;
135 }
136
137 ret = scsi_is_ready(&dop);
138 if (ret) {
139 dprintf(1, "scsi_is_ready returned %d\n", ret);
140 return ret;
141 }
142
143 struct cdbres_read_capacity capdata;
144 ret = cdb_read_capacity(&dop, &capdata);
145 if (ret)
146 return ret;
147
148 // READ CAPACITY returns the address of the last block.
149 // We do not bother with READ CAPACITY(16) because BIOS does not support
150 // 64-bit LBA anyway.
Kevin O'Connorb3064592012-08-14 21:20:10 -0400151 drive->blksize = be32_to_cpu(capdata.blksize);
Kevin O'Connor279dcb12012-02-18 11:02:27 -0500152 if (drive->blksize != DISK_SECTOR_SIZE) {
153 dprintf(1, "%s: unsupported block size %d\n", s, drive->blksize);
154 return -1;
155 }
Kevin O'Connorb3064592012-08-14 21:20:10 -0400156 drive->sectors = (u64)be32_to_cpu(capdata.sectors) + 1;
Paolo Bonzinided04a32011-11-17 10:23:02 +0100157 dprintf(1, "%s blksize=%d sectors=%d\n"
158 , s, drive->blksize, (unsigned)drive->sectors);
159
Paolo Bonzinicb721712012-03-05 12:29:12 +0100160 // We do not recover from USB stalls, so try to be safe and avoid
161 // sending the command if the (obsolete, but still provided by QEMU)
162 // fixed disk geometry page may not be supported.
163 //
164 // We could also send the command only to small disks (e.g. <504MiB)
165 // but some old USB keys only support a very small subset of SCSI which
166 // does not even include the MODE SENSE command!
167 //
Paolo Bonzini5a023062012-08-02 18:22:44 +0200168 if (! CONFIG_COREBOOT && memcmp(vendor, "QEMU", 5) == 0) {
Paolo Bonzinicb721712012-03-05 12:29:12 +0100169 struct cdbres_mode_sense_geom geomdata;
170 ret = cdb_mode_sense_geom(&dop, &geomdata);
171 if (ret == 0) {
172 u32 cylinders;
173 cylinders = geomdata.cyl[0] << 16;
174 cylinders |= geomdata.cyl[1] << 8;
175 cylinders |= geomdata.cyl[2];
176 if (cylinders && geomdata.heads &&
177 drive->sectors <= 0xFFFFFFFFULL &&
178 ((u32)drive->sectors % (geomdata.heads * cylinders) == 0)) {
179 drive->pchs.cylinders = cylinders;
180 drive->pchs.heads = geomdata.heads;
181 drive->pchs.spt = (u32)drive->sectors / (geomdata.heads * cylinders);
182 }
Paolo Bonzini000e0842011-11-16 13:02:54 +0100183 }
184 }
185
Kevin O'Connor279dcb12012-02-18 11:02:27 -0500186 char *desc = znprintf(MAXDESCSIZE, "%s Drive %s %s %s"
187 , s, vendor, product, rev);
188 boot_add_hd(drive, desc, prio);
Paolo Bonzinided04a32011-11-17 10:23:02 +0100189 return 0;
190}
191
Paolo Bonzini8c976e32011-11-16 13:02:51 +0100192int
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500193cdb_get_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data)
194{
195 struct cdb_request_sense cmd;
196 memset(&cmd, 0, sizeof(cmd));
197 cmd.command = CDB_CMD_INQUIRY;
198 cmd.length = sizeof(*data);
199 op->count = 1;
200 op->buf_fl = data;
201 return cdb_cmd_data(op, &cmd, sizeof(*data));
202}
203
Kevin O'Connor76977b22010-02-17 01:01:32 -0500204// Request SENSE
205int
206cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data)
207{
208 struct cdb_request_sense cmd;
209 memset(&cmd, 0, sizeof(cmd));
210 cmd.command = CDB_CMD_REQUEST_SENSE;
211 cmd.length = sizeof(*data);
212 op->count = 1;
213 op->buf_fl = data;
214 return cdb_cmd_data(op, &cmd, sizeof(*data));
215}
216
Paolo Bonzini00823742011-11-16 13:02:42 +0100217// Test unit ready
218int
219cdb_test_unit_ready(struct disk_op_s *op)
220{
221 struct cdb_request_sense cmd;
222 memset(&cmd, 0, sizeof(cmd));
223 cmd.command = CDB_CMD_TEST_UNIT_READY;
224 op->count = 0;
225 op->buf_fl = NULL;
226 return cdb_cmd_data(op, &cmd, 0);
227}
228
Kevin O'Connor76977b22010-02-17 01:01:32 -0500229// Request capacity
230int
231cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data)
232{
233 struct cdb_read_capacity cmd;
234 memset(&cmd, 0, sizeof(cmd));
235 cmd.command = CDB_CMD_READ_CAPACITY;
236 op->count = 1;
237 op->buf_fl = data;
238 return cdb_cmd_data(op, &cmd, sizeof(*data));
239}
240
Paolo Bonzini000e0842011-11-16 13:02:54 +0100241// Mode sense, geometry page.
242int
243cdb_mode_sense_geom(struct disk_op_s *op, struct cdbres_mode_sense_geom *data)
244{
245 struct cdb_mode_sense cmd;
246 memset(&cmd, 0, sizeof(cmd));
247 cmd.command = CDB_CMD_MODE_SENSE;
248 cmd.flags = 8; /* DBD */
249 cmd.page = MODE_PAGE_HD_GEOMETRY;
Kevin O'Connorb3064592012-08-14 21:20:10 -0400250 cmd.count = cpu_to_be16(sizeof(*data));
Paolo Bonzini000e0842011-11-16 13:02:54 +0100251 op->count = 1;
252 op->buf_fl = data;
253 return cdb_cmd_data(op, &cmd, sizeof(*data));
254}
255
Kevin O'Connor76977b22010-02-17 01:01:32 -0500256// Read sectors.
257int
258cdb_read(struct disk_op_s *op)
259{
260 struct cdb_rwdata_10 cmd;
261 memset(&cmd, 0, sizeof(cmd));
262 cmd.command = CDB_CMD_READ_10;
Kevin O'Connorb3064592012-08-14 21:20:10 -0400263 cmd.lba = cpu_to_be32(op->lba);
264 cmd.count = cpu_to_be16(op->count);
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500265 return cdb_cmd_data(op, &cmd, GET_GLOBAL(op->drive_g->blksize));
Kevin O'Connor76977b22010-02-17 01:01:32 -0500266}
Paolo Bonziniddb8ceb2011-11-16 13:02:47 +0100267
268// Write sectors.
269int
270cdb_write(struct disk_op_s *op)
271{
272 struct cdb_rwdata_10 cmd;
273 memset(&cmd, 0, sizeof(cmd));
274 cmd.command = CDB_CMD_WRITE_10;
Kevin O'Connorb3064592012-08-14 21:20:10 -0400275 cmd.lba = cpu_to_be32(op->lba);
276 cmd.count = cpu_to_be16(op->count);
Paolo Bonziniddb8ceb2011-11-16 13:02:47 +0100277 return cdb_cmd_data(op, &cmd, GET_GLOBAL(op->drive_g->blksize));
278}