*/
#include "qemu/osdep.h"
#include "hw/hw.h"
-#include "hw/i386/pc.h"
#include "hw/pci/pci.h"
#include "hw/isa/isa.h"
#include "qemu/error-report.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
+#include "sysemu/blockdev.h"
#include "sysemu/dma.h"
#include "hw/block/block.h"
#include "sysemu/block-backend.h"
{ 190, 0x03, 0x00, 0x45, 0x45, 0x1f, 0x00, 0x1f, 0x1f, 0x00, 0x00, 0x32},
};
+const char *IDE_DMA_CMD_lookup[IDE_DMA__COUNT] = {
+ [IDE_DMA_READ] = "DMA READ",
+ [IDE_DMA_WRITE] = "DMA WRITE",
+ [IDE_DMA_TRIM] = "DMA TRIM",
+ [IDE_DMA_ATAPI] = "DMA ATAPI"
+};
+
+static const char *IDE_DMA_CMD_str(enum ide_dma_cmd enval)
+{
+ if ((unsigned)enval < IDE_DMA__COUNT) {
+ return IDE_DMA_CMD_lookup[enval];
+ }
+ return "DMA UNKNOWN CMD";
+}
+
static void ide_dummy_transfer_stop(IDEState *s);
static void padstr(char *str, const char *src, int len)
if (dev && dev->conf.discard_granularity) {
put_le16(p + 169, 1); /* TRIM support */
}
+ if (dev) {
+ put_le16(p + 217, dev->rotation_rate); /* Nominal media rotation rate */
+ }
ide_identify_size(s);
s->identify_set = 1;
}
}
+static bool ide_sect_range_ok(IDEState *s,
+ uint64_t sector, uint64_t nb_sectors)
+{
+ uint64_t total_sectors;
+
+ blk_get_geometry(s->blk, &total_sectors);
+ if (sector > total_sectors || nb_sectors > total_sectors - sector) {
+ return false;
+ }
+ return true;
+}
+
typedef struct TrimAIOCB {
BlockAIOCB common;
- BlockBackend *blk;
+ IDEState *s;
QEMUBH *bh;
int ret;
QEMUIOVector *qiov;
BlockAIOCB *aiocb;
int i, j;
+ bool is_invalid;
} TrimAIOCB;
static void trim_aio_cancel(BlockAIOCB *acb)
{
TrimAIOCB *iocb = opaque;
- iocb->common.cb(iocb->common.opaque, iocb->ret);
-
+ if (iocb->is_invalid) {
+ ide_dma_error(iocb->s);
+ } else {
+ iocb->common.cb(iocb->common.opaque, iocb->ret);
+ }
qemu_bh_delete(iocb->bh);
iocb->bh = NULL;
qemu_aio_unref(iocb);
static void ide_issue_trim_cb(void *opaque, int ret)
{
TrimAIOCB *iocb = opaque;
+ IDEState *s = iocb->s;
+
if (ret >= 0) {
while (iocb->j < iocb->qiov->niov) {
int j = iocb->j;
continue;
}
+ if (!ide_sect_range_ok(s, sector, count)) {
+ iocb->is_invalid = true;
+ goto done;
+ }
+
/* Got an entry! Submit and exit. */
- iocb->aiocb = blk_aio_pdiscard(iocb->blk,
+ iocb->aiocb = blk_aio_pdiscard(s->blk,
sector << BDRV_SECTOR_BITS,
count << BDRV_SECTOR_BITS,
ide_issue_trim_cb, opaque);
iocb->ret = ret;
}
+done:
iocb->aiocb = NULL;
if (iocb->bh) {
qemu_bh_schedule(iocb->bh);
int64_t offset, QEMUIOVector *qiov,
BlockCompletionFunc *cb, void *cb_opaque, void *opaque)
{
- BlockBackend *blk = opaque;
+ IDEState *s = opaque;
TrimAIOCB *iocb;
- iocb = blk_aio_get(&trim_aiocb_info, blk, cb, cb_opaque);
- iocb->blk = blk;
+ iocb = blk_aio_get(&trim_aiocb_info, s->blk, cb, cb_opaque);
+ iocb->s = s;
iocb->bh = qemu_bh_new(ide_trim_bh_cb, iocb);
iocb->ret = 0;
iocb->qiov = qiov;
iocb->i = -1;
iocb->j = 0;
+ iocb->is_invalid = false;
ide_issue_trim_cb(iocb, 0);
return &iocb->common;
}
ide_set_irq(s->bus);
}
-static bool ide_sect_range_ok(IDEState *s,
- uint64_t sector, uint64_t nb_sectors)
-{
- uint64_t total_sectors;
-
- blk_get_geometry(s->blk, &total_sectors);
- if (sector > total_sectors || nb_sectors > total_sectors - sector) {
- return false;
- }
- return true;
-}
-
static void ide_buffered_readv_cb(void *opaque, int ret)
{
IDEBufferedRequest *req = opaque;
goto eot;
}
-#ifdef DEBUG_AIO
- printf("ide_dma_cb: sector_num=%" PRId64 " n=%d, cmd_cmd=%d\n",
- sector_num, n, s->dma_cmd);
-#endif
+ trace_ide_dma_cb(s, sector_num, n, IDE_DMA_CMD_str(s->dma_cmd));
if ((s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) &&
!ide_sect_range_ok(s, sector_num, n)) {
case IDE_DMA_TRIM:
s->bus->dma->aiocb = dma_blk_io(blk_get_aio_context(s->blk),
&s->sg, offset, BDRV_SECTOR_SIZE,
- ide_issue_trim, s->blk, ide_dma_cb, s,
+ ide_issue_trim, s, ide_dma_cb, s,
DMA_DIRECTION_TO_DEVICE);
break;
default:
bus->ifs[1].select &= ~(1 << 7);
}
+/* IOport [W]rite [R]egisters */
+enum ATA_IOPORT_WR {
+ ATA_IOPORT_WR_DATA = 0,
+ ATA_IOPORT_WR_FEATURES = 1,
+ ATA_IOPORT_WR_SECTOR_COUNT = 2,
+ ATA_IOPORT_WR_SECTOR_NUMBER = 3,
+ ATA_IOPORT_WR_CYLINDER_LOW = 4,
+ ATA_IOPORT_WR_CYLINDER_HIGH = 5,
+ ATA_IOPORT_WR_DEVICE_HEAD = 6,
+ ATA_IOPORT_WR_COMMAND = 7,
+ ATA_IOPORT_WR_NUM_REGISTERS,
+};
+
+const char *ATA_IOPORT_WR_lookup[ATA_IOPORT_WR_NUM_REGISTERS] = {
+ [ATA_IOPORT_WR_DATA] = "Data",
+ [ATA_IOPORT_WR_FEATURES] = "Features",
+ [ATA_IOPORT_WR_SECTOR_COUNT] = "Sector Count",
+ [ATA_IOPORT_WR_SECTOR_NUMBER] = "Sector Number",
+ [ATA_IOPORT_WR_CYLINDER_LOW] = "Cylinder Low",
+ [ATA_IOPORT_WR_CYLINDER_HIGH] = "Cylinder High",
+ [ATA_IOPORT_WR_DEVICE_HEAD] = "Device/Head",
+ [ATA_IOPORT_WR_COMMAND] = "Command"
+};
+
void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
{
IDEBus *bus = opaque;
IDEState *s = idebus_active_if(bus);
int reg_num = addr & 7;
- trace_ide_ioport_write(addr, val, bus, s);
+ trace_ide_ioport_write(addr, ATA_IOPORT_WR_lookup[reg_num], val, bus, s);
/* ignore writes to command block while busy with previous command */
if (reg_num != 7 && (s->status & (BUSY_STAT|DRQ_STAT))) {
switch (reg_num) {
case 0:
break;
- case 1:
- ide_clear_hob(bus);
+ case ATA_IOPORT_WR_FEATURES:
+ ide_clear_hob(bus);
/* NOTE: data is written to the two drives */
- bus->ifs[0].hob_feature = bus->ifs[0].feature;
- bus->ifs[1].hob_feature = bus->ifs[1].feature;
+ bus->ifs[0].hob_feature = bus->ifs[0].feature;
+ bus->ifs[1].hob_feature = bus->ifs[1].feature;
bus->ifs[0].feature = val;
bus->ifs[1].feature = val;
break;
- case 2:
+ case ATA_IOPORT_WR_SECTOR_COUNT:
ide_clear_hob(bus);
bus->ifs[0].hob_nsector = bus->ifs[0].nsector;
bus->ifs[1].hob_nsector = bus->ifs[1].nsector;
bus->ifs[0].nsector = val;
bus->ifs[1].nsector = val;
break;
- case 3:
+ case ATA_IOPORT_WR_SECTOR_NUMBER:
ide_clear_hob(bus);
bus->ifs[0].hob_sector = bus->ifs[0].sector;
bus->ifs[1].hob_sector = bus->ifs[1].sector;
bus->ifs[0].sector = val;
bus->ifs[1].sector = val;
break;
- case 4:
+ case ATA_IOPORT_WR_CYLINDER_LOW:
ide_clear_hob(bus);
bus->ifs[0].hob_lcyl = bus->ifs[0].lcyl;
bus->ifs[1].hob_lcyl = bus->ifs[1].lcyl;
bus->ifs[0].lcyl = val;
bus->ifs[1].lcyl = val;
break;
- case 5:
+ case ATA_IOPORT_WR_CYLINDER_HIGH:
ide_clear_hob(bus);
bus->ifs[0].hob_hcyl = bus->ifs[0].hcyl;
bus->ifs[1].hob_hcyl = bus->ifs[1].hcyl;
bus->ifs[0].hcyl = val;
bus->ifs[1].hcyl = val;
break;
- case 6:
+ case ATA_IOPORT_WR_DEVICE_HEAD:
/* FIXME: HOB readback uses bit 7 */
bus->ifs[0].select = (val & ~0x10) | 0xa0;
bus->ifs[1].select = (val | 0x10) | 0xa0;
bus->unit = (val >> 4) & 1;
break;
default:
- case 7:
+ case ATA_IOPORT_WR_COMMAND:
/* command */
ide_exec_cmd(bus, val);
break;
}
}
+/* IOport [R]ead [R]egisters */
+enum ATA_IOPORT_RR {
+ ATA_IOPORT_RR_DATA = 0,
+ ATA_IOPORT_RR_ERROR = 1,
+ ATA_IOPORT_RR_SECTOR_COUNT = 2,
+ ATA_IOPORT_RR_SECTOR_NUMBER = 3,
+ ATA_IOPORT_RR_CYLINDER_LOW = 4,
+ ATA_IOPORT_RR_CYLINDER_HIGH = 5,
+ ATA_IOPORT_RR_DEVICE_HEAD = 6,
+ ATA_IOPORT_RR_STATUS = 7,
+ ATA_IOPORT_RR_NUM_REGISTERS,
+};
+
+const char *ATA_IOPORT_RR_lookup[ATA_IOPORT_RR_NUM_REGISTERS] = {
+ [ATA_IOPORT_RR_DATA] = "Data",
+ [ATA_IOPORT_RR_ERROR] = "Error",
+ [ATA_IOPORT_RR_SECTOR_COUNT] = "Sector Count",
+ [ATA_IOPORT_RR_SECTOR_NUMBER] = "Sector Number",
+ [ATA_IOPORT_RR_CYLINDER_LOW] = "Cylinder Low",
+ [ATA_IOPORT_RR_CYLINDER_HIGH] = "Cylinder High",
+ [ATA_IOPORT_RR_DEVICE_HEAD] = "Device/Head",
+ [ATA_IOPORT_RR_STATUS] = "Status"
+};
+
uint32_t ide_ioport_read(void *opaque, uint32_t addr)
{
IDEBus *bus = opaque;
//hob = s->select & (1 << 7);
hob = 0;
switch (reg_num) {
- case 0:
+ case ATA_IOPORT_RR_DATA:
ret = 0xff;
break;
- case 1:
+ case ATA_IOPORT_RR_ERROR:
if ((!bus->ifs[0].blk && !bus->ifs[1].blk) ||
(s != bus->ifs && !s->blk)) {
ret = 0;
ret = s->hob_feature;
}
break;
- case 2:
+ case ATA_IOPORT_RR_SECTOR_COUNT:
if (!bus->ifs[0].blk && !bus->ifs[1].blk) {
ret = 0;
} else if (!hob) {
ret = s->hob_nsector;
}
break;
- case 3:
+ case ATA_IOPORT_RR_SECTOR_NUMBER:
if (!bus->ifs[0].blk && !bus->ifs[1].blk) {
ret = 0;
} else if (!hob) {
ret = s->hob_sector;
}
break;
- case 4:
+ case ATA_IOPORT_RR_CYLINDER_LOW:
if (!bus->ifs[0].blk && !bus->ifs[1].blk) {
ret = 0;
} else if (!hob) {
ret = s->hob_lcyl;
}
break;
- case 5:
+ case ATA_IOPORT_RR_CYLINDER_HIGH:
if (!bus->ifs[0].blk && !bus->ifs[1].blk) {
ret = 0;
} else if (!hob) {
ret = s->hob_hcyl;
}
break;
- case 6:
+ case ATA_IOPORT_RR_DEVICE_HEAD:
if (!bus->ifs[0].blk && !bus->ifs[1].blk) {
ret = 0;
} else {
}
break;
default:
- case 7:
+ case ATA_IOPORT_RR_STATUS:
if ((!bus->ifs[0].blk && !bus->ifs[1].blk) ||
(s != bus->ifs && !s->blk)) {
ret = 0;
break;
}
- trace_ide_ioport_read(addr, ret, bus, s);
+ trace_ide_ioport_read(addr, ATA_IOPORT_RR_lookup[reg_num], ret, bus, s);
return ret;
}
IDEState *s = idebus_active_if(bus);
uint8_t *p;
+ trace_ide_data_writew(addr, val, bus, s);
+
/* PIO data access allowed only when DRQ bit is set. The result of a write
* during PIO out is indeterminate, just ignore it. */
if (!(s->status & DRQ_STAT) || ide_is_pio_out(s)) {
s->status &= ~DRQ_STAT;
s->end_transfer_func(s);
}
+
+ trace_ide_data_readw(addr, ret, bus, s);
return ret;
}
IDEState *s = idebus_active_if(bus);
uint8_t *p;
+ trace_ide_data_writel(addr, val, bus, s);
+
/* PIO data access allowed only when DRQ bit is set. The result of a write
* during PIO out is indeterminate, just ignore it. */
if (!(s->status & DRQ_STAT) || ide_is_pio_out(s)) {
/* PIO data access allowed only when DRQ bit is set. The result of a read
* during PIO in is indeterminate, return 0 and don't move forward. */
if (!(s->status & DRQ_STAT) || !ide_is_pio_out(s)) {
- return 0;
+ ret = 0;
+ goto out;
}
p = s->data_ptr;
s->status &= ~DRQ_STAT;
s->end_transfer_func(s);
}
+
+out:
+ trace_ide_data_readl(addr, ret, bus, s);
return ret;
}
/* pending async DMA */
if (bus->dma->aiocb) {
-#ifdef DEBUG_AIO
- printf("aio_cancel\n");
-#endif
+ trace_ide_bus_reset_aio();
blk_aio_cancel(bus->dma->aiocb);
bus->dma->aiocb = NULL;
}
const char *version, const char *serial, const char *model,
uint64_t wwn,
uint32_t cylinders, uint32_t heads, uint32_t secs,
- int chs_trans)
+ int chs_trans, Error **errp)
{
uint64_t nb_sectors;
blk_set_guest_block_size(blk, 2048);
} else {
if (!blk_is_inserted(s->blk)) {
- error_report("Device needs media, but drive is empty");
+ error_setg(errp, "Device needs media, but drive is empty");
return -1;
}
if (blk_is_read_only(blk)) {
- error_report("Can't use a read-only drive");
+ error_setg(errp, "Can't use a read-only drive");
return -1;
}
blk_set_dev_ops(blk, &ide_hd_block_ops, s);
return 0;
}
-static void ide_drive_pio_pre_save(void *opaque)
+static int ide_drive_pio_pre_save(void *opaque)
{
IDEState *s = opaque;
int idx;
} else {
s->end_transfer_fn_idx = idx;
}
+
+ return 0;
}
static bool ide_drive_pio_state_needed(void *opaque)