1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
|
// Support for booting from cdroms (the "El Torito" spec).
//
// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2002 MandrakeSoft S.A.
//
// This file may be distributed under the terms of the GNU LGPLv3 license.
#include "biosvar.h" // GET_GLOBAL
#include "block.h" // struct drive_s
#include "bregs.h" // struct bregs
#include "hw/ata.h" // ATA_CMD_REQUEST_SENSE
#include "hw/blockcmd.h" // CDB_CMD_REQUEST_SENSE
#include "malloc.h" // free
#include "output.h" // dprintf
#include "std/disk.h" // DISK_RET_SUCCESS
#include "string.h" // memset
#include "util.h" // cdrom_prepboot
#include "tcgbios.h" // tpm_*
/****************************************************************
* CD emulation
****************************************************************/
struct eltorito_s CDEmu VARLOW = { .size=sizeof(CDEmu) };
struct drive_s *emulated_drive_gf VARLOW;
struct drive_s *cdemu_drive_gf VARFSEG;
static int
cdemu_read(struct disk_op_s *op)
{
struct drive_s *drive_gf = GET_LOW(emulated_drive_gf);
struct disk_op_s dop;
dop.drive_fl = drive_gf;
dop.command = op->command;
dop.lba = GET_LOW(CDEmu.ilba) + op->lba / 4;
int count = op->count;
op->count = 0;
u8 *cdbuf_fl = GET_GLOBAL(bounce_buf_fl);
if (op->lba & 3) {
// Partial read of first block.
dop.count = 1;
dop.buf_fl = cdbuf_fl;
int ret = process_op(&dop);
if (ret)
return ret;
u8 thiscount = 4 - (op->lba & 3);
if (thiscount > count)
thiscount = count;
count -= thiscount;
memcpy_fl(op->buf_fl, cdbuf_fl + (op->lba & 3) * 512, thiscount * 512);
op->buf_fl += thiscount * 512;
op->count += thiscount;
dop.lba++;
}
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;
}
if (count) {
// Partial read on last block.
dop.count = 1;
dop.buf_fl = cdbuf_fl;
int ret = process_op(&dop);
if (ret)
return ret;
u8 thiscount = count;
memcpy_fl(op->buf_fl, cdbuf_fl, thiscount * 512);
op->count += thiscount;
}
return DISK_RET_SUCCESS;
}
int
cdemu_process_op(struct disk_op_s *op)
{
if (!CONFIG_CDROM_EMU)
return 0;
switch (op->command) {
case CMD_READ:
return cdemu_read(op);
case CMD_WRITE:
case CMD_FORMAT:
return DISK_RET_EWRITEPROTECT;
default:
return default_process_op(op);
}
}
void
cdrom_prepboot(void)
{
if (!CONFIG_CDROM_EMU)
return;
if (!CDCount)
return;
if (create_bounce_buf() < 0)
return;
struct drive_s *drive = malloc_fseg(sizeof(*drive));
if (!drive) {
warn_noalloc();
return;
}
cdemu_drive_gf = drive;
memset(drive, 0, sizeof(*drive));
drive->type = DTYPE_CDEMU;
drive->blksize = DISK_SECTOR_SIZE;
drive->sectors = (u64)-1;
}
/****************************************************************
* CD booting
****************************************************************/
int
cdrom_boot(struct drive_s *drive)
{
ASSERT32FLAT();
struct disk_op_s dop;
int cdid = getDriveId(EXTTYPE_CD, drive);
memset(&dop, 0, sizeof(dop));
dop.drive_fl = drive;
if (!dop.drive_fl || cdid < 0)
return 1;
int ret = scsi_is_ready(&dop);
if (ret)
dprintf(5, "scsi_is_ready returned %d\n", ret);
// 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 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 = process_op(&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
/* measure 2048 bytes (one sector) */
tpm_add_cdrom_catalog(MAKE_FLATPTR(GET_SEG(SS), buffer), sizeof(buffer));
// Fill in el-torito cdrom emulation fields.
emulated_drive_gf = drive;
u8 media = buffer[0x21];
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;
CDEmu.controller_index = drive->cntl_id / 2;
CDEmu.device_spec = drive->cntl_id % 2;
// And we read the image in memory
nbsectors = DIV_ROUND_UP(nbsectors, 4);
dop.lba = lba;
dop.buf_fl = MAKE_FLATPTR(boot_segment, 0);
while (nbsectors) {
int count = nbsectors;
if (count > 64*1024/CDROM_SECTOR_SIZE)
count = 64*1024/CDROM_SECTOR_SIZE;
dop.count = count;
ret = process_op(&dop);
if (ret)
return 12;
nbsectors -= count;
dop.lba += count;
dop.buf_fl += count*CDROM_SECTOR_SIZE;
}
if (media == 0) {
// No emulation requested - return success.
CDEmu.emulated_drive = 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_drive = 0x00;
// XXX - get and set actual floppy count.
set_equipment_flags(0x41, 0x41);
switch (media) {
case 0x01: // 1.2M floppy
CDEmu.chs.sptcyl = 15;
CDEmu.chs.cyllow = 79;
CDEmu.chs.heads = 1;
break;
case 0x02: // 1.44M floppy
CDEmu.chs.sptcyl = 18;
CDEmu.chs.cyllow = 79;
CDEmu.chs.heads = 1;
break;
case 0x03: // 2.88M floppy
CDEmu.chs.sptcyl = 36;
CDEmu.chs.cyllow = 79;
CDEmu.chs.heads = 1;
break;
}
} else {
// Harddrive emulation
CDEmu.emulated_drive = 0x80;
SET_BDA(hdcount, GET_BDA(hdcount) + 1);
// Peak at partition table to get chs.
struct mbr_s *mbr = MAKE_FLATPTR(boot_segment, 0);
CDEmu.chs = mbr->partitions[0].last;
}
// everything is ok, so from now on, the emulation is active
CDEmu.media = media;
dprintf(6, "cdemu media=%d\n", media);
return 0;
}
// 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;
}
|