Exemple #1
0
// Send an ata command that does not transfer any further data.
int
ata_cmd_nondata(struct atadrive_s *adrive_gf, struct ata_pio_command *cmd)
{
    struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf);
    u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
    u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);

    // Disable interrupts
    outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC);

    int ret = send_cmd(adrive_gf, cmd);
    if (ret)
        goto fail;
    ret = ndelay_await_not_bsy(iobase1);
    if (ret < 0)
        goto fail;

    if (ret & ATA_CB_STAT_ERR) {
        dprintf(6, "nondata cmd : read error (status=%02x err=%02x)\n"
                , ret, inb(iobase1 + ATA_CB_ERR));
        ret = -4;
        goto fail;
    }
    if (ret & ATA_CB_STAT_DRQ) {
        dprintf(6, "nondata cmd : DRQ set (status %02x)\n", ret);
        ret = -5;
        goto fail;
    }

fail:
    // Enable interrupts
    outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC);

    return ret;
}
Exemple #2
0
static int
usb_msc_send(struct usbdrive_s *udrive_gf, int dir, void *buf, u32 bytes)
{
    struct usb_pipe *pipe;
    if (dir == USB_DIR_OUT)
        pipe = GET_GLOBALFLAT(udrive_gf->bulkout);
    else
        pipe = GET_GLOBALFLAT(udrive_gf->bulkin);
    return usb_send_bulk(pipe, dir, buf, bytes);
}
Exemple #3
0
// Check for drive RDY for 16bit interface command.
static int
isready(struct atadrive_s *adrive_gf)
{
    // Read the status from controller
    struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf);
    u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
    u8 status = inb(iobase1 + ATA_CB_STAT);
    if ((status & (ATA_CB_STAT_BSY|ATA_CB_STAT_RDY)) == ATA_CB_STAT_RDY)
        return DISK_RET_SUCCESS;
    return DISK_RET_ENOTREADY;
}
Exemple #4
0
// Reset a drive
static void
ata_reset(struct atadrive_s *adrive_gf)
{
    struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf);
    u8 slave = GET_GLOBALFLAT(adrive_gf->slave);
    u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
    u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);

    dprintf(6, "ata_reset drive=%p\n", &adrive_gf->drive);
    // Pulse SRST
    outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST, iobase2+ATA_CB_DC);
    udelay(5);
    outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2+ATA_CB_DC);
    msleep(2);

    // wait for device to become not busy.
    int status = await_not_bsy(iobase1);
    if (status < 0)
        goto done;
    if (slave) {
        // Change device.
        u32 end = timer_calc(IDE_TIMEOUT);
        for (;;) {
            outb(ATA_CB_DH_DEV1, iobase1 + ATA_CB_DH);
            status = ndelay_await_not_bsy(iobase1);
            if (status < 0)
                goto done;
            if (inb(iobase1 + ATA_CB_DH) == ATA_CB_DH_DEV1)
                break;
            // Change drive request failed to take effect - retry.
            if (timer_check(end)) {
                warn_timeout();
                goto done;
            }
        }
    } else {
        // QEMU doesn't reset dh on reset, so set it explicitly.
        outb(ATA_CB_DH_DEV0, iobase1 + ATA_CB_DH);
    }

    // On a user-reset request, wait for RDY if it is an ATA device.
    u8 type=GET_GLOBALFLAT(adrive_gf->drive.type);
    if (type == DTYPE_ATA)
        status = await_rdy(iobase1);

