Compare commits

..
19 Commits
Author SHA1 Message Date
Anthony Liguori 47ec15cdd4 Update version for 1.5.0-rc1
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-05-08 15:54:47 -05:00
Anthony Liguori cad2b59c19 Merge remote-tracking branch 'kwolf/for-anthony' into staging
# By Kevin Wolf (7) and Fam Zheng (3)
# Via Kevin Wolf
* kwolf/for-anthony:
  qemu-iotests: fix 017 018 for vmdk
  qemu-iotests: exclude vmdk and qcow from 043
  qemu-iotests: exclude vmdk for test 042
  qtest/ide-test: Test short and long PRDTs
  qtest/ide-test: Add simple DMA read/write test case
  qtest: Add IDE test case
  libqos/pci: Enable bus mastering
  ide: Reset BMIDEA bit when the bus master is stopped
  de_DE.po: Add missing leading spaces
  ahci: Don't allow creating slave drives

Message-id: 1368023344-29731-1-git-send-email-kwolf@redhat.com
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-05-08 15:54:36 -05:00
Jason Wang 5f5a131865 virtio: properly validate address before accessing config
There are several several issues in the current checking:

- The check was based on the minus of unsigned values which can overflow
- It was done after .{set|get}_config() which can lead crash when config_len
  is zero since vdev->config is NULL

Fix this by:

- Validate the address in virtio_pci_config_{read|write}() before
  .{set|get}_config
- Use addition instead minus to do the validation

Cc: Michael S. Tsirkin <mst@redhat.com>
Cc: Petr Matousek <pmatouse@redhat.com>
Signed-off-by: Jason Wang <jasowang@redhat.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Acked-by: Petr Matousek <pmatouse@redhat.com>
Message-id: 1367905369-10765-1-git-send-email-jasowang@redhat.com
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-05-08 15:54:21 -05:00
Michael S. Tsirkin 62c96360ae virtio-pci: fix level interrupts
mask notifiers are never called without msix,
so devices with backend masking like vhost don't work.
Call mask notifiers explicitly at
startup/cleanup to make it work.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Tested-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2013-05-08 15:54:21 -05:00
Aurelien Jarno d551d220c9 Merge branch 'ppc-for-upstream' of git://github.com/agraf/qemu
* 'ppc-for-upstream' of git://github.com/agraf/qemu:
  PPC: Depend behavior of cmp instructions only on instruction encoding
2013-05-08 21:02:06 +02:00
Alexander Graf 554ecc5774 PPC: Fix rldcl
The implementation for rldcl tried to always fetch its
parameters from the opcode, even though the opcode was
already passed in in decoded and different forms.

Use the parameters instead, fixing rldcl.

Reported-by: Torbjorn Granlund <tg@gmplib.org>
Reviewed-by: Aurelien Jarno <aurelien@aurel32.net>
Signed-off-by: Alexander Graf <agraf@suse.de>
2013-05-08 20:23:20 +02:00
Alexander Graf 36f48d9c78 PPC: Depend behavior of cmp instructions only on instruction encoding
When running an L=1 cmp instruction on a 64bit PPC CPU with SF off, it
still behaves identical to what it does when SF is on. Remove the implicit
difference in the code.

Also, on most 32bit CPUs we should always treat the compare as 32bit
compare, as the CPU will ignore the L bit. This is not true for e500mc,
but that's up for a different patch.

Reported-by: Torbjorn Granlund <tg@gmplib.org>
Reviewed-by: Richard Henderson <rth@twiddle.net>
Signed-off-by: Alexander Graf <agraf@suse.de>
2013-05-08 20:23:20 +02:00
Petar Jovanovic c0f5f9ce86 target-mips: fix incorrect behaviour for INSV
Corner case for INSV instruction when size=32 has not been correctly
implemented. The mask for size should be one bit wider, and preparing the
filter variable should be aware of this case too.

The test for INSV has been extended to include the case that triggers the
bug.

Signed-off-by: Petar Jovanovic <petar.jovanovic@imgtec.com>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
2013-05-08 18:46:38 +02:00
Petar Jovanovic 639eadb9a3 target-mips: add missing check_dspr2 for multiply instructions
The emulator needs to check in hflags if DSP unit has been turned off before
it generates code for MUL_PH, MUL_S_PH, MULQ_S_W, and MULQ_RS_W.

