diff options
Diffstat (limited to 'src/VBox/Devices/PC/BIOS/eltorito.c')
-rw-r--r-- | src/VBox/Devices/PC/BIOS/eltorito.c | 173 |
1 files changed, 104 insertions, 69 deletions
diff --git a/src/VBox/Devices/PC/BIOS/eltorito.c b/src/VBox/Devices/PC/BIOS/eltorito.c index 6e12fc4a..c33c95ae 100644 --- a/src/VBox/Devices/PC/BIOS/eltorito.c +++ b/src/VBox/Devices/PC/BIOS/eltorito.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2011 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -93,11 +93,48 @@ typedef struct { ct_assert(sizeof(cdb_atapi) == 12); +/* Generic ATAPI/SCSI CD-ROM access routine signature. */ +typedef uint16_t (* cd_pkt_func)(uint16_t device_id, uint8_t cmdlen, char __far *cmdbuf, + uint16_t header, uint32_t length, uint8_t inout, char __far *buffer); + +/* Pointers to HW specific CD-ROM access routines. */ +cd_pkt_func pktacc[DSKTYP_CNT] = { + [DSK_TYPE_ATAPI] = { ata_cmd_packet }, +#ifdef VBOX_WITH_AHCI + [DSK_TYPE_AHCI] = { ahci_cmd_packet }, +#endif +#ifdef VBOX_WITH_SCSI + [DSK_TYPE_SCSI] = { scsi_cmd_packet }, +#endif +}; + +#if defined(VBOX_WITH_AHCI) || defined(VBOX_WITH_SCSI) +uint16_t dummy_soft_reset(uint16_t device_id) +{ + return 0; +} +#endif + +/* Generic reset routine signature. */ +typedef uint16_t (* cd_rst_func)(uint16_t device_id); + +/* Pointers to HW specific CD-ROM reset routines. */ +cd_rst_func softrst[DSKTYP_CNT] = { + [DSK_TYPE_ATAPI] = { ata_soft_reset }, +#ifdef VBOX_WITH_AHCI + [DSK_TYPE_AHCI] = { dummy_soft_reset }, +#endif +#ifdef VBOX_WITH_SCSI + [DSK_TYPE_SCSI] = { dummy_soft_reset }, +#endif +}; + + // --------------------------------------------------------------------------- // Start of El-Torito boot functions // --------------------------------------------------------------------------- -// !! TODO !! convert EBDA accesses to far pointers +// !! TODO !! convert EBDA accesses to far pointers extern int diskette_param_table; @@ -106,7 +143,7 @@ void BIOSCALL cdemu_init(void) { // @TODO: a macro or a function for getting the EBDA segment uint16_t ebda_seg = read_word(0x0040,0x000E); - + // the only important data is this one for now write_byte(ebda_seg,(uint16_t)&EbdaData->cdemu.active, 0x00); } @@ -115,7 +152,7 @@ uint8_t BIOSCALL cdemu_isactive(void) { // @TODO: a macro or a function for getting the EBDA segment uint16_t ebda_seg = read_word(0x0040,0x000E); - + return read_byte(ebda_seg,(uint16_t)&EbdaData->cdemu.active); } @@ -123,7 +160,7 @@ uint8_t BIOSCALL cdemu_emulated_drive(void) { // @TODO: a macro or a function for getting the EBDA segment uint16_t ebda_seg = read_word(0x0040,0x000E); - + return read_byte(ebda_seg,(uint16_t)&EbdaData->cdemu.emulated_drive); } @@ -139,17 +176,17 @@ void BIOSCALL int13_eltorito(disk_regs_t r) cdemu = ebda_seg :> &EbdaData->cdemu; - + BX_DEBUG_INT13_ET("%s: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", __func__, AX, BX, CX, DX, ES); // BX_DEBUG_INT13_ET("%s: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n", __func__, get_SS(), DS, ES, DI, SI); - + switch (GET_AH()) { - // FIXME ElTorito Various. Should be implemented + // FIXME ElTorito Various. Not implemented in many real BIOSes. case 0x4a: // ElTorito - Initiate disk emu case 0x4c: // ElTorito - Initiate disk emu and boot case 0x4d: // ElTorito - Return Boot catalog - BX_PANIC("%s: call with AX=%04x. Please report\n", __func__, AX); + BX_INFO("%s: call with AX=%04x not implemented.\n", __func__, AX); goto int13_fail; break; @@ -168,7 +205,7 @@ void BIOSCALL int13_eltorito(disk_regs_t r) write_byte(DS,SI+0x10,cdemu->vdevice.cylinders); write_byte(DS,SI+0x11,cdemu->vdevice.spt); write_byte(DS,SI+0x12,cdemu->vdevice.heads); - + // If we have to terminate emulation if(GET_AL() == 0x00) { // FIXME ElTorito Various. Should be handled accordingly to spec @@ -206,18 +243,18 @@ int13_success: static uint16_t device_is_cdrom(uint8_t device) { bio_dsk_t __far *bios_dsk; - + bios_dsk = read_word(0x0040, 0x000E) :> &EbdaData->bdisk; - + if (device >= BX_MAX_STORAGE_DEVICES) return 0; - + // if (bios_dsk->devices[device].type != DSK_TYPE_ATAPI) // return 0; - + if (bios_dsk->devices[device].device != DSK_DEVICE_CDROM) return 0; - + return 1; } @@ -266,11 +303,7 @@ uint16_t cdrom_boot(void) for (read_try = 0; read_try <= 4; ++read_try) { - //@todo: Use indirect calls instead? - if (device > BX_MAX_ATA_DEVICES) - error = ahci_cmd_packet(device, 12, (char __far *)&atapicmd, 0, 2048L, ATA_DATA_IN, &buffer); - else - error = ata_cmd_packet(device, 12, (char __far *)&atapicmd, 0, 2048L, ATA_DATA_IN, &buffer); + error = pktacc[bios_dsk->devices[device].type](device, 12, (char __far *)&atapicmd, 0, 2048L, ATA_DATA_IN, &buffer); if (!error) break; } @@ -303,11 +336,7 @@ uint16_t cdrom_boot(void) bios_dsk->drqp.sect_sz = 512; #endif - if (device > BX_MAX_ATA_DEVICES) - error = ahci_cmd_packet(device, 12, (char __far *)&atapicmd, 0, 2048L, ATA_DATA_IN, &buffer); - else - error = ata_cmd_packet(device, 12, (char __far *)&atapicmd, 0, 2048L, ATA_DATA_IN, &buffer); - + error = pktacc[bios_dsk->devices[device].type](device, 12, (char __far *)&atapicmd, 0, 2048L, ATA_DATA_IN, &buffer); if (error != 0) return 7; @@ -351,10 +380,16 @@ uint16_t cdrom_boot(void) nbsectors = ((uint16_t *)buffer)[0x26 / 2]; cdemu->sector_count = nbsectors; + /* Sanity check the sector count. In incorrectly mastered CDs, it might + * be zero. If it's more than 512K, reject it as well. + */ + if (nbsectors == 0 || nbsectors > 1024) + return 12; + lba = *((uint32_t *)&buffer[0x28]); cdemu->ilba = lba; - BX_DEBUG_ELTORITO("Emulate drive %02x, type %02x, LBA %lu\n", + BX_DEBUG_ELTORITO("Emulate drive %02x, type %02x, LBA %lu\n", cdemu->emulated_drive, cdemu->media, cdemu->ilba); /* Read the disk image's boot sector into memory. */ @@ -365,19 +400,16 @@ uint16_t cdrom_boot(void) bios_dsk->drqp.nsect = 1 + (nbsectors - 1) / 4; bios_dsk->drqp.sect_sz = 512; - bios_dsk->drqp.skip_a = 2048 - nbsectors * 512UL % 2048; + bios_dsk->drqp.skip_a = (2048 - nbsectors * 512) % 2048; - if (device > BX_MAX_ATA_DEVICES) - error = ahci_cmd_packet(device, 12, (char __far *)&atapicmd, 0, nbsectors*512L, ATA_DATA_IN, MK_FP(boot_segment,0)); - else - error = ata_cmd_packet(device, 12, (char __far *)&atapicmd, 0, nbsectors*512L, ATA_DATA_IN, MK_FP(boot_segment,0)); + error = pktacc[bios_dsk->devices[device].type](device, 12, (char __far *)&atapicmd, 0, nbsectors*512L, ATA_DATA_IN, MK_FP(boot_segment,0)); bios_dsk->drqp.skip_a = 0; if (error != 0) - return 12; + return 13; - BX_DEBUG_ELTORITO("Emulate drive %02x, type %02x, LBA %lu\n", + BX_DEBUG_ELTORITO("Emulate drive %02x, type %02x, LBA %lu\n", cdemu->emulated_drive, cdemu->media, cdemu->ilba); /* Set up emulated drive geometry based on the media type. */ switch (cdemu->media) { @@ -448,23 +480,29 @@ void BIOSCALL int13_cdemu(disk_regs_t r) BX_DEBUG_INT13_ET("%s: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", __func__, AX, BX, CX, DX, ES); /* at this point, we are emulating a floppy/harddisk */ - + // Recompute the device number device = cdemu->controller_index * 2; device += cdemu->device_spec; - + SET_DISK_RET_STATUS(0x00); - + /* basic checks : emulation should be active, dl should equal the emulated drive */ if (!cdemu->active || (cdemu->emulated_drive != GET_DL())) { BX_INFO("%s: function %02x, emulation not active for DL= %02x\n", __func__, GET_AH(), GET_DL()); goto int13_fail; } - + switch (GET_AH()) { - // all those functions return SUCCESS case 0x00: /* disk controller reset */ + if (pktacc[bios_dsk->devices[device].type]) + { + status = softrst[bios_dsk->devices[device].type](device); + } + goto int13_success; + break; + // all those functions return SUCCESS case 0x09: /* initialize drive parameters */ case 0x0c: /* seek to specified cylinder */ case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ? @@ -486,7 +524,7 @@ void BIOSCALL int13_cdemu(disk_regs_t r) status=read_byte(0x0040, 0x0074); SET_AH(status); SET_DISK_RET_STATUS(0); - + /* set CF if error status read */ if (status) goto int13_fail_nostatus; @@ -500,7 +538,7 @@ void BIOSCALL int13_cdemu(disk_regs_t r) vcylinders = cdemu->vdevice.cylinders; vheads = cdemu->vdevice.heads; ilba = cdemu->ilba; - + sector = GET_CL() & 0x003f; cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH(); head = GET_DH(); @@ -514,31 +552,31 @@ void BIOSCALL int13_cdemu(disk_regs_t r) // no sector to read ? if(nbsectors==0) goto int13_success; - + // sanity checks sco openserver needs this! if ((sector > vspt) || (cylinder >= vcylinders) || (head >= vheads)) { goto int13_fail; } - + // After validating the input, verify does nothing if (GET_AH() == 0x04) goto int13_success; - + segment = ES+(BX / 16); offset = BX % 16; - + // calculate the virtual lba inside the image vlba=((((uint32_t)cylinder*(uint32_t)vheads)+(uint32_t)head)*(uint32_t)vspt)+((uint32_t)(sector-1)); - + // In advance so we don't lose the count SET_AL(nbsectors); - + // start lba on cd slba = (uint32_t)vlba / 4; before = (uint32_t)vlba % 4; - + // end lba on cd elba = (uint32_t)(vlba + nbsectors - 1) / 4; @@ -547,16 +585,13 @@ void BIOSCALL int13_cdemu(disk_regs_t r) atapicmd.lba = swap_32(ilba + slba); atapicmd.nsect = swap_16(elba - slba + 1); - bios_dsk->drqp.nsect = elba - slba + 1; + bios_dsk->drqp.nsect = nbsectors; bios_dsk->drqp.sect_sz = 512; bios_dsk->drqp.skip_b = before * 512; - bios_dsk->drqp.skip_a = 2048 - nbsectors * 512UL % 2048 - bios_dsk->drqp.skip_b; + bios_dsk->drqp.skip_a = ((4 - nbsectors % 4 - before) * 512) % 2048; - if (device > BX_MAX_ATA_DEVICES) - status = ahci_cmd_packet(device, 12, (char __far *)&atapicmd, before*512, nbsectors*512L, ATA_DATA_IN, MK_FP(segment,offset)); - else - status = ata_cmd_packet(device, 12, (char __far *)&atapicmd, before*512, nbsectors*512L, ATA_DATA_IN, MK_FP(segment,offset)); + status = pktacc[bios_dsk->devices[device].type](device, 12, (char __far *)&atapicmd, before*512, nbsectors*512L, ATA_DATA_IN, MK_FP(segment,offset)); bios_dsk->drqp.skip_b = 0; bios_dsk->drqp.skip_a = 0; @@ -567,7 +602,7 @@ void BIOSCALL int13_cdemu(disk_regs_t r) SET_AL(0); goto int13_fail_noah; } - + goto int13_success; break; @@ -575,7 +610,7 @@ void BIOSCALL int13_cdemu(disk_regs_t r) vspt = cdemu->vdevice.spt; vcylinders = cdemu->vdevice.cylinders - 1; vheads = cdemu->vdevice.heads - 1; - + SET_AL( 0x00 ); SET_BL( 0x00 ); SET_CH( vcylinders & 0xff ); @@ -585,13 +620,16 @@ void BIOSCALL int13_cdemu(disk_regs_t r) // FIXME ElTorito Harddisk. should send the HD count switch (cdemu->media) { - case 0x01: SET_BL( 0x02 ); break; - case 0x02: SET_BL( 0x04 ); break; - case 0x03: SET_BL( 0x06 ); break; + case 0x01: SET_BL( 0x02 ); break; /* 1.2 MB */ + case 0x02: SET_BL( 0x04 ); break; /* 1.44 MB */ + case 0x03: SET_BL( 0x05 ); break; /* 2.88 MB */ } - DI = (uint16_t)&diskette_param_table; // @todo: or really DPT2? - ES = 0xF000; // @todo: how to make this relocatable? + /* Only set the DPT pointer for emulated floppies. */ + if (cdemu->media < 4) { + DI = (uint16_t)&diskette_param_table; // @todo: should this depend on emulated medium? + ES = 0xF000; // @todo: how to make this relocatable? + } goto int13_success; break; @@ -655,20 +693,20 @@ void BIOSCALL int13_cdrom(uint16_t EHBX, disk_regs_t r) dpt_t __far *dpt; bios_dsk = ebda_seg :> &EbdaData->bdisk; - + BX_DEBUG_INT13_CD("%s: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", __func__, AX, BX, CX, DX, ES); - + SET_DISK_RET_STATUS(0x00); - + /* basic check : device should be 0xE0+ */ if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0 + BX_MAX_STORAGE_DEVICES) ) { BX_DEBUG("%s: function %02x, ELDL out of range %02x\n", __func__, GET_AH(), GET_ELDL()); goto int13_fail; } - + // Get the ata channel device = bios_dsk->cdidmap[GET_ELDL()-0xE0]; - + /* basic check : device has to be valid */ if (device >= BX_MAX_STORAGE_DEVICES) { BX_DEBUG("%s: function %02x, unmapped device for ELDL=%02x\n", __func__, GET_AH(), GET_ELDL()); @@ -757,10 +795,7 @@ void BIOSCALL int13_cdrom(uint16_t EHBX, disk_regs_t r) bios_dsk->drqp.nsect = count; bios_dsk->drqp.sect_sz = 2048; - if (device > BX_MAX_ATA_DEVICES) - status = ahci_cmd_packet(device, 12, (char __far *)&atapicmd, 0, count*2048L, ATA_DATA_IN, MK_FP(segment,offset)); - else - status = ata_cmd_packet(device, 12, (char __far *)&atapicmd, 0, count*2048L, ATA_DATA_IN, MK_FP(segment,offset)); + status = pktacc[bios_dsk->devices[device].type](device, 12, (char __far *)&atapicmd, 0, count*2048L, ATA_DATA_IN, MK_FP(segment,offset)); count = (uint16_t)(bios_dsk->drqp.trsfbytes >> 11); i13x->count = count; |