#include "disk.h" // struct ata_s
#include "ata.h" // ATA_CB_STAT
-#define IDE_SECTOR_SIZE 512
-#define CDROM_SECTOR_SIZE 2048
-
#define IDE_TIMEOUT 32000 //32 seconds max for IDE ops
struct ata_channel_s ATA_channels[CONFIG_MAX_ATA_INTERFACES] VAR16VISIBLE;
int ret = send_cmd(op->driveid, &cmd);
if (ret)
return ret;
- return ata_transfer(op, iswrite, IDE_SECTOR_SIZE);
+ return ata_transfer(op, iswrite, DISK_SECTOR_SIZE);
}
int
init_drive_atapi(int driveid, u16 *buffer)
{
// Send an IDENTIFY_DEVICE_PACKET command to device
- memset(buffer, 0, IDE_SECTOR_SIZE);
+ memset(buffer, 0, DISK_SECTOR_SIZE);
struct disk_op_s dop;
memset(&dop, 0, sizeof(dop));
dop.driveid = driveid;
init_drive_ata(int driveid, u16 *buffer)
{
// Send an IDENTIFY_DEVICE command to device
- memset(buffer, 0, IDE_SECTOR_SIZE);
+ memset(buffer, 0, DISK_SECTOR_SIZE);
struct disk_op_s dop;
memset(&dop, 0, sizeof(dop));
dop.driveid = driveid;
// Success - setup as ATA.
extract_identify(driveid, buffer);
SET_GLOBAL(Drives.drives[driveid].type, DTYPE_ATA);
- SET_GLOBAL(Drives.drives[driveid].blksize, IDE_SECTOR_SIZE);
+ SET_GLOBAL(Drives.drives[driveid].blksize, DISK_SECTOR_SIZE);
SET_GLOBAL(Drives.drives[driveid].pchs.cylinders, buffer[1]);
SET_GLOBAL(Drives.drives[driveid].pchs.heads, buffer[3]);
****************************************************************/
// Execute a disk_op request.
-static int
+int
process_op(struct disk_op_s *op)
{
u8 type = GET_GLOBAL(Drives.drives[op->driveid].type);
return process_atapi_op(op);
case DTYPE_RAMDISK:
return process_ramdisk_op(op);
+ case DTYPE_CDEMU:
+ return process_cdemu_op(op);
default:
op->count = 0;
return DISK_RET_EPARAM;
-// 16bit code to access cdrom drives.
+// Support for booting from cdroms (the "El Torito" spec).
//
// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2002 MandrakeSoft S.A.
* CD emulation
****************************************************************/
-static void
-cdemu_wp(struct bregs *regs, u8 driveid)
+static int
+cdemu_read(struct disk_op_s *op)
{
- disk_ret(regs, DISK_RET_EWRITEPROTECT);
-}
+ u16 ebda_seg = get_ebda_seg();
+ u8 driveid = GET_EBDA2(ebda_seg, cdemu.emulated_driveid);
+ struct disk_op_s dop;
+ dop.driveid = driveid;
+ dop.command = op->command;
+ dop.lba = GET_EBDA2(ebda_seg, cdemu.ilba) + op->lba / 4;
+
+ int count = op->count;
+ op->count = 0;
+ u8 *cdbuf_far = (void*)offsetof(struct extended_bios_data_area_s, cdemu_buf);
+
+ if (op->lba & 3) {
+ // Partial read of first block.
+ dop.count = 1;
+ dop.buf_fl = MAKE_FLATPTR(ebda_seg, cdbuf_far);
+ int ret = process_op(&dop);
+ if (ret)
+ return ret;
+ u8 thiscount = 4 - (op->lba & 3);
+ if (thiscount > count)
+ thiscount = count;
+ count -= thiscount;
+ memcpy_far(FLATPTR_TO_SEG(op->buf_fl)
+ , (void*)FLATPTR_TO_OFFSET(op->buf_fl)
+ , ebda_seg, cdbuf_far + (op->lba & 3) * 512
+ , thiscount * 512);
+ op->buf_fl += thiscount * 512;
+ op->count += thiscount;
+ dop.lba++;
+ }
-static void
-cdemu_1302(struct bregs *regs, u8 driveid)
-{
- cdemu_access(regs, driveid, CMD_READ);
-}
+ if (count > 3) {
+ // Read n number of regular blocks.
+ dop.count = count / 4;
+ dop.buf_fl = op->buf_fl;
+ int ret = process_op(&dop);
+ op->count += dop.count * 4;
+ if (ret)
+ return ret;
+ u8 thiscount = count & ~3;
+ count &= 3;
+ op->buf_fl += thiscount * 512;
+ dop.lba += thiscount / 4;
+ }
-static void
-cdemu_1304(struct bregs *regs, u8 driveid)
-{
- cdemu_access(regs, driveid, CMD_VERIFY);
+ if (count) {
+ // Partial read on last block.
+ dop.count = 1;
+ dop.buf_fl = MAKE_FLATPTR(ebda_seg, cdbuf_far);
+ int ret = process_op(&dop);
+ if (ret)
+ return ret;
+ u8 thiscount = count;
+ memcpy_far(FLATPTR_TO_SEG(op->buf_fl)
+ , (void*)FLATPTR_TO_OFFSET(op->buf_fl)
+ , ebda_seg, cdbuf_far, thiscount * 512);
+ op->count += thiscount;
+ }
+
+ return DISK_RET_SUCCESS;
}
-// read disk drive parameters
-static void
-cdemu_1308(struct bregs *regs, u8 driveid)
+int
+process_cdemu_op(struct disk_op_s *op)
{
- u16 ebda_seg = get_ebda_seg();
- u16 nlc = GET_EBDA2(ebda_seg, cdemu.lchs.cylinders) - 1;
- u16 nlh = GET_EBDA2(ebda_seg, cdemu.lchs.heads) - 1;
- u16 nlspt = GET_EBDA2(ebda_seg, cdemu.lchs.spt);
-
- regs->al = 0x00;
- regs->bl = 0x00;
- regs->ch = nlc & 0xff;
- regs->cl = ((nlc >> 2) & 0xc0) | (nlspt & 0x3f);
- regs->dh = nlh;
- // FIXME ElTorito Various. should send the real count of drives 1 or 2
- // FIXME ElTorito Harddisk. should send the HD count
- regs->dl = 0x02;
- u8 media = GET_EBDA2(ebda_seg, cdemu.media);
- if (media <= 3)
- regs->bl = media * 2;
-
- regs->es = SEG_BIOS;
- regs->di = (u32)&diskette_param_table2;
+ if (!CONFIG_CDROM_EMU)
+ return 0;
- disk_ret(regs, DISK_RET_SUCCESS);
+ switch (op->command) {
+ case CMD_READ:
+ return cdemu_read(op);
+ case CMD_WRITE:
+ case CMD_FORMAT:
+ return DISK_RET_EWRITEPROTECT;
+ case CMD_VERIFY:
+ case CMD_RESET:
+ case CMD_SEEK:
+ case CMD_ISREADY:
+ return DISK_RET_SUCCESS;
+ default:
+ op->count = 0;
+ return DISK_RET_EPARAM;
+ }
}
+int cdemu_driveid VAR16VISIBLE;
+
void
-cdemu_13(struct bregs *regs)
+cdemu_setup()
{
- //debug_stub(regs);
-
- u16 ebda_seg = get_ebda_seg();
- u8 driveid = GET_EBDA2(ebda_seg, cdemu.emulated_driveid);
+ if (!CONFIG_CDROM_EMU)
+ return;
- switch (regs->ah) {
- case 0x02: cdemu_1302(regs, driveid); break;
- case 0x04: cdemu_1304(regs, driveid); break;
- case 0x08: cdemu_1308(regs, driveid); break;
-
- case 0x03:
- case 0x05:
- cdemu_wp(regs, driveid);
- break;
-
- // These functions are the same as standard CDROM.
- case 0x00:
- case 0x01:
- case 0x09:
- case 0x0c:
- case 0x0d:
- case 0x10:
- case 0x11:
- case 0x14:
- case 0x15:
- case 0x16:
- disk_13(regs, driveid);
- break;
-
- default: disk_13XX(regs, driveid); break;
+ int driveid = Drives.drivecount;
+ if (driveid >= ARRAY_SIZE(Drives.drives)) {
+ cdemu_driveid = -1;
+ return;
}
+ Drives.drivecount++;
+ cdemu_driveid = driveid;
+ memset(&Drives.drives[driveid], 0, sizeof(Drives.drives[0]));
+ Drives.drives[driveid].type = DTYPE_CDEMU;
+ Drives.drives[driveid].blksize = DISK_SECTOR_SIZE;
+ Drives.drives[driveid].sectors = (u64)-1;
}
struct eltorito_s {
}
// Emulation of a floppy/harddisk requested
- if (! CONFIG_CDROM_EMU)
+ if (! CONFIG_CDROM_EMU || cdemu_driveid < 0)
return 13;
// Set emulated drive id and increase bios installed hardware
#define DISK_STUB(regs) \
__disk_stub((regs), __LINE__, __func__)
-// Obtain the requested disk lba from an old-style chs request.
-static int
-legacy_lba(struct bregs *regs, u16 lchs_seg, struct chs_s *lchs_far)
+static void
+fillLCHS(u8 driveid, u16 *nlc, u16 *nlh, u16 *nlspt)
{
+ if (CONFIG_CDROM_EMU && driveid == GET_GLOBAL(cdemu_driveid)) {
+ // Emulated drive - get info from ebda. (It's not possible to
+ // populate the geometry directly in the driveid because the
+ // geometry is only known after the bios segment is made
+ // read-only).
+ u16 ebda_seg = get_ebda_seg();
+ *nlc = GET_EBDA2(ebda_seg, cdemu.lchs.cylinders);
+ *nlh = GET_EBDA2(ebda_seg, cdemu.lchs.heads);
+ *nlspt = GET_EBDA2(ebda_seg, cdemu.lchs.spt);
+ return;
+ }
+ *nlc = GET_GLOBAL(Drives.drives[driveid].lchs.cylinders);
+ *nlh = GET_GLOBAL(Drives.drives[driveid].lchs.heads);
+ *nlspt = GET_GLOBAL(Drives.drives[driveid].lchs.spt);
+}
+
+// Perform read/write/verify using old-style chs accesses
+static void
+basic_access(struct bregs *regs, u8 driveid, u16 command)
+{
+ struct disk_op_s dop;
+ dop.driveid = driveid;
+ dop.command = command;
+
u8 count = regs->al;
u16 cylinder = regs->ch | ((((u16)regs->cl) << 2) & 0x300);
u16 sector = regs->cl & 0x3f;
dprintf(1, "int13_harddisk: function %02x, parameter out of range!\n"
, regs->ah);
disk_ret(regs, DISK_RET_EPARAM);
- return -1;
+ return;
}
+ dop.count = count;
- u16 nlc = GET_FARVAR(lchs_seg, lchs_far->cylinders);
- u16 nlh = GET_FARVAR(lchs_seg, lchs_far->heads);
- u16 nlspt = GET_FARVAR(lchs_seg, lchs_far->spt);
+ u16 nlc, nlh, nlspt;
+ fillLCHS(driveid, &nlc, &nlh, &nlspt);
// sanity check on cyl heads, sec
if (cylinder >= nlc || head >= nlh || sector > nlspt) {
" range %04x/%04x/%04x!\n"
, regs->ah, cylinder, head, sector);
disk_ret(regs, DISK_RET_EPARAM);
- return -1;
+ return;
}
// translate lchs to lba
- return (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nlspt)
- + (u32)sector - 1);
-}
+ dop.lba = (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nlspt)
+ + (u32)sector - 1);
-// Perform read/write/verify using old-style chs accesses
-static void
-basic_access(struct bregs *regs, u8 driveid, u16 command)
-{
- struct disk_op_s dop;
- dop.driveid = driveid;
- dop.command = command;
- int lba = legacy_lba(regs, get_global_seg(), &Drives.drives[driveid].lchs);
- if (lba < 0)
- return;
- dop.lba = lba;
- dop.count = regs->al;
dop.buf_fl = MAKE_FLATPTR(regs->es, regs->bx);
int status = send_disk_op(&dop);
disk_ret(regs, status);
}
-// Perform cdemu read/verify
-void
-cdemu_access(struct bregs *regs, u8 driveid, u16 command)
-{
- struct disk_op_s dop;
- dop.driveid = driveid;
- dop.command = command;
- u16 ebda_seg = get_ebda_seg();
- int vlba = legacy_lba(
- regs, ebda_seg
- , (void*)offsetof(struct extended_bios_data_area_s, cdemu.lchs));
- if (vlba < 0)
- return;
- dop.lba = GET_EBDA2(ebda_seg, cdemu.ilba) + vlba / 4;
- u8 count = regs->al;
- u8 *cdbuf_far = (void*)offsetof(struct extended_bios_data_area_s, cdemu_buf);
- u8 *dest_far = (void*)(regs->bx+0);
- regs->al = 0;
- int status = DISK_RET_SUCCESS;
-
- if (vlba & 3) {
- dop.count = 1;
- dop.buf_fl = MAKE_FLATPTR(ebda_seg, cdbuf_far);
- status = send_disk_op(&dop);
- if (status)
- goto fail;
- u8 thiscount = 4 - (vlba & 3);
- if (thiscount > count)
- thiscount = count;
- count -= thiscount;
- memcpy_far(regs->es, dest_far
- , ebda_seg, cdbuf_far + (vlba & 3) * 512
- , thiscount * 512);
- dest_far += thiscount * 512;
- regs->al += thiscount;
- dop.lba++;
- }
-
- if (count > 3) {
- dop.count = count / 4;
- dop.buf_fl = MAKE_FLATPTR(regs->es, dest_far);
- status = send_disk_op(&dop);
- regs->al += dop.count * 4;
- if (status)
- goto fail;
- u8 thiscount = count & ~3;
- count &= 3;
- dest_far += thiscount * 512;
- dop.lba += thiscount / 4;
- }
-
- if (count) {
- dop.count = 1;
- dop.buf_fl = MAKE_FLATPTR(ebda_seg, cdbuf_far);
- status = send_disk_op(&dop);
- if (status)
- goto fail;
- u8 thiscount = count;
- memcpy_far(regs->es, dest_far, ebda_seg, cdbuf_far, thiscount * 512);
- regs->al += thiscount;
- }
-
-fail:
- disk_ret(regs, status);
-}
-
// Perform read/write/verify using new-style "int13ext" accesses.
static void
extended_access(struct bregs *regs, u8 driveid, u16 command)
{
DISK_STUB(regs);
- u16 nlh = GET_GLOBAL(Drives.drives[driveid].lchs.heads);
- u16 nlspt = GET_GLOBAL(Drives.drives[driveid].lchs.spt);
+ u16 nlc, nlh, nlspt;
+ fillLCHS(driveid, &nlc, &nlh, &nlspt);
u8 num_sectors = regs->al;
u8 head = regs->dh;
static void
disk_1308(struct bregs *regs, u8 driveid)
{
+ u16 ebda_seg = get_ebda_seg();
// Get logical geometry from table
- u16 nlc = GET_GLOBAL(Drives.drives[driveid].lchs.cylinders) - 1;
- u16 nlh = GET_GLOBAL(Drives.drives[driveid].lchs.heads) - 1;
- u16 nlspt = GET_GLOBAL(Drives.drives[driveid].lchs.spt);
+ u16 nlc, nlh, nlspt;
+ fillLCHS(driveid, &nlc, &nlh, &nlspt);
+ nlc--;
+ nlh--;
u8 count;
if (regs->dl < EXTSTART_HD) {
// Floppy
count = GET_GLOBAL(Drives.floppycount);
- regs->bx = GET_GLOBAL(Drives.drives[driveid].floppy_type);
+ if (CONFIG_CDROM_EMU && driveid == GET_GLOBAL(cdemu_driveid))
+ regs->bx = GET_EBDA2(ebda_seg, cdemu.media) * 2;
+ else
+ regs->bx = GET_GLOBAL(Drives.drives[driveid].floppy_type);
// set es & di to point to 11 byte diskette param table in ROM
regs->es = SEG_BIOS;
return;
}
+ if (CONFIG_CDROM_EMU && GET_EBDA2(ebda_seg, cdemu.active)) {
+ u8 emudrive = GET_EBDA2(ebda_seg, cdemu.emulated_extdrive);
+ if (((emudrive ^ regs->dl) & 0x80) == 0)
+ // Note extra drive due to emulation.
+ count++;
+ if (regs->dl < EXTSTART_HD && count > 2)
+ // Max of two floppy drives.
+ count = 2;
+ }
+
regs->al = 0;
regs->ch = nlc & 0xff;
regs->cl = ((nlc >> 2) & 0xc0) | (nlspt & 0x3f);
// Hard drive
// Get logical geometry from table
- u16 nlc = GET_GLOBAL(Drives.drives[driveid].lchs.cylinders);
- u16 nlh = GET_GLOBAL(Drives.drives[driveid].lchs.heads);
- u16 nlspt = GET_GLOBAL(Drives.drives[driveid].lchs.spt);
+ u16 nlc, nlh, nlspt;
+ fillLCHS(driveid, &nlc, &nlh, &nlspt);
// Compute sector count seen by int13
u32 lba = (u32)(nlc - 1) * (u32)nlh * (u32)nlspt;
}
}
-void
+static void
disk_13XX(struct bregs *regs, u8 driveid)
{
disk_ret(regs, DISK_RET_EPARAM);
}
-void
+static void
disk_13(struct bregs *regs, u8 driveid)
{
//debug_stub(regs);
if (GET_EBDA2(ebda_seg, cdemu.active)) {
u8 emudrive = GET_EBDA2(ebda_seg, cdemu.emulated_extdrive);
if (extdrive == emudrive) {
- cdemu_13(regs);
+ int cdemuid = GET_GLOBAL(cdemu_driveid);
+ if (regs->ah > 0x16) {
+ // Only old-style commands supported.
+ disk_13XX(regs, cdemuid);
+ return;
+ }
+ disk_13(regs, cdemuid);
return;
}
if (extdrive < EXTSTART_CD && ((emudrive ^ extdrive) & 0x80) == 0)
u64 sectors; // Total sectors count
};
+#define DISK_SECTOR_SIZE 512
+#define CDROM_SECTOR_SIZE 2048
+
#define DTYPE_NONE 0x00
#define DTYPE_FLOPPY 0x01
#define DTYPE_ATA 0x02
#define DTYPE_ATAPI 0x03
#define DTYPE_RAMDISK 0x04
+#define DTYPE_CDEMU 0x05
#define TRANSLATION_NONE 0
#define TRANSLATION_LBA 1
void map_hd_drive(int driveid);
void map_cd_drive(int driveid);
void describe_drive(int driveid);
+int process_op(struct disk_op_s *op);
int send_disk_op(struct disk_op_s *op);
void drive_setup();
int process_floppy_op(struct disk_op_s *op);
void floppy_tick();
-// disk.c
-void disk_13(struct bregs *regs, u8 driveid);
-void disk_13XX(struct bregs *regs, u8 driveid);
-void cdemu_access(struct bregs *regs, u8 driveid, u16 command);
-
// cdrom.c
-void cdrom_13(struct bregs *regs, u8 driveid);
-void cdemu_13(struct bregs *regs);
+extern int cdemu_driveid;
+int process_cdemu_op(struct disk_op_s *op);
+void cdemu_setup();
void cdemu_134b(struct bregs *regs);
int cdrom_boot(int cdid);
#include "pic.h" // eoi_pic1
#include "bregs.h" // struct bregs
-#define FLOPPY_SECTOR_SIZE 512
#define FLOPPY_SIZE_CODE 0x02 // 512 byte sectors
#define FLOPPY_DATALEN 0xff // Not used - because size code is 0x02
#define FLOPPY_MOTOR_TICKS 37 // ~2 seconds
memset(&Drives.drives[driveid], 0, sizeof(Drives.drives[0]));
Drives.drives[driveid].cntl_id = floppyid;
Drives.drives[driveid].type = driver;
- Drives.drives[driveid].blksize = FLOPPY_SECTOR_SIZE;
+ Drives.drives[driveid].blksize = DISK_SECTOR_SIZE;
Drives.drives[driveid].floppy_type = ftype;
- Drives.drives[driveid].sectors = (u16)-1;
+ Drives.drives[driveid].sectors = (u64)-1;
memcpy(&Drives.drives[driveid].lchs, &FloppyInfo[ftype].chs
, sizeof(FloppyInfo[ftype].chs));
int i;
for (i=1; i<ARRAY_SIZE(FloppyInfo); i++) {
struct chs_s *c = &FloppyInfo[i].chs;
- if (c->cylinders * c->heads * c->spt * FLOPPY_SECTOR_SIZE == size)
+ if (c->cylinders * c->heads * c->spt * DISK_SECTOR_SIZE == size)
return i;
}
return -1;
data[7] = FLOPPY_GAPLEN;
data[8] = FLOPPY_DATALEN;
- res = floppy_cmd(op, op->count * FLOPPY_SECTOR_SIZE, data, 9);
+ res = floppy_cmd(op, op->count * DISK_SECTOR_SIZE, data, 9);
if (res)
goto fail;
data[7] = FLOPPY_GAPLEN;
data[8] = FLOPPY_DATALEN;
- res = floppy_cmd(op, op->count * FLOPPY_SECTOR_SIZE, data, 9);
+ res = floppy_cmd(op, op->count * DISK_SECTOR_SIZE, data, 9);
if (res)
goto fail;
boot_setup();
drive_setup();
+ cdemu_setup();
floppy_setup();
ata_setup();
ramdisk_setup();
#include "biosvar.h" // GET_GLOBAL
#include "bregs.h" // struct bregs
-#define RAMDISK_SECTOR_SIZE 512
-
void
describe_ramdisk(int driveid)
{
ramdisk_copy(struct disk_op_s *op, int iswrite)
{
u32 offset = GET_GLOBAL(Drives.drives[op->driveid].cntl_id);
- offset += (u32)op->lba * RAMDISK_SECTOR_SIZE;
+ offset += (u32)op->lba * DISK_SECTOR_SIZE;
u64 opd = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE((u32)op->buf_fl);
u64 ramd = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE(offset);
br.ah = 0x87;
br.es = GET_SEG(SS);
br.si = (u32)gdt;
- br.cx = op->count * RAMDISK_SECTOR_SIZE / 2;
+ br.cx = op->count * DISK_SECTOR_SIZE / 2;
call16_int(0x15, &br);
if (br.flags & F_CF)