summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/PC/BIOS/eltorito.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Devices/PC/BIOS/eltorito.c
parentInitial commit. (diff)
downloadvirtualbox-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.c1025
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
+// ---------------------------------------------------------------------------
+