Signed-off-by: Petar Jovanovic <petar.jovanovic@imgtec.com>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
2013-05-08 18:03:31 +02:00
Fam Zheng 794d00f71d qemu-iotests: fix 017 018 for vmdk
017 and 018 use /bin/mv to move base img from t.IMGFMG to t.IMGFMT.base
after filling data, this is not enough for vmdk, when t.IMGFMT is only a
description text file who points to t-{flat,s001,f001,...}.IMGFMT as
data extent, so testing such subformats alway fails on them.

This patch use the trick of temprorily changing TEST_IMG to avoid using
/bin/mv.

Signed-off-by: Fam Zheng <famz@redhat.com>
Reviewed-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-05-08 15:28:50 +02:00
Fam Zheng dbcdd7cb04 qemu-iotests: exclude vmdk and qcow from 043
043 tests recursive backing file by changing backing file. VMDK has not
implemented this yet, and qcow1 probably never will.

Signed-off-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-05-08 15:28:49 +02:00
Fam Zheng 1afe272663 qemu-iotests: exclude vmdk for test 042
Zero sized disk is not supported by qemu vmdk driver, exclude vmdk from
the test script.

As tested on vmware-vdiskmanager and vmware workstation, zero sized disk
is not supported by vmware, either.

Signed-off-by: Fam Zheng <famz@redhat.com>
Reviewed-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-05-08 15:28:49 +02:00
Kevin Wolf 948eaed171 qtest/ide-test: Test short and long PRDTs
This tests the behaviour of the DMA engine when the given PRDT contains
physical region descriptors for either more or less bytes than the
IDE request is for.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-05-08 15:28:49 +02:00
Kevin Wolf b95739dcf5 qtest/ide-test: Add simple DMA read/write test case
This tests that single sectors can be successfully written and correctly
read back.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-05-08 15:28:49 +02:00
Kevin Wolf acbe48013b qtest: Add IDE test case
This adds a simple IDE test case and starts by verifying that IDENTIFY
can be successfully used and return the correct serial number, version
and the WCE flag is set for cache=writeback.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-05-08 15:28:49 +02:00
Kevin Wolf 9f0332b8cf libqos/pci: Enable bus mastering
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-05-08 15:28:49 +02:00
Kevin Wolf b39f961265 ide: Reset BMIDEA bit when the bus master is stopped
The device is supposed to reset the Bus Master IDE Active bit in the
status register when 0 is written to the Start/Stop Bus Master bit in
the command register.

In the common cases this happens automatically because bdrv_drain_all()
flushes the requests, but with a large PRDT it could remain set.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-05-08 15:28:49 +02:00
Kevin Wolf 3bac85d337 de_DE.po: Add missing leading spaces
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-05-08 15:28:49 +02:00
Kevin Wolf 0ee20e6658 ahci: Don't allow creating slave drives
An IDE bus provided by AHCI can only take a single IDE drive. If you add
a drive as slave, qemu used to accept the command line but the device
wouldn't be actually usable. Catch the situation instead and error out.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-05-08 15:28:49 +02:00
27 changed files with 530 additions and 51 deletions

No files matched your search

