aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.objs1
-rw-r--r--docs/generic-loader.txt22
-rw-r--r--hw/arm/aspeed.c4
-rw-r--r--hw/arm/aspeed_soc.c74
-rw-r--r--hw/arm/boot.c4
-rw-r--r--hw/arm/pxa2xx.c2
-rw-r--r--hw/arm/virt-acpi-build.c14
-rw-r--r--hw/arm/virt.c15
-rw-r--r--hw/char/pl011.c71
-rw-r--r--hw/char/trace-events9
-rw-r--r--hw/dma/pl080.c2
-rw-r--r--hw/dma/xilinx_axidma.c8
-rw-r--r--hw/i386/intel_iommu.c81
-rw-r--r--hw/i386/kvm/apic.c19
-rw-r--r--hw/i386/xen/xen_apic.c6
-rw-r--r--hw/intc/apic.c8
-rw-r--r--hw/intc/apic_common.c1
-rw-r--r--hw/intc/arm_gic_kvm.c14
-rw-r--r--hw/intc/arm_gicv3_cpuif.c23
-rw-r--r--hw/intc/trace-events14
-rw-r--r--hw/ssi/aspeed_smc.c194
-rw-r--r--include/hw/arm/aspeed_soc.h10
-rw-r--r--include/hw/arm/virt-acpi-build.h1
-rw-r--r--include/hw/compat.h4
-rw-r--r--include/hw/i386/apic_internal.h6
-rw-r--r--include/hw/i386/intel_iommu.h2
-rw-r--r--include/hw/ssi/aspeed_smc.h3
-rw-r--r--qapi-schema.json23
-rw-r--r--stubs/vmstate.c5
-rw-r--r--target-arm/cpu.h52
-rw-r--r--target-arm/helper.c74
-rw-r--r--target-arm/kvm.c3
-rw-r--r--target-arm/op_helper.c7
-rw-r--r--target-arm/trace-events10
-rw-r--r--target-arm/translate-a64.c90
-rw-r--r--target-arm/translate.c29
-rw-r--r--target-arm/translate.h2
-rw-r--r--target-i386/cpu-qom.h4
-rw-r--r--target-i386/cpu.c519
-rw-r--r--target-i386/kvm-stub.c5
-rw-r--r--target-i386/kvm.c26
-rw-r--r--target-i386/kvm_i386.h1
-rw-r--r--tests/Makefile.include7
-rw-r--r--tests/m25p80-test.c252
-rw-r--r--tests/ptimer-test-stubs.c7
-rw-r--r--tests/ptimer-test.c22
-rw-r--r--tests/test-x86-cpuid-compat.c44
-rw-r--r--vl.c5
48 files changed, 1408 insertions, 391 deletions
diff --git a/Makefile.objs b/Makefile.objs
index 02fb8e76b8..69fdd48679 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -155,6 +155,7 @@ trace-events-y += hw/alpha/trace-events
trace-events-y += ui/trace-events
trace-events-y += audio/trace-events
trace-events-y += net/trace-events
+trace-events-y += target-arm/trace-events
trace-events-y += target-i386/trace-events
trace-events-y += target-sparc/trace-events
trace-events-y += target-s390x/trace-events
diff --git a/docs/generic-loader.txt b/docs/generic-loader.txt
index 8fcb550414..31bbcd42f6 100644
--- a/docs/generic-loader.txt
+++ b/docs/generic-loader.txt
@@ -8,7 +8,7 @@ The 'loader' device allows the user to load multiple images or values into
QEMU at startup.
Loading Data into Memory Values
----------------------
+-------------------------------
The loader device allows memory values to be set from the command line. This
can be done by following the syntax below:
@@ -36,7 +36,7 @@ An example of loading value 0x8000000e to address 0xfd1a0104 is:
-device loader,addr=0xfd1a0104,data=0x8000000e,data-len=4
Setting a CPU's Program Counter
----------------------
+-------------------------------
The loader device allows the CPU's PC to be set from the command line. This
can be done by following the syntax below:
@@ -55,9 +55,10 @@ An example of setting CPU 0's PC to 0x8000 is:
-device loader,addr=0x8000,cpu-num=0
Loading Files
----------------------
-The loader device also allows files to be loaded into memory. This can be done
-similarly to setting memory values. The syntax is shown below:
+-------------
+The loader device also allows files to be loaded into memory. It can load raw
+files and ELF executable files. Raw files are loaded verbatim. ELF executable
+files are loaded by an ELF loader. The syntax is shown below:
-device loader,file=<file>[,addr=<addr>][,cpu-num=<cpu-num>][,force-raw=<raw>]
@@ -72,8 +73,8 @@ similarly to setting memory values. The syntax is shown below:
for the boot image.
This will also cause the image to be written to the specified
CPU's address space. If not specified, the default is CPU 0.
- <force-raw> - Forces the file to be treated as a raw image. This can be
- used to specify the load address of ELF files.
+ <force-raw> - Setting force-raw=on forces the file to be treated as a raw
+ image. This can be used to load ELF files as if they were raw.
All values are parsed using the standard QemuOps parsing. This allows the user
to specify any values in any format supported. By default the values
@@ -82,3 +83,10 @@ with a '0x'.
An example of loading an ELF file which CPU0 will boot is shown below:
-device loader,file=./images/boot.elf,cpu-num=0
+
+Restrictions and ToDos
+----------------------
+ - At the moment it is just assumed that if you specify a cpu-num then you
+ want to set the PC as well. This might not always be the case. In future
+ the internal state 'set_pc' (which exists in the generic loader now) should
+ be exposed to the user so that they can choose if the PC is set or not.
diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
index 6b18c7f172..c7206fda6d 100644
--- a/hw/arm/aspeed.c
+++ b/hw/arm/aspeed.c
@@ -128,8 +128,8 @@ static void aspeed_board_init(MachineState *machine,
object_property_add_const_link(OBJECT(&bmc->soc), "ram", OBJECT(&bmc->ram),
&error_abort);
- aspeed_board_init_flashes(&bmc->soc.smc, "n25q256a", &error_abort);
- aspeed_board_init_flashes(&bmc->soc.spi, "mx25l25635e", &error_abort);
+ aspeed_board_init_flashes(&bmc->soc.fmc, "n25q256a", &error_abort);
+ aspeed_board_init_flashes(&bmc->soc.spi[0], "mx25l25635e", &error_abort);
aspeed_board_binfo.kernel_filename = machine->kernel_filename;
aspeed_board_binfo.initrd_filename = machine->initrd_filename;
diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c
index c0a3102058..e14f5c217e 100644
--- a/hw/arm/aspeed_soc.c
+++ b/hw/arm/aspeed_soc.c
@@ -25,25 +25,37 @@
#define ASPEED_SOC_IOMEM_BASE 0x1E600000
#define ASPEED_SOC_FMC_BASE 0x1E620000
#define ASPEED_SOC_SPI_BASE 0x1E630000
+#define ASPEED_SOC_SPI2_BASE 0x1E631000
#define ASPEED_SOC_VIC_BASE 0x1E6C0000
#define ASPEED_SOC_SDMC_BASE 0x1E6E0000
#define ASPEED_SOC_SCU_BASE 0x1E6E2000
#define ASPEED_SOC_TIMER_BASE 0x1E782000
#define ASPEED_SOC_I2C_BASE 0x1E78A000
-#define ASPEED_SOC_FMC_FLASH_BASE 0x20000000
-#define ASPEED_SOC_SPI_FLASH_BASE 0x30000000
-
static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, };
#define AST2400_SDRAM_BASE 0x40000000
#define AST2500_SDRAM_BASE 0x80000000
+static const hwaddr aspeed_soc_ast2400_spi_bases[] = { ASPEED_SOC_SPI_BASE };
+static const char *aspeed_soc_ast2400_typenames[] = { "aspeed.smc.spi" };
+
+static const hwaddr aspeed_soc_ast2500_spi_bases[] = { ASPEED_SOC_SPI_BASE,
+ ASPEED_SOC_SPI2_BASE};
+static const char *aspeed_soc_ast2500_typenames[] = {
+ "aspeed.smc.ast2500-spi1", "aspeed.smc.ast2500-spi2" };
+
static const AspeedSoCInfo aspeed_socs[] = {
- { "ast2400-a0", "arm926", AST2400_A0_SILICON_REV, AST2400_SDRAM_BASE },
- { "ast2400", "arm926", AST2400_A0_SILICON_REV, AST2400_SDRAM_BASE },
- { "ast2500-a1", "arm1176", AST2500_A1_SILICON_REV, AST2500_SDRAM_BASE },
+ { "ast2400-a0", "arm926", AST2400_A0_SILICON_REV, AST2400_SDRAM_BASE,
+ 1, aspeed_soc_ast2400_spi_bases,
+ "aspeed.smc.fmc", aspeed_soc_ast2400_typenames },
+ { "ast2400", "arm926", AST2400_A0_SILICON_REV, AST2400_SDRAM_BASE,
+ 1, aspeed_soc_ast2400_spi_bases,
+ "aspeed.smc.fmc", aspeed_soc_ast2400_typenames },
+ { "ast2500-a1", "arm1176", AST2500_A1_SILICON_REV, AST2500_SDRAM_BASE,
+ 2, aspeed_soc_ast2500_spi_bases,
+ "aspeed.smc.ast2500-fmc", aspeed_soc_ast2500_typenames },
};
/*
@@ -75,6 +87,7 @@ static void aspeed_soc_init(Object *obj)
{
AspeedSoCState *s = ASPEED_SOC(obj);
AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s);
+ int i;
s->cpu = cpu_arm_init(sc->info->cpu_model);
@@ -100,13 +113,16 @@ static void aspeed_soc_init(Object *obj)
object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu),
"hw-strap2", &error_abort);
- object_initialize(&s->smc, sizeof(s->smc), "aspeed.smc.fmc");
- object_property_add_child(obj, "smc", OBJECT(&s->smc), NULL);
- qdev_set_parent_bus(DEVICE(&s->smc), sysbus_get_default());
+ object_initialize(&s->fmc, sizeof(s->fmc), sc->info->fmc_typename);
+ object_property_add_child(obj, "fmc", OBJECT(&s->fmc), NULL);
+ qdev_set_parent_bus(DEVICE(&s->fmc), sysbus_get_default());
- object_initialize(&s->spi, sizeof(s->spi), "aspeed.smc.spi");
- object_property_add_child(obj, "spi", OBJECT(&s->spi), NULL);
- qdev_set_parent_bus(DEVICE(&s->spi), sysbus_get_default());
+ for (i = 0; i < sc->info->spis_num; i++) {
+ object_initialize(&s->spi[i], sizeof(s->spi[i]),
+ sc->info->spi_typename[i]);
+ object_property_add_child(obj, "spi", OBJECT(&s->spi[i]), NULL);
+ qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default());
+ }
object_initialize(&s->sdmc, sizeof(s->sdmc), TYPE_ASPEED_SDMC);
object_property_add_child(obj, "sdmc", OBJECT(&s->sdmc), NULL);
@@ -121,6 +137,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
{
int i;
AspeedSoCState *s = ASPEED_SOC(dev);
+ AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s);
Error *err = NULL, *local_err = NULL;
/* IO space */
@@ -178,29 +195,34 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c), 0,
qdev_get_gpio_in(DEVICE(&s->vic), 12));
- /* SMC */
- object_property_set_int(OBJECT(&s->smc), 1, "num-cs", &err);
- object_property_set_bool(OBJECT(&s->smc), true, "realized", &local_err);
+ /* FMC */
+ object_property_set_int(OBJECT(&s->fmc), 1, "num-cs", &err);
+ object_property_set_bool(OBJECT(&s->fmc), true, "realized", &local_err);
error_propagate(&err, local_err);
if (err) {
error_propagate(errp, err);
return;
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->smc), 0, ASPEED_SOC_FMC_BASE);
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->smc), 1, ASPEED_SOC_FMC_FLASH_BASE);
- sysbus_connect_irq(SYS_BUS_DEVICE(&s->smc), 0,
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 0, ASPEED_SOC_FMC_BASE);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->fmc), 1,
+ s->fmc.ctrl->flash_window_base);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->fmc), 0,
qdev_get_gpio_in(DEVICE(&s->vic), 19));
/* SPI */
- object_property_set_int(OBJECT(&s->spi), 1, "num-cs", &err);
- object_property_set_bool(OBJECT(&s->spi), true, "realized", &local_err);
- error_propagate(&err, local_err);
- if (err) {
- error_propagate(errp, err);
- return;
+ for (i = 0; i < sc->info->spis_num; i++) {
+ object_property_set_int(OBJECT(&s->spi[i]), 1, "num-cs", &err);
+ object_property_set_bool(OBJECT(&s->spi[i]), true, "realized",
+ &local_err);
+ error_propagate(&err, local_err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, sc->info->spi_bases[i]);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 1,
+ s->spi[i].ctrl->flash_window_base);
}
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 0, ASPEED_SOC_SPI_BASE);
- sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 1, ASPEED_SOC_SPI_FLASH_BASE);
/* SDMC - SDRAM Memory Controller */
object_property_set_bool(OBJECT(&s->sdmc), true, "realized", &err);
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 1b913a43ca..942416d95a 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -773,6 +773,8 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
*/
assert(!(info->secure_board_setup && kvm_enabled()));
+ info->dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb");
+
/* Load the kernel. */
if (!info->kernel_filename || info->firmware_loaded) {
@@ -833,8 +835,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
elf_machine = EM_ARM;
}
- info->dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb");
-
if (!info->secondary_cpu_reset_hook) {
info->secondary_cpu_reset_hook = default_reset_secondary;
}
diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index 0241e07d84..98982872d7 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -1505,7 +1505,7 @@ static void pxa2xx_i2c_initfn(Object *obj)
PXA2xxI2CState *s = PXA2XX_I2C(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- s->bus = i2c_init_bus(dev, "i2c");
+ s->bus = i2c_init_bus(dev, NULL);
memory_region_init_io(&s->iomem, obj, &pxa2xx_i2c_ops, s,
"pxa2xx-i2c", s->region_size);
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index c77525d33a..fa0655a775 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -554,15 +554,13 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info)
gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST].base);
gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST].size);
- if (!its_class_name()) {
- return;
+ if (its_class_name() && !guest_info->no_its) {
+ gic_its = acpi_data_push(table_data, sizeof *gic_its);
+ gic_its->type = ACPI_APIC_GENERIC_TRANSLATOR;
+ gic_its->length = sizeof(*gic_its);
+ gic_its->translation_id = 0;
+ gic_its->base_address = cpu_to_le64(memmap[VIRT_GIC_ITS].base);
}
-
- gic_its = acpi_data_push(table_data, sizeof *gic_its);
- gic_its->type = ACPI_APIC_GENERIC_TRANSLATOR;
- gic_its->length = sizeof(*gic_its);
- gic_its->translation_id = 0;
- gic_its->base_address = cpu_to_le64(memmap[VIRT_GIC_ITS].base);
} else {
gic_msi = acpi_data_push(table_data, sizeof *gic_msi);
gic_msi->type = ACPI_APIC_GENERIC_MSI_FRAME;
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 795740d9bf..895446f17c 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -84,6 +84,7 @@ typedef struct {
MachineClass parent;
VirtBoardInfo *daughterboard;
bool disallow_affinity_adjustment;
+ bool no_its;
} VirtMachineClass;
typedef struct {
@@ -551,7 +552,8 @@ static void create_v2m(VirtBoardInfo *vbi, qemu_irq *pic)
fdt_add_v2m_gic_node(vbi);
}
-static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, int type, bool secure)
+static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, int type,
+ bool secure, bool no_its)
{
/* We create a standalone GIC */
DeviceState *gicdev;
@@ -615,9 +617,9 @@ static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, int type, bool secure)
fdt_add_gic_node(vbi, type);
- if (type == 3) {
+ if (type == 3 && !no_its) {
create_its(vbi, gicdev);
- } else {
+ } else if (type == 2) {
create_v2m(vbi, pic);
}
}
@@ -1375,7 +1377,7 @@ static void machvirt_init(MachineState *machine)
create_flash(vbi, sysmem, secure_sysmem ? secure_sysmem : sysmem);
- create_gic(vbi, pic, gic_version, vms->secure);
+ create_gic(vbi, pic, gic_version, vms->secure, vmc->no_its);
fdt_add_pmu_nodes(vbi, gic_version);
@@ -1407,6 +1409,7 @@ static void machvirt_init(MachineState *machine)
guest_info->irqmap = vbi->irqmap;
guest_info->use_highmem = vms->highmem;
guest_info->gic_version = gic_version;
+ guest_info->no_its = vmc->no_its;
guest_info_state->machine_done.notify = virt_guest_info_machine_done;
qemu_add_machine_init_done_notifier(&guest_info_state->machine_done);
@@ -1561,8 +1564,12 @@ static void virt_2_7_instance_init(Object *obj)
static void virt_machine_2_7_options(MachineClass *mc)
{
+ VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc));
+
virt_machine_2_8_options(mc);
SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_7);
+ /* ITS was introduced with 2.8 */
+ vmc->no_its = true;
}
DEFINE_VIRT_MACHINE(2, 7)
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 786e605fdd..1a7911f81f 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -11,6 +11,7 @@
#include "hw/sysbus.h"
#include "sysemu/char.h"
#include "qemu/log.h"
+#include "trace.h"
#define TYPE_PL011 "pl011"
#define PL011(obj) OBJECT_CHECK(PL011State, (obj), TYPE_PL011)
@@ -58,6 +59,7 @@ static void pl011_update(PL011State *s)
uint32_t flags;
flags = s->int_level & s->int_enabled;
+ trace_pl011_irq_state(flags != 0);
qemu_set_irq(s->irq, flags != 0);
}
@@ -66,10 +68,8 @@ static uint64_t pl011_read(void *opaque, hwaddr offset,
{
PL011State *s = (PL011State *)opaque;
uint32_t c;
+ uint64_t r;
- if (offset >= 0xfe0 && offset < 0x1000) {
- return s->id[(offset - 0xfe0) >> 2];
- }
switch (offset >> 2) {
case 0: /* UARTDR */
s->flags &= ~PL011_FLAG_RXFF;
@@ -84,41 +84,62 @@ static uint64_t pl011_read(void *opaque, hwaddr offset,
}
if (s->read_count == s->read_trigger - 1)
s->int_level &= ~ PL011_INT_RX;
+ trace_pl011_read_fifo(s->read_count);
s->rsr = c >> 8;
pl011_update(s);
if (s->chr) {
qemu_chr_accept_input(s->chr);
}
- return c;
+ r = c;
+ break;
case 1: /* UARTRSR */
- return s->rsr;
+ r = s->rsr;
+ break;
case 6: /* UARTFR */
- return s->flags;
+ r = s->flags;
+ break;
case 8: /* UARTILPR */
- return s->ilpr;
+ r = s->ilpr;
+ break;
case 9: /* UARTIBRD */
- return s->ibrd;
+ r = s->ibrd;
+ break;
case 10: /* UARTFBRD */
- return s->fbrd;
+ r = s->fbrd;
+ break;
case 11: /* UARTLCR_H */
- return s->lcr;
+ r = s->lcr;
+ break;
case 12: /* UARTCR */
- return s->cr;
+ r = s->cr;
+ break;
case 13: /* UARTIFLS */
- return s->ifl;
+ r = s->ifl;
+ break;
case 14: /* UARTIMSC */
- return s->int_enabled;
+ r = s->int_enabled;
+ break;
case 15: /* UARTRIS */
- return s->int_level;
+ r = s->int_level;
+ break;
case 16: /* UARTMIS */
- return s->int_level & s->int_enabled;
+ r = s->int_level & s->int_enabled;
+ break;
case 18: /* UARTDMACR */
- return s->dmacr;
+ r = s->dmacr;
+ break;
+ case 0x3f8 ... 0x400:
+ r = s->id[(offset - 0xfe0) >> 2];
+ break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"pl011_read: Bad offset %x\n", (int)offset);
- return 0;
+ r = 0;
+ break;
}
+
+ trace_pl011_read(offset, r);
+ return r;
}
static void pl011_set_read_trigger(PL011State *s)
@@ -141,6 +162,8 @@ static void pl011_write(void *opaque, hwaddr offset,
PL011State *s = (PL011State *)opaque;
unsigned char ch;
+ trace_pl011_write(offset, value);
+
switch (offset >> 2) {
case 0: /* UARTDR */
/* ??? Check if transmitter is enabled. */
@@ -207,11 +230,15 @@ static void pl011_write(void *opaque, hwaddr offset,
static int pl011_can_receive(void *opaque)
{
PL011State *s = (PL011State *)opaque;
+ int r;
- if (s->lcr & 0x10)
- return s->read_count < 16;
- else
- return s->read_count < 1;
+ if (s->lcr & 0x10) {
+ r = s->read_count < 16;
+ } else {
+ r = s->read_count < 1;
+ }
+ trace_pl011_can_receive(s->lcr, s->read_count, r);
+ return r;
}
static void pl011_put_fifo(void *opaque, uint32_t value)
@@ -225,7 +252,9 @@ static void pl011_put_fifo(void *opaque, uint32_t value)
s->read_fifo[slot] = value;
s->read_count++;
s->flags &= ~PL011_FLAG_RXFE;
+ trace_pl011_put_fifo(value, s->read_count);
if (!(s->lcr & 0x10) || s->read_count == 16) {
+ trace_pl011_put_fifo_full();
s->flags |= PL011_FLAG_RXFF;
}
if (s->read_count == s->read_trigger) {
diff --git a/hw/char/trace-events b/hw/char/trace-events
index d53577c99d..7fd48bb80d 100644
--- a/hw/char/trace-events
+++ b/hw/char/trace-events
@@ -47,3 +47,12 @@ escc_sunkbd_event_in(int ch, const char *name, int down) "QKeyCode 0x%2.2x [%s],
escc_sunkbd_event_out(int ch) "Translated keycode 0x%2.2x"
escc_kbd_command(int val) "Command %d"
escc_sunmouse_event(int dx, int dy, int buttons_state) "dx=%d dy=%d buttons=%01x"
+
+# hw/char/pl011.c
+pl011_irq_state(int level) "irq state %d"
+pl011_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
+pl011_read_fifo(int read_count) "FIFO read, read_count now %d"
+pl011_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
+pl011_can_receive(uint32_t lcr, int read_count, int r) "LCR %08x read_count %d returning %d"
+pl011_put_fifo(uint32_t c, int read_count) "new char 0x%x read_count now %d"
+pl011_put_fifo_full(void) "FIFO now full, RXFF set"
diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c
index 3bed5c3390..7724c93b8f 100644
--- a/hw/dma/pl080.c
+++ b/hw/dma/pl080.c
@@ -351,7 +351,7 @@ static void pl080_write(void *opaque, hwaddr offset,
break;
case 12: /* Configuration */
s->conf = value;
- if (s->conf & (PL080_CONF_M1 | PL080_CONF_M1)) {
+ if (s->conf & (PL080_CONF_M1 | PL080_CONF_M2)) {
qemu_log_mask(LOG_UNIMP,
"pl080_write: Big-endian DMA not implemented\n");
}
diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c
index b135a5ff12..6065689ad1 100644
--- a/hw/dma/xilinx_axidma.c
+++ b/hw/dma/xilinx_axidma.c
@@ -111,6 +111,7 @@ struct Stream {
unsigned int complete_cnt;
uint32_t regs[R_MAX];
uint8_t app[20];
+ unsigned char txbuf[16 * 1024];
};
struct XilinxAXIDMAStreamSlave {
@@ -256,7 +257,6 @@ static void stream_process_mem2s(struct Stream *s, StreamSlave *tx_data_dev,
StreamSlave *tx_control_dev)
{
uint32_t prev_d;
- unsigned char txbuf[16 * 1024];
unsigned int txlen;
if (!stream_running(s) || stream_idle(s)) {
@@ -277,17 +277,17 @@ static void stream_process_mem2s(struct Stream *s, StreamSlave *tx_data_dev,
}
txlen = s->desc.control & SDESC_CTRL_LEN_MASK;
- if ((txlen + s->pos) > sizeof txbuf) {
+ if ((txlen + s->pos) > sizeof s->txbuf) {
hw_error("%s: too small internal txbuf! %d\n", __func__,
txlen + s->pos);
}
cpu_physical_memory_read(s->desc.buffer_address,
- txbuf + s->pos, txlen);
+ s->txbuf + s->pos, txlen);
s->pos += txlen;
if (stream_desc_eof(&s->desc)) {
- stream_push(tx_data_dev, txbuf, s->pos);
+ stream_push(tx_data_dev, s->txbuf, s->pos);
s->pos = 0;
stream_complete(s);
}
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 2efd69bbd7..1655a65bce 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
+#include "qapi/error.h"
#include "hw/sysbus.h"
#include "exec/address-spaces.h"
#include "intel_iommu_internal.h"
@@ -32,6 +33,8 @@
#include "hw/i386/x86-iommu.h"
#include "hw/pci-host/q35.h"
#include "sysemu/kvm.h"
+#include "hw/i386/apic_internal.h"
+#include "kvm_i386.h"
/*#define DEBUG_INTEL_IOMMU*/
#ifdef DEBUG_INTEL_IOMMU
@@ -280,18 +283,17 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id,
static void vtd_generate_interrupt(IntelIOMMUState *s, hwaddr mesg_addr_reg,
hwaddr mesg_data_reg)
{
- hwaddr addr;
- uint32_t data;
+ MSIMessage msi;
assert(mesg_data_reg < DMAR_REG_SIZE);
assert(mesg_addr_reg < DMAR_REG_SIZE);
- addr = vtd_get_long_raw(s, mesg_addr_reg);
- data = vtd_get_long_raw(s, mesg_data_reg);
+ msi.address = vtd_get_long_raw(s, mesg_addr_reg);
+ msi.data = vtd_get_long_raw(s, mesg_data_reg);
- VTD_DPRINTF(FLOG, "msi: addr 0x%"PRIx64 " data 0x%"PRIx32, addr, data);
- address_space_stl_le(&address_space_memory, addr, data,
- MEMTXATTRS_UNSPECIFIED, NULL);
+ VTD_DPRINTF(FLOG, "msi: addr 0x%"PRIx64 " data 0x%"PRIx32,
+ msi.address, msi.data);
+ apic_get_class()->send_msi(&msi);
}
/* Generate a fault event to software via MSI if conditions are met.
@@ -2012,6 +2014,9 @@ static const MemoryRegionOps vtd_mem_ops = {
static Property vtd_properties[] = {
DEFINE_PROP_UINT32("version", IntelIOMMUState, version, 0),
+ DEFINE_PROP_ON_OFF_AUTO("eim", IntelIOMMUState, intr_eim,
+ ON_OFF_AUTO_AUTO),
+ DEFINE_PROP_BOOL("x-buggy-eim", IntelIOMMUState, buggy_eim, false),
DEFINE_PROP_END_OF_LIST(),
};
@@ -2134,6 +2139,7 @@ static void vtd_generate_msi_message(VTDIrq *irq, MSIMessage *msg_out)
msg.dest_mode = irq->dest_mode;
msg.redir_hint = irq->redir_hint;
msg.dest = irq->dest;
+ msg.__addr_hi = irq->dest & 0xffffff00;
msg.__addr_head = cpu_to_le32(0xfee);
/* Keep this from original MSI address bits */
msg.__not_used = irq->msi_addr_last_bits;
@@ -2293,11 +2299,7 @@ static MemTxResult vtd_mem_ir_write(void *opaque, hwaddr addr,
" for device sid 0x%04x",
to.address, to.data, sid);
- if (dma_memory_write(&address_space_memory, to.address,
- &to.data, size)) {
- VTD_DPRINTF(GENERAL, "error: fail to write 0x%"PRIx64
- " value 0x%"PRIx32, to.address, to.data);
- }
+ apic_get_class()->send_msi(&to);
return MEMTX_OK;
}
@@ -2382,7 +2384,11 @@ static void vtd_init(IntelIOMMUState *s)
s->ecap = VTD_ECAP_QI | VTD_ECAP_IRO;
if (x86_iommu->intr_supported) {
- s->ecap |= VTD_ECAP_IR | VTD_ECAP_EIM | VTD_ECAP_MHMV;
+ s->ecap |= VTD_ECAP_IR | VTD_ECAP_MHMV;
+ if (s->intr_eim == ON_OFF_AUTO_ON) {
+ s->ecap |= VTD_ECAP_EIM;
+ }
+ assert(s->intr_eim != ON_OFF_AUTO_AUTO);
}
vtd_reset_context_cache(s);
@@ -2463,6 +2469,42 @@ static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
return &vtd_as->as;
}
+static bool vtd_decide_config(IntelIOMMUState *s, Error **errp)
+{
+ X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s);
+
+ /* Currently Intel IOMMU IR only support "kernel-irqchip={off|split}" */
+ if (x86_iommu->intr_supported && kvm_irqchip_in_kernel() &&
+ !kvm_irqchip_is_split()) {
+ error_setg(errp, "Intel Interrupt Remapping cannot work with "
+ "kernel-irqchip=on, please use 'split|off'.");
+ return false;
+ }
+ if (s->intr_eim == ON_OFF_AUTO_ON && !x86_iommu->intr_supported) {
+ error_setg(errp, "eim=on cannot be selected without intremap=on");
+ return false;
+ }
+
+ if (s->intr_eim == ON_OFF_AUTO_AUTO) {
+ s->intr_eim = (kvm_irqchip_in_kernel() || s->buggy_eim)
+ && x86_iommu->intr_supported ?
+ ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
+ }
+ if (s->intr_eim == ON_OFF_AUTO_ON && !s->buggy_eim) {
+ if (!kvm_irqchip_in_kernel()) {
+ error_setg(errp, "eim=on requires accel=kvm,kernel-irqchip=split");
+ return false;
+ }
+ if (!kvm_enable_x2apic()) {
+ error_setg(errp, "eim=on requires support on the KVM side"
+ "(X2APIC_API, first shipped in v4.7)");
+ return false;
+ }
+ }
+
+ return true;
+}
+
static void vtd_realize(DeviceState *dev, Error **errp)
{
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
@@ -2472,6 +2514,11 @@ static void vtd_realize(DeviceState *dev, Error **errp)
VTD_DPRINTF(GENERAL, "");
x86_iommu->type = TYPE_INTEL;
+
+ if (!vtd_decide_config(s, errp)) {
+ return;
+ }
+
memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num));
memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s,
"intel_iommu", DMAR_REG_SIZE);
@@ -2486,14 +2533,6 @@ static void vtd_realize(DeviceState *dev, Error **errp)
pci_setup_iommu(bus, vtd_host_dma_iommu, dev);
/* Pseudo address space under root PCI bus. */
pcms->ioapic_as = vtd_host_dma_iommu(bus, s, Q35_PSEUDO_DEVFN_IOAPIC);
-
- /* Currently Intel IOMMU IR only support "kernel-irqchip={off|split}" */
- if (x86_iommu->intr_supported && kvm_irqchip_in_kernel() &&
- !kvm_irqchip_is_split()) {
- error_report("Intel Interrupt Remapping cannot work with "
- "kernel-irqchip=on, please use 'split|off'.");
- exit(1);
- }
}
static void vtd_class_init(ObjectClass *klass, void *data)
diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c
index c016e63fc2..be55102c00 100644
--- a/hw/i386/kvm/apic.c
+++ b/hw/i386/kvm/apic.c
@@ -169,6 +169,17 @@ static void kvm_apic_external_nmi(APICCommonState *s)
run_on_cpu(CPU(s->cpu), do_inject_external_nmi, s);
}
+static void kvm_send_msi(MSIMessage *msg)
+{
+ int ret;
+
+ ret = kvm_irqchip_send_msi(kvm_state, *msg);
+ if (ret < 0) {
+ fprintf(stderr, "KVM: injection failed, MSI lost (%s)\n",
+ strerror(-ret));
+ }
+}
+
static uint64_t kvm_apic_mem_read(void *opaque, hwaddr addr,
unsigned size)
{
@@ -179,13 +190,8 @@ static void kvm_apic_mem_write(void *opaque, hwaddr addr,
uint64_t data, unsigned size)
{
MSIMessage msg = { .address = addr, .data = data };
- int ret;
- ret = kvm_irqchip_send_msi(kvm_state, msg);
- if (ret < 0) {
- fprintf(stderr, "KVM: injection failed, MSI lost (%s)\n",
- strerror(-ret));
- }
+ kvm_send_msi(&msg);
}
static const MemoryRegionOps kvm_apic_io_ops = {
@@ -232,6 +238,7 @@ static void kvm_apic_class_init(ObjectClass *klass, void *data)
k->enable_tpr_reporting = kvm_apic_enable_tpr_reporting;
k->vapic_base_update = kvm_apic_vapic_base_update;
k->external_nmi = kvm_apic_external_nmi;
+ k->send_msi = kvm_send_msi;
}
static const TypeInfo kvm_apic_info = {
diff --git a/hw/i386/xen/xen_apic.c b/hw/i386/xen/xen_apic.c
index 21d68ee04b..55769eba7e 100644
--- a/hw/i386/xen/xen_apic.c
+++ b/hw/i386/xen/xen_apic.c
@@ -68,6 +68,11 @@ static void xen_apic_external_nmi(APICCommonState *s)
{
}
+static void xen_send_msi(MSIMessage *msi)
+{
+ xen_hvm_inject_msi(msi->address, msi->data);
+}
+
static void xen_apic_class_init(ObjectClass *klass, void *data)
{
APICCommonClass *k = APIC_COMMON_CLASS(klass);
@@ -78,6 +83,7 @@ static void xen_apic_class_init(ObjectClass *klass, void *data)
k->get_tpr = xen_apic_get_tpr;
k->vapic_base_update = xen_apic_vapic_base_update;
k->external_nmi = xen_apic_external_nmi;
+ k->send_msi = xen_send_msi;
}
static const TypeInfo xen_apic_info = {
diff --git a/hw/intc/apic.c b/hw/intc/apic.c
index 7bd1d279c4..fe15fb6024 100644
--- a/hw/intc/apic.c
+++ b/hw/intc/apic.c
@@ -740,8 +740,10 @@ static uint32_t apic_mem_readl(void *opaque, hwaddr addr)
return val;
}
-static void apic_send_msi(hwaddr addr, uint32_t data)
+static void apic_send_msi(MSIMessage *msi)
{
+ uint64_t addr = msi->address;
+ uint32_t data = msi->data;
uint8_t dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT;
uint8_t vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT;
uint8_t dest_mode = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1;
@@ -762,7 +764,8 @@ static void apic_mem_writel(void *opaque, hwaddr addr, uint32_t val)
* APIC is connected directly to the CPU.
* Mapping them on the global bus happens to work because
* MSI registers are reserved in APIC MMIO and vice versa. */
- apic_send_msi(addr, val);
+ MSIMessage msi = { .address = addr, .data = val };
+ apic_send_msi(&msi);
return;
}
@@ -913,6 +916,7 @@ static void apic_class_init(ObjectClass *klass, void *data)
k->external_nmi = apic_external_nmi;
k->pre_save = apic_pre_save;
k->post_load = apic_post_load;
+ k->send_msi = apic_send_msi;
}
static const TypeInfo apic_info = {
diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
index 14ac43c186..8d01c9c875 100644
--- a/hw/intc/apic_common.c
+++ b/hw/intc/apic_common.c
@@ -18,6 +18,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*/
#include "qemu/osdep.h"
+#include "qemu/error-report.h"
#include "qapi/error.h"
#include "qemu-common.h"
#include "cpu.h"
diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c
index ae7ac58ffd..11729ee902 100644
--- a/hw/intc/arm_gic_kvm.c
+++ b/hw/intc/arm_gic_kvm.c
@@ -30,20 +30,6 @@
#include "gic_internal.h"
#include "vgic_common.h"
-//#define DEBUG_GIC_KVM
-
-#ifdef DEBUG_GIC_KVM
-static const int debug_gic_kvm = 1;
-#else
-static const int debug_gic_kvm = 0;
-#endif
-
-#define DPRINTF(fmt, ...) do { \
- if (debug_gic_kvm) { \
- printf("arm_gic: " fmt , ## __VA_ARGS__); \
- } \
- } while (0)
-
#define TYPE_KVM_ARM_GIC "kvm-arm-gic"
#define KVM_ARM_GIC(obj) \
OBJECT_CHECK(GICState, (obj), TYPE_KVM_ARM_GIC)
diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index 4633172bec..bca30c49da 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -454,7 +454,8 @@ static void icc_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri,
int irq = value & 0xffffff;
int grp;
- trace_gicv3_icc_eoir_write(gicv3_redist_affid(cs), value);
+ trace_gicv3_icc_eoir_write(ri->crm == 8 ? 0 : 1,
+ gicv3_redist_affid(cs), value);
if (ri->crm == 8) {
/* EOIR0 */
@@ -542,7 +543,7 @@ static uint64_t icc_bpr_read(CPUARMState *env, const ARMCPRegInfo *ri)
bpr = MIN(bpr, 7);
}
- trace_gicv3_icc_bpr_read(gicv3_redist_affid(cs), bpr);
+ trace_gicv3_icc_bpr_read(ri->crm == 8 ? 0 : 1, gicv3_redist_affid(cs), bpr);
return bpr;
}
@@ -553,7 +554,8 @@ static void icc_bpr_write(CPUARMState *env, const ARMCPRegInfo *ri,
GICv3CPUState *cs = icc_cs_from_env(env);
int grp = (ri->crm == 8) ? GICV3_G0 : GICV3_G1;
- trace_gicv3_icc_pmr_write(gicv3_redist_affid(cs), value);
+ trace_gicv3_icc_bpr_write(ri->crm == 8 ? 0 : 1,
+ gicv3_redist_affid(cs), value);
if (grp == GICV3_G1 && gicv3_use_ns_bank(env)) {
grp = GICV3_G1NS;
@@ -591,7 +593,7 @@ static uint64_t icc_ap_read(CPUARMState *env, const ARMCPRegInfo *ri)
value = cs->icc_apr[grp][regno];
- trace_gicv3_icc_ap_read(regno, gicv3_redist_affid(cs), value);
+ trace_gicv3_icc_ap_read(ri->crm & 1, regno, gicv3_redist_affid(cs), value);
return value;
}
@@ -603,7 +605,7 @@ static void icc_ap_write(CPUARMState *env, const ARMCPRegInfo *ri,
int regno = ri->opc2 & 3;
int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1;
- trace_gicv3_icc_ap_write(regno, gicv3_redist_affid(cs), value);
+ trace_gicv3_icc_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value);
if (grp == GICV3_G1 && gicv3_use_ns_bank(env)) {
grp = GICV3_G1NS;
@@ -820,7 +822,8 @@ static uint64_t icc_igrpen_read(CPUARMState *env, const ARMCPRegInfo *ri)
}
value = cs->icc_igrpen[grp];
- trace_gicv3_icc_igrpen_read(gicv3_redist_affid(cs), value);
+ trace_gicv3_icc_igrpen_read(ri->opc2 & 1 ? 1 : 0,
+ gicv3_redist_affid(cs), value);
return value;
}
@@ -830,7 +833,8 @@ static void icc_igrpen_write(CPUARMState *env, const ARMCPRegInfo *ri,
GICv3CPUState *cs = icc_cs_from_env(env);
int grp = ri->opc2 & 1 ? GICV3_G1 : GICV3_G0;
- trace_gicv3_icc_igrpen_write(gicv3_redist_affid(cs), value);
+ trace_gicv3_icc_igrpen_write(ri->opc2 & 1 ? 1 : 0,
+ gicv3_redist_affid(cs), value);
if (grp == GICV3_G1 && gicv3_use_ns_bank(env)) {
grp = GICV3_G1NS;
@@ -843,9 +847,12 @@ static void icc_igrpen_write(CPUARMState *env, const ARMCPRegInfo *ri,
static uint64_t icc_igrpen1_el3_read(CPUARMState *env, const ARMCPRegInfo *ri)
{
GICv3CPUState *cs = icc_cs_from_env(env);
+ uint64_t value;
/* IGRPEN1_EL3 bits 0 and 1 are r/w aliases into IGRPEN1_EL1 NS and S */
- return cs->icc_igrpen[GICV3_G1NS] | (cs->icc_igrpen[GICV3_G1] << 1);
+ value = cs->icc_igrpen[GICV3_G1NS] | (cs->icc_igrpen[GICV3_G1] << 1);
+ trace_gicv3_icc_igrpen1_el3_read(gicv3_redist_affid(cs), value);
+ return value;
}
static void icc_igrpen1_el3_write(CPUARMState *env, const ARMCPRegInfo *ri,
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index a367b46b1c..340f617761 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -85,12 +85,12 @@ gic_acknowledge_irq(int cpu, int irq) "cpu %d acknowledged irq %d"
# hw/intc/arm_gicv3_cpuif.c
gicv3_icc_pmr_read(uint32_t cpu, uint64_t val) "GICv3 ICC_PMR read cpu %x value 0x%" PRIx64
gicv3_icc_pmr_write(uint32_t cpu, uint64_t val) "GICv3 ICC_PMR write cpu %x value 0x%" PRIx64
-gicv3_icc_bpr_read(uint32_t cpu, uint64_t val) "GICv3 ICC_BPR read cpu %x value 0x%" PRIx64
-gicv3_icc_bpr_write(uint32_t cpu, uint64_t val) "GICv3 ICC_BPR write cpu %x value 0x%" PRIx64
-gicv3_icc_ap_read(int regno, uint32_t cpu, uint64_t val) "GICv3 ICC_AP%dR read cpu %x value 0x%" PRIx64
-gicv3_icc_ap_write(int regno, uint32_t cpu, uint64_t val) "GICv3 ICC_AP%dR write cpu %x value 0x%" PRIx64
-gicv3_icc_igrpen_read(uint32_t cpu, uint64_t val) "GICv3 ICC_IGRPEN read cpu %x value 0x%" PRIx64
-gicv3_icc_igrpen_write(uint32_t cpu, uint64_t val) "GICv3 ICC_IGRPEN write cpu %x value 0x%" PRIx64
+gicv3_icc_bpr_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICC_BPR%d read cpu %x value 0x%" PRIx64
+gicv3_icc_bpr_write(int grp, uint32_t cpu, uint64_t val) "GICv3 ICC_BPR%d write cpu %x value 0x%" PRIx64
+gicv3_icc_ap_read(int grp, int regno, uint32_t cpu, uint64_t val) "GICv3 ICC_AP%dR%d read cpu %x value 0x%" PRIx64
+gicv3_icc_ap_write(int grp, int regno, uint32_t cpu, uint64_t val) "GICv3 ICC_AP%dR%d write cpu %x value 0x%" PRIx64
+gicv3_icc_igrpen_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICC_IGRPEN%d read cpu %x value 0x%" PRIx64
+gicv3_icc_igrpen_write(int grp, uint32_t cpu, uint64_t val) "GICv3 ICC_IGRPEN%d write cpu %x value 0x%" PRIx64
gicv3_icc_igrpen1_el3_read(uint32_t cpu, uint64_t val) "GICv3 ICC_IGRPEN1_EL3 read cpu %x value 0x%" PRIx64
gicv3_icc_igrpen1_el3_write(uint32_t cpu, uint64_t val) "GICv3 ICC_IGRPEN1_EL3 write cpu %x value 0x%" PRIx64
gicv3_icc_ctlr_read(uint32_t cpu, uint64_t val) "GICv3 ICC_CTLR read cpu %x value 0x%" PRIx64
@@ -102,7 +102,7 @@ gicv3_cpuif_set_irqs(uint32_t cpuid, int fiqlevel, int irqlevel) "GICv3 CPU i/f
gicv3_icc_generate_sgi(uint32_t cpuid, int irq, int irm, uint32_t aff, uint32_t targetlist) "GICv3 CPU i/f %x generating SGI %d IRM %d target affinity 0x%xxx targetlist 0x%x"
gicv3_icc_iar0_read(uint32_t cpu, uint64_t val) "GICv3 ICC_IAR0 read cpu %x value 0x%" PRIx64
gicv3_icc_iar1_read(uint32_t cpu, uint64_t val) "GICv3 ICC_IAR1 read cpu %x value 0x%" PRIx64
-gicv3_icc_eoir_write(uint32_t cpu, uint64_t val) "GICv3 ICC_EOIR write cpu %x value 0x%" PRIx64
+gicv3_icc_eoir_write(int grp, uint32_t cpu, uint64_t val) "GICv3 ICC_EOIR%d write cpu %x value 0x%" PRIx64
gicv3_icc_hppir0_read(uint32_t cpu, uint64_t val) "GICv3 ICC_HPPIR0 read cpu %x value 0x%" PRIx64
gicv3_icc_hppir1_read(uint32_t cpu, uint64_t val) "GICv3 ICC_HPPIR1 read cpu %x value 0x%" PRIx64
gicv3_icc_dir_write(uint32_t cpu, uint64_t val) "GICv3 ICC_DIR write cpu %x value 0x%" PRIx64
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index d319e04a27..6e8403ebc2 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -79,10 +79,10 @@
/* CEx Segment Address Register */
#define R_SEG_ADDR0 (0x30 / 4)
-#define SEG_SIZE_SHIFT 24 /* 8MB units */
-#define SEG_SIZE_MASK 0x7f
+#define SEG_END_SHIFT 24 /* 8MB units */
+#define SEG_END_MASK 0xff
#define SEG_START_SHIFT 16 /* address bit [A29-A23] */
-#define SEG_START_MASK 0x7f
+#define SEG_START_MASK 0xff
#define R_SEG_ADDR1 (0x34 / 4)
#define R_SEG_ADDR2 (0x38 / 4)
#define R_SEG_ADDR3 (0x3C / 4)
@@ -127,18 +127,22 @@
#define R_SPI_MISC_CTRL (0x10 / 4)
#define R_SPI_TIMINGS (0x14 / 4)
+#define ASPEED_SOC_SMC_FLASH_BASE 0x10000000
+#define ASPEED_SOC_FMC_FLASH_BASE 0x20000000
+#define ASPEED_SOC_SPI_FLASH_BASE 0x30000000
+#define ASPEED_SOC_SPI2_FLASH_BASE 0x38000000
+
/*
* Default segments mapping addresses and size for each slave per
* controller. These can be changed when board is initialized with the
- * Segment Address Registers but they don't seem do be used on the
- * field.
+ * Segment Address Registers.
*/
static const AspeedSegments aspeed_segments_legacy[] = {
{ 0x10000000, 32 * 1024 * 1024 },
};
static const AspeedSegments aspeed_segments_fmc[] = {
- { 0x20000000, 64 * 1024 * 1024 },
+ { 0x20000000, 64 * 1024 * 1024 }, /* start address is readonly */
{ 0x24000000, 32 * 1024 * 1024 },
{ 0x26000000, 32 * 1024 * 1024 },
{ 0x28000000, 32 * 1024 * 1024 },
@@ -149,15 +153,155 @@ static const AspeedSegments aspeed_segments_spi[] = {
{ 0x30000000, 64 * 1024 * 1024 },
};
+static const AspeedSegments aspeed_segments_ast2500_fmc[] = {
+ { 0x20000000, 128 * 1024 * 1024 }, /* start address is readonly */
+ { 0x28000000, 32 * 1024 * 1024 },
+ { 0x2A000000, 32 * 1024 * 1024 },
+};
+
+static const AspeedSegments aspeed_segments_ast2500_spi1[] = {
+ { 0x30000000, 32 * 1024 * 1024 }, /* start address is readonly */
+ { 0x32000000, 96 * 1024 * 1024 }, /* end address is readonly */
+};
+
+static const AspeedSegments aspeed_segments_ast2500_spi2[] = {
+ { 0x38000000, 32 * 1024 * 1024 }, /* start address is readonly */
+ { 0x3A000000, 96 * 1024 * 1024 }, /* end address is readonly */
+};
+
static const AspeedSMCController controllers[] = {
{ "aspeed.smc.smc", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS,
- CONF_ENABLE_W0, 5, aspeed_segments_legacy, 0x6000000 },
+ CONF_ENABLE_W0, 5, aspeed_segments_legacy,
+ ASPEED_SOC_SMC_FLASH_BASE, 0x6000000 },
{ "aspeed.smc.fmc", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS,
- CONF_ENABLE_W0, 5, aspeed_segments_fmc, 0x10000000 },
+ CONF_ENABLE_W0, 5, aspeed_segments_fmc,
+ ASPEED_SOC_FMC_FLASH_BASE, 0x10000000 },
{ "aspeed.smc.spi", R_SPI_CONF, 0xff, R_SPI_CTRL0, R_SPI_TIMINGS,
- SPI_CONF_ENABLE_W0, 1, aspeed_segments_spi, 0x10000000 },
+ SPI_CONF_ENABLE_W0, 1, aspeed_segments_spi,
+ ASPEED_SOC_SPI_FLASH_BASE, 0x10000000 },
+ { "aspeed.smc.ast2500-fmc", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS,
+ CONF_ENABLE_W0, 3, aspeed_segments_ast2500_fmc,
+ ASPEED_SOC_FMC_FLASH_BASE, 0x10000000 },
+ { "aspeed.smc.ast2500-spi1", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS,
+ CONF_ENABLE_W0, 2, aspeed_segments_ast2500_spi1,
+ ASPEED_SOC_SPI_FLASH_BASE, 0x8000000 },
+ { "aspeed.smc.ast2500-spi2", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS,
+ CONF_ENABLE_W0, 2, aspeed_segments_ast2500_spi2,
+ ASPEED_SOC_SPI2_FLASH_BASE, 0x8000000 },
};
+/*
+ * The Segment Register uses a 8MB unit to encode the start address
+ * and the end address of the mapping window of a flash SPI slave :
+ *
+ * | byte 1 | byte 2 | byte 3 | byte 4 |
+ * +--------+--------+--------+--------+
+ * | end | start | 0 | 0 |
+ *
+ */
+static inline uint32_t aspeed_smc_segment_to_reg(const AspeedSegments *seg)
+{
+ uint32_t reg = 0;
+ reg |= ((seg->addr >> 23) & SEG_START_MASK) << SEG_START_SHIFT;
+ reg |= (((seg->addr + seg->size) >> 23) & SEG_END_MASK) << SEG_END_SHIFT;
+ return reg;
+}
+
+static inline void aspeed_smc_reg_to_segment(uint32_t reg, AspeedSegments *seg)
+{
+ seg->addr = ((reg >> SEG_START_SHIFT) & SEG_START_MASK) << 23;
+ seg->size = (((reg >> SEG_END_SHIFT) & SEG_END_MASK) << 23) - seg->addr;
+}
+
+static bool aspeed_smc_flash_overlap(const AspeedSMCState *s,
+ const AspeedSegments *new,
+ int cs)
+{
+ AspeedSegments seg;
+ int i;
+
+ for (i = 0; i < s->ctrl->max_slaves; i++) {
+ if (i == cs) {
+ continue;
+ }
+
+ aspeed_smc_reg_to_segment(s->regs[R_SEG_ADDR0 + i], &seg);
+
+ if (new->addr + new->size > seg.addr &&
+ new->addr < seg.addr + seg.size) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: new segment CS%d [ 0x%"
+ HWADDR_PRIx" - 0x%"HWADDR_PRIx" ] overlaps with "
+ "CS%d [ 0x%"HWADDR_PRIx" - 0x%"HWADDR_PRIx" ]\n",
+ s->ctrl->name, cs, new->addr, new->addr + new->size,
+ i, seg.addr, seg.addr + seg.size);
+ return true;
+ }
+ }
+ return false;
+}
+
+static void aspeed_smc_flash_set_segment(AspeedSMCState *s, int cs,
+ uint64_t new)
+{
+ AspeedSMCFlash *fl = &s->flashes[cs];
+ AspeedSegments seg;
+
+ aspeed_smc_reg_to_segment(new, &seg);
+
+ /* The start address of CS0 is read-only */
+ if (cs == 0 && seg.addr != s->ctrl->flash_window_base) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Tried to change CS0 start address to 0x%"
+ HWADDR_PRIx "\n", s->ctrl->name, seg.addr);
+ return;
+ }
+
+ /*
+ * The end address of the AST2500 spi controllers is also
+ * read-only.
+ */
+ if ((s->ctrl->segments == aspeed_segments_ast2500_spi1 ||
+ s->ctrl->segments == aspeed_segments_ast2500_spi2) &&
+ cs == s->ctrl->max_slaves &&
+ seg.addr + seg.size != s->ctrl->segments[cs].addr +
+ s->ctrl->segments[cs].size) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Tried to change CS%d end address to 0x%"
+ HWADDR_PRIx "\n", s->ctrl->name, cs, seg.addr);
+ return;
+ }
+
+ /* Keep the segment in the overall flash window */
+ if (seg.addr + seg.size <= s->ctrl->flash_window_base ||
+ seg.addr > s->ctrl->flash_window_base + s->ctrl->flash_window_size) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: new segment for CS%d is invalid : "
+ "[ 0x%"HWADDR_PRIx" - 0x%"HWADDR_PRIx" ]\n",
+ s->ctrl->name, cs, seg.addr, seg.addr + seg.size);
+ return;
+ }
+
+ /* Check start address vs. alignment */
+ if (seg.addr % seg.size) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: new segment for CS%d is not "
+ "aligned : [ 0x%"HWADDR_PRIx" - 0x%"HWADDR_PRIx" ]\n",
+ s->ctrl->name, cs, seg.addr, seg.addr + seg.size);
+ }
+
+ /* And segments should not overlap */
+ if (aspeed_smc_flash_overlap(s, &seg, cs)) {
+ return;
+ }
+
+ /* All should be fine now to move the region */
+ memory_region_transaction_begin();
+ memory_region_set_size(&fl->mmio, seg.size);
+ memory_region_set_address(&fl->mmio, seg.addr - s->ctrl->flash_window_base);
+ memory_region_set_enabled(&fl->mmio, true);
+ memory_region_transaction_commit();
+
+ s->regs[R_SEG_ADDR0 + cs] = new;
+}
+
static uint64_t aspeed_smc_flash_default_read(void *opaque, hwaddr addr,
unsigned size)
{
@@ -281,6 +425,12 @@ static void aspeed_smc_reset(DeviceState *d)
s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE;
}
+ /* setup default segment register values for all */
+ for (i = 0; i < s->ctrl->max_slaves; ++i) {
+ s->regs[R_SEG_ADDR0 + i] =
+ aspeed_smc_segment_to_reg(&s->ctrl->segments[i]);
+ }
+
aspeed_smc_update_cs(s);
}
@@ -301,6 +451,7 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
addr == s->r_timings ||
addr == s->r_ce_ctrl ||
addr == R_INTR_CTRL ||
+ (addr >= R_SEG_ADDR0 && addr < R_SEG_ADDR0 + s->ctrl->max_slaves) ||
(addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs)) {
return s->regs[addr];
} else {
@@ -332,6 +483,13 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data,
} else if (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs) {
s->regs[addr] = value;
aspeed_smc_update_cs(s);
+ } else if (addr >= R_SEG_ADDR0 &&
+ addr < R_SEG_ADDR0 + s->ctrl->max_slaves) {
+ int cs = addr - R_SEG_ADDR0;
+
+ if (value != s->regs[R_SEG_ADDR0 + cs]) {
+ aspeed_smc_flash_set_segment(s, cs, value);
+ }
} else {
qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n",
__func__, addr);
@@ -384,23 +542,33 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp)
aspeed_smc_reset(dev);
+ /* The memory region for the controller registers */
memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_smc_ops, s,
s->ctrl->name, ASPEED_SMC_R_MAX * 4);
sysbus_init_mmio(sbd, &s->mmio);
/*
- * Memory region where flash modules are remapped
+ * The container memory region representing the address space
+ * window in which the flash modules are mapped. The size and
+ * address depends on the SoC model and controller type.
*/
snprintf(name, sizeof(name), "%s.flash", s->ctrl->name);
memory_region_init_io(&s->mmio_flash, OBJECT(s),
&aspeed_smc_flash_default_ops, s, name,
- s->ctrl->mapping_window_size);
+ s->ctrl->flash_window_size);
sysbus_init_mmio(sbd, &s->mmio_flash);
- s->flashes = g_new0(AspeedSMCFlash, s->num_cs);
+ s->flashes = g_new0(AspeedSMCFlash, s->ctrl->max_slaves);
- for (i = 0; i < s->num_cs; ++i) {
+ /*
+ * Let's create a sub memory region for each possible slave. All
+ * have a configurable memory segment in the overall flash mapping
+ * window of the controller but, there is not necessarily a flash
+ * module behind to handle the memory accesses. This depends on
+ * the board configuration.
+ */
+ for (i = 0; i < s->ctrl->max_slaves; ++i) {
AspeedSMCFlash *fl = &s->flashes[i];
snprintf(name, sizeof(name), "%s.%d", s->ctrl->name, i);
diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h
index 932704c380..5406b498d7 100644
--- a/include/hw/arm/aspeed_soc.h
+++ b/include/hw/arm/aspeed_soc.h
@@ -20,6 +20,8 @@
#include "hw/i2c/aspeed_i2c.h"
#include "hw/ssi/aspeed_smc.h"
+#define ASPEED_SPIS_NUM 2
+
typedef struct AspeedSoCState {
/*< private >*/
DeviceState parent;
@@ -31,8 +33,8 @@ typedef struct AspeedSoCState {
AspeedTimerCtrlState timerctrl;
AspeedI2CState i2c;
AspeedSCUState scu;
- AspeedSMCState smc;
- AspeedSMCState spi;
+ AspeedSMCState fmc;
+ AspeedSMCState spi[ASPEED_SPIS_NUM];
AspeedSDMCState sdmc;
} AspeedSoCState;
@@ -44,6 +46,10 @@ typedef struct AspeedSoCInfo {
const char *cpu_model;
uint32_t silicon_rev;
hwaddr sdram_base;
+ int spis_num;
+ const hwaddr *spi_bases;
+ const char *fmc_typename;
+ const char **spi_typename;
} AspeedSoCInfo;
typedef struct AspeedSoCClass {
diff --git a/include/hw/arm/virt-acpi-build.h b/include/hw/arm/virt-acpi-build.h
index e43330ad65..f5ec749b8f 100644
--- a/include/hw/arm/virt-acpi-build.h
+++ b/include/hw/arm/virt-acpi-build.h
@@ -33,6 +33,7 @@ typedef struct VirtGuestInfo {
const int *irqmap;
bool use_highmem;
int gic_version;
+ bool no_its;
} VirtGuestInfo;
diff --git a/include/hw/compat.h b/include/hw/compat.h
index ef3fae3e1b..0f06e113be 100644
--- a/include/hw/compat.h
+++ b/include/hw/compat.h
@@ -14,6 +14,10 @@
.driver = "ioapic",\
.property = "version",\
.value = "0x11",\
+ },{\
+ .driver = "intel-iommu",\
+ .property = "x-buggy-eim",\
+ .value = "true",\
},
#define HW_COMPAT_2_6 \
diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h
index 06c4e9f6f9..cdd11fb093 100644
--- a/include/hw/i386/apic_internal.h
+++ b/include/hw/i386/apic_internal.h
@@ -146,6 +146,10 @@ typedef struct APICCommonClass
void (*pre_save)(APICCommonState *s);
void (*post_load)(APICCommonState *s);
void (*reset)(APICCommonState *s);
+ /* send_msi emulates an APIC bus and its proper place would be in a new
+ * device, but it's convenient to have it here for now.
+ */
+ void (*send_msi)(MSIMessage *msi);
} APICCommonClass;
struct APICCommonState {
@@ -222,4 +226,6 @@ static inline int apic_get_bit(uint32_t *tab, int index)
return !!(tab[i] & mask);
}
+APICCommonClass *apic_get_class(void);
+
#endif /* QEMU_APIC_INTERNAL_H */
diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h
index a42dbd745a..1989c1eec1 100644
--- a/include/hw/i386/intel_iommu.h
+++ b/include/hw/i386/intel_iommu.h
@@ -289,6 +289,8 @@ struct IntelIOMMUState {
dma_addr_t intr_root; /* Interrupt remapping table pointer */
uint32_t intr_size; /* Number of IR table entries */
bool intr_eime; /* Extended interrupt mode enabled */
+ OnOffAuto intr_eim; /* Toggle for EIM cabability */
+ bool buggy_eim; /* Force buggy EIM unless eim=off */
};
/* Find the VTD Address space associated with the given bus pointer,
diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
index def3b4507e..bdfbcc0ffa 100644
--- a/include/hw/ssi/aspeed_smc.h
+++ b/include/hw/ssi/aspeed_smc.h
@@ -42,7 +42,8 @@ typedef struct AspeedSMCController {
uint8_t conf_enable_w0;
uint8_t max_slaves;
const AspeedSegments *segments;
- uint32_t mapping_window_size;
+ hwaddr flash_window_base;
+ uint32_t flash_window_size;
} AspeedSMCController;
typedef struct AspeedSMCFlash {
diff --git a/qapi-schema.json b/qapi-schema.json
index ded1179f06..5a8ec38e10 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3101,10 +3101,31 @@
# QEMU version, machine type, machine options and accelerator options.
# A static model is always migration-safe. (since 2.8)
#
+# @unavailable-features: #optional List of properties that prevent
+# the CPU model from running in the current
+# host. (since 2.8)
+#
+# @unavailable-features is a list of QOM property names that
+# represent CPU model attributes that prevent the CPU from running.
+# If the QOM property is read-only, that means there's no known
+# way to make the CPU model run in the current host. Implementations
+# that choose not to provide specific information return the
+# property name "type".
+# If the property is read-write, it means that it MAY be possible
+# to run the CPU model in the current host if that property is
+# changed. Management software can use it as hints to suggest or
+# choose an alternative for the user, or just to generate meaningful
+# error messages explaining why the CPU model can't be used.
+# If @unavailable-features is an empty list, the CPU model is
+# runnable using the current host and machine-type.
+# If @unavailable-features is not present, runnability
+# information for the CPU is not available.
+#
# Since: 1.2.0
##
{ 'struct': 'CpuDefinitionInfo',
- 'data': { 'name': 'str', '*migration-safe': 'bool', 'static': 'bool' } }
+ 'data': { 'name': 'str', '*migration-safe': 'bool', 'static': 'bool',
+ '*unavailable-features': [ 'str' ] } }
##
# @query-cpu-definitions:
diff --git a/stubs/vmstate.c b/stubs/vmstate.c
index 94b831e219..65906271d2 100644
--- a/stubs/vmstate.c
+++ b/stubs/vmstate.c
@@ -3,11 +3,6 @@
#include "migration/vmstate.h"
const VMStateDescription vmstate_dummy = {};
-const VMStateInfo vmstate_info_uint8;
-const VMStateInfo vmstate_info_uint32;
-const VMStateInfo vmstate_info_uint64;
-const VMStateInfo vmstate_info_int64;
-const VMStateInfo vmstate_info_timer;
int vmstate_register_with_alias_id(DeviceState *dev,
int instance_id,
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 76d824d315..2218c00dad 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -2191,7 +2191,11 @@ static inline bool arm_cpu_data_is_big_endian(CPUARMState *env)
#define ARM_TBFLAG_BE_DATA_SHIFT 20
#define ARM_TBFLAG_BE_DATA_MASK (1 << ARM_TBFLAG_BE_DATA_SHIFT)
-/* Bit usage when in AArch64 state: currently we have no A64 specific bits */
+/* Bit usage when in AArch64 state */
+#define ARM_TBFLAG_TBI0_SHIFT 0 /* TBI0 for EL0/1 or TBI for EL2/3 */
+#define ARM_TBFLAG_TBI0_MASK (0x1ull << ARM_TBFLAG_TBI0_SHIFT)
+#define ARM_TBFLAG_TBI1_SHIFT 1 /* TBI1 for EL0/1 */
+#define ARM_TBFLAG_TBI1_MASK (0x1ull << ARM_TBFLAG_TBI1_SHIFT)
/* some convenience accessor macros */
#define ARM_TBFLAG_AARCH64_STATE(F) \
@@ -2222,6 +2226,10 @@ static inline bool arm_cpu_data_is_big_endian(CPUARMState *env)
(((F) & ARM_TBFLAG_NS_MASK) >> ARM_TBFLAG_NS_SHIFT)
#define ARM_TBFLAG_BE_DATA(F) \
(((F) & ARM_TBFLAG_BE_DATA_MASK) >> ARM_TBFLAG_BE_DATA_SHIFT)
+#define ARM_TBFLAG_TBI0(F) \
+ (((F) & ARM_TBFLAG_TBI0_MASK) >> ARM_TBFLAG_TBI0_SHIFT)
+#define ARM_TBFLAG_TBI1(F) \
+ (((F) & ARM_TBFLAG_TBI1_MASK) >> ARM_TBFLAG_TBI1_SHIFT)
static inline bool bswap_code(bool sctlr_b)
{
@@ -2319,12 +2327,51 @@ static inline bool arm_cpu_bswap_data(CPUARMState *env)
}
#endif
+#ifndef CONFIG_USER_ONLY
+/**
+ * arm_regime_tbi0:
+ * @env: CPUARMState
+ * @mmu_idx: MMU index indicating required translation regime
+ *
+ * Extracts the TBI0 value from the appropriate TCR for the current EL
+ *
+ * Returns: the TBI0 value.
+ */
+uint32_t arm_regime_tbi0(CPUARMState *env, ARMMMUIdx mmu_idx);
+
+/**
+ * arm_regime_tbi1:
+ * @env: CPUARMState
+ * @mmu_idx: MMU index indicating required translation regime
+ *
+ * Extracts the TBI1 value from the appropriate TCR for the current EL
+ *
+ * Returns: the TBI1 value.
+ */
+uint32_t arm_regime_tbi1(CPUARMState *env, ARMMMUIdx mmu_idx);
+#else
+/* We can't handle tagged addresses properly in user-only mode */
+static inline uint32_t arm_regime_tbi0(CPUARMState *env, ARMMMUIdx mmu_idx)
+{
+ return 0;
+}
+
+static inline uint32_t arm_regime_tbi1(CPUARMState *env, ARMMMUIdx mmu_idx)
+{
+ return 0;
+}
+#endif
+
static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
target_ulong *cs_base, uint32_t *flags)
{
+ ARMMMUIdx mmu_idx = cpu_mmu_index(env, false);
if (is_a64(env)) {
*pc = env->pc;
*flags = ARM_TBFLAG_AARCH64_STATE_MASK;
+ /* Get control bits for tagged addresses */
+ *flags |= (arm_regime_tbi0(env, mmu_idx) << ARM_TBFLAG_TBI0_SHIFT);
+ *flags |= (arm_regime_tbi1(env, mmu_idx) << ARM_TBFLAG_TBI1_SHIFT);
} else {
*pc = env->regs[15];
*flags = (env->thumb << ARM_TBFLAG_THUMB_SHIFT)
@@ -2343,7 +2390,8 @@ static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
<< ARM_TBFLAG_XSCALE_CPAR_SHIFT);
}
- *flags |= (cpu_mmu_index(env, false) << ARM_TBFLAG_MMUIDX_SHIFT);
+ *flags |= (mmu_idx << ARM_TBFLAG_MMUIDX_SHIFT);
+
/* The SS_ACTIVE and PSTATE_SS bits correspond to the state machine
* states defined in the ARM ARM for software singlestep:
* SS_ACTIVE PSTATE.SS State
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 25f612d493..cb83ee2008 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -1,4 +1,5 @@
#include "qemu/osdep.h"
+#include "trace.h"
#include "cpu.h"
#include "internals.h"
#include "exec/gdbstub.h"
@@ -1560,10 +1561,13 @@ static void gt_recalc_timer(ARMCPU *cpu, int timeridx)
/* Note that this must be unsigned 64 bit arithmetic: */
int istatus = count - offset >= gt->cval;
uint64_t nexttick;
+ int irqstate;
gt->ctl = deposit32(gt->ctl, 2, 1, istatus);
- qemu_set_irq(cpu->gt_timer_outputs[timeridx],
- (istatus && !(gt->ctl & 2)));
+
+ irqstate = (istatus && !(gt->ctl & 2));
+ qemu_set_irq(cpu->gt_timer_outputs[timeridx], irqstate);
+
if (istatus) {
/* Next transition is when count rolls back over to zero */
nexttick = UINT64_MAX;
@@ -1580,11 +1584,13 @@ static void gt_recalc_timer(ARMCPU *cpu, int timeridx)
nexttick = INT64_MAX / GTIMER_SCALE;
}
timer_mod(cpu->gt_timer[timeridx], nexttick);
+ trace_arm_gt_recalc(timeridx, irqstate, nexttick);
} else {
/* Timer disabled: ISTATUS and timer output always clear */
gt->ctl &= ~4;
qemu_set_irq(cpu->gt_timer_outputs[timeridx], 0);
timer_del(cpu->gt_timer[timeridx]);
+ trace_arm_gt_recalc_disabled(timeridx);
}
}
@@ -1610,6 +1616,7 @@ static void gt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri,
int timeridx,
uint64_t value)
{
+ trace_arm_gt_cval_write(timeridx, value);
env->cp15.c14_timer[timeridx].cval = value;
gt_recalc_timer(arm_env_get_cpu(env), timeridx);
}
@@ -1629,6 +1636,7 @@ static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri,
{
uint64_t offset = timeridx == GTIMER_VIRT ? env->cp15.cntvoff_el2 : 0;
+ trace_arm_gt_tval_write(timeridx, value);
env->cp15.c14_timer[timeridx].cval = gt_get_countervalue(env) - offset +
sextract64(value, 0, 32);
gt_recalc_timer(arm_env_get_cpu(env), timeridx);
@@ -1641,6 +1649,7 @@ static void gt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri,
ARMCPU *cpu = arm_env_get_cpu(env);
uint32_t oldval = env->cp15.c14_timer[timeridx].ctl;
+ trace_arm_gt_ctl_write(timeridx, value);
env->cp15.c14_timer[timeridx].ctl = deposit64(oldval, 0, 2, value);
if ((oldval ^ value) & 1) {
/* Enable toggled */
@@ -1649,8 +1658,10 @@ static void gt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri,
/* IMASK toggled: don't need to recalculate,
* just set the interrupt line based on ISTATUS
*/
- qemu_set_irq(cpu->gt_timer_outputs[timeridx],
- (oldval & 4) && !(value & 2));
+ int irqstate = (oldval & 4) && !(value & 2);
+
+ trace_arm_gt_imask_toggle(timeridx, irqstate);
+ qemu_set_irq(cpu->gt_timer_outputs[timeridx], irqstate);
}
}
@@ -1715,6 +1726,7 @@ static void gt_cntvoff_write(CPUARMState *env, const ARMCPRegInfo *ri,
{
ARMCPU *cpu = arm_env_get_cpu(env);
+ trace_arm_gt_cntvoff_write(value);
raw_write(env, ri, value);
gt_recalc_timer(cpu, GTIMER_VIRT);
}
@@ -4060,6 +4072,14 @@ static const ARMCPRegInfo debug_cp_reginfo[] = {
.cp = 14, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 0,
.access = PL1_RW, .accessfn = access_tda,
.type = ARM_CP_NOP },
+ /* Dummy MDCCINT_EL1, since we don't implement the Debug Communications
+ * Channel but Linux may try to access this register. The 32-bit
+ * alias is DBGDCCINT.
+ */
+ { .name = "MDCCINT_EL1", .state = ARM_CP_STATE_BOTH,
+ .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 0,
+ .access = PL1_RW, .accessfn = access_tda,
+ .type = ARM_CP_NOP },
REGINFO_SENTINEL
};
@@ -6720,6 +6740,52 @@ static inline TCR *regime_tcr(CPUARMState *env, ARMMMUIdx mmu_idx)
return &env->cp15.tcr_el[regime_el(env, mmu_idx)];
}
+/* Returns TBI0 value for current regime el */
+uint32_t arm_regime_tbi0(CPUARMState *env, ARMMMUIdx mmu_idx)
+{
+ TCR *tcr;
+ uint32_t el;
+
+ /* For EL0 and EL1, TBI is controlled by stage 1's TCR, so convert
+ * a stage 1+2 mmu index into the appropriate stage 1 mmu index.
+ */
+ if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) {
+ mmu_idx += ARMMMUIdx_S1NSE0;
+ }
+
+ tcr = regime_tcr(env, mmu_idx);
+ el = regime_el(env, mmu_idx);
+
+ if (el > 1) {
+ return extract64(tcr->raw_tcr, 20, 1);
+ } else {
+ return extract64(tcr->raw_tcr, 37, 1);
+ }
+}
+
+/* Returns TBI1 value for current regime el */
+uint32_t arm_regime_tbi1(CPUARMState *env, ARMMMUIdx mmu_idx)
+{
+ TCR *tcr;
+ uint32_t el;
+
+ /* For EL0 and EL1, TBI is controlled by stage 1's TCR, so convert
+ * a stage 1+2 mmu index into the appropriate stage 1 mmu index.
+ */
+ if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) {
+ mmu_idx += ARMMMUIdx_S1NSE0;
+ }
+
+ tcr = regime_tcr(env, mmu_idx);
+ el = regime_el(env, mmu_idx);
+
+ if (el > 1) {
+ return 0;
+ } else {
+ return extract64(tcr->raw_tcr, 38, 1);
+ }
+}
+
/* Return the TTBR associated with this translation regime */
static inline uint64_t regime_ttbr(CPUARMState *env, ARMMMUIdx mmu_idx,
int ttbrn)
diff --git a/target-arm/kvm.c b/target-arm/kvm.c
index dbe393c109..c00b94e42a 100644
--- a/target-arm/kvm.c
+++ b/target-arm/kvm.c
@@ -23,6 +23,7 @@
#include "internals.h"
#include "hw/arm/arm.h"
#include "exec/memattrs.h"
+#include "exec/address-spaces.h"
#include "hw/boards.h"
#include "qemu/log.h"
@@ -283,7 +284,7 @@ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group,
}
if (QSLIST_EMPTY(&kvm_devices_head)) {
- memory_listener_register(&devlistener, NULL);
+ memory_listener_register(&devlistener, &address_space_memory);
qemu_add_machine_init_done_notifier(&notify);
}
kd = g_new0(KVMDevice, 1);
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
index be27b21d52..cd94216591 100644
--- a/target-arm/op_helper.c
+++ b/target-arm/op_helper.c
@@ -479,6 +479,13 @@ void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val)
{
cpsr_write(env, val, CPSR_ERET_MASK, CPSRWriteExceptionReturn);
+ /* Generated code has already stored the new PC value, but
+ * without masking out its low bits, because which bits need
+ * masking depends on whether we're returning to Thumb or ARM
+ * state. Do the masking now.
+ */
+ env->regs[15] &= (env->thumb ? ~1 : ~3);
+
arm_call_el_change_hook(arm_env_get_cpu(env));
}
diff --git a/target-arm/trace-events b/target-arm/trace-events
new file mode 100644
index 0000000000..9f726bdae3
--- /dev/null
+++ b/target-arm/trace-events
@@ -0,0 +1,10 @@
+# See docs/tracing.txt for syntax documentation.
+
+# target-arm/helper.c
+arm_gt_recalc(int timer, int irqstate, uint64_t nexttick) "gt recalc: timer %d irqstate %d next tick %" PRIx64
+arm_gt_recalc_disabled(int timer) "gt recalc: timer %d irqstate 0 timer disabled"
+arm_gt_cval_write(int timer, uint64_t value) "gt_cval_write: timer %d value %" PRIx64
+arm_gt_tval_write(int timer, uint64_t value) "gt_tval_write: timer %d value %" PRIx64
+arm_gt_ctl_write(int timer, uint64_t value) "gt_ctl_write: timer %d value %" PRIx64
+arm_gt_imask_toggle(int timer, int irqstate) "gt_ctl_write: timer %d IMASK toggle, new irqstate %d"
+arm_gt_cntvoff_write(uint64_t value) "gt_cntvoff_write: value %" PRIx64
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index 307e281557..96c222722e 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -41,6 +41,7 @@ static TCGv_i64 cpu_pc;
/* Load/store exclusive handling */
static TCGv_i64 cpu_exclusive_high;
+static TCGv_i64 cpu_reg(DisasContext *s, int reg);
static const char *regnames[] = {
"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
@@ -176,6 +177,76 @@ void gen_a64_set_pc_im(uint64_t val)
tcg_gen_movi_i64(cpu_pc, val);
}
+/* Load the PC from a generic TCG variable.
+ *
+ * If address tagging is enabled via the TCR TBI bits, then loading
+ * an address into the PC will clear out any tag in the it:
+ * + for EL2 and EL3 there is only one TBI bit, and if it is set
+ * then the address is zero-extended, clearing bits [63:56]
+ * + for EL0 and EL1, TBI0 controls addresses with bit 55 == 0
+ * and TBI1 controls addressses with bit 55 == 1.
+ * If the appropriate TBI bit is set for the address then
+ * the address is sign-extended from bit 55 into bits [63:56]
+ *
+ * We can avoid doing this for relative-branches, because the
+ * PC + offset can never overflow into the tag bits (assuming
+ * that virtual addresses are less than 56 bits wide, as they
+ * are currently), but we must handle it for branch-to-register.
+ */
+static void gen_a64_set_pc(DisasContext *s, TCGv_i64 src)
+{
+
+ if (s->current_el <= 1) {
+ /* Test if NEITHER or BOTH TBI values are set. If so, no need to
+ * examine bit 55 of address, can just generate code.
+ * If mixed, then test via generated code
+ */
+ if (s->tbi0 && s->tbi1) {
+ TCGv_i64 tmp_reg = tcg_temp_new_i64();
+ /* Both bits set, sign extension from bit 55 into [63:56] will
+ * cover both cases
+ */
+ tcg_gen_shli_i64(tmp_reg, src, 8);
+ tcg_gen_sari_i64(cpu_pc, tmp_reg, 8);
+ tcg_temp_free_i64(tmp_reg);
+ } else if (!s->tbi0 && !s->tbi1) {
+ /* Neither bit set, just load it as-is */
+ tcg_gen_mov_i64(cpu_pc, src);
+ } else {
+ TCGv_i64 tcg_tmpval = tcg_temp_new_i64();
+ TCGv_i64 tcg_bit55 = tcg_temp_new_i64();
+ TCGv_i64 tcg_zero = tcg_const_i64(0);
+
+ tcg_gen_andi_i64(tcg_bit55, src, (1ull << 55));
+
+ if (s->tbi0) {
+ /* tbi0==1, tbi1==0, so 0-fill upper byte if bit 55 = 0 */
+ tcg_gen_andi_i64(tcg_tmpval, src,
+ 0x00FFFFFFFFFFFFFFull);
+ tcg_gen_movcond_i64(TCG_COND_EQ, cpu_pc, tcg_bit55, tcg_zero,
+ tcg_tmpval, src);
+ } else {
+ /* tbi0==0, tbi1==1, so 1-fill upper byte if bit 55 = 1 */
+ tcg_gen_ori_i64(tcg_tmpval, src,
+ 0xFF00000000000000ull);
+ tcg_gen_movcond_i64(TCG_COND_NE, cpu_pc, tcg_bit55, tcg_zero,
+ tcg_tmpval, src);
+ }
+ tcg_temp_free_i64(tcg_zero);
+ tcg_temp_free_i64(tcg_bit55);
+ tcg_temp_free_i64(tcg_tmpval);
+ }
+ } else { /* EL > 1 */
+ if (s->tbi0) {
+ /* Force tag byte to all zero */
+ tcg_gen_andi_i64(cpu_pc, src, 0x00FFFFFFFFFFFFFFull);
+ } else {
+ /* Load unmodified address */
+ tcg_gen_mov_i64(cpu_pc, src);
+ }
+ }
+}
+
typedef struct DisasCompare64 {
TCGCond cond;
TCGv_i64 value;
@@ -1596,12 +1667,12 @@ static void disas_exc(DisasContext *s, uint32_t insn)
* instruction works properly.
*/
switch (op2_ll) {
- case 1:
+ case 1: /* SVC */
gen_ss_advance(s);
gen_exception_insn(s, 0, EXCP_SWI, syn_aa64_svc(imm16),
default_exception_el(s));
break;
- case 2:
+ case 2: /* HVC */
if (s->current_el == 0) {
unallocated_encoding(s);
break;
@@ -1614,7 +1685,7 @@ static void disas_exc(DisasContext *s, uint32_t insn)
gen_ss_advance(s);
gen_exception_insn(s, 0, EXCP_HVC, syn_aa64_hvc(imm16), 2);
break;
- case 3:
+ case 3: /* SMC */
if (s->current_el == 0) {
unallocated_encoding(s);
break;
@@ -1704,12 +1775,13 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn)
switch (opc) {
case 0: /* BR */
- case 2: /* RET */
- tcg_gen_mov_i64(cpu_pc, cpu_reg(s, rn));
- break;
case 1: /* BLR */
- tcg_gen_mov_i64(cpu_pc, cpu_reg(s, rn));
- tcg_gen_movi_i64(cpu_reg(s, 30), s->pc);
+ case 2: /* RET */
+ gen_a64_set_pc(s, cpu_reg(s, rn));
+ /* BLR also needs to load return address */
+ if (opc == 1) {
+ tcg_gen_movi_i64(cpu_reg(s, 30), s->pc);
+ }
break;
case 4: /* ERET */
if (s->current_el == 0) {
@@ -11175,6 +11247,8 @@ void gen_intermediate_code_a64(ARMCPU *cpu, TranslationBlock *tb)
dc->condexec_mask = 0;
dc->condexec_cond = 0;
dc->mmu_idx = ARM_TBFLAG_MMUIDX(tb->flags);
+ dc->tbi0 = ARM_TBFLAG_TBI0(tb->flags);
+ dc->tbi1 = ARM_TBFLAG_TBI1(tb->flags);
dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx);
#if !defined(CONFIG_USER_ONLY)
dc->user = (dc->current_el == 0);
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 8df24bf35a..164b52a0d0 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -4363,26 +4363,35 @@ static void gen_mrs_banked(DisasContext *s, int r, int sysm, int rn)
s->is_jmp = DISAS_UPDATE;
}
-/* Generate an old-style exception return. Marks pc as dead. */
-static void gen_exception_return(DisasContext *s, TCGv_i32 pc)
+/* Store value to PC as for an exception return (ie don't
+ * mask bits). The subsequent call to gen_helper_cpsr_write_eret()
+ * will do the masking based on the new value of the Thumb bit.
+ */
+static void store_pc_exc_ret(DisasContext *s, TCGv_i32 pc)
{
- TCGv_i32 tmp;
- store_reg(s, 15, pc);
- tmp = load_cpu_field(spsr);
- gen_helper_cpsr_write_eret(cpu_env, tmp);
- tcg_temp_free_i32(tmp);
- s->is_jmp = DISAS_JUMP;
+ tcg_gen_mov_i32(cpu_R[15], pc);
+ tcg_temp_free_i32(pc);
}
/* Generate a v6 exception return. Marks both values as dead. */
static void gen_rfe(DisasContext *s, TCGv_i32 pc, TCGv_i32 cpsr)
{
+ store_pc_exc_ret(s, pc);
+ /* The cpsr_write_eret helper will mask the low bits of PC
+ * appropriately depending on the new Thumb bit, so it must
+ * be called after storing the new PC.
+ */
gen_helper_cpsr_write_eret(cpu_env, cpsr);
tcg_temp_free_i32(cpsr);
- store_reg(s, 15, pc);
s->is_jmp = DISAS_JUMP;
}
+/* Generate an old-style exception return. Marks pc as dead. */
+static void gen_exception_return(DisasContext *s, TCGv_i32 pc)
+{
+ gen_rfe(s, pc, load_cpu_field(spsr));
+}
+
static void gen_nop_hint(DisasContext *s, int val)
{
switch (val) {
@@ -9366,6 +9375,8 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
} else if (i == rn) {
loaded_var = tmp;
loaded_base = 1;
+ } else if (rn == 15 && exc_return) {
+ store_pc_exc_ret(s, tmp);
} else {
store_reg_from_load(s, i, tmp);
}
diff --git a/target-arm/translate.h b/target-arm/translate.h
index dbd7ac83d5..a53f25a407 100644
--- a/target-arm/translate.h
+++ b/target-arm/translate.h
@@ -22,6 +22,8 @@ typedef struct DisasContext {
int user;
#endif
ARMMMUIdx mmu_idx; /* MMU index to use for normal loads/stores */
+ bool tbi0; /* TBI0 for EL0/1 or TBI for EL2/3 */
+ bool tbi1; /* TBI1 for EL0/1, not used for EL2/3 */
bool ns; /* Use non-secure CPREG bank on access */
int fp_excp_el; /* FP exception EL or 0 if enabled */
/* Flag indicating that exceptions from secure mode are routed to EL3. */
diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h
index 5dde658ac4..e724004a78 100644
--- a/target-i386/cpu-qom.h
+++ b/target-i386/cpu-qom.h
@@ -63,6 +63,10 @@ typedef struct X86CPUClass {
bool kvm_required;
+ /* Optional description of CPU model.
+ * If unavailable, cpu_def->model_id is used */
+ const char *model_description;
+
DeviceRealize parent_realize;
void (*parent_reset)(CPUState *cpu);
} X86CPUClass;
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 1c57fce81b..d95514c7dd 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -278,12 +278,12 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
},
[FEAT_1_ECX] = {
.feat_names = {
- "pni|sse3" /* Intel,AMD sse3 */, "pclmulqdq|pclmuldq", "dtes64", "monitor",
- "ds_cpl", "vmx", "smx", "est",
+ "pni" /* Intel,AMD sse3 */, "pclmulqdq", "dtes64", "monitor",
+ "ds-cpl", "vmx", "smx", "est",
"tm2", "ssse3", "cid", NULL,
"fma", "cx16", "xtpr", "pdcm",
- NULL, "pcid", "dca", "sse4.1|sse4_1",
- "sse4.2|sse4_2", "x2apic", "movbe", "popcnt",
+ NULL, "pcid", "dca", "sse4.1",
+ "sse4.2", "x2apic", "movbe", "popcnt",
"tsc-deadline", "aes", "xsave", "osxsave",
"avx", "f16c", "rdrand", "hypervisor",
},
@@ -302,22 +302,22 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
NULL /* cx8 */, NULL /* apic */, NULL, "syscall",
NULL /* mtrr */, NULL /* pge */, NULL /* mca */, NULL /* cmov */,
NULL /* pat */, NULL /* pse36 */, NULL, NULL /* Linux mp */,
- "nx|xd", NULL, "mmxext", NULL /* mmx */,
- NULL /* fxsr */, "fxsr_opt|ffxsr", "pdpe1gb", "rdtscp",
- NULL, "lm|i64", "3dnowext", "3dnow",
+ "nx", NULL, "mmxext", NULL /* mmx */,
+ NULL /* fxsr */, "fxsr-opt", "pdpe1gb", "rdtscp",
+ NULL, "lm", "3dnowext", "3dnow",
},
.cpuid_eax = 0x80000001, .cpuid_reg = R_EDX,
.tcg_features = TCG_EXT2_FEATURES,
},
[FEAT_8000_0001_ECX] = {
.feat_names = {
- "lahf_lm", "cmp_legacy", "svm", "extapic",
+ "lahf-lm", "cmp-legacy", "svm", "extapic",
"cr8legacy", "abm", "sse4a", "misalignsse",
"3dnowprefetch", "osvw", "ibs", "xop",
"skinit", "wdt", NULL, "lwp",
- "fma4", "tce", NULL, "nodeid_msr",
- NULL, "tbm", "topoext", "perfctr_core",
- "perfctr_nb", NULL, NULL, NULL,
+ "fma4", "tce", NULL, "nodeid-msr",
+ NULL, "tbm", "topoext", "perfctr-core",
+ "perfctr-nb", NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
},
.cpuid_eax = 0x80000001, .cpuid_reg = R_ECX,
@@ -339,8 +339,8 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
},
[FEAT_KVM] = {
.feat_names = {
- "kvmclock", "kvm_nopiodelay", "kvm_mmu", "kvmclock",
- "kvm_asyncpf", "kvm_steal_time", "kvm_pv_eoi", "kvm_pv_unhalt",
+ "kvmclock", "kvm-nopiodelay", "kvm-mmu", "kvmclock",
+ "kvm-asyncpf", "kvm-steal-time", "kvm-pv-eoi", "kvm-pv-unhalt",
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
@@ -400,9 +400,9 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
},
[FEAT_SVM] = {
.feat_names = {
- "npt", "lbrv", "svm_lock", "nrip_save",
- "tsc_scale", "vmcb_clean", "flushbyasid", "decodeassists",
- NULL, NULL, "pause_filter", NULL,
+ "npt", "lbrv", "svm-lock", "nrip-save",
+ "tsc-scale", "vmcb-clean", "flushbyasid", "decodeassists",
+ NULL, NULL, "pause-filter", NULL,
"pfthreshold", NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
@@ -414,7 +414,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
},
[FEAT_7_0_EBX] = {
.feat_names = {
- "fsgsbase", "tsc_adjust", NULL, "bmi1",
+ "fsgsbase", "tsc-adjust", NULL, "bmi1",
"hle", "avx2", NULL, "smep",
"bmi2", "erms", "invpcid", "rtm",
NULL, NULL, "mpx", NULL,
@@ -535,6 +535,20 @@ typedef struct ExtSaveArea {
} ExtSaveArea;
static const ExtSaveArea x86_ext_save_areas[] = {
+ [XSTATE_FP_BIT] = {
+ /* x87 FP state component is always enabled if XSAVE is supported */
+ .feature = FEAT_1_ECX, .bits = CPUID_EXT_XSAVE,
+ /* x87 state is in the legacy region of the XSAVE area */
+ .offset = 0,
+ .size = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader),
+ },
+ [XSTATE_SSE_BIT] = {
+ /* SSE state component is always enabled if XSAVE is supported */
+ .feature = FEAT_1_ECX, .bits = CPUID_EXT_XSAVE,
+ /* SSE state is in the legacy region of the XSAVE area */
+ .offset = 0,
+ .size = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader),
+ },
[XSTATE_YMM_BIT] =
{ .feature = FEAT_1_ECX, .bits = CPUID_EXT_AVX,
.offset = offsetof(X86XSaveArea, avx_state),
@@ -568,9 +582,9 @@ static const ExtSaveArea x86_ext_save_areas[] = {
static uint32_t xsave_area_size(uint64_t mask)
{
int i;
- uint64_t ret = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader);
+ uint64_t ret = 0;
- for (i = 2; i < ARRAY_SIZE(x86_ext_save_areas); i++) {
+ for (i = 0; i < ARRAY_SIZE(x86_ext_save_areas); i++) {
const ExtSaveArea *esa = &x86_ext_save_areas[i];
if ((mask >> i) & 1) {
ret = MAX(ret, esa->offset + esa->size);
@@ -650,85 +664,6 @@ void host_cpuid(uint32_t function, uint32_t count,
*edx = vec[3];
}
-#define iswhite(c) ((c) && ((c) <= ' ' || '~' < (c)))
-
-/* general substring compare of *[s1..e1) and *[s2..e2). sx is start of
- * a substring. ex if !NULL points to the first char after a substring,
- * otherwise the string is assumed to sized by a terminating nul.
- * Return lexical ordering of *s1:*s2.
- */
-static int sstrcmp(const char *s1, const char *e1,
- const char *s2, const char *e2)
-{
- for (;;) {
- if (!*s1 || !*s2 || *s1 != *s2)
- return (*s1 - *s2);
- ++s1, ++s2;
- if (s1 == e1 && s2 == e2)
- return (0);
- else if (s1 == e1)
- return (*s2);
- else if (s2 == e2)
- return (*s1);
- }
-}
-
-/* compare *[s..e) to *altstr. *altstr may be a simple string or multiple
- * '|' delimited (possibly empty) strings in which case search for a match
- * within the alternatives proceeds left to right. Return 0 for success,
- * non-zero otherwise.
- */
-static int altcmp(const char *s, const char *e, const char *altstr)
-{
- const char *p, *q;
-
- for (q = p = altstr; ; ) {
- while (*p && *p != '|')
- ++p;
- if ((q == p && !*s) || (q != p && !sstrcmp(s, e, q, p)))
- return (0);
- if (!*p)
- return (1);
- else
- q = ++p;
- }
-}
-
-/* search featureset for flag *[s..e), if found set corresponding bit in
- * *pval and return true, otherwise return false
- */
-static bool lookup_feature(uint32_t *pval, const char *s, const char *e,
- const char **featureset)
-{
- uint32_t mask;
- const char **ppc;
- bool found = false;
-
- for (mask = 1, ppc = featureset; mask; mask <<= 1, ++ppc) {
- if (*ppc && !altcmp(s, e, *ppc)) {
- *pval |= mask;
- found = true;
- }
- }
- return found;
-}
-
-static void add_flagname_to_bitmaps(const char *flagname,
- FeatureWordArray words,
- Error **errp)
-{
- FeatureWord w;
- for (w = 0; w < FEATURE_WORDS; w++) {
- FeatureWordInfo *wi = &feature_word_info[w];
- if (lookup_feature(&words[w], flagname, NULL, wi->feat_names)) {
- break;
- }
- }
- if (w == FEATURE_WORDS) {
- error_setg(errp, "CPU feature %s not found", flagname);
- }
-}
-
/* CPU class name definitions: */
#define X86_CPU_TYPE_SUFFIX "-" TYPE_X86_CPU
@@ -1550,6 +1485,14 @@ static PropValue kvm_default_props[] = {
{ NULL, NULL },
};
+/* TCG-specific defaults that override all CPU models when using TCG
+ */
+static PropValue tcg_default_props[] = {
+ { "vme", "off" },
+ { NULL, NULL },
+};
+
+
void x86_cpu_change_kvm_default(const char *prop, const char *value)
{
PropValue *pv;
@@ -1628,6 +1571,9 @@ static void host_x86_cpu_class_init(ObjectClass *oc, void *data)
cpu_x86_fill_model_id(host_cpudef.model_id);
xcc->cpu_def = &host_cpudef;
+ xcc->model_description =
+ "KVM processor with all supported host features "
+ "(only available in KVM mode)";
/* level, xlevel, xlevel2, and the feature words are initialized on
* instance_init, because they require KVM to be initialized.
@@ -1999,13 +1945,33 @@ static inline void feat2prop(char *s)
}
}
+/* Return the feature property name for a feature flag bit */
+static const char *x86_cpu_feature_name(FeatureWord w, int bitnr)
+{
+ /* XSAVE components are automatically enabled by other features,
+ * so return the original feature name instead
+ */
+ if (w == FEAT_XSAVE_COMP_LO || w == FEAT_XSAVE_COMP_HI) {
+ int comp = (w == FEAT_XSAVE_COMP_HI) ? bitnr + 32 : bitnr;
+
+ if (comp < ARRAY_SIZE(x86_ext_save_areas) &&
+ x86_ext_save_areas[comp].bits) {
+ w = x86_ext_save_areas[comp].feature;
+ bitnr = ctz32(x86_ext_save_areas[comp].bits);
+ }
+ }
+
+ assert(bitnr < 32);
+ assert(w < FEATURE_WORDS);
+ return feature_word_info[w].feat_names[bitnr];
+}
+
/* Compatibily hack to maintain legacy +-feat semantic,
* where +-feat overwrites any feature set by
* feat=on|feat even if the later is parsed after +-feat
* (i.e. "-x2apic,x2apic=on" will result in x2apic disabled)
*/
-static FeatureWordArray plus_features = { 0 };
-static FeatureWordArray minus_features = { 0 };
+static GList *plus_features, *minus_features;
/* Parse "+feature,-feature,feature=foo" CPU feature string
*/
@@ -2036,10 +2002,12 @@ static void x86_cpu_parse_featurestr(const char *typename, char *features,
/* Compatibility syntax: */
if (featurestr[0] == '+') {
- add_flagname_to_bitmaps(featurestr + 1, plus_features, &local_err);
+ plus_features = g_list_append(plus_features,
+ g_strdup(featurestr + 1));
continue;
} else if (featurestr[0] == '-') {
- add_flagname_to_bitmaps(featurestr + 1, minus_features, &local_err);
+ minus_features = g_list_append(minus_features,
+ g_strdup(featurestr + 1));
continue;
}
@@ -2083,6 +2051,59 @@ static void x86_cpu_parse_featurestr(const char *typename, char *features,
}
}
+static void x86_cpu_load_features(X86CPU *cpu, Error **errp);
+static int x86_cpu_filter_features(X86CPU *cpu);
+
+/* Check for missing features that may prevent the CPU class from
+ * running using the current machine and accelerator.
+ */
+static void x86_cpu_class_check_missing_features(X86CPUClass *xcc,
+ strList **missing_feats)
+{
+ X86CPU *xc;
+ FeatureWord w;
+ Error *err = NULL;
+ strList **next = missing_feats;
+
+ if (xcc->kvm_required && !kvm_enabled()) {
+ strList *new = g_new0(strList, 1);
+ new->value = g_strdup("kvm");;
+ *missing_feats = new;
+ return;
+ }
+
+ xc = X86_CPU(object_new(object_class_get_name(OBJECT_CLASS(xcc))));
+
+ x86_cpu_load_features(xc, &err);
+ if (err) {
+ /* Errors at x86_cpu_load_features should never happen,
+ * but in case it does, just report the model as not
+ * runnable at all using the "type" property.
+ */
+ strList *new = g_new0(strList, 1);
+ new->value = g_strdup("type");
+ *next = new;
+ next = &new->next;
+ }
+
+ x86_cpu_filter_features(xc);
+
+ for (w = 0; w < FEATURE_WORDS; w++) {
+ uint32_t filtered = xc->filtered_features[w];
+ int i;
+ for (i = 0; i < 32; i++) {
+ if (filtered & (1UL << i)) {
+ strList *new = g_new0(strList, 1);
+ new->value = g_strdup(x86_cpu_feature_name(w, i));
+ *next = new;
+ next = &new->next;
+ }
+ }
+ }
+
+ object_unref(OBJECT(xc));
+}
+
/* Print all cpuid feature names in featureset
*/
static void listflags(FILE *f, fprintf_function print, const char **featureset)
@@ -2098,23 +2119,62 @@ static void listflags(FILE *f, fprintf_function print, const char **featureset)
}
}
-/* generate CPU information. */
+/* Sort alphabetically by type name, listing kvm_required models last. */
+static gint x86_cpu_list_compare(gconstpointer a, gconstpointer b)
+{
+ ObjectClass *class_a = (ObjectClass *)a;
+ ObjectClass *class_b = (ObjectClass *)b;
+ X86CPUClass *cc_a = X86_CPU_CLASS(class_a);
+ X86CPUClass *cc_b = X86_CPU_CLASS(class_b);
+ const char *name_a, *name_b;
+
+ if (cc_a->kvm_required != cc_b->kvm_required) {
+ /* kvm_required items go last */
+ return cc_a->kvm_required ? 1 : -1;
+ } else {
+ name_a = object_class_get_name(class_a);
+ name_b = object_class_get_name(class_b);
+ return strcmp(name_a, name_b);
+ }
+}
+
+static GSList *get_sorted_cpu_model_list(void)
+{
+ GSList *list = object_class_get_list(TYPE_X86_CPU, false);
+ list = g_slist_sort(list, x86_cpu_list_compare);
+ return list;
+}
+
+static void x86_cpu_list_entry(gpointer data, gpointer user_data)
+{
+ ObjectClass *oc = data;
+ X86CPUClass *cc = X86_CPU_CLASS(oc);
+ CPUListState *s = user_data;
+ char *name = x86_cpu_class_get_model_name(cc);
+ const char *desc = cc->model_description;
+ if (!desc) {
+ desc = cc->cpu_def->model_id;
+ }
+
+ (*s->cpu_fprintf)(s->file, "x86 %16s %-48s\n",
+ name, desc);
+ g_free(name);
+}
+
+/* list available CPU models and flags */
void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf)
{
- X86CPUDefinition *def;
- char buf[256];
int i;
+ CPUListState s = {
+ .file = f,
+ .cpu_fprintf = cpu_fprintf,
+ };
+ GSList *list;
- for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) {
- def = &builtin_x86_defs[i];
- snprintf(buf, sizeof(buf), "%s", def->name);
- (*cpu_fprintf)(f, "x86 %16s %-48s\n", buf, def->model_id);
- }
-#ifdef CONFIG_KVM
- (*cpu_fprintf)(f, "x86 %16s %-48s\n", "host",
- "KVM processor with all supported host features "
- "(only available in KVM mode)");
-#endif
+ (*cpu_fprintf)(f, "Available CPUs:\n");
+ list = get_sorted_cpu_model_list();
+ g_slist_foreach(list, x86_cpu_list_entry, &s);
+ g_slist_free(list);
(*cpu_fprintf)(f, "\nRecognized CPUID flags:\n");
for (i = 0; i < ARRAY_SIZE(feature_word_info); i++) {
@@ -2126,26 +2186,31 @@ void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf)
}
}
-CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
+static void x86_cpu_definition_entry(gpointer data, gpointer user_data)
{
- CpuDefinitionInfoList *cpu_list = NULL;
- X86CPUDefinition *def;
- int i;
+ ObjectClass *oc = data;
+ X86CPUClass *cc = X86_CPU_CLASS(oc);
+ CpuDefinitionInfoList **cpu_list = user_data;
+ CpuDefinitionInfoList *entry;
+ CpuDefinitionInfo *info;
- for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) {
- CpuDefinitionInfoList *entry;
- CpuDefinitionInfo *info;
-
- def = &builtin_x86_defs[i];
- info = g_malloc0(sizeof(*info));
- info->name = g_strdup(def->name);
+ info = g_malloc0(sizeof(*info));
+ info->name = x86_cpu_class_get_model_name(cc);
+ x86_cpu_class_check_missing_features(cc, &info->unavailable_features);
+ info->has_unavailable_features = true;
- entry = g_malloc0(sizeof(*entry));
- entry->value = info;
- entry->next = cpu_list;
- cpu_list = entry;
- }
+ entry = g_malloc0(sizeof(*entry));
+ entry->value = info;
+ entry->next = *cpu_list;
+ *cpu_list = entry;
+}
+CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
+{
+ CpuDefinitionInfoList *cpu_list = NULL;
+ GSList *list = get_sorted_cpu_model_list();
+ g_slist_foreach(list, x86_cpu_definition_entry, &cpu_list);
+ g_slist_free(list);
return cpu_list;
}
@@ -2183,14 +2248,11 @@ static int x86_cpu_filter_features(X86CPU *cpu)
for (w = 0; w < FEATURE_WORDS; w++) {
uint32_t host_feat =
- x86_cpu_get_supported_feature_word(w, cpu->migratable);
+ x86_cpu_get_supported_feature_word(w, false);
uint32_t requested_features = env->features[w];
env->features[w] &= host_feat;
cpu->filtered_features[w] = requested_features & ~env->features[w];
if (cpu->filtered_features[w]) {
- if (cpu->check_cpuid || cpu->enforce_cpuid) {
- report_unavailable_features(w, cpu->filtered_features[w]);
- }
rv = 1;
}
}
@@ -2198,6 +2260,15 @@ static int x86_cpu_filter_features(X86CPU *cpu)
return rv;
}
+static void x86_cpu_report_filtered_features(X86CPU *cpu)
+{
+ FeatureWord w;
+
+ for (w = 0; w < FEATURE_WORDS; w++) {
+ report_unavailable_features(w, cpu->filtered_features[w]);
+ }
+}
+
static void x86_cpu_apply_props(X86CPU *cpu, PropValue *props)
{
PropValue *pv;
@@ -2238,6 +2309,8 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp)
}
x86_cpu_apply_props(cpu, kvm_default_props);
+ } else if (tcg_enabled()) {
+ x86_cpu_apply_props(cpu, tcg_default_props);
}
env->features[FEAT_1_ECX] |= CPUID_EXT_HYPERVISOR;
@@ -2848,9 +2921,8 @@ static void mce_init(X86CPU *cpu)
}
#ifndef CONFIG_USER_ONLY
-static void x86_cpu_apic_create(X86CPU *cpu, Error **errp)
+APICCommonClass *apic_get_class(void)
{
- APICCommonState *apic;
const char *apic_type = "apic";
if (kvm_apic_in_kernel()) {
@@ -2859,7 +2931,15 @@ static void x86_cpu_apic_create(X86CPU *cpu, Error **errp)
apic_type = "xen-apic";
}
- cpu->apic_state = DEVICE(object_new(apic_type));
+ return APIC_COMMON_CLASS(object_class_by_name(apic_type));
+}
+
+static void x86_cpu_apic_create(X86CPU *cpu, Error **errp)
+{
+ APICCommonState *apic;
+ ObjectClass *apic_class = OBJECT_CLASS(apic_get_class());
+
+ cpu->apic_state = DEVICE(object_new(object_class_get_name(apic_class)));
object_property_add_child(OBJECT(cpu), "lapic",
OBJECT(cpu->apic_state), &error_abort);
@@ -2984,8 +3064,8 @@ static void x86_cpu_enable_xsave_components(X86CPU *cpu)
return;
}
- mask = (XSTATE_FP_MASK | XSTATE_SSE_MASK);
- for (i = 2; i < ARRAY_SIZE(x86_ext_save_areas); i++) {
+ mask = 0;
+ for (i = 0; i < ARRAY_SIZE(x86_ext_save_areas); i++) {
const ExtSaveArea *esa = &x86_ext_save_areas[i];
if (env->features[esa->feature] & esa->bits) {
mask |= (1ULL << i);
@@ -2996,33 +3076,13 @@ static void x86_cpu_enable_xsave_components(X86CPU *cpu)
env->features[FEAT_XSAVE_COMP_HI] = mask >> 32;
}
-#define IS_INTEL_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_INTEL_1 && \
- (env)->cpuid_vendor2 == CPUID_VENDOR_INTEL_2 && \
- (env)->cpuid_vendor3 == CPUID_VENDOR_INTEL_3)
-#define IS_AMD_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_AMD_1 && \
- (env)->cpuid_vendor2 == CPUID_VENDOR_AMD_2 && \
- (env)->cpuid_vendor3 == CPUID_VENDOR_AMD_3)
-static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
+/* Load CPUID data based on configured features */
+static void x86_cpu_load_features(X86CPU *cpu, Error **errp)
{
- CPUState *cs = CPU(dev);
- X86CPU *cpu = X86_CPU(dev);
- X86CPUClass *xcc = X86_CPU_GET_CLASS(dev);
CPUX86State *env = &cpu->env;
- Error *local_err = NULL;
- static bool ht_warned;
FeatureWord w;
-
- if (xcc->kvm_required && !kvm_enabled()) {
- char *name = x86_cpu_class_get_model_name(xcc);
- error_setg(&local_err, "CPU model '%s' requires KVM", name);
- g_free(name);
- goto out;
- }
-
- if (cpu->apic_id == UNASSIGNED_APIC_ID) {
- error_setg(errp, "apic-id property was not initialized properly");
- return;
- }
+ GList *l;
+ Error *local_err = NULL;
/*TODO: cpu->host_features incorrectly overwrites features
* set using "feat=on|off". Once we fix this, we can convert
@@ -3036,9 +3096,20 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
}
}
- for (w = 0; w < FEATURE_WORDS; w++) {
- cpu->env.features[w] |= plus_features[w];
- cpu->env.features[w] &= ~minus_features[w];
+ for (l = plus_features; l; l = l->next) {
+ const char *prop = l->data;
+ object_property_set_bool(OBJECT(cpu), true, prop, &local_err);
+ if (local_err) {
+ goto out;
+ }
+ }
+
+ for (l = minus_features; l; l = l->next) {
+ const char *prop = l->data;
+ object_property_set_bool(OBJECT(cpu), false, prop, &local_err);
+ if (local_err) {
+ goto out;
+ }
}
if (!kvm_enabled() || !cpu->expose_kvm) {
@@ -3077,14 +3148,56 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
env->cpuid_xlevel2 = env->cpuid_min_xlevel2;
}
- if (x86_cpu_filter_features(cpu) && cpu->enforce_cpuid) {
- error_setg(&local_err,
- kvm_enabled() ?
- "Host doesn't support requested features" :
- "TCG doesn't support requested features");
+out:
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ }
+}
+
+#define IS_INTEL_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_INTEL_1 && \
+ (env)->cpuid_vendor2 == CPUID_VENDOR_INTEL_2 && \
+ (env)->cpuid_vendor3 == CPUID_VENDOR_INTEL_3)
+#define IS_AMD_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_AMD_1 && \
+ (env)->cpuid_vendor2 == CPUID_VENDOR_AMD_2 && \
+ (env)->cpuid_vendor3 == CPUID_VENDOR_AMD_3)
+static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
+{
+ CPUState *cs = CPU(dev);
+ X86CPU *cpu = X86_CPU(dev);
+ X86CPUClass *xcc = X86_CPU_GET_CLASS(dev);
+ CPUX86State *env = &cpu->env;
+ Error *local_err = NULL;
+ static bool ht_warned;
+
+ if (xcc->kvm_required && !kvm_enabled()) {
+ char *name = x86_cpu_class_get_model_name(xcc);
+ error_setg(&local_err, "CPU model '%s' requires KVM", name);
+ g_free(name);
+ goto out;
+ }
+
+ if (cpu->apic_id == UNASSIGNED_APIC_ID) {
+ error_setg(errp, "apic-id property was not initialized properly");
+ return;
+ }
+
+ x86_cpu_load_features(cpu, &local_err);
+ if (local_err) {
goto out;
}
+ if (x86_cpu_filter_features(cpu) &&
+ (cpu->check_cpuid || cpu->enforce_cpuid)) {
+ x86_cpu_report_filtered_features(cpu);
+ if (cpu->enforce_cpuid) {
+ error_setg(&local_err,
+ kvm_enabled() ?
+ "Host doesn't support requested features" :
+ "TCG doesn't support requested features");
+ goto out;
+ }
+ }
+
/* On AMD CPUs, some CPUID[8000_0001].EDX bits must match the bits on
* CPUID[1].EDX.
*/
@@ -3332,27 +3445,22 @@ static void x86_cpu_register_feature_bit_props(X86CPU *cpu,
FeatureWord w,
int bitnr)
{
- Object *obj = OBJECT(cpu);
- int i;
- char **names;
FeatureWordInfo *fi = &feature_word_info[w];
+ const char *name = fi->feat_names[bitnr];
- if (!fi->feat_names[bitnr]) {
+ if (!name) {
return;
}
- names = g_strsplit(fi->feat_names[bitnr], "|", 0);
-
- feat2prop(names[0]);
- x86_cpu_register_bit_prop(cpu, names[0], &cpu->env.features[w], bitnr);
-
- for (i = 1; names[i]; i++) {
- feat2prop(names[i]);
- object_property_add_alias(obj, names[i], obj, names[0],
- &error_abort);
- }
-
- g_strfreev(names);
+ /* Property names should use "-" instead of "_".
+ * Old names containing underscores are registered as aliases
+ * using object_property_add_alias()
+ */
+ assert(!strchr(name, '_'));
+ /* aliases don't use "|" delimiters anymore, they are registered
+ * manually using object_property_add_alias() */
+ assert(!strchr(name, '|'));
+ x86_cpu_register_bit_prop(cpu, name, &cpu->env.features[w], bitnr);
}
static void x86_cpu_initfn(Object *obj)
@@ -3400,6 +3508,36 @@ static void x86_cpu_initfn(Object *obj)
}
}
+ object_property_add_alias(obj, "sse3", obj, "pni", &error_abort);
+ object_property_add_alias(obj, "pclmuldq", obj, "pclmulqdq", &error_abort);
+ object_property_add_alias(obj, "sse4-1", obj, "sse4.1", &error_abort);
+ object_property_add_alias(obj, "sse4-2", obj, "sse4.2", &error_abort);
+ object_property_add_alias(obj, "xd", obj, "nx", &error_abort);
+ object_property_add_alias(obj, "ffxsr", obj, "fxsr-opt", &error_abort);
+ object_property_add_alias(obj, "i64", obj, "lm", &error_abort);
+
+ object_property_add_alias(obj, "ds_cpl", obj, "ds-cpl", &error_abort);
+ object_property_add_alias(obj, "tsc_adjust", obj, "tsc-adjust", &error_abort);
+ object_property_add_alias(obj, "fxsr_opt", obj, "fxsr-opt", &error_abort);
+ object_property_add_alias(obj, "lahf_lm", obj, "lahf-lm", &error_abort);
+ object_property_add_alias(obj, "cmp_legacy", obj, "cmp-legacy", &error_abort);
+ object_property_add_alias(obj, "nodeid_msr", obj, "nodeid-msr", &error_abort);
+ object_property_add_alias(obj, "perfctr_core", obj, "perfctr-core", &error_abort);
+ object_property_add_alias(obj, "perfctr_nb", obj, "perfctr-nb", &error_abort);
+ object_property_add_alias(obj, "kvm_nopiodelay", obj, "kvm-nopiodelay", &error_abort);
+ object_property_add_alias(obj, "kvm_mmu", obj, "kvm-mmu", &error_abort);
+ object_property_add_alias(obj, "kvm_asyncpf", obj, "kvm-asyncpf", &error_abort);
+ object_property_add_alias(obj, "kvm_steal_time", obj, "kvm-steal-time", &error_abort);
+ object_property_add_alias(obj, "kvm_pv_eoi", obj, "kvm-pv-eoi", &error_abort);
+ object_property_add_alias(obj, "kvm_pv_unhalt", obj, "kvm-pv-unhalt", &error_abort);
+ object_property_add_alias(obj, "svm_lock", obj, "svm-lock", &error_abort);
+ object_property_add_alias(obj, "nrip_save", obj, "nrip-save", &error_abort);
+ object_property_add_alias(obj, "tsc_scale", obj, "tsc-scale", &error_abort);
+ object_property_add_alias(obj, "vmcb_clean", obj, "vmcb-clean", &error_abort);
+ object_property_add_alias(obj, "pause_filter", obj, "pause-filter", &error_abort);
+ object_property_add_alias(obj, "sse4_1", obj, "sse4.1", &error_abort);
+ object_property_add_alias(obj, "sse4_2", obj, "sse4.2", &error_abort);
+
x86_cpu_load_def(cpu, xcc->cpu_def, &error_abort);
}
@@ -3537,11 +3675,6 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data)
cc->cpu_exec_exit = x86_cpu_exec_exit;
dc->cannot_instantiate_with_device_add_yet = false;
- /*
- * Reason: x86_cpu_initfn() calls cpu_exec_init(), which saves the
- * object in cpus -> dangling pointer after final object_unref().
- */
- dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo x86_cpu_type_info = {
diff --git a/target-i386/kvm-stub.c b/target-i386/kvm-stub.c
index cdf1506109..bda4dc2f0c 100644
--- a/target-i386/kvm-stub.c
+++ b/target-i386/kvm-stub.c
@@ -25,6 +25,11 @@ bool kvm_has_smm(void)
return 1;
}
+bool kvm_enable_x2apic(void)
+{
+ return false;
+}
+
/* This function is only called inside conditionals which we
* rely on the compiler to optimize out when CONFIG_KVM is not
* defined.
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index ee1f53e569..0472f45fd0 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -122,6 +122,32 @@ bool kvm_allows_irq0_override(void)
return !kvm_irqchip_in_kernel() || kvm_has_gsi_routing();
}
+static bool kvm_x2apic_api_set_flags(uint64_t flags)
+{
+ KVMState *s = KVM_STATE(current_machine->accelerator);
+
+ return !kvm_vm_enable_cap(s, KVM_CAP_X2APIC_API, 0, flags);
+}
+
+#define MEMORIZE(fn) \
+ ({ \
+ static typeof(fn) _result; \
+ static bool _memorized; \
+ \
+ if (_memorized) { \
+ return _result; \
+ } \
+ _memorized = true; \
+ _result = fn; \
+ })
+
+bool kvm_enable_x2apic(void)
+{
+ return MEMORIZE(
+ kvm_x2apic_api_set_flags(KVM_X2APIC_API_USE_32BIT_IDS |
+ KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK));
+}
+
static int kvm_get_tsc(CPUState *cs)
{
X86CPU *cpu = X86_CPU(cs);
diff --git a/target-i386/kvm_i386.h b/target-i386/kvm_i386.h
index 36407e0a5d..5c369b1c5b 100644
--- a/target-i386/kvm_i386.h
+++ b/target-i386/kvm_i386.h
@@ -43,4 +43,5 @@ int kvm_device_msix_deassign(KVMState *s, uint32_t dev_id);
void kvm_put_apicbase(X86CPU *cpu, uint64_t value);
+bool kvm_enable_x2apic(void);
#endif
diff --git a/tests/Makefile.include b/tests/Makefile.include
index a77777cf9c..cbe38adc32 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -116,6 +116,8 @@ check-unit-$(CONFIG_REPLICATION) += tests/test-replication$(EXESUF)
check-unit-y += tests/test-bufferiszero$(EXESUF)
gcov-files-check-bufferiszero-y = util/bufferiszero.c
check-unit-y += tests/test-uuid$(EXESUF)
+check-unit-y += tests/ptimer-test$(EXESUF)
+gcov-files-ptimer-test-y = hw/core/ptimer.c
check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -295,6 +297,7 @@ check-qtest-sparc64-y = tests/endianness-test$(EXESUF)
check-qtest-arm-y = tests/tmp105-test$(EXESUF)
check-qtest-arm-y += tests/ds1338-test$(EXESUF)
+check-qtest-arm-y += tests/m25p80-test$(EXESUF)
gcov-files-arm-y += hw/misc/tmp105.c
check-qtest-arm-y += tests/virtio-blk-test$(EXESUF)
gcov-files-arm-y += arm-softmmu/hw/block/virtio-blk.c
@@ -306,7 +309,6 @@ check-qtest-xtensaeb-y = $(check-qtest-xtensa-y)
check-qtest-s390x-y = tests/boot-serial-test$(EXESUF)
check-qtest-generic-y += tests/qom-test$(EXESUF)
-check-qtest-generic-y += tests/ptimer-test$(EXESUF)
qapi-schema += alternate-any.json
qapi-schema += alternate-array.json
@@ -514,6 +516,7 @@ tests/test-timed-average$(EXESUF): tests/test-timed-average.o qemu-timer.o \
$(test-util-obj-y)
tests/test-base64$(EXESUF): tests/test-base64.o \
libqemuutil.a libqemustub.a
+tests/ptimer-test$(EXESUF): tests/ptimer-test.o tests/ptimer-test-stubs.o hw/core/ptimer.o libqemustub.a
tests/test-logging$(EXESUF): tests/test-logging.o $(test-util-obj-y)
@@ -626,6 +629,7 @@ tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o \
tests/pxe-test$(EXESUF): tests/pxe-test.o tests/boot-sector.o $(libqos-obj-y)
tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y)
tests/ds1338-test$(EXESUF): tests/ds1338-test.o $(libqos-imx-obj-y)
+tests/m25p80-test$(EXESUF): tests/m25p80-test.o
tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y)
tests/q35-test$(EXESUF): tests/q35-test.o $(libqos-pc-obj-y)
tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y)
@@ -675,7 +679,6 @@ tests/test-filter-redirector$(EXESUF): tests/test-filter-redirector.o $(qtest-ob
tests/test-x86-cpuid-compat$(EXESUF): tests/test-x86-cpuid-compat.o $(qtest-obj-y)
tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y)
tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o
-tests/ptimer-test$(EXESUF): tests/ptimer-test.o tests/ptimer-test-stubs.o hw/core/ptimer.o
tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y)
tests/migration/stress$(EXESUF): tests/migration/stress.o
diff --git a/tests/m25p80-test.c b/tests/m25p80-test.c
new file mode 100644
index 0000000000..cb7ec81f1a
--- /dev/null
+++ b/tests/m25p80-test.c
@@ -0,0 +1,252 @@
+/*
+ * QTest testcase for the M25P80 Flash (Using the Aspeed SPI
+ * Controller)
+ *
+ * Copyright (C) 2016 IBM Corp.
+ *
+ * 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 "qemu/osdep.h"
+#include "qemu/bswap.h"
+#include "libqtest.h"
+
+/*
+ * ASPEED SPI Controller registers
+ */
+#define R_CONF 0x00
+#define CONF_ENABLE_W0 (1 << 16)
+#define R_CE_CTRL 0x04
+#define CRTL_EXTENDED0 0 /* 32 bit addressing for SPI */
+#define R_CTRL0 0x10
+#define CTRL_CE_STOP_ACTIVE (1 << 2)
+#define CTRL_USERMODE 0x3
+
+#define ASPEED_FMC_BASE 0x1E620000
+#define ASPEED_FLASH_BASE 0x20000000
+
+/*
+ * Flash commands
+ */
+enum {
+ JEDEC_READ = 0x9f,
+ BULK_ERASE = 0xc7,
+ READ = 0x03,
+ PP = 0x02,
+ WREN = 0x6,
+ EN_4BYTE_ADDR = 0xB7,
+ ERASE_SECTOR = 0xd8,
+};
+
+#define FLASH_JEDEC 0x20ba19 /* n25q256a */
+#define FLASH_SIZE (32 * 1024 * 1024)
+
+#define PAGE_SIZE 256
+
+/*
+ * Use an explicit bswap for the values read/wrote to the flash region
+ * as they are BE and the Aspeed CPU is LE.
+ */
+static inline uint32_t make_be32(uint32_t data)
+{
+ return bswap32(data);
+}
+
+static void spi_conf(uint32_t value)
+{
+ uint32_t conf = readl(ASPEED_FMC_BASE + R_CONF);
+
+ conf |= value;
+ writel(ASPEED_FMC_BASE + R_CONF, conf);
+}
+
+static void spi_ctrl_start_user(void)
+{
+ uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0);
+
+ ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE;
+ writel(ASPEED_FMC_BASE + R_CTRL0, ctrl);
+
+ ctrl &= ~CTRL_CE_STOP_ACTIVE;
+ writel(ASPEED_FMC_BASE + R_CTRL0, ctrl);
+}
+
+static void spi_ctrl_stop_user(void)
+{
+ uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0);
+
+ ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE;
+ writel(ASPEED_FMC_BASE + R_CTRL0, ctrl);
+}
+
+static void test_read_jedec(void)
+{
+ uint32_t jedec = 0x0;
+
+ spi_conf(CONF_ENABLE_W0);
+
+ spi_ctrl_start_user();
+ writeb(ASPEED_FLASH_BASE, JEDEC_READ);
+ jedec |= readb(ASPEED_FLASH_BASE) << 16;
+ jedec |= readb(ASPEED_FLASH_BASE) << 8;
+ jedec |= readb(ASPEED_FLASH_BASE);
+ spi_ctrl_stop_user();
+
+ g_assert_cmphex(jedec, ==, FLASH_JEDEC);
+}
+
+static void read_page(uint32_t addr, uint32_t *page)
+{
+ int i;
+
+ spi_ctrl_start_user();
+
+ writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR);
+ writeb(ASPEED_FLASH_BASE, READ);
+ writel(ASPEED_FLASH_BASE, make_be32(addr));
+
+ /* Continuous read are supported */
+ for (i = 0; i < PAGE_SIZE / 4; i++) {
+ page[i] = make_be32(readl(ASPEED_FLASH_BASE));
+ }
+ spi_ctrl_stop_user();
+}
+
+static void test_erase_sector(void)
+{
+ uint32_t some_page_addr = 0x600 * PAGE_SIZE;
+ uint32_t page[PAGE_SIZE / 4];
+ int i;
+
+ spi_conf(CONF_ENABLE_W0);
+
+ spi_ctrl_start_user();
+ writeb(ASPEED_FLASH_BASE, WREN);
+ writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR);
+ writeb(ASPEED_FLASH_BASE, ERASE_SECTOR);
+ writel(ASPEED_FLASH_BASE, make_be32(some_page_addr));
+ spi_ctrl_stop_user();
+
+ /* Previous page should be full of zeroes as backend is not
+ * initialized */
+ read_page(some_page_addr - PAGE_SIZE, page);
+ for (i = 0; i < PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, 0x0);
+ }
+
+ /* But this one was erased */
+ read_page(some_page_addr, page);
+ for (i = 0; i < PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, 0xffffffff);
+ }
+}
+
+static void test_erase_all(void)
+{
+ uint32_t some_page_addr = 0x15000 * PAGE_SIZE;
+ uint32_t page[PAGE_SIZE / 4];
+ int i;
+
+ spi_conf(CONF_ENABLE_W0);
+
+ /* Check some random page. Should be full of zeroes as backend is
+ * not initialized */
+ read_page(some_page_addr, page);
+ for (i = 0; i < PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, 0x0);
+ }
+
+ spi_ctrl_start_user();
+ writeb(ASPEED_FLASH_BASE, WREN);
+ writeb(ASPEED_FLASH_BASE, BULK_ERASE);
+ spi_ctrl_stop_user();
+
+ /* Recheck that some random page */
+ read_page(some_page_addr, page);
+ for (i = 0; i < PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, 0xffffffff);
+ }
+}
+
+static void test_write_page(void)
+{
+ uint32_t my_page_addr = 0x14000 * PAGE_SIZE; /* beyond 16MB */
+ uint32_t some_page_addr = 0x15000 * PAGE_SIZE;
+ uint32_t page[PAGE_SIZE / 4];
+ int i;
+
+ spi_conf(CONF_ENABLE_W0);
+
+ spi_ctrl_start_user();
+ writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR);
+ writeb(ASPEED_FLASH_BASE, PP);
+ writel(ASPEED_FLASH_BASE, make_be32(my_page_addr));
+
+ /* Fill the page with its own addresses */
+ for (i = 0; i < PAGE_SIZE / 4; i++) {
+ writel(ASPEED_FLASH_BASE, make_be32(my_page_addr + i * 4));
+ }
+ spi_ctrl_stop_user();
+
+ /* Check what was written */
+ read_page(my_page_addr, page);
+ for (i = 0; i < PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, my_page_addr + i * 4);
+ }
+
+ /* Check some other page. It should be full of 0xff */
+ read_page(some_page_addr, page);
+ for (i = 0; i < PAGE_SIZE / 4; i++) {
+ g_assert_cmphex(page[i], ==, 0xffffffff);
+ }
+}
+
+static char tmp_path[] = "/tmp/qtest.m25p80.XXXXXX";
+
+int main(int argc, char **argv)
+{
+ int ret;
+ int fd;
+ char *args;
+
+ g_test_init(&argc, &argv, NULL);
+
+ fd = mkstemp(tmp_path);
+ g_assert(fd >= 0);
+ ret = ftruncate(fd, FLASH_SIZE);
+ g_assert(ret == 0);
+ close(fd);
+
+ args = g_strdup_printf("-m 256 -machine palmetto-bmc "
+ "-drive file=%s,format=raw,if=mtd",
+ tmp_path);
+ qtest_start(args);
+
+ qtest_add_func("/m25p80/read_jedec", test_read_jedec);
+ qtest_add_func("/m25p80/erase_sector", test_erase_sector);
+ qtest_add_func("/m25p80/erase_all", test_erase_all);
+ qtest_add_func("/m25p80/write_page", test_write_page);
+
+ ret = g_test_run();
+
+ qtest_quit(global_qtest);
+ unlink(tmp_path);
+ g_free(args);
+ return ret;
+}
diff --git a/tests/ptimer-test-stubs.c b/tests/ptimer-test-stubs.c
index 92a22fbb09..e028a81a29 100644
--- a/tests/ptimer-test-stubs.c
+++ b/tests/ptimer-test-stubs.c
@@ -11,9 +11,16 @@
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
#include "sysemu/replay.h"
+#include "migration/vmstate.h"
#include "ptimer-test.h"
+const VMStateInfo vmstate_info_uint8;
+const VMStateInfo vmstate_info_uint32;
+const VMStateInfo vmstate_info_uint64;
+const VMStateInfo vmstate_info_int64;
+const VMStateInfo vmstate_info_timer;
+
struct QEMUBH {
QEMUBHFunc *cb;
void *opaque;
diff --git a/tests/ptimer-test.c b/tests/ptimer-test.c
index f207eeb0eb..7b0ddf64e0 100644
--- a/tests/ptimer-test.c
+++ b/tests/ptimer-test.c
@@ -505,47 +505,47 @@ static void add_ptimer_tests(uint8_t policy)
g_sprintf(policy_name, "default");
}
- qtest_add_data_func(
+ g_test_add_data_func(
g_strdup_printf("/ptimer/set_count policy=%s", policy_name),
ppolicy, check_set_count);
- qtest_add_data_func(
+ g_test_add_data_func(
g_strdup_printf("/ptimer/set_limit policy=%s", policy_name),
ppolicy, check_set_limit);
- qtest_add_data_func(
+ g_test_add_data_func(
g_strdup_printf("/ptimer/oneshot policy=%s", policy_name),
ppolicy, check_oneshot);
- qtest_add_data_func(
+ g_test_add_data_func(
g_strdup_printf("/ptimer/periodic policy=%s", policy_name),
ppolicy, check_periodic);
- qtest_add_data_func(
+ g_test_add_data_func(
g_strdup_printf("/ptimer/on_the_fly_mode_change policy=%s", policy_name),
ppolicy, check_on_the_fly_mode_change);
- qtest_add_data_func(
+ g_test_add_data_func(
g_strdup_printf("/ptimer/on_the_fly_period_change policy=%s", policy_name),
ppolicy, check_on_the_fly_period_change);
- qtest_add_data_func(
+ g_test_add_data_func(
g_strdup_printf("/ptimer/on_the_fly_freq_change policy=%s", policy_name),
ppolicy, check_on_the_fly_freq_change);
- qtest_add_data_func(
+ g_test_add_data_func(
g_strdup_printf("/ptimer/run_with_period_0 policy=%s", policy_name),
ppolicy, check_run_with_period_0);
- qtest_add_data_func(
+ g_test_add_data_func(
g_strdup_printf("/ptimer/run_with_delta_0 policy=%s", policy_name),
ppolicy, check_run_with_delta_0);
- qtest_add_data_func(
+ g_test_add_data_func(
g_strdup_printf("/ptimer/periodic_with_load_0 policy=%s", policy_name),
ppolicy, check_periodic_with_load_0);
- qtest_add_data_func(
+ g_test_add_data_func(
g_strdup_printf("/ptimer/oneshot_with_load_0 policy=%s", policy_name),
ppolicy, check_oneshot_with_load_0);
}
diff --git a/tests/test-x86-cpuid-compat.c b/tests/test-x86-cpuid-compat.c
index 83162a44c0..260dd2795c 100644
--- a/tests/test-x86-cpuid-compat.c
+++ b/tests/test-x86-cpuid-compat.c
@@ -3,6 +3,7 @@
#include "qapi/qmp/qlist.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qint.h"
+#include "qapi/qmp/qbool.h"
#include "libqtest.h"
static char *get_cpu0_qom_path(void)
@@ -34,6 +35,15 @@ static QObject *qom_get(const char *path, const char *prop)
return ret;
}
+static bool qom_get_bool(const char *path, const char *prop)
+{
+ QBool *value = qobject_to_qbool(qom_get(path, prop));
+ bool b = qbool_get_bool(value);
+
+ QDECREF(value);
+ return b;
+}
+
typedef struct CpuidTestArgs {
const char *cmdline;
const char *property;
@@ -66,10 +76,44 @@ static void add_cpuid_test(const char *name, const char *cmdline,
qtest_add_data_func(name, args, test_cpuid_prop);
}
+static void test_plus_minus(void)
+{
+ char *path;
+
+ /* Rules:
+ * 1)"-foo" overrides "+foo"
+ * 2) "[+-]foo" overrides "foo=..."
+ * 3) Old feature names with underscores (e.g. "sse4_2")
+ * should keep working
+ *
+ * Note: rules 1 and 2 are planned to be removed soon, but we
+ * need to keep compatibility for a while until we start
+ * warning users about it.
+ */
+ qtest_start("-cpu pentium,-fpu,+fpu,-mce,mce=on,+cx8,cx8=off,+sse4_1,sse4_2=on");
+ path = get_cpu0_qom_path();
+
+ g_assert_false(qom_get_bool(path, "fpu"));
+ g_assert_false(qom_get_bool(path, "mce"));
+ g_assert_true(qom_get_bool(path, "cx8"));
+
+ /* Test both the original and the alias feature names: */
+ g_assert_true(qom_get_bool(path, "sse4-1"));
+ g_assert_true(qom_get_bool(path, "sse4.1"));
+
+ g_assert_true(qom_get_bool(path, "sse4-2"));
+ g_assert_true(qom_get_bool(path, "sse4.2"));
+
+ qtest_end();
+ g_free(path);
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
+ qtest_add_func("x86/cpuid/parsing-plus-minus", test_plus_minus);
+
/* Original level values for CPU models: */
add_cpuid_test("x86/cpuid/phenom/level",
"-cpu phenom", "level", 5);
diff --git a/vl.c b/vl.c
index 1c0b0ba834..ebd47af6cd 100644
--- a/vl.c
+++ b/vl.c
@@ -4388,11 +4388,6 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
- if (!linux_boot && qemu_opt_get(machine_opts, "dtb")) {
- error_report("-dtb only allowed with -kernel option");
- exit(1);
- }
-
if (semihosting_enabled() && !semihosting_get_argc() && kernel_filename) {
/* fall back to the -kernel/-append */
semihosting_arg_fallback(kernel_filename, kernel_cmdline);