qemu-e2k/hw/ide/pci.c
Marcelo Tosatti 42ee76fe82 ide save/restore current transfer fields
If migration takes place between write of the bmdma address register and
write of the command register (to initiate DMA), the destination will
not properly start the DMA op, hanging the guest:

ata1.00: exception Emask 0x0 SAct 0x0 SErr 0x0 action 0x6 frozen
ata1.00: cmd c8/00:16:41:00:00/00:00:00:00:00/e0 tag 0 dma 11264 in
         res 40/00:00:00:00:00/00:00:00:00:00/00 Emask 0x4 (timeout)
ata1.00: status: { DRDY }

Fix by sending current transfer information in the migration data.

We need to update ide version to 4 for this to work.  As we don't
have subsectios, we need to chain the update increase until
vmstate_ide_pci (quintela)

Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2010-01-26 15:42:02 -06:00

187 lines
5.3 KiB
C

/*
* QEMU IDE Emulation: PCI Bus support.
*
* Copyright (c) 2003 Fabrice Bellard
* Copyright (c) 2006 Openedhand Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <hw/hw.h>
#include <hw/pc.h>
#include <hw/pci.h>
#include <hw/isa.h>
#include "block.h"
#include "block_int.h"
#include "sysemu.h"
#include "dma.h"
#include <hw/ide/pci.h>
void bmdma_cmd_writeb(void *opaque, uint32_t addr, uint32_t val)
{
BMDMAState *bm = opaque;
#ifdef DEBUG_IDE
printf("%s: 0x%08x\n", __func__, val);
#endif
if (!(val & BM_CMD_START)) {
/* XXX: do it better */
ide_dma_cancel(bm);
bm->cmd = val & 0x09;
} else {
if (!(bm->status & BM_STATUS_DMAING)) {
bm->status |= BM_STATUS_DMAING;
/* start dma transfer if possible */
if (bm->dma_cb)
bm->dma_cb(bm, 0);
}
bm->cmd = val & 0x09;
}
}
uint32_t bmdma_addr_readb(void *opaque, uint32_t addr)
{
BMDMAState *bm = opaque;
uint32_t val;
val = (bm->addr >> ((addr & 3) * 8)) & 0xff;
#ifdef DEBUG_IDE
printf("%s: 0x%08x\n", __func__, val);
#endif
return val;
}
void bmdma_addr_writeb(void *opaque, uint32_t addr, uint32_t val)
{
BMDMAState *bm = opaque;
int shift = (addr & 3) * 8;
#ifdef DEBUG_IDE
printf("%s: 0x%08x\n", __func__, val);
#endif
bm->addr &= ~(0xFF << shift);
bm->addr |= ((val & 0xFF) << shift) & ~3;
bm->cur_addr = bm->addr;
}
uint32_t bmdma_addr_readw(void *opaque, uint32_t addr)
{
BMDMAState *bm = opaque;
uint32_t val;
val = (bm->addr >> ((addr & 3) * 8)) & 0xffff;
#ifdef DEBUG_IDE
printf("%s: 0x%08x\n", __func__, val);
#endif
return val;
}
void bmdma_addr_writew(void *opaque, uint32_t addr, uint32_t val)
{
BMDMAState *bm = opaque;
int shift = (addr & 3) * 8;
#ifdef DEBUG_IDE
printf("%s: 0x%08x\n", __func__, val);
#endif
bm->addr &= ~(0xFFFF << shift);
bm->addr |= ((val & 0xFFFF) << shift) & ~3;
bm->cur_addr = bm->addr;
}
uint32_t bmdma_addr_readl(void *opaque, uint32_t addr)
{
BMDMAState *bm = opaque;
uint32_t val;
val = bm->addr;
#ifdef DEBUG_IDE
printf("%s: 0x%08x\n", __func__, val);
#endif
return val;
}
void bmdma_addr_writel(void *opaque, uint32_t addr, uint32_t val)
{
BMDMAState *bm = opaque;
#ifdef DEBUG_IDE
printf("%s: 0x%08x\n", __func__, val);
#endif
bm->addr = val & ~3;
bm->cur_addr = bm->addr;
}
static const VMStateDescription vmstate_bmdma = {
.name = "ide bmdma",
.version_id = 4,
.minimum_version_id = 0,
.minimum_version_id_old = 0,
.fields = (VMStateField []) {
VMSTATE_UINT8(cmd, BMDMAState),
VMSTATE_UINT8(status, BMDMAState),
VMSTATE_UINT32(addr, BMDMAState),
VMSTATE_INT64(sector_num, BMDMAState),
VMSTATE_UINT32(nsector, BMDMAState),
VMSTATE_UINT8(unit, BMDMAState),
VMSTATE_UINT32_V(cur_addr, BMDMAState, 4),
VMSTATE_UINT32_V(cur_prd_last, BMDMAState, 4),
VMSTATE_UINT32_V(cur_prd_addr, BMDMAState, 4),
VMSTATE_UINT32_V(cur_prd_len, BMDMAState, 4),
VMSTATE_END_OF_LIST()
}
};
static int ide_pci_post_load(void *opaque, int version_id)
{
PCIIDEState *d = opaque;
int i;
for(i = 0; i < 2; i++) {
/* current versions always store 0/1, but older version
stored bigger values. We only need last bit */
d->bmdma[i].unit &= 1;
}
return 0;
}
const VMStateDescription vmstate_ide_pci = {
.name = "ide",
.version_id = 4,
.minimum_version_id = 0,
.minimum_version_id_old = 0,
.post_load = ide_pci_post_load,
.fields = (VMStateField []) {
VMSTATE_PCI_DEVICE(dev, PCIIDEState),
VMSTATE_STRUCT_ARRAY(bmdma, PCIIDEState, 2, 0,
vmstate_bmdma, BMDMAState),
VMSTATE_IDE_BUS_ARRAY(bus, PCIIDEState, 2),
VMSTATE_IDE_DRIVES(bus[0].ifs, PCIIDEState),
VMSTATE_IDE_DRIVES(bus[1].ifs, PCIIDEState),
VMSTATE_END_OF_LIST()
}
};
void pci_ide_create_devs(PCIDevice *dev, DriveInfo **hd_table)
{
PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev);
static const int bus[4] = { 0, 0, 1, 1 };
static const int unit[4] = { 0, 1, 0, 1 };
int i;
for (i = 0; i < 4; i++) {
if (hd_table[i] == NULL)
continue;
ide_create_drive(d->bus+bus[i], unit[i], hd_table[i]);
}
}