// Validate drive, find block size / sector count, and register drive. int scsi_init_drive(struct drive_s *drive, const char *s, int prio) { struct disk_op_s dop; memset(&dop, 0, sizeof(dop)); dop.drive_g = drive; struct cdbres_inquiry data; int ret = cdb_get_inquiry(&dop, &data); if (ret) return ret; char vendor[sizeof(data.vendor)+1], product[sizeof(data.product)+1]; char rev[sizeof(data.rev)+1]; strtcpy(vendor, data.vendor, sizeof(vendor)); nullTrailingSpace(vendor); strtcpy(product, data.product, sizeof(product)); nullTrailingSpace(product); strtcpy(rev, data.rev, sizeof(rev)); nullTrailingSpace(rev); int pdt = data.pdt & 0x1f; int removable = !!(data.removable & 0x80); dprintf(1, "%s vendor='%s' product='%s' rev='%s' type=%d removable=%d\n" , s, vendor, product, rev, pdt, removable); drive->removable = removable; if (pdt == SCSI_TYPE_CDROM) { drive->blksize = CDROM_SECTOR_SIZE; drive->sectors = (u64)-1; char *desc = znprintf(MAXDESCSIZE, "DVD/CD [%s Drive %s %s %s]" , s, vendor, product, rev); boot_add_cd(drive, desc, prio); return 0; } ret = scsi_is_ready(&dop); if (ret) { dprintf(1, "scsi_is_ready returned %d\n", ret); return ret; } struct cdbres_read_capacity capdata; ret = cdb_read_capacity(&dop, &capdata); if (ret) return ret; // READ CAPACITY returns the address of the last block. // We do not bother with READ CAPACITY(16) because BIOS does not support // 64-bit LBA anyway. drive->blksize = be32_to_cpu(capdata.blksize); if (drive->blksize != DISK_SECTOR_SIZE) { dprintf(1, "%s: unsupported block size %d\n", s, drive->blksize); return -1; } drive->sectors = (u64)be32_to_cpu(capdata.sectors) + 1; dprintf(1, "%s blksize=%d sectors=%d\n" , s, drive->blksize, (unsigned)drive->sectors); // We do not recover from USB stalls, so try to be safe and avoid // sending the command if the (obsolete, but still provided by QEMU) // fixed disk geometry page may not be supported. // // We could also send the command only to small disks (e.g. <504MiB) // but some old USB keys only support a very small subset of SCSI which // does not even include the MODE SENSE command! // if (! CONFIG_COREBOOT && memcmp(vendor, "QEMU", 5) == 0) { struct cdbres_mode_sense_geom geomdata; ret = cdb_mode_sense_geom(&dop, &geomdata); if (ret == 0) { u32 cylinders; cylinders = geomdata.cyl[0] << 16; cylinders |= geomdata.cyl[1] << 8; cylinders |= geomdata.cyl[2]; if (cylinders && geomdata.heads && drive->sectors <= 0xFFFFFFFFULL && ((u32)drive->sectors % (geomdata.heads * cylinders) == 0)) { drive->pchs.cylinders = cylinders; drive->pchs.heads = geomdata.heads; drive->pchs.spt = (u32)drive->sectors / (geomdata.heads * cylinders); } } } char *desc = znprintf(MAXDESCSIZE, "%s Drive %s %s %s" , s, vendor, product, rev); boot_add_hd(drive, desc, prio); return 0; }
int cdrom_boot(struct drive_s *drive_g) { ASSERT32FLAT(); struct disk_op_s dop; int cdid = getDriveId(EXTTYPE_CD, drive_g); memset(&dop, 0, sizeof(dop)); dop.drive_g = drive_g; if (!dop.drive_g || cdid < 0) return 1; int ret = scsi_is_ready(&dop); if (ret) dprintf(1, "scsi_is_ready returned %d\n", ret); // Read the Boot Record Volume Descriptor u8 buffer[CDROM_SECTOR_SIZE]; dop.lba = 0x11; dop.count = 1; dop.buf_fl = buffer; ret = cdb_read(&dop); if (ret) return 3; // Validity checks if (buffer[0]) return 4; if (strcmp((char*)&buffer[1], "CD001\001EL TORITO SPECIFICATION") != 0) return 5; // ok, now we calculate the Boot catalog address u32 lba = *(u32*)&buffer[0x47]; // And we read the Boot Catalog dop.lba = lba; dop.count = 1; ret = cdb_read(&dop); if (ret) return 7; // Validation entry if (buffer[0x00] != 0x01) return 8; // Header if (buffer[0x01] != 0x00) return 9; // Platform if (buffer[0x1E] != 0x55) return 10; // key 1 if (buffer[0x1F] != 0xAA) return 10; // key 2 // Initial/Default Entry if (buffer[0x20] != 0x88) return 11; // Bootable u8 media = buffer[0x21]; CDEmu.media = media; CDEmu.emulated_drive_gf = dop.drive_g; u16 boot_segment = *(u16*)&buffer[0x22]; if (!boot_segment) boot_segment = 0x07C0; CDEmu.load_segment = boot_segment; CDEmu.buffer_segment = 0x0000; u16 nbsectors = *(u16*)&buffer[0x26]; CDEmu.sector_count = nbsectors; lba = *(u32*)&buffer[0x28]; CDEmu.ilba = lba; // And we read the image in memory dop.lba = lba; dop.count = DIV_ROUND_UP(nbsectors, 4); dop.buf_fl = MAKE_FLATPTR(boot_segment, 0); ret = cdb_read(&dop); if (ret) return 12; if (media == 0) { // No emulation requested - return success. CDEmu.emulated_extdrive = EXTSTART_CD + cdid; return 0; } // Emulation of a floppy/harddisk requested if (! CONFIG_CDROM_EMU || !cdemu_drive_gf) return 13; // Set emulated drive id and increase bios installed hardware // number of devices if (media < 4) { // Floppy emulation CDEmu.emulated_extdrive = 0x00; // XXX - get and set actual floppy count. set_equipment_flags(0x41, 0x41); switch (media) { case 0x01: // 1.2M floppy CDEmu.lchs.spt = 15; CDEmu.lchs.cylinders = 80; CDEmu.lchs.heads = 2; break; case 0x02: // 1.44M floppy CDEmu.lchs.spt = 18; CDEmu.lchs.cylinders = 80; CDEmu.lchs.heads = 2; break; case 0x03: // 2.88M floppy CDEmu.lchs.spt = 36; CDEmu.lchs.cylinders = 80; CDEmu.lchs.heads = 2; break; } } else { // Harddrive emulation CDEmu.emulated_extdrive = 0x80; SET_BDA(hdcount, GET_BDA(hdcount) + 1); // Peak at partition table to get chs. struct mbr_s *mbr = (void*)0; u8 sptcyl = GET_FARVAR(boot_segment, mbr->partitions[0].last.sptcyl); u8 cyllow = GET_FARVAR(boot_segment, mbr->partitions[0].last.cyllow); u8 heads = GET_FARVAR(boot_segment, mbr->partitions[0].last.heads); CDEmu.lchs.spt = sptcyl & 0x3f; CDEmu.lchs.cylinders = ((sptcyl<<2)&0x300) + cyllow + 1; CDEmu.lchs.heads = heads + 1; } // everything is ok, so from now on, the emulation is active CDEmu.active = 0x01; dprintf(6, "cdemu media=%d\n", media); return 0; }