Fix bug causing ata_cmd_packet to insl to wrong address.
Add new cdrom_read helper.
Join ata.hdidmap/cdidmap into one array variable.
Rename CONFIG_ELTORITO_BOOT to CONFIG_CDROM_BOOT.
Add cd emulation code.
// 3 : error
// 4 : not ready
u16
-ata_cmd_packet(u16 device, u16 cmdbuf, u8 cmdlen, u16 header
+ata_cmd_packet(u16 device, u8 *cmdbuf, u8 cmdlen, u16 header
, u32 length, u8 inout, u16 bufseg, u16 bufoff)
{
u16 iobase1, iobase2;
// Send command to device
irq_enable();
- outsw(iobase1, GET_SEG(SS), cmdbuf, cmdlen);
+ outsw(iobase1, GET_SEG(SS), (u32)cmdbuf, cmdlen);
if (inout == ATA_DATA_NO) {
await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
status = inb(iobase1 + ATA_CB_STAT);
- }
- else {
+ } else {
u16 loops = 0;
u8 sc;
while (1) {
if (loops == 0) {//first time through
status = inb(iobase2 + ATA_CB_ASTAT);
await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
- }
- else
+ } else
await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
loops++;
lcount = ((u16)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
// adjust to read what we want
- if(header>lcount) {
+ if (header > lcount) {
lbefore=lcount;
header-=lcount;
lcount=0;
- }
- else {
+ } else {
lbefore=header;
header=0;
lcount-=lbefore;
}
- if(lcount>length) {
+ if (lcount > length) {
lafter=lcount-length;
lcount=length;
length=0;
- }
- else {
+ } else {
lafter=0;
length-=lcount;
}
inw(iobase1);
if (lmode == ATA_MODE_PIO32)
- insl(iobase1, bufoff, bufseg, lcount);
+ insl(iobase1, bufseg, bufoff, lcount);
else
- insw(iobase1, bufoff, bufseg, lcount);
+ insw(iobase1, bufseg, bufoff, lcount);
for (i=0; i<lafter; i++)
if (lmode == ATA_MODE_PIO32)
return 0;
}
+u16
+cdrom_read(u16 device, u32 lba, u32 count, u16 segment, u16 offset, u16 skip)
+{
+ u16 sectors = (count + 2048 - 1) / 2048;
+
+ u8 atacmd[12];
+ memset(atacmd, 0, sizeof(atacmd));
+ atacmd[0]=0x28; // READ command
+ atacmd[7]=(sectors & 0xff00) >> 8; // Sectors
+ atacmd[8]=(sectors & 0x00ff); // Sectors
+ atacmd[2]=(lba & 0xff000000) >> 24; // LBA
+ atacmd[3]=(lba & 0x00ff0000) >> 16;
+ atacmd[4]=(lba & 0x0000ff00) >> 8;
+ atacmd[5]=(lba & 0x000000ff);
+
+ return ata_cmd_packet(device, atacmd, sizeof(atacmd)
+ , skip, count, ATA_DATA_IN
+ , segment, offset);
+}
+
// ---------------------------------------------------------------------------
// ATA/ATAPI driver : device detection
// ---------------------------------------------------------------------------
SET_EBDA(ata.devices[device].lchs.spt, spt);
// fill hdidmap
- SET_EBDA(ata.hdidmap[hdcount], device);
+ SET_EBDA(ata.idmap[0][hdcount], device);
hdcount++;
}
SET_EBDA(ata.devices[device].blksize, blksize);
// fill cdidmap
- SET_EBDA(ata.cdidmap[cdcount], device);
+ SET_EBDA(ata.idmap[1][cdcount], device);
cdcount++;
}
, u16 head, u16 sector, u32 lba, u16 segment, u16 offset);
u16 ata_cmd_data_out(u16 device, u16 command, u16 count, u16 cylinder
, u16 head, u16 sector, u32 lba, u16 segment, u16 offset);
-u16 ata_cmd_packet(u16 device, u16 cmdbuf, u8 cmdlen, u16 header
+u16 ata_cmd_packet(u16 device, u8 *cmdbuf, u8 cmdlen, u16 header
, u32 length, u8 inout, u16 bufseg, u16 bufoff);
+u16 cdrom_read(u16 device, u32 lba, u32 count
+ , u16 segment, u16 offset, u16 skip);
void ata_detect();
// Global defines -- ATA register and register bits.
// ATA devices info
struct ata_device_s devices[CONFIG_MAX_ATA_DEVICES];
//
- // map between (bios hd id - 0x80) and ata channels
- u8 hdcount, hdidmap[CONFIG_MAX_ATA_DEVICES];
-
- // map between (bios cd id - 0xE0) and ata channels
- u8 cdcount, cdidmap[CONFIG_MAX_ATA_DEVICES];
+ // map between bios hd/cd id and ata channels
+ u8 hdcount, cdcount;
+ u8 idmap[2][CONFIG_MAX_ATA_DEVICES];
// Buffer for DPTE table
struct dpte_s dpte;
#include "config.h" // CONFIG_*
#include "cmos.h" // inb_cmos
#include "ata.h" // ata_detect
+#include "disk.h" // cdrom_boot
// We need a copy of this string, but we are not actually a PnP BIOS,
// so make sure it is *not* aligned, so OSes will not see it if they
u8 bootdrv = 0;
u16 bootdev, bootip;
- if (CONFIG_ELTORITO_BOOT) {
+ if (CONFIG_CDROM_BOOT) {
bootdev = inb_cmos(CMOS_BIOS_BOOTFLAG2);
bootdev |= ((inb_cmos(CMOS_BIOS_BOOTFLAG1) & 0xf0) << 4);
bootdev >>= 4 * seq_nr;
bootip = (bootseg & 0x0fff) << 4;
bootseg &= 0xf000;
break;
- case IPL_TYPE_CDROM: /* CD-ROM */
- // XXX
- return;
+ case IPL_TYPE_CDROM: {
+ /* CD-ROM */
+ if (! CONFIG_CDROM_BOOT)
+ break;
+ u16 status = cdrom_boot();
+ if (status) {
+ printf("CDROM boot failure code : %04x\n", status);
+ print_boot_failure(type, 1);
+ return;
+ }
+
+ bootdrv = GET_EBDA(cdemu.emulated_drive);
+ bootseg = GET_EBDA(cdemu.load_segment);
+ /* Canonicalize bootseg:bootip */
+ bootip = (bootseg & 0x0fff) << 4;
+ bootseg &= 0xf000;
break;
+ }
case IPL_TYPE_BEV: {
/* Expansion ROM with a Bootstrap Entry Vector (a far
* pointer) */
#include "util.h" // memset
#include "ata.h" // ATA_CMD_READ_SECTORS
+#define DEBUGF1(fmt, args...) bprintf(0, fmt , ##args)
+#define DEBUGF(fmt, args...)
+
/****************************************************************
* CDROM functions
disk_ret(regs, DISK_RET_SUCCESS);
}
+
+
+/****************************************************************
+ * CD booting
+ ****************************************************************/
+
+// Request SENSE
+static u16
+atapi_get_sense(u16 device, u8 *asc, u8 *ascq)
+{
+ u8 buffer[18];
+ u8 atacmd[12];
+ memset(atacmd, 0, sizeof(atacmd));
+ atacmd[0] = ATA_CMD_REQUEST_SENSE;
+ atacmd[4] = sizeof(buffer);
+ u16 ret = ata_cmd_packet(device, atacmd, sizeof(atacmd)
+ , 18L, 0, ATA_DATA_IN, GET_SEG(SS), (u32)buffer);
+ if (ret != 0)
+ return 0x0002;
+
+ *asc = buffer[12];
+ *ascq = buffer[13];
+
+ return 0;
+}
+
+static u16
+atapi_is_ready(u16 device)
+{
+ if (GET_EBDA(ata.devices[device].type) != ATA_TYPE_ATAPI) {
+ printf("not implemented for non-ATAPI device\n");
+ return -1;
+ }
+
+ DEBUGF("ata_detect_medium: begin\n");
+ u8 packet[12];
+ memset(packet, 0, sizeof(packet));
+ packet[0] = 0x25; /* READ CAPACITY */
+
+ /* Retry READ CAPACITY 50 times unless MEDIUM NOT PRESENT
+ * is reported by the device. If the device reports "IN PROGRESS",
+ * 30 seconds is added. */
+ u8 buf[8];
+ u32 timeout = 5000;
+ u32 time = 0;
+ u8 in_progress = 0;
+ for (;; time+=100) {
+ if (time >= timeout) {
+ DEBUGF("read capacity failed\n");
+ return -1;
+ }
+ u16 ret = ata_cmd_packet(device, packet, sizeof(packet)
+ , 0, 8L, ATA_DATA_IN, GET_SEG(SS), (u32)buf);
+ if (ret == 0)
+ break;
+
+ u8 asc=0, ascq=0;
+ ret = atapi_get_sense(device, &asc, &ascq);
+ if (!ret)
+ continue;
+
+ if (asc == 0x3a) { /* MEDIUM NOT PRESENT */
+ DEBUGF("Device reports MEDIUM NOT PRESENT\n");
+ return -1;
+ }
+
+ if (asc == 0x04 && ascq == 0x01 && !in_progress) {
+ /* IN PROGRESS OF BECOMING READY */
+ printf("Waiting for device to detect medium... ");
+ /* Allow 30 seconds more */
+ timeout = 30000;
+ in_progress = 1;
+ }
+ }
+
+ u32 block_len = (u32) buf[4] << 24
+ | (u32) buf[5] << 16
+ | (u32) buf[6] << 8
+ | (u32) buf[7] << 0;
+
+ if (block_len != 2048 && block_len != 512) {
+ printf("Unsupported sector size %u\n", block_len);
+ return -1;
+ }
+ SET_EBDA(ata.devices[device].blksize, block_len);
+
+ u32 sectors = (u32) buf[0] << 24
+ | (u32) buf[1] << 16
+ | (u32) buf[2] << 8
+ | (u32) buf[3] << 0;
+
+ DEBUGF("sectors=%u\n", sectors);
+ if (block_len == 2048)
+ sectors <<= 2; /* # of sectors in 512-byte "soft" sector */
+ if (sectors != GET_EBDA(ata.devices[device].sectors))
+ printf("%dMB medium detected\n", sectors>>(20-9));
+ SET_EBDA(ata.devices[device].sectors, sectors);
+ return 0;
+}
+
+static u16
+atapi_is_cdrom(u8 device)
+{
+ if (device >= CONFIG_MAX_ATA_DEVICES)
+ return 0;
+
+ if (GET_EBDA(ata.devices[device].type) != ATA_TYPE_ATAPI)
+ return 0;
+
+ if (GET_EBDA(ata.devices[device].device) != ATA_DEVICE_CDROM)
+ return 0;
+
+ return 1;
+}
+
+// Compare a string on the stack to one in the code segment.
+static int
+streq_cs(u8 *s1, char *cs_s2)
+{
+ u8 *s2 = (u8*)cs_s2;
+ for (;;) {
+ if (*s1 != GET_VAR(CS, *s2))
+ return 0;
+ if (! *s1)
+ return 1;
+ s1++;
+ s2++;
+ }
+}
+
+u16
+cdrom_boot()
+{
+ // Find out the first cdrom
+ u8 device;
+ for (device=0; device<CONFIG_MAX_ATA_DEVICES; device++)
+ if (atapi_is_cdrom(device))
+ break;
+
+ u16 ret = atapi_is_ready(device);
+ if (ret)
+ BX_INFO("ata_is_ready returned %d\n", ret);
+
+ // if not found
+ if (device >= CONFIG_MAX_ATA_DEVICES)
+ return 2;
+
+ // Read the Boot Record Volume Descriptor
+ u8 buffer[2048];
+ ret = cdrom_read(device, 0x11, 2048, GET_SEG(SS), (u32)buffer, 0);
+ if (ret)
+ return 3;
+
+ // Validity checks
+ if (buffer[0])
+ return 4;
+ if (!streq_cs(&buffer[1], "CD001\001EL TORITO SPECIFICATION"))
+ return 5;
+
+ // ok, now we calculate the Boot catalog address
+ u32 lba = *(u32*)&buffer[0x47];
+
+ // And we read the Boot Catalog
+ ret = cdrom_read(device, lba, 2048, GET_SEG(SS), (u32)buffer, 0);
+ 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
+
+ SET_EBDA(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
+ SET_EBDA(cdemu.emulated_drive, 0xE0);
+ else if (buffer[0x21] < 4)
+ SET_EBDA(cdemu.emulated_drive, 0x00);
+ else
+ SET_EBDA(cdemu.emulated_drive, 0x80);
+
+ SET_EBDA(cdemu.controller_index, device/2);
+ SET_EBDA(cdemu.device_spec, device%2);
+
+ u16 boot_segment = *(u16*)&buffer[0x22];
+ if (!boot_segment)
+ boot_segment = 0x07C0;
+
+ SET_EBDA(cdemu.load_segment,boot_segment);
+ SET_EBDA(cdemu.buffer_segment,0x0000);
+
+ u16 nbsectors = *(u16*)&buffer[0x26];
+ SET_EBDA(cdemu.sector_count, nbsectors);
+
+ lba = *(u32*)&buffer[0x28];
+ SET_EBDA(cdemu.ilba, lba);
+
+ // And we read the image in memory
+ ret = cdrom_read(device, lba, nbsectors*512
+ , boot_segment, 0, 0);
+ if (ret)
+ return 12;
+
+ // Remember the media type
+ switch (GET_EBDA(cdemu.media)) {
+ case 0x01: // 1.2M floppy
+ SET_EBDA(cdemu.vdevice.spt, 15);
+ SET_EBDA(cdemu.vdevice.cylinders, 80);
+ SET_EBDA(cdemu.vdevice.heads, 2);
+ break;
+ case 0x02: // 1.44M floppy
+ SET_EBDA(cdemu.vdevice.spt, 18);
+ SET_EBDA(cdemu.vdevice.cylinders, 80);
+ SET_EBDA(cdemu.vdevice.heads, 2);
+ break;
+ case 0x03: // 2.88M floppy
+ SET_EBDA(cdemu.vdevice.spt, 36);
+ SET_EBDA(cdemu.vdevice.cylinders, 80);
+ SET_EBDA(cdemu.vdevice.heads, 2);
+ break;
+ case 0x04: { // Harddrive
+ u16 spt = GET_FARVAR(boot_segment,*(u8*)(446+6));
+ u16 cyl = (spt << 2) + GET_FARVAR(boot_segment,*(u8*)(446+7)) + 1;
+ u16 heads = GET_FARVAR(boot_segment,*(u8*)(446+5)) + 1;
+ SET_EBDA(cdemu.vdevice.spt, spt & 0x3f);
+ SET_EBDA(cdemu.vdevice.cylinders, cyl);
+ SET_EBDA(cdemu.vdevice.heads, heads);
+ break;
+ }
+ }
+
+ if (GET_EBDA(cdemu.media) != 0) {
+ // Increase bios installed hardware number of devices
+ if (GET_EBDA(cdemu.emulated_drive) == 0x00)
+ SETBITS_BDA(equipment_list_flags, 0x41);
+ else
+ SET_EBDA(ata.hdcount, GET_EBDA(ata.hdcount) + 1);
+ }
+
+ // everything is ok, so from now on, the emulation is active
+ if (GET_EBDA(cdemu.media))
+ SET_EBDA(cdemu.active, 0x01);
+
+ return 0;
+}
#define CONFIG_PS2_MOUSE 1
#define CONFIG_ATA 1
#define CONFIG_KBD_CALL_INT15_4F 1
-#define CONFIG_ELTORITO_BOOT 1
+#define CONFIG_CDROM_BOOT 1
#define CONFIG_MAX_ATA_INTERFACES 4
#define CONFIG_MAX_ATA_DEVICES (CONFIG_MAX_ATA_INTERFACES*2)
void
emu_access(struct bregs *regs, u8 device, u16 command)
{
- u16 nbsectors = regs->al;
- u16 cylinder = regs->ch | ((((u16) regs->cl) << 2) & 0x300);
- u16 sector = regs->cl & 0x3f;
- u16 head = regs->dh;
+ u16 count = regs->al;
+ u16 cylinder = regs->ch | ((((u16) regs->cl) << 2) & 0x300);
+ u16 sector = regs->cl & 0x3f;
+ u16 head = regs->dh;
- if ((nbsectors > 128) || (nbsectors == 0) || (sector == 0)) {
+ if ((count > 128) || (count == 0) || (sector == 0)) {
BX_INFO("int13_harddisk: function %02x, parameter out of range!\n"
, regs->ah);
disk_ret(regs, DISK_RET_EPARAM);
// start lba on cd
u32 slba = (u32)vlba/4;
u16 before= (u16)vlba%4;
- // end lba on cd
- u32 elba = (u32)(vlba+nbsectors-1)/4;
- u32 count = elba-slba+1;
- u32 lba = ilba+slba;
+ u32 lba = ilba + slba;
u16 segment = regs->es;
u16 offset = regs->bx;
- u8 atacmd[12];
- memset(atacmd, 0, sizeof(atacmd));
- atacmd[0]=0x28; // READ command
- atacmd[7]=(count & 0xff00) >> 8; // Sectors
- atacmd[8]=(count & 0x00ff); // Sectors
- atacmd[2]=(lba & 0xff000000) >> 24; // LBA
- atacmd[3]=(lba & 0x00ff0000) >> 16;
- atacmd[4]=(lba & 0x0000ff00) >> 8;
- atacmd[5]=(lba & 0x000000ff);
-
- u8 status = ata_cmd_packet(device, (u32)atacmd, sizeof(atacmd)
- , before*512, count*2048L
- , ATA_DATA_IN, segment, offset);
-
+ u8 status = cdrom_read(device, lba, count*512, segment, offset, before*512);
if (status != 0) {
BX_INFO("int13_harddisk: function %02x, error %02x !\n",regs->ah,status);
regs->al = 0;
disk_ret(regs, DISK_RET_EBADTRACK);
}
- regs->al = nbsectors;
+ regs->al = count;
disk_ret(regs, DISK_RET_SUCCESS);
}
u8 status;
switch (command) {
case ATA_CMD_READ_SECTORS:
- if (type == ATA_TYPE_ATA) {
+ if (type == ATA_TYPE_ATA)
status = ata_cmd_data_in(device, ATA_CMD_READ_SECTORS
, count, 0, 0, 0
, lba, segment, offset);
- } else {
- u8 atacmd[12];
- memset(atacmd, 0, sizeof(atacmd));
- atacmd[0]=0x28; // READ command
- atacmd[7]=(count & 0xff00) >> 8; // Sectors
- atacmd[8]=(count & 0x00ff); // Sectors
- atacmd[2]=(lba & 0xff000000) >> 24; // LBA
- atacmd[3]=(lba & 0x00ff0000) >> 16;
- atacmd[4]=(lba & 0x0000ff00) >> 8;
- atacmd[5]=(lba & 0x000000ff);
-
- status = ata_cmd_packet(device, (u32)atacmd, sizeof(atacmd)
- , 0, count*2048L
- , ATA_DATA_IN, segment, offset);
- }
+ else
+ status = cdrom_read(device, lba, count*2048, segment, offset, 0);
break;
case ATA_CMD_WRITE_SECTORS:
status = ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS
****************************************************************/
static u8
-get_device(struct bregs *regs, u8 drive)
+get_device(struct bregs *regs, u8 iscd, u8 drive)
{
// basic check : device has to be defined
if (drive >= CONFIG_MAX_ATA_DEVICES) {
}
// Get the ata channel
- u8 device = GET_EBDA(ata.hdidmap[drive]);
+ u8 device = GET_EBDA(ata.idmap[iscd][drive]);
// basic check : device has to be valid
if (device >= CONFIG_MAX_ATA_DEVICES) {
}
if (drive >= 0xe0) {
- u8 device = get_device(regs, drive - 0xe0);
+ u8 device = get_device(regs, 1, drive - 0xe0);
if (device >= CONFIG_MAX_ATA_DEVICES)
return;
cdrom_13(regs, device);
return;
}
- u8 device = get_device(regs, drive - 0x80);
+ u8 device = get_device(regs, 0, drive - 0x80);
if (device >= CONFIG_MAX_ATA_DEVICES)
return;
disk_13(regs, device);
debug_enter(regs);
u8 drive = regs->dl;
- if (CONFIG_ELTORITO_BOOT) {
+ if (CONFIG_CDROM_BOOT) {
if (regs->ah == 0x4b) {
cdemu_134b(regs);
goto done;
cdemu_13(regs);
goto done;
}
- drive--;
+ if (drive < 0xe0)
+ drive--;
}
}
handle_legacy_disk(regs, drive);
void cdrom_13(struct bregs *regs, u8 device);
void cdemu_13(struct bregs *regs);
void cdemu_134b(struct bregs *regs);
+u16 cdrom_boot();
#endif // disk.h
// hdidmap and cdidmap init.
u8 device;
for (device=0; device < CONFIG_MAX_ATA_DEVICES; device++) {
- ebda->ata.hdidmap[device] = CONFIG_MAX_ATA_DEVICES;
- ebda->ata.cdidmap[device] = CONFIG_MAX_ATA_DEVICES;
+ ebda->ata.idmap[0][device] = CONFIG_MAX_ATA_DEVICES;
+ ebda->ata.idmap[1][device] = CONFIG_MAX_ATA_DEVICES;
}
}
ip++;
// CDROM
- if (CONFIG_ELTORITO_BOOT) {
+ if (CONFIG_CDROM_BOOT) {
ip->type = IPL_TYPE_CDROM;
ip++;
}