diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
commit | f8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch) | |
tree | 26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Devices/PC/BIOS/eltorito.c | |
parent | Initial commit. (diff) | |
download | virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.zip |
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/PC/BIOS/eltorito.c')
-rw-r--r-- | src/VBox/Devices/PC/BIOS/eltorito.c | 1025 |
1 files changed, 1025 insertions, 0 deletions
diff --git a/src/VBox/Devices/PC/BIOS/eltorito.c b/src/VBox/Devices/PC/BIOS/eltorito.c new file mode 100644 index 00000000..ff309023 --- /dev/null +++ b/src/VBox/Devices/PC/BIOS/eltorito.c @@ -0,0 +1,1025 @@ +/* $Id: eltorito.c $ */ +/** @file + * PC BIOS - ??? + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * -------------------------------------------------------------------- + * + * This code is based on: + * + * ROM BIOS for use with Bochs/Plex86/QEMU emulation environment + * + * Copyright (C) 2002 MandrakeSoft S.A. + * + * MandrakeSoft S.A. + * 43, rue d'Aboukir + * 75002 Paris - France + * http://www.linux-mandrake.com/ + * http://www.mandrakesoft.com/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/* + * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice + * other than GPL or LGPL is available it will apply instead, Oracle elects to use only + * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where + * a choice of LGPL license versions is made available with the language indicating + * that LGPLv2 or any later version may be used, or where a choice of which version + * of the LGPL is applied is otherwise unspecified. + */ + + +#include <stdint.h> +#include <string.h> +#include "inlines.h" +#include "biosint.h" +#include "ebda.h" +#include "ata.h" + +#if DEBUG_ELTORITO +# define BX_DEBUG_INT13_ET(...) BX_DEBUG(__VA_ARGS__) +#else +# define BX_DEBUG_INT13_ET(...) +#endif + +#if DEBUG_INT13_CD +# define BX_DEBUG_INT13_CD(...) BX_DEBUG(__VA_ARGS__) +#else +# define BX_DEBUG_INT13_CD(...) +#endif + +#if DEBUG_CD_BOOT +# define BX_DEBUG_ELTORITO(...) BX_DEBUG(__VA_ARGS__) +#else +# define BX_DEBUG_ELTORITO(...) +#endif + + +/// @todo put in a header +#define AX r.gr.u.r16.ax +#define BX r.gr.u.r16.bx +#define CX r.gr.u.r16.cx +#define DX r.gr.u.r16.dx +#define SI r.gr.u.r16.si +#define DI r.gr.u.r16.di +#define BP r.gr.u.r16.bp +#define ELDX r.gr.u.r16.sp +#define DS r.ds +#define ES r.es +#define FLAGS r.ra.flags.u.r16.flags + +#pragma pack(1) + +/* READ_10/WRITE_10 CDB padded to 12 bytes for ATAPI. */ +typedef struct { + uint16_t command; /* Command. */ + uint32_t lba; /* LBA, MSB first! */ + uint8_t pad1; /* Unused. */ + uint16_t nsect; /* Sector count, MSB first! */ + uint8_t pad2[3]; /* Unused. */ +} cdb_atapi; + +#pragma pack() + +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 + +extern int diskette_param_table; + +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); +} + +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); +} + +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); +} + +// --------------------------------------------------------------------------- +// Start of int13 for eltorito functions +// --------------------------------------------------------------------------- + +void BIOSCALL int13_eltorito(disk_regs_t r) +{ + /// @todo a macro or a function for getting the EBDA segment + uint16_t ebda_seg=read_word(0x0040,0x000E); + cdemu_t __far *cdemu; + + 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. 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_INFO("%s: call with AX=%04x not implemented.\n", __func__, AX); + goto int13_fail; + break; + + case 0x4b: // ElTorito - Terminate disk emu + // FIXME ElTorito Hardcoded + /// @todo maybe our cdemu struct should match El Torito to allow memcpy()? + write_byte(DS,SI+0x00,0x13); + write_byte(DS,SI+0x01,cdemu->media); + write_byte(DS,SI+0x02,cdemu->emulated_drive); + write_byte(DS,SI+0x03,cdemu->controller_index); + write_dword(DS,SI+0x04,cdemu->ilba); + write_word(DS,SI+0x08,cdemu->device_spec); + write_word(DS,SI+0x0a,cdemu->buffer_segment); + write_word(DS,SI+0x0c,cdemu->load_segment); + write_word(DS,SI+0x0e,cdemu->sector_count); + 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 + cdemu->active = 0; // bye bye + } + + goto int13_success; + break; + + default: + BX_INFO("%s: unsupported AH=%02x\n", __func__, GET_AH()); + goto int13_fail; + break; + } + +int13_fail: + SET_AH(0x01); // defaults to invalid function in AH or invalid parameter + SET_DISK_RET_STATUS(GET_AH()); + SET_CF(); // error occurred + return; + +int13_success: + SET_AH(0x00); // no error + SET_DISK_RET_STATUS(0x00); + CLEAR_CF(); // no error + return; +} + +// --------------------------------------------------------------------------- +// End of int13 for eltorito functions +// --------------------------------------------------------------------------- + +/* Utility routine to check if a device is a CD-ROM. */ +/// @todo this function is kinda useless as the ATAPI type check is obsolete. +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; +} + +// --------------------------------------------------------------------------- +// End of ATA/ATAPI generic functions +// --------------------------------------------------------------------------- +static const char isotag[]="CD001"; +static const char eltorito[]="EL TORITO SPECIFICATION"; +// +// Returns ah: emulated drive, al: error code +// +uint16_t cdrom_boot(void) +{ + /// @todo a macro or a function for getting the EBDA segment + uint16_t ebda_seg=read_word(0x0040,0x000E); + uint8_t buffer[2048]; + cdb_atapi atapicmd; + uint32_t lba; + uint16_t boot_segment, nbsectors, i, error; + uint8_t device; + uint8_t read_try; + cdemu_t __far *cdemu; + bio_dsk_t __far *bios_dsk; + + cdemu = ebda_seg :> &EbdaData->cdemu; + bios_dsk = ebda_seg :> &EbdaData->bdisk; + + /* Find the first CD-ROM. */ + for (device = 0; device < BX_MAX_STORAGE_DEVICES; ++device) { + if (device_is_cdrom(device)) + break; + } + + /* Fail if not found. */ + if (device >= BX_MAX_STORAGE_DEVICES) + return 2; + + /* Read the Boot Record Volume Descriptor (BRVD). */ + _fmemset(&atapicmd, 0, sizeof(atapicmd)); + atapicmd.command = 0x28; // READ 10 command + atapicmd.lba = swap_32(0x11); + atapicmd.nsect = swap_16(1); + + bios_dsk->drqp.nsect = 1; + bios_dsk->drqp.sect_sz = 2048; + + for (read_try = 0; read_try <= 4; ++read_try) + { + error = pktacc[bios_dsk->devices[device].type](device, 12, (char __far *)&atapicmd, 0, 2048L, ATA_DATA_IN, &buffer); + if (!error) + break; + } + if (error) + return 3; + + /* Check for a valid BRVD. */ + if (buffer[0] != 0) + return 4; + /// @todo what's wrong with memcmp()? + for (i = 0; i < 5; ++i) { + if (buffer[1+i] != isotag[i]) + return 5; + } + for (i = 0; i < 23; ++i) + if (buffer[7+i] != eltorito[i]) + return 6; + + // ok, now we calculate the Boot catalog address + lba = *((uint32_t *)&buffer[0x47]); + BX_DEBUG_ELTORITO("BRVD at LBA %lx\n", lba); + + /* Now we read the Boot Catalog. */ + atapicmd.command = 0x28; // READ 10 command + atapicmd.lba = swap_32(lba); + atapicmd.nsect = swap_16(1); + +#if 0 // Not necessary as long as previous values are reused + bios_dsk->drqp.nsect = 1; + bios_dsk->drqp.sect_sz = 512; +#endif + + error = pktacc[bios_dsk->devices[device].type](device, 12, (char __far *)&atapicmd, 0, 2048L, ATA_DATA_IN, &buffer); + if (error != 0) + return 7; + + /// @todo Define a struct for the Boot Catalog, the hardcoded offsets are so dumb... + + /* Check if the Boot Catalog looks valid. */ + 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 + + cdemu->media = buffer[0x21]; + if (buffer[0x21] == 0) { + // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0. + // Win2000 cd boot needs to know it booted from cd + cdemu->emulated_drive = 0xE0; + } + else if (buffer[0x21] < 4) + cdemu->emulated_drive = 0x00; + else + cdemu->emulated_drive = 0x80; + + cdemu->controller_index = device / 2; + cdemu->device_spec = device % 2; + + boot_segment = *((uint16_t *)&buffer[0x22]); + if (boot_segment == 0) + boot_segment = 0x07C0; + + cdemu->load_segment = boot_segment; + cdemu->buffer_segment = 0x0000; + + 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", + cdemu->emulated_drive, cdemu->media, cdemu->ilba); + + /* Read the disk image's boot sector into memory. */ + atapicmd.command = 0x28; // READ 10 command + atapicmd.lba = swap_32(lba); + atapicmd.nsect = swap_16(1 + (nbsectors - 1) / 4); + + bios_dsk->drqp.nsect = 1 + (nbsectors - 1) / 4; + bios_dsk->drqp.sect_sz = 512; + + bios_dsk->drqp.skip_a = (2048 - nbsectors * 512) % 2048; + + 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 13; + + 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) { + case 0x01: /* 1.2M floppy */ + cdemu->vdevice.spt = 15; + cdemu->vdevice.cylinders = 80; + cdemu->vdevice.heads = 2; + break; + case 0x02: /* 1.44M floppy */ + cdemu->vdevice.spt = 18; + cdemu->vdevice.cylinders = 80; + cdemu->vdevice.heads = 2; + break; + case 0x03: /* 2.88M floppy */ + cdemu->vdevice.spt = 36; + cdemu->vdevice.cylinders = 80; + cdemu->vdevice.heads = 2; + break; + case 0x04: /* Hard disk */ + cdemu->vdevice.spt = read_byte(boot_segment,446+6)&0x3f; + cdemu->vdevice.cylinders = ((read_byte(boot_segment,446+6)&~0x3f)<<2) + read_byte(boot_segment,446+7) + 1; + cdemu->vdevice.heads = read_byte(boot_segment,446+5) + 1; + break; + } + BX_DEBUG_ELTORITO("VCHS=%u/%u/%u\n", cdemu->vdevice.cylinders, + cdemu->vdevice.heads, cdemu->vdevice.spt); + + if (cdemu->media != 0) { + /* Increase BIOS installed number of drives (floppy or fixed). */ + if (cdemu->emulated_drive == 0x00) + write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41); + else + write_byte(ebda_seg,(uint16_t)&EbdaData->bdisk.hdcount, read_byte(ebda_seg, (uint16_t)&EbdaData->bdisk.hdcount) + 1); + } + + // everything is ok, so from now on, the emulation is active + if (cdemu->media != 0) + cdemu->active = 0x01; + + // return the boot drive + no error + return (cdemu->emulated_drive*0x100)+0; +} + +// --------------------------------------------------------------------------- +// End of El-Torito boot functions +// --------------------------------------------------------------------------- + +// --------------------------------------------------------------------------- +// Start of int13 when emulating a device from the cd +// --------------------------------------------------------------------------- + +void BIOSCALL int13_cdemu(disk_regs_t r) +{ + /// @todo a macro or a function for getting the EBDA segment + uint16_t ebda_seg=read_word(0x0040,0x000E); + uint8_t device, status; + uint16_t vheads, vspt, vcylinders; + uint16_t head, sector, cylinder, nbsectors; + uint32_t vlba, ilba, slba, elba; + uint16_t before, segment, offset; + cdb_atapi atapicmd; + cdemu_t __far *cdemu; + bio_dsk_t __far *bios_dsk; + int13ext_t __far *i13x; + uint32_t lba; + uint16_t count; + + cdemu = ebda_seg :> &EbdaData->cdemu; + bios_dsk = ebda_seg :> &EbdaData->bdisk; + + 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()) { + + 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 ? + case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ? + case 0x11: /* recalibrate */ + case 0x14: /* controller internal diagnostic */ + case 0x16: /* detect disk change */ + goto int13_success; + break; + + // all those functions return disk write-protected + case 0x03: /* write disk sectors */ + case 0x05: /* format disk track */ + SET_AH(0x03); + goto int13_fail_noah; + break; + + case 0x01: /* read disk status */ + 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; + else + goto int13_success_noah; + break; + + case 0x02: // read disk sectors + case 0x04: // verify disk sectors + vspt = cdemu->vdevice.spt; + 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(); + nbsectors = GET_AL(); + segment = ES; + offset = BX; + + BX_DEBUG_INT13_ET("%s: read to %04x:%04x @ VCHS %u/%u/%u (%u sectors)\n", __func__, + ES, BX, cylinder, head, sector, nbsectors); + + // 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; + + _fmemset(&atapicmd, 0, sizeof(atapicmd)); + atapicmd.command = 0x28; // READ 10 command + atapicmd.lba = swap_32(ilba + slba); + atapicmd.nsect = swap_16(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 = ((4 - nbsectors % 4 - before) * 512) % 2048; + + 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; + + if (status != 0) { + BX_INFO("%s: function %02x, error %02x !\n", __func__, GET_AH(), status); + SET_AH(0x02); + SET_AL(0); + goto int13_fail_noah; + } + + goto int13_success; + break; + + case 0x08: /* read disk drive parameters */ + vspt = cdemu->vdevice.spt; + vcylinders = cdemu->vdevice.cylinders - 1; + vheads = cdemu->vdevice.heads - 1; + + SET_AL( 0x00 ); + SET_BL( 0x00 ); + SET_CH( vcylinders & 0xff ); + SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f )); + SET_DH( vheads ); + SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2 + // FIXME ElTorito Harddisk. should send the HD count + + switch (cdemu->media) { + 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 */ + } + + /* 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; + + case 0x15: /* read disk drive size */ + // FIXME ElTorito Harddisk. What geometry to send ? + SET_AH(0x03); + goto int13_success_noah; + break; + + case 0x41: // IBM/MS installation check + BX = 0xaa55; // install check + SET_AH(0x30); // EDD 2.1 + CX = 0x0007; // ext disk access, removable and edd + goto int13_success_noah; + break; + + case 0x42: // IBM/MS extended read + case 0x44: // IBM/MS verify sectors + case 0x47: // IBM/MS extended seek + + /* Load the I13X struct pointer. */ + i13x = MK_FP(DS, SI); + + count = i13x->count; + segment = i13x->segment; + offset = i13x->offset; + + // Can't use 64 bits lba + lba = i13x->lba2; + if (lba != 0L) { + BX_PANIC("%s: function %02x. Can't use 64bits lba\n", __func__, GET_AH()); + goto int13_fail; + } + + // Get 32 bits lba + lba = i13x->lba1; + + // If verify or seek + if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 )) + goto int13_success; + + BX_DEBUG_INT13_ET("%s: read %u sectors @ LBA %lu to %04X:%04X\n", + __func__, count, lba, segment, offset); + + nbsectors = count; + vlba = lba; + ilba = cdemu->ilba; + + // 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; + + _fmemset(&atapicmd, 0, sizeof(atapicmd)); + atapicmd.command = 0x28; // READ 10 command + atapicmd.lba = swap_32(ilba + slba); + atapicmd.nsect = swap_16(elba - slba + 1); + + bios_dsk->drqp.skip_b = before * 512; + bios_dsk->drqp.skip_a = ((4 - nbsectors % 4 - before) * 512) % 2048; + + 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; + + count = (uint16_t)(bios_dsk->drqp.trsfbytes >> 9); + i13x->count = count; + + if (status != 0) { + BX_INFO("%s: function %02x, status %02x !\n", __func__, GET_AH(), status); + SET_AH(0x0c); + goto int13_fail_noah; + } + + goto int13_success; + break; + + case 0x48: // IBM/MS get drive parameters + if (edd_fill_dpt(DS :> (dpt_t *)SI, bios_dsk, device)) + goto int13_fail; + else + goto int13_success; + break; + + // all those functions return unimplemented + case 0x0a: /* read disk sectors with ECC */ + case 0x0b: /* write disk sectors with ECC */ + case 0x18: /* set media type for format */ + case 0x43: // IBM/MS extended write + case 0x45: // IBM/MS lock/unlock drive + case 0x46: // IBM/MS eject media + case 0x49: // IBM/MS extended media change + case 0x4e: // ? - set hardware configuration + case 0x50: // ? - send packet command + default: + BX_INFO("%s: function AH=%02x unsupported, returns fail\n", __func__, GET_AH()); + goto int13_fail; + break; + } + +int13_fail: + SET_AH(0x01); // defaults to invalid function in AH or invalid parameter +int13_fail_noah: + SET_DISK_RET_STATUS(GET_AH()); +int13_fail_nostatus: + SET_CF(); // error occurred + return; + +int13_success: + SET_AH(0x00); // no error +int13_success_noah: + SET_DISK_RET_STATUS(0x00); + CLEAR_CF(); // no error + return; +} + +// --------------------------------------------------------------------------- +// Start of int13 for cdrom +// --------------------------------------------------------------------------- + +void BIOSCALL int13_cdrom(uint16_t EHBX, disk_regs_t r) +{ + uint16_t ebda_seg = read_word(0x0040,0x000E); + uint8_t device, status, locks; + cdb_atapi atapicmd; + uint32_t lba; + uint16_t count, segment, offset; + bio_dsk_t __far *bios_dsk; + int13ext_t __far *i13x; + + 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()); + goto int13_fail; + } + + switch (GET_AH()) { + + // all those functions return SUCCESS + case 0x00: /* disk controller reset */ + case 0x09: /* initialize drive parameters */ + case 0x0c: /* seek to specified cylinder */ + case 0x0d: /* alternate disk reset */ + case 0x10: /* check drive ready */ + case 0x11: /* recalibrate */ + case 0x14: /* controller internal diagnostic */ + case 0x16: /* detect disk change */ + goto int13_success; + break; + + // all those functions return disk write-protected + case 0x03: /* write disk sectors */ + case 0x05: /* format disk track */ + case 0x43: // IBM/MS extended write + SET_AH(0x03); + goto int13_fail_noah; + break; + + case 0x01: /* read disk status */ + 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; + else + goto int13_success_noah; + break; + + case 0x15: /* read disk drive size */ + SET_AH(0x02); + goto int13_fail_noah; + break; + + case 0x41: // IBM/MS installation check + BX = 0xaa55; // install check + SET_AH(0x30); // EDD 2.1 + CX = 0x0007; // ext disk access, removable and edd + goto int13_success_noah; + break; + + case 0x42: // IBM/MS extended read + case 0x44: // IBM/MS verify sectors + case 0x47: // IBM/MS extended seek + + /* Load the I13X struct pointer. */ + i13x = MK_FP(DS, SI); + + count = i13x->count; + segment = i13x->segment; + offset = i13x->offset; + + // Can't use 64 bits lba + lba = i13x->lba2; + if (lba != 0L) { + BX_PANIC("%s: function %02x. Can't use 64bits lba\n", __func__, GET_AH()); + goto int13_fail; + } + + // Get 32 bits lba + lba = i13x->lba1; + + // If verify or seek + if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 )) + goto int13_success; + + BX_DEBUG_INT13_CD("%s: read %u sectors @ LBA %lu to %04X:%04X\n", + __func__, count, lba, segment, offset); + + _fmemset(&atapicmd, 0, sizeof(atapicmd)); + atapicmd.command = 0x28; // READ 10 command + atapicmd.lba = swap_32(lba); + atapicmd.nsect = swap_16(count); + + bios_dsk->drqp.nsect = count; + bios_dsk->drqp.sect_sz = 2048; + + 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; + + if (status != 0) { + BX_INFO("%s: function %02x, status %02x !\n", __func__, GET_AH(), status); + SET_AH(0x0c); + goto int13_fail_noah; + } + + goto int13_success; + break; + + case 0x45: // IBM/MS lock/unlock drive + if (GET_AL() > 2) + goto int13_fail; + + locks = bios_dsk->devices[device].lock; + + switch (GET_AL()) { + case 0 : // lock + if (locks == 0xff) { + SET_AH(0xb4); + SET_AL(1); + goto int13_fail_noah; + } + bios_dsk->devices[device].lock = ++locks; + SET_AL(1); + break; + case 1 : // unlock + if (locks == 0x00) { + SET_AH(0xb0); + SET_AL(0); + goto int13_fail_noah; + } + bios_dsk->devices[device].lock = --locks; + SET_AL(locks==0?0:1); + break; + case 2 : // status + SET_AL(locks==0?0:1); + break; + } + goto int13_success; + break; + + case 0x46: // IBM/MS eject media + locks = bios_dsk->devices[device].lock; + + if (locks != 0) { + SET_AH(0xb1); // media locked + goto int13_fail_noah; + } + // FIXME should handle 0x31 no media in device + // FIXME should handle 0xb5 valid request failed + +#if 0 /// @todo implement! + // Call removable media eject + ASM_START + push bp + mov bp, sp + + mov ah, #0x52 + int #0x15 + mov _int13_cdrom.status + 2[bp], ah + jnc int13_cdrom_rme_end + mov _int13_cdrom.status, #1 +int13_cdrom_rme_end: + pop bp + ASM_END +#endif + + if (status != 0) { + SET_AH(0xb1); // media locked + goto int13_fail_noah; + } + + goto int13_success; + break; + + case 0x48: // IBM/MS get drive parameters + if (edd_fill_dpt(DS :> (dpt_t *)SI, bios_dsk, device)) + goto int13_fail; + else + goto int13_success; + break; + + case 0x49: // IBM/MS extended media change + // always send changed ?? + SET_AH(06); + goto int13_fail_nostatus; + break; + + case 0x4e: // // IBM/MS set hardware configuration + // DMA, prefetch, PIO maximum not supported + switch (GET_AL()) { + case 0x01: + case 0x03: + case 0x04: + case 0x06: + goto int13_success; + break; + default : + goto int13_fail; + } + break; + + // all those functions return unimplemented + case 0x02: /* read sectors */ + case 0x04: /* verify sectors */ + case 0x08: /* read disk drive parameters */ + case 0x0a: /* read disk sectors with ECC */ + case 0x0b: /* write disk sectors with ECC */ + case 0x18: /* set media type for format */ + case 0x50: // ? - send packet command + default: + BX_INFO("%s: unsupported AH=%02x\n", __func__, GET_AH()); + goto int13_fail; + break; + } + +int13_fail: + SET_AH(0x01); // defaults to invalid function in AH or invalid parameter +int13_fail_noah: + SET_DISK_RET_STATUS(GET_AH()); +int13_fail_nostatus: + SET_CF(); // error occurred + return; + +int13_success: + SET_AH(0x00); // no error +int13_success_noah: + SET_DISK_RET_STATUS(0x00); + CLEAR_CF(); // no error + return; +} + +// --------------------------------------------------------------------------- +// End of int13 for cdrom +// --------------------------------------------------------------------------- + |