// 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; }
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); }
// 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; }
// 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); }
// 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); }
// 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); }
// 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; }
// 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; }
// 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; }
// 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; }
// 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; } }
// 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; }
// 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 {
// 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 {