done:
    // Enable interrupts
    outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC);

    dprintf(6, "ata_reset exit status=%x\n", status);
}
Exemple #5
0
// ElTorito - Terminate disk emu
void
cdemu_134b(struct bregs *regs)
{
    // FIXME ElTorito Hardcoded
    SET_INT13ET(regs, size, 0x13);
    SET_INT13ET(regs, media, GET_LOW(CDEmu.media));
    SET_INT13ET(regs, emulated_drive, GET_LOW(CDEmu.emulated_extdrive));
    struct drive_s *drive_gf = GET_LOW(CDEmu.emulated_drive_gf);
    u8 cntl_id = 0;
    if (drive_gf)
        cntl_id = GET_GLOBALFLAT(drive_gf->cntl_id);
    SET_INT13ET(regs, controller_index, cntl_id / 2);
    SET_INT13ET(regs, device_spec, cntl_id % 2);
    SET_INT13ET(regs, ilba, GET_LOW(CDEmu.ilba));
    SET_INT13ET(regs, buffer_segment, GET_LOW(CDEmu.buffer_segment));
    SET_INT13ET(regs, load_segment, GET_LOW(CDEmu.load_segment));
    SET_INT13ET(regs, sector_count, GET_LOW(CDEmu.sector_count));
    SET_INT13ET(regs, cylinders, GET_LOW(CDEmu.lchs.cylinders));
    SET_INT13ET(regs, sectors, GET_LOW(CDEmu.lchs.spt));
    SET_INT13ET(regs, heads, GET_LOW(CDEmu.lchs.heads));

    // If we have to terminate emulation
    if (regs->al == 0x00) {
        // FIXME ElTorito Various. Should be handled accordingly to spec
        SET_LOW(CDEmu.active, 0x00); // bye bye

        // XXX - update floppy/hd count.
    }

    disk_ret(regs, DISK_RET_SUCCESS);
}
Exemple #6
0
// Perform read/write/verify using new-style "int13ext" accesses.
static void noinline
extended_access(struct bregs *regs, struct drive_s *drive_gf, u16 command)
{
    struct disk_op_s dop;
    struct int13ext_s *param_far = (struct int13ext_s*)(regs->si+0);
    // Get lba and check.
    dop.lba = GET_FARVAR(regs->ds, param_far->lba);
    dop.command = command;
    dop.drive_gf = drive_gf;
    if (dop.lba >= GET_GLOBALFLAT(drive_gf->sectors)) {
        warn_invalid(regs);
        disk_ret(regs, DISK_RET_EPARAM);
        return;
    }

    dop.buf_fl = SEGOFF_TO_FLATPTR(GET_FARVAR(regs->ds, param_far->data));
    dop.count = GET_FARVAR(regs->ds, param_far->count);
    if (! dop.count) {
        // Nothing to do.
        disk_ret(regs, DISK_RET_SUCCESS);
        return;
    }

    int status = send_disk_op(&dop);

    SET_FARVAR(regs->ds, param_far->count, dop.count);

    disk_ret(regs, status);
}
Exemple #7
0
// Low-level usb command transmit function.
int
usb_process_op(struct disk_op_s *op)
{
    if (!CONFIG_USB_MSC)
        return 0;

    dprintf(16, "usb_cmd_data id=%p write=%d count=%d buf=%p\n"
            , op->drive_fl, 0, op->count, op->buf_fl);
    struct usbdrive_s *udrive_gf = container_of(
        op->drive_fl, struct usbdrive_s, drive);

    // Setup command block wrapper.
    struct cbw_s cbw;
    memset(&cbw, 0, sizeof(cbw));
    int blocksize = scsi_fill_cmd(op, cbw.CBWCB, USB_CDB_SIZE);
    if (blocksize < 0)
        return default_process_op(op);
    u32 bytes = blocksize * op->count;
    cbw.dCBWSignature = CBW_SIGNATURE;
    cbw.dCBWTag = 999; // XXX
    cbw.dCBWDataTransferLength = bytes;
    cbw.bmCBWFlags = scsi_is_read(op) ? USB_DIR_IN : USB_DIR_OUT;
    cbw.bCBWLUN = GET_GLOBALFLAT(udrive_gf->lun);
    cbw.bCBWCBLength = USB_CDB_SIZE;

    // Transfer cbw to device.
    int ret = usb_msc_send(udrive_gf, USB_DIR_OUT
                           , MAKE_FLATPTR(GET_SEG(SS), &cbw), sizeof(cbw));
    if (ret)
        goto fail;

    // Transfer data to/from device.
    if (bytes) {
        ret = usb_msc_send(udrive_gf, cbw.bmCBWFlags, op->buf_fl, bytes);
        if (ret)
            goto fail;
    }

    // Transfer csw info.
    struct csw_s csw;
    ret = usb_msc_send(udrive_gf, USB_DIR_IN
                       , MAKE_FLATPTR(GET_SEG(SS), &csw), sizeof(csw));
    if (ret)
        goto fail;

    if (!csw.bCSWStatus)
        return DISK_RET_SUCCESS;
    if (csw.bCSWStatus == 2)
        goto fail;

    if (blocksize)
        op->count -= csw.dCSWDataResidue / blocksize;
    return DISK_RET_EBADTRACK;

fail:
    // XXX - reset connection
    dprintf(1, "USB transmission failed\n");
    return DISK_RET_EBADTRACK;
}
Exemple #8
0
// Get the cylinders/heads/sectors for the given drive.
static struct chs_s
getLCHS(struct drive_s *drive_gf)
{
    struct chs_s res = { };
    if (CONFIG_CDROM_EMU && drive_gf == GET_GLOBAL(cdemu_drive_gf)) {
        // Emulated drive - get info from CDEmu.  (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).
        res.cylinder = GET_LOW(CDEmu.lchs.cylinder);
        res.head = GET_LOW(CDEmu.lchs.head);
        res.sector = GET_LOW(CDEmu.lchs.sector);
        return res;
    }
    res.cylinder = GET_GLOBALFLAT(drive_gf->lchs.cylinder);
    res.head = GET_GLOBALFLAT(drive_gf->lchs.head);
    res.sector = GET_GLOBALFLAT(drive_gf->lchs.sector);
    return res;
}
Exemple #9
0
// Send an ata command to the drive.
static int
send_cmd(struct atadrive_s *adrive_gf, struct ata_pio_command *cmd)
{
    struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf);
    u8 slave = GET_GLOBALFLAT(adrive_gf->slave);
    u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);

    // Select device
    int status = await_not_bsy(iobase1);
    if (status < 0)
        return status;
    u8 newdh = ((cmd->device & ~ATA_CB_DH_DEV1)
                | (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0));
    u8 olddh = inb(iobase1 + ATA_CB_DH);
    outb(newdh, iobase1 + ATA_CB_DH);
    if ((olddh ^ newdh) & (1<<4)) {
        // Was a device change - wait for device to become not busy.
        status = ndelay_await_not_bsy(iobase1);
        if (status < 0)
            return status;
    }

    // Check for ATA_CMD_(READ|WRITE)_(SECTORS|DMA)_EXT commands.
    if ((cmd->command & ~0x11) == ATA_CMD_READ_SECTORS_EXT) {
        outb(cmd->feature2, iobase1 + ATA_CB_FR);
        outb(cmd->sector_count2, iobase1 + ATA_CB_SC);
        outb(cmd->lba_low2, iobase1 + ATA_CB_SN);
        outb(cmd->lba_mid2, iobase1 + ATA_CB_CL);
        outb(cmd->lba_high2, iobase1 + ATA_CB_CH);
    }
    outb(cmd->feature, iobase1 + ATA_CB_FR);
    outb(cmd->sector_count, iobase1 + ATA_CB_SC);
    outb(cmd->lba_low, iobase1 + ATA_CB_SN);
    outb(cmd->lba_mid, iobase1 + ATA_CB_CL);
    outb(cmd->lba_high, iobase1 + ATA_CB_CH);
    outb(cmd->command, iobase1 + ATA_CB_CMD);

    return 0;
}
Exemple #10
0
// read disk drive parameters
static void noinline
disk_1308(struct bregs *regs, struct drive_s *drive_gf)
{
    // Get logical geometry from table
    struct chs_s chs = getLCHS(drive_gf);
    u16 nlc=chs.cylinder, nlh=chs.head, nls=chs.sector;
    nlc--;
    nlh--;
    u8 count;
    if (regs->dl < EXTSTART_HD) {
        // Floppy
        count = GET_GLOBAL(FloppyCount);

        if (CONFIG_CDROM_EMU && drive_gf == GET_GLOBAL(cdemu_drive_gf))
            regs->bx = GET_LOW(CDEmu.media) * 2;
        else
            regs->bx = GET_GLOBALFLAT(drive_gf->floppy_type);

        // set es & di to point to 11 byte diskette param table in ROM
        regs->es = SEG_BIOS;
        regs->di = (u32)&diskette_param_table2;
    } else if (regs->dl < EXTSTART_CD) {
        // Hard drive
        count = GET_BDA(hdcount);
        nlc--;  // last sector reserved
    } else {
        // Not supported on CDROM
        disk_ret(regs, DISK_RET_EPARAM);
        return;
    }

    if (CONFIG_CDROM_EMU && GET_LOW(CDEmu.media)) {
        u8 emudrive = GET_LOW(CDEmu.emulated_drive);
        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) | (nls & 0x3f);
    regs->dh = nlh;

    disk_ret(regs, DISK_RET_SUCCESS);
    regs->dl = count;
}
Exemple #11
0
// Create a scsi command request from a disk_op_s request
int
scsi_fill_cmd(struct disk_op_s *op, void *cdbcmd, int maxcdb)
{
    switch (op->command) {
    case CMD_READ:
    case CMD_WRITE: ;
        struct cdb_rwdata_10 *cmd = cdbcmd;
        memset(cmd, 0, maxcdb);
        cmd->command = (op->command == CMD_READ ? CDB_CMD_READ_10
                        : CDB_CMD_WRITE_10);
        cmd->lba = cpu_to_be32(op->lba);
        cmd->count = cpu_to_be16(op->count);
        return GET_GLOBALFLAT(op->drive_gf->blksize);
    case CMD_SCSI:
        memcpy(cdbcmd, op->cdbcmd, maxcdb);
        return op->blocksize;
    default:
        return -1;
    }
}
Exemple #12
0
// Transfer 'op->count' blocks (of 'blocksize' bytes) to/from drive
// 'op->drive_gf'.
static int
ata_pio_transfer(struct disk_op_s *op, int iswrite, int blocksize)
{
    dprintf(16, "ata_pio_transfer id=%p write=%d count=%d bs=%d buf=%p\n"
            , op->drive_gf, iswrite, op->count, blocksize, op->buf_fl);

    struct atadrive_s *adrive_gf = container_of(
        op->drive_gf, struct atadrive_s, drive);
    struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf);
    u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
    u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
    int count = op->count;
    void *buf_fl = op->buf_fl;
    int status;
    for (;;) {
        if (iswrite) {
            // Write data to controller
            dprintf(16, "Write sector id=%p dest=%p\n", op->drive_gf, buf_fl);
            if (CONFIG_ATA_PIO32)
                outsl_fl(iobase1, buf_fl, blocksize / 4);
            else
                outsw_fl(iobase1, buf_fl, blocksize / 2);
        } else {
            // Read data from controller
            dprintf(16, "Read sector id=%p dest=%p\n", op->drive_gf, buf_fl);
            if (CONFIG_ATA_PIO32)
                insl_fl(iobase1, buf_fl, blocksize / 4);
            else
                insw_fl(iobase1, buf_fl, blocksize / 2);
        }
        buf_fl += blocksize;

        status = pause_await_not_bsy(iobase1, iobase2);
        if (status < 0) {
            // Error
            op->count -= count;
            return status;
        }

        count--;
        if (!count)
            break;
        status &= (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR);
        if (status != ATA_CB_STAT_DRQ) {
            dprintf(6, "ata_pio_transfer : more sectors left (status %02x)\n"
                    , status);
            op->count -= count;
            return -6;
        }
    }

    status &= (ATA_CB_STAT_BSY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ
               | ATA_CB_STAT_ERR);
    if (!iswrite)
        status &= ~ATA_CB_STAT_DF;
    if (status != 0) {
        dprintf(6, "ata_pio_transfer : no sectors left (status %02x)\n", status);
        return -7;
    }

    return 0;
}
Exemple #13
0
// IBM/MS get drive parameters
static void
disk_1348(struct bregs *regs, struct drive_s *drive_g)
{
    u16 size = GET_INT13DPT(regs, size);
    u16 t13 = size == 74;

    // Buffer is too small
    if (size < 26) {
        disk_ret(regs, DISK_RET_EPARAM);
        return;
    }

    // EDD 1.x

    u8  type    = GET_GLOBAL(drive_g->type);
    u16 npc     = GET_GLOBAL(drive_g->pchs.cylinders);
    u16 nph     = GET_GLOBAL(drive_g->pchs.heads);
    u16 npspt   = GET_GLOBAL(drive_g->pchs.spt);
    u64 lba     = GET_GLOBAL(drive_g->sectors);
    u16 blksize = GET_GLOBAL(drive_g->blksize);

    dprintf(DEBUG_HDL_13, "disk_1348 size=%d t=%d chs=%d,%d,%d lba=%d bs=%d\n"
            , size, type, npc, nph, npspt, (u32)lba, blksize);

    SET_INT13DPT(regs, size, 26);
    if (type == DTYPE_ATAPI) {
        // 0x74 = removable, media change, lockable, max values
        SET_INT13DPT(regs, infos, 0x74);
        SET_INT13DPT(regs, cylinders, 0xffffffff);
        SET_INT13DPT(regs, heads, 0xffffffff);
        SET_INT13DPT(regs, spt, 0xffffffff);
        SET_INT13DPT(regs, sector_count, (u64)-1);
    } else {
        if (lba > (u64)npspt*nph*0x3fff) {
            SET_INT13DPT(regs, infos, 0x00); // geometry is invalid
            SET_INT13DPT(regs, cylinders, 0x3fff);
        } else {
            SET_INT13DPT(regs, infos, 0x02); // geometry is valid
            SET_INT13DPT(regs, cylinders, (u32)npc);
        }
        SET_INT13DPT(regs, heads, (u32)nph);
        SET_INT13DPT(regs, spt, (u32)npspt);
        SET_INT13DPT(regs, sector_count, lba);
    }
    SET_INT13DPT(regs, blksize, blksize);

    if (size < 30 ||
        (type != DTYPE_ATA && type != DTYPE_ATAPI &&
         type != DTYPE_VIRTIO_BLK && type != DTYPE_VIRTIO_SCSI)) {
        disk_ret(regs, DISK_RET_SUCCESS);
        return;
    }

    // EDD 2.x

    int bdf;
    u16 iobase1 = 0;
    u64 device_path = 0;
    u8 channel = 0;
    SET_INT13DPT(regs, size, 30);
    if (type == DTYPE_ATA || type == DTYPE_ATAPI) {
        u16 ebda_seg = get_ebda_seg();

        SET_INT13DPT(regs, dpte_segment, ebda_seg);
        SET_INT13DPT(regs, dpte_offset
                     , offsetof(struct extended_bios_data_area_s, dpte));

        // Fill in dpte
        struct atadrive_s *adrive_g = container_of(
            drive_g, struct atadrive_s, drive);
        struct ata_channel_s *chan_gf = GET_GLOBAL(adrive_g->chan_gf);
        u8 slave = GET_GLOBAL(adrive_g->slave);
        u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
        u8 irq = GET_GLOBALFLAT(chan_gf->irq);
        iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
        bdf = GET_GLOBALFLAT(chan_gf->pci_bdf);
        device_path = slave;
        channel = GET_GLOBALFLAT(chan_gf->chanid);

        u16 options = 0;
        if (type == DTYPE_ATA) {
            u8 translation = GET_GLOBAL(drive_g->translation);
            if (translation != TRANSLATION_NONE) {
                options |= 1<<3; // CHS translation
                if (translation == TRANSLATION_LBA)
                    options |= 1<<9;
                if (translation == TRANSLATION_RECHS)
                    options |= 3<<9;
            }
        } else {
            // ATAPI
            options |= 1<<5; // removable device
            options |= 1<<6; // atapi device
        }
        options |= 1<<4; // lba translation
        if (CONFIG_ATA_PIO32)
            options |= 1<<7;

        SET_EBDA2(ebda_seg, dpte.iobase1, iobase1);
        SET_EBDA2(ebda_seg, dpte.iobase2, iobase2 + ATA_CB_DC);
        SET_EBDA2(ebda_seg, dpte.prefix, ((slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0)
                                          | ATA_CB_DH_LBA));
        SET_EBDA2(ebda_seg, dpte.unused, 0xcb);
        SET_EBDA2(ebda_seg, dpte.irq, irq);
        SET_EBDA2(ebda_seg, dpte.blkcount, 1);
        SET_EBDA2(ebda_seg, dpte.dma, 0);
        SET_EBDA2(ebda_seg, dpte.pio, 0);
        SET_EBDA2(ebda_seg, dpte.options, options);
        SET_EBDA2(ebda_seg, dpte.reserved, 0);
        SET_EBDA2(ebda_seg, dpte.revision, 0x11);

        u8 sum = checksum_far(
            ebda_seg, (void*)offsetof(struct extended_bios_data_area_s, dpte), 15);
        SET_EBDA2(ebda_seg, dpte.checksum, -sum);
    } else {
Exemple #14
0
// IBM/MS get drive parameters
static void noinline
disk_1348(struct bregs *regs, struct drive_s *drive_gf)
{
    u16 seg = regs->ds;
    struct int13dpt_s *param_far = (struct int13dpt_s*)(regs->si+0);
    u16 size = GET_FARVAR(seg, param_far->size);
    u16 t13 = size == 74;

    // Buffer is too small
    if (size < 26) {
        disk_ret(regs, DISK_RET_EPARAM);
        return;
    }

    // EDD 1.x

    u8  type    = GET_GLOBALFLAT(drive_gf->type);
    u16 npc     = GET_GLOBALFLAT(drive_gf->pchs.cylinder);
    u16 nph     = GET_GLOBALFLAT(drive_gf->pchs.head);
    u16 nps     = GET_GLOBALFLAT(drive_gf->pchs.sector);
    u64 lba     = GET_GLOBALFLAT(drive_gf->sectors);
    u16 blksize = GET_GLOBALFLAT(drive_gf->blksize);

    dprintf(DEBUG_HDL_13, "disk_1348 size=%d t=%d chs=%d,%d,%d lba=%d bs=%d\n"
            , size, type, npc, nph, nps, (u32)lba, blksize);

    SET_FARVAR(seg, param_far->size, 26);
    if (type == DTYPE_ATA_ATAPI) {
        // 0x74 = removable, media change, lockable, max values
        SET_FARVAR(seg, param_far->infos, 0x74);
        SET_FARVAR(seg, param_far->cylinders, 0xffffffff);
        SET_FARVAR(seg, param_far->heads, 0xffffffff);
        SET_FARVAR(seg, param_far->spt, 0xffffffff);
        SET_FARVAR(seg, param_far->sector_count, (u64)-1);
    } else {
        if (lba > (u64)nps*nph*0x3fff) {
            SET_FARVAR(seg, param_far->infos, 0x00); // geometry is invalid
            SET_FARVAR(seg, param_far->cylinders, 0x3fff);
        } else {
            SET_FARVAR(seg, param_far->infos, 0x02); // geometry is valid
            SET_FARVAR(seg, param_far->cylinders, (u32)npc);
        }
        SET_FARVAR(seg, param_far->heads, (u32)nph);
        SET_FARVAR(seg, param_far->spt, (u32)nps);
        SET_FARVAR(seg, param_far->sector_count, lba);
    }
    SET_FARVAR(seg, param_far->blksize, blksize);

    if (size < 30 ||
        (type != DTYPE_ATA && type != DTYPE_ATA_ATAPI &&
         type != DTYPE_VIRTIO_BLK && type != DTYPE_VIRTIO_SCSI)) {
        disk_ret(regs, DISK_RET_SUCCESS);
        return;
    }

    // EDD 2.x

    int bdf;
    u16 iobase1 = 0;
    u64 device_path = 0;
    u8 channel = 0;
    SET_FARVAR(seg, param_far->size, 30);
    if (type == DTYPE_ATA || type == DTYPE_ATA_ATAPI) {
        SET_FARVAR(seg, param_far->dpte, SEGOFF(SEG_LOW, (u32)&DefaultDPTE));

        // Fill in dpte
        struct atadrive_s *adrive_gf = container_of(
            drive_gf, struct atadrive_s, drive);
        struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf);
        u8 slave = GET_GLOBALFLAT(adrive_gf->slave);
        u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2);
        u8 irq = GET_GLOBALFLAT(chan_gf->irq);
        iobase1 = GET_GLOBALFLAT(chan_gf->iobase1);
        bdf = GET_GLOBALFLAT(chan_gf->pci_bdf);
        device_path = slave;
        channel = GET_GLOBALFLAT(chan_gf->chanid);

        u16 options = 0;
        if (type == DTYPE_ATA) {
            u8 translation = GET_GLOBALFLAT(drive_gf->translation);
            if (translation != TRANSLATION_NONE) {
                options |= 1<<3; // CHS translation
                if (translation == TRANSLATION_LBA)
                    options |= 1<<9;
                if (translation == TRANSLATION_RECHS)
                    options |= 3<<9;
            }
        } else {
            // ATAPI
            options |= 1<<5; // removable device
            options |= 1<<6; // atapi device
        }
        options |= 1<<4; // lba translation
        if (CONFIG_ATA_PIO32)
            options |= 1<<7;

        SET_LOW(DefaultDPTE.iobase1, iobase1);
        SET_LOW(DefaultDPTE.iobase2, iobase2 + ATA_CB_DC);
        SET_LOW(DefaultDPTE.prefix, ((slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0)
                                  | ATA_CB_DH_LBA));
        SET_LOW(DefaultDPTE.unused, 0xcb);
        SET_LOW(DefaultDPTE.irq, irq);
        SET_LOW(DefaultDPTE.blkcount, 1);
        SET_LOW(DefaultDPTE.dma, 0);
        SET_LOW(DefaultDPTE.pio, 0);
        SET_LOW(DefaultDPTE.options, options);
        SET_LOW(DefaultDPTE.reserved, 0);
        SET_LOW(DefaultDPTE.revision, 0x11);

        u8 sum = checksum_far(SEG_LOW, &DefaultDPTE, 15);
        SET_LOW(DefaultDPTE.checksum, -sum);
    } else {