+1 -1
View File
@@ -1 +1 @@
1.4.90
1.4.91
+1 -1
View File
@@ -1163,7 +1163,7 @@ void ahci_init(AHCIState *s, DeviceState *qdev, DMAContext *dma, int ports)
for (i = 0; i < s->ports; i++) {
AHCIDevice *ad = &s->dev[i];
ide_bus_new(&ad->port, qdev, i);
ide_bus_new(&ad->port, qdev, i, 1);
ide_init2(&ad->port, irqs[i]);
ad->hba = s;
+1 -1
View File
@@ -281,7 +281,7 @@ static int pci_cmd646_ide_initfn(PCIDevice *dev)
irq = qemu_allocate_irqs(cmd646_set_irq, d, 2);
for (i = 0; i < 2; i++) {
ide_bus_new(&d->bus[i], &d->dev.qdev, i);
ide_bus_new(&d->bus[i], &d->dev.qdev, i, 2);
ide_init2(&d->bus[i], irq[i]);
bmdma_init(&d->bus[i], &d->bmdma[i], d);
+2 -1
View File
@@ -450,6 +450,7 @@ struct IDEBus {
IDEDevice *slave;
IDEState ifs[2];
int bus_id;
int max_units;
IDEDMA *dma;
uint8_t unit;
uint8_t cmd;
@@ -574,7 +575,7 @@ void ide_atapi_cmd(IDEState *s);
void ide_atapi_cmd_reply_end(IDEState *s);
/* hw/ide/qdev.c */
void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id);
void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id, int max_units);
IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive);
#endif /* HW_IDE_INTERNAL_H */
+1 -1
View File
@@ -69,7 +69,7 @@ static int isa_ide_initfn(ISADevice *dev)
{
ISAIDEState *s = ISA_IDE(dev);
ide_bus_new(&s->bus, DEVICE(dev), 0);
ide_bus_new(&s->bus, DEVICE(dev), 0, 2);
ide_init_ioport(&s->bus, dev, s->iobase, s->iobase2);
isa_init_irq(dev, &s->irq, s->isairq);
ide_init2(&s->bus, s->irq);
+1 -1
View File
@@ -334,7 +334,7 @@ static void macio_ide_initfn(Object *obj)
SysBusDevice *d = SYS_BUS_DEVICE(obj);
MACIOIDEState *s = MACIO_IDE(obj);
ide_bus_new(&s->bus, DEVICE(obj), 0);
ide_bus_new(&s->bus, DEVICE(obj), 0, 2);
memory_region_init_io(&s->mem, &pmac_ide_ops, s, "pmac-ide", 0x1000);
sysbus_init_mmio(d, &s->mem);
sysbus_init_irq(d, &s->irq);
+1 -1
View File
@@ -137,7 +137,7 @@ static void mmio_ide_initfn(Object *obj)
SysBusDevice *d = SYS_BUS_DEVICE(obj);
MMIOState *s = MMIO_IDE(obj);
ide_bus_new(&s->bus, DEVICE(obj), 0);
ide_bus_new(&s->bus, DEVICE(obj), 0, 2);
sysbus_init_irq(d, &s->irq);
}
+1
View File
@@ -312,6 +312,7 @@ void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val)
bdrv_drain_all();
assert(bm->bus->dma->aiocb == NULL);
}
bm->status &= ~BM_STATUS_DMAING;
} else {
bm->cur_addr = bm->addr;
if (!(bm->status & BM_STATUS_DMAING)) {
+1 -1
View File
@@ -135,7 +135,7 @@ static void pci_piix_init_ports(PCIIDEState *d) {
int i;
for (i = 0; i < 2; i++) {
ide_bus_new(&d->bus[i], &d->dev.qdev, i);
ide_bus_new(&d->bus[i], &d->dev.qdev, i, 2);
ide_init_ioport(&d->bus[i], NULL, port_info[i].iobase,
port_info[i].iobase2);
ide_init2(&d->bus[i], isa_get_irq(NULL, port_info[i].isairq));
+9 -1
View File
@@ -47,10 +47,11 @@ static const TypeInfo ide_bus_info = {
.class_init = ide_bus_class_init,
};
void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id)
void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id, int max_units)
{
qbus_create_inplace(&idebus->qbus, TYPE_IDE_BUS, dev, NULL);
idebus->bus_id = bus_id;
idebus->max_units = max_units;
}
static char *idebus_get_fw_dev_path(DeviceState *dev)
@@ -76,6 +77,13 @@ static int ide_qdev_init(DeviceState *qdev)
if (dev->unit == -1) {
dev->unit = bus->master ? 1 : 0;
}
if (dev->unit >= bus->max_units) {
error_report("Can't create IDE unit %d, bus supports only %d units",
dev->unit, bus->max_units);
goto err;
}
switch (dev->unit) {
case 0:
if (bus->master) {
+1 -1
View File
@@ -158,7 +158,7 @@ static void vt82c686b_init_ports(PCIIDEState *d) {
int i;
for (i = 0; i < 2; i++) {
ide_bus_new(&d->bus[i], &d->dev.qdev, i);
ide_bus_new(&d->bus[i], &d->dev.qdev, i, 2);
ide_init_ioport(&d->bus[i], NULL, port_info[i].iobase,
port_info[i].iobase2);
ide_init2(&d->bus[i], isa_get_irq(NULL, port_info[i].isairq));
+5
View File
@@ -744,6 +744,7 @@ static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign,
bool with_irqfd)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(proxy->vdev);
VirtQueue *vq = virtio_get_queue(proxy->vdev, n);
EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
@@ -758,6 +759,10 @@ static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign,
event_notifier_cleanup(notifier);
}
if (!msix_enabled(&proxy->pci_dev) && vdc->guest_notifier_mask) {
vdc->guest_notifier_mask(proxy->vdev, n, !assign);
}
return 0;
}
+18 -12
View File
@@ -568,10 +568,11 @@ uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr)
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
uint8_t val;
k->get_config(vdev, vdev->config);
if (addr > (vdev->config_len - sizeof(val)))
if (addr + sizeof(val) > vdev->config_len) {
return (uint32_t)-1;
}
k->get_config(vdev, vdev->config);
val = ldub_p(vdev->config + addr);
return val;
@@ -582,10 +583,11 @@ uint32_t virtio_config_readw(VirtIODevice *vdev, uint32_t addr)
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
uint16_t val;
k->get_config(vdev, vdev->config);
if (addr > (vdev->config_len - sizeof(val)))
if (addr + sizeof(val) > vdev->config_len) {
return (uint32_t)-1;
}
k->get_config(vdev, vdev->config);
val = lduw_p(vdev->config + addr);
return val;
@@ -596,10 +598,11 @@ uint32_t virtio_config_readl(VirtIODevice *vdev, uint32_t addr)
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
uint32_t val;
k->get_config(vdev, vdev->config);
if (addr > (vdev->config_len - sizeof(val)))
if (addr + sizeof(val) > vdev->config_len) {
return (uint32_t)-1;
}
k->get_config(vdev, vdev->config);
val = ldl_p(vdev->config + addr);
return val;
@@ -610,8 +613,9 @@ void virtio_config_writeb(VirtIODevice *vdev, uint32_t addr, uint32_t data)
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
uint8_t val = data;
if (addr > (vdev->config_len - sizeof(val)))
if (addr + sizeof(val) > vdev->config_len) {
return;
}
stb_p(vdev->config + addr, val);
@@ -625,8 +629,9 @@ void virtio_config_writew(VirtIODevice *vdev, uint32_t addr, uint32_t data)
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
uint16_t val = data;
if (addr > (vdev->config_len - sizeof(val)))
if (addr + sizeof(val) > vdev->config_len) {
return;
}
stw_p(vdev->config + addr, val);
@@ -640,8 +645,9 @@ void virtio_config_writel(VirtIODevice *vdev, uint32_t addr, uint32_t data)
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
uint32_t val = data;
if (addr > (vdev->config_len - sizeof(val)))
if (addr + sizeof(val) > vdev->config_len) {
return;
}
stl_p(vdev->config + addr, val);
+2 -2
View File
@@ -18,11 +18,11 @@ msgstr ""
#: ../ui/gtk.c:213
msgid " - Press Ctrl+Alt+G to release grab"
msgstr "- Strg+Alt+G drücken, um Eingabegeräte freizugeben"
msgstr " - Strg+Alt+G drücken, um Eingabegeräte freizugeben"
#: ../ui/gtk.c:217
msgid " [Paused]"
msgstr "[Angehalten]"
msgstr " [Angehalten]"
#: ../ui/gtk.c:1250
msgid "_Machine"
+2 -2
View File
@@ -2921,7 +2921,7 @@ target_ulong helper_##name(CPUMIPSState *env, target_ulong rs, \
return rt; \
} \
\
filter = ((int32_t)0x01 << size) - 1; \
filter = ((int64_t)0x01 << size) - 1; \
filter = filter << pos; \
temprs = (rs << pos) & filter; \
temprt = rt & ~filter; \
@@ -2930,7 +2930,7 @@ target_ulong helper_##name(CPUMIPSState *env, target_ulong rs, \
return (target_long)(ret_type)temp; \
}
BIT_INSV(insv, 0x1F, 0x1F, int32_t);
BIT_INSV(insv, 0x1F, 0x3F, int32_t);
#ifdef TARGET_MIPS64
BIT_INSV(dinsv, 0x7F, 0x3F, target_long);
#endif
+1
View File
@@ -13400,6 +13400,7 @@ static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2,
/* OPC_MULT_G_2E, OPC_ADDUH_QB_DSP, OPC_MUL_PH_DSP have
* the same mask and op1. */
case OPC_MULT_G_2E:
check_dspr2(ctx);
switch (op2) {
case OPC_MUL_PH:
gen_helper_mul_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env);
+16 -18
View File
@@ -675,48 +675,48 @@ static inline void gen_set_Rc0(DisasContext *ctx, TCGv reg)
/* cmp */
static void gen_cmp(DisasContext *ctx)
{
if (NARROW_MODE(ctx) || !(ctx->opcode & 0x00200000)) {
gen_op_cmp32(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
1, crfD(ctx->opcode));
} else {
if ((ctx->opcode & 0x00200000) && (ctx->insns_flags & PPC_64B)) {
gen_op_cmp(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
1, crfD(ctx->opcode));
} else {
gen_op_cmp32(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
1, crfD(ctx->opcode));
}
}
/* cmpi */
static void gen_cmpi(DisasContext *ctx)
{
if (NARROW_MODE(ctx) || !(ctx->opcode & 0x00200000)) {
gen_op_cmpi32(cpu_gpr[rA(ctx->opcode)], SIMM(ctx->opcode),
1, crfD(ctx->opcode));
} else {
if ((ctx->opcode & 0x00200000) && (ctx->insns_flags & PPC_64B)) {
gen_op_cmpi(cpu_gpr[rA(ctx->opcode)], SIMM(ctx->opcode),
1, crfD(ctx->opcode));
} else {
gen_op_cmpi32(cpu_gpr[rA(ctx->opcode)], SIMM(ctx->opcode),
1, crfD(ctx->opcode));
}
}
/* cmpl */
static void gen_cmpl(DisasContext *ctx)
{
if (NARROW_MODE(ctx) || !(ctx->opcode & 0x00200000)) {
gen_op_cmp32(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
0, crfD(ctx->opcode));
} else {
if ((ctx->opcode & 0x00200000) && (ctx->insns_flags & PPC_64B)) {
gen_op_cmp(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
0, crfD(ctx->opcode));
} else {
gen_op_cmp32(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
0, crfD(ctx->opcode));
}
}
/* cmpli */
static void gen_cmpli(DisasContext *ctx)
{
if (NARROW_MODE(ctx) || !(ctx->opcode & 0x00200000)) {
gen_op_cmpi32(cpu_gpr[rA(ctx->opcode)], UIMM(ctx->opcode),
0, crfD(ctx->opcode));
} else {
if ((ctx->opcode & 0x00200000) && (ctx->insns_flags & PPC_64B)) {
gen_op_cmpi(cpu_gpr[rA(ctx->opcode)], UIMM(ctx->opcode),
0, crfD(ctx->opcode));
} else {
gen_op_cmpi32(cpu_gpr[rA(ctx->opcode)], UIMM(ctx->opcode),
0, crfD(ctx->opcode));
}
}
@@ -1733,8 +1733,6 @@ static inline void gen_rldnm(DisasContext *ctx, uint32_t mb, uint32_t me)
{
TCGv t0;
mb = MB(ctx->opcode);
me = ME(ctx->opcode);
t0 = tcg_temp_new();
tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x3f);
tcg_gen_rotl_tl(t0, cpu_gpr[rS(ctx->opcode)], t0);
+2
View File
@@ -51,6 +51,7 @@ check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
# really in libqtest, not in the testcases themselves.
check-qtest-i386-y = tests/fdc-test$(EXESUF)
gcov-files-i386-y = hw/fdc.c
check-qtest-i386-y += tests/ide-test$(EXESUF)
check-qtest-i386-y += tests/hd-geo-test$(EXESUF)
gcov-files-i386-y += hw/hd-geometry.c
check-qtest-i386-y += tests/rtc-test$(EXESUF)
@@ -127,6 +128,7 @@ libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o
tests/rtc-test$(EXESUF): tests/rtc-test.o
tests/m48t59-test$(EXESUF): tests/m48t59-test.o
tests/fdc-test$(EXESUF): tests/fdc-test.o
tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y)
tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o
tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y)
tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y)
+440
View File
@@ -0,0 +1,440 @@
/*
* IDE test cases
*
* Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com>
*
* 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 <stdint.h>
#include <string.h>
#include <stdio.h>
#include <glib.h>
#include "libqtest.h"
#include "libqos/pci-pc.h"
#include "libqos/malloc-pc.h"
#include "qemu-common.h"
#include "hw/pci/pci_ids.h"
#include "hw/pci/pci_regs.h"
#define TEST_IMAGE_SIZE 64 * 1024 * 1024
#define IDE_PCI_DEV 1
#define IDE_PCI_FUNC 1
#define IDE_BASE 0x1f0
#define IDE_PRIMARY_IRQ 14
enum {
reg_data = 0x0,
reg_nsectors = 0x2,
reg_lba_low = 0x3,
reg_lba_middle = 0x4,
reg_lba_high = 0x5,
reg_device = 0x6,
reg_status = 0x7,
reg_command = 0x7,
};
enum {
BSY = 0x80,
DRDY = 0x40,
DF = 0x20,
DRQ = 0x08,
ERR = 0x01,
};
enum {
LBA = 0x40,
};
enum {
bmreg_cmd = 0x0,
bmreg_status = 0x2,
bmreg_prdt = 0x4,
};
enum {
CMD_READ_DMA = 0xc8,
CMD_WRITE_DMA = 0xca,
CMD_IDENTIFY = 0xec,
CMDF_ABORT = 0x100,
};
enum {
BM_CMD_START = 0x1,
BM_CMD_WRITE = 0x8, /* write = from device to memory */
};
enum {
BM_STS_ACTIVE = 0x1,
BM_STS_ERROR = 0x2,
BM_STS_INTR = 0x4,
};
enum {
PRDT_EOT = 0x80000000,
};
#define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
#define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
static QPCIBus *pcibus = NULL;
static QGuestAllocator *guest_malloc;
static char tmp_path[] = "/tmp/qtest.XXXXXX";
static void ide_test_start(const char *cmdline_fmt, ...)
{
va_list ap;
char *cmdline;
va_start(ap, cmdline_fmt);
cmdline = g_strdup_vprintf(cmdline_fmt, ap);
va_end(ap);
qtest_start(cmdline);
qtest_irq_intercept_in(global_qtest, "ioapic");
guest_malloc = pc_alloc_init();
}
static void ide_test_quit(void)
{
qtest_quit(global_qtest);
}
static QPCIDevice *get_pci_device(uint16_t *bmdma_base)
{
QPCIDevice *dev;
uint16_t vendor_id, device_id;
if (!pcibus) {
pcibus = qpci_init_pc();
}
/* Find PCI device and verify it's the right one */
dev = qpci_device_find(pcibus, QPCI_DEVFN(IDE_PCI_DEV, IDE_PCI_FUNC));
g_assert(dev != NULL);
vendor_id = qpci_config_readw(dev, PCI_VENDOR_ID);
device_id = qpci_config_readw(dev, PCI_DEVICE_ID);
g_assert(vendor_id == PCI_VENDOR_ID_INTEL);
g_assert(device_id == PCI_DEVICE_ID_INTEL_82371SB_1);
/* Map bmdma BAR */
*bmdma_base = (uint16_t)(uintptr_t) qpci_iomap(dev, 4);
qpci_device_enable(dev);
return dev;
}
static void free_pci_device(QPCIDevice *dev)
{
/* libqos doesn't have a function for this, so free it manually */
g_free(dev);
}
typedef struct PrdtEntry {
uint32_t addr;
uint32_t size;
} QEMU_PACKED PrdtEntry;
#define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
#define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
PrdtEntry *prdt, int prdt_entries)
{
QPCIDevice *dev;
uint16_t bmdma_base;
uintptr_t guest_prdt;
size_t len;
bool from_dev;
uint8_t status;
int flags;
dev = get_pci_device(&bmdma_base);
flags = cmd & ~0xff;
cmd &= 0xff;
switch (cmd) {
case CMD_READ_DMA:
from_dev = true;
break;
case CMD_WRITE_DMA:
from_dev = false;
break;
default:
g_assert_not_reached();
}
/* Select device 0 */
outb(IDE_BASE + reg_device, 0 | LBA);
/* Stop any running transfer, clear any pending interrupt */
outb(bmdma_base + bmreg_cmd, 0);
outb(bmdma_base + bmreg_status, BM_STS_INTR);
/* Setup PRDT */
len = sizeof(*prdt) * prdt_entries;
guest_prdt = guest_alloc(guest_malloc, len);
memwrite(guest_prdt, prdt, len);
outl(bmdma_base + bmreg_prdt, guest_prdt);
/* ATA DMA command */
outb(IDE_BASE + reg_nsectors, nb_sectors);
outb(IDE_BASE + reg_lba_low, sector & 0xff);
outb(IDE_BASE + reg_lba_middle, (sector >> 8) & 0xff);
outb(IDE_BASE + reg_lba_high, (sector >> 16) & 0xff);
outb(IDE_BASE + reg_command, cmd);
/* Start DMA transfer */
outb(bmdma_base + bmreg_cmd, BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0));
if (flags & CMDF_ABORT) {
outb(bmdma_base + bmreg_cmd, 0);
}
/* Wait for the DMA transfer to complete */
do {
status = inb(bmdma_base + bmreg_status);
} while ((status & (BM_STS_ACTIVE | BM_STS_INTR)) == BM_STS_ACTIVE);
g_assert_cmpint(get_irq(IDE_PRIMARY_IRQ), ==, !!(status & BM_STS_INTR));
/* Check IDE status code */
assert_bit_set(inb(IDE_BASE + reg_status), DRDY);
assert_bit_clear(inb(IDE_BASE + reg_status), BSY | DRQ);
/* Reading the status register clears the IRQ */
g_assert(!get_irq(IDE_PRIMARY_IRQ));
/* Stop DMA transfer if still active */
if (status & BM_STS_ACTIVE) {
outb(bmdma_base + bmreg_cmd, 0);
}
free_pci_device(dev);
return status;
}
static void test_bmdma_simple_rw(void)
{
uint8_t status;
uint8_t *buf;
uint8_t *cmpbuf;
size_t len = 512;
uintptr_t guest_buf = guest_alloc(guest_malloc, len);
PrdtEntry prdt[] = {
{ .addr = guest_buf, .size = len | PRDT_EOT },
};
buf = g_malloc(len);
cmpbuf = g_malloc(len);
/* Write 0x55 pattern to sector 0 */
memset(buf, 0x55, len);
memwrite(guest_buf, buf, len);
status = send_dma_request(CMD_WRITE_DMA, 0, 1, prdt, ARRAY_SIZE(prdt));
g_assert_cmphex(status, ==, BM_STS_INTR);
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
/* Write 0xaa pattern to sector 1 */
memset(buf, 0xaa, len);
memwrite(guest_buf, buf, len);
status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt, ARRAY_SIZE(prdt));
g_assert_cmphex(status, ==, BM_STS_INTR);
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
/* Read and verify 0x55 pattern in sector 0 */
memset(cmpbuf, 0x55, len);
status = send_dma_request(CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt));
g_assert_cmphex(status, ==, BM_STS_INTR);
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
memread(guest_buf, buf, len);
g_assert(memcmp(buf, cmpbuf, len) == 0);
/* Read and verify 0xaa pattern in sector 1 */
memset(cmpbuf, 0xaa, len);
status = send_dma_request(CMD_READ_DMA, 1, 1, prdt, ARRAY_SIZE(prdt));
g_assert_cmphex(status, ==, BM_STS_INTR);
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
memread(guest_buf, buf, len);
g_assert(memcmp(buf, cmpbuf, len) == 0);
g_free(buf);
g_free(cmpbuf);
}
static void test_bmdma_short_prdt(void)
{
uint8_t status;
PrdtEntry prdt[] = {
{ .addr = 0, .size = 0x10 | PRDT_EOT },
};
/* Normal request */
status = send_dma_request(CMD_READ_DMA, 0, 1,
prdt, ARRAY_SIZE(prdt));
g_assert_cmphex(status, ==, 0);
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
/* Abort the request before it completes */
status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1,
prdt, ARRAY_SIZE(prdt));
g_assert_cmphex(status, ==, 0);
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
}
static void test_bmdma_long_prdt(void)
{
uint8_t status;
PrdtEntry prdt[] = {
{ .addr = 0, .size = 0x1000 | PRDT_EOT },
};
/* Normal request */
status = send_dma_request(CMD_READ_DMA, 0, 1,
prdt, ARRAY_SIZE(prdt));
g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR);
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
/* Abort the request before it completes */
status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1,
prdt, ARRAY_SIZE(prdt));
g_assert_cmphex(status, ==, BM_STS_INTR);
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
}
static void test_bmdma_setup(void)
{
ide_test_start(
"-vnc none "
"-drive file=%s,if=ide,serial=%s,cache=writeback "
"-global ide-hd.ver=%s",
tmp_path, "testdisk", "version");
}
static void test_bmdma_teardown(void)
{
ide_test_quit();
}
static void test_identify(void)
{
uint8_t data;
uint16_t buf[256];
int i;
int ret;
ide_test_start(
"-vnc none "
"-drive file=%s,if=ide,serial=%s,cache=writeback "
"-global ide-hd.ver=%s",
tmp_path, "testdisk", "version");
/* IDENTIFY command on device 0*/
outb(IDE_BASE + reg_device, 0);
outb(IDE_BASE + reg_command, CMD_IDENTIFY);
/* Read in the IDENTIFY buffer and check registers */
data = inb(IDE_BASE + reg_device);
g_assert_cmpint(data & 0x10, ==, 0);
for (i = 0; i < 256; i++) {
data = inb(IDE_BASE + reg_status);
assert_bit_set(data, DRDY | DRQ);
assert_bit_clear(data, BSY | DF | ERR);
((uint16_t*) buf)[i] = inw(IDE_BASE + reg_data);
}
data = inb(IDE_BASE + reg_status);
assert_bit_set(data, DRDY);
assert_bit_clear(data, BSY | DF | ERR | DRQ);
/* Check serial number/version in the buffer */
ret = memcmp(&buf[10], "ettsidks ", 20);
g_assert(ret == 0);
ret = memcmp(&buf[23], "evsroi n", 8);
g_assert(ret == 0);
/* Write cache enabled bit */
assert_bit_set(buf[85], 0x20);
ide_test_quit();
}
int main(int argc, char **argv)
{
const char *arch = qtest_get_arch();
int fd;
int ret;
/* Check architecture */
if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) {
g_test_message("Skipping test for non-x86\n");
return 0;
}
/* Create a temporary raw image */
fd = mkstemp(tmp_path);
g_assert(fd >= 0);
ret = ftruncate(fd, TEST_IMAGE_SIZE);
g_assert(ret == 0);
close(fd);
/* Run the tests */
g_test_init(&argc, &argv, NULL);
qtest_add_func("/ide/identify", test_identify);
qtest_add_func("/ide/bmdma/setup", test_bmdma_setup);
qtest_add_func("/ide/bmdma/simple_rw", test_bmdma_simple_rw);
qtest_add_func("/ide/bmdma/short_prdt", test_bmdma_short_prdt);
qtest_add_func("/ide/bmdma/long_prdt", test_bmdma_long_prdt);
qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown);
ret = g_test_run();
/* Cleanup */
unlink(tmp_path);
return ret;
}
+1 -1
View File
@@ -71,7 +71,7 @@ void qpci_device_enable(QPCIDevice *dev)
/* FIXME -- does this need to be a bus callout? */
cmd = qpci_config_readw(dev, PCI_COMMAND);
cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
qpci_config_writew(dev, PCI_COMMAND, cmd);
}
+3 -1
View File
@@ -46,6 +46,8 @@ _supported_os Linux
TEST_OFFSETS="0 4294967296"
TEST_IMG_SAVE=$TEST_IMG
TEST_IMG=$TEST_IMG.base
_make_test_img 6G
echo "Filling base image"
@@ -63,7 +65,7 @@ _check_test_img
echo "Creating test image with backing file"
echo
mv $TEST_IMG $TEST_IMG.base
TEST_IMG=$TEST_IMG_SAVE
_make_test_img -b $TEST_IMG.base 6G
echo "Filling test image"
+1 -1
View File
@@ -1,5 +1,5 @@
QA output created by 017
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=6442450944
Filling base image
=== IO: pattern 0
+3 -1
View File
@@ -46,6 +46,8 @@ _supported_os Linux
TEST_OFFSETS="0 4294967296"
TEST_IMG_SAVE=$TEST_IMG
TEST_IMG=$TEST_IMG.base
_make_test_img 6G
echo "Filling base image"
@@ -63,7 +65,7 @@ _check_test_img
echo "Creating test image with backing file"
echo
mv $TEST_IMG $TEST_IMG.base
TEST_IMG=$TEST_IMG_SAVE
_make_test_img -b $TEST_IMG.base 6G
echo "Filling test image"
+1 -1
View File
@@ -1,5 +1,5 @@
QA output created by 018
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=6442450944
Filling base image
=== IO: pattern 0
+1 -1
View File
@@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.rc
. ./common.filter
_supported_fmt qcow2 qcow qed vmdk
_supported_fmt qcow2 qcow qed
_supported_proto file
_supported_os Linux
+1 -1
View File
@@ -40,7 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.filter
# Any format supporting backing files
_supported_fmt qcow qcow2 vmdk qed
_supported_fmt qcow2 qed
_supported_proto generic
_supported_os Linux
+13
View File
@@ -19,5 +19,18 @@ int main()
);
assert(rt == result);
dsp = 0x1000;
rt = 0xF0F0F0F0;
rs = 0xA5A5A5A5;
result = 0xA5A5A5A5;
__asm
("wrdsp %2\n\t"
"insv %0, %1\n\t"
: "+r"(rt)
: "r"(rs), "r"(dsp)
);
assert(rt == result);
return 0;
}