|
|
1.1 root 1: // Support for several common scsi like command data block requests
2: //
3: // Copyright (C) 2010 Kevin O'Connor <[email protected]>
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
9: #include "util.h" // htonl
10: #include "disk.h" // struct disk_op_s
11: #include "blockcmd.h" // struct cdb_request_sense
12: #include "ata.h" // atapi_cmd_data
1.1.1.2 root 13: #include "ahci.h" // atapi_cmd_data
1.1 root 14: #include "usb-msc.h" // usb_cmd_data
1.1.1.3 ! root 15: #include "virtio-scsi.h" // virtio_scsi_cmd_data
! 16: #include "boot.h" // boot_add_hd
1.1 root 17:
18: // Route command to low-level handler.
19: static int
20: cdb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
21: {
22: u8 type = GET_GLOBAL(op->drive_g->type);
23: switch (type) {
24: case DTYPE_ATAPI:
25: return atapi_cmd_data(op, cdbcmd, blocksize);
26: case DTYPE_USB:
27: return usb_cmd_data(op, cdbcmd, blocksize);
1.1.1.2 root 28: case DTYPE_AHCI:
29: return ahci_cmd_data(op, cdbcmd, blocksize);
1.1.1.3 ! root 30: case DTYPE_VIRTIO_SCSI:
! 31: return virtio_scsi_cmd_data(op, cdbcmd, blocksize);
1.1 root 32: default:
33: op->count = 0;
34: return DISK_RET_EPARAM;
35: }
36: }
37:
1.1.1.3 ! root 38: // Determine if the command is a request to pull data from the device
! 39: int
! 40: cdb_is_read(u8 *cdbcmd, u16 blocksize)
! 41: {
! 42: return blocksize && cdbcmd[0] != CDB_CMD_WRITE_10;
! 43: }
! 44:
! 45: int
! 46: scsi_is_ready(struct disk_op_s *op)
! 47: {
! 48: dprintf(6, "scsi_is_ready (drive=%p)\n", op->drive_g);
! 49:
! 50: /* Retry TEST UNIT READY for 5 seconds unless MEDIUM NOT PRESENT is
! 51: * reported by the device. If the device reports "IN PROGRESS",
! 52: * 30 seconds is added. */
! 53: int in_progress = 0;
! 54: u64 end = calc_future_tsc(5000);
! 55: for (;;) {
! 56: if (check_tsc(end)) {
! 57: dprintf(1, "test unit ready failed\n");
! 58: return -1;
! 59: }
! 60:
! 61: int ret = cdb_test_unit_ready(op);
! 62: if (!ret)
! 63: // Success
! 64: break;
! 65:
! 66: struct cdbres_request_sense sense;
! 67: ret = cdb_get_sense(op, &sense);
! 68: if (ret)
! 69: // Error - retry.
! 70: continue;
! 71:
! 72: // Sense succeeded.
! 73: if (sense.asc == 0x3a) { /* MEDIUM NOT PRESENT */
! 74: dprintf(1, "Device reports MEDIUM NOT PRESENT\n");
! 75: return -1;
! 76: }
! 77:
! 78: if (sense.asc == 0x04 && sense.ascq == 0x01 && !in_progress) {
! 79: /* IN PROGRESS OF BECOMING READY */
! 80: printf("Waiting for device to detect medium... ");
! 81: /* Allow 30 seconds more */
! 82: end = calc_future_tsc(30000);
! 83: in_progress = 1;
! 84: }
! 85: }
! 86: return 0;
! 87: }
! 88:
! 89: // Validate drive, find block size / sector count, and register drive.
! 90: int
! 91: scsi_init_drive(struct drive_s *drive, const char *s, int prio)
! 92: {
! 93: if (!CONFIG_USB_MSC && !CONFIG_VIRTIO_SCSI)
! 94: return 0;
! 95:
! 96: struct disk_op_s dop;
! 97: memset(&dop, 0, sizeof(dop));
! 98: dop.drive_g = drive;
! 99: struct cdbres_inquiry data;
! 100: int ret = cdb_get_inquiry(&dop, &data);
! 101: if (ret)
! 102: return ret;
! 103: char vendor[sizeof(data.vendor)+1], product[sizeof(data.product)+1];
! 104: char rev[sizeof(data.rev)+1];
! 105: strtcpy(vendor, data.vendor, sizeof(vendor));
! 106: nullTrailingSpace(vendor);
! 107: strtcpy(product, data.product, sizeof(product));
! 108: nullTrailingSpace(product);
! 109: strtcpy(rev, data.rev, sizeof(rev));
! 110: nullTrailingSpace(rev);
! 111: int pdt = data.pdt & 0x1f;
! 112: int removable = !!(data.removable & 0x80);
! 113: dprintf(1, "%s vendor='%s' product='%s' rev='%s' type=%d removable=%d\n"
! 114: , s, vendor, product, rev, pdt, removable);
! 115: drive->removable = removable;
! 116:
! 117: if (pdt == SCSI_TYPE_CDROM) {
! 118: drive->blksize = CDROM_SECTOR_SIZE;
! 119: drive->sectors = (u64)-1;
! 120:
! 121: char *desc = znprintf(MAXDESCSIZE, "DVD/CD [%s Drive %s %s %s]"
! 122: , s, vendor, product, rev);
! 123: boot_add_cd(drive, desc, prio);
! 124: return 0;
! 125: }
! 126:
! 127: ret = scsi_is_ready(&dop);
! 128: if (ret) {
! 129: dprintf(1, "scsi_is_ready returned %d\n", ret);
! 130: return ret;
! 131: }
! 132:
! 133: struct cdbres_read_capacity capdata;
! 134: ret = cdb_read_capacity(&dop, &capdata);
! 135: if (ret)
! 136: return ret;
! 137:
! 138: // READ CAPACITY returns the address of the last block.
! 139: // We do not bother with READ CAPACITY(16) because BIOS does not support
! 140: // 64-bit LBA anyway.
! 141: drive->blksize = ntohl(capdata.blksize);
! 142: if (drive->blksize != DISK_SECTOR_SIZE) {
! 143: dprintf(1, "%s: unsupported block size %d\n", s, drive->blksize);
! 144: return -1;
! 145: }
! 146: drive->sectors = (u64)ntohl(capdata.sectors) + 1;
! 147: dprintf(1, "%s blksize=%d sectors=%d\n"
! 148: , s, drive->blksize, (unsigned)drive->sectors);
! 149:
! 150: // We do not recover from USB stalls, so try to be safe and avoid
! 151: // sending the command if the (obsolete, but still provided by QEMU)
! 152: // fixed disk geometry page may not be supported.
! 153: //
! 154: // We could also send the command only to small disks (e.g. <504MiB)
! 155: // but some old USB keys only support a very small subset of SCSI which
! 156: // does not even include the MODE SENSE command!
! 157: //
! 158: if (! CONFIG_COREBOOT && memcmp(vendor, "QEMU ", 8) == 0) {
! 159: struct cdbres_mode_sense_geom geomdata;
! 160: ret = cdb_mode_sense_geom(&dop, &geomdata);
! 161: if (ret == 0) {
! 162: u32 cylinders;
! 163: cylinders = geomdata.cyl[0] << 16;
! 164: cylinders |= geomdata.cyl[1] << 8;
! 165: cylinders |= geomdata.cyl[2];
! 166: if (cylinders && geomdata.heads &&
! 167: drive->sectors <= 0xFFFFFFFFULL &&
! 168: ((u32)drive->sectors % (geomdata.heads * cylinders) == 0)) {
! 169: drive->pchs.cylinders = cylinders;
! 170: drive->pchs.heads = geomdata.heads;
! 171: drive->pchs.spt = (u32)drive->sectors / (geomdata.heads * cylinders);
! 172: }
! 173: }
! 174: }
! 175:
! 176: char *desc = znprintf(MAXDESCSIZE, "%s Drive %s %s %s"
! 177: , s, vendor, product, rev);
! 178: boot_add_hd(drive, desc, prio);
! 179: return 0;
! 180: }
! 181:
1.1 root 182: int
183: cdb_get_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data)
184: {
185: struct cdb_request_sense cmd;
186: memset(&cmd, 0, sizeof(cmd));
187: cmd.command = CDB_CMD_INQUIRY;
188: cmd.length = sizeof(*data);
189: op->count = 1;
190: op->buf_fl = data;
191: return cdb_cmd_data(op, &cmd, sizeof(*data));
192: }
193:
194: // Request SENSE
195: int
196: cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data)
197: {
198: struct cdb_request_sense cmd;
199: memset(&cmd, 0, sizeof(cmd));
200: cmd.command = CDB_CMD_REQUEST_SENSE;
201: cmd.length = sizeof(*data);
202: op->count = 1;
203: op->buf_fl = data;
204: return cdb_cmd_data(op, &cmd, sizeof(*data));
205: }
206:
1.1.1.3 ! root 207: // Test unit ready
! 208: int
! 209: cdb_test_unit_ready(struct disk_op_s *op)
! 210: {
! 211: struct cdb_request_sense cmd;
! 212: memset(&cmd, 0, sizeof(cmd));
! 213: cmd.command = CDB_CMD_TEST_UNIT_READY;
! 214: op->count = 0;
! 215: op->buf_fl = NULL;
! 216: return cdb_cmd_data(op, &cmd, 0);
! 217: }
! 218:
1.1 root 219: // Request capacity
220: int
221: cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data)
222: {
223: struct cdb_read_capacity cmd;
224: memset(&cmd, 0, sizeof(cmd));
225: cmd.command = CDB_CMD_READ_CAPACITY;
226: op->count = 1;
227: op->buf_fl = data;
228: return cdb_cmd_data(op, &cmd, sizeof(*data));
229: }
230:
1.1.1.3 ! root 231: // Mode sense, geometry page.
! 232: int
! 233: cdb_mode_sense_geom(struct disk_op_s *op, struct cdbres_mode_sense_geom *data)
! 234: {
! 235: struct cdb_mode_sense cmd;
! 236: memset(&cmd, 0, sizeof(cmd));
! 237: cmd.command = CDB_CMD_MODE_SENSE;
! 238: cmd.flags = 8; /* DBD */
! 239: cmd.page = MODE_PAGE_HD_GEOMETRY;
! 240: cmd.count = htons(sizeof(*data));
! 241: op->count = 1;
! 242: op->buf_fl = data;
! 243: return cdb_cmd_data(op, &cmd, sizeof(*data));
! 244: }
! 245:
1.1 root 246: // Read sectors.
247: int
248: cdb_read(struct disk_op_s *op)
249: {
250: struct cdb_rwdata_10 cmd;
251: memset(&cmd, 0, sizeof(cmd));
252: cmd.command = CDB_CMD_READ_10;
253: cmd.lba = htonl(op->lba);
254: cmd.count = htons(op->count);
255: return cdb_cmd_data(op, &cmd, GET_GLOBAL(op->drive_g->blksize));
256: }
1.1.1.3 ! root 257:
! 258: // Write sectors.
! 259: int
! 260: cdb_write(struct disk_op_s *op)
! 261: {
! 262: struct cdb_rwdata_10 cmd;
! 263: memset(&cmd, 0, sizeof(cmd));
! 264: cmd.command = CDB_CMD_WRITE_10;
! 265: cmd.lba = htonl(op->lba);
! 266: cmd.count = htons(op->count);
! 267: return cdb_cmd_data(op, &cmd, GET_GLOBAL(op->drive_g->blksize));
! 268: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.