From: Kevin O'Connor Date: Wed, 5 Mar 2008 03:50:53 +0000 (-0500) Subject: Get CDROM emulation working. X-Git-Url: http://wien.tomnetworks.com/gitweb/?p=seabios.git;a=commitdiff_plain;h=180a959053bd1b955bef8db4dab443f341ee1ce7 Get CDROM emulation working. 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. --- diff --git a/src/ata.c b/src/ata.c index a84e83b..710bd9e 100644 --- a/src/ata.c +++ b/src/ata.c @@ -425,7 +425,7 @@ ata_cmd_data_out(u16 device, u16 command, u16 count, u16 cylinder // 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; @@ -493,13 +493,12 @@ ata_cmd_packet(u16 device, u16 cmdbuf, u8 cmdlen, u16 header // 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) { @@ -507,8 +506,7 @@ ata_cmd_packet(u16 device, u16 cmdbuf, u8 cmdlen, u16 header 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++; @@ -533,23 +531,21 @@ ata_cmd_packet(u16 device, u16 cmdbuf, u8 cmdlen, u16 header 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; } @@ -589,9 +585,9 @@ ata_cmd_packet(u16 device, u16 cmdbuf, u8 cmdlen, u16 header 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> 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 // --------------------------------------------------------------------------- @@ -817,7 +833,7 @@ ata_detect() SET_EBDA(ata.devices[device].lchs.spt, spt); // fill hdidmap - SET_EBDA(ata.hdidmap[hdcount], device); + SET_EBDA(ata.idmap[0][hdcount], device); hdcount++; } @@ -848,7 +864,7 @@ ata_detect() SET_EBDA(ata.devices[device].blksize, blksize); // fill cdidmap - SET_EBDA(ata.cdidmap[cdcount], device); + SET_EBDA(ata.idmap[1][cdcount], device); cdcount++; } diff --git a/src/ata.h b/src/ata.h index 83c48cc..ccd3e2e 100644 --- a/src/ata.h +++ b/src/ata.h @@ -16,8 +16,10 @@ u16 ata_cmd_data_in(u16 device, u16 command, u16 count, u16 cylinder , 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. diff --git a/src/biosvar.h b/src/biosvar.h index eea6df6..55e9481 100644 --- a/src/biosvar.h +++ b/src/biosvar.h @@ -185,11 +185,9 @@ struct ata_s { // 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; diff --git a/src/boot.c b/src/boot.c index 1ab9423..a21136e 100644 --- a/src/boot.c +++ b/src/boot.c @@ -10,6 +10,7 @@ #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 @@ -67,7 +68,7 @@ try_boot(u16 seq_nr) 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; @@ -133,10 +134,24 @@ try_boot(u16 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) */ diff --git a/src/cdrom.c b/src/cdrom.c index 9782e27..b2caaf6 100644 --- a/src/cdrom.c +++ b/src/cdrom.c @@ -9,6 +9,9 @@ #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 @@ -277,3 +280,258 @@ cdemu_134b(struct bregs *regs) 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) + 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; +} diff --git a/src/config.h b/src/config.h index a118c68..199627e 100644 --- a/src/config.h +++ b/src/config.h @@ -6,7 +6,7 @@ #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) diff --git a/src/disk.c b/src/disk.c index 7c3e417..5a2f069 100644 --- a/src/disk.c +++ b/src/disk.c @@ -104,12 +104,12 @@ basic_access(struct bregs *regs, u8 device, u16 command) 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); @@ -142,34 +142,18 @@ emu_access(struct bregs *regs, u8 device, u16 command) // 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); } @@ -203,25 +187,12 @@ extended_access(struct bregs *regs, u8 device, u16 command) 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 @@ -683,7 +654,7 @@ disk_13(struct bregs *regs, u8 device) ****************************************************************/ 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) { @@ -692,7 +663,7 @@ get_device(struct bregs *regs, u8 drive) } // 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) { @@ -718,14 +689,14 @@ handle_legacy_disk(struct bregs *regs, u8 drive) } 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); @@ -746,7 +717,7 @@ handle_13(struct bregs *regs) debug_enter(regs); u8 drive = regs->dl; - if (CONFIG_ELTORITO_BOOT) { + if (CONFIG_CDROM_BOOT) { if (regs->ah == 0x4b) { cdemu_134b(regs); goto done; @@ -756,7 +727,8 @@ handle_13(struct bregs *regs) cdemu_13(regs); goto done; } - drive--; + if (drive < 0xe0) + drive--; } } handle_legacy_disk(regs, drive); diff --git a/src/disk.h b/src/disk.h index badbb41..36e95e5 100644 --- a/src/disk.h +++ b/src/disk.h @@ -117,6 +117,7 @@ void disk_13XX(struct bregs *regs, u8 device); 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 diff --git a/src/post.c b/src/post.c index 00361d7..a1c71ee 100644 --- a/src/post.c +++ b/src/post.c @@ -352,8 +352,8 @@ ata_init() // 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; } } @@ -437,7 +437,7 @@ init_boot_vectors() ip++; // CDROM - if (CONFIG_ELTORITO_BOOT) { + if (CONFIG_CDROM_BOOT) { ip->type = IPL_TYPE_CDROM; ip++; }