blob: 0725b46db6b464016054fe86e51d0c9fee9cc325 [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
Kevin O'Connor1902c942013-10-26 11:48:06 -04008#include "biosvar.h" // GET_GLOBALFLAT
Kevin O'Connor135f3f62013-09-14 23:57:26 -04009#include "block.h" // struct disk_op_s
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040010#include "blockcmd.h" // struct cdb_request_sense
Kevin O'Connorb3064592012-08-14 21:20:10 -040011#include "byteorder.h" // be32_to_cpu
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040012#include "output.h" // dprintf
Kevin O'Connor135f3f62013-09-14 23:57:26 -040013#include "std/disk.h" // DISK_RET_EPARAM
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040014#include "string.h" // memset
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040015#include "util.h" // timer_calc
Kevin O'Connor76977b22010-02-17 01:01:32 -050016
Kevin O'Connorfeeb1c22014-12-29 09:40:46 -050017
18/****************************************************************
19 * Low level command requests
20 ****************************************************************/
21
Kevin O'Connoref102af2014-12-29 09:58:48 -050022static int
Kevin O'Connorfeeb1c22014-12-29 09:40:46 -050023cdb_get_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data)
24{
25 struct cdb_request_sense cmd;
26 memset(&cmd, 0, sizeof(cmd));
27 cmd.command = CDB_CMD_INQUIRY;
28 cmd.length = sizeof(*data);
Kevin O'Connor4dbe8292015-07-07 09:53:54 -040029 op->command = CMD_SCSI;
Kevin O'Connorfeeb1c22014-12-29 09:40:46 -050030 op->count = 1;
31 op->buf_fl = data;
Kevin O'Connor4dbe8292015-07-07 09:53:54 -040032 op->cdbcmd = &cmd;
33 op->blocksize = sizeof(*data);
34 return process_op(op);
Kevin O'Connorfeeb1c22014-12-29 09:40:46 -050035}
36
37// Request SENSE
Kevin O'Connoref102af2014-12-29 09:58:48 -050038static int
Kevin O'Connorfeeb1c22014-12-29 09:40:46 -050039cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data)
40{
41 struct cdb_request_sense cmd;
42 memset(&cmd, 0, sizeof(cmd));
43 cmd.command = CDB_CMD_REQUEST_SENSE;
44 cmd.length = sizeof(*data);
Kevin O'Connor4dbe8292015-07-07 09:53:54 -040045 op->command = CMD_SCSI;
Kevin O'Connorfeeb1c22014-12-29 09:40:46 -050046 op->count = 1;
47 op->buf_fl = data;
Kevin O'Connor4dbe8292015-07-07 09:53:54 -040048 op->cdbcmd = &cmd;
49 op->blocksize = sizeof(*data);
50 return process_op(op);
Kevin O'Connorfeeb1c22014-12-29 09:40:46 -050051}
52
53// Test unit ready
Kevin O'Connoref102af2014-12-29 09:58:48 -050054static int
Kevin O'Connorfeeb1c22014-12-29 09:40:46 -050055cdb_test_unit_ready(struct disk_op_s *op)
56{
57 struct cdb_request_sense cmd;
58 memset(&cmd, 0, sizeof(cmd));
59 cmd.command = CDB_CMD_TEST_UNIT_READY;
Kevin O'Connor4dbe8292015-07-07 09:53:54 -040060 op->command = CMD_SCSI;
Kevin O'Connorfeeb1c22014-12-29 09:40:46 -050061 op->count = 0;
62 op->buf_fl = NULL;
Kevin O'Connor4dbe8292015-07-07 09:53:54 -040063 op->cdbcmd = &cmd;
64 op->blocksize = 0;
65 return process_op(op);
Kevin O'Connorfeeb1c22014-12-29 09:40:46 -050066}
67
68// Request capacity
Kevin O'Connoref102af2014-12-29 09:58:48 -050069static int
Kevin O'Connorfeeb1c22014-12-29 09:40:46 -050070cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data)
71{
72 struct cdb_read_capacity cmd;
73 memset(&cmd, 0, sizeof(cmd));
74 cmd.command = CDB_CMD_READ_CAPACITY;
Kevin O'Connor4dbe8292015-07-07 09:53:54 -040075 op->command = CMD_SCSI;
Kevin O'Connorfeeb1c22014-12-29 09:40:46 -050076 op->count = 1;
77 op->buf_fl = data;
Kevin O'Connor4dbe8292015-07-07 09:53:54 -040078 op->cdbcmd = &cmd;
79 op->blocksize = sizeof(*data);
80 return process_op(op);
Kevin O'Connorfeeb1c22014-12-29 09:40:46 -050081}
82
83// Mode sense, geometry page.
Kevin O'Connoref102af2014-12-29 09:58:48 -050084static int
Kevin O'Connorfeeb1c22014-12-29 09:40:46 -050085cdb_mode_sense_geom(struct disk_op_s *op, struct cdbres_mode_sense_geom *data)
86{
87 struct cdb_mode_sense cmd;
88 memset(&cmd, 0, sizeof(cmd));
89 cmd.command = CDB_CMD_MODE_SENSE;
90 cmd.flags = 8; /* DBD */
91 cmd.page = MODE_PAGE_HD_GEOMETRY;
92 cmd.count = cpu_to_be16(sizeof(*data));
Kevin O'Connor4dbe8292015-07-07 09:53:54 -040093 op->command = CMD_SCSI;
Kevin O'Connorfeeb1c22014-12-29 09:40:46 -050094 op->count = 1;
95 op->buf_fl = data;
Kevin O'Connor4dbe8292015-07-07 09:53:54 -040096 op->cdbcmd = &cmd;
97 op->blocksize = sizeof(*data);
98 return process_op(op);
Kevin O'Connorfeeb1c22014-12-29 09:40:46 -050099}
100
Kevin O'Connorfeeb1c22014-12-29 09:40:46 -0500101
102/****************************************************************
103 * Main SCSI commands
104 ****************************************************************/
105
Kevin O'Connorfb251632015-07-07 10:53:12 -0400106// Create a scsi command request from a disk_op_s request
Kevin O'Connorc7fa7892015-07-07 08:35:51 -0400107int
Kevin O'Connorfb251632015-07-07 10:53:12 -0400108scsi_fill_cmd(struct disk_op_s *op, void *cdbcmd, int maxcdb)
Kevin O'Connorf0a22eb2014-12-29 09:45:15 -0500109{
110 switch (op->command) {
111 case CMD_READ:
Kevin O'Connorfb251632015-07-07 10:53:12 -0400112 case CMD_WRITE: ;
113 struct cdb_rwdata_10 *cmd = cdbcmd;
114 memset(cmd, 0, maxcdb);
115 cmd->command = (op->command == CMD_READ ? CDB_CMD_READ_10
116 : CDB_CMD_WRITE_10);
117 cmd->lba = cpu_to_be32(op->lba);
118 cmd->count = cpu_to_be16(op->count);
119 return GET_GLOBALFLAT(op->drive_gf->blksize);
Kevin O'Connor4dbe8292015-07-07 09:53:54 -0400120 case CMD_SCSI:
Kevin O'Connorfb251632015-07-07 10:53:12 -0400121 memcpy(cdbcmd, op->cdbcmd, maxcdb);
122 return op->blocksize;
Kevin O'Connorf0a22eb2014-12-29 09:45:15 -0500123 default:
Kevin O'Connorfb251632015-07-07 10:53:12 -0400124 return -1;
Kevin O'Connorf0a22eb2014-12-29 09:45:15 -0500125 }
126}
127
Kevin O'Connor5dcd1ee2015-07-07 14:43:01 -0400128// Determine if the command is a request to pull data from the device
129int
130scsi_is_read(struct disk_op_s *op)
131{
132 return op->command == CMD_READ || (op->command == CMD_SCSI && op->blocksize);
133}
134
135// Check if a SCSI device is ready to receive commands
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500136int
Paolo Bonzini8c976e32011-11-16 13:02:51 +0100137scsi_is_ready(struct disk_op_s *op)
138{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400139 dprintf(6, "scsi_is_ready (drive=%p)\n", op->drive_gf);
Paolo Bonzini8c976e32011-11-16 13:02:51 +0100140
141 /* Retry TEST UNIT READY for 5 seconds unless MEDIUM NOT PRESENT is
142 * reported by the device. If the device reports "IN PROGRESS",
143 * 30 seconds is added. */
144 int in_progress = 0;
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400145 u32 end = timer_calc(5000);
Paolo Bonzini8c976e32011-11-16 13:02:51 +0100146 for (;;) {
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400147 if (timer_check(end)) {
Paolo Bonzini8c976e32011-11-16 13:02:51 +0100148 dprintf(1, "test unit ready failed\n");
149 return -1;
150 }
151
152 int ret = cdb_test_unit_ready(op);
153 if (!ret)
154 // Success
155 break;
156
157 struct cdbres_request_sense sense;
158 ret = cdb_get_sense(op, &sense);
159 if (ret)
160 // Error - retry.
161 continue;
162
163 // Sense succeeded.
164 if (sense.asc == 0x3a) { /* MEDIUM NOT PRESENT */
165 dprintf(1, "Device reports MEDIUM NOT PRESENT\n");
166 return -1;
167 }
168
169 if (sense.asc == 0x04 && sense.ascq == 0x01 && !in_progress) {
170 /* IN PROGRESS OF BECOMING READY */
Kevin O'Connor82f32792015-12-23 15:37:51 -0500171 dprintf(1, "Waiting for device to detect medium... ");
Paolo Bonzini8c976e32011-11-16 13:02:51 +0100172 /* Allow 30 seconds more */
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400173 end = timer_calc(30000);
Paolo Bonzini8c976e32011-11-16 13:02:51 +0100174 in_progress = 1;
175 }
176 }
177 return 0;
178}
179
Kevin O'Connor279dcb12012-02-18 11:02:27 -0500180// Validate drive, find block size / sector count, and register drive.
Paolo Bonzinided04a32011-11-17 10:23:02 +0100181int
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500182scsi_drive_setup(struct drive_s *drive, const char *s, int prio)
Paolo Bonzinided04a32011-11-17 10:23:02 +0100183{
Paolo Bonzinided04a32011-11-17 10:23:02 +0100184 struct disk_op_s dop;
185 memset(&dop, 0, sizeof(dop));
Kevin O'Connor1902c942013-10-26 11:48:06 -0400186 dop.drive_gf = drive;
Paolo Bonzinided04a32011-11-17 10:23:02 +0100187 struct cdbres_inquiry data;
188 int ret = cdb_get_inquiry(&dop, &data);
189 if (ret)
190 return ret;
191 char vendor[sizeof(data.vendor)+1], product[sizeof(data.product)+1];
192 char rev[sizeof(data.rev)+1];
193 strtcpy(vendor, data.vendor, sizeof(vendor));
194 nullTrailingSpace(vendor);
195 strtcpy(product, data.product, sizeof(product));
196 nullTrailingSpace(product);
197 strtcpy(rev, data.rev, sizeof(rev));
198 nullTrailingSpace(rev);
Kevin O'Connor279dcb12012-02-18 11:02:27 -0500199 int pdt = data.pdt & 0x1f;
Paolo Bonzinided04a32011-11-17 10:23:02 +0100200 int removable = !!(data.removable & 0x80);
201 dprintf(1, "%s vendor='%s' product='%s' rev='%s' type=%d removable=%d\n"
Kevin O'Connor279dcb12012-02-18 11:02:27 -0500202 , s, vendor, product, rev, pdt, removable);
Paolo Bonzinided04a32011-11-17 10:23:02 +0100203 drive->removable = removable;
204
Kevin O'Connor279dcb12012-02-18 11:02:27 -0500205 if (pdt == SCSI_TYPE_CDROM) {
Paolo Bonzinided04a32011-11-17 10:23:02 +0100206 drive->blksize = CDROM_SECTOR_SIZE;
207 drive->sectors = (u64)-1;
208
Kevin O'Connor279dcb12012-02-18 11:02:27 -0500209 char *desc = znprintf(MAXDESCSIZE, "DVD/CD [%s Drive %s %s %s]"
Paolo Bonzinided04a32011-11-17 10:23:02 +0100210 , s, vendor, product, rev);
Kevin O'Connor279dcb12012-02-18 11:02:27 -0500211 boot_add_cd(drive, desc, prio);
Paolo Bonzinided04a32011-11-17 10:23:02 +0100212 return 0;
213 }
214
215 ret = scsi_is_ready(&dop);
216 if (ret) {
217 dprintf(1, "scsi_is_ready returned %d\n", ret);
218 return ret;
219 }
220
221 struct cdbres_read_capacity capdata;
222 ret = cdb_read_capacity(&dop, &capdata);
223 if (ret)
224 return ret;
225
226 // READ CAPACITY returns the address of the last block.
227 // We do not bother with READ CAPACITY(16) because BIOS does not support
228 // 64-bit LBA anyway.
Kevin O'Connorb3064592012-08-14 21:20:10 -0400229 drive->blksize = be32_to_cpu(capdata.blksize);
Kevin O'Connor279dcb12012-02-18 11:02:27 -0500230 if (drive->blksize != DISK_SECTOR_SIZE) {
231 dprintf(1, "%s: unsupported block size %d\n", s, drive->blksize);
232 return -1;
233 }
Kevin O'Connorb3064592012-08-14 21:20:10 -0400234 drive->sectors = (u64)be32_to_cpu(capdata.sectors) + 1;
Paolo Bonzinided04a32011-11-17 10:23:02 +0100235 dprintf(1, "%s blksize=%d sectors=%d\n"
236 , s, drive->blksize, (unsigned)drive->sectors);
237
Paolo Bonzinicb721712012-03-05 12:29:12 +0100238 // We do not recover from USB stalls, so try to be safe and avoid
239 // sending the command if the (obsolete, but still provided by QEMU)
240 // fixed disk geometry page may not be supported.
241 //
242 // We could also send the command only to small disks (e.g. <504MiB)
243 // but some old USB keys only support a very small subset of SCSI which
244 // does not even include the MODE SENSE command!
245 //
Kevin O'Connor897fb112013-02-07 23:32:48 -0500246 if (CONFIG_QEMU_HARDWARE && memcmp(vendor, "QEMU", 5) == 0) {
Paolo Bonzinicb721712012-03-05 12:29:12 +0100247 struct cdbres_mode_sense_geom geomdata;
248 ret = cdb_mode_sense_geom(&dop, &geomdata);
249 if (ret == 0) {
250 u32 cylinders;
251 cylinders = geomdata.cyl[0] << 16;
252 cylinders |= geomdata.cyl[1] << 8;
253 cylinders |= geomdata.cyl[2];
254 if (cylinders && geomdata.heads &&
255 drive->sectors <= 0xFFFFFFFFULL &&
256 ((u32)drive->sectors % (geomdata.heads * cylinders) == 0)) {
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400257 drive->pchs.cylinder = cylinders;
258 drive->pchs.head = geomdata.heads;
259 drive->pchs.sector = (u32)drive->sectors / (geomdata.heads * cylinders);
Paolo Bonzinicb721712012-03-05 12:29:12 +0100260 }
Paolo Bonzini000e0842011-11-16 13:02:54 +0100261 }
262 }
263
Kevin O'Connor279dcb12012-02-18 11:02:27 -0500264 char *desc = znprintf(MAXDESCSIZE, "%s Drive %s %s %s"
265 , s, vendor, product, rev);
266 boot_add_hd(drive, desc, prio);
Paolo Bonzinided04a32011-11-17 10:23:02 +0100267 return 0;
268}