2009-09-13 01:35:04 +02:00
|
|
|
// Support for booting from cdroms (the "El Torito" spec).
|
2008-03-05 01:56:41 +01:00
|
|
|
//
|
2009-08-12 03:59:37 +02:00
|
|
|
// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net>
|
2008-03-05 01:56:41 +01:00
|
|
|
// Copyright (C) 2002 MandrakeSoft S.A.
|
|
|
|
//
|
2009-01-16 02:52:58 +01:00
|
|
|
// This file may be distributed under the terms of the GNU LGPLv3 license.
|
2008-03-05 01:56:41 +01:00
|
|
|
|
2012-05-14 04:46:12 +02:00
|
|
|
#include "biosvar.h" // GET_GLOBAL
|
2013-09-15 05:57:26 +02:00
|
|
|
#include "block.h" // struct drive_s
|
2013-09-15 03:55:26 +02:00
|
|
|
#include "bregs.h" // struct bregs
|
2013-09-03 02:48:46 +02:00
|
|
|
#include "hw/ata.h" // ATA_CMD_REQUEST_SENSE
|
|
|
|
#include "hw/blockcmd.h" // CDB_CMD_REQUEST_SENSE
|
2013-09-15 02:23:54 +02:00
|
|
|
#include "malloc.h" // free
|
2013-09-15 03:55:26 +02:00
|
|
|
#include "output.h" // dprintf
|
2013-09-15 05:57:26 +02:00
|
|
|
#include "std/disk.h" // DISK_RET_SUCCESS
|
2013-09-15 01:10:40 +02:00
|
|
|
#include "string.h" // memset
|
2013-09-15 05:57:26 +02:00
|
|
|
#include "util.h" // cdrom_prepboot
|
2015-05-26 21:48:33 +02:00
|
|
|
#include "tcgbios.h" // tpm_*
|
2008-03-05 01:56:41 +01:00
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************
|
2009-08-19 04:38:49 +02:00
|
|
|
* CD emulation
|
2008-03-05 01:56:41 +01:00
|
|
|
****************************************************************/
|
|
|
|
|
2014-05-10 07:20:46 +02:00
|
|
|
struct eltorito_s CDEmu VARLOW = { .size=sizeof(CDEmu) };
|
|
|
|
struct drive_s *emulated_drive_gf VARLOW;
|
2013-02-19 05:36:03 +01:00
|
|
|
struct drive_s *cdemu_drive_gf VARFSEG;
|
2010-06-06 22:18:03 +02:00
|
|
|
|
2009-09-13 01:35:04 +02:00
|
|
|
static int
|
|
|
|
cdemu_read(struct disk_op_s *op)
|
2008-03-05 01:56:41 +01:00
|
|
|
{
|
2014-05-10 07:20:46 +02:00
|
|
|
struct drive_s *drive_gf = GET_LOW(emulated_drive_gf);
|
2009-09-13 01:35:04 +02:00
|
|
|
struct disk_op_s dop;
|
2017-07-11 18:24:50 +02:00
|
|
|
dop.drive_fl = drive_gf;
|
2009-09-13 01:35:04 +02:00
|
|
|
dop.command = op->command;
|
2012-05-14 04:46:12 +02:00
|
|
|
dop.lba = GET_LOW(CDEmu.ilba) + op->lba / 4;
|
2009-09-13 01:35:04 +02:00
|
|
|
|
|
|
|
int count = op->count;
|
|
|
|
op->count = 0;
|
2011-08-04 19:36:27 +02:00
|
|
|
u8 *cdbuf_fl = GET_GLOBAL(bounce_buf_fl);
|
2009-09-13 01:35:04 +02:00
|
|
|
|
|
|
|
if (op->lba & 3) {
|
|
|
|
// Partial read of first block.
|
|
|
|
dop.count = 1;
|
2010-06-06 22:18:03 +02:00
|
|
|
dop.buf_fl = cdbuf_fl;
|
2009-09-13 01:35:04 +02:00
|
|
|
int ret = process_op(&dop);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
u8 thiscount = 4 - (op->lba & 3);
|
|
|
|
if (thiscount > count)
|
|
|
|
thiscount = count;
|
|
|
|
count -= thiscount;
|
2010-06-06 22:18:03 +02:00
|
|
|
memcpy_fl(op->buf_fl, cdbuf_fl + (op->lba & 3) * 512, thiscount * 512);
|
2009-09-13 01:35:04 +02:00
|
|
|
op->buf_fl += thiscount * 512;
|
|
|
|
op->count += thiscount;
|
|
|
|
dop.lba++;
|
|
|
|
}
|
2008-03-05 01:56:41 +01:00
|
|
|
|
2009-09-13 01:35:04 +02:00
|
|
|
if (count > 3) {
|
|
|
|
// Read n number of regular blocks.
|
|
|
|
dop.count = count / 4;
|
|
|
|
dop.buf_fl = op->buf_fl;
|
|
|
|
int ret = process_op(&dop);
|
|
|
|
op->count += dop.count * 4;
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
u8 thiscount = count & ~3;
|
|
|
|
count &= 3;
|
|
|
|
op->buf_fl += thiscount * 512;
|
|
|
|
dop.lba += thiscount / 4;
|
|
|
|
}
|
2009-08-09 19:08:21 +02:00
|
|
|
|
2009-09-13 01:35:04 +02:00
|
|
|
if (count) {
|
|
|
|
// Partial read on last block.
|
|
|
|
dop.count = 1;
|
2010-06-06 22:18:03 +02:00
|
|
|
dop.buf_fl = cdbuf_fl;
|
2009-09-13 01:35:04 +02:00
|
|
|
int ret = process_op(&dop);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
u8 thiscount = count;
|
2010-06-06 22:18:03 +02:00
|
|
|
memcpy_fl(op->buf_fl, cdbuf_fl, thiscount * 512);
|
2009-09-13 01:35:04 +02:00
|
|
|
op->count += thiscount;
|
|
|
|
}
|
|
|
|
|
|
|
|
return DISK_RET_SUCCESS;
|
2009-08-09 19:08:21 +02:00
|
|
|
}
|
|
|
|
|
2009-09-13 01:35:04 +02:00
|
|
|
int
|
2015-07-07 20:56:20 +02:00
|
|
|
cdemu_process_op(struct disk_op_s *op)
|
2008-03-05 01:56:41 +01:00
|
|
|
{
|
2009-09-13 01:35:04 +02:00
|
|
|
if (!CONFIG_CDROM_EMU)
|
|
|
|
return 0;
|
2008-03-05 01:56:41 +01:00
|
|
|
|
2009-09-13 01:35:04 +02:00
|
|
|
switch (op->command) {
|
|
|
|
case CMD_READ:
|
|
|
|
return cdemu_read(op);
|
|
|
|
case CMD_WRITE:
|
|
|
|
case CMD_FORMAT:
|
|
|
|
return DISK_RET_EWRITEPROTECT;
|
|
|
|
default:
|
2015-07-07 15:01:52 +02:00
|
|
|
return default_process_op(op);
|
2009-09-13 01:35:04 +02:00
|
|
|
}
|
2008-03-05 01:56:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-01-21 07:14:12 +01:00
|
|
|
cdrom_prepboot(void)
|
2008-03-05 01:56:41 +01:00
|
|
|
{
|
2009-09-13 01:35:04 +02:00
|
|
|
if (!CONFIG_CDROM_EMU)
|
|
|
|
return;
|
2010-12-29 17:05:46 +01:00
|
|
|
if (!CDCount)
|
2010-06-06 22:18:03 +02:00
|
|
|
return;
|
2013-01-21 07:14:12 +01:00
|
|
|
if (create_bounce_buf() < 0)
|
2011-08-04 19:36:27 +02:00
|
|
|
return;
|
2008-03-05 01:56:41 +01:00
|
|
|
|
2013-10-26 17:48:06 +02:00
|
|
|
struct drive_s *drive = malloc_fseg(sizeof(*drive));
|
|
|
|
if (!drive) {
|
2010-02-16 04:48:28 +01:00
|
|
|
warn_noalloc();
|
2009-09-13 01:35:04 +02:00
|
|
|
return;
|
2008-03-05 01:56:41 +01:00
|
|
|
}
|
2013-10-26 17:48:06 +02:00
|
|
|
cdemu_drive_gf = drive;
|
|
|
|
memset(drive, 0, sizeof(*drive));
|
|
|
|
drive->type = DTYPE_CDEMU;
|
|
|
|
drive->blksize = DISK_SECTOR_SIZE;
|
|
|
|
drive->sectors = (u64)-1;
|
2008-03-05 01:56:41 +01:00
|
|
|
}
|
|
|
|
|
2008-03-05 04:50:53 +01:00
|
|
|
|
|
|
|
/****************************************************************
|
|
|
|
* CD booting
|
|
|
|
****************************************************************/
|
|
|
|
|
2008-06-28 18:15:57 +02:00
|
|
|
int
|
2013-10-26 17:48:06 +02:00
|
|
|
cdrom_boot(struct drive_s *drive)
|
2008-03-05 04:50:53 +01:00
|
|
|
{
|
2012-05-14 04:46:12 +02:00
|
|
|
ASSERT32FLAT();
|
2010-02-15 17:56:07 +01:00
|
|
|
struct disk_op_s dop;
|
2013-10-26 17:48:06 +02:00
|
|
|
int cdid = getDriveId(EXTTYPE_CD, drive);
|
2010-02-15 17:56:07 +01:00
|
|
|
memset(&dop, 0, sizeof(dop));
|
2017-07-11 18:24:50 +02:00
|
|
|
dop.drive_fl = drive;
|
|
|
|
if (!dop.drive_fl || cdid < 0)
|
2009-02-16 16:51:24 +01:00
|
|
|
return 1;
|
2008-03-05 04:50:53 +01:00
|
|
|
|
2011-11-16 13:02:51 +01:00
|
|
|
int ret = scsi_is_ready(&dop);
|
2008-03-05 04:50:53 +01:00
|
|
|
if (ret)
|
2020-05-18 18:58:04 +02:00
|
|
|
dprintf(5, "scsi_is_ready returned %d\n", ret);
|
2008-03-05 04:50:53 +01:00
|
|
|
|
|
|
|
// Read the Boot Record Volume Descriptor
|
2012-07-21 18:01:12 +02:00
|
|
|
u8 buffer[CDROM_SECTOR_SIZE];
|
2014-12-29 15:48:24 +01:00
|
|
|
dop.command = CMD_READ;
|
2008-12-31 06:31:03 +01:00
|
|
|
dop.lba = 0x11;
|
|
|
|
dop.count = 1;
|
2012-05-14 04:46:12 +02:00
|
|
|
dop.buf_fl = buffer;
|
2015-07-07 15:53:54 +02:00
|
|
|
ret = process_op(&dop);
|
2008-03-05 04:50:53 +01:00
|
|
|
if (ret)
|
|
|
|
return 3;
|
|
|
|
|
|
|
|
// Validity checks
|
|
|
|
if (buffer[0])
|
|
|
|
return 4;
|
2009-04-18 22:59:47 +02:00
|
|
|
if (strcmp((char*)&buffer[1], "CD001\001EL TORITO SPECIFICATION") != 0)
|
2008-03-05 04:50:53 +01:00
|
|
|
return 5;
|
|
|
|
|
|
|
|
// ok, now we calculate the Boot catalog address
|
|
|
|
u32 lba = *(u32*)&buffer[0x47];
|
|
|
|
|
|
|
|
// And we read the Boot Catalog
|
2008-12-31 06:31:03 +01:00
|
|
|
dop.lba = lba;
|
2010-02-17 07:01:32 +01:00
|
|
|
dop.count = 1;
|
2015-07-07 15:53:54 +02:00
|
|
|
ret = process_op(&dop);
|
2008-03-05 04:50:53 +01:00
|
|
|
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
|
|
|
|
|
2015-05-26 21:48:33 +02:00
|
|
|
/* measure 2048 bytes (one sector) */
|
|
|
|
tpm_add_cdrom_catalog(MAKE_FLATPTR(GET_SEG(SS), buffer), sizeof(buffer));
|
|
|
|
|
2014-05-10 07:20:46 +02:00
|
|
|
// Fill in el-torito cdrom emulation fields.
|
|
|
|
emulated_drive_gf = drive;
|
2008-03-23 01:13:08 +01:00
|
|
|
u8 media = buffer[0x21];
|
2008-03-05 04:50:53 +01:00
|
|
|
|
|
|
|
u16 boot_segment = *(u16*)&buffer[0x22];
|
|
|
|
if (!boot_segment)
|
|
|
|
boot_segment = 0x07C0;
|
2012-05-14 04:46:12 +02:00
|
|
|
CDEmu.load_segment = boot_segment;
|
|
|
|
CDEmu.buffer_segment = 0x0000;
|
2008-03-05 04:50:53 +01:00
|
|
|
|
|
|
|
u16 nbsectors = *(u16*)&buffer[0x26];
|
2012-05-14 04:46:12 +02:00
|
|
|
CDEmu.sector_count = nbsectors;
|
2008-03-05 04:50:53 +01:00
|
|
|
|
|
|
|
lba = *(u32*)&buffer[0x28];
|
2012-05-14 04:46:12 +02:00
|
|
|
CDEmu.ilba = lba;
|
2008-03-05 04:50:53 +01:00
|
|
|
|
2014-05-10 07:20:46 +02:00
|
|
|
CDEmu.controller_index = drive->cntl_id / 2;
|
|
|
|
CDEmu.device_spec = drive->cntl_id % 2;
|
|
|
|
|
2008-03-05 04:50:53 +01:00
|
|
|
// And we read the image in memory
|
2014-12-29 16:15:57 +01:00
|
|
|
nbsectors = DIV_ROUND_UP(nbsectors, 4);
|
2009-08-09 17:32:00 +02:00
|
|
|
dop.lba = lba;
|
2009-01-19 21:44:44 +01:00
|
|
|
dop.buf_fl = MAKE_FLATPTR(boot_segment, 0);
|
2014-12-29 16:15:57 +01:00
|
|
|
while (nbsectors) {
|
|
|
|
int count = nbsectors;
|
|
|
|
if (count > 64*1024/CDROM_SECTOR_SIZE)
|
|
|
|
count = 64*1024/CDROM_SECTOR_SIZE;
|
|
|
|
dop.count = count;
|
2015-07-07 15:53:54 +02:00
|
|
|
ret = process_op(&dop);
|
2014-12-29 16:15:57 +01:00
|
|
|
if (ret)
|
|
|
|
return 12;
|
|
|
|
nbsectors -= count;
|
|
|
|
dop.lba += count;
|
|
|
|
dop.buf_fl += count*CDROM_SECTOR_SIZE;
|
|
|
|
}
|
2008-03-05 04:50:53 +01:00
|
|
|
|
2008-03-23 01:13:08 +01:00
|
|
|
if (media == 0) {
|
|
|
|
// No emulation requested - return success.
|
2014-05-10 07:20:46 +02:00
|
|
|
CDEmu.emulated_drive = EXTSTART_CD + cdid;
|
2008-03-23 01:13:08 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Emulation of a floppy/harddisk requested
|
2010-02-28 07:28:11 +01:00
|
|
|
if (! CONFIG_CDROM_EMU || !cdemu_drive_gf)
|
2008-03-23 01:13:08 +01:00
|
|
|
return 13;
|
|
|
|
|
|
|
|
// Set emulated drive id and increase bios installed hardware
|
|
|
|
// number of devices
|
|
|
|
if (media < 4) {
|
|
|
|
// Floppy emulation
|
2014-05-10 07:20:46 +02:00
|
|
|
CDEmu.emulated_drive = 0x00;
|
2010-02-15 17:56:07 +01:00
|
|
|
// XXX - get and set actual floppy count.
|
2012-06-10 15:09:22 +02:00
|
|
|
set_equipment_flags(0x41, 0x41);
|
2009-02-07 06:04:57 +01:00
|
|
|
|
|
|
|
switch (media) {
|
|
|
|
case 0x01: // 1.2M floppy
|
2014-05-10 07:20:46 +02:00
|
|
|
CDEmu.chs.sptcyl = 15;
|
|
|
|
CDEmu.chs.cyllow = 79;
|
|
|
|
CDEmu.chs.heads = 1;
|
2009-02-07 06:04:57 +01:00
|
|
|
break;
|
|
|
|
case 0x02: // 1.44M floppy
|
2014-05-10 07:20:46 +02:00
|
|
|
CDEmu.chs.sptcyl = 18;
|
|
|
|
CDEmu.chs.cyllow = 79;
|
|
|
|
CDEmu.chs.heads = 1;
|
2009-02-07 06:04:57 +01:00
|
|
|
break;
|
|
|
|
case 0x03: // 2.88M floppy
|
2014-05-10 07:20:46 +02:00
|
|
|
CDEmu.chs.sptcyl = 36;
|
|
|
|
CDEmu.chs.cyllow = 79;
|
|
|
|
CDEmu.chs.heads = 1;
|
2009-02-07 06:04:57 +01:00
|
|
|
break;
|
|
|
|
}
|
2008-03-23 01:13:08 +01:00
|
|
|
} else {
|
|
|
|
// Harddrive emulation
|
2014-05-10 07:20:46 +02:00
|
|
|
CDEmu.emulated_drive = 0x80;
|
2008-12-31 06:09:28 +01:00
|
|
|
SET_BDA(hdcount, GET_BDA(hdcount) + 1);
|
2008-03-23 01:13:08 +01:00
|
|
|
|
2009-02-07 06:04:57 +01:00
|
|
|
// Peak at partition table to get chs.
|
2014-05-10 07:20:46 +02:00
|
|
|
struct mbr_s *mbr = MAKE_FLATPTR(boot_segment, 0);
|
|
|
|
CDEmu.chs = mbr->partitions[0].last;
|
2008-03-05 04:50:53 +01:00
|
|
|
}
|
|
|
|
|
2008-03-23 01:13:08 +01:00
|
|
|
// everything is ok, so from now on, the emulation is active
|
2014-05-10 07:20:46 +02:00
|
|
|
CDEmu.media = media;
|
2008-06-28 18:15:57 +02:00
|
|
|
dprintf(6, "cdemu media=%d\n", media);
|
2008-03-05 04:50:53 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2018-09-21 09:35:03 +02:00
|
|
|
|
|
|
|
// check if media is present and the drive is bootable.
|
|
|
|
// in case it is return the volume label.
|
|
|
|
char*
|
|
|
|
cdrom_media_info(struct drive_s *drive)
|
|
|
|
{
|
|
|
|
ASSERT32FLAT();
|
|
|
|
|
|
|
|
struct disk_op_s dop;
|
|
|
|
memset(&dop, 0, sizeof(dop));
|
|
|
|
dop.drive_fl = drive;
|
|
|
|
|
|
|
|
int ret = scsi_is_ready(&dop);
|
|
|
|
if (ret)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// Read the Boot Record Volume Descriptor
|
|
|
|
u8 buffer[CDROM_SECTOR_SIZE];
|
|
|
|
dop.command = CMD_READ;
|
|
|
|
dop.lba = 0x11;
|
|
|
|
dop.count = 1;
|
|
|
|
dop.buf_fl = buffer;
|
|
|
|
ret = process_op(&dop);
|
|
|
|
if (ret)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// Is it bootable?
|
|
|
|
if (buffer[0])
|
|
|
|
return NULL;
|
|
|
|
if (strcmp((char*)&buffer[1], "CD001\001EL TORITO SPECIFICATION") != 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// Read the Primary Volume Descriptor
|
|
|
|
dop.command = CMD_READ;
|
|
|
|
dop.lba = 0x10;
|
|
|
|
dop.count = 1;
|
|
|
|
dop.buf_fl = buffer;
|
|
|
|
ret = process_op(&dop);
|
|
|
|
if (ret)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// Read volume id, trim trailing spaces
|
|
|
|
char *volume = znprintf(30, "%s", buffer + 40);
|
|
|
|
nullTrailingSpace(volume);
|
|
|
|
return volume;
|
|
|
|
}
|